bug 758236 - update harfbuzz to upstream commit a3547330fa88e30a138f6f17e60d9c7d1e316622. r=jdaggett
authorJonathan Kew <jkew@mozilla.com>
Fri, 01 Jun 2012 08:19:28 +0100
changeset 95499 11c6d420c23c96331c82281beba92fd51b12883f
parent 95498 727f3e801afb8d835e9bce67f26e56c8caaaff0f
child 95500 bfae6ae40e660fe748f37efe4dd6172711a5a31b
push id22819
push usereakhgari@mozilla.com
push dateSat, 02 Jun 2012 18:40:08 +0000
treeherdermozilla-central@f4a7c1a1f514 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs758236
milestone15.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 758236 - update harfbuzz to upstream commit a3547330fa88e30a138f6f17e60d9c7d1e316622. r=jdaggett
gfx/harfbuzz/src/Makefile.am
gfx/harfbuzz/src/Makefile.in
gfx/harfbuzz/src/hb-atomic-private.hh
gfx/harfbuzz/src/hb-buffer-private.hh
gfx/harfbuzz/src/hb-buffer.cc
gfx/harfbuzz/src/hb-cache-private.hh
gfx/harfbuzz/src/hb-fallback-shape.cc
gfx/harfbuzz/src/hb-font.cc
gfx/harfbuzz/src/hb-graphite2.cc
gfx/harfbuzz/src/hb-mutex-private.hh
gfx/harfbuzz/src/hb-object-private.hh
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.cc
gfx/harfbuzz/src/hb-ot-shape-private.hh
gfx/harfbuzz/src/hb-ot-shape.cc
gfx/harfbuzz/src/hb-ot.h
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.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-uniscribe.cc
gfx/harfbuzz/src/hb-version.h
gfx/harfbuzz/src/hb-warning.cc
gfx/harfbuzz/src/hb.h
gfx/harfbuzz/src/indic.cc
gfx/harfbuzz/src/main.cc
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -10,47 +10,52 @@ MAINTAINERCLEANFILES =
 # The following warning options are useful for debugging: -Wpadded -Wcast-align
 #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 \
 	hb-ot-hhea-table.hh \
 	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-tt-font.cc \
 	hb-unicode-private.hh \
 	hb-unicode.cc \
 	hb-warning.cc \
 	$(NULL)
 HBHEADERS = \
 	hb.h \
 	hb-blob.h \
 	hb-buffer.h \
 	hb-common.h \
 	hb-font.h \
+	hb-set.h \
 	hb-shape.h \
 	hb-unicode.h \
 	hb-version.h \
 	$(NULL)
 
 if HAVE_OT
 HBSOURCES += \
 	hb-ot-layout.cc \
@@ -62,16 +67,17 @@ HBSOURCES += \
 	hb-ot-layout-private.hh \
 	hb-ot-map.cc \
 	hb-ot-map-private.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-complex-arabic.cc \
 	hb-ot-shape-complex-arabic-table.hh \
 	hb-ot-shape-complex-indic.cc \
 	hb-ot-shape-complex-indic-machine.hh \
+	hb-ot-shape-complex-indic-private.hh \
 	hb-ot-shape-complex-indic-table.hh \
 	hb-ot-shape-complex-misc.cc \
 	hb-ot-shape-complex-private.hh \
 	hb-ot-shape-normalize-private.hh \
 	hb-ot-shape-normalize.cc \
 	hb-ot-shape-private.hh \
 	$(NULL)
 HBHEADERS += \
@@ -164,23 +170,27 @@ 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
+noinst_PROGRAMS = main indic
 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)
+
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
 	check-header-guards.sh \
 	check-internal-symbols.sh \
 	check-includes.sh \
 	$(NULL)
 
 if HAVE_ICU
--- a/gfx/harfbuzz/src/Makefile.in
+++ b/gfx/harfbuzz/src/Makefile.in
@@ -47,16 +47,17 @@ CPPSRCS	=                        \
   hb-ot-layout.cc                \
   hb-ot-map.cc                   \
   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-unicode.cc                  \
   hb-warning.cc                  \
   $(NULL)
 
 EXPORTS_NAMESPACES = harfbuzz
 
 EXPORTS_harfbuzz = \
@@ -64,16 +65,17 @@ EXPORTS_harfbuzz = \
   hb-blob.h        \
   hb-buffer.h      \
   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-unicode.h     \
   hb-version.h     \
   $(NULL)
 
 LOCAL_INCLUDES  += -I$(srcdir) 
 
 FORCE_STATIC_LIB = 1
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-atomic-private.hh
@@ -0,0 +1,87 @@
+/*
+ * Copyright © 2007  Chris Wilson
+ * Copyright © 2009,2010  Red Hat, 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.
+ *
+ * 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.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris@chris-wilson.co.uk>
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_ATOMIC_PRIVATE_HH
+#define HB_ATOMIC_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+/* atomic_int */
+
+/* We need external help for these */
+
+#if 0
+
+
+#elif !defined(HB_NO_MT) && defined(_MSC_VER) && _MSC_VER >= 1600
+
+#include <intrin.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))
+
+
+#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))
+
+
+#elif !defined(HB_NO_MT) && defined(HAVE_GLIB)
+
+#include <glib.h>
+typedef volatile 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))
+
+
+#else
+
+#define HB_ATOMIC_INT_NIL 1
+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)
+
+#endif
+
+
+#endif /* HB_ATOMIC_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -65,16 +65,25 @@ struct _hb_buffer_t {
   unsigned int len; /* Length of ->info and ->pos arrays */
   unsigned int out_len; /* Length of ->out array if have_output */
 
   unsigned int allocated; /* Length of allocated arrays */
   hb_glyph_info_t     *info;
   hb_glyph_info_t     *out_info;
   hb_glyph_position_t *pos;
 
+  inline hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
+  inline hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; }
+
+  inline hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
+  inline hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
+
+  inline hb_glyph_info_t &prev (void) { return out_info[out_len - 1]; }
+  inline hb_glyph_info_t prev (void) const { return info[out_len - 1]; }
+
   unsigned int serial;
   uint8_t allocated_var_bytes[8];
   const char *allocated_var_owner[8];
 
 
   /* Methods */
 
   HB_INTERNAL void reset (void);
@@ -99,17 +108,17 @@ struct _hb_buffer_t {
   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 uint16_t *glyph_data);
+				   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);
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -266,17 +266,17 @@ hb_buffer_t::replace_glyphs_be16 (unsign
 
   idx  += num_in;
   out_len += num_out;
 }
 
 void
 hb_buffer_t::replace_glyphs (unsigned int num_in,
 			     unsigned int num_out,
-			     const uint16_t *glyph_data)
+			     const uint32_t *glyph_data)
 {
   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);
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-cache-private.hh
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_CACHE_PRIVATE_HH
+#define HB_CACHE_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+/* Implements a lock-free cache for int->int functions. */
+
+template <unsigned int key_bits, unsigned int value_bits, unsigned int cache_bits>
+struct hb_cache_t
+{
+  ASSERT_STATIC (key_bits >= cache_bits);
+  ASSERT_STATIC (key_bits + value_bits - cache_bits < 8 * sizeof (unsigned int));
+
+  inline void clear (void)
+  {
+    memset (values, 255, sizeof (values));
+  }
+
+  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;
+  }
+
+  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;
+    values[k] = v;
+    return true;
+  }
+
+  private:
+  unsigned int values[1<<cache_bits];
+};
+
+typedef hb_cache_t<21, 16, 8> hb_cmap_cache_t;
+typedef hb_cache_t<16, 24, 8> hb_advance_cache_t;
+
+
+#endif /* HB_CACHE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-fallback-shape.cc
+++ b/gfx/harfbuzz/src/hb-fallback-shape.cc
@@ -26,18 +26,18 @@
 
 #include "hb-fallback-shape-private.hh"
 
 #include "hb-buffer-private.hh"
 
 hb_bool_t
 _hb_fallback_shape (hb_font_t          *font,
 		    hb_buffer_t        *buffer,
-		    const hb_feature_t *features,
-		    unsigned int        num_features)
+		    const hb_feature_t *features HB_UNUSED,
+		    unsigned int        num_features HB_UNUSED)
 {
   buffer->guess_properties ();
 
   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);
 
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -28,16 +28,18 @@
 
 #include "hb-ot-layout-private.hh"
 
 #include "hb-font-private.hh"
 #include "hb-blob.h"
 #include "hb-open-file-private.hh"
 #include "hb-ot-head-table.hh"
 
+#include "hb-cache-private.hh"
+
 #include <string.h>
 
 
 
 /*
  * hb_font_funcs_t
  */
 
@@ -724,17 +726,17 @@ hb_face_reference_blob (hb_face_t *face)
 
 void
 hb_face_set_index (hb_face_t    *face,
 		   unsigned int  index)
 {
   if (hb_object_is_inert (face))
     return;
 
-  face->index = 0;
+  face->index = index;
 }
 
 unsigned int
 hb_face_get_index (hb_face_t    *face)
 {
   return face->index;
 }
 
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -242,21 +242,20 @@ hb_bool_t
   while (num_features--)
   {
     const gr_feature_ref *fref = gr_face_find_fref (data->grface, features->tag);
     if (fref)
       gr_fref_set_feature_value (fref, features->value, feats);
     features++;
   }
 
-  unsigned short *gids = NULL;
+  hb_codepoint_t *gids = NULL, *pg;
   hb_gr_cluster_t *clusters = NULL;
   gr_segment *seg = NULL;
   uint32_t *text = NULL;
-  unsigned short *pg;
   const gr_slot *is;
   unsigned int ci = 0, ic = 0;
   float curradvx = 0., curradvy = 0.;
   unsigned int glyphlen = 0;
   unsigned int *p;
 
   text = (uint32_t *) malloc ((charlen + 1) * sizeof (uint32_t));
   if (!text) goto dieout;
@@ -275,17 +274,17 @@ hb_bool_t
 		     gr_utf32, text, charlen,
 		     2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0));
   if (!seg) goto dieout;
 
   glyphlen = gr_seg_n_slots (seg);
   clusters = (hb_gr_cluster_t *) calloc (charlen, sizeof (hb_gr_cluster_t));
   if (!glyphlen || !clusters) goto dieout;
 
-  gids = (uint16_t *) malloc (glyphlen * sizeof (uint16_t));
+  gids = (hb_codepoint_t *) malloc (glyphlen * sizeof (hb_codepoint_t));
   if (!gids) goto dieout;
 
   pg = gids;
   for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
   {
     unsigned int before = gr_slot_before (is);
     unsigned int after = gr_slot_after (is);
     *pg = gr_slot_gid (is);
--- a/gfx/harfbuzz/src/hb-mutex-private.hh
+++ b/gfx/harfbuzz/src/hb-mutex-private.hh
@@ -30,51 +30,56 @@
  */
 
 #ifndef HB_MUTEX_PRIVATE_HH
 #define HB_MUTEX_PRIVATE_HH
 
 #include "hb-private.hh"
 
 
-
 /* mutex */
 
 /* We need external help for these */
 
-#if !defined(HB_NO_MT) && defined(HAVE_GLIB)
+#if 0
 
-#include <glib.h>
-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)
 
 #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)
 
+
 #elif !defined(HB_NO_MT) && 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)
 
+
+#elif !defined(HB_NO_MT) && defined(HAVE_GLIB)
+
+#include <glib.h>
+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)
+
+
 #else
 
 #define HB_MUTEX_IMPL_NIL 1
 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))
--- a/gfx/harfbuzz/src/hb-object-private.hh
+++ b/gfx/harfbuzz/src/hb-object-private.hh
@@ -29,70 +29,27 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OBJECT_PRIVATE_HH
 #define HB_OBJECT_PRIVATE_HH
 
 #include "hb-private.hh"
 
+#include "hb-atomic-private.hh"
 #include "hb-mutex-private.hh"
 
 
-
 /* Debug */
 
 #ifndef HB_DEBUG_OBJECT
 #define HB_DEBUG_OBJECT (HB_DEBUG+0)
 #endif
 
 
-/* atomic_int */
-
-/* We need external help for these */
-
-#if !defined(HB_NO_MT) && defined(HAVE_GLIB)
-
-#include <glib.h>
-typedef volatile 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_get(AI)		g_atomic_int_get (&(AI))
-
-
-#elif !defined(HB_NO_MT) && defined(_MSC_VER) && _MSC_VER >= 1600
-
-#include <intrin.h>
-typedef long hb_atomic_int_t;
-#define hb_atomic_int_add(AI, V)	_InterlockedExchangeAdd (&(AI), V)
-#define hb_atomic_int_get(AI)		(_ReadBarrier (), (AI))
-
-#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)), (AI) - (V))
-#define hb_atomic_int_get(AI)		OSAtomicAdd32Barrier(0, &(AI))
-
-#else
-
-#define HB_ATOMIC_INT_NIL 1
-
-typedef volatile int hb_atomic_int_t;
-#define hb_atomic_int_add(AI, V)	((AI) += (V), (AI) - (V))
-#define hb_atomic_int_get(AI)		(AI)
-
-#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}
 
@@ -191,27 +148,28 @@ struct _hb_object_header_t {
     return user_data.set (key, data, destroy_func, replace);
   }
 
   inline void *get_user_data (hb_user_data_key_t *key) {
     return user_data.get (key);
   }
 
   inline void trace (const char *function) const {
+    if (unlikely (!this)) return;
+    /* XXX We cannot use DEBUG_MSG_FUNC here since that one currecntly only
+     * prints the class name and throws away the template info. */
     DEBUG_MSG (OBJECT, (void *) this,
-	       "refcount=%d %s",
-	       this ? ref_count.get_unsafe () : 0,
-	       function);
+	       "%s refcount=%d",
+	       function,
+	       this ? ref_count.get_unsafe () : 0);
   }
 
 };
 
 
-
-
 /* object */
 
 template <typename Type>
 static inline void hb_object_trace (const Type *obj, const char *function)
 {
   obj->header.trace (function);
 }
 template <typename Type>
@@ -252,12 +210,9 @@ static inline bool hb_object_set_user_da
 template <typename Type>
 static inline void *hb_object_get_user_data (Type               *obj,
 					     hb_user_data_key_t *key)
 {
   return obj->header.get_user_data (key);
 }
 
 
-
-
-
 #endif /* HB_OBJECT_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-open-file-private.hh
+++ b/gfx/harfbuzz/src/hb-open-file-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,16 +18,17 @@
  *
  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OPEN_FILE_PRIVATE_HH
 #define HB_OPEN_FILE_PRIVATE_HH
 
 #include "hb-open-type-private.hh"
 
 
@@ -46,17 +48,17 @@ struct OpenTypeFontFile;
 struct OffsetTable;
 struct TTCHeader;
 
 
 typedef struct TableRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   Tag		tag;		/* 4-byte identifier. */
   CheckSum	checkSum;	/* CheckSum for this table. */
   ULONG		offset;		/* Offset from beginning of TrueType font
 				 * file. */
   ULONG		length;		/* Length of this table. */
   public:
@@ -95,18 +97,17 @@ typedef struct OffsetTable
     unsigned int table_index;
     find_table_index (tag, &table_index);
     return get_table (table_index);
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& c->check_array (tables, TableRecord::static_size, numTables);
+    return TRACE_RETURN (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables));
   }
 
   private:
   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. */
@@ -124,17 +125,17 @@ struct TTCHeaderVersion1
 {
   friend struct TTCHeader;
 
   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 table.sanitize (c, this);
+    return TRACE_RETURN (table.sanitize (c, this));
   }
 
   private:
   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
@@ -163,21 +164,21 @@ struct TTCHeader
     case 2: /* version 2 is compatible with version 1 */
     case 1: return u.version1.get_face (i);
     default:return Null(OpenTypeFontFace);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!u.header.version.sanitize (c))) return false;
+    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 u.version1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.version1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   struct {
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
   FixedVersion	version;	/* Version of the TTC Header (1.0 or 2.0),
@@ -225,24 +226,24 @@ struct OpenTypeFontFile
     case TrueTypeTag:	return u.fontFace;
     case TTCTag:	return u.ttcHeader.get_face (i);
     default:		return Null(OpenTypeFontFace);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!u.tag.sanitize (c))) return false;
+    if (unlikely (!u.tag.sanitize (c))) return TRACE_RETURN (false);
     switch (u.tag) {
     case CFFTag:	/* All the non-collection tags */
     case TrueTag:
     case Typ1Tag:
-    case TrueTypeTag:	return u.fontFace.sanitize (c);
-    case TTCTag:	return u.ttcHeader.sanitize (c);
-    default:		return true;
+    case TrueTypeTag:	return TRACE_RETURN (u.fontFace.sanitize (c));
+    case TTCTag:	return TRACE_RETURN (u.ttcHeader.sanitize (c));
+    default:		return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   Tag			tag;		/* 4-byte identifier. */
   OpenTypeFontFace	fontFace;
   TTCHeader		ttcHeader;
--- a/gfx/harfbuzz/src/hb-open-type-private.hh
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -1,10 +1,11 @@
 /*
  * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -17,16 +18,17 @@
  *
  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OPEN_TYPE_PRIVATE_HH
 #define HB_OPEN_TYPE_PRIVATE_HH
 
 #include "hb-private.hh"
 
 #include "hb-blob.h"
@@ -148,106 +150,97 @@ ASSERT_STATIC (Type::min_size + 1 <= siz
  */
 
 #ifndef HB_DEBUG_SANITIZE
 #define HB_DEBUG_SANITIZE (HB_DEBUG+0)
 #endif
 
 
 #define TRACE_SANITIZE() \
-	hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&c->debug_depth, "SANITIZE", this, NULL, HB_FUNC);
+	hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&c->debug_depth, "SANITIZE", this, HB_FUNC, "");
 
 
 struct hb_sanitize_context_t
 {
   inline void init (hb_blob_t *b)
   {
     this->blob = hb_blob_reference (b);
     this->writable = false;
   }
 
-  inline void setup (void)
+  inline void start_processing (void)
   {
     this->start = hb_blob_get_data (this->blob, NULL);
     this->end = this->start + hb_blob_get_length (this->blob);
     this->edit_count = 0;
     this->debug_depth = 0;
 
-    DEBUG_MSG (SANITIZE, this->blob,
-	       "init [%p..%p] (%lu bytes)",
-	       this->start, this->end,
-	       (unsigned long) (this->end - this->start));
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, +1,
+		     "start [%p..%p] (%lu bytes)",
+		     this->start, this->end,
+		     (unsigned long) (this->end - this->start));
   }
 
-  inline void finish (void)
+  inline void end_processing (void)
   {
-    DEBUG_MSG (SANITIZE, this->blob,
-	       "fini [%p..%p] %u edit requests",
-	       this->start, this->end, this->edit_count);
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, -1,
+		     "end [%p..%p] %u edit requests",
+		     this->start, this->end, this->edit_count);
 
     hb_blob_destroy (this->blob);
     this->blob = NULL;
     this->start = this->end = NULL;
   }
 
   inline bool check_range (const void *base, unsigned int len) const
   {
     const char *p = (const char *) base;
-    bool ret = this->start <= p &&
-	       p <= this->end &&
-	       (unsigned int) (this->end - p) >= len;
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
-		     "%-*d-> range [%p..%p] (%d bytes) in [%p..%p] -> %s",
-		     this->debug_depth, this->debug_depth,
-		     p, p + len, len,
-		     this->start, this->end,
-		     ret ? "pass" : "FAIL");
+    hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+					      "check_range [%p..%p] (%d bytes) in [%p..%p]",
+					      p, p + len, len,
+					      this->start, this->end);
 
-    return likely (ret);
+    return TRACE_RETURN (likely (this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len));
   }
 
   inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
   {
     const char *p = (const char *) base;
     bool overflows = _hb_unsigned_int_mul_overflows (len, record_size);
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
-		     "%-*d-> array [%p..%p] (%d*%d=%ld bytes) in [%p..%p] -> %s",
-		     this->debug_depth, this->debug_depth,
-		     p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
-		     this->start, this->end,
-		     !overflows ? "does not overflow" : "OVERFLOWS FAIL");
+    hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+					      "check_array [%p..%p] (%d*%d=%ld bytes) in [%p..%p]",
+					      p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
+					      this->start, this->end);
 
-    return likely (!overflows && this->check_range (base, record_size * len));
+    return TRACE_RETURN (likely (!overflows && this->check_range (base, record_size * len)));
   }
 
   template <typename Type>
   inline bool check_struct (const Type *obj) const
   {
     return likely (this->check_range (obj, obj->min_size));
   }
 
-  inline bool can_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED)
+  inline bool may_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED)
   {
     const char *p = (const char *) base;
     this->edit_count++;
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
-		     "%-*d-> edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
-		     this->debug_depth, this->debug_depth,
-		     this->edit_count,
-		     p, p + len, len,
-		     this->start, this->end,
-		     this->writable ? "granted" : "REJECTED");
+    hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+					      "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
+					      this->edit_count,
+					      p, p + len, len,
+					      this->start, this->end);
 
-    return this->writable;
+    return TRACE_RETURN (this->writable);
   }
 
-  unsigned int debug_depth;
+  mutable unsigned int debug_depth;
   const char *start, *end;
   bool writable;
   unsigned int edit_count;
   hb_blob_t *blob;
 };
 
 
 
@@ -261,20 +254,20 @@ struct Sanitizer
 
     /* TODO is_sane() stuff */
 
     c->init (blob);
 
   retry:
     DEBUG_MSG_FUNC (SANITIZE, blob, "start");
 
-    c->setup ();
+    c->start_processing ();
 
     if (unlikely (!c->start)) {
-      c->finish ();
+      c->end_processing ();
       return blob;
     }
 
     Type *t = CastP<Type> (const_cast<char *> (c->start));
 
     sane = t->sanitize (c);
     if (sane) {
       if (c->edit_count) {
@@ -298,17 +291,17 @@ struct Sanitizer
 	  c->writable = true;
 	  /* ok, we made it writable by relocating.  try again */
 	  DEBUG_MSG_FUNC (SANITIZE, blob, "retry");
 	  goto retry;
 	}
       }
     }
 
-    c->finish ();
+    c->end_processing ();
 
     DEBUG_MSG_FUNC (SANITIZE, blob, sane ? "PASSED" : "FAILED");
     if (sane)
       return blob;
     else {
       hb_blob_destroy (blob);
       return hb_blob_get_empty ();
     }
@@ -369,17 +362,17 @@ struct IntType
 {
   inline void set (Type i) { v.set (i); }
   inline operator Type(void) const { return v; }
   inline bool operator == (const IntType<Type> &o) const { return v == o.v; }
   inline bool operator != (const IntType<Type> &o) const { return v != o.v; }
   inline int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return likely (c->check_struct (this));
+    return TRACE_RETURN (likely (c->check_struct (this)));
   }
   protected:
   BEInt<Type, sizeof (Type)> v;
   public:
   DEFINE_SIZE_STATIC (sizeof (Type));
 };
 
 /* Typedef these to avoid clash with windows.h */
@@ -399,17 +392,17 @@ typedef SHORT FWORD;
 typedef USHORT UFWORD;
 
 /* Date represented in number of seconds since 12:00 midnight, January 1,
  * 1904. The value is represented as a signed 64-bit integer. */
 struct LONGDATETIME
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return likely (c->check_struct (this));
+    return TRACE_RETURN (likely (c->check_struct (this)));
   }
   private:
   LONG major;
   ULONG minor;
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
@@ -463,17 +456,17 @@ struct CheckSum : ULONG
  */
 
 struct FixedVersion
 {
   inline uint32_t to_int (void) const { return (major << 16) + minor; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   USHORT major;
   USHORT minor;
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
@@ -491,36 +484,36 @@ struct GenericOffsetTo : OffsetType
   {
     unsigned int offset = *this;
     if (unlikely (!offset)) return Null(Type);
     return StructAtOffset<Type> (base, offset);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    if (unlikely (!c->check_struct (this))) return false;
+    if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
     unsigned int offset = *this;
-    if (unlikely (!offset)) return true;
+    if (unlikely (!offset)) return TRACE_RETURN (true);
     Type &obj = StructAtOffset<Type> (base, offset);
-    return likely (obj.sanitize (c)) || neuter (c);
+    return TRACE_RETURN (likely (obj.sanitize (c)) || neuter (c));
   }
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
     TRACE_SANITIZE ();
-    if (unlikely (!c->check_struct (this))) return false;
+    if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
     unsigned int offset = *this;
-    if (unlikely (!offset)) return true;
+    if (unlikely (!offset)) return TRACE_RETURN (true);
     Type &obj = StructAtOffset<Type> (base, offset);
-    return likely (obj.sanitize (c, user_data)) || neuter (c);
+    return TRACE_RETURN (likely (obj.sanitize (c, user_data)) || neuter (c));
   }
 
   private:
   /* Set the offset to Null */
   inline bool neuter (hb_sanitize_context_t *c) {
-    if (c->can_edit (this, this->static_size)) {
+    if (c->may_edit (this, this->static_size)) {
       this->set (0); /* 0 is Null offset */
       return true;
     }
     return false;
   }
 };
 template <typename Base, typename OffsetType, typename Type>
 inline const Type& operator + (const Base &base, GenericOffsetTo<OffsetType, Type> offset) { return offset (base); }
@@ -556,54 +549,53 @@ struct GenericArrayOf
     if (unlikely (i >= len)) return Null(Type);
     return array[i];
   }
   inline unsigned int get_size (void) const
   { return len.static_size + len * Type::static_size; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!sanitize_shallow (c))) return false;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
 
     /* Note: for structs that do not reference other structs,
      * we do not need to call their sanitize() as we already did
      * a bound check on the aggregate array size.  We just include
      * a small unreachable expression to make sure the structs
      * pointed to do have a simple sanitize(), ie. they do not
      * reference other structs via offsets.
      */
     (void) (false && array[0].sanitize (c));
 
-    return true;
+    return TRACE_RETURN (true);
   }
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    if (unlikely (!sanitize_shallow (c))) return false;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!array[i].sanitize (c, base)))
-        return false;
-    return true;
+        return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
   }
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
     TRACE_SANITIZE ();
-    if (unlikely (!sanitize_shallow (c))) return false;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!array[i].sanitize (c, base, user_data)))
-        return false;
-    return true;
+        return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
   }
 
   private:
   inline bool sanitize_shallow (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& c->check_array (this, Type::static_size, len);
+    return TRACE_RETURN (c->check_struct (this) && c->check_array (this, Type::static_size, len));
   }
 
   public:
   LenType len;
   Type array[VAR];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), array);
 };
@@ -635,22 +627,22 @@ struct OffsetListOf : OffsetArrayOf<Type
   inline const Type& operator [] (unsigned int i) const
   {
     if (unlikely (i >= this->len)) return Null(Type);
     return this+this->array[i];
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return OffsetArrayOf<Type>::sanitize (c, this);
+    return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this));
   }
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, T user_data) {
     TRACE_SANITIZE ();
-    return OffsetArrayOf<Type>::sanitize (c, this, user_data);
+    return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this, user_data));
   }
 };
 
 
 /* An array with a USHORT number of elements,
  * starting at second element. */
 template <typename Type>
 struct HeadlessArrayOf
@@ -665,28 +657,28 @@ struct HeadlessArrayOf
 
   inline bool sanitize_shallow (hb_sanitize_context_t *c) {
     return c->check_struct (this)
 	&& c->check_array (this, Type::static_size, len);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!sanitize_shallow (c))) return false;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
 
     /* Note: for structs that do not reference other structs,
      * we do not need to call their sanitize() as we already did
      * a bound check on the aggregate array size.  We just include
      * a small unreachable expression to make sure the structs
      * pointed to do have a simple sanitize(), ie. they do not
      * reference other structs via offsets.
      */
     (void) (false && array[0].sanitize (c));
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   USHORT len;
   Type array[VAR];
   public:
   DEFINE_SIZE_ARRAY (sizeof (USHORT), array);
 };
 
--- a/gfx/harfbuzz/src/hb-ot-head-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-head-table.hh
@@ -1,10 +1,11 @@
 /*
  * Copyright © 2010  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -17,16 +18,17 @@
  *
  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_HEAD_TABLE_HH
 #define HB_OT_HEAD_TABLE_HH
 
 #include "hb-open-type-private.hh"
 
 
@@ -44,17 +46,17 @@ struct head
   inline unsigned int get_upem (void) const {
     unsigned int upem = unitsPerEm;
     /* If no valid head table found, assume 1000, which matches typicaly Type1 usage. */
     return 16 <= upem && upem <= 16384 ? upem : 1000;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this) && likely (version.major == 1);
+    return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
   }
 
   private:
   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
--- a/gfx/harfbuzz/src/hb-ot-hhea-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hhea-table.hh
@@ -1,10 +1,10 @@
 /*
- * 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.
@@ -39,17 +39,17 @@
 
 
 struct hhea
 {
   static const hb_tag_t Tag	= HB_OT_TAG_hhea;
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this) && likely (version.major == 1);
+    return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
   }
 
   private:
   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> */
--- a/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
@@ -1,10 +1,10 @@
 /*
- * 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.
@@ -49,17 +49,17 @@ struct LongHorMetric
 struct hmtx
 {
   static const hb_tag_t Tag	= HB_OT_TAG_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 true;
+    return TRACE_RETURN (true);
   }
 
   private:
   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
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -1,11 +1,11 @@
 /*
  * Copyright © 2007,2008,2009  Red Hat, Inc.
- * Copyright © 2010  Google, Inc.
+ * Copyright © 2010,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,19 +26,19 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #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 NO_CONTEXT		((unsigned int) 0x110000)
 #define NOT_COVERED		((unsigned int) 0x110000)
 #define MAX_NESTING_LEVEL	8
 
 
 
 /*
  *
  * OpenType Layout Common Table Formats
@@ -54,18 +54,17 @@ template <typename Type>
 struct Record
 {
   inline int cmp (hb_tag_t a) const {
     return tag.cmp (a);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& offset.sanitize (c, base);
+    return TRACE_RETURN (c->check_struct (this) && offset.sanitize (c, base));
   }
 
   Tag		tag;		/* 4-byte Tag identifier */
   OffsetTo<Type>
 		offset;		/* Offset from beginning of object holding
 				 * the Record */
   public:
   DEFINE_SIZE_STATIC (6);
