Bug 1249861 - Update harfbuzz to release 1.2.2 from upstream. r=jrmuizel
authorJonathan Kew <jkew@mozilla.com>
Wed, 24 Feb 2016 20:54:53 +0000
changeset 321793 2b0aa1cffeeaabb524b9d2936321af18c7445fbc
parent 321792 d8da9bc56468f58be505e61e72e78bff1d2b4b61
child 321794 b5ec7338bddf9f1147e19c1d4d1b90a0cdb8da9a
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1249861
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1249861 - Update harfbuzz to release 1.2.2 from upstream. r=jrmuizel
gfx/harfbuzz/src/Makefile.am
gfx/harfbuzz/src/hb-blob.cc
gfx/harfbuzz/src/hb-buffer-private.hh
gfx/harfbuzz/src/hb-buffer.cc
gfx/harfbuzz/src/hb-common.cc
gfx/harfbuzz/src/hb-coretext.cc
gfx/harfbuzz/src/hb-graphite2.cc
gfx/harfbuzz/src/hb-open-file-private.hh
gfx/harfbuzz/src/hb-open-type-private.hh
gfx/harfbuzz/src/hb-ot-head-table.hh
gfx/harfbuzz/src/hb-ot-hhea-table.hh
gfx/harfbuzz/src/hb-ot-layout-common-private.hh
gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh
gfx/harfbuzz/src/hb-ot-layout-private.hh
gfx/harfbuzz/src/hb-ot-layout.cc
gfx/harfbuzz/src/hb-ot-maxp-table.hh
gfx/harfbuzz/src/hb-ot-shape-complex-default.cc
gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
gfx/harfbuzz/src/hb-ot-shape-complex-tibetan.cc
gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
gfx/harfbuzz/src/hb-ot-shape.cc
gfx/harfbuzz/src/hb-ot-tag.cc
gfx/harfbuzz/src/hb-unicode-private.hh
gfx/harfbuzz/src/hb-version.h
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -1,11 +1,10 @@
 # Process this file with automake to produce Makefile.in
 
-NULL =
 SUBDIRS =
 DIST_SUBDIRS =
 BUILT_SOURCES =
 EXTRA_DIST =
 CLEANFILES =
 DISTCLEANFILES =
 MAINTAINERCLEANFILES =
 DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
@@ -14,186 +13,95 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-int
 #AM_CXXFLAGS =
 
 # Convenience targets:
 lib: $(BUILT_SOURCES) libharfbuzz.la
 fuzzing: $(BUILT_SOURCES) libharfbuzz-fuzzing.la
 
 lib_LTLIBRARIES = libharfbuzz.la
 
+include Makefile.sources
+
 HBCFLAGS =
 HBLIBS =
 HBNONPCLIBS =
 HBDEPS =
-HBSOURCES =  \
-	hb-atomic-private.hh \
-	hb-blob.cc \
-	hb-buffer-deserialize-json.hh \
-	hb-buffer-deserialize-text.hh \
-	hb-buffer-private.hh \
-	hb-buffer-serialize.cc \
-	hb-buffer.cc \
-	hb-cache-private.hh \
-	hb-common.cc \
-	hb-face-private.hh \
-	hb-face.cc \
-	hb-font-private.hh \
-	hb-font.cc \
-	hb-mutex-private.hh \
-	hb-object-private.hh \
-	hb-open-file-private.hh \
-	hb-open-type-private.hh \
-	hb-ot-cmap-table.hh \
-	hb-ot-glyf-table.hh \
-	hb-ot-head-table.hh \
-	hb-ot-hhea-table.hh \
-	hb-ot-hmtx-table.hh \
-	hb-ot-maxp-table.hh \
-	hb-ot-name-table.hh \
-	hb-ot-os2-table.hh \
-	hb-ot-tag.cc \
-	hb-private.hh \
-	hb-set-private.hh \
-	hb-set.cc \
-	hb-shape.cc \
-	hb-shape-plan-private.hh \
-	hb-shape-plan.cc \
-	hb-shaper-list.hh \
-	hb-shaper-impl-private.hh \
-	hb-shaper-private.hh \
-	hb-shaper.cc \
-	hb-unicode-private.hh \
-	hb-unicode.cc \
-	hb-utf-private.hh \
-	hb-warning.cc \
-	$(NULL)
-HBHEADERS = \
-	hb.h \
-	hb-blob.h \
-	hb-buffer.h \
-	hb-common.h \
-	hb-deprecated.h \
-	hb-face.h \
-	hb-font.h \
-	hb-set.h \
-	hb-shape.h \
-	hb-shape-plan.h \
-	hb-unicode.h \
-	$(NULL)
-HBNODISTHEADERS = \
-	hb-version.h \
-	$(NULL)
+HBSOURCES =  $(HB_BASE_sources)
+HBHEADERS = $(HB_BASE_headers)
+HBNODISTHEADERS = $(HB_NODIST_headers)
 
 if HAVE_OT
-HBSOURCES += \
-	hb-ot-font.cc \
-	hb-ot-layout.cc \
-	hb-ot-layout-common-private.hh \
-	hb-ot-layout-gdef-table.hh \
-	hb-ot-layout-gpos-table.hh \
-	hb-ot-layout-gsubgpos-private.hh \
-	hb-ot-layout-gsub-table.hh \
-	hb-ot-layout-jstf-table.hh \
-	hb-ot-layout-private.hh \
-	hb-ot-map.cc \
-	hb-ot-map-private.hh \
-	hb-ot-shape.cc \
-	hb-ot-shape-complex-arabic.cc \
-	hb-ot-shape-complex-arabic-fallback.hh \
-	hb-ot-shape-complex-arabic-private.hh \
-	hb-ot-shape-complex-arabic-table.hh \
-	hb-ot-shape-complex-arabic-win1256.hh \
-	hb-ot-shape-complex-default.cc \
-	hb-ot-shape-complex-hangul.cc \
-	hb-ot-shape-complex-hebrew.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.cc \
-	hb-ot-shape-complex-myanmar.cc \
-	hb-ot-shape-complex-myanmar-machine.hh \
-	hb-ot-shape-complex-thai.cc \
-	hb-ot-shape-complex-tibetan.cc \
-	hb-ot-shape-complex-use.cc \
-	hb-ot-shape-complex-use-machine.hh \
-	hb-ot-shape-complex-use-private.hh \
-	hb-ot-shape-complex-use-table.cc \
-	hb-ot-shape-complex-private.hh \
-	hb-ot-shape-normalize-private.hh \
-	hb-ot-shape-normalize.cc \
-	hb-ot-shape-fallback-private.hh \
-	hb-ot-shape-fallback.cc \
-	hb-ot-shape-private.hh \
-	$(NULL)
-HBHEADERS += \
-	hb-ot.h \
-	hb-ot-font.h \
-	hb-ot-layout.h \
-	hb-ot-shape.h \
-	hb-ot-tag.h \
-	$(NULL)
+HBSOURCES += $(HB_OT_sources)
+HBHEADERS += $(HB_OT_headers)
 endif
 
 if HAVE_FALLBACK
-HBSOURCES += hb-fallback-shape.cc
+HBSOURCES += $(HB_FALLBACK_sources)
 endif
 
 if HAVE_PTHREAD
 HBCFLAGS += $(PTHREAD_CFLAGS)
 HBNONPCLIBS += $(PTHREAD_LIBS)
 endif
 
 if HAVE_GLIB
 HBCFLAGS += $(GLIB_CFLAGS)
 HBLIBS   += $(GLIB_LIBS)
 HBDEPS   += $(GLIB_DEPS)
-HBSOURCES += hb-glib.cc
-HBHEADERS += hb-glib.h
+HBSOURCES += $(HB_GLIB_sources)
+HBHEADERS += $(HB_GLIB_headers)
 endif
 
 if HAVE_FREETYPE
 HBCFLAGS += $(FREETYPE_CFLAGS)
 HBLIBS   += $(FREETYPE_LIBS)
 # XXX
 # The following creates a recursive dependency on FreeType if FreeType is
 # built with HarfBuzz support enabled.  Newer pkg-config handles that just
 # fine but pkg-config 0.26 as shipped in Ubuntu 14.04 crashes.  Remove
 # in a year or two, or otherwise work around it...
 #HBDEPS   += $(FREETYPE_DEPS)
-HBSOURCES += hb-ft.cc
-HBHEADERS += hb-ft.h
+HBSOURCES += $(HB_FT_sources)
+HBHEADERS += $(HB_FT_headers)
 endif
 
 if HAVE_GRAPHITE2
 HBCFLAGS += $(GRAPHITE2_CFLAGS)
 HBLIBS   += $(GRAPHITE2_LIBS)
 HBDEPS   += $(GRAPHITE2_DEPS)
-HBSOURCES += hb-graphite2.cc
-HBHEADERS += hb-graphite2.h
+HBSOURCES += $(HB_GRAPHITE2_sources)
+HBHEADERS += $(HB_GRAPHITE2_headers)
 endif
 
 if HAVE_UNISCRIBE
 HBCFLAGS += $(UNISCRIBE_CFLAGS)
 HBNONPCLIBS += $(UNISCRIBE_LIBS)
-HBSOURCES += hb-uniscribe.cc
-HBHEADERS += hb-uniscribe.h
+HBSOURCES += $(HB_UNISCRIBE_sources)
+HBHEADERS += $(HB_UNISCRIBE_headers)
+endif
+
+if HAVE_DIRECTWRITE
+HBCFLAGS += $(DIRECTWRITE_CXXFLAGS)
+HBNONPCLIBS += $(DIRECTWRITE_LIBS)
+HBSOURCES += $(HB_DIRECTWRITE_sources)
+HBHEADERS += $(HB_DIRECTWRITE_headers)
 endif
 
 if HAVE_CORETEXT
 HBCFLAGS += $(CORETEXT_CFLAGS)
 HBNONPCLIBS += $(CORETEXT_LIBS)
-HBSOURCES += hb-coretext.cc
-HBHEADERS += hb-coretext.h
+HBSOURCES += $(HB_CORETEXT_sources)
+HBHEADERS += $(HB_CORETEXT_headers)
 endif
 
 if HAVE_UCDN
 SUBDIRS += hb-ucdn
 HBCFLAGS += -I$(srcdir)/hb-ucdn
 HBLIBS   += hb-ucdn/libhb-ucdn.la
