bug 780409 - update harfbuzz to commit 8ba8042... to pick up improvements from toronto hackfest. r=jdaggett
authorJonathan Kew <jkew@mozilla.com>
Mon, 06 Aug 2012 10:52:25 +0100
changeset 101544 e62bc7eb40318d729f69a3e47a6fb5d2227a66a7
parent 101543 77e5cdc7af9a5d58296200221be631b4cd41bb9e
child 101545 f24229bc0ec81d97f9248789a1df23251c81e404
push id23239
push useremorley@mozilla.com
push dateMon, 06 Aug 2012 14:40:43 +0000
treeherdermozilla-central@f24229bc0ec8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs780409
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 780409 - update harfbuzz to commit 8ba8042... to pick up improvements from toronto hackfest. r=jdaggett
gfx/harfbuzz/src/Makefile.am
gfx/harfbuzz/src/Makefile.in
gfx/harfbuzz/src/check-c-linkage-decls.sh
gfx/harfbuzz/src/check-internal-symbols.sh
gfx/harfbuzz/src/check-static-inits.sh
gfx/harfbuzz/src/hb-atomic-private.hh
gfx/harfbuzz/src/hb-blob.cc
gfx/harfbuzz/src/hb-blob.h
gfx/harfbuzz/src/hb-buffer-private.hh
gfx/harfbuzz/src/hb-buffer.cc
gfx/harfbuzz/src/hb-buffer.h
gfx/harfbuzz/src/hb-cache-private.hh
gfx/harfbuzz/src/hb-common.cc
gfx/harfbuzz/src/hb-common.h
gfx/harfbuzz/src/hb-coretext.cc
gfx/harfbuzz/src/hb-coretext.h
gfx/harfbuzz/src/hb-fallback-shape.cc
gfx/harfbuzz/src/hb-font-private.hh
gfx/harfbuzz/src/hb-font.cc
gfx/harfbuzz/src/hb-font.h
gfx/harfbuzz/src/hb-ft.cc
gfx/harfbuzz/src/hb-glib.cc
gfx/harfbuzz/src/hb-graphite2.cc
gfx/harfbuzz/src/hb-icu.cc
gfx/harfbuzz/src/hb-mutex-private.hh
gfx/harfbuzz/src/hb-object-private.hh
gfx/harfbuzz/src/hb-old.cc
gfx/harfbuzz/src/hb-open-file-private.hh
gfx/harfbuzz/src/hb-open-type-private.hh
gfx/harfbuzz/src/hb-ot-head-table.hh
gfx/harfbuzz/src/hb-ot-hhea-table.hh
gfx/harfbuzz/src/hb-ot-hmtx-table.hh
gfx/harfbuzz/src/hb-ot-layout-common-private.hh
gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
gfx/harfbuzz/src/hb-ot-layout-private.hh
gfx/harfbuzz/src/hb-ot-layout.cc
gfx/harfbuzz/src/hb-ot-layout.h
gfx/harfbuzz/src/hb-ot-map-private.hh
gfx/harfbuzz/src/hb-ot-map.cc
gfx/harfbuzz/src/hb-ot-maxp-table.hh
gfx/harfbuzz/src/hb-ot-name-table.hh
gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh
gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl
gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-misc.cc
gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh
gfx/harfbuzz/src/hb-ot-shape-normalize.cc
gfx/harfbuzz/src/hb-ot-shape-private.hh
gfx/harfbuzz/src/hb-ot-shape.cc
gfx/harfbuzz/src/hb-private.hh
gfx/harfbuzz/src/hb-set-private.hh
gfx/harfbuzz/src/hb-set.cc
gfx/harfbuzz/src/hb-set.h
gfx/harfbuzz/src/hb-shape-plan-private.hh
gfx/harfbuzz/src/hb-shape-plan.cc
gfx/harfbuzz/src/hb-shape-plan.h
gfx/harfbuzz/src/hb-shape.cc
gfx/harfbuzz/src/hb-shape.h
gfx/harfbuzz/src/hb-shaper-impl-private.hh
gfx/harfbuzz/src/hb-shaper-list.hh
gfx/harfbuzz/src/hb-shaper-private.hh
gfx/harfbuzz/src/hb-shaper.cc
gfx/harfbuzz/src/hb-tt-font.cc
gfx/harfbuzz/src/hb-unicode-private.hh
gfx/harfbuzz/src/hb-unicode.cc
gfx/harfbuzz/src/hb-unicode.h
gfx/harfbuzz/src/hb-uniscribe.cc
gfx/harfbuzz/src/hb-uniscribe.h
gfx/harfbuzz/src/hb-warning.cc
gfx/harfbuzz/src/indic.cc
gfx/harfbuzz/src/main.cc
gfx/harfbuzz/src/test-would-substitute.cc
gfx/harfbuzz/src/test.cc
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -1,32 +1,34 @@
 # Process this file with automake to produce Makefile.in
 
 NULL =
+SUBDIRS =
+DIST_SUBDIRS =
 BUILT_SOURCES =
 EXTRA_DIST =
 CLEANFILES =
 DISTCLEANFILES =
 MAINTAINERCLEANFILES =
+DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
 
-# The following warning options are useful for debugging: -Wpadded -Wcast-align
+# The following warning options are useful for debugging: -Wpadded
 #AM_CXXFLAGS =
 
 lib_LTLIBRARIES = libharfbuzz.la
 
 HBCFLAGS =
 HBLIBS =
 HBSOURCES =  \
 	hb-atomic-private.hh \
 	hb-blob.cc \
 	hb-buffer-private.hh \
 	hb-buffer.cc \
 	hb-cache-private.hh \
 	hb-common.cc \
-	hb-fallback-shape-private.hh \
 	hb-fallback-shape.cc \
 	hb-font-private.hh \
 	hb-font.cc \
 	hb-mutex-private.hh \
 	hb-object-private.hh \
 	hb-open-file-private.hh \
 	hb-open-type-private.hh \
 	hb-ot-head-table.hh \
@@ -34,16 +36,23 @@ HBSOURCES =  \
 	hb-ot-hmtx-table.hh \
 	hb-ot-maxp-table.hh \
 	hb-ot-name-table.hh \
 	hb-ot-tag.cc \
 	hb-private.hh \
 	hb-set-private.hh \
 	hb-set.cc \
 	hb-shape.cc \
+	hb-shape-plan-private.hh \
+	hb-shape-plan.cc \
+	hb-shape-plan.h \
+	hb-shaper-list.hh \
+	hb-shaper-impl-private.hh \
+	hb-shaper-private.hh \
+	hb-shaper.cc \
 	hb-tt-font.cc \
 	hb-unicode-private.hh \
 	hb-unicode.cc \
 	hb-warning.cc \
 	$(NULL)
 HBHEADERS = \
 	hb.h \
 	hb-blob.h \
@@ -121,28 +130,45 @@ HBCFLAGS += $(FREETYPE_CFLAGS)
 HBLIBS   += $(FREETYPE_LIBS)
 HBSOURCES += hb-ft.cc
 HBHEADERS += hb-ft.h
 endif
 
 if HAVE_GRAPHITE2
 HBCFLAGS += $(GRAPHITE2_CFLAGS)
 HBLIBS   += $(GRAPHITE2_LIBS)
-HBSOURCES += hb-graphite2.cc hb-graphite2-private.hh
+HBSOURCES += hb-graphite2.cc
 HBHEADERS += hb-graphite2.h
 endif
 
 if HAVE_UNISCRIBE
 HBCFLAGS += $(UNISCRIBE_CFLAGS)
 HBLIBS   += $(UNISCRIBE_LIBS)
-HBSOURCES += hb-uniscribe.cc hb-uniscribe-private.hh
+HBSOURCES += hb-uniscribe.cc
 HBHEADERS += hb-uniscribe.h
 endif
 
-CXXLINK = $(LINK)
+if HAVE_CORETEXT
+HBCFLAGS += $(CORETEXT_CFLAGS)
+HBLIBS   += $(CORETEXT_LIBS)
+HBSOURCES += hb-coretext.cc
+HBHEADERS += hb-coretext.h
+endif
+
+if HAVE_HB_OLD
+SUBDIRS += hb-old
+HBCFLAGS += -I$(srcdir)/hb-old
+HBLIBS   += hb-old/libhb-old.la
+HBSOURCES += hb-old.cc
+endif
+DIST_SUBDIRS += hb-old
+
+
+# Use a C linker, not C++; Don't link to libstdc++
+libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS)
 libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS)
 nodist_libharfbuzz_la_SOURCES = $(nodist_HBSOURCES)
 libharfbuzz_la_CPPFLAGS = $(HBCFLAGS)
 libharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
 libharfbuzz_la_LIBADD = $(HBLIBS)
 pkginclude_HEADERS = $(HBHEADERS)
 nodist_pkginclude_HEADERS = hb-version.h
 
@@ -170,44 +196,67 @@ arabic-table: gen-arabic-table.py Arabic
 .PHONY: unicode-tables arabic-table indic-table
 
 BUILT_SOURCES += hb-ot-shape-complex-indic-machine.hh
 EXTRA_DIST += hb-ot-shape-complex-indic-machine.rl
 hb-ot-shape-complex-indic-machine.hh: hb-ot-shape-complex-indic-machine.rl
 	$(AM_V_GEN)$(top_srcdir)/missing --run ragel -e -F1 -o "$@.tmp" "$<" && \
 	mv "$@.tmp" "$@" || ( $(RM) "$@.tmp" && false )
 
-noinst_PROGRAMS = main indic
+noinst_PROGRAMS = main indic test-would-substitute
 bin_PROGRAMS =
 
 main_SOURCES = main.cc
 main_CPPFLAGS = $(HBCFLAGS)
 main_LDADD = libharfbuzz.la $(HBLIBS)
 
 indic_SOURCES = indic.cc
 indic_CPPFLAGS = $(HBCFLAGS)
 indic_LDADD = libharfbuzz.la $(HBLIBS)
 
+test_would_substitute_SOURCES = test-would-substitute.cc
+test_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
+test_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
+
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
 	check-header-guards.sh \
+	check-includes.sh \
 	check-internal-symbols.sh \
-	check-includes.sh \
+	check-static-inits.sh \
 	$(NULL)
 
 if HAVE_ICU
 else
 dist_check_SCRIPTS += check-libstdc++.sh
 endif
 
 TESTS = $(dist_check_SCRIPTS)
 TESTS_ENVIRONMENT = \
 	srcdir="$(srcdir)" \
 	MAKE="$(MAKE) $(AM_MAKEFLAGS)" \
 	HBSOURCES="$(HBSOURCES)" \
 	HBHEADERS="$(HBHEADERS)" \
 	$(NULL)
 
-scan:
-	g-ir-scanner $(HBCFLAGS) $(HBHEADERS) -n hb --strip-prefix=hb --library libharfbuzz.la
-
+#-include $(INTROSPECTION_MAKEFILE)
+#INTROSPECTION_GIRS = hb-1.0.gir
+#INTROSPECTION_SCANNER_ARGS = -I$(srcdir) -n hb --identifier-prefix=hb_
+#INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
+#
+#if HAVE_INTROSPECTION
+#
+#hb-1.0.gir: libharfbuzz.la
+#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)
+#
+#girdir = $(datadir)/gir-1.0
+#gir_DATA = $(INTROSPECTION_GIRS)
+#
+#typelibdir = $(libdir)/girepository-1.0
+#typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
+#
+#CLEANFILES += $(gir_DATA) $(typelib_DATA)
+#endif
 
 -include $(top_srcdir)/git.mk
--- a/gfx/harfbuzz/src/Makefile.in
+++ b/gfx/harfbuzz/src/Makefile.in
@@ -49,16 +49,18 @@ CPPSRCS	=                        \
   hb-ot-shape-complex-arabic.cc  \
   hb-ot-shape-complex-indic.cc   \
   hb-ot-shape-complex-misc.cc    \
   hb-ot-shape-normalize.cc       \
   hb-ot-shape.cc                 \
   hb-ot-tag.cc                   \
   hb-set.cc                      \
   hb-shape.cc                    \
+  hb-shape-plan.cc               \
+  hb-shaper.cc                   \
   hb-unicode.cc                  \
   hb-warning.cc                  \
   $(NULL)
 
 EXPORTS_NAMESPACES = harfbuzz
 
 EXPORTS_harfbuzz = \
   hb.h             \
@@ -67,16 +69,17 @@ EXPORTS_harfbuzz = \
   hb-common.h      \
   hb-font.h        \
   hb-ot.h          \
   hb-ot-layout.h   \
   hb-ot-tag.h      \
   hb-ot-shape.h    \
   hb-set.h         \
   hb-shape.h       \
+  hb-shape-plan.h  \
   hb-unicode.h     \
   hb-version.h     \
   $(NULL)
 
 LOCAL_INCLUDES  += -I$(srcdir) 
 
 FORCE_STATIC_LIB = 1
 
--- a/gfx/harfbuzz/src/check-c-linkage-decls.sh
+++ b/gfx/harfbuzz/src/check-c-linkage-decls.sh
@@ -2,19 +2,27 @@
 
 LC_ALL=C
 export LC_ALL
 
 test -z "$srcdir" && srcdir=.
 stat=0
 
 test "x$HBHEADERS" = x && HBHEADERS=`find . -maxdepth 1 -name 'hb*.h'`
+test "x$HBSOURCES" = x && HBSOURCES=`find . -maxdepth 1 -name 'hb*.h'`
 
 
 for x in $HBHEADERS; do
 	test -f $srcdir/$x && x=$srcdir/$x
 	if ! grep -q HB_BEGIN_DECLS "$x" || ! grep -q HB_END_DECLS "$x"; then
-		echo "Ouch, file $x does not HB_BEGIN_DECLS / HB_END_DECLS"
+		echo "Ouch, file $x does not have HB_BEGIN_DECLS / HB_END_DECLS, but it should"
+		stat=1
+	fi
+done
+for x in $HBSOURCES; do
+	test -f $srcdir/$x && x=$srcdir/$x
+	if grep -q HB_BEGIN_DECLS "$x" || grep -q HB_END_DECLS "$x"; then
+		echo "Ouch, file $x has HB_BEGIN_DECLS / HB_END_DECLS, but it shouldn't"
 		stat=1
 	fi
 done
 
 exit $stat
--- a/gfx/harfbuzz/src/check-internal-symbols.sh
+++ b/gfx/harfbuzz/src/check-internal-symbols.sh
@@ -9,22 +9,28 @@ 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
 
+if which c++filt 2>/dev/null >/dev/null; then
+	cplusplusfilt=c++filt
+else
+	cplusplusfilt=cat
+fi
+
 tested=false
 for suffix in so; do
 	so=.libs/libharfbuzz.$suffix
 	if test -f "$so"; then
-		echo "Checking that we are exposing internal symbols"
-		if nm $so | grep ' T ' | grep -v ' T _fini\>\| T _init\>\| T hb_'; then
+		echo "Checking that we are not exposing internal symbols"
+		if nm $so | grep ' [TW] ' | $cplusplusfilt | grep -v ' T _fini\>\| T _init\>\| T hb_'; 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"
new file mode 100755
--- /dev/null
+++ b/gfx/harfbuzz/src/check-static-inits.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+test -z "$srcdir" && srcdir=.
+stat=0
+
+
+if which objdump 2>/dev/null >/dev/null; then
+	:
+else
+	echo "check-static-inits.sh: 'objdump' not found; skipping test"
+	exit 77
+fi
+
+echo "Checking that no object file has static initializers"
+for obj in .libs/*.o; do
+	if objdump -t "$obj" | grep '[.]ctors'; then
+		echo "Ouch, $obj has static initializers"
+		stat=1
+	fi
+done
+
+echo "Checking that no object file has lazy static C++ constructors/destructors"
+for obj in .libs/*.o; do
+	if objdump -t "$obj" | grep '__c'; then
+		echo "Ouch, $obj has lazy static C++ constructors/destructors"
+		stat=1
+	fi
+done
+
+exit $stat
--- a/gfx/harfbuzz/src/hb-atomic-private.hh
+++ b/gfx/harfbuzz/src/hb-atomic-private.hh
@@ -40,48 +40,74 @@
 /* We need external help for these */
 
 #if 0
 
 
 #elif !defined(HB_NO_MT) && defined(_MSC_VER) && _MSC_VER >= 1600
 
 #include <intrin.h>
+/* On x86, _InterlockedCompareExchangePointer is a macro defined in concrt.h */
+#include <concrt.h>
+
 typedef long hb_atomic_int_t;
 #define hb_atomic_int_add(AI, V)	_InterlockedExchangeAdd (&(AI), (V))
-#define hb_atomic_int_set(AI, V)	((AI) = (V), MemoryBarrier ())
-#define hb_atomic_int_get(AI)		(MemoryBarrier (), (AI))
+
+#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))
 
 
 #elif !defined(HB_NO_MT) && defined(__APPLE__)
 
 #include <libkern/OSAtomic.h>
+
 typedef int32_t hb_atomic_int_t;
 #define hb_atomic_int_add(AI, V)	(OSAtomicAdd32Barrier ((V), &(AI)) - (V))
-#define hb_atomic_int_set(AI, V)	((AI) = (V), OSMemoryBarrier ())
-#define hb_atomic_int_get(AI)		(OSMemoryBarrier (), (AI))
+
+#define hb_atomic_ptr_get(P)		(OSMemoryBarrier (), (void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N)	OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P))
+
 
+#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
+
+typedef int hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V)	__sync_fetch_and_add (&(AI), (V))
+
+#define hb_atomic_ptr_get(P)		(void *) (__sync_synchronize (), *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N)	__sync_bool_compare_and_swap ((P), (O), (N))
 
 #elif !defined(HB_NO_MT) && defined(HAVE_GLIB)
 
 #include <glib.h>
-typedef volatile int hb_atomic_int_t;
+typedef int hb_atomic_int_t;
 #if GLIB_CHECK_VERSION(2,29,5)
 #define hb_atomic_int_add(AI, V)	g_atomic_int_add (&(AI), (V))
 #else
 #define hb_atomic_int_add(AI, V)	g_atomic_int_exchange_and_add (&(AI), (V))
 #endif
-#define hb_atomic_int_set(AI, V)	g_atomic_int_set (&(AI), (V))
-#define hb_atomic_int_get(AI)		g_atomic_int_get (&(AI))
+
+#define hb_atomic_ptr_get(P)		g_atomic_pointer_get (P)
+#define hb_atomic_ptr_cmpexch(P,O,N)	g_atomic_pointer_compare_and_exchange ((void **) (P), (void *) (O), (void *) (N))
 
 
-#else
+#elif !defined(HB_NO_MT)
 
-#define HB_ATOMIC_INT_NIL 1
+#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
 typedef volatile int hb_atomic_int_t;
 #define hb_atomic_int_add(AI, V)	(((AI) += (V)) - (V))
-#define hb_atomic_int_set(AI, V)	((void) ((AI) = (V)))
-#define hb_atomic_int_get(AI)		(AI)
+
+#define hb_atomic_ptr_get(P)		((void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N)	(* (void * volatile *) (P) == (void *) (O) ? (* (void * volatile *) (P) = (void *) (N), true) : false)
+
+
+#else /* HB_NO_MT */
+
+typedef int hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V)	(((AI) += (V)) - (V))
+
+#define hb_atomic_ptr_get(P)		((void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N)	(* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
 
 #endif
 
+/* TODO Add tracing. */
 
 #endif /* HB_ATOMIC_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-blob.cc
+++ b/gfx/harfbuzz/src/hb-blob.cc
@@ -41,42 +41,30 @@
 
 
 
 #ifndef HB_DEBUG_BLOB
 #define HB_DEBUG_BLOB (HB_DEBUG+0)
 #endif
 
 
-struct _hb_blob_t {
+struct hb_blob_t {
   hb_object_header_t header;
+  ASSERT_POD ();
 
   bool immutable;
 
   const char *data;
   unsigned int length;
   hb_memory_mode_t mode;
 
   void *user_data;
   hb_destroy_func_t destroy;
 };
 
-static hb_blob_t _hb_blob_nil = {
-  HB_OBJECT_HEADER_STATIC,
-
-  TRUE, /* immutable */
-
-  NULL, /* data */
-  0, /* length */
-  HB_MEMORY_MODE_READONLY, /* mode */
-
-  NULL, /* user_data */
-  NULL  /* destroy */
-};
-
 
 static bool _try_writable (hb_blob_t *blob);
 
 static void
 _hb_blob_destroy_user_data (hb_blob_t *blob)
 {
   if (blob->destroy) {
     blob->destroy (blob->user_data);
@@ -92,62 +80,75 @@ hb_blob_create (const char        *data,
 		void              *user_data,
 		hb_destroy_func_t  destroy)
 {
   hb_blob_t *blob;
 
   if (!length || !(blob = hb_object_create<hb_blob_t> ())) {
     if (destroy)
       destroy (user_data);
-    return &_hb_blob_nil;
+    return hb_blob_get_empty ();
   }
 
   blob->data = data;
   blob->length = length;
   blob->mode = mode;
 
   blob->user_data = user_data;
   blob->destroy = destroy;
 
   if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {
     blob->mode = HB_MEMORY_MODE_READONLY;
     if (!_try_writable (blob)) {
       hb_blob_destroy (blob);
-      return &_hb_blob_nil;
+      return hb_blob_get_empty ();
     }
   }
 
   return blob;
 }
 
 hb_blob_t *
 hb_blob_create_sub_blob (hb_blob_t    *parent,
 			 unsigned int  offset,
 			 unsigned int  length)
 {
   hb_blob_t *blob;
 
   if (!length || offset >= parent->length)
-    return &_hb_blob_nil;
+    return hb_blob_get_empty ();
 
   hb_blob_make_immutable (parent);
 
   blob = hb_blob_create (parent->data + offset,
 			 MIN (length, parent->length - offset),
 			 parent->mode,
 			 hb_blob_reference (parent),
 			 (hb_destroy_func_t) hb_blob_destroy);
 
   return blob;
 }
 
 hb_blob_t *
 hb_blob_get_empty (void)
 {
-  return &_hb_blob_nil;
+  static const hb_blob_t _hb_blob_nil = {
+    HB_OBJECT_HEADER_STATIC,
+
+    true, /* immutable */
+
+    NULL, /* data */
+    0, /* length */
+    HB_MEMORY_MODE_READONLY, /* mode */
+
+    NULL, /* user_data */
+    NULL  /* destroy */
+  };
+
+  return const_cast<hb_blob_t *> (&_hb_blob_nil);
 }
 
 hb_blob_t *
 hb_blob_reference (hb_blob_t *blob)
 {
   return hb_object_reference (blob);
 }
 
@@ -180,17 +181,17 @@ hb_blob_get_user_data (hb_blob_t        
 
 
 void
 hb_blob_make_immutable (hb_blob_t *blob)
 {
   if (hb_object_is_inert (blob))
     return;
 
-  blob->immutable = TRUE;
+  blob->immutable = true;
 }
 
 hb_bool_t
 hb_blob_is_immutable (hb_blob_t *blob)
 {
   return blob->immutable;
 }
 
@@ -239,86 +240,86 @@ static hb_bool_t
 #elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
   pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);
 #elif defined(HAVE_GETPAGESIZE)
   pagesize = (uintptr_t) getpagesize ();
 #endif
 
   if ((uintptr_t) -1L == pagesize) {
     DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno));
-    return FALSE;
+    return false;
   }
   DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize);
 
   mask = ~(pagesize-1);
   addr = (const char *) (((uintptr_t) blob->data) & mask);
   length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask)  - addr;
   DEBUG_MSG_FUNC (BLOB, blob,
 		  "calling mprotect on [%p..%p] (%lu bytes)",
 		  addr, addr+length, (unsigned long) length);
   if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
     DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno));
-    return FALSE;
+    return false;
   }
 
   blob->mode = HB_MEMORY_MODE_WRITABLE;
 
   DEBUG_MSG_FUNC (BLOB, blob,
 		  "successfully made [%p..%p] (%lu bytes) writable\n",
 		  addr, addr+length, (unsigned long) length);
-  return TRUE;
+  return true;
 #else
-  return FALSE;
+  return false;
 #endif
 }
 
 static bool
 _try_writable_inplace (hb_blob_t *blob)
 {
   DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n");
 
   if (_try_make_writable_inplace_unix (blob))
-    return TRUE;
+    return true;
 
   DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n");
 
   /* Failed to make writable inplace, mark that */
   blob->mode = HB_MEMORY_MODE_READONLY;
-  return FALSE;
+  return false;
 }
 
 static bool
 _try_writable (hb_blob_t *blob)
 {
   if (blob->immutable)
-    return FALSE;
+    return false;
 
   if (blob->mode == HB_MEMORY_MODE_WRITABLE)
-    return TRUE;
+    return true;
 
   if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob))
-    return TRUE;
+    return true;
 
   if (blob->mode == HB_MEMORY_MODE_WRITABLE)
-    return TRUE;
+    return true;
 
 
   DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data);
 
   char *new_data;
 
   new_data = (char *) malloc (blob->length);
   if (unlikely (!new_data))
-    return FALSE;
+    return false;
 
   DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data);
 
   memcpy (new_data, blob->data, blob->length);
   _hb_blob_destroy_user_data (blob);
   blob->mode = HB_MEMORY_MODE_WRITABLE;
   blob->data = new_data;
   blob->user_data = new_data;
   blob->destroy = free;
 
-  return TRUE;
+  return true;
 }
 
 
--- a/gfx/harfbuzz/src/hb-blob.h
+++ b/gfx/harfbuzz/src/hb-blob.h
@@ -38,17 +38,17 @@ HB_BEGIN_DECLS
 
 typedef enum {
   HB_MEMORY_MODE_DUPLICATE,
   HB_MEMORY_MODE_READONLY,
   HB_MEMORY_MODE_WRITABLE,
   HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE
 } hb_memory_mode_t;
 
-typedef struct _hb_blob_t hb_blob_t;
+typedef struct hb_blob_t hb_blob_t;
 
 hb_blob_t *
 hb_blob_create (const char        *data,
 		unsigned int       length,
 		hb_memory_mode_t   mode,
 		void              *user_data,
 		hb_destroy_func_t  destroy);
 
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -35,25 +35,58 @@
 #include "hb-object-private.hh"
 #include "hb-unicode-private.hh"
 
 
 
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20);
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t));
 
-typedef struct _hb_segment_properties_t {
+
+/*
+ * hb_segment_properties_t
+ */
+
+typedef struct hb_segment_properties_t {
     hb_direction_t      direction;
     hb_script_t         script;
     hb_language_t       language;
+    ASSERT_POD ();
 } hb_segment_properties_t;
 
+#define _HB_BUFFER_PROPS_DEFAULT { HB_DIRECTION_INVALID, HB_SCRIPT_INVALID, HB_LANGUAGE_INVALID }
+
+static inline hb_bool_t
+hb_segment_properties_equal (const hb_segment_properties_t *a,
+			     const hb_segment_properties_t *b)
+{
+  return a->direction == b->direction &&
+	 a->script    == b->script    &&
+	 a->language  == b->language;
+}
+
 
-struct _hb_buffer_t {
+static inline long
+hb_segment_properties_hash (const hb_segment_properties_t *p)
+{
+  /* TODO improve */
+  return (long) p->direction +
+	 (long) p->script +
+	 (long) p->language;
+}
+
+
+
+/*
+ * hb_buffer_t
+ */
+
+struct hb_buffer_t {
   hb_object_header_t header;
+  ASSERT_POD ();
 
   /* Information about how the text in the buffer should be treated */
 
   hb_unicode_funcs_t *unicode; /* Unicode functions */
   hb_segment_properties_t props; /* Script, language, direction */
 
   /* Buffer contents */
 
@@ -103,22 +136,21 @@ struct _hb_buffer_t {
   HB_INTERNAL void reverse_range (unsigned int start, unsigned int end);
   HB_INTERNAL void reverse (void);
   HB_INTERNAL void reverse_clusters (void);
   HB_INTERNAL void guess_properties (void);
 
   HB_INTERNAL void swap_buffers (void);
   HB_INTERNAL void clear_output (void);
   HB_INTERNAL void clear_positions (void);
-  HB_INTERNAL void replace_glyphs_be16 (unsigned int num_in,
-					unsigned int num_out,
-					const uint16_t *glyph_data_be);
+
   HB_INTERNAL void replace_glyphs (unsigned int num_in,
 				   unsigned int num_out,
 				   const hb_codepoint_t *glyph_data);
+
   HB_INTERNAL void replace_glyph (hb_codepoint_t glyph_index);
   /* Makes a copy of the glyph at idx to output and replace glyph_index */
   HB_INTERNAL void output_glyph (hb_codepoint_t glyph_index);
   /* Copies glyph at idx to output but doesn't advance idx */
   HB_INTERNAL void copy_glyph (void);
   /* Copies glyph at idx to output and advance idx.
    * If there's no output, just advance idx. */
   HB_INTERNAL void next_glyph (void);
@@ -144,27 +176,26 @@ struct _hb_buffer_t {
 				   unsigned int end);
   HB_INTERNAL void merge_out_clusters (unsigned int start,
 				       unsigned int end);
 
   /* Internal methods */
   HB_INTERNAL bool enlarge (unsigned int size);
 
   inline bool ensure (unsigned int size)
-  { return likely (size <= allocated) ? TRUE : enlarge (size); }
+  { return likely (size < allocated) ? true : enlarge (size); }
 
   HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out);
 
   HB_INTERNAL void *get_scratch_buffer (unsigned int *size);
 };
 
 
 #define HB_BUFFER_XALLOCATE_VAR(b, func, var, owner) \
   b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \
 	   sizeof (b->info[0].var), owner)
 #define HB_BUFFER_ALLOCATE_VAR(b, var) \
 	HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var (), #var)
 #define HB_BUFFER_DEALLOCATE_VAR(b, var) \
 	HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var (), #var)
 
 
-
 #endif /* HB_BUFFER_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -32,31 +32,17 @@
 #include <string.h>
 
 
 
 #ifndef HB_DEBUG_BUFFER
 #define HB_DEBUG_BUFFER (HB_DEBUG+0)
 #endif
 
-
-static hb_buffer_t _hb_buffer_nil = {
-  HB_OBJECT_HEADER_STATIC,
-
-  &_hb_unicode_funcs_default,
-  {
-    HB_DIRECTION_INVALID,
-    HB_SCRIPT_INVALID,
-    NULL,
-  },
-
-  TRUE, /* in_error */
-  TRUE, /* have_output */
-  TRUE  /* have_positions */
-};
+#define _HB_BUFFER_UNICODE_FUNCS_DEFAULT (const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_default))
 
 /* Here is how the buffer works internally:
  *
  * There are two info pointers: info and out_info.  They always have
  * the same allocated size, but different lengths.
  *
  * As an optimization, both info and out_info may point to the
  * same piece of memory, which is owned by info.  This remains the
@@ -74,17 +60,17 @@ static hb_buffer_t _hb_buffer_nil = {
 
 
 /* Internal API */
 
 bool
 hb_buffer_t::enlarge (unsigned int size)
 {
   if (unlikely (in_error))
-    return FALSE;
+    return false;
 
   unsigned int new_allocated = allocated;
   hb_glyph_position_t *new_pos = NULL;
   hb_glyph_info_t *new_info = NULL;
   bool separate_out = out_info != info;
 
   if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0]))))
     goto done;
@@ -96,17 +82,17 @@ hb_buffer_t::enlarge (unsigned int size)
   if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0]))))
     goto done;
 
   new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0]));
   new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0]));
 
 done:
   if (unlikely (!new_pos || !new_info))
-    in_error = TRUE;
+    in_error = true;
 
   if (likely (new_pos))
     pos = new_pos;
 
   if (likely (new_info))
     info = new_info;
 
   out_info = separate_out ? (hb_glyph_info_t *) pos : info;
@@ -115,67 +101,71 @@ done:
 
   return likely (!in_error);
 }
 
 bool
 hb_buffer_t::make_room_for (unsigned int num_in,
 			    unsigned int num_out)
 {
-  if (unlikely (!ensure (out_len + num_out))) return FALSE;
+  if (unlikely (!ensure (out_len + num_out))) return false;
 
   if (out_info == info &&
       out_len + num_out > idx + num_in)
   {
     assert (have_output);
 
     out_info = (hb_glyph_info_t *) pos;
     memcpy (out_info, info, out_len * sizeof (out_info[0]));
   }
 
-  return TRUE;
+  return true;
 }
 
 void *
 hb_buffer_t::get_scratch_buffer (unsigned int *size)
 {
-  have_output = FALSE;
-  have_positions = FALSE;
+  have_output = false;
+  have_positions = false;
+
   out_len = 0;
+  out_info = info;
+
   *size = allocated * sizeof (pos[0]);
   return pos;
 }
 
 
+
 /* HarfBuzz-Internal API */
 
 void
 hb_buffer_t::reset (void)
 {
   if (unlikely (hb_object_is_inert (this)))
     return;
 
   hb_unicode_funcs_destroy (unicode);
-  unicode = _hb_buffer_nil.unicode;
-
-  props = _hb_buffer_nil.props;
+  unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT;
 
-  in_error = FALSE;
-  have_output = FALSE;
-  have_positions = FALSE;
+  hb_segment_properties_t default_props = _HB_BUFFER_PROPS_DEFAULT;
+  props = default_props;
+
+  in_error = false;
+  have_output = false;
+  have_positions = false;
 
   idx = 0;
   len = 0;
   out_len = 0;
+  out_info = info;
 
   serial = 0;
   memset (allocated_var_bytes, 0, sizeof allocated_var_bytes);
   memset (allocated_var_owner, 0, sizeof allocated_var_owner);
-
-  out_info = info;
 }
 
 void
 hb_buffer_t::add (hb_codepoint_t  codepoint,
 		  hb_mask_t       mask,
 		  unsigned int    cluster)
 {
   hb_glyph_info_t *glyph;
@@ -193,42 +183,45 @@ hb_buffer_t::add (hb_codepoint_t  codepo
 }
 
 void
 hb_buffer_t::clear_output (void)
 {
   if (unlikely (hb_object_is_inert (this)))
     return;
 
-  have_output = TRUE;
-  have_positions = FALSE;
+  have_output = true;
+  have_positions = false;
 
   out_len = 0;
   out_info = info;
 }
 
 void
 hb_buffer_t::clear_positions (void)
 {
   if (unlikely (hb_object_is_inert (this)))
     return;
 
-  have_output = FALSE;
-  have_positions = TRUE;
+  have_output = false;
+  have_positions = true;
+
+  out_len = 0;
+  out_info = info;
 
   memset (pos, 0, sizeof (pos[0]) * len);
 }
 
 void
 hb_buffer_t::swap_buffers (void)
 {
   if (unlikely (in_error)) return;
 
   assert (have_output);
-  have_output = FALSE;
+  have_output = false;
 
   if (out_info != info)
   {
     hb_glyph_info_t *tmp_string;
     tmp_string = info;
     info = out_info;
     out_info = tmp_string;
     pos = (hb_glyph_position_t *) out_info;
@@ -237,120 +230,89 @@ hb_buffer_t::swap_buffers (void)
   unsigned int tmp;
   tmp = len;
   len = out_len;
   out_len = tmp;
 
   idx = 0;
 }
 
-void
-hb_buffer_t::replace_glyphs_be16 (unsigned int num_in,
-				  unsigned int num_out,
-				  const uint16_t *glyph_data_be)
-{
-  if (!make_room_for (num_in, num_out)) return;
-
-  hb_glyph_info_t orig_info = info[idx];
-  for (unsigned int i = 1; i < num_in; i++)
-  {
-    hb_glyph_info_t *inf = &info[idx + i];
-    orig_info.cluster = MIN (orig_info.cluster, inf->cluster);
-  }
-
-  hb_glyph_info_t *pinfo = &out_info[out_len];
-  for (unsigned int i = 0; i < num_out; i++)
-  {
-    *pinfo = orig_info;
-    pinfo->codepoint = hb_be_uint16 (glyph_data_be[i]);
-    pinfo++;
-  }
-
-  idx  += num_in;
-  out_len += num_out;
-}
 
 void
 hb_buffer_t::replace_glyphs (unsigned int num_in,
 			     unsigned int num_out,
 			     const uint32_t *glyph_data)
 {
-  if (!make_room_for (num_in, num_out)) return;
+  if (unlikely (!make_room_for (num_in, num_out))) return;
+
+  merge_clusters (idx, idx + num_in);
 
   hb_glyph_info_t orig_info = info[idx];
-  for (unsigned int i = 1; i < num_in; i++)
-  {
-    hb_glyph_info_t *inf = &info[idx + i];
-    orig_info.cluster = MIN (orig_info.cluster, inf->cluster);
-  }
-
   hb_glyph_info_t *pinfo = &out_info[out_len];
   for (unsigned int i = 0; i < num_out; i++)
   {
     *pinfo = orig_info;
     pinfo->codepoint = glyph_data[i];
     pinfo++;
   }
 
   idx  += num_in;
   out_len += num_out;
 }
 
 void
 hb_buffer_t::output_glyph (hb_codepoint_t glyph_index)
 {
-  if (!make_room_for (0, 1)) return;
+  if (unlikely (!make_room_for (0, 1))) return;
 
   out_info[out_len] = info[idx];
   out_info[out_len].codepoint = glyph_index;
 
   out_len++;
 }
 
 void
 hb_buffer_t::copy_glyph (void)
 {
-  if (!make_room_for (0, 1)) return;
+  if (unlikely (!make_room_for (0, 1))) return;
 
   out_info[out_len] = info[idx];
 
   out_len++;
 }
 
 void
 hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index)
 {
-  if (!make_room_for (1, 1)) return;
-
-  out_info[out_len] = info[idx];
+  if (unlikely (out_info != info || out_len != idx)) {
+    if (unlikely (!make_room_for (1, 1))) return;
+    out_info[out_len] = info[idx];
+  }
   out_info[out_len].codepoint = glyph_index;
 
   idx++;
   out_len++;
 }
 
 void
 hb_buffer_t::next_glyph (void)
 {
   if (have_output)
   {
-    if (out_info != info)
-    {
-      if (unlikely (!ensure (out_len + 1))) return;
+    if (unlikely (out_info != info || out_len != idx)) {
+      if (unlikely (!make_room_for (1, 1))) return;
       out_info[out_len] = info[idx];
     }
-    else if (out_len != idx)
-      out_info[out_len] = info[idx];
-
     out_len++;
   }
 
   idx++;
 }
 
+
 void
 hb_buffer_t::set_masks (hb_mask_t    value,
 			hb_mask_t    mask,
 			unsigned int cluster_start,
 			unsigned int cluster_end)
 {
   hb_mask_t not_mask = ~mask;
   value &= mask;
@@ -430,42 +392,76 @@ hb_buffer_t::reverse_clusters (void)
   }
   reverse_range (start, i);
 }
 
 void
 hb_buffer_t::merge_clusters (unsigned int start,
 			     unsigned int end)
 {
-  unsigned int cluster = this->info[start].cluster;
+  if (unlikely (end - start < 2))
+    return;
+
+  unsigned int cluster = info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
-    cluster = MIN (cluster, this->info[i].cluster);
+    cluster = MIN (cluster, info[i].cluster);
+
+  /* Extend end */
+  while (end < len && info[end - 1].cluster == info[end].cluster)
+    end++;
+
+  /* Extend start */
+  while (idx < start && info[start - 1].cluster == info[start].cluster)
+    start--;
+
+  /* If we hit the start of buffer, continue in out-buffer. */
+  if (idx == start)
+    for (unsigned i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
+      out_info[i - 1].cluster = cluster;
+
   for (unsigned int i = start; i < end; i++)
-    this->info[i].cluster = cluster;
+    info[i].cluster = cluster;
 }
 void
 hb_buffer_t::merge_out_clusters (unsigned int start,
 				 unsigned int end)
 {
-  unsigned int cluster = this->out_info[start].cluster;
+  if (unlikely (end - start < 2))
+    return;
+
+  unsigned int cluster = out_info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
-    cluster = MIN (cluster, this->out_info[i].cluster);
+    cluster = MIN (cluster, out_info[i].cluster);
+
+  /* Extend start */
+  while (start && out_info[start - 1].cluster == out_info[start].cluster)
+    start--;
+
+  /* Extend end */
+  while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster)
+    end++;
+
+  /* If we hit the end of out-buffer, continue in buffer. */
+  if (end == out_len)
+    for (unsigned i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
+      info[i].cluster = cluster;
+
   for (unsigned int i = start; i < end; i++)
-    this->out_info[i].cluster = cluster;
+    out_info[i].cluster = cluster;
 }
 
 void
 hb_buffer_t::guess_properties (void)
 {
   /* If script is set to INVALID, guess from buffer contents */
   if (props.script == HB_SCRIPT_INVALID) {
     for (unsigned int i = 0; i < len; i++) {
-      hb_script_t script = hb_unicode_script (unicode, info[i].codepoint);
+      hb_script_t script = unicode->script (info[i].codepoint);
       if (likely (script != HB_SCRIPT_COMMON &&
 		  script != HB_SCRIPT_INHERITED &&
 		  script != HB_SCRIPT_UNKNOWN)) {
         props.script = script;
         break;
       }
     }
   }
@@ -538,27 +534,38 @@ void hb_buffer_t::deallocate_var_all (vo
 /* Public API */
 
 hb_buffer_t *
 hb_buffer_create ()
 {
   hb_buffer_t *buffer;
 
   if (!(buffer = hb_object_create<hb_buffer_t> ()))
-    return &_hb_buffer_nil;
+    return hb_buffer_get_empty ();
 
   buffer->reset ();
 
   return buffer;
 }
 
 hb_buffer_t *
 hb_buffer_get_empty (void)
 {
-  return &_hb_buffer_nil;
+  static const hb_buffer_t _hb_buffer_nil = {
+    HB_OBJECT_HEADER_STATIC,
+
+    _HB_BUFFER_UNICODE_FUNCS_DEFAULT,
+    _HB_BUFFER_PROPS_DEFAULT,
+
+    true, /* in_error */
+    true, /* have_output */
+    true  /* have_positions */
+  };
+
+  return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
 }
 
 hb_buffer_t *
 hb_buffer_reference (hb_buffer_t *buffer)
 {
   return hb_object_reference (buffer);
 }
 
@@ -596,17 +603,17 @@ hb_buffer_get_user_data (hb_buffer_t    
 void
 hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
 			     hb_unicode_funcs_t *unicode)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
   if (!unicode)
-    unicode = _hb_buffer_nil.unicode;
+    unicode = _HB_BUFFER_UNICODE_FUNCS_DEFAULT;
 
   hb_unicode_funcs_reference (unicode);
   hb_unicode_funcs_destroy (buffer->unicode);
   buffer->unicode = unicode;
 }
 
 hb_unicode_funcs_t *
 hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer)
@@ -690,28 +697,31 @@ hb_buffer_add (hb_buffer_t    *buffer,
 {
   buffer->add (codepoint, mask, cluster);
 }
 
 hb_bool_t
 hb_buffer_set_length (hb_buffer_t  *buffer,
 		      unsigned int  length)
 {
+  if (unlikely (hb_object_is_inert (buffer)))
+    return length == 0;
+
   if (!buffer->ensure (length))
-    return FALSE;
+    return false;
 
   /* Wipe the new space */
   if (length > buffer->len) {
     memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len));
     if (buffer->have_positions)
       memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len));
   }
 
   buffer->len = length;
