Merge from upstream harfbuzz

Change-Id: I4feba5e1b841b0325ca00cdb6e4964b1e0256fdc
diff --git a/NEWS b/NEWS
index 63af376..feb71ba 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,141 @@
+Overview of changes leading to 0.9.9
+Wednesday, December 5, 2012
+====================================
+
+- Fix build on Windows.
+- Minor improvements.
+
+
+Overview of changes leading to 0.9.8
+Tuesday, December 4, 2012
+====================================
+
+
+- Actually implement hb_shape_plan_get_shaper ().
+- Make UCDB data tables const.
+- Lots of internal refactoring in OTLayout tables.
+- Flesh out hb_ot_layout_lookup_collect_glyphs().
+
+New API:
+
+hb_ot_layout_collect_lookups()
+hb_ot_layout_get_size_params()
+
+
+Overview of changes leading to 0.9.7
+Sunday, November 21, 2012
+====================================
+
+
+HarfBuzz "All-You-Can-Eat-Sushi" (aka Vancouver) Hackfest and follow-on fixes.
+
+- Fix Arabic contextual joining using pre-context text.
+- Fix Sinhala "split matra" mess.
+- Fix Khmer shaping with broken fonts.
+- Implement Thai "PUA" shaping for old fonts.
+- Do NOT route Kharoshthi script through the Indic shaper.
+- Disable fallback positioning for Indic and Thai shapers.
+- Misc fixes.
+
+
+hb-shape / hb-view changes:
+
+- Add --text-before and --text-after
+- Add --bot / --eot / --preserve-default-ignorables
+- hb-shape --output-format=json
+
+
+New API:
+
+hb_buffer_clear()
+
+hb_buffer_flags_t
+
+HB_BUFFER_FLAGS_DEFAULT
+HB_BUFFER_FLAG_BOT
+HB_BUFFER_FLAG_EOT
+HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES
+
+hb_buffer_set_flags()
+hb_buffer_get_flags()
+
+HB_BUFFER_SERIALIZE_FLAGS
+hb_buffer_serialize_glyphs()
+hb_buffer_deserialize_glyphs()
+hb_buffer_serialize_list_formats()
+
+hb_set_add_range()
+hb_set_del_range()
+hb_set_get_population()
+hb_set_next_range()
+
+hb_face_[sg]et_glyph_count()
+
+hb_segment_properties_t
+HB_SEGMENT_PROPERTIES_DEFAULT
+hb_segment_properties_equal()
+hb_segment_properties_hash()
+
+hb_buffer_set_segment_properties()
+hb_buffer_get_segment_properties()
+
+hb_ot_layout_glyph_class_t
+hb_ot_layout_get_glyph_class()
+hb_ot_layout_get_glyphs_in_class()
+
+hb_shape_plan_t
+hb_shape_plan_create()
+hb_shape_plan_create_cached()
+hb_shape_plan_get_empty()
+hb_shape_plan_reference()
+hb_shape_plan_destroy()
+hb_shape_plan_set_user_data()
+hb_shape_plan_get_user_data()
+hb_shape_plan_execute()
+hb_shape_plan_get_shaper()
+
+hb_ot_shape_plan_collect_lookups()
+
+
+API changes:
+
+- Remove "mask" parameter from hb_buffer_add().
+- Rename hb_ot_layout_would_substitute_lookup() and hb_ot_layout_substitute_closure_lookup().
+- hb-set.h API const correction.
+- Renamed hb_set_min/max() to hb_set_get_min/max().
+- Rename hb_ot_layout_feature_get_lookup_indexes() to hb_ot_layout_feature_get_lookups().
+- Rename hb_buffer_guess_properties() to hb_buffer_guess_segment_properties().
+
+
+
+Overview of changes leading to 0.9.6
+Sunday, November 13, 2012
+====================================
+
+- Don't clear pre-context text if no new context is provided.
+- Fix ReverseChainingSubstLookup, which was totally borked.
+- Adjust output format of hb-shape a bit.
+- Include config.h.in in-tree.  Makes it easier for alternate build systems.
+- Fix hb_buffer_set_length(buffer, 0) invalid memory allocation.
+- Use ICU LayoutEngine's C API instead of C++.  Avoids much headache.
+- Drop glyphs for all of Unicode Default_Ignorable characters.
+- Misc build fixes.
+
+Arabic shaper:
+- Enable 'dlig' and 'mset' features in Arabic shaper.
+- Implement 'Phags-pa shaping, improve Mongolian.
+
+Indic shaper:
+- Decompose Sinhala split matras the way old HarfBuzz / Pango did.
+- Initial support for Consonant Medials.
+- Start adding new-style Myanmar shaping.
+- Make reph and 'pref' logic introspect the font.
+- Route Meetei-Mayek through the Indic shaper.
+- Don't apply 'liga' in Indic shaper.
+- Improve Malayalam pre-base reordering Ra interaction with Chillus.
+
+
+
 Overview of changes leading to 0.9.5
 Sunday, October 14, 2012
 ====================================
diff --git a/TODO b/TODO
index 226b540..76104fa 100644
--- a/TODO
+++ b/TODO
@@ -27,6 +27,8 @@
 API issues to fix before 1.0:
 ============================
 
+- API to accept a list of languages.
+
 - Add default font_funcs / Unicode funcs API and to utils.
 
 - Add init_func to font_funcs.  Adjust ft.
@@ -53,16 +55,10 @@
 
 - Add glib GBoxedType stuff and introspection
 
-- Finish Uniscribe / CoreText face / font get API
-
 - BCP 47 language handling / API (language_matches?)
 
-- Add hb_face_get_glyph_count()?
-
 - Add hb_font_create_linear()?
 
-- Add hb_shape_plan()/hb_shape_planned()
-
 - Add query API for aalt-like features?
 
 - SFNT api? get_num_faces? get_table_tags? (there's something in stash)
@@ -77,7 +73,6 @@
 
 - --output-format should list available formats.
 - Add --width, --height, --auto-size, --align, etc?
-- Add XML and JSON formats to hb-shape
 - --features="init=medi=isol=fina=0"
 
 
diff --git a/config.h.in b/config.h.in
index 923970c..0973c93 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1,9 +1,5 @@
 /* config.h.in.  Generated from configure.ac by autoheader.  */
 
-/* Define to 1 if you have the <ApplicationServices/ApplicationServices.h>
-   header file. */
-#undef HAVE_APPLICATIONSERVICES_APPLICATIONSERVICES_H
-
 /* Define to 1 if you have the `atexit' function. */
 #undef HAVE_ATEXIT
 
@@ -118,12 +114,6 @@
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
-/* Define to 1 if you have the <usp10.h> header file. */
-#undef HAVE_USP10_H
-
-/* Define to 1 if you have the <windows.h> header file. */
-#undef HAVE_WINDOWS_H
-
 /* Define to 1 if you have the `_setmode' function. */
 #undef HAVE__SETMODE
 
diff --git a/configure.ac b/configure.ac
index 93b659b..90ca102 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [0.9.5],
+        [0.9.9],
         [http://bugs.freedesktop.org/enter_bug.cgi?product=harfbuzz],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -208,7 +208,19 @@
 
 dnl ===========================================================================
 
-AC_CHECK_HEADERS(usp10.h windows.h, have_uniscribe=true, have_uniscribe=false)
+AC_MSG_CHECKING([for ScriptShapeOpenType in usp10])
+saved_LIBS=$LIBS
+LIBS="$LIBS -lusp10 -lgdi32"
+AC_LINK_IFELSE([AC_LANG_PROGRAM(
+	[[
+	 #define _WIN32_WINNT 0x0600
+	 #include <windows.h>
+	 #include <usp10.h>
+	 ]],
+	ScriptShapeOpenType)],
+	[have_uniscribe=true; AC_MSG_RESULT(yes)],
+	[have_uniscribe=false;AC_MSG_RESULT(no)])
+LIBS=$saved_LIBS
 if $have_uniscribe; then
 	UNISCRIBE_CFLAGS=
 	UNISCRIBE_LIBS="-lusp10 -lgdi32"
@@ -220,7 +232,7 @@
 
 dnl ===========================================================================
 
-AC_CHECK_HEADERS(ApplicationServices/ApplicationServices.h, have_coretext=true, have_coretext=false)
+AC_CHECK_TYPE(CTFontRef, have_coretext=true, have_coretext=false, [#include <ApplicationServices/ApplicationServices.h>])
 if $have_coretext; then
 	CORETEXT_CFLAGS=
 	CORETEXT_LIBS="-framework ApplicationServices"
@@ -263,3 +275,28 @@
 ])
 
 AC_OUTPUT
+
+AC_MSG_NOTICE([
+
+Build configuration:
+
+Unicode callbacks (you want at least one):
+	Glib:			${have_glib}
+	ICU:			${have_icu}
+	UCDN:			${have_ucdn}
+
+Font callbacks (the more the better):
+	FreeType:		${have_freetype}
+
+Tools used for command-line utilities:
+	Cairo:			${have_cairo}
+
+Additional shapers (the more the better):
+	Graphite2:		${have_graphite}
+
+Test / platform shapers (not normally needed):
+	CoreText:		${have_coretext}
+	ICU Layout Engine:	${have_icu_le}
+	Old HarfBuzz:		${have_hb_old}
+	Uniscribe:		${have_uniscribe}
+])
diff --git a/src/Makefile.am b/src/Makefile.am
index 9f7c7f8..558ce08 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -43,7 +43,6 @@
 	hb-shape.cc \
 	hb-shape-plan-private.hh \
 	hb-shape-plan.cc \
-	hb-shape-plan.h \
 	hb-shaper-list.hh \
 	hb-shaper-impl-private.hh \
 	hb-shaper-private.hh \
@@ -62,6 +61,7 @@
 	hb-font.h \
 	hb-set.h \
 	hb-shape.h \
+	hb-shape-plan.h \
 	hb-unicode.h \
 	hb-version.h \
 	$(NULL)
@@ -81,11 +81,12 @@
 	hb-ot-shape-complex-arabic.cc \
 	hb-ot-shape-complex-arabic-fallback.hh \
 	hb-ot-shape-complex-arabic-table.hh \
+	hb-ot-shape-complex-default.cc \
 	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-thai.cc \
 	hb-ot-shape-complex-private.hh \
 	hb-ot-shape-normalize-private.hh \
 	hb-ot-shape-normalize.cc \
@@ -194,9 +195,13 @@
 harfbuzz_def_dependency = harfbuzz.def
 libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
 else
+if HAVE_ICU
+libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
+else
 # Use a C linker, not C++; Don't link to libstdc++
 libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS)
 endif
+endif
 
 libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS)
 nodist_libharfbuzz_la_SOURCES = $(nodist_HBSOURCES)
@@ -242,21 +247,29 @@
 .PHONY: unicode-tables arabic-table indic-table
 
 EXTRA_DIST += hb-ot-shape-complex-indic-machine.rl
-hb-ot-shape-complex-indic-machine.hh: hb-ot-shape-complex-indic-machine.rl
+$(srcdir)/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 test-would-substitute
+noinst_PROGRAMS = main test test-would-substitute test-size-params
 bin_PROGRAMS =
 
 main_SOURCES = main.cc
 main_CPPFLAGS = $(HBCFLAGS)
 main_LDADD = libharfbuzz.la $(HBLIBS)
 
+test_SOURCES = test.cc
+test_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
+test_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
+
 test_would_substitute_SOURCES = test-would-substitute.cc
 test_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
 test_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
 
+test_size_params_SOURCES = test-size-params.cc
+test_size_params_CPPFLAGS = $(HBCFLAGS)
+test_size_params_LDADD = libharfbuzz.la $(HBLIBS)
+
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
 	check-header-guards.sh \
diff --git a/src/check-exported-symbols.sh b/src/check-exported-symbols.sh
index a7d6f9b..6f0bf7f 100755
--- a/src/check-exported-symbols.sh
+++ b/src/check-exported-symbols.sh
@@ -24,7 +24,7 @@
 		echo "Checking that $so has the same symbol list as $def"
 		{
 			echo EXPORTS
-			nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' T _fini\>\| T _init\>' | cut -d' ' -f3
+			nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' T _fini\>\| T _init\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>' | cut -d' ' -f3
 			stat=1
 			# cheat: copy the last line from the def file!
 			tail -n1 "$def"
diff --git a/src/check-internal-symbols.sh b/src/check-internal-symbols.sh
index f48d144..a8fdc53 100755
--- a/src/check-internal-symbols.sh
+++ b/src/check-internal-symbols.sh
@@ -19,7 +19,7 @@
 	so=`echo .libs/libharfbuzz$suffix`
 	if test -f "$so"; then
 		echo "Checking that we are not exposing internal symbols"
-		if nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' T _fini\>\| T _init\>\| T hb_'; then
+		if nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' T _fini\>\| T _init\>\| T hb_\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>'; then
 			echo "Ouch, internal symbols exposed"
 			stat=1
 		fi
diff --git a/src/hb-atomic-private.hh b/src/hb-atomic-private.hh
index 5861a71..111d7a0 100644
--- a/src/hb-atomic-private.hh
+++ b/src/hb-atomic-private.hh
@@ -69,12 +69,25 @@
 #elif !defined(HB_NO_MT) && defined(__APPLE__)
 
 #include <libkern/OSAtomic.h>
+#ifdef __MAC_OS_X_MIN_REQUIRED
+#include <AvailabilityMacros.h>
+#elif defined(__IPHONE_OS_MIN_REQUIRED)
+#include <Availability.h>
+#endif
 
 typedef int32_t hb_atomic_int_t;
 #define hb_atomic_int_add(AI, V)	(OSAtomicAdd32Barrier ((V), &(AI)) - (V))
 
 #define hb_atomic_ptr_get(P)		(OSMemoryBarrier (), (void *) *(P))
+#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100)
 #define hb_atomic_ptr_cmpexch(P,O,N)	OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P))
+#else
+#if __ppc64__ || __x86_64__
+#define hb_atomic_ptr_cmpexch(P,O,N)    OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P))
+#else
+#define hb_atomic_ptr_cmpexch(P,O,N)    OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P))
+#endif
+#endif
 
 
 #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index c1acffd..13cf4bb 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -36,48 +36,11 @@
 #include "hb-unicode-private.hh"
 
 
-
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20);
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t));
 
 
 /*
- * hb_segment_properties_t
- */
-
-typedef struct hb_segment_properties_t {
-    hb_direction_t      direction;
-    hb_script_t         script;
-    hb_language_t       language;
-    ASSERT_POD ();
-} hb_segment_properties_t;
-
-#define _HB_BUFFER_PROPS_DEFAULT { HB_DIRECTION_INVALID, HB_SCRIPT_INVALID, HB_LANGUAGE_INVALID }
-
-static inline hb_bool_t
-hb_segment_properties_equal (const hb_segment_properties_t *a,
-			     const hb_segment_properties_t *b)
-{
-  return a->direction == b->direction &&
-	 a->script    == b->script    &&
-	 a->language  == b->language;
-}
-
-
-#if 0
-static inline unsigned int
-hb_segment_properties_hash (const hb_segment_properties_t *p)
-{
-  /* TODO improve */
-  return (unsigned int) p->direction +
-	 (unsigned int) p->script +
-	 (intptr_t) (p->language);
-}
-#endif
-
-
-
-/*
  * hb_buffer_t
  */
 
@@ -89,6 +52,7 @@
 
   hb_unicode_funcs_t *unicode; /* Unicode functions */
   hb_segment_properties_t props; /* Script, language, direction */
+  hb_buffer_flags_t flags; /* BOT / EOT / etc. */
 
   /* Buffer contents */
 
@@ -133,6 +97,7 @@
   /* Methods */
 
   HB_INTERNAL void reset (void);
+  HB_INTERNAL void clear (void);
 
   inline unsigned int backtrack_len (void) const
   { return have_output? out_len : idx; }
@@ -144,13 +109,12 @@
   HB_INTERNAL void deallocate_var_all (void);
 
   HB_INTERNAL void add (hb_codepoint_t  codepoint,
-			hb_mask_t       mask,
 			unsigned int    cluster);
 
   HB_INTERNAL void reverse_range (unsigned int start, unsigned int end);
   HB_INTERNAL void reverse (void);
   HB_INTERNAL void reverse_clusters (void);
-  HB_INTERNAL void guess_properties (void);
+  HB_INTERNAL void guess_segment_properties (void);
 
   HB_INTERNAL void swap_buffers (void);
   HB_INTERNAL void remove_output (void);
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index 1eb513c..0a5600b 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -35,6 +35,29 @@
 #define HB_DEBUG_BUFFER (HB_DEBUG+0)
 #endif
 
+
+hb_bool_t
+hb_segment_properties_equal (const hb_segment_properties_t *a,
+			     const hb_segment_properties_t *b)
+{
+  return a->direction == b->direction &&
+	 a->script    == b->script    &&
+	 a->language  == b->language  &&
+	 a->reserved1 == b->reserved1 &&
+	 a->reserved2 == b->reserved2;
+
+}
+
+unsigned int
+hb_segment_properties_hash (const hb_segment_properties_t *p)
+{
+  return (unsigned int) p->direction ^
+	 (unsigned int) p->script ^
+	 (intptr_t) (p->language);
+}
+
+
+
 /* Here is how the buffer works internally:
  *
  * There are two info pointers: info and out_info.  They always have
@@ -142,8 +165,18 @@
   hb_unicode_funcs_destroy (unicode);
   unicode = hb_unicode_funcs_get_default ();
 
-  hb_segment_properties_t default_props = _HB_BUFFER_PROPS_DEFAULT;
+  clear ();
+}
+
+void
+hb_buffer_t::clear (void)
+{
+  if (unlikely (hb_object_is_inert (this)))
+    return;
+
+  hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT;
   props = default_props;
+  flags = HB_BUFFER_FLAGS_DEFAULT;
 
   content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
   in_error = false;
@@ -165,7 +198,6 @@
 
 void
 hb_buffer_t::add (hb_codepoint_t  codepoint,
-		  hb_mask_t       mask,
 		  unsigned int    cluster)
 {
   hb_glyph_info_t *glyph;
@@ -176,7 +208,7 @@
 
   memset (glyph, 0, sizeof (*glyph));
   glyph->codepoint = codepoint;
-  glyph->mask = mask;
+  glyph->mask = 1;
   glyph->cluster = cluster;
 
   len++;
@@ -459,10 +491,10 @@
 }
 
 void
-hb_buffer_t::guess_properties (void)
+hb_buffer_t::guess_segment_properties (void)
 {
-  if (unlikely (!len)) return;
-  assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
+  assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
+	  (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
 
   /* If script is set to INVALID, guess from buffer contents */
   if (props.script == HB_SCRIPT_INVALID) {
@@ -561,7 +593,7 @@
 /* Public API */
 
 hb_buffer_t *
-hb_buffer_create ()
+hb_buffer_create (void)
 {
   hb_buffer_t *buffer;
 
@@ -580,7 +612,8 @@
     HB_OBJECT_HEADER_STATIC,
 
     const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
-    _HB_BUFFER_PROPS_DEFAULT,
+    HB_SEGMENT_PROPERTIES_DEFAULT,
+    HB_BUFFER_FLAGS_DEFAULT,
 
     HB_BUFFER_CONTENT_TYPE_INVALID,
     true, /* in_error */
@@ -715,6 +748,40 @@
   return buffer->props.language;
 }
 
+void
+hb_buffer_set_segment_properties (hb_buffer_t *buffer,
+				  const hb_segment_properties_t *props)
+{
+  if (unlikely (hb_object_is_inert (buffer)))
+    return;
+
+  buffer->props = *props;
+}
+
+void
+hb_buffer_get_segment_properties (hb_buffer_t *buffer,
+				  hb_segment_properties_t *props)
+{
+  *props = buffer->props;
+}
+
+
+void
+hb_buffer_set_flags (hb_buffer_t       *buffer,
+		     hb_buffer_flags_t  flags)
+{
+  if (unlikely (hb_object_is_inert (buffer)))
+    return;
+
+  buffer->flags = flags;
+}
+
+hb_buffer_flags_t
+hb_buffer_get_flags (hb_buffer_t *buffer)
+{
+  return buffer->flags;
+}
+
 
 void
 hb_buffer_reset (hb_buffer_t *buffer)
@@ -722,6 +789,12 @@
   buffer->reset ();
 }
 
+void
+hb_buffer_clear (hb_buffer_t *buffer)
+{
+  buffer->clear ();
+}
+
 hb_bool_t
 hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size)
 {
@@ -737,10 +810,9 @@
 void
 hb_buffer_add (hb_buffer_t    *buffer,
 	       hb_codepoint_t  codepoint,
-	       hb_mask_t       mask,
 	       unsigned int    cluster)
 {
-  buffer->add (codepoint, mask, cluster);
+  buffer->add (codepoint, cluster);
   buffer->clear_context (1);
 }
 
@@ -814,9 +886,9 @@
 }
 
 void
-hb_buffer_guess_properties (hb_buffer_t *buffer)
+hb_buffer_guess_segment_properties (hb_buffer_t *buffer)
 {
-  buffer->guess_properties ();
+  buffer->guess_segment_properties ();
 }
 
 template <typename T>
@@ -841,7 +913,14 @@
 
   buffer->ensure (buffer->len + item_length * sizeof (T) / 4);
 
-  if (!buffer->len)
+  /* If buffer is empty and pre-context provided, install it.
+   * This check is written this way, to make sure people can
+   * provide pre-context in one add_utf() call, then provide
+   * text in a follow-up call.  See:
+   *
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13
+   */
+  if (!buffer->len && item_offset > 0)
   {
     /* Add pre-context */
     buffer->clear_context (0);
@@ -862,7 +941,7 @@
     hb_codepoint_t u;
     const T *old_next = next;
     next = hb_utf_next (next, end, &u);
-    buffer->add (u, 1,  old_next - (const T *) text);
+    buffer->add (u, old_next - (const T *) text);
   }
 
   /* Add post-context */
@@ -985,3 +1064,231 @@
     }
   normalize_glyphs_cluster (buffer, start, end, backward);
 }
+
+
+/*
+ * Serialize
+ */
+
+static const char *serialize_formats[] = {
+  "TEXT",
+  "JSON",
+  NULL
+};
+
+const char **
+hb_buffer_serialize_list_formats (void)
+{
+  return serialize_formats;
+}
+
+hb_buffer_serialize_format_t
+hb_buffer_serialize_format_from_string (const char *str, int len)
+{
+  /* Upper-case it. */
+  return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020);
+}
+
+const char *
+hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format)
+{
+  switch (format)
+  {
+    case HB_BUFFER_SERIALIZE_FORMAT_TEXT:	return serialize_formats[0];
+    case HB_BUFFER_SERIALIZE_FORMAT_JSON:	return serialize_formats[1];
+    default:
+    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:	return NULL;
+  }
+}
+
+static unsigned int
+_hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer,
+				  unsigned int start,
+				  unsigned int end,
+				  char *buf,
+				  unsigned int buf_size,
+				  unsigned int *buf_consumed,
+				  hb_font_t *font,
+				  hb_buffer_serialize_flags_t flags)
+{
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
+
+  *buf_consumed = 0;
+  for (unsigned int i = start; i < end; i++)
+  {
+    char b[1024];
+    char *p = b;
+
+    /* In the following code, we know b is large enough that no overflow can happen. */
+
+#define APPEND(s) HB_STMT_START { strcpy (p, s); p += strlen (s); } HB_STMT_END
+
+    if (i)
+      *p++ = ',';
+
+    *p++ = '{';
+
+    APPEND ("\"g\":");
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES))
+    {
+      char g[128];
+      hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g));
+      *p++ = '"';
+      for (char *q = g; *q; q++) {
+        if (*q == '"')
+	  *p++ = '\\';
+	*p++ = *q;
+      }
+      *p++ = '"';
+    }
+    else
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint);
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster);
+    }
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
+    {
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
+		     pos[i].x_offset, pos[i].y_offset);
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
+		     pos[i].x_advance, pos[i].y_advance);
+    }
+
+    *p++ = '}';
+
+    if (buf_size > (p - b))
+    {
+      unsigned int l = p - b;
+      memcpy (buf, b, l);
+      buf += l;
+      buf_size -= l;
+      *buf_consumed += l;
+      *buf = '\0';
+    } else
+      return i - start;
+  }
+
+  return end - start;
+}
+
+static unsigned int
+_hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
+				  unsigned int start,
+				  unsigned int end,
+				  char *buf,
+				  unsigned int buf_size,
+				  unsigned int *buf_consumed,
+				  hb_font_t *font,
+				  hb_buffer_serialize_flags_t flags)
+{
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
+  hb_direction_t direction = hb_buffer_get_direction (buffer);
+
+  *buf_consumed = 0;
+  for (unsigned int i = start; i < end; i++)
+  {
+    char b[1024];
+    char *p = b;
+
+    /* In the following code, we know b is large enough that no overflow can happen. */
+
+    if (i)
+      *p++ = '|';
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES))
+    {
+      hb_font_glyph_to_string (font, info[i].codepoint, p, 128);
+      p += strlen (p);
+    }
+    else
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint);
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster);
+    }
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
+    {
+      if (pos[i].x_offset || pos[i].y_offset)
+	p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset);
+
+      *p++ = '+';
+      if (HB_DIRECTION_IS_HORIZONTAL (direction) || pos[i].x_advance)
+	p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance);
+      if (HB_DIRECTION_IS_VERTICAL (direction) || pos->y_advance)
+	p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance);
+    }
+
+    if (buf_size > (p - b))
+    {
+      unsigned int l = p - b;
+      memcpy (buf, b, l);
+      buf += l;
+      buf_size -= l;
+      *buf_consumed += l;
+      *buf = '\0';
+    } else
+      return i - start;
+  }
+
+  return end - start;
+}
+
+/* Returns number of items, starting at start, that were serialized. */
+unsigned int
+hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
+			    unsigned int start,
+			    unsigned int end,
+			    char *buf,
+			    unsigned int buf_size,
+			    unsigned int *buf_consumed,
+			    hb_font_t *font, /* May be NULL */
+			    hb_buffer_serialize_format_t format,
+			    hb_buffer_serialize_flags_t flags)
+{
+  assert (start <= end && end <= buffer->len);
+
+  *buf_consumed = 0;
+
+  assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) ||
+	  buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
+
+  if (unlikely (start == end))
+    return 0;
+
+  if (!font)
+    font = hb_font_get_empty ();
+
+  switch (format)
+  {
+    case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
+      return _hb_buffer_serialize_glyphs_text (buffer, start, end,
+					       buf, buf_size, buf_consumed,
+					       font, flags);
+
+    case HB_BUFFER_SERIALIZE_FORMAT_JSON:
+      return _hb_buffer_serialize_glyphs_json (buffer, start, end,
+					       buf, buf_size, buf_consumed,
+					       font, flags);
+
+    default:
+    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
+      return 0;
+
+  }
+}
+
+hb_bool_t
+hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
+			      const char *buf,
+			      unsigned int buf_len,
+			      unsigned int *buf_consumed,
+			      hb_font_t *font, /* May be NULL */
+			      hb_buffer_serialize_format_t format)
+{
+  return false;
+}
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index dc63d1b..48ec4a5 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -36,12 +36,11 @@
 
 #include "hb-common.h"
 #include "hb-unicode.h"
+#include "hb-font.h"
 
 HB_BEGIN_DECLS
 
 
-typedef struct hb_buffer_t hb_buffer_t;
-
 typedef struct hb_glyph_info_t {
   hb_codepoint_t codepoint;
   hb_mask_t      mask;
@@ -62,12 +61,36 @@
   hb_var_int_t   var;
 } hb_glyph_position_t;
 
-typedef enum {
-  HB_BUFFER_CONTENT_TYPE_INVALID = 0,
-  HB_BUFFER_CONTENT_TYPE_UNICODE,
-  HB_BUFFER_CONTENT_TYPE_GLYPHS
-} hb_buffer_content_type_t;
 
+typedef struct hb_segment_properties_t {
+  hb_direction_t  direction;
+  hb_script_t     script;
+  hb_language_t   language;
+  /*< private >*/
+  void           *reserved1;
+  void           *reserved2;
+} hb_segment_properties_t;
+
+#define HB_SEGMENT_PROPERTIES_DEFAULT {HB_DIRECTION_INVALID, \
+				       HB_SCRIPT_INVALID, \
+				       HB_LANGUAGE_INVALID, \
+				       NULL, \
+				       NULL}
+
+hb_bool_t
+hb_segment_properties_equal (const hb_segment_properties_t *a,
+			     const hb_segment_properties_t *b);
+
+unsigned int
+hb_segment_properties_hash (const hb_segment_properties_t *p);
+
+
+
+/*
+ * hb_buffer_t
+ */
+
+typedef struct hb_buffer_t hb_buffer_t;
 
 hb_buffer_t *
 hb_buffer_create (void);
@@ -93,6 +116,12 @@
 			 hb_user_data_key_t *key);
 
 
+typedef enum {
+  HB_BUFFER_CONTENT_TYPE_INVALID = 0,
+  HB_BUFFER_CONTENT_TYPE_UNICODE,
+  HB_BUFFER_CONTENT_TYPE_GLYPHS
+} hb_buffer_content_type_t;
+
 void
 hb_buffer_set_content_type (hb_buffer_t              *buffer,
 			    hb_buffer_content_type_t  content_type);
@@ -126,15 +155,46 @@
 hb_buffer_set_language (hb_buffer_t   *buffer,
 			hb_language_t  language);
 
+
 hb_language_t
 hb_buffer_get_language (hb_buffer_t *buffer);
 
+void
+hb_buffer_set_segment_properties (hb_buffer_t *buffer,
+				  const hb_segment_properties_t *props);
+
+void
+hb_buffer_get_segment_properties (hb_buffer_t *buffer,
+				  hb_segment_properties_t *props);
+
+void
+hb_buffer_guess_segment_properties (hb_buffer_t *buffer);
+
+
+typedef enum {
+  HB_BUFFER_FLAGS_DEFAULT			= 0x00000000,
+  HB_BUFFER_FLAG_BOT				= 0x00000001, /* Beginning-of-text */
+  HB_BUFFER_FLAG_EOT				= 0x00000002, /* End-of-text */
+  HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES	= 0x00000004
+} hb_buffer_flags_t;
+
+void
+hb_buffer_set_flags (hb_buffer_t       *buffer,
+		     hb_buffer_flags_t  flags);
+
+hb_buffer_flags_t
+hb_buffer_get_flags (hb_buffer_t *buffer);
+
 
 /* Resets the buffer.  Afterwards it's as if it was just created,
  * except that it has a larger buffer allocated perhaps... */
 void
 hb_buffer_reset (hb_buffer_t *buffer);
 
+/* Like reset, but does NOT clear unicode_funcs. */
+void
+hb_buffer_clear (hb_buffer_t *buffer);
+
 /* Returns false if allocation failed */
 hb_bool_t
 hb_buffer_pre_allocate (hb_buffer_t  *buffer,
@@ -151,16 +211,12 @@
 void
 hb_buffer_reverse_clusters (hb_buffer_t *buffer);
 
-void
-hb_buffer_guess_properties (hb_buffer_t *buffer);
-
 
 /* Filling the buffer in */
 
 void
 hb_buffer_add (hb_buffer_t    *buffer,
 	       hb_codepoint_t  codepoint,
-	       hb_mask_t       mask,
 	       unsigned int    cluster);
 
 void
@@ -213,11 +269,53 @@
 void
 hb_buffer_normalize_glyphs (hb_buffer_t *buffer);
 
+
 /*
- * NOT IMPLEMENTED
- void
- hb_buffer_normalize_characters (hb_buffer_t *buffer);
-*/
+ * Serialize
+ */
+
+typedef enum {
+  HB_BUFFER_SERIALIZE_FLAGS_DEFAULT		= 0x00000000,
+  HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS		= 0x00000001,
+  HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS		= 0x00000002,
+  HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES	= 0x00000004
+} hb_buffer_serialize_flags_t;
+
+typedef enum {
+  HB_BUFFER_SERIALIZE_FORMAT_TEXT	= HB_TAG('T','E','X','T'),
+  HB_BUFFER_SERIALIZE_FORMAT_JSON	= HB_TAG('J','S','O','N'),
+  HB_BUFFER_SERIALIZE_FORMAT_INVALID	= HB_TAG_NONE
+} hb_buffer_serialize_format_t;
+
+/* len=-1 means str is NUL-terminated. */
+hb_buffer_serialize_format_t
+hb_buffer_serialize_format_from_string (const char *str, int len);
+
+const char *
+hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format);
+
+const char **
+hb_buffer_serialize_list_formats (void);
+
+/* Returns number of items, starting at start, that were serialized. */
+unsigned int
+hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
+			    unsigned int start,
+			    unsigned int end,
+			    char *buf,
+			    unsigned int buf_size,
+			    unsigned int *buf_consumed,
+			    hb_font_t *font, /* May be NULL */
+			    hb_buffer_serialize_format_t format,
+			    hb_buffer_serialize_flags_t flags);
+
+hb_bool_t
+hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
+			      const char *buf,
+			      unsigned int buf_len,
+			      unsigned int *buf_consumed,
+			      hb_font_t *font, /* May be NULL */
+			      hb_buffer_serialize_format_t format);
 
 
 HB_END_DECLS
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 33a514d..9422555 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -363,8 +363,7 @@
 hb_user_data_array_t::set (hb_user_data_key_t *key,
 			   void *              data,
 			   hb_destroy_func_t   destroy,
-			   hb_bool_t           replace,
-			   hb_mutex_t         &lock)
+			   hb_bool_t           replace)
 {
   if (!key)
     return false;
@@ -382,20 +381,13 @@
 }
 
 void *
-hb_user_data_array_t::get (hb_user_data_key_t *key,
-			   hb_mutex_t         &lock)
+hb_user_data_array_t::get (hb_user_data_key_t *key)
 {
   hb_user_data_item_t item = {NULL };
 
   return items.find (key, &item, lock) ? item.data : NULL;
 }
 
-void
-hb_user_data_array_t::finish (hb_mutex_t &lock)
-{
-  items.finish (lock);
-}
-
 
 /* hb_version */
 
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc
index 4152a39..8f94795 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -29,10 +29,6 @@
 #define HB_SHAPER coretext
 #include "hb-shaper-impl-private.hh"
 
-#define GlyphID GlyphID_mac
-#include <ApplicationServices/ApplicationServices.h>
-#undef GlyphID
-
 #include "hb-coretext.h"
 
 
@@ -95,6 +91,14 @@
   free (data);
 }
 
+CGFontRef
+hb_coretext_face_get_cg_font (hb_face_t *face)
+{
+  if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL;
+  hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
+  return face_data->cg_font;
+}
+
 
 /*
  * shaper font data
@@ -153,19 +157,19 @@
 {
 }
 
+CTFontRef
+hb_coretext_font_get_ct_font (hb_font_t *font)
+{
+  if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return NULL;
+  hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
+  return font_data->ct_font;
+}
+
 
 /*
  * shaper
  */
 
-CTFontRef
-hb_coretext_font_get_ct_font (hb_font_t *font)
-{
-  if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return 0;
-  hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
-  return font_data->ct_font;
-}
-
 hb_bool_t
 _hb_coretext_shape (hb_shape_plan_t    *shape_plan,
 		    hb_font_t          *font,
@@ -212,26 +216,22 @@
   CFDictionaryRef attrs = CFDictionaryCreate (kCFAllocatorDefault,
                                               (const void**) &kCTFontAttributeName,
                                               (const void**) &font_data->ct_font,
-                                              1, // count of attributes
+                                              1, /* count of attributes */
                                               &kCFTypeDictionaryKeyCallBacks,
                                               &kCFTypeDictionaryValueCallBacks);
 
-  // TODO: support features
+  /* TODO: support features */
 
-  // Now we can create an attributed string
   CFAttributedStringRef attr_string = CFAttributedStringCreate (kCFAllocatorDefault, string_ref, attrs);
   CFRelease (string_ref);
   CFRelease (attrs);
 
-  // Create the CoreText line from our string, then we're done with it
   CTLineRef line = CTLineCreateWithAttributedString (attr_string);
   CFRelease (attr_string);
 
-  // and finally retrieve the glyph data and store into the gfxTextRun
   CFArrayRef glyph_runs = CTLineGetGlyphRuns (line);
   unsigned int num_runs = CFArrayGetCount (glyph_runs);
 
-  // Iterate through the glyph runs.
   bool success = true;
   buffer->len = 0;
 
@@ -246,11 +246,9 @@
 
     buffer->ensure (buffer->len + num_glyphs);
 
-    // retrieve the laid-out glyph data from the CTRun
-
-    // Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds,
-    // and so copying data to our own buffer with CTRunGetGlyphs will be
-    // extremely rare.
+    /* Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds,
+     * and so copying data to our own buffer with CTRunGetGlyphs will be
+     * extremely rare. */
 
     unsigned int scratch_size;
     char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
@@ -294,7 +292,7 @@
       info->codepoint = glyphs[j];
       info->cluster = string_indices[j];
 
-      // currently, we do all x-positioning by setting the advance, we never use x-offset
+      /* Currently, we do all x-positioning by setting the advance, we never use x-offset. */
       info->mask = advance;
       info->var1.u32 = 0;
       info->var2.u32 = positions[j].y;
@@ -316,12 +314,13 @@
     pos->y_offset = info->var2.u32;
   }
 
-  // Fix up clusters so that we never return out-of-order indices;
-  // if core text has reordered glyphs, we'll merge them to the
-  // beginning of the reordered cluster.
-  // This does *not* mean we'll form the same clusters as Uniscribe
-  // or the native OT backend, only that the cluster indices will be
-  // non-decreasing in the output buffer.
+  /* Fix up clusters so that we never return out-of-order indices;
+   * if core text has reordered glyphs, we'll merge them to the
+   * beginning of the reordered cluster.
+   *
+   * This does *not* mean we'll form the same clusters as Uniscribe
+   * or the native OT backend, only that the cluster indices will be
+   * monotonic in the output buffer. */
   if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
     unsigned int prev_cluster = 0;
     for (unsigned int i = 0; i < count; i++) {
@@ -337,7 +336,6 @@
       prev_cluster = curr_cluster;
     }
   } else {
-    // For RTL runs, we make them non-increasing instead.
     unsigned int prev_cluster = (unsigned int)-1;
     for (unsigned int i = 0; i < count; i++) {
       unsigned int curr_cluster = buffer->info[i].cluster;
diff --git a/src/hb-coretext.h b/src/hb-coretext.h
index 0b34203..c4954fa 100644
--- a/src/hb-coretext.h
+++ b/src/hb-coretext.h
@@ -34,6 +34,9 @@
 HB_BEGIN_DECLS
 
 
+CGFontRef
+hb_coretext_face_get_cg_font (hb_face_t *face);
+
 CTFontRef
 hb_coretext_font_get_ct_font (hb_font_t *font);
 
diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc
index 3f9024f..bdc8a80 100644
--- a/src/hb-fallback-shape.cc
+++ b/src/hb-fallback-shape.cc
@@ -35,13 +35,13 @@
 struct hb_fallback_shaper_face_data_t {};
 
 hb_fallback_shaper_face_data_t *
-_hb_fallback_shaper_face_data_create (hb_face_t *face)
+_hb_fallback_shaper_face_data_create (hb_face_t *face HB_UNUSED)
 {
   return (hb_fallback_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data)
+_hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data HB_UNUSED)
 {
 }
 
@@ -53,13 +53,13 @@
 struct hb_fallback_shaper_font_data_t {};
 
 hb_fallback_shaper_font_data_t *
-_hb_fallback_shaper_font_data_create (hb_font_t *font)
+_hb_fallback_shaper_font_data_create (hb_font_t *font HB_UNUSED)
 {
   return (hb_fallback_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_font_data_destroy (hb_fallback_shaper_font_data_t *data)
+_hb_fallback_shaper_font_data_destroy (hb_fallback_shaper_font_data_t *data HB_UNUSED)
 {
 }
 
@@ -89,7 +89,7 @@
  */
 
 hb_bool_t
-_hb_fallback_shape (hb_shape_plan_t    *shape_plan,
+_hb_fallback_shape (hb_shape_plan_t    *shape_plan HB_UNUSED,
 		    hb_font_t          *font,
 		    hb_buffer_t        *buffer,
 		    const hb_feature_t *features HB_UNUSED,
@@ -98,7 +98,7 @@
   hb_codepoint_t space;
   font->get_glyph (' ', 0, &space);
 
-  buffer->guess_properties ();
+  buffer->guess_segment_properties ();
   buffer->clear_positions ();
 
   unsigned int count = buffer->len;
diff --git a/src/hb-font-private.hh b/src/hb-font-private.hh
index b6dafbf..48fbb0e 100644
--- a/src/hb-font-private.hh
+++ b/src/hb-font-private.hh
@@ -100,6 +100,7 @@
 
   unsigned int index;
   mutable unsigned int upem;
+  mutable unsigned int num_glyphs;
 
   struct hb_shaper_data_t shaper_data;
 
@@ -130,8 +131,16 @@
     return upem;
   }
 
+  inline unsigned int get_num_glyphs (void) const
+  {
+    if (unlikely (num_glyphs == (unsigned int) -1))
+      load_num_glyphs ();
+    return num_glyphs;
+  }
+
   private:
   HB_INTERNAL void load_upem (void) const;
+  HB_INTERNAL void load_num_glyphs (void) const;
 };
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
diff --git a/src/hb-font.cc b/src/hb-font.cc
index 0627032..b59fdeb 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -34,6 +34,7 @@
 #include "hb-blob.h"
 #include "hb-open-file-private.hh"
 #include "hb-ot-head-table.hh"
+#include "hb-ot-maxp-table.hh"
 
 #include "hb-cache-private.hh"
 
@@ -520,6 +521,7 @@
 
   0,    /* index */
   1000, /* upem */
+  0,    /* num_glyphs */
 
   {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
@@ -549,6 +551,7 @@
   face->destroy = destroy;
 
   face->upem = 0;
+  face->num_glyphs = (unsigned int) -1;
 
   return face;
 }
@@ -736,7 +739,6 @@
   return face->get_upem ();
 }
 
-
 void
 hb_face_t::load_upem (void) const
 {
@@ -746,6 +748,31 @@
   hb_blob_destroy (head_blob);
 }
 
+void
+hb_face_set_glyph_count (hb_face_t    *face,
+			 unsigned int  glyph_count)
+{
+  if (hb_object_is_inert (face))
+    return;
+
+  face->num_glyphs = glyph_count;
+}
+
+unsigned int
+hb_face_get_glyph_count (hb_face_t *face)
+{
+  return face->get_num_glyphs ();
+}
+
+void
+hb_face_t::load_num_glyphs (void) const
+{
+  hb_blob_t *maxp_blob = OT::Sanitizer<OT::maxp>::sanitize (reference_table (HB_OT_TAG_maxp));
+  const OT::maxp *maxp_table = OT::Sanitizer<OT::maxp>::lock_instance (maxp_blob);
+  num_glyphs = maxp_table->get_num_glyphs ();
+  hb_blob_destroy (maxp_blob);
+}
+
 
 /*
  * hb_font_t
diff --git a/src/hb-font.h b/src/hb-font.h
index d796856..88d4895 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -105,6 +105,13 @@
 unsigned int
 hb_face_get_upem (hb_face_t *face);
 
+void
+hb_face_set_glyph_count (hb_face_t    *face,
+			 unsigned int  glyph_count);
+
+unsigned int
+hb_face_get_glyph_count (hb_face_t *face);
+
 
 /*
  * hb_font_funcs_t
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index 9ac556e..6198185 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -242,7 +242,7 @@
   FT_Face ft_face = (FT_Face) font_data;
 
   hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size);
-  if (!ret)
+  if (!ret || (size && !*name))
     snprintf (name, size, "gid%u", glyph);
 
   return ret;
diff --git a/src/hb-glib.cc b/src/hb-glib.cc
index 0462758..676e660 100644
--- a/src/hb-glib.cc
+++ b/src/hb-glib.cc
@@ -334,7 +334,7 @@
 }
 
 static unsigned int
-hb_glib_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
+hb_glib_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 					 hb_codepoint_t      u,
 					 hb_codepoint_t     *decomposed,
 					 void               *user_data HB_UNUSED)
diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index 6c890d4..16ef9a4 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -113,7 +113,7 @@
     hb_blob_destroy (silf_blob);
 
   data->face = face;
-  data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_default);
+  data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll);
 
   if (unlikely (!data->grface)) {
     free (data);
@@ -141,6 +141,13 @@
   free (data);
 }
 
+gr_face *
+hb_graphite2_face_get_gr_face (hb_face_t *face)
+{
+  if (unlikely (!hb_graphite2_shaper_face_data_ensure (face))) return NULL;
+  return HB_SHAPER_DATA_GET (face)->grface;
+}
+
 
 /*
  * shaper font data
@@ -168,6 +175,13 @@
   gr_font_destroy (data);
 }
 
+gr_font *
+hb_graphite2_font_get_gr_font (hb_font_t *font)
+{
+  if (unlikely (!hb_graphite2_shaper_font_data_ensure (font))) return NULL;
+  return HB_SHAPER_DATA_GET (font);
+}
+
 
 /*
  * shaper shape_plan data
@@ -311,10 +325,18 @@
   }
   ci++;
 
-  buffer->clear_output ();
+  //buffer->clear_output ();
   for (unsigned int i = 0; i < ci; ++i)
-    buffer->replace_glyphs (clusters[i].num_chars, clusters[i].num_glyphs, gids + clusters[i].base_glyph);
-  buffer->swap_buffers ();
+  {
+    for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j)
+    {
+      hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j];
+      info->codepoint = gids[clusters[i].base_glyph + j];
+      info->cluster = gr_cinfo_base(gr_seg_cinfo(seg, clusters[i].base_char));
+    }
+  }
+  buffer->len = glyph_count;
+  //buffer->swap_buffers ();
 
   if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
     curradvx = gr_seg_advance_X(seg);
diff --git a/src/hb-graphite2.h b/src/hb-graphite2.h
index 8122495..bea68f9 100644
--- a/src/hb-graphite2.h
+++ b/src/hb-graphite2.h
@@ -33,7 +33,13 @@
 
 #define HB_GRAPHITE2_TAG_SILF HB_TAG('S','i','l','f')
 
-/* TODO add gr_font/face etc getters and other glue API */
+
+gr_face *
+hb_graphite2_face_get_gr_face (hb_face_t *face);
+
+gr_font *
+hb_graphite2_font_get_gr_font (hb_font_t *font);
+
 
 HB_END_DECLS
 
diff --git a/src/hb-icu-le.cc b/src/hb-icu-le.cc
index d73752d..c05d330 100644
--- a/src/hb-icu-le.cc
+++ b/src/hb-icu-le.cc
@@ -43,13 +43,13 @@
 struct hb_icu_le_shaper_face_data_t {};
 
 hb_icu_le_shaper_face_data_t *
-_hb_icu_le_shaper_face_data_create (hb_face_t *face)
+_hb_icu_le_shaper_face_data_create (hb_face_t *face HB_UNUSED)
 {
   return (hb_icu_le_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_icu_le_shaper_face_data_destroy (hb_icu_le_shaper_face_data_t *data)
+_hb_icu_le_shaper_face_data_destroy (hb_icu_le_shaper_face_data_t *data HB_UNUSED)
 {
 }
 
@@ -88,7 +88,7 @@
 struct hb_icu_le_shaper_shape_plan_data_t {};
 
 hb_icu_le_shaper_shape_plan_data_t *
-_hb_icu_le_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
+_hb_icu_le_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					  const hb_feature_t *user_features,
 					  unsigned int        num_user_features)
 {
@@ -115,7 +115,7 @@
   LEFontInstance *font_instance = HB_SHAPER_DATA_GET (font);
   le_int32 script_code = hb_icu_script_from_script (shape_plan->props.script);
   le_int32 language_code = -1 /* TODO */;
-  le_int32 typography_flags = 3; // essential for ligatures and kerning
+  le_int32 typography_flags = 3; /* Needed for ligatures and kerning */
   LEErrorCode status = LE_NO_ERROR;
   le_engine *le = le_create ((const le_font *) font_instance,
 			     script_code,
diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh
index c48f242..8a9ae34 100644
--- a/src/hb-object-private.hh
+++ b/src/hb-object-private.hh
@@ -65,7 +65,7 @@
 
 /* user_data */
 
-#define HB_USER_DATA_ARRAY_INIT {HB_LOCKABLE_SET_INIT}
+#define HB_USER_DATA_ARRAY_INIT {HB_MUTEX_INIT, HB_LOCKABLE_SET_INIT}
 struct hb_user_data_array_t
 {
   /* TODO Add tracing. */
@@ -81,20 +81,19 @@
     void finish (void) { if (destroy) destroy (data); }
   };
 
+  hb_mutex_t lock;
   hb_lockable_set_t<hb_user_data_item_t, hb_mutex_t> items;
 
-  inline void init (void) { items.init (); }
+  inline void init (void) { lock.init (); items.init (); }
 
   HB_INTERNAL bool set (hb_user_data_key_t *key,
 			void *              data,
 			hb_destroy_func_t   destroy,
-			hb_bool_t           replace,
-			hb_mutex_t         &lock);
+			hb_bool_t           replace);
 
-  HB_INTERNAL void *get (hb_user_data_key_t *key,
-			hb_mutex_t          &lock);
+  HB_INTERNAL void *get (hb_user_data_key_t *key);
 
-  HB_INTERNAL void finish (hb_mutex_t &lock);
+  inline void finish (void) { items.finish (lock); lock.finish (); }
 };
 
 
@@ -103,10 +102,9 @@
 struct hb_object_header_t
 {
   hb_reference_count_t ref_count;
-  hb_mutex_t mutex;
   hb_user_data_array_t user_data;
 
-#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID, HB_MUTEX_INIT, HB_USER_DATA_ARRAY_INIT}
+#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID, HB_USER_DATA_ARRAY_INIT}
 
   static inline void *create (unsigned int size) {
     hb_object_header_t *obj = (hb_object_header_t *) calloc (1, size);
@@ -119,7 +117,6 @@
 
   inline void init (void) {
     ref_count.init (1);
-    mutex.init ();
     user_data.init ();
   }
 
@@ -140,20 +137,11 @@
       return false;
 
     ref_count.finish (); /* Do this before user_data */
-    user_data.finish (mutex);
-    mutex.finish ();
+    user_data.finish ();
 
     return true;
   }
 
-  inline void lock (void) {
-    mutex.lock ();
-  }
-
-  inline void unlock (void) {
-    mutex.unlock ();
-  }
-
   inline bool set_user_data (hb_user_data_key_t *key,
 			     void *              data,
 			     hb_destroy_func_t   destroy_func,
@@ -161,14 +149,14 @@
     if (unlikely (!this || this->is_inert ()))
       return false;
 
-    return user_data.set (key, data, destroy_func, replace, mutex);
+    return user_data.set (key, data, destroy_func, replace);
   }
 
   inline void *get_user_data (hb_user_data_key_t *key) {
     if (unlikely (!this || this->is_inert ()))
       return NULL;
 
-    return user_data.get (key, mutex);
+    return user_data.get (key);
   }
 
   inline void trace (const char *function) const {
@@ -219,18 +207,6 @@
   return obj->header.destroy ();
 }
 template <typename Type>
-static inline void hb_object_lock (Type *obj)
-{
-  hb_object_trace (obj, HB_FUNC);
-  return obj->header.lock ();
-}
-template <typename Type>
-static inline void hb_object_unlock (Type *obj)
-{
-  hb_object_trace (obj, HB_FUNC);
-  return obj->header.unlock ();
-}
-template <typename Type>
 static inline bool hb_object_set_user_data (Type               *obj,
 					    hb_user_data_key_t *key,
 					    void *              data,
diff --git a/src/hb-old.cc b/src/hb-old.cc
index 529bffa..a7ea8ed 100644
--- a/src/hb-old.cc
+++ b/src/hb-old.cc
@@ -100,7 +100,7 @@
 
     glyphs[i] = u;
   }
-  *numGlyphs = length; // XXX
+  *numGlyphs = length; /* XXX */
 
   return true;
 }
@@ -110,7 +110,7 @@
 			 const HB_Glyph *glyphs,
 			 hb_uint32 numGlyphs,
 			 HB_Fixed *advances,
-			 int flags /*HB_ShaperFlag*/)
+			 int flags /*HB_ShaperFlag*/ HB_UNUSED)
 {
   hb_font_t *font = (hb_font_t *) old_font->userData;
 
@@ -123,7 +123,7 @@
 		  const HB_UChar16 *string,
 		  hb_uint32 length)
 {
-  return true; // TODO
+  return true; /* TODO */
 }
 
 static HB_Error
@@ -135,7 +135,7 @@
 			  HB_Fixed *ypos,
 			  hb_uint32 *nPoints)
 {
-  return HB_Err_Ok; // TODO
+  return HB_Err_Ok; /* TODO */
 }
 
 static void
@@ -230,8 +230,8 @@
   data->klass = &hb_old_font_class;
   data->x_ppem = font->x_ppem;
   data->y_ppem = font->y_ppem;
-  data->x_scale = font->x_scale; // XXX
-  data->y_scale = font->y_scale; // XXX
+  data->x_scale = font->x_scale; /* XXX */
+  data->y_scale = font->y_scale; /* XXX */
   data->userData = font;
 
   return data;
@@ -251,15 +251,15 @@
 struct hb_old_shaper_shape_plan_data_t {};
 
 hb_old_shaper_shape_plan_data_t *
-_hb_old_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
-				       const hb_feature_t *user_features,
-				       unsigned int        num_user_features)
+_hb_old_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
+				       const hb_feature_t *user_features HB_UNUSED,
+				       unsigned int        num_user_features HB_UNUSED)
 {
   return (hb_old_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_old_shaper_shape_plan_data_destroy (hb_old_shaper_shape_plan_data_t *data)
+_hb_old_shaper_shape_plan_data_destroy (hb_old_shaper_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
@@ -269,7 +269,7 @@
  */
 
 hb_bool_t
-_hb_old_shape (hb_shape_plan_t    *shape_plan,
+_hb_old_shape (hb_shape_plan_t    *shape_plan HB_UNUSED,
 	       hb_font_t          *font,
 	       hb_buffer_t        *buffer,
 	       const hb_feature_t *features,
@@ -369,7 +369,7 @@
     *p = MIN (*p, buffer->info[i].cluster);
   }
   for (unsigned int i = 1; i < num_glyphs; i++)
-    if (vis_clusters[i] == -1)
+    if (vis_clusters[i] == (uint32_t) -1)
       vis_clusters[i] = vis_clusters[i - 1];
 
 #undef utf16_index
diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh
index 31fedfb..250504a 100644
--- a/src/hb-open-file-private.hh
+++ b/src/hb-open-file-private.hh
@@ -54,7 +54,7 @@
 typedef struct TableRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
@@ -103,7 +103,7 @@
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables));
   }
 
@@ -131,7 +131,7 @@
   inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (table.sanitize (c, this));
   }
 
@@ -170,7 +170,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     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 */
@@ -232,7 +232,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!u.tag.sanitize (c))) return TRACE_RETURN (false);
     switch (u.tag) {
     case CFFTag:	/* All the non-collection tags */
@@ -255,7 +255,7 @@
 };
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OPEN_FILE_PRIVATE_HH */
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 3067b90..5bfeb16 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -37,6 +37,7 @@
 namespace OT {
 
 
+
 /*
  * Casts
  */
@@ -138,13 +139,13 @@
 /* Generic nul-content Null objects. */
 template <typename Type>
 static inline const Type& Null (void) {
-  ASSERT_STATIC (Type::min_size <= sizeof (_NullPool));
+  ASSERT_STATIC (sizeof (Type) <= sizeof (_NullPool));
   return *CastP<Type> (_NullPool);
 }
 
 /* Specializaiton for arbitrary-content arbitrary-sized Null objects. */
 #define DEFINE_NULL_DATA(Type, data) \
-static const char _Null##Type[Type::min_size + 1] = data; /* +1 is for nul-termination in data */ \
+static const char _Null##Type[sizeof (Type) + 1] = data; /* +1 is for nul-termination in data */ \
 template <> \
 inline const Type& Null<Type> (void) { \
   return *CastP<Type> (_Null##Type); \
@@ -165,12 +166,22 @@
 #endif
 
 
-#define TRACE_SANITIZE() \
-	hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&c->debug_depth, "SANITIZE", this, HB_FUNC, "");
+#define TRACE_SANITIZE(this) \
+	hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 "");
 
 
 struct hb_sanitize_context_t
 {
+  inline const char *get_name (void) { return "SANITIZE"; }
+  static const unsigned int max_debug_depth = HB_DEBUG_SANITIZE;
+  typedef bool return_t;
+  template <typename T>
+  inline return_t process (const T &obj) { return obj.sanitize (this); }
+  static return_t default_return_value (void) { return true; }
+  bool stop_sublookup_iteration (const return_t r HB_UNUSED) const { return false; }
+
   inline void init (hb_blob_t *b)
   {
     this->blob = hb_blob_reference (b);
@@ -205,10 +216,11 @@
   {
     const char *p = (const char *) base;
 
-    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);
+    hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> 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 TRACE_RETURN (likely (this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len));
   }
@@ -218,10 +230,11 @@
     const char *p = (const char *) base;
     bool overflows = _hb_unsigned_int_mul_overflows (len, record_size);
 
-    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);
+    hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> 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 TRACE_RETURN (likely (!overflows && this->check_range (base, record_size * len)));
   }
@@ -237,11 +250,13 @@
     const char *p = (const char *) base;
     this->edit_count++;
 
-    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);
+    hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> 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,
+       this->writable ? "GRANTED" : "DENIED");
 
     return TRACE_RETURN (this->writable);
   }
@@ -336,8 +351,10 @@
 #endif
 
 
-#define TRACE_SERIALIZE() \
-	hb_auto_trace_t<HB_DEBUG_SERIALIZE> trace (&c->debug_depth, "SERIALIZE", c, HB_FUNC, "");
+#define TRACE_SERIALIZE(this) \
+	hb_auto_trace_t<HB_DEBUG_SERIALIZE, bool> trace \
+	(&c->debug_depth, "SERIALIZE", c, HB_FUNC, \
+	 "");
 
 
 struct hb_serialize_context_t
@@ -518,32 +535,43 @@
   inline bool operator != (const BEInt<Type, 4>& o) const { return !(*this == o); }
   private: uint8_t v[4];
 };
+template <typename Type>
+struct BEInt<Type, 3>
+{
+  public:
+  inline void set (Type i) { hb_be_uint24_put (v,i); }
+  inline operator Type (void) const { return hb_be_uint24_get (v); }
+  inline bool operator == (const BEInt<Type, 3>& o) const { return hb_be_uint24_eq (v, o.v); }
+  inline bool operator != (const BEInt<Type, 3>& o) const { return !(*this == o); }
+  private: uint8_t v[3];
+};
 
 /* Integer types in big-endian order and no alignment requirement */
-template <typename Type>
+template <typename Type, unsigned int Size>
 struct IntType
 {
   inline void set (Type i) { v.set (i); }
   inline operator Type(void) const { return v; }
-  inline bool operator == (const IntType<Type> &o) const { return v == o.v; }
-  inline bool operator != (const IntType<Type> &o) const { return v != o.v; }
-  static inline int cmp (const IntType<Type> *a, const IntType<Type> *b) { return b->cmp (*a); }
-  inline int cmp (IntType<Type> va) const { Type a = va; Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
+  inline bool operator == (const IntType<Type,Size> &o) const { return v == o.v; }
+  inline bool operator != (const IntType<Type,Size> &o) const { return v != o.v; }
+  static inline int cmp (const IntType<Type,Size> *a, const IntType<Type,Size> *b) { return b->cmp (*a); }
+  inline int cmp (IntType<Type,Size> va) const { Type a = va; Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
   inline int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (likely (c->check_struct (this)));
   }
   protected:
-  BEInt<Type, sizeof (Type)> v;
+  BEInt<Type, Size> v;
   public:
-  DEFINE_SIZE_STATIC (sizeof (Type));
+  DEFINE_SIZE_STATIC (Size);
 };
 
-typedef IntType<uint16_t> USHORT;	/* 16-bit unsigned integer. */
-typedef IntType<int16_t>  SHORT;	/* 16-bit signed integer. */
-typedef IntType<uint32_t> ULONG;	/* 32-bit unsigned integer. */
-typedef IntType<int32_t>  LONG;		/* 32-bit signed integer. */
+typedef IntType<uint16_t, 2> USHORT;	/* 16-bit unsigned integer. */
+typedef IntType<int16_t,  2> SHORT;	/* 16-bit signed integer. */
+typedef IntType<uint32_t, 4> ULONG;	/* 32-bit unsigned integer. */
+typedef IntType<int32_t,  4> LONG;	/* 32-bit signed integer. */
+typedef IntType<uint32_t, 3> UINT24;	/* 24-bit unsigned integer. */
 
 /* 16-bit signed integer (SHORT) that describes a quantity in FUnits. */
 typedef SHORT FWORD;
@@ -556,7 +584,7 @@
 struct LONGDATETIME
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (likely (c->check_struct (this)));
   }
   private:
@@ -620,7 +648,7 @@
   inline uint32_t to_int (void) const { return (major << 16) + minor; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
@@ -660,7 +688,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
     unsigned int offset = *this;
     if (unlikely (!offset)) return TRACE_RETURN (true);
@@ -669,7 +697,7 @@
   }
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
     unsigned int offset = *this;
     if (unlikely (!offset)) return TRACE_RETURN (true);
@@ -677,7 +705,13 @@
     return TRACE_RETURN (likely (obj.sanitize (c, user_data)) || neuter (c));
   }
 
-  private:
+  inline bool try_set (hb_sanitize_context_t *c, const OffsetType &v) {
+    if (c->may_edit (this, this->static_size)) {
+      this->set (v);
+      return true;
+    }
+    return false;
+  }
   /* Set the offset to Null */
   inline bool neuter (hb_sanitize_context_t *c) {
     if (c->may_edit (this, this->static_size)) {
@@ -733,7 +767,7 @@
   inline bool serialize (hb_serialize_context_t *c,
 			 unsigned int items_len)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     len.set (items_len); /* TODO(serialize) Overflow? */
     if (unlikely (!c->extend (*this))) return TRACE_RETURN (false);
@@ -744,7 +778,7 @@
 			 Supplier<Type> &items,
 			 unsigned int items_len)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!serialize (c, items_len))) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < items_len; i++)
       array[i] = items[i];
@@ -753,7 +787,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
 
     /* Note: for structs that do not reference other structs,
@@ -768,7 +802,7 @@
     return TRACE_RETURN (true);
   }
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
@@ -778,7 +812,7 @@
   }
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
@@ -789,7 +823,7 @@
 
   private:
   inline bool sanitize_shallow (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && c->check_array (this, Type::static_size, len));
   }
 
@@ -831,12 +865,12 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (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 ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this, user_data));
   }
 };
@@ -859,7 +893,7 @@
 			 Supplier<Type> &items,
 			 unsigned int items_len)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     len.set (items_len); /* TODO(serialize) Overflow? */
     if (unlikely (!items_len)) return TRACE_RETURN (true);
@@ -876,7 +910,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
 
     /* Note: for structs that do not reference other structs,
@@ -922,7 +956,7 @@
 };
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OPEN_TYPE_PRIVATE_HH */
diff --git a/src/hb-ot-head-table.hh b/src/hb-ot-head-table.hh
index 0934168..3a94512 100644
--- a/src/hb-ot-head-table.hh
+++ b/src/hb-ot-head-table.hh
@@ -47,12 +47,12 @@
 
   inline unsigned int get_upem (void) const {
     unsigned int upem = unitsPerEm;
-    /* If no valid head table found, assume 1000, which matches typicaly Type1 usage. */
+    /* If no valid head table found, assume 1000, which matches typical Type1 usage. */
     return 16 <= upem && upem <= 16384 ? upem : 1000;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
   }
 
@@ -143,7 +143,7 @@
 };
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OT_HEAD_TABLE_HH */
diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh
index 5009e6b..2b89c4e 100644
--- a/src/hb-ot-hhea-table.hh
+++ b/src/hb-ot-hhea-table.hh
@@ -45,7 +45,7 @@
   static const hb_tag_t Tag	= HB_OT_TAG_hhea;
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
   }
 
@@ -91,7 +91,7 @@
 };
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OT_HHEA_TABLE_HH */
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index feb6016..b94337d 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -53,7 +53,7 @@
   static const hb_tag_t Tag	= HB_OT_TAG_hmtx;
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     /* We don't check for anything specific here.  The users of the
      * struct do all the hard work... */
     return TRACE_RETURN (true);
@@ -86,7 +86,7 @@
 };
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OT_HMTX_TABLE_HH */
diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index f5a067a..e6018db 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -60,9 +60,14 @@
     return tag.cmp (a);
   }
 
+  struct sanitize_closure_t {
+    hb_tag_t tag;
+    void *list_base;
+  };
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
-    TRACE_SANITIZE ();
-    return TRACE_RETURN (c->check_struct (this) && offset.sanitize (c, base));
+    TRACE_SANITIZE (this);
+    const sanitize_closure_t closure = {tag, base};
+    return TRACE_RETURN (c->check_struct (this) && offset.sanitize (c, base, &closure));
   }
 
   Tag		tag;		/* 4-byte Tag identifier */
@@ -115,7 +120,7 @@
   { return this+RecordArrayOf<Type>::operator [](i).offset; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (RecordArrayOf<Type>::sanitize (c, this));
   }
 };
@@ -129,7 +134,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
@@ -192,8 +197,9 @@
    return reqFeatureIndex;;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+  inline bool sanitize (hb_sanitize_context_t *c,
+			const Record<LangSys>::sanitize_closure_t * = NULL) {
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && featureIndex.sanitize (c));
   }
 
@@ -230,8 +236,9 @@
   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 ();
+  inline bool sanitize (hb_sanitize_context_t *c,
+			const Record<Script>::sanitize_closure_t * = NULL) {
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
   }
 
@@ -249,6 +256,219 @@
 typedef RecordListOf<Script> ScriptList;
 
 
+/* http://www.microsoft.com/typography/otspec/features_pt.htm#size */
+struct FeatureParamsSize
+{
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
+
+    /* This subtable has some "history", if you will.  Some earlier versions of
+     * Adobe tools calculated the offset of the FeatureParams sutable from the
+     * beginning of the FeatureList table!  Now, that is dealt with in the
+     * Feature implementation.  But we still need to be able to tell junk from
+     * real data.  Note: We don't check that the nameID actually exists.
+     *
+     * Read Roberts wrote on 9/15/06 on opentype-list@indx.co.uk :
+     *
+     * Yes, it is correct that a new version of the AFDKO (version 2.0) will be
+     * coming out soon, and that the makeotf program will build a font with a
+     * 'size' feature that is correct by the specification.
+     *
+     * The specification for this feature tag is in the "OpenType Layout Tag
+     * Registry". You can see a copy of this at:
+     * http://partners.adobe.com/public/developer/opentype/index_tag8.html#size
+     *
+     * Here is one set of rules to determine if the 'size' feature is built
+     * correctly, or as by the older versions of MakeOTF. You may be able to do
+     * better.
+     *
+     * Assume that the offset to the size feature is according to specification,
+     * and make the following value checks. If it fails, assume the the size
+     * feature is calculated as versions of MakeOTF before the AFDKO 2.0 built it.
+     * If this fails, reject the 'size' feature. The older makeOTF's calculated the
+     * offset from the beginning of the FeatureList table, rather than from the
+     * beginning of the 'size' Feature table.
+     *
+     * If "design size" == 0:
+     *     fails check
+     *
+     * Else if ("subfamily identifier" == 0 and
+     *     "range start" == 0 and
+     *     "range end" == 0 and
+     *     "range start" == 0 and
+     *     "menu name ID" == 0)
+     *     passes check: this is the format used when there is a design size
+     * specified, but there is no recommended size range.
+     *
+     * Else if ("design size" <  "range start" or
+     *     "design size" >   "range end" or
+     *     "range end" <= "range start" or
+     *     "menu name ID"  < 256 or
+     *     "menu name ID"  > 32767 or
+     *     menu name ID is not a name ID which is actually in the name table)
+     *     fails test
+     * Else
+     *     passes test.
+     */
+
+    if (!designSize)
+      return TRACE_RETURN (false);
+    else if (subfamilyID == 0 &&
+	     subfamilyNameID == 0 &&
+	     rangeStart == 0 &&
+	     rangeEnd == 0)
+      return TRACE_RETURN (true);
+    else if (designSize < rangeStart ||
+	     designSize > rangeEnd ||
+	     subfamilyNameID < 256 ||
+	     subfamilyNameID > 32767)
+      return TRACE_RETURN (false);
+    else
+      return TRACE_RETURN (true);
+  }
+
+  USHORT	designSize;	/* Represents the design size in 720/inch
+				 * units (decipoints).  The design size entry
+				 * must be non-zero.  When there is a design
+				 * size but no recommended size range, the
+				 * rest of the array will consist of zeros. */
+  USHORT	subfamilyID;	/* Has no independent meaning, but serves
+				 * as an identifier that associates fonts
+				 * in a subfamily. All fonts which share a
+				 * Preferred or Font Family name and which
+				 * differ only by size range shall have the
+				 * same subfamily value, and no fonts which
+				 * differ in weight or style shall have the
+				 * same subfamily value. If this value is
+				 * zero, the remaining fields in the array
+				 * will be ignored. */
+  USHORT	subfamilyNameID;/* If the preceding value is non-zero, this
+				 * value must be set in the range 256 - 32767
+				 * (inclusive). It records the value of a
+				 * field in the name table, which must
+				 * contain English-language strings encoded
+				 * in Windows Unicode and Macintosh Roman,
+				 * and may contain additional strings
+				 * localized to other scripts and languages.
+				 * Each of these strings is the name an
+				 * application should use, in combination
+				 * with the family name, to represent the
+				 * subfamily in a menu.  Applications will
+				 * choose the appropriate version based on
+				 * their selection criteria. */
+  USHORT	rangeStart;	/* Large end of the recommended usage range
+				 * (inclusive), stored in 720/inch units
+				 * (decipoints). */
+  USHORT	rangeEnd;	/* Small end of the recommended usage range
+				   (exclusive), stored in 720/inch units
+				 * (decipoints). */
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+/* http://www.microsoft.com/typography/otspec/features_pt.htm#ssxx */
+struct FeatureParamsStylisticSet
+{
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE (this);
+    /* Right now minorVersion is at zero.  Which means, any table supports
+     * the uiNameID field. */
+    return TRACE_RETURN (c->check_struct (this));
+  }
+
+  USHORT	minorVersion;	/* (set to 0): This corresponds to a “minor”
+				 * version number. Additional data may be
+				 * added to the end of this Feature Parameters
+				 * table in the future. */
+
+  USHORT	uiNameID;	/* The 'name' table name ID that specifies a
+				 * string (or strings, for multiple languages)
+				 * for a user-interface label for this
+				 * feature.  The values of uiLabelNameId and
+				 * sampleTextNameId are expected to be in the
+				 * font-specific name ID range (256-32767),
+				 * though that is not a requirement in this
+				 * Feature Parameters specification. The
+				 * user-interface label for the feature can
+				 * be provided in multiple languages. An
+				 * English string should be included as a
+				 * fallback. The string should be kept to a
+				 * minimal length to fit comfortably with
+				 * different application interfaces. */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct FeatureParamsCharacterVariants
+{
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE (this);
+    return TRACE_RETURN (c->check_struct (this) &&
+			 characters.sanitize (c));
+  }
+
+  USHORT	format;			/* Format number is set to 0. */
+  USHORT	featUILableNameID;	/* The ‘name’ table name ID that
+					 * specifies a string (or strings,
+					 * for multiple languages) for a
+					 * user-interface label for this
+					 * feature. (May be NULL.) */
+  USHORT	featUITooltipTextNameID;/* The ‘name’ table name ID that
+					 * specifies a string (or strings,
+					 * for multiple languages) that an
+					 * application can use for tooltip
+					 * text for this feature. (May be
+					 * NULL.) */
+  USHORT	sampleTextNameID;	/* The ‘name’ table name ID that
+					 * specifies sample text that
+					 * illustrates the effect of this
+					 * feature. (May be NULL.) */
+  USHORT	numNamedParameters;	/* Number of named parameters. (May
+					 * be zero.) */
+  USHORT	firstParamUILabelNameID;/* The first ‘name’ table name ID
+					 * used to specify strings for
+					 * user-interface labels for the
+					 * feature parameters. (Must be zero
+					 * if numParameters is zero.) */
+  ArrayOf<UINT24>
+		characters;		/* Array of the Unicode Scalar Value
+					 * of the characters for which this
+					 * feature provides glyph variants.
+					 * (May be zero.) */
+  public:
+  DEFINE_SIZE_ARRAY (14, characters);
+};
+
+struct FeatureParams
+{
+  inline bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) {
+    TRACE_SANITIZE (this);
+    if (tag == HB_TAG ('s','i','z','e'))
+      return TRACE_RETURN (u.size.sanitize (c));
+    if ((tag & 0xFFFF0000) == HB_TAG ('s','s','\0','\0')) /* ssXX */
+      return TRACE_RETURN (u.stylisticSet.sanitize (c));
+    if ((tag & 0xFFFF0000) == HB_TAG ('c','v','\0','\0')) /* cvXX */
+      return TRACE_RETURN (u.characterVariants.sanitize (c));
+    return TRACE_RETURN (true);
+  }
+
+  inline const FeatureParamsSize& get_size_params (hb_tag_t tag) const
+  {
+    if (tag == HB_TAG ('s','i','z','e'))
+      return u.size;
+    return Null(FeatureParamsSize);
+  }
+
+  private:
+  union {
+  FeatureParamsSize			size;
+  FeatureParamsStylisticSet		stylisticSet;
+  FeatureParamsCharacterVariants	characterVariants;
+  } u;
+  DEFINE_SIZE_STATIC (17);
+};
+
 struct Feature
 {
   inline unsigned int get_lookup_count (void) const
@@ -260,12 +480,54 @@
 					  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 TRACE_RETURN (c->check_struct (this) && lookupIndex.sanitize (c));
+  inline const FeatureParams &get_feature_params (void) const
+  { return this+featureParams; }
+
+  inline bool sanitize (hb_sanitize_context_t *c,
+			const Record<Feature>::sanitize_closure_t *closure) {
+    TRACE_SANITIZE (this);
+    if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c))))
+      return TRACE_RETURN (false);
+
+    /* Some earlier versions of Adobe tools calculated the offset of the
+     * FeatureParams subtable from the beginning of the FeatureList table!
+     *
+     * If sanitizing "failed" for the FeatureParams subtable, try it with the
+     * alternative location.  We would know sanitize "failed" if old value
+     * of the offset was non-zero, but it's zeroed now.
+     *
+     * Only do this for the 'size' feature, since at the time of the faulty
+     * Adobe tools, only the 'size' feature had FeatureParams defined.
+     */
+
+    Offset orig_offset = featureParams;
+    if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)))
+      return TRACE_RETURN (false);
+
+    if (likely (!orig_offset))
+      return TRACE_RETURN (true);
+
+    if (featureParams == 0 && closure &&
+	closure->tag == HB_TAG ('s','i','z','e') &&
+	closure->list_base && closure->list_base < this)
+    {
+      unsigned int new_offset_int = (unsigned int) orig_offset -
+				    ((char *) this - (char *) closure->list_base);
+
+      Offset new_offset;
+      /* Check that it did not overflow. */
+      new_offset.set (new_offset_int);
+      if (new_offset == new_offset_int &&
+	  featureParams.try_set (c, new_offset) &&
+	  !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))
+	return TRACE_RETURN (false);
+    }
+
+    return TRACE_RETURN (true);
   }
 
-  Offset	featureParams;	/* Offset to Feature Parameters table (if one
+  OffsetTo<FeatureParams>
+		 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 */
@@ -318,7 +580,7 @@
 			 uint32_t lookup_props,
 			 unsigned int num_subtables)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     lookupType.set (lookup_type);
     lookupFlag.set (lookup_props & 0xFFFF);
@@ -332,7 +594,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     /* Real sanitize of the subtables is done by GSUB/GPOS/... */
     if (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false);
     if (lookupFlag & LookupFlag::UseMarkFilteringSet)
@@ -377,7 +639,7 @@
 			 Supplier<GlyphID> &glyphs,
 			 unsigned int num_glyphs)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     glyphArray.len.set (num_glyphs);
     if (unlikely (!c->extend (glyphArray))) return TRACE_RETURN (false);
@@ -388,7 +650,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (glyphArray.sanitize (c));
   }
 
@@ -403,6 +665,8 @@
       glyphs->add (glyphArray[i]);
   }
 
+  public:
+  /* Older compilers need this to be public. */
   struct Iter {
     inline void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
     inline bool more (void) { return i < c->glyphArray.len; }
@@ -414,6 +678,7 @@
     const struct CoverageFormat1 *c;
     unsigned int i;
   };
+  private:
 
   protected:
   USHORT	coverageFormat;	/* Format identifier--format = 1 */
@@ -442,7 +707,7 @@
 			 Supplier<GlyphID> &glyphs,
 			 unsigned int num_glyphs)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
 
     if (unlikely (!num_glyphs)) return TRACE_RETURN (true);
@@ -471,7 +736,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (rangeRecord.sanitize (c));
   }
 
@@ -497,6 +762,8 @@
       rangeRecord[i].add_coverage (glyphs);
   }
 
+  public:
+  /* Older compilers need this to be public. */
   struct Iter {
     inline void init (const CoverageFormat2 &c_) {
       c = &c_;
@@ -522,6 +789,7 @@
     const struct CoverageFormat2 *c;
     unsigned int i, j, coverage;
   };
+  private:
 
   protected:
   USHORT	coverageFormat;	/* Format identifier--format = 2 */
@@ -535,8 +803,6 @@
 
 struct Coverage
 {
-  inline unsigned int operator () (hb_codepoint_t glyph_id) const { return get_coverage (glyph_id); }
-
   inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
   {
     switch (u.format) {
@@ -550,7 +816,7 @@
 			 Supplier<GlyphID> &glyphs,
 			 unsigned int num_glyphs)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     unsigned int num_ranges = 1;
     for (unsigned int i = 1; i < num_glyphs; i++)
@@ -565,7 +831,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -676,10 +942,18 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && classValue.sanitize (c));
   }
 
+  template <typename set_t>
+  inline void add_class (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->add (startGlyph + i);
+  }
+
   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++)
@@ -711,10 +985,18 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (rangeRecord.sanitize (c));
   }
 
+  template <typename set_t>
+  inline void add_class (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].add_coverage (glyphs);
+  }
+
   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++)
@@ -734,8 +1016,6 @@
 
 struct ClassDef
 {
-  inline unsigned int operator () (hb_codepoint_t glyph_id) const { return get_class (glyph_id); }
-
   inline unsigned int get_class (hb_codepoint_t glyph_id) const
   {
     switch (u.format) {
@@ -746,7 +1026,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -755,6 +1035,14 @@
     }
   }
 
+  inline void add_class (hb_set_t *glyphs, unsigned int klass) const {
+    switch (u.format) {
+    case 1: u.format1.add_class (glyphs, klass); return;
+    case 2: u.format2.add_class (glyphs, klass); return;
+    default:return;
+    }
+  }
+
   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);
@@ -830,7 +1118,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && c->check_range (this, this->get_size ()));
   }
 
@@ -848,7 +1136,7 @@
 };
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */
diff --git a/src/hb-ot-layout-gdef-table.hh b/src/hb-ot-layout-gdef-table.hh
index 92ae1cf..ff2d09c 100644
--- a/src/hb-ot-layout-gdef-table.hh
+++ b/src/hb-ot-layout-gdef-table.hh
@@ -51,7 +51,7 @@
 					 unsigned int *point_count /* IN/OUT */,
 					 unsigned int *point_array /* OUT */) const
   {
-    unsigned int index = (this+coverage) (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
     if (index == NOT_COVERED)
     {
       if (point_count)
@@ -72,7 +72,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && attachPoint.sanitize (c, this));
   }
 
@@ -102,7 +102,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
@@ -128,7 +128,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
@@ -151,7 +151,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && deviceTable.sanitize (c, this));
   }
 
@@ -179,7 +179,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -220,7 +220,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (carets.sanitize (c, this));
   }
 
@@ -242,7 +242,7 @@
 				      unsigned int *caret_count /* IN/OUT */,
 				      hb_position_t *caret_array /* OUT */) const
   {
-    unsigned int index = (this+coverage) (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
     if (index == NOT_COVERED)
     {
       if (caret_count)
@@ -254,7 +254,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this));
   }
 
@@ -276,7 +276,7 @@
   { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this));
   }
 
@@ -300,7 +300,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -337,6 +337,8 @@
   inline bool has_glyph_classes (void) const { return glyphClassDef != 0; }
   inline unsigned int get_glyph_class (hb_codepoint_t glyph) const
   { return (this+glyphClassDef).get_class (glyph); }
+  inline void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const
+  { (this+glyphClassDef).add_class (glyphs, klass); }
 
   inline bool has_mark_attachment_types (void) const { return markAttachClassDef != 0; }
   inline unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const
@@ -363,7 +365,7 @@
   { return version.to_int () >= 0x00010002 && (this+markGlyphSetsDef[0]).covers (set_index, glyph_id); }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (version.sanitize (c) &&
 			 likely (version.major == 1) &&
 			 glyphClassDef.sanitize (c, this) &&
@@ -383,13 +385,13 @@
 
     switch (klass) {
     default:
-    case UnclassifiedGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
-    case BaseGlyph:		return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
-    case LigatureGlyph:		return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
-    case ComponentGlyph:	return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
+    case UnclassifiedGlyph:	return HB_OT_LAYOUT_GLYPH_PROPS_UNCLASSIFIED;
+    case BaseGlyph:		return HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH;
+    case LigatureGlyph:		return HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE;
+    case ComponentGlyph:	return HB_OT_LAYOUT_GLYPH_PROPS_COMPONENT;
     case MarkGlyph:
 	  klass = get_mark_attachment_type (glyph);
-	  return HB_OT_LAYOUT_GLYPH_CLASS_MARK | (klass << 8);
+	  return HB_OT_LAYOUT_GLYPH_PROPS_MARK | (klass << 8);
     }
   }
 
@@ -423,7 +425,7 @@
 };
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OT_LAYOUT_GDEF_TABLE_HH */
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 41cb93d..d27ce4f 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -172,12 +172,12 @@
   }
 
   inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     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 ();
+    TRACE_SANITIZE (this);
     unsigned int len = get_len ();
 
     if (!c->check_array (values, get_size (), count)) return TRACE_RETURN (false);
@@ -195,7 +195,7 @@
 
   /* 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 ();
+    TRACE_SANITIZE (this);
 
     if (!has_device ()) return TRACE_RETURN (true);
 
@@ -212,9 +212,6 @@
 
 struct AnchorFormat1
 {
-  friend struct Anchor;
-
-  private:
   inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
 			  hb_position_t *x, hb_position_t *y) const
   {
@@ -223,7 +220,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
@@ -237,9 +234,6 @@
 
 struct AnchorFormat2
 {
-  friend struct Anchor;
-
-  private:
   inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
 			  hb_position_t *x, hb_position_t *y) const
   {
@@ -255,7 +249,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
@@ -270,9 +264,6 @@
 
 struct AnchorFormat3
 {
-  friend struct Anchor;
-
-  private:
   inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
 			  hb_position_t *x, hb_position_t *y) const
   {
@@ -286,7 +277,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
   }
 
@@ -321,7 +312,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -351,7 +342,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     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;
@@ -376,7 +367,7 @@
   friend struct MarkArray;
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base));
   }
 
@@ -396,7 +387,7 @@
 		     const AnchorMatrix &anchors, unsigned int class_count,
 		     unsigned int glyph_pos) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
     unsigned int mark_class = record.klass;
 
@@ -418,7 +409,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (ArrayOf<MarkRecord>::sanitize (c, this));
   }
 };
@@ -428,9 +419,11 @@
 
 struct SinglePosFormat1
 {
-  friend struct SinglePos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -439,8 +432,8 @@
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    TRACE_APPLY (this);
+    unsigned int index = (this+coverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     valueFormat.apply_value (c->font, c->direction, this,
@@ -451,7 +444,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_value (c, this, values));
   }
 
@@ -471,9 +464,11 @@
 
 struct SinglePosFormat2
 {
-  friend struct SinglePos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -482,8 +477,8 @@
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    TRACE_APPLY (this);
+    unsigned int index = (this+coverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     if (likely (index >= valueCount)) return TRACE_RETURN (false);
@@ -497,7 +492,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_values (c, this, values, valueCount));
   }
 
@@ -517,31 +512,19 @@
 
 struct SinglePos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    case 2: return u.format2.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    case 2: return TRACE_RETURN (u.format2.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 2: return TRACE_RETURN (c->process (u.format2));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -577,17 +560,34 @@
 {
   friend struct PairPosFormat1;
 
-  inline bool apply (hb_apply_context_t *c,
-		     const ValueFormat *valueFormats,
-		     unsigned int pos) const
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c,
+			      const ValueFormat *valueFormats) const
   {
-    TRACE_APPLY ();
+    TRACE_COLLECT_GLYPHS (this);
     unsigned int len1 = valueFormats[0].get_len ();
     unsigned int len2 = valueFormats[1].get_len ();
     unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
 
-    unsigned int count = len;
     const PairValueRecord *record = CastP<PairValueRecord> (array);
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      c->input->add (record->secondGlyph);
+      record = &StructAtOffset<PairValueRecord> (record, record_size);
+    }
+  }
+
+  inline bool apply (hb_apply_context_t *c,
+		     const ValueFormat *valueFormats,
+		     unsigned int pos) const
+  {
+    TRACE_APPLY (this);
+    unsigned int len1 = valueFormats[0].get_len ();
+    unsigned int len2 = valueFormats[1].get_len ();
+    unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
+
+    const PairValueRecord *record = CastP<PairValueRecord> (array);
+    unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
     {
       if (c->buffer->info[pos].codepoint == record->secondGlyph)
@@ -615,7 +615,7 @@
   };
 
   inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
        && c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
 
@@ -635,9 +635,14 @@
 
 struct PairPosFormat1
 {
-  friend struct PairPos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+    unsigned int count = pairSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      (this+pairSet[i]).collect_glyphs (c, &valueFormat1);
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -646,11 +651,11 @@
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
     if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    unsigned int index = (this+coverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     if (!skippy_iter.next ()) return TRACE_RETURN (false);
@@ -659,7 +664,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
 
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
@@ -693,9 +698,23 @@
 
 struct PairPosFormat2
 {
-  friend struct PairPos;
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    /* (this+coverage).add_coverage (c->input); // Don't need this. */
 
-  private:
+    /* TODO only add values for pairs that have nonzero adjustments. */
+
+    unsigned int count1 = class1Count;
+    const ClassDef &klass1 = this+classDef1;
+    for (unsigned int i = 0; i < count1; i++)
+      klass1.add_class (c->input, i);
+
+    unsigned int count2 = class2Count;
+    const ClassDef &klass2 = this+classDef2;
+    for (unsigned int i = 0; i < count2; i++)
+      klass2.add_class (c->input, i);
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -704,11 +723,11 @@
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
     if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    unsigned int index = (this+coverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     if (!skippy_iter.next ()) return TRACE_RETURN (false);
@@ -717,8 +736,8 @@
     unsigned int len2 = valueFormat2.get_len ();
     unsigned int record_len = len1 + len2;
 
-    unsigned int klass1 = (this+classDef1) (c->buffer->cur().codepoint);
-    unsigned int klass2 = (this+classDef2) (c->buffer->info[skippy_iter.idx].codepoint);
+    unsigned int klass1 = (this+classDef1).get_class (c->buffer->cur().codepoint);
+    unsigned int klass2 = (this+classDef2).get_class (c->buffer->info[skippy_iter.idx].codepoint);
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return TRACE_RETURN (false);
 
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
@@ -735,7 +754,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
        && coverage.sanitize (c, this)
        && classDef1.sanitize (c, this)
@@ -783,31 +802,19 @@
 
 struct PairPos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    case 2: return u.format2.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    case 2: return TRACE_RETURN (u.format2.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 2: return TRACE_RETURN (c->process (u.format2));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -830,7 +837,7 @@
   friend struct CursivePosFormat1;
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
   }
 
@@ -849,9 +856,11 @@
 
 struct CursivePosFormat1
 {
-  friend struct CursivePos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -860,20 +869,20 @@
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
 
     /* We don't handle mark glyphs here. */
-    if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) return TRACE_RETURN (false);
+    if (c->property & HB_OT_LAYOUT_GLYPH_PROPS_MARK) return TRACE_RETURN (false);
 
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
     if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
-    const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->cur().codepoint)];
+    const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (c->buffer->cur().codepoint)];
     if (!this_record.exitAnchor) return TRACE_RETURN (false);
 
     if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
-    const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[skippy_iter.idx].codepoint)];
+    const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage  (c->buffer->info[skippy_iter.idx].codepoint)];
     if (!next_record.entryAnchor) return TRACE_RETURN (false);
 
     unsigned int i = c->buffer->idx;
@@ -941,7 +950,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
   }
 
@@ -959,29 +968,18 @@
 
 struct CursivePos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1004,9 +1002,13 @@
 
 struct MarkBasePosFormat1
 {
-  friend struct MarkBasePos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+markCoverage).add_coverage (c->input);
+    (this+baseCoverage).add_coverage (c->input);
+    /* TODO only add combinations that have nonzero adjustment. */
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -1015,8 +1017,8 @@
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
-    unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint);
+    TRACE_APPLY (this);
+    unsigned int mark_index = (this+markCoverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
@@ -1030,16 +1032,16 @@
     } while (1);
 
     /* The following assertion is too strong, so we've disabled it. */
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/}
+    if (!(property & HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/}
 
-    unsigned int base_index = (this+baseCoverage) (c->buffer->info[skippy_iter.idx].codepoint);
+    unsigned int base_index = (this+baseCoverage).get_coverage  (c->buffer->info[skippy_iter.idx].codepoint);
     if (base_index == NOT_COVERED) return TRACE_RETURN (false);
 
     return TRACE_RETURN ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     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));
   }
@@ -1065,29 +1067,18 @@
 
 struct MarkBasePos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1115,9 +1106,13 @@
 
 struct MarkLigPosFormat1
 {
-  friend struct MarkLigPos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+markCoverage).add_coverage (c->input);
+    (this+ligatureCoverage).add_coverage (c->input);
+    /* TODO only add combinations that have nonzero adjustment. */
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -1126,8 +1121,8 @@
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
-    unsigned int mark_index = (this+markCoverage) (c->buffer->cur().codepoint);
+    TRACE_APPLY (this);
+    unsigned int mark_index = (this+markCoverage).get_coverage  (c->buffer->cur().codepoint);
     if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
@@ -1136,10 +1131,10 @@
     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 TRACE_RETURN (false);*/}
+    if (!(property & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE)) {/*return TRACE_RETURN (false);*/}
 
     unsigned int j = skippy_iter.idx;
-    unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
+    unsigned int lig_index = (this+ligatureCoverage).get_coverage  (c->buffer->info[j].codepoint);
     if (lig_index == NOT_COVERED) return TRACE_RETURN (false);
 
     const LigatureArray& lig_array = this+ligatureArray;
@@ -1166,7 +1161,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     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));
   }
@@ -1193,29 +1188,18 @@
 
 struct MarkLigPos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1238,9 +1222,13 @@
 
 struct MarkMarkPosFormat1
 {
-  friend struct MarkMarkPos;
-
-  private:
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+mark1Coverage).add_coverage (c->input);
+    (this+mark2Coverage).add_coverage (c->input);
+    /* TODO only add combinations that have nonzero adjustment. */
+  }
 
   inline const Coverage &get_coverage (void) const
   {
@@ -1249,8 +1237,8 @@
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
-    unsigned int mark1_index = (this+mark1Coverage) (c->buffer->cur().codepoint);
+    TRACE_APPLY (this);
+    unsigned int mark1_index = (this+mark1Coverage).get_coverage  (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 */
@@ -1258,7 +1246,7 @@
     hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
     if (!skippy_iter.prev (&property)) return TRACE_RETURN (false);
 
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) return TRACE_RETURN (false);
+    if (!(property & HB_OT_LAYOUT_GLYPH_PROPS_MARK)) return TRACE_RETURN (false);
 
     unsigned int j = skippy_iter.idx;
 
@@ -1283,14 +1271,14 @@
     return TRACE_RETURN (false);
 
     good:
-    unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
+    unsigned int mark2_index = (this+mark2Coverage).get_coverage  (c->buffer->info[j].codepoint);
     if (mark2_index == NOT_COVERED) return TRACE_RETURN (false);
 
     return TRACE_RETURN ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     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));
@@ -1319,29 +1307,18 @@
 
 struct MarkMarkPos
 {
-  friend struct PosLookupSubTable;
-
-  private:
-
-  inline const Coverage &get_coverage (void) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1357,50 +1334,13 @@
 };
 
 
-static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
+struct ContextPos : Context {};
 
-struct ContextPos : Context
+struct ChainContextPos : ChainContext {};
+
+struct ExtensionPos : Extension<ExtensionPos>
 {
-  friend struct PosLookupSubTable;
-
-  private:
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    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 TRACE_RETURN (ChainContext::apply (c, position_lookup));
-  }
-};
-
-
-struct ExtensionPos : Extension
-{
-  friend struct PosLookupSubTable;
-
-  private:
-  inline const struct PosLookupSubTable& get_subtable (void) const
-  {
-    unsigned int offset = get_offset ();
-    if (unlikely (!offset)) return Null(PosLookupSubTable);
-    return StructAtOffset<PosLookupSubTable> (this, offset);
-  }
-
-  inline const Coverage &get_coverage (void) const;
-
-  inline bool apply (hb_apply_context_t *c) const;
-
-  inline bool sanitize (hb_sanitize_context_t *c);
+  typedef struct PosLookupSubTable LookupSubTable;
 };
 
 
@@ -1426,41 +1366,26 @@
     Extension		= 9
   };
 
-  inline const Coverage &get_coverage (unsigned int lookup_type) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c, unsigned int lookup_type) const
   {
+    TRACE_PROCESS (this);
     switch (lookup_type) {
-    case Single:		return u.single.get_coverage ();
-    case Pair:			return u.pair.get_coverage ();
-    case Cursive:		return u.cursive.get_coverage ();
-    case MarkBase:		return u.markBase.get_coverage ();
-    case MarkLig:		return u.markLig.get_coverage ();
-    case MarkMark:		return u.markMark.get_coverage ();
-    case Context:		return u.context.get_coverage ();
-    case ChainContext:		return u.chainContext.get_coverage ();
-    case Extension:		return u.extension.get_coverage ();
-    default:			return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
-  {
-    TRACE_APPLY ();
-    switch (lookup_type) {
-    case Single:		return TRACE_RETURN (u.single.apply (c));
-    case Pair:			return TRACE_RETURN (u.pair.apply (c));
-    case Cursive:		return TRACE_RETURN (u.cursive.apply (c));
-    case MarkBase:		return TRACE_RETURN (u.markBase.apply (c));
-    case MarkLig:		return TRACE_RETURN (u.markLig.apply (c));
-    case MarkMark:		return TRACE_RETURN (u.markMark.apply (c));
-    case Context:		return TRACE_RETURN (u.context.apply (c));
-    case ChainContext:		return TRACE_RETURN (u.chainContext.apply (c));
-    case Extension:		return TRACE_RETURN (u.extension.apply (c));
-    default:			return TRACE_RETURN (false);
+    case Single:		return TRACE_RETURN (u.single.process (c));
+    case Pair:			return TRACE_RETURN (u.pair.process (c));
+    case Cursive:		return TRACE_RETURN (u.cursive.process (c));
+    case MarkBase:		return TRACE_RETURN (u.markBase.process (c));
+    case MarkLig:		return TRACE_RETURN (u.markLig.process (c));
+    case MarkMark:		return TRACE_RETURN (u.markMark.process (c));
+    case Context:		return TRACE_RETURN (u.context.process (c));
+    case ChainContext:		return TRACE_RETURN (u.chainContext.process (c));
+    case Extension:		return TRACE_RETURN (u.extension.process (c));
+    default:			return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.header.sub_format.sanitize (c))
       return TRACE_RETURN (false);
     switch (lookup_type) {
@@ -1480,7 +1405,7 @@
   protected:
   union {
   struct {
-    USHORT			sub_format;
+    USHORT		sub_format;
   } header;
   SinglePos		single;
   PairPos		pair;
@@ -1502,35 +1427,54 @@
   inline const PosLookupSubTable& get_subtable (unsigned int i) const
   { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
 
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
+  {
+    TRACE_PROCESS (this);
+    unsigned int lookup_type = get_type ();
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++) {
+      typename context_t::return_t r = get_subtable (i).process (c, lookup_type);
+      if (c->stop_sublookup_iteration (r))
+        return TRACE_RETURN (r);
+    }
+    return TRACE_RETURN (c->default_return_value ());
+  }
+  template <typename context_t>
+  static inline typename context_t::return_t process_recurse_func (context_t *c, unsigned int lookup_index);
+
+  inline hb_collect_glyphs_context_t::return_t collect_glyphs_lookup (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    c->set_recurse_func (NULL);
+    return TRACE_RETURN (process (c));
+  }
+
   template <typename set_t>
   inline void add_coverage (set_t *glyphs) const
   {
+    hb_get_coverage_context_t c;
     const Coverage *last = NULL;
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++) {
-      const Coverage *c = &get_subtable (i).get_coverage (get_type ());
-      if (c != last) {
-        c->add_coverage (glyphs);
-        last = c;
+      const Coverage *coverage = &get_subtable (i).process (&c, get_type ());
+      if (coverage != last) {
+        coverage->add_coverage (glyphs);
+        last = coverage;
       }
     }
   }
 
   inline bool apply_once (hb_apply_context_t *c) const
   {
-    unsigned int lookup_type = get_type ();
-
+    TRACE_APPLY (this);
     if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props, &c->property))
-      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;
+      return TRACE_RETURN (false);
+    return TRACE_RETURN (process (c));
   }
 
+  static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index);
+
   inline bool apply_string (hb_apply_context_t *c, const hb_set_digest_t *digest) const
   {
     bool ret = false;
@@ -1538,6 +1482,7 @@
     if (unlikely (!c->buffer->len || !c->lookup_mask))
       return false;
 
+    c->set_recurse_func (apply_recurse_func);
     c->set_lookup (*this);
 
     c->buffer->idx = 0;
@@ -1556,7 +1501,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
     OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
     return TRACE_RETURN (list.sanitize (c, this, get_type ()));
@@ -1576,15 +1521,11 @@
   inline const PosLookup& get_lookup (unsigned int i) const
   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
 
-  template <typename set_t>
-  inline void add_coverage (set_t *glyphs, unsigned int lookup_index) const
-  { get_lookup (lookup_index).add_coverage (glyphs); }
-
   static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
   static inline void position_finish (hb_font_t *font, hb_buffer_t *buffer, hb_bool_t zero_width_attahced_marks);
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
     OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
     return TRACE_RETURN (list.sanitize (c, this));
@@ -1673,38 +1614,25 @@
 
 /* Out-of-class implementation for methods recursing */
 
-inline const Coverage & ExtensionPos::get_coverage (void) const
-{
-  return get_subtable ().get_coverage (get_type ());
-}
-
-inline bool ExtensionPos::apply (hb_apply_context_t *c) const
-{
-  TRACE_APPLY ();
-  return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
-}
-
-inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
-{
-  TRACE_SANITIZE ();
-  if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
-  unsigned int offset = get_offset ();
-  if (unlikely (!offset)) return TRACE_RETURN (true);
-  return TRACE_RETURN (StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ()));
-}
-
-static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
+template <typename context_t>
+inline typename context_t::return_t PosLookup::process_recurse_func (context_t *c, unsigned int lookup_index)
 {
   const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos);
   const PosLookup &l = gpos.get_lookup (lookup_index);
+  return l.process (c);
+}
 
-  if (unlikely (c->nesting_level_left == 0))
-    return false;
-
-  hb_apply_context_t new_c (*c);
-  new_c.nesting_level_left--;
-  new_c.set_lookup (l);
-  return l.apply_once (&new_c);
+inline bool PosLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index)
+{
+  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos);
+  const PosLookup &l = gpos.get_lookup (lookup_index);
+  unsigned int saved_lookup_props = c->lookup_props;
+  unsigned int saved_property = c->property;
+  c->set_lookup (l);
+  bool ret = l.apply_once (c);
+  c->lookup_props = saved_lookup_props;
+  c->property = saved_property;
+  return ret;
 }
 
 
@@ -1712,7 +1640,7 @@
 #undef cursive_chain
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 05b18cc..2642acb 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -37,13 +37,9 @@
 
 struct SingleSubstFormat1
 {
-  friend struct SingleSubst;
-
-  private:
-
   inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     Coverage::Iter iter;
     for (iter.init (this+coverage); iter.more (); iter.next ()) {
       hb_codepoint_t glyph_id = iter.get_glyph ();
@@ -52,16 +48,33 @@
     }
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      hb_codepoint_t glyph_id = iter.get_glyph ();
+      c->input->add (glyph_id);
+      c->output->add ((glyph_id + deltaGlyphID) & 0xFFFF);
+    }
+  }
+
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    TRACE_WOULD_APPLY (this);
+    return TRACE_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
-    unsigned int index = (this+coverage) (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* According to the Adobe Annotated OpenType Suite, result is always
@@ -77,7 +90,7 @@
 			 unsigned int num_glyphs,
 			 int delta)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
     deltaGlyphID.set (delta); /* TODO(serilaize) overflow? */
@@ -85,7 +98,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
   }
 
@@ -102,13 +115,9 @@
 
 struct SingleSubstFormat2
 {
-  friend struct SingleSubst;
-
-  private:
-
   inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     Coverage::Iter iter;
     for (iter.init (this+coverage); iter.more (); iter.next ()) {
       if (c->glyphs->has (iter.get_glyph ()))
@@ -116,16 +125,32 @@
     }
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      c->input->add (iter.get_glyph ());
+      c->output->add (substitute[iter.get_coverage ()]);
+    }
+  }
+
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    TRACE_WOULD_APPLY (this);
+    return TRACE_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
-    unsigned int index = (this+coverage) (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     if (unlikely (index >= substitute.len)) return TRACE_RETURN (false);
@@ -141,7 +166,7 @@
 			 Supplier<GlyphID> &substitutes,
 			 unsigned int num_glyphs)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return TRACE_RETURN (false);
     if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
@@ -149,7 +174,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c));
   }
 
@@ -167,37 +192,14 @@
 
 struct SingleSubst
 {
-  friend struct SubstLookupSubTable;
-  friend struct SubstLookup;
-
-  private:
-
-  inline void closure (hb_closure_context_t *c) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: u.format1.closure (c); break;
-    case 2: u.format2.closure (c); break;
-    default:                       break;
-    }
-  }
-
-  inline const Coverage &get_coverage (void) const
-  {
-    switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    case 2: return u.format2.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    case 2: return TRACE_RETURN (u.format2.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 2: return TRACE_RETURN (c->process (u.format2));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
@@ -206,7 +208,7 @@
 			 Supplier<GlyphID> &substitutes,
 			 unsigned int num_glyphs)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
     unsigned int format = 2;
     int delta;
@@ -229,7 +231,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -249,24 +251,28 @@
 
 struct Sequence
 {
-  friend struct MultipleSubstFormat1;
-
-  private:
-
   inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     unsigned int count = substitute.len;
     for (unsigned int i = 0; i < count; i++)
       c->glyphs->add (substitute[i]);
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    unsigned int count = substitute.len;
+    for (unsigned int i = 0; i < count; i++)
+      c->output->add (substitute[i]);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     if (unlikely (!substitute.len)) return TRACE_RETURN (false);
 
-    unsigned int klass = c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE ? HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH : 0;
+    unsigned int klass = c->property & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE ? HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
     unsigned int count = substitute.len;
     for (unsigned int i = 0; i < count; i++) {
       set_lig_props_for_component (c->buffer->cur(), i);
@@ -281,15 +287,14 @@
 			 Supplier<GlyphID> &glyphs,
 			 unsigned int num_glyphs)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     if (unlikely (!substitute.serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
     return TRACE_RETURN (true);
   }
 
-  public:
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (substitute.sanitize (c));
   }
 
@@ -302,13 +307,9 @@
 
 struct MultipleSubstFormat1
 {
-  friend struct MultipleSubst;
-
-  private:
-
   inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     Coverage::Iter iter;
     for (iter.init (this+coverage); iter.more (); iter.next ()) {
       if (c->glyphs->has (iter.get_glyph ()))
@@ -316,16 +317,31 @@
     }
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+    unsigned int count = sequence.len;
+    for (unsigned int i = 0; i < count; i++)
+	(this+sequence[i]).collect_glyphs (c);
+  }
+
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    TRACE_WOULD_APPLY (this);
+    return TRACE_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
 
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     return TRACE_RETURN ((this+sequence[index]).apply (c));
@@ -337,7 +353,7 @@
 			 unsigned int num_glyphs,
 			 Supplier<GlyphID> &substitute_glyphs_list)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     if (unlikely (!sequence.serialize (c, num_glyphs))) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
@@ -350,7 +366,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this));
   }
 
@@ -368,34 +384,13 @@
 
 struct MultipleSubst
 {
-  friend struct SubstLookupSubTable;
-  friend struct SubstLookup;
-
-  private:
-
-  inline void closure (hb_closure_context_t *c) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: u.format1.closure (c); break;
-    default:                       break;
-    }
-  }
-
-  inline const Coverage &get_coverage (void) const
-  {
-    switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
@@ -405,7 +400,7 @@
 			 unsigned int num_glyphs,
 			 Supplier<GlyphID> &substitute_glyphs_list)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
     unsigned int format = 1;
     u.format.set (format);
@@ -416,7 +411,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -437,13 +432,9 @@
 
 struct AlternateSubstFormat1
 {
-  friend struct AlternateSubst;
-
-  private:
-
   inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     Coverage::Iter iter;
     for (iter.init (this+coverage); iter.more (); iter.next ()) {
       if (c->glyphs->has (iter.get_glyph ())) {
@@ -455,17 +446,36 @@
     }
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      c->input->add (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->output->add (alt_set[i]);
+    }
+  }
+
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    TRACE_WOULD_APPLY (this);
+    return TRACE_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
-    unsigned int index = (this+coverage) (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const AlternateSet &alt_set = this+alternateSet[index];
@@ -494,7 +504,7 @@
 			 unsigned int num_glyphs,
 			 Supplier<GlyphID> &alternate_glyphs_list)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     if (unlikely (!alternateSet.serialize (c, num_glyphs))) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
@@ -507,7 +517,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
   }
 
@@ -525,34 +535,13 @@
 
 struct AlternateSubst
 {
-  friend struct SubstLookupSubTable;
-  friend struct SubstLookup;
-
-  private:
-
-  inline void closure (hb_closure_context_t *c) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: u.format1.closure (c); break;
-    default:                       break;
-    }
-  }
-
-  inline const Coverage &get_coverage (void) const
-  {
-    switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
@@ -562,7 +551,7 @@
 			 unsigned int num_glyphs,
 			 Supplier<GlyphID> &alternate_glyphs_list)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
     unsigned int format = 1;
     u.format.set (format);
@@ -573,7 +562,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -591,13 +580,9 @@
 
 struct Ligature
 {
-  friend struct LigatureSet;
-
-  private:
-
   inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     unsigned int count = component.len;
     for (unsigned int i = 1; i < count; i++)
       if (!c->glyphs->has (component[i]))
@@ -605,27 +590,37 @@
     c->glyphs->add (ligGlyph);
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    unsigned int count = component.len;
+    for (unsigned int i = 1; i < count; i++)
+      c->input->add (component[i]);
+    c->output->add (ligGlyph);
+  }
+
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
+    TRACE_WOULD_APPLY (this);
     if (c->len != component.len)
-      return false;
+      return TRACE_RETURN (false);
 
     for (unsigned int i = 1; i < c->len; i++)
       if (likely (c->glyphs[i] != component[i]))
-	return false;
+	return TRACE_RETURN (false);
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     unsigned int count = component.len;
     if (unlikely (count < 1)) return TRACE_RETURN (false);
 
-    unsigned int end_offset;
-    bool is_mark_ligature;
-    unsigned int total_component_count;
+    unsigned int end_offset = 0;
+    bool is_mark_ligature = false;
+    unsigned int total_component_count = 0;
 
     if (likely (!match_input (c, count,
 			      &component[1],
@@ -656,7 +651,7 @@
 			 Supplier<GlyphID> &components, /* Starting from second */
 			 unsigned int num_components /* Including first component */)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     ligGlyph = ligature;
     if (unlikely (!component.serialize (c, components, num_components))) return TRACE_RETURN (false);
@@ -665,7 +660,7 @@
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c));
   }
 
@@ -681,33 +676,38 @@
 
 struct LigatureSet
 {
-  friend struct LigatureSubstFormat1;
-
-  private:
-
   inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     unsigned int num_ligs = ligature.len;
     for (unsigned int i = 0; i < num_ligs; i++)
       (this+ligature[i]).closure (c);
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    unsigned int num_ligs = ligature.len;
+    for (unsigned int i = 0; i < num_ligs; i++)
+      (this+ligature[i]).collect_glyphs (c);
+  }
+
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
+    TRACE_WOULD_APPLY (this);
     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 (c))
-        return true;
+        return TRACE_RETURN (true);
     }
-    return false;
+    return TRACE_RETURN (false);
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     unsigned int num_ligs = ligature.len;
     for (unsigned int i = 0; i < num_ligs; i++)
     {
@@ -724,7 +724,7 @@
 			 unsigned int num_ligatures,
 			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     if (unlikely (!ligature.serialize (c, num_ligatures))) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < num_ligatures; i++)
@@ -737,9 +737,8 @@
     return TRACE_RETURN (true);
   }
 
-  public:
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (ligature.sanitize (c, this));
   }
 
@@ -753,13 +752,9 @@
 
 struct LigatureSubstFormat1
 {
-  friend struct LigatureSubst;
-
-  private:
-
   inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     Coverage::Iter iter;
     for (iter.init (this+coverage); iter.more (); iter.next ()) {
       if (c->glyphs->has (iter.get_glyph ()))
@@ -767,6 +762,16 @@
     }
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    Coverage::Iter iter;
+    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+      c->input->add (iter.get_glyph ());
+      (this+ligatureSet[iter.get_coverage ()]).collect_glyphs (c);
+    }
+  }
+
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
@@ -774,15 +779,20 @@
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    return (this+ligatureSet[(this+coverage) (c->glyphs[0])]).would_apply (c);
+    TRACE_WOULD_APPLY (this);
+    unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
+
+    const LigatureSet &lig_set = this+ligatureSet[index];
+    return TRACE_RETURN (lig_set.would_apply (c));
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
-    unsigned int index = (this+coverage) (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const LigatureSet &lig_set = this+ligatureSet[index];
@@ -797,7 +807,7 @@
 			 Supplier<unsigned int> &component_count_list,
 			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < num_first_glyphs; i++)
@@ -812,7 +822,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
   }
 
@@ -830,42 +840,13 @@
 
 struct LigatureSubst
 {
-  friend struct SubstLookupSubTable;
-  friend struct SubstLookup;
-
-  private:
-
-  inline void closure (hb_closure_context_t *c) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: u.format1.closure (c); break;
-    default:                       break;
-    }
-  }
-
-  inline const Coverage &get_coverage (void) const
-  {
-    switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool would_apply (hb_would_apply_context_t *c) const
-  {
-    switch (u.format) {
-    case 1: return u.format1.would_apply (c);
-    default:return false;
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
@@ -877,7 +858,7 @@
 			 Supplier<unsigned int> &component_count_list,
 			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
     unsigned int format = 1;
     u.format.set (format);
@@ -889,7 +870,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -905,70 +886,13 @@
 };
 
 
-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 {};
 
-struct ContextSubst : Context
+struct ChainContextSubst : ChainContext {};
+
+struct ExtensionSubst : Extension<ExtensionSubst>
 {
-  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 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 TRACE_RETURN (ChainContext::apply (c, substitute_lookup));
-  }
-};
-
-
-struct ExtensionSubst : Extension
-{
-  friend struct SubstLookupSubTable;
-  friend struct SubstLookup;
-
-  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 const Coverage &get_coverage (void) const;
-
-  inline bool would_apply (hb_would_apply_context_t *c) const;
-
-  inline bool apply (hb_apply_context_t *c) const;
-
-  inline bool sanitize (hb_sanitize_context_t *c);
+  typedef struct SubstLookupSubTable LookupSubTable;
 
   inline bool is_reverse (void) const;
 };
@@ -976,13 +900,9 @@
 
 struct ReverseChainSingleSubstFormat1
 {
-  friend struct ReverseChainSingleSubst;
-
-  private:
-
   inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
 
     unsigned int count;
@@ -1005,18 +925,48 @@
     }
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+    unsigned int count;
+
+    (this+coverage).add_coverage (c->input);
+
+    count = backtrack.len;
+    for (unsigned int i = 0; i < count; i++)
+      (this+backtrack[i]).add_coverage (c->before);
+
+    count = lookahead.len;
+    for (unsigned int i = 0; i < count; i++)
+      (this+lookahead[i]).add_coverage (c->after);
+
+    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
+    count = substitute.len;
+    for (unsigned int i = 0; i < count; i++)
+      c->output->add (substitute[i]);
+  }
+
   inline const Coverage &get_coverage (void) const
   {
     return this+coverage;
   }
 
+  inline bool would_apply (hb_would_apply_context_t *c) const
+  {
+    TRACE_WOULD_APPLY (this);
+    return TRACE_RETURN (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
+  }
+
   inline bool apply (hb_apply_context_t *c) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     if (unlikely (c->nesting_level_left != MAX_NESTING_LEVEL))
       return TRACE_RETURN (false); /* No chaining to this type */
 
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
@@ -1039,7 +989,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
       return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
@@ -1071,38 +1021,18 @@
 
 struct ReverseChainSingleSubst
 {
-  friend struct SubstLookupSubTable;
-
-  private:
-
-  inline void closure (hb_closure_context_t *c) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: u.format1.closure (c); break;
-    default:                       break;
-    }
-  }
-
-  inline const Coverage &get_coverage (void) const
-  {
-    switch (u.format) {
-    case 1: return u.format1.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1138,82 +1068,25 @@
     ReverseChainSingle	= 8
   };
 
-  inline void closure (hb_closure_context_t *c,
-		       unsigned int    lookup_type) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c, unsigned int lookup_type) const
   {
-    TRACE_CLOSURE ();
+    TRACE_PROCESS (this);
     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.context.closure (c); break;
-    case ChainContext:		u.chainContext.closure (c); break;
-    case Extension:		u.extension.closure (c); break;
-    case ReverseChainSingle:	u.reverseChainContextSingle.closure (c); break;
-    default:                    break;
-    }
-  }
-
-  inline const Coverage &get_coverage (unsigned int lookup_type) const
-  {
-    switch (lookup_type) {
-    case Single:		return u.single.get_coverage ();
-    case Multiple:		return u.multiple.get_coverage ();
-    case Alternate:		return u.alternate.get_coverage ();
-    case Ligature:		return u.ligature.get_coverage ();
-    case Context:		return u.context.get_coverage ();
-    case ChainContext:		return u.chainContext.get_coverage ();
-    case Extension:		return u.extension.get_coverage ();
-    case ReverseChainSingle:	return u.reverseChainContextSingle.get_coverage ();
-    default:			return Null(Coverage);
-    }
-  }
-
-  inline bool would_apply (hb_would_apply_context_t *c,
-			   unsigned int lookup_type) const
-  {
-    TRACE_WOULD_APPLY ();
-    if (get_coverage (lookup_type).get_coverage (c->glyphs[0]) == NOT_COVERED) return false;
-    if (c->len == 1) {
-      switch (lookup_type) {
-      case Single:
-      case Multiple:
-      case Alternate:
-      case ReverseChainSingle:
-        return true;
-      }
-    }
-
-    /* Only need to look further for lookups that support substitutions
-     * of input longer than 1. */
-    switch (lookup_type) {
-    case Ligature:		return u.ligature.would_apply (c);
-    case Context:		return u.context.would_apply (c);
-    case ChainContext:		return u.chainContext.would_apply (c);
-    case Extension:		return u.extension.would_apply (c);
-    default:			return false;
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
-  {
-    TRACE_APPLY ();
-    switch (lookup_type) {
-    case Single:		return TRACE_RETURN (u.single.apply (c));
-    case Multiple:		return TRACE_RETURN (u.multiple.apply (c));
-    case Alternate:		return TRACE_RETURN (u.alternate.apply (c));
-    case Ligature:		return TRACE_RETURN (u.ligature.apply (c));
-    case Context:		return TRACE_RETURN (u.context.apply (c));
-    case ChainContext:		return TRACE_RETURN (u.chainContext.apply (c));
-    case Extension:		return TRACE_RETURN (u.extension.apply (c));
-    case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.apply (c));
-    default:			return TRACE_RETURN (false);
+    case Single:		return TRACE_RETURN (u.single.process (c));
+    case Multiple:		return TRACE_RETURN (u.multiple.process (c));
+    case Alternate:		return TRACE_RETURN (u.alternate.process (c));
+    case Ligature:		return TRACE_RETURN (u.ligature.process (c));
+    case Context:		return TRACE_RETURN (u.context.process (c));
+    case ChainContext:		return TRACE_RETURN (u.chainContext.process (c));
+    case Extension:		return TRACE_RETURN (u.extension.process (c));
+    case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.process (c));
+    default:			return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.header.sub_format.sanitize (c))
       return TRACE_RETURN (false);
     switch (lookup_type) {
@@ -1264,55 +1137,68 @@
     return lookup_type_is_reverse (type);
   }
 
-  inline void closure (hb_closure_context_t *c) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
+    TRACE_PROCESS (this);
     unsigned int lookup_type = get_type ();
     unsigned int count = get_subtable_count ();
-    for (unsigned int i = 0; i < count; i++)
-      get_subtable (i).closure (c, lookup_type);
+    for (unsigned int i = 0; i < count; i++) {
+      typename context_t::return_t r = get_subtable (i).process (c, lookup_type);
+      if (c->stop_sublookup_iteration (r))
+        return TRACE_RETURN (r);
+    }
+    return TRACE_RETURN (c->default_return_value ());
+  }
+  template <typename context_t>
+  static inline typename context_t::return_t process_recurse_func (context_t *c, unsigned int lookup_index);
+
+  inline hb_closure_context_t::return_t closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE (this);
+    c->set_recurse_func (process_recurse_func<hb_closure_context_t>);
+    return TRACE_RETURN (process (c));
+  }
+
+  inline hb_collect_glyphs_context_t::return_t collect_glyphs_lookup (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    c->set_recurse_func (process_recurse_func<hb_collect_glyphs_context_t>);
+    return TRACE_RETURN (process (c));
   }
 
   template <typename set_t>
   inline void add_coverage (set_t *glyphs) const
   {
+    hb_get_coverage_context_t c;
     const Coverage *last = NULL;
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++) {
-      const Coverage *c = &get_subtable (i).get_coverage (get_type ());
-      if (c != last) {
-        c->add_coverage (glyphs);
-        last = c;
+      const Coverage *coverage = &get_subtable (i).process (&c, get_type ());
+      if (coverage != last) {
+        coverage->add_coverage (glyphs);
+        last = coverage;
       }
     }
   }
 
   inline bool would_apply (hb_would_apply_context_t *c, const hb_set_digest_t *digest) const
   {
-    if (unlikely (!c->len)) return false;
-    if (!digest->may_have (c->glyphs[0])) return false;
-    unsigned int lookup_type = get_type ();
-    unsigned int count = get_subtable_count ();
-    for (unsigned int i = 0; i < count; i++)
-      if (get_subtable (i).would_apply (c, lookup_type))
-	return true;
-    return false;
+    TRACE_WOULD_APPLY (this);
+    if (unlikely (!c->len))  return TRACE_RETURN (false);
+    if (!digest->may_have (c->glyphs[0]))  return TRACE_RETURN (false);
+      return TRACE_RETURN (process (c));
   }
 
   inline bool apply_once (hb_apply_context_t *c) const
   {
-    unsigned int lookup_type = get_type ();
-
+    TRACE_APPLY (this);
     if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props, &c->property))
-      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;
+      return TRACE_RETURN (false);
+    return TRACE_RETURN (process (c));
   }
 
+  static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index);
   inline bool apply_string (hb_apply_context_t *c, const hb_set_digest_t *digest) const
   {
     bool ret = false;
@@ -1320,6 +1206,7 @@
     if (unlikely (!c->buffer->len || !c->lookup_mask))
       return false;
 
+    c->set_recurse_func (apply_recurse_func);
     c->set_lookup (*this);
 
     if (likely (!is_reverse ()))
@@ -1361,11 +1248,9 @@
     return ret;
   }
 
-  private:
   inline SubstLookupSubTable& serialize_subtable (hb_serialize_context_t *c,
 						  unsigned int i)
   { return CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i].serialize (c, this); }
-  public:
 
   inline bool serialize_single (hb_serialize_context_t *c,
 				uint32_t lookup_props,
@@ -1373,7 +1258,7 @@
 			        Supplier<GlyphID> &substitutes,
 			        unsigned int num_glyphs)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Single, lookup_props, 1))) return TRACE_RETURN (false);
     return TRACE_RETURN (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes, num_glyphs));
   }
@@ -1385,7 +1270,7 @@
 				  unsigned int num_glyphs,
 				  Supplier<GlyphID> &substitute_glyphs_list)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Multiple, lookup_props, 1))) return TRACE_RETURN (false);
     return TRACE_RETURN (serialize_subtable (c, 0).u.multiple.serialize (c, glyphs, substitute_len_list, num_glyphs,
 									 substitute_glyphs_list));
@@ -1398,7 +1283,7 @@
 				   unsigned int num_glyphs,
 				   Supplier<GlyphID> &alternate_glyphs_list)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Alternate, lookup_props, 1))) return TRACE_RETURN (false);
     return TRACE_RETURN (serialize_subtable (c, 0).u.alternate.serialize (c, glyphs, alternate_len_list, num_glyphs,
 									  alternate_glyphs_list));
@@ -1413,7 +1298,7 @@
 				  Supplier<unsigned int> &component_count_list,
 				  Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
   {
-    TRACE_SERIALIZE ();
+    TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Ligature, lookup_props, 1))) return TRACE_RETURN (false);
     return TRACE_RETURN (serialize_subtable (c, 0).u.ligature.serialize (c, first_glyphs, ligature_per_first_glyph_count_list, num_first_glyphs,
 									 ligatures_list, component_count_list, component_list));
@@ -1421,7 +1306,7 @@
 
   inline bool sanitize (hb_sanitize_context_t *c)
   {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
     OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
     if (unlikely (!list.sanitize (c, this, get_type ()))) return TRACE_RETURN (false);
@@ -1456,19 +1341,11 @@
   inline const SubstLookup& get_lookup (unsigned int i) const
   { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
 
-  template <typename set_t>
-  inline void add_coverage (set_t *glyphs, unsigned int lookup_index) const
-  { get_lookup (lookup_index).add_coverage (glyphs); }
-
   static inline void substitute_start (hb_font_t *font, hb_buffer_t *buffer);
   static inline void substitute_finish (hb_font_t *font, hb_buffer_t *buffer);
 
-  inline void closure_lookup (hb_closure_context_t *c,
-			      unsigned int          lookup_index) const
-  { return get_lookup (lookup_index).closure (c); }
-
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
     OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
     return TRACE_RETURN (list.sanitize (c, this));
@@ -1501,73 +1378,37 @@
 
 /* Out-of-class implementation for methods recursing */
 
-inline void ExtensionSubst::closure (hb_closure_context_t *c) const
-{
-  get_subtable ().closure (c, get_type ());
-}
-
-inline const Coverage & ExtensionSubst::get_coverage (void) const
-{
-  return get_subtable ().get_coverage (get_type ());
-}
-
-inline bool ExtensionSubst::would_apply (hb_would_apply_context_t *c) const
-{
-  return get_subtable ().would_apply (c, get_type ());
-}
-
-inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
-{
-  TRACE_APPLY ();
-  return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
-}
-
-inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
-{
-  TRACE_SANITIZE ();
-  if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
-  unsigned int offset = get_offset ();
-  if (unlikely (!offset)) return TRACE_RETURN (true);
-  return TRACE_RETURN (StructAtOffset<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 CastR<ExtensionSubst> (get_subtable<SubstLookupSubTable>()).is_reverse ();
   return SubstLookup::lookup_type_is_reverse (type);
 }
 
-static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index)
+template <typename context_t>
+inline typename context_t::return_t SubstLookup::process_recurse_func (context_t *c, unsigned int lookup_index)
 {
   const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
   const SubstLookup &l = gsub.get_lookup (lookup_index);
-
-  if (unlikely (c->nesting_level_left == 0))
-    return;
-
-  c->nesting_level_left--;
-  l.closure (c);
-  c->nesting_level_left++;
+  return l.process (c);
 }
 
-static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
+inline bool SubstLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index)
 {
   const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
   const SubstLookup &l = gsub.get_lookup (lookup_index);
-
-  if (unlikely (c->nesting_level_left == 0))
-    return false;
-
-  hb_apply_context_t new_c (*c);
-  new_c.nesting_level_left--;
-  new_c.set_lookup (l);
-  return l.apply_once (&new_c);
+  unsigned int saved_lookup_props = c->lookup_props;
+  unsigned int saved_property = c->property;
+  c->set_lookup (l);
+  bool ret = l.apply_once (c);
+  c->lookup_props = saved_lookup_props;
+  c->property = saved_property;
+  return ret;
 }
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index dd7bdd3..87abd3b 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -37,44 +37,82 @@
 namespace OT {
 
 
+
+#define TRACE_PROCESS(this) \
+	hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 "");
+
+
 #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, "");
-
+#define TRACE_CLOSURE(this) \
+	hb_auto_trace_t<HB_DEBUG_CLOSURE, hb_void_t> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 "");
 
 struct hb_closure_context_t
 {
+  inline const char *get_name (void) { return "CLOSURE"; }
+  static const unsigned int max_debug_depth = HB_DEBUG_CLOSURE;
+  typedef hb_void_t return_t;
+  typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
+  template <typename T>
+  inline return_t process (const T &obj) { obj.closure (this); return HB_VOID; }
+  static return_t default_return_value (void) { return HB_VOID; }
+  bool stop_sublookup_iteration (const return_t r HB_UNUSED) const { return false; }
+  return_t recurse (unsigned int lookup_index)
+  {
+    if (unlikely (nesting_level_left == 0 || !recurse_func))
+      return default_return_value ();
+
+    nesting_level_left--;
+    recurse_func (this, lookup_index);
+    nesting_level_left++;
+    return HB_VOID;
+  }
+
   hb_face_t *face;
   hb_set_t *glyphs;
+  recurse_func_t recurse_func;
   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_),
+			  recurse_func (NULL),
 			  nesting_level_left (nesting_level_left_),
 			  debug_depth (0) {}
+
+  void set_recurse_func (recurse_func_t func) { recurse_func = func; }
 };
 
 
 
-/* TODO Add TRACE_RETURN annotation to gsub. */
 #ifndef HB_DEBUG_WOULD_APPLY
 #define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
 #endif
 
-#define TRACE_WOULD_APPLY() \
-	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY> trace (&c->debug_depth, "WOULD_APPLY", this, HB_FUNC, "%d glyphs", c->len);
-
+#define TRACE_WOULD_APPLY(this) \
+	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY, bool> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 "%d glyphs", c->len);
 
 struct hb_would_apply_context_t
 {
+  inline const char *get_name (void) { return "WOULD_APPLY"; }
+  static const unsigned int max_debug_depth = HB_DEBUG_WOULD_APPLY;
+  typedef bool return_t;
+  template <typename T>
+  inline return_t process (const T &obj) { return obj.would_apply (this); }
+  static return_t default_return_value (void) { return false; }
+  bool stop_sublookup_iteration (const return_t r) const { return r; }
+
   hb_face_t *face;
   const hb_codepoint_t *glyphs;
   unsigned int len;
@@ -89,31 +127,146 @@
 			      glyphs (glyphs_),
 			      len (len_),
 			      zero_context (zero_context_),
-			      debug_depth (0) {};
+			      debug_depth (0) {}
 };
 
 
+
+#ifndef HB_DEBUG_COLLECT_GLYPHS
+#define HB_DEBUG_COLLECT_GLYPHS (HB_DEBUG+0)
+#endif
+
+#define TRACE_COLLECT_GLYPHS(this) \
+	hb_auto_trace_t<HB_DEBUG_COLLECT_GLYPHS, hb_void_t> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 "");
+
+struct hb_collect_glyphs_context_t
+{
+  inline const char *get_name (void) { return "COLLECT_GLYPHS"; }
+  static const unsigned int max_debug_depth = HB_DEBUG_COLLECT_GLYPHS;
+  typedef hb_void_t return_t;
+  typedef return_t (*recurse_func_t) (hb_collect_glyphs_context_t *c, unsigned int lookup_index);
+  template <typename T>
+  inline return_t process (const T &obj) { obj.collect_glyphs (this); return HB_VOID; }
+  static return_t default_return_value (void) { return HB_VOID; }
+  bool stop_sublookup_iteration (const return_t r HB_UNUSED) const { return false; }
+  return_t recurse (unsigned int lookup_index)
+  {
+    if (unlikely (nesting_level_left == 0 || !recurse_func))
+      return default_return_value ();
+
+    /* Note that GPOS sets recurse_func to NULL already, so it doesn't get
+     * past the previous check.  For GSUB, we only want to collect the output
+     * glyphs in the recursion.  If output is not requested, we can go home now. */
+
+    if (output == hb_set_get_empty ())
+      return HB_VOID;
+
+    hb_set_t *old_before = before;
+    hb_set_t *old_input  = input;
+    hb_set_t *old_after  = after;
+    before = input = after = hb_set_get_empty ();
+
+    nesting_level_left--;
+    recurse_func (this, lookup_index);
+    nesting_level_left++;
+
+    before = old_before;
+    input  = old_input;
+    after  = old_after;
+
+    return HB_VOID;
+  }
+
+  hb_face_t *face;
+  hb_set_t *before;
+  hb_set_t *input;
+  hb_set_t *after;
+  hb_set_t *output;
+  recurse_func_t recurse_func;
+  unsigned int nesting_level_left;
+  unsigned int debug_depth;
+
+  hb_collect_glyphs_context_t (hb_face_t *face_,
+			       hb_set_t  *glyphs_before, /* OUT. May be NULL */
+			       hb_set_t  *glyphs_input,  /* OUT. May be NULL */
+			       hb_set_t  *glyphs_after,  /* OUT. May be NULL */
+			       hb_set_t  *glyphs_output, /* OUT. May be NULL */
+			       unsigned int nesting_level_left_ = MAX_NESTING_LEVEL) :
+			      face (face_),
+			      before (glyphs_before ? glyphs_before : hb_set_get_empty ()),
+			      input  (glyphs_input  ? glyphs_input  : hb_set_get_empty ()),
+			      after  (glyphs_after  ? glyphs_after  : hb_set_get_empty ()),
+			      output (glyphs_output ? glyphs_output : hb_set_get_empty ()),
+			      recurse_func (NULL),
+			      nesting_level_left (nesting_level_left_),
+			      debug_depth (0) {}
+
+  void set_recurse_func (recurse_func_t func) { recurse_func = func; }
+};
+
+
+
+struct hb_get_coverage_context_t
+{
+  inline const char *get_name (void) { return "GET_COVERAGE"; }
+  static const unsigned int max_debug_depth = 0;
+  typedef const Coverage &return_t;
+  template <typename T>
+  inline return_t process (const T &obj) { return obj.get_coverage (); }
+  static return_t default_return_value (void) { return Null(Coverage); }
+
+  hb_get_coverage_context_t (void) :
+			    debug_depth (0) {}
+
+  unsigned int debug_depth;
+};
+
+
+
 #ifndef HB_DEBUG_APPLY
 #define HB_DEBUG_APPLY (HB_DEBUG+0)
 #endif
 
-#define TRACE_APPLY() \
-	hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint);
-
+#define TRACE_APPLY(this) \
+	hb_auto_trace_t<HB_DEBUG_APPLY, bool> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint);
 
 struct hb_apply_context_t
 {
+  inline const char *get_name (void) { return "APPLY"; }
+  static const unsigned int max_debug_depth = HB_DEBUG_APPLY;
+  typedef bool return_t;
+  typedef return_t (*recurse_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
+  template <typename T>
+  inline return_t process (const T &obj) { return obj.apply (this); }
+  static return_t default_return_value (void) { return false; }
+  bool stop_sublookup_iteration (const return_t r) const { return r; }
+  return_t recurse (unsigned int lookup_index)
+  {
+    if (unlikely (nesting_level_left == 0 || !recurse_func))
+      return default_return_value ();
+
+    nesting_level_left--;
+    bool ret = recurse_func (this, lookup_index);
+    nesting_level_left++;
+    return ret;
+  }
+
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t *buffer;
   hb_direction_t direction;
   hb_mask_t lookup_mask;
+  recurse_func_t recurse_func;
   unsigned int nesting_level_left;
   unsigned int lookup_props;
   unsigned int property; /* propety of first glyph */
-  unsigned int debug_depth;
   const GDEF &gdef;
   bool has_glyph_classes;
+  unsigned int debug_depth;
 
 
   hb_apply_context_t (hb_font_t *font_,
@@ -122,18 +275,16 @@
 			font (font_), face (font->face), buffer (buffer_),
 			direction (buffer_->props.direction),
 			lookup_mask (lookup_mask_),
+			recurse_func (NULL),
 			nesting_level_left (MAX_NESTING_LEVEL),
-			lookup_props (0), property (0), debug_depth (0),
+			lookup_props (0), property (0),
 			gdef (*hb_ot_layout_from_face (face)->gdef),
-			has_glyph_classes (gdef.has_glyph_classes ()) {}
+			has_glyph_classes (gdef.has_glyph_classes ()),
+			debug_depth (0) {}
 
-  void set_lookup_props (unsigned int lookup_props_) {
-    lookup_props = lookup_props_;
-  }
-
-  void set_lookup (const Lookup &l) {
-    lookup_props = l.get_props ();
-  }
+  void set_recurse_func (recurse_func_t func) { recurse_func = func; }
+  void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; }
+  void set_lookup (const Lookup &l) { lookup_props = l.get_props (); }
 
   struct mark_skipping_forward_iterator_t
   {
@@ -264,7 +415,7 @@
     if (glyph_props & lookup_props & LookupFlag::IgnoreFlags)
       return false;
 
-    if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
+    if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK))
       return match_properties_mark (glyph, glyph_props, lookup_props);
 
     return true;
@@ -295,7 +446,7 @@
       *property_out = property;
 
     /* If it's a mark, skip it if we don't accept it. */
-    if (unlikely (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
+    if (unlikely (property & HB_OT_LAYOUT_GLYPH_PROPS_MARK))
       return !match_properties (info->codepoint, property, lookup_props);
 
     /* If not a mark, don't skip. */
@@ -339,21 +490,23 @@
 
 
 typedef bool (*intersects_func_t) (hb_set_t *glyphs, const USHORT &value, const void *data);
+typedef void (*collect_glyphs_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 ContextClosureFuncs
 {
   intersects_func_t intersects;
-  closure_lookup_func_t closure;
+};
+struct ContextCollectGlyphsFuncs
+{
+  collect_glyphs_func_t collect;
 };
 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);
@@ -382,6 +535,32 @@
 }
 
 
+static inline void collect_glyph (hb_set_t *glyphs, const USHORT &value, const void *data HB_UNUSED)
+{
+  glyphs->add (value);
+}
+static inline void collect_class (hb_set_t *glyphs, const USHORT &value, const void *data)
+{
+  const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
+  class_def.add_class (glyphs, value);
+}
+static inline void collect_coverage (hb_set_t *glyphs, const USHORT &value, const void *data)
+{
+  const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
+  (data+coverage).add_coverage (glyphs);
+}
+static inline void collect_array (hb_collect_glyphs_context_t *c HB_UNUSED,
+				  hb_set_t *glyphs,
+				  unsigned int count,
+				  const USHORT values[],
+				  collect_glyphs_func_t collect_func,
+				  const void *collect_data)
+{
+  for (unsigned int i = 0; i < count; i++)
+    collect_func (glyphs, values[i], collect_data);
+}
+
+
 static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
 {
   return glyph_id == value;
@@ -397,7 +576,6 @@
   return (data+coverage).get_coverage (glyph_id) != NOT_COVERED;
 }
 
-
 static inline bool would_match_input (hb_would_apply_context_t *c,
 				      unsigned int count, /* Including the first glyph (not matched) */
 				      const USHORT input[], /* Array of input values--start with second glyph */
@@ -422,7 +600,7 @@
 				bool *p_is_mark_ligature = NULL,
 				unsigned int *p_total_component_count = NULL)
 {
-  hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", NULL, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint);
+  TRACE_APPLY (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 TRACE_RETURN (false);
@@ -445,7 +623,7 @@
    *   ligate with a conjunct...)
    */
 
-  bool is_mark_ligature = !!(c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+  bool is_mark_ligature = !!(c->property & HB_OT_LAYOUT_GLYPH_PROPS_MARK);
 
   unsigned int total_component_count = 0;
   total_component_count += get_lig_num_comps (c->buffer->cur());
@@ -478,7 +656,7 @@
 	return TRACE_RETURN (false);
     }
 
-    is_mark_ligature = is_mark_ligature && (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+    is_mark_ligature = is_mark_ligature && (property & HB_OT_LAYOUT_GLYPH_PROPS_MARK);
     total_component_count += get_lig_num_comps (c->buffer->info[skippy_iter.idx]);
   }
 
@@ -495,10 +673,10 @@
 }
 static inline void ligate_input (hb_apply_context_t *c,
 				 unsigned int count, /* Including the first glyph (not matched) */
-				 const USHORT input[], /* Array of input values--start with second glyph */
+				 const USHORT input[] HB_UNUSED, /* Array of input values--start with second glyph */
 				 hb_codepoint_t lig_glyph,
-				 match_func_t match_func,
-				 const void *match_data,
+				 match_func_t match_func HB_UNUSED,
+				 const void *match_data HB_UNUSED,
 				 bool is_mark_ligature,
 				 unsigned int total_component_count)
 {
@@ -530,7 +708,7 @@
    *   https://bugzilla.gnome.org/show_bug.cgi?id=437633
    */
 
-  unsigned int klass = is_mark_ligature ? 0 : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
+  unsigned int klass = is_mark_ligature ? 0 : HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE;
   unsigned int lig_id = is_mark_ligature ? 0 : allocate_lig_id (c->buffer);
   unsigned int last_lig_id = get_lig_id (c->buffer->cur());
   unsigned int last_num_components = get_lig_num_comps (c->buffer->cur());
@@ -579,7 +757,7 @@
 				    match_func_t match_func,
 				    const void *match_data)
 {
-  hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", NULL, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint);
+  TRACE_APPLY (NULL);
 
   hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count, true);
   if (skippy_iter.has_no_chance ())
@@ -604,7 +782,7 @@
 				    const void *match_data,
 				    unsigned int offset)
 {
-  hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", NULL, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint);
+  TRACE_APPLY (NULL);
 
   hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count, true);
   if (skippy_iter.has_no_chance ())
@@ -627,7 +805,7 @@
 struct LookupRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
@@ -640,22 +818,22 @@
 };
 
 
-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)
+template <typename context_t>
+static inline void recurse_lookups (context_t *c,
+				    unsigned int lookupCount,
+				    const LookupRecord lookupRecord[] /* Array of LookupRecords--in design order */)
 {
   for (unsigned int i = 0; i < lookupCount; i++)
-    closure_func (c, lookupRecord->lookupListIndex);
+    c->recurse (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)
+				 const LookupRecord lookupRecord[] /* Array of LookupRecords--in design order */)
 {
-  hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", NULL, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint);
+  TRACE_APPLY (NULL);
+
   unsigned int end = c->buffer->len;
   if (unlikely (count == 0 || c->buffer->idx + count > end))
     return TRACE_RETURN (false);
@@ -684,7 +862,7 @@
       unsigned int old_pos = c->buffer->idx;
 
       /* Apply a lookup */
-      bool done = apply_func (c, lookupRecord->lookupListIndex);
+      bool done = c->recurse (lookupRecord->lookupListIndex);
 
       lookupRecord++;
       lookupCount--;
@@ -718,6 +896,12 @@
   const void *intersects_data;
 };
 
+struct ContextCollectGlyphsLookupContext
+{
+  ContextCollectGlyphsFuncs funcs;
+  const void *collect_data;
+};
+
 struct ContextApplyLookupContext
 {
   ContextApplyFuncs funcs;
@@ -734,17 +918,29 @@
   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);
+    recurse_lookups (c,
+		     lookupCount, lookupRecord);
 }
 
+static inline void context_collect_glyphs_lookup (hb_collect_glyphs_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[],
+						  ContextCollectGlyphsLookupContext &lookup_context)
+{
+  collect_array (c, c->input,
+		 inputCount ? inputCount - 1 : 0, input,
+		 lookup_context.funcs.collect, lookup_context.collect_data);
+  recurse_lookups (c,
+		   lookupCount, lookupRecord);
+}
 
 static inline bool context_would_apply_lookup (hb_would_apply_context_t *c,
 					       unsigned int inputCount, /* Including the first glyph (not matched) */
 					       const USHORT input[], /* Array of input values--start with second glyph */
-					       unsigned int lookupCount,
-					       const LookupRecord lookupRecord[],
+					       unsigned int lookupCount HB_UNUSED,
+					       const LookupRecord lookupRecord[] HB_UNUSED,
 					       ContextApplyLookupContext &lookup_context)
 {
   return would_match_input (c,
@@ -763,19 +959,14 @@
 		      lookup_context.funcs.match, lookup_context.match_data)
       && apply_lookup (c,
 		       inputCount,
-		       lookupCount, lookupRecord,
-		       lookup_context.funcs.apply);
+		       lookupCount, lookupRecord);
 }
 
 struct Rule
 {
-  friend struct RuleSet;
-
-  private:
-
   inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
     context_closure_lookup (c,
 			    inputCount, input,
@@ -783,23 +974,33 @@
 			    lookup_context);
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
+    context_collect_glyphs_lookup (c,
+				   inputCount, input,
+				   lookupCount, lookupRecord,
+				   lookup_context);
+  }
+
   inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
-    TRACE_WOULD_APPLY ();
+    TRACE_WOULD_APPLY (this);
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
     return TRACE_RETURN (context_would_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
   }
 
   inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
     return TRACE_RETURN (context_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return inputCount.sanitize (c)
 	&& lookupCount.sanitize (c)
 	&& c->check_range (input,
@@ -824,15 +1025,23 @@
 {
   inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       (this+rule[i]).closure (c, lookup_context);
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+      (this+rule[i]).collect_glyphs (c, lookup_context);
+  }
+
   inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
-    TRACE_WOULD_APPLY ();
+    TRACE_WOULD_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
     {
@@ -844,7 +1053,7 @@
 
   inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
     {
@@ -855,7 +1064,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (rule.sanitize (c, this));
   }
 
@@ -870,18 +1079,14 @@
 
 struct ContextFormat1
 {
-  friend struct Context;
-
-  private:
-
-  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
 
     const Coverage &cov = (this+coverage);
 
     struct ContextClosureLookupContext lookup_context = {
-      {intersects_glyph, closure_func},
+      {intersects_glyph},
       NULL
     };
 
@@ -893,35 +1098,55 @@
       }
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+
+    struct ContextCollectGlyphsLookupContext lookup_context = {
+      {collect_glyph},
+      NULL
+    };
+
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      (this+ruleSet[i]).collect_glyphs (c, lookup_context);
+  }
+
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    TRACE_WOULD_APPLY ();
+    TRACE_WOULD_APPLY (this);
 
-    const RuleSet &rule_set = this+ruleSet[(this+coverage) (c->glyphs[0])];
+    const RuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])];
     struct ContextApplyLookupContext lookup_context = {
-      {match_glyph, NULL},
+      {match_glyph},
       NULL
     };
     return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
   }
 
-  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  inline const Coverage &get_coverage (void) const
   {
-    TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    return this+coverage;
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED))
       return TRACE_RETURN (false);
 
     const RuleSet &rule_set = this+ruleSet[index];
     struct ContextApplyLookupContext lookup_context = {
-      {match_glyph, apply_func},
+      {match_glyph},
       NULL
     };
     return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
@@ -940,20 +1165,16 @@
 
 struct ContextFormat2
 {
-  friend struct Context;
-
-  private:
-
-  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     if (!(this+coverage).intersects (c->glyphs))
       return;
 
     const ClassDef &class_def = this+classDef;
 
     struct ContextClosureLookupContext lookup_context = {
-      {intersects_class, closure_func},
+      {intersects_class},
       NULL
     };
 
@@ -965,38 +1186,58 @@
       }
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+
+    struct ContextCollectGlyphsLookupContext lookup_context = {
+      {collect_class},
+      NULL
+    };
+
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      (this+ruleSet[i]).collect_glyphs (c, lookup_context);
+  }
+
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    TRACE_WOULD_APPLY ();
+    TRACE_WOULD_APPLY (this);
 
     const ClassDef &class_def = this+classDef;
-    unsigned int index = class_def (c->glyphs[0]);
+    unsigned int index = class_def.get_class (c->glyphs[0]);
     const RuleSet &rule_set = this+ruleSet[index];
     struct ContextApplyLookupContext lookup_context = {
-      {match_class, NULL},
+      {match_class},
       &class_def
     };
     return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
   }
 
-  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  inline const Coverage &get_coverage (void) const
   {
-    TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    return this+coverage;
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const ClassDef &class_def = this+classDef;
-    index = class_def (c->buffer->cur().codepoint);
+    index = class_def.get_class (c->buffer->cur().codepoint);
     const RuleSet &rule_set = this+ruleSet[index];
     struct ContextApplyLookupContext lookup_context = {
-      {match_class, apply_func},
+      {match_class},
       &class_def
     };
     return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
@@ -1018,19 +1259,15 @@
 
 struct ContextFormat3
 {
-  friend struct Context;
-
-  private:
-
-  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     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},
+      {intersects_coverage},
       this
     };
     context_closure_lookup (c,
@@ -1039,34 +1276,56 @@
 			    lookup_context);
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage[0]).add_coverage (c->input);
+
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
+    struct ContextCollectGlyphsLookupContext lookup_context = {
+      {collect_coverage},
+      this
+    };
+
+    context_collect_glyphs_lookup (c,
+				   glyphCount, (const USHORT *) (coverage + 1),
+				   lookupCount, lookupRecord,
+				   lookup_context);
+  }
+
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    TRACE_WOULD_APPLY ();
+    TRACE_WOULD_APPLY (this);
 
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
     struct ContextApplyLookupContext lookup_context = {
-      {match_coverage, NULL},
+      {match_coverage},
       this
     };
     return TRACE_RETURN (context_would_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context));
   }
 
-  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  inline const Coverage &get_coverage (void) const
   {
-    TRACE_APPLY ();
-    unsigned int index = (this+coverage[0]) (c->buffer->cur().codepoint);
+    return this+coverage[0];
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    unsigned int index = (this+coverage[0]).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
     struct ContextApplyLookupContext lookup_context = {
-      {match_coverage, apply_func},
+      {match_coverage},
       this
     };
     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 ();
+    TRACE_SANITIZE (this);
     if (!c->check_struct (this)) return TRACE_RETURN (false);
     unsigned int count = glyphCount;
     if (!c->check_array (coverage, coverage[0].static_size, count)) return TRACE_RETURN (false);
@@ -1092,52 +1351,20 @@
 
 struct Context
 {
-  protected:
-
-  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: u.format1.closure (c, closure_func); break;
-    case 2: u.format2.closure (c, closure_func); break;
-    case 3: u.format3.closure (c, closure_func); break;
-    default:                                     break;
-    }
-  }
-
-  inline const Coverage &get_coverage (void) const
-  {
-    switch (u.format) {
-    case 1: return this + u.format1.coverage;
-    case 2: return this + u.format2.coverage;
-    case 3: return this + u.format3.coverage[0];
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool would_apply (hb_would_apply_context_t *c) const
-  {
-    switch (u.format) {
-    case 1: return u.format1.would_apply (c);
-    case 2: return u.format2.would_apply (c);
-    case 3: return u.format3.would_apply (c);
-    default:return false;
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c, apply_func));
-    case 2: return TRACE_RETURN (u.format2.apply (c, apply_func));
-    case 3: return TRACE_RETURN (u.format3.apply (c, apply_func));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 2: return TRACE_RETURN (c->process (u.format2));
+    case 3: return TRACE_RETURN (c->process (u.format3));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1165,6 +1392,12 @@
   const void *intersects_data[3];
 };
 
+struct ChainContextCollectGlyphsLookupContext
+{
+  ContextCollectGlyphsFuncs funcs;
+  const void *collect_data[3];
+};
+
 struct ChainContextApplyLookupContext
 {
   ContextApplyFuncs funcs;
@@ -1191,20 +1424,43 @@
   && intersects_array (c,
 		       lookaheadCount, lookahead,
 		       lookup_context.funcs.intersects, lookup_context.intersects_data[2]))
-    closure_lookup (c,
-		    lookupCount, lookupRecord,
-		    lookup_context.funcs.closure);
+    recurse_lookups (c,
+		     lookupCount, lookupRecord);
+}
+
+static inline void chain_context_collect_glyphs_lookup (hb_collect_glyphs_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[],
+						        ChainContextCollectGlyphsLookupContext &lookup_context)
+{
+  collect_array (c, c->before,
+		 backtrackCount, backtrack,
+		 lookup_context.funcs.collect, lookup_context.collect_data[0]);
+  collect_array (c, c->input,
+		 inputCount ? inputCount - 1 : 0, input,
+		 lookup_context.funcs.collect, lookup_context.collect_data[1]);
+  collect_array (c, c->after,
+		 lookaheadCount, lookahead,
+		 lookup_context.funcs.collect, lookup_context.collect_data[2]);
+  recurse_lookups (c,
+		   lookupCount, lookupRecord);
 }
 
 static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c,
 						     unsigned int backtrackCount,
-						     const USHORT backtrack[],
+						     const USHORT backtrack[] HB_UNUSED,
 						     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[],
+						     const USHORT lookahead[] HB_UNUSED,
+						     unsigned int lookupCount HB_UNUSED,
+						     const LookupRecord lookupRecord[] HB_UNUSED,
 						     ChainContextApplyLookupContext &lookup_context)
 {
   return (c->zero_context ? !backtrackCount && !lookaheadCount : true)
@@ -1224,7 +1480,7 @@
 					       const LookupRecord lookupRecord[],
 					       ChainContextApplyLookupContext &lookup_context)
 {
-  unsigned int lookahead_offset;
+  unsigned int lookahead_offset = 0;
   return match_input (c,
 		      inputCount, input,
 		      lookup_context.funcs.match, lookup_context.match_data[1],
@@ -1238,19 +1494,14 @@
 			  lookahead_offset)
       && apply_lookup (c,
 		       inputCount,
-		       lookupCount, lookupRecord,
-		       lookup_context.funcs.apply);
+		       lookupCount, lookupRecord);
 }
 
 struct ChainRule
 {
-  friend struct ChainRuleSet;
-
-  private:
-
   inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
@@ -1262,9 +1513,23 @@
 				  lookup_context);
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    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_collect_glyphs_lookup (c,
+					 backtrack.len, backtrack.array,
+					 input.len, input.array,
+					 lookahead.len, lookahead.array,
+					 lookup.len, lookup.array,
+					 lookup_context);
+  }
+
   inline bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
-    TRACE_WOULD_APPLY ();
+    TRACE_WOULD_APPLY (this);
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
@@ -1277,7 +1542,7 @@
 
   inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
@@ -1288,9 +1553,8 @@
 						     lookup.array, lookup_context));
   }
 
-  public:
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!backtrack.sanitize (c)) return TRACE_RETURN (false);
     HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     if (!input.sanitize (c)) return TRACE_RETURN (false);
@@ -1322,15 +1586,23 @@
 {
   inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       (this+rule[i]).closure (c, lookup_context);
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+      (this+rule[i]).collect_glyphs (c, lookup_context);
+  }
+
   inline bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
-    TRACE_WOULD_APPLY ();
+    TRACE_WOULD_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       if ((this+rule[i]).would_apply (c, lookup_context))
@@ -1341,7 +1613,7 @@
 
   inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
-    TRACE_APPLY ();
+    TRACE_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       if ((this+rule[i]).apply (c, lookup_context))
@@ -1351,7 +1623,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (rule.sanitize (c, this));
   }
 
@@ -1365,17 +1637,13 @@
 
 struct ChainContextFormat1
 {
-  friend struct ChainContext;
-
-  private:
-
-  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     const Coverage &cov = (this+coverage);
 
     struct ChainContextClosureLookupContext lookup_context = {
-      {intersects_glyph, closure_func},
+      {intersects_glyph},
       {NULL, NULL, NULL}
     };
 
@@ -1387,34 +1655,54 @@
       }
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+
+    struct ChainContextCollectGlyphsLookupContext lookup_context = {
+      {collect_glyph},
+      {NULL, NULL, NULL}
+    };
+
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      (this+ruleSet[i]).collect_glyphs (c, lookup_context);
+  }
+
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    TRACE_WOULD_APPLY ();
+    TRACE_WOULD_APPLY (this);
 
-    const ChainRuleSet &rule_set = this+ruleSet[(this+coverage) (c->glyphs[0])];
+    const ChainRuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])];
     struct ChainContextApplyLookupContext lookup_context = {
-      {match_glyph, NULL},
+      {match_glyph},
       {NULL, NULL, NULL}
     };
     return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
   }
 
-  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  inline const Coverage &get_coverage (void) const
   {
-    TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    return this+coverage;
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
-      {match_glyph, apply_func},
+      {match_glyph},
       {NULL, NULL, NULL}
     };
     return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
@@ -1432,13 +1720,9 @@
 
 struct ChainContextFormat2
 {
-  friend struct ChainContext;
-
-  private:
-
-  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     if (!(this+coverage).intersects (c->glyphs))
       return;
 
@@ -1447,7 +1731,7 @@
     const ClassDef &lookahead_class_def = this+lookaheadClassDef;
 
     struct ChainContextClosureLookupContext lookup_context = {
-      {intersects_class, closure_func},
+      {intersects_class},
       {&backtrack_class_def,
        &input_class_def,
        &lookahead_class_def}
@@ -1461,35 +1745,55 @@
       }
   }
 
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  {
+    TRACE_COLLECT_GLYPHS (this);
+    (this+coverage).add_coverage (c->input);
+
+    struct ChainContextCollectGlyphsLookupContext lookup_context = {
+      {collect_class},
+      {NULL, NULL, NULL}
+    };
+
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      (this+ruleSet[i]).collect_glyphs (c, lookup_context);
+  }
+
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    TRACE_WOULD_APPLY ();
+    TRACE_WOULD_APPLY (this);
 
     const ClassDef &input_class_def = this+inputClassDef;
 
-    unsigned int index = input_class_def (c->glyphs[0]);
+    unsigned int index = input_class_def.get_class (c->glyphs[0]);
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
-      {match_class, NULL},
+      {match_class},
       {NULL, &input_class_def, NULL}
     };
     return TRACE_RETURN (rule_set.would_apply (c, lookup_context));
   }
 
-  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  inline const Coverage &get_coverage (void) const
   {
-    TRACE_APPLY ();
-    unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
+    return this+coverage;
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    unsigned int index = (this+coverage).get_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);
+    index = input_class_def.get_class (c->buffer->cur().codepoint);
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
-      {match_class, apply_func},
+      {match_class},
       {&backtrack_class_def,
        &input_class_def,
        &lookahead_class_def}
@@ -1498,7 +1802,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && backtrackClassDef.sanitize (c, this) &&
 			 inputClassDef.sanitize (c, this) && lookaheadClassDef.sanitize (c, this) &&
 			 ruleSet.sanitize (c, this));
@@ -1530,13 +1834,9 @@
 
 struct ChainContextFormat3
 {
-  friend struct ChainContext;
-
-  private:
-
-  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  inline void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_CLOSURE (this);
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
 
     if (!(this+input[0]).intersects (c->glyphs))
@@ -1545,7 +1845,7 @@
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     struct ChainContextClosureLookupContext lookup_context = {
-      {intersects_coverage, closure_func},
+      {intersects_coverage},
       {this, this, this}
     };
     chain_context_closure_lookup (c,
@@ -1556,21 +1856,36 @@
 				  lookup_context);
   }
 
-  inline const Coverage &get_coverage (void) const
+  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
+    TRACE_COLLECT_GLYPHS (this);
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
-    return this+input[0];
+
+    (this+input[0]).add_coverage (c->input);
+
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    struct ChainContextCollectGlyphsLookupContext lookup_context = {
+      {collect_coverage},
+      {this, this, this}
+    };
+    chain_context_collect_glyphs_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 would_apply (hb_would_apply_context_t *c) const
   {
-    TRACE_WOULD_APPLY ();
+    TRACE_WOULD_APPLY (this);
 
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     struct ChainContextApplyLookupContext lookup_context = {
-      {match_coverage, NULL},
+      {match_coverage},
       {this, this, this}
     };
     return TRACE_RETURN (chain_context_would_apply_lookup (c,
@@ -1580,18 +1895,24 @@
 							   lookup.len, lookup.array, lookup_context));
   }
 
-  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
+  inline const Coverage &get_coverage (void) const
   {
-    TRACE_APPLY ();
+    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+    return this+input[0];
+  }
+
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
 
-    unsigned int index = (this+input[0]) (c->buffer->cur().codepoint);
+    unsigned int index = (this+input[0]).get_coverage (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 ChainContextApplyLookupContext lookup_context = {
-      {match_coverage, apply_func},
+      {match_coverage},
       {this, this, this}
     };
     return TRACE_RETURN (chain_context_apply_lookup (c,
@@ -1602,7 +1923,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     if (!input.sanitize (c, this)) return TRACE_RETURN (false);
@@ -1635,52 +1956,20 @@
 
 struct ChainContext
 {
-  protected:
-
-  inline void closure (hb_closure_context_t *c, closure_lookup_func_t closure_func) const
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
   {
-    TRACE_CLOSURE ();
+    TRACE_PROCESS (this);
     switch (u.format) {
-    case 1: u.format1.closure (c, closure_func); break;
-    case 2: u.format2.closure (c, closure_func); break;
-    case 3: u.format3.closure (c, closure_func); break;
-    default:                                     break;
-    }
-  }
-
-  inline const Coverage &get_coverage (void) const
-  {
-    switch (u.format) {
-    case 1: return this + u.format1.coverage;
-    case 2: return this + u.format2.coverage;
-    case 3: return u.format3.get_coverage ();
-    default:return Null(Coverage);
-    }
-  }
-
-  inline bool would_apply (hb_would_apply_context_t *c) const
-  {
-    switch (u.format) {
-    case 1: return u.format1.would_apply (c);
-    case 2: return u.format2.would_apply (c);
-    case 3: return u.format3.would_apply (c);
-    default:return false;
-    }
-  }
-
-  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
-  {
-    TRACE_APPLY ();
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.apply (c, apply_func));
-    case 2: return TRACE_RETURN (u.format2.apply (c, apply_func));
-    case 3: return TRACE_RETURN (u.format3.apply (c, apply_func));
-    default:return TRACE_RETURN (false);
+    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 2: return TRACE_RETURN (c->process (u.format2));
+    case 3: return TRACE_RETURN (c->process (u.format3));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1702,14 +1991,11 @@
 
 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 ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
 
@@ -1724,6 +2010,7 @@
   DEFINE_SIZE_STATIC (8);
 };
 
+template <typename T>
 struct Extension
 {
   inline unsigned int get_type (void) const
@@ -1741,8 +2028,22 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+  template <typename X>
+  inline const X& get_subtable (void) const
+  {
+    unsigned int offset = get_offset ();
+    if (unlikely (!offset)) return Null(typename T::LookupSubTable);
+    return StructAtOffset<typename T::LookupSubTable> (this, offset);
+  }
+
+  template <typename context_t>
+  inline typename context_t::return_t process (context_t *c) const
+  {
+    return get_subtable<typename T::LookupSubTable> ().process (c, get_type ());
+  }
+
+  inline bool sanitize_self (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
@@ -1750,6 +2051,14 @@
     }
   }
 
+  inline bool sanitize (hb_sanitize_context_t *c) {
+    TRACE_SANITIZE (this);
+    if (!sanitize_self (c)) return TRACE_RETURN (false);
+    unsigned int offset = get_offset ();
+    if (unlikely (!offset)) return TRACE_RETURN (true);
+    return TRACE_RETURN (StructAtOffset<typename T::LookupSubTable> (this, offset).sanitize (c, get_type ()));
+  }
+
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -1799,7 +2108,7 @@
   { return (this+lookupList)[i]; }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (version.sanitize (c) && likely (version.major == 1) &&
 			 scriptList.sanitize (c, this) &&
 			 featureList.sanitize (c, this) &&
@@ -1820,7 +2129,7 @@
 };
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index 70fda8e..49093de 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -50,12 +50,12 @@
  */
 
 typedef enum {
-  HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED	= 0x0001,
-  HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH	= 0x0002,
-  HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE	= 0x0004,
-  HB_OT_LAYOUT_GLYPH_CLASS_MARK		= 0x0008,
-  HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT	= 0x0010
-} hb_ot_layout_glyph_class_t;
+  HB_OT_LAYOUT_GLYPH_PROPS_UNCLASSIFIED	= 0x0001,
+  HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH	= 0x0002,
+  HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE	= 0x0004,
+  HB_OT_LAYOUT_GLYPH_PROPS_MARK		= 0x0008,
+  HB_OT_LAYOUT_GLYPH_PROPS_COMPONENT	= 0x0010
+} hb_ot_layout_glyph_class_mask_t;
 
 
 
@@ -123,7 +123,7 @@
 static inline unsigned int
 get_lig_num_comps (const hb_glyph_info_t &info)
 {
-  if ((info.glyph_props() & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE) && is_a_ligature (info))
+  if ((info.glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE) && is_a_ligature (info))
     return info.lig_props() & 0x0F;
   else
     return 1;
@@ -138,7 +138,7 @@
 
 
 HB_INTERNAL hb_bool_t
-hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
+hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
 					   unsigned int          lookup_index,
 					   const hb_codepoint_t *glyphs,
 					   unsigned int          glyphs_length,
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index e241e33..e4bac0a 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -33,7 +33,6 @@
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
-#include "hb-ot-maxp-table.hh"
 
 #include <stdlib.h>
 #include <string.h>
@@ -71,9 +70,9 @@
   }
 
   for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
-    layout->gsub->add_coverage (&layout->gsub_digests[i], i);
+    layout->gsub->get_lookup (i).add_coverage (&layout->gsub_digests[i]);
   for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
-    layout->gpos->add_coverage (&layout->gpos_digests[i], i);
+    layout->gpos->get_lookup (i).add_coverage (&layout->gpos_digests[i]);
 
   return layout;
 }
@@ -121,6 +120,20 @@
   return _get_gdef (face).has_glyph_classes ();
 }
 
+hb_ot_layout_glyph_class_t
+hb_ot_layout_get_glyph_class (hb_face_t      *face,
+			      hb_codepoint_t  glyph)
+{
+  return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
+}
+
+void
+hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
+				  hb_ot_layout_glyph_class_t  klass,
+				  hb_set_t                   *glyphs /* OUT */)
+{
+  return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
+}
 
 unsigned int
 hb_ot_layout_get_attach_points (hb_face_t      *face,
@@ -374,12 +387,12 @@
 }
 
 unsigned int
-hb_ot_layout_feature_get_lookup_indexes (hb_face_t    *face,
-					 hb_tag_t      table_tag,
-					 unsigned int  feature_index,
-					 unsigned int  start_offset,
-					 unsigned int *lookup_count /* IN/OUT */,
-					 unsigned int *lookup_indexes /* OUT */)
+hb_ot_layout_feature_get_lookups (hb_face_t    *face,
+				  hb_tag_t      table_tag,
+				  unsigned int  feature_index,
+				  unsigned int  start_offset,
+				  unsigned int *lookup_count /* IN/OUT */,
+				  unsigned int *lookup_indexes /* OUT */)
 {
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   const OT::Feature &f = g.get_feature (feature_index);
@@ -387,6 +400,132 @@
   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
 }
 
+static void
+_hb_ot_layout_collect_lookups_lookups (hb_face_t      *face,
+				       hb_tag_t        table_tag,
+				       unsigned int    feature_index,
+				       hb_set_t       *lookup_indexes /* OUT */)
+{
+  unsigned int lookup_indices[32];
+  unsigned int offset, len;
+
+  offset = 0;
+  do {
+    len = ARRAY_LENGTH (lookup_indices);
+    hb_ot_layout_feature_get_lookups (face,
+				      table_tag,
+				      feature_index,
+				      offset, &len,
+				      lookup_indices);
+
+    for (unsigned int i = 0; i < len; i++)
+      lookup_indexes->add (lookup_indices[i]);
+
+    offset += len;
+  } while (len == ARRAY_LENGTH (lookup_indices));
+}
+
+static void
+_hb_ot_layout_collect_lookups_features (hb_face_t      *face,
+					hb_tag_t        table_tag,
+					unsigned int    script_index,
+					unsigned int    language_index,
+					const hb_tag_t *features,
+					hb_set_t       *lookup_indexes /* OUT */)
+{
+  if (!features)
+  {
+    /* All features */
+    unsigned int count = hb_ot_layout_language_get_feature_tags (face, table_tag, script_index, language_index, 0, NULL, NULL);
+    for (unsigned int feature_index = 0; feature_index < count; feature_index++)
+      _hb_ot_layout_collect_lookups_lookups (face, table_tag, feature_index, lookup_indexes);
+  } else {
+    for (; *features; features++)
+    {
+      unsigned int feature_index;
+      if (hb_ot_layout_language_find_feature (face, table_tag, script_index, language_index, *features, &feature_index))
+        _hb_ot_layout_collect_lookups_lookups (face, table_tag, feature_index, lookup_indexes);
+    }
+  }
+}
+
+static void
+_hb_ot_layout_collect_lookups_languages (hb_face_t      *face,
+					 hb_tag_t        table_tag,
+					 unsigned int    script_index,
+					 const hb_tag_t *languages,
+					 const hb_tag_t *features,
+					 hb_set_t       *lookup_indexes /* OUT */)
+{
+  if (!languages)
+  {
+    /* All languages */
+    unsigned int count = hb_ot_layout_script_get_language_tags (face, table_tag, script_index, 0, NULL, NULL);
+    for (unsigned int language_index = 0; language_index < count; language_index++)
+      _hb_ot_layout_collect_lookups_features (face, table_tag, script_index, language_index, features, lookup_indexes);
+  } else {
+    for (; *languages; languages++)
+    {
+      unsigned int language_index;
+      if (hb_ot_layout_script_find_language (face, table_tag, script_index, *languages, &language_index))
+        _hb_ot_layout_collect_lookups_features (face, table_tag, script_index, language_index, features, lookup_indexes);
+    }
+  }
+}
+
+void
+hb_ot_layout_collect_lookups (hb_face_t      *face,
+			      hb_tag_t        table_tag,
+			      const hb_tag_t *scripts,
+			      const hb_tag_t *languages,
+			      const hb_tag_t *features,
+			      hb_set_t       *lookup_indexes /* OUT */)
+{
+  if (!scripts)
+  {
+    /* All scripts */
+    unsigned int count = hb_ot_layout_table_get_script_tags (face, table_tag, 0, NULL, NULL);
+    for (unsigned int script_index = 0; script_index < count; script_index++)
+      _hb_ot_layout_collect_lookups_languages (face, table_tag, script_index, languages, features, lookup_indexes);
+  } else {
+    for (; *scripts; scripts++)
+    {
+      unsigned int script_index;
+      if (hb_ot_layout_table_find_script (face, table_tag, *scripts, &script_index))
+        _hb_ot_layout_collect_lookups_languages (face, table_tag, script_index, languages, features, lookup_indexes);
+    }
+  }
+}
+
+void
+hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
+				    hb_tag_t      table_tag,
+				    unsigned int  lookup_index,
+				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
+				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
+				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
+				    hb_set_t     *glyphs_output  /* OUT. May be NULL */)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
+
+  OT::hb_collect_glyphs_context_t c (face, glyphs_before, glyphs_input, glyphs_after, glyphs_output);
+
+  switch (table_tag) {
+    case HB_OT_TAG_GSUB:
+    {
+      const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
+      l.collect_glyphs_lookup (&c);
+      return;
+    }
+    case HB_OT_TAG_GPOS:
+    {
+      const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
+      l.collect_glyphs_lookup (&c);
+      return;
+    }
+  }
+}
+
 
 /*
  * OT::GSUB
@@ -399,18 +538,18 @@
 }
 
 hb_bool_t
-hb_ot_layout_would_substitute_lookup (hb_face_t            *face,
+hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
 				      unsigned int          lookup_index,
 				      const hb_codepoint_t *glyphs,
 				      unsigned int          glyphs_length,
 				      hb_bool_t             zero_context)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
-  return hb_ot_layout_would_substitute_lookup_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
+  return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
 }
 
 hb_bool_t
-hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
+hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
 					   unsigned int          lookup_index,
 					   const hb_codepoint_t *glyphs,
 					   unsigned int          glyphs_length,
@@ -452,12 +591,15 @@
 }
 
 void
-hb_ot_layout_substitute_closure_lookup (hb_face_t    *face,
+hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
 				        unsigned int  lookup_index,
 				        hb_set_t     *glyphs)
 {
   OT::hb_closure_context_t c (face, glyphs);
-  _get_gsub (face).closure_lookup (&c, lookup_index);
+
+  const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
+
+  l.closure (&c);
 }
 
 /*
@@ -496,3 +638,48 @@
 {
   OT::GPOS::position_finish (font, buffer, zero_width_attached_marks);
 }
+
+hb_bool_t
+hb_ot_layout_get_size_params (hb_face_t    *face,
+			      unsigned int *design_size,       /* OUT.  May be NULL */
+			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
+			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
+			      unsigned int *range_start,       /* OUT.  May be NULL */
+			      unsigned int *range_end          /* OUT.  May be NULL */)
+{
+  const OT::GPOS &gpos = _get_gpos (face);
+  const hb_tag_t tag = HB_TAG ('s','i','z','e');
+
+  unsigned int num_features = gpos.get_feature_count ();
+  for (unsigned int i = 0; i < num_features; i++)
+  {
+    if (tag == gpos.get_feature_tag (i))
+    {
+      const OT::Feature &f = gpos.get_feature (i);
+      const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
+
+      if (params.designSize)
+      {
+#define PARAM(a, A) if (a) *a = params.A
+	PARAM (design_size, designSize);
+	PARAM (subfamily_id, subfamilyID);
+	PARAM (subfamily_name_id, subfamilyNameID);
+	PARAM (range_start, rangeStart);
+	PARAM (range_end, rangeEnd);
+#undef PARAM
+
+	return true;
+      }
+    }
+  }
+
+#define PARAM(a, A) if (a) *a = 0
+  PARAM (design_size, designSize);
+  PARAM (subfamily_id, subfamilyID);
+  PARAM (subfamily_name_id, subfamilyNameID);
+  PARAM (range_start, rangeStart);
+  PARAM (range_end, rangeEnd);
+#undef PARAM
+
+  return false;
+}
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index d431a38..134f1a6 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -50,6 +50,24 @@
 hb_bool_t
 hb_ot_layout_has_glyph_classes (hb_face_t *face);
 
+typedef enum {
+  HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED	= 0,
+  HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH	= 1,
+  HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE	= 2,
+  HB_OT_LAYOUT_GLYPH_CLASS_MARK		= 3,
+  HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT	= 4
+} hb_ot_layout_glyph_class_t;
+
+hb_ot_layout_glyph_class_t
+hb_ot_layout_get_glyph_class (hb_face_t      *face,
+			      hb_codepoint_t  glyph);
+
+void
+hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
+				  hb_ot_layout_glyph_class_t  klass,
+				  hb_set_t                   *glyphs /* OUT */);
+
+
 /* Not that useful.  Provides list of attach points for a glyph that a
  * client may want to cache */
 unsigned int
@@ -154,12 +172,60 @@
 				    unsigned int *feature_index);
 
 unsigned int
-hb_ot_layout_feature_get_lookup_indexes (hb_face_t    *face,
+hb_ot_layout_feature_get_lookups (hb_face_t    *face,
+				  hb_tag_t      table_tag,
+				  unsigned int  feature_index,
+				  unsigned int  start_offset,
+				  unsigned int *lookup_count /* IN/OUT */,
+				  unsigned int *lookup_indexes /* OUT */);
+
+void
+hb_ot_layout_collect_lookups (hb_face_t      *face,
+			      hb_tag_t        table_tag,
+			      const hb_tag_t *scripts,
+			      const hb_tag_t *languages,
+			      const hb_tag_t *features,
+			      hb_set_t       *lookup_indexes /* OUT */);
+
+void
+hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
+				  hb_tag_t         table_tag,
+				  hb_set_t        *lookup_indexes /* OUT */);
+
+void
+hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
+				    hb_tag_t      table_tag,
+				    unsigned int  lookup_index,
+				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
+				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
+				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
+				    hb_set_t     *glyphs_output  /* OUT. May be NULL */);
+
+#ifdef HB_NOT_IMPLEMENTED
+typedef struct
+{
+  const hb_codepoint_t *before,
+  unsigned int          before_length,
+  const hb_codepoint_t *input,
+  unsigned int          input_length,
+  const hb_codepoint_t *after,
+  unsigned int          after_length,
+} hb_ot_layout_glyph_sequence_t;
+
+typedef hb_bool_t
+(*hb_ot_layout_glyph_sequence_func_t) (hb_font_t    *font,
+				       hb_tag_t      table_tag,
+				       unsigned int  lookup_index,
+				       const hb_ot_layout_glyph_sequence_t *sequence,
+				       void         *user_data);
+
+void
+Xhb_ot_layout_lookup_enumerate_sequences (hb_face_t    *face,
 					 hb_tag_t      table_tag,
-					 unsigned int  feature_index,
-					 unsigned int  start_offset,
-					 unsigned int *lookup_count /* IN/OUT */,
-					 unsigned int *lookup_indexes /* OUT */);
+					 unsigned int  lookup_index,
+					 hb_ot_layout_glyph_sequence_func_t callback,
+					 void         *user_data);
+#endif
 
 
 /*
@@ -170,16 +236,30 @@
 hb_ot_layout_has_substitution (hb_face_t *face);
 
 hb_bool_t
-hb_ot_layout_would_substitute_lookup (hb_face_t            *face,
+hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
 				      unsigned int          lookup_index,
 				      const hb_codepoint_t *glyphs,
 				      unsigned int          glyphs_length,
 				      hb_bool_t             zero_context);
 
 void
-hb_ot_layout_substitute_closure_lookup (hb_face_t    *face,
+hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
 				        unsigned int  lookup_index,
-				        hb_set_t     *glyphs);
+				        hb_set_t     *glyphs
+					/*TODO , hb_bool_t  inclusive */);
+
+#ifdef HB_NOT_IMPLEMENTED
+/* Note: You better have GDEF when using this API, or marks won't do much. */
+hb_bool_t
+Xhb_ot_layout_lookup_substitute (hb_font_t            *font,
+				unsigned int          lookup_index,
+				const hb_ot_layout_glyph_sequence_t *sequence,
+				unsigned int          out_size,
+				hb_codepoint_t       *glyphs_out,   /* OUT */
+				unsigned int         *clusters_out, /* OUT */
+				unsigned int         *out_length    /* OUT */);
+#endif
+
 
 /*
  * GPOS
@@ -188,6 +268,25 @@
 hb_bool_t
 hb_ot_layout_has_positioning (hb_face_t *face);
 
+#ifdef HB_NOT_IMPLEMENTED
+/* Note: You better have GDEF when using this API, or marks won't do much. */
+hb_bool_t
+Xhb_ot_layout_lookup_position (hb_font_t            *font,
+			      unsigned int          lookup_index,
+			      const hb_ot_layout_glyph_sequence_t *sequence,
+			      hb_glyph_position_t  *positions /* IN / OUT */);
+#endif
+
+/* Optical 'size' feature info.  Returns true if found.
+ * http://www.microsoft.com/typography/otspec/features_pt.htm#size */
+hb_bool_t
+hb_ot_layout_get_size_params (hb_face_t    *face,
+			      unsigned int *design_size,       /* OUT.  May be NULL */
+			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
+			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
+			      unsigned int *range_start,       /* OUT.  May be NULL */
+			      unsigned int *range_end          /* OUT.  May be NULL */);
+
 
 HB_END_DECLS
 
diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh
index 7eb85c8..b140207 100644
--- a/src/hb-ot-map-private.hh
+++ b/src/hb-ot-map-private.hh
@@ -115,10 +115,7 @@
     *lookup_count = end - start;
   }
 
-  inline hb_tag_t get_chosen_script (unsigned int table_index) const
-  { return chosen_script[table_index]; }
-
-  HB_INTERNAL void substitute_closure (const struct hb_ot_shape_plan_t *plan, hb_face_t *face, hb_set_t *glyphs) const;
+  HB_INTERNAL void collect_lookups (unsigned int table_index, hb_set_t *lookups) const;
   HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
   HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
 
@@ -130,6 +127,9 @@
     pauses[1].finish ();
   }
 
+  public:
+  hb_tag_t chosen_script[2];
+  bool found_script[2];
 
   private:
 
@@ -140,7 +140,6 @@
 
   hb_mask_t global_mask;
 
-  hb_tag_t chosen_script[2];
   hb_prealloced_array_t<feature_map_t, 8> features;
   hb_prealloced_array_t<lookup_map_t, 32> lookups[2]; /* GSUB/GPOS */
   hb_prealloced_array_t<pause_map_t, 1> pauses[2]; /* GSUB/GPOS */
@@ -151,7 +150,8 @@
 {
   public:
 
-  hb_ot_map_builder_t (void) { memset (this, 0, sizeof (*this)); }
+  HB_INTERNAL hb_ot_map_builder_t (hb_face_t *face_,
+				   const hb_segment_properties_t *props_);
 
   HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value, bool global, bool has_fallback = false);
 
@@ -163,9 +163,7 @@
   inline void add_gpos_pause (hb_ot_map_t::pause_func_t pause_func)
   { add_pause (1, pause_func); }
 
-  HB_INTERNAL void compile (hb_face_t *face,
-			    const hb_segment_properties_t *props,
-			    struct hb_ot_map_t &m);
+  HB_INTERNAL void compile (struct hb_ot_map_t &m);
 
   inline void finish (void) {
     feature_infos.finish ();
@@ -195,6 +193,17 @@
 
   HB_INTERNAL void add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func);
 
+  public:
+
+  hb_face_t *face;
+  hb_segment_properties_t props;
+
+  hb_tag_t chosen_script[2];
+  bool found_script[2];
+  unsigned int script_index[2], language_index[2];
+
+  private:
+
   unsigned int current_stage[2]; /* GSUB/GPOS */
   hb_prealloced_array_t<feature_info_t,16> feature_infos;
   hb_prealloced_array_t<pause_info_t, 1> pauses[2]; /* GSUB/GPOS */
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index f290c98..62f7904 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -41,11 +41,11 @@
   offset = 0;
   do {
     len = ARRAY_LENGTH (lookup_indices);
-    hb_ot_layout_feature_get_lookup_indexes (face,
-					     table_tags[table_index],
-					     feature_index,
-					     offset, &len,
-					     lookup_indices);
+    hb_ot_layout_feature_get_lookups (face,
+				      table_tags[table_index],
+				      feature_index,
+				      offset, &len,
+				      lookup_indices);
 
     for (unsigned int i = 0; i < len; i++) {
       hb_ot_map_t::lookup_map_t *lookup = lookups[table_index].push ();
@@ -59,6 +59,30 @@
   } while (len == ARRAY_LENGTH (lookup_indices));
 }
 
+hb_ot_map_builder_t::hb_ot_map_builder_t (hb_face_t *face_,
+					  const hb_segment_properties_t *props_)
+{
+  memset (this, 0, sizeof (*this));
+
+  face = face_;
+  props = *props_;
+
+
+  /* Fetch script/language indices for GSUB/GPOS.  We need these later to skip
+   * features not available in either table and not waste precious bits for them. */
+
+  hb_tag_t script_tags[3] = {HB_TAG_NONE, HB_TAG_NONE, HB_TAG_NONE};
+  hb_tag_t language_tag;
+
+  hb_ot_tags_from_script (props.script, &script_tags[0], &script_tags[1]);
+  language_tag = hb_ot_tag_from_language (props.language);
+
+  for (unsigned int table_index = 0; table_index < 2; table_index++) {
+    hb_tag_t table_tag = table_tags[table_index];
+    found_script[table_index] = hb_ot_layout_table_choose_script (face, table_tag, script_tags, &script_index[table_index], &chosen_script[table_index]);
+    hb_ot_layout_script_find_language (face, table_tag, script_index[table_index], language_tag, &language_index[table_index]);
+  }
+}
 
 void hb_ot_map_builder_t::add_feature (hb_tag_t tag, unsigned int value, bool global, bool has_fallback)
 {
@@ -114,11 +138,10 @@
     hb_ot_layout_position_lookup (font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask);
 }
 
-void hb_ot_map_t::substitute_closure (const hb_ot_shape_plan_t *plan, hb_face_t *face, hb_set_t *glyphs) const
+void hb_ot_map_t::collect_lookups (unsigned int table_index, hb_set_t *lookups_out) const
 {
-  unsigned int table_index = 0;
   for (unsigned int i = 0; i < lookups[table_index].len; i++)
-    hb_ot_layout_substitute_closure_lookup (face, lookups[table_index][i].index, glyphs);
+    hb_set_add (lookups_out, 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)
@@ -133,33 +156,18 @@
 }
 
 void
-hb_ot_map_builder_t::compile (hb_face_t *face,
-			      const hb_segment_properties_t *props,
-			      hb_ot_map_t &m)
+hb_ot_map_builder_t::compile (hb_ot_map_t &m)
 {
- m.global_mask = 1;
+  m.global_mask = 1;
+
+  for (unsigned int table_index = 0; table_index < 2; table_index++) {
+    m.chosen_script[table_index] = chosen_script[table_index];
+    m.found_script[table_index] = found_script[table_index];
+  }
 
   if (!feature_infos.len)
     return;
 
-
-  /* Fetch script/language indices for GSUB/GPOS.  We need these later to skip
-   * features not available in either table and not waste precious bits for them. */
-
-  hb_tag_t script_tags[3] = {HB_TAG_NONE};
-  hb_tag_t language_tag;
-
-  hb_ot_tags_from_script (props->script, &script_tags[0], &script_tags[1]);
-  language_tag = hb_ot_tag_from_language (props->language);
-
-  unsigned int script_index[2], language_index[2];
-  for (unsigned int table_index = 0; table_index < 2; table_index++) {
-    hb_tag_t table_tag = table_tags[table_index];
-    hb_ot_layout_table_choose_script (face, table_tag, script_tags, &script_index[table_index], &m.chosen_script[table_index]);
-    hb_ot_layout_script_find_language (face, table_tag, script_index[table_index], language_tag, &language_index[table_index]);
-  }
-
-
   /* Sort features and merge duplicates */
   {
     feature_infos.sort ();
diff --git a/src/hb-ot-maxp-table.hh b/src/hb-ot-maxp-table.hh
index 9e113c7..0ce3ebc 100644
--- a/src/hb-ot-maxp-table.hh
+++ b/src/hb-ot-maxp-table.hh
@@ -48,7 +48,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) &&
 			 likely (version.major == 1 || (version.major == 0 && version.minor == 0x5000)));
   }
@@ -63,7 +63,7 @@
 };
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OT_MAXP_TABLE_HH */
diff --git a/src/hb-ot-name-table.hh b/src/hb-ot-name-table.hh
index 6cc7a5e..75a1b94 100644
--- a/src/hb-ot-name-table.hh
+++ b/src/hb-ot-name-table.hh
@@ -57,7 +57,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     /* We can check from base all the way up to the end of string... */
     return TRACE_RETURN (c->check_struct (this) && c->check_range ((char *) base, (unsigned int) length + offset));
   }
@@ -99,7 +99,7 @@
   }
 
   inline bool sanitize_records (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     char *string_pool = (char *) this + stringOffset;
     unsigned int _count = count;
     for (unsigned int i = 0; i < _count; i++)
@@ -108,7 +108,7 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE ();
+    TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) &&
 			 likely (format == 0 || format == 1) &&
 			 c->check_array (nameRecord, nameRecord[0].static_size, count) &&
@@ -126,7 +126,7 @@
 };
 
 
-} // namespace OT
+} /* namespace OT */
 
 
 #endif /* HB_OT_NAME_TABLE_HH */
diff --git a/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh
index bf68561..4fcd0a2 100644
--- a/src/hb-ot-shape-complex-arabic-fallback.hh
+++ b/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -53,7 +53,7 @@
 };
 
 static OT::SubstLookup *
-arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan,
+arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUSED,
 					  hb_font_t *font,
 					  unsigned int feature_index)
 {
@@ -103,7 +103,7 @@
 }
 
 static OT::SubstLookup *
-arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan,
+arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UNUSED,
 					    hb_font_t *font)
 {
   OT::GlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)];
diff --git a/src/hb-ot-shape-complex-arabic-table.hh b/src/hb-ot-shape-complex-arabic-table.hh
index 5e29167..730a275 100644
--- a/src/hb-ot-shape-complex-arabic-table.hh
+++ b/src/hb-ot-shape-complex-arabic-table.hh
@@ -6,8 +6,8 @@
  *
  * on files with these headers:
  *
- * # ArabicShaping-6.1.0.txt
- * # Date: 2011-04-15, 23:16:00 GMT [KW]
+ * # ArabicShaping-6.2.0.txt
+ * # Date: 2012-05-15, 21:05:00 GMT [KW]
  * UnicodeData.txt does not have a header.
  */
 
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index 8428534..35356fe 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -64,15 +64,31 @@
       return j_type;
   }
 
-  /* Mongolian joining data is not in ArabicJoining.txt yet */
+  /* Mongolian joining data is not in ArabicJoining.txt yet. */
   if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1800, 0x18AF)))
   {
+    if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1880, 0x1886)))
+      return JOINING_TYPE_U;
+
     /* All letters, SIBE SYLLABLE BOUNDARY MARKER, and NIRUGU are D */
-    if (gen_cat == HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER || u == 0x1807 || u == 0x180A)
+    if ((FLAG(gen_cat) & (FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) |
+			  FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER)))
+	|| u == 0x1807 || u == 0x180A)
       return JOINING_TYPE_D;
   }
 
-  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x200C, 0x200D))) {
+  /* 'Phags-pa joining data is not in ArabicJoining.txt yet. */
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xA840, 0xA872)))
+  {
+      if (unlikely (u == 0xA872))
+	/* XXX Looks like this should be TYPE_L, but we don't support that yet! */
+	return JOINING_TYPE_R;
+
+      return JOINING_TYPE_D;
+  }
+
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x200C, 0x200D)))
+  {
     return u == 0x200C ? JOINING_TYPE_U : JOINING_TYPE_C;
   }
 
@@ -235,17 +251,18 @@
   HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action);
 
   /* Check pre-context */
-  for (unsigned int i = 0; i < buffer->context_len[0]; i++)
-  {
-    unsigned int this_type = get_joining_type (buffer->context[0][i], buffer->unicode->general_category (buffer->context[0][i]));
+  if (!(buffer->flags & HB_BUFFER_FLAG_BOT))
+    for (unsigned int i = 0; i < buffer->context_len[0]; i++)
+    {
+      unsigned int this_type = get_joining_type (buffer->context[0][i], buffer->unicode->general_category (buffer->context[0][i]));
 
-    if (unlikely (this_type == JOINING_TYPE_T))
-      continue;
+      if (unlikely (this_type == JOINING_TYPE_T))
+	continue;
 
-    const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
-    state = entry->next_state;
-    break;
-  }
+      const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
+      state = entry->next_state;
+      break;
+    }
 
   for (unsigned int i = 0; i < count; i++)
   {
@@ -267,18 +284,19 @@
     state = entry->next_state;
   }
 
-  for (unsigned int i = 0; i < buffer->context_len[1]; i++)
-  {
-    unsigned int this_type = get_joining_type (buffer->context[1][i], buffer->unicode->general_category (buffer->context[0][i]));
+  if (!(buffer->flags & HB_BUFFER_FLAG_EOT))
+    for (unsigned int i = 0; i < buffer->context_len[1]; i++)
+    {
+      unsigned int this_type = get_joining_type (buffer->context[1][i], buffer->unicode->general_category (buffer->context[1][i]));
 
-    if (unlikely (this_type == JOINING_TYPE_T))
-      continue;
+      if (unlikely (this_type == JOINING_TYPE_T))
+	continue;
 
-    const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
-    if (entry->prev_action != NONE && prev != (unsigned int) -1)
-      buffer->info[prev].arabic_shaping_action() = entry->prev_action;
-    break;
-  }
+      const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
+      if (entry->prev_action != NONE && prev != (unsigned int) -1)
+	buffer->info[prev].arabic_shaping_action() = entry->prev_action;
+      break;
+    }
 
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
@@ -287,7 +305,7 @@
 static void
 setup_masks_arabic (const hb_ot_shape_plan_t *plan,
 		    hb_buffer_t              *buffer,
-		    hb_font_t                *font)
+		    hb_font_t                *font HB_UNUSED)
 {
   const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
 
@@ -333,6 +351,9 @@
   data_destroy_arabic,
   NULL, /* preprocess_text_arabic */
   NULL, /* normalization_preference */
+  NULL, /* decompose */
+  NULL, /* compose */
   setup_masks_arabic,
   true, /* zero_width_attached_marks */
+  true, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-default.cc b/src/hb-ot-shape-complex-default.cc
new file mode 100644
index 0000000..5340293
--- /dev/null
+++ b/src/hb-ot-shape-complex-default.cc
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ *
+ * 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-private.hh"
+
+
+/* TODO Add kana, and other small shapers here */
+
+
+/* The default shaper *only* adds additional per-script features.*/
+
+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'),
+  HB_TAG_NONE
+};
+
+static const hb_tag_t tibetan_features[] =
+{
+  HB_TAG('a','b','v','s'),
+  HB_TAG('b','l','w','s'),
+  HB_TAG('a','b','v','m'),
+  HB_TAG('b','l','w','m'),
+  HB_TAG_NONE
+};
+
+static void
+collect_features_default (hb_ot_shape_planner_t *plan)
+{
+  const hb_tag_t *script_features = NULL;
+
+  switch ((hb_tag_t) plan->props.script)
+  {
+    /* Unicode-1.1 additions */
+    case HB_SCRIPT_HANGUL:
+      script_features = hangul_features;
+      break;
+
+    /* Unicode-2.0 additions */
+    case HB_SCRIPT_TIBETAN:
+      script_features = tibetan_features;
+      break;
+  }
+
+  for (; script_features && *script_features; script_features++)
+    plan->map.add_bool_feature (*script_features);
+}
+
+static hb_ot_shape_normalization_mode_t
+normalization_preference_default (const hb_segment_properties_t *props)
+{
+  switch ((hb_tag_t) props->script)
+  {
+    /* Unicode-1.1 additions */
+    case HB_SCRIPT_HANGUL:
+      return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL;
+  }
+  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
+}
+
+static bool
+compose_default (const hb_ot_shape_normalize_context_t *c,
+		 hb_codepoint_t  a,
+		 hb_codepoint_t  b,
+		 hb_codepoint_t *ab)
+{
+  /* Hebrew presentation-form shaping.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=728866
+   * Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
+   * Note that some letters do not have a dagesh presForm encoded.
+   */
+  static const hb_codepoint_t sDageshForms[0x05EA - 0x05D0 + 1] = {
+    0xFB30, /* ALEF */
+    0xFB31, /* BET */
+    0xFB32, /* GIMEL */
+    0xFB33, /* DALET */
+    0xFB34, /* HE */
+    0xFB35, /* VAV */
+    0xFB36, /* ZAYIN */
+    0x0000, /* HET */
+    0xFB38, /* TET */
+    0xFB39, /* YOD */
+    0xFB3A, /* FINAL KAF */
+    0xFB3B, /* KAF */
+    0xFB3C, /* LAMED */
+    0x0000, /* FINAL MEM */
+    0xFB3E, /* MEM */
+    0x0000, /* FINAL NUN */
+    0xFB40, /* NUN */
+    0xFB41, /* SAMEKH */
+    0x0000, /* AYIN */
+    0xFB43, /* FINAL PE */
+    0xFB44, /* PE */
+    0x0000, /* FINAL TSADI */
+    0xFB46, /* TSADI */
+    0xFB47, /* QOF */
+    0xFB48, /* RESH */
+    0xFB49, /* SHIN */
+    0xFB4A /* TAV */
+  };
+
+  bool found = c->unicode->compose (a, b, ab);
+
+  if (!found && (b & ~0x7F) == 0x0580) {
+      /* Special-case Hebrew presentation forms that are excluded from
+       * standard normalization, but wanted for old fonts. */
+      switch (b) {
+      case 0x05B4: /* HIRIQ */
+	  if (a == 0x05D9) { /* YOD */
+	      *ab = 0xFB1D;
+	      found = true;
+	  }
+	  break;
+      case 0x05B7: /* patah */
+	  if (a == 0x05F2) { /* YIDDISH YOD YOD */
+	      *ab = 0xFB1F;
+	      found = true;
+	  } else if (a == 0x05D0) { /* ALEF */
+	      *ab = 0xFB2E;
+	      found = true;
+	  }
+	  break;
+      case 0x05B8: /* QAMATS */
+	  if (a == 0x05D0) { /* ALEF */
+	      *ab = 0xFB2F;
+	      found = true;
+	  }
+	  break;
+      case 0x05B9: /* HOLAM */
+	  if (a == 0x05D5) { /* VAV */
+	      *ab = 0xFB4B;
+	      found = true;
+	  }
+	  break;
+      case 0x05BC: /* DAGESH */
+	  if (a >= 0x05D0 && a <= 0x05EA) {
+	      *ab = sDageshForms[a - 0x05D0];
+	      found = (*ab != 0);
+	  } else if (a == 0xFB2A) { /* SHIN WITH SHIN DOT */
+	      *ab = 0xFB2C;
+	      found = true;
+	  } else if (a == 0xFB2B) { /* SHIN WITH SIN DOT */
+	      *ab = 0xFB2D;
+	      found = true;
+	  }
+	  break;
+      case 0x05BF: /* RAFE */
+	  switch (a) {
+	  case 0x05D1: /* BET */
+	      *ab = 0xFB4C;
+	      found = true;
+	      break;
+	  case 0x05DB: /* KAF */
+	      *ab = 0xFB4D;
+	      found = true;
+	      break;
+	  case 0x05E4: /* PE */
+	      *ab = 0xFB4E;
+	      found = true;
+	      break;
+	  }
+	  break;
+      case 0x05C1: /* SHIN DOT */
+	  if (a == 0x05E9) { /* SHIN */
+	      *ab = 0xFB2A;
+	      found = true;
+	  } else if (a == 0xFB49) { /* SHIN WITH DAGESH */
+	      *ab = 0xFB2C;
+	      found = true;
+	  }
+	  break;
+      case 0x05C2: /* SIN DOT */
+	  if (a == 0x05E9) { /* SHIN */
+	      *ab = 0xFB2B;
+	      found = true;
+	  } else if (a == 0xFB49) { /* SHIN WITH DAGESH */
+	      *ab = 0xFB2D;
+	      found = true;
+	  }
+	  break;
+      }
+  }
+
+  return found;
+}
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
+{
+  "default",
+  collect_features_default,
+  NULL, /* override_features */
+  NULL, /* data_create */
+  NULL, /* data_destroy */
+  NULL, /* preprocess_text */
+  normalization_preference_default,
+  NULL, /* decompose */
+  compose_default,
+  NULL, /* setup_masks */
+  true, /* zero_width_attached_marks */
+  true, /* fallback_position */
+};
diff --git a/src/hb-ot-shape-complex-indic-machine.hh b/src/hb-ot-shape-complex-indic-machine.hh
index 53f22e3..f97ec52 100644
--- a/src/hb-ot-shape-complex-indic-machine.hh
+++ b/src/hb-ot-shape-complex-indic-machine.hh
@@ -52,44 +52,44 @@
 	7u, 7u, 4u, 4u, 6u, 6u, 16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 4u, 7u, 
 	6u, 6u, 16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 4u, 14u, 4u, 14u, 4u, 14u, 
 	4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 1u, 16u, 
-	3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 
-	3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 
-	3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 8u, 14u, 5u, 9u, 9u, 9u, 9u, 9u, 
-	3u, 13u, 3u, 9u, 8u, 9u, 3u, 9u, 3u, 13u, 3u, 14u, 3u, 14u, 4u, 14u, 
+	3u, 17u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 3u, 14u, 4u, 14u, 1u, 16u, 
+	3u, 17u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 3u, 14u, 4u, 14u, 1u, 16u, 
+	3u, 17u, 3u, 14u, 4u, 14u, 5u, 14u, 8u, 14u, 5u, 9u, 9u, 9u, 9u, 9u, 
+	3u, 17u, 3u, 9u, 8u, 9u, 3u, 9u, 3u, 13u, 3u, 14u, 3u, 14u, 4u, 14u, 
 	5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 
 	4u, 14u, 6u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 
 	1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 
 	1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 
 	1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 
-	1u, 16u, 3u, 14u, 3u, 14u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 
-	3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 
-	3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 8u, 14u, 
-	5u, 9u, 9u, 9u, 9u, 9u, 3u, 13u, 3u, 9u, 8u, 9u, 3u, 9u, 3u, 13u, 
+	1u, 16u, 3u, 14u, 3u, 14u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 
+	3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 
+	3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 3u, 14u, 4u, 14u, 5u, 14u, 8u, 14u, 
+	5u, 9u, 9u, 9u, 9u, 9u, 3u, 17u, 3u, 9u, 8u, 9u, 3u, 9u, 3u, 13u, 
 	3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 
 	4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 6u, 14u, 3u, 14u, 1u, 16u, 3u, 14u, 
 	3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 
 	1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 
 	1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 
 	1u, 16u, 1u, 16u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 
-	3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 
-	3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 
-	8u, 14u, 5u, 9u, 9u, 9u, 9u, 9u, 3u, 13u, 3u, 9u, 8u, 9u, 3u, 9u, 
+	3u, 17u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 3u, 14u, 4u, 14u, 1u, 16u, 
+	3u, 17u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 3u, 14u, 4u, 14u, 5u, 14u, 
+	8u, 14u, 5u, 9u, 9u, 9u, 9u, 9u, 3u, 17u, 3u, 9u, 8u, 9u, 3u, 9u, 
 	3u, 13u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 
 	3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 6u, 14u, 3u, 14u, 1u, 16u, 
 	3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 
 	3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 
 	1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 
 	1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 4u, 14u, 3u, 14u, 4u, 14u, 3u, 14u, 
-	3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 
-	3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 
-	3u, 14u, 4u, 14u, 5u, 14u, 8u, 14u, 5u, 9u, 9u, 9u, 9u, 9u, 3u, 13u, 
+	3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 
+	3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 17u, 
+	3u, 14u, 4u, 14u, 5u, 14u, 8u, 14u, 5u, 9u, 9u, 9u, 9u, 9u, 3u, 17u, 
 	3u, 9u, 8u, 9u, 3u, 9u, 3u, 13u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 
 	3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 
 	6u, 14u, 3u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 
 	1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 
 	1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 
 	3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 
-	1u, 16u, 3u, 14u, 1u, 16u, 0
+	1u, 16u, 3u, 17u, 1u, 16u, 0
 };
 
 static const char _indic_syllable_machine_key_spans[] = {
@@ -111,44 +111,44 @@
 	1, 1, 1, 1, 4, 1, 1, 4, 
 	1, 1, 4, 1, 1, 11, 11, 11, 
 	11, 11, 11, 11, 11, 11, 11, 16, 
-	12, 12, 11, 16, 12, 12, 11, 16, 
-	12, 12, 11, 16, 12, 12, 11, 16, 
-	12, 12, 11, 10, 7, 5, 1, 1, 
-	11, 7, 2, 7, 11, 12, 12, 11, 
+	15, 12, 11, 16, 15, 12, 11, 16, 
+	15, 12, 11, 16, 15, 12, 11, 16, 
+	15, 12, 11, 10, 7, 5, 1, 1, 
+	15, 7, 2, 7, 11, 12, 12, 11, 
 	10, 12, 11, 10, 12, 11, 10, 12, 
 	11, 9, 12, 11, 16, 12, 12, 16, 
 	16, 16, 16, 16, 12, 12, 16, 16, 
 	16, 16, 16, 12, 12, 16, 16, 16, 
 	16, 16, 12, 12, 16, 16, 16, 16, 
-	16, 12, 12, 12, 12, 11, 16, 12, 
-	12, 11, 16, 12, 12, 11, 16, 12, 
-	12, 11, 16, 12, 12, 11, 10, 7, 
-	5, 1, 1, 11, 7, 2, 7, 11, 
+	16, 12, 12, 12, 12, 11, 16, 15, 
+	12, 11, 16, 15, 12, 11, 16, 15, 
+	12, 11, 16, 15, 12, 11, 10, 7, 
+	5, 1, 1, 15, 7, 2, 7, 11, 
 	12, 12, 11, 10, 12, 11, 10, 12, 
 	11, 10, 12, 11, 9, 12, 16, 12, 
 	12, 16, 16, 16, 16, 16, 12, 12, 
 	16, 16, 16, 16, 16, 12, 12, 16, 
 	16, 16, 16, 16, 12, 12, 16, 16, 
 	16, 16, 11, 16, 12, 12, 11, 16, 
-	12, 12, 11, 16, 12, 12, 11, 16, 
-	12, 12, 11, 16, 12, 12, 11, 10, 
-	7, 5, 1, 1, 11, 7, 2, 7, 
+	15, 12, 11, 16, 15, 12, 11, 16, 
+	15, 12, 11, 16, 15, 12, 11, 10, 
+	7, 5, 1, 1, 15, 7, 2, 7, 
 	11, 12, 12, 11, 10, 12, 11, 10, 
 	12, 11, 10, 12, 11, 9, 12, 16, 
 	12, 12, 16, 16, 16, 16, 16, 12, 
 	12, 16, 16, 16, 16, 16, 12, 12, 
 	16, 16, 16, 16, 16, 12, 12, 16, 
 	16, 16, 16, 16, 11, 12, 11, 12, 
-	12, 11, 16, 12, 12, 11, 16, 12, 
-	12, 11, 16, 12, 12, 11, 16, 12, 
-	12, 11, 10, 7, 5, 1, 1, 11, 
+	12, 11, 16, 15, 12, 11, 16, 15, 
+	12, 11, 16, 15, 12, 11, 16, 15, 
+	12, 11, 10, 7, 5, 1, 1, 15, 
 	7, 2, 7, 11, 12, 12, 11, 10, 
 	12, 11, 10, 12, 11, 10, 12, 11, 
 	9, 12, 16, 12, 12, 16, 16, 16, 
 	16, 16, 12, 12, 16, 16, 16, 16, 
 	16, 12, 12, 16, 16, 16, 16, 16, 
 	12, 12, 16, 16, 16, 16, 16, 12, 
-	16, 12, 16
+	16, 15, 16
 };
 
 static const short _indic_syllable_machine_index_offsets[] = {
@@ -170,44 +170,44 @@
 	681, 683, 685, 687, 689, 694, 696, 698, 
 	703, 705, 707, 712, 714, 716, 728, 740, 
 	752, 764, 776, 788, 800, 812, 824, 836, 
-	853, 866, 879, 891, 908, 921, 934, 946, 
-	963, 976, 989, 1001, 1018, 1031, 1044, 1056, 
-	1073, 1086, 1099, 1111, 1122, 1130, 1136, 1138, 
-	1140, 1152, 1160, 1163, 1171, 1183, 1196, 1209, 
-	1221, 1232, 1245, 1257, 1268, 1281, 1293, 1304, 
-	1317, 1329, 1339, 1352, 1364, 1381, 1394, 1407, 
-	1424, 1441, 1458, 1475, 1492, 1505, 1518, 1535, 
-	1552, 1569, 1586, 1603, 1616, 1629, 1646, 1663, 
-	1680, 1697, 1714, 1727, 1740, 1757, 1774, 1791, 
-	1808, 1825, 1838, 1851, 1864, 1877, 1889, 1906, 
-	1919, 1932, 1944, 1961, 1974, 1987, 1999, 2016, 
-	2029, 2042, 2054, 2071, 2084, 2097, 2109, 2120, 
-	2128, 2134, 2136, 2138, 2150, 2158, 2161, 2169, 
-	2181, 2194, 2207, 2219, 2230, 2243, 2255, 2266, 
-	2279, 2291, 2302, 2315, 2327, 2337, 2350, 2367, 
-	2380, 2393, 2410, 2427, 2444, 2461, 2478, 2491, 
-	2504, 2521, 2538, 2555, 2572, 2589, 2602, 2615, 
-	2632, 2649, 2666, 2683, 2700, 2713, 2726, 2743, 
-	2760, 2777, 2794, 2806, 2823, 2836, 2849, 2861, 
-	2878, 2891, 2904, 2916, 2933, 2946, 2959, 2971, 
-	2988, 3001, 3014, 3026, 3043, 3056, 3069, 3081, 
-	3092, 3100, 3106, 3108, 3110, 3122, 3130, 3133, 
-	3141, 3153, 3166, 3179, 3191, 3202, 3215, 3227, 
-	3238, 3251, 3263, 3274, 3287, 3299, 3309, 3322, 
-	3339, 3352, 3365, 3382, 3399, 3416, 3433, 3450, 
-	3463, 3476, 3493, 3510, 3527, 3544, 3561, 3574, 
-	3587, 3604, 3621, 3638, 3655, 3672, 3685, 3698, 
-	3715, 3732, 3749, 3766, 3783, 3795, 3808, 3820, 
-	3833, 3846, 3858, 3875, 3888, 3901, 3913, 3930, 
-	3943, 3956, 3968, 3985, 3998, 4011, 4023, 4040, 
-	4053, 4066, 4078, 4089, 4097, 4103, 4105, 4107, 
-	4119, 4127, 4130, 4138, 4150, 4163, 4176, 4188, 
-	4199, 4212, 4224, 4235, 4248, 4260, 4271, 4284, 
-	4296, 4306, 4319, 4336, 4349, 4362, 4379, 4396, 
-	4413, 4430, 4447, 4460, 4473, 4490, 4507, 4524, 
-	4541, 4558, 4571, 4584, 4601, 4618, 4635, 4652, 
-	4669, 4682, 4695, 4712, 4729, 4746, 4763, 4780, 
-	4793, 4810, 4823
+	853, 869, 882, 894, 911, 927, 940, 952, 
+	969, 985, 998, 1010, 1027, 1043, 1056, 1068, 
+	1085, 1101, 1114, 1126, 1137, 1145, 1151, 1153, 
+	1155, 1171, 1179, 1182, 1190, 1202, 1215, 1228, 
+	1240, 1251, 1264, 1276, 1287, 1300, 1312, 1323, 
+	1336, 1348, 1358, 1371, 1383, 1400, 1413, 1426, 
+	1443, 1460, 1477, 1494, 1511, 1524, 1537, 1554, 
+	1571, 1588, 1605, 1622, 1635, 1648, 1665, 1682, 
+	1699, 1716, 1733, 1746, 1759, 1776, 1793, 1810, 
+	1827, 1844, 1857, 1870, 1883, 1896, 1908, 1925, 
+	1941, 1954, 1966, 1983, 1999, 2012, 2024, 2041, 
+	2057, 2070, 2082, 2099, 2115, 2128, 2140, 2151, 
+	2159, 2165, 2167, 2169, 2185, 2193, 2196, 2204, 
+	2216, 2229, 2242, 2254, 2265, 2278, 2290, 2301, 
+	2314, 2326, 2337, 2350, 2362, 2372, 2385, 2402, 
+	2415, 2428, 2445, 2462, 2479, 2496, 2513, 2526, 
+	2539, 2556, 2573, 2590, 2607, 2624, 2637, 2650, 
+	2667, 2684, 2701, 2718, 2735, 2748, 2761, 2778, 
+	2795, 2812, 2829, 2841, 2858, 2871, 2884, 2896, 
+	2913, 2929, 2942, 2954, 2971, 2987, 3000, 3012, 
+	3029, 3045, 3058, 3070, 3087, 3103, 3116, 3128, 
+	3139, 3147, 3153, 3155, 3157, 3173, 3181, 3184, 
+	3192, 3204, 3217, 3230, 3242, 3253, 3266, 3278, 
+	3289, 3302, 3314, 3325, 3338, 3350, 3360, 3373, 
+	3390, 3403, 3416, 3433, 3450, 3467, 3484, 3501, 
+	3514, 3527, 3544, 3561, 3578, 3595, 3612, 3625, 
+	3638, 3655, 3672, 3689, 3706, 3723, 3736, 3749, 
+	3766, 3783, 3800, 3817, 3834, 3846, 3859, 3871, 
+	3884, 3897, 3909, 3926, 3942, 3955, 3967, 3984, 
+	4000, 4013, 4025, 4042, 4058, 4071, 4083, 4100, 
+	4116, 4129, 4141, 4152, 4160, 4166, 4168, 4170, 
+	4186, 4194, 4197, 4205, 4217, 4230, 4243, 4255, 
+	4266, 4279, 4291, 4302, 4315, 4327, 4338, 4351, 
+	4363, 4373, 4386, 4403, 4416, 4429, 4446, 4463, 
+	4480, 4497, 4514, 4527, 4540, 4557, 4574, 4591, 
+	4608, 4625, 4638, 4651, 4668, 4685, 4702, 4719, 
+	4736, 4749, 4762, 4779, 4796, 4813, 4830, 4847, 
+	4860, 4877, 4893
 };
 
 static const short _indic_syllable_machine_indicies[] = {
@@ -319,504 +319,512 @@
 	157, 158, 81, 159, 160, 152, 161, 161, 
 	162, 163, 164, 165, 152, 167, 168, 169, 
 	170, 5, 171, 172, 173, 166, 166, 37, 
-	174, 166, 175, 168, 176, 176, 5, 171, 
-	172, 173, 166, 166, 166, 174, 166, 168, 
-	176, 176, 5, 171, 172, 173, 166, 166, 
-	166, 174, 166, 177, 166, 166, 166, 18, 
-	178, 166, 171, 172, 166, 166, 166, 166, 
-	179, 166, 177, 166, 180, 181, 182, 183, 
-	5, 171, 172, 173, 166, 166, 35, 184, 
-	166, 185, 181, 186, 186, 5, 171, 172, 
-	173, 166, 166, 166, 184, 166, 181, 186, 
-	186, 5, 171, 172, 173, 166, 166, 166, 
-	184, 166, 187, 166, 166, 166, 18, 188, 
-	166, 171, 172, 166, 166, 166, 166, 179, 
-	166, 187, 166, 189, 190, 191, 192, 5, 
-	171, 172, 173, 166, 166, 33, 193, 166, 
-	194, 190, 195, 195, 5, 171, 172, 173, 
-	166, 166, 166, 193, 166, 190, 195, 195, 
-	5, 171, 172, 173, 166, 166, 166, 193, 
-	166, 196, 166, 166, 166, 18, 197, 166, 
-	171, 172, 166, 166, 166, 166, 179, 166, 
-	196, 166, 198, 199, 200, 201, 5, 171, 
-	172, 173, 166, 166, 31, 202, 166, 203, 
-	199, 204, 204, 5, 171, 172, 173, 166, 
-	166, 166, 202, 166, 199, 204, 204, 5, 
-	171, 172, 173, 166, 166, 166, 202, 166, 
-	205, 166, 166, 166, 18, 206, 166, 171, 
-	172, 166, 166, 166, 166, 179, 166, 205, 
-	166, 207, 208, 209, 210, 5, 171, 172, 
-	173, 166, 166, 29, 211, 166, 212, 208, 
-	213, 213, 5, 171, 172, 173, 166, 166, 
-	166, 211, 166, 208, 213, 213, 5, 171, 
-	172, 173, 166, 166, 166, 211, 166, 18, 
-	214, 166, 171, 172, 166, 166, 166, 166, 
-	179, 166, 171, 172, 166, 166, 166, 166, 
-	179, 166, 215, 166, 166, 166, 172, 166, 
-	172, 166, 216, 166, 217, 166, 218, 219, 
-	166, 171, 172, 166, 166, 166, 3, 166, 
-	2, 166, 166, 166, 166, 171, 172, 166, 
-	171, 172, 166, 217, 166, 166, 166, 166, 
-	171, 172, 166, 217, 166, 218, 166, 166, 
-	171, 172, 166, 166, 166, 3, 166, 18, 
-	166, 220, 220, 5, 171, 172, 166, 166, 
-	166, 166, 179, 166, 221, 27, 222, 223, 
-	8, 171, 172, 166, 166, 166, 166, 179, 
-	166, 27, 222, 223, 8, 171, 172, 166, 
-	166, 166, 166, 179, 166, 222, 222, 8, 
-	171, 172, 166, 166, 166, 166, 179, 166, 
-	224, 24, 225, 226, 11, 171, 172, 166, 
-	166, 166, 166, 179, 166, 24, 225, 226, 
-	11, 171, 172, 166, 166, 166, 166, 179, 
-	166, 225, 225, 11, 171, 172, 166, 166, 
-	166, 166, 179, 166, 227, 21, 228, 229, 
-	14, 171, 172, 166, 166, 166, 166, 179, 
-	166, 21, 228, 229, 14, 171, 172, 166, 
-	166, 166, 166, 179, 166, 228, 228, 14, 
-	171, 172, 166, 166, 166, 166, 179, 166, 
-	230, 18, 166, 231, 166, 171, 172, 166, 
-	166, 166, 166, 179, 166, 18, 166, 231, 
-	166, 171, 172, 166, 166, 166, 166, 179, 
-	166, 232, 166, 171, 172, 166, 166, 166, 
-	166, 179, 166, 18, 166, 166, 166, 166, 
-	171, 172, 166, 166, 166, 166, 179, 166, 
-	208, 213, 213, 5, 171, 172, 166, 166, 
-	166, 166, 211, 166, 1, 2, 166, 166, 
-	18, 214, 166, 171, 172, 166, 166, 166, 
-	166, 179, 166, 1, 166, 207, 208, 213, 
-	213, 5, 171, 172, 173, 166, 166, 166, 
-	211, 166, 207, 208, 209, 213, 5, 171, 
-	172, 173, 166, 166, 29, 211, 166, 205, 
-	166, 233, 166, 220, 220, 5, 171, 172, 
-	166, 166, 166, 166, 179, 166, 205, 166, 
-	205, 166, 166, 166, 166, 166, 166, 171, 
-	172, 166, 166, 166, 166, 179, 166, 205, 
-	166, 205, 166, 166, 166, 166, 234, 166, 
-	171, 172, 166, 166, 166, 166, 179, 166, 
-	205, 166, 205, 166, 233, 166, 166, 166, 
-	166, 171, 172, 166, 166, 166, 166, 179, 
-	166, 205, 166, 205, 2, 166, 166, 18, 
-	206, 166, 171, 172, 166, 166, 166, 166, 
-	179, 166, 205, 166, 198, 199, 204, 204, 
-	5, 171, 172, 173, 166, 166, 166, 202, 
-	166, 198, 199, 200, 204, 5, 171, 172, 
-	173, 166, 166, 31, 202, 166, 196, 166, 
-	235, 166, 220, 220, 5, 171, 172, 166, 
-	166, 166, 166, 179, 166, 196, 166, 196, 
-	166, 166, 166, 166, 166, 166, 171, 172, 
-	166, 166, 166, 166, 179, 166, 196, 166, 
-	196, 166, 166, 166, 166, 236, 166, 171, 
-	172, 166, 166, 166, 166, 179, 166, 196, 
-	166, 196, 166, 235, 166, 166, 166, 166, 
-	171, 172, 166, 166, 166, 166, 179, 166, 
-	196, 166, 196, 2, 166, 166, 18, 197, 
-	166, 171, 172, 166, 166, 166, 166, 179, 
-	166, 196, 166, 189, 190, 195, 195, 5, 
-	171, 172, 173, 166, 166, 166, 193, 166, 
-	189, 190, 191, 195, 5, 171, 172, 173, 
-	166, 166, 33, 193, 166, 187, 166, 237, 
-	166, 220, 220, 5, 171, 172, 166, 166, 
-	166, 166, 179, 166, 187, 166, 187, 166, 
-	166, 166, 166, 166, 166, 171, 172, 166, 
-	166, 166, 166, 179, 166, 187, 166, 187, 
-	166, 166, 166, 166, 238, 166, 171, 172, 
-	166, 166, 166, 166, 179, 166, 187, 166, 
-	187, 166, 237, 166, 166, 166, 166, 171, 
+	174, 166, 166, 153, 166, 175, 168, 176, 
+	176, 5, 171, 172, 173, 166, 166, 166, 
+	174, 166, 168, 176, 176, 5, 171, 172, 
+	173, 166, 166, 166, 174, 166, 177, 166, 
+	166, 166, 18, 178, 166, 171, 172, 166, 
+	166, 166, 166, 179, 166, 177, 166, 180, 
+	181, 182, 183, 5, 171, 172, 173, 166, 
+	166, 35, 184, 166, 166, 177, 166, 185, 
+	181, 186, 186, 5, 171, 172, 173, 166, 
+	166, 166, 184, 166, 181, 186, 186, 5, 
+	171, 172, 173, 166, 166, 166, 184, 166, 
+	187, 166, 166, 166, 18, 188, 166, 171, 
 	172, 166, 166, 166, 166, 179, 166, 187, 
-	166, 187, 2, 166, 166, 18, 188, 166, 
+	166, 189, 190, 191, 192, 5, 171, 172, 
+	173, 166, 166, 33, 193, 166, 166, 187, 
+	166, 194, 190, 195, 195, 5, 171, 172, 
+	173, 166, 166, 166, 193, 166, 190, 195, 
+	195, 5, 171, 172, 173, 166, 166, 166, 
+	193, 166, 196, 166, 166, 166, 18, 197, 
+	166, 171, 172, 166, 166, 166, 166, 179, 
+	166, 196, 166, 198, 199, 200, 201, 5, 
+	171, 172, 173, 166, 166, 31, 202, 166, 
+	166, 196, 166, 203, 199, 204, 204, 5, 
+	171, 172, 173, 166, 166, 166, 202, 166, 
+	199, 204, 204, 5, 171, 172, 173, 166, 
+	166, 166, 202, 166, 205, 166, 166, 166, 
+	18, 206, 166, 171, 172, 166, 166, 166, 
+	166, 179, 166, 205, 166, 207, 208, 209, 
+	210, 5, 171, 172, 173, 166, 166, 29, 
+	211, 166, 166, 205, 166, 212, 208, 213, 
+	213, 5, 171, 172, 173, 166, 166, 166, 
+	211, 166, 208, 213, 213, 5, 171, 172, 
+	173, 166, 166, 166, 211, 166, 18, 214, 
+	166, 171, 172, 166, 166, 166, 166, 179, 
+	166, 171, 172, 166, 166, 166, 166, 179, 
+	166, 215, 166, 166, 166, 172, 166, 172, 
+	166, 216, 166, 217, 166, 218, 219, 166, 
+	171, 172, 166, 166, 166, 3, 166, 166, 
+	166, 1, 166, 2, 166, 166, 166, 166, 
+	171, 172, 166, 171, 172, 166, 217, 166, 
+	166, 166, 166, 171, 172, 166, 217, 166, 
+	218, 166, 166, 171, 172, 166, 166, 166, 
+	3, 166, 18, 166, 220, 220, 5, 171, 
+	172, 166, 166, 166, 166, 179, 166, 221, 
+	27, 222, 223, 8, 171, 172, 166, 166, 
+	166, 166, 179, 166, 27, 222, 223, 8, 
 	171, 172, 166, 166, 166, 166, 179, 166, 
-	187, 166, 180, 181, 186, 186, 5, 171, 
-	172, 173, 166, 166, 166, 184, 166, 180, 
-	181, 182, 186, 5, 171, 172, 173, 166, 
-	166, 35, 184, 166, 177, 166, 239, 166, 
-	220, 220, 5, 171, 172, 166, 166, 166, 
-	166, 179, 166, 177, 166, 177, 166, 166, 
+	222, 222, 8, 171, 172, 166, 166, 166, 
+	166, 179, 166, 224, 24, 225, 226, 11, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	24, 225, 226, 11, 171, 172, 166, 166, 
+	166, 166, 179, 166, 225, 225, 11, 171, 
+	172, 166, 166, 166, 166, 179, 166, 227, 
+	21, 228, 229, 14, 171, 172, 166, 166, 
+	166, 166, 179, 166, 21, 228, 229, 14, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	228, 228, 14, 171, 172, 166, 166, 166, 
+	166, 179, 166, 230, 18, 166, 231, 166, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	18, 166, 231, 166, 171, 172, 166, 166, 
+	166, 166, 179, 166, 232, 166, 171, 172, 
+	166, 166, 166, 166, 179, 166, 18, 166, 
+	166, 166, 166, 171, 172, 166, 166, 166, 
+	166, 179, 166, 208, 213, 213, 5, 171, 
+	172, 166, 166, 166, 166, 211, 166, 1, 
+	2, 166, 166, 18, 214, 166, 171, 172, 
+	166, 166, 166, 166, 179, 166, 1, 166, 
+	207, 208, 213, 213, 5, 171, 172, 173, 
+	166, 166, 166, 211, 166, 207, 208, 209, 
+	213, 5, 171, 172, 173, 166, 166, 29, 
+	211, 166, 205, 166, 233, 166, 220, 220, 
+	5, 171, 172, 166, 166, 166, 166, 179, 
+	166, 205, 166, 205, 166, 166, 166, 166, 
+	166, 166, 171, 172, 166, 166, 166, 166, 
+	179, 166, 205, 166, 205, 166, 166, 166, 
+	166, 234, 166, 171, 172, 166, 166, 166, 
+	166, 179, 166, 205, 166, 205, 166, 233, 
 	166, 166, 166, 166, 171, 172, 166, 166, 
-	166, 166, 179, 166, 177, 166, 177, 166, 
-	166, 166, 166, 240, 166, 171, 172, 166, 
-	166, 166, 166, 179, 166, 177, 166, 177, 
-	166, 239, 166, 166, 166, 166, 171, 172, 
+	166, 166, 179, 166, 205, 166, 205, 2, 
+	166, 166, 18, 206, 166, 171, 172, 166, 
+	166, 166, 166, 179, 166, 205, 166, 198, 
+	199, 204, 204, 5, 171, 172, 173, 166, 
+	166, 166, 202, 166, 198, 199, 200, 204, 
+	5, 171, 172, 173, 166, 166, 31, 202, 
+	166, 196, 166, 235, 166, 220, 220, 5, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	196, 166, 196, 166, 166, 166, 166, 166, 
+	166, 171, 172, 166, 166, 166, 166, 179, 
+	166, 196, 166, 196, 166, 166, 166, 166, 
+	236, 166, 171, 172, 166, 166, 166, 166, 
+	179, 166, 196, 166, 196, 166, 235, 166, 
+	166, 166, 166, 171, 172, 166, 166, 166, 
+	166, 179, 166, 196, 166, 196, 2, 166, 
+	166, 18, 197, 166, 171, 172, 166, 166, 
+	166, 166, 179, 166, 196, 166, 189, 190, 
+	195, 195, 5, 171, 172, 173, 166, 166, 
+	166, 193, 166, 189, 190, 191, 195, 5, 
+	171, 172, 173, 166, 166, 33, 193, 166, 
+	187, 166, 237, 166, 220, 220, 5, 171, 
+	172, 166, 166, 166, 166, 179, 166, 187, 
+	166, 187, 166, 166, 166, 166, 166, 166, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	187, 166, 187, 166, 166, 166, 166, 238, 
+	166, 171, 172, 166, 166, 166, 166, 179, 
+	166, 187, 166, 187, 166, 237, 166, 166, 
+	166, 166, 171, 172, 166, 166, 166, 166, 
+	179, 166, 187, 166, 187, 2, 166, 166, 
+	18, 188, 166, 171, 172, 166, 166, 166, 
+	166, 179, 166, 187, 166, 180, 181, 186, 
+	186, 5, 171, 172, 173, 166, 166, 166, 
+	184, 166, 180, 181, 182, 186, 5, 171, 
+	172, 173, 166, 166, 35, 184, 166, 177, 
+	166, 239, 166, 220, 220, 5, 171, 172, 
 	166, 166, 166, 166, 179, 166, 177, 166, 
-	177, 2, 166, 166, 18, 178, 166, 171, 
+	177, 166, 166, 166, 166, 166, 166, 171, 
 	172, 166, 166, 166, 166, 179, 166, 177, 
-	166, 167, 168, 176, 176, 5, 171, 172, 
-	173, 166, 166, 166, 174, 166, 167, 168, 
-	169, 176, 5, 171, 172, 173, 166, 166, 
-	37, 174, 166, 242, 243, 244, 245, 43, 
-	246, 247, 241, 241, 241, 75, 248, 241, 
-	249, 243, 250, 245, 43, 246, 247, 241, 
-	241, 241, 241, 248, 241, 243, 250, 245, 
-	43, 246, 247, 241, 241, 241, 241, 248, 
-	241, 251, 241, 241, 241, 56, 252, 241, 
-	246, 247, 241, 241, 241, 241, 253, 241, 
-	251, 241, 254, 255, 256, 257, 43, 246, 
-	247, 241, 241, 241, 73, 258, 241, 259, 
-	255, 260, 260, 43, 246, 247, 241, 241, 
-	241, 241, 258, 241, 255, 260, 260, 43, 
-	246, 247, 241, 241, 241, 241, 258, 241, 
-	261, 241, 241, 241, 56, 262, 241, 246, 
-	247, 241, 241, 241, 241, 253, 241, 261, 
-	241, 263, 264, 265, 266, 43, 246, 247, 
-	241, 241, 241, 71, 267, 241, 268, 264, 
-	269, 269, 43, 246, 247, 241, 241, 241, 
-	241, 267, 241, 264, 269, 269, 43, 246, 
-	247, 241, 241, 241, 241, 267, 241, 270, 
-	241, 241, 241, 56, 271, 241, 246, 247, 
-	241, 241, 241, 241, 253, 241, 270, 241, 
-	272, 273, 274, 275, 43, 246, 247, 241, 
-	241, 241, 69, 276, 241, 277, 273, 278, 
-	278, 43, 246, 247, 241, 241, 241, 241, 
-	276, 241, 273, 278, 278, 43, 246, 247, 
-	241, 241, 241, 241, 276, 241, 279, 241, 
-	241, 241, 56, 280, 241, 246, 247, 241, 
-	241, 241, 241, 253, 241, 279, 241, 281, 
-	282, 283, 284, 43, 246, 247, 241, 241, 
-	241, 67, 285, 241, 286, 282, 287, 287, 
-	43, 246, 247, 241, 241, 241, 241, 285, 
-	241, 282, 287, 287, 43, 246, 247, 241, 
-	241, 241, 241, 285, 241, 56, 288, 241, 
-	246, 247, 241, 241, 241, 241, 253, 241, 
-	246, 247, 241, 241, 241, 241, 253, 241, 
-	289, 241, 241, 241, 247, 241, 247, 241, 
-	290, 241, 291, 241, 292, 293, 241, 246, 
-	247, 241, 241, 241, 41, 241, 40, 241, 
-	241, 241, 241, 246, 247, 241, 246, 247, 
-	241, 291, 241, 241, 241, 241, 246, 247, 
-	241, 291, 241, 292, 241, 241, 246, 247, 
-	241, 241, 241, 41, 241, 56, 241, 294, 
-	294, 43, 246, 247, 241, 241, 241, 241, 
-	253, 241, 295, 65, 296, 297, 46, 246, 
-	247, 241, 241, 241, 241, 253, 241, 65, 
-	296, 297, 46, 246, 247, 241, 241, 241, 
-	241, 253, 241, 296, 296, 46, 246, 247, 
-	241, 241, 241, 241, 253, 241, 298, 62, 
-	299, 300, 49, 246, 247, 241, 241, 241, 
-	241, 253, 241, 62, 299, 300, 49, 246, 
-	247, 241, 241, 241, 241, 253, 241, 299, 
-	299, 49, 246, 247, 241, 241, 241, 241, 
-	253, 241, 301, 59, 302, 303, 52, 246, 
-	247, 241, 241, 241, 241, 253, 241, 59, 
-	302, 303, 52, 246, 247, 241, 241, 241, 
-	241, 253, 241, 302, 302, 52, 246, 247, 
-	241, 241, 241, 241, 253, 241, 304, 56, 
-	241, 305, 241, 246, 247, 241, 241, 241, 
-	241, 253, 241, 56, 241, 305, 241, 246, 
-	247, 241, 241, 241, 241, 253, 241, 306, 
-	241, 246, 247, 241, 241, 241, 241, 253, 
-	241, 56, 241, 241, 241, 241, 246, 247, 
-	241, 241, 241, 241, 253, 241, 39, 40, 
-	241, 241, 56, 288, 241, 246, 247, 241, 
-	241, 241, 241, 253, 241, 39, 241, 281, 
-	282, 287, 287, 43, 246, 247, 241, 241, 
-	241, 241, 285, 241, 281, 282, 283, 287, 
-	43, 246, 247, 241, 241, 241, 67, 285, 
-	241, 279, 241, 307, 241, 294, 294, 43, 
-	246, 247, 241, 241, 241, 241, 253, 241, 
-	279, 241, 279, 241, 241, 241, 241, 241, 
-	241, 246, 247, 241, 241, 241, 241, 253, 
-	241, 279, 241, 279, 241, 241, 241, 241, 
-	308, 241, 246, 247, 241, 241, 241, 241, 
-	253, 241, 279, 241, 279, 241, 307, 241, 
-	241, 241, 241, 246, 247, 241, 241, 241, 
-	241, 253, 241, 279, 241, 279, 40, 241, 
-	241, 56, 280, 241, 246, 247, 241, 241, 
-	241, 241, 253, 241, 279, 241, 272, 273, 
-	278, 278, 43, 246, 247, 241, 241, 241, 
-	241, 276, 241, 272, 273, 274, 278, 43, 
-	246, 247, 241, 241, 241, 69, 276, 241, 
-	270, 241, 309, 241, 294, 294, 43, 246, 
-	247, 241, 241, 241, 241, 253, 241, 270, 
-	241, 270, 241, 241, 241, 241, 241, 241, 
-	246, 247, 241, 241, 241, 241, 253, 241, 
-	270, 241, 270, 241, 241, 241, 241, 310, 
-	241, 246, 247, 241, 241, 241, 241, 253, 
-	241, 270, 241, 270, 241, 309, 241, 241, 
-	241, 241, 246, 247, 241, 241, 241, 241, 
-	253, 241, 270, 241, 270, 40, 241, 241, 
-	56, 271, 241, 246, 247, 241, 241, 241, 
-	241, 253, 241, 270, 241, 263, 264, 269, 
-	269, 43, 246, 247, 241, 241, 241, 241, 
-	267, 241, 263, 264, 265, 269, 43, 246, 
-	247, 241, 241, 241, 71, 267, 241, 261, 
-	241, 311, 241, 294, 294, 43, 246, 247, 
-	241, 241, 241, 241, 253, 241, 261, 241, 
-	261, 241, 241, 241, 241, 241, 241, 246, 
-	247, 241, 241, 241, 241, 253, 241, 261, 
-	241, 261, 241, 241, 241, 241, 312, 241, 
-	246, 247, 241, 241, 241, 241, 253, 241, 
-	261, 241, 261, 241, 311, 241, 241, 241, 
-	241, 246, 247, 241, 241, 241, 241, 253, 
-	241, 261, 241, 261, 40, 241, 241, 56, 
-	262, 241, 246, 247, 241, 241, 241, 241, 
-	253, 241, 261, 241, 254, 255, 260, 260, 
-	43, 246, 247, 241, 241, 241, 241, 258, 
-	241, 254, 255, 256, 260, 43, 246, 247, 
-	241, 241, 241, 73, 258, 241, 251, 241, 
-	313, 241, 294, 294, 43, 246, 247, 241, 
-	241, 241, 241, 253, 241, 251, 241, 251, 
-	241, 241, 241, 241, 241, 241, 246, 247, 
-	241, 241, 241, 241, 253, 241, 251, 241, 
-	251, 241, 241, 241, 241, 314, 241, 246, 
-	247, 241, 241, 241, 241, 253, 241, 251, 
-	241, 251, 241, 313, 241, 241, 241, 241, 
-	246, 247, 241, 241, 241, 241, 253, 241, 
-	251, 241, 74, 42, 42, 43, 241, 241, 
-	241, 241, 241, 241, 74, 241, 251, 40, 
-	241, 241, 56, 252, 241, 246, 247, 241, 
-	241, 241, 241, 253, 241, 251, 241, 242, 
+	166, 177, 166, 166, 166, 166, 240, 166, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	177, 166, 177, 166, 239, 166, 166, 166, 
+	166, 171, 172, 166, 166, 166, 166, 179, 
+	166, 177, 166, 177, 2, 166, 166, 18, 
+	178, 166, 171, 172, 166, 166, 166, 166, 
+	179, 166, 177, 166, 167, 168, 176, 176, 
+	5, 171, 172, 173, 166, 166, 166, 174, 
+	166, 167, 168, 169, 176, 5, 171, 172, 
+	173, 166, 166, 37, 174, 166, 242, 243, 
+	244, 245, 43, 246, 247, 241, 241, 241, 
+	75, 248, 241, 249, 243, 250, 245, 43, 
+	246, 247, 241, 241, 241, 241, 248, 241, 
 	243, 250, 245, 43, 246, 247, 241, 241, 
-	241, 241, 248, 241, 316, 156, 317, 317, 
-	81, 159, 160, 315, 315, 315, 315, 163, 
-	315, 156, 317, 317, 81, 159, 160, 315, 
-	315, 315, 315, 163, 315, 318, 315, 315, 
-	315, 95, 319, 315, 159, 160, 315, 315, 
-	315, 315, 320, 315, 318, 315, 321, 322, 
-	323, 324, 81, 159, 160, 315, 315, 315, 
-	112, 325, 315, 326, 322, 327, 327, 81, 
-	159, 160, 315, 315, 315, 315, 325, 315, 
-	322, 327, 327, 81, 159, 160, 315, 315, 
-	315, 315, 325, 315, 328, 315, 315, 315, 
-	95, 329, 315, 159, 160, 315, 315, 315, 
-	315, 320, 315, 328, 315, 330, 331, 332, 
-	333, 81, 159, 160, 315, 315, 315, 110, 
-	334, 315, 335, 331, 336, 336, 81, 159, 
-	160, 315, 315, 315, 315, 334, 315, 331, 
-	336, 336, 81, 159, 160, 315, 315, 315, 
-	315, 334, 315, 337, 315, 315, 315, 95, 
-	338, 315, 159, 160, 315, 315, 315, 315, 
-	320, 315, 337, 315, 339, 340, 341, 342, 
-	81, 159, 160, 315, 315, 315, 108, 343, 
-	315, 344, 340, 345, 345, 81, 159, 160, 
-	315, 315, 315, 315, 343, 315, 340, 345, 
-	345, 81, 159, 160, 315, 315, 315, 315, 
-	343, 315, 346, 315, 315, 315, 95, 347, 
-	315, 159, 160, 315, 315, 315, 315, 320, 
-	315, 346, 315, 348, 349, 350, 351, 81, 
-	159, 160, 315, 315, 315, 106, 352, 315, 
-	353, 349, 354, 354, 81, 159, 160, 315, 
-	315, 315, 315, 352, 315, 349, 354, 354, 
-	81, 159, 160, 315, 315, 315, 315, 352, 
-	315, 95, 355, 315, 159, 160, 315, 315, 
-	315, 315, 320, 315, 159, 160, 315, 315, 
-	315, 315, 320, 315, 356, 315, 315, 315, 
-	160, 315, 160, 315, 357, 315, 358, 315, 
-	359, 360, 315, 159, 160, 315, 315, 315, 
-	79, 315, 78, 315, 315, 315, 315, 159, 
-	160, 315, 159, 160, 315, 358, 315, 315, 
-	315, 315, 159, 160, 315, 358, 315, 359, 
-	315, 315, 159, 160, 315, 315, 315, 79, 
-	315, 95, 315, 361, 361, 81, 159, 160, 
-	315, 315, 315, 315, 320, 315, 362, 104, 
-	363, 364, 85, 159, 160, 315, 315, 315, 
-	315, 320, 315, 104, 363, 364, 85, 159, 
-	160, 315, 315, 315, 315, 320, 315, 363, 
-	363, 85, 159, 160, 315, 315, 315, 315, 
-	320, 315, 365, 101, 366, 367, 88, 159, 
-	160, 315, 315, 315, 315, 320, 315, 101, 
-	366, 367, 88, 159, 160, 315, 315, 315, 
-	315, 320, 315, 366, 366, 88, 159, 160, 
-	315, 315, 315, 315, 320, 315, 368, 98, 
-	369, 370, 91, 159, 160, 315, 315, 315, 
-	315, 320, 315, 98, 369, 370, 91, 159, 
-	160, 315, 315, 315, 315, 320, 315, 369, 
-	369, 91, 159, 160, 315, 315, 315, 315, 
-	320, 315, 371, 95, 315, 372, 315, 159, 
-	160, 315, 315, 315, 315, 320, 315, 95, 
-	315, 372, 315, 159, 160, 315, 315, 315, 
-	315, 320, 315, 373, 315, 159, 160, 315, 
-	315, 315, 315, 320, 315, 95, 315, 315, 
-	315, 315, 159, 160, 315, 315, 315, 315, 
-	320, 315, 77, 78, 315, 315, 95, 355, 
-	315, 159, 160, 315, 315, 315, 315, 320, 
-	315, 77, 315, 348, 349, 354, 354, 81, 
-	159, 160, 315, 315, 315, 315, 352, 315, 
-	348, 349, 350, 354, 81, 159, 160, 315, 
-	315, 315, 106, 352, 315, 346, 315, 374, 
-	315, 361, 361, 81, 159, 160, 315, 315, 
-	315, 315, 320, 315, 346, 315, 346, 315, 
-	315, 315, 315, 315, 315, 159, 160, 315, 
-	315, 315, 315, 320, 315, 346, 315, 346, 
-	315, 315, 315, 315, 375, 315, 159, 160, 
-	315, 315, 315, 315, 320, 315, 346, 315, 
-	346, 315, 374, 315, 315, 315, 315, 159, 
-	160, 315, 315, 315, 315, 320, 315, 346, 
-	315, 346, 78, 315, 315, 95, 347, 315, 
-	159, 160, 315, 315, 315, 315, 320, 315, 
-	346, 315, 339, 340, 345, 345, 81, 159, 
-	160, 315, 315, 315, 315, 343, 315, 339, 
-	340, 341, 345, 81, 159, 160, 315, 315, 
-	315, 108, 343, 315, 337, 315, 376, 315, 
-	361, 361, 81, 159, 160, 315, 315, 315, 
-	315, 320, 315, 337, 315, 337, 315, 315, 
-	315, 315, 315, 315, 159, 160, 315, 315, 
-	315, 315, 320, 315, 337, 315, 337, 315, 
-	315, 315, 315, 377, 315, 159, 160, 315, 
-	315, 315, 315, 320, 315, 337, 315, 337, 
-	315, 376, 315, 315, 315, 315, 159, 160, 
-	315, 315, 315, 315, 320, 315, 337, 315, 
-	337, 78, 315, 315, 95, 338, 315, 159, 
-	160, 315, 315, 315, 315, 320, 315, 337, 
-	315, 330, 331, 336, 336, 81, 159, 160, 
-	315, 315, 315, 315, 334, 315, 330, 331, 
-	332, 336, 81, 159, 160, 315, 315, 315, 
-	110, 334, 315, 328, 315, 378, 315, 361, 
-	361, 81, 159, 160, 315, 315, 315, 315, 
-	320, 315, 328, 315, 328, 315, 315, 315, 
-	315, 315, 315, 159, 160, 315, 315, 315, 
-	315, 320, 315, 328, 315, 328, 315, 315, 
-	315, 315, 379, 315, 159, 160, 315, 315, 
-	315, 315, 320, 315, 328, 315, 328, 315, 
-	378, 315, 315, 315, 315, 159, 160, 315, 
-	315, 315, 315, 320, 315, 328, 315, 328, 
-	78, 315, 315, 95, 329, 315, 159, 160, 
-	315, 315, 315, 315, 320, 315, 328, 315, 
-	321, 322, 327, 327, 81, 159, 160, 315, 
-	315, 315, 315, 325, 315, 321, 322, 323, 
-	327, 81, 159, 160, 315, 315, 315, 112, 
-	325, 315, 318, 315, 380, 315, 361, 361, 
-	81, 159, 160, 315, 315, 315, 315, 320, 
-	315, 318, 315, 318, 315, 315, 315, 315, 
-	315, 315, 159, 160, 315, 315, 315, 315, 
-	320, 315, 318, 315, 318, 315, 315, 315, 
-	315, 381, 315, 159, 160, 315, 315, 315, 
-	315, 320, 315, 318, 315, 318, 315, 380, 
-	315, 315, 315, 315, 159, 160, 315, 315, 
-	315, 315, 320, 315, 318, 315, 318, 78, 
-	315, 315, 95, 319, 315, 159, 160, 315, 
-	315, 315, 315, 320, 315, 318, 315, 113, 
-	80, 80, 81, 382, 382, 382, 382, 382, 
-	162, 113, 382, 155, 156, 317, 317, 81, 
+	241, 241, 248, 241, 251, 241, 241, 241, 
+	56, 252, 241, 246, 247, 241, 241, 241, 
+	241, 253, 241, 251, 241, 254, 255, 256, 
+	257, 43, 246, 247, 241, 241, 241, 73, 
+	258, 241, 241, 251, 241, 259, 255, 260, 
+	260, 43, 246, 247, 241, 241, 241, 241, 
+	258, 241, 255, 260, 260, 43, 246, 247, 
+	241, 241, 241, 241, 258, 241, 261, 241, 
+	241, 241, 56, 262, 241, 246, 247, 241, 
+	241, 241, 241, 253, 241, 261, 241, 263, 
+	264, 265, 266, 43, 246, 247, 241, 241, 
+	241, 71, 267, 241, 241, 261, 241, 268, 
+	264, 269, 269, 43, 246, 247, 241, 241, 
+	241, 241, 267, 241, 264, 269, 269, 43, 
+	246, 247, 241, 241, 241, 241, 267, 241, 
+	270, 241, 241, 241, 56, 271, 241, 246, 
+	247, 241, 241, 241, 241, 253, 241, 270, 
+	241, 272, 273, 274, 275, 43, 246, 247, 
+	241, 241, 241, 69, 276, 241, 241, 270, 
+	241, 277, 273, 278, 278, 43, 246, 247, 
+	241, 241, 241, 241, 276, 241, 273, 278, 
+	278, 43, 246, 247, 241, 241, 241, 241, 
+	276, 241, 279, 241, 241, 241, 56, 280, 
+	241, 246, 247, 241, 241, 241, 241, 253, 
+	241, 279, 241, 281, 282, 283, 284, 43, 
+	246, 247, 241, 241, 241, 67, 285, 241, 
+	241, 279, 241, 286, 282, 287, 287, 43, 
+	246, 247, 241, 241, 241, 241, 285, 241, 
+	282, 287, 287, 43, 246, 247, 241, 241, 
+	241, 241, 285, 241, 56, 288, 241, 246, 
+	247, 241, 241, 241, 241, 253, 241, 246, 
+	247, 241, 241, 241, 241, 253, 241, 289, 
+	241, 241, 241, 247, 241, 247, 241, 290, 
+	241, 291, 241, 292, 293, 241, 246, 247, 
+	241, 241, 241, 41, 241, 241, 241, 39, 
+	241, 40, 241, 241, 241, 241, 246, 247, 
+	241, 246, 247, 241, 291, 241, 241, 241, 
+	241, 246, 247, 241, 291, 241, 292, 241, 
+	241, 246, 247, 241, 241, 241, 41, 241, 
+	56, 241, 294, 294, 43, 246, 247, 241, 
+	241, 241, 241, 253, 241, 295, 65, 296, 
+	297, 46, 246, 247, 241, 241, 241, 241, 
+	253, 241, 65, 296, 297, 46, 246, 247, 
+	241, 241, 241, 241, 253, 241, 296, 296, 
+	46, 246, 247, 241, 241, 241, 241, 253, 
+	241, 298, 62, 299, 300, 49, 246, 247, 
+	241, 241, 241, 241, 253, 241, 62, 299, 
+	300, 49, 246, 247, 241, 241, 241, 241, 
+	253, 241, 299, 299, 49, 246, 247, 241, 
+	241, 241, 241, 253, 241, 301, 59, 302, 
+	303, 52, 246, 247, 241, 241, 241, 241, 
+	253, 241, 59, 302, 303, 52, 246, 247, 
+	241, 241, 241, 241, 253, 241, 302, 302, 
+	52, 246, 247, 241, 241, 241, 241, 253, 
+	241, 304, 56, 241, 305, 241, 246, 247, 
+	241, 241, 241, 241, 253, 241, 56, 241, 
+	305, 241, 246, 247, 241, 241, 241, 241, 
+	253, 241, 306, 241, 246, 247, 241, 241, 
+	241, 241, 253, 241, 56, 241, 241, 241, 
+	241, 246, 247, 241, 241, 241, 241, 253, 
+	241, 39, 40, 241, 241, 56, 288, 241, 
+	246, 247, 241, 241, 241, 241, 253, 241, 
+	39, 241, 281, 282, 287, 287, 43, 246, 
+	247, 241, 241, 241, 241, 285, 241, 281, 
+	282, 283, 287, 43, 246, 247, 241, 241, 
+	241, 67, 285, 241, 279, 241, 307, 241, 
+	294, 294, 43, 246, 247, 241, 241, 241, 
+	241, 253, 241, 279, 241, 279, 241, 241, 
+	241, 241, 241, 241, 246, 247, 241, 241, 
+	241, 241, 253, 241, 279, 241, 279, 241, 
+	241, 241, 241, 308, 241, 246, 247, 241, 
+	241, 241, 241, 253, 241, 279, 241, 279, 
+	241, 307, 241, 241, 241, 241, 246, 247, 
+	241, 241, 241, 241, 253, 241, 279, 241, 
+	279, 40, 241, 241, 56, 280, 241, 246, 
+	247, 241, 241, 241, 241, 253, 241, 279, 
+	241, 272, 273, 278, 278, 43, 246, 247, 
+	241, 241, 241, 241, 276, 241, 272, 273, 
+	274, 278, 43, 246, 247, 241, 241, 241, 
+	69, 276, 241, 270, 241, 309, 241, 294, 
+	294, 43, 246, 247, 241, 241, 241, 241, 
+	253, 241, 270, 241, 270, 241, 241, 241, 
+	241, 241, 241, 246, 247, 241, 241, 241, 
+	241, 253, 241, 270, 241, 270, 241, 241, 
+	241, 241, 310, 241, 246, 247, 241, 241, 
+	241, 241, 253, 241, 270, 241, 270, 241, 
+	309, 241, 241, 241, 241, 246, 247, 241, 
+	241, 241, 241, 253, 241, 270, 241, 270, 
+	40, 241, 241, 56, 271, 241, 246, 247, 
+	241, 241, 241, 241, 253, 241, 270, 241, 
+	263, 264, 269, 269, 43, 246, 247, 241, 
+	241, 241, 241, 267, 241, 263, 264, 265, 
+	269, 43, 246, 247, 241, 241, 241, 71, 
+	267, 241, 261, 241, 311, 241, 294, 294, 
+	43, 246, 247, 241, 241, 241, 241, 253, 
+	241, 261, 241, 261, 241, 241, 241, 241, 
+	241, 241, 246, 247, 241, 241, 241, 241, 
+	253, 241, 261, 241, 261, 241, 241, 241, 
+	241, 312, 241, 246, 247, 241, 241, 241, 
+	241, 253, 241, 261, 241, 261, 241, 311, 
+	241, 241, 241, 241, 246, 247, 241, 241, 
+	241, 241, 253, 241, 261, 241, 261, 40, 
+	241, 241, 56, 262, 241, 246, 247, 241, 
+	241, 241, 241, 253, 241, 261, 241, 254, 
+	255, 260, 260, 43, 246, 247, 241, 241, 
+	241, 241, 258, 241, 254, 255, 256, 260, 
+	43, 246, 247, 241, 241, 241, 73, 258, 
+	241, 251, 241, 313, 241, 294, 294, 43, 
+	246, 247, 241, 241, 241, 241, 253, 241, 
+	251, 241, 251, 241, 241, 241, 241, 241, 
+	241, 246, 247, 241, 241, 241, 241, 253, 
+	241, 251, 241, 251, 241, 241, 241, 241, 
+	314, 241, 246, 247, 241, 241, 241, 241, 
+	253, 241, 251, 241, 251, 241, 313, 241, 
+	241, 241, 241, 246, 247, 241, 241, 241, 
+	241, 253, 241, 251, 241, 74, 42, 42, 
+	43, 241, 241, 241, 241, 241, 241, 74, 
+	241, 251, 40, 241, 241, 56, 252, 241, 
+	246, 247, 241, 241, 241, 241, 253, 241, 
+	251, 241, 242, 243, 250, 245, 43, 246, 
+	247, 241, 241, 241, 241, 248, 241, 316, 
+	156, 317, 317, 81, 159, 160, 315, 315, 
+	315, 315, 163, 315, 156, 317, 317, 81, 
 	159, 160, 315, 315, 315, 315, 163, 315, 
-	113, 80, 80, 81, 382, 382, 382, 382, 
-	382, 382, 113, 382, 384, 385, 386, 387, 
-	119, 388, 389, 383, 383, 383, 151, 390, 
-	383, 391, 385, 387, 387, 119, 388, 389, 
-	383, 383, 383, 383, 390, 383, 385, 387, 
-	387, 119, 388, 389, 383, 383, 383, 383, 
-	390, 383, 392, 383, 383, 383, 132, 393, 
-	383, 388, 389, 383, 383, 383, 383, 394, 
-	383, 392, 383, 395, 396, 397, 398, 119, 
-	388, 389, 383, 383, 383, 149, 399, 383, 
-	400, 396, 401, 401, 119, 388, 389, 383, 
-	383, 383, 383, 399, 383, 396, 401, 401, 
-	119, 388, 389, 383, 383, 383, 383, 399, 
-	383, 402, 383, 383, 383, 132, 403, 383, 
-	388, 389, 383, 383, 383, 383, 394, 383, 
-	402, 383, 404, 405, 406, 407, 119, 388, 
-	389, 383, 383, 383, 147, 408, 383, 409, 
-	405, 410, 410, 119, 388, 389, 383, 383, 
-	383, 383, 408, 383, 405, 410, 410, 119, 
-	388, 389, 383, 383, 383, 383, 408, 383, 
-	411, 383, 383, 383, 132, 412, 383, 388, 
-	389, 383, 383, 383, 383, 394, 383, 411, 
-	383, 413, 414, 415, 416, 119, 388, 389, 
-	383, 383, 383, 145, 417, 383, 418, 414, 
-	419, 419, 119, 388, 389, 383, 383, 383, 
-	383, 417, 383, 414, 419, 419, 119, 388, 
-	389, 383, 383, 383, 383, 417, 383, 420, 
-	383, 383, 383, 132, 421, 383, 388, 389, 
-	383, 383, 383, 383, 394, 383, 420, 383, 
-	422, 423, 424, 425, 119, 388, 389, 383, 
-	383, 383, 143, 426, 383, 427, 423, 428, 
-	428, 119, 388, 389, 383, 383, 383, 383, 
-	426, 383, 423, 428, 428, 119, 388, 389, 
-	383, 383, 383, 383, 426, 383, 132, 429, 
-	383, 388, 389, 383, 383, 383, 383, 394, 
-	383, 388, 389, 383, 383, 383, 383, 394, 
-	383, 430, 383, 383, 383, 389, 383, 389, 
-	383, 431, 383, 432, 383, 433, 434, 383, 
-	388, 389, 383, 383, 383, 117, 383, 116, 
-	383, 383, 383, 383, 388, 389, 383, 388, 
-	389, 383, 432, 383, 383, 383, 383, 388, 
-	389, 383, 432, 383, 433, 383, 383, 388, 
-	389, 383, 383, 383, 117, 383, 132, 383, 
-	435, 435, 119, 388, 389, 383, 383, 383, 
-	383, 394, 383, 436, 141, 437, 438, 122, 
-	388, 389, 383, 383, 383, 383, 394, 383, 
-	141, 437, 438, 122, 388, 389, 383, 383, 
-	383, 383, 394, 383, 437, 437, 122, 388, 
-	389, 383, 383, 383, 383, 394, 383, 439, 
-	138, 440, 441, 125, 388, 389, 383, 383, 
-	383, 383, 394, 383, 138, 440, 441, 125, 
-	388, 389, 383, 383, 383, 383, 394, 383, 
-	440, 440, 125, 388, 389, 383, 383, 383, 
-	383, 394, 383, 442, 135, 443, 444, 128, 
-	388, 389, 383, 383, 383, 383, 394, 383, 
-	135, 443, 444, 128, 388, 389, 383, 383, 
-	383, 383, 394, 383, 443, 443, 128, 388, 
-	389, 383, 383, 383, 383, 394, 383, 445, 
-	132, 383, 446, 383, 388, 389, 383, 383, 
-	383, 383, 394, 383, 132, 383, 446, 383, 
-	388, 389, 383, 383, 383, 383, 394, 383, 
-	447, 383, 388, 389, 383, 383, 383, 383, 
-	394, 383, 132, 383, 383, 383, 383, 388, 
-	389, 383, 383, 383, 383, 394, 383, 115, 
-	116, 383, 383, 132, 429, 383, 388, 389, 
-	383, 383, 383, 383, 394, 383, 115, 383, 
-	422, 423, 428, 428, 119, 388, 389, 383, 
-	383, 383, 383, 426, 383, 422, 423, 424, 
-	428, 119, 388, 389, 383, 383, 383, 143, 
-	426, 383, 420, 383, 448, 383, 435, 435, 
-	119, 388, 389, 383, 383, 383, 383, 394, 
-	383, 420, 383, 420, 383, 383, 383, 383, 
-	383, 383, 388, 389, 383, 383, 383, 383, 
-	394, 383, 420, 383, 420, 383, 383, 383, 
-	383, 449, 383, 388, 389, 383, 383, 383, 
-	383, 394, 383, 420, 383, 420, 383, 448, 
-	383, 383, 383, 383, 388, 389, 383, 383, 
-	383, 383, 394, 383, 420, 383, 420, 116, 
-	383, 383, 132, 421, 383, 388, 389, 383, 
-	383, 383, 383, 394, 383, 420, 383, 413, 
-	414, 419, 419, 119, 388, 389, 383, 383, 
-	383, 383, 417, 383, 413, 414, 415, 419, 
-	119, 388, 389, 383, 383, 383, 145, 417, 
-	383, 411, 383, 450, 383, 435, 435, 119, 
-	388, 389, 383, 383, 383, 383, 394, 383, 
-	411, 383, 411, 383, 383, 383, 383, 383, 
-	383, 388, 389, 383, 383, 383, 383, 394, 
-	383, 411, 383, 411, 383, 383, 383, 383, 
-	451, 383, 388, 389, 383, 383, 383, 383, 
-	394, 383, 411, 383, 411, 383, 450, 383, 
-	383, 383, 383, 388, 389, 383, 383, 383, 
-	383, 394, 383, 411, 383, 411, 116, 383, 
-	383, 132, 412, 383, 388, 389, 383, 383, 
-	383, 383, 394, 383, 411, 383, 404, 405, 
-	410, 410, 119, 388, 389, 383, 383, 383, 
-	383, 408, 383, 404, 405, 406, 410, 119, 
-	388, 389, 383, 383, 383, 147, 408, 383, 
-	402, 383, 452, 383, 435, 435, 119, 388, 
-	389, 383, 383, 383, 383, 394, 383, 402, 
-	383, 402, 383, 383, 383, 383, 383, 383, 
-	388, 389, 383, 383, 383, 383, 394, 383, 
-	402, 383, 402, 383, 383, 383, 383, 453, 
-	383, 388, 389, 383, 383, 383, 383, 394, 
-	383, 402, 383, 402, 383, 452, 383, 383, 
-	383, 383, 388, 389, 383, 383, 383, 383, 
-	394, 383, 402, 383, 402, 116, 383, 383, 
-	132, 403, 383, 388, 389, 383, 383, 383, 
-	383, 394, 383, 402, 383, 395, 396, 401, 
-	401, 119, 388, 389, 383, 383, 383, 383, 
-	399, 383, 395, 396, 397, 401, 119, 388, 
-	389, 383, 383, 383, 149, 399, 383, 392, 
-	383, 454, 383, 435, 435, 119, 388, 389, 
-	383, 383, 383, 383, 394, 383, 392, 383, 
-	392, 383, 383, 383, 383, 383, 383, 388, 
-	389, 383, 383, 383, 383, 394, 383, 392, 
-	383, 392, 383, 383, 383, 383, 455, 383, 
-	388, 389, 383, 383, 383, 383, 394, 383, 
-	392, 383, 392, 383, 454, 383, 383, 383, 
-	383, 388, 389, 383, 383, 383, 383, 394, 
-	383, 392, 383, 392, 116, 383, 383, 132, 
-	393, 383, 388, 389, 383, 383, 383, 383, 
-	394, 383, 392, 383, 384, 385, 387, 387, 
+	318, 315, 315, 315, 95, 319, 315, 159, 
+	160, 315, 315, 315, 315, 320, 315, 318, 
+	315, 321, 322, 323, 324, 81, 159, 160, 
+	315, 315, 315, 112, 325, 315, 315, 318, 
+	315, 326, 322, 327, 327, 81, 159, 160, 
+	315, 315, 315, 315, 325, 315, 322, 327, 
+	327, 81, 159, 160, 315, 315, 315, 315, 
+	325, 315, 328, 315, 315, 315, 95, 329, 
+	315, 159, 160, 315, 315, 315, 315, 320, 
+	315, 328, 315, 330, 331, 332, 333, 81, 
+	159, 160, 315, 315, 315, 110, 334, 315, 
+	315, 328, 315, 335, 331, 336, 336, 81, 
+	159, 160, 315, 315, 315, 315, 334, 315, 
+	331, 336, 336, 81, 159, 160, 315, 315, 
+	315, 315, 334, 315, 337, 315, 315, 315, 
+	95, 338, 315, 159, 160, 315, 315, 315, 
+	315, 320, 315, 337, 315, 339, 340, 341, 
+	342, 81, 159, 160, 315, 315, 315, 108, 
+	343, 315, 315, 337, 315, 344, 340, 345, 
+	345, 81, 159, 160, 315, 315, 315, 315, 
+	343, 315, 340, 345, 345, 81, 159, 160, 
+	315, 315, 315, 315, 343, 315, 346, 315, 
+	315, 315, 95, 347, 315, 159, 160, 315, 
+	315, 315, 315, 320, 315, 346, 315, 348, 
+	349, 350, 351, 81, 159, 160, 315, 315, 
+	315, 106, 352, 315, 315, 346, 315, 353, 
+	349, 354, 354, 81, 159, 160, 315, 315, 
+	315, 315, 352, 315, 349, 354, 354, 81, 
+	159, 160, 315, 315, 315, 315, 352, 315, 
+	95, 355, 315, 159, 160, 315, 315, 315, 
+	315, 320, 315, 159, 160, 315, 315, 315, 
+	315, 320, 315, 356, 315, 315, 315, 160, 
+	315, 160, 315, 357, 315, 358, 315, 359, 
+	360, 315, 159, 160, 315, 315, 315, 79, 
+	315, 315, 315, 77, 315, 78, 315, 315, 
+	315, 315, 159, 160, 315, 159, 160, 315, 
+	358, 315, 315, 315, 315, 159, 160, 315, 
+	358, 315, 359, 315, 315, 159, 160, 315, 
+	315, 315, 79, 315, 95, 315, 361, 361, 
+	81, 159, 160, 315, 315, 315, 315, 320, 
+	315, 362, 104, 363, 364, 85, 159, 160, 
+	315, 315, 315, 315, 320, 315, 104, 363, 
+	364, 85, 159, 160, 315, 315, 315, 315, 
+	320, 315, 363, 363, 85, 159, 160, 315, 
+	315, 315, 315, 320, 315, 365, 101, 366, 
+	367, 88, 159, 160, 315, 315, 315, 315, 
+	320, 315, 101, 366, 367, 88, 159, 160, 
+	315, 315, 315, 315, 320, 315, 366, 366, 
+	88, 159, 160, 315, 315, 315, 315, 320, 
+	315, 368, 98, 369, 370, 91, 159, 160, 
+	315, 315, 315, 315, 320, 315, 98, 369, 
+	370, 91, 159, 160, 315, 315, 315, 315, 
+	320, 315, 369, 369, 91, 159, 160, 315, 
+	315, 315, 315, 320, 315, 371, 95, 315, 
+	372, 315, 159, 160, 315, 315, 315, 315, 
+	320, 315, 95, 315, 372, 315, 159, 160, 
+	315, 315, 315, 315, 320, 315, 373, 315, 
+	159, 160, 315, 315, 315, 315, 320, 315, 
+	95, 315, 315, 315, 315, 159, 160, 315, 
+	315, 315, 315, 320, 315, 77, 78, 315, 
+	315, 95, 355, 315, 159, 160, 315, 315, 
+	315, 315, 320, 315, 77, 315, 348, 349, 
+	354, 354, 81, 159, 160, 315, 315, 315, 
+	315, 352, 315, 348, 349, 350, 354, 81, 
+	159, 160, 315, 315, 315, 106, 352, 315, 
+	346, 315, 374, 315, 361, 361, 81, 159, 
+	160, 315, 315, 315, 315, 320, 315, 346, 
+	315, 346, 315, 315, 315, 315, 315, 315, 
+	159, 160, 315, 315, 315, 315, 320, 315, 
+	346, 315, 346, 315, 315, 315, 315, 375, 
+	315, 159, 160, 315, 315, 315, 315, 320, 
+	315, 346, 315, 346, 315, 374, 315, 315, 
+	315, 315, 159, 160, 315, 315, 315, 315, 
+	320, 315, 346, 315, 346, 78, 315, 315, 
+	95, 347, 315, 159, 160, 315, 315, 315, 
+	315, 320, 315, 346, 315, 339, 340, 345, 
+	345, 81, 159, 160, 315, 315, 315, 315, 
+	343, 315, 339, 340, 341, 345, 81, 159, 
+	160, 315, 315, 315, 108, 343, 315, 337, 
+	315, 376, 315, 361, 361, 81, 159, 160, 
+	315, 315, 315, 315, 320, 315, 337, 315, 
+	337, 315, 315, 315, 315, 315, 315, 159, 
+	160, 315, 315, 315, 315, 320, 315, 337, 
+	315, 337, 315, 315, 315, 315, 377, 315, 
+	159, 160, 315, 315, 315, 315, 320, 315, 
+	337, 315, 337, 315, 376, 315, 315, 315, 
+	315, 159, 160, 315, 315, 315, 315, 320, 
+	315, 337, 315, 337, 78, 315, 315, 95, 
+	338, 315, 159, 160, 315, 315, 315, 315, 
+	320, 315, 337, 315, 330, 331, 336, 336, 
+	81, 159, 160, 315, 315, 315, 315, 334, 
+	315, 330, 331, 332, 336, 81, 159, 160, 
+	315, 315, 315, 110, 334, 315, 328, 315, 
+	378, 315, 361, 361, 81, 159, 160, 315, 
+	315, 315, 315, 320, 315, 328, 315, 328, 
+	315, 315, 315, 315, 315, 315, 159, 160, 
+	315, 315, 315, 315, 320, 315, 328, 315, 
+	328, 315, 315, 315, 315, 379, 315, 159, 
+	160, 315, 315, 315, 315, 320, 315, 328, 
+	315, 328, 315, 378, 315, 315, 315, 315, 
+	159, 160, 315, 315, 315, 315, 320, 315, 
+	328, 315, 328, 78, 315, 315, 95, 329, 
+	315, 159, 160, 315, 315, 315, 315, 320, 
+	315, 328, 315, 321, 322, 327, 327, 81, 
+	159, 160, 315, 315, 315, 315, 325, 315, 
+	321, 322, 323, 327, 81, 159, 160, 315, 
+	315, 315, 112, 325, 315, 318, 315, 380, 
+	315, 361, 361, 81, 159, 160, 315, 315, 
+	315, 315, 320, 315, 318, 315, 318, 315, 
+	315, 315, 315, 315, 315, 159, 160, 315, 
+	315, 315, 315, 320, 315, 318, 315, 318, 
+	315, 315, 315, 315, 381, 315, 159, 160, 
+	315, 315, 315, 315, 320, 315, 318, 315, 
+	318, 315, 380, 315, 315, 315, 315, 159, 
+	160, 315, 315, 315, 315, 320, 315, 318, 
+	315, 318, 78, 315, 315, 95, 319, 315, 
+	159, 160, 315, 315, 315, 315, 320, 315, 
+	318, 315, 113, 80, 80, 81, 382, 382, 
+	382, 382, 382, 162, 113, 382, 155, 156, 
+	317, 317, 81, 159, 160, 315, 315, 315, 
+	315, 163, 315, 113, 80, 80, 81, 382, 
+	382, 382, 382, 382, 382, 113, 382, 384, 
+	385, 386, 387, 119, 388, 389, 383, 383, 
+	383, 151, 390, 383, 391, 385, 387, 387, 
 	119, 388, 389, 383, 383, 383, 383, 390, 
-	383, 153, 154, 382, 382, 382, 382, 382, 
-	382, 382, 382, 161, 161, 382, 382, 382, 
-	153, 382, 167, 456, 169, 170, 5, 171, 
-	172, 173, 166, 166, 37, 174, 166, 177, 
-	154, 166, 166, 18, 178, 166, 171, 172, 
-	166, 161, 161, 166, 179, 166, 177, 166, 
-	0
+	383, 385, 387, 387, 119, 388, 389, 383, 
+	383, 383, 383, 390, 383, 392, 383, 383, 
+	383, 132, 393, 383, 388, 389, 383, 383, 
+	383, 383, 394, 383, 392, 383, 395, 396, 
+	397, 398, 119, 388, 389, 383, 383, 383, 
+	149, 399, 383, 383, 392, 383, 400, 396, 
+	401, 401, 119, 388, 389, 383, 383, 383, 
+	383, 399, 383, 396, 401, 401, 119, 388, 
+	389, 383, 383, 383, 383, 399, 383, 402, 
+	383, 383, 383, 132, 403, 383, 388, 389, 
+	383, 383, 383, 383, 394, 383, 402, 383, 
+	404, 405, 406, 407, 119, 388, 389, 383, 
+	383, 383, 147, 408, 383, 383, 402, 383, 
+	409, 405, 410, 410, 119, 388, 389, 383, 
+	383, 383, 383, 408, 383, 405, 410, 410, 
+	119, 388, 389, 383, 383, 383, 383, 408, 
+	383, 411, 383, 383, 383, 132, 412, 383, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	411, 383, 413, 414, 415, 416, 119, 388, 
+	389, 383, 383, 383, 145, 417, 383, 383, 
+	411, 383, 418, 414, 419, 419, 119, 388, 
+	389, 383, 383, 383, 383, 417, 383, 414, 
+	419, 419, 119, 388, 389, 383, 383, 383, 
+	383, 417, 383, 420, 383, 383, 383, 132, 
+	421, 383, 388, 389, 383, 383, 383, 383, 
+	394, 383, 420, 383, 422, 423, 424, 425, 
+	119, 388, 389, 383, 383, 383, 143, 426, 
+	383, 383, 420, 383, 427, 423, 428, 428, 
+	119, 388, 389, 383, 383, 383, 383, 426, 
+	383, 423, 428, 428, 119, 388, 389, 383, 
+	383, 383, 383, 426, 383, 132, 429, 383, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	430, 383, 383, 383, 389, 383, 389, 383, 
+	431, 383, 432, 383, 433, 434, 383, 388, 
+	389, 383, 383, 383, 117, 383, 383, 383, 
+	115, 383, 116, 383, 383, 383, 383, 388, 
+	389, 383, 388, 389, 383, 432, 383, 383, 
+	383, 383, 388, 389, 383, 432, 383, 433, 
+	383, 383, 388, 389, 383, 383, 383, 117, 
+	383, 132, 383, 435, 435, 119, 388, 389, 
+	383, 383, 383, 383, 394, 383, 436, 141, 
+	437, 438, 122, 388, 389, 383, 383, 383, 
+	383, 394, 383, 141, 437, 438, 122, 388, 
+	389, 383, 383, 383, 383, 394, 383, 437, 
+	437, 122, 388, 389, 383, 383, 383, 383, 
+	394, 383, 439, 138, 440, 441, 125, 388, 
+	389, 383, 383, 383, 383, 394, 383, 138, 
+	440, 441, 125, 388, 389, 383, 383, 383, 
+	383, 394, 383, 440, 440, 125, 388, 389, 
+	383, 383, 383, 383, 394, 383, 442, 135, 
+	443, 444, 128, 388, 389, 383, 383, 383, 
+	383, 394, 383, 135, 443, 444, 128, 388, 
+	389, 383, 383, 383, 383, 394, 383, 443, 
+	443, 128, 388, 389, 383, 383, 383, 383, 
+	394, 383, 445, 132, 383, 446, 383, 388, 
+	389, 383, 383, 383, 383, 394, 383, 132, 
+	383, 446, 383, 388, 389, 383, 383, 383, 
+	383, 394, 383, 447, 383, 388, 389, 383, 
+	383, 383, 383, 394, 383, 132, 383, 383, 
+	383, 383, 388, 389, 383, 383, 383, 383, 
+	394, 383, 115, 116, 383, 383, 132, 429, 
+	383, 388, 389, 383, 383, 383, 383, 394, 
+	383, 115, 383, 422, 423, 428, 428, 119, 
+	388, 389, 383, 383, 383, 383, 426, 383, 
+	422, 423, 424, 428, 119, 388, 389, 383, 
+	383, 383, 143, 426, 383, 420, 383, 448, 
+	383, 435, 435, 119, 388, 389, 383, 383, 
+	383, 383, 394, 383, 420, 383, 420, 383, 
+	383, 383, 383, 383, 383, 388, 389, 383, 
+	383, 383, 383, 394, 383, 420, 383, 420, 
+	383, 383, 383, 383, 449, 383, 388, 389, 
+	383, 383, 383, 383, 394, 383, 420, 383, 
+	420, 383, 448, 383, 383, 383, 383, 388, 
+	389, 383, 383, 383, 383, 394, 383, 420, 
+	383, 420, 116, 383, 383, 132, 421, 383, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	420, 383, 413, 414, 419, 419, 119, 388, 
+	389, 383, 383, 383, 383, 417, 383, 413, 
+	414, 415, 419, 119, 388, 389, 383, 383, 
+	383, 145, 417, 383, 411, 383, 450, 383, 
+	435, 435, 119, 388, 389, 383, 383, 383, 
+	383, 394, 383, 411, 383, 411, 383, 383, 
+	383, 383, 383, 383, 388, 389, 383, 383, 
+	383, 383, 394, 383, 411, 383, 411, 383, 
+	383, 383, 383, 451, 383, 388, 389, 383, 
+	383, 383, 383, 394, 383, 411, 383, 411, 
+	383, 450, 383, 383, 383, 383, 388, 389, 
+	383, 383, 383, 383, 394, 383, 411, 383, 
+	411, 116, 383, 383, 132, 412, 383, 388, 
+	389, 383, 383, 383, 383, 394, 383, 411, 
+	383, 404, 405, 410, 410, 119, 388, 389, 
+	383, 383, 383, 383, 408, 383, 404, 405, 
+	406, 410, 119, 388, 389, 383, 383, 383, 
+	147, 408, 383, 402, 383, 452, 383, 435, 
+	435, 119, 388, 389, 383, 383, 383, 383, 
+	394, 383, 402, 383, 402, 383, 383, 383, 
+	383, 383, 383, 388, 389, 383, 383, 383, 
+	383, 394, 383, 402, 383, 402, 383, 383, 
+	383, 383, 453, 383, 388, 389, 383, 383, 
+	383, 383, 394, 383, 402, 383, 402, 383, 
+	452, 383, 383, 383, 383, 388, 389, 383, 
+	383, 383, 383, 394, 383, 402, 383, 402, 
+	116, 383, 383, 132, 403, 383, 388, 389, 
+	383, 383, 383, 383, 394, 383, 402, 383, 
+	395, 396, 401, 401, 119, 388, 389, 383, 
+	383, 383, 383, 399, 383, 395, 396, 397, 
+	401, 119, 388, 389, 383, 383, 383, 149, 
+	399, 383, 392, 383, 454, 383, 435, 435, 
+	119, 388, 389, 383, 383, 383, 383, 394, 
+	383, 392, 383, 392, 383, 383, 383, 383, 
+	383, 383, 388, 389, 383, 383, 383, 383, 
+	394, 383, 392, 383, 392, 383, 383, 383, 
+	383, 455, 383, 388, 389, 383, 383, 383, 
+	383, 394, 383, 392, 383, 392, 383, 454, 
+	383, 383, 383, 383, 388, 389, 383, 383, 
+	383, 383, 394, 383, 392, 383, 392, 116, 
+	383, 383, 132, 393, 383, 388, 389, 383, 
+	383, 383, 383, 394, 383, 392, 383, 384, 
+	385, 387, 387, 119, 388, 389, 383, 383, 
+	383, 383, 390, 383, 153, 154, 382, 382, 
+	382, 382, 382, 382, 382, 382, 161, 161, 
+	382, 382, 382, 153, 382, 167, 456, 169, 
+	170, 5, 171, 172, 173, 166, 166, 37, 
+	174, 166, 166, 153, 166, 177, 154, 166, 
+	166, 18, 178, 166, 171, 172, 166, 161, 
+	161, 166, 179, 166, 177, 166, 0
 };
 
 static const short _indic_syllable_machine_trans_targs[] = {
@@ -1129,7 +1137,7 @@
 
 
 
-#line 90 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 91 "../../src/hb-ot-shape-complex-indic-machine.rl"
 
 
 #define found_syllable(syllable_type) \
@@ -1145,11 +1153,11 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts, te, act;
+  unsigned int p, pe, eof, ts HB_UNUSED, te, act;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 1153 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1161 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
 	{
 	cs = indic_syllable_machine_start;
 	ts = 0;
@@ -1157,7 +1165,7 @@
 	act = 0;
 	}
 
-#line 111 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 112 "../../src/hb-ot-shape-complex-indic-machine.rl"
 
 
   p = 0;
@@ -1166,7 +1174,7 @@
   unsigned int last = 0;
   unsigned int syllable_serial = 1;
   
-#line 1170 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1178 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
 	{
 	int _slen;
 	int _trans;
@@ -1177,10 +1185,10 @@
 _resume:
 	switch ( _indic_syllable_machine_from_state_actions[cs] ) {
 	case 9:
-#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 1 "NONE"
 	{ts = p;}
 	break;
-#line 1184 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1192 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
 	}
 
 	_keys = _indic_syllable_machine_trans_keys + (cs<<1);
@@ -1199,67 +1207,67 @@
 
 	switch ( _indic_syllable_machine_trans_actions[_trans] ) {
 	case 2:
-#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 1 "NONE"
 	{te = p+1;}
 	break;
 	case 13:
-#line 82 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{te = p+1;{ found_syllable (consonant_syllable); }}
 	break;
 	case 15:
-#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{te = p+1;{ found_syllable (vowel_syllable); }}
 	break;
 	case 20:
-#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{te = p+1;{ found_syllable (standalone_cluster); }}
 	break;
 	case 17:
-#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{te = p+1;{ found_syllable (broken_cluster); }}
 	break;
 	case 10:
-#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 87 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{te = p+1;{ found_syllable (non_indic_cluster); }}
 	break;
 	case 12:
-#line 82 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{te = p;p--;{ found_syllable (consonant_syllable); }}
 	break;
 	case 14:
-#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{te = p;p--;{ found_syllable (vowel_syllable); }}
 	break;
 	case 19:
-#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{te = p;p--;{ found_syllable (standalone_cluster); }}
 	break;
 	case 16:
-#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{te = p;p--;{ found_syllable (broken_cluster); }}
 	break;
 	case 18:
-#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 87 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{te = p;p--;{ found_syllable (non_indic_cluster); }}
 	break;
 	case 1:
-#line 82 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
 	break;
 	case 3:
-#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (vowel_syllable); }}
 	break;
 	case 7:
-#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (standalone_cluster); }}
 	break;
 	case 4:
-#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
 	break;
 	case 5:
-#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 1 "NONE"
 	{	switch( act ) {
 	case 4:
 	{{p = ((te))-1;} found_syllable (broken_cluster); }
@@ -1271,27 +1279,27 @@
 	}
 	break;
 	case 6:
-#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 1 "NONE"
 	{te = p+1;}
-#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{act = 4;}
 	break;
 	case 11:
-#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 1 "NONE"
 	{te = p+1;}
-#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 87 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{act = 5;}
 	break;
-#line 1286 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1294 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
 	}
 
 _again:
 	switch ( _indic_syllable_machine_to_state_actions[cs] ) {
 	case 8:
-#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 1295 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1303 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
 	}
 
 	if ( ++p != pe )
@@ -1307,7 +1315,7 @@
 
 	}
 
-#line 120 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 121 "../../src/hb-ot-shape-complex-indic-machine.rl"
 
 }
 
diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index b745466..5c73ebf 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -55,8 +55,9 @@
 Coeng = 14;
 Repha = 15;
 Ra    = 16;
+CM    = 17;
 
-c = (C | Ra);			# is_consonant
+c = (C | Ra)CM*;		# is_consonant
 n = ((ZWNJ?.RS)? (N.N?)?);	# is_consonant_modifier
 z = ZWJ|ZWNJ;			# is_joiner
 h = H | Coeng;			# is_halant_or_coeng
@@ -102,7 +103,7 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts, te, act;
+  unsigned int p, pe, eof, ts HB_UNUSED, te, act;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   %%{
diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index 91b0be5..e36090e 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -39,7 +39,7 @@
 #define indic_position() complex_var_u8_1() /* indic_matra_category_t */
 
 
-#define INDIC_TABLE_ELEMENT_TYPE uint8_t
+#define INDIC_TABLE_ELEMENT_TYPE uint16_t
 
 /* Cateories used in the OpenType spec:
  * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx
@@ -63,7 +63,8 @@
   OT_RS, /* Register Shifter, used in Khmer OT spec */
   OT_Coeng,
   OT_Repha,
-  OT_Ra /* Not explicitly listed in the OT spec, but used in the grammar. */
+  OT_Ra, /* Not explicitly listed in the OT spec, but used in the grammar. */
+  OT_CM
 };
 
 /* Visual positions in a syllable from left to right. */
@@ -103,7 +104,7 @@
   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_MEDIAL	= OT_CM,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER	= OT_NBSP,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED	= OT_C,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_REPHA	= OT_Repha,
@@ -111,7 +112,7 @@
   INDIC_SYLLABIC_CATEGORY_NUKTA			= OT_N,
   INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER	= OT_RS,
   INDIC_SYLLABIC_CATEGORY_TONE_LETTER		= OT_X,
-  INDIC_SYLLABIC_CATEGORY_TONE_MARK		= OT_X,
+  INDIC_SYLLABIC_CATEGORY_TONE_MARK		= OT_N,
   INDIC_SYLLABIC_CATEGORY_VIRAMA		= OT_H,
   INDIC_SYLLABIC_CATEGORY_VISARGA		= OT_SM,
   INDIC_SYLLABIC_CATEGORY_VOWEL			= OT_V,
@@ -138,16 +139,16 @@
   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
+  INDIC_MATRA_CATEGORY_OVERSTRUCK		= POS_AFTER_MAIN,
+  INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT	= POS_PRE_M
 };
 
 /* 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))
+   ASSERT_STATIC_EXPR_ZERO (S < 255 && M < 255) + \
+   ((M << 8) | S))
 
 
 #include "hb-ot-shape-complex-indic-table.hh"
@@ -221,7 +222,7 @@
     case POS_ABOVE_C:	return MATRA_POS_TOP (u);
     case POS_BELOW_C:	return MATRA_POS_BOTTOM (u);
   };
-  abort ();
+  return side;
 }
 
 
@@ -285,7 +286,7 @@
  * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
  * cannot happen in a consonant syllable.  The plus side however is, we can call the
  * consonant syllable logic from the vowel syllable function and get it all right! */
-#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE))
+#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CM) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE))
 static inline bool
 is_consonant (const hb_glyph_info_t &info)
 {
@@ -304,8 +305,8 @@
 {
   hb_codepoint_t u = info.codepoint;
   unsigned int type = get_indic_categories (u);
-  indic_category_t cat = (indic_category_t) (type & 0x0F);
-  indic_position_t pos = (indic_position_t) (type >> 4);
+  indic_category_t cat = (indic_category_t) (type & 0x7F);
+  indic_position_t pos = (indic_position_t) (type >> 8);
 
 
   /*
diff --git a/src/hb-ot-shape-complex-indic-table.hh b/src/hb-ot-shape-complex-indic-table.hh
index 5b4b344..70765b6 100644
--- a/src/hb-ot-shape-complex-indic-table.hh
+++ b/src/hb-ot-shape-complex-indic-table.hh
@@ -6,12 +6,12 @@
  *
  * on files with these headers:
  *
- * # IndicSyllabicCategory-6.1.0.txt
- * # Date: 2011-08-31, 23:54:00 GMT [KW]
- * # IndicMatraCategory-6.1.0.txt
- * # Date: 2011-08-31, 23:50:00 GMT [KW]
- * # Blocks-6.1.0.txt
- * # Date: 2011-06-14, 18:26:00 GMT [KW, LI]
+ * # IndicSyllabicCategory-6.2.0.txt
+ * # Date: 2012-05-15, 21:12:00 GMT [KW]
+ * # IndicMatraCategory-6.2.0.txt
+ * # Date: 2012-05-15, 21:10:00 GMT [KW]
+ * # Blocks-6.2.0.txt
+ * # Date: 2012-05-14, 22:42:00 GMT [KW, LI]
  */
 
 #ifndef HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 30a9a1f..0bc163a 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -123,6 +123,8 @@
   {HB_SCRIPT_MALAYALAM,	true, 0x0D4D,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA},
   {HB_SCRIPT_SINHALA,	false,0x0DCA,BASE_POS_FIRST,REPH_POS_AFTER_MAIN, REPH_MODE_EXPLICIT},
   {HB_SCRIPT_KHMER,	false,0x17D2,BASE_POS_FIRST,REPH_POS_DEFAULT,    REPH_MODE_VIS_REPHA},
+  /* Myanmar does not have the "old_indic" behavior, even though it has a "new" tag. */
+  {HB_SCRIPT_MYANMAR,	false, 0x1039,BASE_POS_LAST, REPH_POS_DEFAULT,    REPH_MODE_EXPLICIT},
 };
 
 
@@ -247,6 +249,8 @@
   /* Uniscribe does not apply 'kern'. */
   if (indic_options ().uniscribe_bug_compatible)
     plan->map.add_feature (HB_TAG('k','e','r','n'), 0, true);
+
+  plan->map.add_feature (HB_TAG('l','i','g','a'), 0, true);
 }
 
 
@@ -265,7 +269,7 @@
 				hb_face_t         *face) const
   {
     for (unsigned int i = 0; i < count; i++)
-      if (hb_ot_layout_would_substitute_lookup_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context))
+      if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context))
 	return true;
     return false;
   }
@@ -303,6 +307,7 @@
   bool is_old_spec;
   hb_codepoint_t virama_glyph;
 
+  would_substitute_feature_t rphf;
   would_substitute_feature_t pref;
   would_substitute_feature_t blwf;
   would_substitute_feature_t pstf;
@@ -324,9 +329,10 @@
       break;
     }
 
-  indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.get_chosen_script (0) & 0x000000FF) != '2');
+  indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FF) != '2');
   indic_plan->virama_glyph = (hb_codepoint_t) -1;
 
+  indic_plan->rphf.init (&plan->map, HB_TAG('r','p','h','f'));
   indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'));
   indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f'));
   indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f'));
@@ -428,7 +434,9 @@
  * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
 
 static void
-initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer,
+initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
+				       hb_face_t *face,
+				       hb_buffer_t *buffer,
 				       unsigned int start, unsigned int end)
 {
   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
@@ -459,22 +467,29 @@
     unsigned int limit = start;
     if (indic_plan->mask_array[RPHF] &&
 	start + 3 <= end &&
-	info[start].indic_category() == OT_Ra &&
-	info[start + 1].indic_category() == OT_H &&
 	(/* TODO Handle other Reph modes. */
 	 (indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) ||
 	 (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && info[start + 2].indic_category() == OT_ZWJ)
 	))
     {
-      limit += 2;
-      while (limit < end && is_joiner (info[limit]))
-        limit++;
-      base = start;
-      has_reph = true;
+      /* See if it matches the 'rphf' feature. */
+      hb_codepoint_t glyphs[2] = {info[start].codepoint, info[start + 1].codepoint};
+      if (indic_plan->rphf.would_substitute (glyphs, ARRAY_LENGTH (glyphs), true, face))
+      {
+	limit += 2;
+	while (limit < end && is_joiner (info[limit]))
+	  limit++;
+	base = start;
+	has_reph = true;
+      }
     };
 
     switch (indic_plan->config->base_pos)
     {
+      default:
+        assert (false);
+	/* fallthrough */
+
       case BASE_POS_LAST:
       {
 	/* -> starting from the end of the syllable, move backwards */
@@ -514,7 +529,7 @@
 	     * half form.
 	     * A ZWJ before a Halant, requests a subjoined form instead, and hence
 	     * search continues.  This is particularly important for Bengali
-	     * sequence Ra,H,Ya that shouls form Ya-Phalaa by subjoining Ya. */
+	     * sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya. */
 	    if (start < i &&
 		info[i].indic_category() == OT_ZWJ &&
 		info[i - 1].indic_category() == OT_H)
@@ -548,9 +563,6 @@
 	    info[i].indic_position() = POS_BELOW_C;
       }
       break;
-
-      default:
-      abort ();
     }
 
     /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
@@ -651,13 +663,17 @@
       if ((FLAG (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | HALANT_OR_COENG_FLAGS)))
       {
 	info[i].indic_position() = last_pos;
-	if (unlikely (indic_options ().uniscribe_bug_compatible &&
-		      info[i].indic_category() == OT_H &&
+	if (unlikely (info[i].indic_category() == OT_H &&
 		      info[i].indic_position() == POS_PRE_M))
 	{
 	  /*
 	   * Uniscribe doesn't move the Halant with Left Matra.
 	   * TEST: U+092B,U+093F,U+094DE
+	   * We follow.  This is important for the Sinhala
+	   * U+0DDA split matra since it decomposes to U+0DD9,U+0DCA
+	   * where U+0DD9 is a left matra and U+0DCA is the virama.
+	   * We don't want to move the virama with the left matra.
+	   * TEST: U+0D9A,U+0DDA
 	   */
 	  for (unsigned int j = i; j > start; j--)
 	    if (info[j - 1].indic_position() != POS_PRE_M) {
@@ -725,9 +741,9 @@
   if (indic_plan->mask_array[PREF] && base + 2 < end)
   {
     /* Find a Halant,Ra sequence and mark it for pre-base reordering processing. */
-    for (unsigned int i = base + 1; i + 1 < end; i++)
-      if (is_halant_or_coeng (info[i + (indic_plan->is_old_spec ? 1 : 0)]) &&
-	  info[i + (indic_plan->is_old_spec ? 0 : 1)].indic_category() == OT_Ra)
+    for (unsigned int i = base + 1; i + 1 < end; i++) {
+      hb_codepoint_t glyphs[2] = {info[i].codepoint, info[i + 1].codepoint};
+      if (indic_plan->pref.would_substitute (glyphs, ARRAY_LENGTH (glyphs), true, face))
       {
 	info[i++].mask |= indic_plan->mask_array[PREF];
 	info[i++].mask |= indic_plan->mask_array[PREF];
@@ -743,6 +759,7 @@
 
 	break;
       }
+    }
   }
 
   /* Apply ZWJ/ZWNJ effects */
@@ -768,15 +785,17 @@
 
 static void
 initial_reordering_vowel_syllable (const hb_ot_shape_plan_t *plan,
+				   hb_face_t *face,
 				   hb_buffer_t *buffer,
 				   unsigned int start, unsigned int end)
 {
   /* We made the vowels look like consonants.  So let's call the consonant logic! */
-  initial_reordering_consonant_syllable (plan, buffer, start, end);
+  initial_reordering_consonant_syllable (plan, face, buffer, start, end);
 }
 
 static void
 initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan,
+				       hb_face_t *face,
 				       hb_buffer_t *buffer,
 				       unsigned int start, unsigned int end)
 {
@@ -792,20 +811,22 @@
       return;
   }
 
-  initial_reordering_consonant_syllable (plan, buffer, start, end);
+  initial_reordering_consonant_syllable (plan, face, buffer, start, end);
 }
 
 static void
 initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan,
+				   hb_face_t *face,
 				   hb_buffer_t *buffer,
 				   unsigned int start, unsigned int end)
 {
   /* We already inserted dotted-circles, so just call the standalone_cluster. */
-  initial_reordering_standalone_cluster (plan, buffer, start, end);
+  initial_reordering_standalone_cluster (plan, face, buffer, start, end);
 }
 
 static void
 initial_reordering_non_indic_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
+				      hb_face_t *face HB_UNUSED,
 				      hb_buffer_t *buffer HB_UNUSED,
 				      unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
 {
@@ -816,21 +837,22 @@
 
 static void
 initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
+			     hb_face_t *face,
 			     hb_buffer_t *buffer,
 			     unsigned int start, unsigned int end)
 {
   syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
   switch (syllable_type) {
-  case consonant_syllable:	initial_reordering_consonant_syllable (plan, buffer, start, end); return;
-  case vowel_syllable:		initial_reordering_vowel_syllable     (plan, buffer, start, end); return;
-  case standalone_cluster:	initial_reordering_standalone_cluster (plan, buffer, start, end); return;
-  case broken_cluster:		initial_reordering_broken_cluster     (plan, buffer, start, end); return;
-  case non_indic_cluster:	initial_reordering_non_indic_cluster  (plan, buffer, start, end); return;
+  case consonant_syllable:	initial_reordering_consonant_syllable (plan, face, buffer, start, end); return;
+  case vowel_syllable:		initial_reordering_vowel_syllable     (plan, face, buffer, start, end); return;
+  case standalone_cluster:	initial_reordering_standalone_cluster (plan, face, buffer, start, end); return;
+  case broken_cluster:		initial_reordering_broken_cluster     (plan, face, buffer, start, end); return;
+  case non_indic_cluster:	initial_reordering_non_indic_cluster  (plan, face, buffer, start, end); return;
   }
 }
 
 static inline void
-insert_dotted_circles (const hb_ot_shape_plan_t *plan,
+insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		       hb_font_t *font,
 		       hb_buffer_t *buffer)
 {
@@ -850,7 +872,7 @@
   if (!font->get_glyph (0x25CC, 0, &dottedcircle_glyph))
     return;
 
-  hb_glyph_info_t dottedcircle;
+  hb_glyph_info_t dottedcircle = {0};
   dottedcircle.codepoint = 0x25CC;
   set_indic_properties (dottedcircle);
   dottedcircle.codepoint = dottedcircle_glyph;
@@ -893,11 +915,11 @@
   unsigned int last_syllable = info[0].syllable();
   for (unsigned int i = 1; i < count; i++)
     if (last_syllable != info[i].syllable()) {
-      initial_reordering_syllable (plan, buffer, last, i);
+      initial_reordering_syllable (plan, font->face, buffer, last, i);
       last = i;
       last_syllable = info[last].syllable();
     }
-  initial_reordering_syllable (plan, buffer, last, count);
+  initial_reordering_syllable (plan, font->face, buffer, last, count);
 }
 
 static void
@@ -1224,7 +1246,7 @@
 
 static void
 final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font,
+		  hb_font_t *font HB_UNUSED,
 		  hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
@@ -1251,11 +1273,113 @@
 
 
 static hb_ot_shape_normalization_mode_t
-normalization_preference_indic (const hb_ot_shape_plan_t *plan)
+normalization_preference_indic (const hb_segment_properties_t *props HB_UNUSED)
 {
   return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
 }
 
+static bool
+decompose_indic (const hb_ot_shape_normalize_context_t *c,
+		 hb_codepoint_t  ab,
+		 hb_codepoint_t *a,
+		 hb_codepoint_t *b)
+{
+  switch (ab)
+  {
+    /* Don't decompose these. */
+    case 0x0931  : return false;
+    case 0x0B94  : return false;
+
+
+    /*
+     * Decompose split matras that don't have Unicode decompositions.
+     */
+
+    case 0x0F77  : *a = 0x0FB2; *b= 0x0F81; return true;
+    case 0x0F79  : *a = 0x0FB3; *b= 0x0F81; return true;
+    case 0x17BE  : *a = 0x17C1; *b= 0x17BE; return true;
+    case 0x17BF  : *a = 0x17C1; *b= 0x17BF; return true;
+    case 0x17C0  : *a = 0x17C1; *b= 0x17C0; return true;
+    case 0x17C4  : *a = 0x17C1; *b= 0x17C4; return true;
+    case 0x17C5  : *a = 0x17C1; *b= 0x17C5; return true;
+    case 0x1925  : *a = 0x1920; *b= 0x1923; return true;
+    case 0x1926  : *a = 0x1920; *b= 0x1924; return true;
+    case 0x1B3C  : *a = 0x1B42; *b= 0x1B3C; return true;
+    case 0x1112E  : *a = 0x11127; *b= 0x11131; return true;
+    case 0x1112F  : *a = 0x11127; *b= 0x11132; return true;
+#if 0
+    /* This one has no decomposition in Unicode, but needs no decomposition either. */
+    /* case 0x0AC9  : return false; */
+    case 0x0B57  : *a = no decomp, -> RIGHT; return true;
+    case 0x1C29  : *a = no decomp, -> LEFT; return true;
+    case 0xA9C0  : *a = no decomp, -> RIGHT; return true;
+    case 0x111BF  : *a = no decomp, -> ABOVE; return true;
+#endif
+  }
+
+  if ((ab == 0x0DDA || hb_in_range<hb_codepoint_t> (ab, 0x0DDC, 0x0DDE)))
+  {
+    /*
+     * Sinhala split matras...  Let the fun begin.
+     *
+     * These four characters have Unicode decompositions.  However, Uniscribe
+     * decomposes them "Khmer-style", that is, it uses the character itself to
+     * get the second half.  The first half of all four decompositions is always
+     * U+0DD9.
+     *
+     * Now, there are buggy fonts, namely, the widely used lklug.ttf, that are
+     * broken with Uniscribe.  But we need to support them.  As such, we only
+     * do the Uniscribe-style decomposition if the character is transformed into
+     * its "sec.half" form by the 'pstf' feature.  Otherwise, we fall back to
+     * Unicode decomposition.
+     *
+     * Note that we can't unconditionally use Unicode decomposition.  That would
+     * break some other fonts, that are designed to work with Uniscribe, and
+     * don't have positioning features for the Unicode-style decomposition.
+     *
+     * Argh...
+     *
+     * The Uniscribe behavior is now documented in the newly published Sinhala
+     * spec in 2012:
+     *
+     *   http://www.microsoft.com/typography/OpenTypeDev/sinhala/intro.htm#shaping
+     */
+
+    const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan->data;
+
+    hb_codepoint_t glyph;
+
+    if (indic_options ().uniscribe_bug_compatible ||
+	(c->font->get_glyph (ab, 0, &glyph) &&
+	 indic_plan->pstf.would_substitute (&glyph, 1, true, c->font->face)))
+    {
+      /* Ok, safe to use Uniscribe-style decomposition. */
+      *a = 0x0DD9;
+      *b = ab;
+      return true;
+    }
+  }
+
+  return c->unicode->decompose (ab, a, b);
+}
+
+static bool
+compose_indic (const hb_ot_shape_normalize_context_t *c,
+	       hb_codepoint_t  a,
+	       hb_codepoint_t  b,
+	       hb_codepoint_t *ab)
+{
+  /* Avoid recomposing split matras. */
+  if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a)))
+    return false;
+
+  /* Composition-exclusion exceptions that we want to recompose. */
+  if (a == 0x09AF && b == 0x09BC) { *ab = 0x09DF; return true; }
+
+  return c->unicode->compose (a, b, ab);
+}
+
+
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
 {
   "indic",
@@ -1265,6 +1389,9 @@
   data_destroy_indic,
   NULL, /* preprocess_text */
   normalization_preference_indic,
+  decompose_indic,
+  compose_indic,
   setup_masks_indic,
   false, /* zero_width_attached_marks */
+  false, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-misc.cc b/src/hb-ot-shape-complex-misc.cc
deleted file mode 100644
index 13bc22b..0000000
--- a/src/hb-ot-shape-complex-misc.cc
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * 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.
- *
- * 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-private.hh"
-
-
-/* TODO Add kana, and other small shapers here */
-
-
-/* The default shaper *only* adds additional per-script features.*/
-
-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'),
-  HB_TAG_NONE
-};
-
-static const hb_tag_t tibetan_features[] =
-{
-  HB_TAG('a','b','v','s'),
-  HB_TAG('b','l','w','s'),
-  HB_TAG('a','b','v','m'),
-  HB_TAG('b','l','w','m'),
-  HB_TAG_NONE
-};
-
-static void
-collect_features_default (hb_ot_shape_planner_t *plan)
-{
-  const hb_tag_t *script_features = NULL;
-
-  switch ((hb_tag_t) plan->props.script)
-  {
-    /* Unicode-1.1 additions */
-    case HB_SCRIPT_HANGUL:
-      script_features = hangul_features;
-      break;
-
-    /* Unicode-2.0 additions */
-    case HB_SCRIPT_TIBETAN:
-      script_features = tibetan_features;
-      break;
-  }
-
-  for (; script_features && *script_features; script_features++)
-    plan->map.add_bool_feature (*script_features);
-}
-
-static hb_ot_shape_normalization_mode_t
-normalization_preference_default (const hb_ot_shape_plan_t *plan)
-{
-  switch ((hb_tag_t) plan->props.script)
-  {
-    /* Unicode-1.1 additions */
-    case HB_SCRIPT_HANGUL:
-      return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL;
-  }
-  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
-}
-
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
-{
-  "default",
-  collect_features_default,
-  NULL, /* override_features */
-  NULL, /* data_create */
-  NULL, /* data_destroy */
-  NULL, /* preprocess_text */
-  normalization_preference_default,
-  NULL, /* setup_masks */
-  true, /* zero_width_attached_marks */
-};
-
-
-/* Thai / Lao shaper */
-
-static void
-preprocess_text_thai (const hb_ot_shape_plan_t *plan 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 AA, *and* move the
-   * NIKHAHIT backwards over any tone mark (0E48-0E4B).
-   *
-   * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32>
-   *
-   * This reordering is legit only when the NIKHAHIT comes from a SARA AM, not
-   * when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably
-   * not what a user wanted, but the rendering is nevertheless nikhahit above
-   * chattawa.
-   *
-   * Same for Lao.
-   *
-   * Note:
-   *
-   * Uniscribe also does so below-marks reordering.  Namely, it positions U+0E3A
-   * after U+0E38 and U+0E39.  We do that by modifying the ccc for U+0E3A.
-   * See unicode->modified_combining_class ().  Lao does NOT have a U+0E3A
-   * equivalent.
-   */
-
-
-  /*
-   * Here are the characters of significance:
-   *
-   *			Thai	Lao
-   * SARA AM:		U+0E33	U+0EB3
-   * SARA AA:		U+0E32	U+0EB2
-   * Nikhahit:		U+0E4D	U+0ECD
-   *
-   * Testing shows that Uniscribe reorder the following marks:
-   * Thai:	<0E31,0E34..0E37,0E47..0E4E>
-   * Lao:	<0EB1,0EB4..0EB7,0EC7..0ECE>
-   *
-   * Note how the Lao versions are the same as Thai + 0x80.
-   */
-
-  /* We only get one script at a time, so a script-agnostic implementation
-   * is adequate here. */
-#define IS_SARA_AM(x) (((x) & ~0x0080) == 0x0E33)
-#define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0xE33 + 0xE4D)
-#define SARA_AA_FROM_SARA_AM(x) ((x) - 1)
-#define IS_TONE_MARK(x) (hb_in_ranges<hb_codepoint_t> ((x) & ~0x0080, 0x0E34, 0x0E37, 0x0E47, 0x0E4E, 0x0E31, 0x0E31))
-
-  buffer->clear_output ();
-  unsigned int count = buffer->len;
-  for (buffer->idx = 0; buffer->idx < count;)
-  {
-    hb_codepoint_t u = buffer->cur().codepoint;
-    if (likely (!IS_SARA_AM (u))) {
-      buffer->next_glyph ();
-      continue;
-    }
-
-    /* Is SARA AM. Decompose and reorder. */
-    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))
-      start--;
-
-    if (start + 2 < end)
-    {
-      /* Move Nikhahit (end-2) to the beginning */
-      buffer->merge_out_clusters (start, end);
-      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;
-    }
-    else
-    {
-      /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the
-       * previous cluster. */
-      if (start)
-	buffer->merge_out_clusters (start - 1, end);
-    }
-  }
-  buffer->swap_buffers ();
-}
-
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai =
-{
-  "thai",
-  NULL, /* collect_features */
-  NULL, /* override_features */
-  NULL, /* data_create */
-  NULL, /* data_destroy */
-  preprocess_text_thai,
-  NULL, /* normalization_preference */
-  NULL, /* setup_masks */
-  true, /* zero_width_attached_marks */
-};
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index e84c490..26871c2 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -56,6 +56,7 @@
   /* collect_features()
    * Called during shape_plan().
    * Shapers should use plan->map to add their features and callbacks.
+   * May be NULL.
    */
   void (*collect_features) (hb_ot_shape_planner_t *plan);
 
@@ -63,6 +64,7 @@
    * Called during shape_plan().
    * Shapers should use plan->map to override features and add callbacks after
    * common features are added.
+   * May be NULL.
    */
   void (*override_features) (hb_ot_shape_planner_t *plan);
 
@@ -78,13 +80,15 @@
    * Called when the shape_plan is being destroyed.
    * plan->data is passed here for destruction.
    * If NULL is returned, means a plan failure.
-   * May be NULL. */
+   * May be NULL.
+   */
   void (*data_destroy) (void *data);
 
 
   /* preprocess_text()
    * Called during shape().
    * Shapers can use to modify text before shaping starts.
+   * May be NULL.
    */
   void (*preprocess_text) (const hb_ot_shape_plan_t *plan,
 			   hb_buffer_t              *buffer,
@@ -93,20 +97,41 @@
 
   /* normalization_preference()
    * Called during shape().
+   * May be NULL.
    */
   hb_ot_shape_normalization_mode_t
-  (*normalization_preference) (const hb_ot_shape_plan_t *plan);
+  (*normalization_preference) (const hb_segment_properties_t *props);
+
+  /* decompose()
+   * Called during shape()'s normalization.
+   * May be NULL.
+   */
+  bool (*decompose) (const hb_ot_shape_normalize_context_t *c,
+		     hb_codepoint_t  ab,
+		     hb_codepoint_t *a,
+		     hb_codepoint_t *b);
+
+  /* compose()
+   * Called during shape()'s normalization.
+   * May be NULL.
+   */
+  bool (*compose) (const hb_ot_shape_normalize_context_t *c,
+		   hb_codepoint_t  a,
+		   hb_codepoint_t  b,
+		   hb_codepoint_t *ab);
 
   /* setup_masks()
    * Called during shape().
    * Shapers should use map to get feature masks and set on buffer.
    * Shapers may NOT modify characters.
+   * May be NULL.
    */
   void (*setup_masks) (const hb_ot_shape_plan_t *plan,
 		       hb_buffer_t              *buffer,
 		       hb_font_t                *font);
 
   bool zero_width_attached_marks;
+  bool fallback_position;
 };
 
 #define HB_COMPLEX_SHAPER_IMPLEMENT(name) extern HB_INTERNAL const hb_ot_complex_shaper_t _hb_ot_complex_shaper_##name;
@@ -115,9 +140,9 @@
 
 
 static inline const hb_ot_complex_shaper_t *
-hb_ot_shape_complex_categorize (const hb_segment_properties_t *props)
+hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
 {
-  switch ((hb_tag_t) props->script)
+  switch ((hb_tag_t) planner->props.script)
   {
     default:
       return &_hb_ot_complex_shaper_default;
@@ -130,11 +155,18 @@
 
     /* Unicode-5.0 additions */
     case HB_SCRIPT_NKO:
+    case HB_SCRIPT_PHAGS_PA:
 
     /* Unicode-6.0 additions */
     case HB_SCRIPT_MANDAIC:
 
-      return &_hb_ot_complex_shaper_arabic;
+      /* For Arabic script, use the Arabic shaper even if no OT script tag was found.
+       * This is because we do fallback shaping for Arabic script (and not others). */
+      if (planner->map.chosen_script[0] != HB_OT_TAG_DEFAULT_SCRIPT ||
+	  planner->props.script == HB_SCRIPT_ARABIC)
+	return &_hb_ot_complex_shaper_arabic;
+      else
+	return &_hb_ot_complex_shaper_default;
 
 
     /* Unicode-1.1 additions */
@@ -170,9 +202,6 @@
     /* Unicode-5.1 additions */
     case HB_SCRIPT_SAURASHTRA:
 
-    /* Unicode-5.2 additions */
-    case HB_SCRIPT_MEETEI_MAYEK:
-
     /* Unicode-6.0 additions */
     case HB_SCRIPT_BATAK:
     case HB_SCRIPT_BRAHMI:
@@ -197,11 +226,9 @@
     case HB_SCRIPT_TAI_LE:
 
     /* Unicode-4.1 additions */
+    case HB_SCRIPT_KHAROSHTHI:
     case HB_SCRIPT_SYLOTI_NAGRI:
 
-    /* Unicode-5.0 additions */
-    case HB_SCRIPT_PHAGS_PA:
-
     /* Unicode-5.1 additions */
     case HB_SCRIPT_KAYAH_LI:
 
@@ -209,12 +236,6 @@
     case HB_SCRIPT_TAI_VIET:
 
 
-    /* May need Indic treatment in the future? */
-
-    /* Unicode-3.0 additions */
-    case HB_SCRIPT_MYANMAR:
-
-
 #endif
 
     /* Unicode-1.1 additions */
@@ -229,12 +250,10 @@
     case HB_SCRIPT_TELUGU:
 
     /* Unicode-3.0 additions */
-    case HB_SCRIPT_KHMER:
     case HB_SCRIPT_SINHALA:
 
     /* Unicode-4.1 additions */
     case HB_SCRIPT_BUGINESE:
-    case HB_SCRIPT_KHAROSHTHI:
     case HB_SCRIPT_NEW_TAI_LUE:
 
     /* Unicode-5.0 additions */
@@ -249,14 +268,44 @@
     /* Unicode-5.2 additions */
     case HB_SCRIPT_JAVANESE:
     case HB_SCRIPT_KAITHI:
+    case HB_SCRIPT_MEETEI_MAYEK:
     case HB_SCRIPT_TAI_THAM:
 
+
     /* Unicode-6.1 additions */
     case HB_SCRIPT_CHAKMA:
     case HB_SCRIPT_SHARADA:
     case HB_SCRIPT_TAKRI:
 
-      return &_hb_ot_complex_shaper_indic;
+      /* Only use Indic shaper if the font has Indic tables. */
+      if (planner->map.found_script[0])
+	return &_hb_ot_complex_shaper_indic;
+      else
+	return &_hb_ot_complex_shaper_default;
+
+    case HB_SCRIPT_KHMER:
+      /* A number of Khmer fonts in the wild don't have a 'pref' feature,
+       * and as such won't shape properly via the Indic shaper;
+       * however, they typically have 'liga' / 'clig' features that implement
+       * the necessary "reordering" by means of ligature substitutions.
+       * So we send such pref-less fonts through the generic shaper instead. */
+      if (planner->map.found_script[0] &&
+	  hb_ot_layout_language_find_feature (planner->face, HB_OT_TAG_GSUB,
+					      planner->map.script_index[0],
+					      planner->map.language_index[0],
+					      HB_TAG ('p','r','e','f'),
+					      NULL))
+	return &_hb_ot_complex_shaper_indic;
+      else
+	return &_hb_ot_complex_shaper_default;
+
+    case HB_SCRIPT_MYANMAR:
+      /* For Myanmar, we only want to use the Indic shaper if the "new" script
+       * tag is found.  For "old" script tag we want to use the default shaper. */
+      if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','2'))
+	return &_hb_ot_complex_shaper_indic;
+      else
+	return &_hb_ot_complex_shaper_default;
   }
 }
 
diff --git a/src/hb-ot-shape-complex-thai.cc b/src/hb-ot-shape-complex-thai.cc
new file mode 100644
index 0000000..24d476a
--- /dev/null
+++ b/src/hb-ot-shape-complex-thai.cc
@@ -0,0 +1,378 @@
+/*
+ * 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.
+ *
+ * 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-private.hh"
+
+
+/* Thai / Lao shaper */
+
+
+/* PUA shaping */
+
+
+enum thai_consonant_type_t
+{
+  NC,
+  AC,
+  RC,
+  DC,
+  NOT_CONSONANT,
+  NUM_CONSONANT_TYPES = NOT_CONSONANT
+};
+
+static thai_consonant_type_t
+get_consonant_type (hb_codepoint_t u)
+{
+  if (u == 0x0E1B || u == 0x0E1D || u == 0x0E1F/* || u == 0x0E2C*/)
+    return AC;
+  if (u == 0x0E0D || u == 0x0E10)
+    return RC;
+  if (u == 0x0E0E || u == 0x0E0F)
+    return DC;
+  if (hb_in_range<hb_codepoint_t> (u, 0x0E01, 0x0E2E))
+    return NC;
+  return NOT_CONSONANT;
+}
+
+
+enum thai_mark_type_t
+{
+  AV,
+  BV,
+  T,
+  NOT_MARK,
+  NUM_MARK_TYPES = NOT_MARK
+};
+
+static thai_mark_type_t
+get_mark_type (hb_codepoint_t u)
+{
+  if (u == 0x0E31 || hb_in_range<hb_codepoint_t> (u, 0x0E34, 0x0E37) ||
+      u == 0x0E47 || hb_in_range<hb_codepoint_t> (u, 0x0E4D, 0x0E4E))
+    return AV;
+  if (hb_in_range<hb_codepoint_t> (u, 0x0E38, 0x0E3A))
+    return BV;
+  if (hb_in_range<hb_codepoint_t> (u, 0x0E48, 0x0E4C))
+    return T;
+  return NOT_MARK;
+}
+
+
+enum thai_action_t
+{
+  NOP,
+  SD,  /* Shift combining-mark down */
+  SL,  /* Shift combining-mark left */
+  SDL, /* Shift combining-mark down-left */
+  RD   /* Remove descender from base */
+};
+
+static hb_codepoint_t
+thai_pua_shape (hb_codepoint_t u, thai_action_t action, hb_font_t *font)
+{
+  struct thai_pua_mapping_t {
+    hb_codepoint_t u;
+    hb_codepoint_t win_pua;
+    hb_codepoint_t mac_pua;
+  } const *pua_mappings = NULL;
+  static const thai_pua_mapping_t SD_mappings[] = {
+    {0x0E48, 0xF70A, 0xF88B}, /* MAI EK */
+    {0x0E49, 0xF70B, 0xF88E}, /* MAI THO */
+    {0x0E4A, 0xF70C, 0xF891}, /* MAI TRI */
+    {0x0E4B, 0xF70D, 0xF894}, /* MAI CHATTAWA */
+    {0x0E4C, 0xF70E, 0xF897}, /* THANTHAKHAT */
+    {0x0E38, 0xF718, 0xF89B}, /* SARA U */
+    {0x0E39, 0xF719, 0xF89C}, /* SARA UU */
+    {0x0E3A, 0xF71A, 0xF89D}, /* PHINTHU */
+    {0x0000, 0x0000, 0x0000}
+  };
+  static const thai_pua_mapping_t SDL_mappings[] = {
+    {0x0E48, 0xF705, 0xF88C}, /* MAI EK */
+    {0x0E49, 0xF706, 0xF88F}, /* MAI THO */
+    {0x0E4A, 0xF707, 0xF892}, /* MAI TRI */
+    {0x0E4B, 0xF708, 0xF895}, /* MAI CHATTAWA */
+    {0x0E4C, 0xF709, 0xF898}, /* THANTHAKHAT */
+    {0x0000, 0x0000, 0x0000}
+  };
+  static const thai_pua_mapping_t SL_mappings[] = {
+    {0x0E48, 0xF713, 0xF88A}, /* MAI EK */
+    {0x0E49, 0xF714, 0xF88D}, /* MAI THO */
+    {0x0E4A, 0xF715, 0xF890}, /* MAI TRI */
+    {0x0E4B, 0xF716, 0xF893}, /* MAI CHATTAWA */
+    {0x0E4C, 0xF717, 0xF896}, /* THANTHAKHAT */
+    {0x0E31, 0xF710, 0xF884}, /* MAI HAN-AKAT */
+    {0x0E34, 0xF701, 0xF885}, /* SARA I */
+    {0x0E35, 0xF702, 0xF886}, /* SARA II */
+    {0x0E36, 0xF703, 0xF887}, /* SARA UE */
+    {0x0E37, 0xF704, 0xF888}, /* SARA UEE */
+    {0x0E47, 0xF712, 0xF889}, /* MAITAIKHU */
+    {0x0E4D, 0xF711, 0xF899}, /* NIKHAHIT */
+    {0x0000, 0x0000, 0x0000}
+  };
+  static const thai_pua_mapping_t RD_mappings[] = {
+    {0x0E0D, 0xF70F, 0xF89A}, /* YO YING */
+    {0x0E10, 0xF700, 0xF89E}, /* THO THAN */
+    {0x0000, 0x0000, 0x0000}
+  };
+
+  switch (action) {
+    default: assert (false); /* Fallthrough */
+    case NOP: return u;
+    case SD:  pua_mappings = SD_mappings; break;
+    case SDL: pua_mappings = SDL_mappings; break;
+    case SL:  pua_mappings = SL_mappings; break;
+    case RD:  pua_mappings = RD_mappings; break;
+  }
+  for (; pua_mappings->u; pua_mappings++)
+    if (pua_mappings->u == u)
+    {
+      hb_codepoint_t glyph;
+      if (hb_font_get_glyph (font, pua_mappings->win_pua, 0, &glyph))
+	return pua_mappings->win_pua;
+      if (hb_font_get_glyph (font, pua_mappings->mac_pua, 0, &glyph))
+	return pua_mappings->mac_pua;
+      break;
+    }
+  return u;
+}
+
+
+static enum thai_above_state_t
+{     /* Cluster above looks like: */
+  T0, /*  ⣤                      */
+  T1, /*     ⣼                   */
+  T2, /*        ⣾                */
+  T3, /*           ⣿             */
+  NUM_ABOVE_STATES
+} thai_above_start_state[NUM_CONSONANT_TYPES + 1/* For NOT_CONSONANT */] =
+{
+  T0, /* NC */
+  T1, /* AC */
+  T0, /* RC */
+  T0, /* DC */
+  T3, /* NOT_CONSONANT */
+};
+
+static const struct thai_above_state_machine_edge_t {
+  thai_action_t action;
+  thai_above_state_t next_state;
+} thai_above_state_machine[NUM_ABOVE_STATES][NUM_MARK_TYPES] =
+{        /*AV*/    /*BV*/    /*T*/
+/*T0*/ {{NOP,T3}, {NOP,T0}, {SD, T3}},
+/*T1*/ {{SL, T2}, {NOP,T1}, {SDL,T2}},
+/*T2*/ {{NOP,T3}, {NOP,T2}, {SL, T3}},
+/*T3*/ {{NOP,T3}, {NOP,T3}, {NOP,T3}},
+};
+
+
+static enum thai_below_state_t
+{
+  B0, /* No descender */
+  B1, /* Removable descender */
+  B2, /* Strict descender */
+  NUM_BELOW_STATES
+} thai_below_start_state[NUM_CONSONANT_TYPES + 1/* For NOT_CONSONANT */] =
+{
+  B0, /* NC */
+  B0, /* AC */
+  B1, /* RC */
+  B2, /* DC */
+  B2, /* NOT_CONSONANT */
+};
+
+static const struct thai_below_state_machine_edge_t {
+  thai_action_t action;
+  thai_below_state_t next_state;
+} thai_below_state_machine[NUM_BELOW_STATES][NUM_MARK_TYPES] =
+{        /*AV*/    /*BV*/    /*T*/
+/*B0*/ {{NOP,B0}, {NOP,B2}, {NOP, B0}},
+/*B1*/ {{NOP,B1}, {RD, B2}, {NOP, B1}},
+/*B2*/ {{NOP,B2}, {SD, B2}, {NOP, B2}},
+};
+
+
+static void
+do_thai_pua_shaping (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		     hb_buffer_t              *buffer,
+		     hb_font_t                *font)
+{
+  thai_above_state_t above_state = thai_above_start_state[NOT_CONSONANT];
+  thai_below_state_t below_state = thai_below_start_state[NOT_CONSONANT];
+  unsigned int base = 0;
+
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    thai_mark_type_t mt = get_mark_type (info[i].codepoint);
+
+    if (mt == NOT_MARK) {
+      thai_consonant_type_t ct = get_consonant_type (info[i].codepoint);
+      above_state = thai_above_start_state[ct];
+      below_state = thai_below_start_state[ct];
+      base = i;
+      continue;
+    }
+
+    const thai_above_state_machine_edge_t &above_edge = thai_above_state_machine[above_state][mt];
+    const thai_below_state_machine_edge_t &below_edge = thai_below_state_machine[below_state][mt];
+    above_state = above_edge.next_state;
+    below_state = below_edge.next_state;
+
+    /* At least one of the above/below actions is NOP. */
+    thai_action_t action = above_edge.action != NOP ? above_edge.action : below_edge.action;
+
+    if (action == RD)
+      info[base].codepoint = thai_pua_shape (info[base].codepoint, action, font);
+    else
+      info[i].codepoint = thai_pua_shape (info[i].codepoint, action, font);
+  }
+}
+
+
+static void
+preprocess_text_thai (const hb_ot_shape_plan_t *plan,
+		      hb_buffer_t              *buffer,
+		      hb_font_t                *font)
+{
+  /* This function implements the shaping logic documented here:
+   *
+   *   http://linux.thai.net/~thep/th-otf/shaping.html
+   *
+   * The first shaping rule listed there is needed even if the font has Thai
+   * OpenType tables.  The rest do fallback positioning based on PUA codepoints.
+   * We implement that only if there exist no Thai GSUB in the font.
+   */
+
+  /* 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 AA, *and* move the
+   * NIKHAHIT backwards over any tone mark (0E48-0E4B).
+   *
+   * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32>
+   *
+   * This reordering is legit only when the NIKHAHIT comes from a SARA AM, not
+   * when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably
+   * not what a user wanted, but the rendering is nevertheless nikhahit above
+   * chattawa.
+   *
+   * Same for Lao.
+   *
+   * Note:
+   *
+   * Uniscribe also does some below-marks reordering.  Namely, it positions U+0E3A
+   * after U+0E38 and U+0E39.  We do that by modifying the ccc for U+0E3A.
+   * See unicode->modified_combining_class ().  Lao does NOT have a U+0E3A
+   * equivalent.
+   */
+
+
+  /*
+   * Here are the characters of significance:
+   *
+   *			Thai	Lao
+   * SARA AM:		U+0E33	U+0EB3
+   * SARA AA:		U+0E32	U+0EB2
+   * Nikhahit:		U+0E4D	U+0ECD
+   *
+   * Testing shows that Uniscribe reorder the following marks:
+   * Thai:	<0E31,0E34..0E37,0E47..0E4E>
+   * Lao:	<0EB1,0EB4..0EB7,0EC7..0ECE>
+   *
+   * Note how the Lao versions are the same as Thai + 0x80.
+   */
+
+  /* We only get one script at a time, so a script-agnostic implementation
+   * is adequate here. */
+#define IS_SARA_AM(x) (((x) & ~0x0080) == 0x0E33)
+#define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0xE33 + 0xE4D)
+#define SARA_AA_FROM_SARA_AM(x) ((x) - 1)
+#define IS_TONE_MARK(x) (hb_in_ranges<hb_codepoint_t> ((x) & ~0x0080, 0x0E34, 0x0E37, 0x0E47, 0x0E4E, 0x0E31, 0x0E31))
+
+  buffer->clear_output ();
+  unsigned int count = buffer->len;
+  for (buffer->idx = 0; buffer->idx < count;)
+  {
+    hb_codepoint_t u = buffer->cur().codepoint;
+    if (likely (!IS_SARA_AM (u))) {
+      buffer->next_glyph ();
+      continue;
+    }
+
+    /* Is SARA AM. Decompose and reorder. */
+    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))
+      start--;
+
+    if (start + 2 < end)
+    {
+      /* Move Nikhahit (end-2) to the beginning */
+      buffer->merge_out_clusters (start, end);
+      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;
+    }
+    else
+    {
+      /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the
+       * previous cluster. */
+      if (start)
+	buffer->merge_out_clusters (start - 1, end);
+    }
+  }
+  buffer->swap_buffers ();
+
+  /* If font has Thai GSUB, we are done. */
+  if (plan->props.script == HB_SCRIPT_THAI && !plan->map.found_script[0])
+    do_thai_pua_shaping (plan, buffer, font);
+}
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai =
+{
+  "thai",
+  NULL, /* collect_features */
+  NULL, /* override_features */
+  NULL, /* data_create */
+  NULL, /* data_destroy */
+  preprocess_text_thai,
+  NULL, /* normalization_preference */
+  NULL, /* decompose */
+  NULL, /* compose */
+  NULL, /* setup_masks */
+  true, /* zero_width_attached_marks */
+  false,/* fallback_position */
+};
diff --git a/src/hb-ot-shape-fallback.cc b/src/hb-ot-shape-fallback.cc
index eb7777e..6f3426e 100644
--- a/src/hb-ot-shape-fallback.cc
+++ b/src/hb-ot-shape-fallback.cc
@@ -138,10 +138,10 @@
     /* Lao */
 
     case HB_MODIFIED_COMBINING_CLASS_CCC118: /* sign u / sign uu */
-      return HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT;
+      return HB_UNICODE_COMBINING_CLASS_BELOW;
 
     case HB_MODIFIED_COMBINING_CLASS_CCC122: /* mai */
-      return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT;
+      return HB_UNICODE_COMBINING_CLASS_ABOVE;
 
 
     /* Tibetan */
@@ -161,8 +161,8 @@
 }
 
 void
-_hb_ot_shape_fallback_position_recategorize_marks (const hb_ot_shape_plan_t *plan,
-						   hb_font_t *font,
+_hb_ot_shape_fallback_position_recategorize_marks (const hb_ot_shape_plan_t *plan HB_UNUSED,
+						   hb_font_t *font HB_UNUSED,
 						   hb_buffer_t  *buffer)
 {
   unsigned int count = buffer->len;
@@ -311,7 +311,7 @@
   hb_glyph_extents_t component_extents = base_extents;
   unsigned int last_lig_component = (unsigned int) -1;
   unsigned int last_combining_class = 255;
-  hb_glyph_extents_t cluster_extents;
+  hb_glyph_extents_t cluster_extents = base_extents; /* Initialization is just to shut gcc up. */
   for (unsigned int i = base + 1; i < end; i++)
     if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]))
     {
diff --git a/src/hb-ot-shape-normalize-private.hh b/src/hb-ot-shape-normalize-private.hh
index c5fcbea..8112f03 100644
--- a/src/hb-ot-shape-normalize-private.hh
+++ b/src/hb-ot-shape-normalize-private.hh
@@ -35,6 +35,8 @@
 /* buffer var allocations, used during the normalization process */
 #define glyph_index()	var1.u32
 
+struct hb_ot_shape_plan_t;
+
 enum hb_ot_shape_normalization_mode_t {
   HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED,
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* never composes base-to-base */
@@ -44,8 +46,26 @@
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS
 };
 
-HB_INTERNAL void _hb_ot_shape_normalize (hb_font_t *font,
+HB_INTERNAL void _hb_ot_shape_normalize (const hb_ot_shape_plan_t *shaper,
 					 hb_buffer_t *buffer,
-					 hb_ot_shape_normalization_mode_t mode);
+					 hb_font_t *font);
+
+
+struct hb_ot_shape_normalize_context_t
+{
+  const hb_ot_shape_plan_t *plan;
+  hb_buffer_t *buffer;
+  hb_font_t *font;
+  hb_unicode_funcs_t *unicode;
+  bool (*decompose) (const hb_ot_shape_normalize_context_t *c,
+		     hb_codepoint_t  ab,
+		     hb_codepoint_t *a,
+		     hb_codepoint_t *b);
+  bool (*compose) (const hb_ot_shape_normalize_context_t *c,
+		   hb_codepoint_t  a,
+		   hb_codepoint_t  b,
+		   hb_codepoint_t *ab);
+};
+
 
 #endif /* HB_OT_SHAPE_NORMALIZE_PRIVATE_HH */
diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index 18a3f3f..c5325e4 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -25,6 +25,7 @@
  */
 
 #include "hb-ot-shape-normalize-private.hh"
+#include "hb-ot-shape-complex-private.hh"
 #include "hb-ot-shape-private.hh"
 
 
@@ -81,181 +82,24 @@
  *     egrep  "`echo -n ';('; grep ';<' UnicodeData.txt | cut -d';' -f1 | tr '\n' '|'; echo ') '`" UnicodeData.txt
  */
 
-static hb_bool_t
-decompose_func (hb_unicode_funcs_t *unicode,
-		hb_codepoint_t  ab,
-		hb_codepoint_t *a,
-		hb_codepoint_t *b)
+static bool
+decompose_unicode (const hb_ot_shape_normalize_context_t *c,
+		   hb_codepoint_t  ab,
+		   hb_codepoint_t *a,
+		   hb_codepoint_t *b)
 {
-  /* XXX FIXME, move these to complex shapers and propagage to normalizer.*/
-  switch (ab) {
-    case 0x0AC9  : return false;
-
-    case 0x0931  : return false;
-    case 0x0B94  : return false;
-
-    /* These ones have Unicode decompositions, but we do it
-     * this way to be close to what Uniscribe does. */
-    case 0x0DDA  : *a = 0x0DD9; *b= 0x0DDA; return true;
-    case 0x0DDC  : *a = 0x0DD9; *b= 0x0DDC; return true;
-    case 0x0DDD  : *a = 0x0DD9; *b= 0x0DDD; return true;
-    case 0x0DDE  : *a = 0x0DD9; *b= 0x0DDE; return true;
-
-    case 0x0F77  : *a = 0x0FB2; *b= 0x0F81; return true;
-    case 0x0F79  : *a = 0x0FB3; *b= 0x0F81; return true;
-    case 0x17BE  : *a = 0x17C1; *b= 0x17BE; return true;
-    case 0x17BF  : *a = 0x17C1; *b= 0x17BF; return true;
-    case 0x17C0  : *a = 0x17C1; *b= 0x17C0; return true;
-    case 0x17C4  : *a = 0x17C1; *b= 0x17C4; return true;
-    case 0x17C5  : *a = 0x17C1; *b= 0x17C5; return true;
-    case 0x1925  : *a = 0x1920; *b= 0x1923; return true;
-    case 0x1926  : *a = 0x1920; *b= 0x1924; return true;
-    case 0x1B3C  : *a = 0x1B42; *b= 0x1B3C; return true;
-    case 0x1112E  : *a = 0x11127; *b= 0x11131; return true;
-    case 0x1112F  : *a = 0x11127; *b= 0x11132; return true;
-#if 0
-    case 0x0B57  : *a = 0xno decomp, -> RIGHT; return true;
-    case 0x1C29  : *a = 0xno decomp, -> LEFT; return true;
-    case 0xA9C0  : *a = 0xno decomp, -> RIGHT; return true;
-    case 0x111BF  : *a = 0xno decomp, -> ABOVE; return true;
-#endif
-  }
-  return unicode->decompose (ab, a, b);
+  return c->unicode->decompose (ab, a, b);
 }
 
-static hb_bool_t
-compose_func (hb_unicode_funcs_t *unicode,
-	      hb_codepoint_t  a,
-	      hb_codepoint_t  b,
-	      hb_codepoint_t *ab)
+static bool
+compose_unicode (const hb_ot_shape_normalize_context_t *c,
+		 hb_codepoint_t  a,
+		 hb_codepoint_t  b,
+		 hb_codepoint_t *ab)
 {
-  /* XXX, this belongs to indic normalizer. */
-  if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (a)))
-    return false;
-  /* XXX, add composition-exclusion exceptions to Indic shaper. */
-  if (a == 0x09AF && b == 0x09BC) { *ab = 0x09DF; return true; }
-
-  /* XXX, these belong to the hebew / default shaper. */
-  /* Hebrew presentation-form shaping.
-   * https://bugzilla.mozilla.org/show_bug.cgi?id=728866 */
-  // Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
-  // note that some letters do not have a dagesh presForm encoded
-  static const hb_codepoint_t sDageshForms[0x05EA - 0x05D0 + 1] = {
-    0xFB30, // ALEF
-    0xFB31, // BET
-    0xFB32, // GIMEL
-    0xFB33, // DALET
-    0xFB34, // HE
-    0xFB35, // VAV
-    0xFB36, // ZAYIN
-    0, // HET
-    0xFB38, // TET
-    0xFB39, // YOD
-    0xFB3A, // FINAL KAF
-    0xFB3B, // KAF
-    0xFB3C, // LAMED
-    0, // FINAL MEM
-    0xFB3E, // MEM
-    0, // FINAL NUN
-    0xFB40, // NUN
-    0xFB41, // SAMEKH
-    0, // AYIN
-    0xFB43, // FINAL PE
-    0xFB44, // PE
-    0, // FINAL TSADI
-    0xFB46, // TSADI
-    0xFB47, // QOF
-    0xFB48, // RESH
-    0xFB49, // SHIN
-    0xFB4A // TAV
-  };
-
-  hb_bool_t found = unicode->compose (a, b, ab);
-
-  if (!found && (b & ~0x7F) == 0x0580) {
-      // special-case Hebrew presentation forms that are excluded from
-      // standard normalization, but wanted for old fonts
-      switch (b) {
-      case 0x05B4: // HIRIQ
-	  if (a == 0x05D9) { // YOD
-	      *ab = 0xFB1D;
-	      found = true;
-	  }
-	  break;
-      case 0x05B7: // patah
-	  if (a == 0x05F2) { // YIDDISH YOD YOD
-	      *ab = 0xFB1F;
-	      found = true;
-	  } else if (a == 0x05D0) { // ALEF
-	      *ab = 0xFB2E;
-	      found = true;
-	  }
-	  break;
-      case 0x05B8: // QAMATS
-	  if (a == 0x05D0) { // ALEF
-	      *ab = 0xFB2F;
-	      found = true;
-	  }
-	  break;
-      case 0x05B9: // HOLAM
-	  if (a == 0x05D5) { // VAV
-	      *ab = 0xFB4B;
-	      found = true;
-	  }
-	  break;
-      case 0x05BC: // DAGESH
-	  if (a >= 0x05D0 && a <= 0x05EA) {
-	      *ab = sDageshForms[a - 0x05D0];
-	      found = (*ab != 0);
-	  } else if (a == 0xFB2A) { // SHIN WITH SHIN DOT
-	      *ab = 0xFB2C;
-	      found = true;
-	  } else if (a == 0xFB2B) { // SHIN WITH SIN DOT
-	      *ab = 0xFB2D;
-	      found = true;
-	  }
-	  break;
-      case 0x05BF: // RAFE
-	  switch (a) {
-	  case 0x05D1: // BET
-	      *ab = 0xFB4C;
-	      found = true;
-	      break;
-	  case 0x05DB: // KAF
-	      *ab = 0xFB4D;
-	      found = true;
-	      break;
-	  case 0x05E4: // PE
-	      *ab = 0xFB4E;
-	      found = true;
-	      break;
-	  }
-	  break;
-      case 0x05C1: // SHIN DOT
-	  if (a == 0x05E9) { // SHIN
-	      *ab = 0xFB2A;
-	      found = true;
-	  } else if (a == 0xFB49) { // SHIN WITH DAGESH
-	      *ab = 0xFB2C;
-	      found = true;
-	  }
-	  break;
-      case 0x05C2: // SIN DOT
-	  if (a == 0x05E9) { // SHIN
-	      *ab = 0xFB2B;
-	      found = true;
-	  } else if (a == 0xFB49) { // SHIN WITH DAGESH
-	      *ab = 0xFB2D;
-	      found = true;
-	  }
-	  break;
-      }
-  }
-
-  return found;
+  return c->unicode->compose (a, b, ab);
 }
 
-
 static inline void
 set_glyph (hb_glyph_info_t &info, hb_font_t *font)
 {
@@ -285,38 +129,38 @@
 
 /* Returns 0 if didn't decompose, number of resulting characters otherwise. */
 static inline unsigned int
-decompose (hb_font_t *font, hb_buffer_t *buffer, bool shortest, hb_codepoint_t ab)
+decompose (const hb_ot_shape_normalize_context_t *c, bool shortest, hb_codepoint_t ab)
 {
   hb_codepoint_t a, b, a_glyph, b_glyph;
 
-  if (!decompose_func (buffer->unicode, ab, &a, &b) ||
-      (b && !font->get_glyph (b, 0, &b_glyph)))
+  if (!c->decompose (c, ab, &a, &b) ||
+      (b && !c->font->get_glyph (b, 0, &b_glyph)))
     return 0;
 
-  bool has_a = font->get_glyph (a, 0, &a_glyph);
+  bool has_a = c->font->get_glyph (a, 0, &a_glyph);
   if (shortest && has_a) {
     /* Output a and b */
-    output_char (buffer, a, a_glyph);
+    output_char (c->buffer, a, a_glyph);
     if (likely (b)) {
-      output_char (buffer, b, b_glyph);
+      output_char (c->buffer, b, b_glyph);
       return 2;
     }
     return 1;
   }
 
   unsigned int ret;
-  if ((ret = decompose (font, buffer, shortest, a))) {
+  if ((ret = decompose (c, shortest, a))) {
     if (b) {
-      output_char (buffer, b, b_glyph);
+      output_char (c->buffer, b, b_glyph);
       return ret + 1;
     }
     return ret;
   }
 
   if (has_a) {
-    output_char (buffer, a, a_glyph);
+    output_char (c->buffer, a, a_glyph);
     if (likely (b)) {
-      output_char (buffer, b, b_glyph);
+      output_char (c->buffer, b, b_glyph);
       return 2;
     }
     return 1;
@@ -327,41 +171,42 @@
 
 /* Returns 0 if didn't decompose, number of resulting characters otherwise. */
 static inline bool
-decompose_compatibility (hb_font_t *font, hb_buffer_t *buffer, hb_codepoint_t u)
+decompose_compatibility (const hb_ot_shape_normalize_context_t *c, hb_codepoint_t u)
 {
   unsigned int len, i;
   hb_codepoint_t decomposed[HB_UNICODE_MAX_DECOMPOSITION_LEN];
   hb_codepoint_t glyphs[HB_UNICODE_MAX_DECOMPOSITION_LEN];
 
-  len = buffer->unicode->decompose_compatibility (u, decomposed);
+  len = c->buffer->unicode->decompose_compatibility (u, decomposed);
   if (!len)
     return 0;
 
   for (i = 0; i < len; i++)
-    if (!font->get_glyph (decomposed[i], 0, &glyphs[i]))
+    if (!c->font->get_glyph (decomposed[i], 0, &glyphs[i]))
       return 0;
 
   for (i = 0; i < len; i++)
-    output_char (buffer, decomposed[i], glyphs[i]);
+    output_char (c->buffer, decomposed[i], glyphs[i]);
 
   return len;
 }
 
 /* Returns true if recomposition may be benefitial. */
 static inline bool
-decompose_current_character (hb_font_t *font, hb_buffer_t *buffer, bool shortest)
+decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shortest)
 {
+  hb_buffer_t * const buffer = c->buffer;
   hb_codepoint_t glyph;
   unsigned int len = 1;
 
   /* Kind of a cute waterfall here... */
-  if (shortest && font->get_glyph (buffer->cur().codepoint, 0, &glyph))
+  if (shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph))
     next_char (buffer, glyph);
-  else if ((len = decompose (font, buffer, shortest, buffer->cur().codepoint)))
+  else if ((len = decompose (c, shortest, buffer->cur().codepoint)))
     skip_char (buffer);
-  else if (!shortest && font->get_glyph (buffer->cur().codepoint, 0, &glyph))
+  else if (!shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph))
     next_char (buffer, glyph);
-  else if ((len = decompose_compatibility (font, buffer, buffer->cur().codepoint)))
+  else if ((len = decompose_compatibility (c, buffer->cur().codepoint)))
     skip_char (buffer);
   else
     next_char (buffer, glyph); /* glyph is initialized in earlier branches. */
@@ -374,49 +219,51 @@
 }
 
 static inline void
-handle_variation_selector_cluster (hb_font_t *font, hb_buffer_t *buffer, unsigned int end)
+handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end)
 {
+  hb_buffer_t * const buffer = c->buffer;
   for (; buffer->idx < end - 1;) {
     if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) {
       /* The next two lines are some ugly lines... But work. */
-      font->get_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index());
+      c->font->get_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index());
       buffer->replace_glyphs (2, 1, &buffer->cur().codepoint);
     } else {
-      set_glyph (buffer->cur(), font);
+      set_glyph (buffer->cur(), c->font);
       buffer->next_glyph ();
     }
   }
   if (likely (buffer->idx < end)) {
-    set_glyph (buffer->cur(), font);
+    set_glyph (buffer->cur(), c->font);
     buffer->next_glyph ();
   }
 }
 
 /* Returns true if recomposition may be benefitial. */
 static inline bool
-decompose_multi_char_cluster (hb_font_t *font, hb_buffer_t *buffer, unsigned int end)
+decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end)
 {
+  hb_buffer_t * const buffer = c->buffer;
   /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
   for (unsigned int i = buffer->idx; i < end; i++)
     if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) {
-      handle_variation_selector_cluster (font, buffer, end);
+      handle_variation_selector_cluster (c, end);
       return false;
     }
 
   while (buffer->idx < end)
-    decompose_current_character (font, buffer, false);
+    decompose_current_character (c, false);
   /* We can be smarter here and only return true if there are at least two ccc!=0 marks.
    * But does not matter. */
   return true;
 }
 
 static inline bool
-decompose_cluster (hb_font_t *font, hb_buffer_t *buffer, bool short_circuit, unsigned int end)
+decompose_cluster (const hb_ot_shape_normalize_context_t *c, bool short_circuit, unsigned int end)
 {
-  if (likely (buffer->idx + 1 == end))
-    return decompose_current_character (font, buffer, short_circuit);
+  if (likely (c->buffer->idx + 1 == end))
+    return decompose_current_character (c, short_circuit);
   else
-    return decompose_multi_char_cluster (font, buffer, end);
+    return decompose_multi_char_cluster (c, end);
 }
 
 
@@ -431,9 +278,22 @@
 
 
 void
-_hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
-			hb_ot_shape_normalization_mode_t mode)
+_hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
+			hb_buffer_t *buffer,
+			hb_font_t *font)
 {
+  hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference ?
+					  plan->shaper->normalization_preference (&buffer->props) :
+					  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT;
+  const hb_ot_shape_normalize_context_t c = {
+    plan,
+    buffer,
+    font,
+    buffer->unicode,
+    plan->shaper->decompose ? plan->shaper->decompose : decompose_unicode,
+    plan->shaper->compose   ? plan->shaper->compose   : compose_unicode
+  };
+
   bool short_circuit = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED &&
 		       mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
   bool can_use_recompose = false;
@@ -457,7 +317,7 @@
       if (buffer->cur().cluster != buffer->info[end].cluster)
         break;
 
-    can_use_recompose = decompose_cluster (font, buffer, short_circuit, end) || can_use_recompose;
+    can_use_recompose = decompose_cluster (&c, short_circuit, end) || can_use_recompose;
   }
   buffer->swap_buffers ();
 
@@ -517,10 +377,10 @@
 	(starter == buffer->out_len - 1 ||
 	 _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) &&
 	/* And compose. */
-	compose_func (buffer->unicode,
-		      buffer->out_info[starter].codepoint,
-		      buffer->cur().codepoint,
-		      &composed) &&
+	c.compose (&c,
+		   buffer->out_info[starter].codepoint,
+		   buffer->cur().codepoint,
+		   &composed) &&
 	/* And the font has glyph for the composite. */
 	font->get_glyph (composed, 0, &glyph))
     {
diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape-private.hh
index ae01215..23e80b7 100644
--- a/src/hb-ot-shape-private.hh
+++ b/src/hb-ot-shape-private.hh
@@ -46,7 +46,16 @@
   hb_ot_map_t map;
   const void *data;
 
-  inline void substitute_closure (hb_face_t *face, hb_set_t *glyphs) const { map.substitute_closure (this, face, glyphs); }
+  inline void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const
+  {
+    unsigned int table_index;
+    switch (table_tag) {
+      case HB_OT_TAG_GSUB: table_index = 0; break;
+      case HB_OT_TAG_GPOS: table_index = 1; break;
+      default: return;
+    }
+    map.collect_lookups (table_index, lookups);
+  }
   inline void substitute (hb_font_t *font, hb_buffer_t *buffer) const { map.substitute (this, font, buffer); }
   inline void position (hb_font_t *font, hb_buffer_t *buffer) const { map.position (this, font, buffer); }
 
@@ -65,14 +74,14 @@
 			 face (master_plan->face),
 			 props (master_plan->props),
 			 shaper (NULL),
-			 map () {}
+			 map (face, &props) {}
   ~hb_ot_shape_planner_t (void) { map.finish (); }
 
   inline void compile (hb_ot_shape_plan_t &plan)
   {
     plan.props = props;
     plan.shaper = shaper;
-    map.compile (face, &props, plan.map);
+    map.compile (plan.map);
   }
 
   private:
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 9a6260a..96461d7 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -175,7 +175,7 @@
 
   hb_ot_shape_planner_t planner (shape_plan);
 
-  planner.shaper = hb_ot_shape_complex_categorize (&shape_plan->props);
+  planner.shaper = hb_ot_shape_complex_categorize (&planner);
 
   hb_ot_shape_collect_features (&planner, &shape_plan->props, user_features, num_user_features);
 
@@ -237,7 +237,7 @@
 static void
 hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font)
 {
-  if (buffer->context_len[0] ||
+  if (!(buffer->flags & HB_BUFFER_FLAG_BOT) ||
       _hb_glyph_info_get_general_category (&buffer->info[0]) !=
       HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
     return;
@@ -348,8 +348,8 @@
   unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++)
     c->buffer->info[i].glyph_props() = _hb_glyph_info_get_general_category (&c->buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK ?
-				       HB_OT_LAYOUT_GLYPH_CLASS_MARK :
-				       HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
+				       HB_OT_LAYOUT_GLYPH_PROPS_MARK :
+				       HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH;
 }
 
 static inline void
@@ -362,10 +362,7 @@
 
   HB_BUFFER_ALLOCATE_VAR (c->buffer, glyph_index);
 
-  _hb_ot_shape_normalize (c->font, c->buffer,
-			  c->plan->shaper->normalization_preference ?
-			  c->plan->shaper->normalization_preference (c->plan) :
-			  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT);
+  _hb_ot_shape_normalize (c->plan, c->buffer, c->font);
 
   hb_ot_shape_setup_masks (c);
 
@@ -455,12 +452,6 @@
 }
 
 static inline void
-hb_ot_position_complex_fallback (hb_ot_shape_context_t *c)
-{
-  _hb_ot_shape_fallback_position (c->plan, c->font, c->buffer);
-}
-
-static inline void
 hb_ot_truetype_kern (hb_ot_shape_context_t *c)
 {
   /* TODO Check for kern=0 */
@@ -486,34 +477,33 @@
 }
 
 static inline void
-hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
-{
-  hb_ot_truetype_kern (c);
-}
-
-static inline void
 hb_ot_position (hb_ot_shape_context_t *c)
 {
   hb_ot_position_default (c);
 
   hb_bool_t fallback = !hb_ot_position_complex (c);
 
-  if (fallback)
-    hb_ot_position_complex_fallback (c);
+  if (fallback && c->plan->shaper->fallback_position)
+    _hb_ot_shape_fallback_position (c->plan, c->font, c->buffer);
 
   if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
     hb_buffer_reverse (c->buffer);
 
+  /* Visual fallback goes here. */
+
   if (fallback)
-    hb_position_complex_fallback_visual (c);
+    hb_ot_truetype_kern (c);
 }
 
 
 /* Post-process */
 
 static void
-hb_ot_hide_zerowidth (hb_ot_shape_context_t *c)
+hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c)
 {
+  if (c->buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)
+    return;
+
   hb_codepoint_t space = 0;
 
   unsigned int count = c->buffer->len;
@@ -557,7 +547,7 @@
   hb_ot_substitute (c);
   hb_ot_position (c);
 
-  hb_ot_hide_zerowidth (c);
+  hb_ot_hide_default_ignorables (c);
 
   HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props1);
   HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props0);
@@ -582,16 +572,36 @@
 }
 
 
-
-static inline void
-hb_ot_map_glyphs_dumb (hb_font_t    *font,
-		       hb_buffer_t  *buffer)
+void
+hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
+				  hb_tag_t         table_tag,
+				  hb_set_t        *lookup_indexes /* OUT */)
 {
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    font->get_glyph (buffer->info[i].codepoint, 0, &buffer->info[i].codepoint);
+  /* XXX Does the first part always succeed? */
+  HB_SHAPER_DATA_GET (shape_plan)->collect_lookups (table_tag, lookup_indexes);
 }
 
+
+/* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */
+static void
+add_char (hb_font_t          *font,
+	  hb_unicode_funcs_t *unicode,
+	  hb_bool_t           mirror,
+	  hb_codepoint_t      u,
+	  hb_set_t           *glyphs)
+{
+  hb_codepoint_t glyph;
+  if (font->get_glyph (u, 0, &glyph))
+    glyphs->add (glyph);
+  if (mirror)
+  {
+    hb_codepoint_t m = unicode->mirroring (u);
+    if (m != u && font->get_glyph (m, 0, &glyph))
+      glyphs->add (glyph);
+  }
+}
+
+
 void
 hb_ot_shape_glyphs_closure (hb_font_t          *font,
 			    hb_buffer_t        *buffer,
@@ -601,29 +611,30 @@
 {
   hb_ot_shape_plan_t plan;
 
-  buffer->guess_properties ();
+  buffer->guess_segment_properties ();
 
-  /* TODO cache / ensure correct backend, etc. */
-  hb_shape_plan_t *shape_plan = hb_shape_plan_create (font->face, &buffer->props, features, num_features, NULL);
+  const char *shapers[] = {"ot", NULL};
+  hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
+							     features, num_features, shapers);
 
-  /* TODO: normalization? have shapers do closure()? */
-  /* TODO: Deal with mirrored chars? */
-  hb_ot_map_glyphs_dumb (font, buffer);
+  bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL;
 
-  /* 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++)
-    glyphs->add (buffer->info[i].codepoint);
+    add_char (font, buffer->unicode, mirror, buffer->info[i].codepoint, glyphs);
+
+  hb_set_t lookups;
+  lookups.init ();
+  hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, &lookups);
 
   /* And find transitive closure. */
   hb_set_t copy;
   copy.init ();
-
   do {
     copy.set (glyphs);
-    HB_SHAPER_DATA_GET (shape_plan)->substitute_closure (font->face, glyphs);
-  } while (!copy.equal (glyphs));
+    for (hb_codepoint_t lookup_index = -1; hb_set_next (&lookups, &lookup_index);)
+      hb_ot_layout_lookup_substitute_closure (font->face, lookup_index, glyphs);
+  } while (!copy.is_equal (glyphs));
 
   hb_shape_plan_destroy (shape_plan);
 }
diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc
index ac60e96..91ebec7 100644
--- a/src/hb-ot-tag.cc
+++ b/src/hb-ot-tag.cc
@@ -23,7 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
- * Google Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod, Roozbeh Pournader
  */
 
 #include "hb-private.hh"
@@ -38,6 +38,8 @@
 static hb_tag_t
 hb_ot_old_tag_from_script (hb_script_t script)
 {
+  /* This seems to be accurate as of end of 2012. */
+
   switch ((hb_tag_t) script) {
     case HB_SCRIPT_INVALID:		return HB_OT_TAG_DEFAULT_SCRIPT;
 
@@ -91,6 +93,7 @@
     case HB_SCRIPT_ORIYA:		return HB_TAG('o','r','y','2');
     case HB_SCRIPT_TAMIL:		return HB_TAG('t','m','l','2');
     case HB_SCRIPT_TELUGU:		return HB_TAG('t','e','l','2');
+    case HB_SCRIPT_MYANMAR:		return HB_TAG('m','y','m','2');
   }
 
   return HB_OT_TAG_DEFAULT_SCRIPT;
@@ -109,6 +112,7 @@
     case HB_TAG('o','r','y','2'):	return HB_SCRIPT_ORIYA;
     case HB_TAG('t','m','l','2'):	return HB_SCRIPT_TAMIL;
     case HB_TAG('t','e','l','2'):	return HB_SCRIPT_TELUGU;
+    case HB_TAG('m','y','m','2'):	return HB_SCRIPT_MYANMAR;
   }
 
   return HB_SCRIPT_UNKNOWN;
@@ -165,7 +169,9 @@
  * Draft OpenType 1.5 spec, with with the ISO 639-3 codes from
  * 2008/08/04, matching on name, and finally adjusted manually.
  *
- * Many items still missing.  Those are commented out at the end.
+ * Updated on 2012/12/07 with more research into remaining codes.
+ *
+ * Some items still missing.  Those are commented out at the end.
  * Keep sorted for bsearch.
  */
 
@@ -173,43 +179,63 @@
   {"aa",	HB_TAG('A','F','R',' ')},	/* Afar */
   {"ab",	HB_TAG('A','B','K',' ')},	/* Abkhazian */
   {"abq",	HB_TAG('A','B','A',' ')},	/* Abaza */
+  {"ada",	HB_TAG('D','N','G',' ')},	/* Dangme */
   {"ady",	HB_TAG('A','D','Y',' ')},	/* Adyghe */
   {"af",	HB_TAG('A','F','K',' ')},	/* Afrikaans */
+  {"aii",	HB_TAG('S','W','A',' ')},	/* Swadaya Aramaic */
   {"aiw",	HB_TAG('A','R','I',' ')},	/* Aari */
+  {"alt",	HB_TAG('A','L','T',' ')},	/* [Southern] Altai */
   {"am",	HB_TAG('A','M','H',' ')},	/* Amharic */
+  {"amf",	HB_TAG('H','B','N',' ')},	/* Hammer-Banna */
   {"ar",	HB_TAG('A','R','A',' ')},	/* Arabic */
   {"arn",	HB_TAG('M','A','P',' ')},	/* Mapudungun */
   {"as",	HB_TAG('A','S','M',' ')},	/* Assamese */
+  {"ath",	HB_TAG('A','T','H',' ')},	/* Athapaskan [family] */
+  {"atv",	HB_TAG('A','L','T',' ')},	/* [Northern] Altai */
   {"av",	HB_TAG('A','V','R',' ')},	/* Avaric */
   {"awa",	HB_TAG('A','W','A',' ')},	/* Awadhi */
   {"ay",	HB_TAG('A','Y','M',' ')},	/* Aymara */
   {"az",	HB_TAG('A','Z','E',' ')},	/* Azerbaijani */
   {"ba",	HB_TAG('B','S','H',' ')},	/* Bashkir */
+  {"bai",	HB_TAG('B','M','L',' ')},	/* Bamileke [family] */
   {"bal",	HB_TAG('B','L','I',' ')},	/* Baluchi */
+  {"bci",	HB_TAG('B','A','U',' ')},	/* Baule */
   {"bcq",	HB_TAG('B','C','H',' ')},	/* Bench */
+  {"be",	HB_TAG('B','E','L',' ')},  	/* Belarussian */
   {"bem",	HB_TAG('B','E','M',' ')},	/* Bemba (Zambia) */
+  {"ber",	HB_TAG('B','E','R',' ')},  	/* Berber [family] */
   {"bfq",	HB_TAG('B','A','D',' ')},	/* Badaga */
   {"bft",	HB_TAG('B','L','T',' ')},	/* Balti */
+  {"bfy",	HB_TAG('B','A','G',' ')},	/* Baghelkhandi */
   {"bg",	HB_TAG('B','G','R',' ')},	/* Bulgarian */
   {"bhb",	HB_TAG('B','H','I',' ')},	/* Bhili */
   {"bho",	HB_TAG('B','H','O',' ')},	/* Bhojpuri */
   {"bik",	HB_TAG('B','I','K',' ')},	/* Bikol */
   {"bin",	HB_TAG('E','D','O',' ')},	/* Bini */
+  {"bjt",	HB_TAG('B','L','N',' ')},	/* Balanta-Ganja */
+  {"bla",	HB_TAG('B','K','F',' ')},	/* Blackfoot */
+  {"ble",	HB_TAG('B','L','N',' ')},	/* Balanta-Kentohe */
   {"bm",	HB_TAG('B','M','B',' ')},	/* Bambara */
   {"bn",	HB_TAG('B','E','N',' ')},	/* Bengali */
   {"bo",	HB_TAG('T','I','B',' ')},	/* Tibetan */
   {"br",	HB_TAG('B','R','E',' ')},	/* Breton */
+  {"bra",	HB_TAG('B','R','I',' ')},	/* Braj Bhasha */
   {"brh",	HB_TAG('B','R','H',' ')},	/* Brahui */
   {"bs",	HB_TAG('B','O','S',' ')},	/* Bosnian */
   {"btb",	HB_TAG('B','T','I',' ')},	/* Beti (Cameroon) */
+  {"bxr",	HB_TAG('R','B','U',' ')},	/* Russian Buriat */
+  {"byn",	HB_TAG('B','I','L',' ')},	/* Bilen */
   {"ca",	HB_TAG('C','A','T',' ')},	/* Catalan */
   {"ce",	HB_TAG('C','H','E',' ')},	/* Chechen */
   {"ceb",	HB_TAG('C','E','B',' ')},	/* Cebuano */
   {"chp",	HB_TAG('C','H','P',' ')},	/* Chipewyan */
   {"chr",	HB_TAG('C','H','R',' ')},	/* Cherokee */
+  {"ckt",	HB_TAG('C','H','K',' ')},	/* Chukchi */
   {"cop",	HB_TAG('C','O','P',' ')},	/* Coptic */
   {"cr",	HB_TAG('C','R','E',' ')},	/* Cree */
   {"crh",	HB_TAG('C','R','T',' ')},	/* Crimean Tatar */
+  {"crj",	HB_TAG('E','C','R',' ')},	/* [Southern] East Cree */
+  {"crl",	HB_TAG('E','C','R',' ')},	/* [Northern] East Cree */
   {"crm",	HB_TAG('M','C','R',' ')},	/* Moose Cree */
   {"crx",	HB_TAG('C','R','R',' ')},	/* Carrier */
   {"cs",	HB_TAG('C','S','Y',' ')},	/* Czech */
@@ -222,10 +248,12 @@
   {"dar",	HB_TAG('D','A','R',' ')},	/* Dargwa */
   {"de",	HB_TAG('D','E','U',' ')},	/* German */
   {"din",	HB_TAG('D','N','K',' ')},	/* Dinka */
+  {"dje",	HB_TAG('D','J','R',' ')},	/* Djerma */
   {"dng",	HB_TAG('D','U','N',' ')},	/* Dungan */
   {"doi",	HB_TAG('D','G','R',' ')},	/* Dogri */
   {"dsb",	HB_TAG('L','S','B',' ')},	/* Lower Sorbian */
   {"dv",	HB_TAG('D','I','V',' ')},	/* Dhivehi */
+  {"dyu",	HB_TAG('J','U','L',' ')},	/* Jula */
   {"dz",	HB_TAG('D','Z','N',' ')},	/* Dzongkha */
   {"ee",	HB_TAG('E','W','E',' ')},	/* Ewe */
   {"efi",	HB_TAG('E','F','I',' ')},	/* Efik */
@@ -253,20 +281,27 @@
   {"gag",	HB_TAG('G','A','G',' ')},	/* Gagauz */
   {"gbm",	HB_TAG('G','A','W',' ')},	/* Garhwali */
   {"gd",	HB_TAG('G','A','E',' ')},	/* Scottish Gaelic */
+  {"gez",	HB_TAG('G','E','Z',' ')},	/* Ge'ez */
   {"gl",	HB_TAG('G','A','L',' ')},	/* Galician */
   {"gld",	HB_TAG('N','A','N',' ')},	/* Nanai */
   {"gn",	HB_TAG('G','U','A',' ')},	/* Guarani */
   {"gon",	HB_TAG('G','O','N',' ')},	/* Gondi */
   {"grt",	HB_TAG('G','R','O',' ')},	/* Garo */
+  {"gru",	HB_TAG('S','O','G',' ')},	/* Sodo Gurage */
   {"gu",	HB_TAG('G','U','J',' ')},	/* Gujarati */
   {"guk",	HB_TAG('G','M','Z',' ')},	/* Gumuz */
   {"gv",	HB_TAG('M','N','X',' ')},	/* Manx Gaelic */
   {"ha",	HB_TAG('H','A','U',' ')},	/* Hausa */
   {"har",	HB_TAG('H','R','I',' ')},	/* Harari */
+  {"haw",	HB_TAG('H','A','W',' ')},  	/* Hawaiin */
   {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
   {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
   {"hil",	HB_TAG('H','I','L',' ')},	/* Hiligaynon */
+  {"hnd",	HB_TAG('H','N','D',' ')},	/* [Southern] Hindko */
+  {"hne",	HB_TAG('C','H','H',' ')},	/* Chattisgarhi */
+  {"hno",	HB_TAG('H','N','D',' ')},	/* [Northern] Hindko */
   {"hoc",	HB_TAG('H','O',' ',' ')},	/* Ho */
+  {"hoj",	HB_TAG('H','A','R',' ')},	/* Harauti */
   {"hr",	HB_TAG('H','R','V',' ')},	/* Croatian */
   {"hsb",	HB_TAG('U','S','B',' ')},	/* Upper Sorbian */
   {"ht",	HB_TAG('H','A','I',' ')},	/* Haitian */
@@ -275,6 +310,8 @@
   {"id",	HB_TAG('I','N','D',' ')},	/* Indonesian */
   {"ig",	HB_TAG('I','B','O',' ')},	/* Igbo */
   {"igb",	HB_TAG('E','B','I',' ')},	/* Ebira */
+  {"ijo",	HB_TAG('I','J','O',' ')},	/* Ijo [family] */
+  {"ilo",	HB_TAG('I','L','O',' ')},	/* Ilokano */
   {"inh",	HB_TAG('I','N','G',' ')},	/* Ingush */
   {"is",	HB_TAG('I','S','L',' ')},	/* Icelandic */
   {"it",	HB_TAG('I','T','A',' ')},	/* Italian */
@@ -282,19 +319,25 @@
   {"ja",	HB_TAG('J','A','N',' ')},	/* Japanese */
   {"jv",	HB_TAG('J','A','V',' ')},	/* Javanese */
   {"ka",	HB_TAG('K','A','T',' ')},	/* Georgian */
+  {"kaa",	HB_TAG('K','R','K',' ')},	/* Karakalpak */
   {"kam",	HB_TAG('K','M','B',' ')},	/* Kamba (Kenya) */
+  {"kar",	HB_TAG('K','R','N',' ')},	/* Karen [family] */
   {"kbd",	HB_TAG('K','A','B',' ')},	/* Kabardian */
   {"kdr",	HB_TAG('K','R','M',' ')},	/* Karaim */
   {"kdt",	HB_TAG('K','U','Y',' ')},	/* Kuy */
+  {"kex",	HB_TAG('K','K','N',' ')},	/* Kokni */
   {"kfr",	HB_TAG('K','A','C',' ')},	/* Kachchi */
   {"kfy",	HB_TAG('K','M','N',' ')},	/* Kumaoni */
   {"kha",	HB_TAG('K','S','I',' ')},	/* Khasi */
+  {"khb",	HB_TAG('X','B','D',' ')},	/* Tai Lue */
   {"khw",	HB_TAG('K','H','W',' ')},	/* Khowar */
   {"ki",	HB_TAG('K','I','K',' ')},	/* Kikuyu */
+  {"kjh",	HB_TAG('K','H','A',' ')},	/* Khakass */
   {"kk",	HB_TAG('K','A','Z',' ')},	/* Kazakh */
   {"kl",	HB_TAG('G','R','N',' ')},	/* Kalaallisut */
   {"kln",	HB_TAG('K','A','L',' ')},	/* Kalenjin */
   {"km",	HB_TAG('K','H','M',' ')},	/* Central Khmer */
+  {"kmb",	HB_TAG('M','B','N',' ')},	/* [North] Mbundu */
   {"kmw",	HB_TAG('K','M','O',' ')},	/* Komo (Democratic Republic of Congo) */
   {"kn",	HB_TAG('K','A','N',' ')},	/* Kannada */
   {"ko",	HB_TAG('K','O','R',' ')},	/* Korean */
@@ -312,6 +355,7 @@
   {"ku",	HB_TAG('K','U','R',' ')},	/* Kurdish */
   {"kum",	HB_TAG('K','U','M',' ')},	/* Kumyk */
   {"kvd",	HB_TAG('K','U','I',' ')},	/* Kui (Indonesia) */
+  {"kxc",	HB_TAG('K','M','S',' ')},	/* Komso */
   {"kxu",	HB_TAG('K','U','I',' ')},	/* Kui (India) */
   {"ky",	HB_TAG('K','I','R',' ')},	/* Kirghiz */
   {"la",	HB_TAG('L','A','T',' ')},	/* Latin */
@@ -319,13 +363,19 @@
   {"lb",	HB_TAG('L','T','Z',' ')},	/* Luxembourgish */
   {"lbe",	HB_TAG('L','A','K',' ')},	/* Lak */
   {"lbj",	HB_TAG('L','D','K',' ')},	/* Ladakhi */
+  {"lez",	HB_TAG('L','E','Z',' ')},	/* Lezgi */
+  {"lg",	HB_TAG('L','U','G',' ')},	/* Luganda */
   {"lif",	HB_TAG('L','M','B',' ')},	/* Limbu */
   {"lld",	HB_TAG('L','A','D',' ')},	/* Ladin */
+  {"lmn",	HB_TAG('L','A','M',' ')},	/* Lambani */
   {"ln",	HB_TAG('L','I','N',' ')},	/* Lingala */
   {"lo",	HB_TAG('L','A','O',' ')},	/* Lao */
   {"lt",	HB_TAG('L','T','H',' ')},	/* Lithuanian */
+  {"lu",	HB_TAG('L','U','B',' ')},	/* Luba-Katanga */
+  {"lua",	HB_TAG('L','U','B',' ')},	/* Luba-Kasai */
   {"luo",	HB_TAG('L','U','O',' ')},	/* Luo (Kenya and Tanzania) */
-  {"luw",	HB_TAG('L','U','O',' ')},	/* Luo (Cameroon) */
+  {"lus",	HB_TAG('M','I','Z',' ')},	/* Mizo */
+  {"luy",	HB_TAG('L','U','H',' ')},	/* Luhya [macrolanguage] */
   {"lv",	HB_TAG('L','V','I',' ')},	/* Latvian */
   {"lzz",	HB_TAG('L','A','Z',' ')},	/* Laz */
   {"mai",	HB_TAG('M','T','H',' ')},	/* Maithili */
@@ -334,6 +384,7 @@
   {"mdy",	HB_TAG('M','L','E',' ')},	/* Male (Ethiopia) */
   {"men",	HB_TAG('M','D','E',' ')},	/* Mende (Sierra Leone) */
   {"mg",	HB_TAG('M','L','G',' ')},	/* Malagasy */
+  {"mhr",	HB_TAG('L','M','A',' ')},	/* Low Mari */
   {"mi",	HB_TAG('M','R','I',' ')},	/* Maori */
   {"mk",	HB_TAG('M','K','D',' ')},	/* Macedonian */
   {"ml",	HB_TAG('M','L','R',' ')},	/* Malayalam */
@@ -347,14 +398,17 @@
   {"moh",	HB_TAG('M','O','H',' ')},	/* Mohawk */
   {"mpe",	HB_TAG('M','A','J',' ')},	/* Majang */
   {"mr",	HB_TAG('M','A','R',' ')},	/* Marathi */
+  {"mrj",	HB_TAG('H','M','A',' ')},	/* High Mari */
   {"ms",	HB_TAG('M','L','Y',' ')},	/* Malay */
   {"mt",	HB_TAG('M','T','S',' ')},	/* Maltese */
   {"mwr",	HB_TAG('M','A','W',' ')},	/* Marwari */
   {"my",	HB_TAG('B','R','M',' ')},	/* Burmese */
   {"mym",	HB_TAG('M','E','N',' ')},	/* Me'en */
   {"myv",	HB_TAG('E','R','Z',' ')},	/* Erzya */
+  {"nag",	HB_TAG('N','A','G',' ')},	/* Naga-Assamese */
   {"nb",	HB_TAG('N','O','R',' ')},	/* Norwegian Bokmål */
   {"nco",	HB_TAG('S','I','B',' ')},	/* Sibe */
+  {"nd",	HB_TAG('N','D','B',' ')},	/* [North] Ndebele */
   {"ne",	HB_TAG('N','E','P',' ')},	/* Nepali */
   {"new",	HB_TAG('N','E','W',' ')},	/* Newari */
   {"ng",	HB_TAG('N','D','G',' ')},	/* Ndonga */
@@ -364,33 +418,45 @@
   {"nl",	HB_TAG('N','L','D',' ')},	/* Dutch */
   {"nn",	HB_TAG('N','Y','N',' ')},	/* Norwegian Nynorsk */
   {"no",	HB_TAG('N','O','R',' ')},	/* Norwegian (deprecated) */
+  {"nod",	HB_TAG('N','T','A',' ')},	/* Northern Tai */
   {"nog",	HB_TAG('N','O','G',' ')},	/* Nogai */
   {"nqo",	HB_TAG('N','K','O',' ')},	/* N'Ko */
+  {"nr",	HB_TAG('N','D','B',' ')},	/* [South] Ndebele */
   {"nsk",	HB_TAG('N','A','S',' ')},	/* Naskapi */
+  {"nso",	HB_TAG('S','O','T',' ')},	/* [Northern] Sotho */
   {"ny",	HB_TAG('C','H','I',' ')},	/* Nyanja */
+  {"nyn",	HB_TAG('N','K','L',' ')},	/* Nkole */
   {"oc",	HB_TAG('O','C','I',' ')},	/* Occitan (post 1500) */
   {"oj",	HB_TAG('O','J','B',' ')},	/* Ojibwa */
+  {"ojs",	HB_TAG('O','C','R',' ')},	/* Oji-Cree */
   {"om",	HB_TAG('O','R','O',' ')},	/* Oromo */
   {"or",	HB_TAG('O','R','I',' ')},	/* Oriya */
   {"os",	HB_TAG('O','S','S',' ')},	/* Ossetian */
   {"pa",	HB_TAG('P','A','N',' ')},	/* Panjabi */
+  {"pce",	HB_TAG('P','L','G',' ')},	/* [Ruching] Palaung */
   {"pi",	HB_TAG('P','A','L',' ')},	/* Pali */
   {"pl",	HB_TAG('P','L','K',' ')},	/* Polish */
+  {"pll",	HB_TAG('P','L','G',' ')},	/* [Shwe] Palaung */
   {"plp",	HB_TAG('P','A','P',' ')},	/* Palpa */
   {"prs",	HB_TAG('D','R','I',' ')},	/* Dari */
   {"ps",	HB_TAG('P','A','S',' ')},	/* Pushto */
   {"pt",	HB_TAG('P','T','G',' ')},	/* Portuguese */
   {"raj",	HB_TAG('R','A','J',' ')},	/* Rajasthani */
+  {"rbb",	HB_TAG('P','L','G',' ')},	/* [Rumai] Palaung */
   {"ria",	HB_TAG('R','I','A',' ')},	/* Riang (India) */
   {"ril",	HB_TAG('R','I','A',' ')},	/* Riang (Myanmar) */
+  {"rki",	HB_TAG('A','R','K',' ')},	/* Arakanese */
+  {"rm",	HB_TAG('R','M','S',' ')},	/* Rhaeto-Romanic */
   {"ro",	HB_TAG('R','O','M',' ')},	/* Romanian */
   {"rom",	HB_TAG('R','O','Y',' ')},	/* Romany */
   {"ru",	HB_TAG('R','U','S',' ')},	/* Russian */
   {"rue",	HB_TAG('R','S','Y',' ')},	/* Rusyn */
+  {"rw",	HB_TAG('R','U','A',' ')},	/* Ruanda */
   {"sa",	HB_TAG('S','A','N',' ')},	/* Sanskrit */
   {"sah",	HB_TAG('Y','A','K',' ')},	/* Yakut */
   {"sat",	HB_TAG('S','A','T',' ')},	/* Santali */
   {"sck",	HB_TAG('S','A','D',' ')},	/* Sadri */
+  {"scs",	HB_TAG('S','L','A',' ')},	/* [North] Slavey */
   {"sd",	HB_TAG('S','N','D',' ')},	/* Sindhi */
   {"se",	HB_TAG('N','S','M',' ')},	/* Northern Sami */
   {"seh",	HB_TAG('S','N','A',' ')},	/* Sena */
@@ -413,6 +479,8 @@
   {"sq",	HB_TAG('S','Q','I',' ')},	/* Albanian */
   {"sr",	HB_TAG('S','R','B',' ')},	/* Serbian */
   {"srr",	HB_TAG('S','R','R',' ')},	/* Serer */
+  {"ss",	HB_TAG('S','W','Z',' ')},	/* Swazi */
+  {"st",	HB_TAG('S','O','T',' ')},	/* [Southern] Sotho */
   {"suq",	HB_TAG('S','U','R',' ')},	/* Suri */
   {"sv",	HB_TAG('S','V','E',' ')},	/* Swedish */
   {"sva",	HB_TAG('S','V','A',' ')},	/* Svan */
@@ -420,31 +488,34 @@
   {"swb",	HB_TAG('C','M','R',' ')},	/* Comorian */
   {"syr",	HB_TAG('S','Y','R',' ')},	/* Syriac */
   {"ta",	HB_TAG('T','A','M',' ')},	/* Tamil */
+  {"tab",	HB_TAG('T','A','B',' ')},	/* Tabasaran */
   {"tcy",	HB_TAG('T','U','L',' ')},	/* Tulu */
   {"te",	HB_TAG('T','E','L',' ')},	/* Telugu */
+  {"tem",	HB_TAG('T','M','N',' ')},	/* Temne */
   {"tg",	HB_TAG('T','A','J',' ')},	/* Tajik */
   {"th",	HB_TAG('T','H','A',' ')},	/* Thai */
   {"ti",	HB_TAG('T','G','Y',' ')},	/* Tigrinya */
   {"tig",	HB_TAG('T','G','R',' ')},	/* Tigre */
   {"tk",	HB_TAG('T','K','M',' ')},	/* Turkmen */
   {"tn",	HB_TAG('T','N','A',' ')},	/* Tswana */
-  {"tnz",	HB_TAG('T','N','G',' ')},	/* Tonga (Thailand) */
-  {"to",	HB_TAG('T','N','G',' ')},	/* Tonga (Tonga Islands) */
-  {"tog",	HB_TAG('T','N','G',' ')},	/* Tonga (Nyasa) */
-  {"toi",	HB_TAG('T','N','G',' ')},	/* Tonga (Zambia) */
+  {"to",	HB_TAG('T','G','N',' ')},	/* Tonga (Tonga Islands) */
   {"tr",	HB_TAG('T','R','K',' ')},	/* Turkish */
+  {"tru",	HB_TAG('T','U','A',' ')},	/* Turoyo Aramaic */
   {"ts",	HB_TAG('T','S','G',' ')},	/* Tsonga */
   {"tt",	HB_TAG('T','A','T',' ')},	/* Tatar */
   {"tw",	HB_TAG('T','W','I',' ')},	/* Twi */
   {"ty",	HB_TAG('T','H','T',' ')},	/* Tahitian */
+  {"tyv",	HB_TAG('T','U','V',' ')},	/* Tuvin */
   {"udm",	HB_TAG('U','D','M',' ')},	/* Udmurt */
   {"ug",	HB_TAG('U','Y','G',' ')},	/* Uighur */
   {"uk",	HB_TAG('U','K','R',' ')},	/* Ukrainian */
+  {"umb",	HB_TAG('M','B','N',' ')},	/* [South] Mbundu */
   {"unr",	HB_TAG('M','U','N',' ')},	/* Mundari */
   {"ur",	HB_TAG('U','R','D',' ')},	/* Urdu */
   {"uz",	HB_TAG('U','Z','B',' ')},	/* Uzbek */
   {"ve",	HB_TAG('V','E','N',' ')},	/* Venda */
   {"vi",	HB_TAG('V','I','T',' ')},	/* Vietnamese */
+  {"vmw",	HB_TAG('M','A','K',' ')},	/* Makua */
   {"wbm",	HB_TAG('W','A',' ',' ')},	/* Wa */
   {"wbr",	HB_TAG('W','A','G',' ')},	/* Wagdi */
   {"wo",	HB_TAG('W','L','F',' ')},	/* Wolof */
@@ -458,115 +529,56 @@
   {"zne",	HB_TAG('Z','N','D',' ')},	/* Zande */
   {"zu",	HB_TAG('Z','U','L',' ')} 	/* Zulu */
 
-  /* I couldn't find the language id for these */
+  /* The corresponding languages IDs for the following IDs are unclear,
+   * overlap, or are architecturally weird. Needs more research. */
 
-/*{"??",	HB_TAG('A','G','W',' ')},*/	/* Agaw */
-/*{"??",	HB_TAG('A','L','S',' ')},*/	/* Alsatian */
-/*{"??",	HB_TAG('A','L','T',' ')},*/	/* Altai */
-/*{"??",	HB_TAG('A','R','K',' ')},*/	/* Arakanese */
-/*{"??",	HB_TAG('A','T','H',' ')},*/	/* Athapaskan */
-/*{"??",	HB_TAG('B','A','G',' ')},*/	/* Baghelkhandi */
-/*{"??",	HB_TAG('B','A','L',' ')},*/	/* Balkar */
-/*{"??",	HB_TAG('B','A','U',' ')},*/	/* Baule */
-/*{"??",	HB_TAG('B','B','R',' ')},*/	/* Berber */
+/*{"ahg/awn/xan?",	HB_TAG('A','G','W',' ')},*/	/* Agaw */
+/*{"gsw?/gsw-FR?",	HB_TAG('A','L','S',' ')},*/	/* Alsatian */
+/*{"krc",	HB_TAG('B','A','L',' ')},*/	/* Balkar */
 /*{"??",	HB_TAG('B','C','R',' ')},*/	/* Bible Cree */
-/*{"??",	HB_TAG('B','E','L',' ')},*/	/* Belarussian */
-/*{"??",	HB_TAG('B','I','L',' ')},*/	/* Bilen */
-/*{"??",	HB_TAG('B','K','F',' ')},*/	/* Blackfoot */
-/*{"??",	HB_TAG('B','L','N',' ')},*/	/* Balante */
-/*{"??",	HB_TAG('B','M','L',' ')},*/	/* Bamileke */
-/*{"??",	HB_TAG('B','R','I',' ')},*/	/* Braj Bhasha */
-/*{"??",	HB_TAG('C','H','G',' ')},*/	/* Chaha Gurage */
-/*{"??",	HB_TAG('C','H','H',' ')},*/	/* Chattisgarhi */
-/*{"??",	HB_TAG('C','H','K',' ')},*/	/* Chukchi */
-/*{"??",	HB_TAG('D','J','R',' ')},*/	/* Djerma */
-/*{"??",	HB_TAG('D','N','G',' ')},*/	/* Dangme */
-/*{"??",	HB_TAG('E','C','R',' ')},*/	/* Eastern Cree */
-/*{"??",	HB_TAG('F','A','N',' ')},*/	/* French Antillean */
-/*{"??",	HB_TAG('F','L','E',' ')},*/	/* Flemish */
-/*{"??",	HB_TAG('F','N','E',' ')},*/	/* Forest Nenets */
-/*{"??",	HB_TAG('F','T','A',' ')},*/	/* Futa */
-/*{"??",	HB_TAG('G','A','R',' ')},*/	/* Garshuni */
-/*{"??",	HB_TAG('G','E','Z',' ')},*/	/* Ge'ez */
-/*{"??",	HB_TAG('H','A','L',' ')},*/	/* Halam */
-/*{"??",	HB_TAG('H','A','R',' ')},*/	/* Harauti */
-/*{"??",	HB_TAG('H','A','W',' ')},*/	/* Hawaiin */
-/*{"??",	HB_TAG('H','B','N',' ')},*/	/* Hammer-Banna */
-/*{"??",	HB_TAG('H','M','A',' ')},*/	/* High Mari */
-/*{"??",	HB_TAG('H','N','D',' ')},*/	/* Hindko */
-/*{"??",	HB_TAG('I','J','O',' ')},*/	/* Ijo */
-/*{"??",	HB_TAG('I','L','O',' ')},*/	/* Ilokano */
-/*{"??",	HB_TAG('I','R','T',' ')},*/	/* Irish Traditional */
-/*{"??",	HB_TAG('J','U','L',' ')},*/	/* Jula */
-/*{"??",	HB_TAG('K','A','R',' ')},*/	/* Karachay */
-/*{"??",	HB_TAG('K','E','B',' ')},*/	/* Kebena */
-/*{"??",	HB_TAG('K','G','E',' ')},*/	/* Khutsuri Georgian */
-/*{"??",	HB_TAG('K','H','A',' ')},*/	/* Khakass */
-/*{"??",	HB_TAG('K','H','K',' ')},*/	/* Khanty-Kazim */
-/*{"??",	HB_TAG('K','H','S',' ')},*/	/* Khanty-Shurishkar */
-/*{"??",	HB_TAG('K','H','V',' ')},*/	/* Khanty-Vakhi */
-/*{"??",	HB_TAG('K','I','S',' ')},*/	/* Kisii */
-/*{"??",	HB_TAG('K','K','N',' ')},*/	/* Kokni */
-/*{"??",	HB_TAG('K','M','S',' ')},*/	/* Komso */
-/*{"??",	HB_TAG('K','O','D',' ')},*/	/* Kodagu */
-/*{"??",	HB_TAG('K','O','H',' ')},*/	/* Korean Old Hangul */
-/*{"??",	HB_TAG('K','O','N',' ')},*/	/* Kikongo */
-/*{"??",	HB_TAG('K','R','K',' ')},*/	/* Karakalpak */
-/*{"??",	HB_TAG('K','R','N',' ')},*/	/* Karen */
-/*{"??",	HB_TAG('K','U','L',' ')},*/	/* Kulvi */
+/*{"sgw?",	HB_TAG('C','H','G',' ')},*/	/* Chaha Gurage */
+/*{"acf/gcf?",	HB_TAG('F','A','N',' ')},*/	/* French Antillean */
+/*{"vls/nl-be",	HB_TAG('F','L','E',' ')},*/	/* Flemish */
+/*{"enf?/yrk?",	HB_TAG('F','N','E',' ')},*/	/* Forest Nenets */
+/*{"fuf?",	HB_TAG('F','T','A',' ')},*/	/* Futa */
+/*{"ar-Syrc?",	HB_TAG('G','A','R',' ')},*/	/* Garshuni */
+/*{"cfm/rnl?",	HB_TAG('H','A','L',' ')},*/	/* Halam */
+/*{"ga-Latg?/Latg?",	HB_TAG('I','R','T',' ')},*/	/* Irish Traditional */
+/*{"krc",	HB_TAG('K','A','R',' ')},*/	/* Karachay */
+/*{"alw?/ktb?",	HB_TAG('K','E','B',' ')},*/	/* Kebena */
+/*{"Geok",	HB_TAG('K','G','E',' ')},*/	/* Khutsuri Georgian */
+/*{"kca",	HB_TAG('K','H','K',' ')},*/	/* Khanty-Kazim */
+/*{"kca",	HB_TAG('K','H','S',' ')},*/	/* Khanty-Shurishkar */
+/*{"kca",	HB_TAG('K','H','V',' ')},*/	/* Khanty-Vakhi */
+/*{"guz?/kqs?/kss?",	HB_TAG('K','I','S',' ')},*/	/* Kisii */
+/*{"kfa/kfi?/kpb?/xua?/xuj?",	HB_TAG('K','O','D',' ')},*/	/* Kodagu */
+/*{"okm?/oko?",	HB_TAG('K','O','H',' ')},*/	/* Korean Old Hangul */
+/*{"kon?/ktu?/...",	HB_TAG('K','O','N',' ')},*/	/* Kikongo */
+/*{"kfx?",	HB_TAG('K','U','L',' ')},*/	/* Kulvi */
 /*{"??",	HB_TAG('L','A','H',' ')},*/	/* Lahuli */
-/*{"??",	HB_TAG('L','A','M',' ')},*/	/* Lambani */
 /*{"??",	HB_TAG('L','C','R',' ')},*/	/* L-Cree */
-/*{"??",	HB_TAG('L','E','Z',' ')},*/	/* Lezgi */
-/*{"??",	HB_TAG('L','M','A',' ')},*/	/* Low Mari */
-/*{"??",	HB_TAG('L','U','B',' ')},*/	/* Luba */
-/*{"??",	HB_TAG('L','U','G',' ')},*/	/* Luganda */
-/*{"??",	HB_TAG('L','U','H',' ')},*/	/* Luhya */
-/*{"??",	HB_TAG('M','A','K',' ')},*/	/* Makua */
 /*{"??",	HB_TAG('M','A','L',' ')},*/	/* Malayalam Traditional */
-/*{"??",	HB_TAG('M','B','N',' ')},*/	/* Mbundu */
-/*{"??",	HB_TAG('M','I','Z',' ')},*/	/* Mizo */
-/*{"??",	HB_TAG('M','L','N',' ')},*/	/* Malinke */
-/*{"??",	HB_TAG('M','N','K',' ')},*/	/* Maninka */
+/*{"mnk?/mlq?/...",	HB_TAG('M','L','N',' ')},*/	/* Malinke */
+/*{"man?/myq?/mku?/msc?/...",	HB_TAG('M','N','K',' ')},*/	/* Maninka */
 /*{"??",	HB_TAG('M','O','R',' ')},*/	/* Moroccan */
-/*{"??",	HB_TAG('N','A','G',' ')},*/	/* Naga-Assamese */
 /*{"??",	HB_TAG('N','C','R',' ')},*/	/* N-Cree */
-/*{"??",	HB_TAG('N','D','B',' ')},*/	/* Ndebele */
-/*{"??",	HB_TAG('N','G','R',' ')},*/	/* Nagari */
 /*{"??",	HB_TAG('N','H','C',' ')},*/	/* Norway House Cree */
-/*{"??",	HB_TAG('N','K','L',' ')},*/	/* Nkole */
-/*{"??",	HB_TAG('N','T','A',' ')},*/	/* Northern Tai */
-/*{"??",	HB_TAG('O','C','R',' ')},*/	/* Oji-Cree */
-/*{"??",	HB_TAG('P','A','A',' ')},*/	/* Palestinian Aramaic */
-/*{"??",	HB_TAG('P','G','R',' ')},*/	/* Polytonic Greek */
-/*{"??",	HB_TAG('P','L','G',' ')},*/	/* Palaung */
-/*{"??",	HB_TAG('Q','I','N',' ')},*/	/* Chin */
-/*{"??",	HB_TAG('R','B','U',' ')},*/	/* Russian Buriat */
+/*{"jpa?/sam?",	HB_TAG('P','A','A',' ')},*/	/* Palestinian Aramaic */
+/*{"polyton",	HB_TAG('P','G','R',' ')},*/	/* Polytonic Greek */
+/*{"??",	HB_TAG('Q','I','N',' ')},*/	/* Asho Chin */
 /*{"??",	HB_TAG('R','C','R',' ')},*/	/* R-Cree */
-/*{"??",	HB_TAG('R','M','S',' ')},*/	/* Rhaeto-Romanic */
-/*{"??",	HB_TAG('R','U','A',' ')},*/	/* Ruanda */
-/*{"??",	HB_TAG('S','A','Y',' ')},*/	/* Sayisi */
-/*{"??",	HB_TAG('S','E','K',' ')},*/	/* Sekota */
-/*{"??",	HB_TAG('S','I','G',' ')},*/	/* Silte Gurage */
-/*{"??",	HB_TAG('S','L','A',' ')},*/	/* Slavey */
-/*{"??",	HB_TAG('S','O','G',' ')},*/	/* Sodo Gurage */
-/*{"??",	HB_TAG('S','O','T',' ')},*/	/* Sotho */
-/*{"??",	HB_TAG('S','W','A',' ')},*/	/* Swadaya Aramaic */
-/*{"??",	HB_TAG('S','W','Z',' ')},*/	/* Swazi */
-/*{"??",	HB_TAG('S','X','T',' ')},*/	/* Sutu */
-/*{"??",	HB_TAG('T','A','B',' ')},*/	/* Tabasaran */
+/*{"chp?",	HB_TAG('S','A','Y',' ')},*/	/* Sayisi */
+/*{"xan?",	HB_TAG('S','E','K',' ')},*/	/* Sekota */
+/*{"stv/wle?/xst?",	HB_TAG('S','I','G',' ')},*/	/* Silte Gurage */
+/*{"ngo?",	HB_TAG('S','X','T',' ')},*/	/* Sutu */
 /*{"??",	HB_TAG('T','C','R',' ')},*/	/* TH-Cree */
-/*{"??",	HB_TAG('T','G','N',' ')},*/	/* Tongan */
-/*{"??",	HB_TAG('T','M','N',' ')},*/	/* Temne */
-/*{"??",	HB_TAG('T','N','E',' ')},*/	/* Tundra Nenets */
+/*{"tnz?/tog?/toi?",	HB_TAG('T','N','G',' ')},*/	/* Tonga */
+/*{"enh?/yrk?",	HB_TAG('T','N','E',' ')},*/	/* Tundra Nenets */
 /*{"??",	HB_TAG('T','O','D',' ')},*/	/* Todo */
-/*{"??",	HB_TAG('T','U','A',' ')},*/	/* Turoyo Aramaic */
-/*{"??",	HB_TAG('T','U','V',' ')},*/	/* Tuvin */
 /*{"??",	HB_TAG('W','C','R',' ')},*/	/* West-Cree */
-/*{"??",	HB_TAG('X','B','D',' ')},*/	/* Tai Lue */
 /*{"??",	HB_TAG('Y','C','R',' ')},*/	/* Y-Cree */
 /*{"??",	HB_TAG('Y','I','C',' ')},*/	/* Yi Classic */
-/*{"??",	HB_TAG('Y','I','M',' ')},*/	/* Yi Modern */
+/*{"ii?/Yiii?",	HB_TAG('Y','I','M',' ')},*/	/* Yi Modern */
 /*{"??",	HB_TAG('Z','H','P',' ')},*/	/* Chinese Phonetic */
 };
 
diff --git a/src/hb-ot.h b/src/hb-ot.h
index 2d750c3..8073906 100644
--- a/src/hb-ot.h
+++ b/src/hb-ot.h
@@ -35,6 +35,7 @@
 
 HB_BEGIN_DECLS
 
+/* TODO remove */
 void
 hb_ot_shape_glyphs_closure (hb_font_t          *font,
 			    hb_buffer_t        *buffer,
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 42c0259..be0d505 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -62,6 +62,12 @@
 #endif
 
 
+/* Void! */
+struct _hb_void_t {};
+typedef const _hb_void_t &hb_void_t;
+#define HB_VOID (* (const _hb_void_t *) NULL)
+
+
 /* Basics */
 
 
@@ -76,7 +82,7 @@
 
 #undef  ARRAY_LENGTH
 template <typename Type, unsigned int n>
-static inline unsigned int ARRAY_LENGTH (const Type (&a)[n]) { return n; }
+static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; }
 /* A const version, but does not detect erratically being called on pointers. */
 #define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
 
@@ -503,6 +509,10 @@
 #define hb_be_uint32_get(v)	(uint32_t) ((v[0] << 24) + (v[1] << 16) + (v[2] << 8) + v[3])
 #define hb_be_uint32_eq(a,b)	(a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3])
 
+#define hb_be_uint24_put(v,V)	HB_STMT_START { v[0] = (V>>16); v[1] = (V>>8); v[2] (V); } HB_STMT_END
+#define hb_be_uint24_get(v)	(uint32_t) ((v[0] << 16) + (v[1] << 8) + v[2])
+#define hb_be_uint24_eq(a,b)	(a[0] == b[0] && a[1] == b[1] && a[2] == b[2])
+
 
 /* ASCII tag/character handling */
 
@@ -582,7 +592,11 @@
   } else
     fprintf (stderr, "   " VRBAR LBAR);
 
-  if (func) {
+  if (func)
+  {
+    /* Skip "typename" */
+    if (0 == strncmp (func, "typename ", 9))
+      func += 9;
     /* Skip return type */
     const char *space = strchr (func, ' ');
     if (space)
@@ -657,10 +671,39 @@
 
 
 /*
+ * Printer
+ */
+
+template <typename T>
+struct hb_printer_t {};
+
+template <>
+struct hb_printer_t<bool> {
+  const char *print (bool v) { return v ? "true" : "false"; }
+};
+
+template <>
+struct hb_printer_t<hb_void_t> {
+  const char *print (hb_void_t) { return ""; }
+};
+
+
+/*
  * Trace
  */
 
-template <int max_level>
+template <typename T>
+static inline void _hb_warn_no_return (bool returned)
+{
+  if (unlikely (!returned)) {
+    fprintf (stderr, "OUCH, returned with no call to TRACE_RETURN.  This is a bug, please report.\n");
+  }
+}
+template <>
+inline void _hb_warn_no_return<hb_void_t> (bool returned HB_UNUSED)
+{}
+
+template <int max_level, typename ret_t>
 struct hb_auto_trace_t {
   explicit inline hb_auto_trace_t (unsigned int *plevel_,
 				   const char *what_,
@@ -678,23 +721,23 @@
   }
   inline ~hb_auto_trace_t (void)
   {
-    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_warn_no_return<ret_t> (returned);
+    if (!returned) {
       _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1, " ");
-      return;
     }
-
     if (plevel) --*plevel;
   }
 
-  inline bool ret (bool v, unsigned int line = 0)
+  inline ret_t ret (ret_t v, unsigned int line = 0)
   {
     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 (line %d)", v ? "true" : "false", line);
+    _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1,
+			      "return %s (line %d)",
+			      hb_printer_t<ret_t>().print (v), line);
     if (plevel) --*plevel;
     plevel = NULL;
     returned = true;
@@ -703,12 +746,12 @@
 
   private:
   unsigned int *plevel;
-  bool returned;
   const char *what;
   const void *obj;
+  bool returned;
 };
-template <> /* Optimize when tracing is disabled */
-struct hb_auto_trace_t<0> {
+template <typename ret_t> /* Optimize when tracing is disabled */
+struct hb_auto_trace_t<0, ret_t> {
   explicit inline hb_auto_trace_t (unsigned int *plevel_ HB_UNUSED,
 				   const char *what HB_UNUSED,
 				   const void *obj HB_UNUSED,
@@ -716,8 +759,7 @@
 				   const char *message HB_UNUSED,
 				   ...) {}
 
-  template <typename T>
-  inline T ret (T v, unsigned int line = 0) { return v; }
+  inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; }
 };
 
 #define TRACE_RETURN(RET) trace.ret (RET, __LINE__)
diff --git a/src/hb-set-private.hh b/src/hb-set-private.hh
index 4413579..c736b69 100644
--- a/src/hb-set-private.hh
+++ b/src/hb-set-private.hh
@@ -98,7 +98,7 @@
 
   private:
 
-  mask_t mask_for (hb_codepoint_t g) const { return ((mask_t) 1) << (g & (sizeof (mask_t) * 8 - 1)); }
+  static inline mask_t mask_for (hb_codepoint_t g) { return ((mask_t) 1) << (g & (sizeof (mask_t) * 8 - 1)); }
   mask_t mask;
 };
 
@@ -147,7 +147,7 @@
   inline void clear (void) {
     memset (elts, 0, sizeof elts);
   }
-  inline bool empty (void) const {
+  inline bool is_empty (void) const {
     for (unsigned int i = 0; i < ARRAY_LENGTH (elts); i++)
       if (elts[i])
         return false;
@@ -161,6 +161,7 @@
   }
   inline void add_range (hb_codepoint_t a, hb_codepoint_t b)
   {
+    /* TODO Speedup */
     for (unsigned int i = a; i < b + 1; i++)
       add (i);
   }
@@ -169,6 +170,12 @@
     if (unlikely (g > MAX_G)) return;
     elt (g) &= ~mask (g);
   }
+  inline void del_range (hb_codepoint_t a, hb_codepoint_t b)
+  {
+    /* TODO Speedup */
+    for (unsigned int i = a; i < b + 1; i++)
+      del (i);
+  }
   inline bool has (hb_codepoint_t g) const
   {
     if (unlikely (g > MAX_G)) return false;
@@ -185,7 +192,7 @@
         return true;
     return false;
   }
-  inline bool equal (const hb_set_t *other) const
+  inline bool is_equal (const hb_set_t *other) const
   {
     for (unsigned int i = 0; i < ELTS; i++)
       if (elts[i] != other->elts[i])
@@ -217,7 +224,7 @@
     for (unsigned int i = 0; i < ELTS; i++)
       elts[i] ^= other->elts[i];
   }
-  inline bool next (hb_codepoint_t *codepoint)
+  inline bool next (hb_codepoint_t *codepoint) const
   {
     if (unlikely (*codepoint == SENTINEL)) {
       hb_codepoint_t i = get_min ();
@@ -234,6 +241,28 @@
       }
     return false;
   }
+  inline bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const
+  {
+    hb_codepoint_t i;
+
+    i = *last;
+    if (!next (&i))
+      return false;
+
+    *last = *first = i;
+    while (next (&i) && i == *last + 1)
+      (*last)++;
+
+    return true;
+  }
+
+  inline unsigned int get_population (void) const
+  {
+    unsigned int count = 0;
+    for (unsigned int i = 0; i < ELTS; i++)
+      count += _hb_popcount32 (elts[i]);
+    return count;
+  }
   inline hb_codepoint_t get_min (void) const
   {
     for (unsigned int i = 0; i < ELTS; i++)
diff --git a/src/hb-set.cc b/src/hb-set.cc
index 4225e3c..93f983a 100644
--- a/src/hb-set.cc
+++ b/src/hb-set.cc
@@ -32,7 +32,7 @@
 
 
 hb_set_t *
-hb_set_create ()
+hb_set_create (void)
 {
   hb_set_t *set;
 
@@ -73,25 +73,25 @@
 }
 
 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)
+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)
+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)
+hb_set_allocation_successful (const hb_set_t  *set HB_UNUSED)
 {
   return true;
 }
@@ -103,13 +103,13 @@
 }
 
 hb_bool_t
-hb_set_empty (hb_set_t *set)
+hb_set_is_empty (const hb_set_t *set)
 {
-  return set->empty ();
+  return set->is_empty ();
 }
 
 hb_bool_t
-hb_set_has (hb_set_t       *set,
+hb_set_has (const hb_set_t *set,
 	    hb_codepoint_t  codepoint)
 {
   return set->has (codepoint);
@@ -123,69 +123,99 @@
 }
 
 void
+hb_set_add_range (hb_set_t       *set,
+		  hb_codepoint_t  first,
+		  hb_codepoint_t  last)
+{
+  set->add_range (first, last);
+}
+
+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)
+void
+hb_set_del_range (hb_set_t       *set,
+		  hb_codepoint_t  first,
+		  hb_codepoint_t  last)
 {
-  return set->equal (other);
+  set->del_range (first, last);
+}
+
+hb_bool_t
+hb_set_is_equal (const hb_set_t *set,
+		 const hb_set_t *other)
+{
+  return set->is_equal (other);
 }
 
 void
-hb_set_set (hb_set_t *set,
-	    hb_set_t *other)
+hb_set_set (hb_set_t       *set,
+	    const hb_set_t *other)
 {
   set->set (other);
 }
 
 void
-hb_set_union (hb_set_t *set,
-	      hb_set_t *other)
+hb_set_union (hb_set_t       *set,
+	      const hb_set_t *other)
 {
   set->union_ (other);
 }
 
 void
-hb_set_intersect (hb_set_t *set,
-		  hb_set_t *other)
+hb_set_intersect (hb_set_t       *set,
+		  const hb_set_t *other)
 {
   set->intersect (other);
 }
 
 void
-hb_set_subtract (hb_set_t *set,
-		 hb_set_t *other)
+hb_set_subtract (hb_set_t       *set,
+		 const hb_set_t *other)
 {
   set->subtract (other);
 }
 
 void
-hb_set_symmetric_difference (hb_set_t *set,
-			     hb_set_t *other)
+hb_set_symmetric_difference (hb_set_t       *set,
+			     const hb_set_t *other)
 {
   set->symmetric_difference (other);
 }
 
+unsigned int
+hb_set_get_population (const hb_set_t *set)
+{
+  return set->get_population ();
+}
+
 hb_codepoint_t
-hb_set_min (hb_set_t *set)
+hb_set_get_min (const hb_set_t *set)
 {
   return set->get_min ();
 }
 
 hb_codepoint_t
-hb_set_max (hb_set_t *set)
+hb_set_get_max (const hb_set_t *set)
 {
   return set->get_max ();
 }
 
 hb_bool_t
-hb_set_next (hb_set_t       *set,
+hb_set_next (const hb_set_t *set,
 	     hb_codepoint_t *codepoint)
 {
   return set->next (codepoint);
 }
+
+hb_bool_t
+hb_set_next_range (const hb_set_t *set,
+		   hb_codepoint_t *first,
+		   hb_codepoint_t *last)
+{
+  return set->next_range (first, last);
+}
diff --git a/src/hb-set.h b/src/hb-set.h
index 1838889..ec3d119 100644
--- a/src/hb-set.h
+++ b/src/hb-set.h
@@ -65,16 +65,16 @@
 
 /* Returns false if allocation has failed before */
 hb_bool_t
-hb_set_allocation_successful (hb_set_t  *set);
+hb_set_allocation_successful (const hb_set_t *set);
 
 void
 hb_set_clear (hb_set_t *set);
 
 hb_bool_t
-hb_set_empty (hb_set_t *set);
+hb_set_is_empty (const hb_set_t *set);
 
 hb_bool_t
-hb_set_has (hb_set_t       *set,
+hb_set_has (const hb_set_t *set,
 	    hb_codepoint_t  codepoint);
 
 /* Right now limited to 16-bit integers.  Eventually will do full codepoint range, sans -1
@@ -84,47 +84,64 @@
 	    hb_codepoint_t  codepoint);
 
 void
+hb_set_add_range (hb_set_t       *set,
+		  hb_codepoint_t  first,
+		  hb_codepoint_t  last);
+
+void
 hb_set_del (hb_set_t       *set,
 	    hb_codepoint_t  codepoint);
 
+void
+hb_set_del_range (hb_set_t       *set,
+		  hb_codepoint_t  first,
+		  hb_codepoint_t  last);
+
 hb_bool_t
-hb_set_equal (hb_set_t *set,
-	      hb_set_t *other);
+hb_set_is_equal (const hb_set_t *set,
+		 const hb_set_t *other);
 
 void
-hb_set_set (hb_set_t *set,
-	    hb_set_t *other);
+hb_set_set (hb_set_t       *set,
+	    const hb_set_t *other);
 
 void
-hb_set_union (hb_set_t *set,
-	      hb_set_t *other);
+hb_set_union (hb_set_t       *set,
+	      const hb_set_t *other);
 
 void
-hb_set_intersect (hb_set_t *set,
-		  hb_set_t *other);
+hb_set_intersect (hb_set_t       *set,
+		  const hb_set_t *other);
 
 void
-hb_set_subtract (hb_set_t *set,
-		 hb_set_t *other);
+hb_set_subtract (hb_set_t       *set,
+		 const hb_set_t *other);
 
 void
-hb_set_symmetric_difference (hb_set_t *set,
-			     hb_set_t *other);
+hb_set_symmetric_difference (hb_set_t       *set,
+			     const hb_set_t *other);
+
+unsigned int
+hb_set_get_population (const hb_set_t *set);
 
 /* Returns -1 if set empty. */
 hb_codepoint_t
-hb_set_min (hb_set_t *set);
+hb_set_get_min (const hb_set_t *set);
 
 /* Returns -1 if set empty. */
 hb_codepoint_t
-hb_set_max (hb_set_t *set);
+hb_set_get_max (const hb_set_t *set);
 
 /* Pass -1 in to get started. */
 hb_bool_t
-hb_set_next (hb_set_t       *set,
+hb_set_next (const hb_set_t *set,
 	     hb_codepoint_t *codepoint);
 
-/* TODO: Add faster iteration API? */
+/* Pass -1 for first and last to get started. */
+hb_bool_t
+hb_set_next_range (const hb_set_t *set,
+		   hb_codepoint_t *first,
+		   hb_codepoint_t *last);
 
 
 HB_END_DECLS
diff --git a/src/hb-shape-plan-private.hh b/src/hb-shape-plan-private.hh
index d6a57d6..dd014e3 100644
--- a/src/hb-shape-plan-private.hh
+++ b/src/hb-shape-plan-private.hh
@@ -28,9 +28,8 @@
 #define HB_SHAPE_PLAN_PRIVATE_HH
 
 #include "hb-private.hh"
-
 #include "hb-shape-plan.h"
-
+#include "hb-object-private.hh"
 #include "hb-shaper-private.hh"
 
 
@@ -44,6 +43,7 @@
   hb_segment_properties_t props;
 
   hb_shape_func_t *shaper_func;
+  const char *shaper_name;
 
   struct hb_shaper_data_t shaper_data;
 };
diff --git a/src/hb-shape-plan.cc b/src/hb-shape-plan.cc
index 038f6af..22a226f 100644
--- a/src/hb-shape-plan.cc
+++ b/src/hb-shape-plan.cc
@@ -24,8 +24,6 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
-
 #include "hb-shape-plan-private.hh"
 #include "hb-shaper-private.hh"
 #include "hb-font-private.hh"
@@ -51,6 +49,7 @@
 	    HB_SHAPER_DATA (shaper, shape_plan) = \
 	      HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, user_features, num_user_features); \
 	    shape_plan->shaper_func = _hb_##shaper##_shape; \
+	    shape_plan->shaper_name = #shaper; \
 	    return; \
 	  } \
 	} HB_STMT_END
@@ -119,9 +118,10 @@
 
     true, /* default_shaper_list */
     NULL, /* face */
-    _HB_BUFFER_PROPS_DEFAULT, /* props */
+    HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
 
     NULL, /* shaper_func */
+    NULL, /* shaper_name */
 
     {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
@@ -153,9 +153,26 @@
   free (shape_plan);
 }
 
+hb_bool_t
+hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
+			     hb_user_data_key_t *key,
+			     void *              data,
+			     hb_destroy_func_t   destroy,
+			     hb_bool_t           replace)
+{
+  return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
+}
+
+void *
+hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
+			     hb_user_data_key_t *key)
+{
+  return hb_object_get_user_data (shape_plan, key);
+}
+
 
 hb_bool_t
-hb_shape_plan_execute (hb_shape_plan      *shape_plan,
+hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
 		       hb_font_t          *font,
 		       hb_buffer_t        *buffer,
 		       const hb_feature_t *features,
@@ -283,3 +300,9 @@
 
   return hb_shape_plan_reference (shape_plan);
 }
+
+const char *
+hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
+{
+  return shape_plan->shaper_name;
+}
diff --git a/src/hb-shape-plan.h b/src/hb-shape-plan.h
index f1a14a9..8f54552 100644
--- a/src/hb-shape-plan.h
+++ b/src/hb-shape-plan.h
@@ -24,52 +24,66 @@
  * Google Author(s): Behdad Esfahbod
  */
 
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
 #ifndef HB_SHAPE_PLAN_H
 #define HB_SHAPE_PLAN_H
 
-/* TODO To become public one day */
+#include "hb-common.h"
+#include "hb-font.h"
 
-#include "hb-private.hh"
+HB_BEGIN_DECLS
 
-#include "hb-buffer-private.hh"
+typedef struct hb_shape_plan_t hb_shape_plan_t;
 
-
-typedef struct hb_shape_plan_t hb_shape_plan;
-
-/*
- * hb_shape_plan_t
- */
-
-HB_INTERNAL hb_shape_plan_t *
+hb_shape_plan_t *
 hb_shape_plan_create (hb_face_t                     *face,
 		      const hb_segment_properties_t *props,
 		      const hb_feature_t            *user_features,
 		      unsigned int                   num_user_features,
 		      const char * const            *shaper_list);
 
-HB_INTERNAL hb_shape_plan_t *
+hb_shape_plan_t *
 hb_shape_plan_create_cached (hb_face_t                     *face,
 			     const hb_segment_properties_t *props,
 			     const hb_feature_t            *user_features,
 			     unsigned int                   num_user_features,
 			     const char * const            *shaper_list);
 
-HB_INTERNAL hb_shape_plan_t *
+hb_shape_plan_t *
 hb_shape_plan_get_empty (void);
 
-HB_INTERNAL hb_shape_plan_t *
+hb_shape_plan_t *
 hb_shape_plan_reference (hb_shape_plan_t *shape_plan);
 
-HB_INTERNAL void
+void
 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan);
 
+hb_bool_t
+hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
+			     hb_user_data_key_t *key,
+			     void *              data,
+			     hb_destroy_func_t   destroy,
+			     hb_bool_t           replace);
 
-HB_INTERNAL hb_bool_t
-hb_shape_plan_execute (hb_shape_plan      *shape_plan,
+void *
+hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
+			     hb_user_data_key_t *key);
+
+
+hb_bool_t
+hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
 		       hb_font_t          *font,
 		       hb_buffer_t        *buffer,
 		       const hb_feature_t *features,
 		       unsigned int        num_features);
 
+const char *
+hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan);
+
+
+HB_END_DECLS
 
 #endif /* HB_SHAPE_PLAN_H */
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 4d64823..389ce3e 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -176,7 +176,7 @@
   len += 4;
   while (len && s[len - 1] == ' ')
     len--;
-  if (feature->start != 0 || feature->start != (unsigned int) -1)
+  if (feature->start != 0 || feature->end != (unsigned int) -1)
   {
     s[len++] = '[';
     if (feature->start)
@@ -255,7 +255,7 @@
 
   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
 
-  buffer->guess_properties ();
+  buffer->guess_segment_properties ();
 
   hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list);
   hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features);
diff --git a/src/hb-shaper-list.hh b/src/hb-shaper-list.hh
index 6c02de4..b9c029e 100644
--- a/src/hb-shaper-list.hh
+++ b/src/hb-shaper-list.hh
@@ -29,15 +29,11 @@
 #endif /* HB_SHAPER_LIST_HH */ /* Dummy header guards */
 
 /* v--- Add new shapers in the right place here. */
+
 #ifdef HAVE_GRAPHITE2
+/* Only picks up fonts that have a "Silf" table. */
 HB_SHAPER_IMPLEMENT (graphite2)
 #endif
-#ifdef HAVE_UNISCRIBE
-HB_SHAPER_IMPLEMENT (uniscribe)
-#endif
-#ifdef HAVE_CORETEXT
-HB_SHAPER_IMPLEMENT (coretext)
-#endif
 
 #ifdef HAVE_OT
 HB_SHAPER_IMPLEMENT (ot) /* <--- This is our main OpenType shaper. */
@@ -46,9 +42,14 @@
 #ifdef HAVE_HB_OLD
 HB_SHAPER_IMPLEMENT (old)
 #endif
-
 #ifdef HAVE_ICU_LE
 HB_SHAPER_IMPLEMENT (icu_le)
 #endif
+#ifdef HAVE_UNISCRIBE
+HB_SHAPER_IMPLEMENT (uniscribe)
+#endif
+#ifdef HAVE_CORETEXT
+HB_SHAPER_IMPLEMENT (coretext)
+#endif
 
 HB_SHAPER_IMPLEMENT (fallback) /* <--- This should be last. */
diff --git a/src/hb-shaper-private.hh b/src/hb-shaper-private.hh
index 186318d..9d30c1e 100644
--- a/src/hb-shaper-private.hh
+++ b/src/hb-shaper-private.hh
@@ -29,8 +29,6 @@
 
 #include "hb-private.hh"
 
-#include "hb-shape-plan.h" /* TODO remove */
-
 typedef hb_bool_t hb_shape_func_t (hb_shape_plan_t    *shape_plan,
 				   hb_font_t          *font,
 				   hb_buffer_t        *buffer,
diff --git a/src/hb-shaper.cc b/src/hb-shaper.cc
index a16ffc8..1c1aed9 100644
--- a/src/hb-shaper.cc
+++ b/src/hb-shaper.cc
@@ -25,8 +25,8 @@
  */
 
 #include "hb-private.hh"
-
 #include "hb-shaper-private.hh"
+#include "hb-atomic-private.hh"
 
 
 static const hb_shaper_pair_t all_shapers[] = {
diff --git a/src/hb-tt-font.cc b/src/hb-tt-font.cc
index b7198ef..c503a40 100644
--- a/src/hb-tt-font.cc
+++ b/src/hb-tt-font.cc
@@ -68,7 +68,7 @@
 static inline const hhea&
 _get_hhea (hb_face_t *face)
 {
-//  return likely (face->tt && face->tt->hhea) ? *face->tt->hhea : Null(hhea);
+  return likely (face->tt && face->tt->hhea) ? *face->tt->hhea : Null(hhea);
 }
 
 
diff --git a/src/hb-ucdn.cc b/src/hb-ucdn.cc
index 3506304..61e6ad3 100644
--- a/src/hb-ucdn.cc
+++ b/src/hb-ucdn.cc
@@ -129,57 +129,60 @@
 
 static hb_unicode_combining_class_t
 hb_ucdn_combining_class(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
-        void *user_data)
+			void *user_data HB_UNUSED)
 {
     return (hb_unicode_combining_class_t) ucdn_get_combining_class(unicode);
 }
 
 static unsigned int
 hb_ucdn_eastasian_width(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
-        void *user_data)
+			void *user_data HB_UNUSED)
 {
     int w = ucdn_get_east_asian_width(unicode);
     return (w == UCDN_EAST_ASIAN_F || w == UCDN_EAST_ASIAN_W) ? 2 : 1;
 }
 
 static hb_unicode_general_category_t
-hb_ucdn_general_category(hb_unicode_funcs_t *ufuncs,
-        hb_codepoint_t unicode, void *user_data)
+hb_ucdn_general_category(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
+			 void *user_data HB_UNUSED)
 {
     return (hb_unicode_general_category_t)ucdn_get_general_category(unicode);
 }
 
 static hb_codepoint_t
 hb_ucdn_mirroring(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
-        void *user_data)
+		  void *user_data HB_UNUSED)
 {
     return ucdn_mirror(unicode);
 }
 
 static hb_script_t
 hb_ucdn_script(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
-        void *user_data)
+	       void *user_data HB_UNUSED)
 {
     return ucdn_script_translate[ucdn_get_script(unicode)];
 }
 
 static hb_bool_t
-hb_ucdn_compose(hb_unicode_funcs_t *ufuncs, hb_codepoint_t a,
-        hb_codepoint_t b, hb_codepoint_t *ab, void *user_data)
+hb_ucdn_compose(hb_unicode_funcs_t *ufuncs,
+		hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab,
+		void *user_data HB_UNUSED)
 {
     return ucdn_compose(ab, a, b);
 }
 
 static hb_bool_t
-hb_ucdn_decompose(hb_unicode_funcs_t *ufuncs, hb_codepoint_t ab,
-        hb_codepoint_t *a, hb_codepoint_t *b, void *user_data)
+hb_ucdn_decompose(hb_unicode_funcs_t *ufuncs,
+		  hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b,
+		  void *user_data HB_UNUSED)
 {
     return ucdn_decompose(ab, a, b);
 }
 
 static unsigned int
-hb_ucdn_decompose_compatibility(hb_unicode_funcs_t *ufuncs, hb_codepoint_t u,
-        hb_codepoint_t *decomposed, void *user_data)
+hb_ucdn_decompose_compatibility(hb_unicode_funcs_t *ufuncs,
+				hb_codepoint_t u, hb_codepoint_t *decomposed,
+				void *user_data HB_UNUSED)
 {
     return ucdn_compat_decompose(u, decomposed);
 }
diff --git a/src/hb-ucdn/ucdn.c b/src/hb-ucdn/ucdn.c
index 5fbc0f3..b61e34a 100644
--- a/src/hb-ucdn/ucdn.c
+++ b/src/hb-ucdn/ucdn.c
@@ -20,13 +20,13 @@
 #include "ucdn.h"
 
 typedef struct {
-    const unsigned char category;
-    const unsigned char combining;
-    const unsigned char bidi_class;
-    const unsigned char mirrored;
-    const unsigned char east_asian_width;
-    const unsigned char normalization_check;
-    const unsigned char script;
+    unsigned char category;
+    unsigned char combining;
+    unsigned char bidi_class;
+    unsigned char mirrored;
+    unsigned char east_asian_width;
+    unsigned char normalization_check;
+    unsigned char script;
 } UCDRecord;
 
 typedef struct {
@@ -51,7 +51,7 @@
 #define TCOUNT 28
 #define NCOUNT (VCOUNT * TCOUNT)
 
-static UCDRecord *get_ucd_record(uint32_t code)
+static const UCDRecord *get_ucd_record(uint32_t code)
 {
     int index, offset;
 
@@ -68,7 +68,7 @@
     return &ucd_records[index];
 }
 
-static unsigned short *get_decomp_record(uint32_t code)
+static const unsigned short *get_decomp_record(uint32_t code)
 {
     int index, offset;
 
@@ -86,12 +86,12 @@
     return &decomp_data[index];
 }
 
-static int get_comp_index(uint32_t code, Reindex *idx)
+static int get_comp_index(uint32_t code, const Reindex *idx)
 {
     int i;
 
     for (i = 0; idx[i].start; i++) {
-        Reindex *cur = &idx[i];
+        const Reindex *cur = &idx[i];
         if (code < cur->start)
             return -1;
         if (code <= cur->start + cur->count) {
@@ -151,9 +151,9 @@
     }
 }
 
-static uint32_t decode_utf16(unsigned short **code_ptr)
+static uint32_t decode_utf16(const unsigned short **code_ptr)
 {
-    unsigned short *code = *code_ptr;
+    const unsigned short *code = *code_ptr;
 
     if ((code[0] & 0xd800) != 0xd800) {
         *code_ptr += 1;
@@ -220,7 +220,7 @@
 
 int ucdn_decompose(uint32_t code, uint32_t *a, uint32_t *b)
 {
-    unsigned short *rec;
+    const unsigned short *rec;
     int len;
 
     if (hangul_pair_decompose(code, a, b))
@@ -268,7 +268,7 @@
 int ucdn_compat_decompose(uint32_t code, uint32_t *decomposed)
 {
     int i, len;
-    unsigned short *rec = get_decomp_record(code);
+    const unsigned short *rec = get_decomp_record(code);
     len = rec[0] >> 8;
 
     if (len == 0)
diff --git a/src/hb-ucdn/unicodedata_db.h b/src/hb-ucdn/unicodedata_db.h
index 305a7ff..20244ac 100644
--- a/src/hb-ucdn/unicodedata_db.h
+++ b/src/hb-ucdn/unicodedata_db.h
@@ -2,7 +2,7 @@
 
 #define UNIDATA_VERSION "6.2.0"
 /* a list of unique database records */
-static UCDRecord ucd_records[] = {
+static const UCDRecord ucd_records[] = {
     {2, 0, 18, 0, 5, 0, 102},
     {0, 0, 14, 0, 5, 0, 0},
     {0, 0, 16, 0, 5, 0, 0},
@@ -704,7 +704,7 @@
 };
 
 #define BIDI_MIRROR_LEN 364
-static MirrorPair mirror_pairs[] = {
+static const MirrorPair mirror_pairs[] = {
     {40, 41},
     {41, 40},
     {60, 62},
@@ -1074,7 +1074,7 @@
 /* Reindexing of NFC first characters. */
 #define TOTAL_FIRST 372
 #define TOTAL_LAST 56
-static Reindex nfc_first[] = {
+static const Reindex nfc_first[] = {
   { 60, 2, 0},
   { 65, 15, 3},
   { 82, 8, 19},
@@ -1285,7 +1285,7 @@
   {0,0,0}
 };
 
-static Reindex nfc_last[] = {
+static const Reindex nfc_last[] = {
   { 768, 4, 0},
   { 774, 6, 5},
   { 783, 0, 12},
@@ -1487,7 +1487,7 @@
 /* index tables for the database records */
 #define SHIFT1 5
 #define SHIFT2 3
-static unsigned char index0[] = {
+static const unsigned char index0[] = {
     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 
     21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 
     39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 53, 53, 53, 
@@ -1737,7 +1737,7 @@
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 131, 
 };
 
-static unsigned short index1[] = {
+static const unsigned short index1[] = {
     0, 1, 0, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, 0, 0, 0, 14, 15, 
     16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 29, 31, 32, 
     33, 34, 35, 27, 30, 29, 27, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 
@@ -2042,7 +2042,7 @@
     854, 854, 854, 854, 854, 854, 854, 1119, 
 };
 
-static unsigned short index2[] = {
+static const unsigned short index2[] = {
     1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 4, 3, 1, 1, 1, 1, 1, 1, 3, 3, 3, 2, 
     5, 6, 6, 7, 8, 7, 6, 6, 9, 10, 6, 11, 12, 13, 12, 12, 14, 14, 14, 14, 14, 
     14, 14, 14, 14, 14, 12, 6, 15, 16, 15, 6, 6, 17, 17, 17, 17, 17, 17, 17, 
@@ -2624,7 +2624,7 @@
 };
 
 /* decomposition data */
-static unsigned short decomp_data[] = {
+static const unsigned short decomp_data[] = {
     0, 257, 32, 514, 32, 776, 259, 97, 514, 32, 772, 259, 50, 259, 51, 514, 
     32, 769, 258, 956, 514, 32, 807, 259, 49, 259, 111, 772, 49, 8260, 52, 
     772, 49, 8260, 50, 772, 51, 8260, 52, 512, 65, 768, 512, 65, 769, 512, 
@@ -3702,7 +3702,7 @@
 /* index tables for the decomposition data */
 #define DECOMP_SHIFT1 6
 #define DECOMP_SHIFT2 4
-static unsigned char decomp_index0[] = {
+static const unsigned char decomp_index0[] = {
     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
     5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 13, 5, 5, 5, 5, 5, 
     5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 14, 15, 5, 5, 5, 5, 16, 5, 
@@ -3751,7 +3751,7 @@
     5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
 };
 
-static unsigned short decomp_index1[] = {
+static const unsigned short decomp_index1[] = {
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 
     14, 0, 0, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 0, 0, 0, 0, 0, 0, 0, 
     25, 0, 26, 27, 0, 0, 0, 0, 0, 28, 0, 0, 29, 30, 31, 32, 33, 34, 35, 0, 
@@ -3825,7 +3825,7 @@
     0, 0, 0, 
 };
 
-static unsigned short decomp_index2[] = {
+static const unsigned short decomp_index2[] = {
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
     3, 0, 6, 0, 0, 0, 0, 8, 0, 0, 11, 13, 15, 18, 0, 0, 20, 23, 25, 0, 27, 
     31, 35, 0, 39, 42, 45, 48, 51, 54, 0, 57, 60, 63, 66, 69, 72, 75, 78, 81, 
@@ -4414,7 +4414,7 @@
 /* NFC pairs */
 #define COMP_SHIFT1 2
 #define COMP_SHIFT2 1
-static unsigned short comp_index0[] = {
+static const unsigned short comp_index0[] = {
     0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 
     0, 0, 0, 0, 7, 0, 8, 9, 0, 0, 0, 10, 11, 12, 0, 0, 0, 0, 13, 14, 15, 16, 
     0, 0, 0, 17, 18, 19, 20, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 22, 23, 24, 0, 0, 
@@ -4538,7 +4538,7 @@
     0, 0, 0, 0, 0, 0, 541, 0, 0, 0, 0, 0, 0, 542, 0, 0, 0, 0, 0, 0, 543, 
 };
 
-static unsigned short comp_index1[] = {
+static const unsigned short comp_index1[] = {
     0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 4, 5, 6, 7, 8, 9, 10, 0, 
     11, 12, 0, 13, 0, 0, 0, 14, 15, 0, 0, 0, 0, 16, 0, 0, 17, 18, 0, 19, 0, 
     20, 0, 0, 0, 0, 21, 0, 0, 0, 0, 22, 0, 23, 0, 0, 24, 0, 25, 26, 0, 27, 0, 
@@ -4651,7 +4651,7 @@
     725, 0, 0, 0, 726, 
 };
 
-static unsigned int comp_data[] = {
+static const unsigned int comp_data[] = {
     0, 0, 0, 8814, 0, 8800, 0, 8815, 192, 193, 194, 195, 256, 258, 550, 196, 
     7842, 197, 0, 461, 512, 514, 0, 7840, 0, 7680, 260, 0, 7682, 0, 0, 7684, 
     7686, 0, 0, 262, 264, 0, 266, 0, 0, 268, 0, 199, 7690, 0, 0, 270, 0, 
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 18b88b2..2f01c28 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -188,6 +188,22 @@
   free (data);
 }
 
+LOGFONTW *
+hb_uniscribe_font_get_logfontw (hb_font_t *font)
+{
+  if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return NULL;
+  hb_uniscribe_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
+  return &font_data->log_font;
+}
+
+HFONT
+hb_uniscribe_font_get_hfont (hb_font_t *font)
+{
+  if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return NULL;
+  hb_uniscribe_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
+  return font_data->hfont;
+}
+
 
 /*
  * shaper shape_plan data
@@ -213,23 +229,6 @@
  * shaper
  */
 
-LOGFONTW *
-hb_uniscribe_font_get_logfontw (hb_font_t *font)
-{
-  if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return NULL;
-    return NULL;
-  hb_uniscribe_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
-  return &font_data->log_font;
-}
-
-HFONT
-hb_uniscribe_font_get_hfont (hb_font_t *font)
-{
-  if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return NULL;
-  hb_uniscribe_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
-  return font_data->hfont;
-}
-
 
 hb_bool_t
 _hb_uniscribe_shape (hb_shape_plan_t    *shape_plan,
diff --git a/src/hb-utf-private.hh b/src/hb-utf-private.hh
index 8cde827..b9a6519 100644
--- a/src/hb-utf-private.hh
+++ b/src/hb-utf-private.hh
@@ -77,8 +77,8 @@
 	     const uint8_t *start,
 	     hb_codepoint_t *unicode)
 {
-  const uint8_t *end = text;
-  while (start < text && (*--text & 0xc0) == 0x80 && end - text < 4)
+  const uint8_t *end = text--;
+  while (start < text && (*text & 0xc0) == 0x80 && end - text < 4)
     text--;
 
   hb_codepoint_t c = *text, mask;
@@ -176,7 +176,7 @@
 
 static inline const uint32_t *
 hb_utf_next (const uint32_t *text,
-	     const uint32_t *end,
+	     const uint32_t *end HB_UNUSED,
 	     hb_codepoint_t *unicode)
 {
   *unicode = *text++;
@@ -185,7 +185,7 @@
 
 static inline const uint32_t *
 hb_utf_prev (const uint32_t *text,
-	     const uint32_t *start,
+	     const uint32_t *start HB_UNUSED,
 	     hb_codepoint_t *unicode)
 {
   *unicode = *--text;
diff --git a/src/hb.h b/src/hb.h
index d36040e..52c479c 100644
--- a/src/hb.h
+++ b/src/hb.h
@@ -34,6 +34,7 @@
 #include "hb-font.h"
 #include "hb-set.h"
 #include "hb-shape.h"
+#include "hb-shape-plan.h"
 #include "hb-unicode.h"
 #include "hb-version.h"
 
diff --git a/src/test-size-params.cc b/src/test-size-params.cc
new file mode 100644
index 0000000..947b566
--- /dev/null
+++ b/src/test-size-params.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright © 2010,2011  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "hb.h"
+#include "hb-ot.h"
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+
+int
+main (int argc, char **argv)
+{
+  hb_blob_t *blob = NULL;
+
+  if (argc != 2) {
+    fprintf (stderr, "usage: %s font-file\n", argv[0]);
+    exit (1);
+  }
+
+  /* Create the blob */
+  {
+    const char *font_data;
+    unsigned int len;
+    hb_destroy_func_t destroy;
+    void *user_data;
+    hb_memory_mode_t mm;
+
+#ifdef HAVE_GLIB
+    GMappedFile *mf = g_mapped_file_new (argv[1], false, NULL);
+    font_data = g_mapped_file_get_contents (mf);
+    len = g_mapped_file_get_length (mf);
+    destroy = (hb_destroy_func_t) g_mapped_file_unref;
+    user_data = (void *) mf;
+    mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
+#else
+    FILE *f = fopen (argv[1], "rb");
+    fseek (f, 0, SEEK_END);
+    len = ftell (f);
+    fseek (f, 0, SEEK_SET);
+    font_data = (const char *) malloc (len);
+    if (!font_data) len = 0;
+    len = fread ((char *) font_data, 1, len, f);
+    destroy = free;
+    user_data = (void *) font_data;
+    fclose (f);
+    mm = HB_MEMORY_MODE_WRITABLE;
+#endif
+
+    blob = hb_blob_create (font_data, len, mm, user_data, destroy);
+  }
+
+  /* Create the face */
+  hb_face_t *face = hb_face_create (blob, 0 /* first face */);
+  hb_blob_destroy (blob);
+  blob = NULL;
+
+  unsigned int p[5];
+  bool ret = hb_ot_layout_get_size_params (face, p, p+1, p+2, p+3, p+4);
+
+  printf ("%g %u %u %g %g\n", p[0]/10., p[1], p[2], p[3]/10., p[4]/10.);
+
+  return !ret;
+}
diff --git a/src/test-would-substitute.cc b/src/test-would-substitute.cc
index d15aec4..4731e26 100644
--- a/src/test-would-substitute.cc
+++ b/src/test-would-substitute.cc
@@ -99,5 +99,5 @@
       (argc > 4 &&
        !hb_font_glyph_from_string (font, argv[4], -1, &glyphs[1])))
     return 2;
-  return !hb_ot_layout_would_substitute_lookup (face, strtol (argv[2], NULL, 0), glyphs, len, false);
+  return !hb_ot_layout_lookup_would_substitute (face, strtol (argv[2], NULL, 0), glyphs, len, false);
 }
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index e6c0c01..c5a015f 100644
--- a/test/api/Makefile.am
+++ b/test/api/Makefile.am
@@ -26,6 +26,15 @@
 	test-version \
 	$(NULL)
 
+test_unicode_CPPFLAGS = $(AM_CPPFLAGS)
+if HAVE_GLIB
+test_unicode_CPPFLAGS += $(GLIB_CFLAGS)
+endif
+if HAVE_ICU
+test_unicode_CPPFLAGS += $(ICU_CFLAGS)
+endif
+
+
 if HAVE_OT
 TEST_PROGS += \
 	test-ot-tag \
@@ -49,7 +58,6 @@
 test_cplusplus_CPPFLAGS += $(FREETYPE_CFLAGS)
 endif
 
-
 # Default test running environment
 TESTS = $(TEST_PROGS)
 TESTS_ENVIRONMENT = \
diff --git a/test/api/test-buffer.c b/test/api/test-buffer.c
index 1ddc3d8..f826f2e 100644
--- a/test/api/test-buffer.c
+++ b/test/api/test-buffer.c
@@ -71,7 +71,7 @@
 
     case BUFFER_ONE_BY_ONE:
       for (i = 1; i < G_N_ELEMENTS (utf32) - 1; i++)
-	hb_buffer_add (b, utf32[i], 1, i);
+	hb_buffer_add (b, utf32[i], i);
       break;
 
     case BUFFER_UTF32:
@@ -110,6 +110,7 @@
   g_assert (hb_buffer_get_direction (b) == HB_DIRECTION_INVALID);
   g_assert (hb_buffer_get_script (b) == HB_SCRIPT_INVALID);
   g_assert (hb_buffer_get_language (b) == NULL);
+  g_assert (hb_buffer_get_flags (b) == HB_BUFFER_FLAGS_DEFAULT);
 
 
   /* test property changes are retained */
@@ -127,8 +128,35 @@
   hb_buffer_set_language (b, hb_language_from_string ("fa", -1));
   g_assert (hb_buffer_get_language (b) == hb_language_from_string ("Fa", -1));
 
+  hb_buffer_set_flags (b, HB_BUFFER_FLAG_BOT);
+  g_assert (hb_buffer_get_flags (b) == HB_BUFFER_FLAG_BOT);
 
-  /* test reset clears properties */
+
+
+  /* test clear clears all properties but unicode_funcs */
+
+  hb_buffer_clear (b);
+
+  g_assert (hb_buffer_get_unicode_funcs (b) == ufuncs);
+  g_assert (hb_buffer_get_direction (b) == HB_DIRECTION_INVALID);
+  g_assert (hb_buffer_get_script (b) == HB_SCRIPT_INVALID);
+  g_assert (hb_buffer_get_language (b) == NULL);
+  g_assert (hb_buffer_get_flags (b) == HB_BUFFER_FLAGS_DEFAULT);
+
+
+  /* test reset clears all properties */
+
+  hb_buffer_set_direction (b, HB_DIRECTION_RTL);
+  g_assert (hb_buffer_get_direction (b) == HB_DIRECTION_RTL);
+
+  hb_buffer_set_script (b, HB_SCRIPT_ARABIC);
+  g_assert (hb_buffer_get_script (b) == HB_SCRIPT_ARABIC);
+
+  hb_buffer_set_language (b, hb_language_from_string ("fa", -1));
+  g_assert (hb_buffer_get_language (b) == hb_language_from_string ("Fa", -1));
+
+  hb_buffer_set_flags (b, HB_BUFFER_FLAG_BOT);
+  g_assert (hb_buffer_get_flags (b) == HB_BUFFER_FLAG_BOT);
 
   hb_buffer_reset (b);
 
@@ -136,6 +164,7 @@
   g_assert (hb_buffer_get_direction (b) == HB_DIRECTION_INVALID);
   g_assert (hb_buffer_get_script (b) == HB_SCRIPT_INVALID);
   g_assert (hb_buffer_get_language (b) == NULL);
+  g_assert (hb_buffer_get_flags (b) == HB_BUFFER_FLAGS_DEFAULT);
 }
 
 static void
diff --git a/test/api/test-object.c b/test/api/test-object.c
index 66e8d33..ae4d1cc 100644
--- a/test/api/test-object.c
+++ b/test/api/test-object.c
@@ -219,7 +219,7 @@
   for (i = 0; i < G_N_ELEMENTS (objects); i++) {
     const object_t *o = &objects[i];
     void *obj;
-    hb_user_data_key_t key[2];
+    hb_user_data_key_t key[1001];
 
     {
       unsigned int j;
diff --git a/test/api/test-ot-tag.c b/test/api/test-ot-tag.c
index 81b6678..79e2bbf 100644
--- a/test/api/test-ot-tag.c
+++ b/test/api/test-ot-tag.c
@@ -132,6 +132,7 @@
   test_indic_tags ("ory2", "orya", HB_SCRIPT_ORIYA);
   test_indic_tags ("tml2", "taml", HB_SCRIPT_TAMIL);
   test_indic_tags ("tel2", "telu", HB_SCRIPT_TELUGU);
+  test_indic_tags ("mym2", "mymr", HB_SCRIPT_MYANMAR);
 }
 
 
diff --git a/test/shaping/hb_test_tools.py b/test/shaping/hb_test_tools.py
index 6139ec1..ccb0e1c 100644
--- a/test/shaping/hb_test_tools.py
+++ b/test/shaping/hb_test_tools.py
@@ -295,7 +295,7 @@
 	def test_passed (lines):
 		lines = list (lines)
 		# XXX This is a hack, but does the job for now.
-		if any (l.find("space|space") >= 0 for l in lines if l[0] == '+'): return True
+		if any (l.find("space+0|space+0") >= 0 for l in lines if l[0] == '+'): return True
 		if any (l.find("uni25CC") >= 0 for l in lines if l[0] == '+'): return True
 		if any (l.find("dottedcircle") >= 0 for l in lines if l[0] == '+'): return True
 		if any (l.find("glyph0") >= 0 for l in lines if l[0] == '+'): return True
diff --git a/test/shaping/texts/in-tree/shaper-arabic/MANIFEST b/test/shaping/texts/in-tree/shaper-arabic/MANIFEST
index cb39e70..eb8f9ec 100644
--- a/test/shaping/texts/in-tree/shaper-arabic/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-arabic/MANIFEST
@@ -2,4 +2,5 @@
 script-mandaic
 script-mongolian
 script-nko
+script-phags-pa
 script-syriac
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/MANIFEST b/test/shaping/texts/in-tree/shaper-arabic/script-arabic/MANIFEST
index 28f10bc..62e050d 100644
--- a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-arabic/script-arabic/MANIFEST
@@ -1,2 +1,3 @@
+language-persian
 language-urdu
 misc
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-persian/MANIFEST b/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-persian/MANIFEST
new file mode 100644
index 0000000..a6ac235
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-persian/MANIFEST
@@ -0,0 +1 @@
+mehran.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-persian/mehran.txt b/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-persian/mehran.txt
new file mode 100755
index 0000000..4be0786
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-persian/mehran.txt
@@ -0,0 +1,8 @@
+‫دَر فارسی گَچْ‌پَژْ هست. این «ی» فارسی است.‬

+‫حرف «ع» را به چٰهار شکلِ «ع‍» و «‍ع‍» و «‍ع» و «‌ع‌» می‌توان نشان داد.‬

+‫تشخیصِ اِعْ‌ًٌَُراب ناهمخوان از وظایف حروفْ‌چین است.‬

+‫دو اِعراب همخوان مانند « َ» و « ّ» به شکل « َّ» باهم ترکیب می‌شوند.‬

+‫لازم است حروف‌چین رفتار درستی با کشیدهٔ یونی‌کدی داشته باشد.‬

+‫مثلاً بتواند کلـمهٔ «پیِٓـــــــچ» یا حروف «ــٖٓـ» را به درستی نمایش دهد.‬

+‫حرف «لام» و «الف» باید به شکل لیگاتوری نمایش داده شوند.‬

+‫کلمهٔ «بَلَاٰ» از آزمون‌های سطح پائین حروف‌چین است.‬
\ No newline at end of file
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/MANIFEST
index e7eedf6..6088e7a 100644
--- a/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/MANIFEST
@@ -1,2 +1,3 @@
 misc.txt
+non-joining.txt
 poem.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/misc.txt b/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/misc.txt
index 009112a..57317de 100644
--- a/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/misc.txt
+++ b/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/misc.txt
@@ -1,3 +1,6 @@
 ᠬᠦᠮᠦᠨ ᠮᠤᠩᠭᠣᠯ ᠪᠢᠴᠢᠭ᠌
 ᠪᠢᠴᠢᠭ᠌ ᠬᠦᠮᠦᠨ ᠮᠤᠩᠭᠣᠯ
 ᠮᠤᠩᠭᠣᠯ ᠪᠢᠴᠢᠭ᠌ ᠬᠦᠮᠦᠨ
+ᠡ᠆ᠡ
+ᠡ᠇ᠡ
+ᠡ᠊ᠡ
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/non-joining.txt b/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/non-joining.txt
new file mode 100644
index 0000000..93e9dd6
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/non-joining.txt
@@ -0,0 +1,8 @@
+ᠡᢀᠡ
+ᠡᢁᠡ
+ᠡᢂᠡ
+ᠡᢃᠡ
+ᠡᢄᠡ
+ᠡᢅᠡ
+ᠡᢆᠡ
+ᠡᢇᠡ
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-phags-pa/MANIFEST b/test/shaping/texts/in-tree/shaper-arabic/script-phags-pa/MANIFEST
new file mode 100644
index 0000000..b8752e7
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-arabic/script-phags-pa/MANIFEST
@@ -0,0 +1 @@
+misc
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-phags-pa/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-arabic/script-phags-pa/misc/MANIFEST
new file mode 100644
index 0000000..29cfb2f
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-arabic/script-phags-pa/misc/MANIFEST
@@ -0,0 +1 @@
+misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-phags-pa/misc/misc.txt b/test/shaping/texts/in-tree/shaper-arabic/script-phags-pa/misc/misc.txt
new file mode 100644
index 0000000..d535b67
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-arabic/script-phags-pa/misc/misc.txt
@@ -0,0 +1,14 @@
+ꡳꡡ
+ꡡꡳꡡ
+ꡞꡞꡞ ꡞ
+ꡟꡟꡟ ꡟ
+ꡠꡠꡠ ꡠ
+ꡡꡡꡡ ꡡ
+‍ꡡ‍
+‍ꡡ
+ꡡ‍
+ꡞ‌ꡟ‌ꡠ‌ꡡ
+ꡉꡞ
+ꡉꡞ︀
+ꡪꡞ
+ꡪꡞ︀
diff --git a/test/shaping/texts/in-tree/shaper-default/MANIFEST b/test/shaping/texts/in-tree/shaper-default/MANIFEST
index f14322c..f2a6e7d 100644
--- a/test/shaping/texts/in-tree/shaper-default/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-default/MANIFEST
@@ -1,4 +1,6 @@
+script-ethiopic
 script-han
 script-hebrew
 script-hiragana
 script-linear-b
+script-tifinagh
diff --git a/test/shaping/texts/in-tree/shaper-default/script-tifinagh/MANIFEST b/test/shaping/texts/in-tree/shaper-default/script-tifinagh/MANIFEST
new file mode 100644
index 0000000..b8752e7
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-default/script-tifinagh/MANIFEST
@@ -0,0 +1 @@
+misc
diff --git a/test/shaping/texts/in-tree/shaper-default/script-tifinagh/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-default/script-tifinagh/misc/MANIFEST
new file mode 100644
index 0000000..29cfb2f
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-default/script-tifinagh/misc/MANIFEST
@@ -0,0 +1 @@
+misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-default/script-tifinagh/misc/misc.txt b/test/shaping/texts/in-tree/shaper-default/script-tifinagh/misc/misc.txt
new file mode 100644
index 0000000..0c307eb
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-default/script-tifinagh/misc/misc.txt
@@ -0,0 +1,11 @@
+ⵎⵜ
+ⵎ⵿ⵜ
+ⵏⴾ
+ⵏ⵿ⴾ
+ⵏⵜ
+ⵏ⵿ⵜ
+ⵔⵜ
+ⵔ⵿ⵜ
+ⵙⵜ
+ⵙ⵿ⵜ
+
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/MANIFEST
index 7eff9e1..a00d7ae 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/MANIFEST
@@ -1,3 +1,4 @@
 extensive.txt
 misc.txt
 reph.txt
+split-matras.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/misc.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/misc.txt
index 8715f72..363fcb6 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/misc.txt
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/misc.txt
@@ -37,3 +37,4 @@
 න‍්ගේ
 ර්‍
 ක්‍රා
+කේ
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/split-matras.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/split-matras.txt
new file mode 100644
index 0000000..2a73a40
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/split-matras.txt
@@ -0,0 +1,4 @@
+කේ
+කො
+කෝ
+කෞ
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/MANIFEST
index 29cfb2f..7f461ee 100644
--- a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/MANIFEST
@@ -1 +1,2 @@
 misc.txt
+utn11.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/utn11.txt b/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/utn11.txt
new file mode 100644
index 0000000..d5cea7c
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/utn11.txt
@@ -0,0 +1,34 @@
+စာ
+ခါ
+သိက္ခာ
+သဒ္ဓါ
+ညို
+ထုံး
+နေ
+ပေါ
+ဖျား
+ကြေး
+မွေး
+မှု
+ပတ္တာ
+ထင်
+ကြဉ်
+ကော်
+စင်္ကြံ
+သင်္ဘော
+ပသျှူး
+မြွှာ
+သျှောင်
+ကောင်လေးတွေကျောင်းကိုသွားကြတယ်။
+အိပ်ခန်းတံခါးကို
+အိပ်ခန်းတံ⁠ခါးကို
+အင်္ဝေ
+အငွေ
+ယောက်ျား
+ကျွန်ုပ်
+ဝါကျ
+ဂိမှာန်
+ဥယ‌ျာန
+က္လ
+ကျ္လပ်
+နိယ္အ်
diff --git a/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/MANIFEST
index 6aa865b..911099e 100644
--- a/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/MANIFEST
@@ -1,2 +1,3 @@
 phinthu.txt
+pua-shaping.txt
 sara-am.txt
diff --git a/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/pua-shaping.txt b/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/pua-shaping.txt
new file mode 100644
index 0000000..c17834b
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/pua-shaping.txt
@@ -0,0 +1,11 @@
+นี
+น่
+นี่
+น่ี
+ป็
+ญ
+ญุ
+ฝิ
+ฝิ่
+ฝ่
+ฎู
diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc
index 6dce7a1..fd9756b 100644
--- a/util/hb-ot-shape-closure.cc
+++ b/util/hb-ot-shape-closure.cc
@@ -61,29 +61,31 @@
   }
   void consume_line (hb_buffer_t  *buffer,
 		     const char   *text,
-		     unsigned int  text_len)
+		     unsigned int  text_len,
+		     const char   *text_before,
+		     const char   *text_after)
   {
     hb_set_clear (glyphs);
     shaper.shape_closure (text, text_len, font, buffer, glyphs);
 
-    if (hb_set_empty (glyphs))
+    if (hb_set_is_empty (glyphs))
       return;
 
     /* Print it out! */
     bool first = true;
     for (hb_codepoint_t i = -1; hb_set_next (glyphs, &i);)
-      if (hb_set_has (glyphs, i)) {
-        if (first)
-	  first = false;
-	else
-	  printf (" ");
-	char glyph_name[32];
-	if (show_glyph_names) {
-	  hb_font_get_glyph_name (font, i, glyph_name, sizeof (glyph_name));
-	  printf ("%s", glyph_name);
-	} else
-	  printf ("%u", i);
-      }
+    {
+      if (first)
+	first = false;
+      else
+	printf (" ");
+      char glyph_name[32];
+      if (show_glyph_names) {
+	hb_font_get_glyph_name (font, i, glyph_name, sizeof (glyph_name));
+	printf ("%s", glyph_name);
+      } else
+	printf ("%u", i);
+    }
   }
   void finish (const font_options_t *font_opts)
   {
diff --git a/util/hb-shape.cc b/util/hb-shape.cc
index b23519b..ca94152 100644
--- a/util/hb-shape.cc
+++ b/util/hb-shape.cc
@@ -40,6 +40,26 @@
     gs = g_string_new (NULL);
     line_no = 0;
     font = hb_font_reference (font_opts->get_font ());
+
+    if (!options.output_format)
+      output_format = HB_BUFFER_SERIALIZE_FORMAT_TEXT;
+    else
+      output_format = hb_buffer_serialize_format_from_string (options.output_format, -1);
+    if (!hb_buffer_serialize_format_to_string (output_format))
+    {
+      fail (false, "Unknown output format `%s'; supported formats are: %s",
+	    options.output_format,
+	    g_strjoinv (" / ", (gchar**) hb_buffer_serialize_list_formats ()));
+    }
+
+    unsigned int flags = HB_BUFFER_SERIALIZE_FLAGS_DEFAULT;
+    if (!format.show_glyph_names)
+      flags |= HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES;
+    if (!format.show_clusters)
+      flags |= HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS;
+    if (!format.show_positions)
+      flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS;
+    format_flags = (hb_buffer_serialize_flags_t) flags;
   }
   void new_line (void)
   {
@@ -51,7 +71,7 @@
 		     hb_bool_t     utf8_clusters)
   {
     g_string_set_size (gs, 0);
-    format.serialize_buffer_of_text (buffer, line_no, text, text_len, font, utf8_clusters, gs);
+    format.serialize_buffer_of_text (buffer, line_no, text, text_len, font, gs);
     fprintf (options.fp, "%s", gs->str);
   }
   void shape_failed (hb_buffer_t  *buffer,
@@ -69,7 +89,8 @@
 		       hb_bool_t     utf8_clusters)
   {
     g_string_set_size (gs, 0);
-    format.serialize_buffer_of_glyphs (buffer, line_no, text, text_len, font, utf8_clusters, gs);
+    format.serialize_buffer_of_glyphs (buffer, line_no, text, text_len, font,
+				       output_format, format_flags, gs);
     fprintf (options.fp, "%s", gs->str);
   }
   void finish (const font_options_t *font_opts)
@@ -87,6 +108,8 @@
   GString *gs;
   unsigned int line_no;
   hb_font_t *font;
+  hb_buffer_serialize_format_t output_format;
+  hb_buffer_serialize_flags_t format_flags;
 };
 
 int
diff --git a/util/main-font-text.hh b/util/main-font-text.hh
index 44e3bfb..ac51b2d 100644
--- a/util/main-font-text.hh
+++ b/util/main-font-text.hh
@@ -61,7 +61,7 @@
     unsigned int text_len;
     const char *text;
     while ((text = input.get_line (&text_len)))
-      consumer.consume_line (buffer, text, text_len);
+      consumer.consume_line (buffer, text, text_len, input.text_before, input.text_after);
     hb_buffer_destroy (buffer);
 
     consumer.finish (&font_opts);
diff --git a/util/options.cc b/util/options.cc
index dc7aeed..6246654 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -244,11 +244,11 @@
   GOptionEntry entries[] =
   {
     {"annotate",	0, 0, G_OPTION_ARG_NONE,	&this->annotate,		"Annotate output rendering",				NULL},
-    {"background",	0, 0, G_OPTION_ARG_STRING,	&this->back,			"Set background color (default: "DEFAULT_BACK")",	"red/#rrggbb/#rrggbbaa"},
-    {"foreground",	0, 0, G_OPTION_ARG_STRING,	&this->fore,			"Set foreground color (default: "DEFAULT_FORE")",	"red/#rrggbb/#rrggbbaa"},
+    {"background",	0, 0, G_OPTION_ARG_STRING,	&this->back,			"Set background color (default: " DEFAULT_BACK ")",	"red/#rrggbb/#rrggbbaa"},
+    {"foreground",	0, 0, G_OPTION_ARG_STRING,	&this->fore,			"Set foreground color (default: " DEFAULT_FORE ")",	"red/#rrggbb/#rrggbbaa"},
     {"line-space",	0, 0, G_OPTION_ARG_DOUBLE,	&this->line_space,		"Set space between lines (default: 0)",			"units"},
-    {"margin",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_margin,	"Margin around output (default: "G_STRINGIFY(DEFAULT_MARGIN)")","one to four numbers"},
-    {"font-size",	0, 0, G_OPTION_ARG_DOUBLE,	&this->font_size,		"Font size (default: "G_STRINGIFY(DEFAULT_FONT_SIZE)")","size"},
+    {"margin",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_margin,	"Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"},
+    {"font-size",	0, 0, G_OPTION_ARG_DOUBLE,	&this->font_size,		"Font size (default: " G_STRINGIFY(DEFAULT_FONT_SIZE) ")","size"},
     {NULL}
   };
   parser->add_group (entries,
@@ -271,6 +271,9 @@
     {"direction",	0, 0, G_OPTION_ARG_STRING,	&this->direction,		"Set text direction (default: auto)",	"ltr/rtl/ttb/btt"},
     {"language",	0, 0, G_OPTION_ARG_STRING,	&this->language,		"Set text language (default: $LANG)",	"langstr"},
     {"script",		0, 0, G_OPTION_ARG_STRING,	&this->script,			"Set text script (default: auto)",	"ISO-15924 tag"},
+    {"bot",		0, 0, G_OPTION_ARG_NONE,	&this->bot,			"Treat text as beginning-of-paragraph",	NULL},
+    {"eot",		0, 0, G_OPTION_ARG_NONE,	&this->eot,			"Treat text as end-of-paragraph",	NULL},
+    {"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE,	&this->preserve_default_ignorables,	"Preserve Default-Ignorable characters",	NULL},
     {"utf8-clusters",	0, 0, G_OPTION_ARG_NONE,	&this->utf8_clusters,		"Use UTF8 byte indices, not char indices",	NULL},
     {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE,	&this->normalize_glyphs,	"Rearrange glyph clusters in nominal order",	NULL},
     {NULL}
@@ -313,7 +316,7 @@
     "\n"
     "    Mixing it all:\n"
     "\n"
-    "      \"kern[3:5]=0\" 1         3         5         # Turn feature off for range";
+    "      \"aalt[3:5]=2\" 2         3         5         # Turn 2nd alternate on for range";
 
   GOptionEntry entries2[] =
   {
@@ -349,7 +352,9 @@
   GOptionEntry entries[] =
   {
     {"text",		0, 0, G_OPTION_ARG_STRING,	&this->text,			"Set input text",			"string"},
-    {"text-file",	0, 0, G_OPTION_ARG_STRING,	&this->text_file,		"Set input text file-name\n\n    If no text is provided, standard input is used for input.",		"filename"},
+    {"text-file",	0, 0, G_OPTION_ARG_STRING,	&this->text_file,		"Set input text file-name\n\n    If no text is provided, standard input is used for input.\n",		"filename"},
+    {"text-before",	0, 0, G_OPTION_ARG_STRING,	&this->text_before,		"Set text context before each line",	"string"},
+    {"text-after",	0, 0, G_OPTION_ARG_STRING,	&this->text_after,		"Set text context after each line",	"string"},
     {NULL}
   };
   parser->add_group (entries,
@@ -610,45 +615,23 @@
 void
 format_options_t::serialize_glyphs (hb_buffer_t *buffer,
 				    hb_font_t   *font,
-				    hb_bool_t    utf8_clusters,
+				    hb_buffer_serialize_format_t output_format,
+				    hb_buffer_serialize_flags_t flags,
 				    GString     *gs)
 {
-  unsigned int num_glyphs = hb_buffer_get_length (buffer);
-  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
-  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
-
   g_string_append_c (gs, '[');
-  for (unsigned int i = 0; i < num_glyphs; i++)
-  {
-    if (i)
-      g_string_append_c (gs, '|');
+  unsigned int num_glyphs = hb_buffer_get_length (buffer);
+  unsigned int start = 0;
 
-    char glyph_name[128];
-    if (show_glyph_names) {
-      hb_font_glyph_to_string (font, info->codepoint, glyph_name, sizeof (glyph_name));
-      g_string_append_printf (gs, "%s", glyph_name);
-    } else
-      g_string_append_printf (gs, "%u", info->codepoint);
-
-    if (show_clusters) {
-      g_string_append_printf (gs, "=%u", info->cluster);
-      if (utf8_clusters)
-	g_string_append (gs, "u8");
-    }
-
-    if (show_positions && (pos->x_offset || pos->y_offset)) {
-      g_string_append_c (gs, '@');
-      if (pos->x_offset) g_string_append_printf (gs, "%d", pos->x_offset);
-      if (pos->y_offset) g_string_append_printf (gs, ",%d", pos->y_offset);
-    }
-    if (show_positions && (pos->x_advance || pos->y_advance)) {
-      g_string_append_c (gs, '+');
-      if (pos->x_advance) g_string_append_printf (gs, "%d", pos->x_advance);
-      if (pos->y_advance) g_string_append_printf (gs, ",%d", pos->y_advance);
-    }
-
-    info++;
-    pos++;
+  while (start < num_glyphs) {
+    char buf[1024];
+    unsigned int consumed;
+    start += hb_buffer_serialize_glyphs (buffer, start, num_glyphs,
+					 buf, sizeof (buf), &consumed,
+					 font, output_format, flags);
+    if (!consumed)
+      break;
+    g_string_append (gs, buf);
   }
   g_string_append_c (gs, ']');
 }
@@ -665,7 +648,6 @@
 					    const char   *text,
 					    unsigned int  text_len,
 					    hb_font_t    *font,
-					    hb_bool_t     utf8_clusters,
 					    GString      *gs)
 {
   if (show_text) {
@@ -697,10 +679,11 @@
 					      const char   *text,
 					      unsigned int  text_len,
 					      hb_font_t    *font,
-					      hb_bool_t     utf8_clusters,
+					      hb_buffer_serialize_format_t output_format,
+					      hb_buffer_serialize_flags_t format_flags,
 					      GString      *gs)
 {
   serialize_line_no (line_no, gs);
-  serialize_glyphs (buffer, font, utf8_clusters, gs);
+  serialize_glyphs (buffer, font, output_format, format_flags, gs);
   g_string_append_c (gs, '\n');
 }
diff --git a/util/options.hh b/util/options.hh
index 5d25d9e..093f052 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -144,6 +144,7 @@
   shape_options_t (option_parser_t *parser)
   {
     direction = language = script = NULL;
+    bot = eot = preserve_default_ignorables = false;
     features = NULL;
     num_features = 0;
     shapers = NULL;
@@ -165,12 +166,24 @@
     hb_buffer_set_direction (buffer, hb_direction_from_string (direction, -1));
     hb_buffer_set_script (buffer, hb_script_from_string (script, -1));
     hb_buffer_set_language (buffer, hb_language_from_string (language, -1));
+    hb_buffer_set_flags (buffer, (hb_buffer_flags_t) (HB_BUFFER_FLAGS_DEFAULT |
+			 (bot ? HB_BUFFER_FLAG_BOT : 0) |
+			 (eot ? HB_BUFFER_FLAG_EOT : 0) |
+			 (preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0)));
   }
 
-  void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len)
+  void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len,
+			const char *text_before, const char *text_after)
   {
-    hb_buffer_reset (buffer);
+    hb_buffer_clear (buffer);
+    if (text_before) {
+      unsigned int len = strlen (text_before);
+      hb_buffer_add_utf8 (buffer, text_before, len, len, 0);
+    }
     hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len);
+    if (text_after) {
+      hb_buffer_add_utf8 (buffer, text_after, -1, 0, 0);
+    }
 
     if (!utf8_clusters) {
       /* Reset cluster values to refer to Unicode character index
@@ -205,9 +218,16 @@
     hb_ot_shape_glyphs_closure (font, buffer, features, num_features, glyphs);
   }
 
+  /* Buffer properties */
   const char *direction;
   const char *language;
   const char *script;
+
+  /* Buffer flags */
+  hb_bool_t bot;
+  hb_bool_t eot;
+  hb_bool_t preserve_default_ignorables;
+
   hb_feature_t *features;
   unsigned int num_features;
   char **shapers;
@@ -245,6 +265,9 @@
 struct text_options_t : option_group_t
 {
   text_options_t (option_parser_t *parser) {
+    text_before = NULL;
+    text_after = NULL;
+
     text = NULL;
     text_file = NULL;
 
@@ -273,6 +296,9 @@
 
   const char *get_line (unsigned int *len);
 
+  const char *text_before;
+  const char *text_after;
+
   const char *text;
   const char *text_file;
 
@@ -338,7 +364,8 @@
 			  GString      *gs);
   void serialize_glyphs (hb_buffer_t  *buffer,
 			 hb_font_t    *font,
-			 hb_bool_t    utf8_clusters,
+			 hb_buffer_serialize_format_t format,
+			 hb_buffer_serialize_flags_t flags,
 			 GString      *gs);
   void serialize_line_no (unsigned int  line_no,
 			  GString      *gs);
@@ -347,7 +374,6 @@
 				 const char   *text,
 				 unsigned int  text_len,
 				 hb_font_t    *font,
-				 hb_bool_t     utf8_clusters,
 				 GString      *gs);
   void serialize_message (unsigned int  line_no,
 			  const char   *msg,
@@ -357,7 +383,8 @@
 				   const char   *text,
 				   unsigned int  text_len,
 				   hb_font_t    *font,
-				   hb_bool_t     utf8_clusters,
+				   hb_buffer_serialize_format_t output_format,
+				   hb_buffer_serialize_flags_t format_flags,
 				   GString      *gs);
 
 
diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh
index 220daa4..bf1a22d 100644
--- a/util/shape-consumer.hh
+++ b/util/shape-consumer.hh
@@ -45,11 +45,13 @@
   }
   void consume_line (hb_buffer_t  *buffer,
 		     const char   *text,
-		     unsigned int  text_len)
+		     unsigned int  text_len,
+		     const char   *text_before,
+		     const char   *text_after)
   {
     output.new_line ();
 
-    shaper.populate_buffer (buffer, text, text_len);
+    shaper.populate_buffer (buffer, text, text_len, text_before, text_after);
     output.consume_text (buffer, text, text_len, shaper.utf8_clusters);
 
     if (!shaper.shape (font, buffer)) {