@@ -109,31 +108,35 @@ struct RecordArrayOf : SortedArrayOf<Rec
 template <typename Type>
 struct RecordListOf : RecordArrayOf<Type>
 {
   inline const Type& operator [] (unsigned int i) const
   { return this+RecordArrayOf<Type>::operator [](i).offset; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return RecordArrayOf<Type>::sanitize (c, this);
+    return TRACE_RETURN (RecordArrayOf<Type>::sanitize (c, this));
   }
 };
 
 
 struct RangeRecord
 {
   inline int cmp (hb_codepoint_t g) const {
     hb_codepoint_t a = start, b = end;
     return g < a ? -1 : g <= b ? 0 : +1 ;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
+  }
+
+  inline bool intersects (const hb_set_t *glyphs) const {
+    return glyphs->intersects (start, end);
   }
 
   GlyphID	start;		/* First GlyphID in the range */
   GlyphID	end;		/* Last GlyphID in the range */
   USHORT	value;		/* Value */
   public:
   DEFINE_SIZE_STATIC (6);
 };
@@ -178,18 +181,17 @@ struct LangSys
   {
     if (reqFeatureIndex == 0xffff)
       return Index::NOT_FOUND_INDEX;
    return reqFeatureIndex;;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& featureIndex.sanitize (c);
+    return TRACE_RETURN (c->check_struct (this) && featureIndex.sanitize (c));
   }
 
   Offset	lookupOrder;	/* = Null (reserved for an offset to a
 				 * reordering table) */
   USHORT	reqFeatureIndex;/* Index of a feature required for this
 				 * language system--if no required features
 				 * = 0xFFFF */
   IndexArray	featureIndex;	/* Array of indices into the FeatureList */
@@ -217,18 +219,17 @@ struct Script
   inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const
   { return langSys.find_index (tag, index); }
 
   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 defaultLangSys.sanitize (c, this)
-	&& langSys.sanitize (c, this);
+    return TRACE_RETURN (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
   }
 
   private:
   OffsetTo<LangSys>
 		defaultLangSys;	/* Offset to DefaultLangSys table--from
 				 * beginning of Script table--may be Null */
   RecordArrayOf<LangSys>
 		langSys;	/* Array of LangSysRecords--listed
@@ -248,18 +249,17 @@ struct Feature
   { return lookupIndex[i]; }
   inline unsigned int get_lookup_indexes (unsigned int start_index,
 					  unsigned int *lookup_count /* IN/OUT */,
 					  unsigned int *lookup_tags /* OUT */) const
   { return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& lookupIndex.sanitize (c);
+    return TRACE_RETURN (c->check_struct (this) && lookupIndex.sanitize (c));
   }
 
   Offset	featureParams;	/* Offset to Feature Parameters table (if one
 				 * has been defined for the feature), relative
 				 * to the beginning of the Feature Table; = Null
 				 * if not required */
   IndexArray	 lookupIndex;	/* Array of LookupList indices */
   public:
@@ -303,24 +303,23 @@ struct Lookup
       flag += (markFilteringSet << 16);
     }
     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 false;
+    if (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false);
     if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet))
     {
       USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
-      if (!markFilteringSet.sanitize (c)) return false;
+      if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false);
     }
-    return true;
+    return TRACE_RETURN (true);
   }
 
   USHORT	lookupType;		/* Different enumerations for GSUB and GPOS */
   USHORT	lookupFlag;		/* Lookup qualifiers */
   ArrayOf<Offset>
 		subTable;		/* Array of SubTables */
   USHORT	markFilteringSetX[VAR];	/* Index (base 0) into GDEF mark glyph sets
 					 * structure. This field is only present if bit
@@ -346,19 +345,35 @@ struct CoverageFormat1
     int i = glyphArray.search (glyph_id);
     if (i != -1)
         return i;
     return NOT_COVERED;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return glyphArray.sanitize (c);
+    return TRACE_RETURN (glyphArray.sanitize (c));
+  }
+
+  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
+    return glyphs->has (glyphArray[index]);
   }
 
+  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:
   USHORT	coverageFormat;	/* Format identifier--format = 1 */
   SortedArrayOf<GlyphID>
 		glyphArray;	/* Array of GlyphIDs--in numerical order */
   public:
   DEFINE_SIZE_ARRAY (4, glyphArray);
 };
 
@@ -374,19 +389,60 @@ struct CoverageFormat2
       const RangeRecord &range = rangeRecord[i];
       return (unsigned int) range.value + (glyph_id - range.start);
     }
     return NOT_COVERED;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rangeRecord.sanitize (c);
+    return TRACE_RETURN (rangeRecord.sanitize (c));
+  }
+
+  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
+    unsigned int i;
+    unsigned int count = rangeRecord.len;
+    for (i = 0; i < count; i++) {
+      const RangeRecord &range = rangeRecord[i];
+      if (range.value <= index &&
+	  index < (unsigned int) range.value + (range.end - range.start) &&
+	  range.intersects (glyphs))
+        return true;
+      else if (index < range.value)
+        return false;
+    }
+    return false;
   }
 
+  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; }
+    inline void next (void) {
+      coverage++;
+      if (j == c->rangeRecord[i].end) {
+        i++;
+	if (more ())
+	  j = c->rangeRecord[i].start;
+	return;
+      }
+      j++;
+    }
+    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:
   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);
@@ -402,24 +458,89 @@ struct Coverage
     case 1: return u.format1.get_coverage(glyph_id);
     case 2: return u.format2.get_coverage(glyph_id);
     default:return NOT_COVERED;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
+    }
+  }
+
+  inline bool intersects (const hb_set_t *glyphs) const {
+    /* TODO speed this up */
+    Coverage::Iter iter;
+    for (iter.init (*this); iter.more (); iter.next ()) {
+      if (glyphs->has (iter.get_glyph ()))
+        return true;
+    }
+    return false;
+  }
+
+  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;
     }
   }
 
+  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;
+      }
+    }
+    inline bool more (void) {
+      switch (format) {
+      case 1: return u.format1.more ();
+      case 2: return u.format2.more ();
+      default:return true;
+      }
+    }
+    inline void next (void) {
+      switch (format) {
+      case 1: u.format1.next (); break;
+      case 2: u.format2.next (); break;
+      default:                   break;
+      }
+    }
+    inline uint16_t get_glyph (void) {
+      switch (format) {
+      case 1: return u.format1.get_glyph ();
+      case 2: return u.format2.get_glyph ();
+      default:return true;
+      }
+    }
+    inline uint16_t get_coverage (void) {
+      switch (format) {
+      case 1: return u.format1.get_coverage ();
+      case 2: return u.format2.get_coverage ();
+      default:return true;
+      }
+    }
+
+    private:
+    unsigned int format;
+    union {
+    CoverageFormat1::Iter	format1;
+    CoverageFormat2::Iter	format2;
+    } u;
+  };
+
   private:
   union {
   USHORT		format;		/* Format identifier */
   CoverageFormat1	format1;
   CoverageFormat2	format2;
   } u;
   public:
   DEFINE_SIZE_UNION (2, format);
@@ -439,18 +560,25 @@ struct ClassDefFormat1
   {
     if ((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 c->check_struct (this)
-	&& classValue.sanitize (c);
+    return TRACE_RETURN (c->check_struct (this) && classValue.sanitize (c));
+  }
+
+  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;
   }
 
   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);
@@ -466,17 +594,25 @@ struct ClassDefFormat2
     int i = rangeRecord.search (glyph_id);
     if (i != -1)
       return rangeRecord[i].value;
     return 0;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rangeRecord.sanitize (c);
+    return TRACE_RETURN (rangeRecord.sanitize (c));
+  }
+
+  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;
   }
 
   USHORT	classFormat;	/* Format identifier--format = 2 */
   SortedArrayOf<RangeRecord>
 		rangeRecord;	/* Array of glyph ranges--ordered by
 				 * Start GlyphID */
   public:
   DEFINE_SIZE_ARRAY (4, rangeRecord);
@@ -492,21 +628,29 @@ struct ClassDef
     case 1: return u.format1.get_class(glyph_id);
     case 2: return u.format2.get_class(glyph_id);
     default:return 0;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
+    }
+  }
+
+  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:
   union {
   USHORT		format;		/* Format identifier */
   ClassDefFormat1	format1;
   ClassDefFormat2	format2;
@@ -568,18 +712,17 @@ struct Device
   {
     unsigned int f = deltaFormat;
     if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * USHORT::static_size;
     return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f)));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& c->check_range (this, this->get_size ());
+    return TRACE_RETURN (c->check_struct (this) && c->check_range (this, this->get_size ()));
   }
 
   private:
   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
--- a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
@@ -1,11 +1,11 @@
 /*
  * Copyright © 2007,2008,2009  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.
@@ -66,18 +66,17 @@ struct AttachList
 	point_array[i] = array[i];
     }
 
     return points.len;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& attachPoint.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && attachPoint.sanitize (c, this));
   }
 
   private:
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table -- from
 					 * beginning of AttachList table */
   OffsetArrayOf<AttachPoint>
 		attachPoint;		/* Array of AttachPoint tables
@@ -97,17 +96,17 @@ struct CaretValueFormat1
   private:
   inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id HB_UNUSED) const
   {
     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 c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
   USHORT	caretValueFormat;	/* Format identifier--format = 1 */
   SHORT		coordinate;		/* X or Y value, in design units */
   public:
   DEFINE_SIZE_STATIC (4);
 };
@@ -123,17 +122,17 @@ struct CaretValueFormat2
     if (hb_font_get_glyph_contour_point_for_origin (font, 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 c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
   USHORT	caretValueFormat;	/* Format identifier--format = 2 */
   USHORT	caretValuePoint;	/* Contour point index on glyph */
   public:
   DEFINE_SIZE_STATIC (4);
 };
@@ -146,18 +145,17 @@ struct CaretValueFormat3
   {
     return HB_DIRECTION_IS_HORIZONTAL (direction) ?
            font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font) :
            font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& deviceTable.sanitize (c, this);
+    return TRACE_RETURN (c->check_struct (this) && deviceTable.sanitize (c, this));
   }
 
   private:
   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
@@ -175,22 +173,22 @@ struct CaretValue
     case 2: return u.format2.get_caret_value (font, direction, glyph_id);
     case 3: return u.format3.get_caret_value (font, direction, glyph_id);
     default:return 0;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    case 3: return u.format3.sanitize (c);
-    default:return true;
+    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:
   union {
   USHORT		format;		/* Format identifier */
   CaretValueFormat1	format1;
   CaretValueFormat2	format2;
@@ -216,17 +214,17 @@ struct LigGlyph
 	caret_array[i] = (this+array[i]).get_caret_value (font, direction, glyph_id);
     }
 
     return carets.len;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return carets.sanitize (c, this);
+    return TRACE_RETURN (carets.sanitize (c, this));
   }
 
   private:
   OffsetArrayOf<CaretValue>
 		carets;			/* Offset array of CaretValue tables
 					 * --from beginning of LigGlyph table
 					 * --in increasing coordinate order */
   public:
@@ -250,18 +248,17 @@ struct LigCaretList
       return 0;
     }
     const LigGlyph &lig_glyph = this+ligGlyph[index];
     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 coverage.sanitize (c, this)
-	&& ligGlyph.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this));
   }
 
   private:
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of LigCaretList table */
   OffsetArrayOf<LigGlyph>
 		ligGlyph;		/* Array of LigGlyph tables
@@ -273,17 +270,17 @@ struct LigCaretList
 
 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 coverage.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 1 */
   LongOffsetArrayOf<Coverage>
 		coverage;		/* Array of long offsets to mark set
 					 * coverage tables */
   public:
@@ -297,20 +294,20 @@ struct MarkGlyphSets
     switch (u.format) {
     case 1: return u.format1.covers (set_index, glyph_id);
     default:return false;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   MarkGlyphSetsFormat1	format1;
   } u;
@@ -360,22 +357,23 @@ struct GDEF
   { return (this+ligCaretList).get_lig_carets (font, direction, glyph_id, start_offset, caret_count, caret_array); }
 
   inline bool has_mark_sets (void) const { return version.to_int () >= 0x00010002 && markGlyphSetsDef[0] != 0; }
   inline bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const
   { return version.to_int () >= 0x00010002 && (this+markGlyphSetsDef[0]).covers (set_index, glyph_id); }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return version.sanitize (c) && likely (version.major == 1)
-	&& glyphClassDef.sanitize (c, this)
-	&& attachList.sanitize (c, this)
-	&& ligCaretList.sanitize (c, this)
-	&& markAttachClassDef.sanitize (c, this)
-	&& (version.to_int () < 0x00010002 || markGlyphSetsDef[0].sanitize (c, this));
+    return TRACE_RETURN (version.sanitize (c) &&
+			 likely (version.major == 1) &&
+			 glyphClassDef.sanitize (c, this) &&
+			 attachList.sanitize (c, this) &&
+			 ligCaretList.sanitize (c, this) &&
+			 markAttachClassDef.sanitize (c, this) &&
+			 (version.to_int () < 0x00010002 || markGlyphSetsDef[0].sanitize (c, this)));
   }
 
 
   /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing
    * glyph class and other bits, and high 8-bit gthe mark attachment type (if any).
    * Not to be confused with lookup_props which is very similar. */
   inline unsigned int get_glyph_props (hb_codepoint_t glyph) const
   {
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -1,11 +1,11 @@
 /*
  * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
- * Copyright © 2010  Google, Inc.
+ * Copyright © 2010,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.
@@ -166,50 +166,49 @@ struct ValueFormat : USHORT
 
   inline bool has_device (void) const {
     unsigned int format = *this;
     return (format & devices) != 0;
   }
 
   inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) {
     TRACE_SANITIZE ();
-    return c->check_range (values, get_size ())
-	&& (!has_device () || sanitize_value_devices (c, base, values));
+    return TRACE_RETURN (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
   }
 
   inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) {
     TRACE_SANITIZE ();
     unsigned int len = get_len ();
 
-    if (!c->check_array (values, get_size (), count)) return false;
+    if (!c->check_array (values, get_size (), count)) return TRACE_RETURN (false);
 
-    if (!has_device ()) return true;
+    if (!has_device ()) return TRACE_RETURN (true);
 
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
-        return false;
+        return TRACE_RETURN (false);
       values += len;
     }
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
   inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) {
     TRACE_SANITIZE ();
 
-    if (!has_device ()) return true;
+    if (!has_device ()) return TRACE_RETURN (true);
 
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
-        return false;
+        return TRACE_RETURN (false);
       values += stride;
     }
 
-    return true;
+    return TRACE_RETURN (true);
   }
 };
 
 
 struct AnchorFormat1
 {
   friend struct Anchor;
 
@@ -218,17 +217,17 @@ struct AnchorFormat1
 			  hb_position_t *x, hb_position_t *y) const
   {
       *x = font->em_scale_x (xCoordinate);
       *y = font->em_scale_y (yCoordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
   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);
@@ -250,17 +249,17 @@ struct AnchorFormat2
       if (x_ppem || y_ppem)
 	ret = hb_font_get_glyph_contour_point_for_origin (font, 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 c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
   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:
@@ -281,19 +280,17 @@ struct AnchorFormat3
       if (font->x_ppem)
 	*x += (this+xDeviceTable).get_x_delta (font);
       if (font->y_ppem)
 	*y += (this+yDeviceTable).get_x_delta (font);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& xDeviceTable.sanitize (c, this)
-	&& yDeviceTable.sanitize (c, this);
+    return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
   }
 
   private:
   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
@@ -318,22 +315,22 @@ struct Anchor
     case 2: u.format2.get_anchor (font, glyph_id, x, y); return;
     case 3: u.format3.get_anchor (font, glyph_id, x, y); return;
     default:						 return;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    case 3: return u.format3.sanitize (c);
-    default:return true;
+    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:
   union {
   USHORT		format;		/* Format identifier */
   AnchorFormat1		format1;
   AnchorFormat2		format2;
@@ -348,23 +345,23 @@ struct AnchorMatrix
 {
   inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
     if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
     return this+matrix[row * cols + col];
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
     TRACE_SANITIZE ();
-    if (!c->check_struct (this)) return false;
-    if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return false;
+    if (!c->check_struct (this)) return TRACE_RETURN (false);
+    if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false);
     unsigned int count = rows * cols;
-    if (!c->check_array (matrix, matrix[0].static_size, count)) return false;
+    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 false;
-    return true;
+      if (!matrix[i].sanitize (c, this)) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
   }
 
   USHORT	rows;			/* Number of rows */
   private:
   OffsetTo<Anchor>
 		matrix[VAR];		/* Matrix of offsets to Anchor tables--
 					 * from beginning of AnchorMatrix table */
   public:
@@ -373,18 +370,17 @@ struct AnchorMatrix
 
 
 struct MarkRecord
 {
   friend struct MarkArray;
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& markAnchor.sanitize (c, base);
+    return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base));
   }
 
   private:
   USHORT	klass;			/* Class defined for this mark */
   OffsetTo<Anchor>
 		markAnchor;		/* Offset to Anchor table--from
 					 * beginning of MarkArray table */
   public:
@@ -402,61 +398,58 @@ struct MarkArray : ArrayOf<MarkRecord>	/
     const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
     unsigned int mark_class = record.klass;
 
     const Anchor& mark_anchor = this + record.markAnchor;
     const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
 
     hb_position_t mark_x, mark_y, base_x, base_y;
 
-    mark_anchor.get_anchor (c->font, c->buffer->info[c->buffer->idx].codepoint, &mark_x, &mark_y);
+    mark_anchor.get_anchor (c->font, c->buffer->cur().codepoint, &mark_x, &mark_y);
     glyph_anchor.get_anchor (c->font, c->buffer->info[glyph_pos].codepoint, &base_x, &base_y);
 
-    hb_glyph_position_t &o = c->buffer->pos[c->buffer->idx];
+    hb_glyph_position_t &o = c->buffer->cur_pos();
     o.x_offset = base_x - mark_x;
     o.y_offset = base_y - mark_y;
     o.attach_lookback() = c->buffer->idx - glyph_pos;
 
     c->buffer->idx++;
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return ArrayOf<MarkRecord>::sanitize (c, this);
+    return TRACE_RETURN (ArrayOf<MarkRecord>::sanitize (c, this));
   }
 };
 
 
 /* Lookups */
 
 struct SinglePosFormat1
 {
   friend struct SinglePos;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    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->pos[c->buffer->idx]);
+			     values, c->buffer->cur_pos());
 
     c->buffer->idx++;
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& coverage.sanitize (c, this)
-	&& valueFormat.sanitize_value (c, this, values);
+    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_value (c, this, values));
   }
 
   private:
   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
@@ -471,36 +464,32 @@ struct SinglePosFormat1
 struct SinglePosFormat2
 {
   friend struct SinglePos;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    if (likely (index >= valueCount))
-      return false;
+    if (likely (index >= valueCount)) return TRACE_RETURN (false);
 
     valueFormat.apply_value (c->font, c->direction, this,
 			     &values[index * valueFormat.get_len ()],
-			     c->buffer->pos[c->buffer->idx]);
+			     c->buffer->cur_pos());
 
     c->buffer->idx++;
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& coverage.sanitize (c, this)
-	&& valueFormat.sanitize_values (c, this, values, valueCount);
+    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_values (c, this, values, valueCount));
   }
 
   private:
   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
@@ -516,29 +505,29 @@ struct SinglePos
 {
   friend struct PosLookupSubTable;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    case 2: return u.format2.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    case 2: return TRACE_RETURN (u.format2.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   SinglePosFormat1	format1;
   SinglePosFormat2	format2;
@@ -575,46 +564,46 @@ struct PairSet
 
     unsigned int count = len;
     const PairValueRecord *record = CastP<PairValueRecord> (array);
     for (unsigned int i = 0; i < count; i++)
     {
       if (c->buffer->info[pos].codepoint == record->secondGlyph)
       {
 	valueFormats[0].apply_value (c->font, c->direction, this,
-				     &record->values[0], c->buffer->pos[c->buffer->idx]);
+				     &record->values[0], c->buffer->cur_pos());
 	valueFormats[1].apply_value (c->font, c->direction, this,
 				     &record->values[len1], c->buffer->pos[pos]);
 	if (len2)
 	  pos++;
 	c->buffer->idx = pos;
-	return true;
+	return TRACE_RETURN (true);
       }
       record = &StructAtOffset<PairValueRecord> (record, record_size);
     }
 
-    return false;
+    return TRACE_RETURN (false);
   }
 
   struct sanitize_closure_t {
     void *base;
     ValueFormat *valueFormats;
     unsigned int len1; /* valueFormats[0].get_len() */
     unsigned int stride; /* 1 + len1 + len2 */
   };
 
   inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
     TRACE_SANITIZE ();
     if (!(c->check_struct (this)
-       && c->check_array (array, USHORT::static_size * closure->stride, len))) return false;
+       && c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
 
     unsigned int count = len;
     PairValueRecord *record = CastP<PairValueRecord> (array);
-    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);
+    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:
   USHORT	len;			/* Number of PairValueRecords */
   USHORT	array[VAR];		/* Array of PairValueRecords--ordered
 					 * by GlyphID of the second glyph */
   public:
   DEFINE_SIZE_ARRAY (2, array);
@@ -624,44 +613,39 @@ struct PairPosFormat1
 {
   friend struct PairPos;
 
   private:
   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 false;
+    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    if (!skippy_iter.next ())
-      return false;
+    if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
-    return (this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx);
+    return TRACE_RETURN ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
 
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
     PairSet::sanitize_closure_t closure = {
       this,
       &valueFormat1,
       len1,
       1 + len1 + len2
     };
 
-    return c->check_struct (this)
-	&& coverage.sanitize (c, this)
-	&& pairSet.sanitize (c, this, &closure);
+    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
   }
 
   private:
   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
@@ -681,63 +665,59 @@ struct PairPosFormat2
 {
   friend struct PairPos;
 
   private:
   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 false;
+    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    if (!skippy_iter.next ())
-      return false;
+    if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
     unsigned int record_len = len1 + len2;
 
-    unsigned int klass1 = (this+classDef1) (c->buffer->info[c->buffer->idx].codepoint);
+    unsigned int klass1 = (this+classDef1) (c->buffer->cur().codepoint);
     unsigned int klass2 = (this+classDef2) (c->buffer->info[skippy_iter.idx].codepoint);
-    if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
-      return false;
+    if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return TRACE_RETURN (false);
 
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
     valueFormat1.apply_value (c->font, c->direction, this,
-			      v, c->buffer->pos[c->buffer->idx]);
+			      v, c->buffer->cur_pos());
     valueFormat2.apply_value (c->font, c->direction, this,
 			      v + len1, c->buffer->pos[skippy_iter.idx]);
 
     c->buffer->idx = skippy_iter.idx;
     if (len2)
       c->buffer->idx++;
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (!(c->check_struct (this)
        && coverage.sanitize (c, this)
        && classDef1.sanitize (c, this)
-       && classDef2.sanitize (c, this))) return false;
+       && classDef2.sanitize (c, this))) return TRACE_RETURN (false);
 
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
     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 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);
+    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:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ValueFormat	valueFormat1;		/* ValueRecord definition--for the
@@ -769,29 +749,29 @@ struct PairPos
 {
   friend struct PosLookupSubTable;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    case 2: return u.format2.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    case 2: return TRACE_RETURN (u.format2.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   PairPosFormat1	format1;
   PairPosFormat2	format2;
@@ -800,18 +780,17 @@ struct PairPos
 
 
 struct EntryExitRecord
 {
   friend struct CursivePosFormat1;
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    return entryAnchor.sanitize (c, base)
-	&& exitAnchor.sanitize (c, base);
+    return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
   }
 
   private:
   OffsetTo<Anchor>
 		entryAnchor;		/* Offset to EntryAnchor table--from
 					 * beginning of CursivePos
 					 * subtable--may be NULL */
   OffsetTo<Anchor>
@@ -827,33 +806,28 @@ struct CursivePosFormat1
   friend struct CursivePos;
 
   private:
   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 false;
+    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);
-    if (skippy_iter.has_no_chance ())
-      return false;
+    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->info[c->buffer->idx].codepoint)];
-    if (!this_record.exitAnchor)
-      return false;
+    const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->cur().codepoint)];
+    if (!this_record.exitAnchor) return TRACE_RETURN (false);
 
-    if (!skippy_iter.next ())
-      return false;
+    if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
     const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[skippy_iter.idx].codepoint)];
-    if (!next_record.entryAnchor)
-      return false;
+    if (!next_record.entryAnchor) return TRACE_RETURN (false);
 
     unsigned int i = c->buffer->idx;
     unsigned int j = skippy_iter.idx;
 
     hb_position_t entry_x, entry_y, exit_x, exit_y;
     (this+this_record.exitAnchor).get_anchor (c->font, c->buffer->info[i].codepoint, &exit_x, &exit_y);
     (this+next_record.entryAnchor).get_anchor (c->font, c->buffer->info[j].codepoint, &entry_x, &entry_y);
 
@@ -906,23 +880,22 @@ struct CursivePosFormat1
       pos[j].cursive_chain() = i - j;
       if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
 	pos[j].y_offset = exit_y - entry_y;
       else
 	pos[j].x_offset = exit_x - entry_x;
     }
 
     c->buffer->idx = j;
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& entryExitRecord.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ArrayOf<EntryExitRecord>
@@ -936,27 +909,27 @@ struct CursivePos
 {
   friend struct PosLookupSubTable;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   CursivePosFormat1	format1;
   } u;
@@ -971,44 +944,37 @@ typedef AnchorMatrix BaseArray;		/* base
 struct MarkBasePosFormat1
 {
   friend struct MarkBasePos;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (mark_index == NOT_COVERED))
-      return false;
+    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 false;
+    if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false);
 
     /* The following assertion is too strong, so we've disabled it. */
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
-    {/*return false;*/}
+    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 false;
+    if (base_index == NOT_COVERED) return TRACE_RETURN (false);
 
-    return (this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx);
+    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 c->check_struct (this)
-        && markCoverage.sanitize (c, this)
-	&& baseCoverage.sanitize (c, this)
-	&& markArray.sanitize (c, this)
-	&& baseArray.sanitize (c, this, (unsigned int) classCount);
+    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:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		markCoverage;		/* Offset to MarkCoverage table--from
 					 * beginning of MarkBasePos subtable */
   OffsetTo<Coverage>
@@ -1029,27 +995,27 @@ struct MarkBasePos
 {
   friend struct PosLookupSubTable;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   MarkBasePosFormat1	format1;
   } u;
@@ -1069,66 +1035,61 @@ typedef OffsetListOf<LigatureAttach> Lig
 struct MarkLigPosFormat1
 {
   friend struct MarkLigPos;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (mark_index == NOT_COVERED))
-      return false;
+    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 false;
+    if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false);
 
     /* The following assertion is too strong, so we've disabled it. */
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
-    {/*return false;*/}
+    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)) {/*return TRACE_RETURN (false);*/}
 
     unsigned int j = skippy_iter.idx;
     unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
-    if (lig_index == NOT_COVERED)
-      return false;
+    if (lig_index == NOT_COVERED) return TRACE_RETURN (false);
 
     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 false;
+    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 (c->buffer->info[j].lig_id() && c->buffer->info[j].lig_id() == c->buffer->info[c->buffer->idx].lig_id() && c->buffer->info[c->buffer->idx].lig_comp())
+    if (get_lig_id (c->buffer->info[j]) &&
+	get_lig_id (c->buffer->cur()) &&
+	get_lig_comp (c->buffer->cur()) > 0)
     {
-      comp_index = c->buffer->info[c->buffer->idx].lig_comp() - 1;
+      comp_index = get_lig_comp (c->buffer->cur()) - 1;
       if (comp_index >= comp_count)
 	comp_index = comp_count - 1;
     }
     else
       comp_index = comp_count - 1;
 
-    return (this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j);
+    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 c->check_struct (this)
-        && markCoverage.sanitize (c, this)
-	&& ligatureCoverage.sanitize (c, this)
-	&& markArray.sanitize (c, this)
-	&& ligatureArray.sanitize (c, this, (unsigned int) classCount);
+    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:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		markCoverage;		/* Offset to Mark Coverage table--from
 					 * beginning of MarkLigPos subtable */
   OffsetTo<Coverage>
@@ -1150,27 +1111,27 @@ struct MarkLigPos
 {
   friend struct PosLookupSubTable;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   MarkLigPosFormat1	format1;
   } u;
@@ -1185,52 +1146,47 @@ typedef AnchorMatrix Mark2Array;	/* mark
 struct MarkMarkPosFormat1
 {
   friend struct MarkMarkPos;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    unsigned int mark1_index = (this+mark1Coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (mark1_index == NOT_COVERED))
-      return false;
+    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 false;
+    if (!skippy_iter.prev (&property)) return TRACE_RETURN (false);
 
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
-      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 ((c->buffer->info[j].lig_comp() != c->buffer->info[c->buffer->idx].lig_comp()) ||
-	(c->buffer->info[j].lig_comp() && c->buffer->info[j].lig_id() != c->buffer->info[c->buffer->idx].lig_id()))
-      return false;
+    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 mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
-    if (mark2_index == NOT_COVERED)
-      return false;
+    if (mark2_index == NOT_COVERED) return TRACE_RETURN (false);
 
-    return (this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j);
+    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 c->check_struct (this)
-	&& mark1Coverage.sanitize (c, this)
-	&& mark2Coverage.sanitize (c, this)
-	&& mark1Array.sanitize (c, this)
-	&& mark2Array.sanitize (c, this, (unsigned int) classCount);
+    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:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		mark1Coverage;		/* Offset to Combining Mark1 Coverage
 					 * table--from beginning of MarkMarkPos
 					 * subtable */
@@ -1253,27 +1209,27 @@ struct MarkMarkPos
 {
   friend struct PosLookupSubTable;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   MarkMarkPosFormat1	format1;
   } u;