-  return TRUE;
+  return true;
 }
 
 unsigned int
 hb_buffer_get_length (hb_buffer_t *buffer)
 {
   return buffer->len;
 }
 
@@ -876,8 +886,84 @@ hb_buffer_add_utf32 (hb_buffer_t    *buf
 		     int             item_length)
 {
 #define UTF_NEXT(S, E, U)	((U) = *(S), (S)+1)
   ADD_UTF (uint32_t);
 #undef UTF_NEXT
 }
 
 
+static int
+compare_info_codepoint (const hb_glyph_info_t *pa,
+			const hb_glyph_info_t *pb)
+{
+  return (int) pb->codepoint - (int) pa->codepoint;
+}
+
+static inline void
+normalize_glyphs_cluster (hb_buffer_t *buffer,
+			  unsigned int start,
+			  unsigned int end,
+			  bool backward)
+{
+  hb_glyph_position_t *pos = buffer->pos;
+
+  /* Total cluster advance */
+  hb_position_t total_x_advance = 0, total_y_advance = 0;
+  for (unsigned int i = start; i < end; i++)
+  {
+    total_x_advance += pos[i].x_advance;
+    total_y_advance += pos[i].y_advance;
+  }
+
+  hb_position_t x_advance = 0, y_advance = 0;
+  for (unsigned int i = start; i < end; i++)
+  {
+    pos[i].x_offset += x_advance;
+    pos[i].y_offset += y_advance;
+
+    x_advance += pos[i].x_advance;
+    y_advance += pos[i].y_advance;
+
+    pos[i].x_advance = 0;
+    pos[i].y_advance = 0;
+  }
+
+  if (backward)
+  {
+    /* Transfer all cluster advance to the last glyph. */
+    pos[end - 1].x_advance = total_x_advance;
+    pos[end - 1].y_advance = total_y_advance;
+
+    hb_bubble_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start);
+  } else {
+    /* Transfer all cluster advance to the first glyph. */
+    pos[start].x_advance += total_x_advance;
+    pos[start].y_advance += total_y_advance;
+    for (unsigned int i = start + 1; i < end; i++) {
+      pos[i].x_offset -= total_x_advance;
+      pos[i].y_offset -= total_y_advance;
+    }
+    hb_bubble_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1);
+  }
+}
+
+void
+hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
+{
+  assert (buffer->have_positions);
+  /* XXX assert (buffer->have_glyphs); */
+
+  bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
+
+  unsigned int count = buffer->len;
+  if (unlikely (!count)) return;
+  hb_glyph_info_t *info = buffer->info;
+
+  unsigned int start = 0;
+  unsigned int end;
+  for (end = start + 1; end < count; end++)
+    if (info[start].cluster != info[end].cluster) {
+      normalize_glyphs_cluster (buffer, start, end, backward);
+      start = end;
+    }
+  normalize_glyphs_cluster (buffer, start, end, backward);
+}
--- a/gfx/harfbuzz/src/hb-buffer.h
+++ b/gfx/harfbuzz/src/hb-buffer.h
@@ -35,29 +35,29 @@
 #define HB_BUFFER_H
 
 #include "hb-common.h"
 #include "hb-unicode.h"
 
 HB_BEGIN_DECLS
 
 
-typedef struct _hb_buffer_t hb_buffer_t;
+typedef struct hb_buffer_t hb_buffer_t;
 
-typedef struct _hb_glyph_info_t {
+typedef struct hb_glyph_info_t {
   hb_codepoint_t codepoint;
   hb_mask_t      mask;
   uint32_t       cluster;
 
   /*< private >*/
   hb_var_int_t   var1;
   hb_var_int_t   var2;
 } hb_glyph_info_t;
 
-typedef struct _hb_glyph_position_t {
+typedef struct hb_glyph_position_t {
   hb_position_t  x_advance;
   hb_position_t  y_advance;
   hb_position_t  x_offset;
   hb_position_t  y_offset;
 
   /*< private >*/
   hb_var_int_t   var;
 } hb_glyph_position_t;
@@ -116,23 +116,23 @@ hb_language_t
 hb_buffer_get_language (hb_buffer_t *buffer);
 
 
 /* Resets the buffer.  Afterwards it's as if it was just created,
  * except that it has a larger buffer allocated perhaps... */
 void
 hb_buffer_reset (hb_buffer_t *buffer);
 
-/* Returns FALSE if allocation failed */
+/* Returns false if allocation failed */
 hb_bool_t
 hb_buffer_pre_allocate (hb_buffer_t  *buffer,
 		        unsigned int  size);
 
 
-/* Returns FALSE if allocation has failed before */
+/* Returns false if allocation has failed before */
 hb_bool_t
 hb_buffer_allocation_successful (hb_buffer_t  *buffer);
 
 void
 hb_buffer_reverse (hb_buffer_t *buffer);
 
 void
 hb_buffer_reverse_clusters (hb_buffer_t *buffer);
@@ -188,11 +188,24 @@ hb_buffer_get_glyph_infos (hb_buffer_t  
                            unsigned int *length);
 
 /* Return value valid as long as buffer not modified */
 hb_glyph_position_t *
 hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
                                unsigned int *length);
 
 
+/* Reorders a glyph buffer to have canonical in-cluster glyph order / position.
+ * The resulting clusters should behave identical to pre-reordering clusters.
+ * NOTE: This has nothing to do with Unicode normalization. */
+void
+hb_buffer_normalize_glyphs (hb_buffer_t *buffer);
+
+/*
+ * NOT IMPLEMENTED
+void
+hb_buffer_normalize_characters (hb_buffer_t *buffer);
+*/
+
+
 HB_END_DECLS
 
 #endif /* HB_BUFFER_H */
--- a/gfx/harfbuzz/src/hb-cache-private.hh
+++ b/gfx/harfbuzz/src/hb-cache-private.hh
@@ -44,16 +44,18 @@ struct hb_cache_t
   }
 
   inline bool get (unsigned int key, unsigned int *value)
   {
     unsigned int k = key & ((1<<cache_bits)-1);
     unsigned int v = values[k];
     if ((v >> value_bits) != (key >> cache_bits))
       return false;
+    *value = v & ((1<<value_bits)-1);
+    return true;
   }
 
   inline bool set (unsigned int key, unsigned int value)
   {
     if (unlikely ((key >> key_bits) || (value >> value_bits)))
       return false; /* Overflows */
     unsigned int k = key & ((1<<cache_bits)-1);
     unsigned int v = ((key>>cache_bits)<<value_bits) | value;
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -93,44 +93,42 @@ hb_direction_to_string (hb_direction_t d
     return direction_strings[direction - HB_DIRECTION_LTR];
 
   return "invalid";
 }
 
 
 /* hb_language_t */
 
-struct _hb_language_t {
+struct hb_language_impl_t {
   const char s[1];
 };
 
 static const char canon_map[256] = {
    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
   '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
   '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
    0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
 };
 
 static hb_bool_t
-lang_equal (const void *v1,
-	    const void *v2)
+lang_equal (hb_language_t  v1,
+	    const void    *v2)
 {
   const unsigned char *p1 = (const unsigned char *) v1;
   const unsigned char *p2 = (const unsigned char *) v2;
 
-  while (canon_map[*p1] && canon_map[*p1] == canon_map[*p2])
-    {
-      p1++, p2++;
-    }
+  while (*p1 && *p1 == canon_map[*p2])
+    p1++, p2++;
 
-  return (canon_map[*p1] == canon_map[*p2]);
+  return *p1 == canon_map[*p2];
 }
 
 #if 0
 static unsigned int
 lang_hash (const void *key)
 {
   const unsigned char *p = key;
   unsigned int h = 0;
@@ -142,16 +140,17 @@ lang_hash (const void *key)
 
   return h;
 }
 #endif
 
 
 struct hb_language_item_t {
 
+  struct hb_language_item_t *next;
   hb_language_t lang;
 
   inline bool operator == (const char *s) const {
     return lang_equal (lang, s);
   }
 
   inline hb_language_item_t & operator = (const char *s) {
     lang = (hb_language_t) strdup (s);
@@ -159,35 +158,77 @@ struct hb_language_item_t {
       *p = canon_map[*p];
 
     return *this;
   }
 
   void finish (void) { free (lang); }
 };
 
-static struct hb_static_lang_set_t : hb_lockable_set_t<hb_language_item_t, hb_static_mutex_t> {
-  ~hb_static_lang_set_t (void) { this->finish (lock); }
-  hb_static_mutex_t lock;
-} langs;
+
+/* Thread-safe lock-free language list */
+
+static hb_language_item_t *langs;
+
+static
+void free_langs (void)
+{
+  while (langs) {
+    hb_language_item_t *next = langs->next;
+    langs->finish ();
+    free (langs);
+    langs = next;
+  }
+}
+
+static hb_language_item_t *
+lang_find_or_insert (const char *key)
+{
+retry:
+  hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
+
+  for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
+    if (*lang == key)
+      return lang;
+
+  /* Not found; allocate one. */
+  hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
+  if (unlikely (!lang))
+    return NULL;
+  lang->next = first_lang;
+  *lang = key;
+
+  if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
+    free (lang);
+    goto retry;
+  }
+
+#ifdef HAVE_ATEXIT
+  if (!first_lang)
+    atexit (free_langs); /* First person registers atexit() callback. */
+#endif
+
+  return lang;
+}
+
 
 hb_language_t
 hb_language_from_string (const char *str, int len)
 {
   if (!str || !len || !*str)
     return HB_LANGUAGE_INVALID;
 
   char strbuf[32];
   if (len >= 0) {
     len = MIN (len, (int) sizeof (strbuf) - 1);
     str = (char *) memcpy (strbuf, str, len);
     strbuf[len] = '\0';
   }
 
-  hb_language_item_t *item = langs.find_or_insert (str, langs.lock);
+  hb_language_item_t *item = lang_find_or_insert (str);
 
   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
 }
 
 const char *
 hb_language_to_string (hb_language_t language)
 {
   /* This is actually NULL-safe! */
@@ -309,57 +350,51 @@ hb_script_get_horizontal_direction (hb_s
   }
 
   return HB_DIRECTION_LTR;
 }
 
 
 /* hb_user_data_array_t */
 
-
-/* NOTE: Currently we use a global lock for user_data access
- * threadsafety.  If one day we add a mutex to any object, we
- * should switch to using that insted for these too.
- */
-
-static hb_static_mutex_t user_data_lock;
-
 bool
 hb_user_data_array_t::set (hb_user_data_key_t *key,
 			   void *              data,
 			   hb_destroy_func_t   destroy,
-			   hb_bool_t           replace)
+			   hb_bool_t           replace,
+			   hb_mutex_t         &lock)
 {
   if (!key)
     return false;
 
   if (replace) {
     if (!data && !destroy) {
-      items.remove (key, user_data_lock);
+      items.remove (key, lock);
       return true;
     }
   }
   hb_user_data_item_t item = {key, data, destroy};
-  bool ret = !!items.replace_or_insert (item, user_data_lock, replace);
+  bool ret = !!items.replace_or_insert (item, lock, replace);
 
   return ret;
 }
 
 void *
-hb_user_data_array_t::get (hb_user_data_key_t *key)
+hb_user_data_array_t::get (hb_user_data_key_t *key,
+			   hb_mutex_t         &lock)
 {
   hb_user_data_item_t item = {NULL };
 
-  return items.find (key, &item, user_data_lock) ? item.data : NULL;
+  return items.find (key, &item, lock) ? item.data : NULL;
 }
 
 void
-hb_user_data_array_t::finish (void)
+hb_user_data_array_t::finish (hb_mutex_t &lock)
 {
-  items.finish (user_data_lock);
+  items.finish (lock);
 }
 
 
 /* hb_version */
 
 void
 hb_version (unsigned int *major,
 	    unsigned int *minor,
--- a/gfx/harfbuzz/src/hb-common.h
+++ b/gfx/harfbuzz/src/hb-common.h
@@ -28,23 +28,25 @@
 
 #ifndef HB_H_IN
 #error "Include <hb.h> instead."
 #endif
 
 #ifndef HB_COMMON_H
 #define HB_COMMON_H
 
+#ifndef HB_BEGIN_DECLS
 # ifdef __cplusplus
 #  define HB_BEGIN_DECLS	extern "C" {
 #  define HB_END_DECLS		}
 # else /* !__cplusplus */
 #  define HB_BEGIN_DECLS
 #  define HB_END_DECLS
 # endif /* !__cplusplus */
+#endif
 
 HB_BEGIN_DECLS
 
 #if !defined (HB_DONT_DEFINE_STDINT)
 
 #if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \
     defined (_sgi) || defined (__sun) || defined (sun) || \
     defined (__digital__) || defined (__HP_cc)
@@ -119,72 +121,36 @@ hb_direction_to_string (hb_direction_t d
 #define HB_DIRECTION_IS_FORWARD(dir)	((((unsigned int) (dir)) & ~2U) == 4)
 #define HB_DIRECTION_IS_BACKWARD(dir)	((((unsigned int) (dir)) & ~2U) == 5)
 #define HB_DIRECTION_IS_VALID(dir)	((((unsigned int) (dir)) & ~3U) == 4)
 #define HB_DIRECTION_REVERSE(dir)	((hb_direction_t) (((unsigned int) (dir)) ^ 1)) /* Direction must be valid */
 
 
 /* hb_language_t */
 
-typedef struct _hb_language_t *hb_language_t;
+typedef struct hb_language_impl_t *hb_language_t;
 
 /* len=-1 means str is NUL-terminated */
 hb_language_t
 hb_language_from_string (const char *str, int len);
 
 const char *
 hb_language_to_string (hb_language_t language);
 
 #define HB_LANGUAGE_INVALID ((hb_language_t) NULL)
 
 hb_language_t
 hb_language_get_default (void);
 
 
-/* hb_unicode_general_category_t */
-
-typedef enum
-{
-  HB_UNICODE_GENERAL_CATEGORY_CONTROL,			/* Cc */
-  HB_UNICODE_GENERAL_CATEGORY_FORMAT,			/* Cf */
-  HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED,		/* Cn */
-  HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE,		/* Co */
-  HB_UNICODE_GENERAL_CATEGORY_SURROGATE,		/* Cs */
-  HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER,		/* Ll */
-  HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER,		/* Lm */
-  HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER,		/* Lo */
-  HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER,		/* Lt */
-  HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER,		/* Lu */
-  HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK,		/* Mc */
-  HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK,		/* Me */
-  HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK,		/* Mn */
-  HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER,		/* Nd */
-  HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER,		/* Nl */
-  HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER,		/* No */
-  HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION,	/* Pc */
-  HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION,		/* Pd */
-  HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION,	/* Pe */
-  HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION,	/* Pf */
-  HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION,	/* Pi */
-  HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION,	/* Po */
-  HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION,		/* Ps */
-  HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL,		/* Sc */
-  HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL,		/* Sk */
-  HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL,		/* Sm */
-  HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL,		/* So */
-  HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR,		/* Zl */
-  HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR,	/* Zp */
-  HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR		/* Zs */
-} hb_unicode_general_category_t;
-
-
 /* hb_script_t */
 
 /* http://unicode.org/iso15924/ */
 /* http://goo.gl/x9ilM */
+/* Unicode Character Database property: Script (sc) */
 typedef enum
 {
   /* Unicode-1.1 additions */
   HB_SCRIPT_COMMON			= HB_TAG ('Z','y','y','y'),
   HB_SCRIPT_ARABIC			= HB_TAG ('A','r','a','b'),
   HB_SCRIPT_ARMENIAN			= HB_TAG ('A','r','m','n'),
   HB_SCRIPT_BENGALI			= HB_TAG ('B','e','n','g'),
   HB_SCRIPT_BOPOMOFO			= HB_TAG ('B','o','p','o'),
@@ -328,17 +294,17 @@ hb_tag_t
 hb_script_to_iso15924_tag (hb_script_t script);
 
 hb_direction_t
 hb_script_get_horizontal_direction (hb_script_t script);
 
 
 /* User data */
 
-typedef struct _hb_user_data_key_t {
+typedef struct hb_user_data_key_t {
   /*< private >*/
   char unused;
 } hb_user_data_key_t;
 
 typedef void (*hb_destroy_func_t) (void *user_data);
 
 
 HB_END_DECLS
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -0,0 +1,357 @@
+/*
+ * Copyright © 2012  Mozilla Foundation.
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#define HB_SHAPER coretext
+#include "hb-shaper-impl-private.hh"
+
+#define GlyphID GlyphID_mac
+#include <ApplicationServices/ApplicationServices.h>
+#undef GlyphID
+
+#include "hb-coretext.h"
+
+
+#ifndef HB_DEBUG_CORETEXT
+#define HB_DEBUG_CORETEXT (HB_DEBUG+0)
+#endif
+
+
+HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face)
+HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font)
+
+
+/*
+ * shaper face data
+ */
+
+struct hb_coretext_shaper_face_data_t {
+  CGFontRef cg_font;
+};
+
+static void
+release_data (void *info, const void *data, size_t size)
+{
+  assert (hb_blob_get_length ((hb_blob_t *) info) == size &&
+          hb_blob_get_data ((hb_blob_t *) info, NULL) == data);
+
+  hb_blob_destroy ((hb_blob_t *) info);
+}
+
+hb_coretext_shaper_face_data_t *
+_hb_coretext_shaper_face_data_create (hb_face_t *face)
+{
+  hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t));
+  if (unlikely (!data))
+    return NULL;
+
+  hb_blob_t *blob = hb_face_reference_blob (face);
+  unsigned int blob_length;
+  const char *blob_data = hb_blob_get_data (blob, &blob_length);
+  if (unlikely (!blob_length))
+    DEBUG_MSG (CORETEXT, face, "Face has empty blob");
+
+  CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data);
+  data->cg_font = CGFontCreateWithDataProvider (provider);
+  CGDataProviderRelease (provider);
+
+  if (unlikely (!data->cg_font)) {
+    DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed");
+    free (data);
+    return NULL;
+  }
+
+  return data;
+}
+
+void
+_hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data)
+{
+  CFRelease (data->cg_font);
+  free (data);
+}
+
+
+/*
+ * shaper font data
+ */
+
+struct hb_coretext_shaper_font_data_t {
+  CTFontRef ct_font;
+};
+
+hb_coretext_shaper_font_data_t *
+_hb_coretext_shaper_font_data_create (hb_font_t *font)
+{
+  if (unlikely (!hb_coretext_shaper_face_data_ensure (font->face))) return NULL;
+
+  hb_coretext_shaper_font_data_t *data = (hb_coretext_shaper_font_data_t *) calloc (1, sizeof (hb_coretext_shaper_font_data_t));
+  if (unlikely (!data))
+    return NULL;
+
+  hb_face_t *face = font->face;
+  hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
+
+  data->ct_font = CTFontCreateWithGraphicsFont (face_data->cg_font, font->y_scale, NULL, NULL);
+  if (unlikely (!data->ct_font)) {
+    DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed");
+    free (data);
+    return NULL;
+  }
+
+  return data;
+}
+
+void
+_hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data)
+{
+  CFRelease (data->ct_font);
+  free (data);
+}
+
+
+/*
+ * shaper shape_plan data
+ */
+
+struct hb_coretext_shaper_shape_plan_data_t {};
+
+hb_coretext_shaper_shape_plan_data_t *
+_hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
+					     const hb_feature_t *user_features,
+					     unsigned int        num_user_features)
+{
+  return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+}
+
+void
+_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data)
+{
+}
+
+
+/*
+ * shaper
+ */
+
+CTFontRef
+hb_coretext_font_get_ct_font (hb_font_t *font)
+{
+  if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return 0;
+  hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
+  return font_data->ct_font;
+}
+
+hb_bool_t
+_hb_coretext_shape (hb_shape_plan_t    *shape_plan,
+		    hb_font_t          *font,
+                    hb_buffer_t        *buffer,
+                    const hb_feature_t *features,
+                    unsigned int        num_features)
+{
+  hb_face_t *face = font->face;
+  hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
+  hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
+
+#define FAIL(...) \
+  HB_STMT_START { \
+    DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
+    return false; \
+  } HB_STMT_END;
+
+  unsigned int scratch_size;
+  char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
+
+#define utf16_index() var1.u32
+
+  UniChar *pchars = (UniChar *) scratch;
+  unsigned int chars_len = 0;
+  for (unsigned int i = 0; i < buffer->len; i++) {
+    hb_codepoint_t c = buffer->info[i].codepoint;
+    buffer->info[i].utf16_index() = chars_len;
+    if (likely (c < 0x10000))
+      pchars[chars_len++] = c;
+    else if (unlikely (c >= 0x110000))
+      pchars[chars_len++] = 0xFFFD;
+    else {
+      pchars[chars_len++] = 0xD800 + ((c - 0x10000) >> 10);
+      pchars[chars_len++] = 0xDC00 + ((c - 0x10000) & ((1 << 10) - 1));
+    }
+  }
+
+#undef utf16_index
+
+  CFStringRef string_ref = CFStringCreateWithCharactersNoCopy (kCFAllocatorDefault,
+                                                               pchars, chars_len,
+                                                               kCFAllocatorNull);
+
+  CFDictionaryRef attrs = CFDictionaryCreate (kCFAllocatorDefault,
+                                              (const void**) &kCTFontAttributeName,
+                                              (const void**) &font_data->ct_font,
+                                              1, // count of attributes
+                                              &kCFTypeDictionaryKeyCallBacks,
+                                              &kCFTypeDictionaryValueCallBacks);
+
+  // TODO: support features
+
+  // Now we can create an attributed string
+  CFAttributedStringRef attr_string = CFAttributedStringCreate (kCFAllocatorDefault, string_ref, attrs);
+  CFRelease (string_ref);
+  CFRelease (attrs);
+
+  // Create the CoreText line from our string, then we're done with it
+  CTLineRef line = CTLineCreateWithAttributedString (attr_string);
+  CFRelease (attr_string);
+
+  // and finally retrieve the glyph data and store into the gfxTextRun
+  CFArrayRef glyph_runs = CTLineGetGlyphRuns (line);
+  unsigned int num_runs = CFArrayGetCount (glyph_runs);
+
+  // Iterate through the glyph runs.
+  bool success = true;
+  buffer->len = 0;
+
+  const CFRange range_all = CFRangeMake (0, 0);
+
+  for (unsigned int i = 0; i < num_runs; i++) {
+    CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (glyph_runs, i);
+
+    unsigned int num_glyphs = CTRunGetGlyphCount (run);
+    if (num_glyphs == 0)
+      continue;
+
+    buffer->ensure (buffer->len + num_glyphs);
+
+    // retrieve the laid-out glyph data from the CTRun
+
+    // Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds,
+    // and so copying data to our own buffer with CTRunGetGlyphs will be
+    // extremely rare.
+
+    unsigned int scratch_size;
+    char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
+
+#define ALLOCATE_ARRAY(Type, name, len) \
+  Type *name = (Type *) scratch; \
+  scratch += (len) * sizeof ((name)[0]); \
+  scratch_size -= (len) * sizeof ((name)[0]);
+
+    const CGGlyph* glyphs = CTRunGetGlyphsPtr (run);
+    if (!glyphs) {
+      ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs);
+      CTRunGetGlyphs (run, range_all, glyph_buf);
+      glyphs = glyph_buf;
+    }
+
+    const CGPoint* positions = CTRunGetPositionsPtr (run);
+    if (!positions) {
+      ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs);
+      CTRunGetPositions (run, range_all, position_buf);
+      positions = position_buf;
+    }
+
+    const CFIndex* string_indices = CTRunGetStringIndicesPtr (run);
+    if (!string_indices) {
+      ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs);
+      CTRunGetStringIndices (run, range_all, index_buf);
+      string_indices = index_buf;
+    }
+
+#undef ALLOCATE_ARRAY
+
+    double run_width = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL);
+
+    for (unsigned int j = 0; j < num_glyphs; j++) {
+      double advance = (j + 1 < num_glyphs ? positions[j + 1].x : positions[0].x + run_width) - positions[j].x;
+
+      hb_glyph_info_t *info = &buffer->info[buffer->len];
+      hb_glyph_position_t *pos = &buffer->pos[buffer->len];
+
+      info->codepoint = glyphs[j];
+      info->cluster = string_indices[j];
+
+      // currently, we do all x-positioning by setting the advance, we never use x-offset
+      info->mask = advance;
+      info->var1.u32 = 0;
+      info->var2.u32 = positions[j].y;
+
+      buffer->len++;
+    }
+  }
+
+  buffer->clear_positions ();
+
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; ++i) {
+    hb_glyph_info_t *info = &buffer->info[i];
+    hb_glyph_position_t *pos = &buffer->pos[i];
+
+    /* TODO vertical */
+    pos->x_advance = info->mask;
+    pos->x_offset = info->var1.u32;
+    pos->y_offset = info->var2.u32;
+  }
+
+  // Fix up clusters so that we never return out-of-order indices;
+  // if core text has reordered glyphs, we'll merge them to the
+  // beginning of the reordered cluster.
+  // This does *not* mean we'll form the same clusters as Uniscribe
+  // or the native OT backend, only that the cluster indices will be
+  // non-decreasing in the output buffer.
+  if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
+    unsigned int prev_cluster = 0;
+    for (unsigned int i = 0; i < count; i++) {
+      unsigned int curr_cluster = buffer->info[i].cluster;
+      if (curr_cluster < prev_cluster) {
+        for (unsigned int j = i; j > 0; j--) {
+          if (buffer->info[j - 1].cluster > curr_cluster)
+            buffer->info[j - 1].cluster = curr_cluster;
+          else
+            break;
+        }
+      }
+      prev_cluster = curr_cluster;
+    }
+  } else {
+    // For RTL runs, we make them non-increasing instead.
+    unsigned int prev_cluster = (unsigned int)-1;
+    for (unsigned int i = 0; i < count; i++) {
+      unsigned int curr_cluster = buffer->info[i].cluster;
+      if (curr_cluster > prev_cluster) {
+        for (unsigned int j = i; j > 0; j--) {
+          if (buffer->info[j - 1].cluster < curr_cluster)
+            buffer->info[j - 1].cluster = curr_cluster;
+          else
+            break;
+        }
+      }
+      prev_cluster = curr_cluster;
+    }
+  }
+
+  return true;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-coretext.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2012  Mozilla Foundation.
+ *
+ *  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.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ */
+
+#ifndef HB_CORETEXT_H
+#define HB_CORETEXT_H
+
+#include "hb.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+HB_BEGIN_DECLS
+
+
+CTFontRef
+hb_coretext_font_get_ct_font (hb_font_t *font);
+
+
+HB_END_DECLS
+
+#endif /* HB_CORETEXT_H */
--- a/gfx/harfbuzz/src/hb-fallback-shape.cc
+++ b/gfx/harfbuzz/src/hb-fallback-shape.cc
@@ -19,43 +19,111 @@
  * 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-fallback-shape-private.hh"
+#define HB_SHAPER fallback
+#include "hb-shaper-impl-private.hh"
+
+
+/*
+ * shaper face data
+ */
+
+struct hb_fallback_shaper_face_data_t {};
+
+hb_fallback_shaper_face_data_t *
+_hb_fallback_shaper_face_data_create (hb_face_t *face)
+{
+  return (hb_fallback_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+}
+
+void
+_hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data)
+{
+}
+
+
+/*
+ * shaper font data
+ */
+
+struct hb_fallback_shaper_font_data_t {};
 
-#include "hb-buffer-private.hh"
+hb_fallback_shaper_font_data_t *
+_hb_fallback_shaper_font_data_create (hb_font_t *font)
+{
+  return (hb_fallback_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+}
+
+void
+_hb_fallback_shaper_font_data_destroy (hb_fallback_shaper_font_data_t *data)
+{
+}
+
+
+/*
+ * shaper shape_plan data
+ */
+
+struct hb_fallback_shaper_shape_plan_data_t {};
+
+hb_fallback_shaper_shape_plan_data_t *
+_hb_fallback_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
+					    const hb_feature_t *user_features,
+					    unsigned int        num_user_features)
+{
+  return (hb_fallback_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+}
+
+void
+_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shaper_shape_plan_data_t *data)
+{
+}
+
+
+/*
+ * shaper
+ */
 
 hb_bool_t
-_hb_fallback_shape (hb_font_t          *font,
+_hb_fallback_shape (hb_shape_plan_t    *shape_plan,
+		    hb_font_t          *font,
 		    hb_buffer_t        *buffer,
 		    const hb_feature_t *features HB_UNUSED,
 		    unsigned int        num_features HB_UNUSED)
 {
+  hb_codepoint_t space;
+  font->get_glyph (' ', 0, &space);
+
   buffer->guess_properties ();
+  buffer->clear_positions ();
 
   unsigned int count = buffer->len;
 
   for (unsigned int i = 0; i < count; i++)
-    hb_font_get_glyph (font, buffer->info[i].codepoint, 0, &buffer->info[i].codepoint);
-
-  buffer->clear_positions ();
-
-  for (unsigned int i = 0; i < count; i++) {
-    hb_font_get_glyph_advance_for_direction (font, buffer->info[i].codepoint,
-					     buffer->props.direction,
-					     &buffer->pos[i].x_advance,
-					     &buffer->pos[i].y_advance);
-    hb_font_subtract_glyph_origin_for_direction (font, buffer->info[i].codepoint,
-						 buffer->props.direction,
-						 &buffer->pos[i].x_offset,
-						 &buffer->pos[i].y_offset);
+  {
+    if (buffer->unicode->is_zero_width (buffer->info[i].codepoint)) {
+      buffer->info[i].codepoint = space;
+      buffer->pos[i].x_advance = 0;
+      buffer->pos[i].y_advance = 0;
+      continue;
+    }
+    font->get_glyph (buffer->info[i].codepoint, 0, &buffer->info[i].codepoint);
+    font->get_glyph_advance_for_direction (buffer->info[i].codepoint,
+					   buffer->props.direction,
+					   &buffer->pos[i].x_advance,
+					   &buffer->pos[i].y_advance);
+    font->subtract_glyph_origin_for_direction (buffer->info[i].codepoint,
+					       buffer->props.direction,
+					       &buffer->pos[i].x_offset,
+					       &buffer->pos[i].y_offset);
   }
 
   if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
     hb_buffer_reverse (buffer);
 
-  return TRUE;
+  return true;
 }
--- a/gfx/harfbuzz/src/hb-font-private.hh
+++ b/gfx/harfbuzz/src/hb-font-private.hh
@@ -28,16 +28,18 @@
 
 #ifndef HB_FONT_PRIVATE_HH
 #define HB_FONT_PRIVATE_HH
 
 #include "hb-private.hh"
 
 #include "hb-font.h"
 #include "hb-object-private.hh"
+#include "hb-shaper-private.hh"
+#include "hb-shape-plan-private.hh"
 
 
 
 /*
  * hb_font_funcs_t
  */
 
 #define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \
@@ -45,20 +47,23 @@
   HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \
   HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \
   HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \
   HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \
   HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \
   HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning) \
   HB_FONT_FUNC_IMPLEMENT (glyph_extents) \
   HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_name) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \
   /* ^--- Add new callbacks here */
 