-HBSOURCES += hb-ucdn.cc
+HBSOURCES += $(HB_UCDN_sources)
 endif
 DIST_SUBDIRS += hb-ucdn
 
 
 # Put the library together
 
 HBLIBS += $(HBNONPCLIBS)
 
@@ -234,43 +142,43 @@ libharfbuzz_fuzzing_la_SOURCES = $(libha
 libharfbuzz_fuzzing_la_CPPFLAGS = $(libharfbuzz_la_CPPFLAGS) $(FUZZING_CPPFLAGS)
 libharfbuzz_fuzzing_la_LDFLAGS = $(libharfbuzz_la_LDFLAGS)
 libharfbuzz_fuzzing_la_LIBADD = $(libharfbuzz_la_LIBADD)
 EXTRA_libharfbuzz_fuzzing_la_DEPENDENCIES = $(EXTRA_libharfbuzz_la_DEPENDENCIES)
 CLEANFILES += libharfbuzz-fuzzing.la
 
 if HAVE_ICU
 lib_LTLIBRARIES += libharfbuzz-icu.la
-libharfbuzz_icu_la_SOURCES = hb-icu.cc
+libharfbuzz_icu_la_SOURCES = $(HB_ICU_sources)
 libharfbuzz_icu_la_CPPFLAGS = $(ICU_CFLAGS)
 libharfbuzz_icu_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
 libharfbuzz_icu_la_LIBADD = $(ICU_LIBS) libharfbuzz.la
-pkginclude_HEADERS += hb-icu.h
+pkginclude_HEADERS += $(HB_ICU_headers)
 pkgconfig_DATA += harfbuzz-icu.pc
 endif
 EXTRA_DIST += harfbuzz-icu.pc.in
 
 if HAVE_GOBJECT
 lib_LTLIBRARIES += libharfbuzz-gobject.la
-libharfbuzz_gobject_la_SOURCES = hb-gobject-structs.cc
-nodist_libharfbuzz_gobject_la_SOURCES = hb-gobject-enums.cc
+libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_sources)
+nodist_libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_ENUM_sources)
 libharfbuzz_gobject_la_CPPFLAGS = $(GOBJECT_CFLAGS)
 libharfbuzz_gobject_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
 libharfbuzz_gobject_la_LIBADD = $(GOBJECT_LIBS) libharfbuzz.la
-pkginclude_HEADERS += hb-gobject.h hb-gobject-structs.h
-nodist_pkginclude_HEADERS += hb-gobject-enums.h
+pkginclude_HEADERS += $(HB_GOBJECT_headers)
+nodist_pkginclude_HEADERS += $(HB_GOBJECT_ENUM_headers)
 pkgconfig_DATA += harfbuzz-gobject.pc
 
 BUILT_SOURCES += \
-	hb-gobject-enums.cc \
-	hb-gobject-enums.h \
+	$(HB_GOBJECT_ENUM_sources) \
+	$(HB_GOBJECT_ENUM_headers) \
 	$(NULL)
 DISTCLEANFILES += \
-	hb-gobject-enums.cc \
-	hb-gobject-enums.h \
+	$(HB_GOBJECT_ENUM_sources) \
+	$(HB_GOBJECT_ENUM_headers) \
 	$(NULL)
 hb-gobject-enums.%: hb-gobject-enums.%.tmpl $(HBHEADERS)
 	$(AM_V_GEN) $(GLIB_MKENUMS) \
 		--identifier-prefix hb_ --symbol-prefix hb_gobject \
 		--template $^ | \
 	sed 's/_t_get_type/_get_type/g; s/_T (/ (/g' > "$@" \
 	|| ($(RM) "$@"; false)
 endif
@@ -432,20 +340,20 @@ HarfBuzz_0_0_gir_CFLAGS = \
 HarfBuzz_0_0_gir_LIBS = \
 	libharfbuzz.la \
 	libharfbuzz-gobject.la \
 	$(NULL)
 HarfBuzz_0_0_gir_FILES = \
 	$(HBHEADERS) \
 	$(HBNODISTHEADERS) \
 	$(HBSOURCES) \
-	hb-gobject-enums.cc \
-	hb-gobject-enums.h \
-	hb-gobject-structs.cc \
-	hb-gobject-structs.h \
+	$(HB_GOBJECT_ENUM_sources) \
+	$(HB_GOBJECT_ENUM_headers) \
+	$(HB_GOBJECT_sources) \
+	$(HB_GOBJECT_STRUCTS_headers) \
 	$(NULL)
 
 girdir = $(datadir)/gir-1.0
 gir_DATA = $(INTROSPECTION_GIRS)
 
 typelibdir = $(libdir)/girepository-1.0
 typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
 
--- a/gfx/harfbuzz/src/hb-blob.cc
+++ b/gfx/harfbuzz/src/hb-blob.cc
@@ -99,17 +99,16 @@ hb_blob_create (const char        *data,
 		hb_memory_mode_t   mode,
 		void              *user_data,
 		hb_destroy_func_t  destroy)
 {
   hb_blob_t *blob;
 
   if (!length ||
       length >= 1u << 31 ||
-      data + length < data /* overflows */ ||
       !(blob = hb_object_create<hb_blob_t> ())) {
     if (destroy)
       destroy (user_data);
     return hb_blob_get_empty ();
   }
 
   blob->data = data;
   blob->length = length;
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -51,18 +51,17 @@ ASSERT_STATIC (sizeof (hb_glyph_info_t) 
 HB_MARK_AS_FLAG_T (hb_buffer_flags_t);
 HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t);
 
 enum hb_buffer_scratch_flags_t {
   HB_BUFFER_SCRATCH_FLAG_DEFAULT			= 0x00000000u,
   HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII			= 0x00000001u,
   HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES		= 0x00000002u,
   HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK		= 0x00000004u,
-  HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_CURSIVE		= 0x00000008u,
-  HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT		= 0x00000010u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT		= 0x00000008u,
   /* Reserved for complex shapers' internal use. */
   HB_BUFFER_SCRATCH_FLAG_COMPLEX0			= 0x01000000u,
   HB_BUFFER_SCRATCH_FLAG_COMPLEX1			= 0x02000000u,
   HB_BUFFER_SCRATCH_FLAG_COMPLEX2			= 0x04000000u,
   HB_BUFFER_SCRATCH_FLAG_COMPLEX3			= 0x08000000u,
 };
 HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t);
 
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -402,16 +402,18 @@ bool
 hb_buffer_t::move_to (unsigned int i)
 {
   if (!have_output)
   {
     assert (i <= len);
     idx = i;
     return true;
   }
+  if (unlikely (in_error))
+    return false;
 
   assert (i <= out_len + (len - idx));
 
   if (out_len < i)
   {
     unsigned int count = i - out_len;
     if (unlikely (!make_room_for (count, count))) return false;
 
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -535,17 +535,17 @@ hb_user_data_array_t::set (hb_user_data_
   bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
 
   return ret;
 }
 
 void *
 hb_user_data_array_t::get (hb_user_data_key_t *key)
 {
-  hb_user_data_item_t item = {NULL };
+  hb_user_data_item_t item = {NULL, NULL, NULL};
 
   return items.find (key, &item, lock) ? item.data : NULL;
 }
 
 
 /* hb_version */
 
 /**
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -22,17 +22,16 @@
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Mozilla Author(s): Jonathan Kew
  * Google Author(s): Behdad Esfahbod
  */
 
 #define HB_SHAPER coretext
-#define hb_coretext_shaper_face_data_t CGFont
 #include "hb-shaper-impl-private.hh"
 
 #include "hb-coretext.h"
 
 
 #ifndef HB_DEBUG_CORETEXT
 #define HB_DEBUG_CORETEXT (HB_DEBUG+0)
 #endif
@@ -73,159 +72,177 @@ hb_coretext_face_create (CGFontRef cg_fo
 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face)
 HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font)
 
 
 /*
  * shaper face data
  */
 
+static CTFontDescriptorRef
+get_last_resort_font_desc (void)
+{
+  // TODO Handle allocation failures?
+  CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0);
+  CFArrayRef cascade_list = CFArrayCreate (kCFAllocatorDefault,
+					   (const void **) &last_resort,
+					   1,
+					   &kCFTypeArrayCallBacks);
+  CFRelease (last_resort);
+  CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault,
+						   (const void **) &kCTFontCascadeListAttribute,
+						   (const void **) &cascade_list,
+						   1,
+						   &kCFTypeDictionaryKeyCallBacks,
+						   &kCFTypeDictionaryValueCallBacks);
+  CFRelease (cascade_list);
+
+  CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes);
+  CFRelease (attributes);
+  return font_desc;
+}
+
 static void
 release_data (void *info, const void *data, size_t size)
 {
   assert (hb_blob_get_length ((hb_blob_t *) info) == size &&
           hb_blob_get_data ((hb_blob_t *) info, NULL) == data);
 
   hb_blob_destroy ((hb_blob_t *) info);
 }
 
-hb_coretext_shaper_face_data_t *
-_hb_coretext_shaper_face_data_create (hb_face_t *face)
+static CGFontRef
+create_cg_font (hb_face_t *face)
 {
-  hb_coretext_shaper_face_data_t *data = NULL;
-
+  CGFontRef cg_font = NULL;
   if (face->destroy == (hb_destroy_func_t) CGFontRelease)
   {
-    data = CGFontRetain ((CGFontRef) face->user_data);
+    cg_font = CGFontRetain ((CGFontRef) face->user_data);
   }
   else
   {
     hb_blob_t *blob = hb_face_reference_blob (face);
     unsigned int blob_length;
     const char *blob_data = hb_blob_get_data (blob, &blob_length);
     if (unlikely (!blob_length))
       DEBUG_MSG (CORETEXT, face, "Face has empty blob");
 
     CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data);
     if (likely (provider))
     {
-      data = CGFontCreateWithDataProvider (provider);
+      cg_font = CGFontCreateWithDataProvider (provider);
+      if (unlikely (!cg_font))
+	DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed");
       CGDataProviderRelease (provider);
     }
   }