@@ -1285,29 +1241,29 @@ static inline bool position_lookup (hb_a
 struct ContextPos : Context
 {
   friend struct PosLookupSubTable;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    return Context::apply (c, position_lookup);
+    return TRACE_RETURN (Context::apply (c, position_lookup));
   }
 };
 
 struct ChainContextPos : ChainContext
 {
   friend struct PosLookupSubTable;
 
   private:
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    return ChainContext::apply (c, position_lookup);
+    return TRACE_RETURN (ChainContext::apply (c, position_lookup));
   }
 };
 
 
 struct ExtensionPos : Extension
 {
   friend struct PosLookupSubTable;
 
@@ -1346,42 +1302,42 @@ struct PosLookupSubTable
     ChainContext	= 8,
     Extension		= 9
   };
 
   inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
   {
     TRACE_APPLY ();
     switch (lookup_type) {
-    case Single:		return u.single.apply (c);
-    case Pair:			return u.pair.apply (c);
-    case Cursive:		return u.cursive.apply (c);
-    case MarkBase:		return u.markBase.apply (c);
-    case MarkLig:		return u.markLig.apply (c);
-    case MarkMark:		return u.markMark.apply (c);
-    case Context:		return u.c.apply (c);
-    case ChainContext:		return u.chainContext.apply (c);
-    case Extension:		return u.extension.apply (c);
-    default:return false;
+    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 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 ();
     switch (lookup_type) {
-    case Single:		return u.single.sanitize (c);
-    case Pair:			return u.pair.sanitize (c);
-    case Cursive:		return u.cursive.sanitize (c);
-    case MarkBase:		return u.markBase.sanitize (c);
-    case MarkLig:		return u.markLig.sanitize (c);
-    case MarkMark:		return u.markMark.sanitize (c);
-    case Context:		return u.c.sanitize (c);
-    case ChainContext:		return u.chainContext.sanitize (c);
-    case Extension:		return u.extension.sanitize (c);
-    default:return true;
+    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 ChainContext:		return TRACE_RETURN (u.chainContext.sanitize (c));
+    case Extension:		return TRACE_RETURN (u.extension.sanitize (c));
+    default:			return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		sub_format;
   SinglePos		single;
   PairPos		pair;
@@ -1402,84 +1358,79 @@ struct PosLookup : Lookup
 {
   inline const PosLookupSubTable& get_subtable (unsigned int i) const
   { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
 
   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->info[c->buffer->idx], c->lookup_props, &c->property))
+    if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property))
       return false;
 
     for (unsigned int i = 0; i < get_subtable_count (); i++)
       if (get_subtable (i).apply (c, lookup_type))
 	return true;
 
     return false;
   }
 
-   inline bool apply_string (hb_font_t   *font,
-			     hb_buffer_t *buffer,
-			     hb_mask_t    mask) const
+  inline bool apply_string (hb_apply_context_t *c) const
   {
     bool ret = false;
 
-    if (unlikely (!buffer->len))
+    if (unlikely (!c->buffer->len))
       return false;
 
-    hb_apply_context_t c (font, font->face, buffer, mask, *this);
+    c->set_lookup (*this);
 
-    buffer->idx = 0;
-    while (buffer->idx < buffer->len)
+    c->buffer->idx = 0;
+    while (c->buffer->idx < c->buffer->len)
     {
-      if ((buffer->info[buffer->idx].mask & mask) && apply_once (&c))
+      if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
 	ret = true;
       else
-	buffer->idx++;
+	c->buffer->idx++;
     }
 
     return ret;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!Lookup::sanitize (c))) return false;
+    if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
     OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
-    return list.sanitize (c, this, get_type ());
+    return TRACE_RETURN (list.sanitize (c, this, get_type ()));
   }
 };
 
 typedef OffsetListOf<PosLookup> PosLookupList;
 
 /*
  * GPOS -- The Glyph Positioning Table
  */
 
 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)); }
 
-  inline bool position_lookup (hb_font_t    *font,
-			       hb_buffer_t  *buffer,
-			       unsigned int  lookup_index,
-			       hb_mask_t     mask) const
-  { return get_lookup (lookup_index).apply_string (font, buffer, mask); }
+  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);
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!GSUBGPOS::sanitize (c))) return false;
+    if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
     OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
-    return list.sanitize (c, this);
+    return TRACE_RETURN (list.sanitize (c, this));
   }
   public:
   DEFINE_SIZE_STATIC (10);
 };
 
 
 static void
 fix_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction)
@@ -1545,51 +1496,50 @@ GPOS::position_finish (hb_buffer_t *buff
   /* 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);
 
-  HB_BUFFER_DEALLOCATE_VAR (buffer, lig_comp);
-  HB_BUFFER_DEALLOCATE_VAR (buffer, lig_id);
+  HB_BUFFER_DEALLOCATE_VAR (buffer, syllable);
+  HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props);
   HB_BUFFER_DEALLOCATE_VAR (buffer, props_cache);
 }
 
 
 /* Out-of-class implementation for methods recursing */
 
 inline bool ExtensionPos::apply (hb_apply_context_t *c) const
 {
   TRACE_APPLY ();
-  return get_subtable ().apply (c, get_type ());
+  return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
 }
 
 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
 {
   TRACE_SANITIZE ();
-  if (unlikely (!Extension::sanitize (c))) return false;
+  if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
   unsigned int offset = get_offset ();
-  if (unlikely (!offset)) return true;
-  return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ());
+  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 PosLookup &l = gpos.get_lookup (lookup_index);
 
   if (unlikely (c->nesting_level_left == 0))
     return false;
 
-  if (unlikely (c->context_length < 1))
-    return false;
-
-  hb_apply_context_t new_c (*c, l);
+  hb_apply_context_t new_c (*c);
+  new_c.nesting_level_left--;
+  new_c.set_lookup (l);
   return l.apply_once (&new_c);
 }
 
 
 #undef attach_lookback
 #undef cursive_chain
 
 
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -1,11 +1,11 @@
 /*
  * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
- * Copyright © 2010  Google, Inc.
+ * Copyright © 2010,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.
@@ -34,36 +34,50 @@
 
 
 struct SingleSubstFormat1
 {
   friend struct SingleSubst;
 
   private:
 
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    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
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
+    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
     unsigned int index = (this+coverage) (glyph_id);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* According to the Adobe Annotated OpenType Suite, result is always
      * limited to 16bit. */
     glyph_id = (glyph_id + deltaGlyphID) & 0xFFFF;
     c->replace_glyph (glyph_id);
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& deltaGlyphID.sanitize (c);
+    return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
   }
 
   private:
   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
@@ -73,37 +87,49 @@ struct SingleSubstFormat1
 };
 
 struct SingleSubstFormat2
 {
   friend struct SingleSubst;
 
   private:
 
+  inline void closure (hb_closure_context_t *c) const
+  {
+    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
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
+    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
     unsigned int index = (this+coverage) (glyph_id);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    if (unlikely (index >= substitute.len))
-      return false;
+    if (unlikely (index >= substitute.len)) return TRACE_RETURN (false);
 
     glyph_id = substitute[index];
     c->replace_glyph (glyph_id);
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& substitute.sanitize (c);
+    return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   ArrayOf<GlyphID>
@@ -114,33 +140,52 @@ struct SingleSubstFormat2
 };
 
 struct SingleSubst
 {
   friend struct SubstLookupSubTable;
 
   private:
 
+  inline void closure (hb_closure_context_t *c) const
+  {
+    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
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (glyph_id);
+    case 2: return u.format2.would_apply (glyph_id);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    case 2: return u.format2.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    case 2: return TRACE_RETURN (u.format2.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   SingleSubstFormat1	format1;
   SingleSubstFormat2	format2;
@@ -148,63 +193,83 @@ struct SingleSubst
 };
 
 
 struct Sequence
 {
   friend struct MultipleSubstFormat1;
 
   private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    unsigned int count = substitute.len;
+    for (unsigned int i = 0; i < count; i++)
+      c->glyphs->add (substitute[i]);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    if (unlikely (!substitute.len))
-      return false;
+    if (unlikely (!substitute.len)) return TRACE_RETURN (false);
 
-    if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
-      c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH);
-    c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array);
+    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);
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return substitute.sanitize (c);
+    return TRACE_RETURN (substitute.sanitize (c));
   }
 
   private:
   ArrayOf<GlyphID>
 		substitute;		/* String of GlyphIDs to substitute */
   public:
   DEFINE_SIZE_ARRAY (2, substitute);
 };
 
 struct MultipleSubstFormat1
 {
   friend struct MultipleSubst;
 
   private:
 
+  inline void closure (hb_closure_context_t *c) const
+  {
+    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
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
 
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    return (this+sequence[index]).apply (c);
+    return TRACE_RETURN ((this+sequence[index]).apply (c));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& sequence.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   OffsetArrayOf<Sequence>
@@ -215,31 +280,48 @@ struct MultipleSubstFormat1
 };
 
 struct MultipleSubst
 {
   friend struct SubstLookupSubTable;
 
   private:
 
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: u.format1.closure (c); break;
+    default:                       break;
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (glyph_id);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   MultipleSubstFormat1	format1;
   } u;
@@ -250,50 +332,66 @@ typedef ArrayOf<GlyphID> AlternateSet;	/
 					 * arbitrary order */
 
 struct AlternateSubstFormat1
 {
   friend struct AlternateSubst;
 
   private:
 
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      if (c->glyphs->has (iter.get_glyph ())) {
+	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
+  {
+    return (this+coverage) (glyph_id) != NOT_COVERED;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
-    hb_mask_t glyph_mask = c->buffer->info[c->buffer->idx].mask;
-    hb_mask_t lookup_mask = c->lookup_mask;
+    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
     unsigned int index = (this+coverage) (glyph_id);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const AlternateSet &alt_set = this+alternateSet[index];
 
-    if (unlikely (!alt_set.len))
-      return false;
+    if (unlikely (!alt_set.len)) return TRACE_RETURN (false);
+
+    hb_mask_t glyph_mask = c->buffer->cur().mask;
+    hb_mask_t lookup_mask = c->lookup_mask;
 
     /* Note: This breaks badly if two features enabled this lookup together. */
     unsigned int shift = _hb_ctz (lookup_mask);
     unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
 
-    if (unlikely (alt_index > alt_set.len || alt_index == 0))
-      return false;
+    if (unlikely (alt_index > alt_set.len || alt_index == 0)) return TRACE_RETURN (false);
 
     glyph_id = alt_set[alt_index - 1];
 
     c->replace_glyph (glyph_id);
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& alternateSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   OffsetArrayOf<AlternateSet>
@@ -304,119 +402,144 @@ struct AlternateSubstFormat1
 };
 
 struct AlternateSubst
 {
   friend struct SubstLookupSubTable;
 
   private:
 
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: u.format1.closure (c); break;
+    default:                       break;
+    }
+  }
+
+  inline bool would_apply (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (glyph_id);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   AlternateSubstFormat1	format1;
   } u;
 };
 
 
 struct Ligature
 {
   friend struct LigatureSet;
 
   private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    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
+  {
+    return component.len == 2 && component[1] == second;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     unsigned int count = component.len;
-    if (unlikely (count < 2))
-      return false;
+    if (unlikely (count < 2)) 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 false;
+    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;
 
     for (unsigned int i = 1; i < count; i++)
     {
       unsigned int property;
 
-      if (!skippy_iter.next (&property))
-	return false;
+      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 false;
+      if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i])) return TRACE_RETURN (false);
     }
 
-    if (first_was_mark && found_non_mark)
-      c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
+    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);
-    c->buffer->info[c->buffer->idx].lig_comp() = 0;
-    c->buffer->info[c->buffer->idx].lig_id() = lig_id;
+    set_lig_props (c->buffer->cur(), lig_id, 0);
 
     if (skippy_iter.idx < c->buffer->idx + count) /* No input glyphs skipped */
     {
-      c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph);
+      c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph, klass);
     }
     else
     {
       c->replace_glyph (ligGlyph);
 
       /* 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++)
       {
 	while (c->should_mark_skip_current_glyph ())
 	{
-	  c->buffer->info[c->buffer->idx].lig_comp() = i;
-	  c->buffer->info[c->buffer->idx].lig_id() = lig_id;
-	  c->replace_glyph (c->buffer->info[c->buffer->idx].codepoint);
+	  set_lig_props (c->buffer->cur(),  lig_id, i);
+	  c->replace_glyph (c->buffer->cur().codepoint);
 	}
 
 	/* Skip the base glyph */
 	c->buffer->idx++;
       }
     }
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return ligGlyph.sanitize (c)
-        && component.sanitize (c);
+    return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c));
   }
 
   private:
   GlyphID	ligGlyph;		/* GlyphID of ligature to substitute */
   HeadlessArrayOf<GlyphID>
 		component;		/* Array of component GlyphIDs--start
 					 * with the second  component--ordered
 					 * in writing direction */
@@ -424,66 +547,102 @@ struct Ligature
   DEFINE_SIZE_ARRAY (4, component);
 };
 
 struct LigatureSet
 {
   friend struct LigatureSubstFormat1;
 
   private:
+
+  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
+  {
+    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))
+        return true;
+    }
+    return false;
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     unsigned int num_ligs = ligature.len;
     for (unsigned int i = 0; i < num_ligs; i++)
     {
       const Ligature &lig = this+ligature[i];
-      if (lig.apply (c))
-        return true;
+      if (lig.apply (c)) return TRACE_RETURN (true);
     }
 
-    return false;
+    return TRACE_RETURN (false);
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return ligature.sanitize (c, this);
+    return TRACE_RETURN (ligature.sanitize (c, this));
   }
 
   private:
   OffsetArrayOf<Ligature>
 		ligature;		/* Array LigatureSet tables
 					 * ordered by preference */
   public:
   DEFINE_SIZE_ARRAY (2, ligature);
 };
 
 struct LigatureSubstFormat1
 {
   friend struct LigatureSubst;
 
   private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    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
+  {
+    unsigned int index;
+    return (index = (this+coverage) (first)) != NOT_COVERED &&
+	   (this+ligatureSet[index]).would_apply (second);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
+    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
     unsigned int index = (this+coverage) (glyph_id);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const LigatureSet &lig_set = this+ligatureSet[index];
-    return lig_set.apply (c);
+    return TRACE_RETURN (lig_set.apply (c));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& ligatureSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
   OffsetArrayOf<LigatureSet>
@@ -493,65 +652,98 @@ struct LigatureSubstFormat1
   DEFINE_SIZE_ARRAY (6, ligatureSet);
 };
 
 struct LigatureSubst
 {
   friend struct SubstLookupSubTable;
 
   private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    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
+  {
+    switch (u.format) {
+    case 1: return u.format1.would_apply (first, second);
+    default:return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   LigatureSubstFormat1	format1;
   } u;
 };
 
 
 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
+static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index);
 
 struct ContextSubst : Context
 {
   friend struct SubstLookupSubTable;
 
   private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    return Context::closure (c, closure_lookup);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    return Context::apply (c, substitute_lookup);
+    return TRACE_RETURN (Context::apply (c, substitute_lookup));
   }
 };
 
 struct ChainContextSubst : ChainContext
 {
   friend struct SubstLookupSubTable;
 
   private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    return ChainContext::closure (c, closure_lookup);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    return ChainContext::apply (c, substitute_lookup);
+    return TRACE_RETURN (ChainContext::apply (c, substitute_lookup));
   }
 };
 
 
 struct ExtensionSubst : Extension
 {
   friend struct SubstLookupSubTable;
   friend struct SubstLookup;
@@ -559,68 +751,96 @@ struct ExtensionSubst : Extension
   private:
   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 bool apply (hb_apply_context_t *c) const;
 
   inline bool sanitize (hb_sanitize_context_t *c);
 
   inline bool is_reverse (void) const;
 };
 
 
 struct ReverseChainSingleSubstFormat1
 {
   friend struct ReverseChainSingleSubst;
 
   private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+    unsigned int count;
+
+    count = backtrack.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+backtrack[i]).intersects (c->glyphs))
+        return;
+
+    count = lookahead.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+lookahead[i]).intersects (c->glyphs))
+        return;
+
+    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 bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    if (unlikely (c->context_length != NO_CONTEXT))
-      return false; /* No chaining to this type */
+    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->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
 
     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->info[c->buffer->idx].codepoint = substitute[index];
+      c->buffer->cur().codepoint = substitute[index];
       c->buffer->idx--; /* Reverse! */
-      return true;
+      return TRACE_RETURN (true);
     }
 
-    return false;
+    return TRACE_RETURN (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!(coverage.sanitize (c, this)
-       && backtrack.sanitize (c, this)))
-      return false;
+    if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
+      return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     if (!lookahead.sanitize (c, this))
-      return false;
+      return TRACE_RETURN (false);
     ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
-    return substitute.sanitize (c);
+    return TRACE_RETURN (substitute.sanitize (c));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetArrayOf<Coverage>
@@ -638,31 +858,41 @@ struct ReverseChainSingleSubstFormat1
   DEFINE_SIZE_MIN (10);
 };
 
 struct ReverseChainSingleSubst
 {
   friend struct SubstLookupSubTable;
 
   private:
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE ();
+    switch (u.format) {
+    case 1: u.format1.closure (c); break;
+    default:                       break;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT				format;		/* Format identifier */
   ReverseChainSingleSubstFormat1	format1;
   } u;
@@ -684,44 +914,83 @@ struct SubstLookupSubTable
     Alternate		= 3,
     Ligature		= 4,
     Context		= 5,
     ChainContext	= 6,
     Extension		= 7,
     ReverseChainSingle	= 8
   };
 
+  inline void closure (hb_closure_context_t *c,
+		       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 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
+  {
+    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;
+    }
+  }
+  inline bool would_apply (hb_codepoint_t first,
+			   hb_codepoint_t second,
+			   unsigned int lookup_type) const
+  {
+    switch (lookup_type) {
+    case Ligature:		return u.ligature.would_apply (first, second);
+    case Extension:		return u.extension.would_apply (first, second);
+    default:			return false;
+    }
+  }
+
   inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
   {
     TRACE_APPLY ();
     switch (lookup_type) {
-    case Single:		return u.single.apply (c);
-    case Multiple:		return u.multiple.apply (c);
-    case Alternate:		return u.alternate.apply (c);
-    case Ligature:		return u.ligature.apply (c);
-    case Context:		return u.c.apply (c);
-    case ChainContext:		return u.chainContext.apply (c);
-    case Extension:		return u.extension.apply (c);
-    case ReverseChainSingle:	return u.reverseChainContextSingle.apply (c);
-    default:return false;
+    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 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 ();
     switch (lookup_type) {
-    case Single:		return u.single.sanitize (c);
-    case Multiple:		return u.multiple.sanitize (c);
-    case Alternate:		return u.alternate.sanitize (c);
-    case Ligature:		return u.ligature.sanitize (c);
-    case Context:		return u.c.sanitize (c);
-    case ChainContext:		return u.chainContext.sanitize (c);
-    case Extension:		return u.extension.sanitize (c);
-    case ReverseChainSingle:	return u.reverseChainContextSingle.sanitize (c);
-    default:return true;
+    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 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:
   union {
   USHORT			sub_format;
   SingleSubst			single;
   MultipleSubst			multiple;
@@ -748,185 +1017,237 @@ struct SubstLookup : Lookup
   inline bool is_reverse (void) const
   {
     unsigned int type = get_type ();
     if (unlikely (type == SubstLookupSubTable::Extension))
       return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
     return lookup_type_is_reverse (type);
   }
 
+  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
+  {
+    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))
+	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->info[c->buffer->idx], c->lookup_props, &c->property))
+    if (!_hb_ot_layout_check_glyph_property (c->face, &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 ();
-      unsigned int type = get_subtable(0).u.extension.get_type ();
       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;
   }
 
-  inline bool apply_string (hb_face_t   *face,
-			    hb_buffer_t *buffer,
-			    hb_mask_t    mask) const
+  inline bool apply_string (hb_apply_context_t *c) const
   {
     bool ret = false;
 
-    if (unlikely (!buffer->len))
+    if (unlikely (!c->buffer->len))
       return false;
 
-    hb_apply_context_t c (NULL, face, buffer, mask, *this);
+    c->set_lookup (*this);
 
     if (likely (!is_reverse ()))
     {
 	/* in/out forward substitution */
-	buffer->clear_output ();
-	buffer->idx = 0;
-	while (buffer->idx < buffer->len)
+	c->buffer->clear_output ();
+	c->buffer->idx = 0;
+	while (c->buffer->idx < c->buffer->len)
 	{
-	  if ((buffer->info[buffer->idx].mask & mask) && apply_once (&c))
+	  if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
 	    ret = true;
 	  else
-	    buffer->next_glyph ();
+	    c->buffer->next_glyph ();
 
 	}
 	if (ret)
-	  buffer->swap_buffers ();
+	  c->buffer->swap_buffers ();
     }
     else
     {
 	/* in-place backward substitution */
-	buffer->idx = buffer->len - 1;
+	c->buffer->idx = c->buffer->len - 1;
 	do
 	{
-	  if ((buffer->info[buffer->idx].mask & mask) && apply_once (&c))
+	  if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
 	    ret = true;
 	  else
-	    buffer->idx--;
+	    c->buffer->idx--;
 
 	}
-	while ((int) buffer->idx >= 0);
+	while ((int) c->buffer->idx >= 0);
     }
 
     return ret;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!Lookup::sanitize (c))) return false;
+    if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
     OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
-    return list.sanitize (c, this, get_type ());
+    return TRACE_RETURN (list.sanitize (c, this, get_type ()));
   }
 };
 
 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)); }
 
-  inline bool substitute_lookup (hb_face_t    *face,
-				 hb_buffer_t  *buffer,
-			         unsigned int  lookup_index,
-				 hb_mask_t     mask) const
-  { return get_lookup (lookup_index).apply_string (face, buffer, mask); }
+  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);
 
+  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 false;
+    if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
     OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
-    return list.sanitize (c, this);
+    return TRACE_RETURN (list.sanitize (c, this));
   }
   public:
   DEFINE_SIZE_STATIC (10);
 };
 
 
 void
 GSUB::substitute_start (hb_buffer_t *buffer)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
-  HB_BUFFER_ALLOCATE_VAR (buffer, lig_id);
-  HB_BUFFER_ALLOCATE_VAR (buffer, lig_comp);
+  HB_BUFFER_ALLOCATE_VAR (buffer, lig_props);
+  HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
 
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
-    buffer->info[i].props_cache() = buffer->info[i].lig_id() = buffer->info[i].lig_comp() = 0;
+    buffer->info[i].props_cache() = buffer->info[i].lig_props() = buffer->info[i].syllable() = 0;
 }
 
 void
-GSUB::substitute_finish (hb_buffer_t *buffer)
+GSUB::substitute_finish (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
+{
+  return get_subtable ().would_apply (glyph_id, get_type ());
+}
+
+inline bool ExtensionSubst::would_apply (hb_codepoint_t first, hb_codepoint_t second) const
+{
+  return get_subtable ().would_apply (first, second, get_type ());
+}
+
 inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
 {
   TRACE_APPLY ();
-  return get_subtable ().apply (c, get_type ());
+  return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
 }
 
 inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
 {
   TRACE_SANITIZE ();
-  if (unlikely (!Extension::sanitize (c))) return false;
+  if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
   unsigned int offset = get_offset ();
-  if (unlikely (!offset)) return true;
-  return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ());
+  if (unlikely (!offset)) return TRACE_RETURN (true);
+  return TRACE_RETURN (StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ()));
 }
 
 inline bool ExtensionSubst::is_reverse (void) const
 {
   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 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 SubstLookup &l = gsub.get_lookup (lookup_index);
 
   if (unlikely (c->nesting_level_left == 0))
     return false;
 
-  if (unlikely (c->context_length < 1))
-    return false;
-
-  hb_apply_context_t new_c (*c, l);
+  hb_apply_context_t new_c (*c);
+  new_c.nesting_level_left--;
+  new_c.set_lookup (l);
   return l.apply_once (&new_c);
 }
 
 
 
 #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -1,11 +1,11 @@
 /*
  * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
- * Copyright © 2010  Google, Inc.
+ * Copyright © 2010,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.
@@ -28,246 +28,316 @@
 
 #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"
 
 
-/* buffer var allocations */
-#define lig_id() var2.u8[2] /* unique ligature id */
-#define lig_comp() var2.u8[3] /* component number in the ligature (0 = base) */
+
+/* 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 ();
-  if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflow */
+  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;
 }
 
 
 
+#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 */
+
+
+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_),
+			  nesting_level_left (nesting_level_left_),
+			  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, NULL, HB_FUNC);
+	hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "idx %d codepoint %u", c->buffer->cur().codepoint);
 
 
 
 struct hb_apply_context_t
 {
-  unsigned int debug_depth;
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t *buffer;
   hb_direction_t direction;
   hb_mask_t lookup_mask;
-  unsigned int context_length;
   unsigned int nesting_level_left;
   unsigned int lookup_props;
   unsigned int property; /* propety of first glyph */
+  unsigned int debug_depth;
 
 
   hb_apply_context_t (hb_font_t *font_,
 		      hb_face_t *face_,
 		      hb_buffer_t *buffer_,
-		      hb_mask_t lookup_mask_,
-		      const Lookup &l,
-		      unsigned int context_length_ = NO_CONTEXT,
-		      unsigned int nesting_level_left_ = MAX_NESTING_LEVEL) :
+		      hb_mask_t lookup_mask_) :
 			font (font_), face (face_), buffer (buffer_),
 			direction (buffer_->props.direction),
 			lookup_mask (lookup_mask_),
-			context_length (context_length_),
-			nesting_level_left (nesting_level_left_),
-			lookup_props (l.get_props ()),
-			property (0) {}
+			nesting_level_left (MAX_NESTING_LEVEL),
+			lookup_props (0), property (0), debug_depth (0) {}
 
-  hb_apply_context_t (const hb_apply_context_t &c, const Lookup &l) {
-    *this = c;
-    nesting_level_left--;
+  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_,
 					     unsigned int start_index_,
-					     unsigned int num_items_)
+					     unsigned int num_items_,
+					     bool context_match = false)
     {
       c = c_;
       idx = start_index_;
       num_items = num_items_;
-      end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
+      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 bool next (unsigned int *property_out,
 		      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));
       num_items--;
-      return true;
+      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:
     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
   {
     inline mark_skipping_backward_iterator_t (hb_apply_context_t *c_,
 					      unsigned int start_index_,
-					      unsigned int num_items_)
+					      unsigned int num_items_,
+					      hb_mask_t mask_ = 0,
+					      bool match_syllable_ = true)
     {
       c = c_;
       idx = start_index_;
       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 bool prev (unsigned int *property_out,
 		      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));
       num_items--;
-      return true;
+      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:
     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
   {
-    return _hb_ot_layout_skip_mark (face, &buffer->info[buffer->idx], lookup_props, NULL);
+    return _hb_ot_layout_skip_mark (face, &buffer->cur(), lookup_props, NULL);
   }
 
 
 
-  inline void replace_glyph (hb_codepoint_t glyph_index) const
+  inline void replace_glyph (hb_codepoint_t glyph_index,
+			     unsigned int klass = 0) const
   {
-    clear_property ();
+    buffer->cur().props_cache() = klass; /*XXX if has gdef? */
     buffer->replace_glyph (glyph_index);
   }
   inline void replace_glyphs_be16 (unsigned int num_in,
 				   unsigned int num_out,
-				   const uint16_t *glyph_data_be) const
-  {
-    clear_property ();
-    buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
-  }
-
-  inline void guess_glyph_class (unsigned int klass)
+				   const uint16_t *glyph_data_be,
+				   unsigned int klass = 0) const
   {
-    /* XXX if ! has gdef */
-    buffer->info[buffer->idx].props_cache() = klass;
-  }
-
-  private:
-  inline void clear_property (void) const
-  {
-    /* XXX if has gdef */
-    buffer->info[buffer->idx].props_cache() = 0;
+    buffer->cur().props_cache() = klass; /* XXX if has gdef? */
+    buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
   }
 };
 
 
 
+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);
 typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
 
-struct ContextFuncs
+struct ContextClosureFuncs
+{
+  intersects_func_t intersects;
+  closure_lookup_func_t closure;
+};
+struct ContextApplyFuncs
 {
   match_func_t match;
   apply_lookup_func_t apply;
 };
 
+static inline bool intersects_glyph (hb_set_t *glyphs, const USHORT &value, const void *data HB_UNUSED)
+{
+  return glyphs->has (value);
+}
+static inline bool intersects_class (hb_set_t *glyphs, const USHORT &value, const void *data)
+{
+  const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
+  return class_def.intersects_class (glyphs, value);
+}
+static inline bool intersects_coverage (hb_set_t *glyphs, const USHORT &value, const void *data)
+{
+  const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
+  return (data+coverage).intersects (glyphs);
+}
+
+static inline bool intersects_array (hb_closure_context_t *c,
+				     unsigned int count,
+				     const USHORT values[],
+				     intersects_func_t intersects_func,
+				     const void *intersects_data)
+{
+  for (unsigned int i = 0; i < count; i++)
+    if (likely (!intersects_func (c->glyphs, values[i], intersects_data)))
+      return false;
+  return true;
+}
+
 
 static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
 {
   return glyph_id == value;
 }
-
 static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
 {
   const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
   return class_def.get_class (glyph_id) == value;
 }
-
 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) (glyph_id) != NOT_COVERED;
+  return (data+coverage).get_coverage (glyph_id) != NOT_COVERED;
 }
 
 
 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 *context_length_out)
+				unsigned int *end_offset = NULL)
 {
   hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
   if (skippy_iter.has_no_chance ())
     return false;
 
   for (unsigned int i = 1; i < count; i++)
   {
     if (!skippy_iter.next ())
       return false;
 
     if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, input[i - 1], match_data)))
       return false;
   }
 