-struct _hb_font_funcs_t {
+struct hb_font_funcs_t {
   hb_object_header_t header;
+  ASSERT_POD ();
 
   hb_bool_t immutable;
 
   /* Don't access these directly.  Call hb_font_get_*() instead. */
 
   struct {
 #define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name;
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
@@ -78,54 +83,69 @@ struct _hb_font_funcs_t {
   } destroy;
 };
 
 
 /*
  * hb_face_t
  */
 
-struct _hb_face_t {
+struct hb_face_t {
   hb_object_header_t header;
+  ASSERT_POD ();
 
   hb_bool_t immutable;
 
   hb_reference_table_func_t  reference_table;
   void                      *user_data;
   hb_destroy_func_t          destroy;
 
-  struct hb_ot_layout_t *ot_layout;
-
   unsigned int index;
   unsigned int upem;
+
+  struct hb_shaper_data_t shaper_data;
+
+  struct plan_node_t {
+    hb_shape_plan_t *shape_plan;
+    plan_node_t *next;
+  } *shape_plans;
 };
 
+#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
+#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, face);
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
+
 
 /*
  * hb_font_t
  */
 
-struct _hb_font_t {
+struct hb_font_t {
   hb_object_header_t header;
+  ASSERT_POD ();
 
   hb_bool_t immutable;
 
   hb_font_t *parent;
   hb_face_t *face;
 
   int x_scale;
   int y_scale;
 
   unsigned int x_ppem;
   unsigned int y_ppem;
 
   hb_font_funcs_t   *klass;
   void              *user_data;
   hb_destroy_func_t  destroy;
 
+  struct hb_shaper_data_t shaper_data;
+
 
   /* Convert from font-space to user-space */
   inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, this->x_scale); }
   inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, this->y_scale); }
 
   /* Convert from parent-font user-space to our user-space */
   inline hb_position_t parent_scale_x_distance (hb_position_t v) {
     if (unlikely (parent && parent->x_scale != x_scale))
@@ -149,15 +169,224 @@ struct _hb_font_t {
     *y = parent_scale_y_distance (*y);
   }
   inline void parent_scale_position (hb_position_t *x, hb_position_t *y) {
     *x = parent_scale_x_position (*x);
     *y = parent_scale_y_position (*y);
   }
 
 
+  /* Public getters */
+
+  inline hb_bool_t get_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector,
+			      hb_codepoint_t *glyph)
+  {
+    *glyph = 0;
+    return klass->get.glyph (this, user_data,
+			     unicode, variation_selector, glyph,
+			     klass->user_data.glyph);
+  }
+
+  inline hb_position_t get_glyph_h_advance (hb_codepoint_t glyph)
+  {
+    return klass->get.glyph_h_advance (this, user_data,
+				       glyph,
+				       klass->user_data.glyph_h_advance);
+  }
+
+  inline hb_position_t get_glyph_v_advance (hb_codepoint_t glyph)
+  {
+    return klass->get.glyph_v_advance (this, user_data,
+				       glyph,
+				       klass->user_data.glyph_v_advance);
+  }
+
+  inline hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph,
+				       hb_position_t *x, hb_position_t *y)
+  {
+    *x = *y = 0;
+    return klass->get.glyph_h_origin (this, user_data,
+				      glyph, x, y,
+				      klass->user_data.glyph_h_origin);
+  }
+
+  inline hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph,
+				       hb_position_t *x, hb_position_t *y)
+  {
+    *x = *y = 0;
+    return klass->get.glyph_v_origin (this, user_data,
+				      glyph, x, y,
+				      klass->user_data.glyph_v_origin);
+  }
+
+  inline hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, hb_codepoint_t right_glyph)
+  {
+    return klass->get.glyph_h_kerning (this, user_data,
+				       left_glyph, right_glyph,
+				       klass->user_data.glyph_h_kerning);
+  }
+
+  inline hb_position_t get_glyph_v_kerning (hb_codepoint_t left_glyph, hb_codepoint_t right_glyph)
+  {
+    return klass->get.glyph_v_kerning (this, user_data,
+				       left_glyph, right_glyph,
+				       klass->user_data.glyph_v_kerning);
+  }
+
+  inline hb_bool_t get_glyph_extents (hb_codepoint_t glyph,
+				      hb_glyph_extents_t *extents)
+  {
+    memset (extents, 0, sizeof (*extents));
+    return klass->get.glyph_extents (this, user_data,
+				     glyph,
+				     extents,
+				     klass->user_data.glyph_extents);
+  }
+
+  inline hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index,
+					    hb_position_t *x, hb_position_t *y)
+  {
+    *x = *y = 0;
+    return klass->get.glyph_contour_point (this, user_data,
+					   glyph, point_index,
+					   x, y,
+					   klass->user_data.glyph_contour_point);
+  }
+
+  inline hb_bool_t get_glyph_name (hb_codepoint_t glyph,
+				   char *name, unsigned int size)
+  {
+    return klass->get.glyph_name (this, user_data,
+				  glyph,
+				  name, size,
+				  klass->user_data.glyph_name);
+  }
+
+  inline hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */
+					hb_codepoint_t *glyph)
+  {
+    return klass->get.glyph_from_name (this, user_data,
+				       name, len,
+				       glyph,
+				       klass->user_data.glyph_from_name);
+  }
+
+
+  /* A bit higher-level, and with fallback */
+
+  inline void get_glyph_advance_for_direction (hb_codepoint_t glyph,
+					       hb_direction_t direction,
+					       hb_position_t *x, hb_position_t *y)
+  {
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) {
+      *x = get_glyph_h_advance (glyph);
+      *y = 0;
+    } else {
+      *x = 0;
+      *y = get_glyph_v_advance (glyph);
+    }
+  }
+
+  /* Internal only */
+  inline void guess_v_origin_minus_h_origin (hb_codepoint_t glyph,
+					     hb_position_t *x, hb_position_t *y)
+  {
+    *x = get_glyph_h_advance (glyph) / 2;
+
+    /* TODO use font_metics.ascent */
+    *y = y_scale;
+  }
+
+  inline void get_glyph_origin_for_direction (hb_codepoint_t glyph,
+					      hb_direction_t direction,
+					      hb_position_t *x, hb_position_t *y)
+  {
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) {
+      hb_bool_t ret = get_glyph_h_origin (glyph, x, y);
+      if (!ret && (ret = get_glyph_v_origin (glyph, x, y))) {
+	hb_position_t dx, dy;
+	guess_v_origin_minus_h_origin (glyph, &dx, &dy);
+	*x -= dx; *y -= dy;
+      }
+    } else {
+      hb_bool_t ret = get_glyph_v_origin (glyph, x, y);
+      if (!ret && (ret = get_glyph_h_origin (glyph, x, y))) {
+	hb_position_t dx, dy;
+	guess_v_origin_minus_h_origin (glyph, &dx, &dy);
+	*x += dx; *y += dy;
+      }
+    }
+  }
+
+  inline void add_glyph_origin_for_direction (hb_codepoint_t glyph,
+					      hb_direction_t direction,
+					      hb_position_t *x, hb_position_t *y)
+  {
+    hb_position_t origin_x, origin_y;
+
+    get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y);
+
+    *x += origin_x;
+    *y += origin_y;
+  }
+
+  inline void subtract_glyph_origin_for_direction (hb_codepoint_t glyph,
+						   hb_direction_t direction,
+						   hb_position_t *x, hb_position_t *y)
+  {
+    hb_position_t origin_x, origin_y;
+
+    get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y);
+
+    *x -= origin_x;
+    *y -= origin_y;
+  }
+
+  inline void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
+					       hb_direction_t direction,
+					       hb_position_t *x, hb_position_t *y)
+  {
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) {
+      *x = get_glyph_h_kerning (first_glyph, second_glyph);
+      *y = 0;
+    } else {
+      *x = 0;
+      *y = get_glyph_v_kerning (first_glyph, second_glyph);
+    }
+  }
+
+  inline hb_bool_t get_glyph_extents_for_origin (hb_codepoint_t glyph,
+						 hb_direction_t direction,
+						 hb_glyph_extents_t *extents)
+  {
+    hb_bool_t ret = get_glyph_extents (glyph, extents);
+
+    if (ret)
+      subtract_glyph_origin_for_direction (glyph, direction, &extents->x_bearing, &extents->y_bearing);
+
+    return ret;
+  }
+
+  inline hb_bool_t get_glyph_contour_point_for_origin (hb_codepoint_t glyph, unsigned int point_index,
+						       hb_direction_t direction,
+						       hb_position_t *x, hb_position_t *y)
+  {
+    hb_bool_t ret = get_glyph_contour_point (glyph, point_index, x, y);
+
+    if (ret)
+      subtract_glyph_origin_for_direction (glyph, direction, x, y);
+
+    return ret;
+  }
+
+
   private:
   inline hb_position_t em_scale (int16_t v, int scale) { return v * (int64_t) scale / hb_face_get_upem (this->face); }
 };
 
+#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
+#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font);
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 
 
 #endif /* HB_FONT_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -1,10 +1,11 @@
 /*
  * Copyright © 2009  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -17,16 +18,17 @@
  *
  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
 #include "hb-ot-layout-private.hh"
 
 #include "hb-font-private.hh"
 #include "hb-blob.h"
@@ -39,124 +41,120 @@
 
 
 
 /*
  * hb_font_funcs_t
  */
 
 static hb_bool_t
-hb_font_get_glyph_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_nil (hb_font_t *font,
 		       void *font_data HB_UNUSED,
 		       hb_codepoint_t unicode,
 		       hb_codepoint_t variation_selector,
 		       hb_codepoint_t *glyph,
 		       void *user_data HB_UNUSED)
 {
   if (font->parent)
     return hb_font_get_glyph (font->parent, unicode, variation_selector, glyph);
 
   *glyph = 0;
-  return FALSE;
+  return false;
 }
 
 static hb_position_t
-hb_font_get_glyph_h_advance_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_h_advance_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
 				 hb_codepoint_t glyph,
 				 void *user_data HB_UNUSED)
 {
   if (font->parent)
     return font->parent_scale_x_distance (hb_font_get_glyph_h_advance (font->parent, glyph));
 
   return font->x_scale;
 }
 
 static hb_position_t
-hb_font_get_glyph_v_advance_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_v_advance_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
 				 hb_codepoint_t glyph,
 				 void *user_data HB_UNUSED)
 {
   if (font->parent)
     return font->parent_scale_y_distance (hb_font_get_glyph_v_advance (font->parent, glyph));
 
   return font->y_scale;
 }
 
 static hb_bool_t
-hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_h_origin_nil (hb_font_t *font,
 				void *font_data HB_UNUSED,
 				hb_codepoint_t glyph,
 				hb_position_t *x,
 				hb_position_t *y,
 				void *user_data HB_UNUSED)
 {
   if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent,
-						glyph,
-						x, y);
+    hb_bool_t ret = hb_font_get_glyph_h_origin (font->parent, glyph, x, y);
     if (ret)
       font->parent_scale_position (x, y);
     return ret;
   }
 
   *x = *y = 0;
-  return FALSE;
+  return false;
 }
 
 static hb_bool_t
-hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_v_origin_nil (hb_font_t *font,
 				void *font_data HB_UNUSED,
 				hb_codepoint_t glyph,
 				hb_position_t *x,
 				hb_position_t *y,
 				void *user_data HB_UNUSED)
 {
   if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent,
-						glyph,
-						x, y);
+    hb_bool_t ret = hb_font_get_glyph_v_origin (font->parent, glyph, x, y);
     if (ret)
       font->parent_scale_position (x, y);
     return ret;
   }
 
   *x = *y = 0;
-  return FALSE;
+  return false;
 }
 
 static hb_position_t
-hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_h_kerning_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
 				 hb_codepoint_t left_glyph,
 				 hb_codepoint_t right_glyph,
 				 void *user_data HB_UNUSED)
 {
   if (font->parent)
     return font->parent_scale_x_distance (hb_font_get_glyph_h_kerning (font->parent, left_glyph, right_glyph));
 
   return 0;
 }
 
 static hb_position_t
-hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_v_kerning_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
 				 hb_codepoint_t top_glyph,
 				 hb_codepoint_t bottom_glyph,
 				 void *user_data HB_UNUSED)
 {
   if (font->parent)
     return font->parent_scale_y_distance (hb_font_get_glyph_v_kerning (font->parent, top_glyph, bottom_glyph));
 
   return 0;
 }
 
 static hb_bool_t
-hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_extents_nil (hb_font_t *font,
 			       void *font_data HB_UNUSED,
 			       hb_codepoint_t glyph,
 			       hb_glyph_extents_t *extents,
 			       void *user_data HB_UNUSED)
 {
   if (font->parent) {
     hb_bool_t ret = hb_font_get_glyph_extents (font->parent,
 					       glyph,
@@ -164,72 +162,98 @@ hb_font_get_glyph_extents_nil (hb_font_t
     if (ret) {
       font->parent_scale_position (&extents->x_bearing, &extents->y_bearing);
       font->parent_scale_distance (&extents->width, &extents->height);
     }
     return ret;
   }
 
   memset (extents, 0, sizeof (*extents));
-  return FALSE;
+  return false;
 }
 
 static hb_bool_t
-hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED,
+hb_font_get_glyph_contour_point_nil (hb_font_t *font,
 				     void *font_data HB_UNUSED,
 				     hb_codepoint_t glyph,
 				     unsigned int point_index,
 				     hb_position_t *x,
 				     hb_position_t *y,
 				     void *user_data HB_UNUSED)
 {
   if (font->parent) {
-    hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent,
-						     glyph, point_index,
-						     x, y);
+    hb_bool_t ret = hb_font_get_glyph_contour_point (font->parent, glyph, point_index, x, y);
     if (ret)
       font->parent_scale_position (x, y);
     return ret;
   }
 
   *x = *y = 0;
-  return FALSE;
+  return false;
+}
+
+static hb_bool_t
+hb_font_get_glyph_name_nil (hb_font_t *font,
+			    void *font_data HB_UNUSED,
+			    hb_codepoint_t glyph,
+			    char *name, unsigned int size,
+			    void *user_data HB_UNUSED)
+{
+  if (font->parent)
+    return hb_font_get_glyph_name (font->parent, glyph, name, size);
+
+  snprintf (name, size, "gid%u", glyph);
+  return false;
+}
+
+static hb_bool_t
+hb_font_get_glyph_from_name_nil (hb_font_t *font,
+				 void *font_data HB_UNUSED,
+				 const char *name, int len, /* -1 means nul-terminated */
+				 hb_codepoint_t *glyph,
+				 void *user_data HB_UNUSED)
+{
+  if (font->parent)
+    return hb_font_get_glyph_from_name (font->parent, name, len, glyph);
+
+  *glyph = 0;
+  return false;
 }
 
 
-static hb_font_funcs_t _hb_font_funcs_nil = {
+static const hb_font_funcs_t _hb_font_funcs_nil = {
   HB_OBJECT_HEADER_STATIC,
 
-  TRUE, /* immutable */
+  true, /* immutable */
 
   {
 #define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil,
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
   }
 };
 
 
 hb_font_funcs_t *
 hb_font_funcs_create (void)
 {
   hb_font_funcs_t *ffuncs;
 
   if (!(ffuncs = hb_object_create<hb_font_funcs_t> ()))
-    return &_hb_font_funcs_nil;
+    return hb_font_funcs_get_empty ();
 
   ffuncs->get = _hb_font_funcs_nil.get;
 
   return ffuncs;
 }
 
 hb_font_funcs_t *
 hb_font_funcs_get_empty (void)
 {
-  return &_hb_font_funcs_nil;
+  return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil);
 }
 
 hb_font_funcs_t *
 hb_font_funcs_reference (hb_font_funcs_t *ffuncs)
 {
   return hb_object_reference (ffuncs);
 }
 
@@ -265,17 +289,17 @@ hb_font_funcs_get_user_data (hb_font_fun
 
 
 void
 hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs)
 {
   if (hb_object_is_inert (ffuncs))
     return;
 
-  ffuncs->immutable = TRUE;
+  ffuncs->immutable = true;
 }
 
 hb_bool_t
 hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs)
 {
   return ffuncs->immutable;
 }
 
@@ -307,280 +331,219 @@ hb_font_funcs_set_##name##_func (hb_font
     ffuncs->destroy.name = NULL;                                         \
   }                                                                      \
 }
 
 HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
 
 
+/* Public getters */
+
 hb_bool_t
 hb_font_get_glyph (hb_font_t *font,
 		   hb_codepoint_t unicode, hb_codepoint_t variation_selector,
 		   hb_codepoint_t *glyph)
 {
-  *glyph = 0;
-  return font->klass->get.glyph (font, font->user_data,
-				 unicode, variation_selector, glyph,
-				 font->klass->user_data.glyph);
+  return font->get_glyph (unicode, variation_selector, glyph);
 }
 
 hb_position_t
 hb_font_get_glyph_h_advance (hb_font_t *font,
 			     hb_codepoint_t glyph)
 {
-  return font->klass->get.glyph_h_advance (font, font->user_data,
-					   glyph,
-					   font->klass->user_data.glyph_h_advance);
+  return font->get_glyph_h_advance (glyph);
 }
 
 hb_position_t
 hb_font_get_glyph_v_advance (hb_font_t *font,
 			     hb_codepoint_t glyph)
 {
-  return font->klass->get.glyph_v_advance (font, font->user_data,
-					   glyph,
-					   font->klass->user_data.glyph_v_advance);
+  return font->get_glyph_v_advance (glyph);
 }
 
 hb_bool_t
 hb_font_get_glyph_h_origin (hb_font_t *font,
 			    hb_codepoint_t glyph,
 			    hb_position_t *x, hb_position_t *y)
 {
-  *x = *y = 0;
-  return font->klass->get.glyph_h_origin (font, font->user_data,
-					   glyph, x, y,
-					   font->klass->user_data.glyph_h_origin);
+  return font->get_glyph_h_origin (glyph, x, y);
 }
 
 hb_bool_t
 hb_font_get_glyph_v_origin (hb_font_t *font,
 			    hb_codepoint_t glyph,
 			    hb_position_t *x, hb_position_t *y)
 {
-  *x = *y = 0;
-  return font->klass->get.glyph_v_origin (font, font->user_data,
-					   glyph, x, y,
-					   font->klass->user_data.glyph_v_origin);
+  return font->get_glyph_v_origin (glyph, x, y);
 }
 
 hb_position_t
 hb_font_get_glyph_h_kerning (hb_font_t *font,
 			     hb_codepoint_t left_glyph, hb_codepoint_t right_glyph)
 {
-  return font->klass->get.glyph_h_kerning (font, font->user_data,
-					   left_glyph, right_glyph,
-					   font->klass->user_data.glyph_h_kerning);
+  return font->get_glyph_h_kerning (left_glyph, right_glyph);
 }
 
 hb_position_t
 hb_font_get_glyph_v_kerning (hb_font_t *font,
 			     hb_codepoint_t left_glyph, hb_codepoint_t right_glyph)
 {
-  return font->klass->get.glyph_v_kerning (font, font->user_data,
-				     left_glyph, right_glyph,
-				     font->klass->user_data.glyph_v_kerning);
+  return font->get_glyph_v_kerning (left_glyph, right_glyph);
 }
 
 hb_bool_t
 hb_font_get_glyph_extents (hb_font_t *font,
 			   hb_codepoint_t glyph,
 			   hb_glyph_extents_t *extents)
 {
-  memset (extents, 0, sizeof (*extents));
-  return font->klass->get.glyph_extents (font, font->user_data,
-					 glyph,
-					 extents,
-					 font->klass->user_data.glyph_extents);
+  return font->get_glyph_extents (glyph, extents);
 }
 
 hb_bool_t
 hb_font_get_glyph_contour_point (hb_font_t *font,
 				 hb_codepoint_t glyph, unsigned int point_index,
 				 hb_position_t *x, hb_position_t *y)
 {
-  *x = *y = 0;
-  return font->klass->get.glyph_contour_point (font, font->user_data,
-					       glyph, point_index,
-					       x, y,
-					       font->klass->user_data.glyph_contour_point);
+  return font->get_glyph_contour_point (glyph, point_index, x, y);
+}
+
+hb_bool_t
+hb_font_get_glyph_name (hb_font_t *font,
+			hb_codepoint_t glyph,
+			char *name, unsigned int size)
+{
+  return font->get_glyph_name (glyph, name, size);
+}
+
+hb_bool_t
+hb_font_get_glyph_from_name (hb_font_t *font,
+			     const char *name, int len, /* -1 means nul-terminated */
+			     hb_codepoint_t *glyph)
+{
+  return font->get_glyph_from_name (name, len, glyph);
 }
 
 
 /* A bit higher-level, and with fallback */
 
 void
 hb_font_get_glyph_advance_for_direction (hb_font_t *font,
 					 hb_codepoint_t glyph,
 					 hb_direction_t direction,
 					 hb_position_t *x, hb_position_t *y)
 {
-  if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) {
-    *x = hb_font_get_glyph_h_advance (font, glyph);
-    *y = 0;
-  } else {
-    *x = 0;
-    *y = hb_font_get_glyph_v_advance (font, glyph);
-  }
+  return font->get_glyph_advance_for_direction (glyph, direction, x, y);
 }
 
-static void
-guess_v_origin_minus_h_origin (hb_font_t *font,
-			       hb_codepoint_t glyph,
-			       hb_position_t *x, hb_position_t *y)
-{
-  *x = hb_font_get_glyph_h_advance (font, glyph) / 2;
-
-  /* TODO use font_metics.ascent */
-  *y = font->y_scale;
-}
-
-
 void
 hb_font_get_glyph_origin_for_direction (hb_font_t *font,
 					hb_codepoint_t glyph,
 					hb_direction_t direction,
 					hb_position_t *x, hb_position_t *y)
 {
-  if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) {
-    hb_bool_t ret = hb_font_get_glyph_h_origin (font, glyph, x, y);
-    if (!ret && (ret = hb_font_get_glyph_v_origin (font, glyph, x, y))) {
-      hb_position_t dx, dy;
-      guess_v_origin_minus_h_origin (font, glyph, &dx, &dy);
-      *x -= dx; *y -= dy;
-    }
-  } else {
-    hb_bool_t ret = hb_font_get_glyph_v_origin (font, glyph, x, y);
-    if (!ret && (ret = hb_font_get_glyph_h_origin (font, glyph, x, y))) {
-      hb_position_t dx, dy;
-      guess_v_origin_minus_h_origin (font, glyph, &dx, &dy);
-      *x += dx; *y += dy;
-    }
-  }
+  return font->get_glyph_origin_for_direction (glyph, direction, x, y);
 }
 
 void
 hb_font_add_glyph_origin_for_direction (hb_font_t *font,
 					hb_codepoint_t glyph,
 					hb_direction_t direction,
 					hb_position_t *x, hb_position_t *y)
 {
-  hb_position_t origin_x, origin_y;
-
-  hb_font_get_glyph_origin_for_direction (font, glyph, direction, &origin_x, &origin_y);
-
-  *x += origin_x;
-  *y += origin_y;
+  return font->add_glyph_origin_for_direction (glyph, direction, x, y);
 }
 
 void
 hb_font_subtract_glyph_origin_for_direction (hb_font_t *font,
 					     hb_codepoint_t glyph,
 					     hb_direction_t direction,
 					     hb_position_t *x, hb_position_t *y)
 {
-  hb_position_t origin_x, origin_y;
-
-  hb_font_get_glyph_origin_for_direction (font, glyph, direction, &origin_x, &origin_y);
-
-  *x -= origin_x;
-  *y -= origin_y;
+  return font->subtract_glyph_origin_for_direction (glyph, direction, x, y);
 }
 
 void
 hb_font_get_glyph_kerning_for_direction (hb_font_t *font,
 					 hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
 					 hb_direction_t direction,
 					 hb_position_t *x, hb_position_t *y)
 {
-  if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) {
-    *x = hb_font_get_glyph_h_kerning (font, first_glyph, second_glyph);
-    *y = 0;
-  } else {
-    *x = 0;
-    *y = hb_font_get_glyph_v_kerning (font, first_glyph, second_glyph);
-  }
+  return font->get_glyph_kerning_for_direction (first_glyph, second_glyph, direction, x, y);
 }
 
 hb_bool_t
 hb_font_get_glyph_extents_for_origin (hb_font_t *font,
 				      hb_codepoint_t glyph,
 				      hb_direction_t direction,
 				      hb_glyph_extents_t *extents)
 {
-  hb_bool_t ret = hb_font_get_glyph_extents (font, glyph, extents);
-
-  if (ret)
-    hb_font_subtract_glyph_origin_for_direction (font, glyph, direction, &extents->x_bearing, &extents->y_bearing);
-
-  return ret;
+  return font->get_glyph_extents_for_origin (glyph, direction, extents);
 }
 
 hb_bool_t
 hb_font_get_glyph_contour_point_for_origin (hb_font_t *font,
 					    hb_codepoint_t glyph, unsigned int point_index,
 					    hb_direction_t direction,
 					    hb_position_t *x, hb_position_t *y)
 {
-  hb_bool_t ret = hb_font_get_glyph_contour_point (font, glyph, point_index, x, y);
-
-  if (ret)
-    hb_font_subtract_glyph_origin_for_direction (font, glyph, direction, x, y);
-
-  return ret;
+  return font->get_glyph_contour_point_for_origin (glyph, point_index, direction, x, y);
 }
 
 
 /*
  * hb_face_t
  */
 
-static hb_face_t _hb_face_nil = {
+static const hb_face_t _hb_face_nil = {
   HB_OBJECT_HEADER_STATIC,
 
-  TRUE, /* immutable */
+  true, /* immutable */
 
   NULL, /* reference_table */
   NULL, /* user_data */
   NULL, /* destroy */
 
-  NULL, /* ot_layout */
+  0,    /* index */
+  1000, /* upem */
 
-  0,    /* index */
-  1000  /* upem */
+  {
+#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+  },
+
+  NULL, /* shape_plans */
 };
 
 
 hb_face_t *
 hb_face_create_for_tables (hb_reference_table_func_t  reference_table,
 			   void                      *user_data,
 			   hb_destroy_func_t          destroy)
 {
   hb_face_t *face;
 
   if (!reference_table || !(face = hb_object_create<hb_face_t> ())) {
     if (destroy)
       destroy (user_data);
-    return &_hb_face_nil;
+    return hb_face_get_empty ();
   }
 
   face->reference_table = reference_table;
   face->user_data = user_data;
   face->destroy = destroy;
 
-  face->ot_layout = _hb_ot_layout_create (face);
-
   face->upem = 0;
 
   return face;
 }
 
 
-typedef struct _hb_face_for_data_closure_t {
+typedef struct hb_face_for_data_closure_t {
   hb_blob_t *blob;
   unsigned int  index;
 } hb_face_for_data_closure_t;
 
 static hb_face_for_data_closure_t *
 _hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index)
 {
   hb_face_for_data_closure_t *closure;
@@ -622,51 +585,64 @@ static hb_blob_t *
 
 hb_face_t *
 hb_face_create (hb_blob_t    *blob,
 		unsigned int  index)
 {
   hb_face_t *face;
 
   if (unlikely (!blob || !hb_blob_get_length (blob)))
-    return &_hb_face_nil;
+    return hb_face_get_empty ();
 
   hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (Sanitizer<OpenTypeFontFile>::sanitize (hb_blob_reference (blob)), index);
 
   if (unlikely (!closure))
-    return &_hb_face_nil;
+    return hb_face_get_empty ();
 
   face = hb_face_create_for_tables (_hb_face_for_data_reference_table,
 				    closure,
 				    (hb_destroy_func_t) _hb_face_for_data_closure_destroy);
 
   hb_face_set_index (face, index);
 
   return face;
 }
 
 hb_face_t *
 hb_face_get_empty (void)
 {
-  return &_hb_face_nil;
+  return const_cast<hb_face_t *> (&_hb_face_nil);
 }
 
 
 hb_face_t *
 hb_face_reference (hb_face_t *face)
 {
   return hb_object_reference (face);
 }
 
 void
 hb_face_destroy (hb_face_t *face)
 {
   if (!hb_object_destroy (face)) return;
 
-  _hb_ot_layout_destroy (face->ot_layout);
+  /* The cached shape_plans think they have a reference on us, and
+   * try to release it.  Make sure that doesn't mess up. */
+  face->header.ref_count.ref_count = HB_REFERENCE_COUNT_INVALID_VALUE;
+  for (hb_face_t::plan_node_t *node = face->shape_plans; node; )
+  {
+    hb_face_t::plan_node_t *next = node->next;
+    hb_shape_plan_destroy (node->shape_plan);
+    free (node);
+    node = next;
+  }
+
+#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, face);
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
 
   if (face->destroy)
     face->destroy (face->user_data);
 
   free (face);
 }
 
 hb_bool_t
@@ -762,100 +738,109 @@ hb_face_get_upem (hb_face_t *face)
   return face->upem;
 }
 
 
 /*
  * hb_font_t
  */
 
-static hb_font_t _hb_font_nil = {
-  HB_OBJECT_HEADER_STATIC,
-
-  TRUE, /* immutable */
-
-  NULL, /* parent */
-  &_hb_face_nil,
-
-  0, /* x_scale */
-  0, /* y_scale */
-
-  0, /* x_ppem */
-  0, /* y_ppem */
-
-  &_hb_font_funcs_nil, /* klass */
-  NULL, /* user_data */
-  NULL  /* destroy */
-};
-
 hb_font_t *
 hb_font_create (hb_face_t *face)
 {
   hb_font_t *font;
 
   if (unlikely (!face))
-    face = &_hb_face_nil;
+    face = hb_face_get_empty ();
   if (unlikely (hb_object_is_inert (face)))
-    return &_hb_font_nil;
+    return hb_font_get_empty ();
   if (!(font = hb_object_create<hb_font_t> ()))
-    return &_hb_font_nil;
+    return hb_font_get_empty ();
 
   hb_face_make_immutable (face);
   font->face = hb_face_reference (face);
-  font->klass = &_hb_font_funcs_nil;
+  font->klass = hb_font_funcs_get_empty ();
 
   return font;
 }
 
 hb_font_t *
 hb_font_create_sub_font (hb_font_t *parent)
 {
   if (unlikely (!parent))
-    return &_hb_font_nil;
+    return hb_font_get_empty ();
 
   hb_font_t *font = hb_font_create (parent->face);
 
   if (unlikely (hb_object_is_inert (font)))
     return font;
 
   hb_font_make_immutable (parent);
   font->parent = hb_font_reference (parent);
 
   font->x_scale = parent->x_scale;
   font->y_scale = parent->y_scale;
   font->x_ppem = parent->x_ppem;
   font->y_ppem = parent->y_ppem;
 
-  font->klass = &_hb_font_funcs_nil;
-
   return font;
 }
 
 hb_font_t *
 hb_font_get_empty (void)
 {
-  return &_hb_font_nil;
+  static const hb_font_t _hb_font_nil = {
+    HB_OBJECT_HEADER_STATIC,
+
+    true, /* immutable */
+
+    NULL, /* parent */
+    const_cast<hb_face_t *> (&_hb_face_nil),
+
+    0, /* x_scale */
+    0, /* y_scale */
+
+    0, /* x_ppem */
+    0, /* y_ppem */
+
+    const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */
+    NULL, /* user_data */
+    NULL, /* destroy */
+
+    {
+#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+    }
+  };
+
+  return const_cast<hb_font_t *> (&_hb_font_nil);
 }
 
 hb_font_t *
 hb_font_reference (hb_font_t *font)
 {
   return hb_object_reference (font);
 }
 
 void
 hb_font_destroy (hb_font_t *font)
 {
   if (!hb_object_destroy (font)) return;
 
+#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, font);
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+
+  if (font->destroy)
+    font->destroy (font->user_data);
+
   hb_font_destroy (font->parent);
   hb_face_destroy (font->face);
   hb_font_funcs_destroy (font->klass);
-  if (font->destroy)
-    font->destroy (font->user_data);
 
   free (font);
 }
 
 hb_bool_t
 hb_font_set_user_data (hb_font_t          *font,
 		       hb_user_data_key_t *key,
 		       void *              data,
@@ -911,17 +896,17 @@ hb_font_set_funcs (hb_font_t         *fo
       destroy (user_data);
     return;
   }
 
   if (font->destroy)
     font->destroy (font->user_data);
 
   if (!klass)
-    klass = &_hb_font_funcs_nil;
+    klass = hb_font_funcs_get_empty ();
 
   hb_font_funcs_reference (klass);
   hb_font_funcs_destroy (font->klass);
   font->klass = klass;
   font->user_data = user_data;
   font->destroy = destroy;
 }
 
@@ -981,10 +966,8 @@ hb_font_set_ppem (hb_font_t *font,
 void
 hb_font_get_ppem (hb_font_t *font,
 		  unsigned int *x_ppem,
 		  unsigned int *y_ppem)
 {
   if (x_ppem) *x_ppem = font->x_ppem;
   if (y_ppem) *y_ppem = font->y_ppem;
 }
-
-
--- a/gfx/harfbuzz/src/hb-font.h
+++ b/gfx/harfbuzz/src/hb-font.h
@@ -32,18 +32,18 @@
 #define HB_FONT_H
 
 #include "hb-common.h"
 #include "hb-blob.h"
 
 HB_BEGIN_DECLS
 
 
-typedef struct _hb_face_t hb_face_t;
-typedef struct _hb_font_t hb_font_t;
+typedef struct hb_face_t hb_face_t;
+typedef struct hb_font_t hb_font_t;
 
 /*
  * hb_face_t
  */
 
 hb_face_t *
 hb_face_create (hb_blob_t    *blob,
 		unsigned int  index);
@@ -105,17 +105,17 @@ hb_face_set_upem (hb_face_t    *face,
 unsigned int
 hb_face_get_upem (hb_face_t *face);
 
 
 /*
  * hb_font_funcs_t
  */
 
-typedef struct _hb_font_funcs_t hb_font_funcs_t;
+typedef struct hb_font_funcs_t hb_font_funcs_t;
 
 hb_font_funcs_t *
 hb_font_funcs_create (void);
 
 hb_font_funcs_t *
 hb_font_funcs_get_empty (void);
 
 hb_font_funcs_t *
@@ -140,17 +140,17 @@ hb_font_funcs_get_user_data (hb_font_fun
 void
 hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs);
 
 hb_bool_t
 hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs);
 
 /* funcs */
 
-typedef struct _hb_glyph_extents_t
+typedef struct hb_glyph_extents_t
 {
   hb_position_t x_bearing;
   hb_position_t y_bearing;
   hb_position_t width;
   hb_position_t height;
 } hb_glyph_extents_t;
 
 
@@ -187,16 +187,26 @@ typedef hb_bool_t (*hb_font_get_glyph_ex
 						       hb_glyph_extents_t *extents,
 						       void *user_data);
 typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, void *font_data,
 							     hb_codepoint_t glyph, unsigned int point_index,
 							     hb_position_t *x, hb_position_t *y,
 							     void *user_data);
 
 
+typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_data,
+						    hb_codepoint_t glyph,
+						    char *name, unsigned int size,
+						    void *user_data);
+typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *font_data,
+							 const char *name, int len, /* -1 means nul-terminated */
+							 hb_codepoint_t *glyph,
+							 void *user_data);
+
+
 /* func setters */
 
 void
 hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
 			      hb_font_get_glyph_func_t glyph_func,
 			      void *user_data, hb_destroy_func_t destroy);
 
 void
@@ -230,16 +240,25 @@ void
 hb_font_funcs_set_glyph_extents_func (hb_font_funcs_t *ffuncs,
 				      hb_font_get_glyph_extents_func_t func,
 				      void *user_data, hb_destroy_func_t destroy);
 void
 hb_font_funcs_set_glyph_contour_point_func (hb_font_funcs_t *ffuncs,
 					    hb_font_get_glyph_contour_point_func_t func,
 					    void *user_data, hb_destroy_func_t destroy);
 
+void
+hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs,
+				   hb_font_get_glyph_name_func_t glyph_func,
+				   void *user_data, hb_destroy_func_t destroy);
+void
+hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_from_name_func_t glyph_func,
+					void *user_data, hb_destroy_func_t destroy);
+
 
 /* func dispatch */
 
 hb_bool_t
 hb_font_get_glyph (hb_font_t *font,
 		   hb_codepoint_t unicode, hb_codepoint_t variation_selector,
 		   hb_codepoint_t *glyph);
 
@@ -271,16 +290,25 @@ hb_font_get_glyph_extents (hb_font_t *fo
 			   hb_codepoint_t glyph,
 			   hb_glyph_extents_t *extents);
 
 hb_bool_t
 hb_font_get_glyph_contour_point (hb_font_t *font,
 				 hb_codepoint_t glyph, unsigned int point_index,
 				 hb_position_t *x, hb_position_t *y);
 
+hb_bool_t
+hb_font_get_glyph_name (hb_font_t *font,
+			hb_codepoint_t glyph,
+			char *name, unsigned int size);
+hb_bool_t
+hb_font_get_glyph_from_name (hb_font_t *font,
+			     const char *name, int len, /* -1 means nul-terminated */
+			     hb_codepoint_t *glyph);
+
 
 /* high-level funcs, with fallback */
 
 void
 hb_font_get_glyph_advance_for_direction (hb_font_t *font,
 					 hb_codepoint_t glyph,
 					 hb_direction_t direction,
 					 hb_position_t *x, hb_position_t *y);
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -76,17 +76,17 @@ hb_ft_get_glyph (hb_font_t *font HB_UNUS
 
 {
   FT_Face ft_face = (FT_Face) font_data;
 
 #ifdef HAVE_FT_FACE_GETCHARVARIANTINDEX
   if (unlikely (variation_selector)) {
     *glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
     if (*glyph)
-      return TRUE;
+      return true;
   }
 #endif
 
   *glyph = FT_Get_Char_Index (ft_face, unicode);
   return *glyph != 0;
 }
 
 static hb_position_t
@@ -127,52 +127,53 @@ static hb_bool_t
 hb_ft_get_glyph_h_origin (hb_font_t *font HB_UNUSED,
 			  void *font_data HB_UNUSED,
 			  hb_codepoint_t glyph HB_UNUSED,
 			  hb_position_t *x HB_UNUSED,
 			  hb_position_t *y HB_UNUSED,
 			  void *user_data HB_UNUSED)
 {
   /* We always work in the horizontal coordinates. */
-  return TRUE;
+  return true;
 }
 
 static hb_bool_t
 hb_ft_get_glyph_v_origin (hb_font_t *font HB_UNUSED,
 			  void *font_data,
 			  hb_codepoint_t glyph,
 			  hb_position_t *x,
 			  hb_position_t *y,
 			  void *user_data HB_UNUSED)
 {
   FT_Face ft_face = (FT_Face) font_data;
   int load_flags = FT_LOAD_DEFAULT;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
-    return FALSE;
+    return false;
 
   /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates
    * have a Y growing upward.  Hence the extra negation. */
   *x = ft_face->glyph->metrics.horiBearingX -   ft_face->glyph->metrics.vertBearingX;
   *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY);
 
-  return TRUE;
+  return true;
 }
 
 static hb_position_t
-hb_ft_get_glyph_h_kerning (hb_font_t *font HB_UNUSED,
+hb_ft_get_glyph_h_kerning (hb_font_t *font,
 			   void *font_data,
 			   hb_codepoint_t left_glyph,
 			   hb_codepoint_t right_glyph,
 			   void *user_data HB_UNUSED)
 {
   FT_Face ft_face = (FT_Face) font_data;
   FT_Vector kerningv;
 
-  if (FT_Get_Kerning (ft_face, left_glyph, right_glyph, FT_KERNING_DEFAULT, &kerningv))
+  FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED;
+  if (FT_Get_Kerning (ft_face, left_glyph, right_glyph, mode, &kerningv))
     return 0;
 
   return kerningv.x;
 }
 
 static hb_position_t
 hb_ft_get_glyph_v_kerning (hb_font_t *font HB_UNUSED,
 			   void *font_data HB_UNUSED,
@@ -190,74 +191,108 @@ hb_ft_get_glyph_extents (hb_font_t *font
 			 hb_codepoint_t glyph,
 			 hb_glyph_extents_t *extents,
 			 void *user_data HB_UNUSED)
 {
   FT_Face ft_face = (FT_Face) font_data;
   int load_flags = FT_LOAD_DEFAULT;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
-    return FALSE;
+    return false;
 
   extents->x_bearing = ft_face->glyph->metrics.horiBearingX;
   extents->y_bearing = ft_face->glyph->metrics.horiBearingY;
   extents->width = ft_face->glyph->metrics.width;
   extents->height = ft_face->glyph->metrics.height;
-  return TRUE;
+  return true;
 }
 
 static hb_bool_t
 hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED,
 			       void *font_data,
 			       hb_codepoint_t glyph,
 			       unsigned int point_index,
 			       hb_position_t *x,
 			       hb_position_t *y,
 			       void *user_data HB_UNUSED)
 {
   FT_Face ft_face = (FT_Face) font_data;
   int load_flags = FT_LOAD_DEFAULT;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
-      return FALSE;
+      return false;
 
   if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE))
-      return FALSE;
+      return false;
 
   if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points))
-      return FALSE;
+      return false;
 
   *x = ft_face->glyph->outline.points[point_index].x;
   *y = ft_face->glyph->outline.points[point_index].y;
 
-  return TRUE;
+  return true;
+}
+
+static hb_bool_t
+hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED,
+		      void *font_data,
+		      hb_codepoint_t glyph,
+		      char *name, unsigned int size,
+		      void *user_data HB_UNUSED)
+{
+  FT_Face ft_face = (FT_Face) font_data;
+
+  hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size);
+  if (!ret)
+    snprintf (name, size, "gid%u", glyph);
+
+  return ret;
 }
 