+  return cg_font;
+}
 
-  if (unlikely (!data)) {
-    DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed");
+static CTFontRef
+create_ct_font (CGFontRef cg_font, CGFloat font_size)
+{
+  CTFontRef ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, NULL, NULL);
+  if (unlikely (!ct_font)) {
+    DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed");
+    return NULL;
+  }
+
+  /* Create font copy with cascade list that has LastResort first; this speeds up CoreText
+   * font fallback which we don't need anyway. */
+  {
+    CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc ();
+    CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, NULL, last_resort_font_desc);
+    CFRelease (last_resort_font_desc);
+    if (new_ct_font)
+    {
+      CFRelease (ct_font);
+      ct_font = new_ct_font;
+    }
+    else
+      DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed");
+  }
+
+ return ct_font;
+}
+
+struct hb_coretext_shaper_face_data_t {
+  CGFontRef cg_font;
+  CTFontRef ct_font;
+};
+
+hb_coretext_shaper_face_data_t *
+_hb_coretext_shaper_face_data_create (hb_face_t *face)
+{
+  hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t));
+  if (unlikely (!data))
+    return NULL;
+
+  data->cg_font = create_cg_font (face);
+  if (unlikely (!data->cg_font))
+  {
+    DEBUG_MSG (CORETEXT, face, "CGFont creation failed..");
+    free (data);
+    return NULL;
+  }
+
+  /* We use 36pt size instead of UPEM, because CoreText implements the 'trak' table,
+   * which can make the font too tight at large sizes.  36pt should be a good semi-neutral
+   * size.
+   *
+   * Since we always create CTFont at a fixed size, our CTFont lives in face_data
+   * instead of font_data.  Which is good, because when people change scale on
+   * hb_font_t, we won't need to update our CTFont. */
+  data->ct_font = create_ct_font (data->cg_font, 36.);
+  if (unlikely (!data->ct_font))
+  {
+    DEBUG_MSG (CORETEXT, face, "CTFont creation failed.");
+    CFRelease (data->cg_font);
+    free (data);
+    return NULL;
   }
 
   return data;
 }
 
 void
 _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data)
 {
-  CFRelease (data);
+  CFRelease (data->ct_font);
+  CFRelease (data->cg_font);
+  free (data);
 }
 
 /*
  * Since: 0.9.10
  */
 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;
+  return face_data->cg_font;
 }
 
 
 /*
  * shaper font data
  */
 
-struct hb_coretext_shaper_font_data_t {
-  CTFontRef ct_font;
-  CGFloat x_mult, y_mult; /* From CT space to HB space. */
-};
+struct hb_coretext_shaper_font_data_t {};
 
 hb_coretext_shaper_font_data_t *
-_hb_coretext_shaper_font_data_create (hb_font_t *font)
+_hb_coretext_shaper_font_data_create (hb_font_t *font HB_UNUSED)
 {
-  if (unlikely (!hb_coretext_shaper_face_data_ensure (font->face))) return NULL;
-
-  hb_coretext_shaper_font_data_t *data = (hb_coretext_shaper_font_data_t *) calloc (1, sizeof (hb_coretext_shaper_font_data_t));
-  if (unlikely (!data))
-    return NULL;
-
-  hb_face_t *face = font->face;
-  hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-
-  /* Choose a CoreText font size and calculate multipliers to convert to HarfBuzz space. */
-  /* TODO: use upem instead of 36? */
-  CGFloat font_size = 36.; /* Default... */
-  /* No idea if the following is even a good idea. */
-  if (font->y_ppem)
-    font_size = font->y_ppem;
-
-  if (font_size < 0)
-    font_size = -font_size;
-  data->x_mult = (CGFloat) font->x_scale / font_size;
-  data->y_mult = (CGFloat) font->y_scale / font_size;
-  data->ct_font = CTFontCreateWithGraphicsFont (face_data, font_size, NULL, NULL);
-  if (unlikely (!data->ct_font)) {
-    DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed");
-    free (data);
-    return NULL;
-  }
-
-  /* Create font copy with cascade list that has LastResort first; this speeds up CoreText
-   * font fallback which we don't need anyway. */
-  {
-    // TODO Handle allocation failures?
-    CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize(CFSTR("LastResort"), 0);
-    CFArrayRef cascade_list = CFArrayCreate (kCFAllocatorDefault,
-					     (const void **) &last_resort,
-					     1,
-					     &kCFTypeArrayCallBacks);
-    CFRelease (last_resort);
-    CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault,
-						     (const void **) &kCTFontCascadeListAttribute,
-						     (const void **) &cascade_list,
-						     1,
-						     &kCFTypeDictionaryKeyCallBacks,
-						     &kCFTypeDictionaryValueCallBacks);
-    CFRelease (cascade_list);
-
-    CTFontDescriptorRef new_font_desc = CTFontDescriptorCreateWithAttributes (attributes);
-    CFRelease (attributes);
-
-    CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (data->ct_font, 0.0, NULL, new_font_desc);
-    if (new_ct_font)
-    {
-      CFRelease (data->ct_font);
-      data->ct_font = new_ct_font;
-    }
-    else
-      DEBUG_MSG (CORETEXT, font, "Font copy with empty cascade list failed");
-  }
-
-  if (unlikely (!data->ct_font)) {
-    DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed");
-    free (data);
-    return NULL;
-  }
-
-  return data;
+  return (hb_coretext_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
 _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data)
 {
-  CFRelease (data->ct_font);
-  free (data);
 }
 
 
 /*
  * shaper shape_plan data
  */
 
 struct hb_coretext_shaper_shape_plan_data_t {};
@@ -241,19 +258,20 @@ hb_coretext_shaper_shape_plan_data_t *
 void
 _hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
 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;
+  hb_face_t *face = font->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->ct_font;
 }
 
 
 /*
  * shaper
  */
 
 struct feature_record_t {
@@ -476,25 +494,29 @@ hb_bool_t
 _hb_coretext_shape (hb_shape_plan_t    *shape_plan,
 		    hb_font_t          *font,
                     hb_buffer_t        *buffer,
                     const hb_feature_t *features,
                     unsigned int        num_features)
 {
   hb_face_t *face = font->face;
   hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-  hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
+
+  CGFloat ct_font_size = CTFontGetSize (face_data->ct_font);
+  CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size;
+  CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size;
 
   /* Attach marks to their bases, to match the 'ot' shaper.
    * Adapted from hb-ot-shape:hb_form_clusters().
    * Note that this only makes us be closer to the 'ot' shaper,
    * but by no means the same.  For example, if there's
    * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will
    * continue pointing to B2 even though B2 was merged into B1's
    * cluster... */
+  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
   {
     hb_unicode_funcs_t *unicode = buffer->unicode;
     unsigned int count = buffer->len;
     hb_glyph_info_t *info = buffer->info;
     for (unsigned int i = 1; i < count; i++)
       if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint)))
 	buffer->merge_clusters (i - 1, i + 1);
   }
@@ -607,17 +629,17 @@ hb_bool_t
 							   1,
 							   &kCFTypeDictionaryKeyCallBacks,
 							   &kCFTypeDictionaryValueCallBacks);
 	  CFRelease (features_array);
 
 	  CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes);
 	  CFRelease (attributes);
 
-	  range->font = CTFontCreateCopyWithAttributes (font_data->ct_font, 0.0, NULL, font_desc);
+	  range->font = CTFontCreateCopyWithAttributes (face_data->ct_font, 0.0, NULL, font_desc);
 	  CFRelease (font_desc);
 	}
 	else
 	{
 	  range->font = NULL;
 	}
 
 	range->index_first = last_index;
@@ -764,17 +786,17 @@ resize_and_retry:
 							    kCFAllocatorNull);
 	if (unlikely (!lang))
 	  FAIL ("CFStringCreateWithCStringNoCopy failed");
 	CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
 					kCTLanguageAttributeName, lang);
 	CFRelease (lang);
       }
       CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
-				      kCTFontAttributeName, font_data->ct_font);
+				      kCTFontAttributeName, face_data->ct_font);
 
       if (num_features)
       {
 	unsigned int start = 0;
 	range_record_t *last_range = &range_records[0];
 	for (unsigned int k = 0; k < chars_len; k++)
 	{
 	  range_record_t *range = last_range;
@@ -857,17 +879,17 @@ resize_and_retry:
       /* CoreText does automatic font fallback (AKA "cascading") for  characters
        * not supported by the requested font, and provides no way to turn it off,
        * so we must detect if the returned run uses a font other than the requested
        * one and fill in the buffer with .notdef glyphs instead of random glyph
        * indices from a different font.
        */
       CFDictionaryRef attributes = CTRunGetAttributes (run);
       CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName));