-  *context_length_out = skippy_iter.idx - c->buffer->idx + 1;
+  if (end_offset)
+    *end_offset = skippy_iter.idx - c->buffer->idx + 1;
 
   return true;
 }
 
 static inline bool match_backtrack (hb_apply_context_t *c,
 				    unsigned int count,
 				    const USHORT backtrack[],
 				    match_func_t match_func,
 				    const void *match_data)
 {
-  hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count);
+  hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count, true);
   if (skippy_iter.has_no_chance ())
     return false;
 
   for (unsigned int i = 0; i < count; i++)
   {
     if (!skippy_iter.prev ())
       return false;
 
@@ -280,17 +350,17 @@ static inline bool match_backtrack (hb_a
 
 static inline bool match_lookahead (hb_apply_context_t *c,
 				    unsigned int count,
 				    const USHORT lookahead[],
 				    match_func_t match_func,
 				    const void *match_data,
 				    unsigned int offset)
 {
-  hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count);
+  hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count, true);
   if (skippy_iter.has_no_chance ())
     return false;
 
   for (unsigned int i = 0; i < count; i++)
   {
     if (!skippy_iter.next ())
       return false;
 
@@ -302,36 +372,44 @@ static inline bool match_lookahead (hb_a
 }
 
 
 
 struct LookupRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   USHORT	sequenceIndex;		/* Index into current glyph
 					 * sequence--first glyph = 0 */
   USHORT	lookupListIndex;	/* Lookup to apply to that
 					 * position--zero--based */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
 
+static inline void closure_lookup (hb_closure_context_t *c,
+				   unsigned int lookupCount,
+				   const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
+				   closure_lookup_func_t closure_func)
+{
+  for (unsigned int i = 0; i < lookupCount; i++)
+    closure_func (c, lookupRecord->lookupListIndex);
+}
 
 static inline bool apply_lookup (hb_apply_context_t *c,
 				 unsigned int count, /* Including the first glyph */
 				 unsigned int lookupCount,
 				 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
 				 apply_lookup_func_t apply_func)
 {
-  unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
+  unsigned int end = c->buffer->len;
   if (unlikely (count == 0 || c->buffer->idx + count > end))
     return false;
 
   /* TODO We don't support lookupRecord arrays that are not increasing:
    *      Should be easy for in_place ones at least. */
 
   /* Note: If sublookup is reverse, it will underflow after the first loop
    * and we jump out of it.  Not entirely disastrous.  So we don't check
@@ -377,131 +455,185 @@ static inline bool apply_lookup (hb_appl
 
   return true;
 }
 
 
 
 /* Contextual lookups */
 
-struct ContextLookupContext
+struct ContextClosureLookupContext
 {
-  ContextFuncs funcs;
+  ContextClosureFuncs funcs;
+  const void *intersects_data;
+};
+
+struct ContextApplyLookupContext
+{
+  ContextApplyFuncs funcs;
   const void *match_data;
 };
 
-static inline bool context_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[],
-				   ContextLookupContext &lookup_context)
+static inline void context_closure_lookup (hb_closure_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[],
+					   ContextClosureLookupContext &lookup_context)
 {
-  hb_apply_context_t new_context = *c;
+  if (intersects_array (c,
+			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_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,
 		      inputCount, input,
-		      lookup_context.funcs.match, lookup_context.match_data,
-		      &new_context.context_length)
-      && apply_lookup (&new_context,
+		      lookup_context.funcs.match, lookup_context.match_data)
+      && apply_lookup (c,
 		       inputCount,
 		       lookupCount, lookupRecord,
 		       lookup_context.funcs.apply);
 }
 
 struct Rule
 {
   friend struct RuleSet;
 
   private:
-  inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
+
+  inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
+  {
+    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 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 context_lookup (c,
-			   inputCount, input,
-			   lookupCount, lookupRecord,
-			   lookup_context);
+    return TRACE_RETURN (context_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return inputCount.sanitize (c)
 	&& lookupCount.sanitize (c)
 	&& c->check_range (input,
-				 input[0].static_size * inputCount
-				 + lookupRecordX[0].static_size * lookupCount);
+			   input[0].static_size * inputCount
+			   + lookupRecordX[0].static_size * lookupCount);
   }
 
   private:
   USHORT	inputCount;		/* Total number of glyphs in input
-					 * glyph sequence--includes the  first
+					 * 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 */
   public:
   DEFINE_SIZE_ARRAY2 (4, input, lookupRecordX);
 };
 
 struct RuleSet
 {
-  inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
+  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 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 true;
+        return TRACE_RETURN (true);
     }
-
-    return false;
+    return TRACE_RETURN (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rule.sanitize (c, this);
+    return TRACE_RETURN (rule.sanitize (c, this));
   }
 
   private:
   OffsetArrayOf<Rule>
 		rule;			/* Array of Rule tables
 					 * ordered by preference */
   public:
   DEFINE_SIZE_ARRAY (2, rule);
 };
 
 
 struct ContextFormat1
 {
   friend struct Context;
 
   private:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+
+    const Coverage &cov = (this+coverage);
+
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_glyph, closure_func},
+      NULL
+    };
+
+    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 apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
+    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED))
-      return false;
+      return TRACE_RETURN (false);
 
     const RuleSet &rule_set = this+ruleSet[index];
-    struct ContextLookupContext lookup_context = {
+    struct ContextApplyLookupContext lookup_context = {
       {match_glyph, apply_func},
       NULL
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& ruleSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetArrayOf<RuleSet>
@@ -512,38 +644,57 @@ struct ContextFormat1
 };
 
 
 struct ContextFormat2
 {
   friend struct Context;
 
   private:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    if (!(this+coverage).intersects (c->glyphs))
+      return;
+
+    const ClassDef &class_def = this+classDef;
+
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_class, closure_func},
+      NULL
+    };
+
+    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 apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    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->info[c->buffer->idx].codepoint);
+    index = class_def (c->buffer->cur().codepoint);
     const RuleSet &rule_set = this+ruleSet[index];
-    struct ContextLookupContext lookup_context = {
+    struct ContextApplyLookupContext lookup_context = {
       {match_class, apply_func},
       &class_def
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-        && classDef.sanitize (c, this)
-	&& ruleSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetTo<ClassDef>
@@ -557,43 +708,57 @@ struct ContextFormat2
 };
 
 
 struct ContextFormat3
 {
   friend struct Context;
 
   private:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    if (!(this+coverage[0]).intersects (c->glyphs))
+      return;
+
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_coverage, closure_func},
+      this
+    };
+    context_closure_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->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    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 ContextLookupContext lookup_context = {
+    struct ContextApplyLookupContext lookup_context = {
       {match_coverage, apply_func},
       this
     };
-    return context_lookup (c,
-			   glyphCount, (const USHORT *) (coverage + 1),
-			   lookupCount, lookupRecord,
-			   lookup_context);
+    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 false;
+    if (!c->check_struct (this)) return TRACE_RETURN (false);
     unsigned int count = glyphCount;
-    if (!c->check_array (coverage, coverage[0].static_size, count)) 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 false;
+      if (!coverage[i].sanitize (c, this)) return TRACE_RETURN (false);
     LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
-    return c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount);
+    return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 3 */
   USHORT	glyphCount;		/* Number of glyphs in the input glyph
 					 * sequence */
   USHORT	lookupCount;		/* Number of LookupRecords */
   OffsetTo<Coverage>
@@ -603,120 +768,171 @@ struct ContextFormat3
 					 * design order */
   public:
   DEFINE_SIZE_ARRAY2 (6, coverage, lookupRecordX);
 };
 
 struct Context
 {
   protected:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    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 bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c, apply_func);
-    case 2: return u.format2.apply (c, apply_func);
-    case 3: return u.format3.apply (c, apply_func);
-    default:return false;
+    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);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    case 3: return u.format3.sanitize (c);
-    default:return true;
+    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:
   union {
   USHORT		format;		/* Format identifier */
   ContextFormat1	format1;
   ContextFormat2	format2;
   ContextFormat3	format3;
   } u;
 };
 
 
 /* Chaining Contextual lookups */
 
-struct ChainContextLookupContext
+struct ChainContextClosureLookupContext
 {
-  ContextFuncs funcs;
+  ContextClosureFuncs funcs;
+  const void *intersects_data[3];
+};
+
+struct ChainContextApplyLookupContext
+{
+  ContextApplyFuncs funcs;
   const void *match_data[3];
 };
 
-static inline bool chain_context_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[],
-					 ChainContextLookupContext &lookup_context)
+static inline void chain_context_closure_lookup (hb_closure_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[],
+						 ChainContextClosureLookupContext &lookup_context)
 {
-  /* First guess */
-  if (unlikely (c->buffer->backtrack_len () < backtrackCount ||
-		c->buffer->idx + inputCount + lookaheadCount > c->buffer->len ||
-		inputCount + lookaheadCount > c->context_length))
-    return false;
+  if (intersects_array (c,
+			backtrackCount, backtrack,
+			lookup_context.funcs.intersects, lookup_context.intersects_data[0])
+   && intersects_array (c,
+			inputCount ? inputCount - 1 : 0, input,
+			lookup_context.funcs.intersects, lookup_context.intersects_data[1])
+  && intersects_array (c,
+		       lookaheadCount, lookahead,
+		       lookup_context.funcs.intersects, lookup_context.intersects_data[2]))
+    closure_lookup (c,
+		    lookupCount, lookupRecord,
+		    lookup_context.funcs.closure);
+}
 
-  hb_apply_context_t new_context = *c;
+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,
 		      inputCount, input,
 		      lookup_context.funcs.match, lookup_context.match_data[1],
-		      &new_context.context_length)
+		      &lookahead_offset)
       && match_lookahead (c,
 			  lookaheadCount, lookahead,
 			  lookup_context.funcs.match, lookup_context.match_data[2],
-			  new_context.context_length)
-      && apply_lookup (&new_context,
+			  lookahead_offset)
+      && apply_lookup (c,
 		       inputCount,
 		       lookupCount, lookupRecord,
 		       lookup_context.funcs.apply);
 }
 
 struct ChainRule
 {
   friend struct ChainRuleSet;
 
   private:
-  inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
+
+  inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
+  {
+    TRACE_CLOSURE ();
+    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
+    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    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 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 chain_context_lookup (c,
-				 backtrack.len, backtrack.array,
-				 input.len, input.array,
-				 lookahead.len, lookahead.array,
-				 lookup.len, lookup.array,
-				 lookup_context);
+    return TRACE_RETURN (chain_context_apply_lookup (c,
+						     backtrack.len, backtrack.array,
+						     input.len, input.array,
+						     lookahead.len, lookahead.array, lookup.len,
+						     lookup.array, lookup_context));
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!backtrack.sanitize (c)) return false;
+    if (!backtrack.sanitize (c)) return TRACE_RETURN (false);
     HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
-    if (!input.sanitize (c)) return false;
+    if (!input.sanitize (c)) return TRACE_RETURN (false);
     ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
-    if (!lookahead.sanitize (c)) return false;
+    if (!lookahead.sanitize (c)) return TRACE_RETURN (false);
     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return lookup.sanitize (c);
+    return TRACE_RETURN (lookup.sanitize (c));
   }
 
   private:
   ArrayOf<USHORT>
 		backtrack;		/* Array of backtracking values
 					 * (to be matched before the input
 					 * sequence) */
   HeadlessArrayOf<USHORT>
@@ -729,66 +945,89 @@ struct ChainRule
 		lookupX;		/* Array of LookupRecords--in
 					 * design order) */
   public:
   DEFINE_SIZE_MIN (8);
 };
 
 struct ChainRuleSet
 {
-  inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
+  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 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 true;
-    }
+        return TRACE_RETURN (true);
 
-    return false;
+    return TRACE_RETURN (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rule.sanitize (c, this);
+    return TRACE_RETURN (rule.sanitize (c, this));
   }
 
   private:
   OffsetArrayOf<ChainRule>
 		rule;			/* Array of ChainRule tables
 					 * ordered by preference */
   public:
   DEFINE_SIZE_ARRAY (2, rule);
 };
 
 struct ChainContextFormat1
 {
   friend struct ChainContext;
 
   private:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    const Coverage &cov = (this+coverage);
+
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_glyph, closure_func},
+      {NULL, NULL, NULL}
+    };
+
+    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 apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    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 ChainContextLookupContext lookup_context = {
+    struct ChainContextApplyLookupContext lookup_context = {
       {match_glyph, apply_func},
       {NULL, NULL, NULL}
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& ruleSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetArrayOf<ChainRuleSet>
@@ -798,45 +1037,68 @@ struct ChainContextFormat1
   DEFINE_SIZE_ARRAY (6, ruleSet);
 };
 
 struct ChainContextFormat2
 {
   friend struct ChainContext;
 
   private:
-  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
   {
-    TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    TRACE_CLOSURE ();
+    if (!(this+coverage).intersects (c->glyphs))
+      return;
 
     const ClassDef &backtrack_class_def = this+backtrackClassDef;
     const ClassDef &input_class_def = this+inputClassDef;
     const ClassDef &lookahead_class_def = this+lookaheadClassDef;
 
-    index = input_class_def (c->buffer->info[c->buffer->idx].codepoint);
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_class, closure_func},
+      {&backtrack_class_def,
+       &input_class_def,
+       &lookahead_class_def}
+    };
+
+    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 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;
+    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
+
+    index = input_class_def (c->buffer->cur().codepoint);
     const ChainRuleSet &rule_set = this+ruleSet[index];
-    struct ChainContextLookupContext lookup_context = {
+    struct ChainContextApplyLookupContext lookup_context = {
       {match_class, apply_func},
       {&backtrack_class_def,
        &input_class_def,
        &lookahead_class_def}
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& backtrackClassDef.sanitize (c, this)
-	&& inputClassDef.sanitize (c, this)
-	&& lookaheadClassDef.sanitize (c, this)
-	&& ruleSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && backtrackClassDef.sanitize (c, this) &&
+			 inputClassDef.sanitize (c, this) && lookaheadClassDef.sanitize (c, this) &&
+			 ruleSet.sanitize (c, this));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
   OffsetTo<ClassDef>
@@ -859,48 +1121,68 @@ struct ChainContextFormat2
 };
 
 struct ChainContextFormat3
 {
   friend struct ChainContext;
 
   private:
 
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+    if (!(this+input[0]).intersects (c->glyphs))
+      return;
+
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_coverage, closure_func},
+      {this, this, this}
+    };
+    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 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->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    unsigned int index = (this+input[0]) (c->buffer->cur().codepoint);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    struct ChainContextLookupContext lookup_context = {
+    struct ChainContextApplyLookupContext lookup_context = {
       {match_coverage, apply_func},
       {this, this, this}
     };
-    return chain_context_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);
+    return TRACE_RETURN (chain_context_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 sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!backtrack.sanitize (c, this)) return false;
+    if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
-    if (!input.sanitize (c, this)) return false;
+    if (!input.sanitize (c, this)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
-    if (!lookahead.sanitize (c, this)) return false;
+    if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false);
     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return lookup.sanitize (c);
+    return TRACE_RETURN (lookup.sanitize (c));
   }
 
   private:
   USHORT	format;			/* Format identifier--format = 3 */
   OffsetArrayOf<Coverage>
 		backtrack;		/* Array of coverage tables
 					 * in backtracking sequence, in  glyph
 					 * sequence order */
@@ -917,35 +1199,47 @@ struct ChainContextFormat3
 					 * design order) */
   public:
   DEFINE_SIZE_MIN (10);
 };
 
 struct ChainContext
 {
   protected:
+
+  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  {
+    TRACE_CLOSURE ();
+    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 bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c, apply_func);
-    case 2: return u.format2.apply (c, apply_func);
-    case 3: return u.format3.apply (c, apply_func);
-    default:return false;
+    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);
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    case 3: return u.format3.sanitize (c);
-    default:return true;
+    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:
   union {
   USHORT		format;	/* Format identifier */
   ChainContextFormat1	format1;
   ChainContextFormat2	format2;
@@ -959,17 +1253,17 @@ struct ExtensionFormat1
   friend struct Extension;
 
   protected:
   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 c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
   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,
@@ -992,20 +1286,20 @@ struct Extension
     switch (u.format) {
     case 1: return u.format1.get_offset ();
     default:return 0;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
   private:
   union {
   USHORT		format;		/* Format identifier */
   ExtensionFormat1	format1;
   } u;
@@ -1049,20 +1343,20 @@ struct GSUBGPOS
 
   inline unsigned int get_lookup_count (void) const
   { return (this+lookupList).len; }
   inline const Lookup& get_lookup (unsigned int i) const
   { return (this+lookupList)[i]; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return version.sanitize (c) && likely (version.major == 1)
-	&& scriptList.sanitize (c, this)
-	&& featureList.sanitize (c, this)
-	&& lookupList.sanitize (c, this);
+    return TRACE_RETURN (version.sanitize (c) && likely (version.major == 1) &&
+			 scriptList.sanitize (c, this) &&
+			 featureList.sanitize (c, this) &&
+			 lookupList.sanitize (c, this));
   }
 
   protected:
   FixedVersion	version;	/* Version of the GSUB/GPOS table--initially set
 				 * to 0x00010000 */
   OffsetTo<ScriptList>
 		scriptList;  	/* ScriptList table */
   OffsetTo<FeatureList>
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -28,26 +28,24 @@
 #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"
 
 
 
 /*
  * GDEF
  */
 
-/* buffer var allocations */
-#define props_cache() var1.u16[1] /* glyph_props cache */
-
 /* 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;
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -27,17 +27,16 @@
  */
 
 #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_ot_layout_t *
@@ -456,25 +455,34 @@ hb_ot_layout_substitute_start (hb_buffer
 }
 
 hb_bool_t
 hb_ot_layout_substitute_lookup (hb_face_t    *face,
 				hb_buffer_t  *buffer,
 				unsigned int  lookup_index,
 				hb_mask_t     mask)
 {
-  return _get_gsub (face).substitute_lookup (face, buffer, lookup_index, mask);
+  hb_apply_context_t c (NULL, face, buffer, mask);
+  return _get_gsub (face).substitute_lookup (&c, lookup_index);
 }
 
 void
 hb_ot_layout_substitute_finish (hb_buffer_t  *buffer HB_UNUSED)
 {
   GSUB::substitute_finish (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);
+  _get_gsub (face).closure_lookup (&c, lookup_index);
+}
 
 /*
  * GPOS
  */
 
 hb_bool_t
 hb_ot_layout_has_positioning (hb_face_t *face)
 {
@@ -488,56 +496,19 @@ hb_ot_layout_position_start (hb_buffer_t
 }
 
 hb_bool_t
 hb_ot_layout_position_lookup   (hb_font_t    *font,
 				hb_buffer_t  *buffer,
 				unsigned int  lookup_index,
 				hb_mask_t     mask)
 {
-  return _get_gpos (font->face).position_lookup (font, buffer, lookup_index, mask);
+  hb_apply_context_t c (font, font->face, buffer, mask);
+  return _get_gpos (font->face).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_buffer_t  *buffer)
 {
-  /* force diacritics to have zero width */
-  unsigned int count = buffer->len;
-  if (hb_ot_layout_has_glyph_classes (face)) {
-    const GDEF& gdef = _get_gdef (face);
-    if (buffer->props.direction == HB_DIRECTION_RTL) {
-      for (unsigned int i = 1; i < count; i++) {
-        if (gdef.get_glyph_class (buffer->info[i].codepoint) == GDEF::MarkGlyph) {
-          buffer->pos[i].x_advance = 0;
-        }
-      }
-    } else {
-      for (unsigned int i = 1; i < count; i++) {
-        if (gdef.get_glyph_class (buffer->info[i].codepoint) == GDEF::MarkGlyph) {
-          hb_glyph_position_t& pos = buffer->pos[i];
-          pos.x_offset -= pos.x_advance;
-          pos.x_advance = 0;
-        }
-      }
-    }
-  } else {
-    /* no GDEF classes available, so use General Category as a fallback */
-    if (buffer->props.direction == HB_DIRECTION_RTL) {
-      for (unsigned int i = 1; i < count; i++) {
-        if (buffer->info[i].general_category() == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
-          buffer->pos[i].x_advance = 0;
-        }
-      }
-    } else {
-      for (unsigned int i = 1; i < count; i++) {
-        if (buffer->info[i].general_category() == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
-          hb_glyph_position_t& pos = buffer->pos[i];
-          pos.x_offset -= pos.x_advance;
-          pos.x_advance = 0;
-        }
-      }
-    }
-  }
-
   GPOS::position_finish (buffer);
 }
 
 
--- a/gfx/harfbuzz/src/hb-ot-layout.h
+++ b/gfx/harfbuzz/src/hb-ot-layout.h
@@ -177,16 +177,22 @@ hb_ot_layout_substitute_lookup (hb_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);
 
+
+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. */
@@ -196,14 +202,14 @@ hb_ot_layout_position_start (hb_buffer_t
 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_ot_layout_position_finish (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
@@ -64,16 +64,20 @@ struct hb_ot_map_t
   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 ();
   }
 
--- a/gfx/harfbuzz/src/hb-ot-map.cc
+++ b/gfx/harfbuzz/src/hb-ot-map.cc
@@ -90,16 +90,31 @@ void hb_ot_map_t::apply (unsigned int ta
 
     pause->callback.func (this, face_or_font, buffer, pause->callback.user_data);
   }
 
   for (; i < lookups[table_index].len; i++)
     apply_lookup_func (face_or_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
+{
+  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)
 {
   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;
--- a/gfx/harfbuzz/src/hb-ot-maxp-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-maxp-table.hh
@@ -1,10 +1,10 @@
 /*
- * 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,19 +42,18 @@ struct maxp
   static const hb_tag_t Tag	= HB_OT_TAG_maxp;
 
   inline unsigned int get_num_glyphs (void) const {
     return numGlyphs;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this) &&
-	   likely (version.major == 1 ||
-		   (version.major == 0 && version.minor == 0x5000));
+    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:
   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:
--- a/gfx/harfbuzz/src/hb-ot-name-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-name-table.hh
@@ -1,10 +1,10 @@
 /*
- * 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.
@@ -52,18 +52,17 @@ struct NameRecord
     ret = b->nameID.cmp (a->nameID);
     if (ret) return ret;
     return 0;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
     /* We can check from base all the way up to the end of string... */
-    return c->check_struct (this) &&
-	   c->check_range ((char *) base, (unsigned int) length + offset);
+    return TRACE_RETURN (c->check_struct (this) && c->check_range ((char *) base, (unsigned int) length + offset));
   }
 
   USHORT	platformID;	/* Platform ID. */
   USHORT	encodingID;	/* Platform-specific encoding ID. */
   USHORT	languageID;	/* Language ID. */
   USHORT	nameID;		/* Name ID. */
   USHORT	length;		/* String length (in bytes). */
   USHORT	offset;		/* String offset from start of storage area (in bytes). */
@@ -97,26 +96,26 @@ struct name
     return length;
   }
 
   inline bool sanitize_records (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     char *string_pool = (char *) this + stringOffset;
     unsigned int _count = count;
     for (unsigned int i = 0; i < _count; i++)
-      if (!nameRecord[i].sanitize (c, string_pool)) return false;
-    return true;
+      if (!nameRecord[i].sanitize (c, string_pool)) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this) &&
-	   likely (format == 0 || format == 1) &&
-	   c->check_array (nameRecord, nameRecord[0].static_size, count) &&
-	   sanitize_records (c);
+    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:
   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. */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -20,21 +20,22 @@
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-private.hh"
 
 
 
 /* buffer var allocations */
-#define arabic_shaping_action() complex_var_temporary_u16() /* arabic shaping action */
+#define arabic_shaping_action() complex_var_temporary_u8() /* arabic shaping action */
 
 
 /*
  * Bits used in the joining tables
  */
 enum {
   JOINING_TYPE_U		= 0,
   JOINING_TYPE_R		= 1,
@@ -159,17 +160,18 @@ static const struct arabic_state_table_e
 
   /* 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)
+_hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map,
+					      const hb_segment_properties_t *props)
 {
   /* 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
    *
@@ -216,44 +218,46 @@ arabic_fallback_shape (hb_font_t *font, 
     hb_codepoint_t shaped = get_arabic_shape (u, buffer->info[i].arabic_shaping_action());
     if (shaped != u && hb_font_get_glyph (font, shaped, 0, &glyph))
       buffer->info[i].codepoint = shaped;
   }
 
   /* Mandatory ligatures */
   buffer->clear_output ();
   for (buffer->idx = 0; buffer->idx + 1 < count;) {
-    uint16_t ligature = get_ligature (buffer->info[buffer->idx].codepoint,
-				      buffer->info[buffer->idx + 1].codepoint);
+    hb_codepoint_t ligature = get_ligature (buffer->cur().codepoint,
+					    buffer->cur(+1).codepoint);
     if (likely (!ligature) || !(hb_font_get_glyph (font, ligature, 0, &glyph))) {
       buffer->next_glyph ();
       continue;
     }
 
     buffer->replace_glyphs (2, 1, &ligature);
 
     /* Technically speaking we can skip marks and stuff, like the GSUB path does.
      * But who cares, we're in fallback! */
   }
   for (; buffer->idx < count;)
       buffer->next_glyph ();
   buffer->swap_buffers ();
 }
 
 void
-_hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer, hb_font_t *font)
+_hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map,
+					 hb_buffer_t *buffer,
+					 hb_font_t *font)
 {
   unsigned int count = buffer->len;
   unsigned int prev = 0, state = 0;
 
   HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action);
 
   for (unsigned int i = 0; i < count; i++)
   {
-    unsigned int this_type = get_joining_type (buffer->info[i].codepoint, (hb_unicode_general_category_t) buffer->info[i].general_category());
+    unsigned int this_type = get_joining_type (buffer->info[i].codepoint, _hb_glyph_info_get_general_category (&buffer->info[i]));
 
     if (unlikely (this_type == JOINING_TYPE_T)) {
       buffer->info[i].arabic_shaping_action() = NONE;
       continue;
     }
 
     const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh
@@ -1,12 +1,12 @@
 
 #line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
 /*
- * 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.
@@ -31,262 +31,263 @@
 
 #include "hb-private.hh"
 
 HB_BEGIN_DECLS
 
 
 #line 38 "hb-ot-shape-complex-indic-machine.hh.tmp"
 static const unsigned char _indic_syllable_machine_trans_keys[] = {
-	0u, 0u, 1u, 2u, 1u, 2u, 5u, 5u, 5u, 5u, 1u, 5u, 5u, 5u, 1u, 2u, 
-	0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 
-	0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 
-	0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 
-	0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 0u, 12u, 
-	0u, 12u, 0
+	5u, 5u, 1u, 2u, 1u, 2u, 5u, 5u, 1u, 5u, 1u, 2u, 5u, 5u, 1u, 13u, 
+	4u, 11u, 4u, 11u, 5u, 11u, 1u, 10u, 1u, 10u, 10u, 10u, 10u, 10u, 4u, 10u, 
+	5u, 10u, 8u, 10u, 5u, 10u, 6u, 10u, 9u, 10u, 4u, 11u, 1u, 13u, 4u, 10u, 
+	4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 8u, 10u, 10u, 10u, 10u, 10u, 4u, 10u, 
+	4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 8u, 10u, 10u, 10u, 10u, 10u, 0
 };
 
 static const char _indic_syllable_machine_key_spans[] = {
-	0, 2, 2, 1, 1, 5, 1, 2, 
-	13, 13, 13, 13, 13, 13, 13, 13, 
-	13, 13, 13, 13, 13, 13, 13, 13, 
-	13, 13, 13, 13, 13, 13, 13, 13, 
-	13, 13, 13, 13, 13, 13, 13, 13, 
-	13
+	1, 2, 2, 1, 5, 2, 1, 13, 
+	8, 8, 7, 10, 10, 1, 1, 7, 
+	6, 3, 6, 5, 2, 8, 13, 7, 
+	7, 6, 7, 6, 3, 1, 1, 7, 
+	7, 6, 7, 6, 3, 1, 1
 };
 
-static const short _indic_syllable_machine_index_offsets[] = {
-	0, 0, 3, 6, 8, 10, 16, 18, 
-	21, 35, 49, 63, 77, 91, 105, 119, 
-	133, 147, 161, 175, 189, 203, 217, 231, 
-	245, 259, 273, 287, 301, 315, 329, 343, 
-	357, 371, 385, 399, 413, 427, 441, 455, 
-	469
+static const unsigned char _indic_syllable_machine_index_offsets[] = {
+	0, 2, 5, 8, 10, 16, 19, 21, 
+	35, 44, 53, 61, 72, 83, 85, 87, 
+	95, 102, 106, 113, 119, 122, 131, 145, 
+	153, 161, 168, 176, 183, 187, 189, 191, 
+	199, 207, 214, 222, 229, 233, 235
 };
 
 static const char _indic_syllable_machine_indicies[] = {
-	0, 0, 1, 2, 2, 1, 3, 
-	1, 4, 1, 0, 0, 1, 1, 4, 
-	1, 5, 1, 6, 6, 1, 7, 6, 
-	8, 9, 1, 1, 1, 1, 1, 1, 
-	1, 1, 10, 1, 11, 12, 13, 14, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	15, 1, 16, 17, 18, 19, 20, 21, 
-	22, 22, 23, 24, 25, 26, 27, 1, 
-	16, 17, 18, 19, 20, 28, 22, 22, 
-	23, 24, 25, 26, 27, 1, 29, 30, 
-	31, 32, 33, 4, 34, 35, 36, 37, 
-	38, 1, 39, 1, 29, 30, 31, 32, 
-	1, 4, 34, 35, 36, 37, 38, 1, 
-	39, 1, 29, 30, 31, 32, 1, 1, 
-	1, 1, 36, 37, 38, 1, 39, 1, 
-	29, 30, 31, 32, 40, 0, 1, 1, 
-	36, 37, 38, 1, 39, 1, 29, 30, 
-	31, 32, 1, 0, 1, 1, 36, 37, 
-	38, 1, 39, 1, 29, 30, 31, 32, 
-	1, 1, 1, 1, 1, 1, 38, 1, 
-	39, 1, 29, 30, 31, 32, 1, 1, 
-	1, 1, 1, 1, 41, 1, 39, 1, 
-	29, 30, 31, 32, 1, 1, 1, 1, 
-	1, 1, 1, 1, 39, 1, 42, 43, 
-	44, 45, 46, 3, 47, 47, 48, 49, 
-	50, 1, 51, 1, 42, 43, 44, 45, 
-	1, 3, 47, 47, 48, 49, 50, 1, 
-	51, 1, 42, 43, 44, 45, 1, 1, 
-	1, 1, 48, 49, 50, 1, 51, 1, 
-	42, 43, 44, 45, 52, 2, 1, 1, 
-	48, 49, 50, 1, 51, 1, 42, 43, 
-	44, 45, 1, 2, 1, 1, 48, 49, 
-	50, 1, 51, 1, 42, 43, 44, 45, 
-	1, 1, 1, 1, 1, 1, 50, 1, 
-	51, 1, 42, 43, 44, 45, 1, 1, 
-	1, 1, 1, 1, 53, 1, 51, 1, 
-	42, 43, 44, 45, 1, 1, 1, 1, 
-	1, 1, 1, 1, 51, 1, 16, 17, 
-	18, 19, 1, 21, 22, 22, 23, 24, 
-	25, 26, 27, 1, 16, 6, 6, 19, 
-	1, 1, 54, 54, 1, 24, 25, 1, 
-	27, 1, 16, 6, 6, 19, 1, 1, 
-	1, 1, 1, 24, 25, 1, 27, 1, 
-	16, 17, 18, 19, 1, 1, 1, 1, 
-	1, 1, 25, 1, 27, 1, 16, 17, 
-	18, 19, 1, 1, 1, 1, 1, 1, 
-	55, 1, 27, 1, 16, 17, 18, 19, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	27, 1, 16, 17, 18, 19, 56, 57, 
-	1, 1, 23, 24, 25, 1, 27, 1, 
-	16, 17, 18, 19, 1, 57, 1, 1, 
-	23, 24, 25, 1, 27, 1, 16, 17, 
-	18, 19, 1, 1, 1, 1, 23, 24, 
-	25, 1, 27, 1, 16, 17, 18, 19, 
-	1, 58, 1, 1, 23, 24, 25, 1, 
-	27, 1, 16, 17, 18, 19, 1, 1, 
-	59, 59, 1, 24, 25, 1, 27, 1, 
-	16, 17, 18, 19, 1, 1, 1, 1, 
-	1, 24, 25, 1, 27, 1, 16, 6, 
-	6, 9, 1, 1, 54, 54, 1, 24, 
-	25, 1, 10, 1, 0
+	1, 0, 2, 2, 0, 4, 4, 3, 
+	5, 3, 4, 4, 3, 3, 5, 3, 
+	7, 7, 6, 8, 6, 2, 10, 11, 
+	9, 9, 9, 9, 9, 9, 9, 9, 
+	12, 12, 9, 14, 15, 16, 16, 17, 
+	18, 19, 20, 13, 21, 15, 16, 16, 
+	17, 18, 19, 20, 13, 15, 16, 16, 
+	17, 18, 19, 20, 13, 2, 2, 13, 
+	13, 13, 22, 22, 13, 18, 19, 13, 
+	2, 2, 13, 13, 13, 13, 13, 13, 
+	18, 19, 13, 19, 13, 23, 13, 24, 
+	25, 13, 13, 17, 18, 19, 13, 25, 
+	13, 13, 17, 18, 19, 13, 17, 18, 
+	19, 13, 26, 13, 13, 17, 18, 19, 
+	13, 27, 27, 13, 18, 19, 13, 18, 
+	19, 13, 14, 28, 16, 16, 17, 18, 
+	19, 20, 13, 2, 2, 11, 13, 13, 
+	22, 22, 13, 18, 19, 13, 12, 12, 
+	13, 30, 5, 31, 32, 33, 34, 35, 
+	29, 4, 5, 31, 32, 33, 34, 35, 
+	29, 5, 31, 32, 33, 34, 35, 29, 
+	36, 37, 29, 29, 33, 34, 35, 29, 
+	37, 29, 29, 33, 34, 35, 29, 33, 
+	34, 35, 29, 35, 29, 38, 29, 40, 
+	8, 41, 41, 42, 43, 44, 39, 7, 
+	8, 41, 41, 42, 43, 44, 39, 8, 
+	41, 41, 42, 43, 44, 39, 45, 46, 
+	39, 39, 42, 43, 44, 39, 46, 39, 
+	39, 42, 43, 44, 39, 42, 43, 44, 
+	39, 44, 39, 47, 39, 0
 };
 
 static const char _indic_syllable_machine_trans_targs[] = {
-	14, 0, 22, 2, 1, 7, 10, 9, 
-	11, 12, 20, 9, 10, 11, 12, 20, 
-	9, 10, 11, 12, 28, 29, 6, 34, 
-	31, 32, 37, 20, 40, 9, 10, 11, 
-	12, 13, 4, 5, 15, 17, 18, 20, 
-	16, 19, 9, 10, 11, 12, 21, 3, 
-	23, 25, 26, 20, 24, 27, 30, 33, 
-	35, 36, 38, 39
+	7, 1, 8, 7, 25, 2, 7, 33, 
+	5, 7, 21, 23, 31, 7, 9, 11, 
+	0, 15, 13, 14, 18, 10, 12, 7, 
+	16, 17, 19, 20, 22, 7, 24, 3, 
+	4, 26, 29, 30, 27, 28, 7, 7, 
+	32, 6, 34, 37, 38, 35, 36, 7
 };
 
 static const char _indic_syllable_machine_trans_actions[] = {
+	1, 0, 2, 3, 2, 0, 4, 2, 
+	0, 7, 2, 2, 2, 8, 2, 0, 
+	0, 0, 0, 0, 0, 2, 0, 9, 
+	0, 0, 0, 0, 0, 10, 2, 0, 
+	0, 0, 0, 0, 0, 0, 11, 12, 
+	2, 0, 0, 0, 0, 0, 0, 13
+};
+
+static const char _indic_syllable_machine_to_state_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 5, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 1, 1, 1, 1, 1, 
-	2, 2, 2, 2, 0, 0, 0, 0, 
-	0, 0, 0, 2, 0, 3, 3, 3, 
-	3, 0, 0, 0, 0, 0, 0, 3, 
-	0, 0, 4, 4, 4, 4, 0, 0, 
-	0, 0, 0, 4, 0, 0, 0, 0, 
-	0, 0, 0, 0
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0
 };
 
-static const char _indic_syllable_machine_eof_actions[] = {
+static const char _indic_syllable_machine_from_state_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 6, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 1, 2, 2, 3, 3, 3, 3, 
-	3, 3, 3, 3, 4, 4, 4, 4, 
-	4, 4, 4, 4, 2, 2, 2, 2, 
-	2, 2, 2, 2, 2, 2, 2, 2, 
-	2
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0
 };
 
-static const int indic_syllable_machine_start = 8;
-static const int indic_syllable_machine_first_final = 8;
-static const int indic_syllable_machine_error = 0;
+static const unsigned char _indic_syllable_machine_eof_trans[] = {
+	1, 1, 4, 4, 4, 7, 7, 0, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 30, 
+	30, 30, 30, 30, 30, 30, 30, 40, 
+	40, 40, 40, 40, 40, 40, 40
+};
 
-static const int indic_syllable_machine_en_main = 8;
+static const int indic_syllable_machine_start = 7;
+static const int indic_syllable_machine_first_final = 7;
+static const int indic_syllable_machine_error = -1;
+
+static const int indic_syllable_machine_en_main = 7;
 
 
 #line 38 "../../src/hb-ot-shape-complex-indic-machine.rl"
 
 
 
-#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 79 "../../src/hb-ot-shape-complex-indic-machine.rl"
 
 
+#define process_syllable(func) \
+  HB_STMT_START { \
+    /* fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #func); */ \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = syllable_serial; \
+    PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (!syllable_serial)) syllable_serial++; \
+  } HB_STMT_END
 
 static void
 find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array)
 {
-  unsigned int p, pe, eof;
+  unsigned int p, pe, eof, ts, te, act;
   int cs;
+  hb_glyph_info_t *info = buffer->info;
   
-#line 182 "hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 170 "hb-ot-shape-complex-indic-machine.hh.tmp"
 	{
 	cs = indic_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
 	}
 
-#line 94 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 101 "../../src/hb-ot-shape-complex-indic-machine.rl"
 
 
   p = 0;
   pe = eof = buffer->len;
 
   unsigned int last = 0;
+  uint8_t syllable_serial = 1;
   
-#line 195 "hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 187 "hb-ot-shape-complex-indic-machine.hh.tmp"
 	{
 	int _slen;
 	int _trans;
 	const unsigned char *_keys;
 	const char *_inds;
 	if ( p == pe )
 		goto _test_eof;
-	if ( cs == 0 )
-		goto _out;
 _resume:
+	switch ( _indic_syllable_machine_from_state_actions[cs] ) {
+	case 6:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 201 "hb-ot-shape-complex-indic-machine.hh.tmp"
+	}
+
 	_keys = _indic_syllable_machine_trans_keys + (cs<<1);
 	_inds = _indic_syllable_machine_indicies + _indic_syllable_machine_index_offsets[cs];
 
 	_slen = _indic_syllable_machine_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=( buffer->info[p].indic_category()) &&
-		( buffer->info[p].indic_category()) <= _keys[1] ?
-		( buffer->info[p].indic_category()) - _keys[0] : _slen ];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].indic_category()) &&
+		( info[p].indic_category()) <= _keys[1] ?
+		( info[p].indic_category()) - _keys[0] : _slen ];
 