-static hb_font_funcs_t ft_ffuncs = {
-  HB_OBJECT_HEADER_STATIC,
-
-  TRUE, /* immutable */
+static hb_bool_t
+hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED,
+			   void *font_data,
+			   const char *name, int len, /* -1 means nul-terminated */
+			   hb_codepoint_t *glyph,
+			   void *user_data HB_UNUSED)
+{
+  FT_Face ft_face = (FT_Face) font_data;
 
-  {
-    hb_ft_get_glyph,
-    hb_ft_get_glyph_h_advance,
-    hb_ft_get_glyph_v_advance,
-    hb_ft_get_glyph_h_origin,
-    hb_ft_get_glyph_v_origin,
-    hb_ft_get_glyph_h_kerning,
-    hb_ft_get_glyph_v_kerning,
-    hb_ft_get_glyph_extents,
-    hb_ft_get_glyph_contour_point,
+  if (len < 0)
+    *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name);
+  else {
+    /* Make a nul-terminated version. */
+    char buf[128];
+    len = MIN (len, (int) sizeof (buf) - 1);
+    strncpy (buf, name, len);
+    buf[len] = '\0';
+    *glyph = FT_Get_Name_Index (ft_face, buf);
   }
-};
+
+  return *glyph != 0;
+}
+
 
 static hb_font_funcs_t *
 _hb_ft_get_font_funcs (void)
 {
-  return &ft_ffuncs;
+  static const hb_font_funcs_t ft_ffuncs = {
+    HB_OBJECT_HEADER_STATIC,
+
+    true, /* immutable */
+
+    {
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_ft_get_##name,
+      HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+    }
+  };
+
+  return const_cast<hb_font_funcs_t *> (&ft_ffuncs);
 }
 
 
 static hb_blob_t *
 reference_table  (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
 {
   FT_Face ft_face = (FT_Face) user_data;
   FT_Byte *buffer;
@@ -359,36 +394,49 @@ hb_ft_font_create (FT_Face           ft_
   hb_font_set_ppem (font,
 		    ft_face->size->metrics.x_ppem,
 		    ft_face->size->metrics.y_ppem);
 
   return font;
 }
 
 
-
+/* Thread-safe, lock-free, FT_Library */
 
 static FT_Library ft_library;
-static hb_bool_t ft_library_initialized;
-static struct ft_library_destructor {
-  ~ft_library_destructor (void) {
-    if (ft_library)
-      FT_Done_FreeType (ft_library);
-  }
-} static_ft_library_destructor;
+
+static
+void free_ft_library (void)
+{
+  FT_Done_FreeType (ft_library);
+}
 
 static FT_Library
-_get_ft_library (void)
+get_ft_library (void)
 {
-  if (unlikely (!ft_library_initialized)) {
-    FT_Init_FreeType (&ft_library);
-    ft_library_initialized = TRUE;
+retry:
+  FT_Library library = (FT_Library) hb_atomic_ptr_get (&ft_library);
+
+  if (unlikely (!library))
+  {
+    /* Not found; allocate one. */
+    if (FT_Init_FreeType (&library))
+      return NULL;
+
+    if (!hb_atomic_ptr_cmpexch (&ft_library, NULL, library)) {
+      FT_Done_FreeType (library);
+      goto retry;
+    }
+
+#ifdef HAVE_ATEXIT
+    atexit (free_ft_library); /* First person registers atexit() callback. */
+#endif
   }
 
-  return ft_library;
+  return library;
 }
 
 static void
 _release_blob (FT_Face ft_face)
 {
   hb_blob_destroy ((hb_blob_t *) ft_face->generic.data);
 }
 
@@ -397,34 +445,37 @@ hb_ft_font_set_funcs (hb_font_t *font)
 {
   hb_blob_t *blob = hb_face_reference_blob (font->face);
   unsigned int blob_length;
   const char *blob_data = hb_blob_get_data (blob, &blob_length);
   if (unlikely (!blob_length))
     DEBUG_MSG (FT, font, "Font face has empty blob");
 
   FT_Face ft_face = NULL;
-  FT_Error err = FT_New_Memory_Face (_get_ft_library (),
+  FT_Error err = FT_New_Memory_Face (get_ft_library (),
 				     (const FT_Byte *) blob_data,
 				     blob_length,
 				     hb_face_get_index (font->face),
 				     &ft_face);
 
   if (unlikely (err)) {
     hb_blob_destroy (blob);
     DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed");
     return;
   }
 
   FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE);
 
   FT_Set_Char_Size (ft_face,
 		    font->x_scale, font->y_scale,
+		    0, 0);
+#if 0
 		    font->x_ppem * 72 * 64 / font->x_scale,
 		    font->y_ppem * 72 * 64 / font->y_scale);
+#endif
 
   ft_face->generic.data = blob;
   ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob;
 
   hb_font_set_funcs (font,
 		     _hb_ft_get_font_funcs (),
 		     ft_face,
 		     (hb_destroy_func_t) FT_Done_Face);
--- a/gfx/harfbuzz/src/hb-glib.cc
+++ b/gfx/harfbuzz/src/hb-glib.cc
@@ -187,23 +187,23 @@ hb_glib_script_from_script (hb_script_t 
   if (unlikely (script == HB_SCRIPT_INVALID))
     return G_UNICODE_SCRIPT_INVALID_CODE;
 
   return G_UNICODE_SCRIPT_UNKNOWN;
 #endif
 }
 
 
-static unsigned int
+static hb_unicode_combining_class_t
 hb_glib_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 				 hb_codepoint_t      unicode,
 				 void               *user_data HB_UNUSED)
 
 {
-  return g_unichar_combining_class (unicode);
+  return (hb_unicode_combining_class_t) g_unichar_combining_class (unicode);
 }
 
 static unsigned int
 hb_glib_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 				 hb_codepoint_t      unicode,
 				 void               *user_data HB_UNUSED)
 {
   return g_unichar_iswide (unicode) ? 2 : 1;
@@ -246,35 +246,35 @@ hb_glib_unicode_compose (hb_unicode_func
 #if GLIB_CHECK_VERSION(2,29,12)
   return g_unichar_compose (a, b, ab);
 #endif
 
   /* We don't ifdef-out the fallback code such that compiler always
    * sees it and makes sure it's compilable. */
 
   if (!a || !b)
-    return FALSE;
+    return false;
 
   gchar utf8[12];
   gchar *normalized;
-  gint len;
+  int len;
   hb_bool_t ret;
 
   len = g_unichar_to_utf8 (a, utf8);
   len += g_unichar_to_utf8 (b, utf8 + len);
   normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFC);
   len = g_utf8_strlen (normalized, -1);
   if (unlikely (!len))
-    return FALSE;
+    return false;
 
   if (len == 1) {
     *ab = g_utf8_get_char (normalized);
-    ret = TRUE;
+    ret = true;
   } else {
-    ret = FALSE;
+    ret = false;
   }
 
   g_free (normalized);
   return ret;
 }
 
 static hb_bool_t
 hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
@@ -287,24 +287,24 @@ hb_glib_unicode_decompose (hb_unicode_fu
   return g_unichar_decompose (ab, a, b);
 #endif
 
   /* We don't ifdef-out the fallback code such that compiler always
    * sees it and makes sure it's compilable. */
 
   gchar utf8[6];
   gchar *normalized;
-  gint len;
+  int len;
   hb_bool_t ret;
 
   len = g_unichar_to_utf8 (ab, utf8);
   normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFD);
   len = g_utf8_strlen (normalized, -1);
   if (unlikely (!len))
-    return FALSE;
+    return false;
 
   if (len == 1) {
     *a = g_utf8_get_char (normalized);
     *b = 0;
     ret = *a != ab;
   } else if (len == 2) {
     *a = g_utf8_get_char (normalized);
     *b = g_utf8_get_char (g_utf8_next_char (normalized));
@@ -313,46 +313,76 @@ hb_glib_unicode_decompose (hb_unicode_fu
      * the second part :-(. */
     gchar *recomposed = g_utf8_normalize (normalized, -1, G_NORMALIZE_NFC);
     hb_codepoint_t c = g_utf8_get_char (recomposed);
     if (c != ab && c != *a) {
       *a = c;
       *b = 0;
     }
     g_free (recomposed);
-    ret = TRUE;
+    ret = true;
   } else {
     /* If decomposed to more than two characters, take the last one,
      * and recompose the rest to get the first component. */
     gchar *end = g_utf8_offset_to_pointer (normalized, len - 1);
     gchar *recomposed;
     *b = g_utf8_get_char (end);
     recomposed = g_utf8_normalize (normalized, end - normalized, G_NORMALIZE_NFC);
     /* We expect that recomposed has exactly one character now. */
     *a = g_utf8_get_char (recomposed);
     g_free (recomposed);
-    ret = TRUE;
+    ret = true;
   }
 
   g_free (normalized);
   return ret;
 }
 
+static unsigned int
+hb_glib_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
+					 hb_codepoint_t      u,
+					 hb_codepoint_t     *decomposed,
+					 void               *user_data HB_UNUSED)
+{
+#if GLIB_CHECK_VERSION(2,29,12)
+  return g_unichar_fully_decompose (u, TRUE, decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN);
+#endif
 
-extern HB_INTERNAL hb_unicode_funcs_t _hb_unicode_funcs_glib;
-hb_unicode_funcs_t _hb_glib_unicode_funcs = {
+  /* If the user doesn't have GLib >= 2.29.12 we have to perform
+   * a round trip to UTF-8 and the associated memory management dance. */
+  gchar utf8[6];
+  gchar *utf8_decomposed, *c;
+  gsize utf8_len, utf8_decomposed_len, i;
+
+  /* Convert @u to UTF-8 and normalise it in NFKD mode. This performs the compatibility decomposition. */
+  utf8_len = g_unichar_to_utf8 (u, utf8);
+  utf8_decomposed = g_utf8_normalize (utf8, utf8_len, G_NORMALIZE_NFKD);
+  utf8_decomposed_len = g_utf8_strlen (utf8_decomposed, -1);
+
+  assert (utf8_decomposed_len <= HB_UNICODE_MAX_DECOMPOSITION_LEN);
+
+  for (i = 0, c = utf8_decomposed; i < utf8_decomposed_len; i++, c = g_utf8_next_char (c))
+    *decomposed++ = g_utf8_get_char (c);
+
+  g_free (utf8_decomposed);
+
+  return utf8_decomposed_len;
+}
+
+extern HB_INTERNAL const hb_unicode_funcs_t _hb_glib_unicode_funcs;
+const hb_unicode_funcs_t _hb_glib_unicode_funcs = {
   HB_OBJECT_HEADER_STATIC,
 
   NULL, /* parent */
-  TRUE, /* immutable */
+  true, /* immutable */
   {
 #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_glib_unicode_##name,
     HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_UNICODE_FUNC_IMPLEMENT
   }
 };
 
 hb_unicode_funcs_t *
 hb_glib_get_unicode_funcs (void)
 {
-  return &_hb_glib_unicode_funcs;
+  return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
 }
 
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -103,17 +103,17 @@ static const void *hb_gr_get_table (cons
   unsigned int tlen;
   const char *d = hb_blob_get_data (blob, &tlen);
   *len = tlen;
   return d;
 }
 
 static float hb_gr_get_advance (const void *hb_font, unsigned short gid)
 {
-  return hb_font_get_glyph_h_advance ((hb_font_t *) hb_font, gid);
+  return ((hb_font_t *) font)->get_glyph_h_advance (gid);
 }
 
 static void _hb_gr_face_data_destroy (void *data)
 {
   hb_gr_face_data_t *f = (hb_gr_face_data_t *) data;
   hb_gr_tablelist_t *tlist = f->tlist;
   while (tlist)
   {
@@ -154,17 +154,17 @@ static hb_gr_face_data_t *
   }
 
   data->face = face;
   data->grface = gr_make_face (data, &hb_gr_get_table, gr_face_default);
 
 
   if (unlikely (!hb_face_set_user_data (face, &hb_gr_data_key, data,
 					(hb_destroy_func_t) _hb_gr_face_data_destroy,
-					FALSE)))
+					false)))
   {
     _hb_gr_face_data_destroy (data);
     data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key);
     if (data)
       return data;
     else
       return &_hb_gr_face_data_nil;
   }
@@ -186,24 +186,22 @@ static hb_gr_font_data_t *
   hb_blob_t *silf_blob = hb_face_reference_table (font->face, HB_GRAPHITE_TAG_Silf);
   if (!hb_blob_get_length (silf_blob))
   {
     hb_blob_destroy (silf_blob);
     return &_hb_gr_font_data_nil;
   }
 
   data->grface = _hb_gr_face_get_data (font->face)->grface;
-  int scale;
-  hb_font_get_scale (font, &scale, NULL);
-  data->grfont = gr_make_font_with_advance_fn (scale, font, &hb_gr_get_advance, data->grface);
+  data->grfont = gr_make_font_with_advance_fn (font->x_scale, font, &hb_gr_get_advance, data->grface);
 
 
   if (unlikely (!hb_font_set_user_data (font, &hb_gr_data_key, data,
 					(hb_destroy_func_t) _hb_gr_font_data_destroy,
-					FALSE)))
+					false)))
   {
     _hb_gr_font_data_destroy (data);
     data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key);
     if (data)
       return data;
     else
       return &_hb_gr_font_data_nil;
   }
@@ -220,24 +218,24 @@ hb_bool_t
 {
 
   buffer->guess_properties ();
 
   /* XXX We do a hell of a lot of stuff just to figure out this font
    * is not graphite!  Shouldn't do. */
 
   hb_gr_font_data_t *data = _hb_gr_font_get_data (font);
-  if (!data->grface) return FALSE;
+  if (!data->grface) return false;
 
   unsigned int charlen;
   hb_glyph_info_t *bufferi = hb_buffer_get_glyph_infos (buffer, &charlen);
 
   int success = 0;
 
-  if (!charlen) return TRUE;
+  if (!charlen) return true;
 
   const char *lang = hb_language_to_string (hb_buffer_get_language (buffer));
   const char *lang_end = strchr (lang, '-');
   int lang_len = lang_end ? lang_end - lang : -1;
   gr_feature_val *feats = gr_face_featureval_for_lang (data->grface, lang ? hb_tag_from_string (lang, lang_len) : 0);
 
   while (num_features--)
   {
--- a/gfx/harfbuzz/src/hb-icu.cc
+++ b/gfx/harfbuzz/src/hb-icu.cc
@@ -58,23 +58,23 @@ hb_icu_script_from_script (hb_script_t s
   for (unsigned int i = 0; i < USCRIPT_CODE_LIMIT; i++)
     if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script))
       return (UScriptCode) i;
 
   return USCRIPT_UNKNOWN;
 }
 
 
-static unsigned int
+static hb_unicode_combining_class_t
 hb_icu_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 				hb_codepoint_t      unicode,
 				void               *user_data HB_UNUSED)
 
 {
-  return u_getCombiningClass (unicode);
+  return (hb_unicode_combining_class_t) u_getCombiningClass (unicode);
 }
 
 static unsigned int
 hb_icu_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 				hb_codepoint_t      unicode,
 				void               *user_data HB_UNUSED)
 {
   switch (u_getIntPropertyValue(unicode, UCHAR_EAST_ASIAN_WIDTH))
@@ -167,69 +167,69 @@ hb_icu_unicode_script (hb_unicode_funcs_
 static hb_bool_t
 hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 			hb_codepoint_t      a,
 			hb_codepoint_t      b,
 			hb_codepoint_t     *ab,
 			void               *user_data HB_UNUSED)
 {
   if (!a || !b)
-    return FALSE;
+    return false;
 
   UChar utf16[4], normalized[5];
   int len;
   hb_bool_t ret, err;
   UErrorCode icu_err;
 
   len = 0;
-  err = FALSE;
+  err = false;
   U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), a, err);
-  if (err) return FALSE;
+  if (err) return false;
   U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), b, err);
-  if (err) return FALSE;
+  if (err) return false;
 
   icu_err = U_ZERO_ERROR;
   len = unorm_normalize (utf16, len, UNORM_NFC, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
   if (U_FAILURE (icu_err))
-    return FALSE;
+    return false;
   if (u_countChar32 (normalized, len) == 1) {
     U16_GET_UNSAFE (normalized, 0, *ab);
-    ret = TRUE;
+    ret = true;
   } else {
-    ret = FALSE;
+    ret = false;
   }
 
   return ret;
 }
 
 static hb_bool_t
 hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 			  hb_codepoint_t      ab,
 			  hb_codepoint_t     *a,
 			  hb_codepoint_t     *b,
 			  void               *user_data HB_UNUSED)
 {
-  UChar utf16[2], normalized[20];
+  UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1];
   int len;
   hb_bool_t ret, err;
   UErrorCode icu_err;
 
   /* This function is a monster! Maybe it wasn't a good idea adding a
    * pairwise decompose API... */
   /* Watchout for the dragons.  Err, watchout for macros changing len. */
 
   len = 0;
-  err = FALSE;
+  err = false;
   U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), ab, err);
-  if (err) return FALSE;
+  if (err) return false;
 
   icu_err = U_ZERO_ERROR;
   len = unorm_normalize (utf16, len, UNORM_NFD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
   if (U_FAILURE (icu_err))
-    return FALSE;
+    return false;
 
   len = u_countChar32 (normalized, len);
 
   if (len == 1) {
     U16_GET_UNSAFE (normalized, 0, *a);
     *b = 0;
     ret = *a != ab;
   } else if (len == 2) {
@@ -239,53 +239,90 @@ hb_icu_unicode_decompose (hb_unicode_fun
 
     /* Here's the ugly part: if ab decomposes to a single character and
      * that character decomposes again, we have to detect that and undo
      * the second part :-(. */
     UChar recomposed[20];
     icu_err = U_ZERO_ERROR;
     unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
     if (U_FAILURE (icu_err))
-      return FALSE;
+      return false;
     hb_codepoint_t c;
     U16_GET_UNSAFE (recomposed, 0, c);
     if (c != *a && c != ab) {
       *a = c;
       *b = 0;
     }
-    ret = TRUE;
+    ret = true;
   } else {
     /* If decomposed to more than two characters, take the last one,
      * and recompose the rest to get the first component. */
-    U16_PREV_UNSAFE (normalized, len, *b);
-    UChar recomposed[20];
+    U16_PREV_UNSAFE (normalized, len, *b); /* Changes len in-place. */
+    UChar recomposed[18 * 2];
     icu_err = U_ZERO_ERROR;
     len = unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
     if (U_FAILURE (icu_err))
-      return FALSE;
+      return false;
     /* We expect that recomposed has exactly one character now. */
+    if (unlikely (u_countChar32 (recomposed, len) != 1))
+      return false;
     U16_GET_UNSAFE (recomposed, 0, *a);
-    ret = TRUE;
+    ret = true;
   }
 
   return ret;
 }
 
-extern HB_INTERNAL hb_unicode_funcs_t _hb_unicode_funcs_icu;
-hb_unicode_funcs_t _hb_icu_unicode_funcs = {
+static unsigned int
+hb_icu_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED,
+					hb_codepoint_t      u,
+					hb_codepoint_t     *decomposed,
+					void               *user_data HB_UNUSED)
+{
+  UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1];
+  int len;
+  int32_t utf32_len;
+  hb_bool_t err;
+  UErrorCode icu_err;
+
+  /* Copy @u into a UTF-16 array to be passed to ICU. */
+  len = 0;
+  err = FALSE;
+  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), u, err);
+  if (err)
+    return 0;
+
+  /* Normalise the codepoint using NFKD mode. */
+  icu_err = U_ZERO_ERROR;
+  len = unorm_normalize (utf16, len, UNORM_NFKD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
+  if (icu_err)
+    return 0;
+
+  /* Convert the decomposed form from UTF-16 to UTF-32. */
+  icu_err = U_ZERO_ERROR;
+  u_strToUTF32 ((UChar32*) decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN, &utf32_len, normalized, len, &icu_err);
+  if (icu_err)
+    return 0;
+
+  return utf32_len;
+}
+
+
+extern HB_INTERNAL const hb_unicode_funcs_t _hb_icu_unicode_funcs;
+const hb_unicode_funcs_t _hb_icu_unicode_funcs = {
   HB_OBJECT_HEADER_STATIC,
 
   NULL, /* parent */
-  TRUE, /* immutable */
+  true, /* immutable */
   {
 #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_icu_unicode_##name,
     HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_UNICODE_FUNC_IMPLEMENT
   }
 };
 
 hb_unicode_funcs_t *
 hb_icu_get_unicode_funcs (void)
 {
-  return &_hb_icu_unicode_funcs;
+  return const_cast<hb_unicode_funcs_t *> (&_hb_icu_unicode_funcs);
 }
 
 
--- a/gfx/harfbuzz/src/hb-mutex-private.hh
+++ b/gfx/harfbuzz/src/hb-mutex-private.hh
@@ -1,12 +1,12 @@
 /*
  * Copyright © 2007  Chris Wilson
  * Copyright © 2009,2010  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -45,75 +45,105 @@
 #elif !defined(HB_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__)
 
 #include <windows.h>
 typedef CRITICAL_SECTION hb_mutex_impl_t;
 #define HB_MUTEX_IMPL_INIT	{ NULL, 0, 0, NULL, NULL, 0 }
 #define hb_mutex_impl_init(M)	InitializeCriticalSection (M)
 #define hb_mutex_impl_lock(M)	EnterCriticalSection (M)
 #define hb_mutex_impl_unlock(M)	LeaveCriticalSection (M)
-#define hb_mutex_impl_free(M)	DeleteCriticalSection (M)
+#define hb_mutex_impl_finish(M)	DeleteCriticalSection (M)
 
 
-#elif !defined(HB_NO_MT) && defined(__APPLE__)
+#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__))
 
 #include <pthread.h>
 typedef pthread_mutex_t hb_mutex_impl_t;
 #define HB_MUTEX_IMPL_INIT	PTHREAD_MUTEX_INITIALIZER
 #define hb_mutex_impl_init(M)	pthread_mutex_init (M, NULL)
 #define hb_mutex_impl_lock(M)	pthread_mutex_lock (M)
 #define hb_mutex_impl_unlock(M)	pthread_mutex_unlock (M)
-#define hb_mutex_impl_free(M)	pthread_mutex_destroy (M)
+#define hb_mutex_impl_finish(M)	pthread_mutex_destroy (M)
 
 
 #elif !defined(HB_NO_MT) && defined(HAVE_GLIB)
 
 #include <glib.h>
+#if !GLIB_CHECK_VERSION(2,32,0)
 typedef GStaticMutex hb_mutex_impl_t;
 #define HB_MUTEX_IMPL_INIT	G_STATIC_MUTEX_INIT
 #define hb_mutex_impl_init(M)	g_static_mutex_init (M)
 #define hb_mutex_impl_lock(M)	g_static_mutex_lock (M)
 #define hb_mutex_impl_unlock(M)	g_static_mutex_unlock (M)
-#define hb_mutex_impl_free(M)	g_static_mutex_free (M)
+#define hb_mutex_impl_finish(M)	g_static_mutex_free (M)
+#else
+typedef GMutex hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT	{0}
+#define hb_mutex_impl_init(M)	g_mutex_init (M)
+#define hb_mutex_impl_lock(M)	g_mutex_lock (M)
+#define hb_mutex_impl_unlock(M)	g_mutex_unlock (M)
+#define hb_mutex_impl_finish(M)	g_mutex_clear (M)
+#endif
 
 
-#else
+#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
 
-#define HB_MUTEX_IMPL_NIL 1
+#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD)
+# include <sched.h>
+# define HB_SCHED_YIELD() sched_yield ()
+#else
+# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END
+#endif
+
+/* This actually is not a totally awful implementation. */
 typedef volatile int hb_mutex_impl_t;
 #define HB_MUTEX_IMPL_INIT	0
-#define hb_mutex_impl_init(M)	((void) (*(M) = 0))
-#define hb_mutex_impl_lock(M)	((void) (*(M) = 1))
-#define hb_mutex_impl_unlock(M)	((void) (*(M) = 0))
-#define hb_mutex_impl_free(M)	((void) (*(M) = 2))
+#define hb_mutex_impl_init(M)	*(M) = 0
+#define hb_mutex_impl_lock(M)	HB_STMT_START { while (__sync_lock_test_and_set((M), 1)) HB_SCHED_YIELD (); } HB_STMT_END
+#define hb_mutex_impl_unlock(M)	__sync_lock_release (M)
+#define hb_mutex_impl_finish(M)	HB_STMT_START {} HB_STMT_END
+
+
+#elif !defined(HB_NO_MT)
+
+#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD)
+# include <sched.h>
+# define HB_SCHED_YIELD() sched_yield ()
+#else
+# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END
+#endif
+
+#define HB_MUTEX_INT_NIL 1 /* Warn that fallback implementation is in use. */
+typedef volatile int hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT	0
+#define hb_mutex_impl_init(M)	*(M) = 0
+#define hb_mutex_impl_lock(M)	HB_STMT_START { while (*(M)) HB_SCHED_YIELD (); (*(M))++; } HB_STMT_END
+#define hb_mutex_impl_unlock(M)	(*(M))--;
+#define hb_mutex_impl_finish(M)	HB_STMT_START {} HB_STMT_END
+
+
+#else /* HB_NO_MT */
+
+typedef int hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT	0
+#define hb_mutex_impl_init(M)	HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_lock(M)	HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_unlock(M)	HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_finish(M)	HB_STMT_START {} HB_STMT_END
 
 #endif
 
 
+#define HB_MUTEX_INIT		{HB_MUTEX_IMPL_INIT}
 struct hb_mutex_t
 {
+  /* TODO Add tracing. */
+
   hb_mutex_impl_t m;
 
   inline void init   (void) { hb_mutex_impl_init   (&m); }
   inline void lock   (void) { hb_mutex_impl_lock   (&m); }
   inline void unlock (void) { hb_mutex_impl_unlock (&m); }
-  inline void free   (void) { hb_mutex_impl_free   (&m); }
-};
-
-#define HB_MUTEX_INIT		{HB_MUTEX_IMPL_INIT}
-#define hb_mutex_init(M)	(M)->init ()
-#define hb_mutex_lock(M)	(M)->lock ()
-#define hb_mutex_unlock(M)	(M)->unlock ()
-#define hb_mutex_free(M)	(M)->free ()
-
-
-struct hb_static_mutex_t : hb_mutex_t
-{
-  hb_static_mutex_t (void)  { this->init (); }
-  ~hb_static_mutex_t (void) { this->free (); }
-
-  private:
-  NO_COPY (hb_static_mutex_t);
+  inline void finish (void) { hb_mutex_impl_finish (&m); }
 };
 
 
-
 #endif /* HB_MUTEX_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-object-private.hh
+++ b/gfx/harfbuzz/src/hb-object-private.hh
@@ -1,12 +1,12 @@
 /*
  * Copyright © 2007  Chris Wilson
  * Copyright © 2009,2010  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -42,82 +42,90 @@
 
 #ifndef HB_DEBUG_OBJECT
 #define HB_DEBUG_OBJECT (HB_DEBUG+0)
 #endif
 
 
 /* reference_count */
 
-typedef struct {
-  hb_atomic_int_t ref_count;
-
 #define HB_REFERENCE_COUNT_INVALID_VALUE ((hb_atomic_int_t) -1)
 #define HB_REFERENCE_COUNT_INVALID {HB_REFERENCE_COUNT_INVALID_VALUE}
+struct hb_reference_count_t
+{
+  hb_atomic_int_t ref_count;
 
-  inline void init (int v) { ref_count = v; /* non-atomic is fine */ }
-  inline int inc (void) { return hb_atomic_int_add (ref_count,  1); }
-  inline int dec (void) { return hb_atomic_int_add (ref_count, -1); }
+  inline void init (int v) { ref_count = v; }
+  inline int inc (void) { return hb_atomic_int_add (const_cast<hb_atomic_int_t &> (ref_count),  1); }
+  inline int dec (void) { return hb_atomic_int_add (const_cast<hb_atomic_int_t &> (ref_count), -1); }
+  inline void finish (void) { ref_count = HB_REFERENCE_COUNT_INVALID_VALUE; }
 
-  inline int get (void) { return hb_atomic_int_get (ref_count); }
-  inline int get_unsafe (void) const { return ref_count; }
   inline bool is_invalid (void) const { return ref_count == HB_REFERENCE_COUNT_INVALID_VALUE; }
 
-} hb_reference_count_t;
+};
 
 
 /* user_data */
 
-struct hb_user_data_array_t {
+#define HB_USER_DATA_ARRAY_INIT {HB_LOCKABLE_SET_INIT}
+struct hb_user_data_array_t
+{
+  /* TODO Add tracing. */
 
   struct hb_user_data_item_t {
     hb_user_data_key_t *key;
     void *data;
     hb_destroy_func_t destroy;
 
     inline bool operator == (hb_user_data_key_t *other_key) const { return key == other_key; }
     inline bool operator == (hb_user_data_item_t &other) const { return key == other.key; }
 
     void finish (void) { if (destroy) destroy (data); }
   };
 
-  hb_lockable_set_t<hb_user_data_item_t, hb_static_mutex_t> items;
+  hb_lockable_set_t<hb_user_data_item_t, hb_mutex_t> items;
+
+  inline void init (void) { items.init (); }
 
   HB_INTERNAL bool set (hb_user_data_key_t *key,
 			void *              data,
 			hb_destroy_func_t   destroy,
-			hb_bool_t           replace);
+			hb_bool_t           replace,
+			hb_mutex_t         &lock);
 
-  HB_INTERNAL void *get (hb_user_data_key_t *key);
+  HB_INTERNAL void *get (hb_user_data_key_t *key,
+			hb_mutex_t          &lock);
 
-  HB_INTERNAL void finish (void);
+  HB_INTERNAL void finish (hb_mutex_t &lock);
 };
 
 
 /* object_header */
 
-typedef struct _hb_object_header_t hb_object_header_t;
-
-struct _hb_object_header_t {
+struct hb_object_header_t
+{
   hb_reference_count_t ref_count;
+  hb_mutex_t mutex;
   hb_user_data_array_t user_data;
 
-#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID}
+#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID, HB_MUTEX_INIT, HB_USER_DATA_ARRAY_INIT}
 
   static inline void *create (unsigned int size) {
     hb_object_header_t *obj = (hb_object_header_t *) calloc (1, size);
 
     if (likely (obj))
       obj->init ();
 
     return obj;
   }
 
   inline void init (void) {
     ref_count.init (1);
+    mutex.init ();
+    user_data.init ();
   }
 
   inline bool is_inert (void) const {
     return unlikely (ref_count.is_invalid ());
   }
 
   inline void reference (void) {
     if (unlikely (!this || this->is_inert ()))
@@ -126,47 +134,60 @@ struct _hb_object_header_t {
   }
 
   inline bool destroy (void) {
     if (unlikely (!this || this->is_inert ()))
       return false;
     if (ref_count.dec () != 1)
       return false;
 
-    ref_count.init (HB_REFERENCE_COUNT_INVALID_VALUE);
-
-    user_data.finish ();
+    ref_count.finish (); /* Do this before user_data */
+    user_data.finish (mutex);
+    mutex.finish ();
 
     return true;
   }
 
+  inline void lock (void) {
+    mutex.lock ();
+  }
+
+  inline void unlock (void) {
+    mutex.unlock ();
+  }
+
   inline bool set_user_data (hb_user_data_key_t *key,
 			     void *              data,
 			     hb_destroy_func_t   destroy_func,
 			     hb_bool_t           replace) {
     if (unlikely (!this || this->is_inert ()))
       return false;
 
-    return user_data.set (key, data, destroy_func, replace);
+    return user_data.set (key, data, destroy_func, replace, mutex);
   }
 
   inline void *get_user_data (hb_user_data_key_t *key) {
-    return user_data.get (key);
+    if (unlikely (!this || this->is_inert ()))
+      return NULL;
+
+    return user_data.get (key, mutex);
   }
 
   inline void trace (const char *function) const {
     if (unlikely (!this)) return;
-    /* XXX We cannot use DEBUG_MSG_FUNC here since that one currecntly only
+    /* TODO We cannot use DEBUG_MSG_FUNC here since that one currently only
      * prints the class name and throws away the template info. */
     DEBUG_MSG (OBJECT, (void *) this,
 	       "%s refcount=%d",
 	       function,
-	       this ? ref_count.get_unsafe () : 0);
+	       this ? ref_count.ref_count : 0);
   }
 
+  private:
+  ASSERT_POD ();
 };
 
 
 /* object */
 
 template <typename Type>
 static inline void hb_object_trace (const Type *obj, const char *function)
 {
@@ -193,16 +214,28 @@ static inline Type *hb_object_reference 
 }
 template <typename Type>
 static inline bool hb_object_destroy (Type *obj)
 {
   hb_object_trace (obj, HB_FUNC);
   return obj->header.destroy ();
 }
 template <typename Type>
+static inline void hb_object_lock (Type *obj)
+{
+  hb_object_trace (obj, HB_FUNC);
+  return obj->header.lock ();
+}
+template <typename Type>
+static inline void hb_object_unlock (Type *obj)
+{
+  hb_object_trace (obj, HB_FUNC);
+  return obj->header.unlock ();
+}
+template <typename Type>
 static inline bool hb_object_set_user_data (Type               *obj,
 					    hb_user_data_key_t *key,
 					    void *              data,
 					    hb_destroy_func_t   destroy,
 					    hb_bool_t           replace)
 {
   return obj->header.set_user_data (key, data, destroy, replace);
 }
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-old.cc
@@ -0,0 +1,407 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#define HB_SHAPER old
+#define hb_old_shaper_face_data_t HB_FaceRec_
+#define hb_old_shaper_font_data_t HB_Font_
+#include "hb-shaper-impl-private.hh"
+
+#include <harfbuzz.h>
+
+
+#ifndef HB_DEBUG_OLD
+#define HB_DEBUG_OLD (HB_DEBUG+0)
+#endif
+
+
+static HB_Script
+hb_old_script_from_script (hb_script_t script)
+{
+  switch ((hb_tag_t) script)
+  {
+    default:
+    case HB_SCRIPT_COMMON:		return HB_Script_Common;
+    case HB_SCRIPT_GREEK:		return HB_Script_Greek;
+    case HB_SCRIPT_CYRILLIC:		return HB_Script_Cyrillic;
+    case HB_SCRIPT_ARMENIAN:		return HB_Script_Armenian;
+    case HB_SCRIPT_HEBREW:		return HB_Script_Hebrew;
+    case HB_SCRIPT_ARABIC:		return HB_Script_Arabic;
+    case HB_SCRIPT_SYRIAC:		return HB_Script_Syriac;
+    case HB_SCRIPT_THAANA:		return HB_Script_Thaana;
+    case HB_SCRIPT_DEVANAGARI:		return HB_Script_Devanagari;
+    case HB_SCRIPT_BENGALI:		return HB_Script_Bengali;
+    case HB_SCRIPT_GURMUKHI:		return HB_Script_Gurmukhi;
+    case HB_SCRIPT_GUJARATI:		return HB_Script_Gujarati;
+    case HB_SCRIPT_ORIYA:		return HB_Script_Oriya;
+    case HB_SCRIPT_TAMIL:		return HB_Script_Tamil;
+    case HB_SCRIPT_TELUGU:		return HB_Script_Telugu;
+    case HB_SCRIPT_KANNADA:		return HB_Script_Kannada;
+    case HB_SCRIPT_MALAYALAM:		return HB_Script_Malayalam;
+    case HB_SCRIPT_SINHALA:		return HB_Script_Sinhala;
+    case HB_SCRIPT_THAI:		return HB_Script_Thai;
+    case HB_SCRIPT_LAO:			return HB_Script_Lao;
+    case HB_SCRIPT_TIBETAN:		return HB_Script_Tibetan;
+    case HB_SCRIPT_MYANMAR:		return HB_Script_Myanmar;
+    case HB_SCRIPT_GEORGIAN:		return HB_Script_Georgian;
+    case HB_SCRIPT_HANGUL:		return HB_Script_Hangul;
+    case HB_SCRIPT_OGHAM:		return HB_Script_Ogham;
+    case HB_SCRIPT_RUNIC:		return HB_Script_Runic;
+    case HB_SCRIPT_KHMER:		return HB_Script_Khmer;
+    case HB_SCRIPT_NKO:			return HB_Script_Nko;
+    case HB_SCRIPT_INHERITED:		return HB_Script_Inherited;
+  }
+}
+
+
+static HB_Bool
+hb_old_convertStringToGlyphIndices (HB_Font old_font,
+				    const HB_UChar16 *string,
+				    hb_uint32 length,
+				    HB_Glyph *glyphs,
+				    hb_uint32 *numGlyphs,
+				    HB_Bool rightToLeft)
+{
+  hb_font_t *font = (hb_font_t *) old_font->userData;
+
+  for (unsigned int i = 0; i < length; i++)
+  {
+    hb_codepoint_t u;
+
+    /* XXX Handle UTF-16.  Ugh */
+    u = string[i];
+
+    if (rightToLeft)
+      u = hb_unicode_funcs_get_default ()->mirroring (u);
+
+    font->get_glyph (u, 0, &u); /* TODO Variation selectors */
+
+    glyphs[i] = u;
+  }
+  *numGlyphs = length; // XXX
+
+  return true;
+}
+
+static void
+hb_old_getGlyphAdvances (HB_Font old_font,
+			 const HB_Glyph *glyphs,
+			 hb_uint32 numGlyphs,
+			 HB_Fixed *advances,
+			 int flags /*HB_ShaperFlag*/)
+{
+  hb_font_t *font = (hb_font_t *) old_font->userData;
+
+  for (unsigned int i = 0; i < numGlyphs; i++)
+    advances[i] = font->get_glyph_h_advance (glyphs[i]);
+}
+
+static HB_Bool
+hb_old_canRender (HB_Font old_font,
+		  const HB_UChar16 *string,
+		  hb_uint32 length)
+{
+  return true; // TODO
+}
+
+static HB_Error
+hb_old_getPointInOutline (HB_Font old_font,
+			  HB_Glyph glyph,
+			  int flags /*HB_ShaperFlag*/,
+			  hb_uint32 point,
+			  HB_Fixed *xpos,
+			  HB_Fixed *ypos,
+			  hb_uint32 *nPoints)
+{
+  return HB_Err_Ok; // TODO
+}
+
+static void
+hb_old_getGlyphMetrics (HB_Font old_font,
+			HB_Glyph glyph,
+			HB_GlyphMetrics *metrics)
+{
+  hb_font_t *font = (hb_font_t *) old_font->userData;
+
+  hb_glyph_extents_t extents;
+
+  font->get_glyph_extents (glyph, &extents);
+
+  metrics->x       = extents.x_bearing;
+  metrics->y       = extents.y_bearing;
+  metrics->width   = extents.width;
+  metrics->height  = -extents.height;
+  metrics->xOffset = font->get_glyph_h_advance (glyph);
+  metrics->yOffset = 0;
+}
+
+static HB_Fixed
+hb_old_getFontMetric (HB_Font old_font,
+		      HB_FontMetric metric)
+{
+  hb_font_t *font = (hb_font_t *) old_font->userData;
+
+  switch (metric)
+  {
+    case HB_FontAscent:
+       return font->y_scale; /* XXX We don't have ascent data yet. */
+
+    default:
+      return 0;
+  }
+}
+
+static const HB_FontClass hb_old_font_class = {
+  hb_old_convertStringToGlyphIndices,
+  hb_old_getGlyphAdvances,
+  hb_old_canRender,
+  hb_old_getPointInOutline,
+  hb_old_getGlyphMetrics,
+  hb_old_getFontMetric
+};
+
+
+
+static HB_Error
+table_func (void *font, HB_Tag tag, HB_Byte *buffer, HB_UInt *length)
+{
+  hb_face_t *face = (hb_face_t *) font;
+  hb_blob_t *blob = hb_face_reference_table (face, (hb_tag_t) tag);
+  unsigned int capacity = *length;
+  *length = hb_blob_get_length (blob);
+  memcpy (buffer, hb_blob_get_data (blob, NULL), MIN (capacity, *length));
+  hb_blob_destroy (blob);
+ return HB_Err_Ok;
+}
+
+
+/*
+ * shaper face data
+ */
+
+hb_old_shaper_face_data_t *
+_hb_old_shaper_face_data_create (hb_face_t *face)
+{
+  return HB_NewFace (face, table_func);
+}
+
+void
+_hb_old_shaper_face_data_destroy (hb_old_shaper_face_data_t *data)
+{
+  HB_FreeFace (data);
+}
+
+
+/*
+ * shaper font data
+ */
+
+hb_old_shaper_font_data_t *
+_hb_old_shaper_font_data_create (hb_font_t *font)
+{
+  HB_FontRec *data = (HB_FontRec *) calloc (1, sizeof (HB_FontRec));
+  if (unlikely (!data)) {
+    DEBUG_MSG (OLD, font, "malloc()ing HB_Font failed");
+    return NULL;
+  }
+
+  data->klass = &hb_old_font_class;
+  data->x_ppem = font->x_ppem;
+  data->y_ppem = font->y_ppem;
+  data->x_scale = font->x_scale; // XXX
+  data->y_scale = font->y_scale; // XXX
+  data->userData = font;
+
+  return data;
+}
+
+void
+_hb_old_shaper_font_data_destroy (hb_old_shaper_font_data_t *data)
+{
+  free (data);
+}
+
+
+/*
+ * shaper shape_plan data
+ */
+
+struct hb_old_shaper_shape_plan_data_t {};
+
+hb_old_shaper_shape_plan_data_t *
+_hb_old_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
+				       const hb_feature_t *user_features,
+				       unsigned int        num_user_features)
+{
+  return (hb_old_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+}
+
+void
+_hb_old_shaper_shape_plan_data_destroy (hb_old_shaper_shape_plan_data_t *data)
+{
+}
+
+
+/*
+ * shaper
+ */
+
+hb_bool_t
+_hb_old_shape (hb_shape_plan_t    *shape_plan,
+	       hb_font_t          *font,
+	       hb_buffer_t        *buffer,
+	       const hb_feature_t *features,
+	       unsigned int        num_features)
+{
+  hb_face_t *face = font->face;
+  HB_Face old_face = HB_SHAPER_DATA_GET (face);
+  HB_Font old_font = HB_SHAPER_DATA_GET (font);
+
+  bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
+
+retry:
+
+  unsigned int scratch_size;
+  char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
+
+#define utf16_index() var1.u32
+  HB_UChar16 *pchars = (HB_UChar16 *) scratch;
+  unsigned int chars_len = 0;
+  for (unsigned int i = 0; i < buffer->len; i++) {
+    hb_codepoint_t c = buffer->info[i].codepoint;
+    buffer->info[i].utf16_index() = chars_len;
+    if (likely (c < 0x10000))
+      pchars[chars_len++] = c;
+    else if (unlikely (c >= 0x110000))
+      pchars[chars_len++] = 0xFFFD;
+    else {
+      pchars[chars_len++] = 0xD800 + ((c - 0x10000) >> 10);
+      pchars[chars_len++] = 0xDC00 + ((c - 0x10000) & ((1 << 10) - 1));
+    }
+  }
+
+
+#define ALLOCATE_ARRAY(Type, name, len) \
+  name = (Type *) scratch; \
+  scratch += (len) * sizeof ((name)[0]); \
+  scratch_size -= (len) * sizeof ((name)[0]);
+
+
+  HB_ShaperItem item = {0};
+
+  ALLOCATE_ARRAY (const HB_UChar16, item.string, chars_len);
+  ALLOCATE_ARRAY (unsigned short, item.log_clusters, chars_len + 2);
+  item.stringLength = chars_len;
+  item.item.pos = 0;
+  item.item.length = item.stringLength;
+  item.item.script = hb_old_script_from_script (buffer->props.script);
+  item.item.bidiLevel = backward ? 1 : 0;
+
+  item.font = old_font;
+  item.face = old_face;
+  item.shaperFlags = 0;
+
+  item.glyphIndicesPresent = false;
+
+  /* TODO Alignment. */
+  unsigned int num_glyphs = scratch_size / (sizeof (HB_Glyph) +
+					    sizeof (HB_GlyphAttributes) +
+					    sizeof (HB_Fixed) +
+					    sizeof (HB_FixedPoint) +
+					    sizeof (uint32_t));
+
+  item.num_glyphs = num_glyphs;
+  ALLOCATE_ARRAY (HB_Glyph, item.glyphs, num_glyphs);
+  ALLOCATE_ARRAY (HB_GlyphAttributes, item.attributes, num_glyphs);
+  ALLOCATE_ARRAY (HB_Fixed, item.advances, num_glyphs);
+  ALLOCATE_ARRAY (HB_FixedPoint, item.offsets, num_glyphs);
+  uint32_t *vis_clusters;
+  ALLOCATE_ARRAY (uint32_t, vis_clusters, num_glyphs);
+
+#undef ALLOCATE_ARRAY
+
+  if (!HB_ShapeItem (&item))
+  {
+    if (unlikely (item.num_glyphs > num_glyphs))
+    {
+      buffer->ensure (buffer->allocated * 2);
+      if (buffer->in_error)
+        return false;
+      goto retry;
+    }
+    return false;
+  }
+  num_glyphs = item.num_glyphs;
+
+  /* Ok, we've got everything we need, now compose output buffer,
+   * very, *very*, carefully! */
+
+  /* Calculate visual-clusters.  That's what we ship. */
+  for (unsigned int i = 0; i < num_glyphs; i++)
+    vis_clusters[i] = -1;
+  for (unsigned int i = 0; i < buffer->len; i++) {
+    uint32_t *p = &vis_clusters[item.log_clusters[buffer->info[i].utf16_index()]];
+    *p = MIN (*p, buffer->info[i].cluster);
+  }
+  for (unsigned int i = 1; i < num_glyphs; i++)
+    if (vis_clusters[i] == -1)
+      vis_clusters[i] = vis_clusters[i - 1];
+
+#undef utf16_index
+
+  buffer->ensure (num_glyphs);
+  if (buffer->in_error)
+    return false;
+
+
+  buffer->len = num_glyphs;
+  hb_glyph_info_t *info = buffer->info;
+  for (unsigned int i = 0; i < num_glyphs; i++)
+  {
+    info[i].codepoint = item.glyphs[i];
+    info[i].cluster = vis_clusters[i];
+
+    info[i].mask = item.advances[i];
+    info[i].var1.u32 = item.offsets[i].x;
+    info[i].var2.u32 = item.offsets[i].y;
+  }
+
+  buffer->clear_positions ();
+
+  for (unsigned int i = 0; i < num_glyphs; ++i) {
+    hb_glyph_info_t *info = &buffer->info[i];
+    hb_glyph_position_t *pos = &buffer->pos[i];
+
+    /* TODO vertical */
+    pos->x_advance = info->mask;
+    pos->x_offset = info->var1.u32;
+    pos->y_offset = info->var2.u32;
+  }
+
+  if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+    buffer->reverse ();
+
+  return true;
+}
--- a/gfx/harfbuzz/src/hb-open-file-private.hh
+++ b/gfx/harfbuzz/src/hb-open-file-private.hh
@@ -100,17 +100,17 @@ typedef struct OffsetTable
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables));
   }
 