-      if (!CFEqual (run_ct_font, font_data->ct_font))
+      if (!CFEqual (run_ct_font, face_data->ct_font))
       {
 	/* The run doesn't use our main font instance.  We have to figure out
 	 * whether font fallback happened, or this is just CoreText giving us
 	 * another CTFont using the same underlying CGFont.  CoreText seems
 	 * to do that in a variety of situations, one of which being vertical
 	 * text, but also perhaps for caching reasons.
 	 *
 	 * First, see if it uses any of our subfonts created to set font features...
@@ -897,23 +919,23 @@ resize_and_retry:
 	    matched = true;
 	    break;
 	  }
 	if (!matched)
 	{
 	  CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0);
 	  if (run_cg_font)
 	  {
-	    matched = CFEqual (run_cg_font, face_data);
+	    matched = CFEqual (run_cg_font, face_data->cg_font);
 	    CFRelease (run_cg_font);
 	  }
 	}
 	if (!matched)
 	{
-	  CFStringRef font_ps_name = CTFontCopyName (font_data->ct_font, kCTFontPostScriptNameKey);
+	  CFStringRef font_ps_name = CTFontCopyName (face_data->ct_font, kCTFontPostScriptNameKey);
 	  CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey);
 	  CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0);
 	  CFRelease (run_ps_name);
 	  CFRelease (font_ps_name);
 	  if (result == kCFCompareEqualTo)
 	    matched = true;
 	}
 	if (!matched)
@@ -1023,17 +1045,16 @@ resize_and_retry:
         SCRATCH_SAVE();
 	const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : NULL;
 	if (!positions) {
 	  ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry);
 	  CTRunGetPositions (run, range_all, position_buf);
 	  positions = position_buf;
 	}
 	hb_glyph_info_t *info = run_info;
-	CGFloat x_mult = font_data->x_mult, y_mult = font_data->y_mult;
 	if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
 	{
 	  hb_position_t x_offset = (positions[0].x - advances_so_far) * x_mult;
 	  for (unsigned int j = 0; j < num_glyphs; j++)
 	  {
 	    double advance;
 	    if (likely (j + 1 < num_glyphs))
 	      advance = positions[j + 1].x - positions[j].x;
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -211,16 +211,17 @@ void
  */
 
 struct hb_graphite2_cluster_t {
   unsigned int base_char;
   unsigned int num_chars;
   unsigned int base_glyph;
   unsigned int num_glyphs;
   unsigned int cluster;
+  float advance;
 };
 
 hb_bool_t
 _hb_graphite2_shape (hb_shape_plan_t    *shape_plan,
 		     hb_font_t          *font,
 		     hb_buffer_t        *buffer,
 		     const hb_feature_t *features,
 		     unsigned int        num_features)
@@ -305,104 +306,118 @@ hb_bool_t
   ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count);
 
 #undef ALLOCATE_ARRAY
 
   memset (clusters, 0, sizeof (clusters[0]) * buffer->len);
 
   hb_codepoint_t *pg = gids;
   clusters[0].cluster = buffer->info[0].cluster;
+  float curradv = HB_DIRECTION_IS_BACKWARD(buffer->props.direction) ? gr_slot_origin_X(gr_seg_first_slot(seg)) : 0.;
+  if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+  {
+    curradv = gr_slot_origin_X(gr_seg_first_slot(seg));
+    clusters[0].advance = gr_seg_advance_X(seg) - curradv;
+  }
   for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
   {
     unsigned int before = gr_slot_before (is);
     unsigned int after = gr_slot_after (is);
     *pg = gr_slot_gid (is);
     pg++;
     while (clusters[ci].base_char > before && ci)
     {
       clusters[ci-1].num_chars += clusters[ci].num_chars;
       clusters[ci-1].num_glyphs += clusters[ci].num_glyphs;
+      clusters[ci-1].advance += clusters[ci].advance;
       ci--;
     }
 
     if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars)
     {
       hb_graphite2_cluster_t *c = clusters + ci + 1;
       c->base_char = clusters[ci].base_char + clusters[ci].num_chars;
       c->cluster = buffer->info[c->base_char].cluster;
       c->num_chars = before - c->base_char;
       c->base_glyph = ic;
       c->num_glyphs = 0;
-      ci++;
+      if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+      {
+        ci++;
+        clusters[ci].advance = curradv - gr_slot_origin_X(is);
+      } else {
+        clusters[ci].advance = gr_slot_origin_X(is) - curradv;
+        ci++;
+      }
+      curradv = gr_slot_origin_X(is);
     }
     clusters[ci].num_glyphs++;
 
     if (clusters[ci].base_char + clusters[ci].num_chars < after + 1)
 	clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
   }
+
+  if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+    clusters[ci].advance = gr_seg_advance_X(seg) - curradv;
   ci++;
 
   for (unsigned int i = 0; i < ci; ++i)
   {
     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 = clusters[i].cluster;
+      info->var1.i32 = clusters[i].advance;     // all glyphs in the cluster get the same advance
     }
   }
   buffer->len = glyph_count;
 
   float yscale = font->y_scale / font->x_scale;
   /* Positioning. */
   if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
   {
-    hb_glyph_position_t *pPos;
-    for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_slot (seg);
-         is; pPos++, is = gr_slot_next_in_segment (is))
+    int currclus = -1;
+    const hb_glyph_info_t *info = buffer->info;
+    hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL);
+    curradvx = 0;
+    for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is))
     {
       pPos->x_offset = gr_slot_origin_X (is) - curradvx;
       pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy;
-      pPos->x_advance = gr_slot_advance_X (is, grface, grfont);
+      if (info->cluster != currclus) {
+        pPos->x_advance = info->var1.i32;
+        curradvx += pPos->x_advance;
+        currclus = info->cluster;
+      } else
+        pPos->x_advance = 0.;
+
       pPos->y_advance = gr_slot_advance_Y (is, grface, grfont) * yscale;
-      curradvx += pPos->x_advance;
       curradvy += pPos->y_advance;
     }
-    pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx;
   }
   else
   {
-    hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL) + buffer->len - 1;
-    const hb_glyph_info_t *info = buffer->info + buffer->len - 1;
-    const hb_glyph_info_t *tinfo;
-    const gr_slot *tis;
     int currclus = -1;
-    float clusx = 0., clusy = 0.;
-    for (is = gr_seg_last_slot (seg); is; pPos--, info--, is = gr_slot_prev_in_segment (is))
+    const hb_glyph_info_t *info = buffer->info;
+    hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL);
+    curradvx = gr_seg_advance_X(seg);
+    for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is))
     {
       if (info->cluster != currclus)
       {
-        curradvx += clusx;
-        curradvy += clusy;
+        pPos->x_advance = info->var1.i32;
+        if (currclus != -1) curradvx -= info[-1].var1.i32;
         currclus = info->cluster;
-        clusx = 0.;
-        clusy = 0.;
-        for (tis = is, tinfo = info; tis && tinfo->cluster == currclus; tis = gr_slot_prev_in_segment (tis), tinfo--)
-        {
-          clusx += gr_slot_advance_X (tis, grface, grfont);
-          clusy += gr_slot_advance_Y (tis, grface, grfont) * yscale;
-        }
-        curradvx += clusx;
-        curradvy += clusy;
-      }
-      pPos->x_advance = gr_slot_advance_X (is, grface, grfont);
+      } else
+      pPos->x_advance = 0.;
+
       pPos->y_advance = gr_slot_advance_Y (is, grface, grfont) * yscale;
-      curradvx -= pPos->x_advance;
       curradvy -= pPos->y_advance;
-      pPos->x_offset = gr_slot_origin_X (is) - curradvx;
+      pPos->x_offset = gr_slot_origin_X (is) - curradvx + pPos->x_advance;
       pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy;
     }
     hb_buffer_reverse_clusters (buffer);
   }
 
   if (feats) gr_featureval_destroy (feats);
   gr_seg_destroy (seg);
 
--- a/gfx/harfbuzz/src/hb-open-file-private.hh
+++ b/gfx/harfbuzz/src/hb-open-file-private.hh
@@ -135,17 +135,17 @@ struct TTCHeaderVersion1
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (table.sanitize (c, this));
   }
 
   protected:
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
-  FixedVersion	version;	/* Version of the TTC Header (1.0),
+  FixedVersion<>version;	/* Version of the TTC Header (1.0),
 				 * 0x00010000u */
   ArrayOf<OffsetTo<OffsetTable, ULONG>, ULONG>
 		table;		/* Array of offsets to the OffsetTable for each font
 				 * from the beginning of the file */
   public:
   DEFINE_SIZE_ARRAY (12, table);
 };
 
@@ -182,17 +182,17 @@ struct TTCHeader
     default:return_trace (true);
     }
   }
 
   protected:
   union {
   struct {
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
-  FixedVersion	version;	/* Version of the TTC Header (1.0 or 2.0),
+  FixedVersion<>version;	/* Version of the TTC Header (1.0 or 2.0),
 				 * 0x00010000u or 0x00020000u */
   }			header;
   TTCHeaderVersion1	version1;
   } u;
 };
 
 
 /*
--- a/gfx/harfbuzz/src/hb-open-type-private.hh
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -734,30 +734,31 @@ struct CheckSum : ULONG
   DEFINE_SIZE_STATIC (4);
 };
 
 
 /*
  * Version Numbers
  */
 
+template <typename FixedType=USHORT>
 struct FixedVersion
 {
-  inline uint32_t to_int (void) const { return (major << 16) + minor; }
+  inline uint32_t to_int (void) const { return (major << sizeof(FixedType)) + minor; }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
-  USHORT major;
-  USHORT minor;
+  FixedType major;
+  FixedType minor;
   public:
-  DEFINE_SIZE_STATIC (4);
+  DEFINE_SIZE_STATIC (2 * sizeof(FixedType));
 };
 
 
 
 /*
  * Template subclasses of Offset that do the dereferencing.
  * Use: (base+offset)
  */
--- a/gfx/harfbuzz/src/hb-ot-head-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-head-table.hh
@@ -56,19 +56,19 @@ struct head
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  version.major == 1 &&
 		  magicNumber == 0x5F0F3CF5u);
   }
 
   protected:
-  FixedVersion	version;		/* Version of the head table--currently
+  FixedVersion<>version;		/* Version of the head table--currently
 					 * 0x00010000u for version 1.0. */