+_eof_trans:
 	cs = _indic_syllable_machine_trans_targs[_trans];
 
 	if ( _indic_syllable_machine_trans_actions[_trans] == 0 )
 		goto _again;
 
 	switch ( _indic_syllable_machine_trans_actions[_trans] ) {
 	case 2:
-#line 62 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ found_consonant_syllable (map, buffer, mask_array, last, p); }
-#line 67 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ buffer->merge_clusters (last, p); last = p; }
+#line 1 "NONE"
+	{te = p+1;}
+	break;
+	case 9:
+#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ process_syllable (consonant_syllable); }}
+	break;
+	case 11:
+#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ process_syllable (vowel_syllable); }}
+	break;
+	case 13:
+#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ process_syllable (standalone_cluster); }}
+	break;
+	case 7:
+#line 75 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ process_syllable (non_indic); }}
+	break;
+	case 8:
+#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ process_syllable (consonant_syllable); }}
+	break;
+	case 10:
+#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ process_syllable (vowel_syllable); }}
+	break;
+	case 12:
+#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ process_syllable (standalone_cluster); }}
+	break;
+	case 1:
+#line 72 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ process_syllable (consonant_syllable); }}
 	break;
 	case 3:
-#line 63 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ found_vowel_syllable (map, buffer, mask_array, last, p); }
-#line 67 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ buffer->merge_clusters (last, p); last = p; }
+#line 73 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ process_syllable (vowel_syllable); }}
 	break;
 	case 4:
-#line 64 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ found_standalone_cluster (map, buffer, mask_array, last, p); }
-#line 67 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ buffer->merge_clusters (last, p); last = p; }
+#line 74 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ process_syllable (standalone_cluster); }}
 	break;
-	case 1:
-#line 65 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ found_non_indic (map, buffer, mask_array, last, p); }
-#line 67 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ buffer->merge_clusters (last, p); last = p; }
-	break;
-#line 244 "hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 263 "hb-ot-shape-complex-indic-machine.hh.tmp"
 	}
 
 _again:
-	if ( cs == 0 )
-		goto _out;
+	switch ( _indic_syllable_machine_to_state_actions[cs] ) {
+	case 5:
+#line 1 "NONE"
+	{ts = 0;}
+	break;
+#line 272 "hb-ot-shape-complex-indic-machine.hh.tmp"
+	}
+
 	if ( ++p != pe )
 		goto _resume;
 	_test_eof: {}
 	if ( p == eof )
 	{
-	switch ( _indic_syllable_machine_eof_actions[cs] ) {
-	case 2:
-#line 62 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ found_consonant_syllable (map, buffer, mask_array, last, p); }
-#line 67 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ buffer->merge_clusters (last, p); last = p; }
-	break;
-	case 3:
-#line 63 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ found_vowel_syllable (map, buffer, mask_array, last, p); }
-#line 67 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ buffer->merge_clusters (last, p); last = p; }
-	break;
-	case 4:
-#line 64 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ found_standalone_cluster (map, buffer, mask_array, last, p); }
-#line 67 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ buffer->merge_clusters (last, p); last = p; }
-	break;
-	case 1:
-#line 65 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ found_non_indic (map, buffer, mask_array, last, p); }
-#line 67 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{ buffer->merge_clusters (last, p); last = p; }
-	break;
-#line 280 "hb-ot-shape-complex-indic-machine.hh.tmp"
+	if ( _indic_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _indic_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
 	}
 	}
 
-	_out: {}
 	}
 
-#line 102 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 110 "../../src/hb-ot-shape-complex-indic-machine.rl"
 
 }
 
 HB_END_DECLS
 
 #endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl
@@ -1,10 +1,10 @@
 /*
- * 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.
@@ -48,60 +48,68 @@ N    = 4;
 H    = 5;
 ZWNJ = 6;
 ZWJ  = 7;
 M    = 8;
 SM   = 9;
 VD   = 10;
 A    = 11;
 NBSP = 12;
+DOTTEDCIRCLE = 13;
 
 c = C | Ra;
+n = N N?;
 z = ZWJ|ZWNJ;
 matra_group = M N? H?;
 syllable_tail = SM? (VD VD?)?;
+place_holder = NBSP | DOTTEDCIRCLE;
 
-action found_consonant_syllable { found_consonant_syllable (map, buffer, mask_array, last, p); }
-action found_vowel_syllable { found_vowel_syllable (map, buffer, mask_array, last, p); }
-action found_standalone_cluster { found_standalone_cluster (map, buffer, mask_array, last, p); }
-action found_non_indic { found_non_indic (map, buffer, mask_array, last, p); }
 
-action next_syllable { buffer->merge_clusters (last, p); last = p; }
+consonant_syllable =	(c.n? (H.z?|z.H))* c.n? A? (H.z? | matra_group*)? syllable_tail;
+vowel_syllable =	(Ra H)? V n? (z?.H.c | ZWJ.c)* matra_group* syllable_tail;
+standalone_cluster =	(Ra H)? place_holder n? (z? H c)* matra_group* syllable_tail;
+other =			any;
 
-consonant_syllable =	(c.N? (H.z?|z.H))* c.N? A? (H.z? | matra_group*)? syllable_tail %(found_consonant_syllable);
-vowel_syllable =	(Ra H)? V N? (z?.H.c | ZWJ.c)? matra_group* syllable_tail %(found_vowel_syllable);
-standalone_cluster =	(Ra H)? NBSP N? (z? H c)? matra_group* syllable_tail %(found_standalone_cluster);
-non_indic = X %(found_non_indic);
+main := |*
+	consonant_syllable	=> { process_syllable (consonant_syllable); };
+	vowel_syllable		=> { process_syllable (vowel_syllable); };
+	standalone_cluster	=> { process_syllable (standalone_cluster); };
+	other			=> { process_syllable (non_indic); };
+*|;
 
-syllable =
-	  consonant_syllable
-	| vowel_syllable
-	| standalone_cluster
-	| non_indic
-	;
-
-main := (syllable %(next_syllable))**;
 
 }%%
 
+#define process_syllable(func) \
+  HB_STMT_START { \
+    /* fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #func); */ \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = syllable_serial; \
+    PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (!syllable_serial)) syllable_serial++; \
+  } HB_STMT_END
 
 static void
 find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array)
 {
-  unsigned int p, pe, eof;
+  unsigned int p, pe, eof, ts, te, act;
   int cs;
+  hb_glyph_info_t *info = buffer->info;
   %%{
     write init;
-    getkey buffer->info[p].indic_category();
+    getkey info[p].indic_category();
   }%%
 
   p = 0;
   pe = eof = buffer->len;
 
   unsigned int last = 0;
+  uint8_t syllable_serial = 1;
   %%{
     write exec;
   }%%
 }
 
 HB_END_DECLS
 
 #endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
@@ -0,0 +1,270 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH
+#define HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+#include "hb-ot-shape-complex-private.hh"
+
+
+/* buffer var allocations */
+#define indic_category() complex_var_persistent_u8_0() /* indic_category_t */
+#define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */
+
+#define INDIC_TABLE_ELEMENT_TYPE uint8_t
+
+/* Cateories used in the OpenType spec:
+ * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx
+ */
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum indic_category_t {
+  OT_X = 0,
+  OT_C,
+  OT_Ra, /* Not explicitly listed in the OT spec, but used in the grammar. */
+  OT_V,
+  OT_N,
+  OT_H,
+  OT_ZWNJ,
+  OT_ZWJ,
+  OT_M,
+  OT_SM,
+  OT_VD,
+  OT_A,
+  OT_NBSP,
+  OT_DOTTEDCIRCLE /* Not in the spec, but special in Uniscribe. /Very very/ special! */
+};
+
+/* Visual positions in a syllable from left to right. */
+enum indic_position_t {
+  POS_RA_TO_BECOME_REPH,
+  POS_PRE_M,
+  POS_PRE_C,
+  POS_BASE_C,
+  POS_ABOVE_C,
+  POS_BELOW_C,
+  POS_ABOVE_M,
+  POS_BELOW_M,
+  POS_POST_C,
+  POS_POST_M,
+  POS_SMVD
+};
+
+/* Categories used in IndicSyllabicCategory.txt from UCD. */
+enum indic_syllabic_category_t {
+  INDIC_SYLLABIC_CATEGORY_OTHER			= OT_X,
+
+  INDIC_SYLLABIC_CATEGORY_AVAGRAHA		= OT_X,
+  INDIC_SYLLABIC_CATEGORY_BINDU			= OT_SM,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT		= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER	= OT_NBSP,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER	= OT_X,
+  INDIC_SYLLABIC_CATEGORY_NUKTA			= OT_N,
+  INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER	= OT_X,
+  INDIC_SYLLABIC_CATEGORY_TONE_LETTER		= OT_X,
+  INDIC_SYLLABIC_CATEGORY_TONE_MARK		= OT_X,
+  INDIC_SYLLABIC_CATEGORY_VIRAMA		= OT_H,
+  INDIC_SYLLABIC_CATEGORY_VISARGA		= OT_SM,
+  INDIC_SYLLABIC_CATEGORY_VOWEL			= OT_V,
+  INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT	= OT_M,
+  INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT	= OT_V
+};
+
+/* Categories used in IndicSMatraCategory.txt from UCD */
+enum indic_matra_category_t {
+  INDIC_MATRA_CATEGORY_NOT_APPLICABLE		= POS_BASE_C,
+
+  INDIC_MATRA_CATEGORY_LEFT			= POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP			= POS_ABOVE_M,
+  INDIC_MATRA_CATEGORY_BOTTOM			= POS_BELOW_M,
+  INDIC_MATRA_CATEGORY_RIGHT			= POS_POST_M,
+
+  /* We don't really care much about these since we decompose them
+   * in the generic pre-shaping layer.  They will only be used if
+   * the font does not cover the decomposition.  In which case, we
+   * define these as aliases to the place we want the split-matra
+   * glyph to show up.  Quite arbitrary.
+   *
+   * TODO: There are some split matras without Unicode decompositions.
+   * We have to figure out what to do with them.
+   */
+  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= POS_POST_M,
+  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= POS_BELOW_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= POS_POST_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= POS_POST_M,
+
+  INDIC_MATRA_CATEGORY_INVISIBLE		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
+  INDIC_MATRA_CATEGORY_OVERSTRUCK		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
+  INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT	= INDIC_MATRA_CATEGORY_NOT_APPLICABLE
+};
+
+/* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation
+ * because gcc fails to optimize the latter and fills the table in at runtime. */
+#define INDIC_COMBINE_CATEGORIES(S,M) \
+  (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || (S == INDIC_SYLLABIC_CATEGORY_VIRAMA || S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT)) + \
+   ASSERT_STATIC_EXPR_ZERO (S < 16 && M < 16) + \
+   ((M << 4) | S))
+
+
+#include "hb-ot-shape-complex-indic-table.hh"
+
+/* XXX
+ * This is a hack for now.  We should:
+ * 1. Move this data into the main Indic table,
+ * and/or
+ * 2. Probe font lookups to determine consonant positions.
+ */
+static const struct consonant_position_t {
+  hb_codepoint_t u;
+  indic_position_t position;
+} consonant_positions[] = {
+  {0x0930, POS_BELOW_C},
+  {0x09AC, POS_BELOW_C},
+  {0x09AF, POS_POST_C},
+  {0x09B0, POS_BELOW_C},
+  {0x09F0, POS_BELOW_C},
+  {0x0A2F, POS_POST_C},
+  {0x0A30, POS_BELOW_C},
+  {0x0A35, POS_BELOW_C},
+  {0x0A39, POS_BELOW_C},
+  {0x0AB0, POS_BELOW_C},
+  {0x0B24, POS_BELOW_C},
+  {0x0B28, POS_BELOW_C},
+  {0x0B2C, POS_BELOW_C},
+  {0x0B2D, POS_BELOW_C},
+  {0x0B2E, POS_BELOW_C},
+  {0x0B2F, POS_POST_C},
+  {0x0B30, POS_BELOW_C},
+  {0x0B32, POS_BELOW_C},
+  {0x0B33, POS_BELOW_C},
+  {0x0B5F, POS_POST_C},
+  {0x0B71, POS_BELOW_C},
+  {0x0C15, POS_BELOW_C},
+  {0x0C16, POS_BELOW_C},
+  {0x0C17, POS_BELOW_C},
+  {0x0C18, POS_BELOW_C},
+  {0x0C19, POS_BELOW_C},
+  {0x0C1A, POS_BELOW_C},
+  {0x0C1B, POS_BELOW_C},
+  {0x0C1C, POS_BELOW_C},
+  {0x0C1D, POS_BELOW_C},
+  {0x0C1E, POS_BELOW_C},
+  {0x0C1F, POS_BELOW_C},
+  {0x0C20, POS_BELOW_C},
+  {0x0C21, POS_BELOW_C},
+  {0x0C22, POS_BELOW_C},
+  {0x0C23, POS_BELOW_C},
+  {0x0C24, POS_BELOW_C},
+  {0x0C25, POS_BELOW_C},
+  {0x0C26, POS_BELOW_C},
+  {0x0C27, POS_BELOW_C},
+  {0x0C28, POS_BELOW_C},
+  {0x0C2A, POS_BELOW_C},
+  {0x0C2B, POS_BELOW_C},
+  {0x0C2C, POS_BELOW_C},
+  {0x0C2D, POS_BELOW_C},
+  {0x0C2E, POS_BELOW_C},
+  {0x0C2F, POS_BELOW_C},
+  {0x0C30, POS_BELOW_C},
+  {0x0C32, POS_BELOW_C},
+  {0x0C33, POS_BELOW_C},
+  {0x0C35, POS_BELOW_C},
+  {0x0C36, POS_BELOW_C},
+  {0x0C37, POS_BELOW_C},
+  {0x0C38, POS_BELOW_C},
+  {0x0C39, POS_BELOW_C},
+  {0x0C95, POS_BELOW_C},
+  {0x0C96, POS_BELOW_C},
+  {0x0C97, POS_BELOW_C},
+  {0x0C98, POS_BELOW_C},
+  {0x0C99, POS_BELOW_C},
+  {0x0C9A, POS_BELOW_C},
+  {0x0C9B, POS_BELOW_C},
+  {0x0C9C, POS_BELOW_C},
+  {0x0C9D, POS_BELOW_C},
+  {0x0C9E, POS_BELOW_C},
+  {0x0C9F, POS_BELOW_C},
+  {0x0CA0, POS_BELOW_C},
+  {0x0CA1, POS_BELOW_C},
+  {0x0CA2, POS_BELOW_C},
+  {0x0CA3, POS_BELOW_C},
+  {0x0CA4, POS_BELOW_C},
+  {0x0CA5, POS_BELOW_C},
+  {0x0CA6, POS_BELOW_C},
+  {0x0CA7, POS_BELOW_C},
+  {0x0CA8, POS_BELOW_C},
+  {0x0CAA, POS_BELOW_C},
+  {0x0CAB, POS_BELOW_C},
+  {0x0CAC, POS_BELOW_C},
+  {0x0CAD, POS_BELOW_C},
+  {0x0CAE, POS_BELOW_C},
+  {0x0CAF, POS_BELOW_C},
+  {0x0CB0, POS_BELOW_C},
+  {0x0CB2, POS_BELOW_C},
+  {0x0CB3, POS_BELOW_C},
+  {0x0CB5, POS_BELOW_C},
+  {0x0CB6, POS_BELOW_C},
+  {0x0CB7, POS_BELOW_C},
+  {0x0CB8, POS_BELOW_C},
+  {0x0CB9, POS_BELOW_C},
+  {0x0CDE, POS_BELOW_C},
+  {0x0D2F, POS_POST_C},
+  {0x0D30, POS_POST_C},
+  {0x0D32, POS_BELOW_C},
+  {0x0D35, POS_POST_C},
+};
+
+/* XXX
+ * This is a hack for now.  We should move this data into the main Indic table.
+ * Or completely remove it and just check in the tables.
+ */
+static const hb_codepoint_t ra_chars[] = {
+  0x0930, /* Devanagari */
+  0x09B0, /* Bengali */
+  0x09F0, /* Bengali */
+  0x0A30, /* Gurmukhi */	/* No Reph */
+  0x0AB0, /* Gujarati */
+  0x0B30, /* Oriya */
+  0x0BB0, /* Tamil */		/* No Reph */
+  0x0C30, /* Telugu */		/* No Reph */
+  0x0CB0, /* Kannada */
+  0x0D30, /* Malayalam */	/* No Reph */
+};
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -1,10 +1,10 @@
 /*
- * 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.
@@ -19,241 +19,29 @@
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-private.hh"
-
-
-
-/* buffer var allocations */
-#define indic_category() complex_var_persistent_u8_0() /* indic_category_t */
-#define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */
-
-#define INDIC_TABLE_ELEMENT_TYPE uint8_t
-
-/* Cateories used in the OpenType spec:
- * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx
- */
-/* Note: This enum is duplicated in the -machine.rl source file.
- * Not sure how to avoid duplication. */
-enum indic_category_t {
-  OT_X = 0,
-  OT_C,
-  OT_Ra, /* Not explicitly listed in the OT spec, but used in the grammar. */
-  OT_V,
-  OT_N,
-  OT_H,
-  OT_ZWNJ,
-  OT_ZWJ,
-  OT_M,
-  OT_SM,
-  OT_VD,
-  OT_A,
-  OT_NBSP
-};
-
-/* Visual positions in a syllable from left to right. */
-enum indic_position_t {
-  POS_PRE = 1,
-  POS_BASE = 3,
-  POS_ABOVE = 5,
-  POS_BELOW = 7,
-  POS_POST = 9
-};
-
-/* Categories used in IndicSyllabicCategory.txt from UCD */
-/* The assignments are guesswork */
-enum indic_syllabic_category_t {
-  INDIC_SYLLABIC_CATEGORY_OTHER			= OT_X,
-
-  INDIC_SYLLABIC_CATEGORY_AVAGRAHA		= OT_X,
-  INDIC_SYLLABIC_CATEGORY_BINDU			= OT_SM,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT		= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER	= OT_NBSP,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER	= OT_X,
-  INDIC_SYLLABIC_CATEGORY_NUKTA			= OT_N,
-  INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER	= OT_X,
-  INDIC_SYLLABIC_CATEGORY_TONE_LETTER		= OT_X,
-  INDIC_SYLLABIC_CATEGORY_TONE_MARK		= OT_X,
-  INDIC_SYLLABIC_CATEGORY_VIRAMA		= OT_H,
-  INDIC_SYLLABIC_CATEGORY_VISARGA		= OT_SM,
-  INDIC_SYLLABIC_CATEGORY_VOWEL			= OT_V,
-  INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT	= OT_M,
-  INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT	= OT_V
-};
-
-/* Categories used in IndicSMatraCategory.txt from UCD */
-enum indic_matra_category_t {
-  INDIC_MATRA_CATEGORY_NOT_APPLICABLE		= POS_BASE,
-
-  INDIC_MATRA_CATEGORY_LEFT			= POS_PRE - 1, /* Move *before* existing "pre" chars */
-  INDIC_MATRA_CATEGORY_TOP			= POS_ABOVE,
-  INDIC_MATRA_CATEGORY_BOTTOM			= POS_BELOW,
-  INDIC_MATRA_CATEGORY_RIGHT			= POS_POST,
-
-  /* We don't really care much about these since we decompose them
-   * in the generic pre-shaping layer.  They will only be used if
-   * the font does not cover the decomposition.  In which case, we
-   * define these as aliases to the place we want the split-matra
-   * glyph to show up.  Quite arbitrary. */
-  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= INDIC_MATRA_CATEGORY_RIGHT,
-
-  INDIC_MATRA_CATEGORY_INVISIBLE		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
-  INDIC_MATRA_CATEGORY_OVERSTRUCK		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
-  INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT	= INDIC_MATRA_CATEGORY_NOT_APPLICABLE
-};
-
-/* Note: We use ASSERT_STATIC_EXPR_ZERO() instead of ASSERT_STATIC_EXPR() and the comma operation
- * because gcc fails to optimize the latter and fills the table in at runtime. */
-#define INDIC_COMBINE_CATEGORIES(S,M) \
-  (ASSERT_STATIC_EXPR_ZERO (M == INDIC_MATRA_CATEGORY_NOT_APPLICABLE || (S == INDIC_SYLLABIC_CATEGORY_VIRAMA || S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT)) + \
-   ASSERT_STATIC_EXPR_ZERO (S < 16 && M < 16) + \
-   ((M << 4) | S))
-
-#include "hb-ot-shape-complex-indic-table.hh"
+#include "hb-ot-shape-complex-indic-private.hh"
+#include "hb-ot-shape-private.hh"
 