-  private:
+  protected:
   Tag		sfnt_version;	/* '\0\001\0\00' if TrueType / 'OTTO' if CFF */
   USHORT	numTables;	/* Number of tables. */
   USHORT	searchRange;	/* (Maximum power of 2 <= numTables) x 16 */
   USHORT	entrySelector;	/* Log2(maximum power of 2 <= numTables). */
   USHORT	rangeShift;	/* NumTables x 16-searchRange. */
   TableRecord	tables[VAR];	/* TableRecord entries. numTables items */
   public:
   DEFINE_SIZE_ARRAY (12, tables);
@@ -128,17 +128,17 @@ struct TTCHeaderVersion1
   inline unsigned int get_face_count (void) const { return table.len; }
   inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (table.sanitize (c, this));
   }
 
-  private:
+  protected:
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
   FixedVersion	version;	/* Version of the TTC Header (1.0),
 				 * 0x00010000 */
   LongOffsetLongArrayOf<OffsetTable>
 		table;		/* Array of offsets to the OffsetTable for each font
 				 * from the beginning of the file */
   public:
   DEFINE_SIZE_ARRAY (12, table);
@@ -172,17 +172,17 @@ struct TTCHeader
     if (unlikely (!u.header.version.sanitize (c))) return TRACE_RETURN (false);
     switch (u.header.version.major) {
     case 2: /* version 2 is compatible with version 1 */
     case 1: return TRACE_RETURN (u.version1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   struct {
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
   FixedVersion	version;	/* Version of the TTC Header (1.0 or 2.0),
 				 * 0x00010000 or 0x00020000 */
   }			header;
   TTCHeaderVersion1	version1;
   } u;
@@ -237,17 +237,17 @@ struct OpenTypeFontFile
     case TrueTag:
     case Typ1Tag:
     case TrueTypeTag:	return TRACE_RETURN (u.fontFace.sanitize (c));
     case TTCTag:	return TRACE_RETURN (u.ttcHeader.sanitize (c));
     default:		return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   Tag			tag;		/* 4-byte identifier. */
   OpenTypeFontFace	fontFace;
   TTCHeader		ttcHeader;
   } u;
   public:
   DEFINE_SIZE_UNION (4, tag);
 };
--- a/gfx/harfbuzz/src/hb-open-type-private.hh
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -75,58 +75,67 @@ inline Type& StructAfter(TObject &X)
 
 
 
 /*
  * Size checking
  */
 
 /* Check _assertion in a method environment */
-#define _DEFINE_SIZE_ASSERTION(_assertion) \
-  inline void _size_assertion (void) const \
-  { ASSERT_STATIC (_assertion); }
+#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \
+  inline void _instance_assertion_on_line_##_line (void) const \
+  { \
+    ASSERT_STATIC (_assertion); \
+    ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \
+  }
+# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion)
+# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion)
+
 /* Check that _code compiles in a method environment */
-#define _DEFINE_COMPILES_ASSERTION(_code) \
-  inline void _compiles_assertion (void) const \
+#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \
+  inline void _compiles_assertion_on_line_##_line (void) const \
   { _code; }
+# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code)
+# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code)
 
 
 #define DEFINE_SIZE_STATIC(size) \
-  _DEFINE_SIZE_ASSERTION (sizeof (*this) == (size)); \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \
   static const unsigned int static_size = (size); \
   static const unsigned int min_size = (size)
 
 /* Size signifying variable-sized array */
 #define VAR 1
 
 #define DEFINE_SIZE_UNION(size, _member) \
-  _DEFINE_SIZE_ASSERTION (this->u._member.static_size == (size)); \
+  DEFINE_INSTANCE_ASSERTION (this->u._member.static_size == (size)); \
   static const unsigned int min_size = (size)
 
 #define DEFINE_SIZE_MIN(size) \
-  _DEFINE_SIZE_ASSERTION (sizeof (*this) >= (size)); \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \
   static const unsigned int min_size = (size)
 
 #define DEFINE_SIZE_ARRAY(size, array) \
-  _DEFINE_SIZE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \
-  _DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \
+  DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \
   static const unsigned int min_size = (size)
 
 #define DEFINE_SIZE_ARRAY2(size, array1, array2) \
-  _DEFINE_SIZE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \
-  _DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \
+  DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \
   static const unsigned int min_size = (size)
 
 
 
 /*
  * Null objects
  */
 
 /* Global nul-content Null pool.  Enlarge as necessary. */
+/* TODO This really should be a extern HB_INTERNAL and defined somewhere... */
 static const void *_NullPool[64 / sizeof (void *)];
 
 /* Generic nul-content Null objects. */
 template <typename Type>
 static inline const Type& Null (void) {
   ASSERT_STATIC (Type::min_size <= sizeof (_NullPool));
   return *CastP<Type> (_NullPool);
 }
@@ -328,18 +337,16 @@ struct Sanitizer
 
 /*
  * Int types
  */
 
 
 template <typename Type, int Bytes> struct BEInt;
 
-/* LONGTERMTODO: On machines allowing unaligned access, we can make the
- * following tighter by using byteswap instructions on ints directly. */
 template <typename Type>
 struct BEInt<Type, 2>
 {
   public:
   inline void set (Type i) { hb_be_uint16_put (v,i); }
   inline operator Type (void) const { return hb_be_uint16_get (v); }
   inline bool operator == (const BEInt<Type, 2>& o) const { return hb_be_uint16_eq (v, o.v); }
   inline bool operator != (const BEInt<Type, 2>& o) const { return !(*this == o); }
@@ -684,19 +691,28 @@ struct HeadlessArrayOf
 
 
 /* An array with sorted elements.  Supports binary searching. */
 template <typename Type>
 struct SortedArrayOf : ArrayOf<Type> {
 
   template <typename SearchType>
   inline int search (const SearchType &x) const {
-    struct Cmp {
-      static int cmp (const SearchType *a, const Type *b) { return b->cmp (*a); }
-    };
-    const Type *p = (const Type *) bsearch (&x, this->array, this->len, sizeof (this->array[0]), (hb_compare_func_t) Cmp::cmp);
-    return p ? p - this->array : -1;
+    unsigned int count = this->len;
+    /* Linear search is *much* faster for small counts. */
+    if (likely (count < 32)) {
+      for (unsigned int i = 0; i < count; i++)
+	if (this->array[i].cmp (x) == 0)
+	  return i;
+      return -1;
+    } else {
+      struct Cmp {
+	static int cmp (const SearchType *a, const Type *b) { return b->cmp (*a); }
+      };
+      const Type *p = (const Type *) bsearch (&x, this->array, this->len, sizeof (this->array[0]), (hb_compare_func_t) Cmp::cmp);
+      return p ? p - this->array : -1;
+    }
   }
 };
 
 
 
 #endif /* HB_OPEN_TYPE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-head-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-head-table.hh
@@ -49,17 +49,17 @@ struct head
     return 16 <= upem && upem <= 16384 ? upem : 1000;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
   }
 
-  private:
+  protected:
   FixedVersion	version;		/* Version of the head table--currently
 					 * 0x00010000 for version 1.0. */
   FixedVersion	fontRevision;		/* Set by font manufacturer. */
   ULONG		checkSumAdjustment;	/* To compute: set it to 0, sum the
 					 * entire font as ULONG, then store
 					 * 0xB1B0AFBA - sum. */
   ULONG		magicNumber;		/* Set to 0x5F0F3CF5. */
   USHORT	flags;			/* Bit 0: Baseline for font at y=0;
--- a/gfx/harfbuzz/src/hb-ot-hhea-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hhea-table.hh
@@ -42,17 +42,17 @@ struct hhea
 {
   static const hb_tag_t Tag	= HB_OT_TAG_hhea;
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
   }
 
-  private:
+  protected:
   FixedVersion	version;		/* 0x00010000 for version 1.0. */
   FWORD		ascender;		/* Typographic ascent. <a
 					 * href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html">
 					 * (Distance from baseline of highest
 					 * ascender)</a> */
   FWORD		descender;		/* Typographic descent. <a
 					 * href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html">
 					 * (Distance from baseline of lowest
--- a/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
@@ -52,17 +52,17 @@ struct hmtx
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     /* We don't check for anything specific here.  The users of the
      * struct do all the hard work... */
     return TRACE_RETURN (true);
   }
 
-  private:
+  protected:
   LongHorMetric	longHorMetric[VAR];	/* Paired advance width and left side
 					 * bearing values for each glyph. The
 					 * value numOfHMetrics comes from
 					 * the 'hhea' table. If the font is
 					 * monospaced, only one entry need
 					 * be in the array, but that entry is
 					 * required. The last entry applies to
 					 * all subsequent glyphs. */
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -29,17 +29,17 @@
 #ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH
 #define HB_OT_LAYOUT_COMMON_PRIVATE_HH
 
 #include "hb-ot-layout-private.hh"
 #include "hb-open-type-private.hh"
 #include "hb-set-private.hh"
 
 
-#define NOT_COVERED		((unsigned int) 0x110000)
+#define NOT_COVERED		((unsigned int) -1)
 #define MAX_NESTING_LEVEL	8
 
 
 
 /*
  *
  * OpenType Layout Common Table Formats
  *
@@ -129,16 +129,21 @@ struct RangeRecord
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this));
   }
 
   inline bool intersects (const hb_set_t *glyphs) const {
     return glyphs->intersects (start, end);
   }
 
+  template <typename set_t>
+  inline void add_coverage (set_t *glyphs) const {
+    glyphs->add_range (start, end);
+  }
+
   GlyphID	start;		/* First GlyphID in the range */
   GlyphID	end;		/* Last GlyphID in the range */
   USHORT	value;		/* Value */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 DEFINE_NULL_DATA (RangeRecord, "\000\001");
 
@@ -222,17 +227,17 @@ struct Script
   inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; }
   inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
   }
 
-  private:
+  protected:
   OffsetTo<LangSys>
 		defaultLangSys;	/* Offset to DefaultLangSys table--from
 				 * beginning of Script table--may be Null */
   RecordArrayOf<LangSys>
 		langSys;	/* Array of LangSysRecords--listed
 				 * alphabetically by LangSysTag */
   public:
   DEFINE_SIZE_ARRAY (4, langSys);
@@ -304,16 +309,17 @@ struct Lookup
     }
     return flag;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     /* Real sanitize of the subtables is done by GSUB/GPOS/... */
     if (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false);
+    if (!subTable.len) TRACE_RETURN (false);
     if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet))
     {
       USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
       if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false);
     }
     return TRACE_RETURN (true);
   }
 
@@ -338,43 +344,49 @@ typedef OffsetListOf<Lookup> LookupList;
 struct CoverageFormat1
 {
   friend struct Coverage;
 
   private:
   inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
   {
     int i = glyphArray.search (glyph_id);
-    if (i != -1)
-        return i;
-    return NOT_COVERED;
+    ASSERT_STATIC (((unsigned int) -1) == NOT_COVERED);
+    return i;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (glyphArray.sanitize (c));
   }
 
   inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
     return glyphs->has (glyphArray[index]);
   }
 
+  template <typename set_t>
+  inline void add_coverage (set_t *glyphs) const {
+    unsigned int count = glyphArray.len;
+    for (unsigned int i = 0; i < count; i++)
+      glyphs->add (glyphArray[i]);
+  }
+
   struct Iter {
     inline void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
     inline bool more (void) { return i < c->glyphArray.len; }
     inline void next (void) { i++; }
     inline uint16_t get_glyph (void) { return c->glyphArray[i]; }
     inline uint16_t get_coverage (void) { return i; }
 
     private:
     const struct CoverageFormat1 *c;
     unsigned int i;
   };
 
-  private:
+  protected:
   USHORT	coverageFormat;	/* Format identifier--format = 1 */
   SortedArrayOf<GlyphID>
 		glyphArray;	/* Array of GlyphIDs--in numerical order */
   public:
   DEFINE_SIZE_ARRAY (4, glyphArray);
 };
 
 struct CoverageFormat2
@@ -407,16 +419,23 @@ struct CoverageFormat2
 	  range.intersects (glyphs))
         return true;
       else if (index < range.value)
         return false;
     }
     return false;
   }
 
+  template <typename set_t>
+  inline void add_coverage (set_t *glyphs) const {
+    unsigned int count = rangeRecord.len;
+    for (unsigned int i = 0; i < count; i++)
+      rangeRecord[i].add_coverage (glyphs);
+  }
+
   struct Iter {
     inline void init (const CoverageFormat2 &c_) {
       c = &c_;
       coverage = 0;
       i = 0;
       j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0;
     }
     inline bool more (void) { return i < c->rangeRecord.len; }
@@ -433,17 +452,17 @@ struct CoverageFormat2
     inline uint16_t get_glyph (void) { return j; }
     inline uint16_t get_coverage (void) { return coverage; }
 
     private:
     const struct CoverageFormat2 *c;
     unsigned int i, j, coverage;
   };
 
-  private:
+  protected:
   USHORT	coverageFormat;	/* Format identifier--format = 2 */
   SortedArrayOf<RangeRecord>
 		rangeRecord;	/* Array of glyph ranges--ordered by
 				 * Start GlyphID. rangeCount entries
 				 * long */
   public:
   DEFINE_SIZE_ARRAY (4, rangeRecord);
 };
@@ -484,16 +503,25 @@ struct Coverage
   inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
     switch (u.format) {
     case 1: return u.format1.intersects_coverage (glyphs, index);
     case 2: return u.format2.intersects_coverage (glyphs, index);
     default:return false;
     }
   }
 
+  template <typename set_t>
+  inline void add_coverage (set_t *glyphs) const {
+    switch (u.format) {
+    case 1: u.format1.add_coverage (glyphs); break;
+    case 2: u.format2.add_coverage (glyphs); break;
+    default:                                 break;
+    }
+  }
+
   struct Iter {
     Iter (void) : format (0) {};
     inline void init (const Coverage &c_) {
       format = c_.u.format;
       switch (format) {
       case 1: return u.format1.init (c_.u.format1);
       case 2: return u.format2.init (c_.u.format2);
       default:return;
@@ -531,17 +559,17 @@ struct Coverage
     private:
     unsigned int format;
     union {
     CoverageFormat1::Iter	format1;
     CoverageFormat2::Iter	format2;
     } u;
   };
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   CoverageFormat1	format1;
   CoverageFormat2	format2;
   } u;
   public:
   DEFINE_SIZE_UNION (2, format);
 };
@@ -553,17 +581,17 @@ struct Coverage
 
 struct ClassDefFormat1
 {
   friend struct ClassDef;
 
   private:
   inline unsigned int get_class (hb_codepoint_t glyph_id) const
   {
-    if ((unsigned int) (glyph_id - startGlyph) < classValue.len)
+    if (unlikely ((unsigned int) (glyph_id - startGlyph) < classValue.len))
       return classValue[glyph_id - startGlyph];
     return 0;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && classValue.sanitize (c));
   }
@@ -571,16 +599,17 @@ struct ClassDefFormat1
   inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
     unsigned int count = classValue.len;
     for (unsigned int i = 0; i < count; i++)
       if (classValue[i] == klass && glyphs->has (startGlyph + i))
         return true;
     return false;
   }
 
+  protected:
   USHORT	classFormat;		/* Format identifier--format = 1 */
   GlyphID	startGlyph;		/* First GlyphID of the classValueArray */
   ArrayOf<USHORT>
 		classValue;		/* Array of Class Values--one per GlyphID */
   public:
   DEFINE_SIZE_ARRAY (6, classValue);
 };
 
@@ -605,16 +634,17 @@ struct ClassDefFormat2
   inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
     unsigned int count = rangeRecord.len;
     for (unsigned int i = 0; i < count; i++)
       if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs))
         return true;
     return false;
   }
 
+  protected:
   USHORT	classFormat;	/* Format identifier--format = 2 */
   SortedArrayOf<RangeRecord>
 		rangeRecord;	/* Array of glyph ranges--ordered by
 				 * Start GlyphID */
   public:
   DEFINE_SIZE_ARRAY (4, rangeRecord);
 };
 
@@ -644,17 +674,17 @@ struct ClassDef
   inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
     switch (u.format) {
     case 1: return u.format1.intersects_class (glyphs, klass);
     case 2: return u.format2.intersects_class (glyphs, klass);
     default:return false;
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   ClassDefFormat1	format1;
   ClassDefFormat2	format2;
   } u;
   public:
   DEFINE_SIZE_UNION (2, format);
 };
@@ -715,17 +745,17 @@ struct Device
     return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f)));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && c->check_range (this, this->get_size ()));
   }
 
-  private:
+  protected:
   USHORT	startSize;		/* Smallest size to correct--in ppem */
   USHORT	endSize;		/* Largest size to correct--in ppem */
   USHORT	deltaFormat;		/* Format of DeltaValue array data: 1, 2, or 3
 					 * 1	Signed 2-bit value, 8 values per uint16
 					 * 2	Signed 4-bit value, 4 values per uint16
 					 * 3	Signed 8-bit value, 2 values per uint16
 					 */
   USHORT	deltaValue[VAR];	/* Array of compressed data */
--- a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
@@ -69,17 +69,17 @@ struct AttachList
     return points.len;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && attachPoint.sanitize (c, this));
   }
 
-  private:
+  protected:
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table -- from
 					 * beginning of AttachList table */
   OffsetArrayOf<AttachPoint>
 		attachPoint;		/* Array of AttachPoint tables
 					 * in Coverage Index order */
   public:
   DEFINE_SIZE_ARRAY (4, attachPoint);
@@ -99,43 +99,43 @@ struct CaretValueFormat1
     return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this));
   }
 
-  private:
+  protected:
   USHORT	caretValueFormat;	/* Format identifier--format = 1 */
   SHORT		coordinate;		/* X or Y value, in design units */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
 struct CaretValueFormat2
 {
   friend struct CaretValue;
 
   private:
   inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const
   {
     hb_position_t x, y;
-    if (hb_font_get_glyph_contour_point_for_origin (font, glyph_id, caretValuePoint, direction, &x, &y))
+    if (font->get_glyph_contour_point_for_origin (glyph_id, caretValuePoint, direction, &x, &y))
       return HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
     else
       return 0;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this));
   }
 
-  private:
+  protected:
   USHORT	caretValueFormat;	/* Format identifier--format = 2 */
   USHORT	caretValuePoint;	/* Contour point index on glyph */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
 struct CaretValueFormat3
 {
@@ -148,17 +148,17 @@ struct CaretValueFormat3
            font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && deviceTable.sanitize (c, this));
   }
 
-  private:
+  protected:
   USHORT	caretValueFormat;	/* Format identifier--format = 3 */
   SHORT		coordinate;		/* X or Y value, in design units */
   OffsetTo<Device>
 		deviceTable;		/* Offset to Device table for X or Y
 					 * value--from beginning of CaretValue
 					 * table */
   public:
   DEFINE_SIZE_STATIC (6);
@@ -182,17 +182,17 @@ struct CaretValue
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     case 2: return TRACE_RETURN (u.format2.sanitize (c));
     case 3: return TRACE_RETURN (u.format3.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   CaretValueFormat1	format1;
   CaretValueFormat2	format2;
   CaretValueFormat3	format3;
   } u;
   public:
   DEFINE_SIZE_UNION (2, format);
@@ -217,17 +217,17 @@ struct LigGlyph
     return carets.len;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (carets.sanitize (c, this));
   }
 
-  private:
+  protected:
   OffsetArrayOf<CaretValue>
 		carets;			/* Offset array of CaretValue tables
 					 * --from beginning of LigGlyph table
 					 * --in increasing coordinate order */
   public:
   DEFINE_SIZE_ARRAY (2, carets);
 };
 
@@ -251,17 +251,17 @@ struct LigCaretList
     return lig_glyph.get_lig_carets (font, direction, glyph_id, start_offset, caret_count, caret_array);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this));
   }
 
-  private:
+  protected:
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of LigCaretList table */
   OffsetArrayOf<LigGlyph>
 		ligGlyph;		/* Array of LigGlyph tables
 					 * in Coverage Index order */
   public:
   DEFINE_SIZE_ARRAY (4, ligGlyph);
@@ -273,17 +273,17 @@ struct MarkGlyphSetsFormat1
   inline bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
   { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   LongOffsetArrayOf<Coverage>
 		coverage;		/* Array of long offsets to mark set
 					 * coverage tables */
   public:
   DEFINE_SIZE_ARRAY (4, coverage);
 };
 
@@ -301,17 +301,17 @@ struct MarkGlyphSets
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   MarkGlyphSetsFormat1	format1;
   } u;
   public:
   DEFINE_SIZE_UNION (2, format);
 };
 
@@ -387,17 +387,17 @@ struct GDEF
     case ComponentGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
     case MarkGlyph:
 	  klass = get_mark_attachment_type (glyph);
 	  return HB_OT_LAYOUT_GLYPH_CLASS_MARK | (klass << 8);
     }
   }
 
 
-  private:
+  protected:
   FixedVersion	version;		/* Version of the GDEF table--currently
 					 * 0x00010002 */
   OffsetTo<ClassDef>
 		glyphClassDef;		/* Offset to class definition table
 					 * for glyph type--from beginning of
 					 * GDEF header (may be Null) */
   OffsetTo<AttachList>
 		attachList;		/* Offset to list of glyphs with
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -220,17 +220,17 @@ struct AnchorFormat1
       *y = font->em_scale_y (yCoordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   SHORT		xCoordinate;		/* Horizontal value--in design units */
   SHORT		yCoordinate;		/* Vertical value--in design units */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 struct AnchorFormat2
@@ -242,27 +242,27 @@ struct AnchorFormat2
 			  hb_position_t *x, hb_position_t *y) const
   {
       unsigned int x_ppem = font->x_ppem;
       unsigned int y_ppem = font->y_ppem;
       hb_position_t cx, cy;
       hb_bool_t ret = false;
 
       if (x_ppem || y_ppem)
-	ret = hb_font_get_glyph_contour_point_for_origin (font, glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
+	ret = font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
       *x = x_ppem && ret ? cx : font->em_scale_x (xCoordinate);
       *y = y_ppem && ret ? cy : font->em_scale_y (yCoordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 2 */
   SHORT		xCoordinate;		/* Horizontal value--in design units */
   SHORT		yCoordinate;		/* Vertical value--in design units */
   USHORT	anchorPoint;		/* Index to glyph contour point */
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
@@ -283,17 +283,17 @@ struct AnchorFormat3
 	*y += (this+yDeviceTable).get_x_delta (font);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 3 */
   SHORT		xCoordinate;		/* Horizontal value--in design units */
   SHORT		yCoordinate;		/* Vertical value--in design units */
   OffsetTo<Device>
 		xDeviceTable;		/* Offset to Device table for X
 					 * coordinate-- from beginning of
 					 * Anchor table (may be NULL) */
   OffsetTo<Device>
@@ -324,17 +324,17 @@ struct Anchor
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     case 2: return TRACE_RETURN (u.format2.sanitize (c));
     case 3: return TRACE_RETURN (u.format3.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   AnchorFormat1		format1;
   AnchorFormat2		format2;
   AnchorFormat3		format3;
   } u;
   public:
   DEFINE_SIZE_UNION (2, format);
@@ -355,17 +355,17 @@ struct AnchorMatrix
     unsigned int count = rows * cols;
     if (!c->check_array (matrix, matrix[0].static_size, count)) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < count; i++)
       if (!matrix[i].sanitize (c, this)) return TRACE_RETURN (false);
     return TRACE_RETURN (true);
   }
 
   USHORT	rows;			/* Number of rows */
-  private:
+  protected:
   OffsetTo<Anchor>
 		matrix[VAR];		/* Matrix of offsets to Anchor tables--
 					 * from beginning of AnchorMatrix table */
   public:
   DEFINE_SIZE_ARRAY (2, matrix);
 };
 
 
@@ -373,17 +373,17 @@ struct MarkRecord
 {
   friend struct MarkArray;
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base));
   }
 
-  private:
+  protected:
   USHORT	klass;			/* Class defined for this mark */
   OffsetTo<Anchor>
 		markAnchor;		/* Offset to Anchor table--from
 					 * beginning of MarkArray table */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
@@ -424,16 +424,22 @@ struct MarkArray : ArrayOf<MarkRecord>	/
 
 /* Lookups */
 
 struct SinglePosFormat1
 {
   friend struct SinglePos;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    return this+coverage;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     valueFormat.apply_value (c->font, c->direction, this,
 			     values, c->buffer->cur_pos());
@@ -442,17 +448,17 @@ struct SinglePosFormat1
     return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_value (c, this, values));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ValueFormat	valueFormat;		/* Defines the types of data in the
 					 * ValueRecord */
   ValueRecord	values;			/* Defines positioning
 					 * value(s)--applied to all glyphs in
@@ -461,16 +467,22 @@ struct SinglePosFormat1
   DEFINE_SIZE_ARRAY (6, values);
 };
 
 struct SinglePosFormat2
 {
   friend struct SinglePos;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    return this+coverage;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     if (likely (index >= valueCount)) return TRACE_RETURN (false);
 
@@ -482,17 +494,17 @@ struct SinglePosFormat2
     return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_values (c, this, values, valueCount));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ValueFormat	valueFormat;		/* Defines the types of data in the
 					 * ValueRecord */
   USHORT	valueCount;		/* Number of ValueRecords */
   ValueRecord	values;			/* Array of ValueRecords--positioning
@@ -501,16 +513,26 @@ struct SinglePosFormat2
   DEFINE_SIZE_ARRAY (8, values);
 };
 
 struct SinglePos
 {
   friend struct PosLookupSubTable;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_coverage ();
+    case 2: return u.format2.get_coverage ();
+    default:return Null(Coverage);
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
     case 2: return TRACE_RETURN (u.format2.apply (c));
     default:return TRACE_RETURN (false);
     }
@@ -521,30 +543,30 @@ struct SinglePos
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     case 2: return TRACE_RETURN (u.format2.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   SinglePosFormat1	format1;
   SinglePosFormat2	format2;
   } u;
 };
 
 
 struct PairValueRecord
 {
   friend struct PairSet;
 
-  private:
+  protected:
   GlyphID	secondGlyph;		/* GlyphID of second glyph in the
 					 * pair--first glyph is listed in the
 					 * Coverage table */
   ValueRecord	values;			/* Positioning data for the first glyph
 					 * followed by for second glyph */
   public:
   DEFINE_SIZE_ARRAY (2, values);
 };
@@ -596,29 +618,35 @@ struct PairSet
        && c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
 
     unsigned int count = len;
     PairValueRecord *record = CastP<PairValueRecord> (array);
     return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
 		      && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
   }
 
-  private:
+  protected:
   USHORT	len;			/* Number of PairValueRecords */
   USHORT	array[VAR];		/* Array of PairValueRecords--ordered
 					 * by GlyphID of the second glyph */
   public:
   DEFINE_SIZE_ARRAY (2, array);
 };
 
 struct PairPosFormat1
 {
   friend struct PairPos;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    return this+coverage;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     hb_apply_context_t::mark_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) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
@@ -638,17 +666,17 @@ struct PairPosFormat1
       &valueFormat1,
       len1,
       1 + len1 + len2
     };
 
     return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ValueFormat	valueFormat1;		/* Defines the types of data in
 					 * ValueRecord1--for the first glyph
 					 * in the pair--may be zero (0) */
   ValueFormat	valueFormat2;		/* Defines the types of data in
@@ -661,16 +689,22 @@ struct PairPosFormat1
   DEFINE_SIZE_ARRAY (10, pairSet);
 };
 
 struct PairPosFormat2
 {
   friend struct PairPos;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    return this+coverage;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     hb_apply_context_t::mark_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) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
@@ -710,17 +744,17 @@ struct PairPosFormat2
     unsigned int stride = len1 + len2;
     unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
     unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
     return TRACE_RETURN (c->check_array (values, record_size, count) &&
 			 valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
 			 valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ValueFormat	valueFormat1;		/* ValueRecord definition--for the
 					 * first glyph of the pair--may be zero
 					 * (0) */
   ValueFormat	valueFormat2;		/* ValueRecord definition--for the
@@ -745,16 +779,26 @@ struct PairPosFormat2
   DEFINE_SIZE_ARRAY (16, values);
 };
 
 struct PairPos
 {
   friend struct PosLookupSubTable;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_coverage ();
+    case 2: return u.format2.get_coverage ();
+    default:return Null(Coverage);
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
     case 2: return TRACE_RETURN (u.format2.apply (c));
     default:return TRACE_RETURN (false);
     }
@@ -765,17 +809,17 @@ struct PairPos
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     case 2: return TRACE_RETURN (u.format2.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   PairPosFormat1	format1;
   PairPosFormat2	format2;
   } u;
 };
 
 
@@ -783,17 +827,17 @@ struct EntryExitRecord
 {
   friend struct CursivePosFormat1;
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
   }
 
-  private:
+  protected:
   OffsetTo<Anchor>
 		entryAnchor;		/* Offset to EntryAnchor table--from
 					 * beginning of CursivePos
 					 * subtable--may be NULL */
   OffsetTo<Anchor>
 		exitAnchor;		/* Offset to ExitAnchor table--from
 					 * beginning of CursivePos
 					 * subtable--may be NULL */
@@ -801,16 +845,22 @@ struct EntryExitRecord
   DEFINE_SIZE_STATIC (4);
 };
 
 struct CursivePosFormat1
 {
   friend struct CursivePos;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    return this+coverage;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
 
     /* We don't handle mark glyphs here. */
     if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) return TRACE_RETURN (false);
 
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
@@ -888,33 +938,42 @@ struct CursivePosFormat1
     return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ArrayOf<EntryExitRecord>
 		entryExitRecord;	/* Array of EntryExit records--in
 					 * Coverage Index order */
   public:
   DEFINE_SIZE_ARRAY (6, entryExitRecord);
 };
 
 struct CursivePos
 {
   friend struct PosLookupSubTable;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_coverage ();
+    default:return Null(Coverage);
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
     default:return TRACE_RETURN (false);
     }
   }
@@ -923,17 +982,17 @@ struct CursivePos
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   CursivePosFormat1	format1;
   } u;
 };
 
 
 typedef AnchorMatrix BaseArray;		/* base-major--
@@ -941,43 +1000,54 @@ typedef AnchorMatrix BaseArray;		/* base
 					 * mark-minor--
 					 * ordered by class--zero-based. */
 
 struct MarkBasePosFormat1
 {
   friend struct MarkBasePos;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    return this+markCoverage;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint);
     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);