-  FixedVersion	fontRevision;		/* Set by font manufacturer. */
+  FixedVersion<>fontRevision;		/* Set by font manufacturer. */
   ULONG		checkSumAdjustment;	/* To compute: set it to 0, sum the
 					 * entire font as ULONG, then store
 					 * 0xB1B0AFBAu - sum. */
   ULONG		magicNumber;		/* Set to 0x5F0F3CF5u. */
   USHORT	flags;			/* Bit 0: Baseline for font at y=0;
 					 * Bit 1: Left sidebearing point at x=0;
 					 * Bit 2: Instructions may depend on point size;
 					 * Bit 3: Force ppem to integer values for all
--- a/gfx/harfbuzz/src/hb-ot-hhea-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hhea-table.hh
@@ -51,17 +51,17 @@ struct _hea
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && likely (version.major == 1));
   }
 
   public:
-  FixedVersion	version;		/* 0x00010000u for version 1.0. */
+  FixedVersion<>version;		/* 0x00010000u for version 1.0. */
   FWORD		ascender;		/* Typographic ascent. */
   FWORD		descender;		/* Typographic descent. */
   FWORD		lineGap;		/* Typographic line gap. */
   UFWORD	advanceMax;		/* Maximum advance width/height value in
 					 * metrics table. */
   FWORD		minLeadingBearing;	/* Minimum left/top sidebearing value in
 					 * metrics table. */
   FWORD		minTrailingBearing;	/* Minimum right/bottom sidebearing value;
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -1165,28 +1165,41 @@ struct Device
 {
 
   inline hb_position_t get_x_delta (hb_font_t *font) const
   { return get_delta (font->x_ppem, font->x_scale); }
 
   inline hb_position_t get_y_delta (hb_font_t *font) const
   { return get_delta (font->y_ppem, font->y_scale); }
 
+  inline unsigned int get_size (void) const
+  {
+    unsigned int f = deltaFormat;
+    if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * USHORT::static_size;
+    return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f)));
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && c->check_range (this, this->get_size ()));
+  }
+
+  private:
+
   inline int get_delta (unsigned int ppem, int scale) const
   {
     if (!ppem) return 0;
 
     int pixels = get_delta_pixels (ppem);
 
     if (!pixels) return 0;
 
     return (int) (pixels * (int64_t) scale / ppem);
   }
-
-
   inline int get_delta_pixels (unsigned int ppem_size) const
   {
     unsigned int f = deltaFormat;
     if (unlikely (f < 1 || f > 3))
       return 0;
 
     if (ppem_size < startSize || ppem_size > endSize)
       return 0;
@@ -1200,29 +1213,16 @@ struct Device
     int delta = bits & mask;
 
     if ((unsigned int) delta >= ((mask + 1) >> 1))
       delta -= mask + 1;
 
     return delta;
   }
 
-  inline unsigned int get_size (void) const
-  {
-    unsigned int f = deltaFormat;
-    if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * USHORT::static_size;
-    return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f)));
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && c->check_range (this, this->get_size ()));
-  }
-
   protected:
   USHORT	startSize;		/* Smallest size to correct--in ppem */
   USHORT	endSize;		/* Largest size to correct--in ppem */
   USHORT	deltaFormat;		/* Format of DeltaValue array data: 1, 2, or 3
 					 * 1	Signed 2-bit value, 8 values per uint16
 					 * 2	Signed 4-bit value, 4 values per uint16
 					 * 3	Signed 8-bit value, 2 values per uint16
 					 */
--- a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
@@ -404,17 +404,17 @@ struct GDEF
     case MarkGlyph:
 	  klass = get_mark_attachment_type (glyph);
 	  return HB_OT_LAYOUT_GLYPH_PROPS_MARK | (klass << 8);
     }
   }
 
 
   protected:
-  FixedVersion	version;		/* Version of the GDEF table--currently
+  FixedVersion<>version;		/* Version of the GDEF table--currently
 					 * 0x00010002u */
   OffsetTo<ClassDef>
 		glyphClassDef;		/* Offset to class definition table
 					 * for glyph type--from beginning of
 					 * GDEF header (may be Null) */
   OffsetTo<AttachList>
 		attachList;		/* Offset to list of glyphs with
 					 * attachment points--from beginning
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -31,18 +31,27 @@
 
 #include "hb-ot-layout-gsubgpos-private.hh"
 
 
 namespace OT {
 
 
 /* buffer **position** var allocations */
-#define attach_lookback() var.u16[0] /* number of glyphs to go back to attach this glyph to its base */
-#define cursive_chain() var.i16[1] /* character to which this connects, may be positive or negative */
+#define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */
+#define attach_type() var.u8[2] /* attachment type */
+/* Note! if attach_chain() is zero, the value of attach_type() is irrelevant. */
+
+enum attach_type_t {
+  ATTACH_TYPE_NONE	= 0X00,
+
+  /* Each attachment should be either a mark or a cursive; can't be both. */
+  ATTACH_TYPE_MARK	= 0X01,
+  ATTACH_TYPE_CURSIVE	= 0X02,
+};
 
 
 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
 
 typedef USHORT Value;
 
 typedef Value ValueRecord[VAR];
 
@@ -420,17 +429,18 @@ struct MarkArray : ArrayOf<MarkRecord>	/
     hb_position_t mark_x, mark_y, base_x, base_y;
 
     mark_anchor.get_anchor (c->font, buffer->cur().codepoint, &mark_x, &mark_y);
     glyph_anchor.get_anchor (c->font, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
 
     hb_glyph_position_t &o = buffer->cur_pos();
     o.x_offset = base_x - mark_x;
     o.y_offset = base_y - mark_y;
-    o.attach_lookback() = buffer->idx - glyph_pos;
+    o.attach_type() = ATTACH_TYPE_MARK;
+    o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
     buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
 
     buffer->idx++;
     return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -902,19 +912,16 @@ struct CursivePosFormat1
     return this+coverage;
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
 
-    /* We don't handle mark glyphs here. */
-    if (unlikely (_hb_glyph_info_is_mark (&buffer->cur()))) return_trace (false);
-
     const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (buffer->cur().codepoint)];
     if (!this_record.exitAnchor) return_trace (false);
 
     hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     if (!skippy_iter.next ()) return_trace (false);
 
     const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint)];
@@ -988,18 +995,19 @@ struct CursivePosFormat1
 
     /* If child was already connected to someone else, walk through its old
      * chain and reverse the link direction, such that the whole tree of its
      * previous connection now attaches to new parent.  Watch out for case
      * where new parent is on the path from old chain...
      */
     reverse_cursive_minor_offset (pos, child, c->direction, parent);
 
-    pos[child].cursive_chain() = parent - child;
-    buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_CURSIVE;
+    pos[child].attach_type() = ATTACH_TYPE_CURSIVE;
+    pos[child].attach_chain() = (int) parent - (int) child;
+    buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
     if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
       pos[child].y_offset = y_offset;
     else
       pos[child].x_offset = x_offset;
 
     buffer->idx = j;
     return_trace (true);
   }
@@ -1064,29 +1072,29 @@ struct MarkBasePosFormat1
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
     if (likely (mark_index == NOT_COVERED)) return_trace (false);
 
-    /* now we search backwards for a non-mark glyph */
+    /* Now we search backwards for a non-mark glyph */
     hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
     do {
       if (!skippy_iter.prev ()) return_trace (false);
       /* We only want to attach to the first of a MultipleSubst sequence.  Reject others. */
       if (0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx])) break;
       skippy_iter.reject ();
     } while (1);
 
     /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
-    if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { /*return_trace (false);*/ }
+    //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
 
     unsigned int base_index = (this+baseCoverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint);
     if (base_index == NOT_COVERED) return_trace (false);
 
     return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -1165,24 +1173,24 @@ struct MarkLigPosFormat1
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
     if (likely (mark_index == NOT_COVERED)) return_trace (false);
 
-    /* now we search backwards for a non-mark glyph */
+    /* Now we search backwards for a non-mark glyph */
     hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
     if (!skippy_iter.prev ()) return_trace (false);
 
     /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
-    if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { /*return_trace (false);*/ }
+    //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); }
 
     unsigned int j = skippy_iter.idx;
     unsigned int lig_index = (this+ligatureCoverage).get_coverage  (buffer->info[j].codepoint);
     if (lig_index == NOT_COVERED) return_trace (false);
 
     const LigatureArray& lig_array = this+ligatureArray;
     const LigatureAttach& lig_attach = lig_array[lig_index];
 
@@ -1496,124 +1504,126 @@ typedef OffsetListOf<PosLookup> PosLooku
 struct GPOS : GSUBGPOS
 {
   static const hb_tag_t tableTag	= HB_OT_TAG_GPOS;
 
   inline const PosLookup& get_lookup (unsigned int i) const
   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
 
   static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
-  static inline void position_finish (hb_font_t *font, hb_buffer_t *buffer);
+  static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer);
+  static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer);
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!GSUBGPOS::sanitize (c))) return_trace (false);
     const OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
     return_trace (list.sanitize (c, this));
   }
   public:
   DEFINE_SIZE_STATIC (10);
 };
 
 
 static void
 reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent)
 {
-  unsigned int j = pos[i].cursive_chain();
-  if (likely (!j))
+  int chain = pos[i].attach_chain(), type = pos[i].attach_type();
+  if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE)))
     return;
 
-  j += i;
+  pos[i].attach_chain() = 0;
 
-  pos[i].cursive_chain() = 0;
+  unsigned int j = (int) i + chain;
 
   /* Stop if we see new parent in the chain. */
   if (j == new_parent)
     return;
 
   reverse_cursive_minor_offset (pos, j, direction, new_parent);
 
   if (HB_DIRECTION_IS_HORIZONTAL (direction))
     pos[j].y_offset = -pos[i].y_offset;
   else
     pos[j].x_offset = -pos[i].x_offset;
 
-  pos[j].cursive_chain() = i - j;
+  pos[j].attach_chain() = -chain;
+  pos[j].attach_type() = type;
 }
 static void
-fix_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction)
+propagate_attachment_offsets (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction)
 {
-  unsigned int j = pos[i].cursive_chain();
-  if (likely (!j))
+  /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate
+   * offset of glyph they are attached to. */
+  int chain = pos[i].attach_chain(), type = pos[i].attach_type();
+  if (likely (!chain))
     return;
 
-  j += i;
+  unsigned int j = (int) i + chain;
 
-  pos[i].cursive_chain() = 0;
-
-  fix_cursive_minor_offset (pos, j, direction);
+  pos[i].attach_chain() = 0;
 
-  if (HB_DIRECTION_IS_HORIZONTAL (direction))
-    pos[i].y_offset += pos[j].y_offset;
-  else
-    pos[i].x_offset += pos[j].x_offset;
-}
+  propagate_attachment_offsets (pos, j, direction);
+
+  assert (!!(type & ATTACH_TYPE_MARK) ^ !!(type & ATTACH_TYPE_CURSIVE));
 