-/* XXX
- * This is a hack for now.  We should:
- * 1. Move this data into the main Indic table,
- * and/or
- * 2. Probe font lookups to determine consonant positions.
- */
-static const struct consonant_position_t {
-  hb_codepoint_t u;
-  indic_position_t position;
-} consonant_positions[] = {
-  {0x0930, POS_BELOW},
-  {0x09AC, POS_BELOW},
-  {0x09AF, POS_POST},
-  {0x09B0, POS_BELOW},
-  {0x09F0, POS_BELOW},
-  {0x0A2F, POS_POST},
-  {0x0A30, POS_BELOW},
-  {0x0A35, POS_BELOW},
-  {0x0A39, POS_BELOW},
-  {0x0AB0, POS_BELOW},
-  {0x0B24, POS_BELOW},
-  {0x0B28, POS_BELOW},
-  {0x0B2C, POS_BELOW},
-  {0x0B2D, POS_BELOW},
-  {0x0B2E, POS_BELOW},
-  {0x0B2F, POS_POST},
-  {0x0B30, POS_BELOW},
-  {0x0B32, POS_BELOW},
-  {0x0B33, POS_BELOW},
-  {0x0B5F, POS_POST},
-  {0x0B71, POS_BELOW},
-  {0x0C15, POS_BELOW},
-  {0x0C16, POS_BELOW},
-  {0x0C17, POS_BELOW},
-  {0x0C18, POS_BELOW},
-  {0x0C19, POS_BELOW},
-  {0x0C1A, POS_BELOW},
-  {0x0C1B, POS_BELOW},
-  {0x0C1C, POS_BELOW},
-  {0x0C1D, POS_BELOW},
-  {0x0C1E, POS_BELOW},
-  {0x0C1F, POS_BELOW},
-  {0x0C20, POS_BELOW},
-  {0x0C21, POS_BELOW},
-  {0x0C22, POS_BELOW},
-  {0x0C23, POS_BELOW},
-  {0x0C24, POS_BELOW},
-  {0x0C25, POS_BELOW},
-  {0x0C26, POS_BELOW},
-  {0x0C27, POS_BELOW},
-  {0x0C28, POS_BELOW},
-  {0x0C2A, POS_BELOW},
-  {0x0C2B, POS_BELOW},
-  {0x0C2C, POS_BELOW},
-  {0x0C2D, POS_BELOW},
-  {0x0C2E, POS_BELOW},
-  {0x0C2F, POS_BELOW},
-  {0x0C30, POS_BELOW},
-  {0x0C32, POS_BELOW},
-  {0x0C33, POS_BELOW},
-  {0x0C35, POS_BELOW},
-  {0x0C36, POS_BELOW},
-  {0x0C37, POS_BELOW},
-  {0x0C38, POS_BELOW},
-  {0x0C39, POS_BELOW},
-  {0x0C95, POS_BELOW},
-  {0x0C96, POS_BELOW},
-  {0x0C97, POS_BELOW},
-  {0x0C98, POS_BELOW},
-  {0x0C99, POS_BELOW},
-  {0x0C9A, POS_BELOW},
-  {0x0C9B, POS_BELOW},
-  {0x0C9C, POS_BELOW},
-  {0x0C9D, POS_BELOW},
-  {0x0C9E, POS_BELOW},
-  {0x0C9F, POS_BELOW},
-  {0x0CA0, POS_BELOW},
-  {0x0CA1, POS_BELOW},
-  {0x0CA2, POS_BELOW},
-  {0x0CA3, POS_BELOW},
-  {0x0CA4, POS_BELOW},
-  {0x0CA5, POS_BELOW},
-  {0x0CA6, POS_BELOW},
-  {0x0CA7, POS_BELOW},
-  {0x0CA8, POS_BELOW},
-  {0x0CAA, POS_BELOW},
-  {0x0CAB, POS_BELOW},
-  {0x0CAC, POS_BELOW},
-  {0x0CAD, POS_BELOW},
-  {0x0CAE, POS_BELOW},
-  {0x0CAF, POS_BELOW},
-  {0x0CB0, POS_BELOW},
-  {0x0CB2, POS_BELOW},
-  {0x0CB3, POS_BELOW},
-  {0x0CB5, POS_BELOW},
-  {0x0CB6, POS_BELOW},
-  {0x0CB7, POS_BELOW},
-  {0x0CB8, POS_BELOW},
-  {0x0CB9, POS_BELOW},
-  {0x0CDE, POS_BELOW},
-  {0x0D2F, POS_POST},
-  {0x0D30, POS_POST},
-  {0x0D32, POS_BELOW},
-  {0x0D35, POS_POST},
-};
+static const struct indic_options_t
+{
+  indic_options_t (void)
+  {
+    char *c = getenv ("HB_OT_INDIC_OPTIONS");
+    uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
+  }
 
-/* XXX
- * This is a hack for now.  We should move this data into the main Indic table.
- */
-static const hb_codepoint_t ra_chars[] = {
-  0x0930, /* Devanagari */
-  0x09B0, /* Bengali */
-  0x09F0, /* Bengali */
-//0x09F1, /* Bengali */
-//0x0A30, /* Gurmukhi */
-  0x0AB0, /* Gujarati */
-  0x0B30, /* Oriya */
-//0x0BB0, /* Tamil */
-//0x0C30, /* Telugu */
-  0x0CB0, /* Kannada */
-//0x0D30, /* Malayalam */
-};
+  bool uniscribe_bug_compatible;
+} options;
 
 static int
 compare_codepoint (const void *pa, const void *pb)
 {
   hb_codepoint_t a = * (hb_codepoint_t *) pa;
   hb_codepoint_t b = * (hb_codepoint_t *) pb;
 
   return a < b ? -1 : a == b ? 0 : +1;
@@ -264,17 +52,17 @@ consonant_position (hb_codepoint_t u)
 {
   consonant_position_t *record;
 
   record = (consonant_position_t *) bsearch (&u, consonant_positions,
 					     ARRAY_LENGTH (consonant_positions),
 					     sizeof (consonant_positions[0]),
 					     compare_codepoint);
 
-  return record ? record->position : POS_BASE;
+  return record ? record->position : POS_BASE_C;
 }
 
 static bool
 is_ra (hb_codepoint_t u)
 {
   return !!bsearch (&u, ra_chars,
 		    ARRAY_LENGTH (ra_chars),
 		    sizeof (ra_chars[0]),
@@ -285,211 +73,263 @@ static bool
 is_joiner (const hb_glyph_info_t &info)
 {
   return !!(FLAG (info.indic_category()) & (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ)));
 }
 
 static bool
 is_consonant (const hb_glyph_info_t &info)
 {
-  return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra)));
+  /* Note:
+   *
+   * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
+   * cannot happen in a consonant syllable.  The plus side however is, we can call the
+   * consonant syllable logic from the vowel syllable function and get it all right! */
+  return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE)));
 }
 
-static const struct {
+struct feature_list_t {
   hb_tag_t tag;
   hb_bool_t is_global;
-} indic_basic_features[] =
+};
+
+static const feature_list_t
+indic_basic_features[] =
 {
   {HB_TAG('n','u','k','t'), true},
   {HB_TAG('a','k','h','n'), false},
   {HB_TAG('r','p','h','f'), false},
-  {HB_TAG('r','k','r','f'), false},
+  {HB_TAG('r','k','r','f'), true},
   {HB_TAG('p','r','e','f'), false},
   {HB_TAG('b','l','w','f'), false},
   {HB_TAG('h','a','l','f'), false},
-  {HB_TAG('v','a','t','u'), true},
   {HB_TAG('p','s','t','f'), false},
   {HB_TAG('c','j','c','t'), false},
+  {HB_TAG('v','a','t','u'), true},
 };
 
 /* Same order as the indic_basic_features array */
 enum {
   _NUKT,
   AKHN,
   RPHF,
-  RKRF,
+  _RKRF,
   PREF,
   BLWF,
   HALF,
-  _VATU,
   PSTF,
-  CJCT
+  CJCT,
+  VATU
 };
 
-static const hb_tag_t indic_other_features[] =
+static const feature_list_t
+indic_other_features[] =
 {
-  HB_TAG('p','r','e','s'),
-  HB_TAG('a','b','v','s'),
-  HB_TAG('b','l','w','s'),
-  HB_TAG('p','s','t','s'),
-  HB_TAG('h','a','l','n'),
+  {HB_TAG('i','n','i','t'), false},
+  {HB_TAG('p','r','e','s'), true},
+  {HB_TAG('a','b','v','s'), true},
+  {HB_TAG('b','l','w','s'), true},
+  {HB_TAG('p','s','t','s'), true},
+  {HB_TAG('h','a','l','n'), true},
 
-  HB_TAG('d','i','s','t'),
-  HB_TAG('a','b','v','m'),
-  HB_TAG('b','l','w','m'),
+  {HB_TAG('d','i','s','t'), true},
+  {HB_TAG('a','b','v','m'), true},
+  {HB_TAG('b','l','w','m'), true},
+};
+
+/* Same order as the indic_other_features array */
+enum {
+  INIT
 };
 
 
 static void
 initial_reordering (const hb_ot_map_t *map,
 		    hb_face_t *face,
 		    hb_buffer_t *buffer,
 		    void *user_data HB_UNUSED);
 static void
 final_reordering (const hb_ot_map_t *map,
 		  hb_face_t *face,
 		  hb_buffer_t *buffer,
 		  void *user_data HB_UNUSED);
 
 void
-_hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map, const hb_segment_properties_t  *props)
+_hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map,
+					     const hb_segment_properties_t *props HB_UNUSED)
 {
   map->add_bool_feature (HB_TAG('l','o','c','l'));
   /* The Indic specs do not require ccmp, but we apply it here since if
    * there is a use of it, it's typically at the beginning. */
   map->add_bool_feature (HB_TAG('c','c','m','p'));
 
   map->add_gsub_pause (initial_reordering, NULL);
 
-  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_basic_features); i++)
+  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_basic_features); i++) {
     map->add_bool_feature (indic_basic_features[i].tag, indic_basic_features[i].is_global);
+    map->add_gsub_pause (NULL, NULL);
+  }
 
   map->add_gsub_pause (final_reordering, NULL);
 
-  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_other_features); i++)
-    map->add_bool_feature (indic_other_features[i], true);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_other_features); i++) {
+    map->add_bool_feature (indic_other_features[i].tag, indic_other_features[i].is_global);
+    map->add_gsub_pause (NULL, NULL);
+  }
 }
 
 
 hb_ot_shape_normalization_mode_t
 _hb_ot_shape_complex_normalization_preference_indic (void)
 {
   /* We want split matras decomposed by the common shaping logic. */
   return HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED;
 }
 
 
 void
-_hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, hb_font_t *font)
+_hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map HB_UNUSED,
+					hb_buffer_t *buffer,
+					hb_font_t *font HB_UNUSED)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_position);
 
   /* We cannot setup masks here.  We save information about characters
    * and setup masks later on in a pause-callback. */
 
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
   {
-    unsigned int type = get_indic_categories (buffer->info[i].codepoint);
+    hb_glyph_info_t &info = buffer->info[i];
+    unsigned int type = get_indic_categories (info.codepoint);
 
-    buffer->info[i].indic_category() = type & 0x0F;
-    buffer->info[i].indic_position() = type >> 4;
+    info.indic_category() = type & 0x0F;
+    info.indic_position() = type >> 4;
 
-    if (buffer->info[i].indic_category() == OT_C) {
-      buffer->info[i].indic_position() = consonant_position (buffer->info[i].codepoint);
-      if (is_ra (buffer->info[i].codepoint))
-	buffer->info[i].indic_category() = OT_Ra;
-    } else if (buffer->info[i].codepoint == 0x200C)
-      buffer->info[i].indic_category() = OT_ZWNJ;
-    else if (buffer->info[i].codepoint == 0x200D)
-      buffer->info[i].indic_category() = OT_ZWJ;
+    /* The spec says U+0952 is OT_A.  However, testing shows that Uniscribe
+     * treats U+0951..U+0952 all as OT_VD.
+     * TESTS:
+     * U+092E,U+0947,U+0952
+     * U+092E,U+0952,U+0947
+     * U+092E,U+0947,U+0951
+     * U+092E,U+0951,U+0947
+     * */
+    if (unlikely (hb_in_range<hb_codepoint_t> (info.codepoint, 0x0951, 0x0954)))
+      info.indic_category() = OT_VD;
+
+    if (info.indic_category() == OT_C) {
+      info.indic_position() = consonant_position (info.codepoint);
+      if (is_ra (info.codepoint))
+	info.indic_category() = OT_Ra;
+    } else if (info.indic_category() == OT_SM ||
+	       info.indic_category() == OT_VD) {
+      info.indic_position() = POS_SMVD;
+    } else if (unlikely (info.codepoint == 0x200C))
+      info.indic_category() = OT_ZWNJ;
+    else if (unlikely (info.codepoint == 0x200D))
+      info.indic_category() = OT_ZWJ;
+    else if (unlikely (info.codepoint == 0x25CC))
+      info.indic_category() = OT_DOTTEDCIRCLE;
   }
 }
 
 static int
 compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
   int a = pa->indic_position();
   int b = pb->indic_position();
 
   return a < b ? -1 : a == b ? 0 : +1;
 }
 
+/* Rules from:
+ * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
+
 static void
-found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
-			  unsigned int start, unsigned int end)
+initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
+				       unsigned int start, unsigned int end)
 {
-  unsigned int i;
   hb_glyph_info_t *info = buffer->info;
 
-  /* Comments from:
-   * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
 
   /* 1. Find base consonant:
    *
    * The shaping engine finds the base consonant of the syllable, using the
    * following algorithm: starting from the end of the syllable, move backwards
    * until a consonant is found that does not have a below-base or post-base
    * form (post-base forms have to follow below-base forms), or that is not a
    * pre-base reordering Ra, or arrive at the first consonant. The consonant
    * stopped at will be the base.
    *
    *   o If the syllable starts with Ra + Halant (in a script that has Reph)
    *     and has more than one consonant, Ra is excluded from candidates for
    *     base consonants.
    */
 
   unsigned int base = end;
+  bool has_reph = false;
 
-  /* -> starting from the end of the syllable, move backwards */
-  i = end;
-  unsigned int limit = start;
-  if (info[start].indic_category() == OT_Ra && start + 2 <= end) {
-    limit += 2;
-    base = start;
-  };
-  do {
-    i--;
-    /* -> until a consonant is found */
-    if (is_consonant (info[i]))
+  {
+    /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
+     *    and has more than one consonant, Ra is excluded from candidates for
+     *    base consonants. */
+    unsigned int limit = start;
+    if (mask_array[RPHF] &&
+	start + 3 <= end &&
+	info[start].indic_category() == OT_Ra &&
+	info[start + 1].indic_category() == OT_H &&
+	!is_joiner (info[start + 2]))
     {
-      /* -> that does not have a below-base or post-base form
-       * (post-base forms have to follow below-base forms), */
-      if (info[i].indic_position() != POS_BELOW &&
-	  info[i].indic_position() != POS_POST)
-      {
-        base = i;
-	break;
-      }
+      limit += 2;
+      base = start;
+      has_reph = true;
+    };
 
-      /* -> or that is not a pre-base reordering Ra,
-       *
-       * TODO
-       */
+    /* -> starting from the end of the syllable, move backwards */
+    unsigned int i = end;
+    do {
+      i--;
+      /* -> until a consonant is found */
+      if (is_consonant (info[i]))
+      {
+	/* -> that does not have a below-base or post-base form
+	 * (post-base forms have to follow below-base forms), */
+	if (info[i].indic_position() != POS_BELOW_C &&
+	    info[i].indic_position() != POS_POST_C)
+	{
+	  base = i;
+	  break;
+	}
+
+	/* -> or that is not a pre-base reordering Ra,
+	 *
+	 * TODO
+	 */
 
-      /* ->  o If the syllable starts with Ra + Halant (in a script that has Reph)
-       *       and has more than one consonant, Ra is excluded from candidates for
-       *       base consonants.
-       *
-       * IMPLEMENTATION NOTES:
-       *
-       * We do this by adjusting limit accordingly before entering the loop.
-       */
+	/* -> or arrive at the first consonant. The consonant stopped at will
+	 * be the base. */
+	base = i;
+      }
+      else
+	if (is_joiner (info[i]))
+	  break;
+    } while (i > limit);
+    if (base < start)
+      base = start; /* Just in case... */
 
-      /* -> or arrive at the first consonant. The consonant stopped at will
-       * be the base. */
-      base = i;
+
+    /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
+     *    and has more than one consonant, Ra is excluded from candidates for
+     *    base consonants. */
+    if (has_reph && base == start) {
+      /* Have no other consonant, so Reph is not formed and Ra becomes base. */
+      has_reph = false;
     }
-    else
-      if (is_joiner (info[i]))
-        break;
-  } while (i > limit);
-  if (base < start)
-    base = start; /* Just in case... */
+  }
 
 
   /* 2. Decompose and reorder Matras:
    *
    * Each matra and any syllable modifier sign in the cluster are moved to the
    * appropriate position relative to the consonant(s) in the cluster. The
    * shaping engine decomposes two- or three-part matras into their constituent
    * parts before any repositioning. Matra characters are classified by which
@@ -516,233 +356,410 @@ found_consonant_syllable (const hb_ot_ma
    * IMPLEMENTATION NOTES:
    *
    * We don't need to do this: the normalize() routine already did this for us.
    */
 
 
   /* Reorder characters */
 
-  for (i = start; i < base; i++)
-    info[i].indic_position() = POS_PRE;
-  info[base].indic_position() = POS_BASE;
-
+  for (unsigned int i = start; i < base; i++)
+    info[i].indic_position() = POS_PRE_C;
+  info[base].indic_position() = POS_BASE_C;
 
   /* Handle beginning Ra */
-  if (start + 3 <= end &&
-      info[start].indic_category() == OT_Ra &&
-      info[start + 1].indic_category() == OT_H &&
-      !is_joiner (info[start + 2]))
-   {
-    info[start].indic_position() = POS_POST;
-    info[start].mask = mask_array[RPHF];
-   }
+  if (has_reph)
+    info[start].indic_position() = POS_RA_TO_BECOME_REPH;
 
   /* For old-style Indic script tags, move the first post-base Halant after
    * last consonant. */
   if ((map->get_chosen_script (0) & 0x000000FF) != '2') {
     /* We should only do this for Indic scripts which have a version two I guess. */
-    for (i = base + 1; i < end; i++)
+    for (unsigned int i = base + 1; i < end; i++)
       if (info[i].indic_category() == OT_H) {
         unsigned int j;
         for (j = end - 1; j > i; j--)
-	  if ((FLAG (info[j].indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra))))
+	  if (is_consonant (info[j]))
 	    break;
 	if (j > i) {
 	  /* Move Halant to after last consonant. */
 	  hb_glyph_info_t t = info[i];
 	  memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0]));
 	  info[j] = t;
 	}
         break;
       }
   }
 
   /* Attach ZWJ, ZWNJ, nukta, and halant to previous char to move with them. */
-  for (i = start + 1; i < end; i++)
-    if ((FLAG (info[i].indic_category()) &
-	 (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H))))
-      info[i].indic_position() = info[i - 1].indic_position();
+  if (!options.uniscribe_bug_compatible)
+  {
+    /* Please update the Uniscribe branch when touching this! */
+    for (unsigned int i = start + 1; i < end; i++)
+      if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H))))
+	info[i].indic_position() = info[i - 1].indic_position();
+  } else {
+    /*
+     * Uniscribe doesn't move the Halant with Left Matra.
+     * TEST: U+092B,U+093F,U+094DE
+     */
+    /* Please update the non-Uniscribe branch when touching this! */
+    for (unsigned int i = start + 1; i < end; i++)
+      if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H)))) {
+	info[i].indic_position() = info[i - 1].indic_position();
+	if (info[i].indic_category() == OT_H && info[i].indic_position() == POS_PRE_M)
+	  for (unsigned int j = i; j > start; j--)
+	    if (info[j - 1].indic_position() != POS_PRE_M) {
+	      info[i].indic_position() = info[j - 1].indic_position();
+	      break;
+	    }
+      }
+  }
 
   /* We do bubble-sort, skip malicious clusters attempts */
-  if (end - start > 20)
-    return;
-
-  /* Sit tight, rock 'n roll! */
-  hb_bubble_sort (info + start, end - start, compare_indic_order);
+  if (end - start < 64)
+  {
+    /* Sit tight, rock 'n roll! */
+    hb_bubble_sort (info + start, end - start, compare_indic_order);
+    /* Find base again */
+    base = end;
+    for (unsigned int i = start; i < end; i++)
+      if (info[i].indic_position() == POS_BASE_C) {
+        base = i;
+	break;
+      }
+  }
 
   /* Setup masks now */
 
   {
     hb_mask_t mask;
 
+    /* Reph */
+    for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++)
+      info[i].mask |= mask_array[RPHF];
+
     /* Pre-base */
     mask = mask_array[HALF] | mask_array[AKHN] | mask_array[CJCT];
-    for (i = start; i < base; i++)
+    for (unsigned int i = start; i < base; i++)
       info[i].mask  |= mask;
     /* Base */
     mask = mask_array[AKHN] | mask_array[CJCT];
     info[base].mask |= mask;
     /* Post-base */
     mask = mask_array[BLWF] | mask_array[PSTF] | mask_array[CJCT];
-    for (i = base + 1; i < end; i++)
+    for (unsigned int i = base + 1; i < end; i++)
       info[i].mask  |= mask;
   }
 
   /* Apply ZWJ/ZWNJ effects */
-  for (i = start + 1; i < end; i++)
+  for (unsigned int i = start + 1; i < end; i++)
     if (is_joiner (info[i])) {
       bool non_joiner = info[i].indic_category() == OT_ZWNJ;
       unsigned int j = i;
 
       do {
 	j--;
 
-	/* Reading the Unicode and OpenType specs, I think the following line
-	 * is correct, but this is not what the test suite expects currently.
-	 * The test suite has been drinking, not me...  But disable while
-	 * investigating.
-	 */
-	//info[j].mask &= !mask_array[CJCT];
+	info[j].mask &= ~mask_array[CJCT];
 	if (non_joiner)
-	  info[j].mask &= !mask_array[HALF];
+	  info[j].mask &= ~mask_array[HALF];
 
       } while (j > start && !is_consonant (info[j]));
     }
 }
 
 
 static void
-found_vowel_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
-		      unsigned int start, unsigned int end)
+initial_reordering_vowel_syllable (const hb_ot_map_t *map,
+				   hb_buffer_t *buffer,
+				   hb_mask_t *mask_array,
+				   unsigned int start, unsigned int end)
 {
-  /* TODO
-   * Not clear to me how this should work.  Do the matras move to before the
-   * independent vowel?  No idea.
-   */
+  /* We made the vowels look like consonants.  So let's call the consonant logic! */
+  initial_reordering_consonant_syllable (map, buffer, mask_array, start, end);
 }
 
 static void
-found_standalone_cluster (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
-			  unsigned int start, unsigned int end)
+initial_reordering_standalone_cluster (const hb_ot_map_t *map,
+				       hb_buffer_t *buffer,
+				       hb_mask_t *mask_array,
+				       unsigned int start, unsigned int end)
 {
-  /* TODO
-   * Easiest thing to do here is to convert the NBSP to consonant and
-   * call found_consonant_syllable.
-   */
+  /* We treat NBSP/dotted-circle as if they are consonants, so we should just chain.
+   * Only if not in compatibility mode that is... */
+
+  if (options.uniscribe_bug_compatible)
+  {
+    /* For dotted-circle, this is what Uniscribe does:
+     * If dotted-circle is the last glyph, it just does nothing.
+     * Ie. It doesn't form Reph. */
+    if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE)
+      return;
+  }
+
+  initial_reordering_consonant_syllable (map, buffer, mask_array, start, end);
 }
 
 static void
-found_non_indic (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
-		 unsigned int start, unsigned int end)
+initial_reordering_non_indic (const hb_ot_map_t *map HB_UNUSED,
+			      hb_buffer_t *buffer HB_UNUSED,
+			      hb_mask_t *mask_array HB_UNUSED,
+			      unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
 {
   /* Nothing to do right now.  If we ever switch to using the output
    * buffer in the reordering process, we'd need to next_glyph() here. */
 }
 
 #include "hb-ot-shape-complex-indic-machine.hh"
 
 static void
-remove_joiners (hb_buffer_t *buffer)
-{
-  /* For now we remove joiners.  However, Uniscbire seems to keep them
-   * and output a zero-width space glyph for them.  It is not clear to
-   * me how that is supposed to interact with GSUB. */
-
-  buffer->clear_output ();
-  unsigned int count = buffer->len;
-  for (buffer->idx = 0; buffer->idx < count;)
-    if (unlikely (is_joiner (buffer->info[buffer->idx])))
-      buffer->skip_glyph ();
-    else
-      buffer->next_glyph ();
-
-  buffer->swap_buffers ();
-}
-
-static void
 initial_reordering (const hb_ot_map_t *map,
-		    hb_face_t *face,
+		    hb_face_t *face HB_UNUSED,
 		    hb_buffer_t *buffer,
 		    void *user_data HB_UNUSED)
 {
   hb_mask_t mask_array[ARRAY_LENGTH (indic_basic_features)] = {0};
   unsigned int num_masks = ARRAY_LENGTH (indic_basic_features);
   for (unsigned int i = 0; i < num_masks; i++)
     mask_array[i] = map->get_1_mask (indic_basic_features[i].tag);
 
   find_syllables (map, buffer, mask_array);
-
-  remove_joiners (buffer);
 }
 
 static void
-final_reordering (const hb_ot_map_t *map,
-		  hb_face_t *face,
-		  hb_buffer_t *buffer,
-		  void *user_data HB_UNUSED)
+final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
+			   unsigned int start, unsigned int end)
 {
+  hb_glyph_info_t *info = buffer->info;
+
   /* 4. Final reordering:
    *
    * After the localized forms and basic shaping forms GSUB features have been
    * applied (see below), the shaping engine performs some final glyph
    * reordering before applying all the remaining font features to the entire
    * cluster.
-   *
-   *   o Reorder matras:
+   */
+
+  /* Find base again */
+  unsigned int base = end;
+  for (unsigned int i = start; i < end; i++)
+    if (info[i].indic_position() == POS_BASE_C) {
+      base = i;
+      break;
+    }
+
+  if (base == start) {
+    /* There's no Reph, and no left Matra to reposition.  Just merge the cluster
+     * and go home. */
+    buffer->merge_clusters (start, end);
+    return;
+  }
+
+  unsigned int start_of_last_cluster = base;
+
+  /*   o Reorder matras:
    *
    *     If a pre-base matra character had been reordered before applying basic
    *     features, the glyph can be moved closer to the main consonant based on
    *     whether half-forms had been formed. Actual position for the matra is
    *     defined as “after last standalone halant glyph, after initial matra
    *     position and before the main consonant”. If ZWJ or ZWNJ follow this
    *     halant, position is moved after it.
-   *
-   *   o Reorder reph:
+   */
+
+  {
+    unsigned int new_matra_pos = base - 1;
+    while (new_matra_pos > start &&
+	   !(FLAG (info[new_matra_pos].indic_category()) & (FLAG (OT_M) | FLAG (OT_H))))
+      new_matra_pos--;
+    /* If we found no Halant we are done.  Otherwise only proceed if the Halant does
+     * not belong to the Matra itself! */
+    if (info[new_matra_pos].indic_category() == OT_H &&
+	info[new_matra_pos].indic_position() != POS_PRE_M) {
+      /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
+      if (new_matra_pos + 1 < end && is_joiner (info[new_matra_pos + 1]))
+	new_matra_pos++;
+
+      /* Now go see if there's actually any matras... */
+      for (unsigned int i = new_matra_pos; i > start; i--)
+	if (info[i - 1].indic_position () == POS_PRE_M)
+	{
+	  unsigned int old_matra_pos = i - 1;
+	  hb_glyph_info_t matra = info[old_matra_pos];
+	  memmove (&info[old_matra_pos], &info[old_matra_pos + 1], (new_matra_pos - old_matra_pos) * sizeof (info[0]));
+	  info[new_matra_pos] = matra;
+	  start_of_last_cluster = MIN (new_matra_pos, start_of_last_cluster);
+	  new_matra_pos--;
+	}
+    }
+  }
+
+
+  /*   o Reorder reph:
    *
    *     Reph’s original position is always at the beginning of the syllable,
    *     (i.e. it is not reordered at the character reordering stage). However,
    *     it will be reordered according to the basic-forms shaping results.
    *     Possible positions for reph, depending on the script, are; after main,
    *     before post-base consonant forms, and after post-base consonant forms.
-   *
-   *       1. If reph should be positioned after post-base consonant forms,
-   *          proceed to step 5.
-   *
-   *       2. If the reph repositioning class is not after post-base: target
-   *          position is after the first explicit halant glyph between the
-   *          first post-reph consonant and last main consonant. If ZWJ or ZWNJ
-   *          are following this halant, position is moved after it. If such
-   *          position is found, this is the target position. Otherwise,
-   *          proceed to the next step.
-   *
-   *          Note: in old-implementation fonts, where classifications were
-   *          fixed in shaping engine, there was no case where reph position
-   *          will be found on this step.
-   *
-   *       3. If reph should be repositioned after the main consonant: from the
-   *          first consonant not ligated with main, or find the first
-   *          consonant that is not a potential pre-base reordering Ra.
-   *
-   *
-   *       4. If reph should be positioned before post-base consonant, find
-   *          first post-base classified consonant not ligated with main. If no
-   *          consonant is found, the target position should be before the
-   *          first matra, syllable modifier sign or vedic sign.
-   *
-   *       5. If no consonant is found in steps 3 or 4, move reph to a position
-   *          immediately before the first post-base matra, syllable modifier
-   *          sign or vedic sign that has a reordering class after the intended
-   *          reph position. For example, if the reordering position for reph
-   *          is post-main, it will skip above-base matras that also have a
-   *          post-main position.
-   *
-   *       6. Otherwise, reorder reph to the end of the syllable.
-   *
-   *   o Reorder pre-base reordering consonants:
+   */
+
+  /* If there's anything after the Ra that has the REPH pos, it ought to be halant.
+   * Which means that the font has failed to ligate the Reph.  In which case, we
+   * shouldn't move. */
+  if (start + 1 < end &&
+      info[start].indic_position() == POS_RA_TO_BECOME_REPH &&
+      info[start + 1].indic_position() != POS_RA_TO_BECOME_REPH)
+  {
+      unsigned int new_reph_pos;
+
+     enum reph_position_t {
+       REPH_AFTER_MAIN,
+       REPH_BEFORE_SUBSCRIPT,
+       REPH_AFTER_SUBSCRIPT,
+       REPH_BEFORE_POSTSCRIPT,
+       REPH_AFTER_POSTSCRIPT,
+     } reph_pos;
+
+     /* XXX Figure out old behavior too */
+     switch ((hb_tag_t) buffer->props.script)
+     {
+       case HB_SCRIPT_MALAYALAM:
+       case HB_SCRIPT_ORIYA:
+	 reph_pos = REPH_AFTER_MAIN;
+	 break;
+
+       case HB_SCRIPT_GURMUKHI:
+	 reph_pos = REPH_BEFORE_SUBSCRIPT;
+	 break;
+
+       case HB_SCRIPT_BENGALI:
+	 reph_pos = REPH_AFTER_SUBSCRIPT;
+	 break;
+
+       default:
+       case HB_SCRIPT_DEVANAGARI:
+       case HB_SCRIPT_GUJARATI:
+	 reph_pos = REPH_BEFORE_POSTSCRIPT;
+	 break;
+
+       case HB_SCRIPT_KANNADA:
+       case HB_SCRIPT_TAMIL:
+       case HB_SCRIPT_TELUGU:
+	 reph_pos = REPH_AFTER_POSTSCRIPT;
+	 break;
+     }
+
+    /*       1. If reph should be positioned after post-base consonant forms,
+     *          proceed to step 5.
+     */
+    if (reph_pos == REPH_AFTER_POSTSCRIPT)
+    {
+      goto reph_step_5;
+    }
+
+    /*       2. If the reph repositioning class is not after post-base: target
+     *          position is after the first explicit halant glyph between the
+     *          first post-reph consonant and last main consonant. If ZWJ or ZWNJ
+     *          are following this halant, position is moved after it. If such
+     *          position is found, this is the target position. Otherwise,
+     *          proceed to the next step.
+     *
+     *          Note: in old-implementation fonts, where classifications were
+     *          fixed in shaping engine, there was no case where reph position
+     *          will be found on this step.
+     */
+    {
+      new_reph_pos = start + 1;
+      while (new_reph_pos < base && info[new_reph_pos].indic_category() != OT_H)
+	new_reph_pos++;
+
+      if (new_reph_pos < base && info[new_reph_pos].indic_category() == OT_H) {
+	/* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
+	if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
+	  new_reph_pos++;
+	goto reph_move;
+      }
+    }
+
+    /*       3. If reph should be repositioned after the main consonant: find the
+     *          first consonant not ligated with main, or find the first
+     *          consonant that is not a potential pre-base reordering Ra.
+     */
+    if (reph_pos == REPH_AFTER_MAIN)
+    {
+      /* XXX */
+    }
+
+    /*       4. If reph should be positioned before post-base consonant, find
+     *          first post-base classified consonant not ligated with main. If no
+     *          consonant is found, the target position should be before the
+     *          first matra, syllable modifier sign or vedic sign.
+     */
+    /* This is our take on what step 4 is trying to say (and failing, BADLY). */
+    if (reph_pos == REPH_AFTER_SUBSCRIPT)
+    {
+      new_reph_pos = base;
+      while (new_reph_pos < end &&
+	     !( FLAG (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_POST_M) | FLAG (POS_SMVD))))
+	new_reph_pos++;
+      if (new_reph_pos < end)
+        goto reph_move;
+    }
+
+    /*       5. If no consonant is found in steps 3 or 4, move reph to a position
+     *          immediately before the first post-base matra, syllable modifier
+     *          sign or vedic sign that has a reordering class after the intended
+     *          reph position. For example, if the reordering position for reph
+     *          is post-main, it will skip above-base matras that also have a
+     *          post-main position.
+     */
+    reph_step_5:
+    {
+      /* XXX */
+    }
+
+    /*       6. Otherwise, reorder reph to the end of the syllable.
+     */
+    {
+      new_reph_pos = end - 1;
+      while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_SMVD)
+	new_reph_pos--;
+
+      /*
+       * If the Reph is to be ending up after a Matra,Halant sequence,
+       * position it before that Halant so it can interact with the Matra.
+       * However, if it's a plain Consonant,Halant we shouldn't do that.
+       * Uniscribe doesn't do this.
+       * TEST: U+0930,U+094D,U+0915,U+094B,U+094D
+       */
+      if (!options.uniscribe_bug_compatible &&
+	  unlikely (info[new_reph_pos].indic_category() == OT_H)) {
+	for (unsigned int i = base + 1; i < new_reph_pos; i++)
+	  if (info[i].indic_category() == OT_M) {
+	    /* Ok, got it. */
+	    new_reph_pos--;
+	  }
+      }
+      goto reph_move;
+    }
+
+    reph_move:
+    {
+      /* Move */
+      hb_glyph_info_t reph = info[start];
+      memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0]));
+      info[new_reph_pos] = reph;
+      start_of_last_cluster = start; /* Yay, one big cluster! */
+    }
+  }
+
+
+  /*   o Reorder pre-base reordering consonants:
    *
    *     If a pre-base reordering consonant is found, reorder it according to
    *     the following rules:
    *
    *       1. Only reorder a glyph produced by substitution during application
    *          of the feature. (Note that a font may shape a Ra consonant with
    *          the feature generally but block it in certain contexts.)
    *
@@ -752,14 +769,77 @@ final_reordering (const hb_ot_map_t *map
    *       3. If position is not found, reorder immediately before main
    *          consonant.
    */
 
   /* TODO */
 
 
 