+    do {
+      if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) 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_CLASS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/}
 
     unsigned int base_index = (this+baseCoverage) (c->buffer->info[skippy_iter.idx].codepoint);
     if (base_index == NOT_COVERED) return TRACE_RETURN (false);
 
     return TRACE_RETURN ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && baseCoverage.sanitize (c, this) &&
 			 markArray.sanitize (c, this) && baseArray.sanitize (c, this, (unsigned int) classCount));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		markCoverage;		/* Offset to MarkCoverage table--from
 					 * beginning of MarkBasePos subtable */
   OffsetTo<Coverage>
 		baseCoverage;		/* Offset to BaseCoverage table--from
 					 * beginning of MarkBasePos subtable */
   USHORT	classCount;		/* Number of classes defined for marks */
@@ -991,16 +1061,25 @@ struct MarkBasePosFormat1
   DEFINE_SIZE_STATIC (12);
 };
 
 struct MarkBasePos
 {
   friend struct PosLookupSubTable;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_coverage ();
+    default:return Null(Coverage);
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
     default:return TRACE_RETURN (false);
     }
   }
@@ -1009,17 +1088,17 @@ struct MarkBasePos
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   MarkBasePosFormat1	format1;
   } u;
 };
 
 
 typedef AnchorMatrix LigatureAttach;	/* component-major--
@@ -1032,16 +1111,22 @@ typedef OffsetListOf<LigatureAttach> Lig
 					 * tables ordered by
 					 * LigatureCoverage Index */
 
 struct MarkLigPosFormat1
 {
   friend struct MarkLigPos;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    return this+markCoverage;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint);
     if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
     unsigned int property;
@@ -1057,42 +1142,39 @@ struct MarkLigPosFormat1
 
     const LigatureArray& lig_array = this+ligatureArray;
     const LigatureAttach& lig_attach = lig_array[lig_index];
 
     /* Find component to attach to */
     unsigned int comp_count = lig_attach.rows;
     if (unlikely (!comp_count)) return TRACE_RETURN (false);
 
-    unsigned int comp_index;
     /* We must now check whether the ligature ID of the current mark glyph
      * is identical to the ligature ID of the found ligature.  If yes, we
      * can directly use the component index.  If not, we attach the mark
      * glyph to the last component of the ligature. */
-    if (get_lig_id (c->buffer->info[j]) &&
-	get_lig_id (c->buffer->cur()) &&
-	get_lig_comp (c->buffer->cur()) > 0)
-    {
-      comp_index = get_lig_comp (c->buffer->cur()) - 1;
-      if (comp_index >= comp_count)
-	comp_index = comp_count - 1;
-    }
+    unsigned int comp_index;
+    unsigned int lig_id = get_lig_id (c->buffer->info[j]);
+    unsigned int mark_id = get_lig_id (c->buffer->cur());
+    unsigned int mark_comp = get_lig_comp (c->buffer->cur());
+    if (lig_id && lig_id == mark_id && mark_comp > 0)
+      comp_index = MIN (comp_count, get_lig_comp (c->buffer->cur())) - 1;
     else
       comp_index = comp_count - 1;
 
     return TRACE_RETURN ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && ligatureCoverage.sanitize (c, this) &&
 			 markArray.sanitize (c, this) && ligatureArray.sanitize (c, this, (unsigned int) classCount));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		markCoverage;		/* Offset to Mark Coverage table--from
 					 * beginning of MarkLigPos subtable */
   OffsetTo<Coverage>
 		ligatureCoverage;	/* Offset to Ligature Coverage
 					 * table--from beginning of MarkLigPos
 					 * subtable */
@@ -1107,16 +1189,25 @@ struct MarkLigPosFormat1
   DEFINE_SIZE_STATIC (12);
 };
 
 struct MarkLigPos
 {
   friend struct PosLookupSubTable;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_coverage ();
+    default:return Null(Coverage);
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
     default:return TRACE_RETURN (false);
     }
   }
@@ -1125,17 +1216,17 @@ struct MarkLigPos
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   MarkLigPosFormat1	format1;
   } u;
 };
 
 
 typedef AnchorMatrix Mark2Array;	/* mark2-major--
@@ -1143,53 +1234,72 @@ typedef AnchorMatrix Mark2Array;	/* mark
 					 * mark1-minor--
 					 * ordered by class--zero-based. */
 
 struct MarkMarkPosFormat1
 {
   friend struct MarkMarkPos;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    return this+mark1Coverage;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     unsigned int mark1_index = (this+mark1Coverage) (c->buffer->cur().codepoint);
     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);
 
     if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) return TRACE_RETURN (false);
 
     unsigned int j = skippy_iter.idx;
 
-    /* Two marks match only if they belong to the same base, or same component
-     * of the same ligature.  That is, the component numbers must match, and
-     * if those are non-zero, the ligid number should also match. */
-    if ((get_lig_comp (c->buffer->cur())) ||
-	(get_lig_comp (c->buffer->info[j]) > 0 &&
-	 get_lig_id (c->buffer->cur())))
-      return TRACE_RETURN (false);
+    unsigned int id1 = get_lig_id (c->buffer->cur());
+    unsigned int id2 = get_lig_id (c->buffer->info[j]);
+    unsigned int comp1 = get_lig_comp (c->buffer->cur());
+    unsigned int comp2 = get_lig_comp (c->buffer->info[j]);
 
+    if (likely (id1 == id2)) {
+      if (id1 == 0) /* Marks belonging to the same base. */
+	goto good;
+      else if (comp1 == comp2) /* Marks belonging to the same ligature component. */
+        goto good;
+    } else {
+      /* If ligature ids don't match, it may be the case that one of the marks
+       * itself is a ligature.  In which case match. */
+      if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2))
+	goto good;
+    }
+
+    /* Didn't match. */
+    return TRACE_RETURN (false);
+
+    good:
     unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
     if (mark2_index == NOT_COVERED) return TRACE_RETURN (false);
 
     return TRACE_RETURN ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) && mark1Coverage.sanitize (c, this) &&
 			 mark2Coverage.sanitize (c, this) && mark1Array.sanitize (c, this)
 			 && mark2Array.sanitize (c, this, (unsigned int) classCount));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		mark1Coverage;		/* Offset to Combining Mark1 Coverage
 					 * table--from beginning of MarkMarkPos
 					 * subtable */
   OffsetTo<Coverage>
 		mark2Coverage;		/* Offset to Combining Mark2 Coverage
 					 * table--from beginning of MarkMarkPos
@@ -1205,16 +1315,25 @@ struct MarkMarkPosFormat1
   DEFINE_SIZE_STATIC (12);
 };
 
 struct MarkMarkPos
 {
   friend struct PosLookupSubTable;
 
   private:
+
+  inline const Coverage &get_coverage (void) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_coverage ();
+    default:return Null(Coverage);
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
     default:return TRACE_RETURN (false);
     }
   }
@@ -1223,17 +1342,17 @@ struct MarkMarkPos
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   MarkMarkPosFormat1	format1;
   } u;
 };
 
 
 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
@@ -1270,16 +1389,18 @@ struct ExtensionPos : Extension
   private:
   inline const struct PosLookupSubTable& get_subtable (void) const
   {
     unsigned int offset = get_offset ();
     if (unlikely (!offset)) return Null(PosLookupSubTable);
     return StructAtOffset<PosLookupSubTable> (this, offset);
   }
 
+  inline const Coverage &get_coverage (void) const;
+
   inline bool apply (hb_apply_context_t *c) const;
 
   inline bool sanitize (hb_sanitize_context_t *c);
 };
 
 
 
 /*
@@ -1298,99 +1419,137 @@ struct PosLookupSubTable
     MarkBase		= 4,
     MarkLig		= 5,
     MarkMark		= 6,
     Context		= 7,
     ChainContext	= 8,
     Extension		= 9
   };
 
+  inline const Coverage &get_coverage (unsigned int lookup_type) const
+  {
+    switch (lookup_type) {
+    case Single:		return u.single.get_coverage ();
+    case Pair:			return u.pair.get_coverage ();
+    case Cursive:		return u.cursive.get_coverage ();
+    case MarkBase:		return u.markBase.get_coverage ();
+    case MarkLig:		return u.markLig.get_coverage ();
+    case MarkMark:		return u.markMark.get_coverage ();
+    case Context:		return u.context.get_coverage ();
+    case ChainContext:		return u.chainContext.get_coverage ();
+    case Extension:		return u.extension.get_coverage ();
+    default:			return Null(Coverage);
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
   {
     TRACE_APPLY ();
     switch (lookup_type) {
     case Single:		return TRACE_RETURN (u.single.apply (c));
     case Pair:			return TRACE_RETURN (u.pair.apply (c));
     case Cursive:		return TRACE_RETURN (u.cursive.apply (c));
     case MarkBase:		return TRACE_RETURN (u.markBase.apply (c));
     case MarkLig:		return TRACE_RETURN (u.markLig.apply (c));
     case MarkMark:		return TRACE_RETURN (u.markMark.apply (c));
-    case Context:		return TRACE_RETURN (u.c.apply (c));
+    case Context:		return TRACE_RETURN (u.context.apply (c));
     case ChainContext:		return TRACE_RETURN (u.chainContext.apply (c));
     case Extension:		return TRACE_RETURN (u.extension.apply (c));
     default:			return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
     TRACE_SANITIZE ();
+    if (!u.header.sub_format.sanitize (c))
+      return TRACE_RETURN (false);
     switch (lookup_type) {
     case Single:		return TRACE_RETURN (u.single.sanitize (c));
     case Pair:			return TRACE_RETURN (u.pair.sanitize (c));
     case Cursive:		return TRACE_RETURN (u.cursive.sanitize (c));
     case MarkBase:		return TRACE_RETURN (u.markBase.sanitize (c));
     case MarkLig:		return TRACE_RETURN (u.markLig.sanitize (c));
     case MarkMark:		return TRACE_RETURN (u.markMark.sanitize (c));
-    case Context:		return TRACE_RETURN (u.c.sanitize (c));
+    case Context:		return TRACE_RETURN (u.context.sanitize (c));
     case ChainContext:		return TRACE_RETURN (u.chainContext.sanitize (c));
     case Extension:		return TRACE_RETURN (u.extension.sanitize (c));
     default:			return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
-  USHORT		sub_format;
+  struct {
+    USHORT			sub_format;
+  } header;
   SinglePos		single;
   PairPos		pair;
   CursivePos		cursive;
   MarkBasePos		markBase;
   MarkLigPos		markLig;
   MarkMarkPos		markMark;
-  ContextPos		c;
+  ContextPos		context;
   ChainContextPos	chainContext;
   ExtensionPos		extension;
   } u;
   public:
-  DEFINE_SIZE_UNION (2, sub_format);
+  DEFINE_SIZE_UNION (2, header.sub_format);
 };
 
 
 struct PosLookup : Lookup
 {
   inline const PosLookupSubTable& get_subtable (unsigned int i) const
   { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
 
+  template <typename set_t>
+  inline void add_coverage (set_t *glyphs) const
+  {
+    const Coverage *last = NULL;
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++) {
+      const Coverage *c = &get_subtable (i).get_coverage (get_type ());
+      if (c != last) {
+        c->add_coverage (glyphs);
+        last = c;
+      }
+    }
+  }
+
   inline bool apply_once (hb_apply_context_t *c) const
   {
     unsigned int lookup_type = get_type ();
 
-    if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property))
+    if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props, &c->property))
       return false;
 
-    for (unsigned int i = 0; i < get_subtable_count (); i++)
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++)
       if (get_subtable (i).apply (c, lookup_type))
 	return true;
 
     return false;
   }
 
   inline bool apply_string (hb_apply_context_t *c) const
   {
     bool ret = false;
 
     if (unlikely (!c->buffer->len))
       return false;
 
     c->set_lookup (*this);
 
     c->buffer->idx = 0;
+
     while (c->buffer->idx < c->buffer->len)
     {
-      if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
+      if ((c->buffer->cur().mask & c->lookup_mask) &&
+	  c->digest.may_have (c->buffer->cur().codepoint) &&
+	  apply_once (c))
 	ret = true;
       else
 	c->buffer->idx++;
     }
 
     return ret;
   }
 
@@ -1410,21 +1569,25 @@ typedef OffsetListOf<PosLookup> PosLooku
 
 struct GPOS : GSUBGPOS
 {
   static const hb_tag_t Tag	= HB_OT_TAG_GPOS;
 
   inline const PosLookup& get_lookup (unsigned int i) const
   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
 
+  template <typename set_t>
+  inline void add_coverage (set_t *glyphs, unsigned int lookup_index) const
+  { get_lookup (lookup_index).add_coverage (glyphs); }
+
   inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
   { return get_lookup (lookup_index).apply_string (c); }
 
-  static inline void position_start (hb_buffer_t *buffer);
-  static inline void position_finish (hb_buffer_t *buffer);
+  static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
+  static inline void position_finish (hb_font_t *font, hb_buffer_t *buffer, hb_bool_t zero_width_attahced_marks);
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
     OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
     return TRACE_RETURN (list.sanitize (c, this));
   }
   public:
@@ -1447,73 +1610,80 @@ fix_cursive_minor_offset (hb_glyph_posit
 
     if (HB_DIRECTION_IS_HORIZONTAL (direction))
       pos[i].y_offset += pos[j].y_offset;
     else
       pos[i].x_offset += pos[j].x_offset;
 }
 
 static void
-fix_mark_attachment (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction)
+fix_mark_attachment (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, hb_bool_t zero_width_attached_marks)
 {
   if (likely (!(pos[i].attach_lookback())))
     return;
 
   unsigned int j = i - pos[i].attach_lookback();
 
-  pos[i].x_advance = 0;
-  pos[i].y_advance = 0;
+  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;
 
   if (HB_DIRECTION_IS_FORWARD (direction))
     for (unsigned int k = j; k < i; k++) {
       pos[i].x_offset -= pos[k].x_advance;
       pos[i].y_offset -= pos[k].y_advance;
     }
   else
     for (unsigned int k = j + 1; k < i + 1; k++) {
       pos[i].x_offset += pos[k].x_advance;
       pos[i].y_offset += pos[k].y_advance;
     }
 }
 
 void
-GPOS::position_start (hb_buffer_t *buffer)
+GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
 {
   buffer->clear_positions ();
 
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
     buffer->pos[i].attach_lookback() = buffer->pos[i].cursive_chain() = 0;
 }
 
 void
-GPOS::position_finish (hb_buffer_t *buffer)
+GPOS::position_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer, hb_bool_t zero_width_attached_marks)
 {
   unsigned int len;
   hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
   hb_direction_t direction = buffer->props.direction;
 
   /* Handle cursive connections */
   for (unsigned int i = 0; i < len; i++)
     fix_cursive_minor_offset (pos, i, direction);
 
   /* Handle attachments */
   for (unsigned int i = 0; i < len; i++)
-    fix_mark_attachment (pos, i, direction);
+    fix_mark_attachment (pos, i, direction, zero_width_attached_marks);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, syllable);
   HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props);
-  HB_BUFFER_DEALLOCATE_VAR (buffer, props_cache);
+  HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_props);
 }
 
 
 /* Out-of-class implementation for methods recursing */
 
+inline const Coverage & ExtensionPos::get_coverage (void) const
+{
+  return get_subtable ().get_coverage (get_type ());
+}
+
 inline bool ExtensionPos::apply (hb_apply_context_t *c) const
 {
   TRACE_APPLY ();
   return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
 }
 
 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
 {
@@ -1521,17 +1691,17 @@ inline bool ExtensionPos::sanitize (hb_s
   if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
   unsigned int offset = get_offset ();
   if (unlikely (!offset)) return TRACE_RETURN (true);
   return TRACE_RETURN (StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ()));
 }
 
 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
 {
-  const GPOS &gpos = *(c->face->ot_layout->gpos);
+  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos);
   const PosLookup &l = gpos.get_lookup (lookup_index);
 
   if (unlikely (c->nesting_level_left == 0))
     return false;
 
   hb_apply_context_t new_c (*c);
   new_c.nesting_level_left--;
   new_c.set_lookup (l);
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -45,19 +45,19 @@ struct SingleSubstFormat1
     Coverage::Iter iter;
     for (iter.init (this+coverage); iter.more (); iter.next ()) {
       hb_codepoint_t glyph_id = iter.get_glyph ();
       if (c->glyphs->has (glyph_id))
 	c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFF);
     }
   }
 
-  inline bool would_apply (hb_codepoint_t glyph_id) const
+  inline const Coverage &get_coverage (void) const
   {
-    return (this+coverage) (glyph_id) != NOT_COVERED;
+    return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
     unsigned int index = (this+coverage) (glyph_id);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
@@ -70,17 +70,17 @@ struct SingleSubstFormat1
     return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   SHORT		deltaGlyphID;		/* Add to original GlyphID to get
 					 * substitute GlyphID */
   public:
   DEFINE_SIZE_STATIC (6);
@@ -97,19 +97,19 @@ struct SingleSubstFormat2
     TRACE_CLOSURE ();
     Coverage::Iter iter;
     for (iter.init (this+coverage); iter.more (); iter.next ()) {
       if (c->glyphs->has (iter.get_glyph ()))
 	c->glyphs->add (substitute[iter.get_coverage ()]);
     }
   }
 
-  inline bool would_apply (hb_codepoint_t glyph_id) const
+  inline const Coverage &get_coverage (void) const
   {
-    return (this+coverage) (glyph_id) != NOT_COVERED;
+    return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
     unsigned int index = (this+coverage) (glyph_id);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
@@ -122,17 +122,17 @@ struct SingleSubstFormat2
     return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   ArrayOf<GlyphID>
 		substitute;		/* Array of substitute
 					 * GlyphIDs--ordered by Coverage Index */
   public:
@@ -150,22 +150,22 @@ struct SingleSubst
     TRACE_CLOSURE ();
     switch (u.format) {
     case 1: u.format1.closure (c); break;
     case 2: u.format2.closure (c); break;
     default:                       break;
     }
   }
 
-  inline bool would_apply (hb_codepoint_t glyph_id) const
+  inline const Coverage &get_coverage (void) const
   {
     switch (u.format) {
-    case 1: return u.format1.would_apply (glyph_id);
-    case 2: return u.format2.would_apply (glyph_id);
-    default:return false;
+    case 1: return u.format1.get_coverage ();
+    case 2: return u.format2.get_coverage ();
+    default:return Null(Coverage);
     }
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
@@ -179,17 +179,17 @@ struct SingleSubst
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     case 2: return TRACE_RETURN (u.format2.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   SingleSubstFormat1	format1;
   SingleSubstFormat2	format2;
   } u;
 };
 
 
@@ -208,28 +208,33 @@ struct Sequence
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     if (unlikely (!substitute.len)) return TRACE_RETURN (false);
 
     unsigned int klass = c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE ? HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH : 0;
-    c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array, klass);
+    unsigned int count = substitute.len;
+    for (unsigned int i = 0; i < count; i++) {
+      set_lig_props_for_component (c->buffer->cur(), i);
+      c->output_glyph (substitute.array[i], klass);
+    }
+    c->buffer->skip_glyph ();
 
     return TRACE_RETURN (true);
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (substitute.sanitize (c));
   }
 
-  private:
+  protected:
   ArrayOf<GlyphID>
 		substitute;		/* String of GlyphIDs to substitute */
   public:
   DEFINE_SIZE_ARRAY (2, substitute);
 };
 
 struct MultipleSubstFormat1
 {
@@ -242,19 +247,19 @@ struct MultipleSubstFormat1
     TRACE_CLOSURE ();
     Coverage::Iter iter;
     for (iter.init (this+coverage); iter.more (); iter.next ()) {
       if (c->glyphs->has (iter.get_glyph ()))
 	(this+sequence[iter.get_coverage ()]).closure (c);
     }
   }
 
-  inline bool would_apply (hb_codepoint_t glyph_id) const
+  inline const Coverage &get_coverage (void) const
   {
-    return (this+coverage) (glyph_id) != NOT_COVERED;
+    return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
 
     unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
@@ -262,17 +267,17 @@ struct MultipleSubstFormat1
     return TRACE_RETURN ((this+sequence[index]).apply (c));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   OffsetArrayOf<Sequence>
 		sequence;		/* Array of Sequence tables
 					 * ordered by Coverage Index */
   public:
@@ -289,21 +294,21 @@ struct MultipleSubst
   {
     TRACE_CLOSURE ();
     switch (u.format) {
     case 1: u.format1.closure (c); break;
     default:                       break;
     }
   }
 
-  inline bool would_apply (hb_codepoint_t glyph_id) const
+  inline const Coverage &get_coverage (void) const
   {
     switch (u.format) {
-    case 1: return u.format1.would_apply (glyph_id);
-    default:return false;
+    case 1: return u.format1.get_coverage ();
+    default:return Null(Coverage);
     }
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
@@ -315,17 +320,17 @@ struct MultipleSubst
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   MultipleSubstFormat1	format1;
   } u;
 };
 
 
 typedef ArrayOf<GlyphID> AlternateSet;	/* Array of alternate GlyphIDs--in
@@ -346,19 +351,19 @@ struct AlternateSubstFormat1
 	const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
 	unsigned int count = alt_set.len;
 	for (unsigned int i = 0; i < count; i++)
 	  c->glyphs->add (alt_set[i]);
       }
     }
   }
 
-  inline bool would_apply (hb_codepoint_t glyph_id) const
+  inline const Coverage &get_coverage (void) const
   {
-    return (this+coverage) (glyph_id) != NOT_COVERED;
+    return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
     unsigned int index = (this+coverage) (glyph_id);
@@ -384,17 +389,17 @@ struct AlternateSubstFormat1
     return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   OffsetArrayOf<AlternateSet>
 		alternateSet;		/* Array of AlternateSet tables
 					 * ordered by Coverage Index */
   public:
@@ -411,21 +416,21 @@ struct AlternateSubst
   {
     TRACE_CLOSURE ();
     switch (u.format) {
     case 1: u.format1.closure (c); break;
     default:                       break;
     }
   }
 
-  inline bool would_apply (hb_codepoint_t glyph_id) const
+  inline const Coverage &get_coverage (void) const
   {
     switch (u.format) {
-    case 1: return u.format1.would_apply (glyph_id);
-    default:return false;
+    case 1: return u.format1.get_coverage ();
+    default:return Null(Coverage);
     }
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
@@ -437,17 +442,17 @@ struct AlternateSubst
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   AlternateSubstFormat1	format1;
   } u;
 };
 
 
 struct Ligature
@@ -461,88 +466,166 @@ struct Ligature
     TRACE_CLOSURE ();
     unsigned int count = component.len;
     for (unsigned int i = 1; i < count; i++)
       if (!c->glyphs->has (component[i]))
         return;
     c->glyphs->add (ligGlyph);
   }
 
-  inline bool would_apply (hb_codepoint_t second) const
+  inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    return component.len == 2 && component[1] == second;
+    return c->len == 1 || (c->len == 2 && component.len == 2 && component[1] == c->second);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     unsigned int count = component.len;
-    if (unlikely (count < 2)) return TRACE_RETURN (false);
+    if (unlikely (count < 1)) return TRACE_RETURN (false);
 
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
     if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
-    bool found_non_mark = false;
+    /*
+     * This is perhaps the trickiest part of GSUB...  Remarks:
+     *
+     * - If all components of the ligature were marks, we call this a mark ligature.
+     *
+     * - If there is no GDEF, and the ligature is NOT a mark ligature, we categorize
+     *   it as a ligature glyph.  Though, really, this will not really be used...
+     *
+     * - If it *is* a mark ligature, we don't allocate a new ligature id, and leave
+     *   the ligature to keep its old ligature id.  This will allow it to attach to
+     *   a base ligature in GPOS.  Eg. if the sequence is: LAM,LAM,SHADDA,FATHA,HEH,
+     *   and LAM,LAM,HEH for a ligature, they will leave SHADDA and FATHA wit a
+     *   ligature id and component value of 2.  Then if SHADDA,FATHA form a ligature
+     *   later, we don't want them to lose their ligature id/component, otherwise
+     *   GPOS will fail to correctly position the mark ligature on top of the
+     *   LAM,LAM,HEH ligature.  See:
+     *     https://bugzilla.gnome.org/show_bug.cgi?id=676343
+     *
+     * - If a ligature is formed of components that some of which are also ligatures
+     *   themselves, and those ligature components had marks attached to *their*
+     *   components, we have to attach the marks to the new ligature component
+     *   positions!  Now *that*'s tricky!  And these marks may be following the
+     *   last component of the whole sequence, so we should loop forward looking
+     *   for them and update them.
+     *
+     *   Eg. the sequence is LAM,LAM,SHADDA,FATHA,HEH, and the font first forms a
+     *   'calt' ligature of LAM,HEH, leaving the SHADDA and FATHA with a ligature
+     *   id and component == 1.  Now, during 'liga', the LAM and the LAM-HEH ligature
+     *   form a LAM-LAM-HEH ligature.  We need to reassign the SHADDA and FATHA to
+     *   the new ligature with a component value of 2.
+     *
+     *   This in fact happened to a font...  See:
+     *   https://bugzilla.gnome.org/show_bug.cgi?id=437633
+     *
+     * - Ligatures cannot be formed across glyphs attached to different components
+     *   of previous ligatures.  Eg. the sequence is LAM,SHADDA,LAM,FATHA,HEH, and
+     *   LAM,LAM,HEH form a ligature, leaving SHADDA,FATHA next to eachother.
+     *   However, it would be wrong to ligate that SHADDA,FATHA sequence.o
+     *   There is an exception to this: If a ligature tries ligating with marks that
+     *   belong to it itself, go ahead, assuming that the font designer knows what
+     *   they are doing (otherwise it can break Indic stuff when a matra wants to
+     *   ligate with a conjunct...)
+     */
+
+    bool is_mark_ligature = !!(c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+
+    unsigned int total_component_count = 0;
+    total_component_count += get_lig_num_comps (c->buffer->cur());
+
+    unsigned int first_lig_id = get_lig_id (c->buffer->cur());
+    unsigned int first_lig_comp = get_lig_comp (c->buffer->cur());
 
     for (unsigned int i = 1; i < count; i++)
     {
       unsigned int property;
 
       if (!skippy_iter.next (&property)) return TRACE_RETURN (false);
 
-      found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+      if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i])) return TRACE_RETURN (false);
+
+      unsigned int this_lig_id = get_lig_id (c->buffer->info[skippy_iter.idx]);
+      unsigned int this_lig_comp = get_lig_comp (c->buffer->info[skippy_iter.idx]);
 
-      if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i])) return TRACE_RETURN (false);
+      if (first_lig_id && first_lig_comp) {
+        /* If first component was attached to a previous ligature component,
+	 * all subsequent components should be attached to the same ligature
+	 * component, otherwise we shouldn't ligate them. */
+        if (first_lig_id != this_lig_id || first_lig_comp != this_lig_comp)
+	  return TRACE_RETURN (false);
+      } else {
+        /* If first component was NOT attached to a previous ligature component,
+	 * all subsequent components should also NOT be attached to any ligature
+	 * component, unless they are attached to the first component itself! */
+        if (this_lig_id && this_lig_comp && (this_lig_id != first_lig_id))
+	  return TRACE_RETURN (false);
+      }
+
+      is_mark_ligature = is_mark_ligature && (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+      total_component_count += get_lig_num_comps (c->buffer->info[skippy_iter.idx]);
     }
 
-    unsigned int klass = first_was_mark && found_non_mark ? HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE : 0;
-
-    /* Allocate new ligature id */
-    unsigned int lig_id = allocate_lig_id (c->buffer);
-    set_lig_props (c->buffer->cur(), lig_id, 0);
+    /* Deal, we are forming the ligature. */
+    c->buffer->merge_clusters (c->buffer->idx, skippy_iter.idx + 1);
 
-    if (skippy_iter.idx < c->buffer->idx + count) /* No input glyphs skipped */
-    {
-      c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph, klass);
-    }
-    else
-    {
-      c->replace_glyph (ligGlyph);
+    unsigned int klass = is_mark_ligature ? 0 : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
+    unsigned int lig_id = is_mark_ligature ? 0 : allocate_lig_id (c->buffer);
+    unsigned int last_lig_id = get_lig_id (c->buffer->cur());
+    unsigned int last_num_components = get_lig_num_comps (c->buffer->cur());
+    unsigned int components_so_far = last_num_components;
+
+    if (!is_mark_ligature)
+      set_lig_props_for_ligature (c->buffer->cur(), lig_id, total_component_count);
+    c->replace_glyph (ligGlyph, klass);
 
-      /* Now we must do a second loop to copy the skipped glyphs to
-	 `out' and assign component values to it.  We start with the
-	 glyph after the first component.  Glyphs between component
-	 i and i+1 belong to component i.  Together with the lig_id
-	 value it is later possible to check whether a specific
-	 component value really belongs to a given ligature. */
-
-      for (unsigned int i = 1; i < count; i++)
+    for (unsigned int i = 1; i < count; i++)
+    {
+      while (c->should_mark_skip_current_glyph ())
       {
-	while (c->should_mark_skip_current_glyph ())
-	{
-	  set_lig_props (c->buffer->cur(),  lig_id, i);
-	  c->replace_glyph (c->buffer->cur().codepoint);
+	if (!is_mark_ligature) {
+	  unsigned int new_lig_comp = components_so_far - last_num_components +
+				      MIN (MAX (get_lig_comp (c->buffer->cur()), 1u), last_num_components);
+	  set_lig_props_for_mark (c->buffer->cur(), lig_id, new_lig_comp);
 	}
+	c->buffer->next_glyph ();
+      }
 
-	/* Skip the base glyph */
-	c->buffer->idx++;
+      last_lig_id = get_lig_id (c->buffer->cur());
+      last_num_components = get_lig_num_comps (c->buffer->cur());
+      components_so_far += last_num_components;
+
+      /* Skip the base glyph */
+      c->buffer->idx++;
+    }
+
+    if (!is_mark_ligature && last_lig_id) {
+      /* Re-adjust components for any marks following. */
+      for (unsigned int i = c->buffer->idx; i < c->buffer->len; i++) {
+	if (last_lig_id == get_lig_id (c->buffer->info[i])) {
+	  unsigned int new_lig_comp = components_so_far - last_num_components +
+				      MIN (MAX (get_lig_comp (c->buffer->info[i]), 1u), last_num_components);
+	  set_lig_props_for_mark (c->buffer->info[i], lig_id, new_lig_comp);
+	} else
+	  break;
       }
     }
 
     return TRACE_RETURN (true);
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c));
   }
 
-  private:
+  protected:
   GlyphID	ligGlyph;		/* GlyphID of ligature to substitute */
   HeadlessArrayOf<GlyphID>
 		component;		/* Array of component GlyphIDs--start
 					 * with the second  component--ordered
 					 * in writing direction */
   public:
   DEFINE_SIZE_ARRAY (4, component);
 };
@@ -556,23 +639,23 @@ struct LigatureSet
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
     unsigned int num_ligs = ligature.len;
     for (unsigned int i = 0; i < num_ligs; i++)
       (this+ligature[i]).closure (c);
   }
 
-  inline bool would_apply (hb_codepoint_t second) const
+  inline bool would_apply (hb_would_apply_context_t *c) const
   {
     unsigned int num_ligs = ligature.len;
     for (unsigned int i = 0; i < num_ligs; i++)
     {
       const Ligature &lig = this+ligature[i];
-      if (lig.would_apply (second))
+      if (lig.would_apply (c))
         return true;
     }
     return false;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
@@ -587,17 +670,17 @@ struct LigatureSet
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (ligature.sanitize (c, this));
   }
 
-  private:
+  protected:
   OffsetArrayOf<Ligature>
 		ligature;		/* Array LigatureSet tables
 					 * ordered by preference */
   public:
   DEFINE_SIZE_ARRAY (2, ligature);
 };
 
 struct LigatureSubstFormat1
@@ -611,21 +694,24 @@ struct LigatureSubstFormat1
     TRACE_CLOSURE ();
     Coverage::Iter iter;
     for (iter.init (this+coverage); iter.more (); iter.next ()) {
       if (c->glyphs->has (iter.get_glyph ()))
 	(this+ligatureSet[iter.get_coverage ()]).closure (c);
     }
   }
 
-  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+  inline const Coverage &get_coverage (void) const
   {
-    unsigned int index;
-    return (index = (this+coverage) (first)) != NOT_COVERED &&
-	   (this+ligatureSet[index]).would_apply (second);
+    return this+coverage;
+  }
+
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    return (this+ligatureSet[(this+coverage) (c->first)]).would_apply (c);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
     unsigned int index = (this+coverage) (glyph_id);
@@ -635,17 +721,17 @@ struct LigatureSubstFormat1
     return TRACE_RETURN (lig_set.apply (c));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   OffsetArrayOf<LigatureSet>
 		ligatureSet;		/* Array LigatureSet tables
 					 * ordered by Coverage Index */
   public:
@@ -662,20 +748,28 @@ struct LigatureSubst
   {
     TRACE_CLOSURE ();
     switch (u.format) {
     case 1: u.format1.closure (c); break;
     default:                       break;
     }
   }
 
-  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+  inline const Coverage &get_coverage (void) const
   {
     switch (u.format) {
-    case 1: return u.format1.would_apply (first, second);
+    case 1: return u.format1.get_coverage ();
+    default:return Null(Coverage);
+    }
+  }
+
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (c);
     default:return false;
     }
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
@@ -688,17 +782,17 @@ struct LigatureSubst
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   LigatureSubstFormat1	format1;
   } u;
 };
 
 
 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
@@ -752,18 +846,20 @@ struct ExtensionSubst : Extension
   inline const struct SubstLookupSubTable& get_subtable (void) const
   {
     unsigned int offset = get_offset ();
     if (unlikely (!offset)) return Null(SubstLookupSubTable);
     return StructAtOffset<SubstLookupSubTable> (this, offset);
   }
 
   inline void closure (hb_closure_context_t *c) const;
-  inline bool would_apply (hb_codepoint_t glyph_id) const;
-  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const;
+
+  inline const Coverage &get_coverage (void) const;
+
+  inline bool would_apply (hb_would_apply_context_t *c) const;
 
   inline bool apply (hb_apply_context_t *c) const;
 
   inline bool sanitize (hb_sanitize_context_t *c);
 
   inline bool is_reverse (void) const;
 };
 
@@ -794,16 +890,21 @@ struct ReverseChainSingleSubstFormat1
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
     Coverage::Iter iter;
     for (iter.init (this+coverage); iter.more (); iter.next ()) {
       if (c->glyphs->has (iter.get_glyph ()))
 	c->glyphs->add (substitute[iter.get_coverage ()]);
     }
   }
 
+  inline const Coverage &get_coverage (void) const
+  {
+    return this+coverage;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     if (unlikely (c->nesting_level_left != MAX_NESTING_LEVEL))
       return TRACE_RETURN (false); /* No chaining to this type */
 
     unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
@@ -814,17 +915,17 @@ struct ReverseChainSingleSubstFormat1
     if (match_backtrack (c,
 			 backtrack.len, (USHORT *) backtrack.array,
 			 match_coverage, this) &&
         match_lookahead (c,
 			 lookahead.len, (USHORT *) lookahead.array,
 			 match_coverage, this,
 			 1))
     {
-      c->buffer->cur().codepoint = substitute[index];
+      c->replace_glyph_inplace (substitute[index]);
       c->buffer->idx--; /* Reverse! */
       return TRACE_RETURN (true);
     }
 
     return TRACE_RETURN (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -833,17 +934,17 @@ struct ReverseChainSingleSubstFormat1
       return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     if (!lookahead.sanitize (c, this))
       return TRACE_RETURN (false);
     ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
     return TRACE_RETURN (substitute.sanitize (c));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetArrayOf<Coverage>
 		backtrack;		/* Array of coverage tables
 					 * in backtracking sequence, in  glyph
 					 * sequence order */
@@ -868,16 +969,24 @@ struct ReverseChainSingleSubst
   {
     TRACE_CLOSURE ();
     switch (u.format) {
     case 1: u.format1.closure (c); break;
     default:                       break;
     }
   }
 
+  inline const Coverage &get_coverage (void) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_coverage ();
+    default:return Null(Coverage);
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
     default:return TRACE_RETURN (false);
     }
   }
@@ -886,17 +995,17 @@ struct ReverseChainSingleSubst
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT				format;		/* Format identifier */
   ReverseChainSingleSubstFormat1	format1;
   } u;
 };
 
 
 
@@ -923,91 +1032,106 @@ struct SubstLookupSubTable
 		       unsigned int    lookup_type) const
   {
     TRACE_CLOSURE ();
     switch (lookup_type) {
     case Single:		u.single.closure (c); break;
     case Multiple:		u.multiple.closure (c); break;
     case Alternate:		u.alternate.closure (c); break;
     case Ligature:		u.ligature.closure (c); break;
-    case Context:		u.c.closure (c); break;
+    case Context:		u.context.closure (c); break;
     case ChainContext:		u.chainContext.closure (c); break;
     case Extension:		u.extension.closure (c); break;
     case ReverseChainSingle:	u.reverseChainContextSingle.closure (c); break;
     default:                    break;
     }
   }
 
-  inline bool would_apply (hb_codepoint_t glyph_id,
-			   unsigned int lookup_type) const
+  inline const Coverage &get_coverage (unsigned int lookup_type) const
   {
     switch (lookup_type) {
-    case Single:		return u.single.would_apply (glyph_id);
-    case Multiple:		return u.multiple.would_apply (glyph_id);
-    case Alternate:		return u.alternate.would_apply (glyph_id);
-    case Extension:		return u.extension.would_apply (glyph_id);
-    default:			return false;
+    case Single:		return u.single.get_coverage ();
+    case Multiple:		return u.multiple.get_coverage ();
+    case Alternate:		return u.alternate.get_coverage ();
+    case Ligature:		return u.ligature.get_coverage ();
+    case Context:		return u.context.get_coverage ();
+    case ChainContext:		return u.chainContext.get_coverage ();
+    case Extension:		return u.extension.get_coverage ();
+    case ReverseChainSingle:	return u.reverseChainContextSingle.get_coverage ();
+    default:			return Null(Coverage);
     }
   }