-static void
-fix_mark_attachment (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction)
-{
-  if (likely (!(pos[i].attach_lookback())))
-    return;
-
-  unsigned int j = i - pos[i].attach_lookback();
-
-  pos[i].x_offset += pos[j].x_offset;
-  pos[i].y_offset += pos[j].y_offset;
+  if (type & ATTACH_TYPE_CURSIVE)
+  {
+    if (HB_DIRECTION_IS_HORIZONTAL (direction))
+      pos[i].y_offset += pos[j].y_offset;
+    else
+      pos[i].x_offset += pos[j].x_offset;
+  }
+  else /*if (type & ATTACH_TYPE_MARK)*/
+  {
+    pos[i].x_offset += pos[j].x_offset;
+    pos[i].y_offset += pos[j].y_offset;
 
-  if (HB_DIRECTION_IS_FORWARD (direction))
-    for (unsigned int k = j; k < i; k++) {
-      pos[i].x_offset -= pos[k].x_advance;
-      pos[i].y_offset -= pos[k].y_advance;
-    }
-  else
-    for (unsigned int k = j + 1; k < i + 1; k++) {
-      pos[i].x_offset += pos[k].x_advance;
-      pos[i].y_offset += pos[k].y_advance;
-    }
+    assert (j < i);
+    if (HB_DIRECTION_IS_FORWARD (direction))
+      for (unsigned int k = j; k < i; k++) {
+	pos[i].x_offset -= pos[k].x_advance;
+	pos[i].y_offset -= pos[k].y_advance;
+      }
+    else
+      for (unsigned int k = j + 1; k < i + 1; k++) {
+	pos[i].x_offset += pos[k].x_advance;
+	pos[i].y_offset += pos[k].y_advance;
+      }
+  }
 }
 
 void
 GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
 {
-  buffer->clear_positions ();
-
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
-    buffer->pos[i].attach_lookback() = buffer->pos[i].cursive_chain() = 0;
+    buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0;
 }
 
 void
-GPOS::position_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
+GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
+{
+  //_hb_buffer_assert_gsubgpos_vars (buffer);
+}
+
+void
+GPOS::position_finish_offsets (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
 {
   _hb_buffer_assert_gsubgpos_vars (buffer);
 
   unsigned int len;
   hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
   hb_direction_t direction = buffer->props.direction;
 
-  /* Handle cursive connections */
-  if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_CURSIVE)
-    for (unsigned int i = 0; i < len; i++)
-      fix_cursive_minor_offset (pos, i, direction);
-
   /* Handle attachments */
   if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT)
     for (unsigned int i = 0; i < len; i++)
-      fix_mark_attachment (pos, i, direction);
+      propagate_attachment_offsets (pos, i, direction);
 }
 
 
 /* Out-of-class implementation for methods recursing */
 
 template <typename context_t>
 /*static*/ inline typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
 {
@@ -1632,16 +1642,16 @@ template <typename context_t>
   c->set_lookup_props (l.get_props ());
   bool ret = l.dispatch (c);
   c->set_lookup_index (saved_lookup_index);
   c->set_lookup_props (saved_lookup_props);
   return ret;
 }
 
 
-#undef attach_lookback
-#undef cursive_chain
+#undef attach_chain
+#undef attach_type
 
 
 } /* namespace OT */
 
 
 #endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -1263,17 +1263,16 @@ typedef OffsetListOf<SubstLookup> SubstL
 struct GSUB : GSUBGPOS
 {
   static const hb_tag_t tableTag	= HB_OT_TAG_GSUB;
 
   inline const SubstLookup& get_lookup (unsigned int i) const
   { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
 
   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 bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!GSUBGPOS::sanitize (c))) return_trace (false);
     const OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
     return_trace (list.sanitize (c, this));
   }
@@ -1292,21 +1291,16 @@ GSUB::substitute_start (hb_font_t *font,
   for (unsigned int i = 0; i < count; i++)
   {
     _hb_glyph_info_set_glyph_props (&buffer->info[i], gdef.get_glyph_props (buffer->info[i].codepoint));
     _hb_glyph_info_clear_lig_props (&buffer->info[i]);
     buffer->info[i].syllable() = 0;
   }
 }
 
-void
-GSUB::substitute_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED)
-{
-}
-
 
 /* Out-of-class implementation for methods recursing */
 
 /*static*/ inline bool ExtensionSubst::is_reverse (void) const
 {
   unsigned int type = get_type ();
   if (unlikely (type == SubstLookupSubTable::Extension))
     return CastR<ExtensionSubst> (get_subtable<LookupSubTable>()).is_reverse ();
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -966,17 +966,17 @@ static inline bool apply_lookup (hb_appl
     end = bl + match_length;
 
     int delta = bl - buffer->idx;
     /* Convert positions to new indexing. */
     for (unsigned int j = 0; j < count; j++)
       match_positions[j] += delta;
   }
 
-  for (unsigned int i = 0; i < lookupCount; i++)
+  for (unsigned int i = 0; i < lookupCount && !buffer->in_error; i++)
   {
     unsigned int idx = lookupRecord[i].sequenceIndex;
     if (idx >= count)
       continue;
 
     /* Don't recurse to ourself at same position.
      * Note that this test is too naive, it doesn't catch longer loops. */
     if (idx == 0 && lookupRecord[i].lookupListIndex == c->lookup_index)
@@ -2272,17 +2272,17 @@ struct GSUBGPOS
     return_trace (version.sanitize (c) &&
 		  likely (version.major == 1) &&
 		  scriptList.sanitize (c, this) &&
 		  featureList.sanitize (c, this) &&
 		  lookupList.sanitize (c, this));
   }
 
   protected:
-  FixedVersion	version;	/* Version of the GSUB/GPOS table--initially set
+  FixedVersion<>version;	/* Version of the GSUB/GPOS table--initially set
 				 * to 0x00010000u */
   OffsetTo<ScriptList>
 		scriptList;  	/* ScriptList table */
   OffsetTo<FeatureList>
 		featureList; 	/* FeatureList table */
   OffsetTo<LookupList>
 		lookupList; 	/* LookupList table */
   public:
--- a/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh
@@ -213,17 +213,17 @@ struct JSTF
   {
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
 		  likely (version.major == 1) &&
 		  scriptList.sanitize (c, this));
   }
 
   protected:
-  FixedVersion	version;	/* Version of the JSTF table--initially set
+  FixedVersion<>version;	/* Version of the JSTF table--initially set
 				 * to 0x00010000u */
   RecordArrayOf<JstfScript>
 		scriptList;  	/* Array of JstfScripts--listed
 				 * alphabetically by ScriptTag */
   public:
   DEFINE_SIZE_ARRAY (6, scriptList);
 };
 
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -94,31 +94,30 @@ namespace OT {
 }
 
 HB_INTERNAL void
 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
 				const OT::SubstLookup &lookup,
 				const hb_ot_layout_lookup_accelerator_t &accel);
 
 
-/* Should be called after all the substitute_lookup's are done */
-HB_INTERNAL void
-hb_ot_layout_substitute_finish (hb_font_t    *font,
-				hb_buffer_t  *buffer);
-
-
-/* Should be called before all the position_lookup's are done.  Resets positions to zero. */
+/* Should be called before all the position_lookup's are done. */
 HB_INTERNAL void
 hb_ot_layout_position_start (hb_font_t    *font,
 			     hb_buffer_t  *buffer);
 
-/* Should be called after all the position_lookup's are done */
+/* Should be called after all the position_lookup's are done, to finish advances. */
 HB_INTERNAL void
-hb_ot_layout_position_finish (hb_font_t    *font,
-			      hb_buffer_t  *buffer);
+hb_ot_layout_position_finish_advances (hb_font_t    *font,
+				       hb_buffer_t  *buffer);
+
+/* Should be called after hb_ot_layout_position_finish_advances, to finish offsets. */
+HB_INTERNAL void
+hb_ot_layout_position_finish_offsets (hb_font_t    *font,
+				      hb_buffer_t  *buffer);
 
 
 
 /*
  * hb_ot_layout_t
  */
 
 namespace OT {
@@ -252,32 +251,45 @@ static inline void
     buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII;
     if (unlikely (unicode->is_default_ignorable (u)))
     {
       buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES;
       props |=  UPROPS_MASK_IGNORABLE;
       if (u == 0x200Cu) props |= UPROPS_MASK_ZWNJ;
       if (u == 0x200Du) props |= UPROPS_MASK_ZWJ;
     }
-    else if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_NON_ENCLOSING_MARK (gen_cat)))
+    else if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_NON_ENCLOSING_MARK_OR_MODIFIER_SYMBOL (gen_cat)))
     {
+      /* The above check is just an optimization to let in only things we need further
+       * processing on. */
+
       /* Only Mn and Mc can have non-zero ccc:
        * http://www.unicode.org/policies/stability_policy.html#Property_Value
        * """
        * Canonical_Combining_Class, General_Category
        * All characters other than those with General_Category property values
        * Spacing_Mark (Mc) and Nonspacing_Mark (Mn) have the Canonical_Combining_Class
        * property value 0.
        * 1.1.5+
        * """
        *
        * Also, all Mn's that are Default_Ignorable, have ccc=0, hence
        * the "else if".
        */
       props |= unicode->modified_combining_class (info->codepoint)<<8;
+
+      /* Recategorize emoji skin-tone modifiers as Unicode mark, so they
+       * behave correctly in non-native directionality.  They originally
+       * are MODIFIER_SYMBOL.  Fixes:
+       * https://github.com/behdad/harfbuzz/issues/169
+       */
+      if (unlikely (hb_in_range (u, 0x1F3FBu, 0x1F3FFu)))
+      {
+	props = gen_cat = HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK;
+      }
     }
   }
 
   info->unicode_props() = props;
 }
 
 static inline void
 _hb_glyph_info_set_general_category (hb_glyph_info_t *info,
@@ -348,16 +360,22 @@ static inline hb_bool_t
 }
 
 static inline hb_bool_t
 _hb_glyph_info_is_zwj (const hb_glyph_info_t *info)
 {
   return !!(info->unicode_props() & UPROPS_MASK_ZWJ);
 }
 