+  /* Apply 'init' to the Left Matra if it's a word start. */
+  if (info[start].indic_position () == POS_PRE_M &&
+      (!start ||
+       !(FLAG (_hb_glyph_info_get_general_category (&info[start - 1])) &
+	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))))
+    info[start].mask |= mask_array[INIT];
+
+
+
+  /* Finish off the clusters and go home! */
+
+  if (!options.uniscribe_bug_compatible)
+  {
+    /* This is what Uniscribe does.  Ie. add cluster boundaries after Halant,ZWNJ.
+     * This means, half forms are submerged into the main consonants cluster.
+     * This is unnecessary, and makes cursor positioning harder, but that's what
+     * Uniscribe does. */
+    unsigned int cluster_start = start;
+    for (unsigned int i = start + 1; i < start_of_last_cluster; i++)
+      if (info[i - 1].indic_category() == OT_H && info[i].indic_category() == OT_ZWNJ) {
+        i++;
+	buffer->merge_clusters (cluster_start, i);
+	cluster_start = i;
+      }
+    start_of_last_cluster = cluster_start;
+  }
+
+  buffer->merge_clusters (start_of_last_cluster, end);
+}
+
+
+static void
+final_reordering (const hb_ot_map_t *map,
+		  hb_face_t *face HB_UNUSED,
+		  hb_buffer_t *buffer,
+		  void *user_data HB_UNUSED)
+{
+  unsigned int count = buffer->len;
+  if (!count) return;
+
+  hb_mask_t mask_array[ARRAY_LENGTH (indic_other_features)] = {0};
+  unsigned int num_masks = ARRAY_LENGTH (indic_other_features);
+  for (unsigned int i = 0; i < num_masks; i++)
+    mask_array[i] = map->get_1_mask (indic_other_features[i].tag);
+
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int last = 0;
+  unsigned int last_syllable = info[0].syllable();
+  for (unsigned int i = 1; i < count; i++)
+    if (last_syllable != info[i].syllable()) {
+      final_reordering_syllable (buffer, mask_array, last, i);
+      last = i;
+      last_syllable = info[last].syllable();
+    }
+  final_reordering_syllable (buffer, mask_array, last, count);
+
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
 }
 
 
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-misc.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-misc.cc
@@ -32,77 +32,86 @@
 /* When adding trivial shapers, eg. kana, hangul, etc, we can either
  * add a full shaper enum value for them, or switch on the script in
  * the default complex shaper.  The former is faster, so I think that's
  * what we would do, and hence the default complex shaper shall remain
  * empty.
  */
 
 void
-_hb_ot_shape_complex_collect_features_default (hb_ot_map_builder_t *map, const hb_segment_properties_t  *props)
+_hb_ot_shape_complex_collect_features_default (hb_ot_map_builder_t *map HB_UNUSED,
+					       const hb_segment_properties_t *props HB_UNUSED)
 {
 }
 
 hb_ot_shape_normalization_mode_t
 _hb_ot_shape_complex_normalization_preference_default (void)
 {
   return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
 }
 
 void
-_hb_ot_shape_complex_setup_masks_default (hb_ot_map_t *map, hb_buffer_t *buffer, hb_font_t *font)
+_hb_ot_shape_complex_setup_masks_default (hb_ot_map_t *map HB_UNUSED,
+					  hb_buffer_t *buffer HB_UNUSED,
+					  hb_font_t *font HB_UNUSED)
 {
 }
 
 
 
 /* Hangul shaper */
 
 static const hb_tag_t hangul_features[] =
 {
   HB_TAG('l','j','m','o'),
   HB_TAG('v','j','m','o'),
   HB_TAG('t','j','m','o'),
 };
 
 void
-_hb_ot_shape_complex_collect_features_hangul (hb_ot_map_builder_t *map, const hb_segment_properties_t  *props)
+_hb_ot_shape_complex_collect_features_hangul (hb_ot_map_builder_t *map,
+					      const hb_segment_properties_t *props HB_UNUSED)
 {
   for (unsigned int i = 0; i < ARRAY_LENGTH (hangul_features); i++)
     map->add_bool_feature (hangul_features[i]);
 }
 
 hb_ot_shape_normalization_mode_t
 _hb_ot_shape_complex_normalization_preference_hangul (void)
 {
   return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL;
 }
 
 void
-_hb_ot_shape_complex_setup_masks_hangul (hb_ot_map_t *map, hb_buffer_t *buffer, hb_font_t *font)
+_hb_ot_shape_complex_setup_masks_hangul (hb_ot_map_t *map HB_UNUSED,
+					 hb_buffer_t *buffer HB_UNUSED,
+					 hb_font_t *font HB_UNUSED)
 {
 }
 
 
 
 /* Thai / Lao shaper */
 
 void
-_hb_ot_shape_complex_collect_features_thai (hb_ot_map_builder_t *map, const hb_segment_properties_t  *props)
+_hb_ot_shape_complex_collect_features_thai (hb_ot_map_builder_t *map HB_UNUSED,
+					    const hb_segment_properties_t *props HB_UNUSED)
 {
 }
 
 hb_ot_shape_normalization_mode_t
 _hb_ot_shape_complex_normalization_preference_thai (void)
 {
   return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL;
 }
 
 void
-_hb_ot_shape_complex_setup_masks_thai (hb_ot_map_t *map, hb_buffer_t *buffer, hb_font_t *font)
+_hb_ot_shape_complex_setup_masks_thai (hb_ot_map_t *map HB_UNUSED,
+				       hb_buffer_t *buffer,
+				       hb_font_t *font HB_UNUSED)
 {
   /* The following is NOT specified in the MS OT Thai spec, however, it seems
    * to be what Uniscribe and other engines implement.  According to Eric Muller:
    *
    * When you have a sara am, decompose it in nikhahit + sara a, *and* mode the
    * nihka hit backwards over any *tone* mark (0E48-0E4B).
    *
    * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32>
@@ -136,25 +145,25 @@ void
 #define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0xE33 + 0xE4D)
 #define SARA_AA_FROM_SARA_AM(x) ((x) - 1)
 #define IS_TONE_MARK(x) (((x) & ~0x0083) == 0x0E48)
 
   buffer->clear_output ();
   unsigned int count = buffer->len;
   for (buffer->idx = 0; buffer->idx < count;)
   {
-    hb_codepoint_t u = buffer->info[buffer->idx].codepoint;
+    hb_codepoint_t u = buffer->cur().codepoint;
     if (likely (!IS_SARA_AM (u))) {
       buffer->next_glyph ();
       continue;
     }
 
     /* Is SARA AM. Decompose and reorder. */
-    uint16_t decomposed[2] = {uint16_t (NIKHAHIT_FROM_SARA_AM (u)),
-			      uint16_t (SARA_AA_FROM_SARA_AM (u))};
+    hb_codepoint_t decomposed[2] = {hb_codepoint_t (NIKHAHIT_FROM_SARA_AM (u)),
+				    hb_codepoint_t (SARA_AA_FROM_SARA_AM (u))};
     buffer->replace_glyphs (1, 2, decomposed);
     if (unlikely (buffer->in_error))
       return;
 
     /* Ok, let's see... */
     unsigned int end = buffer->out_len;
     unsigned int start = end - 2;
     while (start > 0 && IS_TONE_MARK (buffer->out_info[start - 1].codepoint))
@@ -162,21 +171,22 @@ void
 
     /* Move Nikhahit (end-2) to the beginning */
     hb_glyph_info_t t = buffer->out_info[end - 2];
     memmove (buffer->out_info + start + 1,
 	     buffer->out_info + start,
 	     sizeof (buffer->out_info[0]) * (end - start - 2));
     buffer->out_info[start] = t;
 
+    /* XXX Make this easier! */
     /* Make cluster */
     for (; start > 0 && buffer->out_info[start - 1].cluster == buffer->out_info[start].cluster; start--)
       ;
     for (; buffer->idx < count;)
-      if (buffer->info[buffer->idx].cluster == buffer->out_info[buffer->out_len - 1].cluster)
+      if (buffer->cur().cluster == buffer->prev().cluster)
         buffer->next_glyph ();
       else
         break;
     end = buffer->out_len;
 
     buffer->merge_out_clusters (start, end);
   }
   buffer->swap_buffers ();
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
@@ -30,26 +30,28 @@
 #include "hb-private.hh"
 
 #include "hb-ot-map-private.hh"
 #include "hb-ot-shape-normalize-private.hh"
 
 
 
 /* buffer var allocations, used during the entire shaping process */
-#define general_category() var1.u8[0] /* unicode general_category (hb_unicode_general_category_t) */
-#define combining_class() var1.u8[1] /* unicode combining_class (uint8_t) */
+#define unicode_props0()	var1.u8[0]
+#define unicode_props1()	var1.u8[1]
+
+/* buffer var allocations, used during the GSUB/GPOS processing */
+#define props_cache()		var1.u16[1] /* GSUB/GPOS glyph_props cache */
+#define syllable()		var2.u8[0] /* GSUB/GPOS shaping boundaries */
+#define lig_props()		var2.u8[1] /* GSUB/GPOS ligature tracking */
 
 /* buffer var allocations, used by complex shapers */
-#define complex_var_persistent_u8_0()	var2.u8[0]
-#define complex_var_persistent_u8_1()	var2.u8[1]
-#define complex_var_persistent_u16()	var2.u16[0]
-#define complex_var_temporary_u8_0()	var2.u8[2]
-#define complex_var_temporary_u8_1()	var2.u8[3]
-#define complex_var_temporary_u16()	var2.u16[1]
+#define complex_var_persistent_u8_0()	var2.u8[2]
+#define complex_var_persistent_u8_1()	var2.u8[3]
+#define complex_var_temporary_u8()	var2.u8[0]
 
 
 #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
   HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \
   HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \
   HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
@@ -61,17 +63,17 @@ enum hb_ot_complex_shaper_t {
   /* Just here to avoid enum trailing comma: */
   hb_ot_complex_shaper_generic = hb_ot_complex_shaper_default
 #undef HB_COMPLEX_SHAPER_IMPLEMENT
 };
 
 static inline hb_ot_complex_shaper_t
 hb_ot_shape_complex_categorize (const hb_segment_properties_t *props)
 {
-  switch ((int) props->script)
+  switch ((hb_tag_t) props->script)
   {
     default:
       return hb_ot_complex_shaper_default;
 
 
     /* Unicode-1.1 additions */
     case HB_SCRIPT_ARABIC:
     case HB_SCRIPT_MONGOLIAN:
@@ -131,17 +133,17 @@ hb_ot_shape_complex_categorize (const hb
     /* Unicode-6.0 additions */
     case HB_SCRIPT_BATAK:
     case HB_SCRIPT_BRAHMI:
 
 
     /* Simple */
 
     /* Unicode-1.1 additions */
-    /* TODO These two need their own shaper I guess? */
+    /* These have their own shaper now. */
     case HB_SCRIPT_LAO:
     case HB_SCRIPT_THAI:
 
     /* Unicode-2.0 additions */
     case HB_SCRIPT_TIBETAN:
 
     /* Unicode-3.2 additions */
     case HB_SCRIPT_TAGALOG:
--- a/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
@@ -63,85 +63,77 @@
  *
  *   - When a font does not support a character but supports its decomposition,
  *     well, use the decomposition.
  *
  *   - The Indic shaper requests decomposed output.  This will handle splitting
  *     matra for the Indic shaper.
  */
 
-static inline void
-set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode)
-{
-  info->general_category() = hb_unicode_general_category (unicode, info->codepoint);
-  info->combining_class() = _hb_unicode_modified_combining_class (unicode, info->codepoint);
-}
-
 static void
-output_glyph (hb_font_t *font, hb_buffer_t *buffer,
-	      hb_codepoint_t glyph)
+output_glyph (hb_buffer_t *buffer, hb_codepoint_t glyph)
 {
   buffer->output_glyph (glyph);
-  set_unicode_props (&buffer->out_info[buffer->out_len - 1], buffer->unicode);
+  _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer->unicode);
 }
 
 static bool
 decompose (hb_font_t *font, hb_buffer_t *buffer,
 	   bool shortest,
 	   hb_codepoint_t ab)
 {
   hb_codepoint_t a, b, glyph;
 
   if (!hb_unicode_decompose (buffer->unicode, ab, &a, &b) ||
       (b && !hb_font_get_glyph (font, b, 0, &glyph)))
     return FALSE;
 
   bool has_a = hb_font_get_glyph (font, a, 0, &glyph);
   if (shortest && has_a) {
     /* Output a and b */
-    output_glyph (font, buffer, a);
+    output_glyph (buffer, a);
     if (b)
-      output_glyph (font, buffer, b);
+      output_glyph (buffer, b);
     return TRUE;
   }
 
   if (decompose (font, buffer, shortest, a)) {
     if (b)
-      output_glyph (font, buffer, b);
+      output_glyph (buffer, b);
     return TRUE;
   }
 
   if (has_a) {
-    output_glyph (font, buffer, a);
+    output_glyph (buffer, a);
     if (b)
-      output_glyph (font, buffer, b);
+      output_glyph (buffer, b);
     return TRUE;
   }
 
   return FALSE;
 }
 
 static void
 decompose_current_glyph (hb_font_t *font, hb_buffer_t *buffer,
 			 bool shortest)
 {
-  if (decompose (font, buffer, shortest, buffer->info[buffer->idx].codepoint))
+  if (decompose (font, buffer, shortest, buffer->cur().codepoint))
     buffer->skip_glyph ();
   else
     buffer->next_glyph ();
 }
 
 static void
 decompose_single_char_cluster (hb_font_t *font, hb_buffer_t *buffer,
 			       bool will_recompose)
 {
   hb_codepoint_t glyph;
 
   /* If recomposing and font supports this, we're good to go */
-  if (will_recompose && hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, 0, &glyph)) {
+  if (will_recompose && hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph)) {
     buffer->next_glyph ();
     return;
   }
 
   decompose_current_glyph (font, buffer, will_recompose);
 }
 
 static void
@@ -158,18 +150,18 @@ decompose_multi_char_cluster (hb_font_t 
 
   while (buffer->idx < end)
     decompose_current_glyph (font, buffer, FALSE);
 }
 
 static int
 compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
-  unsigned int a = pa->combining_class();
-  unsigned int b = pb->combining_class();
+  unsigned int a = _hb_glyph_info_get_modified_combining_class (pa);
+  unsigned int b = _hb_glyph_info_get_modified_combining_class (pb);
 
   return a < b ? -1 : a == b ? 0 : +1;
 }
 
 void
 _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
 			hb_ot_shape_normalization_mode_t mode)
 {
@@ -187,17 +179,17 @@ void
   /* First round, decompose */
 
   buffer->clear_output ();
   count = buffer->len;
   for (buffer->idx = 0; buffer->idx < count;)
   {
     unsigned int end;
     for (end = buffer->idx + 1; end < count; end++)
-      if (buffer->info[buffer->idx].cluster != buffer->info[end].cluster)
+      if (buffer->cur().cluster != buffer->info[end].cluster)
         break;
 
     if (buffer->idx + 1 == end)
       decompose_single_char_cluster (font, buffer, recompose);
     else {
       decompose_multi_char_cluster (font, buffer, end);
       has_multichar_clusters = TRUE;
     }
@@ -209,22 +201,22 @@ void
     return; /* Done! */
 
 
   /* Second round, reorder (inplace) */
 
   count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
   {
-    if (buffer->info[i].combining_class() == 0)
+    if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0)
       continue;
 
     unsigned int end;
     for (end = i + 1; end < count; end++)
-      if (buffer->info[end].combining_class() == 0)
+      if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0)
         break;
 
     /* We are going to do a bubble-sort.  Only do this if the
      * sequence is short.  Doing it on long sequences can result
      * in an O(n^2) DoS. */
     if (end - i > 10) {
       i = end;
       continue;
@@ -249,38 +241,39 @@ void
   unsigned int starter = 0;
   buffer->next_glyph ();
   while (buffer->idx < count)
   {
     hb_codepoint_t composed, glyph;
     if (/* If mode is NOT COMPOSED_FULL (ie. it's COMPOSED_DIACRITICS), we don't try to
 	 * compose a CCC=0 character with it's preceding starter. */
 	(mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL ||
-	 buffer->info[buffer->idx].combining_class() != 0) &&
+	 _hb_glyph_info_get_modified_combining_class (&buffer->cur()) != 0) &&
 	/* If there's anything between the starter and this char, they should have CCC
 	 * smaller than this character's. */
 	(starter == buffer->out_len - 1 ||
-	 buffer->out_info[buffer->out_len - 1].combining_class() < buffer->info[buffer->idx].combining_class()) &&
+	 _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) &&
 	/* And compose. */
 	hb_unicode_compose (buffer->unicode,
 			    buffer->out_info[starter].codepoint,
-			    buffer->info[buffer->idx].codepoint,
+			    buffer->cur().codepoint,
 			    &composed) &&
 	/* And the font has glyph for the composite. */
 	hb_font_get_glyph (font, composed, 0, &glyph))
     {
       /* Composes. Modify starter and carry on. */
       buffer->out_info[starter].codepoint = composed;
-      set_unicode_props (&buffer->out_info[starter], buffer->unicode);
+      /* XXX update cluster */
+      _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode);
 
       buffer->skip_glyph ();
       continue;
     }
 
     /* Blocked, or doesn't compose. */
     buffer->next_glyph ();
 
-    if (buffer->out_info[buffer->out_len - 1].combining_class() == 0)
+    if (_hb_glyph_info_get_modified_combining_class (&buffer->prev()) == 0)
       starter = buffer->out_len - 1;
   }
   buffer->swap_buffers ();
 
 }
--- a/gfx/harfbuzz/src/hb-ot-shape-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-private.hh
@@ -48,9 +48,36 @@ struct hb_ot_shape_plan_t
 
 
 HB_INTERNAL hb_bool_t
 _hb_ot_shape (hb_font_t          *font,
 	      hb_buffer_t        *buffer,
 	      const hb_feature_t *features,
 	      unsigned int        num_features);
 
+
+inline void
+_hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode)
+{
+  info->unicode_props0() = ((unsigned int) hb_unicode_general_category (unicode, info->codepoint)) |
+			   (_hb_unicode_is_zero_width (info->codepoint) ? 0x80 : 0);
+  info->unicode_props1() = _hb_unicode_modified_combining_class (unicode, info->codepoint);
+}
+
+inline hb_unicode_general_category_t
+_hb_glyph_info_get_general_category (const hb_glyph_info_t *info)
+{
+  return (hb_unicode_general_category_t) (info->unicode_props0() & 0x7F);
+}
+
+inline unsigned int
+_hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info)
+{
+  return info->unicode_props1();
+}
+
+inline hb_bool_t
+_hb_glyph_info_is_zero_width (const hb_glyph_info_t *info)
+{
+  return !!(info->unicode_props0() & 0x80);
+}
+
 #endif /* HB_OT_SHAPE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -25,28 +25,30 @@
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-ot-shape-private.hh"
 #include "hb-ot-shape-normalize-private.hh"
 
 #include "hb-font-private.hh"
+#include "hb-set-private.hh"
 
 
 
 hb_tag_t common_features[] = {
   HB_TAG('c','c','m','p'),
   HB_TAG('l','i','g','a'),
   HB_TAG('l','o','c','l'),
   HB_TAG('m','a','r','k'),
   HB_TAG('m','k','m','k'),
   HB_TAG('r','l','i','g'),
 };
 
+
 hb_tag_t horizontal_features[] = {
   HB_TAG('c','a','l','t'),
   HB_TAG('c','l','i','g'),
   HB_TAG('c','u','r','s'),
   HB_TAG('k','e','r','n'),
 };
 
 /* Note:
@@ -164,41 +166,34 @@ hb_ot_shape_setup_masks (hb_ot_shape_con
   }
 }
 
 
 /* Main shaper */
 
 /* Prepare */
 
-static inline void
-set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode)
-{
-  info->general_category() = hb_unicode_general_category (unicode, info->codepoint);
-  info->combining_class() = _hb_unicode_modified_combining_class (unicode, info->codepoint);
-}
-
 static void
 hb_set_unicode_props (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
-    set_unicode_props (&buffer->info[i], buffer->unicode);
+    _hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode);
 }
 
 static void
 hb_form_clusters (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   for (unsigned int i = 1; i < count; i++)
-    if (FLAG (buffer->info[i].general_category()) &
+    if (FLAG (_hb_glyph_info_get_general_category (&buffer->info[i])) &
 	(FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
 	 FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
 	 FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
-      buffer->info[i].cluster = buffer->info[i - 1].cluster;
+      buffer->info[i].cluster = buffer->info[i - 1].cluster; /* XXX do the min() here */
 }
 
 static void
 hb_ensure_native_direction (hb_buffer_t *buffer)
 {
   hb_direction_t direction = buffer->props.direction;
 
   /* TODO vertical:
@@ -244,27 +239,26 @@ hb_map_glyphs (hb_font_t    *font,
 
   if (unlikely (!buffer->len))
     return;
 
   buffer->clear_output ();
 
   unsigned int count = buffer->len - 1;
   for (buffer->idx = 0; buffer->idx < count;) {
-    if (unlikely (_hb_unicode_is_variation_selector (buffer->info[buffer->idx + 1].codepoint))) {
-      hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, buffer->info[buffer->idx + 1].codepoint, &glyph);
-      buffer->replace_glyph (glyph);
-      buffer->skip_glyph ();
+    if (unlikely (_hb_unicode_is_variation_selector (buffer->cur(+1).codepoint))) {
+      hb_font_get_glyph (font, buffer->cur().codepoint, buffer->cur(+1).codepoint, &glyph);
+      buffer->replace_glyphs (2, 1, &glyph);
     } else {
-      hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, 0, &glyph);
+      hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph);
       buffer->replace_glyph (glyph);
     }
   }
   if (likely (buffer->idx < buffer->len)) {
-    hb_font_get_glyph (font, buffer->info[buffer->idx].codepoint, 0, &glyph);
+    hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph);
     buffer->replace_glyph (glyph);
   }
   buffer->swap_buffers ();
 }
 
 static void
 hb_substitute_default (hb_ot_shape_context_t *c)
 {
@@ -331,46 +325,25 @@ hb_ot_position_complex (hb_ot_shape_cont
 						   HB_DIRECTION_LTR,
 						   &c->buffer->pos[i].x_offset,
 						   &c->buffer->pos[i].y_offset);
     }
 
     c->applied_position_complex = TRUE;
   }
 
-  hb_ot_layout_position_finish (c->face, c->buffer);
+  hb_ot_layout_position_finish (c->buffer);
 
   return;
 }
 
 static void
-hb_position_complex_fallback (hb_ot_shape_context_t *c)
+hb_position_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED)
 {
   /* TODO Mark pos */
-  unsigned int count = c->buffer->len;
-  if (c->buffer->props.direction == HB_DIRECTION_RTL) {
-    for (unsigned int i = 1; i < count; i++) {
-      if (FLAG (c->buffer->info[i].general_category()) &
-	  (FLAG (HB_UNICODE_GENERAL_CATEGORY_FORMAT) |
-	   FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
-	   FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
-        c->buffer->pos[i].x_advance = 0;
-    }
-  } else {
-    for (unsigned int i = 1; i < count; i++) {
-      if (FLAG (c->buffer->info[i].general_category()) &
-	  (FLAG (HB_UNICODE_GENERAL_CATEGORY_FORMAT) |
-	   FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
-	   FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) {
-        hb_glyph_position_t& pos = c->buffer->pos[i];
-        pos.x_offset = -pos.x_advance;
-        pos.x_advance = 0;
-      }
-    }
-  }
 }
 
 static void
 hb_truetype_kern (hb_ot_shape_context_t *c)
 {
   /* TODO Check for kern=0 */
   unsigned int count = c->buffer->len;
   for (unsigned int i = 1; i < count; i++) {
@@ -395,31 +368,48 @@ hb_truetype_kern (hb_ot_shape_context_t 
 }
 
 static void
 hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
 {
   hb_truetype_kern (c);
 }
 
+static void
+hb_hide_zerowidth (hb_ot_shape_context_t *c)
+{
+  /* TODO Save the space character in the font? */
+  hb_codepoint_t space;
+  if (!hb_font_get_glyph (c->font, ' ', 0, &space))
+    return; /* No point! */
+
+  unsigned int count = c->buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    if (unlikely (_hb_glyph_info_is_zero_width (&c->buffer->info[i]))) {
+      c->buffer->info[i].codepoint = space;
+      c->buffer->pos[i].x_advance = 0;
+      c->buffer->pos[i].y_advance = 0;
+    }
+}
+
 
 /* Do it! */
 
 static void
 hb_ot_shape_execute_internal (hb_ot_shape_context_t *c)
 {
   c->buffer->deallocate_var_all ();
 
   /* Save the original direction, we use it later. */
   c->target_direction = c->buffer->props.direction;
 
-  HB_BUFFER_ALLOCATE_VAR (c->buffer, general_category);
-  HB_BUFFER_ALLOCATE_VAR (c->buffer, combining_class);
+  HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props0);
+  HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props1);
 
-  hb_set_unicode_props (c->buffer); /* BUFFER: Set general_category and combining_class */
+  hb_set_unicode_props (c->buffer);
 
   hb_form_clusters (c->buffer);
 
   hb_ensure_native_direction (c->buffer);
 
   _hb_ot_shape_normalize (c->font, c->buffer, hb_ot_shape_complex_normalization_preference (c->plan->shaper));
 
   hb_ot_shape_setup_masks (c);
@@ -443,18 +433,20 @@ hb_ot_shape_execute_internal (hb_ot_shap
 
     if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
       hb_buffer_reverse (c->buffer);
 
     if (position_fallback)
       hb_position_complex_fallback_visual (c);
   }
 
-  HB_BUFFER_DEALLOCATE_VAR (c->buffer, combining_class);
-  HB_BUFFER_DEALLOCATE_VAR (c->buffer, general_category);
+  hb_hide_zerowidth (c);
+
+  HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props1);
+  HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props0);
 
   c->buffer->props.direction = c->target_direction;
 
   c->buffer->deallocate_var_all ();
 }
 
 static void
 hb_ot_shape_plan_internal (hb_ot_shape_plan_t       *plan,
@@ -495,8 +487,42 @@ hb_bool_t
 
   buffer->guess_properties ();
 
   hb_ot_shape_plan_internal (&plan, font->face, &buffer->props, features, num_features);
   hb_ot_shape_execute (&plan, font, buffer, features, num_features);
 
   return TRUE;
 }
+
+
+void
+hb_ot_shape_glyphs_closure (hb_font_t          *font,
+			    hb_buffer_t        *buffer,
+			    const hb_feature_t *features,
+			    unsigned int        num_features,
+			    hb_set_t           *glyphs)
+{
+  hb_ot_shape_plan_t plan;
+
+  buffer->guess_properties ();
+
+  hb_ot_shape_plan_internal (&plan, font->face, &buffer->props, features, num_features);
+
+  /* TODO: normalization? have shapers do closure()? */
+  /* TODO: Deal with mirrored chars? */
+  hb_map_glyphs (font, buffer);
+
+  /* Seed it.  It's user's responsibility to have cleard glyphs
+   * if that's what they desire. */
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    hb_set_add (glyphs, buffer->info[i].codepoint);
+
+  /* And find transitive closure. */
+  hb_set_t copy;
+  copy.init ();
+
+  do {
+    copy.set (glyphs);
+    plan.map.substitute_closure (font->face, glyphs);
+  } while (!copy.equal (glyphs));
+}
--- a/gfx/harfbuzz/src/hb-ot.h
+++ b/gfx/harfbuzz/src/hb-ot.h
@@ -29,12 +29,20 @@
 #define HB_OT_H_IN
 
 #include "hb.h"
 
 #include "hb-ot-layout.h"
 #include "hb-ot-tag.h"
 
 HB_BEGIN_DECLS
+
+void
+hb_ot_shape_glyphs_closure (hb_font_t          *font,
+			    hb_buffer_t        *buffer,
+			    const hb_feature_t *features,
+			    unsigned int        num_features,
+			    hb_set_t           *glyphs);
+
 HB_END_DECLS
 
 #undef HB_OT_H_IN
 #endif /* HB_OT_H */
--- a/gfx/harfbuzz/src/hb-private.hh
+++ b/gfx/harfbuzz/src/hb-private.hh
@@ -1,11 +1,11 @@
 /*
  * Copyright © 2007,2008,2009  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -29,19 +29,21 @@
 #ifndef HB_PRIVATE_HH
 #define HB_PRIVATE_HH
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
 #include "hb.h"
+#define HB_H_IN
+#ifdef HAVE_OT
 #include "hb-ot.h"
-#define HB_H_IN
 #define HB_OT_H_IN
+#endif
 
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
 #include <assert.h>
 
 /* We only use these two for debug output.  However, the debug code is
  * always seen by the compiler (and optimized out in non-debug builds.
@@ -84,16 +86,18 @@ template <typename Type> static inline T
 
 #define _ASSERT_STATIC1(_line, _cond) typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1]
 #define _ASSERT_STATIC0(_line, _cond) _ASSERT_STATIC1 (_line, (_cond))
 #define ASSERT_STATIC(_cond) _ASSERT_STATIC0 (__LINE__, (_cond))
 
 #define ASSERT_STATIC_EXPR(_cond) ((void) sizeof (char[(_cond) ? 1 : -1]))
 #define ASSERT_STATIC_EXPR_ZERO(_cond) (0 * sizeof (char[(_cond) ? 1 : -1]))
 
+#define _PASTE1(a,b) a##b
+#define PASTE(a,b) _PASTE1(a,b)
 
 /* Lets assert int types.  Saves trouble down the road. */
 
 ASSERT_STATIC (sizeof (int8_t) == 1);
 ASSERT_STATIC (sizeof (uint8_t) == 1);
 ASSERT_STATIC (sizeof (int16_t) == 2);
 ASSERT_STATIC (sizeof (uint16_t) == 2);
 ASSERT_STATIC (sizeof (int32_t) == 4);
@@ -494,104 +498,183 @@ static inline bool
 	   unsigned int max_level)
 {
   return level < max_level;
 }
 
 #define DEBUG_LEVEL(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT))
 #define DEBUG(WHAT) (DEBUG_LEVEL (WHAT, 0))
 
-template <int max_level> inline bool /* always returns TRUE */
+template <int max_level> inline void
+_hb_debug_msg_va (const char *what,
+		  const void *obj,
+		  const char *func,
+		  bool indented,
+		  unsigned int level,
+		  int level_dir,
+		  const char *message,
+		  va_list ap)
+{
+  if (!_hb_debug (level, max_level))
+    return;
+
+  fprintf (stderr, "%-10s", what ? what : "");
+
+  if (obj)
+    fprintf (stderr, "(%0*lx) ", (unsigned int) (2 * sizeof (void *)), (unsigned long) obj);
+  else
+    fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), "");
+
+  if (indented) {
+    static const char bars[] = "││││││││││││││││││││││││││││││││││││││││";
+    fprintf (stderr, "%2d %s├%s",
+	     level,
+	     bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), 3 * level),
+	     level_dir ? (level_dir > 0 ? "╮" : "╯") : "╴");
+  } else
+    fprintf (stderr, "   ├╴");
+
+  if (func) {
+    /* If there's a class name, just write that. */
+    const char *dotdot = strstr (func, "::");
+    const char *space = strchr (func, ' ');
+    if (space && dotdot && space < dotdot)
+      func = space + 1;
+    unsigned int func_len = dotdot ? dotdot - func : strlen (func);
+    fprintf (stderr, "%.*s: ", func_len, func);
+  }
+
+  if (message)
+    vfprintf (stderr, message, ap);
+
+  fprintf (stderr, "\n");
+}
+template <> inline void
+_hb_debug_msg_va<0> (const char *what HB_UNUSED,
+		     const void *obj HB_UNUSED,
+		     const char *func HB_UNUSED,
+		     bool indented HB_UNUSED,
+		     unsigned int level HB_UNUSED,
+		     int level_dir HB_UNUSED,
+		     const char *message HB_UNUSED,
+		     va_list ap HB_UNUSED) {}
+
+template <int max_level> inline void
 _hb_debug_msg (const char *what,
 	       const void *obj,
 	       const char *func,
 	       bool indented,
-	       int level,
+	       unsigned int level,
+	       int level_dir,
 	       const char *message,
-	       ...) HB_PRINTF_FUNC(6, 7);
-template <int max_level> inline bool /* always returns TRUE */
+	       ...) HB_PRINTF_FUNC(7, 8);
+template <int max_level> inline void
 _hb_debug_msg (const char *what,
 	       const void *obj,
 	       const char *func,
 	       bool indented,
-	       int level,
+	       unsigned int level,
+	       int level_dir,
 	       const char *message,
 	       ...)
 {
   va_list ap;
   va_start (ap, message);
-
-  (void) (_hb_debug (level, max_level) &&
-	  fprintf (stderr, "%s", what) &&
-	  (obj && fprintf (stderr, "(%p)", obj), TRUE) &&
-	  fprintf (stderr, ": ") &&
-	  (func && fprintf (stderr, "%s: ", func), TRUE) &&
-	  (indented && fprintf (stderr, "%-*d-> ", level + 1, level), TRUE) &&
-	  vfprintf (stderr, message, ap) &&
-	  fprintf (stderr, "\n"));
-
+  _hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap);
   va_end (ap);