-  inline bool would_apply (hb_codepoint_t first,
-			   hb_codepoint_t second,
+
+  inline bool would_apply (hb_would_apply_context_t *c,
 			   unsigned int lookup_type) const
   {
+    TRACE_WOULD_APPLY ();
+    if (get_coverage (lookup_type).get_coverage (c->first) == NOT_COVERED) return false;
+    if (c->len == 1) return true; /* Done! */
+
+    /* Only need to look further for lookups that support substitutions
+     * of input longer than 1. */
     switch (lookup_type) {
-    case Ligature:		return u.ligature.would_apply (first, second);
-    case Extension:		return u.extension.would_apply (first, second);
+    case Ligature:		return u.ligature.would_apply (c);
+    case Context:		return u.context.would_apply (c);
+    case ChainContext:		return u.chainContext.would_apply (c);
+    case Extension:		return u.extension.would_apply (c);
     default:			return false;
     }
   }
 
   inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
   {
     TRACE_APPLY ();
     switch (lookup_type) {
     case Single:		return TRACE_RETURN (u.single.apply (c));
     case Multiple:		return TRACE_RETURN (u.multiple.apply (c));
     case Alternate:		return TRACE_RETURN (u.alternate.apply (c));
     case Ligature:		return TRACE_RETURN (u.ligature.apply (c));
-    case Context:		return TRACE_RETURN (u.c.apply (c));
+    case Context:		return TRACE_RETURN (u.context.apply (c));
     case ChainContext:		return TRACE_RETURN (u.chainContext.apply (c));
     case Extension:		return TRACE_RETURN (u.extension.apply (c));
     case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.apply (c));
     default:			return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
     TRACE_SANITIZE ();
+    if (!u.header.sub_format.sanitize (c))
+      return TRACE_RETURN (false);
     switch (lookup_type) {
     case Single:		return TRACE_RETURN (u.single.sanitize (c));
     case Multiple:		return TRACE_RETURN (u.multiple.sanitize (c));
     case Alternate:		return TRACE_RETURN (u.alternate.sanitize (c));
     case Ligature:		return TRACE_RETURN (u.ligature.sanitize (c));
-    case Context:		return TRACE_RETURN (u.c.sanitize (c));
+    case Context:		return TRACE_RETURN (u.context.sanitize (c));
     case ChainContext:		return TRACE_RETURN (u.chainContext.sanitize (c));
     case Extension:		return TRACE_RETURN (u.extension.sanitize (c));
     case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.sanitize (c));
     default:			return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
-  USHORT			sub_format;
+  struct {
+    USHORT			sub_format;
+  } header;
   SingleSubst			single;
   MultipleSubst			multiple;
   AlternateSubst		alternate;
   LigatureSubst			ligature;
-  ContextSubst			c;
+  ContextSubst			context;
   ChainContextSubst		chainContext;
   ExtensionSubst		extension;
   ReverseChainSingleSubst	reverseChainContextSingle;
   } u;
   public:
-  DEFINE_SIZE_UNION (2, sub_format);
+  DEFINE_SIZE_UNION (2, header.sub_format);
 };
 
 
 struct SubstLookup : Lookup
 {
   inline const SubstLookupSubTable& get_subtable (unsigned int i) const
   { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
 
@@ -1025,56 +1149,48 @@ struct SubstLookup : Lookup
   inline void closure (hb_closure_context_t *c) const
   {
     unsigned int lookup_type = get_type ();
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++)
       get_subtable (i).closure (c, lookup_type);
   }
 
-  inline bool would_apply (hb_codepoint_t glyph_id) const
+  template <typename set_t>
+  inline void add_coverage (set_t *glyphs) const
   {
+    const Coverage *last = NULL;
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++) {
+      const Coverage *c = &get_subtable (i).get_coverage (get_type ());
+      if (c != last) {
+        c->add_coverage (glyphs);
+        last = c;
+      }
+    }
+  }
+
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    if (!c->digest.may_have (c->first)) return false;
     unsigned int lookup_type = get_type ();
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++)
-      if (get_subtable (i).would_apply (glyph_id, lookup_type))
-	return true;
-    return false;
-  }
-  inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
-  {
-    unsigned int lookup_type = get_type ();
-    unsigned int count = get_subtable_count ();
-    for (unsigned int i = 0; i < count; i++)
-      if (get_subtable (i).would_apply (first, second, lookup_type))
+      if (get_subtable (i).would_apply (c, lookup_type))
 	return true;
     return false;
   }
 
   inline bool apply_once (hb_apply_context_t *c) const
   {
     unsigned int lookup_type = get_type ();
 
-    if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property))
+    if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props, &c->property))
       return false;
 
-    if (unlikely (lookup_type == SubstLookupSubTable::Extension))
-    {
-      /* The spec says all subtables should have the same type.
-       * This is specially important if one has a reverse type!
-       *
-       * This is rather slow to do this here for every glyph,
-       * but it's easiest, and who uses extension lookups anyway?!*/
-      unsigned int type = get_subtable(0).u.extension.get_type ();
-      unsigned int count = get_subtable_count ();
-      for (unsigned int i = 1; i < count; i++)
-        if (get_subtable(i).u.extension.get_type () != type)
-	  return false;
-    }
-
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++)
       if (get_subtable (i).apply (c, lookup_type))
 	return true;
 
     return false;
   }
 
@@ -1087,71 +1203,97 @@ struct SubstLookup : Lookup
 
     c->set_lookup (*this);
 
     if (likely (!is_reverse ()))
     {
 	/* in/out forward substitution */
 	c->buffer->clear_output ();
 	c->buffer->idx = 0;
+
 	while (c->buffer->idx < c->buffer->len)
 	{
-	  if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
+	  if ((c->buffer->cur().mask & c->lookup_mask) &&
+	      c->digest.may_have (c->buffer->cur().codepoint) &&
+	      apply_once (c))
 	    ret = true;
 	  else
 	    c->buffer->next_glyph ();
-
 	}
 	if (ret)
 	  c->buffer->swap_buffers ();
     }
     else
     {
 	/* in-place backward substitution */
 	c->buffer->idx = c->buffer->len - 1;
 	do
 	{
-	  if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
+	  if ((c->buffer->cur().mask & c->lookup_mask) &&
+	      c->digest.may_have (c->buffer->cur().codepoint) &&
+	      apply_once (c))
 	    ret = true;
 	  else
 	    c->buffer->idx--;
 
 	}
 	while ((int) c->buffer->idx >= 0);
     }
 
     return ret;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
     OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
-    return TRACE_RETURN (list.sanitize (c, this, get_type ()));
+    if (unlikely (!list.sanitize (c, this, get_type ()))) return TRACE_RETURN (false);
+
+    if (unlikely (get_type () == SubstLookupSubTable::Extension))
+    {
+      /* The spec says all subtables of an Extension lookup should
+       * 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. */
+      unsigned int type = get_subtable (0).u.extension.get_type ();
+      unsigned int count = get_subtable_count ();
+      for (unsigned int i = 1; i < count; i++)
+        if (get_subtable (i).u.extension.get_type () != type)
+	  return TRACE_RETURN (false);
+    }
+    return TRACE_RETURN (true);
   }
 };
 
 typedef OffsetListOf<SubstLookup> SubstLookupList;
 
 /*
  * GSUB -- The Glyph Substitution Table
  */
 
 struct GSUB : GSUBGPOS
 {
   static const hb_tag_t Tag	= HB_OT_TAG_GSUB;
 
   inline const SubstLookup& get_lookup (unsigned int i) const
   { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
 
+  template <typename set_t>
+  inline void add_coverage (set_t *glyphs, unsigned int lookup_index) const
+  { get_lookup (lookup_index).add_coverage (glyphs); }
+
+  inline bool would_substitute_lookup (hb_would_apply_context_t *c, unsigned int lookup_index) const
+  { return get_lookup (lookup_index).would_apply (c); }
+
   inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
   { return get_lookup (lookup_index).apply_string (c); }
 
-  static inline void substitute_start (hb_buffer_t *buffer);
-  static inline void substitute_finish (hb_buffer_t *buffer);
+  static inline void substitute_start (hb_font_t *font, hb_buffer_t *buffer);
+  static inline void substitute_finish (hb_font_t *font, hb_buffer_t *buffer);
 
   inline void closure_lookup (hb_closure_context_t *c,
 			      unsigned int          lookup_index) const
   { return get_lookup (lookup_index).closure (c); }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
@@ -1159,48 +1301,51 @@ struct GSUB : GSUBGPOS
     return TRACE_RETURN (list.sanitize (c, this));
   }
   public:
   DEFINE_SIZE_STATIC (10);
 };
 
 
 void
-GSUB::substitute_start (hb_buffer_t *buffer)
+GSUB::substitute_start (hb_font_t *font, hb_buffer_t *buffer)
 {
-  HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
+  HB_BUFFER_ALLOCATE_VAR (buffer, glyph_props);
   HB_BUFFER_ALLOCATE_VAR (buffer, lig_props);
   HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
 
+  const GDEF &gdef = *hb_ot_layout_from_face (font->face)->gdef;
   unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    buffer->info[i].props_cache() = buffer->info[i].lig_props() = buffer->info[i].syllable() = 0;
+  for (unsigned int i = 0; i < count; i++) {
+    buffer->info[i].lig_props() = buffer->info[i].syllable() = 0;
+    buffer->info[i].glyph_props() = gdef.get_glyph_props (buffer->info[i].codepoint);
+  }
 }
 
 void
-GSUB::substitute_finish (hb_buffer_t *buffer HB_UNUSED)
+GSUB::substitute_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED)
 {
 }
 
 
 /* Out-of-class implementation for methods recursing */
 
 inline void ExtensionSubst::closure (hb_closure_context_t *c) const
 {
   get_subtable ().closure (c, get_type ());
 }
 
-inline bool ExtensionSubst::would_apply (hb_codepoint_t glyph_id) const
+inline const Coverage & ExtensionSubst::get_coverage (void) const
 {
-  return get_subtable ().would_apply (glyph_id, get_type ());
+  return get_subtable ().get_coverage (get_type ());
 }
 
-inline bool ExtensionSubst::would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+inline bool ExtensionSubst::would_apply (hb_would_apply_context_t *c) const
 {
-  return get_subtable ().would_apply (first, second, get_type ());
+  return get_subtable ().would_apply (c, get_type ());
 }
 
 inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
 {
   TRACE_APPLY ();
   return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
 }
 
@@ -1218,30 +1363,30 @@ inline bool ExtensionSubst::is_reverse (
   unsigned int type = get_type ();
   if (unlikely (type == SubstLookupSubTable::Extension))
     return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
   return SubstLookup::lookup_type_is_reverse (type);
 }
 
 static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index)
 {
-  const GSUB &gsub = *(c->face->ot_layout->gsub);
+  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
   const SubstLookup &l = gsub.get_lookup (lookup_index);
 
   if (unlikely (c->nesting_level_left == 0))
     return;
 
   c->nesting_level_left--;
   l.closure (c);
   c->nesting_level_left++;
 }
 
 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
 {
-  const GSUB &gsub = *(c->face->ot_layout->gsub);
+  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
   const SubstLookup &l = gsub.get_lookup (lookup_index);
 
   if (unlikely (c->nesting_level_left == 0))
     return false;
 
   hb_apply_context_t new_c (*c);
   new_c.nesting_level_left--;
   new_c.set_lookup (l);
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -26,106 +26,114 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
 #define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
 
 #include "hb-buffer-private.hh"
 #include "hb-ot-layout-gdef-table.hh"
-
-
-
-/* unique ligature id */
-/* component number in the ligature (0 = base) */
-static inline void
-set_lig_props (hb_glyph_info_t &info, unsigned int lig_id, unsigned int lig_comp)
-{
-  info.lig_props() = (lig_id << 4) | (lig_comp & 0x0F);
-}
-static inline unsigned int
-get_lig_id (hb_glyph_info_t &info)
-{
-  return info.lig_props() >> 4;
-}
-static inline unsigned int
-get_lig_comp (hb_glyph_info_t &info)
-{
-  return info.lig_props() & 0x0F;
-}
-
-static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
-  uint8_t lig_id = buffer->next_serial () & 0x0F;
-  if (unlikely (!lig_id))
-    lig_id = allocate_lig_id (buffer); /* in case of overflow */
-  return lig_id;
-}
+#include "hb-set-private.hh"
 
 
 
 #ifndef HB_DEBUG_CLOSURE
 #define HB_DEBUG_CLOSURE (HB_DEBUG+0)
 #endif
 
 #define TRACE_CLOSURE() \
 	hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, "");
 
 
-/* TODO Add TRACE_RETURN annotation for would_apply */
+/* TODO Add TRACE_RETURN annotation to gsub. */
+#ifndef HB_DEBUG_WOULD_APPLY
+#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
+#endif
+
+#define TRACE_WOULD_APPLY() \
+	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY> trace (&c->debug_depth, "WOULD_APPLY", this, HB_FUNC, "first %u second %u", c->first, c->second);
 
 
 struct hb_closure_context_t
 {
   hb_face_t *face;
   hb_set_t *glyphs;
   unsigned int nesting_level_left;
   unsigned int debug_depth;
 
 
   hb_closure_context_t (hb_face_t *face_,
 			hb_set_t *glyphs_,
 		        unsigned int nesting_level_left_ = MAX_NESTING_LEVEL) :
-			  face (face_), glyphs (glyphs_),
+			  face (face_),
+			  glyphs (glyphs_),
 			  nesting_level_left (nesting_level_left_),
 			  debug_depth (0) {}
 };
 
 
 
+
+struct hb_would_apply_context_t
+{
+  hb_face_t *face;
+  hb_codepoint_t first;
+  hb_codepoint_t second;
+  unsigned int len;
+  const hb_set_digest_t digest;
+  unsigned int debug_depth;
+
+  hb_would_apply_context_t (hb_face_t *face_,
+			    hb_codepoint_t first_,
+			    hb_codepoint_t second_,
+			    const hb_set_digest_t *digest_
+			    ) :
+			      face (face_),
+			      first (first_), second (second_), len (second == (hb_codepoint_t) -1 ? 1 : 2),
+			      digest (*digest_),
+			      debug_depth (0) {};
+};
+
+
 #ifndef HB_DEBUG_APPLY
 #define HB_DEBUG_APPLY (HB_DEBUG+0)
 #endif
 
 #define TRACE_APPLY() \
-	hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "idx %d codepoint %u", c->buffer->cur().codepoint);
-
+	hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint);
 
 
 struct hb_apply_context_t
 {
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t *buffer;
   hb_direction_t direction;
   hb_mask_t lookup_mask;
   unsigned int nesting_level_left;
   unsigned int lookup_props;
   unsigned int property; /* propety of first glyph */
   unsigned int debug_depth;
+  const GDEF &gdef;
+  bool has_glyph_classes;
+  const hb_set_digest_t digest;
 
 
   hb_apply_context_t (hb_font_t *font_,
-		      hb_face_t *face_,
 		      hb_buffer_t *buffer_,
-		      hb_mask_t lookup_mask_) :
-			font (font_), face (face_), buffer (buffer_),
+		      hb_mask_t lookup_mask_,
+		      const hb_set_digest_t *digest_) :
+			font (font_), face (font->face), buffer (buffer_),
 			direction (buffer_->props.direction),
 			lookup_mask (lookup_mask_),
 			nesting_level_left (MAX_NESTING_LEVEL),
-			lookup_props (0), property (0), debug_depth (0) {}
+			lookup_props (0), property (0), debug_depth (0),
+			gdef (*hb_ot_layout_from_face (face)->gdef),
+			has_glyph_classes (gdef.has_glyph_classes ()),
+			digest (*digest_) {}
 
   void set_lookup (const Lookup &l) {
     lookup_props = l.get_props ();
   }
 
   struct mark_skipping_forward_iterator_t
   {
     inline mark_skipping_forward_iterator_t (hb_apply_context_t *c_,
@@ -139,36 +147,40 @@ struct hb_apply_context_t
       mask = context_match ? -1 : c->lookup_mask;
       syllable = context_match ? 0 : c->buffer->cur().syllable ();
       end = c->buffer->len;
     }
     inline bool has_no_chance (void) const
     {
       return unlikely (num_items && idx + num_items >= end);
     }
+    inline void reject (void)
+    {
+      num_items++;
+    }
     inline bool next (unsigned int *property_out,
-		      unsigned int lookup_props)
+		      unsigned int  lookup_props)
     {
       assert (num_items > 0);
       do
       {
 	if (has_no_chance ())
 	  return false;
 	idx++;
-      } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[idx], lookup_props, property_out));
+      } 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);
     }
 
     unsigned int idx;
-    private:
+    protected:
     hb_apply_context_t *c;
     unsigned int num_items;
     hb_mask_t mask;
     uint8_t syllable;
     unsigned int end;
   };
 
   struct mark_skipping_backward_iterator_t
@@ -184,62 +196,147 @@ struct hb_apply_context_t
       num_items = num_items_;
       mask = mask_ ? mask_ : c->lookup_mask;
       syllable = match_syllable_ ? c->buffer->cur().syllable () : 0;
     }
     inline bool has_no_chance (void) const
     {
       return unlikely (idx < num_items);
     }
+    inline void reject (void)
+    {
+      num_items++;
+    }
     inline bool prev (unsigned int *property_out,
-		      unsigned int lookup_props)
+		      unsigned int  lookup_props)
     {
       assert (num_items > 0);
       do
       {
 	if (has_no_chance ())
 	  return false;
 	idx--;
-      } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[idx], lookup_props, property_out));
+      } 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);
     }
 
     unsigned int idx;
-    private:
+    protected:
     hb_apply_context_t *c;
     unsigned int num_items;
     hb_mask_t mask;
     uint8_t syllable;
   };
 
-  inline bool should_mark_skip_current_glyph (void) const
+  inline bool
+  match_properties_mark (hb_codepoint_t  glyph,
+			 unsigned int    glyph_props,
+			 unsigned int    lookup_props) const
+  {
+    /* If using mark filtering sets, the high short of
+     * lookup_props has the set index.
+     */
+    if (lookup_props & LookupFlag::UseMarkFilteringSet)
+      return gdef.mark_set_covers (lookup_props >> 16, glyph);
+
+    /* The second byte of lookup_props has the meaning
+     * "ignore marks of attachment type different than
+     * the attachment type specified."
+     */
+    if (lookup_props & LookupFlag::MarkAttachmentType)
+      return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType);
+
+    return true;
+  }
+
+  inline bool
+  match_properties (hb_codepoint_t  glyph,
+		    unsigned int    glyph_props,
+		    unsigned int    lookup_props) const
   {
-    return _hb_ot_layout_skip_mark (face, &buffer->cur(), lookup_props, NULL);
+    /* Not covered, if, for example, glyph class is ligature and
+     * lookup_props includes LookupFlags::IgnoreLigatures
+     */
+    if (glyph_props & lookup_props & LookupFlag::IgnoreFlags)
+      return false;
+
+    if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
+      return match_properties_mark (glyph, glyph_props, lookup_props);
+
+    return true;
+  }
+
+  inline bool
+  check_glyph_property (hb_glyph_info_t *info,
+			unsigned int  lookup_props,
+			unsigned int *property_out) 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_CLASS_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))
+      buffer->cur().glyph_props() = gdef.get_glyph_props (glyph_index);
+    else if (class_guess)
+      buffer->cur().glyph_props() = class_guess;
+  }
+
+  inline void output_glyph (hb_codepoint_t glyph_index,
+			    unsigned int class_guess = 0) const
+  {
+    set_class (glyph_index, class_guess);
+    buffer->output_glyph (glyph_index);
+  }
   inline void replace_glyph (hb_codepoint_t glyph_index,
-			     unsigned int klass = 0) const
+			     unsigned int class_guess = 0) const
   {
-    buffer->cur().props_cache() = klass; /*XXX if has gdef? */
+    set_class (glyph_index, class_guess);
     buffer->replace_glyph (glyph_index);
   }
-  inline void replace_glyphs_be16 (unsigned int num_in,
-				   unsigned int num_out,
-				   const uint16_t *glyph_data_be,
-				   unsigned int klass = 0) const
+  inline void replace_glyph_inplace (hb_codepoint_t glyph_index,
+				     unsigned int class_guess = 0) const
   {
-    buffer->cur().props_cache() = klass; /* XXX if has gdef? */
-    buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
+    set_class (glyph_index, class_guess);
+    buffer->cur().codepoint = glyph_index;
   }
 };
 
 
 
 typedef bool (*intersects_func_t) (hb_set_t *glyphs, const USHORT &value, const void *data);
 typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
 typedef void (*closure_lookup_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
@@ -295,16 +392,31 @@ static inline bool match_class (hb_codep
 }
 static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
 {
   const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
   return (data+coverage).get_coverage (glyph_id) != NOT_COVERED;
 }
 
 
+static inline bool would_match_input (hb_would_apply_context_t *c,
+				      unsigned int count, /* Including the first glyph (not matched) */
+				      const USHORT input[], /* Array of input values--start with second glyph */
+				      match_func_t match_func,
+				      const void *match_data)
+{
+  if (count != c->len)
+    return false;
+
+  for (unsigned int i = 1; i < count; i++)
+    if (likely (!match_func (c->second, input[i - 1], match_data)))
+      return false;
+
+  return true;
+}
 static inline bool match_input (hb_apply_context_t *c,
 				unsigned int count, /* Including the first glyph (not matched) */
 				const USHORT input[], /* Array of input values--start with second glyph */
 				match_func_t match_func,
 				const void *match_data,
 				unsigned int *end_offset = NULL)
 {
   hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
@@ -483,16 +595,27 @@ static inline void context_closure_looku
 			inputCount ? inputCount - 1 : 0, input,
 			lookup_context.funcs.intersects, lookup_context.intersects_data))
     closure_lookup (c,
 		    lookupCount, lookupRecord,
 		    lookup_context.funcs.closure);
 }
 
 
+static inline bool context_would_apply_lookup (hb_would_apply_context_t *c,
+					       unsigned int inputCount, /* Including the first glyph (not matched) */
+					       const USHORT input[], /* Array of input values--start with second glyph */
+					       unsigned int lookupCount,
+					       const LookupRecord lookupRecord[],
+					       ContextApplyLookupContext &lookup_context)
+{
+  return would_match_input (c,
+			    inputCount, input,
+			    lookup_context.funcs.match, lookup_context.match_data);
+}
 static inline bool context_apply_lookup (hb_apply_context_t *c,
 					 unsigned int inputCount, /* Including the first glyph (not matched) */
 					 const USHORT input[], /* Array of input values--start with second glyph */
 					 unsigned int lookupCount,
 					 const LookupRecord lookupRecord[],
 					 ContextApplyLookupContext &lookup_context)
 {
   return match_input (c,
@@ -515,16 +638,23 @@ struct Rule
     TRACE_CLOSURE ();
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
     context_closure_lookup (c,
 			    inputCount, input,
 			    lookupCount, lookupRecord,
 			    lookup_context);
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
+  {
+    TRACE_WOULD_APPLY ();
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
+    return TRACE_RETURN (context_would_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
+  }
+
   inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY ();
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
     return TRACE_RETURN (context_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
   }
 
   public:
@@ -532,17 +662,17 @@ struct Rule
     TRACE_SANITIZE ();
     return inputCount.sanitize (c)
 	&& lookupCount.sanitize (c)
 	&& c->check_range (input,
 			   input[0].static_size * inputCount
 			   + lookupRecordX[0].static_size * lookupCount);
   }
 
-  private:
+  protected:
   USHORT	inputCount;		/* Total number of glyphs in input
 					 * glyph sequence--includes the first
 					 * glyph */
   USHORT	lookupCount;		/* Number of LookupRecords */
   USHORT	input[VAR];		/* Array of match inputs--start with
 					 * second glyph */
   LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
 					 * design order */
@@ -555,16 +685,28 @@ struct RuleSet
   inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE ();
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       (this+rule[i]).closure (c, lookup_context);
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
+  {
+    TRACE_WOULD_APPLY ();
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+    {
+      if ((this+rule[i]).would_apply (c, lookup_context))
+        return TRACE_RETURN (true);
+    }
+    return TRACE_RETURN (false);
+  }
+
   inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY ();
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
     {
       if ((this+rule[i]).apply (c, lookup_context))
         return TRACE_RETURN (true);
@@ -572,17 +714,17 @@ struct RuleSet
     return TRACE_RETURN (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (rule.sanitize (c, this));
   }
 
-  private:
+  protected:
   OffsetArrayOf<Rule>
 		rule;			/* Array of Rule tables
 					 * ordered by preference */
   public:
   DEFINE_SIZE_ARRAY (2, rule);
 };
 
 
@@ -606,16 +748,28 @@ struct ContextFormat1
     unsigned int count = ruleSet.len;
     for (unsigned int i = 0; i < count; i++)
       if (cov.intersects_coverage (c->glyphs, i)) {
 	const RuleSet &rule_set = this+ruleSet[i];
 	rule_set.closure (c, lookup_context);
       }
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    TRACE_WOULD_APPLY ();
+
+    const RuleSet &rule_set = this+ruleSet[(this+coverage) (c->first)];
+    struct ContextApplyLookupContext lookup_context = {
+      {match_glyph, NULL},
+      NULL
+    };
+    return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED))
       return TRACE_RETURN (false);
 
     const RuleSet &rule_set = this+ruleSet[index];
@@ -626,17 +780,17 @@ struct ContextFormat1
     return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetArrayOf<RuleSet>
 		ruleSet;		/* Array of RuleSet tables
 					 * ordered by Coverage Index */
   public:
@@ -666,16 +820,30 @@ struct ContextFormat2
     unsigned int count = ruleSet.len;
     for (unsigned int i = 0; i < count; i++)
       if (class_def.intersects_class (c->glyphs, i)) {
 	const RuleSet &rule_set = this+ruleSet[i];
 	rule_set.closure (c, lookup_context);
       }
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    TRACE_WOULD_APPLY ();
+
+    const ClassDef &class_def = this+classDef;
+    unsigned int index = class_def (c->first);
+    const RuleSet &rule_set = this+ruleSet[index];
+    struct ContextApplyLookupContext lookup_context = {
+      {match_class, NULL},
+      &class_def
+    };
+    return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const ClassDef &class_def = this+classDef;
     index = class_def (c->buffer->cur().codepoint);
@@ -687,17 +855,17 @@ struct ContextFormat2
     return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetTo<ClassDef>
 		classDef;		/* Offset to glyph ClassDef table--from
 					 * beginning of table */
   OffsetArrayOf<RuleSet>
@@ -726,16 +894,28 @@ struct ContextFormat3
       this
     };
     context_closure_lookup (c,
 			    glyphCount, (const USHORT *) (coverage + 1),
 			    lookupCount, lookupRecord,
 			    lookup_context);
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    TRACE_WOULD_APPLY ();
+
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
+    struct ContextApplyLookupContext lookup_context = {
+      {match_coverage, NULL},
+      this
+    };
+    return TRACE_RETURN (context_would_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context));
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage[0]) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
     struct ContextApplyLookupContext lookup_context = {
@@ -744,24 +924,25 @@ struct ContextFormat3
     };
     return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (!c->check_struct (this)) return TRACE_RETURN (false);
     unsigned int count = glyphCount;
+    if (unlikely (!glyphCount)) return TRACE_RETURN (false);
     if (!c->check_array (coverage, coverage[0].static_size, count)) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < count; i++)
       if (!coverage[i].sanitize (c, this)) return TRACE_RETURN (false);
     LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
     return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 3 */
   USHORT	glyphCount;		/* Number of glyphs in the input glyph
 					 * sequence */
   USHORT	lookupCount;		/* Number of LookupRecords */
   OffsetTo<Coverage>
 		coverage[VAR];		/* Array of offsets to Coverage
 					 * table in glyph sequence order */
   LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
@@ -780,16 +961,36 @@ struct Context
     switch (u.format) {
     case 1: u.format1.closure (c, closure_func); break;
     case 2: u.format2.closure (c, closure_func); break;
     case 3: u.format3.closure (c, closure_func); break;
     default:                                     break;
     }
   }
 
+  inline const Coverage &get_coverage (void) const
+  {
+    switch (u.format) {
+    case 1: return this + u.format1.coverage;
+    case 2: return this + u.format2.coverage;
+    case 3: return this + u.format3.coverage[0];
+    default:return Null(Coverage);
+    }
+  }
+
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (c);
+    case 2: return u.format2.would_apply (c);
+    case 3: return u.format3.would_apply (c);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c, apply_func));
     case 2: return TRACE_RETURN (u.format2.apply (c, apply_func));
     case 3: return TRACE_RETURN (u.format3.apply (c, apply_func));
     default:return TRACE_RETURN (false);
@@ -802,17 +1003,17 @@ struct Context
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     case 2: return TRACE_RETURN (u.format2.sanitize (c));
     case 3: return TRACE_RETURN (u.format3.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   ContextFormat1	format1;
   ContextFormat2	format2;
   ContextFormat3	format3;
   } u;
 };
 
@@ -851,35 +1052,53 @@ static inline void chain_context_closure
   && intersects_array (c,
 		       lookaheadCount, lookahead,
 		       lookup_context.funcs.intersects, lookup_context.intersects_data[2]))
     closure_lookup (c,
 		    lookupCount, lookupRecord,
 		    lookup_context.funcs.closure);
 }
 
+static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c,
+						     unsigned int backtrackCount,
+						     const USHORT backtrack[],
+						     unsigned int inputCount, /* Including the first glyph (not matched) */
+						     const USHORT input[], /* Array of input values--start with second glyph */
+						     unsigned int lookaheadCount,
+						     const USHORT lookahead[],
+						     unsigned int lookupCount,
+						     const LookupRecord lookupRecord[],
+						     ChainContextApplyLookupContext &lookup_context)
+{
+  return !backtrackCount
+      && !lookaheadCount
+      && would_match_input (c,
+			    inputCount, input,
+			    lookup_context.funcs.match, lookup_context.match_data[1]);
+}
+
 static inline bool chain_context_apply_lookup (hb_apply_context_t *c,
 					       unsigned int backtrackCount,
 					       const USHORT backtrack[],
 					       unsigned int inputCount, /* Including the first glyph (not matched) */
 					       const USHORT input[], /* Array of input values--start with second glyph */
 					       unsigned int lookaheadCount,
 					       const USHORT lookahead[],
 					       unsigned int lookupCount,
 					       const LookupRecord lookupRecord[],
 					       ChainContextApplyLookupContext &lookup_context)
 {
   unsigned int lookahead_offset;
-  return match_backtrack (c,
-			  backtrackCount, backtrack,
-			  lookup_context.funcs.match, lookup_context.match_data[0])
-      && match_input (c,
+  return match_input (c,
 		      inputCount, input,
 		      lookup_context.funcs.match, lookup_context.match_data[1],
 		      &lookahead_offset)
+      && match_backtrack (c,
+			  backtrackCount, backtrack,
+			  lookup_context.funcs.match, lookup_context.match_data[0])
       && match_lookahead (c,
 			  lookaheadCount, lookahead,
 			  lookup_context.funcs.match, lookup_context.match_data[2],
 			  lookahead_offset)
       && apply_lookup (c,
 		       inputCount,
 		       lookupCount, lookupRecord,
 		       lookup_context.funcs.apply);
@@ -900,16 +1119,29 @@ struct ChainRule
     chain_context_closure_lookup (c,
 				  backtrack.len, backtrack.array,
 				  input.len, input.array,
 				  lookahead.len, lookahead.array,
 				  lookup.len, lookup.array,
 				  lookup_context);
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
+  {
+    TRACE_WOULD_APPLY ();
+    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
+    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    return TRACE_RETURN (chain_context_would_apply_lookup (c,
+							   backtrack.len, backtrack.array,
+							   input.len, input.array,
+							   lookahead.len, lookahead.array, lookup.len,
+							   lookup.array, lookup_context));
+  }
+
   inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY ();
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     return TRACE_RETURN (chain_context_apply_lookup (c,
 						     backtrack.len, backtrack.array,
@@ -925,17 +1157,17 @@ struct ChainRule
     HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     if (!input.sanitize (c)) return TRACE_RETURN (false);
     ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
     if (!lookahead.sanitize (c)) return TRACE_RETURN (false);
     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     return TRACE_RETURN (lookup.sanitize (c));
   }
 
-  private:
+  protected:
   ArrayOf<USHORT>
 		backtrack;		/* Array of backtracking values
 					 * (to be matched before the input
 					 * sequence) */
   HeadlessArrayOf<USHORT>
 		inputX;			/* Array of input values (start with
 					 * second glyph) */
   ArrayOf<USHORT>
@@ -953,33 +1185,44 @@ struct ChainRuleSet
   inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE ();
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       (this+rule[i]).closure (c, lookup_context);
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
+  {
+    TRACE_WOULD_APPLY ();
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+      if ((this+rule[i]).would_apply (c, lookup_context))
+        return TRACE_RETURN (true);
+
+    return TRACE_RETURN (false);
+  }
+
   inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY ();
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       if ((this+rule[i]).apply (c, lookup_context))
         return TRACE_RETURN (true);
 
     return TRACE_RETURN (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (rule.sanitize (c, this));
   }
 
-  private:
+  protected:
   OffsetArrayOf<ChainRule>
 		rule;			/* Array of ChainRule tables
 					 * ordered by preference */
   public:
   DEFINE_SIZE_ARRAY (2, rule);
 };
 
 struct ChainContextFormat1
@@ -1001,16 +1244,28 @@ struct ChainContextFormat1
     unsigned int count = ruleSet.len;
     for (unsigned int i = 0; i < count; i++)
       if (cov.intersects_coverage (c->glyphs, i)) {
 	const ChainRuleSet &rule_set = this+ruleSet[i];
 	rule_set.closure (c, lookup_context);
       }
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    TRACE_WOULD_APPLY ();
+
+    const ChainRuleSet &rule_set = this+ruleSet[(this+coverage) (c->first)];
+    struct ChainContextApplyLookupContext lookup_context = {
+      {match_glyph, NULL},
+      {NULL, NULL, NULL}
+    };
+    return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
@@ -1020,17 +1275,17 @@ struct ChainContextFormat1
     return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetArrayOf<ChainRuleSet>
 		ruleSet;		/* Array of ChainRuleSet tables
 					 * ordered by Coverage Index */
   public:
@@ -1063,16 +1318,31 @@ struct ChainContextFormat2
     unsigned int count = ruleSet.len;
     for (unsigned int i = 0; i < count; i++)
       if (input_class_def.intersects_class (c->glyphs, i)) {
 	const ChainRuleSet &rule_set = this+ruleSet[i];
 	rule_set.closure (c, lookup_context);
       }
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    TRACE_WOULD_APPLY ();
+
+    const ClassDef &input_class_def = this+inputClassDef;
+
+    unsigned int index = input_class_def (c->first);
+    const ChainRuleSet &rule_set = this+ruleSet[index];
+    struct ChainContextApplyLookupContext lookup_context = {
+      {match_class, NULL},
+      {NULL, &input_class_def, NULL}
+    };
+    return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const ClassDef &backtrack_class_def = this+backtrackClassDef;
     const ClassDef &input_class_def = this+inputClassDef;
@@ -1091,17 +1361,17 @@ struct ChainContextFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && backtrackClassDef.sanitize (c, this) &&
 			 inputClassDef.sanitize (c, this) && lookaheadClassDef.sanitize (c, this) &&
 			 ruleSet.sanitize (c, this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetTo<ClassDef>
 		backtrackClassDef;	/* Offset to glyph ClassDef table
 					 * containing backtrack sequence
 					 * data--from beginning of table */
@@ -1143,16 +1413,40 @@ struct ChainContextFormat3
     chain_context_closure_lookup (c,
 				  backtrack.len, (const USHORT *) backtrack.array,
 				  input.len, (const USHORT *) input.array + 1,
 				  lookahead.len, (const USHORT *) lookahead.array,
 				  lookup.len, lookup.array,
 				  lookup_context);
   }
 
+  inline const Coverage &get_coverage (void) const
+  {
+    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+    return this+input[0];
+  }
+
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    TRACE_WOULD_APPLY ();
+
+    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    struct ChainContextApplyLookupContext lookup_context = {
+      {match_coverage, NULL},
+      {this, this, this}
+    };
+    return TRACE_RETURN (chain_context_would_apply_lookup (c,
+							   backtrack.len, (const USHORT *) backtrack.array,
+							   input.len, (const USHORT *) input.array + 1,
+							   lookahead.len, (const USHORT *) lookahead.array,
+							   lookup.len, lookup.array, lookup_context));
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
 
     unsigned int index = (this+input[0]) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
@@ -1169,23 +1463,24 @@ struct ChainContextFormat3
 						     lookup.len, lookup.array, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     if (!input.sanitize (c, this)) return TRACE_RETURN (false);
+    if (unlikely (!input.len)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
     if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false);
     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     return TRACE_RETURN (lookup.sanitize (c));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier--format = 3 */
   OffsetArrayOf<Coverage>
 		backtrack;		/* Array of coverage tables
 					 * in backtracking sequence, in  glyph
 					 * sequence order */
   OffsetArrayOf<Coverage>
 		inputX		;	/* Array of coverage
 					 * tables in input sequence, in glyph
@@ -1211,16 +1506,36 @@ struct ChainContext
     switch (u.format) {
     case 1: u.format1.closure (c, closure_func); break;
     case 2: u.format2.closure (c, closure_func); break;
     case 3: u.format3.closure (c, closure_func); break;
     default:                                     break;
     }
   }
 
+  inline const Coverage &get_coverage (void) const
+  {
+    switch (u.format) {
+    case 1: return this + u.format1.coverage;
+    case 2: return this + u.format2.coverage;
+    case 3: return u.format3.get_coverage ();
+    default:return Null(Coverage);
+    }
+  }
+
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (c);
+    case 2: return u.format2.would_apply (c);
+    case 3: return u.format3.would_apply (c);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c, apply_func));
     case 2: return TRACE_RETURN (u.format2.apply (c, apply_func));
     case 3: return TRACE_RETURN (u.format3.apply (c, apply_func));
     default:return TRACE_RETURN (false);
@@ -1233,17 +1548,17 @@ struct ChainContext
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     case 2: return TRACE_RETURN (u.format2.sanitize (c));
     case 3: return TRACE_RETURN (u.format3.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;	/* Format identifier */
   ChainContextFormat1	format1;
   ChainContextFormat2	format2;
   ChainContextFormat3	format3;
   } u;
 };
 
@@ -1256,17 +1571,17 @@ struct ExtensionFormat1
   inline unsigned int get_type (void) const { return extensionLookupType; }
   inline unsigned int get_offset (void) const { return extensionOffset; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this));
   }
 
-  private:
+  protected:
   USHORT	format;			/* Format identifier. Set to 1. */
   USHORT	extensionLookupType;	/* Lookup type of subtable referenced
 					 * by ExtensionOffset (i.e. the
 					 * extension subtable). */
   ULONG		extensionOffset;	/* Offset to the extension subtable,
 					 * of lookup type subtable. */
   public:
   DEFINE_SIZE_STATIC (8);
@@ -1293,17 +1608,17 @@ struct Extension
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
 
-  private:
+  protected:
   union {
   USHORT		format;		/* Format identifier */
   ExtensionFormat1	format1;
   } u;
 };
 
 
 /*
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -1,10 +1,11 @@
 /*
  * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -17,76 +18,192 @@
  *
  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_LAYOUT_PRIVATE_HH
 #define HB_OT_LAYOUT_PRIVATE_HH
 
 #include "hb-private.hh"
 
 #include "hb-ot-layout.h"
 
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-set-private.hh"
 
 
+/* buffer var allocations, used during the GSUB/GPOS processing */
+#define glyph_props()		var1.u16[1] /* GDEF glyph properties */
+#define syllable()		var2.u8[0] /* GSUB/GPOS shaping boundaries */
+#define lig_props()		var2.u8[1] /* GSUB/GPOS ligature tracking */
+
+#define hb_ot_layout_from_face(face) ((hb_ot_layout_t *) face->shaper_data.ot)
 
 /*
  * GDEF
  */
 