+static inline hb_bool_t
+_hb_glyph_info_is_joiner (const hb_glyph_info_t *info)
+{
+  return !!(info->unicode_props() & (UPROPS_MASK_ZWNJ | UPROPS_MASK_ZWJ));
+}
+
 static inline void
 _hb_glyph_info_flip_joiners (hb_glyph_info_t *info)
 {
   info->unicode_props() ^= UPROPS_MASK_ZWNJ | UPROPS_MASK_ZWJ;
 }
 
 /* lig_props: aka lig_id / lig_comp
  *
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -766,22 +766,16 @@ hb_ot_layout_lookup_would_substitute_fas
 }
 
 void
 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
 {
   OT::GSUB::substitute_start (font, buffer);
 }
 
-void
-hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
-{
-  OT::GSUB::substitute_finish (font, buffer);
-}
-
 /**
  * hb_ot_layout_lookup_substitute_closure:
  *
  * Since: 0.9.7
  **/
 void
 hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
 				        unsigned int  lookup_index,
@@ -806,19 +800,25 @@ hb_ot_layout_has_positioning (hb_face_t 
 
 void
 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
 {
   OT::GPOS::position_start (font, buffer);
 }
 
 void
-hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
+hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer)
 {
-  OT::GPOS::position_finish (font, buffer);
+  OT::GPOS::position_finish_advances (font, buffer);
+}
+
+void
+hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
+{
+  OT::GPOS::position_finish_offsets (font, buffer);
 }
 
 /**
  * hb_ot_layout_get_size_params:
  *
  * Since: 0.9.10
  **/
 hb_bool_t
@@ -897,124 +897,166 @@ struct GPOSProxy
     table (*hb_ot_layout_from_face (face)->gpos),
     accels (hb_ot_layout_from_face (face)->gpos_accels) {}
 
   const OT::GPOS &table;
   const hb_ot_layout_lookup_accelerator_t *accels;
 };
 
 
-template <typename Obj>
+struct hb_get_subtables_context_t :
+       OT::hb_dispatch_context_t<hb_get_subtables_context_t, hb_void_t, HB_DEBUG_APPLY>
+{
+  template <typename Type>
+  static inline bool apply_to (const void *obj, OT::hb_apply_context_t *c)
+  {
+    const Type *typed_obj = (const Type *) obj;
+    return typed_obj->apply (c);
+  }
+
+  typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_apply_context_t *c);
+
+  struct hb_applicable_t
+  {
+    inline void init (const void *obj_, hb_apply_func_t apply_func_)
+    {
+      obj = obj_;
+      apply_func = apply_func_;
+    }
+
+    inline bool apply (OT::hb_apply_context_t *c) const { return apply_func (obj, c); }
+
+    private:
+    const void *obj;
+    hb_apply_func_t apply_func;
+  };
+
+  typedef hb_auto_array_t<hb_applicable_t> array_t;
+
+  /* Dispatch interface. */
+  inline const char *get_name (void) { return "GET_SUBTABLES"; }
+  template <typename T>
+  inline return_t dispatch (const T &obj)
+  {
+    hb_applicable_t *entry = array.push();
+    if (likely (entry))
+      entry->init (&obj, apply_to<T>);
+    return HB_VOID;
+  }
+  static return_t default_return_value (void) { return HB_VOID; }
+  bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; }
+
+  hb_get_subtables_context_t (array_t &array_) :
+			      array (array_),
+			      debug_depth (0) {}
+
+  array_t &array;
+  unsigned int debug_depth;
+};
+
 static inline bool
 apply_forward (OT::hb_apply_context_t *c,
-	       const Obj &obj,
-	       const hb_ot_layout_lookup_accelerator_t &accel)
+	       const hb_ot_layout_lookup_accelerator_t &accel,
+	       const hb_get_subtables_context_t::array_t &subtables)
 {
   bool ret = false;
   hb_buffer_t *buffer = c->buffer;
   while (buffer->idx < buffer->len && !buffer->in_error)
   {
+    bool applied = false;
     if (accel.may_have (buffer->cur().codepoint) &&
 	(buffer->cur().mask & c->lookup_mask) &&
-	c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
-	obj.apply (c))
+	c->check_glyph_property (&buffer->cur(), c->lookup_props))
+     {
+       for (unsigned int i = 0; i < subtables.len; i++)
+         if (subtables[i].apply (c))
+	 {
+	   applied = true;
+	   break;
+	 }
+     }
+
+    if (applied)
       ret = true;
     else
       buffer->next_glyph ();
   }
   return ret;
 }
 
-template <typename Obj>
 static inline bool
 apply_backward (OT::hb_apply_context_t *c,
-		const Obj &obj,
-		const hb_ot_layout_lookup_accelerator_t &accel)
+	       const hb_ot_layout_lookup_accelerator_t &accel,
+	       const hb_get_subtables_context_t::array_t &subtables)
 {
   bool ret = false;
   hb_buffer_t *buffer = c->buffer;
   do
   {
     if (accel.may_have (buffer->cur().codepoint) &&
 	(buffer->cur().mask & c->lookup_mask) &&
-	c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
-	obj.apply (c))
-      ret = true;
+	c->check_glyph_property (&buffer->cur(), c->lookup_props))
+    {
+     for (unsigned int i = 0; i < subtables.len; i++)
+       if (subtables[i].apply (c))
+       {
+	 ret = true;
+	 break;
+       }
+    }
     /* The reverse lookup doesn't "advance" cursor (for good reason). */
     buffer->idx--;
 
   }
   while ((int) buffer->idx >= 0);
   return ret;
 }
 
-struct hb_apply_forward_context_t :
-       OT::hb_dispatch_context_t<hb_apply_forward_context_t, bool, HB_DEBUG_APPLY>
-{
-  inline const char *get_name (void) { return "APPLY_FWD"; }
-  template <typename T>
-  inline return_t dispatch (const T &obj) { return apply_forward (c, obj, accel); }
-  static return_t default_return_value (void) { return false; }
-  bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return true; }
-
-  hb_apply_forward_context_t (OT::hb_apply_context_t *c_,
-			      const hb_ot_layout_lookup_accelerator_t &accel_) :
-				c (c_),
-				accel (accel_),
-				debug_depth (0) {}
-
-  OT::hb_apply_context_t *c;
-  const hb_ot_layout_lookup_accelerator_t &accel;
-  unsigned int debug_depth;
-};
-
 template <typename Proxy>
 static inline void
 apply_string (OT::hb_apply_context_t *c,
 	      const typename Proxy::Lookup &lookup,
 	      const hb_ot_layout_lookup_accelerator_t &accel)
 {
   hb_buffer_t *buffer = c->buffer;
 
   if (unlikely (!buffer->len || !c->lookup_mask))
     return;
 
   c->set_lookup_props (lookup.get_props ());
 
+  hb_get_subtables_context_t::array_t subtables;
+  hb_get_subtables_context_t c_get_subtables (subtables);
+  lookup.dispatch (&c_get_subtables);
+
   if (likely (!lookup.is_reverse ()))
   {
     /* in/out forward substitution/positioning */
     if (Proxy::table_index == 0)
       buffer->clear_output ();
     buffer->idx = 0;
 
     bool ret;
-    if (lookup.get_subtable_count () == 1)
-    {
-      hb_apply_forward_context_t c_forward (c, accel);
-      ret = lookup.dispatch (&c_forward);
-    }
-    else
-      ret = apply_forward (c, lookup, accel);
+    ret = apply_forward (c, accel, subtables);
     if (ret)
     {
       if (!Proxy::inplace)
 	buffer->swap_buffers ();
       else
 	assert (!buffer->has_separate_output ());
     }
   }
   else
   {
     /* in-place backward substitution/positioning */
     if (Proxy::table_index == 0)
       buffer->remove_output ();
     buffer->idx = buffer->len - 1;
 
-    apply_backward (c, lookup, accel);
+    apply_backward (c, accel, subtables);
   }
 }
 
 template <typename Proxy>
 inline void hb_ot_map_t::apply (const Proxy &proxy,
 				const hb_ot_shape_plan_t *plan,
 				hb_font_t *font,
 				hb_buffer_t *buffer) const
--- a/gfx/harfbuzz/src/hb-ot-maxp-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-maxp-table.hh
@@ -53,17 +53,17 @@ struct maxp
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  likely (version.major == 1 ||
 			  (version.major == 0 && version.minor == 0x5000u)));
   }
 
   /* We only implement version 0.5 as none of the extra fields in version 1.0 are useful. */
   protected:
-  FixedVersion	version;		/* Version of the maxp table (0.5 or 1.0),
+  FixedVersion<>version;		/* Version of the maxp table (0.5 or 1.0),
 					 * 0x00005000u or 0x00010000u. */
   USHORT	numGlyphs;		/* The number of glyphs in the font. */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 
 } /* namespace OT */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-default.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-default.cc