-
-  return TRUE;
 }
-template <> inline bool /* always returns TRUE */
-_hb_debug_msg<0> (const char *what,
-		  const void *obj,
-		  const char *func,
-		  bool indented,
-		  int level,
-		  const char *message,
-		  ...) HB_PRINTF_FUNC(6, 7);
-template <> inline bool /* always returns TRUE */
-_hb_debug_msg<0> (const char *what,
-		  const void *obj,
-		  const char *func,
-		  bool indented,
-		  int level,
-		  const char *message,
-		  ...)
-{
-  return TRUE;
-}
+template <> inline void
+_hb_debug_msg<0> (const char *what HB_UNUSED,
+		  const void *obj HB_UNUSED,
+		  const char *func HB_UNUSED,
+		  bool indented HB_UNUSED,
+		  unsigned int level HB_UNUSED,
+		  int level_dir HB_UNUSED,
+		  const char *message HB_UNUSED,
+		  ...) HB_PRINTF_FUNC(7, 8);
+template <> inline void
+_hb_debug_msg<0> (const char *what HB_UNUSED,
+		  const void *obj HB_UNUSED,
+		  const char *func HB_UNUSED,
+		  bool indented HB_UNUSED,
+		  unsigned int level HB_UNUSED,
+		  int level_dir HB_UNUSED,
+		  const char *message HB_UNUSED,
+		  ...) {}
 
-#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL, FALSE, (LEVEL), __VA_ARGS__)
-#define DEBUG_MSG(WHAT, OBJ, ...) DEBUG_MSG_LEVEL (WHAT, OBJ, 0, __VA_ARGS__)
-#define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, FALSE, 0, __VA_ARGS__)
+#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...)	_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL,    TRUE, (LEVEL), (LEVEL_DIR), __VA_ARGS__)
+#define DEBUG_MSG(WHAT, OBJ, ...) 				_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL,    FALSE, 0, 0, __VA_ARGS__)
+#define DEBUG_MSG_FUNC(WHAT, OBJ, ...)				_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, FALSE, 0, 0, __VA_ARGS__)
 
 
 /*
  * Trace
  */
 
 template <int max_level>
 struct hb_auto_trace_t {
   explicit inline hb_auto_trace_t (unsigned int *plevel_,
-				   const char *what,
-				   const void *obj,
+				   const char *what_,
+				   const void *obj_,
 				   const char *func,
-				   const char *message) : plevel(plevel_)
+				   const char *message,
+				   ...) : plevel (plevel_), what (what_), obj (obj_), returned (false)
+  {
+    if (plevel) ++*plevel;
+
+    va_list ap;
+    va_start (ap, message);
+    _hb_debug_msg_va<max_level> (what, obj, func, TRUE, plevel ? *plevel : 0, +1, message, ap);
+    va_end (ap);
+  }
+  inline ~hb_auto_trace_t (void)
   {
-    if (max_level) ++*plevel;
-    /* TODO support variadic args here */
-    _hb_debug_msg<max_level> (what, obj, func, TRUE, *plevel, "%s", message);
+    if (unlikely (!returned)) {
+      fprintf (stderr, "OUCH, returned with no call to TRACE_RETURN.  This is a bug, please report.  Level was %d.\n", plevel ? *plevel : -1);
+      _hb_debug_msg<max_level> (what, obj, NULL, TRUE, plevel ? *plevel : 1, -1, " ");
+      return;
+    }
+
+    if (plevel) --*plevel;
   }
-  ~hb_auto_trace_t (void) { if (max_level) --*plevel; }
+
+  inline bool ret (bool v)
+  {
+    if (unlikely (returned)) {
+      fprintf (stderr, "OUCH, double calls to TRACE_RETURN.  This is a bug, please report.\n");
+      return v;
+    }
+
+    _hb_debug_msg<max_level> (what, obj, NULL, TRUE, plevel ? *plevel : 1, -1, "return %s", v ? "true" : "false");
+    if (plevel) --*plevel;
+    plevel = NULL;
+    returned = true;
+    return v;
+  }
 
   private:
   unsigned int *plevel;
+  bool returned;
+  const char *what;
+  const void *obj;
 };
 template <> /* Optimize when tracing is disabled */
 struct hb_auto_trace_t<0> {
-  explicit inline hb_auto_trace_t (unsigned int *plevel_,
-				   const char *what,
-				   const void *obj,
-				   const char *func,
-				   const char *message) {}
+  explicit inline hb_auto_trace_t (unsigned int *plevel_ HB_UNUSED,
+				   const char *what HB_UNUSED,
+				   const void *obj HB_UNUSED,
+				   const char *func HB_UNUSED,
+				   const char *message HB_UNUSED,
+				   ...) {}
+
+  template <typename T>
+  inline T ret (T v) { return v; }
 };
 
+#define TRACE_RETURN(RET) trace.ret (RET)
 
 /* Misc */
 
 
 /* Pre-mature optimization:
  * Checks for lo <= u <= hi but with an optimization if lo and hi
  * are only different in a contiguous set of lower-most bits.
  */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-set-private.hh
@@ -0,0 +1,169 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_SET_PRIVATE_HH
+#define HB_SET_PRIVATE_HH
+
+#include "hb-private.hh"
+#include "hb-set.h"
+#include "hb-object-private.hh"
+
+
+/* TODO Make this faster and memmory efficient. */
+
+struct _hb_set_t
+{
+  inline void init (void) {
+    clear ();
+  }
+  inline void fini (void) {
+  }
+  inline void clear (void) {
+    memset (elts, 0, sizeof elts);
+  }
+  inline bool empty (void) const {
+    for (unsigned int i = 0; i < ARRAY_LENGTH (elts); i++)
+      if (elts[i])
+        return false;
+    return true;
+  }
+  inline void add (hb_codepoint_t g)
+  {
+    if (unlikely (g == SENTINEL)) return;
+    if (unlikely (g > MAX_G)) return;
+    elt (g) |= mask (g);
+  }
+  inline void del (hb_codepoint_t g)
+  {
+    if (unlikely (g > MAX_G)) return;
+    elt (g) &= ~mask (g);
+  }
+  inline bool has (hb_codepoint_t g) const
+  {
+    if (unlikely (g > MAX_G)) return false;
+    return !!(elt (g) & mask (g));
+  }
+  inline bool intersects (hb_codepoint_t first,
+			  hb_codepoint_t last) const
+  {
+    if (unlikely (first > MAX_G)) return false;
+    if (unlikely (last  > MAX_G)) last = MAX_G;
+    unsigned int end = last + 1;
+    for (hb_codepoint_t i = first; i < end; i++)
+      if (has (i))
+        return true;
+    return false;
+  }
+  inline bool equal (const hb_set_t *other) const
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      if (elts[i] != other->elts[i])
+        return false;
+    return true;
+  }
+  inline void set (const hb_set_t *other)
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      elts[i] = other->elts[i];
+  }
+  inline void union_ (const hb_set_t *other)
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      elts[i] |= other->elts[i];
+  }
+  inline void intersect (const hb_set_t *other)
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      elts[i] &= other->elts[i];
+  }
+  inline void subtract (const hb_set_t *other)
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      elts[i] &= ~other->elts[i];
+  }
+  inline void symmetric_difference (const hb_set_t *other)
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      elts[i] ^= other->elts[i];
+  }
+  inline bool next (hb_codepoint_t *codepoint)
+  {
+    if (unlikely (*codepoint == SENTINEL)) {
+      hb_codepoint_t i = get_min ();
+      if (i != SENTINEL) {
+        *codepoint = i;
+	return true;
+      } else
+        return false;
+    }
+    for (hb_codepoint_t i = *codepoint + 1; i < MAX_G + 1; i++)
+      if (has (i)) {
+        *codepoint = i;
+	return true;
+      }
+    return false;
+  }
+  inline hb_codepoint_t get_min (void) const
+  {
+    for (unsigned int i = 0; i < ELTS; i++)
+      if (elts[i])
+	for (unsigned int j = 0; i < BITS; j++)
+	  if (elts[i] & (1 << j))
+	    return i * BITS + j;
+    return SENTINEL;
+  }
+  inline hb_codepoint_t get_max (void) const
+  {
+    for (unsigned int i = ELTS; i; i--)
+      if (elts[i - 1])
+	for (unsigned int j = BITS; j; j--)
+	  if (elts[i - 1] & (1 << (j - 1)))
+	    return (i - 1) * BITS + (j - 1);
+    return SENTINEL;
+  }
+
+  typedef uint32_t elt_t;
+  static const unsigned int MAX_G = 65536 - 1; /* XXX Fix this... */
+  static const unsigned int SHIFT = 5;
+  static const unsigned int BITS = (1 << SHIFT);
+  static const unsigned int MASK = BITS - 1;
+  static const unsigned int ELTS = (MAX_G + 1 + (BITS - 1)) / BITS;
+  static  const hb_codepoint_t SENTINEL = (hb_codepoint_t) -1;
+
+  elt_t &elt (hb_codepoint_t g) { return elts[g >> SHIFT]; }
+  elt_t elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; }
+  elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & MASK); }
+
+  hb_object_header_t header;
+  elt_t elts[ELTS]; /* XXX 8kb */
+
+  ASSERT_STATIC (sizeof (elt_t) * 8 == BITS);
+  ASSERT_STATIC (sizeof (elt_t) * 8 * ELTS > MAX_G);
+};
+
+
+
+#endif /* HB_SET_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-set.cc
@@ -0,0 +1,191 @@
+/*
+ * 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
+ */
+
+#include "hb-set-private.hh"
+
+
+
+/* Public API */
+
+static hb_set_t _hb_set_nil = {
+  HB_OBJECT_HEADER_STATIC,
+
+  {0} /* elts */
+};
+
+
+hb_set_t *
+hb_set_create ()
+{
+  hb_set_t *set;
+
+  if (!(set = hb_object_create<hb_set_t> ()))
+    return &_hb_set_nil;
+
+  set->clear ();
+
+  return set;
+}
+
+hb_set_t *
+hb_set_get_empty (void)
+{
+  return &_hb_set_nil;
+}
+
+hb_set_t *
+hb_set_reference (hb_set_t *set)
+{
+  return hb_object_reference (set);
+}
+
+void
+hb_set_destroy (hb_set_t *set)
+{
+  if (!hb_object_destroy (set)) return;
+
+  set->fini ();
+
+  free (set);
+}
+
+hb_bool_t
+hb_set_set_user_data (hb_set_t        *set,
+			 hb_user_data_key_t *key,
+			 void *              data,
+			 hb_destroy_func_t   destroy,
+			 hb_bool_t           replace)
+{
+  return hb_object_set_user_data (set, key, data, destroy, replace);
+}
+
+void *
+hb_set_get_user_data (hb_set_t        *set,
+			 hb_user_data_key_t *key)
+{
+  return hb_object_get_user_data (set, key);
+}
+
+
+hb_bool_t
+hb_set_allocation_successful (hb_set_t  *set HB_UNUSED)
+{
+  return TRUE;
+}
+
+void
+hb_set_clear (hb_set_t *set)
+{
+  set->clear ();
+}
+
+hb_bool_t
+hb_set_empty (hb_set_t *set)
+{
+  return set->empty ();
+}
+
+hb_bool_t
+hb_set_has (hb_set_t       *set,
+	    hb_codepoint_t  codepoint)
+{
+  return set->has (codepoint);
+}
+
+void
+hb_set_add (hb_set_t       *set,
+	    hb_codepoint_t  codepoint)
+{
+  set->add (codepoint);
+}
+
+void
+hb_set_del (hb_set_t       *set,
+	    hb_codepoint_t  codepoint)
+{
+  set->del (codepoint);
+}
+
+hb_bool_t
+hb_set_equal (hb_set_t *set,
+	      hb_set_t *other)
+{
+  return set->equal (other);
+}
+
+void
+hb_set_set (hb_set_t *set,
+	    hb_set_t *other)
+{
+  set->set (other);
+}
+
+void
+hb_set_union (hb_set_t *set,
+	      hb_set_t *other)
+{
+  set->union_ (other);
+}
+
+void
+hb_set_intersect (hb_set_t *set,
+		  hb_set_t *other)
+{
+  set->intersect (other);
+}
+
+void
+hb_set_subtract (hb_set_t *set,
+		 hb_set_t *other)
+{
+  set->subtract (other);
+}
+
+void
+hb_set_symmetric_difference (hb_set_t *set,
+			     hb_set_t *other)
+{
+  set->symmetric_difference (other);
+}
+
+hb_codepoint_t
+hb_set_min (hb_set_t *set)
+{
+  return set->get_min ();
+}
+
+hb_codepoint_t
+hb_set_max (hb_set_t *set)
+{
+  return set->get_max ();
+}
+
+hb_bool_t
+hb_set_next (hb_set_t       *set,
+	     hb_codepoint_t *codepoint)
+{
+  return set->next (codepoint);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-set.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_SET_H
+#define HB_SET_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+
+typedef struct _hb_set_t hb_set_t;
+
+
+hb_set_t *
+hb_set_create (void);
+
+hb_set_t *
+hb_set_get_empty (void);
+
+hb_set_t *
+hb_set_reference (hb_set_t *set);
+
+void
+hb_set_destroy (hb_set_t *set);
+
+hb_bool_t
+hb_set_set_user_data (hb_set_t           *set,
+		      hb_user_data_key_t *key,
+		      void *              data,
+		      hb_destroy_func_t   destroy,
+		      hb_bool_t           replace);
+
+void *
+hb_set_get_user_data (hb_set_t           *set,
+		      hb_user_data_key_t *key);
+
+
+/* Returns FALSE if allocation has failed before */
+hb_bool_t
+hb_set_allocation_successful (hb_set_t  *set);
+
+void
+hb_set_clear (hb_set_t *set);
+
+hb_bool_t
+hb_set_empty (hb_set_t *set);
+
+hb_bool_t
+hb_set_has (hb_set_t       *set,
+	    hb_codepoint_t  codepoint);
+
+/* Right now limited to 16-bit integers.  Eventually will do full codepoint range, sans -1
+ * which we will use as a sentinel. */
+void
+hb_set_add (hb_set_t       *set,
+	    hb_codepoint_t  codepoint);
+
+void
+hb_set_del (hb_set_t       *set,
+	    hb_codepoint_t  codepoint);
+
+hb_bool_t
+hb_set_equal (hb_set_t *set,
+	      hb_set_t *other);
+
+void
+hb_set_set (hb_set_t *set,
+	    hb_set_t *other);
+
+void
+hb_set_union (hb_set_t *set,
+	      hb_set_t *other);
+
+void
+hb_set_intersect (hb_set_t *set,
+		  hb_set_t *other);
+
+void
+hb_set_subtract (hb_set_t *set,
+		 hb_set_t *other);
+
+void
+hb_set_symmetric_difference (hb_set_t *set,
+			     hb_set_t *other);
+
+/* Returns -1 if set empty. */
+hb_codepoint_t
+hb_set_min (hb_set_t *set);
+
+/* Returns -1 if set empty. */
+hb_codepoint_t
+hb_set_max (hb_set_t *set);
+
+/* Pass -1 in to get started. */
+hb_bool_t
+hb_set_next (hb_set_t       *set,
+	     hb_codepoint_t *codepoint);
+
+/* TODO: Add faster iteration API? */
+
+
+HB_END_DECLS
+
+#endif /* HB_SET_H */
--- a/gfx/harfbuzz/src/hb-shape.cc
+++ b/gfx/harfbuzz/src/hb-shape.cc
@@ -116,16 +116,18 @@ hb_shape_list_shapers (void)
 
 hb_bool_t
 hb_shape_full (hb_font_t          *font,
 	       hb_buffer_t        *buffer,
 	       const hb_feature_t *features,
 	       unsigned int        num_features,
 	       const char * const *shaper_list)
 {
+  hb_font_make_immutable (font); /* So we can safely cache stuff on it */
+
   if (likely (!shaper_list)) {
     for (unsigned int i = 0; i < ARRAY_LENGTH (shapers); i++)
       if (likely (shapers[i].func (font, buffer, features, num_features)))
         return TRUE;
   } else {
     while (*shaper_list) {
       for (unsigned int i = 0; i < ARRAY_LENGTH (shapers); i++)
 	if (0 == strcmp (*shaper_list, shapers[i].name)) {
--- a/gfx/harfbuzz/src/hb-tt-font.cc
+++ b/gfx/harfbuzz/src/hb-tt-font.cc
@@ -19,22 +19,23 @@
  * 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-font-private.hh" /* Shall be first since may include windows.h */
+
 #include "hb-open-type-private.hh"
 
 #include "hb-ot-hhea-table.hh"
 #include "hb-ot-hmtx-table.hh"
 
-#include "hb-font-private.hh"
 #include "hb-blob.h"
 
 #include <string.h>
 
 
 
 #if 0
 struct hb_tt_font_t
--- a/gfx/harfbuzz/src/hb-unicode-private.hh
+++ b/gfx/harfbuzz/src/hb-unicode-private.hh
@@ -109,10 +109,48 @@ HB_INTERNAL unsigned int
 static inline hb_bool_t
 _hb_unicode_is_variation_selector (hb_codepoint_t unicode)
 {
   return unlikely ((unicode >=  0x180B && unicode <=  0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR ONE..THREE */
 		   (unicode >=  0xFE00 && unicode <=  0xFE0F) || /* VARIATION SELECTOR-1..16 */
 		   (unicode >= 0xE0100 && unicode <= 0xE01EF));  /* VARIATION SELECTOR-17..256 */
 }
 
+/* Zero-Width invisible characters:
+ *
+ *  00AD  SOFT HYPHEN
+ *  034F  COMBINING GRAPHEME JOINER
+ *
+ *  200B  ZERO WIDTH SPACE
+ *  200C  ZERO WIDTH NON-JOINER
+ *  200D  ZERO WIDTH JOINER
+ *  200E  LEFT-TO-RIGHT MARK
+ *  200F  RIGHT-TO-LEFT MARK
+ *
+ *  2028  LINE SEPARATOR
+ *
+ *  202A  LEFT-TO-RIGHT EMBEDDING
+ *  202B  RIGHT-TO-LEFT EMBEDDING
+ *  202C  POP DIRECTIONAL FORMATTING
+ *  202D  LEFT-TO-RIGHT OVERRIDE
+ *  202E  RIGHT-TO-LEFT OVERRIDE
+ *
+ *  2060  WORD JOINER
+ *  2061  FUNCTION APPLICATION
+ *  2062  INVISIBLE TIMES
+ *  2063  INVISIBLE SEPARATOR
+ *
+ *  FEFF  ZERO WIDTH NO-BREAK SPACE
+ */
+static inline hb_bool_t
+_hb_unicode_is_zero_width (hb_codepoint_t ch)
+{
+  return ((ch & ~0x007F) == 0x2000 && (
+	  (ch >= 0x200B && ch <= 0x200F) ||
+	  (ch >= 0x202A && ch <= 0x202E) ||
+	  (ch >= 0x2060 && ch <= 0x2063) ||
+	  (ch == 0x2028)
+	 )) || unlikely (ch == 0x00AD
+		      || ch == 0x034F
+		      || ch == 0xFEFF);
+}
 
 #endif /* HB_UNICODE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-unicode.cc
+++ b/gfx/harfbuzz/src/hb-unicode.cc
@@ -80,28 +80,26 @@ hb_unicode_script_nil (hb_unicode_funcs_
 
 static hb_bool_t
 hb_unicode_compose_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
 			hb_codepoint_t      a         HB_UNUSED,
 			hb_codepoint_t      b         HB_UNUSED,
 			hb_codepoint_t     *ab        HB_UNUSED,
 			void               *user_data HB_UNUSED)
 {
-  /* TODO handle Hangul jamo here? */
   return FALSE;
 }
 
 static hb_bool_t
 hb_unicode_decompose_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
 			  hb_codepoint_t      ab        HB_UNUSED,
 			  hb_codepoint_t     *a         HB_UNUSED,
 			  hb_codepoint_t     *b         HB_UNUSED,
 			  void               *user_data HB_UNUSED)
 {
-  /* TODO handle Hangul jamo here? */
   return FALSE;
 }
 
 
 hb_unicode_funcs_t _hb_unicode_funcs_nil = {
   HB_OBJECT_HEADER_STATIC,
 
   NULL, /* parent */
--- a/gfx/harfbuzz/src/hb-uniscribe.cc
+++ b/gfx/harfbuzz/src/hb-uniscribe.cc
@@ -354,16 +354,19 @@ retry:
 				glyphs_size - glyphs_offset,
 				/* out */
 				log_clusters + chars_offset,
 				char_props + chars_offset,
 				glyphs + glyphs_offset,
 				glyph_props + glyphs_offset,
 				(int *) &glyphs_len);
 
+      for (unsigned int j = chars_offset; j < chars_offset + item_chars_len; j++)
+        log_clusters[j] += glyphs_offset;
+
       if (unlikely (items[i].a.fNoGlyphIndex))
 	FAIL ("ScriptShapeOpenType() set fNoGlyphIndex");
       if (unlikely (hr == E_OUTOFMEMORY))
       {
         buffer->ensure (buffer->allocated * 2);
 	if (buffer->in_error)
 	  FAIL ("Buffer resize failed");
 	goto retry;
--- a/gfx/harfbuzz/src/hb-version.h
+++ b/gfx/harfbuzz/src/hb-version.h
@@ -32,20 +32,20 @@
 #define HB_VERSION_H
 
 #include "hb-common.h"
 
 HB_BEGIN_DECLS
 
 
 #define HB_VERSION_MAJOR 0
-#define HB_VERSION_MINOR 7
+#define HB_VERSION_MINOR 9
 #define HB_VERSION_MICRO 0
 
-#define HB_VERSION_STRING "0.7.0"
+#define HB_VERSION_STRING "0.9.0"
 
 #define HB_VERSION_CHECK(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) >= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
 
 
 void
 hb_version (unsigned int *major,
--- a/gfx/harfbuzz/src/hb-warning.cc
+++ b/gfx/harfbuzz/src/hb-warning.cc
@@ -19,18 +19,18 @@
  * 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-atomic-private.hh"
 #include "hb-mutex-private.hh"
-#include "hb-object-private.hh"
 
 
 #if !defined(HB_NO_MT) && defined(HB_ATOMIC_INT_NIL)
 #ifdef _MSC_VER
 #pragma message("Could not find any system to define atomic_int macros, library will NOT be thread-safe")
 #else
 #warning "Could not find any system to define atomic_int macros, library will NOT be thread-safe"
 #endif
--- a/gfx/harfbuzz/src/hb.h
+++ b/gfx/harfbuzz/src/hb.h
@@ -27,16 +27,17 @@
 #ifndef HB_H
 #define HB_H
 #define HB_H_IN
 
 #include "hb-blob.h"
 #include "hb-buffer.h"
 #include "hb-common.h"
 #include "hb-font.h"
+#include "hb-set.h"
 #include "hb-shape.h"
 #include "hb-unicode.h"
 #include "hb-version.h"
 
 HB_BEGIN_DECLS
 HB_END_DECLS
 
 #undef HB_H_IN
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/indic.cc
@@ -0,0 +1,46 @@
+/*
+ * 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
+ */
+
+#include "hb-ot-shape-complex-indic-private.hh"
+
+int
+main (void)
+{
+  hb_unicode_funcs_t *funcs = hb_unicode_funcs_get_default ();
+
+  printf ("There are split matras without a Unicode decomposition:\n");
+  for (hb_codepoint_t u = 0; u < 0x110000; u++)
+  {
+    unsigned int type = get_indic_categories (u);
+
+    unsigned int category = type & 0x0F;
+    unsigned int position = type >> 4;
+
+    hb_codepoint_t a, b;
+    if (!hb_unicode_decompose (funcs, u, &a, &b))
+      printf ("U+%04X\n", u);
+  }
+}
--- a/gfx/harfbuzz/src/main.cc
+++ b/gfx/harfbuzz/src/main.cc
@@ -119,20 +119,21 @@ main (int argc, char **argv)
 	  if (!script.has_default_lang_sys())
 	    printf ("      No default language system\n");
 	  int num_langsys = script.get_lang_sys_count ();
 	  printf ("      %d language system(s) found in script\n", num_langsys);
 	  for (int n_langsys = script.has_default_lang_sys() ? -1 : 0; n_langsys < num_langsys; n_langsys++) {
 	    const LangSys &langsys = n_langsys == -1
 				   ? script.get_default_lang_sys ()
 				   : script.get_lang_sys (n_langsys);
-	    printf (n_langsys == -1
-		   ? "      Default Language System\n"
-		   : "      Language System %2d of %2d: %.4s\n", n_langsys, num_langsys,
-	            (const char *)script.get_lang_sys_tag (n_langsys));
+	    if (n_langsys == -1)
+	      printf ("      Default Language System\n");
+	    else
+	      printf ("      Language System %2d of %2d: %.4s\n", n_langsys, num_langsys,
+		      (const char *)script.get_lang_sys_tag (n_langsys));
 	    if (langsys.get_required_feature_index () == Index::NOT_FOUND_INDEX)
 	      printf ("        No required feature\n");
 
 	    int num_features = langsys.get_feature_count ();
 	    printf ("        %d feature(s) found in language system\n", num_features);
 	    for (int n_feature = 0; n_feature < num_features; n_feature++) {
 	      printf ("        Feature index %2d of %2d: %d\n", n_feature, num_features,
 	              langsys.get_feature_index (n_feature));