-/* XXX cleanup */
 typedef enum {
   HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED	= 0x0001,
   HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH	= 0x0002,
   HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE	= 0x0004,
   HB_OT_LAYOUT_GLYPH_CLASS_MARK		= 0x0008,
   HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT	= 0x0010
 } hb_ot_layout_glyph_class_t;
 
 
-HB_INTERNAL unsigned int
-_hb_ot_layout_get_glyph_property (hb_face_t       *face,
-				  hb_glyph_info_t *info);
+
+/*
+ * GSUB/GPOS
+ */
+
+/* lig_id / lig_comp
+ *
+ * When a ligature is formed:
+ *
+ *   - The ligature glyph and any marks in between all the same newly allocated
+ *     lig_id,
+ *   - The ligature glyph will get lig_num_comps set to the number of components
+ *   - The marks get lig_comp > 0, reflecting which component of the ligature
+ *     they were applied to.
+ *   - This is used in GPOS to attach marks to the right component of a ligature
+ *     in MarkLigPos.
+ *
+ * When a multiple-substitution is done:
+ *
+ *   - All resulting glyphs will have lig_id = 0,
+ *   - The resulting glyphs will have lig_comp = 0, 1, 2, ... respectively.
+ *   - This is used in GPOS to attach marks to the first component of a
+ *     multiple substitution in MarkBasePos.
+ *
+ * The numbers are also used in GPOS to do mark-to-mark positioning only
+ * to marks that belong to the same component of a ligature in MarkMarPos.
+ */
+#define IS_LIG_BASE 0x10
+static inline void
+set_lig_props_for_ligature (hb_glyph_info_t &info, unsigned int lig_id, unsigned int lig_num_comps)
+{
+  info.lig_props() = (lig_id << 5) | IS_LIG_BASE | (lig_num_comps & 0x0F);
+}
+static inline void
+set_lig_props_for_mark (hb_glyph_info_t &info, unsigned int lig_id, unsigned int lig_comp)
+{
+  info.lig_props() = (lig_id << 5) | (lig_comp & 0x0F);
+}
+static inline void
+set_lig_props_for_component (hb_glyph_info_t &info, unsigned int comp)
+{
+  set_lig_props_for_mark (info, 0, comp);
+}
+
+static inline unsigned int
+get_lig_id (const hb_glyph_info_t &info)
+{
+  return info.lig_props() >> 5;
+}
+static inline bool
+is_a_ligature (const hb_glyph_info_t &info)
+{
+  return !!(info.lig_props() & IS_LIG_BASE);
+}
+static inline unsigned int
+get_lig_comp (const hb_glyph_info_t &info)
+{
+  if (is_a_ligature (info))
+    return 0;
+  else
+    return info.lig_props() & 0x0F;
+}
+static inline unsigned int
+get_lig_num_comps (const hb_glyph_info_t &info)
+{
+  if ((info.glyph_props() & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE) && is_a_ligature (info))
+    return info.lig_props() & 0x0F;
+  else
+    return 1;
+}
+
+static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
+  uint8_t lig_id = buffer->next_serial () & 0x07;
+  if (unlikely (!lig_id))
+    lig_id = allocate_lig_id (buffer); /* in case of overflow */
+  return lig_id;
+}
+
 
 HB_INTERNAL hb_bool_t
-_hb_ot_layout_check_glyph_property (hb_face_t    *face,
-				    hb_glyph_info_t *ginfo,
-				    unsigned int  lookup_props,
-				    unsigned int *property_out);
+hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
+					   const hb_codepoint_t *glyphs,
+					   unsigned int          glyphs_length,
+					   unsigned int          lookup_index);
+
+
+/* Should be called before all the substitute_lookup's are done. */
+HB_INTERNAL void
+hb_ot_layout_substitute_start (hb_font_t    *font,
+			       hb_buffer_t  *buffer);
 
 HB_INTERNAL hb_bool_t
-_hb_ot_layout_skip_mark (hb_face_t    *face,
-			 hb_glyph_info_t *ginfo,
-			 unsigned int  lookup_props,
-			 unsigned int *property_out);
+hb_ot_layout_substitute_lookup (hb_font_t    *font,
+				hb_buffer_t  *buffer,
+				unsigned int  lookup_index,
+				hb_mask_t     mask);
+
+/* Should be called after all the substitute_lookup's are done */
+HB_INTERNAL void
+hb_ot_layout_substitute_finish (hb_font_t    *font,
+				hb_buffer_t  *buffer);
+
+
+/* Should be called before all the position_lookup's are done.  Resets positions to zero. */
+HB_INTERNAL void
+hb_ot_layout_position_start (hb_font_t    *font,
+			     hb_buffer_t  *buffer);
+
+HB_INTERNAL hb_bool_t
+hb_ot_layout_position_lookup (hb_font_t    *font,
+			      hb_buffer_t  *buffer,
+			      unsigned int  lookup_index,
+			      hb_mask_t     mask);
+
+/* 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_ot_layout_t
  */
 
 struct hb_ot_layout_t
 {
   hb_blob_t *gdef_blob;
   hb_blob_t *gsub_blob;
   hb_blob_t *gpos_blob;
 
   const struct GDEF *gdef;
   const struct GSUB *gsub;
   const struct GPOS *gpos;
+
+  unsigned int gsub_lookup_count;
+  unsigned int gpos_lookup_count;
+
+  hb_set_digest_t *gsub_digests;
+  hb_set_digest_t *gpos_digests;
 };
 
 
 HB_INTERNAL hb_ot_layout_t *
 _hb_ot_layout_create (hb_face_t *face);
 
 HB_INTERNAL void
 _hb_ot_layout_destroy (hb_ot_layout_t *layout);
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -1,12 +1,13 @@
 /*
  * Copyright © 1998-2004  David Turner and Werner Lemberg
  * Copyright © 2006  Behdad Esfahbod
  * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -19,165 +20,113 @@
  *
  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-ot-layout-private.hh"
 
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
 #include "hb-ot-maxp-table.hh"
-#include "hb-ot-shape-private.hh"
 
 
 #include <stdlib.h>
 #include <string.h>
 
 
+HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
 
 hb_ot_layout_t *
 _hb_ot_layout_create (hb_face_t *face)
 {
-  /* TODO Remove this object altogether */
   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
+  if (unlikely (!layout))
+    return NULL;
 
   layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GDEF));
   layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob);
 
   layout->gsub_blob = Sanitizer<GSUB>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GSUB));
   layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob);
 
   layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GPOS));
   layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
 
+  layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
+  layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
+
+  layout->gsub_digests = (hb_set_digest_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_set_digest_t));
+  layout->gpos_digests = (hb_set_digest_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_set_digest_t));
+
+  if (unlikely ((layout->gsub_lookup_count && !layout->gsub_digests) ||
+		(layout->gpos_lookup_count && !layout->gpos_digests)))
+  {
+    _hb_ot_layout_destroy (layout);
+    return NULL;
+  }
+
+  for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
+    layout->gsub->add_coverage (&layout->gsub_digests[i], i);
+  for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
+    layout->gpos->add_coverage (&layout->gpos_digests[i], i);
+
   return layout;
 }
 
 void
 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
 {
   hb_blob_destroy (layout->gdef_blob);
   hb_blob_destroy (layout->gsub_blob);
   hb_blob_destroy (layout->gpos_blob);
 
+  free (layout->gsub_digests);
+  free (layout->gpos_digests);
+
   free (layout);
 }
 
 static inline const GDEF&
 _get_gdef (hb_face_t *face)
 {
-  return likely (face->ot_layout && face->ot_layout->gdef) ? *face->ot_layout->gdef : Null(GDEF);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GDEF);
+  return *hb_ot_layout_from_face (face)->gdef;
 }
 static inline const GSUB&
 _get_gsub (hb_face_t *face)
 {
-  return likely (face->ot_layout && face->ot_layout->gsub) ? *face->ot_layout->gsub : Null(GSUB);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GSUB);
+  return *hb_ot_layout_from_face (face)->gsub;
 }
 static inline const GPOS&
 _get_gpos (hb_face_t *face)
 {
-  return likely (face->ot_layout && face->ot_layout->gpos) ? *face->ot_layout->gpos : Null(GPOS);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GPOS);
+  return *hb_ot_layout_from_face (face)->gpos;
 }
 
 
 /*
  * GDEF
  */
 
 hb_bool_t
 hb_ot_layout_has_glyph_classes (hb_face_t *face)
 {
   return _get_gdef (face).has_glyph_classes ();
 }
 
-unsigned int
-_hb_ot_layout_get_glyph_property (hb_face_t       *face,
-				  hb_glyph_info_t *info)
-{
-  if (!info->props_cache())
-  {
-    const GDEF &gdef = _get_gdef (face);
-    info->props_cache() = gdef.get_glyph_props (info->codepoint);
-  }
-
-  return info->props_cache();
-}
-
-static hb_bool_t
-_hb_ot_layout_match_properties (hb_face_t      *face,
-				hb_codepoint_t  codepoint,
-				unsigned int    glyph_props,
-				unsigned int    lookup_props)
-{
-  /* Not covered, if, for example, glyph class is ligature and
-   * lookup_props includes LookupFlags::IgnoreLigatures
-   */
-  if (glyph_props & lookup_props & LookupFlag::IgnoreFlags)
-    return false;
-
-  if (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
-  {
-    /* If using mark filtering sets, the high short of
-     * lookup_props has the set index.
-     */
-    if (lookup_props & LookupFlag::UseMarkFilteringSet)
-      return _get_gdef (face).mark_set_covers (lookup_props >> 16, codepoint);
-
-    /* The second byte of lookup_props has the meaning
-     * "ignore marks of attachment type different than
-     * the attachment type specified."
-     */
-    if (lookup_props & LookupFlag::MarkAttachmentType && glyph_props & LookupFlag::MarkAttachmentType)
-      return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType);
-  }
-
-  return true;
-}
-
-hb_bool_t
-_hb_ot_layout_check_glyph_property (hb_face_t    *face,
-				    hb_glyph_info_t *ginfo,
-				    unsigned int  lookup_props,
-				    unsigned int *property_out)
-{
-  unsigned int property;
-
-  property = _hb_ot_layout_get_glyph_property (face, ginfo);
-  (void) (property_out && (*property_out = property));
-
-  return _hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
-}
-
-hb_bool_t
-_hb_ot_layout_skip_mark (hb_face_t    *face,
-			 hb_glyph_info_t *ginfo,
-			 unsigned int  lookup_props,
-			 unsigned int *property_out)
-{
-  unsigned int property;
-
-  property = _hb_ot_layout_get_glyph_property (face, ginfo);
-  (void) (property_out && (*property_out = property));
-
-  /* If it's a mark, skip it we don't accept it. */
-  if (unlikely (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
-    return !_hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
-
-  /* If not a mark, don't skip. */
-  return false;
-}
-
-
 
 unsigned int
 hb_ot_layout_get_attach_points (hb_face_t      *face,
 				hb_codepoint_t  glyph,
 				unsigned int    start_offset,
 				unsigned int   *point_count /* IN/OUT */,
 				unsigned int   *point_array /* OUT */)
 {
@@ -190,16 +139,17 @@ hb_ot_layout_get_ligature_carets (hb_fon
 				  hb_codepoint_t  glyph,
 				  unsigned int    start_offset,
 				  unsigned int   *caret_count /* IN/OUT */,
 				  int            *caret_array /* OUT */)
 {
   return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
 }
 
+
 /*
  * GSUB/GPOS
  */
 
 static const GSUBGPOS&
 get_gsubgpos_table (hb_face_t *face,
 		    hb_tag_t   table_tag)
 {
@@ -228,29 +178,29 @@ hb_ot_layout_table_find_script (hb_face_
 				hb_tag_t      table_tag,
 				hb_tag_t      script_tag,
 				unsigned int *script_index)
 {
   ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
   const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   if (g.find_script_index (script_tag, script_index))
-    return TRUE;
+    return true;
 
   /* try finding 'DFLT' */
   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
-    return FALSE;
+    return false;
 
   /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
    * including many versions of DejaVu Sans Mono! */
   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
-    return FALSE;
+    return false;
 
   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
-  return FALSE;
+  return false;
 }
 
 hb_bool_t
 hb_ot_layout_table_choose_script (hb_face_t      *face,
 				  hb_tag_t        table_tag,
 				  const hb_tag_t *script_tags,
 				  unsigned int   *script_index,
 				  hb_tag_t       *chosen_script)
@@ -258,48 +208,48 @@ hb_ot_layout_table_choose_script (hb_fac
   ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
   const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   while (*script_tags)
   {
     if (g.find_script_index (*script_tags, script_index)) {
       if (chosen_script)
         *chosen_script = *script_tags;
-      return TRUE;
+      return true;
     }
     script_tags++;
   }
 
   /* try finding 'DFLT' */
   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
     if (chosen_script)
       *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
-    return FALSE;
+    return false;
   }
 
   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
     if (chosen_script)
       *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
-    return FALSE;
+    return false;
   }
 
   /* 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;
-    return FALSE;
+    return false;
   }
 
   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
   if (chosen_script)
     *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
-  return FALSE;
+  return false;
 }
 
 unsigned int
 hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
 				     hb_tag_t      table_tag,
 				     unsigned int  start_offset,
 				     unsigned int *feature_count /* IN/OUT */,
 				     hb_tag_t     *feature_tags /* OUT */)
@@ -329,24 +279,24 @@ hb_ot_layout_script_find_language (hb_fa
 				   unsigned int  script_index,
 				   hb_tag_t      language_tag,
 				   unsigned int *language_index)
 {
   ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
   const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
 
   if (s.find_lang_sys_index (language_tag, language_index))
-    return TRUE;
+    return true;
 
   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
   if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
-    return FALSE;
+    return false;
 
   if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
-  return FALSE;
+  return false;
 }
 
 hb_bool_t
 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
 						  hb_tag_t      table_tag,
 						  unsigned int  script_index,
 						  unsigned int  language_index,
 						  unsigned int *feature_index)
@@ -411,22 +361,22 @@ hb_ot_layout_language_find_feature (hb_f
   const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 
   unsigned int num_features = l.get_feature_count ();
   for (unsigned int i = 0; i < num_features; i++) {
     unsigned int f_index = l.get_feature_index (i);
 
     if (feature_tag == g.get_feature_tag (f_index)) {
       if (feature_index) *feature_index = f_index;
-      return TRUE;
+      return true;
     }
   }
 
   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
-  return FALSE;
+  return false;
 }
 
 unsigned int
 hb_ot_layout_feature_get_lookup_indexes (hb_face_t    *face,
 					 hb_tag_t      table_tag,
 					 unsigned int  feature_index,
 					 unsigned int  start_offset,
 					 unsigned int *lookup_count /* IN/OUT */,
@@ -444,36 +394,59 @@ hb_ot_layout_feature_get_lookup_indexes 
  */
 
 hb_bool_t
 hb_ot_layout_has_substitution (hb_face_t *face)
 {
   return &_get_gsub (face) != &Null(GSUB);
 }
 
-void
-hb_ot_layout_substitute_start (hb_buffer_t  *buffer)
+hb_bool_t
+hb_ot_layout_would_substitute_lookup (hb_face_t            *face,
+				      const hb_codepoint_t *glyphs,
+				      unsigned int          glyphs_length,
+				      unsigned int          lookup_index)
 {
-  GSUB::substitute_start (buffer);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
+  return hb_ot_layout_would_substitute_lookup_fast (face, glyphs, glyphs_length, lookup_index);
 }
 
 hb_bool_t
-hb_ot_layout_substitute_lookup (hb_face_t    *face,
+hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
+					   const hb_codepoint_t *glyphs,
+					   unsigned int          glyphs_length,
+					   unsigned int          lookup_index)
+{
+  if (unlikely (glyphs_length < 1 || glyphs_length > 2)) return false;
+  if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
+  hb_would_apply_context_t c (face, glyphs[0], glyphs_length == 2 ? glyphs[1] : -1, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
+  return hb_ot_layout_from_face (face)->gsub->would_substitute_lookup (&c, lookup_index);
+}
+
+void
+hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
+{
+  GSUB::substitute_start (font, buffer);
+}
+
+hb_bool_t
+hb_ot_layout_substitute_lookup (hb_font_t    *font,
 				hb_buffer_t  *buffer,
 				unsigned int  lookup_index,
 				hb_mask_t     mask)
 {
-  hb_apply_context_t c (NULL, face, buffer, mask);
-  return _get_gsub (face).substitute_lookup (&c, lookup_index);
+  if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gsub_lookup_count)) return false;
+  hb_apply_context_t c (font, buffer, mask, &hb_ot_layout_from_face (font->face)->gsub_digests[lookup_index]);
+  return hb_ot_layout_from_face (font->face)->gsub->substitute_lookup (&c, lookup_index);
 }
 
 void
-hb_ot_layout_substitute_finish (hb_buffer_t  *buffer HB_UNUSED)
+hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
 {
-  GSUB::substitute_finish (buffer);
+  GSUB::substitute_finish (font, buffer);
 }
 
 void
 hb_ot_layout_substitute_closure_lookup (hb_face_t    *face,
 				        hb_set_t     *glyphs,
 				        unsigned int  lookup_index)
 {
   hb_closure_context_t c (face, glyphs);
@@ -486,55 +459,29 @@ hb_ot_layout_substitute_closure_lookup (
 
 hb_bool_t
 hb_ot_layout_has_positioning (hb_face_t *face)
 {
   return &_get_gpos (face) != &Null(GPOS);
 }
 
 void
-hb_ot_layout_position_start (hb_buffer_t  *buffer)
+hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
 {
-  GPOS::position_start (buffer);
+  GPOS::position_start (font, buffer);
 }
 
 hb_bool_t
-hb_ot_layout_position_lookup   (hb_font_t    *font,
-				hb_buffer_t  *buffer,
-				unsigned int  lookup_index,
-				hb_mask_t     mask)
+hb_ot_layout_position_lookup (hb_font_t    *font,
+			      hb_buffer_t  *buffer,
+			      unsigned int  lookup_index,
+			      hb_mask_t     mask)
 {
-  hb_apply_context_t c (font, font->face, buffer, mask);
-  return _get_gpos (font->face).position_lookup (&c, lookup_index);
+  if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gpos_lookup_count)) return false;
+  hb_apply_context_t c (font, buffer, mask, &hb_ot_layout_from_face (font->face)->gpos_digests[lookup_index]);
+  return hb_ot_layout_from_face (font->face)->gpos->position_lookup (&c, lookup_index);
 }
 
 void
-hb_ot_layout_position_finish (hb_face_t *face, hb_buffer_t *buffer)
+hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer, hb_bool_t zero_width_attached_marks)
 {
-  /* force diacritics to have zero width */
-  unsigned int count = buffer->len;
-  const hb_glyph_info_t *info = buffer->info;
-  hb_glyph_position_t *positions = buffer->pos;
-  /*
-   * Forcibly zero widths of chars with GC=Mn; we don't use the GDEF 'Mark' class
-   * because some fonts (e.g. C-DAC Yogesh) classify spacing Indic matras as 'Mark'
-   * but we don't want to force their width to zero.
-   */
-  if (buffer->props.direction == HB_DIRECTION_RTL) {
-    for (unsigned int i = 1; i < count; i++) {
-      if (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
-        positions[i].x_advance = 0;
-      }
-    }
-  } else {
-    for (unsigned int i = 1; i < count; i++) {
-      if (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
-        hb_glyph_position_t& pos = positions[i];
-        pos.x_offset -= pos.x_advance;
-        pos.x_advance = 0;
-      }
-    }
-  }
-
-  GPOS::position_finish (buffer);
+  GPOS::position_finish (font, buffer, zero_width_attached_marks);
 }
-
-
--- a/gfx/harfbuzz/src/hb-ot-layout.h
+++ b/gfx/harfbuzz/src/hb-ot-layout.h
@@ -37,16 +37,17 @@
 
 HB_BEGIN_DECLS
 
 
 #define HB_OT_TAG_GDEF HB_TAG('G','D','E','F')
 #define HB_OT_TAG_GSUB HB_TAG('G','S','U','B')
 #define HB_OT_TAG_GPOS HB_TAG('G','P','O','S')
 
+
 /*
  * GDEF
  */
 
 hb_bool_t
 hb_ot_layout_has_glyph_classes (hb_face_t *face);
 
 /* Not that useful.  Provides list of attach points for a glyph that a
@@ -163,53 +164,31 @@ hb_ot_layout_feature_get_lookup_indexes 
 
 /*
  * GSUB
  */
 
 hb_bool_t
 hb_ot_layout_has_substitution (hb_face_t *face);
 
-/* Should be called before all the substitute_lookup's are done. */
-void
-hb_ot_layout_substitute_start (hb_buffer_t  *buffer);
-
+/* Supports length 1 or 2 right now. */
 hb_bool_t
-hb_ot_layout_substitute_lookup (hb_face_t    *face,
-				hb_buffer_t  *buffer,
-				unsigned int  lookup_index,
-				hb_mask_t     mask);
-
-/* Should be called after all the substitute_lookup's are done */
-void
-hb_ot_layout_substitute_finish (hb_buffer_t  *buffer);
-
+hb_ot_layout_would_substitute_lookup (hb_face_t            *face,
+				      const hb_codepoint_t *glyphs,
+				      unsigned int          glyphs_length,
+				      unsigned int          lookup_index);
 
 void
 hb_ot_layout_substitute_closure_lookup (hb_face_t    *face,
 				        hb_set_t     *glyphs,
 				        unsigned int  lookup_index);
 
 /*
  * GPOS
  */
 
 hb_bool_t
 hb_ot_layout_has_positioning (hb_face_t *face);
 
-/* Should be called before all the position_lookup's are done.  Resets positions to zero. */
-void
-hb_ot_layout_position_start (hb_buffer_t  *buffer);
-
-hb_bool_t
-hb_ot_layout_position_lookup (hb_font_t    *font,
-			      hb_buffer_t  *buffer,
-			      unsigned int  lookup_index,
-			      hb_mask_t     mask);
-
-/* Should be called after all the position_lookup's are done */
-void
-hb_ot_layout_position_finish (hb_face_t *face, hb_buffer_t  *buffer);
-
 
 HB_END_DECLS
 
 #endif /* HB_OT_LAYOUT_H */
--- a/gfx/harfbuzz/src/hb-ot-map-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-map-private.hh
@@ -1,11 +1,11 @@
 /*
  * Copyright © 2009,2010  Red Hat, Inc.
- * Copyright © 2010,2011  Google, Inc.
+ * Copyright © 2010,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.
@@ -26,68 +26,27 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_MAP_PRIVATE_HH
 #define HB_OT_MAP_PRIVATE_HH
 
 #include "hb-buffer-private.hh"
 
-#include "hb-ot-layout.h"
-
+#include "hb-ot-layout-private.hh"
 
 
 static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS};
 
 struct hb_ot_map_t
 {
   friend struct hb_ot_map_builder_t;
 
   public:
 
-  hb_ot_map_t (void) { memset (this, 0, sizeof (*this)); }
-
-  typedef void (*gsub_pause_func_t) (const hb_ot_map_t *map, hb_face_t *face, hb_buffer_t *buffer, void *user_data);
-  typedef void (*gpos_pause_func_t) (const hb_ot_map_t *map, hb_font_t *font, hb_buffer_t *buffer, void *user_data);
-
-  inline hb_mask_t get_global_mask (void) const { return global_mask; }
-
-  inline hb_mask_t get_mask (hb_tag_t tag, unsigned int *shift = NULL) const {
-    const feature_map_t *map = features.bsearch (&tag);
-    if (shift) *shift = map ? map->shift : 0;
-    return map ? map->mask : 0;
-  }
-
-  inline hb_mask_t get_1_mask (hb_tag_t tag) const {
-    const feature_map_t *map = features.bsearch (&tag);
-    return map ? map->_1_mask : 0;
-  }
-
-  inline hb_tag_t get_chosen_script (unsigned int table_index) const
-  { return chosen_script[table_index]; }
-
-  inline void substitute (hb_face_t *face, hb_buffer_t *buffer) const
-  { apply (0, (hb_ot_map_t::apply_lookup_func_t) hb_ot_layout_substitute_lookup, face, buffer); }
-  inline void position (hb_font_t *font, hb_buffer_t *buffer) const
-  { apply (1, (hb_ot_map_t::apply_lookup_func_t) hb_ot_layout_position_lookup, font, buffer); }
-
-  HB_INTERNAL void substitute_closure (hb_face_t *face,
-				       hb_set_t *glyphs) const;
-
-
-  inline void finish (void) {
-    features.finish ();
-    lookups[0].finish ();
-    lookups[1].finish ();
-    pauses[0].finish ();
-    pauses[1].finish ();
-  }
-
-  private:
-
   struct feature_map_t {
     hb_tag_t tag; /* should be first for our bsearch to work */
     unsigned int index[2]; /* GSUB/GPOS */
     unsigned int stage[2]; /* GSUB/GPOS */
     unsigned int shift;
     hb_mask_t mask;
     hb_mask_t _1_mask; /* mask for value=1, for quick access */
 
@@ -98,42 +57,86 @@ struct hb_ot_map_t
   struct lookup_map_t {
     unsigned int index;
     hb_mask_t mask;
 
     static int cmp (const lookup_map_t *a, const lookup_map_t *b)
     { return a->index < b->index ? -1 : a->index > b->index ? 1 : 0; }
   };
 
-  typedef void (*pause_func_t) (const hb_ot_map_t *map, void *face_or_font, hb_buffer_t *buffer, void *user_data);
-  typedef struct {
-    pause_func_t func;
-    void *user_data;
-  } pause_callback_t;
+  typedef void (*pause_func_t) (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer);
 
   struct pause_map_t {
     unsigned int num_lookups; /* Cumulative */
-    pause_callback_t callback;
+    pause_func_t callback;
   };
 
-  typedef hb_bool_t (*apply_lookup_func_t) (void *face_or_font,
-					    hb_buffer_t  *buffer,
-					    unsigned int  lookup_index,
-					    hb_mask_t     mask);
+
+  hb_ot_map_t (void) { memset (this, 0, sizeof (*this)); }
+
+  inline hb_mask_t get_global_mask (void) const { return global_mask; }
+
+  inline hb_mask_t get_mask (hb_tag_t feature_tag, unsigned int *shift = NULL) const {
+    const feature_map_t *map = features.bsearch (&feature_tag);
+    if (shift) *shift = map ? map->shift : 0;
+    return map ? map->mask : 0;
+  }
+
+  inline hb_mask_t get_1_mask (hb_tag_t feature_tag) const {
+    const feature_map_t *map = features.bsearch (&feature_tag);
+    return map ? map->_1_mask : 0;
+  }
+
+  inline unsigned int get_feature_index (unsigned int table_index, hb_tag_t feature_tag) const {
+    const feature_map_t *map = features.bsearch (&feature_tag);
+    return map ? map->index[table_index] : HB_OT_LAYOUT_NO_FEATURE_INDEX;
+  }
+
+  inline unsigned int get_feature_stage (unsigned int table_index, hb_tag_t feature_tag) const {
+    const feature_map_t *map = features.bsearch (&feature_tag);
+    return map ? map->stage[table_index] : (unsigned int) -1;
+  }
+
+  inline void get_stage_lookups (unsigned int table_index, unsigned int stage,
+				 const struct lookup_map_t **plookups, unsigned int *lookup_count) const {
+    if (unlikely (stage == (unsigned int) -1)) {
+      *plookups = NULL;
+      *lookup_count = 0;
+      return;
+    }
+    assert (stage <= pauses[table_index].len);
+    unsigned int start = stage ? pauses[table_index][stage - 1].num_lookups : 0;
+    unsigned int end   = stage < pauses[table_index].len ? pauses[table_index][stage].num_lookups : lookups[table_index].len;
+    *plookups = &lookups[table_index][start];
+    *lookup_count = end - start;
+  }
+
+  inline hb_tag_t get_chosen_script (unsigned int table_index) const
+  { return chosen_script[table_index]; }
+
+  HB_INTERNAL void substitute_closure (const struct hb_ot_shape_plan_t *plan, hb_face_t *face, hb_set_t *glyphs) const;
+  HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
+  HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
+
+  inline void finish (void) {
+    features.finish ();
+    lookups[0].finish ();
+    lookups[1].finish ();
+    pauses[0].finish ();
+    pauses[1].finish ();
+  }
+
+
+  private:
 
   HB_INTERNAL void add_lookups (hb_face_t    *face,
 				unsigned int  table_index,
 				unsigned int  feature_index,
 				hb_mask_t     mask);
 
-  HB_INTERNAL void apply (unsigned int table_index,
-			  hb_ot_map_t::apply_lookup_func_t apply_lookup_func,
-			  void *face_or_font,
-			  hb_buffer_t *buffer) const;
-
   hb_mask_t global_mask;
 
   hb_tag_t chosen_script[2];
   hb_prealloced_array_t<feature_map_t, 8> features;
   hb_prealloced_array_t<lookup_map_t, 32> lookups[2]; /* GSUB/GPOS */
   hb_prealloced_array_t<pause_map_t, 1> pauses[2]; /* GSUB/GPOS */
 };
 
@@ -144,20 +147,20 @@ struct hb_ot_map_builder_t
 
   hb_ot_map_builder_t (void) { memset (this, 0, sizeof (*this)); }
 
   HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value, bool global);
 
   inline void add_bool_feature (hb_tag_t tag, bool global = true)
   { add_feature (tag, 1, global); }
 
-  inline void add_gsub_pause (hb_ot_map_t::gsub_pause_func_t pause_func, void *user_data)
-  { add_pause (0, (hb_ot_map_t::pause_func_t) pause_func, user_data); }
-  inline void add_gpos_pause (hb_ot_map_t::gpos_pause_func_t pause_func, void *user_data)
-  { add_pause (1, (hb_ot_map_t::pause_func_t) pause_func, user_data); }
+  inline void add_gsub_pause (hb_ot_map_t::pause_func_t pause_func)
+  { add_pause (0, pause_func); }
+  inline void add_gpos_pause (hb_ot_map_t::pause_func_t pause_func)
+  { add_pause (1, pause_func); }
 
   HB_INTERNAL void compile (hb_face_t *face,
 			    const hb_segment_properties_t *props,
 			    struct hb_ot_map_t &m);
 
   inline void finish (void) {
     feature_infos.finish ();
     pauses[0].finish ();
@@ -175,20 +178,20 @@ struct hb_ot_map_builder_t
     unsigned int stage[2]; /* GSUB/GPOS */
 
     static int cmp (const feature_info_t *a, const feature_info_t *b)
     { return (a->tag != b->tag) ?  (a->tag < b->tag ? -1 : 1) : (a->seq < b->seq ? -1 : 1); }
   };
 
   struct pause_info_t {
     unsigned int stage;
-    hb_ot_map_t::pause_callback_t callback;
+    hb_ot_map_t::pause_func_t callback;
   };
 
-  HB_INTERNAL void add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func, void *user_data);
+  HB_INTERNAL void add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func);
 
   unsigned int current_stage[2]; /* GSUB/GPOS */
   hb_prealloced_array_t<feature_info_t,16> feature_infos;
   hb_prealloced_array_t<pause_info_t, 1> pauses[2]; /* GSUB/GPOS */
 };
 
 
 
--- a/gfx/harfbuzz/src/hb-ot-map.cc
+++ b/gfx/harfbuzz/src/hb-ot-map.cc
@@ -23,19 +23,16 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-ot-map-private.hh"
 
-#include "hb-ot-shape-private.hh"
-
-
 
 void
 hb_ot_map_t::add_lookups (hb_face_t    *face,
 			  unsigned int  table_index,
 			  unsigned int  feature_index,
 			  hb_mask_t     mask)
 {
   unsigned int lookup_indices[32];
@@ -71,60 +68,77 @@ void hb_ot_map_builder_t::add_feature (h
   info->seq = feature_infos.len;
   info->max_value = value;
   info->global = global;
   info->default_value = global ? value : 0;
   info->stage[0] = current_stage[0];
   info->stage[1] = current_stage[1];
 }
 
-void hb_ot_map_t::apply (unsigned int table_index,
-			 hb_ot_map_t::apply_lookup_func_t apply_lookup_func,
-			 void *face_or_font,
-			 hb_buffer_t *buffer) const
+/* Keep the next two functions in sync. */
+
+void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
 {
+  const unsigned int table_index = 0;
   unsigned int i = 0;
 
   for (unsigned int pause_index = 0; pause_index < pauses[table_index].len; pause_index++) {
     const pause_map_t *pause = &pauses[table_index][pause_index];
     for (; i < pause->num_lookups; i++)
-      apply_lookup_func (face_or_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);
 
-    pause->callback.func (this, face_or_font, buffer, pause->callback.user_data);
+    buffer->clear_output ();
+
+    if (pause->callback)
+      pause->callback (plan, font, buffer);
   }
 
   for (; i < lookups[table_index].len; i++)
-    apply_lookup_func (face_or_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);
 }
 
-void hb_ot_map_t::substitute_closure (hb_face_t *face,
-				      hb_set_t *glyphs) const
+void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
+{
+  const unsigned int table_index = 1;
+  unsigned int i = 0;
+
+  for (unsigned int pause_index = 0; pause_index < pauses[table_index].len; pause_index++) {
+    const pause_map_t *pause = &pauses[table_index][pause_index];
+    for (; i < pause->num_lookups; i++)
+      hb_ot_layout_position_lookup (font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask);
+
+    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);
+}
+
+void hb_ot_map_t::substitute_closure (const hb_ot_shape_plan_t *plan, hb_face_t *face, hb_set_t *glyphs) const
 {
   unsigned int table_index = 0;
   unsigned int i = 0;
 
   for (unsigned int pause_index = 0; pause_index < pauses[table_index].len; pause_index++) {
     const pause_map_t *pause = &pauses[table_index][pause_index];
     for (; i < pause->num_lookups; i++)
       hb_ot_layout_substitute_closure_lookup (face, glyphs, lookups[table_index][i].index);
   }
 
   for (; i < lookups[table_index].len; i++)
     hb_ot_layout_substitute_closure_lookup (face, glyphs, lookups[table_index][i].index);
 }
 
-void hb_ot_map_builder_t::add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func, void *user_data)
+void hb_ot_map_builder_t::add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func)
 {
-  if (pause_func) {
-    pause_info_t *p = pauses[table_index].push ();
-    if (likely (p)) {
-      p->stage = current_stage[table_index];
-      p->callback.func = pause_func;
-      p->callback.user_data = user_data;
-    }
+  pause_info_t *p = pauses[table_index].push ();
+  if (likely (p)) {
+    p->stage = current_stage[table_index];
+    p->callback = pause_func;
   }
 
   current_stage[table_index]++;
 }
 
 void
 hb_ot_map_builder_t::compile (hb_face_t *face,
 			      const hb_segment_properties_t *props,
@@ -228,18 +242,18 @@ hb_ot_map_builder_t::compile (hb_face_t 
 	m.global_mask |= (info->default_value << map->shift) & map->mask;
     }
     map->_1_mask = (1 << map->shift) & map->mask;
 
   }
   feature_infos.shrink (0); /* Done with these */
 
 
-  add_gsub_pause (NULL, NULL);
-  add_gpos_pause (NULL, NULL);
+  add_gsub_pause (NULL);
+  add_gpos_pause (NULL);
 
   for (unsigned int table_index = 0; table_index < 2; table_index++) {
     hb_tag_t table_tag = table_tags[table_index];
 
     /* Collect lookup indices for features */
 
     unsigned int required_feature_index;
     if (hb_ot_layout_language_get_required_feature_index (face,
@@ -280,10 +294,8 @@ hb_ot_map_builder_t::compile (hb_face_t 
 	  pause_map->callback = pauses[table_index][pause_index].callback;
 	}
 
 	pause_index++;
       }
     }
   }
 }
-
-
--- a/gfx/harfbuzz/src/hb-ot-maxp-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-maxp-table.hh
@@ -47,17 +47,17 @@ struct maxp
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) &&
 			 likely (version.major == 1 || (version.major == 0 && version.minor == 0x5000)));
   }
 
   /* We only implement version 0.5 as none of the extra fields in version 1.0 are useful. */
-  private:
+  protected:
   FixedVersion	version;		/* Version of the maxp table (0.5 or 1.0),
 					 * 0x00005000 or 0x00010000. */
   USHORT	numGlyphs;		/* The number of glyphs in the font. */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 
--- a/gfx/harfbuzz/src/hb-ot-name-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-name-table.hh
@@ -109,17 +109,17 @@ struct name
     TRACE_SANITIZE ();
     return TRACE_RETURN (c->check_struct (this) &&
 			 likely (format == 0 || format == 1) &&
 			 c->check_array (nameRecord, nameRecord[0].static_size, count) &&
 			 sanitize_records (c));
   }
 
   /* We only implement format 0 for now. */
-  private:
+  protected:
   USHORT	format;			/* Format selector (=0/1). */
   USHORT	count;			/* Number of name records. */
   Offset	stringOffset;		/* Offset to start of string storage (from start of table). */
   NameRecord	nameRecord[VAR];	/* The name records where count is the number of records. */
   public:
   DEFINE_SIZE_ARRAY (6, nameRecord);
 };
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -159,96 +159,91 @@ static const struct arabic_state_table_e
   { {NONE,NONE,0}, {ISOL,ISOL,1}, {ISOL,ISOL,2}, {ISOL,FIN2,5}, {ISOL,ISOL,6}, },
 
   /* State 6: prev was DALATH/RISH, not willing to join. */
   { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN3,5}, {NONE,ISOL,6}, }
 };
 
 
 
-void
-_hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map,
-					      const hb_segment_properties_t *props)
+static void
+collect_features_arabic (hb_ot_shape_planner_t *plan)
 {
+  hb_ot_map_builder_t *map = &plan->map;
+
   /* For Language forms (in ArabicOT speak), we do the iso/fina/medi/init together,
    * then rlig and calt each in their own stage.  This makes IranNastaliq's ALLAH
    * ligature work correctly. It's unfortunate though...
    *
    * This also makes Arial Bold in Windows7 work.  See:
    * https://bugzilla.mozilla.org/show_bug.cgi?id=644184
    *
    * 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_gsub_pause (NULL, NULL);
+  map->add_gsub_pause (NULL);
 
-  unsigned int num_features = props->script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
+  unsigned int num_features = plan->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
   for (unsigned int i = 0; i < num_features; i++)
     map->add_bool_feature (arabic_syriac_features[i], false);
 
-  map->add_gsub_pause (NULL, NULL);