@@ -35,11 +35,11 @@ const hb_ot_complex_shaper_t _hb_ot_comp
   NULL, /* data_create */
   NULL, /* data_destroy */
   NULL, /* preprocess_text */
   NULL, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
   NULL, /* decompose */
   NULL, /* compose */
   NULL, /* setup_masks */
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -1241,17 +1241,17 @@ insert_dotted_circles (const hb_ot_shape
 
       hb_glyph_info_t ginfo = dottedcircle;
       ginfo.cluster = buffer->cur().cluster;
       ginfo.mask = buffer->cur().mask;
       ginfo.syllable() = buffer->cur().syllable();
       /* TODO Set glyph_props? */
 
       /* Insert dottedcircle after possible Repha. */
-      while (buffer->idx < buffer->len &&
+      while (buffer->idx < buffer->len && !buffer->in_error &&
 	     last_syllable == buffer->cur().syllable() &&
 	     buffer->cur().indic_category() == OT_Repha)
         buffer->next_glyph ();
 
       buffer->output_info (ginfo);
     }
     else
       buffer->next_glyph ();
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
@@ -446,17 +446,17 @@ insert_dotted_circles (const hb_ot_shape
   dottedcircle.codepoint = 0x25CCu;
   set_myanmar_properties (dottedcircle);
   dottedcircle.codepoint = dottedcircle_glyph;
 
   buffer->clear_output ();
 
   buffer->idx = 0;
   unsigned int last_syllable = 0;
-  while (buffer->idx < buffer->len)
+  while (buffer->idx < buffer->len && !buffer->in_error)
   {
     unsigned int syllable = buffer->cur().syllable();
     syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
     if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
     {
       last_syllable = syllable;
 
       hb_glyph_info_t ginfo = dottedcircle;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
@@ -36,18 +36,16 @@
 
 /* buffer var allocations, used by complex shapers */
 #define complex_var_u8_0()	var2.u8[2]
 #define complex_var_u8_1()	var2.u8[3]
 
 
 enum hb_ot_shape_zero_width_marks_type_t {
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
-//  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY,
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE,
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE
 };
 
 
 /* Master OT shaper list */
 #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
   HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
@@ -372,11 +372,11 @@ const hb_ot_complex_shaper_t _hb_ot_comp
   NULL, /* data_create */
   NULL, /* data_destroy */
   preprocess_text_thai,
   NULL, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
   NULL, /* decompose */
   NULL, /* compose */
   NULL, /* setup_masks */
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   false,/* fallback_position */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-tibetan.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-tibetan.cc
@@ -52,11 +52,11 @@ const hb_ot_complex_shaper_t _hb_ot_comp
   NULL, /* data_create */
   NULL, /* data_destroy */
   NULL, /* preprocess_text */
   NULL, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
   NULL, /* decompose */
   NULL, /* compose */
   NULL, /* setup_masks */
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
@@ -517,17 +517,17 @@ insert_dotted_circles (const hb_ot_shape
 
       hb_glyph_info_t ginfo = dottedcircle;
       ginfo.cluster = buffer->cur().cluster;
       ginfo.mask = buffer->cur().mask;
       ginfo.syllable() = buffer->cur().syllable();
       /* TODO Set glyph_props? */
 
       /* Insert dottedcircle after possible Repha. */
-      while (buffer->idx < buffer->len &&
+      while (buffer->idx < buffer->len && !buffer->in_error &&
 	     last_syllable == buffer->cur().syllable() &&
 	     buffer->cur().use_category() == USE_R)
         buffer->next_glyph ();
 
       buffer->output_info (ginfo);
     }
     else
       buffer->next_glyph ();
@@ -578,11 +578,11 @@ const hb_ot_complex_shaper_t _hb_ot_comp
   data_create_use,
   data_destroy_use,
   NULL, /* preprocess_text */
   NULL, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   NULL, /* decompose */
   compose_use,
   setup_masks_use,
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
   false, /* fallback_position */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -140,17 +140,17 @@ void
 
 /*
  * shaper font data
  */
 
 struct hb_ot_shaper_font_data_t {};
 
 hb_ot_shaper_font_data_t *
-_hb_ot_shaper_font_data_create (hb_font_t *font)
+_hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED)
 {
   return (hb_ot_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
 _hb_ot_shaper_font_data_destroy (hb_ot_shaper_font_data_t *data)
 {
 }
@@ -262,23 +262,24 @@ hb_insert_dotted_circle (hb_buffer_t *bu
 
 static void
 hb_form_clusters (hb_buffer_t *buffer)
 {
   if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) ||
       buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
     return;
 
-  /* Loop duplicated in hb_ensure_native_direction(). */
+  /* Loop duplicated in hb_ensure_native_direction(), and in _hb-coretext.cc */
   unsigned int base = 0;
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 1; i < count; i++)
   {
-    if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))))
+    if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])) &&
+		!_hb_glyph_info_is_joiner (&info[i])))
     {
       buffer->merge_clusters (base, i);
       base = i;
     }
   }
   buffer->merge_clusters (base, count);
 }
 
@@ -579,18 +580,16 @@ hb_ot_substitute_complex (hb_ot_shape_co
 
   hb_ot_layout_substitute_start (c->font, buffer);
 
   if (!hb_ot_layout_has_glyph_classes (c->face))
     hb_synthesize_glyph_classes (c);
 
   c->plan->substitute (c->font, buffer);
 
-  hb_ot_layout_substitute_finish (c->font, buffer);
-
   return;
 }
 
 static inline void
 hb_ot_substitute (hb_ot_shape_context_t *c)
 {
   hb_ot_substitute_default (c);
 
@@ -611,39 +610,18 @@ adjust_mark_offsets (hb_glyph_position_t
 static inline void
 zero_mark_width (hb_glyph_position_t *pos)
 {
   pos->x_advance = 0;
   pos->y_advance = 0;
 }
 
 static inline void
-zero_mark_widths_by_unicode (hb_buffer_t *buffer, bool adjust_offsets)
-{
-  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII))
-    return;
-
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  for (unsigned int i = 0; i < count; i++)
-    if (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
-    {
-      if (adjust_offsets)
-        adjust_mark_offsets (&buffer->pos[i]);
-      zero_mark_width (&buffer->pos[i]);
-    }
-}
-
-static inline void
 zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets)
 {
-  /* This one is a hack; Technically GDEF can mark ASCII glyphs as marks, but we don't listen. */
-  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII))
-    return;
-
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
     if (_hb_glyph_info_is_mark (&info[i]))
     {
       if (adjust_offsets)
         adjust_mark_offsets (&buffer->pos[i]);
       zero_mark_width (&buffer->pos[i]);
@@ -681,19 +659,22 @@ hb_ot_position_default (hb_ot_shape_cont
   }
   if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK)
     _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer);
 }
 
 static inline bool
 hb_ot_position_complex (hb_ot_shape_context_t *c)
 {
+  hb_ot_layout_position_start (c->font, c->buffer);
+
   bool ret = false;
   unsigned int count = c->buffer->len;
   bool has_positioning = (bool) hb_ot_layout_has_positioning (c->face);
+
   /* If the font has no GPOS, AND, no fallback positioning will
    * happen, AND, direction is forward, then when zeroing mark
    * widths, we shift the mark with it, such that the mark
    * is positioned hanging over the previous glyph.  When
    * direction is backward we don't shift and it will end up
    * hanging over the next glyph after the final reordering.
    * If fallback positinoing happens or GPOS is present, we don't
    * care.
@@ -702,25 +683,18 @@ hb_ot_position_complex (hb_ot_shape_cont
                                        HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction));
 
   switch (c->plan->shaper->zero_width_marks)
   {
     case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
       zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
       break;
 
-    /* Not currently used for any shaper:
-    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY:
-      zero_mark_widths_by_unicode (c->buffer, adjust_offsets_when_zeroing);
-      break;
-    */
-
     default:
     case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
-    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE:
     case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
       break;
   }
 
   if (has_positioning)
   {
     hb_glyph_info_t *info = c->buffer->info;
     hb_glyph_position_t *pos = c->buffer->pos;
@@ -743,47 +717,43 @@ hb_ot_position_complex (hb_ot_shape_cont
 					  &pos[i].x_offset,
 					  &pos[i].y_offset);
 
     ret = true;
   }
 
   switch (c->plan->shaper->zero_width_marks)
   {
-    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE:
-      zero_mark_widths_by_unicode (c->buffer, adjust_offsets_when_zeroing);
-      break;
-
     case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
       zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
       break;
 
     default:
     case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
-    //case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY:
     case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
       break;
   }
 
+  /* Finishing off GPOS has to follow a certain order. */
+  hb_ot_layout_position_finish_advances (c->font, c->buffer);
+  hb_ot_zero_width_default_ignorables (c);
+  hb_ot_layout_position_finish_offsets (c->font, c->buffer);
+
   return ret;
 }
 
 static inline void
 hb_ot_position (hb_ot_shape_context_t *c)
 {
-  hb_ot_layout_position_start (c->font, c->buffer);
+  c->buffer->clear_positions ();
 
   hb_ot_position_default (c);
 
   hb_bool_t fallback = !hb_ot_position_complex (c);
 
-  hb_ot_zero_width_default_ignorables (c);
-
-  hb_ot_layout_position_finish (c->font, c->buffer);
-
   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. */
 
--- a/gfx/harfbuzz/src/hb-ot-tag.cc
+++ b/gfx/harfbuzz/src/hb-ot-tag.cc
@@ -922,32 +922,32 @@ hb_ot_tag_to_language (hb_tag_t tag)
     buf[9] = tag & 0xFF;
     if (buf[9] == 0x20)
       buf[9] = '\0';
     buf[10] = '\0';
     return hb_language_from_string ((char *) buf, -1);
   }
 }
 
+#ifdef MAIN
 static inline void
 test_langs_sorted (void)
 {
   for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages); i++)
   {
     int c = lang_compare_first_component (ot_languages[i-1].language, ot_languages[i].language);
     if (c >= 0)
     {
       fprintf (stderr, "ot_languages not sorted at index %d: %s %d %s\n",
 	       i, ot_languages[i-1].language, c, ot_languages[i].language);
       abort();
     }
   }
 }
 
-#ifdef MAIN
 int
 main (void)
 {
   test_langs_sorted ();
   return 0;
 }
 
 #endif
--- a/gfx/harfbuzz/src/hb-unicode-private.hh
+++ b/gfx/harfbuzz/src/hb-unicode-private.hh
@@ -352,14 +352,15 @@ extern HB_INTERNAL const hb_unicode_func
 /* Misc */
 
 #define HB_UNICODE_GENERAL_CATEGORY_IS_MARK(gen_cat) \
 	(FLAG_SAFE (gen_cat) & \
 	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \
 	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \
 	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
 
-#define HB_UNICODE_GENERAL_CATEGORY_IS_NON_ENCLOSING_MARK(gen_cat) \
+#define HB_UNICODE_GENERAL_CATEGORY_IS_NON_ENCLOSING_MARK_OR_MODIFIER_SYMBOL(gen_cat) \
 	(FLAG_SAFE (gen_cat) & \
 	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \
-	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | \
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL)))
 
 #endif /* HB_UNICODE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-version.h
+++ b/gfx/harfbuzz/src/hb-version.h
@@ -32,20 +32,20 @@
 #define HB_VERSION_H
 
 #include "hb-common.h"
 
 HB_BEGIN_DECLS
 
 
 #define HB_VERSION_MAJOR 1
-#define HB_VERSION_MINOR 1
-#define HB_VERSION_MICRO 3
+#define HB_VERSION_MINOR 2
+#define HB_VERSION_MICRO 2
 
-#define HB_VERSION_STRING "1.1.3"
+#define HB_VERSION_STRING "1.2.2"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
 
 
 HB_EXTERN void
 hb_version (unsigned int *major,