Bug 1476334 - Update HarfBuzz to version 1.8.7. r=jfkthame
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 09 Aug 2018 09:07:27 -0400
changeset 430840 b0cdb1cc6d65a2e3c02aa6fe2ea84258121ecc04
parent 430839 43fd4a3dbdc7bdc9e5bcc1a30e71ffee0d85545c
child 430841 25a65f1051c29d44aca7ecfe6af6bfada92a912f
child 430867 185d367f1916d83ba4d0ff19f45437f5c47a212e
push id34412
push usernerli@mozilla.com
push dateThu, 09 Aug 2018 21:31:30 +0000
treeherdermozilla-central@25a65f1051c2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1476334
milestone63.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 1476334 - Update HarfBuzz to version 1.8.7. r=jfkthame
gfx/harfbuzz/NEWS
gfx/harfbuzz/README-mozilla
gfx/harfbuzz/configure.ac
gfx/harfbuzz/src/Makefile.am
gfx/harfbuzz/src/Makefile.sources
gfx/harfbuzz/src/dump-emoji.cc
gfx/harfbuzz/src/dump-fon.cc
gfx/harfbuzz/src/gen-use-table.py
gfx/harfbuzz/src/hb-aat-fmtx-table.hh
gfx/harfbuzz/src/hb-aat-gcid-table.hh
gfx/harfbuzz/src/hb-aat-layout-common-private.hh
gfx/harfbuzz/src/hb-aat-layout.cc
gfx/harfbuzz/src/hb-atomic-private.hh
gfx/harfbuzz/src/hb-blob-private.hh
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-debug.hh
gfx/harfbuzz/src/hb-directwrite.cc
gfx/harfbuzz/src/hb-dsalgs.hh
gfx/harfbuzz/src/hb-face-private.hh
gfx/harfbuzz/src/hb-face.cc
gfx/harfbuzz/src/hb-face.h
gfx/harfbuzz/src/hb-fallback-shape.cc
gfx/harfbuzz/src/hb-font-private.hh
gfx/harfbuzz/src/hb-font.cc
gfx/harfbuzz/src/hb-font.h
gfx/harfbuzz/src/hb-ft.cc
gfx/harfbuzz/src/hb-gobject-structs.cc
gfx/harfbuzz/src/hb-gobject-structs.h
gfx/harfbuzz/src/hb-graphite2.cc
gfx/harfbuzz/src/hb-iter-private.hh
gfx/harfbuzz/src/hb-machinery-private.hh
gfx/harfbuzz/src/hb-map.cc
gfx/harfbuzz/src/hb-null.hh
gfx/harfbuzz/src/hb-object-private.hh
gfx/harfbuzz/src/hb-open-file-private.hh
gfx/harfbuzz/src/hb-open-type-private.hh
gfx/harfbuzz/src/hb-ot-cmap-table.hh
gfx/harfbuzz/src/hb-ot-color-cbdt-table.hh
gfx/harfbuzz/src/hb-ot-color-sbix-table.hh
gfx/harfbuzz/src/hb-ot-color-svg-table.hh
gfx/harfbuzz/src/hb-ot-font.cc
gfx/harfbuzz/src/hb-ot-glyf-table.hh
gfx/harfbuzz/src/hb-ot-head-table.hh
gfx/harfbuzz/src/hb-ot-hmtx-table.hh
gfx/harfbuzz/src/hb-ot-kern-table.hh
gfx/harfbuzz/src/hb-ot-layout-base-table.hh
gfx/harfbuzz/src/hb-ot-layout-common-private.hh
gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
gfx/harfbuzz/src/hb-ot-layout-private.hh
gfx/harfbuzz/src/hb-ot-layout.cc
gfx/harfbuzz/src/hb-ot-layout.h
gfx/harfbuzz/src/hb-ot-math-table.hh
gfx/harfbuzz/src/hb-ot-math.cc
gfx/harfbuzz/src/hb-ot-maxp-table.hh
gfx/harfbuzz/src/hb-ot-os2-table.hh
gfx/harfbuzz/src/hb-ot-post-table.hh
gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh
gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc
gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc
gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
gfx/harfbuzz/src/hb-ot-shape.cc
gfx/harfbuzz/src/hb-ot-var-fvar-table.hh
gfx/harfbuzz/src/hb-ot-var.cc
gfx/harfbuzz/src/hb-private.hh
gfx/harfbuzz/src/hb-shape-plan-private.hh
gfx/harfbuzz/src/hb-shape-plan.cc
gfx/harfbuzz/src/hb-shaper-private.hh
gfx/harfbuzz/src/hb-static.cc
gfx/harfbuzz/src/hb-subset-glyf.cc
gfx/harfbuzz/src/hb-subset-plan.cc
gfx/harfbuzz/src/hb-subset.cc
gfx/harfbuzz/src/hb-unicode-private.hh
gfx/harfbuzz/src/hb-unicode.cc
gfx/harfbuzz/src/hb-uniscribe.cc
gfx/harfbuzz/src/hb-vector-private.hh
gfx/harfbuzz/src/hb-version.h
gfx/harfbuzz/src/main.cc
gfx/harfbuzz/update.sh
--- a/gfx/harfbuzz/NEWS
+++ b/gfx/harfbuzz/NEWS
@@ -1,8 +1,44 @@
+Overview of changes leading to 1.8.7
+Wednesday, August 8, 2018
+====================================
+- Fix assertion failure with GDEF-blacklisted fonts.
+
+
+Overview of changes leading to 1.8.6
+Tuesday, August 7, 2018
+====================================
+- Internal code shuffling.
+- New API to speed up getting advance widths for implementations
+  that have heavy overhead in get_h_advance callback:
++hb_font_funcs_set_glyph_h_advances_func
++hb_font_funcs_set_glyph_v_advances_func
++hb_font_get_glyph_advances_for_direction
++hb_font_get_glyph_h_advances
++hb_font_get_glyph_h_advances_func_t
++hb_font_get_glyph_v_advances
++hb_font_get_glyph_v_advances_func_t
+
+
+Overview of changes leading to 1.8.5
+Wednesday, August 1, 2018
+====================================
+- Major Khmer shaper improvements to better match Microsoft.
+- Indic bug fixes.
+- Internal improvements to atomic operations.
+
+
+Overview of changes leading to 1.8.4
+Tuesday, July 17, 2018
+====================================
+- Fix build on non-C++11.
+- Use C++-style GCC atomics and C++11 atomics.
+
+
 Overview of changes leading to 1.8.3
 Wednesday, July 11, 2018
 ====================================
 - A couple of Indic / USE bug fixes.
 - Disable vectorization, as it was causing unaligned access bus error on
   certain 32bit architectures.
 
 
--- a/gfx/harfbuzz/README-mozilla
+++ b/gfx/harfbuzz/README-mozilla
@@ -1,12 +1,12 @@
 This directory contains the HarfBuzz source from the upstream repo:
 https://github.com/harfbuzz/harfbuzz
 
-Current version: 1.8.3 [commit 2b76767bf572364d3d647cdd139f2044a7ad06b2]
+Current version: 1.8.7 [commit b6fdcf4f8bd09e065c767939125861c9dc8ff18f]
 
 UPDATING:
 
 Our in-tree copy of HarfBuzz does not depend on any generated files from the
 upstream build system. Therefore, it should be sufficient to simply overwrite
 the in-tree files one the updated ones from upstream to perform updates.
 
 To simplify this, the in-tree copy can be updated by running
--- a/gfx/harfbuzz/configure.ac
+++ b/gfx/harfbuzz/configure.ac
@@ -1,11 +1,11 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.8.3],
+        [1.8.7],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
 
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_SRCDIR([src/harfbuzz.pc.in])
 AC_CONFIG_HEADERS([config.h])
 
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -9,16 +9,17 @@ CLEANFILES =
 DISTCLEANFILES =
 MAINTAINERCLEANFILES =
 DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
 TESTS =
 check_PROGRAMS =
 
 # Convenience targets:
 lib: $(BUILT_SOURCES) libharfbuzz.la libharfbuzz-subset.la
+libs: $(BUILT_SOURCES) $(lib_LTLIBRARIES)
 fuzzing: $(BUILT_SOURCES) libharfbuzz-fuzzing.la libharfbuzz-subset-fuzzing.la
 
 lib_LTLIBRARIES = libharfbuzz.la
 
 include Makefile.sources
 
 HBCFLAGS =
 HBLIBS =
@@ -368,25 +369,21 @@ TESTS += $(dist_check_SCRIPTS)
 
 if !WITH_LIBSTDCXX
 dist_check_SCRIPTS += \
 	check-libstdc++.sh \
 	$(NULL)
 endif
 
 check_PROGRAMS += \
-	dump-fon \
 	dump-indic-data \
 	dump-khmer-data \
 	dump-myanmar-data \
 	dump-use-data \
 	$(NULL)
-dump_fon_SOURCES = dump-fon.cc
-dump_fon_CPPFLAGS = $(HBCFLAGS)
-dump_fon_LDADD = libharfbuzz.la $(HBLIBS)
 dump_indic_data_SOURCES = dump-indic-data.cc hb-ot-shape-complex-indic-table.cc
 dump_indic_data_CPPFLAGS = $(HBCFLAGS)
 dump_indic_data_LDADD = libharfbuzz.la $(HBLIBS)
 dump_khmer_data_SOURCES = dump-khmer-data.cc hb-ot-shape-complex-indic-table.cc
 dump_khmer_data_CPPFLAGS = $(HBCFLAGS)
 dump_khmer_data_LDADD = libharfbuzz.la $(HBLIBS)
 dump_myanmar_data_SOURCES = dump-myanmar-data.cc hb-ot-shape-complex-indic-table.cc
 dump_myanmar_data_CPPFLAGS = $(HBCFLAGS)
--- a/gfx/harfbuzz/src/Makefile.sources
+++ b/gfx/harfbuzz/src/Makefile.sources
@@ -9,19 +9,22 @@ HB_BASE_sources = \
 	hb-buffer.cc \
 	hb-common.cc \
 	hb-debug.hh \
 	hb-dsalgs.hh \
 	hb-face-private.hh \
 	hb-face.cc \
 	hb-font-private.hh \
 	hb-font.cc \
+	hb-iter-private.hh \
 	hb-map-private.hh \
 	hb-map.cc \
+	hb-machinery-private.hh \
 	hb-mutex-private.hh \
+	hb-null.hh \
 	hb-object-private.hh \
 	hb-open-file-private.hh \
 	hb-open-type-private.hh \
 	hb-ot-color-cbdt-table.hh \
 	hb-ot-cmap-table.hh \
 	hb-ot-glyf-table.hh \
 	hb-ot-hdmx-table.hh \
 	hb-ot-head-table.hh \
@@ -45,16 +48,17 @@ HB_BASE_sources = \
 	hb-shaper-list.hh \
 	hb-shaper-impl-private.hh \
 	hb-shaper-private.hh \
 	hb-shaper.cc \
 	hb-static.cc \
 	hb-string-array.hh \
 	hb-unicode-private.hh \
 	hb-unicode.cc \
+	hb-vector-private.hh \
 	hb-utf-private.hh \
 	hb-warning.cc \
 	$(NULL)
 
 HB_BASE_RAGEL_GENERATED_sources = \
 	hb-buffer-deserialize-json.hh \
 	hb-buffer-deserialize-text.hh \
 	$(NULL)
@@ -88,18 +92,16 @@ HB_OT_sources = \
 	hb-aat-layout-common-private.hh \
 	hb-aat-layout-ankr-table.hh \
 	hb-aat-layout-bsln-table.hh \
 	hb-aat-layout-feat-table.hh \
 	hb-aat-layout-kerx-table.hh \
 	hb-aat-layout-morx-table.hh \
 	hb-aat-layout-trak-table.hh \
 	hb-aat-layout-private.hh \
-	hb-aat-fmtx-table.hh \
-	hb-aat-gcid-table.hh \
 	hb-aat-ltag-table.hh \
 	hb-ot-font.cc \
 	hb-ot-layout.cc \
 	hb-ot-layout-base-table.hh \
 	hb-ot-layout-common-private.hh \
 	hb-ot-layout-gdef-table.hh \
 	hb-ot-layout-gpos-table.hh \
 	hb-ot-layout-gsubgpos-private.hh \
--- a/gfx/harfbuzz/src/dump-emoji.cc
+++ b/gfx/harfbuzz/src/dump-emoji.cc
@@ -228,22 +228,20 @@ int main (int argc, char **argv)
   sbix.dump (sbix_callback);
   sbix.fini ();
 
   OT::SVG::accelerator_t svg;
   svg.init (face);
   svg.dump (svg_callback);
   svg.fini ();
 
-  OT::Sanitizer<OT::COLR> sanitizerCOLR;
-  hb_blob_t* colr_blob = sanitizerCOLR.sanitize (face->reference_table (HB_OT_TAG_COLR));
+  hb_blob_t* colr_blob = hb_sanitize_context_t ().reference_table<OT::COLR> (face);
   const OT::COLR *colr = colr_blob->as<OT::COLR> ();
 
-  OT::Sanitizer<OT::CPAL> sanitizerCPAL;
-  hb_blob_t* cpal_blob = sanitizerCPAL.sanitize (face->reference_table (HB_OT_TAG_CPAL));
+  hb_blob_t* cpal_blob = hb_sanitize_context_t ().reference_table<OT::CPAL> (face);
   const OT::CPAL *cpal = cpal_blob->as<OT::CPAL> ();
 
   cairo_font_face_t *cairo_face;
   {
     FT_Library library;
     FT_Init_FreeType (&library);
     FT_Face ftface;
     FT_New_Face (library, argv[1], 0, &ftface);
deleted file mode 100644
--- a/gfx/harfbuzz/src/dump-fon.cc
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright © 2018  Ebrahim Byagowi
- *
- *  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.
- */
-
-#include "hb-static.cc"
-#include <stdio.h>
-#include <stdlib.h>
-#include "hb-open-type-private.hh"
-
-template <typename Type, int Bytes> struct LEInt;
-
-template <typename Type>
-struct LEInt<Type, 1>
-{
-  public:
-  inline void set (Type V)
-  {
-    v = V;
-  }
-  inline operator Type (void) const
-  {
-    return v;
-  }
-  private: uint8_t v;
-};
-template <typename Type>
-struct LEInt<Type, 2>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[1] = (V >>  8) & 0xFF;
-    v[0] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[1] <<  8)
-         + (v[0]      );
-  }
-  private: uint8_t v[2];
-};
-template <typename Type>
-struct LEInt<Type, 3>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[2] = (V >> 16) & 0xFF;
-    v[1] = (V >>  8) & 0xFF;
-    v[0] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[2] << 16)
-         + (v[1] <<  8)
-         + (v[0]      );
-  }
-  private: uint8_t v[3];
-};
-template <typename Type>
-struct LEInt<Type, 4>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[3] = (V >> 24) & 0xFF;
-    v[2] = (V >> 16) & 0xFF;
-    v[1] = (V >>  8) & 0xFF;
-    v[0] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[3] << 24)
-         + (v[2] << 16)
-         + (v[1] <<  8)
-         + (v[0]      );
-  }
-  private: uint8_t v[4];
-};
-
-template <typename Type, unsigned int Size>
-struct LEIntType
-{
-  inline void set (Type i) { v.set (i); }
-  inline operator Type(void) const { return v; }
-  inline bool sanitize (OT::hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
-  }
-  protected:
-  LEInt<Type, Size> v;
-  public:
-  DEFINE_SIZE_STATIC (Size);
-};
-
-typedef LEIntType<uint8_t,  1> LEUINT8;		/* 8-bit unsigned integer. */
-typedef LEIntType<int8_t,   1> LEINT8;		/* 8-bit signed integer. */
-typedef LEIntType<uint16_t, 2> LEUINT16;	/* 16-bit unsigned integer. */
-typedef LEIntType<int16_t,  2> LEINT16;		/* 16-bit signed integer. */
-typedef LEIntType<uint32_t, 4> LEUINT32;	/* 32-bit unsigned integer. */
-typedef LEIntType<int32_t,  4> LEINT32;		/* 32-bit signed integer. */
-typedef LEIntType<uint32_t, 3> LEUINT24;	/* 24-bit unsigned integer. */
-
-
-struct LE_FONTINFO16
-{
-  inline bool sanitize (OT::hb_sanitize_context_t *c, unsigned int length) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) && c->check_range (this, length)));
-  }
-
-  // https://msdn.microsoft.com/en-us/library/cc194829.aspx
-  enum charset_t
-  {
-    // dfCharSet possible values and the codepage they are indicating to
-    ANSI	= 0x00,	// 1252
-    DEFAULT	= 0x01,	//
-    SYMBOL	= 0x02,	//
-    SHIFTJIS	= 0x80,	// 932
-    HANGUL	= 0x81,	// 949
-    GB2312	= 0x86,	// 936
-    CHINESEBIG5	= 0x88,	// 950
-    GREEK	= 0xA1,	// 1253
-    TURKISH	= 0xA2,	// 1254
-    HEBREW	= 0xB1,	// 1255
-    ARABIC	= 0xB2,	// 1256
-    BALTIC	= 0xBA,	// 1257
-    RUSSIAN	= 0xCC,	// 1251
-    THAI	= 0xDE,	// 874
-    EE		= 0xEE,	// 1250
-    OEM		= 0xFF	//
-  };
-
-  inline const char* get_charset() const
-  {
-    switch (dfCharSet) {
-    case ANSI: return "ISO8859";
-    case DEFAULT: return "WinDefault";
-    case SYMBOL: return "Symbol";
-    case SHIFTJIS: return "JISX0208.1983";
-    case HANGUL: return "MSHangul";
-    case GB2312: return "GB2312.1980";
-    case CHINESEBIG5: return "Big5";
-    case GREEK: return "CP1253";
-    case TURKISH: return "CP1254";
-    case HEBREW: return "CP1255";
-    case ARABIC: return "CP1256";
-    case BALTIC: return "CP1257";
-    case RUSSIAN: return "CP1251";
-    case THAI: return "CP874";
-    case EE: return "CP1250";
-    case OEM: return "OEM";
-    default: return "Unknown";
-    }
-  }
-
-  inline unsigned int get_version () const
-  {
-    return dfVersion;
-  }
-
-  inline unsigned int get_weight () const
-  {
-    return dfWeight;
-  }
-
-  enum weight_t {
-    DONTCARE	= 0,
-    THIN	= 100,
-    EXTRALIGHT	= 200,
-    ULTRALIGHT	= 200,
-    LIGHT	= 300,
-    NORMAL	= 400,
-    REGULAR	= 400,
-    MEDIUM	= 500,
-    SEMIBOLD	= 600,
-    DEMIBOLD	= 600,
-    BOLD	= 700,
-    EXTRABOLD	= 800,
-    ULTRABOLD	= 800,
-    HEAVY	= 900,
-    BLACK	= 900
-  };
-
-  inline void dump () const
-  {
-    // With https://github.com/juanitogan/mkwinfont/blob/master/python/dewinfont.py help
-    // Should be implemented differently eventually, but for now
-    unsigned int ctstart;
-    unsigned int ctsize;
-    if (dfVersion == 0x200)
-    {
-      ctstart = 0x76;
-      ctsize = 4;
-    }
-    else
-    {
-      return; // must of ".fon"s are version 2 and even dewinfont V1 implmentation doesn't seem correct
-      ctstart = 0x94;
-      ctsize = 6;
-    }
-    // unsigned int maxwidth = 0;
-    for (unsigned int i = dfFirstChar; i < dfLastChar; ++i)
-    {
-      unsigned int entry = ctstart + ctsize * (i-dfFirstChar);
-      unsigned int w = (uint16_t) OT::StructAtOffset<LEUINT16> (this, entry);
-
-      unsigned int off;
-      if (ctsize == 4)
-        off = (uint16_t) OT::StructAtOffset<LEUINT16> (this, entry+2);
-      else
-        off = (uint32_t) OT::StructAtOffset<LEUINT32> (this, entry+2);
-
-      unsigned int widthbytes = (w + 7) / 8;
-      for (unsigned int j = 0; j < dfPixHeight; ++j)
-      {
-        for (unsigned int k = 0; k < widthbytes; ++k)
-	{
-	  unsigned int bytepos = off + k * dfPixHeight + j;
-	  const uint8_t b = (uint8_t) OT::StructAtOffset<LEINT8> (this, bytepos);
-	  for (unsigned int a = 128; a > 0; a >>= 1)
-	    printf (b & a ? "x" : ".");
-	}
-	printf ("\n");
-      }
-      printf ("\n\n");
-    }
-  }
-
-  protected:
-  LEUINT16	dfVersion;
-  LEUINT32	dfSize;
-  LEUINT8	dfCopyright[60];
-  LEUINT16	dfType;
-  LEUINT16	dfPoints;
-  LEUINT16	dfVertRes;
-  LEUINT16	dfHorizRes;
-  LEUINT16	dfAscent;
-  LEUINT16	dfInternalLeading;
-  LEUINT16	dfExternalLeading;
-  LEUINT8	dfItalic;
-  LEUINT8	dfUnderline;
-  LEUINT8	dfStrikeOut;
-  LEUINT16	dfWeight; // see weight_t
-  LEUINT8	dfCharSet;  // see charset_t
-  LEUINT16	dfPixWidth;
-  LEUINT16	dfPixHeight;
-  LEUINT8	dfPitchAndFamily;
-  LEUINT16	dfAvgWidth;
-  LEUINT16	dfMaxWidth;
-  LEUINT8	dfFirstChar;
-  LEUINT8	dfLastChar;
-  LEUINT8	dfDefaultChar;
-  LEUINT8	dfBreakChar;
-  LEUINT16	dfWidthBytes;
-  LEUINT32	dfDevice;
-  LEUINT32	dfFace;
-  LEUINT32	dfBitsPointer;
-  LEUINT32	dfBitsOffset;
-  LEUINT8	dfReserved;
-//   LEUINT32	dfFlags;
-//   LEUINT16	dfAspace;
-//   LEUINT16	dfBspace;
-//   LEUINT16	dfCspace;
-//   LEUINT32	dfColorPointer;
-//   LEUINT32	dfReserved1[4];
-  OT::UnsizedArrayOf<LEUINT8>
-		dataZ;
-  public:
-  DEFINE_SIZE_ARRAY (118, dataZ);
-};
-
-struct NE_NAMEINFO
-{
-  friend struct NE_TYPEINFO;
-
-  inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base, unsigned int shift) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) &&
-			  get_font (base, shift).sanitize (c, length << shift)));
-  }
-
-  inline const LE_FONTINFO16& get_font (const void *base, int shift) const
-  {
-    return OT::StructAtOffset<LE_FONTINFO16> (base, offset << shift);
-  }
-
-  enum resource_type_flag_t {
-    NONE     = 0x0000,
-    MOVEABLE = 0x0010,
-    PURE     = 0x0020,
-    PRELOAD  = 0x0040
-  };
-
-  protected:
-  LEUINT16	offset;	// Should be shifted with alignmentShiftCount before use
-  LEUINT16	length;	// Should be shifted with alignmentShiftCount before use
-  LEUINT16	flags;	// resource_type_flag_t
-  LEUINT16	id;
-  LEUINT16	handle;
-  LEUINT16	usage;
-  public:
-  DEFINE_SIZE_STATIC (12);
-};
-
-struct NE_TYPEINFO
-{
-  inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base, unsigned int shift) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && resources.sanitize (c, count, base, shift));
-  }
-
-  inline unsigned int get_size (void) const
-  { return 8 + count * NE_NAMEINFO::static_size; }
-
-  inline const NE_TYPEINFO& next () const
-  {
-    const NE_TYPEINFO& next = OT::StructAfter<NE_TYPEINFO> (*this);
-    if (type_id == 0)
-      return Null(NE_TYPEINFO);
-    return next;
-  }
-
-  inline const LE_FONTINFO16& get_font (unsigned int idx, const void *base, int shift) const
-  {
-    if (idx < count)
-      return resources[idx].get_font (base, shift);
-    return Null(LE_FONTINFO16);
-  }
-
-  inline unsigned int get_count () const
-  {
-    return count;
-  }
-
-  inline unsigned int get_type_id () const
-  {
-    return type_id;
-  }
-
-  enum type_id_t {
-    CURSOR		= 0x8001,
-    BITMAP		= 0x8002,
-    ICON		= 0x8003,
-    MENU		= 0x8004,
-    DIALOG		= 0x8005,
-    STRING		= 0x8006,
-    FONT_DIRECTORY	= 0x8007,
-    FONT		= 0x8008,
-    ACCELERATOR_TABLE	= 0x8009,
-    RESOURCE_DATA	= 0x800a,
-    GROUP_CURSOR	= 0x800c,
-    GROUP_ICON		= 0x800e,
-    VERSION		= 0x8010
-  };
-
-  protected:
-  LEUINT16	type_id; // see type_id_t
-  LEUINT16	count;
-  LEUINT32	resloader;
-  OT::UnsizedArrayOf<NE_NAMEINFO>
-		resources;
-  public:
-  DEFINE_SIZE_ARRAY (8, resources);
-};
-
-struct NE_RESOURCE_TABLE
-{
-  inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-
-    if (!c->check_struct (this))
-      return_trace (false);
-
-    const NE_TYPEINFO* n = &chain;
-    while (n != &Null(NE_TYPEINFO) && c->check_struct (n) && n->get_type_id () != 0)
-    {
-      if (n->get_type_id () == NE_TYPEINFO::FONT)
-	return_trace (n->sanitize (c, base, alignmentShiftCount));
-      n = &n->next();
-    }
-    return_trace (false);
-  }
-
-  inline unsigned int get_shift_value () const
-  {
-    return alignmentShiftCount;
-  }
-
-  inline const NE_TYPEINFO& get_fonts_entry () const
-  {
-    const NE_TYPEINFO* n = &chain;
-    while (n != &Null(NE_TYPEINFO) && n->get_type_id () != 0)
-    {
-      if (n->get_type_id () == NE_TYPEINFO::FONT)
-	return *n;
-      n = &n->next();
-    }
-    return Null(NE_TYPEINFO);
-  }
-
-  protected:
-  LEUINT16	alignmentShiftCount;
-  NE_TYPEINFO	chain;
-  // It is followed by an array of OT::ArrayOf<LEUINT8, LEUINT8> chars;
-  public:
-  DEFINE_SIZE_MIN (2);
-};
-
-// https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L2467
-struct LE_IMAGE_OS2_HEADER
-{
-  inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) && (this+rsrctab).sanitize (c, base)));
-  }
-
-  inline const NE_RESOURCE_TABLE& get_resource_table () const
-  {
-    if (magic != 0x454E) // Only NE containers are support for now, NE == 0x454E
-      return Null(NE_RESOURCE_TABLE);
-    return this+rsrctab;
-  }
-
-  protected:
-  LEUINT16	magic;		/* 00 NE signature 'NE' */
-  LEUINT8	ver;		/* 02 Linker version number */
-  LEUINT8	rev;		/* 03 Linker revision number */
-  LEUINT16	enttab;		/* 04 Offset to entry table relative to NE */
-  LEUINT16	cbenttab;	/* 06 Length of entry table in bytes */
-  LEUINT32	crc;		/* 08 Checksum */
-  LEUINT16	flags;		/* 0c Flags about segments in this file */
-  LEUINT16	autodata;	/* 0e Automatic data segment number */
-  LEUINT16	heap;		/* 10 Initial size of local heap */
-  LEUINT16	stack;		/* 12 Initial size of stack */
-  LEUINT32	csip;		/* 14 Initial CS:IP */
-  LEUINT32	sssp;		/* 18 Initial SS:SP */
-  LEUINT16	cseg;		/* 1c # of entries in segment table */
-  LEUINT16	cmod;		/* 1e # of entries in module reference tab. */
-  LEUINT16	cbnrestab;	/* 20 Length of nonresident-name table     */
-  LEUINT16	segtab;		/* 22 Offset to segment table */
-  OT::OffsetTo<NE_RESOURCE_TABLE, LEUINT16>
-		rsrctab;	/* 24 Offset to resource table */
-  LEUINT16	restab;		/* 26 Offset to resident-name table */
-  LEUINT16	modtab;		/* 28 Offset to module reference table */
-  LEUINT16	imptab;		/* 2a Offset to imported name table */
-  LEUINT32	nrestab;	/* 2c Offset to nonresident-name table */
-  LEUINT16	cmovent;	/* 30 # of movable entry points */
-  LEUINT16	align;		/* 32 Logical sector alignment shift count */
-  LEUINT16	cres;		/* 34 # of resource segments */
-  LEUINT8	exetyp;		/* 36 Flags indicating target OS */
-  LEUINT8	flagsothers;	/* 37 Additional information flags */
-  LEUINT16	pretthunks;	/* 38 Offset to return thunks */
-  LEUINT16	psegrefbytes;	/* 3a Offset to segment ref. bytes */
-  LEUINT16	swaparea;	/* 3c Reserved by Microsoft */
-  LEUINT16	expver;		/* 3e Expected Windows version number */
-  public:
-  DEFINE_SIZE_STATIC (64);
-};
-
-struct LE_IMAGE_DOS_HEADER {
-  inline bool sanitize (OT::hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) &&
-			  get_os2_header ().sanitize (c, this)));
-  }
-
-  inline const LE_IMAGE_OS2_HEADER& get_os2_header () const
-  {
-    return this+e_lfanew;
-  }
-
-  protected:
-  LEUINT16	e_magic;	// Magic number
-  LEUINT16	e_cblp;		// Bytes on last page of file
-  LEUINT16	e_cp;		// Pages in file
-  LEUINT16	e_crlc;		// Relocations
-  LEUINT16	e_cparhdr;	// Size of header in paragraphs
-  LEUINT16	e_minalloc;	// Minimum extra paragraphs needed
-  LEUINT16	e_maxalloc;	// Maximum extra paragraphs needed
-  LEUINT16	e_ss;		// Initial (relative) SS value
-  LEUINT16	e_sp;		// Initial SP value
-  LEUINT16	e_csum;		// Checksum
-  LEUINT16	e_ip;		// Initial IP value
-  LEUINT16	e_cs;		// Initial (relative) CS value
-  LEUINT16	e_lfarlc;	// File address of relocation table
-  LEUINT16	e_ovno;		// Overlay number
-  LEUINT16	e_res_0;	// Reserved words
-  LEUINT16	e_res_1;	// Reserved words
-  LEUINT16	e_res_2;	// Reserved words
-  LEUINT16	e_res_3;	// Reserved words
-  LEUINT16	e_oemid;	// OEM identifier (for e_oeminfo)
-  LEUINT16	e_oeminfo;	// OEM information; e_oemid specific
-  LEUINT16	e_res2_0;	// Reserved words
-  LEUINT16	e_res2_1;	// Reserved words
-  LEUINT16	e_res2_2;	// Reserved words
-  LEUINT16	e_res2_3;	// Reserved words
-  LEUINT16	e_res2_4;	// Reserved words
-  LEUINT16	e_res2_5;	// Reserved words
-  LEUINT16	e_res2_6;	// Reserved words
-  LEUINT16	e_res2_7;	// Reserved words
-  LEUINT16	e_res2_8;	// Reserved words
-  LEUINT16	e_res2_9;	// Reserved words
-  OT::OffsetTo<LE_IMAGE_OS2_HEADER, LEUINT32>
-		e_lfanew;	// File address of new exe header
-  public:
-  DEFINE_SIZE_STATIC (64);
-};
-
-int main (int argc, char** argv) {
-  hb_blob_t *blob = hb_blob_create_from_file (argv[1]);
-
-  OT::Sanitizer<LE_IMAGE_DOS_HEADER> sanitizer;
-  hb_blob_t *font_blob = sanitizer.sanitize (blob);
-  const LE_IMAGE_DOS_HEADER* dos_header = font_blob->as<LE_IMAGE_DOS_HEADER> ();
-
-  const NE_RESOURCE_TABLE &rtable = dos_header->get_os2_header ().get_resource_table ();
-  int shift = rtable.get_shift_value ();
-  const NE_TYPEINFO& entry = rtable.get_fonts_entry ();
-  for (unsigned int i = 0; i < entry.get_count (); ++i)
-  {
-    const LE_FONTINFO16& font = entry.get_font (i, dos_header, shift);
-    printf ("version: %x, weight: %d, charset: %s\n", font.get_version (),
-	    font.get_weight (), font.get_charset ());
-    // font.dump ();
-  }
-  return 0;
-}
--- a/gfx/harfbuzz/src/gen-use-table.py
+++ b/gfx/harfbuzz/src/gen-use-table.py
@@ -323,16 +323,19 @@ def map_to_use(data):
 		if U == 0x1A7F: UISC = Consonant_Final; UIPC = Bottom
 
 		# TODO: https://github.com/harfbuzz/harfbuzz/pull/609
 		if U == 0x20F0: UISC = Cantillation_Mark; UIPC = Top
 
 		# TODO: https://github.com/harfbuzz/harfbuzz/pull/626
 		if U == 0xA8B4: UISC = Consonant_Medial
 
+		# TODO: https://github.com/harfbuzz/harfbuzz/issues/1105
+		if U == 0x11134: UISC = Gemination_Mark
+
 		values = [k for k,v in items if v(U,UISC,UGC)]
 		assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values)
 		USE = values[0]
 
 		# Resolve Indic_Positional_Category
 
 		# TODO: Not in Unicode 8.0 yet, but in spec.
 		if U == 0x1B6C: UIPC = Bottom
@@ -351,16 +354,23 @@ def map_to_use(data):
 		if U == 0x11302: UIPC = Top
 		if U == 0x1133C: UIPC = Bottom
 		if U == 0x1171E: UIPC = Left # Correct?!
 		if 0x1CF2 <= U <= 0x1CF3: UIPC = Right
 		if 0x1CF8 <= U <= 0x1CF9: UIPC = Top
 		# https://github.com/roozbehp/unicode-data/issues/8
 		if U == 0x0A51: UIPC = Bottom
 
+		# TODO: https://github.com/harfbuzz/harfbuzz/pull/982
+		if UBlock == 'Chakma' and is_VOWEL (U, UISC, UGC):
+			if UIPC == Top:
+				UIPC = Bottom
+			elif UIPC == Bottom:
+				UIPC = Top
+
 		assert (UIPC in [Not_Applicable, Visual_Order_Left] or
 			USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC)
 
 		pos_mapping = use_positions.get(USE, None)
 		if pos_mapping:
 			values = [k for k,v in pos_mapping.items() if v and UIPC in v]
 			assert len(values) == 1, "%s %s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC, values)
 			USE = USE + values[0]
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-aat-fmtx-table.hh
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright © 2018  Ebrahim Byagowi
- *
- *  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.
- */
-
-#ifndef HB_AAT_FMTX_TABLE_HH
-#define HB_AAT_FMTX_TABLE_HH
-
-#include "hb-aat-layout-common-private.hh"
-
-/*
- * fmtx -- Font Metrics
- * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fmtx.html
- */
-#define HB_AAT_TAG_fmtx HB_TAG('f','m','t','x')
-
-
-namespace AAT {
-
-
-struct fmtx
-{
-  static const hb_tag_t tableTag = HB_AAT_TAG_fmtx;
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
-  }
-
-  FixedVersion<>version;		/* Version (set to 0x00020000). */
-  HBUINT32	glyphIndex;		/* The glyph whose points represent the metrics. */
-  HBUINT8	horizontalBefore;	/* Point number for the horizontal ascent. */
-  HBUINT8	horizontalAfter;	/* Point number for the horizontal descent. */
-  HBUINT8	horizontalCaretHead;	/* Point number for the horizontal caret head. */
-  HBUINT8	horizontalCaretBase;	/* Point number for the horizontal caret base. */
-  HBUINT8	verticalBefore;		/* Point number for the vertical ascent. */
-  HBUINT8	verticalAfter;		/* Point number for the vertical descent. */
-  HBUINT8	verticalCaretHead;	/* Point number for the vertical caret head. */
-  HBUINT8	verticalCaretBase;	/* Point number for the vertical caret base. */
-  public:
-  DEFINE_SIZE_STATIC (16);
-};
-
-} /* namespace AAT */
-
-
-#endif /* HB_AAT_FMTX_TABLE_HH */
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-aat-gcid-table.hh
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright © 2018  Ebrahim Byagowi
- *
- *  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.
- */
-
-#ifndef HB_AAT_GCID_TABLE_HH
-#define HB_AAT_GCID_TABLE_HH
-
-#include "hb-aat-layout-common-private.hh"
-
-/*
- * gcid -- Glyphs CIDs
- * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gcid.html
- */
-#define HB_AAT_TAG_gcid HB_TAG('g','c','i','d')
-
-
-namespace AAT {
-
-
-struct gcid
-{
-  static const hb_tag_t tableTag = HB_AAT_TAG_gcid;
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) && CIDs.sanitize (c)));
-  }
-
-  protected:
-  HBUINT16	version;	/* Version number (set to 0) */
-  HBUINT16	format;		/* Data format (set to 0) */
-  HBUINT32	size;		/* Size of the table, including header */
-  HBUINT16	registry;	/* The registry ID */
-  HBUINT8	registryName[64];
-				/* The registry name in ASCII */
-  HBUINT16	order;		/* The order ID */
-  HBUINT8	orderName[64];	/* The order name in ASCII */
-  HBUINT16	supplementVersion;
-				/* The supplement version */
-  ArrayOf<HBUINT16>
-		CIDs;		/* The CIDs for the glyphs in the font,
-				 * starting with glyph 0. If a glyph does not correspond
-				 * to a CID in the identified collection, 0xFFFF is used.
-				 * This should not exceed the number of glyphs in the font. */
-  public:
-  DEFINE_SIZE_ARRAY (144, CIDs);
-};
-
-} /* namespace AAT */
-
-
-#endif /* HB_AAT_GCID_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-aat-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-aat-layout-common-private.hh
@@ -156,17 +156,17 @@ struct LookupFormat0
   {
     if (unlikely (glyph_id >= num_glyphs)) return nullptr;
     return &arrayZ[glyph_id];
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (arrayZ.sanitize (c, c->num_glyphs));
+    return_trace (arrayZ.sanitize (c, c->get_num_glyphs ()));
   }
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 0 */
   UnsizedArrayOf<T>
 		arrayZ;		/* Array of lookup values, indexed by glyph index. */
   public:
   DEFINE_SIZE_ARRAY (2, arrayZ);
@@ -620,17 +620,17 @@ struct hb_aat_apply_context_t :
 
   inline hb_aat_apply_context_t (hb_font_t *font_,
 				 hb_buffer_t *buffer_,
 				 hb_blob_t *table) :
 		font (font_), face (font->face), buffer (buffer_),
 		sanitizer (), lookup_index (0), debug_depth (0)
   {
     sanitizer.init (table);
-    sanitizer.num_glyphs = face->get_num_glyphs ();
+    sanitizer.set_num_glyphs (face->get_num_glyphs ());
     sanitizer.start_processing ();
   }
 
   inline void set_lookup_index (unsigned int i) { lookup_index = i; }
 
   inline ~hb_aat_apply_context_t (void)
   {
     sanitizer.end_processing ();
--- a/gfx/harfbuzz/src/hb-aat-layout.cc
+++ b/gfx/harfbuzz/src/hb-aat-layout.cc
@@ -31,116 +31,58 @@
 
 #include "hb-aat-layout-private.hh"
 #include "hb-aat-layout-ankr-table.hh"
 #include "hb-aat-layout-bsln-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-layout-feat-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-layout-kerx-table.hh"
 #include "hb-aat-layout-morx-table.hh"
 #include "hb-aat-layout-trak-table.hh"
-#include "hb-aat-fmtx-table.hh" // Just so we compile it; unused otherwise.
-#include "hb-aat-gcid-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-ltag-table.hh" // Just so we compile it; unused otherwise.
 
 /*
- * morx/kerx/trak
+ * morx/kerx/trak/ankr
  */
 
-#if 0
-static inline const AAT::ankr&
-_get_ankr (hb_face_t *face, hb_blob_t **blob = nullptr)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
-  {
-    if (blob)
-      *blob = hb_blob_get_empty ();
-    return Null(AAT::ankr);
-  }
-  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  const AAT::ankr& ankr = *(layout->ankr.get ());
-  if (blob)
-    *blob = layout->ankr.blob;
-  return ankr;
-}
-
-static inline const AAT::kerx&
-_get_kerx (hb_face_t *face, hb_blob_t **blob = nullptr)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
-  {
-    if (blob)
-      *blob = hb_blob_get_empty ();
-    return Null(AAT::kerx);
-  }
-  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  /* XXX this doesn't call set_num_glyphs on sanitizer. */
-  const AAT::kerx& kerx = *(layout->kerx.get ());
-  if (blob)
-    *blob = layout->kerx.blob;
-  return kerx;
-}
-
 static inline const AAT::morx&
 _get_morx (hb_face_t *face, hb_blob_t **blob = nullptr)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
   {
     if (blob)
       *blob = hb_blob_get_empty ();
     return Null(AAT::morx);
   }
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  /* XXX this doesn't call set_num_glyphs on sanitizer. */
-  const AAT::morx& morx = *(layout->morx.get ());
+  const AAT::morx& morx = *(layout->table.morx.get ());
   if (blob)
-    *blob = layout->morx.blob;
+    *blob = layout->table.morx.get_blob ();
   return morx;
 }
 
-static inline const AAT::trak&
-_get_trak (hb_face_t *face, hb_blob_t **blob = nullptr)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
-  {
-    if (blob)
-      *blob = hb_blob_get_empty ();
-    return Null(AAT::trak);
-  }
-  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  const AAT::trak& trak = *(layout->trak.get ());
-  if (blob)
-    *blob = layout->trak.blob;
-  return trak;
-}
-#endif
-
 // static inline void
 // _hb_aat_layout_create (hb_face_t *face)
 // {
-//   OT::Sanitizer<AAT::morx> sanitizer;
-//   sanitizer.set_num_glyphs (face->get_num_glyphs ());
-//   hb_blob_t *morx_blob = sanitizer.sanitize (face->reference_table (HB_AAT_TAG_morx));
+//   hb_blob_t *morx_blob = hb_sanitize_context_t ().reference_table<AAT::morx> (face);
 //   morx_blob->as<AAT::morx> ();
 
 //   if (0)
 //   {
 //     morx_blob->as<AAT::Lookup<OT::GlyphID> > ()->get_value (1, face->get_num_glyphs ());
 //   }
 // }
 
 void
 hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer)
 {
-#if 0
   hb_blob_t *blob;
   const AAT::morx& morx = _get_morx (font->face, &blob);
 
   AAT::hb_aat_apply_context_t c (font, buffer, blob);
   morx.apply (&c);
-#endif
 }
 
 void
 hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer)
 {
 #if 0
   hb_blob_t *blob;
   const AAT::ankr& ankr = _get_ankr (font->face, &blob);
--- a/gfx/harfbuzz/src/hb-atomic-private.hh
+++ b/gfx/harfbuzz/src/hb-atomic-private.hh
@@ -41,142 +41,225 @@
 
 #if defined(hb_atomic_int_impl_add) \
  && defined(hb_atomic_ptr_impl_get) \
  && defined(hb_atomic_ptr_impl_cmpexch)
 
 /* Defined externally, i.e. in config.h; must have typedef'ed hb_atomic_int_impl_t as well. */
 
 
+#elif !defined(HB_NO_MT) && defined(__ATOMIC_CONSUME)
+
+/* C++11-style GCC primitives. */
+
+typedef int hb_atomic_int_impl_t;
+#define hb_atomic_int_impl_add(AI, V)		__atomic_fetch_add ((AI), (V), __ATOMIC_ACQ_REL)
+#define hb_atomic_int_impl_set_relaxed(AI, V)	__atomic_store_n ((AI), (V), __ATOMIC_RELAXED)
+#define hb_atomic_int_impl_get_relaxed(AI)	__atomic_load_n ((AI), __ATOMIC_RELAXED)
+
+#define hb_atomic_ptr_impl_get(P)		__atomic_load_n ((P), __ATOMIC_CONSUME)
+static inline bool
+_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
+{
+  const void *O = O_; // Need lvalue
+  return __atomic_compare_exchange_n ((void **) P, (void **) &O, (void *) N, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
+}
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	_hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N))
+
+#elif !defined(HB_NO_MT) && __cplusplus >= 201103L
+
+/* C++11 atomics. */
+
+#include <atomic>
+
+typedef int hb_atomic_int_impl_t;
+#define hb_atomic_int_impl_add(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
+#define hb_atomic_int_impl_set_relaxed(AI, V)	(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_relaxed))
+#define hb_atomic_int_impl_get_relaxed(AI)	(reinterpret_cast<std::atomic<int> *> (AI)->load (std::memory_order_relaxed))
+
+#define hb_atomic_ptr_impl_get(P)		(reinterpret_cast<std::atomic<void*> *> (P)->load (std::memory_order_consume))
+static inline bool
+_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
+{
+  const void *O = O_; // Need lvalue
+  return reinterpret_cast<std::atomic<const void*> *> (P)->compare_exchange_weak (O, N, std::memory_order_acq_rel, std::memory_order_relaxed);
+}
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	_hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N))
+
+
 #elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__))
 
 #include <windows.h>
 
-/* MinGW has a convoluted history of supporting MemoryBarrier
- * properly.  As such, define a function to wrap the whole
- * thing. */
-static inline void _HBMemoryBarrier (void) {
+static inline void _hb_memory_barrier (void)
+{
 #if !defined(MemoryBarrier)
+  /* MinGW has a convoluted history of supporting MemoryBarrier. */
   long dummy = 0;
   InterlockedExchange (&dummy, 1);
 #else
   MemoryBarrier ();
 #endif
 }
+#define _hb_memory_barrier()			_hb_memory_barrier ()
 
 typedef LONG hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		InterlockedExchangeAdd (&(AI), (V))
+#define hb_atomic_int_impl_add(AI, V)		InterlockedExchangeAdd ((AI), (V))
 
-#define hb_atomic_ptr_impl_get(P)		(_HBMemoryBarrier (), (void *) *(P))
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	(InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O))
 
 
 #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
 
+#define _hb_memory_barrier()			__sync_synchronize ()
+
 typedef int hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		__sync_fetch_and_add (&(AI), (V))
+#define hb_atomic_int_impl_add(AI, V)		__sync_fetch_and_add ((AI), (V))
 
-#define hb_atomic_ptr_impl_get(P)		(void *) (__sync_synchronize (), *(P))
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	__sync_bool_compare_and_swap ((P), (O), (N))
 
 
 #elif !defined(HB_NO_MT) && defined(HAVE_SOLARIS_ATOMIC_OPS)
 
 #include <atomic.h>
 #include <mbarrier.h>
 
+#define _hb_memory_r_barrier()			__machine_r_barrier ()
+#define _hb_memory_w_barrier()			__machine_w_barrier ()
+#define _hb_memory_barrier()			__machine_rw_barrier ()
+
 typedef unsigned int hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		( ({__machine_rw_barrier ();}), atomic_add_int_nv (&(AI), (V)) - (V))
 
-#define hb_atomic_ptr_impl_get(P)		( ({__machine_rw_barrier ();}), (void *) *(P))
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)	( ({__machine_rw_barrier ();}), atomic_cas_ptr ((void **) (P), (void *) (O), (void *) (N)) == (void *) (O) ? true : false)
+static inline int _hb_fetch_and_add (hb_atomic_int_impl_t *AI, int V)
+{
+  _hb_memory_w_barrier ();
+  int result = atomic_add_int_nv (AI, V);
+  _hb_memory_r_barrier ();
+  return result;
+}
+static inline bool _hb_compare_and_swap_ptr (const void **P, const void *O, const void *N)
+{
+  _hb_memory_w_barrier ();
+  int result = atomic_cas_ptr ((void **) P, (void *) O, (void *) N) == (void *) O;
+  _hb_memory_r_barrier ();
+  return result;
+}
+
+#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add ((AI), (V))
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swap_ptr ((const void **) (P), (O), (N))
 
 
 #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
 
+#define _hb_memory_barrier()			OSMemoryBarrier ()
 
 typedef int32_t hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		(OSAtomicAdd32Barrier ((V), &(AI)) - (V))
+#define hb_atomic_int_impl_add(AI, V)		(OSAtomicAdd32Barrier ((V), (AI)) - (V))
 
-#define hb_atomic_ptr_impl_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_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P))
 #else
 #if __ppc64__ || __x86_64__ || __aarch64__
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap64Barrier ((int64_t) (void *) (O), (int64_t) (void *) (N), (int64_t*) (P))
 #else
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap32Barrier ((int32_t) (void *) (O), (int32_t) (void *) (N), (int32_t*) (P))
 #endif
 #endif
 
 
 #elif !defined(HB_NO_MT) && defined(_AIX) && defined(__IBMCPP__)
 
 #include <builtins.h>
 
+#define _hb_memory_barrier()			__lwsync ()
 
-static inline int _hb_fetch_and_add(volatile int* AI, unsigned int V) {
-  __lwsync();
-  int result = __fetch_and_add(AI, V);
-  __isync();
+typedef int hb_atomic_int_impl_t;
+
+static inline int _hb_fetch_and_add (hb_atomic_int_impl_t *AI, int V)
+{
+  _hb_memory_barrier ();
+  int result = __fetch_and_add (AI, V);
+  _hb_memory_barrier ();
   return result;
 }
-static inline int _hb_compare_and_swaplp(volatile long* P, long O, long N) {
-  __sync();
-  int result = __compare_and_swaplp (P, &O, N);
-  __sync();
+static inline bool _hb_compare_and_swaplp (long *P, long O, long N)
+{
+  _hb_memory_barrier ();
+  bool result = __compare_and_swaplp (P, &O, N);
+  _hb_memory_barrier ();
   return result;
 }
 
-typedef int hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add (&(AI), (V))
+#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add ((AI), (V))
 
-#define hb_atomic_ptr_impl_get(P)               (__sync(), (void *) *(P))
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swaplp ((long*)(P), (long)(O), (long)(N))
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swaplp ((long *) (P), (long) (O), (long) (N))
+static_assert ((sizeof (long) == sizeof (void *)), "");
+
 
 #elif !defined(HB_NO_MT)
 
 #define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
 
+#define _hb_memory_barrier()
+
 typedef volatile int hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		(((AI) += (V)) - (V))
+#define hb_atomic_int_impl_add(AI, V)		((*(AI) += (V)) - (V))
 
-#define hb_atomic_ptr_impl_get(P)		((void *) *(P))
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	(* (void * volatile *) (P) == (void *) (O) ? (* (void * volatile *) (P) = (void *) (N), true) : false)
 
 
 #else /* HB_NO_MT */
 
 typedef int hb_atomic_int_impl_t;
-#define hb_atomic_int_impl_add(AI, V)		(((AI) += (V)) - (V))
+#define hb_atomic_int_impl_add(AI, V)		((*(AI) += (V)) - (V))
 
-#define hb_atomic_ptr_impl_get(P)		((void *) *(P))
+#define _hb_memory_barrier()
+
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	(* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
 
 
 #endif
 
 
+#ifndef _hb_memory_r_barrier
+#define _hb_memory_r_barrier()			_hb_memory_barrier ()
+#endif
+#ifndef _hb_memory_w_barrier
+#define _hb_memory_w_barrier()			_hb_memory_barrier ()
+#endif
+#ifndef HB_ATOMIC_INT_INIT
 #define HB_ATOMIC_INT_INIT(V)          {V}
+#endif
+#ifndef hb_atomic_int_impl_set_relaxed
+#define hb_atomic_int_impl_set_relaxed(AI, V)	(*(AI) = (V))
+#endif
+#ifndef hb_atomic_int_impl_get_relaxed
+#define hb_atomic_int_impl_get_relaxed(AI)	(*(AI))
+#endif
+#ifndef hb_atomic_ptr_impl_get
+inline void *hb_atomic_ptr_impl_get (void **P)	{ void *v = *P; _hb_memory_r_barrier (); return v; }
+#endif
+
 
 struct hb_atomic_int_t
 {
-  hb_atomic_int_impl_t v;
+  mutable hb_atomic_int_impl_t v;
 
-  inline void set_unsafe (int v_) { v = v_; }
-  inline int get_unsafe (void) const { return v; }
-  inline int inc (void) { return hb_atomic_int_impl_add (const_cast<hb_atomic_int_impl_t &> (v),  1); }
-  inline int dec (void) { return hb_atomic_int_impl_add (const_cast<hb_atomic_int_impl_t &> (v), -1); }
+  inline void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
+  inline int get_relaxed (void) const { return hb_atomic_int_impl_get_relaxed (&v); }
+  inline int inc (void) { return hb_atomic_int_impl_add (&v,  1); }
+  inline int dec (void) { return hb_atomic_int_impl_add (&v, -1); }
 };
 
 
-#define hb_atomic_ptr_get(P) hb_atomic_ptr_impl_get(P)
+#define hb_atomic_ptr_get(P) hb_atomic_ptr_impl_get((void **) P)
 #define hb_atomic_ptr_cmpexch(P,O,N) hb_atomic_ptr_impl_cmpexch((P),(O),(N))
 
 
 #endif /* HB_ATOMIC_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-blob-private.hh
+++ b/gfx/harfbuzz/src/hb-blob-private.hh
@@ -52,21 +52,16 @@ struct hb_blob_t
       destroy = nullptr;
     }
   }
 
   HB_INTERNAL bool try_make_writable (void);
   HB_INTERNAL bool try_make_writable_inplace (void);
   HB_INTERNAL bool try_make_writable_inplace_unix (void);
 
-  inline void lock (void)
-  {
-    hb_blob_make_immutable (this);
-  }
-
   template <typename Type>
   inline const Type* as (void) const
   {
     return unlikely (!data) ? &Null(Type) : reinterpret_cast<const Type *> (data);
   }
 
   public:
   hb_object_header_t header;
@@ -76,11 +71,12 @@ struct hb_blob_t
 
   const char *data;
   unsigned int length;
   hb_memory_mode_t mode;
 
   void *user_data;
   hb_destroy_func_t destroy;
 };
+DECLARE_NULL_INSTANCE (hb_blob_t);
 
 
 #endif /* HB_BLOB_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-blob.cc
+++ b/gfx/harfbuzz/src/hb-blob.cc
@@ -40,16 +40,30 @@
 #include <sys/mman.h>
 #endif /* HAVE_SYS_MMAN_H */
 
 #include <stdio.h>
 #include <errno.h>
 #include <stdlib.h>
 
 
+DEFINE_NULL_INSTANCE (hb_blob_t) =
+{
+  HB_OBJECT_HEADER_STATIC,
+
+  true, /* immutable */
+
+  nullptr, /* data */
+  0, /* length */
+  HB_MEMORY_MODE_READONLY, /* mode */
+
+  nullptr, /* user_data */
+  nullptr  /* destroy */
+};
+
 /**
  * hb_blob_create: (skip)
  * @data: Pointer to blob data.
  * @length: Length of @data in bytes.
  * @mode: Memory mode for @data.
  * @user_data: Data parameter to pass to @destroy.
  * @destroy: Callback to call when @data is not needed anymore.
  *
@@ -177,30 +191,17 @@ hb_blob_copy_writable_or_fail (hb_blob_t
  *
  * Return value: (transfer full): the empty blob.
  *
  * Since: 0.9.2
  **/
 hb_blob_t *
 hb_blob_get_empty (void)
 {
-  static const hb_blob_t _hb_blob_nil = {
-    HB_OBJECT_HEADER_STATIC,
-
-    true, /* immutable */
-
-    nullptr, /* data */
-    0, /* length */
-    HB_MEMORY_MODE_READONLY, /* mode */
-
-    nullptr, /* user_data */
-    nullptr  /* destroy */
-  };
-
-  return const_cast<hb_blob_t *> (&_hb_blob_nil);
+  return const_cast<hb_blob_t *> (&Null(hb_blob_t));
 }
 
 /**
  * hb_blob_reference: (skip)
  * @blob: a blob.
  *
  * Increases the reference count on @blob.
  *
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -78,17 +78,18 @@ enum hb_buffer_scratch_flags_t {
 };
 HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t);
 
 
 /*
  * hb_buffer_t
  */
 
-struct hb_buffer_t {
+struct hb_buffer_t
+{
   hb_object_header_t header;
   ASSERT_POD ();
 
   /* Information about how the text in the buffer should be treated */
   hb_unicode_funcs_t *unicode; /* Unicode functions */
   hb_buffer_flags_t flags; /* BOT / EOT / etc. */
   hb_buffer_cluster_level_t cluster_level;
   hb_codepoint_t replacement; /* U+FFFD or something else. */
@@ -347,16 +348,17 @@ struct hb_buffer_t {
   }
   inline void
   safe_to_break_all (void)
   {
     for (unsigned int i = 0; i < len; i++)
       info[i].mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
   }
 };
+DECLARE_NULL_INSTANCE (hb_buffer_t);
 
 
 /* Loop over clusters. Duplicated in foreach_syllable(). */
 #define foreach_cluster(buffer, start, end) \
   for (unsigned int \
        _count = buffer->len, \
        start = 0, end = _count ? _next_cluster (buffer, 0) : 0; \
        start < _count; \
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -696,16 +696,38 @@ hb_buffer_t::guess_segment_properties (v
     /* TODO get_default_for_script? using $LANGUAGE */
     props.language = hb_language_get_default ();
   }
 }
 
 
 /* Public API */
 
+DEFINE_NULL_INSTANCE (hb_buffer_t) =
+{
+  HB_OBJECT_HEADER_STATIC,
+
+  const_cast<hb_unicode_funcs_t *> (&_hb_Null_hb_unicode_funcs_t),
+  HB_BUFFER_FLAG_DEFAULT,
+  HB_BUFFER_CLUSTER_LEVEL_DEFAULT,
+  HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
+  HB_BUFFER_SCRATCH_FLAG_DEFAULT,
+  HB_BUFFER_MAX_LEN_DEFAULT,
+  HB_BUFFER_MAX_OPS_DEFAULT,
+
+  HB_BUFFER_CONTENT_TYPE_INVALID,
+  HB_SEGMENT_PROPERTIES_DEFAULT,
+  false, /* successful */
+  true, /* have_output */
+  true  /* have_positions */
+
+  /* Zero is good enough for everything else. */
+};
+
+
 /**
  * hb_buffer_create: (Xconstructor)
  *
  * Creates a new #hb_buffer_t with all properties to defaults.
  *
  * Return value: (transfer full):
  * A newly allocated #hb_buffer_t with a reference count of 1. The initial
  * reference count should be released with hb_buffer_destroy() when you are done
@@ -738,37 +760,17 @@ hb_buffer_create (void)
  *
  * Return value: (transfer full):
  *
  * Since: 0.9.2
  **/
 hb_buffer_t *
 hb_buffer_get_empty (void)
 {
-  static const hb_buffer_t _hb_buffer_nil = {
-    HB_OBJECT_HEADER_STATIC,
-
-    const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
-    HB_BUFFER_FLAG_DEFAULT,
-    HB_BUFFER_CLUSTER_LEVEL_DEFAULT,
-    HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
-    HB_BUFFER_SCRATCH_FLAG_DEFAULT,
-    HB_BUFFER_MAX_LEN_DEFAULT,
-    HB_BUFFER_MAX_OPS_DEFAULT,
-
-    HB_BUFFER_CONTENT_TYPE_INVALID,
-    HB_SEGMENT_PROPERTIES_DEFAULT,
-    false, /* successful */
-    true, /* have_output */
-    true  /* have_positions */
-
-    /* Zero is good enough for everything else. */
-  };
-
-  return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
+  return const_cast<hb_buffer_t *> (&Null(hb_buffer_t));
 }
 
 /**
  * hb_buffer_reference: (skip)
  * @buffer: an #hb_buffer_t.
  *
  * Increases the reference count on @buffer by one. This prevents @buffer from
  * being destroyed until a matching call to hb_buffer_destroy() is made.
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -32,30 +32,30 @@
 #include <locale.h>
 #ifdef HAVE_XLOCALE_H
 #include <xlocale.h>
 #endif
 
 
 /* hb_options_t */
 
-hb_options_union_t _hb_options;
+hb_atomic_int_t _hb_options;
 
 void
 _hb_options_init (void)
 {
   hb_options_union_t u;
   u.i = 0;
   u.opts.initialized = 1;
 
   char *c = getenv ("HB_OPTIONS");
   u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
 
   /* This is idempotent and threadsafe. */
-  _hb_options = u;
+  _hb_options.set_relaxed (u.i);
 }
 
 
 /* hb_tag_t */
 
 /**
  * hb_tag_from_string:
  * @str: (array length=len) (element-type uint8_t):
@@ -1064,8 +1064,17 @@ hb_variation_to_string (hb_variation_t *
   s[len++] = '=';
   len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
 
   assert (len < ARRAY_LENGTH (s));
   len = MIN (len, size - 1);
   memcpy (buf, s, len);
   buf[len] = '\0';
 }
+
+/* If there is no visibility control, then hb-static.cc will NOT
+ * define anything.  Instead, we get it to define one set in here
+ * only, so only libharfbuzz.so defines them, not other libs. */
+#ifdef HB_NO_VISIBILITY
+#undef HB_NO_VISIBILITY
+#include "hb-static.cc"
+#define HB_NO_VISIBILITY 1
+#endif
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -264,32 +264,32 @@ create_ct_font (CGFontRef cg_font, CGFlo
       DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed");
   }
 
   if (original_url)
     CFRelease (original_url);
   return ct_font;
 }
 
-hb_coretext_shaper_face_data_t *
+hb_coretext_face_data_t *
 _hb_coretext_shaper_face_data_create (hb_face_t *face)
 {
   CGFontRef cg_font = create_cg_font (face);
 
   if (unlikely (!cg_font))
   {
     DEBUG_MSG (CORETEXT, face, "CGFont creation failed..");
     return nullptr;
   }
 
-  return (hb_coretext_shaper_face_data_t *) cg_font;
+  return (hb_coretext_face_data_t *) cg_font;
 }
 
 void
-_hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data)
+_hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t *data)
 {
   CFRelease ((CGFontRef) data);
 }
 
 hb_face_t *
 hb_coretext_face_create (CGFontRef cg_font)
 {
   return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), _hb_cg_font_release);
@@ -301,36 +301,36 @@ hb_coretext_face_create (CGFontRef cg_fo
 CGFontRef
 hb_coretext_face_get_cg_font (hb_face_t *face)
 {
   if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr;
   return (CGFontRef) HB_SHAPER_DATA_GET (face);
 }
 
 
-hb_coretext_shaper_font_data_t *
+hb_coretext_font_data_t *
 _hb_coretext_shaper_font_data_create (hb_font_t *font)
 {
   hb_face_t *face = font->face;
   if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr;
   CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face);
 
   CTFontRef ct_font = create_ct_font (cg_font, coretext_font_size_from_ptem (font->ptem));
 
   if (unlikely (!ct_font))
   {
     DEBUG_MSG (CORETEXT, font, "CGFont creation failed..");
     return nullptr;
   }
 
-  return (hb_coretext_shaper_font_data_t *) ct_font;
+  return (hb_coretext_font_data_t *) ct_font;
 }
 
 void
-_hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data)
+_hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data)
 {
   CFRelease ((CTFontRef) data);
 }
 
 /*
  * Since: 1.7.2
  */
 hb_font_t *
@@ -343,17 +343,17 @@ hb_coretext_font_create (CTFontRef ct_fo
   hb_face_destroy (face);
 
   if (unlikely (hb_object_is_inert (font)))
     return font;
 
   hb_font_set_ptem (font, coretext_font_size_to_ptem (CTFontGetSize(ct_font)));
 
   /* Let there be dragons here... */
-  HB_SHAPER_DATA_GET (font) = (hb_coretext_shaper_font_data_t *) CFRetain (ct_font);
+  HB_SHAPER_DATA_GET (font) = (hb_coretext_font_data_t *) CFRetain (ct_font);
 
   return font;
 }
 
 CTFontRef
 hb_coretext_font_get_ct_font (hb_font_t *font)
 {
   if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return nullptr;
@@ -361,30 +361,30 @@ hb_coretext_font_get_ct_font (hb_font_t 
 }
 
 
 
 /*
  * shaper shape_plan data
  */
 
-struct hb_coretext_shaper_shape_plan_data_t {};
+struct hb_coretext_shape_plan_data_t {};
 
-hb_coretext_shaper_shape_plan_data_t *
+hb_coretext_shape_plan_data_t *
 _hb_coretext_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,
 					     const int          *coords HB_UNUSED,
 					     unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_coretext_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
  * shaper
  */
 
@@ -1324,79 +1324,79 @@ fail:
 
 HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, face)
 HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, font)
 
 /*
  * shaper face data
  */
 
-struct hb_coretext_aat_shaper_face_data_t {};
+struct hb_coretext_aat_face_data_t {};
 
-hb_coretext_aat_shaper_face_data_t *
+hb_coretext_aat_face_data_t *
 _hb_coretext_aat_shaper_face_data_create (hb_face_t *face)
 {
   static const hb_tag_t tags[] = {HB_CORETEXT_TAG_MORX, HB_CORETEXT_TAG_MORT, HB_CORETEXT_TAG_KERX};
 
   for (unsigned int i = 0; i < ARRAY_LENGTH (tags); i++)
   {
     hb_blob_t *blob = face->reference_table (tags[i]);
     if (hb_blob_get_length (blob))
     {
       hb_blob_destroy (blob);
-      return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
+      return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
     }
     hb_blob_destroy (blob);
   }
 
   return nullptr;
 }
 
 void
-_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t *data HB_UNUSED)
+_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_face_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
  * shaper font data
  */
 
-struct hb_coretext_aat_shaper_font_data_t {};
+struct hb_coretext_aat_font_data_t {};
 
-hb_coretext_aat_shaper_font_data_t *
+hb_coretext_aat_font_data_t *
 _hb_coretext_aat_shaper_font_data_create (hb_font_t *font)
 {
-  return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
+  return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
 }
 
 void
-_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t *data HB_UNUSED)
+_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_font_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
  * shaper shape_plan data
  */
 
-struct hb_coretext_aat_shaper_shape_plan_data_t {};
+struct hb_coretext_aat_shape_plan_data_t {};
 
-hb_coretext_aat_shaper_shape_plan_data_t *
+hb_coretext_aat_shape_plan_data_t *
 _hb_coretext_aat_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,
 					     const int          *coords HB_UNUSED,
 					     unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_coretext_aat_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_coretext_aat_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
  * shaper
  */
 
--- a/gfx/harfbuzz/src/hb-debug.hh
+++ b/gfx/harfbuzz/src/hb-debug.hh
@@ -23,23 +23,65 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_DEBUG_HH
 #define HB_DEBUG_HH
 
 #include "hb-private.hh"
+#include "hb-atomic-private.hh"
 #include "hb-dsalgs.hh"
 
 
 #ifndef HB_DEBUG
 #define HB_DEBUG 0
 #endif
 
+
+/*
+ * Global runtime options.
+ */
+
+struct hb_options_t
+{
+  unsigned int unused : 1; /* In-case sign bit is here. */
+  unsigned int initialized : 1;
+  unsigned int uniscribe_bug_compatible : 1;
+};
+
+union hb_options_union_t {
+  int i;
+  hb_options_t opts;
+};
+static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), "");
+
+HB_INTERNAL void
+_hb_options_init (void);
+
+extern HB_INTERNAL hb_atomic_int_t _hb_options;
+
+static inline hb_options_t
+hb_options (void)
+{
+  /* Make a local copy, so we can access bitfield threadsafely. */
+  hb_options_union_t u;
+  u.i = _hb_options.get_relaxed ();
+
+  if (unlikely (!u.i))
+    _hb_options_init ();
+
+  return u.opts;
+}
+
+
+/*
+ * Debug output (needs enabling at compile time.)
+ */
+
 static inline bool
 _hb_debug (unsigned int level,
 	   unsigned int max_level)
 {
   return level < max_level;
 }
 
 #define DEBUG_LEVEL_ENABLED(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT))
--- a/gfx/harfbuzz/src/hb-directwrite.cc
+++ b/gfx/harfbuzz/src/hb-directwrite.cc
@@ -128,30 +128,30 @@ public:
   }
 };
 
 
 /*
 * shaper face data
 */
 
-struct hb_directwrite_shaper_face_data_t
+struct hb_directwrite_face_data_t
 {
   IDWriteFactory *dwriteFactory;
   IDWriteFontFile *fontFile;
   IDWriteFontFileStream *fontFileStream;
   IDWriteFontFileLoader *fontFileLoader;
   IDWriteFontFace *fontFace;
   hb_blob_t *faceBlob;
 };
 
-hb_directwrite_shaper_face_data_t *
+hb_directwrite_face_data_t *
 _hb_directwrite_shaper_face_data_create (hb_face_t *face)
 {
-  hb_directwrite_shaper_face_data_t *data = new hb_directwrite_shaper_face_data_t;
+  hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t;
   if (unlikely (!data))
     return nullptr;
 
   // TODO: factory and fontFileLoader should be cached separately
   IDWriteFactory* dwriteFactory;
   DWriteCreateFactory (
     DWRITE_FACTORY_TYPE_SHARED,
     __uuidof (IDWriteFactory),
@@ -201,17 +201,17 @@ hb_directwrite_shaper_face_data_t *
   data->fontFileLoader = fontFileLoader;
   data->fontFace = fontFace;
   data->faceBlob = blob;
 
   return data;
 }
 
 void
-_hb_directwrite_shaper_face_data_destroy (hb_directwrite_shaper_face_data_t *data)
+_hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data)
 {
   if (data->fontFace)
     data->fontFace->Release ();
   if (data->fontFile)
     data->fontFile->Release ();
   if (data->dwriteFactory)
   {
     if (data->fontFileLoader)
@@ -228,57 +228,57 @@ void
     delete data;
 }
 
 
 /*
  * shaper font data
  */
 
-struct hb_directwrite_shaper_font_data_t
+struct hb_directwrite_font_data_t
 {
 };
 
-hb_directwrite_shaper_font_data_t *
+hb_directwrite_font_data_t *
 _hb_directwrite_shaper_font_data_create (hb_font_t *font)
 {
   if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return nullptr;
 
-  hb_directwrite_shaper_font_data_t *data = new hb_directwrite_shaper_font_data_t;
+  hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t;
   if (unlikely (!data))
     return nullptr;
 
   return data;
 }
 
 void
-_hb_directwrite_shaper_font_data_destroy (hb_directwrite_shaper_font_data_t *data)
+_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
 {
   delete data;
 }
 
 
 /*
  * shaper shape_plan data
  */
 
-struct hb_directwrite_shaper_shape_plan_data_t {};
+struct hb_directwrite_shape_plan_data_t {};
 
-hb_directwrite_shaper_shape_plan_data_t *
+hb_directwrite_shape_plan_data_t *
 _hb_directwrite_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,
 					       const int          *coords HB_UNUSED,
 					       unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_directwrite_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_directwrite_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
 // Most of TextAnalysis is originally written by Bas Schouten for Mozilla project
 // but now is relicensed to MIT for HarfBuzz use
 class TextAnalysis
   : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
 {
@@ -550,18 +550,18 @@ static hb_bool_t
 _hb_directwrite_shape_full (hb_shape_plan_t    *shape_plan,
   hb_font_t          *font,
   hb_buffer_t        *buffer,
   const hb_feature_t *features,
   unsigned int        num_features,
   float               lineWidth)
 {
   hb_face_t *face = font->face;
-  hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-  hb_directwrite_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
+  hb_directwrite_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
+  hb_directwrite_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
   IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
   IDWriteFontFace *fontFace = face_data->fontFace;
 
   IDWriteTextAnalyzer* analyzer;
   dwriteFactory->CreateTextAnalyzer (&analyzer);
 
   unsigned int scratch_size;
   hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
--- a/gfx/harfbuzz/src/hb-dsalgs.hh
+++ b/gfx/harfbuzz/src/hb-dsalgs.hh
@@ -227,16 +227,28 @@ hb_ctz (T v)
   return 0; /* Shut up stupid compiler. */
 }
 
 
 /*
  * Tiny stuff.
  */
 
+/* ASCII tag/character handling */
+static inline bool ISALPHA (unsigned char c)
+{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
+static inline bool ISALNUM (unsigned char c)
+{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); }
+static inline bool ISSPACE (unsigned char c)
+{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; }
+static inline unsigned char TOUPPER (unsigned char c)
+{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; }
+static inline unsigned char TOLOWER (unsigned char c)
+{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; }
+
 #undef MIN
 template <typename Type>
 static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
 
 #undef MAX
 template <typename Type>
 static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
 
@@ -257,16 +269,47 @@ hb_unsigned_mul_overflows (unsigned int 
 }
 
 static inline unsigned int
 hb_ceil_to_4 (unsigned int v)
 {
   return ((v - 1) | 3) + 1;
 }
 
+template <typename T> class hb_assert_unsigned_t;
+template <> class hb_assert_unsigned_t<unsigned char> {};
+template <> class hb_assert_unsigned_t<unsigned short> {};
+template <> class hb_assert_unsigned_t<unsigned int> {};
+template <> class hb_assert_unsigned_t<unsigned long> {};
+
+template <typename T> static inline bool
+hb_in_range (T u, T lo, T hi)
+{
+  /* The sizeof() is here to force template instantiation.
+   * I'm sure there are better ways to do this but can't think of
+   * one right now.  Declaring a variable won't work as HB_UNUSED
+   * is unusable on some platforms and unused types are less likely
+   * to generate a warning than unused variables. */
+  static_assert ((sizeof (hb_assert_unsigned_t<T>) >= 0), "");
+
+  /* The casts below are important as if T is smaller than int,
+   * the subtract results will become a signed int! */
+  return (T)(u - lo) <= (T)(hi - lo);
+}
+template <typename T> static inline bool
+hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2)
+{
+  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2);
+}
+template <typename T> static inline bool
+hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
+{
+  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
+}
+
 
 /*
  * Sort and search.
  */
 
 static inline void *
 hb_bsearch_r (const void *key, const void *base,
 	      size_t nmemb, size_t size,
@@ -440,315 +483,16 @@ hb_codepoint_parse (const char *s, unsig
   unsigned long v = strtoul (buf, &end, base);
   if (errno) return false;
   if (*end) return false;
   *out = v;
   return true;
 }
 
 
-#define HB_VECTOR_INIT {0, 0, false, nullptr}
-template <typename Type, unsigned int StaticSize=8>
-struct hb_vector_t
-{
-  unsigned int len;
-  unsigned int allocated;
-  bool successful;
-  Type *arrayZ;
-  Type static_array[StaticSize];
-
-  void init (void)
-  {
-    len = 0;
-    allocated = ARRAY_LENGTH (static_array);
-    successful = true;
-    arrayZ = static_array;
-  }
-
-  inline Type& operator [] (unsigned int i)
-  {
-    if (unlikely (i >= len))
-      return Crap (Type);
-    return arrayZ[i];
-  }
-  inline const Type& operator [] (unsigned int i) const
-  {
-    if (unlikely (i >= len))
-      return Null(Type);
-    return arrayZ[i];
-  }
-
-  inline Type *push (void)
-  {
-    if (unlikely (!resize (len + 1)))
-      return &Crap(Type);
-    return &arrayZ[len - 1];
-  }
-  inline Type *push (const Type& v)
-  {
-    Type *p = push ();
-    *p = v;
-    return p;
-  }
-
-  /* Allocate for size but don't adjust len. */
-  inline bool alloc (unsigned int size)
-  {
-    if (unlikely (!successful))
-      return false;
-
-    if (likely (size <= allocated))
-      return true;
-
-    /* Reallocate */
-
-    unsigned int new_allocated = allocated;
-    while (size >= new_allocated)
-      new_allocated += (new_allocated >> 1) + 8;
-
-    Type *new_array = nullptr;
-
-    if (arrayZ == static_array)
-    {
-      new_array = (Type *) calloc (new_allocated, sizeof (Type));
-      if (new_array)
-        memcpy (new_array, arrayZ, len * sizeof (Type));
-    }
-    else
-    {
-      bool overflows = (new_allocated < allocated) || hb_unsigned_mul_overflows (new_allocated, sizeof (Type));
-      if (likely (!overflows))
-        new_array = (Type *) realloc (arrayZ, new_allocated * sizeof (Type));
-    }
-
-    if (unlikely (!new_array))
-    {
-      successful = false;
-      return false;
-    }
-
-    arrayZ = new_array;
-    allocated = new_allocated;
-
-    return true;
-  }
-
-  inline bool resize (int size_)
-  {
-    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
-    if (!alloc (size))
-      return false;
-
-    if (size > len)
-      memset (arrayZ + len, 0, (size - len) * sizeof (*arrayZ));
-
-    len = size;
-    return true;
-  }
-
-  inline void pop (void)
-  {
-    if (!len) return;
-    len--;
-  }
-
-  inline void remove (unsigned int i)
-  {
-     if (unlikely (i >= len))
-       return;
-     memmove (static_cast<void *> (&arrayZ[i]),
-	      static_cast<void *> (&arrayZ[i + 1]),
-	      (len - i - 1) * sizeof (Type));
-     len--;
-  }
-
-  inline void shrink (int size_)
-  {
-    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
-     if (size < len)
-       len = size;
-  }
-
-  template <typename T>
-  inline Type *find (T v) {
-    for (unsigned int i = 0; i < len; i++)
-      if (arrayZ[i] == v)
-	return &arrayZ[i];
-    return nullptr;
-  }
-  template <typename T>
-  inline const Type *find (T v) const {
-    for (unsigned int i = 0; i < len; i++)
-      if (arrayZ[i] == v)
-	return &arrayZ[i];
-    return nullptr;
-  }
-
-  inline void qsort (int (*cmp)(const void*, const void*))
-  {
-    ::qsort (arrayZ, len, sizeof (Type), cmp);
-  }
-
-  inline void qsort (void)
-  {
-    ::qsort (arrayZ, len, sizeof (Type), Type::cmp);
-  }
-
-  inline void qsort (unsigned int start, unsigned int end)
-  {
-    ::qsort (arrayZ + start, end - start, sizeof (Type), Type::cmp);
-  }
-
-  template <typename T>
-  inline Type *lsearch (const T &x)
-  {
-    for (unsigned int i = 0; i < len; i++)
-      if (0 == this->arrayZ[i].cmp (&x))
-	return &arrayZ[i];
-    return nullptr;
-  }
-
-  template <typename T>
-  inline Type *bsearch (const T &x)
-  {
-    unsigned int i;
-    return bfind (x, &i) ? &arrayZ[i] : nullptr;
-  }
-  template <typename T>
-  inline const Type *bsearch (const T &x) const
-  {
-    unsigned int i;
-    return bfind (x, &i) ? &arrayZ[i] : nullptr;
-  }
-  template <typename T>
-  inline bool bfind (const T &x, unsigned int *i) const
-  {
-    int min = 0, max = (int) this->len - 1;
-    while (min <= max)
-    {
-      int mid = (min + max) / 2;
-      int c = this->arrayZ[mid].cmp (&x);
-      if (c < 0)
-        max = mid - 1;
-      else if (c > 0)
-        min = mid + 1;
-      else
-      {
-        *i = mid;
-	return true;
-      }
-    }
-    if (max < 0 || (max < (int) this->len && this->arrayZ[max].cmp (&x) > 0))
-      max++;
-    *i = max;
-    return false;
-  }
-
-  inline void fini (void)
-  {
-    if (arrayZ != static_array)
-      free (arrayZ);
-    arrayZ = nullptr;
-    allocated = len = 0;
-  }
-};
-
-
-#define HB_LOCKABLE_SET_INIT {HB_VECTOR_INIT}
-template <typename item_t, typename lock_t>
-struct hb_lockable_set_t
-{
-  hb_vector_t <item_t, 1> items;
-
-  inline void init (void) { items.init (); }
-
-  template <typename T>
-  inline item_t *replace_or_insert (T v, lock_t &l, bool replace)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item) {
-      if (replace) {
-	item_t old = *item;
-	*item = v;
-	l.unlock ();
-	old.fini ();
-      }
-      else {
-        item = nullptr;
-	l.unlock ();
-      }
-    } else {
-      item = items.push (v);
-      l.unlock ();
-    }
-    return item;
-  }
-
-  template <typename T>
-  inline void remove (T v, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item) {
-      item_t old = *item;
-      *item = items[items.len - 1];
-      items.pop ();
-      l.unlock ();
-      old.fini ();
-    } else {
-      l.unlock ();
-    }
-  }
-
-  template <typename T>
-  inline bool find (T v, item_t *i, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item)
-      *i = *item;
-    l.unlock ();
-    return !!item;
-  }
-
-  template <typename T>
-  inline item_t *find_or_insert (T v, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (!item) {
-      item = items.push (v);
-    }
-    l.unlock ();
-    return item;
-  }
-
-  inline void fini (lock_t &l)
-  {
-    if (!items.len) {
-      /* No need for locking. */
-      items.fini ();
-      return;
-    }
-    l.lock ();
-    while (items.len) {
-      item_t old = items[items.len - 1];
-	items.pop ();
-	l.unlock ();
-	old.fini ();
-	l.lock ();
-    }
-    items.fini ();
-    l.unlock ();
-  }
-
-};
-
-
 template <typename Type>
 struct hb_auto_t : Type
 {
   hb_auto_t (void) { Type::init (); }
   ~hb_auto_t (void) { Type::fini (); }
   private: /* Hide */
   void init (void) {}
   void fini (void) {}
@@ -849,16 +593,15 @@ struct hb_vector_size_t
     return r;
   }
 
   private:
   static_assert (byte_size / sizeof (elt_t) * sizeof (elt_t) == byte_size, "");
   union {
     elt_t v[byte_size / sizeof (elt_t)];
 #if HB_VECTOR_SIZE
-    typedef unsigned long vec_t __attribute__((vector_size (HB_VECTOR_SIZE / 8)));
-    vec_t vec[byte_size / sizeof (vec_t)];
+    hb_vector_size_impl_t vec[byte_size / sizeof (hb_vector_size_impl_t)];
 #endif
   } u;
 };
 
 
 #endif /* HB_DSALGS_HH */
--- a/gfx/harfbuzz/src/hb-face-private.hh
+++ b/gfx/harfbuzz/src/hb-face-private.hh
@@ -34,42 +34,40 @@
 #include "hb-shaper-private.hh"
 #include "hb-shape-plan-private.hh"
 
 
 /*
  * hb_face_t
  */
 
-struct hb_face_t {
+struct hb_face_t
+{
   hb_object_header_t header;
   ASSERT_POD ();
 
   hb_bool_t immutable;
 
   hb_reference_table_func_t  reference_table_func;
   void                      *user_data;
   hb_destroy_func_t          destroy;
 
   unsigned int index;			/* Face index in a collection, zero-based. */
   mutable unsigned int upem;		/* Units-per-EM. */
   mutable unsigned int num_glyphs;	/* Number of glyphs. */
 
   struct hb_shaper_data_t shaper_data;	/* Various shaper data. */
 
-  /* Various non-shaping data. */
-  /* ... */
-
   /* Cache */
-  struct plan_node_t {
+  struct plan_node_t
+  {
     hb_shape_plan_t *shape_plan;
     plan_node_t *next;
   } *shape_plans;
 
-
   inline hb_blob_t *reference_table (hb_tag_t tag) const
   {
     hb_blob_t *blob;
 
     if (unlikely (!reference_table_func))
       return hb_blob_get_empty ();
 
     blob = reference_table_func (/*XXX*/const_cast<hb_face_t *> (this), tag, user_data);
@@ -92,18 +90,17 @@ struct hb_face_t {
       load_num_glyphs ();
     return num_glyphs;
   }
 
   private:
   HB_INTERNAL void load_upem (void) const;
   HB_INTERNAL void load_num_glyphs (void) const;
 };
-
-extern HB_INTERNAL const hb_face_t _hb_face_nil;
+DECLARE_NULL_INSTANCE (hb_face_t);
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, face);
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
 #undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 
 
--- a/gfx/harfbuzz/src/hb-face.cc
+++ b/gfx/harfbuzz/src/hb-face.cc
@@ -26,19 +26,16 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
 #include "hb-face-private.hh"
 #include "hb-blob-private.hh"
 #include "hb-open-file-private.hh"
-#include "hb-ot-head-table.hh"
-#include "hb-ot-maxp-table.hh"
-
 
 
 /**
  * hb_face_count: Get number of faces on the blob
  * @blob:
  *
  *
  *
@@ -47,27 +44,32 @@
  * Since: 1.7.7
  **/
 unsigned int
 hb_face_count (hb_blob_t *blob)
 {
   if (unlikely (!blob))
     return 0;
 
-  hb_blob_t *sanitized = OT::Sanitizer<OT::OpenTypeFontFile> ().sanitize (blob);
+  /* TODO We shouldn't be sanitizing blob.  Port to run sanitizer and return if not sane. */
+  /* Make API signature const after. */
+  hb_blob_t *sanitized = hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (hb_blob_reference (blob));
   const OT::OpenTypeFontFile& ot = *sanitized->as<OT::OpenTypeFontFile> ();
+  unsigned int ret = ot.get_face_count ();
+  hb_blob_destroy (sanitized);
 
-  return ot.get_face_count ();
+  return ret;
 }
 
 /*
  * hb_face_t
  */
 
-const hb_face_t _hb_face_nil = {
+DEFINE_NULL_INSTANCE (hb_face_t) =
+{
   HB_OBJECT_HEADER_STATIC,
 
   true, /* immutable */
 
   nullptr, /* reference_table_func */
   nullptr, /* user_data */
   nullptr, /* destroy */
 
@@ -183,17 +185,17 @@ hb_face_t *
 hb_face_create (hb_blob_t    *blob,
 		unsigned int  index)
 {
   hb_face_t *face;
 
   if (unlikely (!blob))
     blob = hb_blob_get_empty ();
 
-  hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer<OT::OpenTypeFontFile>().sanitize (hb_blob_reference (blob)), index);
+  hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (hb_blob_reference (blob)), index);
 
   if (unlikely (!closure))
     return hb_face_get_empty ();
 
   face = hb_face_create_for_tables (_hb_face_for_data_reference_table,
 				    closure,
 				    _hb_face_for_data_closure_destroy);
 
@@ -209,17 +211,17 @@ hb_face_create (hb_blob_t    *blob,
  *
  * Return value: (transfer full)
  *
  * Since: 0.9.2
  **/
 hb_face_t *
 hb_face_get_empty (void)
 {
-  return const_cast<hb_face_t *> (&_hb_face_nil);
+  return const_cast<hb_face_t *> (&Null(hb_face_t));
 }
 
 
 /**
  * hb_face_reference: (skip)
  * @face: a face.
  *
  *
@@ -296,17 +298,17 @@ hb_face_set_user_data (hb_face_t        
  *
  *
  *
  * Return value: (transfer none):
  *
  * Since: 0.9.2
  **/
 void *
-hb_face_get_user_data (hb_face_t          *face,
+hb_face_get_user_data (const hb_face_t    *face,
 		       hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (face, key);
 }
 
 /**
  * hb_face_make_immutable:
  * @face: a face.
@@ -330,17 +332,17 @@ hb_face_make_immutable (hb_face_t *face)
  *
  *
  *
  * Return value:
  *
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_face_is_immutable (hb_face_t *face)
+hb_face_is_immutable (const hb_face_t *face)
 {
   return face->immutable;
 }
 
 
 /**
  * hb_face_reference_table:
  * @face: a face.
@@ -348,18 +350,18 @@ hb_face_is_immutable (hb_face_t *face)
  *
  *
  *
  * Return value: (transfer full):
  *
  * Since: 0.9.2
  **/
 hb_blob_t *
-hb_face_reference_table (hb_face_t *face,
-			 hb_tag_t   tag)
+hb_face_reference_table (const hb_face_t *face,
+			 hb_tag_t tag)
 {
   return face->reference_table (tag);
 }
 
 /**
  * hb_face_reference_blob:
  * @face: a face.
  *
@@ -400,17 +402,17 @@ hb_face_set_index (hb_face_t    *face,
  *
  *
  *
  * Return value:
  *
  * Since: 0.9.2
  **/
 unsigned int
-hb_face_get_index (hb_face_t    *face)
+hb_face_get_index (const hb_face_t *face)
 {
   return face->index;
 }
 
 /**
  * hb_face_set_upem:
  * @face: a face.
  * @upem:
@@ -435,30 +437,21 @@ hb_face_set_upem (hb_face_t    *face,
  *
  *
  *
  * Return value:
  *
  * Since: 0.9.2
  **/
 unsigned int
-hb_face_get_upem (hb_face_t *face)
+hb_face_get_upem (const hb_face_t *face)
 {
   return face->get_upem ();
 }
 
-void
-hb_face_t::load_upem (void) const
-{
-  hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (reference_table (HB_OT_TAG_head));
-  const OT::head *head_table = head_blob->as<OT::head> ();
-  upem = head_table->get_upem ();
-  hb_blob_destroy (head_blob);
-}
-
 /**
  * hb_face_set_glyph_count:
  * @face: a face.
  * @glyph_count:
  *
  *
  *
  * Since: 0.9.7
@@ -479,42 +472,33 @@ hb_face_set_glyph_count (hb_face_t    *f
  *
  *
  *
  * Return value:
  *
  * Since: 0.9.7
  **/
 unsigned int
-hb_face_get_glyph_count (hb_face_t *face)
+hb_face_get_glyph_count (const 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 = maxp_blob->as<OT::maxp> ();
-  num_glyphs = maxp_table->get_num_glyphs ();
-  hb_blob_destroy (maxp_blob);
-}
-
 /**
  * hb_face_get_table_tags:
  * @face: a face.
  *
  * Retrieves table tags for a face, if possible.
  *
  * Return value: total number of tables, or 0 if not possible to list.
  *
  * Since: 1.6.0
  **/
 unsigned int
-hb_face_get_table_tags (hb_face_t    *face,
+hb_face_get_table_tags (const hb_face_t *face,
 			unsigned int  start_offset,
 			unsigned int *table_count, /* IN/OUT */
 			hb_tag_t     *table_tags /* OUT */)
 {
   if (face->destroy != (hb_destroy_func_t) _hb_face_for_data_closure_destroy)
   {
     if (table_count)
       *table_count = 0;
--- a/gfx/harfbuzz/src/hb-face.h
+++ b/gfx/harfbuzz/src/hb-face.h
@@ -71,55 +71,55 @@ hb_face_destroy (hb_face_t *face);
 HB_EXTERN hb_bool_t
 hb_face_set_user_data (hb_face_t          *face,
 		       hb_user_data_key_t *key,
 		       void *              data,
 		       hb_destroy_func_t   destroy,
 		       hb_bool_t           replace);
 
 HB_EXTERN void *
-hb_face_get_user_data (hb_face_t          *face,
+hb_face_get_user_data (const hb_face_t    *face,
 		       hb_user_data_key_t *key);
 
 HB_EXTERN void
 hb_face_make_immutable (hb_face_t *face);
 
 HB_EXTERN hb_bool_t
-hb_face_is_immutable (hb_face_t *face);
+hb_face_is_immutable (const hb_face_t *face);
 
 
 HB_EXTERN hb_blob_t *
-hb_face_reference_table (hb_face_t *face,
-			 hb_tag_t   tag);
+hb_face_reference_table (const hb_face_t *face,
+			 hb_tag_t tag);
 
 HB_EXTERN hb_blob_t *
 hb_face_reference_blob (hb_face_t *face);
 
 HB_EXTERN void
 hb_face_set_index (hb_face_t    *face,
 		   unsigned int  index);
 
 HB_EXTERN unsigned int
-hb_face_get_index (hb_face_t    *face);
+hb_face_get_index (const hb_face_t *face);
 
 HB_EXTERN void
 hb_face_set_upem (hb_face_t    *face,
 		  unsigned int  upem);
 
 HB_EXTERN unsigned int
-hb_face_get_upem (hb_face_t *face);
+hb_face_get_upem (const hb_face_t *face);
 
 HB_EXTERN void
 hb_face_set_glyph_count (hb_face_t    *face,
 			 unsigned int  glyph_count);
 
 HB_EXTERN unsigned int
-hb_face_get_glyph_count (hb_face_t *face);
+hb_face_get_glyph_count (const hb_face_t *face);
 
 HB_EXTERN unsigned int
-hb_face_get_table_tags (hb_face_t    *face,
+hb_face_get_table_tags (const hb_face_t *face,
 			unsigned int  start_offset,
 			unsigned int *table_count, /* IN/OUT */
 			hb_tag_t     *table_tags /* OUT */);
 
 HB_END_DECLS
 
 #endif /* HB_FACE_H */
--- a/gfx/harfbuzz/src/hb-fallback-shape.cc
+++ b/gfx/harfbuzz/src/hb-fallback-shape.cc
@@ -31,66 +31,66 @@
 HB_SHAPER_DATA_ENSURE_DEFINE(fallback, face)
 HB_SHAPER_DATA_ENSURE_DEFINE(fallback, font)
 
 
 /*
  * shaper face data
  */
 
-struct hb_fallback_shaper_face_data_t {};
+struct hb_fallback_face_data_t {};
 
-hb_fallback_shaper_face_data_t *
+hb_fallback_face_data_t *
 _hb_fallback_shaper_face_data_create (hb_face_t *face HB_UNUSED)
 {
-  return (hb_fallback_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_fallback_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data HB_UNUSED)
+_hb_fallback_shaper_face_data_destroy (hb_fallback_face_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
  * shaper font data
  */
 
-struct hb_fallback_shaper_font_data_t {};
+struct hb_fallback_font_data_t {};
 
-hb_fallback_shaper_font_data_t *
+hb_fallback_font_data_t *
 _hb_fallback_shaper_font_data_create (hb_font_t *font HB_UNUSED)
 {
-  return (hb_fallback_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_fallback_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_font_data_destroy (hb_fallback_shaper_font_data_t *data HB_UNUSED)
+_hb_fallback_shaper_font_data_destroy (hb_fallback_font_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
  * shaper shape_plan data
  */
 
-struct hb_fallback_shaper_shape_plan_data_t {};
+struct hb_fallback_shape_plan_data_t {};
 
-hb_fallback_shaper_shape_plan_data_t *
+hb_fallback_shape_plan_data_t *
 _hb_fallback_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,
 					    const int          *coords HB_UNUSED,
 					    unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_fallback_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_fallback_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
  * shaper
  */
 
--- a/gfx/harfbuzz/src/hb-font-private.hh
+++ b/gfx/harfbuzz/src/hb-font-private.hh
@@ -30,39 +30,41 @@
 #define HB_FONT_PRIVATE_HH
 
 #include "hb-private.hh"
 
 #include "hb-face-private.hh"
 #include "hb-shaper-private.hh"
 
 
-
 /*
  * hb_font_funcs_t
  */
 
 #define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \
   HB_FONT_FUNC_IMPLEMENT (font_h_extents) \
   HB_FONT_FUNC_IMPLEMENT (font_v_extents) \
   HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \
   HB_FONT_FUNC_IMPLEMENT (variation_glyph) \
   HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \
   HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_h_advances) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_v_advances) \
   HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \
   HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \
   HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \
   HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning) \
   HB_FONT_FUNC_IMPLEMENT (glyph_extents) \
   HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \
   HB_FONT_FUNC_IMPLEMENT (glyph_name) \
   HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \
   /* ^--- Add new callbacks here */
 
-struct hb_font_funcs_t {
+struct hb_font_funcs_t
+{
   hb_object_header_t header;
   ASSERT_POD ();
 
   hb_bool_t immutable;
 
   struct {
 #define HB_FONT_FUNC_IMPLEMENT(name) void *name;
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
@@ -84,24 +86,25 @@ struct hb_font_funcs_t {
     } f;
     void (*array[0
 #define HB_FONT_FUNC_IMPLEMENT(name) +1
       HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
 		]) (void);
   } get;
 };
-
+DECLARE_NULL_INSTANCE (hb_font_funcs_t);
 
 
 /*
  * hb_font_t
  */
 
-struct hb_font_t {
+struct hb_font_t
+{
   hb_object_header_t header;
   ASSERT_POD ();
 
   hb_bool_t immutable;
 
   hb_font_t *parent;
   hb_face_t *face;
 
@@ -228,16 +231,42 @@ struct hb_font_t {
 
   inline hb_position_t get_glyph_v_advance (hb_codepoint_t glyph)
   {
     return klass->get.f.glyph_v_advance (this, user_data,
 					 glyph,
 					 klass->user_data.glyph_v_advance);
   }
 
+  inline void get_glyph_h_advances (unsigned int count,
+				    hb_codepoint_t *first_glyph,
+				    unsigned int glyph_stride,
+				    hb_position_t *first_advance,
+				    unsigned int advance_stride)
+  {
+    return klass->get.f.glyph_h_advances (this, user_data,
+					  count,
+					  first_glyph, glyph_stride,
+					  first_advance, advance_stride,
+					  klass->user_data.glyph_h_advances);
+  }
+
+  inline void get_glyph_v_advances (unsigned int count,
+				    hb_codepoint_t *first_glyph,
+				    unsigned int glyph_stride,
+				    hb_position_t *first_advance,
+				    unsigned int advance_stride)
+  {
+    return klass->get.f.glyph_v_advances (this, user_data,
+					  count,
+					  first_glyph, glyph_stride,
+					  first_advance, advance_stride,
+					  klass->user_data.glyph_v_advances);
+  }
+
   inline hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph,
 				       hb_position_t *x, hb_position_t *y)
   {
     *x = *y = 0;
     return klass->get.f.glyph_h_origin (this, user_data,
 					glyph, x, y,
 					klass->user_data.glyph_h_origin);
   }
@@ -336,23 +365,33 @@ struct hb_font_t {
     else
       get_v_extents_with_fallback (extents);
   }
 
   inline void get_glyph_advance_for_direction (hb_codepoint_t glyph,
 					       hb_direction_t direction,
 					       hb_position_t *x, hb_position_t *y)
   {
-    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) {
+    *x = *y = 0;
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
       *x = get_glyph_h_advance (glyph);
-      *y = 0;
-    } else {
-      *x = 0;
+    else
       *y = get_glyph_v_advance (glyph);
-    }
+  }
+  inline void get_glyph_advances_for_direction (hb_direction_t direction,
+						unsigned count,
+						hb_codepoint_t *first_glyph,
+						unsigned glyph_stride,
+						hb_position_t *first_advance,
+						unsigned advance_stride)
+  {
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+      get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
+    else
+      get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
   }
 
   inline void guess_v_origin_minus_h_origin (hb_codepoint_t glyph,
 					     hb_position_t *x, hb_position_t *y)
   {
     *x = get_glyph_h_advance (glyph) / 2;
 
     /* TODO cache this somehow?! */
@@ -548,16 +587,17 @@ struct hb_font_t {
   {
     return (hb_position_t) round (v * scale / face->get_upem ());
   }
   inline float em_fscale (int16_t v, int scale)
   {
     return (float) v * scale / face->get_upem ();
   }
 };
+DECLARE_NULL_INSTANCE (hb_font_t);
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font);
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
 #undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 
 
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -24,36 +24,37 @@
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
 #include "hb-font-private.hh"
+#include "hb-machinery-private.hh"
 
 
 /*
  * hb_font_funcs_t
  */
 
 static hb_bool_t
 hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED,
 				void *font_data HB_UNUSED,
 				hb_font_extents_t *metrics,
 				void *user_data HB_UNUSED)
 {
   memset (metrics, 0, sizeof (*metrics));
   return false;
 }
 static hb_bool_t
-hb_font_get_font_h_extents_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_font_extents_t *metrics,
-				   void *user_data HB_UNUSED)
+hb_font_get_font_h_extents_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_font_extents_t *metrics,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_font_h_extents (metrics);
   if (ret) {
     metrics->ascender = font->parent_scale_y_distance (metrics->ascender);
     metrics->descender = font->parent_scale_y_distance (metrics->descender);
     metrics->line_gap = font->parent_scale_y_distance (metrics->line_gap);
   }
   return ret;
@@ -64,20 +65,20 @@ hb_font_get_font_v_extents_nil (hb_font_
 				void *font_data HB_UNUSED,
 				hb_font_extents_t *metrics,
 				void *user_data HB_UNUSED)
 {
   memset (metrics, 0, sizeof (*metrics));
   return false;
 }
 static hb_bool_t
-hb_font_get_font_v_extents_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_font_extents_t *metrics,
-				   void *user_data HB_UNUSED)
+hb_font_get_font_v_extents_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_font_extents_t *metrics,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_font_v_extents (metrics);
   if (ret) {
     metrics->ascender = font->parent_scale_x_distance (metrics->ascender);
     metrics->descender = font->parent_scale_x_distance (metrics->descender);
     metrics->line_gap = font->parent_scale_x_distance (metrics->line_gap);
   }
   return ret;
@@ -89,101 +90,165 @@ hb_font_get_nominal_glyph_nil (hb_font_t
 			       hb_codepoint_t unicode,
 			       hb_codepoint_t *glyph,
 			       void *user_data HB_UNUSED)
 {
   *glyph = 0;
   return false;
 }
 static hb_bool_t
-hb_font_get_nominal_glyph_parent (hb_font_t *font,
-				  void *font_data HB_UNUSED,
-				  hb_codepoint_t unicode,
-				  hb_codepoint_t *glyph,
-				  void *user_data HB_UNUSED)
+hb_font_get_nominal_glyph_default (hb_font_t *font,
+				   void *font_data HB_UNUSED,
+				   hb_codepoint_t unicode,
+				   hb_codepoint_t *glyph,
+				   void *user_data HB_UNUSED)
 {
   return font->parent->get_nominal_glyph (unicode, glyph);
 }
 
 static hb_bool_t
 hb_font_get_variation_glyph_nil (hb_font_t *font HB_UNUSED,
 				 void *font_data HB_UNUSED,
 				 hb_codepoint_t unicode,
 				 hb_codepoint_t variation_selector,
 				 hb_codepoint_t *glyph,
 				 void *user_data HB_UNUSED)
 {
   *glyph = 0;
   return false;
 }
 static hb_bool_t
-hb_font_get_variation_glyph_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t unicode,
-				    hb_codepoint_t variation_selector,
-				    hb_codepoint_t *glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_variation_glyph_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t unicode,
+				     hb_codepoint_t variation_selector,
+				     hb_codepoint_t *glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent->get_variation_glyph (unicode, variation_selector, glyph);
 }
 
 
 static hb_position_t
 hb_font_get_glyph_h_advance_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
-				 hb_codepoint_t glyph,
+				 hb_codepoint_t glyph HB_UNUSED,
 				 void *user_data HB_UNUSED)
 {
   return font->x_scale;
 }
 static hb_position_t
-hb_font_get_glyph_h_advance_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_h_advance_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph));
 }
 
 static hb_position_t
 hb_font_get_glyph_v_advance_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
-				 hb_codepoint_t glyph,
+				 hb_codepoint_t glyph HB_UNUSED,
 				 void *user_data HB_UNUSED)
 {
   /* TODO use font_extents.ascender+descender */
   return font->y_scale;
 }
 static hb_position_t
-hb_font_get_glyph_v_advance_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_v_advance_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph));
 }
 
+#define hb_font_get_glyph_h_advances_nil hb_font_get_glyph_h_advances_default
+static void
+hb_font_get_glyph_h_advances_default (hb_font_t* font,
+				      void* font_data HB_UNUSED,
+				      unsigned int count,
+				      hb_codepoint_t *first_glyph,
+				      unsigned int glyph_stride,
+				      hb_position_t *first_advance,
+				      unsigned int advance_stride,
+				      void *user_data HB_UNUSED)
+{
+  if (font->has_glyph_h_advance_func ())
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance = font->get_glyph_h_advance (*first_glyph);
+      first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+    }
+    return;
+  }
+
+  font->parent->get_glyph_h_advances (count,
+				      first_glyph, glyph_stride,
+				      first_advance, advance_stride);
+  for (unsigned int i = 0; i < count; i++)
+  {
+    *first_advance = font->parent_scale_x_distance (*first_advance);
+    first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+  }
+}
+
+#define hb_font_get_glyph_v_advances_nil hb_font_get_glyph_v_advances_default
+static void
+hb_font_get_glyph_v_advances_default (hb_font_t* font,
+				      void* font_data HB_UNUSED,
+				      unsigned int count,
+				      hb_codepoint_t *first_glyph,
+				      unsigned int glyph_stride,
+				      hb_position_t *first_advance,
+				      unsigned int advance_stride,
+				      void *user_data HB_UNUSED)
+{
+  if (font->has_glyph_v_advance_func ())
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance = font->get_glyph_v_advance (*first_glyph);
+      first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+    }
+    return;
+  }
+
+  font->parent->get_glyph_v_advances (count,
+				      first_glyph, glyph_stride,
+				      first_advance, advance_stride);
+  for (unsigned int i = 0; i < count; i++)
+  {
+    *first_advance = font->parent_scale_y_distance (*first_advance);
+    first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+  }
+}
+
 static hb_bool_t
 hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED,
 				void *font_data HB_UNUSED,
 				hb_codepoint_t glyph,
 				hb_position_t *x,
 				hb_position_t *y,
 				void *user_data HB_UNUSED)
 {
   *x = *y = 0;
   return true;
 }
 static hb_bool_t
-hb_font_get_glyph_h_origin_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_codepoint_t glyph,
-				   hb_position_t *x,
-				   hb_position_t *y,
-				   void *user_data HB_UNUSED)
+hb_font_get_glyph_h_origin_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_codepoint_t glyph,
+				    hb_position_t *x,
+				    hb_position_t *y,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y);
   if (ret)
     font->parent_scale_position (x, y);
   return ret;
 }
 
 static hb_bool_t
@@ -193,22 +258,22 @@ hb_font_get_glyph_v_origin_nil (hb_font_
 				hb_position_t *x,
 				hb_position_t *y,
 				void *user_data HB_UNUSED)
 {
   *x = *y = 0;
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_v_origin_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_codepoint_t glyph,
-				   hb_position_t *x,
-				   hb_position_t *y,
-				   void *user_data HB_UNUSED)
+hb_font_get_glyph_v_origin_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_codepoint_t glyph,
+				    hb_position_t *x,
+				    hb_position_t *y,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y);
   if (ret)
     font->parent_scale_position (x, y);
   return ret;
 }
 
 static hb_position_t
@@ -216,60 +281,60 @@ hb_font_get_glyph_h_kerning_nil (hb_font
 				 void *font_data HB_UNUSED,
 				 hb_codepoint_t left_glyph,
 				 hb_codepoint_t right_glyph,
 				 void *user_data HB_UNUSED)
 {
   return 0;
 }
 static hb_position_t
-hb_font_get_glyph_h_kerning_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t left_glyph,
-				    hb_codepoint_t right_glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_h_kerning_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t left_glyph,
+				     hb_codepoint_t right_glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph));
 }
 
 static hb_position_t
 hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED,
 				 void *font_data HB_UNUSED,
 				 hb_codepoint_t top_glyph,
 				 hb_codepoint_t bottom_glyph,
 				 void *user_data HB_UNUSED)
 {
   return 0;
 }
 static hb_position_t
-hb_font_get_glyph_v_kerning_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t top_glyph,
-				    hb_codepoint_t bottom_glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_v_kerning_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t top_glyph,
+				     hb_codepoint_t bottom_glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph));
 }
 
 static hb_bool_t
 hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED,
 			       void *font_data HB_UNUSED,
 			       hb_codepoint_t glyph,
 			       hb_glyph_extents_t *extents,
 			       void *user_data HB_UNUSED)
 {
   memset (extents, 0, sizeof (*extents));
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_extents_parent (hb_font_t *font,
-				  void *font_data HB_UNUSED,
-				  hb_codepoint_t glyph,
-				  hb_glyph_extents_t *extents,
-				  void *user_data HB_UNUSED)
+hb_font_get_glyph_extents_default (hb_font_t *font,
+				   void *font_data HB_UNUSED,
+				   hb_codepoint_t glyph,
+				   hb_glyph_extents_t *extents,
+				   void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents);
   if (ret) {
     font->parent_scale_position (&extents->x_bearing, &extents->y_bearing);
     font->parent_scale_distance (&extents->width, &extents->height);
   }
   return ret;
 }
@@ -282,23 +347,23 @@ hb_font_get_glyph_contour_point_nil (hb_
 				     hb_position_t *x,
 				     hb_position_t *y,
 				     void *user_data HB_UNUSED)
 {
   *x = *y = 0;
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_contour_point_parent (hb_font_t *font,
-					void *font_data HB_UNUSED,
-					hb_codepoint_t glyph,
-					unsigned int point_index,
-					hb_position_t *x,
-					hb_position_t *y,
-					void *user_data HB_UNUSED)
+hb_font_get_glyph_contour_point_default (hb_font_t *font,
+					 void *font_data HB_UNUSED,
+					 hb_codepoint_t glyph,
+					 unsigned int point_index,
+					 hb_position_t *x,
+					 hb_position_t *y,
+					 void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y);
   if (ret)
     font->parent_scale_position (x, y);
   return ret;
 }
 
 static hb_bool_t
@@ -307,46 +372,47 @@ hb_font_get_glyph_name_nil (hb_font_t *f
 			    hb_codepoint_t glyph,
 			    char *name, unsigned int size,
 			    void *user_data HB_UNUSED)
 {
   if (size) *name = '\0';
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_name_parent (hb_font_t *font,
-			       void *font_data HB_UNUSED,
-			       hb_codepoint_t glyph,
-			       char *name, unsigned int size,
-			       void *user_data HB_UNUSED)
+hb_font_get_glyph_name_default (hb_font_t *font,
+				void *font_data HB_UNUSED,
+				hb_codepoint_t glyph,
+				char *name, unsigned int size,
+				void *user_data HB_UNUSED)
 {
   return font->parent->get_glyph_name (glyph, name, size);
 }
 
 static hb_bool_t
 hb_font_get_glyph_from_name_nil (hb_font_t *font HB_UNUSED,
 				 void *font_data HB_UNUSED,
 				 const char *name, int len, /* -1 means nul-terminated */
 				 hb_codepoint_t *glyph,
 				 void *user_data HB_UNUSED)
 {
   *glyph = 0;
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_from_name_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    const char *name, int len, /* -1 means nul-terminated */
-				    hb_codepoint_t *glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_from_name_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     const char *name, int len, /* -1 means nul-terminated */
+				     hb_codepoint_t *glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent->get_glyph_from_name (name, len, glyph);
 }
 
-static const hb_font_funcs_t _hb_font_funcs_nil = {
+DEFINE_NULL_INSTANCE (hb_font_funcs_t) =
+{
   HB_OBJECT_HEADER_STATIC,
 
   true, /* immutable */
 
   {
 #define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
@@ -359,34 +425,35 @@ static const hb_font_funcs_t _hb_font_fu
   {
     {
 #define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil,
       HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
     }
   }
 };
-static const hb_font_funcs_t _hb_font_funcs_parent = {
+
+static const hb_font_funcs_t _hb_font_funcs_default = {
   HB_OBJECT_HEADER_STATIC,
 
   true, /* immutable */
 
   {
 #define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
   },
   {
 #define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
   },
   {
     {
-#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_parent,
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_default,
       HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
     }
   }
 };
 
 
 /**
@@ -401,34 +468,34 @@ static const hb_font_funcs_t _hb_font_fu
 hb_font_funcs_t *
 hb_font_funcs_create (void)
 {
   hb_font_funcs_t *ffuncs;
 
   if (!(ffuncs = hb_object_create<hb_font_funcs_t> ()))
     return hb_font_funcs_get_empty ();
 
-  ffuncs->get = _hb_font_funcs_parent.get;
+  ffuncs->get = _hb_font_funcs_default.get;
 
   return ffuncs;
 }
 
 /**
  * hb_font_funcs_get_empty:
  *
  * 
  *
  * Return value: (transfer full): 
  *
  * Since: 0.9.2
  **/
 hb_font_funcs_t *
 hb_font_funcs_get_empty (void)
 {
-  return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_parent);
+  return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_default);
 }
 
 /**
  * hb_font_funcs_reference: (skip)
  * @ffuncs: font functions.
  *
  * 
  *
@@ -557,31 +624,30 @@ hb_font_funcs_set_##name##_func (hb_font
   if (ffuncs->destroy.name)                                              \
     ffuncs->destroy.name (ffuncs->user_data.name);                       \
                                                                          \
   if (func) {                                                            \
     ffuncs->get.f.name = func;                                           \
     ffuncs->user_data.name = user_data;                                  \
     ffuncs->destroy.name = destroy;                                      \
   } else {                                                               \
-    ffuncs->get.f.name = hb_font_get_##name##_parent;                    \
-    ffuncs->user_data.name = nullptr;                                       \
-    ffuncs->destroy.name = nullptr;                                         \
+    ffuncs->get.f.name = hb_font_get_##name##_default;                   \
+    ffuncs->user_data.name = nullptr;                                    \
+    ffuncs->destroy.name = nullptr;                                      \
   }                                                                      \
 }
 
 HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
 
 bool
 hb_font_t::has_func (unsigned int i)
 {
-  if (parent && parent != hb_font_get_empty () && parent->has_func (i))
-    return true;
-  return this->klass->get.array[i] != _hb_font_funcs_parent.get.array[i];
+  return (this->klass->get.array[i] != _hb_font_funcs_default.get.array[i]) ||
+	 (parent && parent != &_hb_Null_hb_font_t && parent->has_func (i));
 }
 
 /* Public getters */
 
 /**
  * hb_font_get_h_extents:
  * @font: a font.
  * @extents: (out):
@@ -713,16 +779,53 @@ hb_font_get_glyph_h_advance (hb_font_t *
 hb_position_t
 hb_font_get_glyph_v_advance (hb_font_t *font,
 			     hb_codepoint_t glyph)
 {
   return font->get_glyph_v_advance (glyph);
 }
 
 /**
+ * hb_font_get_glyph_h_advances:
+ * @font: a font.
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+void
+hb_font_get_glyph_h_advances (hb_font_t* font,
+			      unsigned count,
+			      hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride)
+{
+  font->get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
+}
+/**
+ * hb_font_get_glyph_v_advances:
+ * @font: a font.
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+void
+hb_font_get_glyph_v_advances (hb_font_t* font,
+			      unsigned count,
+			      hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride)
+{
+  font->get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
+}
+
+/**
  * hb_font_get_glyph_h_origin:
  * @font: a font.
  * @glyph: 
  * @x: (out): 
  * @y: (out): 
  *
  * 
  *
@@ -916,16 +1019,36 @@ hb_font_get_extents_for_direction (hb_fo
 void
 hb_font_get_glyph_advance_for_direction (hb_font_t *font,
 					 hb_codepoint_t glyph,
 					 hb_direction_t direction,
 					 hb_position_t *x, hb_position_t *y)
 {
   return font->get_glyph_advance_for_direction (glyph, direction, x, y);
 }
+/**
+ * hb_font_get_glyph_advances_for_direction:
+ * @font: a font.
+ * @direction: 
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+HB_EXTERN void
+hb_font_get_glyph_advances_for_direction (hb_font_t* font,
+					  hb_direction_t direction,
+					  unsigned count,
+					  hb_codepoint_t *first_glyph,
+					  unsigned glyph_stride,
+					  hb_position_t *first_advance,
+					  unsigned advance_stride)
+{
+  font->get_glyph_advances_for_direction (direction, count, first_glyph, glyph_stride, first_advance, advance_stride);
+}
 
 /**
  * hb_font_get_glyph_origin_for_direction:
  * @font: a font.
  * @glyph: 
  * @direction: 
  * @x: (out): 
  * @y: (out): 
@@ -1095,16 +1218,47 @@ hb_font_glyph_from_string (hb_font_t *fo
   return font->glyph_from_string (s, len, glyph);
 }
 
 
 /*
  * hb_font_t
  */
 
+DEFINE_NULL_INSTANCE (hb_font_t) =
+{
+  HB_OBJECT_HEADER_STATIC,
+
+  true, /* immutable */
+
+  nullptr, /* parent */
+  const_cast<hb_face_t *> (&_hb_Null_hb_face_t),
+
+  1000, /* x_scale */
+  1000, /* y_scale */
+
+  0, /* x_ppem */
+  0, /* y_ppem */
+  0, /* ptem */
+
+  0, /* num_coords */
+  nullptr, /* coords */
+
+  const_cast<hb_font_funcs_t *> (&_hb_Null_hb_font_funcs_t), /* klass */
+  nullptr, /* user_data */
+  nullptr, /* destroy */
+
+  {
+#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+  }
+};
+
+
 /**
  * hb_font_create: (Xconstructor)
  * @face: a face.
  *
  * 
  *
  * Return value: (transfer full): 
  *
@@ -1182,46 +1336,17 @@ hb_font_create_sub_font (hb_font_t *pare
  *
  * Return value: (transfer full)
  *
  * Since: 0.9.2
  **/
 hb_font_t *
 hb_font_get_empty (void)
 {
-  static const hb_font_t _hb_font_nil = {
-    HB_OBJECT_HEADER_STATIC,
-
-    true, /* immutable */
-
-    nullptr, /* parent */
-    const_cast<hb_face_t *> (&_hb_face_nil),
-
-    1000, /* x_scale */
-    1000, /* y_scale */
-
-    0, /* x_ppem */
-    0, /* y_ppem */
-    0, /* ptem */
-
-    0, /* num_coords */
-    nullptr, /* coords */
-
-    const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */
-    nullptr, /* user_data */
-    nullptr, /* destroy */
-
-    {
-#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-    }
-  };
-
-  return const_cast<hb_font_t *> (&_hb_font_nil);
+  return const_cast<hb_font_t *> (&Null(hb_font_t));
 }
 
 /**
  * hb_font_reference: (skip)
  * @font: a font.
  *
  * 
  *
--- a/gfx/harfbuzz/src/hb-font.h
+++ b/gfx/harfbuzz/src/hb-font.h
@@ -127,16 +127,26 @@ typedef hb_bool_t (*hb_font_get_variatio
 
 
 typedef hb_position_t (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, void *font_data,
 							   hb_codepoint_t glyph,
 							   void *user_data);
 typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t;
 typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t;
 
+typedef void (*hb_font_get_glyph_advances_func_t) (hb_font_t* font, void* font_data,
+						   unsigned count,
+						   hb_codepoint_t *first_glyph,
+						   unsigned glyph_stride,
+						   hb_position_t *first_advance,
+						   unsigned advance_stride,
+						   void *user_data);
+typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_h_advances_func_t;
+typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t;
+
 typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data,
 						      hb_codepoint_t glyph,
 						      hb_position_t *x, hb_position_t *y,
 						      void *user_data);
 typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t;
 typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t;
 
 typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data,
@@ -260,16 +270,48 @@ hb_font_funcs_set_glyph_h_advance_func (
  * Since: 0.9.2
  **/
 HB_EXTERN void
 hb_font_funcs_set_glyph_v_advance_func (hb_font_funcs_t *ffuncs,
 					hb_font_get_glyph_v_advance_func_t func,
 					void *user_data, hb_destroy_func_t destroy);
 
 /**
+ * hb_font_funcs_set_glyph_h_advances_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_h_advances_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_h_advances_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
+/**
+ * hb_font_funcs_set_glyph_v_advances_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_v_advances_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_v_advances_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
+/**
  * hb_font_funcs_set_glyph_h_origin_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
  * @user_data:
  * @destroy:
  *
  * 
  *
@@ -412,16 +454,31 @@ hb_font_get_variation_glyph (hb_font_t *
 
 HB_EXTERN hb_position_t
 hb_font_get_glyph_h_advance (hb_font_t *font,
 			     hb_codepoint_t glyph);
 HB_EXTERN hb_position_t
 hb_font_get_glyph_v_advance (hb_font_t *font,
 			     hb_codepoint_t glyph);
 
+HB_EXTERN void
+hb_font_get_glyph_h_advances (hb_font_t* font,
+			      unsigned count,
+			      hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride);
+HB_EXTERN void
+hb_font_get_glyph_v_advances (hb_font_t* font,
+			      unsigned count,
+			      hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride);
+
 HB_EXTERN hb_bool_t
 hb_font_get_glyph_h_origin (hb_font_t *font,
 			    hb_codepoint_t glyph,
 			    hb_position_t *x, hb_position_t *y);
 HB_EXTERN hb_bool_t
 hb_font_get_glyph_v_origin (hb_font_t *font,
 			    hb_codepoint_t glyph,
 			    hb_position_t *x, hb_position_t *y);
@@ -467,16 +524,24 @@ hb_font_get_extents_for_direction (hb_fo
 				   hb_direction_t direction,
 				   hb_font_extents_t *extents);
 HB_EXTERN void
 hb_font_get_glyph_advance_for_direction (hb_font_t *font,
 					 hb_codepoint_t glyph,
 					 hb_direction_t direction,
 					 hb_position_t *x, hb_position_t *y);
 HB_EXTERN void
+hb_font_get_glyph_advances_for_direction (hb_font_t* font,
+					  hb_direction_t direction,
+					  unsigned count,
+					  hb_codepoint_t *first_glyph,
+					  unsigned glyph_stride,
+					  hb_position_t *first_advance,
+					  unsigned advance_stride);
+HB_EXTERN void
 hb_font_get_glyph_origin_for_direction (hb_font_t *font,
 					hb_codepoint_t glyph,
 					hb_direction_t direction,
 					hb_position_t *x, hb_position_t *y);
 HB_EXTERN void
 hb_font_add_glyph_origin_for_direction (hb_font_t *font,
 					hb_codepoint_t glyph,
 					hb_direction_t direction,
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -492,17 +492,20 @@ reference_table  (hb_face_t *face HB_UNU
     return nullptr;
 
   buffer = (FT_Byte *) malloc (length);
   if (!buffer)
     return nullptr;
 
   error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length);
   if (error)
+  {
+    free (buffer);
     return nullptr;
+  }
 
   return hb_blob_create ((const char *) buffer, length,
 			 HB_MEMORY_MODE_WRITABLE,
 			 buffer, free);
 }
 
 /**
  * hb_ft_face_create:
--- a/gfx/harfbuzz/src/hb-gobject-structs.cc
+++ b/gfx/harfbuzz/src/hb-gobject-structs.cc
@@ -66,16 +66,17 @@ hb_gobject_##name##_get_type (void) \
 	HB_DEFINE_BOXED_TYPE (name, _hb_##name##_reference, _hb_##name##_destroy);
 
 HB_DEFINE_OBJECT_TYPE (buffer)
 HB_DEFINE_OBJECT_TYPE (blob)
 HB_DEFINE_OBJECT_TYPE (face)
 HB_DEFINE_OBJECT_TYPE (font)
 HB_DEFINE_OBJECT_TYPE (font_funcs)
 HB_DEFINE_OBJECT_TYPE (set)
+HB_DEFINE_OBJECT_TYPE (map)
 HB_DEFINE_OBJECT_TYPE (shape_plan)
 HB_DEFINE_OBJECT_TYPE (unicode_funcs)
 HB_DEFINE_VALUE_TYPE (feature)
 HB_DEFINE_VALUE_TYPE (glyph_info)
 HB_DEFINE_VALUE_TYPE (glyph_position)
 HB_DEFINE_VALUE_TYPE (segment_properties)
 HB_DEFINE_VALUE_TYPE (user_data_key)
 
--- a/gfx/harfbuzz/src/hb-gobject-structs.h
+++ b/gfx/harfbuzz/src/hb-gobject-structs.h
@@ -85,16 +85,20 @@ HB_EXTERN GType
 hb_gobject_font_funcs_get_type (void);
 #define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ())
 
 HB_EXTERN GType
 hb_gobject_set_get_type (void);
 #define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ())
 
 HB_EXTERN GType
+hb_gobject_map_get_type (void);
+#define HB_GOBJECT_TYPE_MAP (hb_gobject_map_get_type ())
+
+HB_EXTERN GType
 hb_gobject_shape_plan_get_type (void);
 #define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ())
 
 /**
  * hb_gobject_unicode_funcs_get_type:
  *
  * Since: 0.9.2
  **/
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -43,25 +43,25 @@ HB_SHAPER_DATA_ENSURE_DEFINE(graphite2, 
  */
 
 typedef struct hb_graphite2_tablelist_t {
   struct hb_graphite2_tablelist_t *next;
   hb_blob_t *blob;
   unsigned int tag;
 } hb_graphite2_tablelist_t;
 
-struct hb_graphite2_shaper_face_data_t {
+struct hb_graphite2_face_data_t {
   hb_face_t *face;
   gr_face   *grface;
   hb_graphite2_tablelist_t *tlist;
 };
 
 static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len)
 {
-  hb_graphite2_shaper_face_data_t *face_data = (hb_graphite2_shaper_face_data_t *) data;
+  hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data;
   hb_graphite2_tablelist_t *tlist = face_data->tlist;
 
   hb_blob_t *blob = nullptr;
 
   for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next)
     if (p->tag == tag) {
       blob = p->blob;
       break;
@@ -88,46 +88,46 @@ retry:
   }
 
   unsigned int tlen;
   const char *d = hb_blob_get_data (blob, &tlen);
   *len = tlen;
   return d;
 }
 
-hb_graphite2_shaper_face_data_t *
+hb_graphite2_face_data_t *
 _hb_graphite2_shaper_face_data_create (hb_face_t *face)
 {
   hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF);
   /* Umm, we just reference the table to check whether it exists.
    * Maybe add better API for this? */
   if (!hb_blob_get_length (silf_blob))
   {
     hb_blob_destroy (silf_blob);
     return nullptr;
   }
   hb_blob_destroy (silf_blob);
 
-  hb_graphite2_shaper_face_data_t *data = (hb_graphite2_shaper_face_data_t *) calloc (1, sizeof (hb_graphite2_shaper_face_data_t));
+  hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) calloc (1, sizeof (hb_graphite2_face_data_t));
   if (unlikely (!data))
     return nullptr;
 
   data->face = face;
   data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll);
 
   if (unlikely (!data->grface)) {
     free (data);
     return nullptr;
   }
 
   return data;
 }
 
 void
-_hb_graphite2_shaper_face_data_destroy (hb_graphite2_shaper_face_data_t *data)
+_hb_graphite2_shaper_face_data_destroy (hb_graphite2_face_data_t *data)
 {
   hb_graphite2_tablelist_t *tlist = data->tlist;
 
   while (tlist)
   {
     hb_graphite2_tablelist_t *old = tlist;
     hb_blob_destroy (tlist->blob);
     tlist = tlist->next;
@@ -149,26 +149,26 @@ hb_graphite2_face_get_gr_face (hb_face_t
   return HB_SHAPER_DATA_GET (face)->grface;
 }
 
 
 /*
  * shaper font data
  */
 
-struct hb_graphite2_shaper_font_data_t {};
+struct hb_graphite2_font_data_t {};
 
-hb_graphite2_shaper_font_data_t *
+hb_graphite2_font_data_t *
 _hb_graphite2_shaper_font_data_create (hb_font_t *font HB_UNUSED)
 {
-  return (hb_graphite2_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_graphite2_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_graphite2_shaper_font_data_destroy (hb_graphite2_shaper_font_data_t *data HB_UNUSED)
+_hb_graphite2_shaper_font_data_destroy (hb_graphite2_font_data_t *data HB_UNUSED)
 {
 }
 
 /*
  * Since: 0.9.10
  */
 gr_font *
 hb_graphite2_font_get_gr_font (hb_font_t *font)
@@ -176,30 +176,30 @@ hb_graphite2_font_get_gr_font (hb_font_t
   return nullptr;
 }
 
 
 /*
  * shaper shape_plan data
  */
 
-struct hb_graphite2_shaper_shape_plan_data_t {};
+struct hb_graphite2_shape_plan_data_t {};
 
-hb_graphite2_shaper_shape_plan_data_t *
+hb_graphite2_shape_plan_data_t *
 _hb_graphite2_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,
 					     const int          *coords HB_UNUSED,
 					     unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_graphite2_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_graphite2_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_graphite2_shaper_shape_plan_data_destroy (hb_graphite2_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_graphite2_shaper_shape_plan_data_destroy (hb_graphite2_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
  * shaper
  */
 
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-iter-private.hh
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2018  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_ITER_PRIVATE_HH
+#define HB_ITER_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+/* Unified iterator object.
+ *
+ * The goal of this template is to make the same iterator interface
+ * available to all types, and make it very easy and compact to use.
+ * Iterator objects are small, light-weight, objects that can be
+ * copied by value.  If the collection / object being iterated on
+ * is writable, then the iterator points to lvalues, otherwise it
+ * returns rvalues.
+ *
+ * The way to declare, initialize, and use iterators, eg.:
+ *
+ *   Iter<const int *> s (src);
+ *   Iter<int *> t (dst);
+ *   for (; s && t; s++, t++)
+ *     *s = *t;
+ */
+
+template <typename T>
+struct Iter;
+
+#if 0
+template <typename T>
+struct Iter
+{
+  explicit inline Iter (const T &c);
+};
+#endif
+
+template <typename T>
+struct Iter<T *>
+{
+  /* Type of items. */
+  typedef T Value;
+
+  /* Constructors. */
+  inline Iter (T *array_, int length_) :
+    array (array_), length (MAX (length_, 0)) {}
+  template <unsigned int length_>
+  explicit inline Iter (T (&array_)[length_]) :
+    array (array_), length (length_) {}
+
+  /* Emptiness. */
+  explicit inline operator bool (void) const { return bool (length); }
+
+  /* Current item. */
+  inline T &operator * (void)
+  {
+    if (unlikely (!length)) return CrapOrNull(T);
+    return *array;
+  }
+  inline T &operator -> (void)
+  {
+    return (operator *);
+  }
+
+  /* Next. */
+  inline Iter<T *> & operator ++ (void)
+  {
+    if (unlikely (!length)) return *this;
+    array++;
+    length--;
+    return *this;
+  }
+  /* Might return void, or a copy of pre-increment iterator. */
+  inline void operator ++ (int)
+  {
+    if (unlikely (!length)) return;
+    array++;
+    length--;
+  }
+
+  /* Some iterators might implement len(). */
+  inline unsigned int len (void) const { return length; }
+
+  /* Some iterators might implement fast-forward.
+   * Only implement it if it's constant-time. */
+  inline void operator += (unsigned int n)
+  {
+    n = MIN (n, length);
+    array += n;
+    length -= n;
+  }
+
+  /* Some iterators might implement random-access.
+   * Only implement it if it's constant-time. */
+  inline Iter<T *> & operator [] (unsigned int i)
+  {
+    if (unlikely (i >= length)) return CrapOrNull(T);
+    return array[i];
+  }
+
+  private:
+  T *array;
+  unsigned int length;
+};
+
+/* XXX Remove
+ * Just to test these compile. */
+static inline void
+m (void)
+{
+  const int src[10] = {};
+  int dst[20];
+
+  Iter<const int *> s (src);
+  Iter<const int *> s2 (src, 5);
+  Iter<int *> t (dst);
+
+  s2 = s;
+
+  for (; s && t; ++s, ++t)
+   {
+    *t = *s;
+   }
+}
+
+#endif /* HB_ITER_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-machinery-private.hh
@@ -0,0 +1,720 @@
+/*
+ * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
+ * Copyright © 2012,2018  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_MACHINERY_PRIVATE_HH
+#define HB_MACHINERY_PRIVATE_HH
+
+#include "hb-private.hh"
+#include "hb-blob-private.hh"
+
+#include "hb-iter-private.hh"
+
+
+/*
+ * Casts
+ */
+
+/* Cast to struct T, reference to reference */
+template<typename Type, typename TObject>
+static inline const Type& CastR(const TObject &X)
+{ return reinterpret_cast<const Type&> (X); }
+template<typename Type, typename TObject>
+static inline Type& CastR(TObject &X)
+{ return reinterpret_cast<Type&> (X); }
+
+/* Cast to struct T, pointer to pointer */
+template<typename Type, typename TObject>
+static inline const Type* CastP(const TObject *X)
+{ return reinterpret_cast<const Type*> (X); }
+template<typename Type, typename TObject>
+static inline Type* CastP(TObject *X)
+{ return reinterpret_cast<Type*> (X); }
+
+/* StructAtOffset<T>(P,Ofs) returns the struct T& that is placed at memory
+ * location pointed to by P plus Ofs bytes. */
+template<typename Type>
+static inline const Type& StructAtOffset(const void *P, unsigned int offset)
+{ return * reinterpret_cast<const Type*> ((const char *) P + offset); }
+template<typename Type>
+static inline Type& StructAtOffset(void *P, unsigned int offset)
+{ return * reinterpret_cast<Type*> ((char *) P + offset); }
+
+/* StructAfter<T>(X) returns the struct T& that is placed after X.
+ * Works with X of variable size also.  X must implement get_size() */
+template<typename Type, typename TObject>
+static inline const Type& StructAfter(const TObject &X)
+{ return StructAtOffset<Type>(&X, X.get_size()); }
+template<typename Type, typename TObject>
+static inline Type& StructAfter(TObject &X)
+{ return StructAtOffset<Type>(&X, X.get_size()); }
+
+
+/*
+ * Size checking
+ */
+
+/* Check _assertion in a method environment */
+#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \
+  inline void _instance_assertion_on_line_##_line (void) const \
+  { \
+    static_assert ((_assertion), ""); \
+    ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \
+  }
+# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion)
+# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion)
+
+/* Check that _code compiles in a method environment */
+#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \
+  inline void _compiles_assertion_on_line_##_line (void) const \
+  { _code; }
+# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code)
+# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code)
+
+
+#define DEFINE_SIZE_STATIC(size) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \
+  static const unsigned int static_size = (size); \
+  static const unsigned int min_size = (size); \
+  inline unsigned int get_size (void) const { return (size); }
+
+#define DEFINE_SIZE_UNION(size, _member) \
+  DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \
+  static const unsigned int min_size = (size)
+
+#define DEFINE_SIZE_MIN(size) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \
+  static const unsigned int min_size = (size)
+
+#define DEFINE_SIZE_ARRAY(size, array) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \
+  DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \
+  static const unsigned int min_size = (size)
+
+#define DEFINE_SIZE_ARRAY2(size, array1, array2) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \
+  DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \
+  static const unsigned int min_size = (size)
+
+
+/*
+ * Dispatch
+ */
+
+template <typename Context, typename Return, unsigned int MaxDebugDepth>
+struct hb_dispatch_context_t
+{
+  static const unsigned int max_debug_depth = MaxDebugDepth;
+  typedef Return return_t;
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format) { return true; }
+  static return_t no_dispatch_return_value (void) { return Context::default_return_value (); }
+};
+
+
+/*
+ * Sanitize
+ */
+
+/* This limits sanitizing time on really broken fonts. */
+#ifndef HB_SANITIZE_MAX_EDITS
+#define HB_SANITIZE_MAX_EDITS 32
+#endif
+#ifndef HB_SANITIZE_MAX_OPS_FACTOR
+#define HB_SANITIZE_MAX_OPS_FACTOR 8
+#endif
+#ifndef HB_SANITIZE_MAX_OPS_MIN
+#define HB_SANITIZE_MAX_OPS_MIN 16384
+#endif
+
+struct hb_sanitize_context_t :
+       hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
+{
+  inline hb_sanitize_context_t (void) :
+	debug_depth (0),
+	start (nullptr), end (nullptr),
+	writable (false), edit_count (0), max_ops (0),
+	blob (nullptr),
+	num_glyphs (65536),
+	num_glyphs_set (false) {}
+
+  inline const char *get_name (void) { return "SANITIZE"; }
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format)
+  { return format->sanitize (this); }
+  template <typename T>
+  inline return_t dispatch (const T &obj) { return obj.sanitize (this); }
+  static return_t default_return_value (void) { return true; }
+  static return_t no_dispatch_return_value (void) { return false; }
+  bool stop_sublookup_iteration (const return_t r) const { return !r; }
+
+  inline void init (hb_blob_t *b)
+  {
+    this->blob = hb_blob_reference (b);
+    this->writable = false;
+  }
+
+  inline void set_num_glyphs (unsigned int num_glyphs_)
+  {
+    num_glyphs = num_glyphs_;
+    num_glyphs_set = true;
+  }
+  inline unsigned int get_num_glyphs (void) { return num_glyphs; }
+
+  inline void start_processing (void)
+  {
+    this->start = this->blob->data;
+    this->end = this->start + this->blob->length;
+    assert (this->start <= this->end); /* Must not overflow. */
+    this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR,
+			 (unsigned) HB_SANITIZE_MAX_OPS_MIN);
+    this->edit_count = 0;
+    this->debug_depth = 0;
+
+    DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1,
+		     "start [%p..%p] (%lu bytes)",
+		     this->start, this->end,
+		     (unsigned long) (this->end - this->start));
+  }
+
+  inline void end_processing (void)
+  {
+    DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1,
+		     "end [%p..%p] %u edit requests",
+		     this->start, this->end, this->edit_count);
+
+    hb_blob_destroy (this->blob);
+    this->blob = nullptr;
+    this->start = this->end = nullptr;
+  }
+
+  inline bool check_range (const void *base, unsigned int len) const
+  {
+    const char *p = (const char *) base;
+    bool ok = this->max_ops-- > 0 &&
+	      this->start <= p &&
+	      p <= this->end &&
+	      (unsigned int) (this->end - p) >= len;
+
+    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+       "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s",
+       p, p + len, len,
+       this->start, this->end,
+       ok ? "OK" : "OUT-OF-RANGE");
+
+    return likely (ok);
+  }
+
+  inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
+  {
+    const char *p = (const char *) base;
+    bool overflows = hb_unsigned_mul_overflows (len, record_size);
+    unsigned int array_size = record_size * len;
+    bool ok = !overflows && this->check_range (base, array_size);
+
+    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+       "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s",
+       p, p + (record_size * len), record_size, len, (unsigned int) array_size,
+       this->start, this->end,
+       overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE");
+
+    return likely (ok);
+  }
+
+  template <typename Type>
+  inline bool check_struct (const Type *obj) const
+  {
+    return likely (this->check_range (obj, obj->min_size));
+  }
+
+  inline bool may_edit (const void *base, unsigned int len)
+  {
+    if (this->edit_count >= HB_SANITIZE_MAX_EDITS)
+      return false;
+
+    const char *p = (const char *) base;
+    this->edit_count++;
+
+    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+       "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 this->writable;
+  }
+
+  template <typename Type, typename ValueType>
+  inline bool try_set (const Type *obj, const ValueType &v) {
+    if (this->may_edit (obj, obj->static_size)) {
+      const_cast<Type *> (obj)->set (v);
+      return true;
+    }
+    return false;
+  }
+
+  template <typename Type>
+  inline hb_blob_t *sanitize_blob (hb_blob_t *blob)
+  {
+    bool sane;
+
+    init (blob);
+
+  retry:
+    DEBUG_MSG_FUNC (SANITIZE, start, "start");
+
+    start_processing ();
+
+    if (unlikely (!start))
+    {
+      end_processing ();
+      return blob;
+    }
+
+    Type *t = CastP<Type> (const_cast<char *> (start));
+
+    sane = t->sanitize (this);
+    if (sane)
+    {
+      if (edit_count)
+      {
+	DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %d edits; going for second round", edit_count);
+
+        /* sanitize again to ensure no toe-stepping */
+        edit_count = 0;
+	sane = t->sanitize (this);
+	if (edit_count) {
+	  DEBUG_MSG_FUNC (SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count);
+	  sane = false;
+	}
+      }
+    }
+    else
+    {
+      if (edit_count && !writable) {
+        start = hb_blob_get_data_writable (blob, nullptr);
+	end = start + blob->length;
+
+	if (start)
+	{
+	  writable = true;
+	  /* ok, we made it writable by relocating.  try again */
+	  DEBUG_MSG_FUNC (SANITIZE, start, "retry");
+	  goto retry;
+	}
+      }
+    }
+
+    end_processing ();
+
+    DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED");
+    if (sane)
+    {
+      hb_blob_make_immutable (blob);
+      return blob;
+    }
+    else
+    {
+      hb_blob_destroy (blob);
+      return hb_blob_get_empty ();
+    }
+  }
+
+  template <typename Type>
+  inline hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag)
+  {
+    if (!num_glyphs_set)
+      set_num_glyphs (hb_face_get_glyph_count (face));
+    return sanitize_blob<Type> (hb_face_reference_table (face, tableTag));
+  }
+
+  mutable unsigned int debug_depth;
+  const char *start, *end;
+  private:
+  bool writable;
+  unsigned int edit_count;
+  mutable int max_ops;
+  hb_blob_t *blob;
+  unsigned int num_glyphs;
+  bool  num_glyphs_set;
+};
+
+
+/*
+ * Serialize
+ */
+
+struct hb_serialize_context_t
+{
+  inline hb_serialize_context_t (void *start_, unsigned int size)
+  {
+    this->start = (char *) start_;
+    this->end = this->start + size;
+
+    this->ran_out_of_room = false;
+    this->head = this->start;
+    this->debug_depth = 0;
+  }
+
+  template <typename Type>
+  inline Type *start_serialize (void)
+  {
+    DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
+		     "start [%p..%p] (%lu bytes)",
+		     this->start, this->end,
+		     (unsigned long) (this->end - this->start));
+
+    return start_embed<Type> ();
+  }
+
+  inline void end_serialize (void)
+  {
+    DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1,
+		     "end [%p..%p] serialized %d bytes; %s",
+		     this->start, this->end,
+		     (int) (this->head - this->start),
+		     this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room");
+  }
+
+  template <typename Type>
+  inline Type *copy (void)
+  {
+    assert (!this->ran_out_of_room);
+    unsigned int len = this->head - this->start;
+    void *p = malloc (len);
+    if (p)
+      memcpy (p, this->start, len);
+    return reinterpret_cast<Type *> (p);
+  }
+
+  template <typename Type>
+  inline Type *allocate_size (unsigned int size)
+  {
+    if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) {
+      this->ran_out_of_room = true;
+      return nullptr;
+    }
+    memset (this->head, 0, size);
+    char *ret = this->head;
+    this->head += size;
+    return reinterpret_cast<Type *> (ret);
+  }
+
+  template <typename Type>
+  inline Type *allocate_min (void)
+  {
+    return this->allocate_size<Type> (Type::min_size);
+  }
+
+  template <typename Type>
+  inline Type *start_embed (void)
+  {
+    Type *ret = reinterpret_cast<Type *> (this->head);
+    return ret;
+  }
+
+  template <typename Type>
+  inline Type *embed (const Type &obj)
+  {
+    unsigned int size = obj.get_size ();
+    Type *ret = this->allocate_size<Type> (size);
+    if (unlikely (!ret)) return nullptr;
+    memcpy (ret, obj, size);
+    return ret;
+  }
+
+  template <typename Type>
+  inline Type *extend_min (Type &obj)
+  {
+    unsigned int size = obj.min_size;
+    assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
+    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
+    return reinterpret_cast<Type *> (&obj);
+  }
+
+  template <typename Type>
+  inline Type *extend (Type &obj)
+  {
+    unsigned int size = obj.get_size ();
+    assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
+    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
+    return reinterpret_cast<Type *> (&obj);
+  }
+
+  unsigned int debug_depth;
+  char *start, *end, *head;
+  bool ran_out_of_room;
+};
+
+
+/*
+ * Supplier
+ */
+
+template <typename Type>
+struct Supplier
+{
+  inline Supplier (const Type *array, unsigned int len_, unsigned int stride_=sizeof(Type))
+  {
+    head = array;
+    len = len_;
+    stride = stride_;
+  }
+  inline const Type operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= len)) return Type ();
+    return * (const Type *) (const void *) ((const char *) head + stride * i);
+  }
+
+  inline Supplier<Type> & operator += (unsigned int count)
+  {
+    if (unlikely (count > len))
+      count = len;
+    len -= count;
+    head = (const Type *) (const void *) ((const char *) head + stride * count);
+    return *this;
+  }
+
+  private:
+  inline Supplier (const Supplier<Type> &); /* Disallow copy */
+  inline Supplier<Type>& operator= (const Supplier<Type> &); /* Disallow copy */
+
+  unsigned int len;
+  unsigned int stride;
+  const Type *head;
+};
+
+
+/*
+ * Big-endian integers.
+ */
+
+template <typename Type, int Bytes> struct BEInt;
+
+template <typename Type>
+struct BEInt<Type, 1>
+{
+  public:
+  inline void set (Type V)
+  {
+    v = V;
+  }
+  inline operator Type (void) const
+  {
+    return v;
+  }
+  private: uint8_t v;
+};
+template <typename Type>
+struct BEInt<Type, 2>
+{
+  public:
+  inline void set (Type V)
+  {
+    v[0] = (V >>  8) & 0xFF;
+    v[1] = (V      ) & 0xFF;
+  }
+  inline operator Type (void) const
+  {
+    return (v[0] <<  8)
+         + (v[1]      );
+  }
+  private: uint8_t v[2];
+};
+template <typename Type>
+struct BEInt<Type, 3>
+{
+  public:
+  inline void set (Type V)
+  {
+    v[0] = (V >> 16) & 0xFF;
+    v[1] = (V >>  8) & 0xFF;
+    v[2] = (V      ) & 0xFF;
+  }
+  inline operator Type (void) const
+  {
+    return (v[0] << 16)
+         + (v[1] <<  8)
+         + (v[2]      );
+  }
+  private: uint8_t v[3];
+};
+template <typename Type>
+struct BEInt<Type, 4>
+{
+  public:
+  inline void set (Type V)
+  {
+    v[0] = (V >> 24) & 0xFF;
+    v[1] = (V >> 16) & 0xFF;
+    v[2] = (V >>  8) & 0xFF;
+    v[3] = (V      ) & 0xFF;
+  }
+  inline operator Type (void) const
+  {
+    return (v[0] << 24)
+         + (v[1] << 16)
+         + (v[2] <<  8)
+         + (v[3]      );
+  }
+  private: uint8_t v[4];
+};
+
+
+/*
+ * Lazy loaders.
+ */
+
+template <unsigned int WheresFace,
+	  typename Subclass,
+	  typename Returned,
+	  typename Stored = Returned>
+struct hb_lazy_loader_t
+{
+  static_assert (WheresFace > 0, "");
+
+  /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
+  inline const Subclass* thiz (void) const { return static_cast<const Subclass *> (this); }
+  inline Subclass* thiz (void) { return static_cast<Subclass *> (this); }
+
+  inline void init0 (void) {} /* Init, when memory is already set to 0. No-op for us. */
+  inline void init (void)
+  {
+    instance = nullptr;
+  }
+  inline void fini (void)
+  {
+    if (instance)
+      thiz ()->destroy (instance);
+  }
+
+  inline const Returned * operator -> (void) const { return thiz ()->get (); }
+  inline const Returned & operator * (void) const { return *thiz ()->get (); }
+
+  inline Stored * get_stored (void) const
+  {
+  retry:
+    Stored *p = (Stored *) hb_atomic_ptr_get (&this->instance);
+    if (unlikely (!p))
+    {
+      hb_face_t *face = *(((hb_face_t **) this) - WheresFace);
+      if (likely (!p))
+	p = thiz ()->create (face);
+      if (unlikely (!p))
+	p = thiz ()->create (nullptr); /* Produce nil object. */
+      assert (p);
+      if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<Stored **>(&this->instance), nullptr, p)))
+      {
+        thiz ()->destroy (p);
+	goto retry;
+      }
+    }
+    return p;
+  }
+
+  inline void set_stored (Stored *instance_)
+  {
+    /* This *must* be called when there are no other threads accessing.
+     * However, to make TSan, etc, happy, we using cmpexch. */
+  retry:
+    Stored *p = (Stored *) hb_atomic_ptr_get (&this->instance);
+    if (p)
+    {
+      if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<Stored **>(&this->instance), p, instance_)))
+        goto retry;
+      thiz ()->destroy (p);
+    }
+  }
+
+  inline const Returned * get (void) const
+  {
+    return thiz ()->convert (get_stored ());
+  }
+
+  static inline const Returned* convert (const Stored *p)
+  {
+    return p;
+  }
+
+  private:
+  /* Must only have one pointer. */
+  mutable Stored *instance;
+};
+
+/* Specializations. */
+
+template <unsigned int WheresFace, typename T>
+struct hb_object_lazy_loader_t : hb_lazy_loader_t<WheresFace, hb_object_lazy_loader_t<WheresFace, T>, T>
+{
+  static inline T *create (hb_face_t *face)
+  {
+    if (unlikely (!face))
+      return const_cast<T *> (&Null(T));
+    T *p = (T *) calloc (1, sizeof (T));
+    if (unlikely (!p))
+      p = const_cast<T *> (&Null(T));
+    else
+      p->init (face);
+    return p;
+  }
+  static inline void destroy (T *p)
+  {
+    if (p != &Null(T))
+    {
+      p->fini();
+      free (p);
+    }
+  }
+};
+
+template <unsigned int WheresFace, typename T>
+struct hb_table_lazy_loader_t : hb_lazy_loader_t<WheresFace, hb_table_lazy_loader_t<WheresFace, T>, T, hb_blob_t>
+{
+  static inline hb_blob_t *create (hb_face_t *face)
+  {
+    if (unlikely (!face))
+      return hb_blob_get_empty ();
+    return hb_sanitize_context_t ().reference_table<T> (face);
+  }
+  static inline void destroy (hb_blob_t *p)
+  {
+    hb_blob_destroy (p);
+  }
+  static inline const T* convert (const hb_blob_t *blob)
+  {
+    return blob->as<T> ();
+  }
+
+  inline hb_blob_t* get_blob (void) const
+  {
+    return this->get_stored ();
+  }
+};
+
+
+#endif /* HB_MACHINERY_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-map.cc
+++ b/gfx/harfbuzz/src/hb-map.cc
@@ -152,18 +152,16 @@ hb_map_allocation_successful (const hb_m
 /**
  * hb_map_set:
  * @map: a map.
  * @key:
  * @value:
  *
  *
  *
- * Return value:
- *
  * Since: 1.7.7
  **/
 void
 hb_map_set (hb_map_t       *map,
 	    hb_codepoint_t  key,
 	    hb_codepoint_t  value)
 {
   map->set (key, value);
@@ -183,33 +181,33 @@ hb_map_get (const hb_map_t *map,
 	    hb_codepoint_t  key)
 {
   return map->get (key);
 }
 
 /**
  * hb_map_del:
  * @map: a map.
- * @codepoint:
+ * @key:
  *
  *
  *
  * Since: 1.7.7
  **/
 void
 hb_map_del (hb_map_t       *map,
 	    hb_codepoint_t  key)
 {
   map->del (key);
 }
 
 /**
  * hb_map_has:
  * @map: a map.
- * @codepoint:
+ * @key:
  *
  *
  *
  * Since: 1.7.7
  **/
 hb_bool_t
 hb_map_has (const hb_map_t *map,
 	    hb_codepoint_t  key)
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-null.hh
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2018  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_NULL_HH
+#define HB_NULL_HH
+
+#include "hb-private.hh"
+
+
+/*
+ * Static pools
+ */
+
+/* Global nul-content Null pool.  Enlarge as necessary. */
+
+#define HB_NULL_POOL_SIZE 264
+
+extern HB_INTERNAL
+hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)];
+
+/* Generic nul-content Null objects. */
+template <typename Type>
+static inline Type const & Null (void) {
+  static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
+  return *reinterpret_cast<Type const *> (_hb_NullPool);
+}
+#define Null(Type) Null<Type>()
+
+/* Specializaitons for arbitrary-content Null objects expressed in bytes. */
+#define DECLARE_NULL_NAMESPACE_BYTES(Namespace, Type) \
+} /* Close namespace. */ \
+extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::min_size]; \
+template <> \
+/*static*/ inline const Namespace::Type& Null<Namespace::Type> (void) { \
+  return *reinterpret_cast<const Namespace::Type *> (_hb_Null_##Namespace##_##Type); \
+} \
+namespace Namespace { \
+static_assert (true, "Just so we take semicolon after.")
+#define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \
+const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::min_size]
+
+/* Specializaitons for arbitrary-content Null objects expressed as struct initializer. */
+#define DECLARE_NULL_INSTANCE(Type) \
+extern HB_INTERNAL const Type _hb_Null_##Type; \
+template <> \
+/*static*/ inline const Type& Null<Type> (void) { \
+  return _hb_Null_##Type; \
+} \
+static_assert (true, "Just so we take semicolon after.")
+#define DEFINE_NULL_INSTANCE(Type) \
+const Type _hb_Null_##Type
+
+/* Global writable pool.  Enlarge as necessary. */
+
+/* To be fully correct, CrapPool must be thread_local. However, we do not rely on CrapPool
+ * for correct operation. It only exist to catch and divert program logic bugs instead of
+ * causing bad memory access. So, races there are not actually introducing incorrectness
+ * in the code. Has ~12kb binary size overhead to have it, also clang build fails with it. */
+extern HB_INTERNAL
+/*thread_local*/ hb_vector_size_impl_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)];
+
+/* CRAP pool: Common Region for Access Protection. */
+template <typename Type>
+static inline Type& Crap (void) {
+  static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
+  Type *obj = reinterpret_cast<Type *> (_hb_CrapPool);
+  *obj = Null(Type);
+  return *obj;
+}
+#define Crap(Type) Crap<Type>()
+
+template <typename Type>
+struct CrapOrNull {
+  static inline Type & get (void) { return Crap(Type); }
+};
+template <typename Type>
+struct CrapOrNull<const Type> {
+  static inline Type const & get (void) { return Null(Type); }
+};
+#define CrapOrNull(Type) CrapOrNull<Type>::get ()
+
+
+#endif /* HB_NULL_HH */
--- a/gfx/harfbuzz/src/hb-object-private.hh
+++ b/gfx/harfbuzz/src/hb-object-private.hh
@@ -30,36 +30,134 @@
  */
 
 #ifndef HB_OBJECT_PRIVATE_HH
 #define HB_OBJECT_PRIVATE_HH
 
 #include "hb-private.hh"
 #include "hb-atomic-private.hh"
 #include "hb-mutex-private.hh"
+#include "hb-vector-private.hh"
 
 
-/* reference_count */
+/*
+ * Lockable set
+ */
+
+template <typename item_t, typename lock_t>
+struct hb_lockable_set_t
+{
+  hb_vector_t <item_t, 1> items;
+
+  inline void init (void) { items.init (); }
+
+  template <typename T>
+  inline item_t *replace_or_insert (T v, lock_t &l, bool replace)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (item) {
+      if (replace) {
+	item_t old = *item;
+	*item = v;
+	l.unlock ();
+	old.fini ();
+      }
+      else {
+        item = nullptr;
+	l.unlock ();
+      }
+    } else {
+      item = items.push (v);
+      l.unlock ();
+    }
+    return item;
+  }
+
+  template <typename T>
+  inline void remove (T v, lock_t &l)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (item) {
+      item_t old = *item;
+      *item = items[items.len - 1];
+      items.pop ();
+      l.unlock ();
+      old.fini ();
+    } else {
+      l.unlock ();
+    }
+  }
+
+  template <typename T>
+  inline bool find (T v, item_t *i, lock_t &l)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (item)
+      *i = *item;
+    l.unlock ();
+    return !!item;
+  }
+
+  template <typename T>
+  inline item_t *find_or_insert (T v, lock_t &l)
+  {
+    l.lock ();
+    item_t *item = items.find (v);
+    if (!item) {
+      item = items.push (v);
+    }
+    l.unlock ();
+    return item;
+  }
+
+  inline void fini (lock_t &l)
+  {
+    if (!items.len) {
+      /* No need for locking. */
+      items.fini ();
+      return;
+    }
+    l.lock ();
+    while (items.len) {
+      item_t old = items[items.len - 1];
+	items.pop ();
+	l.unlock ();
+	old.fini ();
+	l.lock ();
+    }
+    items.fini ();
+    l.unlock ();
+  }
+
+};
+
+
+/*
+ * Reference-count.
+ */
 
 #define HB_REFERENCE_COUNT_INERT_VALUE 0
 #define HB_REFERENCE_COUNT_POISON_VALUE -0x0000DEAD
 #define HB_REFERENCE_COUNT_INIT {HB_ATOMIC_INT_INIT (HB_REFERENCE_COUNT_INERT_VALUE)}
 
 struct hb_reference_count_t
 {
   hb_atomic_int_t ref_count;
 
-  inline void init (int v) { ref_count.set_unsafe (v); }
-  inline int get_unsafe (void) const { return ref_count.get_unsafe (); }
+  inline void init (int v) { ref_count.set_relaxed (v); }
+  inline int get_relaxed (void) const { return ref_count.get_relaxed (); }
   inline int inc (void) { return ref_count.inc (); }
   inline int dec (void) { return ref_count.dec (); }
-  inline void fini (void) { ref_count.set_unsafe (HB_REFERENCE_COUNT_POISON_VALUE); }
+  inline void fini (void) { ref_count.set_relaxed (HB_REFERENCE_COUNT_POISON_VALUE); }
 
-  inline bool is_inert (void) const { return ref_count.get_unsafe () == HB_REFERENCE_COUNT_INERT_VALUE; }
-  inline bool is_valid (void) const { return ref_count.get_unsafe () > 0; }
+  inline bool is_inert (void) const { return ref_count.get_relaxed () == HB_REFERENCE_COUNT_INERT_VALUE; }
+  inline bool is_valid (void) const { return ref_count.get_relaxed () > 0; }
 };
 
 
 /* user_data */
 
 struct hb_user_data_array_t
 {
   struct hb_user_data_item_t {
@@ -84,39 +182,43 @@ struct hb_user_data_array_t
 			hb_bool_t           replace);
 
   HB_INTERNAL void *get (hb_user_data_key_t *key);
 
   inline void fini (void) { items.fini (lock); lock.fini (); }
 };
 
 
-/* object_header */
+/*
+ * Object header
+ */
 
 struct hb_object_header_t
 {
   hb_reference_count_t ref_count;
-  hb_user_data_array_t *user_data;
+  mutable hb_user_data_array_t *user_data;
 
 #define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INIT, nullptr}
 
   private:
   ASSERT_POD ();
 };
 
 
-/* object */
+/*
+ * Object
+ */
 
 template <typename Type>
 static inline void hb_object_trace (const Type *obj, const char *function)
 {
   DEBUG_MSG (OBJECT, (void *) obj,
 	     "%s refcount=%d",
 	     function,
-	     obj ? obj->header.ref_count.get_unsafe () : 0);
+	     obj ? obj->header.ref_count.get_relaxed () : 0);
 }
 
 template <typename Type>
 static inline Type *hb_object_create (void)
 {
   Type *obj = (Type *) calloc (1, sizeof (Type));
 
   if (unlikely (!obj))
--- a/gfx/harfbuzz/src/hb-open-file-private.hh
+++ b/gfx/harfbuzz/src/hb-open-file-private.hh
@@ -479,18 +479,16 @@ struct ResourceForkHeader
 };
 
 /*
  * OpenType Font File
  */
 
 struct OpenTypeFontFile
 {
-  static const hb_tag_t tableTag	= HB_TAG ('_','_','_','_'); /* Sanitizer needs this. */
-
   enum {
     CFFTag		= HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */
     TrueTypeTag	= HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */
     TTCTag		= HB_TAG ('t','t','c','f'), /* TrueType Collection */
     DFontTag		= HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */
     TrueTag		= HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */
     Typ1Tag		= HB_TAG ('t','y','p','1')  /* Obsolete Apple Type1 font in SFNT container */
   };
--- a/gfx/harfbuzz/src/hb-open-type-private.hh
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -27,579 +27,35 @@
  */
 
 #ifndef HB_OPEN_TYPE_PRIVATE_HH
 #define HB_OPEN_TYPE_PRIVATE_HH
 
 #include "hb-private.hh"
 #include "hb-blob-private.hh"
 #include "hb-face-private.hh"
+#include "hb-machinery-private.hh"
 
 
 namespace OT {
 
 
-
-/*
- * Casts
- */
-
-/* Cast to struct T, reference to reference */
-template<typename Type, typename TObject>
-static inline const Type& CastR(const TObject &X)
-{ return reinterpret_cast<const Type&> (X); }
-template<typename Type, typename TObject>
-static inline Type& CastR(TObject &X)
-{ return reinterpret_cast<Type&> (X); }
-
-/* Cast to struct T, pointer to pointer */
-template<typename Type, typename TObject>
-static inline const Type* CastP(const TObject *X)
-{ return reinterpret_cast<const Type*> (X); }
-template<typename Type, typename TObject>
-static inline Type* CastP(TObject *X)
-{ return reinterpret_cast<Type*> (X); }
-
-/* StructAtOffset<T>(P,Ofs) returns the struct T& that is placed at memory
- * location pointed to by P plus Ofs bytes. */
-template<typename Type>
-static inline const Type& StructAtOffset(const void *P, unsigned int offset)
-{ return * reinterpret_cast<const Type*> ((const char *) P + offset); }
-template<typename Type>
-static inline Type& StructAtOffset(void *P, unsigned int offset)
-{ return * reinterpret_cast<Type*> ((char *) P + offset); }
-
-/* StructAfter<T>(X) returns the struct T& that is placed after X.
- * Works with X of variable size also.  X must implement get_size() */
-template<typename Type, typename TObject>
-static inline const Type& StructAfter(const TObject &X)
-{ return StructAtOffset<Type>(&X, X.get_size()); }
-template<typename Type, typename TObject>
-static inline Type& StructAfter(TObject &X)
-{ return StructAtOffset<Type>(&X, X.get_size()); }
-
-
-
-/*
- * Size checking
- */
-
-/* Check _assertion in a method environment */
-#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \
-  inline void _instance_assertion_on_line_##_line (void) const \
-  { \
-    static_assert ((_assertion), ""); \
-    ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \
-  }
-# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion)
-# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion)
-
-/* Check that _code compiles in a method environment */
-#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \
-  inline void _compiles_assertion_on_line_##_line (void) const \
-  { _code; }
-# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code)
-# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code)
-
-
-#define DEFINE_SIZE_STATIC(size) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \
-  static const unsigned int static_size = (size); \
-  static const unsigned int min_size = (size); \
-  inline unsigned int get_size (void) const { return (size); }
-
-#define DEFINE_SIZE_UNION(size, _member) \
-  DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \
-  static const unsigned int min_size = (size)
-
-#define DEFINE_SIZE_MIN(size) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \
-  static const unsigned int min_size = (size)
-
-#define DEFINE_SIZE_ARRAY(size, array) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \
-  DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \
-  static const unsigned int min_size = (size)
-
-#define DEFINE_SIZE_ARRAY2(size, array1, array2) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \
-  DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \
-  static const unsigned int min_size = (size)
-
-
-
-/*
- * Dispatch
- */
-
-template <typename Context, typename Return, unsigned int MaxDebugDepth>
-struct hb_dispatch_context_t
-{
-  static const unsigned int max_debug_depth = MaxDebugDepth;
-  typedef Return return_t;
-  template <typename T, typename F>
-  inline bool may_dispatch (const T *obj, const F *format) { return true; }
-  static return_t no_dispatch_return_value (void) { return Context::default_return_value (); }
-};
-
-
-/*
- * Sanitize
- */
-
-/* This limits sanitizing time on really broken fonts. */
-#ifndef HB_SANITIZE_MAX_EDITS
-#define HB_SANITIZE_MAX_EDITS 32
-#endif
-#ifndef HB_SANITIZE_MAX_OPS_FACTOR
-#define HB_SANITIZE_MAX_OPS_FACTOR 8
-#endif
-#ifndef HB_SANITIZE_MAX_OPS_MIN
-#define HB_SANITIZE_MAX_OPS_MIN 16384
-#endif
-
-struct hb_sanitize_context_t :
-       hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
-{
-  inline hb_sanitize_context_t (void) :
-	debug_depth (0),
-	start (nullptr), end (nullptr),
-	writable (false), edit_count (0), max_ops (0),
-	blob (nullptr),
-	num_glyphs (0) {}
-
-  inline const char *get_name (void) { return "SANITIZE"; }
-  template <typename T, typename F>
-  inline bool may_dispatch (const T *obj, const F *format)
-  { return format->sanitize (this); }
-  template <typename T>
-  inline return_t dispatch (const T &obj) { return obj.sanitize (this); }
-  static return_t default_return_value (void) { return true; }
-  static return_t no_dispatch_return_value (void) { return false; }
-  bool stop_sublookup_iteration (const return_t r) const { return !r; }
-
-  inline void init (hb_blob_t *b)
-  {
-    this->blob = hb_blob_reference (b);
-    this->writable = false;
-  }
-
-  inline void start_processing (void)
-  {
-    this->start = hb_blob_get_data (this->blob, nullptr);
-    this->end = this->start + this->blob->length;
-    assert (this->start <= this->end); /* Must not overflow. */
-    this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR,
-			 (unsigned) HB_SANITIZE_MAX_OPS_MIN);
-    this->edit_count = 0;
-    this->debug_depth = 0;
-
-    DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1,
-		     "start [%p..%p] (%lu bytes)",
-		     this->start, this->end,
-		     (unsigned long) (this->end - this->start));
-  }
-
-  inline void end_processing (void)
-  {
-    DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1,
-		     "end [%p..%p] %u edit requests",
-		     this->start, this->end, this->edit_count);
-
-    hb_blob_destroy (this->blob);
-    this->blob = nullptr;
-    this->start = this->end = nullptr;
-  }
-
-  inline bool check_range (const void *base, unsigned int len) const
-  {
-    const char *p = (const char *) base;
-    bool ok = this->max_ops-- > 0 &&
-	      this->start <= p &&
-	      p <= this->end &&
-	      (unsigned int) (this->end - p) >= len;
-
-    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
-       "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s",
-       p, p + len, len,
-       this->start, this->end,
-       ok ? "OK" : "OUT-OF-RANGE");
-
-    return likely (ok);
-  }
-
-  inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
-  {
-    const char *p = (const char *) base;
-    bool overflows = hb_unsigned_mul_overflows (len, record_size);
-    unsigned int array_size = record_size * len;
-    bool ok = !overflows && this->check_range (base, array_size);
-
-    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
-       "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s",
-       p, p + (record_size * len), record_size, len, (unsigned int) array_size,
-       this->start, this->end,
-       overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE");
-
-    return likely (ok);
-  }
-
-  template <typename Type>
-  inline bool check_struct (const Type *obj) const
-  {
-    return likely (this->check_range (obj, obj->min_size));
-  }
-
-  inline bool may_edit (const void *base, unsigned int len)
-  {
-    if (this->edit_count >= HB_SANITIZE_MAX_EDITS)
-      return false;
-
-    const char *p = (const char *) base;
-    this->edit_count++;
-
-    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
-       "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 this->writable;
-  }
-
-  template <typename Type, typename ValueType>
-  inline bool try_set (const Type *obj, const ValueType &v) {
-    if (this->may_edit (obj, obj->static_size)) {
-      const_cast<Type *> (obj)->set (v);
-      return true;
-    }
-    return false;
-  }
-
-  mutable unsigned int debug_depth;
-  const char *start, *end;
-  bool writable;
-  unsigned int edit_count;
-  mutable int max_ops;
-  hb_blob_t *blob;
-  unsigned int num_glyphs;
-};
-
-
-
-/* Template to sanitize an object. */
-template <typename Type>
-struct Sanitizer
-{
-  inline Sanitizer (void) {}
-
-  inline hb_blob_t *sanitize (hb_blob_t *blob) {
-    bool sane;
-
-    /* TODO is_sane() stuff */
-
-    c->init (blob);
-
-  retry:
-    DEBUG_MSG_FUNC (SANITIZE, c->start, "start");
-
-    c->start_processing ();
-
-    if (unlikely (!c->start)) {
-      c->end_processing ();
-      return blob;
-    }
-
-    Type *t = CastP<Type> (const_cast<char *> (c->start));
-
-    sane = t->sanitize (c);
-    if (sane) {
-      if (c->edit_count) {
-	DEBUG_MSG_FUNC (SANITIZE, c->start, "passed first round with %d edits; going for second round", c->edit_count);
-
-        /* sanitize again to ensure no toe-stepping */
-        c->edit_count = 0;
-	sane = t->sanitize (c);
-	if (c->edit_count) {
-	  DEBUG_MSG_FUNC (SANITIZE, c->start, "requested %d edits in second round; FAILLING", c->edit_count);
-	  sane = false;
-	}
-      }
-    } else {
-      unsigned int edit_count = c->edit_count;
-      if (edit_count && !c->writable) {
-        c->start = hb_blob_get_data_writable (blob, nullptr);
-	c->end = c->start + blob->length;
-
-	if (c->start) {
-	  c->writable = true;
-	  /* ok, we made it writable by relocating.  try again */
-	  DEBUG_MSG_FUNC (SANITIZE, c->start, "retry");
-	  goto retry;
-	}
-      }
-    }
-
-    c->end_processing ();
-
-    DEBUG_MSG_FUNC (SANITIZE, c->start, sane ? "PASSED" : "FAILED");
-    if (sane)
-    {
-      blob->lock ();
-      return blob;
-    }
-    else
-    {
-      hb_blob_destroy (blob);
-      return hb_blob_get_empty ();
-    }
-  }
-
-  inline void set_num_glyphs (unsigned int num_glyphs) { c->num_glyphs = num_glyphs; }
-
-  private:
-  hb_sanitize_context_t c[1];
-};
-
-
-
-/*
- * Serialize
- */
-
-
-struct hb_serialize_context_t
-{
-  inline hb_serialize_context_t (void *start_, unsigned int size)
-  {
-    this->start = (char *) start_;
-    this->end = this->start + size;
-
-    this->ran_out_of_room = false;
-    this->head = this->start;
-    this->debug_depth = 0;
-  }
-
-  template <typename Type>
-  inline Type *start_serialize (void)
-  {
-    DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
-		     "start [%p..%p] (%lu bytes)",
-		     this->start, this->end,
-		     (unsigned long) (this->end - this->start));
-
-    return start_embed<Type> ();
-  }
-
-  inline void end_serialize (void)
-  {
-    DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1,
-		     "end [%p..%p] serialized %d bytes; %s",
-		     this->start, this->end,
-		     (int) (this->head - this->start),
-		     this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room");
-
-  }
-
-  template <typename Type>
-  inline Type *copy (void)
-  {
-    assert (!this->ran_out_of_room);
-    unsigned int len = this->head - this->start;
-    void *p = malloc (len);
-    if (p)
-      memcpy (p, this->start, len);
-    return reinterpret_cast<Type *> (p);
-  }
-
-  template <typename Type>
-  inline Type *allocate_size (unsigned int size)
-  {
-    if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) {
-      this->ran_out_of_room = true;
-      return nullptr;
-    }
-    memset (this->head, 0, size);
-    char *ret = this->head;
-    this->head += size;
-    return reinterpret_cast<Type *> (ret);
-  }
-
-  template <typename Type>
-  inline Type *allocate_min (void)
-  {
-    return this->allocate_size<Type> (Type::min_size);
-  }
-
-  template <typename Type>
-  inline Type *start_embed (void)
-  {
-    Type *ret = reinterpret_cast<Type *> (this->head);
-    return ret;
-  }
-
-  template <typename Type>
-  inline Type *embed (const Type &obj)
-  {
-    unsigned int size = obj.get_size ();
-    Type *ret = this->allocate_size<Type> (size);
-    if (unlikely (!ret)) return nullptr;
-    memcpy (ret, obj, size);
-    return ret;
-  }
-
-  template <typename Type>
-  inline Type *extend_min (Type &obj)
-  {
-    unsigned int size = obj.min_size;
-    assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
-    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
-    return reinterpret_cast<Type *> (&obj);
-  }
-
-  template <typename Type>
-  inline Type *extend (Type &obj)
-  {
-    unsigned int size = obj.get_size ();
-    assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
-    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
-    return reinterpret_cast<Type *> (&obj);
-  }
-
-  inline void truncate (void *new_head)
-  {
-    assert (this->start < new_head && new_head <= this->head);
-    this->head = (char *) new_head;
-  }
-
-  unsigned int debug_depth;
-  char *start, *end, *head;
-  bool ran_out_of_room;
-};
-
-template <typename Type>
-struct Supplier
-{
-  inline Supplier (const Type *array, unsigned int len_, unsigned int stride_=sizeof(Type))
-  {
-    head = array;
-    len = len_;
-    stride = stride_;
-  }
-  inline const Type operator [] (unsigned int i) const
-  {
-    if (unlikely (i >= len)) return Type ();
-    return * (const Type *) (const void *) ((const char *) head + stride * i);
-  }
-
-  inline Supplier<Type> & operator += (unsigned int count)
-  {
-    if (unlikely (count > len))
-      count = len;
-    len -= count;
-    head = (const Type *) (const void *) ((const char *) head + stride * count);
-    return *this;
-  }
-
-  private:
-  inline Supplier (const Supplier<Type> &); /* Disallow copy */
-  inline Supplier<Type>& operator= (const Supplier<Type> &); /* Disallow copy */
-
-  unsigned int len;
-  unsigned int stride;
-  const Type *head;
-};
-
-
 /*
  *
  * The OpenType Font File: Data Types
  */
 
 
 /* "The following data types are used in the OpenType font file.
  *  All OpenType fonts use Motorola-style byte ordering (Big Endian):" */
 
 /*
  * Int types
  */
 
-
-template <typename Type, int Bytes> struct BEInt;
-
-template <typename Type>
-struct BEInt<Type, 1>
-{
-  public:
-  inline void set (Type V)
-  {
-    v = V;
-  }
-  inline operator Type (void) const
-  {
-    return v;
-  }
-  private: uint8_t v;
-};
-template <typename Type>
-struct BEInt<Type, 2>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[0] = (V >>  8) & 0xFF;
-    v[1] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[0] <<  8)
-         + (v[1]      );
-  }
-  private: uint8_t v[2];
-};
-template <typename Type>
-struct BEInt<Type, 3>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[0] = (V >> 16) & 0xFF;
-    v[1] = (V >>  8) & 0xFF;
-    v[2] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[0] << 16)
-         + (v[1] <<  8)
-         + (v[2]      );
-  }
-  private: uint8_t v[3];
-};
-template <typename Type>
-struct BEInt<Type, 4>
-{
-  public:
-  inline void set (Type V)
-  {
-    v[0] = (V >> 24) & 0xFF;
-    v[1] = (V >> 16) & 0xFF;
-    v[2] = (V >>  8) & 0xFF;
-    v[3] = (V      ) & 0xFF;
-  }
-  inline operator Type (void) const
-  {
-    return (v[0] << 24)
-         + (v[1] << 16)
-         + (v[2] <<  8)
-         + (v[3]      );
-  }
-  private: uint8_t v[4];
-};
-
 /* Integer types in big-endian order and no alignment requirement */
 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,Size> &o) const { return (Type) v == (Type) o.v; }
   inline bool operator != (const IntType<Type,Size> &o) const { return !(*this == o); }
@@ -679,29 +135,28 @@ struct LONGDATETIME
 struct Tag : HBUINT32
 {
   /* What the char* converters return is NOT nul-terminated.  Print using "%.4s" */
   inline operator const char* (void) const { return reinterpret_cast<const char *> (&this->v); }
   inline operator char* (void) { return reinterpret_cast<char *> (&this->v); }
   public:
   DEFINE_SIZE_STATIC (4);
 };
-DEFINE_NULL_DATA (OT, Tag, "    ");
 
 /* Glyph index number, same as uint16 (length = 16 bits) */
 typedef HBUINT16 GlyphID;
 
 /* Name-table index, same as uint16 (length = 16 bits) */
 typedef HBUINT16 NameID;
 
 /* Script/language-system/feature index */
 struct Index : HBUINT16 {
   static const unsigned int NOT_FOUND_INDEX = 0xFFFFu;
 };
-DEFINE_NULL_DATA (OT, Index, "\xff\xff");
+DECLARE_NULL_NAMESPACE_BYTES (OT, Index);
 
 /* Offset, Null offset = 0 */
 template <typename Type>
 struct Offset : Type
 {
   inline bool is_null (void) const { return 0 == *this; }
 
   inline void *serialize (hb_serialize_context_t *c, const void *base)
@@ -760,17 +215,16 @@ struct FixedVersion
 
   FixedType major;
   FixedType minor;
   public:
   DEFINE_SIZE_STATIC (2 * sizeof(FixedType));
 };
 
 
-
 /*
  * Template subclasses of Offset that do the dereferencing.
  * Use: (base+offset)
  */
 
 template <typename Type, typename OffsetType=HBUINT16>
 struct OffsetTo : Offset<OffsetType>
 {
@@ -826,17 +280,16 @@ static inline const Type& operator + (co
 template <typename Base, typename OffsetType, typename Type>
 static inline Type& operator + (Base &base, OffsetTo<Type, OffsetType> &offset) { return offset (base); }
 
 
 /*
  * Array Types
  */
 
-
 /* TODO Use it in ArrayOf, HeadlessArrayOf, and other places around the code base?? */
 template <typename Type>
 struct UnsizedArrayOf
 {
   inline const Type& operator [] (unsigned int i) const { return arrayZ[i]; }
   inline Type& operator [] (unsigned int i) { return arrayZ[i]; }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
@@ -908,17 +361,16 @@ struct UnsizedOffsetListOf : UnsizedOffs
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, T user_data) const
   {
     TRACE_SANITIZE (this);
     return_trace ((UnsizedOffsetArrayOf<Type, OffsetType>::sanitize (c, count, this, user_data)));
   }
 };
 
-
 /* An array with a number of elements. */
 template <typename Type, typename LenType=HBUINT16>
 struct ArrayOf
 {
   const Type *sub_array (unsigned int start_offset, unsigned int *pcount /* IN/OUT */) const
   {
     unsigned int count = len;
     if (unlikely (start_offset > count))
@@ -1061,17 +513,16 @@ struct OffsetListOf : OffsetArrayOf<Type
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, T user_data) const
   {
     TRACE_SANITIZE (this);
     return_trace (OffsetArrayOf<Type>::sanitize (c, this, user_data));
   }
 };
 
-
 /* An array starting at second element. */
 template <typename Type, typename LenType=HBUINT16>
 struct HeadlessArrayOf
 {
   inline const Type& operator [] (unsigned int i) const
   {
     if (unlikely (i >= len || !i)) return Null(Type);
     return arrayZ[i-1];
@@ -1126,20 +577,17 @@ struct HeadlessArrayOf
 
   public:
   LenType len;
   Type arrayZ[VAR];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
 };
 
-
-/*
- * An array with sorted elements.  Supports binary searching.
- */
+/* An array with sorted elements.  Supports binary searching. */
 template <typename Type, typename LenType=HBUINT16>
 struct SortedArrayOf : ArrayOf<Type, LenType>
 {
   template <typename SearchType>
   inline int bsearch (const SearchType &x) const
   {
     /* Hand-coded bsearch here since this is in the hot inner loop. */
     const Type *arr = this->arrayZ;
@@ -1154,20 +602,17 @@ struct SortedArrayOf : ArrayOf<Type, Len
         min = mid + 1;
       else
         return mid;
     }
     return -1;
   }
 };
 
-/*
- * Binary-search arrays
- */
-
+/* Binary-search arrays */
 struct BinSearchHeader
 {
   inline operator uint32_t (void) const { return len; }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -1193,107 +638,12 @@ struct BinSearchHeader
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
 template <typename Type>
 struct BinSearchArrayOf : SortedArrayOf<Type, BinSearchHeader> {};
 
 
-/* Lazy struct and blob loaders. */
-
-/* Logic is shared between hb_lazy_loader_t and hb_table_lazy_loader_t */
-template <typename T>
-struct hb_lazy_loader_t
-{
-  inline void init (hb_face_t *face_)
-  {
-    face = face_;
-    instance = nullptr;
-  }
-
-  inline void fini (void)
-  {
-    if (instance && instance != &Null(T))
-    {
-      instance->fini();
-      free (instance);
-    }
-  }
-
-  inline const T* get (void) const
-  {
-  retry:
-    T *p = (T *) hb_atomic_ptr_get (&instance);
-    if (unlikely (!p))
-    {
-      p = (T *) calloc (1, sizeof (T));
-      if (unlikely (!p))
-        p = const_cast<T *> (&Null(T));
-      else
-	p->init (face);
-      if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), nullptr, p)))
-      {
-	if (p != &Null(T))
-	  p->fini ();
-	goto retry;
-      }
-    }
-    return p;
-  }
-
-  inline const T* operator-> (void) const
-  {
-    return get ();
-  }
-
-  private:
-  hb_face_t *face;
-  T *instance;
-};
-
-/* Logic is shared between hb_lazy_loader_t and hb_table_lazy_loader_t */
-template <typename T>
-struct hb_table_lazy_loader_t
-{
-  inline void init (hb_face_t *face_)
-  {
-    face = face_;
-    blob = nullptr;
-  }
-
-  inline void fini (void)
-  {
-    hb_blob_destroy (blob);
-  }
-
-  inline const T* get (void) const
-  {
-  retry:
-    hb_blob_t *blob_ = (hb_blob_t *) hb_atomic_ptr_get (&blob);
-    if (unlikely (!blob_))
-    {
-      blob_ = OT::Sanitizer<T>().sanitize (face->reference_table (T::tableTag));
-      if (!hb_atomic_ptr_cmpexch (&blob, nullptr, blob_))
-      {
-	hb_blob_destroy (blob_);
-	goto retry;
-      }
-      blob = blob_;
-    }
-    return blob_->as<T> ();
-  }
-
-  inline const T* operator-> (void) const
-  {
-    return get();
-  }
-
-  private:
-  hb_face_t *face;
-  mutable hb_blob_t *blob;
-};
-
-
 } /* namespace OT */
 
 
 #endif /* HB_OPEN_TYPE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-cmap-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-cmap-table.hh
@@ -32,16 +32,19 @@
 #include "hb-subset-plan.hh"
 
 /*
  * cmap -- Character to Glyph Index Mapping
  * https://docs.microsoft.com/en-us/typography/opentype/spec/cmap
  */
 #define HB_OT_TAG_cmap HB_TAG('c','m','a','p')
 
+#ifndef HB_MAX_UNICODE_CODEPOINT_VALUE
+#define HB_MAX_UNICODE_CODEPOINT_VALUE 0x10FFFF
+#endif
 
 namespace OT {
 
 
 struct CmapSubtableFormat0
 {
   inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
@@ -432,18 +435,20 @@ struct CmapSubtableLongSegmented
     *glyph = T::group_get_glyph (groups[i], codepoint);
     return true;
   }
 
   inline void get_all_codepoints (hb_set_t *out) const
   {
     for (unsigned int i = 0; i < this->groups.len; i++) {
       hb_set_add_range (out,
-			this->groups[i].startCharCode,
-			this->groups[i].endCharCode);
+			MIN ((unsigned int) this->groups[i].startCharCode,
+			     (unsigned int) HB_MAX_UNICODE_CODEPOINT_VALUE),
+			MIN ((unsigned int) this->groups[i].endCharCode,
+			     (unsigned int) HB_MAX_UNICODE_CODEPOINT_VALUE));
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && groups.sanitize (c));
   }
@@ -795,58 +800,58 @@ struct cmap
 
   inline bool _subset (const hb_subset_plan_t *plan,
                        const subset_plan &cmap_subset_plan,
 		       size_t dest_sz,
 		       void *dest) const
   {
     hb_serialize_context_t c (dest, dest_sz);
 
-    OT::cmap *cmap = c.start_serialize<OT::cmap> ();
-    if (unlikely (!c.extend_min (*cmap)))
+    cmap *table = c.start_serialize<cmap> ();
+    if (unlikely (!c.extend_min (*table)))
     {
       return false;
     }
 
-    cmap->version.set (0);
+    table->version.set (0);
 
-    if (unlikely (!cmap->encodingRecord.serialize (&c, /* numTables */ 3)))
+    if (unlikely (!table->encodingRecord.serialize (&c, /* numTables */ 3)))
       return false;
 
     // TODO(grieger): Convert the below to a for loop
 
     // Format 4, Plat 0 Encoding Record
-    EncodingRecord &format4_plat0_rec = cmap->encodingRecord[0];
+    EncodingRecord &format4_plat0_rec = table->encodingRecord[0];
     format4_plat0_rec.platformID.set (0); // Unicode
     format4_plat0_rec.encodingID.set (3);
 
     // Format 4, Plat 3 Encoding Record
-    EncodingRecord &format4_plat3_rec = cmap->encodingRecord[1];
+    EncodingRecord &format4_plat3_rec = table->encodingRecord[1];
     format4_plat3_rec.platformID.set (3); // Windows
     format4_plat3_rec.encodingID.set (1); // Unicode BMP
 
     // Format 12 Encoding Record
-    EncodingRecord &format12_rec = cmap->encodingRecord[2];
+    EncodingRecord &format12_rec = table->encodingRecord[2];
     format12_rec.platformID.set (3); // Windows
     format12_rec.encodingID.set (10); // Unicode UCS-4
 
     // Write out format 4 sub table
     {
-      CmapSubtable &subtable = format4_plat0_rec.subtable.serialize (&c, cmap);
+      CmapSubtable &subtable = format4_plat0_rec.subtable.serialize (&c, table);
       format4_plat3_rec.subtable.set (format4_plat0_rec.subtable);
       subtable.u.format.set (4);
 
       CmapSubtableFormat4 &format4 = subtable.u.format4;
       if (unlikely (!format4.serialize (&c, plan, cmap_subset_plan.format4_segments)))
         return false;
     }
 
     // Write out format 12 sub table.
     {
-      CmapSubtable &subtable = format12_rec.subtable.serialize (&c, cmap);
+      CmapSubtable &subtable = format12_rec.subtable.serialize (&c, table);
       subtable.u.format.set (12);
 
       CmapSubtableFormat12 &format12 = subtable.u.format12;
       if (unlikely (!format12.serialize (&c, cmap_subset_plan.format12_groups)))
         return false;
     }
 
     c.end_serialize ();
@@ -889,67 +894,67 @@ struct cmap
     hb_blob_destroy (cmap_prime);
     return result;
   }
 
   struct accelerator_t
   {
     inline void init (hb_face_t *face)
     {
-      this->blob = OT::Sanitizer<OT::cmap>().sanitize (face->reference_table (HB_OT_TAG_cmap));
-      const OT::cmap *cmap = this->blob->as<OT::cmap> ();
-      const OT::CmapSubtable *subtable = nullptr;
-      const OT::CmapSubtableFormat14 *subtable_uvs = nullptr;
+      this->blob = hb_sanitize_context_t().reference_table<cmap> (face);
+      const cmap *table = this->blob->as<cmap> ();
+      const CmapSubtable *subtable = nullptr;
+      const CmapSubtableFormat14 *subtable_uvs = nullptr;
 
       bool symbol = false;
       /* 32-bit subtables. */
-      if (!subtable) subtable = cmap->find_subtable (3, 10);
-      if (!subtable) subtable = cmap->find_subtable (0, 6);
-      if (!subtable) subtable = cmap->find_subtable (0, 4);
+      if (!subtable) subtable = table->find_subtable (3, 10);
+      if (!subtable) subtable = table->find_subtable (0, 6);
+      if (!subtable) subtable = table->find_subtable (0, 4);
       /* 16-bit subtables. */
-      if (!subtable) subtable = cmap->find_subtable (3, 1);
-      if (!subtable) subtable = cmap->find_subtable (0, 3);
-      if (!subtable) subtable = cmap->find_subtable (0, 2);
-      if (!subtable) subtable = cmap->find_subtable (0, 1);
-      if (!subtable) subtable = cmap->find_subtable (0, 0);
+      if (!subtable) subtable = table->find_subtable (3, 1);
+      if (!subtable) subtable = table->find_subtable (0, 3);
+      if (!subtable) subtable = table->find_subtable (0, 2);
+      if (!subtable) subtable = table->find_subtable (0, 1);
+      if (!subtable) subtable = table->find_subtable (0, 0);
       if (!subtable)
       {
-	subtable = cmap->find_subtable (3, 0);
+	subtable = table->find_subtable (3, 0);
 	if (subtable) symbol = true;
       }
       /* Meh. */
-      if (!subtable) subtable = &Null(OT::CmapSubtable);
+      if (!subtable) subtable = &Null(CmapSubtable);
 
       /* UVS subtable. */
       if (!subtable_uvs)
       {
-	const OT::CmapSubtable *st = cmap->find_subtable (0, 5);
+	const CmapSubtable *st = table->find_subtable (0, 5);
 	if (st && st->u.format == 14)
 	  subtable_uvs = &st->u.format14;
       }
       /* Meh. */
-      if (!subtable_uvs) subtable_uvs = &Null(OT::CmapSubtableFormat14);
+      if (!subtable_uvs) subtable_uvs = &Null(CmapSubtableFormat14);
 
       this->uvs_table = subtable_uvs;
 
       this->get_glyph_data = subtable;
       if (unlikely (symbol))
       {
-	this->get_glyph_func = get_glyph_from_symbol<OT::CmapSubtable>;
+	this->get_glyph_func = get_glyph_from_symbol<CmapSubtable>;
 	this->get_all_codepoints_func = null_get_all_codepoints_func;
       } else {
 	switch (subtable->u.format) {
 	/* Accelerate format 4 and format 12. */
 	default:
-	  this->get_glyph_func = get_glyph_from<OT::CmapSubtable>;
+	  this->get_glyph_func = get_glyph_from<CmapSubtable>;
 	  this->get_all_codepoints_func = null_get_all_codepoints_func;
 	  break;
 	case 12:
-	  this->get_glyph_func = get_glyph_from<OT::CmapSubtableFormat12>;
-	  this->get_all_codepoints_func = get_all_codepoints_from<OT::CmapSubtableFormat12>;
+	  this->get_glyph_func = get_glyph_from<CmapSubtableFormat12>;
+	  this->get_all_codepoints_func = get_all_codepoints_from<CmapSubtableFormat12>;
 	  break;
 	case  4:
 	  {
 	    this->format4_accel.init (&subtable->u.format4);
 	    this->get_glyph_data = &this->format4_accel;
 	    this->get_glyph_func = this->format4_accel.get_glyph_func;
 	    this->get_all_codepoints_func = this->format4_accel.get_all_codepoints_func;
 	  }
@@ -972,19 +977,19 @@ struct cmap
     inline bool get_variation_glyph (hb_codepoint_t  unicode,
 				     hb_codepoint_t  variation_selector,
 				     hb_codepoint_t *glyph) const
     {
       switch (this->uvs_table->get_glyph_variant (unicode,
 						  variation_selector,
 						  glyph))
       {
-	case OT::GLYPH_VARIANT_NOT_FOUND:		return false;
-	case OT::GLYPH_VARIANT_FOUND:		return true;
-	case OT::GLYPH_VARIANT_USE_DEFAULT:	break;
+	case GLYPH_VARIANT_NOT_FOUND:	return false;
+	case GLYPH_VARIANT_FOUND:	return true;
+	case GLYPH_VARIANT_USE_DEFAULT:	break;
       }
 
       return get_nominal_glyph (unicode, glyph);
     }
 
     inline void get_all_codepoints (hb_set_t *out) const
     {
       this->get_all_codepoints_func (get_glyph_data, out);
@@ -1041,19 +1046,19 @@ struct cmap
       return false;
     }
 
     private:
     hb_cmap_get_glyph_func_t get_glyph_func;
     const void *get_glyph_data;
     hb_cmap_get_all_codepoints_func_t get_all_codepoints_func;
 
-    OT::CmapSubtableFormat4::accelerator_t format4_accel;
+    CmapSubtableFormat4::accelerator_t format4_accel;
 
-    const OT::CmapSubtableFormat14 *uvs_table;
+    const CmapSubtableFormat14 *uvs_table;
     hb_blob_t *blob;
   };
 
   protected:
 
   inline const CmapSubtable *find_subtable (unsigned int platform_id,
 					    unsigned int encoding_id) const
   {
--- a/gfx/harfbuzz/src/hb-ot-color-cbdt-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-color-cbdt-table.hh
@@ -389,18 +389,18 @@ struct CBDT
   }
 
   struct accelerator_t
   {
     inline void init (hb_face_t *face)
     {
       upem = hb_face_get_upem (face);
 
-      cblc_blob = Sanitizer<CBLC>().sanitize (face->reference_table (HB_OT_TAG_CBLC));
-      cbdt_blob = Sanitizer<CBDT>().sanitize (face->reference_table (HB_OT_TAG_CBDT));
+      cblc_blob = hb_sanitize_context_t().reference_table<CBLC> (face);
+      cbdt_blob = hb_sanitize_context_t().reference_table<CBDT> (face);
       cbdt_len = hb_blob_get_length (cbdt_blob);
 
       if (hb_blob_get_length (cblc_blob) == 0) {
 	cblc = nullptr;
 	cbdt = nullptr;
 	return;  /* Not a bitmap font. */
       }
       cblc = cblc_blob->as<CBLC> ();
--- a/gfx/harfbuzz/src/hb-ot-color-sbix-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-color-sbix-table.hh
@@ -63,17 +63,17 @@ struct SBIXGlyph
 struct SBIXStrike
 {
   friend struct sbix;
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  imageOffsetsZ.sanitize_shallow (c, c->num_glyphs + 1));
+		  imageOffsetsZ.sanitize_shallow (c, c->get_num_glyphs () + 1));
   }
 
   protected:
   HBUINT16	ppem;		/* The PPEM size for which this strike was designed. */
   HBUINT16	resolution;	/* The device pixel density (in PPI) for which this
 				 * strike was designed. (E.g., 96 PPI, 192 PPI.) */
   UnsizedArrayOf<LOffsetTo<SBIXGlyph> >
 		imageOffsetsZ;	/* Offset from the beginning of the strike data header
@@ -91,24 +91,19 @@ struct sbix
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) && strikes.sanitize (c, this)));
   }
 
   struct accelerator_t
   {
     inline void init (hb_face_t *face)
     {
-      num_glyphs = hb_face_get_glyph_count (face);
-
-      OT::Sanitizer<OT::sbix> sanitizer;
-      sanitizer.set_num_glyphs (num_glyphs);
-      sbix_blob = sanitizer.sanitize (face->reference_table (HB_OT_TAG_sbix));
+      sbix_blob = hb_sanitize_context_t().reference_table<sbix> (face);
       sbix_len = hb_blob_get_length (sbix_blob);
-      sbix_table = sbix_blob->as<OT::sbix> ();
-
+      sbix_table = sbix_blob->as<sbix> ();
     }
 
     inline void fini (void)
     {
       hb_blob_destroy (sbix_blob);
     }
 
     inline void dump (void (*callback) (const uint8_t* data, unsigned int length,
@@ -129,17 +124,16 @@ struct sbix
     }
 
     private:
     hb_blob_t *sbix_blob;
     const sbix *sbix_table;
 
     unsigned int sbix_len;
     unsigned int num_glyphs;
-
   };
 
   protected:
   HBUINT16	version;	/* Table version number — set to 1 */
   HBUINT16	flags;		/* Bit 0: Set to 1. Bit 1: Draw outlines.
 				 * Bits 2 to 15: reserved (set to 0). */
   LArrayOf<LOffsetTo<SBIXStrike> >
 		strikes;	/* Offsets from the beginning of the 'sbix'
--- a/gfx/harfbuzz/src/hb-ot-color-svg-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-color-svg-table.hh
@@ -91,21 +91,19 @@ struct SVG
     return_trace (likely (c->check_struct (this) &&
 			  (this+svgDocIndex).sanitize (c)));
   }
 
   struct accelerator_t
   {
     inline void init (hb_face_t *face)
     {
-      OT::Sanitizer<OT::SVG> sanitizer;
-      svg_blob = sanitizer.sanitize (face->reference_table (HB_OT_TAG_SVG));
+      svg_blob = hb_sanitize_context_t().reference_table<SVG> (face);
       svg_len = hb_blob_get_length (svg_blob);
-      svg = svg_blob->as<OT::SVG> ();
-
+      svg = svg_blob->as<SVG> ();
     }
 
     inline void fini (void)
     {
       hb_blob_destroy (svg_blob);
     }
 
     inline void
--- a/gfx/harfbuzz/src/hb-ot-font.cc
+++ b/gfx/harfbuzz/src/hb-ot-font.cc
@@ -36,57 +36,71 @@
 #include "hb-ot-kern-table.hh"
 #include "hb-ot-post-table.hh"
 
 #include "hb-ot-color-cbdt-table.hh"
 
 
 struct hb_ot_font_t
 {
+  inline void init (hb_face_t *face)
+  {
+    cmap.init (face);
+    h_metrics.init (face);
+    v_metrics.init (face, h_metrics.ascender - h_metrics.descender); /* TODO Can we do this lazily? */
+
+    this->face = face;
+    glyf.init ();
+    cbdt.init ();
+    post.init ();
+    kern.init ();
+  }
+  inline void fini (void)
+  {
+    cmap.fini ();
+    h_metrics.fini ();
+    v_metrics.fini ();
+
+    glyf.fini ();
+    cbdt.fini ();
+    post.fini ();
+    kern.fini ();
+  }
+
   OT::cmap::accelerator_t cmap;
   OT::hmtx::accelerator_t h_metrics;
   OT::vmtx::accelerator_t v_metrics;
-  OT::hb_lazy_loader_t<OT::glyf::accelerator_t> glyf;
-  OT::hb_lazy_loader_t<OT::CBDT::accelerator_t> cbdt;
-  OT::hb_lazy_loader_t<OT::post::accelerator_t> post;
-  OT::hb_lazy_loader_t<OT::kern::accelerator_t> kern;
+
+  hb_face_t *face; /* MUST be JUST before the lazy loaders. */
+  hb_object_lazy_loader_t<1, OT::glyf::accelerator_t> glyf;
+  hb_object_lazy_loader_t<2, OT::CBDT::accelerator_t> cbdt;
+  hb_object_lazy_loader_t<3, OT::post::accelerator_t> post;
+  hb_object_lazy_loader_t<4, OT::kern::accelerator_t> kern;
 };
 
 
 static hb_ot_font_t *
 _hb_ot_font_create (hb_face_t *face)
 {
   hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t));
 
   if (unlikely (!ot_font))
     return nullptr;
 
-  ot_font->cmap.init (face);
-  ot_font->h_metrics.init (face);
-  ot_font->v_metrics.init (face, ot_font->h_metrics.ascender - ot_font->h_metrics.descender); /* TODO Can we do this lazily? */
-  ot_font->glyf.init (face);
-  ot_font->cbdt.init (face);
-  ot_font->post.init (face);
-  ot_font->kern.init (face);
+  ot_font->init (face);
 
   return ot_font;
 }
 
 static void
 _hb_ot_font_destroy (void *data)
 {
   hb_ot_font_t *ot_font = (hb_ot_font_t *) data;
 
-  ot_font->cmap.fini ();
-  ot_font->h_metrics.fini ();
-  ot_font->v_metrics.fini ();
-  ot_font->glyf.fini ();
-  ot_font->cbdt.fini ();
-  ot_font->post.fini ();
-  ot_font->kern.fini ();
+  ot_font->fini ();
 
   free (ot_font);
 }
 
 
 static hb_bool_t
 hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED,
 			 void *font_data,
--- a/gfx/harfbuzz/src/hb-ot-glyf-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-glyf-table.hh
@@ -98,24 +98,24 @@ struct glyf
     hb_blob_destroy (glyf_prime);
 
     return success;
   }
 
   static bool
   _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca)
   {
-    hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_head));
+    hb_blob_t *head_blob = hb_sanitize_context_t().reference_table<head> (plan->source);
     hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob);
     hb_blob_destroy (head_blob);
 
     if (unlikely (!head_prime_blob))
       return false;
 
-    OT::head *head_prime = (OT::head *) hb_blob_get_data_writable (head_prime_blob, nullptr);
+    head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr);
     head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1);
     bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob);
 
     hb_blob_destroy (head_prime_blob);
     return success;
   }
 
   struct GlyphHeader
@@ -231,30 +231,30 @@ struct glyf
   };
 
   struct accelerator_t
   {
     inline void init (hb_face_t *face)
     {
       memset (this, 0, sizeof (accelerator_t));
 
-      hb_blob_t *head_blob = Sanitizer<head>().sanitize (face->reference_table (HB_OT_TAG_head));
+      hb_blob_t *head_blob = hb_sanitize_context_t().reference_table<head> (face);
       const head *head_table = head_blob->as<head> ();
-      if (head_table == &Null(head) || (unsigned int) head_table->indexToLocFormat > 1 || head_table->glyphDataFormat != 0)
+      if (head_table->indexToLocFormat > 1 || head_table->glyphDataFormat != 0)
       {
-	/* head table is not present, or in an unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
+	/* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
 	hb_blob_destroy (head_blob);
 	return;
       }
       short_offset = 0 == head_table->indexToLocFormat;
       hb_blob_destroy (head_blob);
 
-      loca_blob = Sanitizer<loca>().sanitize (face->reference_table (HB_OT_TAG_loca));
+      loca_blob = hb_sanitize_context_t().reference_table<loca> (face);
       loca_table = loca_blob->as<loca> ();
-      glyf_blob = Sanitizer<glyf>().sanitize (face->reference_table (HB_OT_TAG_glyf));
+      glyf_blob = hb_sanitize_context_t().reference_table<glyf> (face);
       glyf_table = glyf_blob->as<glyf> ();
 
       num_glyphs = MAX (1u, hb_blob_get_length (loca_blob) / (short_offset ? 2 : 4)) - 1;
       glyf_len = hb_blob_get_length (glyf_blob);
     }
 
     inline void fini (void)
     {
@@ -265,17 +265,17 @@ struct glyf
     /*
      * Returns true if the referenced glyph is a valid glyph and a composite glyph.
      * If true is returned a pointer to the composite glyph will be written into
      * composite.
      */
     inline bool get_composite (hb_codepoint_t glyph,
 			       CompositeGlyphHeader::Iterator *composite /* OUT */) const
     {
-      if (this->glyf_table == &Null(glyf) || !num_glyphs)
+      if (unlikely (!num_glyphs))
 	return false;
 
       unsigned int start_offset, end_offset;
       if (!get_offsets (glyph, &start_offset, &end_offset))
         return false; /* glyph not found */
 
       return CompositeGlyphHeader::get_iterator ((const char*) this->glyf_table + start_offset,
 						 end_offset - start_offset,
--- a/gfx/harfbuzz/src/hb-ot-head-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-head-table.hh
@@ -136,18 +136,18 @@ struct head
   HBUINT16	lowestRecPPEM;		/* Smallest readable size in pixels. */
   HBINT16	fontDirectionHint;	/* Deprecated (Set to 2).
 					 * 0: Fully mixed directional glyphs;
 					 * 1: Only strongly left to right;
 					 * 2: Like 1 but also contains neutrals;
 					 * -1: Only strongly right to left;
 					 * -2: Like -1 but also contains neutrals. */
   public:
-  HBINT16	indexToLocFormat;	/* 0 for short offsets, 1 for long. */
-  HBINT16	glyphDataFormat;	/* 0 for current format. */
+  HBUINT16	indexToLocFormat;	/* 0 for short offsets, 1 for long. */
+  HBUINT16	glyphDataFormat;	/* 0 for current format. */
 
   DEFINE_SIZE_STATIC (54);
 };
 
 
 } /* namespace OT */
 
 
--- a/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
@@ -64,17 +64,17 @@ struct hmtxvmtx
      * struct do all the hard work... */
     return_trace (true);
   }
 
 
   inline bool subset_update_header (hb_subset_plan_t *plan,
                                     unsigned int num_hmetrics) const
   {
-    hb_blob_t *src_blob = OT::Sanitizer<H> ().sanitize (plan->source->reference_table (H::tableTag));
+    hb_blob_t *src_blob = hb_sanitize_context_t().reference_table<H> (plan->source, H::tableTag);
     hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail(src_blob);
     hb_blob_destroy (src_blob);
 
     if (unlikely (!dest_blob)) {
       return false;
     }
 
     unsigned int length;
@@ -190,44 +190,44 @@ struct hmtxvmtx
     inline void init (hb_face_t *face,
 		      unsigned int default_advance_ = 0)
     {
       default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);
 
       bool got_font_extents = false;
       if (T::os2Tag)
       {
-	hb_blob_t *os2_blob = Sanitizer<os2> ().sanitize (face->reference_table (T::os2Tag));
+	hb_blob_t *os2_blob = hb_sanitize_context_t().reference_table<os2> (face);
 	const os2 *os2_table = os2_blob->as<os2> ();
 #define USE_TYPO_METRICS (1u<<7)
 	if (0 != (os2_table->fsSelection & USE_TYPO_METRICS))
 	{
 	  ascender = os2_table->sTypoAscender;
 	  descender = os2_table->sTypoDescender;
 	  line_gap = os2_table->sTypoLineGap;
 	  got_font_extents = (ascender | descender) != 0;
 	}
 	hb_blob_destroy (os2_blob);
       }
 
-      hb_blob_t *_hea_blob = Sanitizer<H> ().sanitize (face->reference_table (H::tableTag));
+      hb_blob_t *_hea_blob = hb_sanitize_context_t().reference_table<H> (face);
       const H *_hea_table = _hea_blob->as<H> ();
       num_advances = _hea_table->numberOfLongMetrics;
       if (!got_font_extents)
       {
 	ascender = _hea_table->ascender;
 	descender = _hea_table->descender;
 	line_gap = _hea_table->lineGap;
 	got_font_extents = (ascender | descender) != 0;
       }
       hb_blob_destroy (_hea_blob);
 
       has_font_extents = got_font_extents;
 
-      blob = Sanitizer<hmtxvmtx> ().sanitize (face->reference_table (T::tableTag));
+      blob = hb_sanitize_context_t().reference_table<hmtxvmtx> (face, T::tableTag);
 
       /* Cap num_metrics() and num_advances() based on table length. */
       unsigned int len = hb_blob_get_length (blob);
       if (unlikely (num_advances * 4 > len))
 	num_advances = len / 4;
       num_metrics = num_advances + (len - 4 * num_advances) / 2;
 
       /* We MUST set num_metrics to zero if num_advances is zero.
@@ -235,17 +235,17 @@ struct hmtxvmtx
       if (unlikely (!num_advances))
       {
 	num_metrics = num_advances = 0;
 	hb_blob_destroy (blob);
 	blob = hb_blob_get_empty ();
       }
       table = blob->as<hmtxvmtx> ();
 
-      var_blob = Sanitizer<HVARVVAR> ().sanitize (face->reference_table (T::variationsTag));
+      var_blob = hb_sanitize_context_t().reference_table<HVARVVAR> (face, T::variationsTag);
       var_table = var_blob->as<HVARVVAR> ();
     }
 
     inline void fini (void)
     {
       hb_blob_destroy (blob);
       hb_blob_destroy (var_blob);
     }
--- a/gfx/harfbuzz/src/hb-ot-kern-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-kern-table.hh
@@ -357,17 +357,17 @@ struct kern
     default:return_trace (true);
     }
   }
 
   struct accelerator_t
   {
     inline void init (hb_face_t *face)
     {
-      blob = Sanitizer<kern>().sanitize (face->reference_table (HB_OT_TAG_kern));
+      blob = hb_sanitize_context_t().reference_table<kern> (face);
       table = blob->as<kern> ();
       table_length = blob->length;
     }
     inline void fini (void)
     {
       hb_blob_destroy (blob);
     }
 
--- a/gfx/harfbuzz/src/hb-ot-layout-base-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-base-table.hh
@@ -28,36 +28,39 @@
 #ifndef HB_OT_LAYOUT_BASE_TABLE_HH
 #define HB_OT_LAYOUT_BASE_TABLE_HH
 
 #include "hb-open-type-private.hh"
 #include "hb-ot-layout-common-private.hh"
 
 namespace OT {
 
-#define NOT_INDEXED   ((unsigned int) -1)
-
 /*
  * BASE -- Baseline
  * https://docs.microsoft.com/en-us/typography/opentype/spec/base
  */
 
+
+/* XXX Review this. */
+#define NOT_INDEXED		((unsigned int) -1)
+
+
 struct BaseCoordFormat1
 {
   inline int get_coord (void) const { return coordinate; }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 1 */
-  HBINT16	coordinate;	/* X or Y value, in design units */
+  FWORD		coordinate;	/* X or Y value, in design units */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
 struct BaseCoordFormat2
 {
   inline int get_coord (void) const
   {
@@ -68,17 +71,17 @@ struct BaseCoordFormat2
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 2 */
-  HBINT16	coordinate;	/* X or Y value, in design units */
+  FWORD		coordinate;	/* X or Y value, in design units */
   GlyphID	referenceGlyph;	/* Glyph ID of control glyph */
   HBUINT16	coordPoint;	/* Index of contour point on the
 				 * reference glyph */
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
 struct BaseCoordFormat3
@@ -92,28 +95,29 @@ struct BaseCoordFormat3
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && deviceTable.sanitize (c, this));
   }
 
   protected:
   HBUINT16		format;		/* Format identifier--format = 3 */
-  HBINT16		coordinate;	/* X or Y value, in design units */
+  FWORD			coordinate;	/* X or Y value, in design units */
   OffsetTo<Device>	deviceTable;	/* Offset to Device table for X or
 					 * Y value, from beginning of
 					 * BaseCoord table (may be NULL). */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 struct BaseCoord
 {
   inline int get_coord (void) const
   {
+    /* XXX wire up direction and font. */
     switch (u.format) {
     case 1: return u.format1.get_coord ();
     case 2: return u.format2.get_coord ();
     case 3: return u.format3.get_coord ();
     default:return 0;
     }
   }
 
@@ -137,24 +141,20 @@ struct BaseCoord
     BaseCoordFormat3	format3;
   } u;
   public:
   DEFINE_SIZE_UNION (2, format);
 };
 
 struct FeatMinMaxRecord
 {
-  inline int get_min_value (void) const
-  { return (this+minCoord).get_coord(); }
+  inline int get_min_value (void) const { return (this+minCoord).get_coord(); }
+  inline int get_max_value (void) const { return (this+maxCoord).get_coord(); }
 
-  inline int get_max_value (void) const
-  { return (this+maxCoord).get_coord(); }
-
-  inline const Tag &get_tag () const
-  { return tag; }
+  inline const Tag& get_tag () const { return tag; }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  minCoord.sanitize (c, base) &&
 		  maxCoord.sanitize (c, base));
   }
@@ -176,17 +176,17 @@ struct FeatMinMaxRecord
 struct MinMax
 {
   inline unsigned int get_feature_tag_index (Tag featureTableTag) const
   {
     /* TODO bsearch */
     unsigned int count = featMinMaxRecords.len;
     for (unsigned int i = 0; i < count; i++)
     {
-      Tag tag = featMinMaxRecords[i].get_tag();
+      Tag tag = featMinMaxRecords[i].get_tag ();
       int cmp = tag.cmp(featureTableTag);
       if (cmp == 0) return i;
       if (cmp > 0)  return NOT_INDEXED;
     }
     return NOT_INDEXED;
   }
 
   inline int get_min_value (unsigned int featureTableTagIndex) const
@@ -228,23 +228,23 @@ struct MinMax
 
 /* TODO... */
 struct BaseLangSysRecord
 {
   inline const Tag& get_tag(void) const
   { return baseLangSysTag; }
 
   inline unsigned int get_feature_tag_index (Tag featureTableTag) const
-  { return (this+minMax).get_feature_tag_index(featureTableTag); }
+  { return (this+minMax).get_feature_tag_index( featureTableTag); }
 
   inline int get_min_value (unsigned int featureTableTagIndex) const
-  { return (this+minMax).get_min_value(featureTableTagIndex); }
+  { return (this+minMax).get_min_value( featureTableTagIndex); }
 
   inline int get_max_value (unsigned int featureTableTagIndex) const
-  { return (this+minMax).get_max_value(featureTableTagIndex); }
+  { return (this+minMax).get_max_value (featureTableTagIndex); }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  minMax.sanitize (c, base));
   }
 
@@ -258,171 +258,168 @@ struct BaseLangSysRecord
 
 struct BaseValues
 {
   inline unsigned int get_default_base_tag_index (void) const
   { return defaultIndex; }
 
   inline int get_base_coord (unsigned int baselineTagIndex) const
   {
-    return (this+baseCoords[baselineTagIndex]).get_coord();
+    return (this+baseCoords[baselineTagIndex]).get_coord ();
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-      defaultIndex <= baseCoordCount &&
-      baseCoords.sanitize (c, this));
+		  baseCoords.sanitize (c, this));
   }
 
   protected:
   Index				defaultIndex;
-  HBUINT16			baseCoordCount;
   OffsetArrayOf<BaseCoord>	baseCoords;
   public:
-  DEFINE_SIZE_ARRAY (6, baseCoords);
-
+  DEFINE_SIZE_ARRAY (4, baseCoords);
 };
 
 struct BaseScript {
 
   inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const
   {
+    /* XXX bsearch */
     Tag tag;
     int cmp;
-    for (unsigned int i = 0; i < baseLangSysCount; i++) {
-      tag = baseLangSysRecords[i].get_tag();
+    unsigned int count = baseLangSysRecords.len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      tag = baseLangSysRecords[i].get_tag ();
       // taking advantage of alphabetical order
       cmp = tag.cmp(baseLangSysTag);
       if (cmp == 0) return i;
       if (cmp > 0)  return NOT_INDEXED;
     }
     return NOT_INDEXED;
   }
 
   inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const
   {
-    if (baseLangSysIndex == NOT_INDEXED) {
+    if (baseLangSysIndex == NOT_INDEXED)
+    {
       if (unlikely(defaultMinMax)) return NOT_INDEXED;
-      return (this+defaultMinMax).get_feature_tag_index(featureTableTag);
+      return (this+defaultMinMax).get_feature_tag_index (featureTableTag);
     }
-    if (unlikely(baseLangSysIndex >= baseLangSysCount)) return NOT_INDEXED;
-    return baseLangSysRecords[baseLangSysIndex].get_feature_tag_index(featureTableTag);
+    return baseLangSysRecords[baseLangSysIndex].get_feature_tag_index (featureTableTag);
   }
 
   inline int get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (baseLangSysIndex == NOT_INDEXED)
-      return (this+defaultMinMax).get_min_value(featureTableTagIndex);
-    return baseLangSysRecords[baseLangSysIndex].get_max_value(featureTableTagIndex);
+      return (this+defaultMinMax).get_min_value (featureTableTagIndex);
+    return baseLangSysRecords[baseLangSysIndex].get_max_value (featureTableTagIndex);
   }
 
   inline int get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (baseLangSysIndex == NOT_INDEXED)
-      return (this+defaultMinMax).get_min_value(featureTableTagIndex);
-    return baseLangSysRecords[baseLangSysIndex].get_max_value(featureTableTagIndex);
+      return (this+defaultMinMax).get_min_value (featureTableTagIndex);
+    return baseLangSysRecords[baseLangSysIndex].get_max_value (featureTableTagIndex);
   }
 
   inline unsigned int get_default_base_tag_index (void) const
-  { return (this+baseValues).get_default_base_tag_index(); }
+  { return (this+baseValues).get_default_base_tag_index (); }
 
   inline int get_base_coord (unsigned int baselineTagIndex) const
-  { return (this+baseValues).get_base_coord(baselineTagIndex); }
+  { return (this+baseValues).get_base_coord (baselineTagIndex); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-      baseValues.sanitize (c, this) &&
-      defaultMinMax.sanitize (c, this) &&
-      baseLangSysRecords.sanitize (c, this));
+		  baseValues.sanitize (c, this) &&
+		  defaultMinMax.sanitize (c, this) &&
+		  baseLangSysRecords.sanitize (c, this));
   }
 
   protected:
-  OffsetTo<BaseValues>        baseValues;
-  OffsetTo<MinMax>            defaultMinMax;
-  HBUINT16                      baseLangSysCount;
-  ArrayOf<BaseLangSysRecord>  baseLangSysRecords;
+  OffsetTo<BaseValues>		baseValues;
+  OffsetTo<MinMax>		defaultMinMax;
+  ArrayOf<BaseLangSysRecord>	baseLangSysRecords;
 
   public:
-    DEFINE_SIZE_ARRAY (8, baseLangSysRecords);
+    DEFINE_SIZE_ARRAY (6, baseLangSysRecords);
 };
 
 
 struct BaseScriptRecord {
 
   inline const Tag& get_tag (void) const
   { return baseScriptTag; }
 
   inline unsigned int get_default_base_tag_index(void) const
-  { return (this+baseScript).get_default_base_tag_index(); }
+  { return (this+baseScript).get_default_base_tag_index (); }
 
   inline int get_base_coord(unsigned int baselineTagIndex) const
-  { return (this+baseScript).get_base_coord(baselineTagIndex); }
+  { return (this+baseScript).get_base_coord (baselineTagIndex); }
 
   inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const
-  { return (this+baseScript).get_lang_tag_index(baseLangSysTag); }
+  { return (this+baseScript).get_lang_tag_index (baseLangSysTag); }
 
   inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const
-  { return (this+baseScript).get_feature_tag_index(baseLangSysIndex, featureTableTag); }
+  { return (this+baseScript).get_feature_tag_index (baseLangSysIndex, featureTableTag); }
 
   inline int get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  { return (this+baseScript).get_max_value(baseLangSysIndex, featureTableTagIndex); }
+  { return (this+baseScript).get_max_value (baseLangSysIndex, featureTableTagIndex); }
 
   inline int get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  { return (this+baseScript).get_min_value(baseLangSysIndex, featureTableTagIndex); }
+  { return (this+baseScript).get_min_value (baseLangSysIndex, featureTableTagIndex); }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-      baseScript != Null(OffsetTo<BaseScript>) &&
-      baseScript.sanitize (c, base));
+		  baseScript.sanitize (c, base));
   }
 
   protected:
   Tag                   baseScriptTag;
   OffsetTo<BaseScript>  baseScript;
 
   public:
     DEFINE_SIZE_STATIC (6);
 };
 
 struct BaseScriptList {
 
   inline unsigned int get_base_script_index (Tag baseScriptTag) const
   {
-    for (unsigned int i = 0; i < baseScriptCount; i++)
+    /* XXX bsearch? */
+    unsigned int count = baseScriptRecords.len;
+    for (unsigned int i = 0; i < count; i++)
       if (baseScriptRecords[i].get_tag() == baseScriptTag)
         return i;
     return NOT_INDEXED;
   }
 
   inline unsigned int get_default_base_tag_index (unsigned int baseScriptIndex) const
   {
-    if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED;
     return baseScriptRecords[baseScriptIndex].get_default_base_tag_index();
   }
 
   inline int get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   {
     return baseScriptRecords[baseScriptIndex].get_base_coord(baselineTagIndex);
   }
 
   inline unsigned int get_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
   {
-    if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED;
     return baseScriptRecords[baseScriptIndex].get_lang_tag_index(baseLangSysTag);
   }
 
   inline unsigned int get_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
   {
-    if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED;
     return baseScriptRecords[baseScriptIndex].get_feature_tag_index(baseLangSysIndex, featureTableTag);
   }
 
   inline int get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     return baseScriptRecords[baseScriptIndex].get_max_value(baseLangSysIndex, featureTableTagIndex);
   }
 
@@ -430,82 +427,76 @@ struct BaseScriptList {
   {
     return baseScriptRecords[baseScriptIndex].get_min_value(baseLangSysIndex, featureTableTagIndex);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-      baseScriptRecords.sanitize (c, this));
+		  baseScriptRecords.sanitize (c, this));
   }
 
   protected:
-  HBUINT16                    baseScriptCount;
-  ArrayOf<BaseScriptRecord> baseScriptRecords;
+  ArrayOf<BaseScriptRecord>	baseScriptRecords;
 
   public:
-  DEFINE_SIZE_ARRAY (4, baseScriptRecords);
-
+  DEFINE_SIZE_ARRAY (2, baseScriptRecords);
 };
 
 struct BaseTagList
 {
-
-  inline unsigned int get_tag_index(Tag baselineTag) const
+  inline unsigned int get_tag_index (Tag baselineTag) const
   {
-    for (unsigned int i = 0; i < baseTagCount; i++)
+    /* TODO bsearch? */
+    unsigned int count = baselineTags.len;
+    for (unsigned int i = 0; i < count; i++)
       if (baselineTags[i] == baselineTag)
         return i;
     return NOT_INDEXED;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
   protected:
-  HBUINT16        baseTagCount;
   SortedArrayOf<Tag>  baselineTags;
 
   public:
-  DEFINE_SIZE_ARRAY (4, baselineTags);
+  DEFINE_SIZE_ARRAY (2, baselineTags);
 };
 
 struct Axis
 {
 
-  inline unsigned int get_base_tag_index(Tag baselineTag) const
+  inline unsigned int get_base_tag_index (Tag baselineTag) const
   {
-    if (unlikely(baseTagList == Null(OffsetTo<BaseTagList>))) return NOT_INDEXED;
     return (this+baseTagList).get_tag_index(baselineTag);
   }
 
   inline unsigned int get_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
   {
-    if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NOT_INDEXED;
     return (this+baseScriptList).get_default_base_tag_index(baseScriptIndex);
   }
 
-  inline int get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline int get_base_coord (unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   {
     return (this+baseScriptList).get_base_coord(baseScriptIndex, baselineTagIndex);
   }
 
   inline unsigned int get_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
   {
-    if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NOT_INDEXED;
     return (this+baseScriptList).get_lang_tag_index(baseScriptIndex, baseLangSysTag);
   }
 
   inline unsigned int get_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
   {
-    if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NOT_INDEXED;
     return (this+baseScriptList).get_feature_tag_index(baseScriptIndex, baseLangSysIndex, featureTableTag);
   }
 
   inline int get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     return (this+baseScriptList).get_max_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
@@ -513,138 +504,120 @@ struct Axis
   {
     return (this+baseScriptList).get_min_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-      baseTagList.sanitize (c, this) &&
-      baseScriptList.sanitize (c, this));
+		  baseTagList.sanitize (c, this) &&
+		  baseScriptList.sanitize (c, this));
   }
 
   protected:
-  OffsetTo<BaseTagList>     baseTagList;
-  OffsetTo<BaseScriptList>  baseScriptList;
+  OffsetTo<BaseTagList>		baseTagList;
+  OffsetTo<BaseScriptList>	baseScriptList;
 
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
 struct BASE
 {
   static const hb_tag_t tableTag = HB_OT_TAG_BASE;
 
-  inline bool has_vert_axis(void)
-  { return vertAxis != Null(OffsetTo<Axis>); }
+  inline bool has_v_axis(void) { return vAxis != 0; }
 
-  inline bool has_horiz_axis(void)
-  { return horizAxis != Null(OffsetTo<Axis>); }
+  inline bool has_h_axis(void) { return hAxis != 0; }
 
-  // horizontal axis base coords:
-
-  inline unsigned int get_horiz_base_tag_index(Tag baselineTag) const
+  inline unsigned int get_h_base_tag_index (Tag baselineTag) const
   {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_base_tag_index(baselineTag);
+    return (this+hAxis).get_base_tag_index(baselineTag);
   }
 
-  inline unsigned int get_horiz_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
+  inline unsigned int get_h_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
   {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
+    return (this+hAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
   }
 
-  inline int get_horiz_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline int get_h_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   {
-    return (this+horizAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
+    return (this+hAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
   }
 
-  // vertical axis base coords:
-
-  inline unsigned int get_vert_base_tag_index(Tag baselineTag) const
+  inline unsigned int get_v_base_tag_index(Tag baselineTag) const
   {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_base_tag_index(baselineTag);
+    return (this+vAxis).get_base_tag_index(baselineTag);
   }
 
-  inline unsigned int get_vert_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
+  inline unsigned int get_v_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
   {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
+    return (this+vAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
   }
 
-  inline int get_vert_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline int get_v_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   {
-    return (this+vertAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
+    return (this+vAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
   }
 
-  // horizontal axis min/max coords:
-
-  inline unsigned int get_horiz_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
+  inline unsigned int get_h_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
   {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
+    return (this+hAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
   }
 
-  inline unsigned int get_horiz_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
+  inline unsigned int get_h_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
   {
-    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+horizAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
+    return (this+hAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
   }
 
-  inline int get_horiz_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline int get_h_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
-    return (this+horizAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+    return (this+hAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
-  inline int get_horiz_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline int get_h_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
-    return (this+horizAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+    return (this+hAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
-    // vertical axis min/max coords:
-
-  inline unsigned int get_vert_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
+  inline unsigned int get_v_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
   {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
+    return (this+vAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
   }
 
-  inline unsigned int get_vert_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
+  inline unsigned int get_v_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
   {
-    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
-    return (this+vertAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
+    return (this+vAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
   }
 
-  inline int get_vert_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline int get_v_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
-    return (this+vertAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+    return (this+vAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
-  inline int get_vert_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline int get_v_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
-    return (this+vertAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+    return (this+vAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  likely (version.major == 1) &&
-		  horizAxis.sanitize (c, this) &&
-		  vertAxis.sanitize (c, this) &&
+		  hAxis.sanitize (c, this) &&
+		  vAxis.sanitize (c, this) &&
 		  (version.to_int () < 0x00010001u || varStore.sanitize (c, this)));
   }
 
   protected:
   FixedVersion<>  version;
-  OffsetTo<Axis>  horizAxis;
-  OffsetTo<Axis>  vertAxis;
+  OffsetTo<Axis>  hAxis;
+  OffsetTo<Axis>  vAxis;
   LOffsetTo<VariationStore>
 		varStore;		/* Offset to the table of Item Variation
 					 * Store--from beginning of BASE
 					 * header (may be NULL).  Introduced
 					 * in version 0x00010001. */
   public:
   DEFINE_SIZE_MIN (8);
 };
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -36,16 +36,25 @@
 
 
 #ifndef HB_MAX_NESTING_LEVEL
 #define HB_MAX_NESTING_LEVEL	6
 #endif
 #ifndef HB_MAX_CONTEXT_LENGTH
 #define HB_MAX_CONTEXT_LENGTH	64
 #endif
+#ifndef HB_CLOSURE_MAX_STAGES
+/*
+ * The maximum number of times a lookup can be applied during shaping.
+ * Used to limit the number of iterations of the closure algorithm.
+ * This must be larger than the number of times add_pause() is
+ * called in a collect_features call of any shaper.
+ */
+#define HB_CLOSURE_MAX_STAGES	32
+#endif
 
 
 namespace OT {
 
 
 #define NOT_COVERED		((unsigned int) -1)
 
 
@@ -159,33 +168,38 @@ struct RangeRecord
   }
 
   GlyphID	start;		/* First GlyphID in the range */
   GlyphID	end;		/* Last GlyphID in the range */
   HBUINT16	value;		/* Value */
   public:
   DEFINE_SIZE_STATIC (6);
 };
-DEFINE_NULL_DATA (OT, RangeRecord, "\000\001");
+DECLARE_NULL_NAMESPACE_BYTES (OT, RangeRecord);
 
 
 struct IndexArray : ArrayOf<Index>
 {
   inline unsigned int get_indexes (unsigned int start_offset,
 				   unsigned int *_count /* IN/OUT */,
 				   unsigned int *_indexes /* OUT */) const
   {
     if (_count) {
       const HBUINT16 *arr = this->sub_array (start_offset, _count);
       unsigned int count = *_count;
       for (unsigned int i = 0; i < count; i++)
 	_indexes[i] = arr[i];
     }
     return this->len;
   }
+
+  inline void add_indexes_to (hb_set_t* output /* OUT */) const
+  {
+    output->add_array (arrayZ, len);
+  }
 };
 
 
 struct Script;
 struct LangSys;
 struct Feature;
 
 
@@ -194,16 +208,18 @@ struct LangSys
   inline unsigned int get_feature_count (void) const
   { return featureIndex.len; }
   inline hb_tag_t get_feature_index (unsigned int i) const
   { return featureIndex[i]; }
   inline unsigned int get_feature_indexes (unsigned int start_offset,
 					   unsigned int *feature_count /* IN/OUT */,
 					   unsigned int *feature_indexes /* OUT */) const
   { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); }
+  inline void add_feature_indexes_to (hb_set_t *feature_indexes) const
+  { featureIndex.add_indexes_to (feature_indexes); }
 
   inline bool has_required_feature (void) const { return reqFeatureIndex != 0xFFFFu; }
   inline unsigned int get_required_feature_index (void) const
   {
     if (reqFeatureIndex == 0xFFFFu)
       return Index::NOT_FOUND_INDEX;
    return reqFeatureIndex;;
   }
@@ -219,18 +235,17 @@ struct LangSys
 				 * reordering table) */
   HBUINT16	reqFeatureIndex;/* Index of a feature required for this
 				 * language system--if no required features
 				 * = 0xFFFFu */
   IndexArray	featureIndex;	/* Array of indices into the FeatureList */
   public:
   DEFINE_SIZE_ARRAY (6, featureIndex);
 };
-DEFINE_NULL_DATA (OT, LangSys, "\0\0\xFF\xFF");
-
+DECLARE_NULL_NAMESPACE_BYTES (OT, LangSys);
 
 struct Script
 {
   inline unsigned int get_lang_sys_count (void) const
   { return langSys.len; }
   inline const Tag& get_lang_sys_tag (unsigned int i) const
   { return langSys.get_tag (i); }
   inline unsigned int get_lang_sys_tags (unsigned int start_offset,
@@ -535,19 +550,16 @@ struct Feature
 
       OffsetTo<FeatureParams> new_offset;
       /* Check that it did not overflow. */
       new_offset.set (new_offset_int);
       if (new_offset == new_offset_int &&
 	  c->try_set (&featureParams, new_offset) &&
 	  !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))
 	return_trace (false);
-
-      if (c->edit_count > 1)
-        c->edit_count--; /* This was a "legitimate" edit; don't contribute to error count. */
     }
 
     return_trace (true);
   }
 
   OffsetTo<FeatureParams>
 		 featureParams;	/* Offset to Feature Parameters table (if one
 				 * has been defined for the feature), relative
@@ -592,16 +604,24 @@ struct Lookup
 
   template <typename SubTableType>
   inline const OffsetArrayOf<SubTableType>& get_subtables (void) const
   { return CastR<OffsetArrayOf<SubTableType> > (subTable); }
   template <typename SubTableType>
   inline OffsetArrayOf<SubTableType>& get_subtables (void)
   { return CastR<OffsetArrayOf<SubTableType> > (subTable); }
 
+  inline unsigned int get_size (void) const
+  {
+    const HBUINT16 &markFilteringSet = StructAfter<const HBUINT16> (subTable);
+    if (lookupFlag & LookupFlag::UseMarkFilteringSet)
+      return (const char *) &StructAfter<const char> (markFilteringSet) - (const char *) this;
+    return (const char *) &markFilteringSet - (const char *) this;
+  }
+
   inline unsigned int get_type (void) const { return lookupType; }
 
   /* lookup_props is a 32-bit integer where the lower 16-bit is LookupFlag and
    * higher 16-bit is mark-filtering-set if the lookup uses one.
    * Not to be confused with glyph_props which is very similar. */
   inline uint32_t get_props (void) const
   {
     unsigned int flag = lookupFlag;
@@ -634,16 +654,17 @@ struct Lookup
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
     lookupType.set (lookup_type);
     lookupFlag.set (lookup_props & 0xFFFFu);
     if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false);
     if (lookupFlag & LookupFlag::UseMarkFilteringSet)
     {
+      if (unlikely (!c->extend (*this))) return_trace (false);
       HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable);
       markFilteringSet.set (lookup_props >> 16);
     }
     return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
--- a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
@@ -344,16 +344,17 @@ struct GDEF
   enum GlyphClasses {
     UnclassifiedGlyph	= 0,
     BaseGlyph		= 1,
     LigatureGlyph	= 2,
     MarkGlyph		= 3,
     ComponentGlyph	= 4
   };
 
+  inline bool has_data (void) const { return version.to_int () != 0; }
   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
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -1623,24 +1623,24 @@ GPOS::position_finish_offsets (hb_font_t
 }
 
 
 /* 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)
 {
-  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos);
+  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->table.GPOS);
   const PosLookup &l = gpos.get_lookup (lookup_index);
   return l.dispatch (c);
 }
 
 /*static*/ inline bool PosLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
 {
-  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos);
+  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->table.GPOS);
   const PosLookup &l = gpos.get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
   unsigned int saved_lookup_index = c->lookup_index;
   c->set_lookup_index (lookup_index);
   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);
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -42,17 +42,17 @@ struct SingleSubstFormat1
     TRACE_CLOSURE (this);
     Coverage::Iter iter;
     for (iter.init (this+coverage); iter.more (); iter.next ())
     {
       /* TODO Switch to range-based API to work around malicious fonts.
        * https://github.com/harfbuzz/harfbuzz/issues/363 */
       hb_codepoint_t glyph_id = iter.get_glyph ();
       if (c->glyphs->has (glyph_id))
-	c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
+	c->out->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
     }
   }
 
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
     Coverage::Iter iter;
@@ -127,17 +127,17 @@ struct SingleSubstFormat2
     TRACE_CLOSURE (this);
     Coverage::Iter iter;
     unsigned int count = substitute.len;
     for (iter.init (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
-	c->glyphs->add (substitute[iter.get_coverage ()]);
+	c->out->add (substitute[iter.get_coverage ()]);
     }
   }
 
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
     Coverage::Iter iter;
@@ -258,17 +258,17 @@ struct SingleSubst
 
 struct Sequence
 {
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     unsigned int count = substitute.len;
     for (unsigned int i = 0; i < count; i++)
-      c->glyphs->add (substitute[i]);
+      c->out->add (substitute[i]);
   }
 
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     c->output->add_array (substitute.arrayZ, substitute.len);
   }
 
@@ -459,17 +459,17 @@ struct AlternateSubstFormat1
     for (iter.init (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ())) {
 	const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
 	unsigned int count = alt_set.len;
 	for (unsigned int i = 0; i < count; i++)
-	  c->glyphs->add (alt_set[i]);
+	  c->out->add (alt_set[i]);
       }
     }
   }
 
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -600,17 +600,17 @@ struct Ligature
 {
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     unsigned int count = component.len;
     for (unsigned int i = 1; i < count; i++)
       if (!c->glyphs->has (component[i]))
         return;
-    c->glyphs->add (ligGlyph);
+    c->out->add (ligGlyph);
   }
 
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     c->input->add_array (component.arrayZ, component.len ? component.len - 1 : 0);
     c->output->add (ligGlyph);
   }
@@ -952,17 +952,17 @@ struct ReverseChainSingleSubstFormat1
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
     Coverage::Iter iter;
     count = substitute.len;
     for (iter.init (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
-	c->glyphs->add (substitute[iter.get_coverage ()]);
+	c->out->add (substitute[iter.get_coverage ()]);
     }
   }
 
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
 
@@ -1158,17 +1158,22 @@ struct SubstLookup : Lookup
 
   inline hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
   {
     TRACE_CLOSURE (this);
     if (!c->should_visit_lookup (this_index))
       return_trace (HB_VOID);
 
     c->set_recurse_func (dispatch_closure_recurse_func);
-    return_trace (dispatch (c));
+
+    hb_closure_context_t::return_t ret = dispatch (c);
+
+    c->flush ();
+
+    return_trace (ret);
   }
 
   inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
     return_trace (dispatch (c));
   }
@@ -1260,17 +1265,22 @@ struct SubstLookup : Lookup
 
   template <typename context_t>
   static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
 
   static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index)
   {
     if (!c->should_visit_lookup (lookup_index))
       return HB_VOID;
-    return dispatch_recurse_func (c, lookup_index);
+
+    hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index);
+
+    c->flush ();
+
+    return ret;
   }
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   { return Lookup::dispatch<SubstLookupSubTable> (c); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -1320,17 +1330,17 @@ struct GSUB : GSUBGPOS
 };
 
 
 void
 GSUB::substitute_start (hb_font_t *font, hb_buffer_t *buffer)
 {
   _hb_buffer_assert_gsubgpos_vars (buffer);
 
-  const GDEF &gdef = *hb_ot_layout_from_face (font->face)->gdef;
+  const GDEF &gdef = *hb_ot_layout_from_face (font->face)->table.GDEF;
   unsigned int count = buffer->len;
   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;
   }
 }
@@ -1344,24 +1354,24 @@ GSUB::substitute_start (hb_font_t *font,
   if (unlikely (type == SubstLookupSubTable::Extension))
     return CastR<ExtensionSubst> (get_subtable<LookupSubTable>()).is_reverse ();
   return SubstLookup::lookup_type_is_reverse (type);
 }
 
 template <typename context_t>
 /*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
 {
-  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
+  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->table.GSUB);
   const SubstLookup &l = gsub.get_lookup (lookup_index);
   return l.dispatch (c);
 }
 
 /*static*/ inline bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
 {
-  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
+  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->table.GSUB);
   const SubstLookup &l = gsub.get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
   unsigned int saved_lookup_index = c->lookup_index;
   c->set_lookup_index (lookup_index);
   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);
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -64,39 +64,51 @@ struct hb_closure_context_t :
     if (is_lookup_done (lookup_index))
       return false;
     done_lookups->set (lookup_index, glyphs->get_population ());
     return true;
   }
 
   bool is_lookup_done (unsigned int lookup_index)
   {
-    // Have we visited this lookup with the current set of glyphs?
+    /* Have we visited this lookup with the current set of glyphs? */
     return done_lookups->get (lookup_index) == glyphs->get_population ();
   }
 
   hb_face_t *face;
   hb_set_t *glyphs;
+  hb_auto_t<hb_set_t> out[1];
   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_,
                         hb_map_t *done_lookups_,
 		        unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
 			  face (face_),
 			  glyphs (glyphs_),
 			  recurse_func (nullptr),
 			  nesting_level_left (nesting_level_left_),
 			  debug_depth (0),
                           done_lookups (done_lookups_) {}
 
+  ~hb_closure_context_t (void)
+  {
+    flush ();
+  }
+
   void set_recurse_func (recurse_func_t func) { recurse_func = func; }
 
+  void flush (void)
+  {
+    hb_set_union (glyphs, out);
+    hb_set_clear (out);
+  }
+
   private:
   hb_map_t *done_lookups;
 };
 
 
 struct hb_would_apply_context_t :
        hb_dispatch_context_t<hb_would_apply_context_t, bool, HB_DEBUG_WOULD_APPLY>
 {
@@ -463,17 +475,17 @@ struct hb_ot_apply_context_t :
 
 
   hb_ot_apply_context_t (unsigned int table_index_,
 		      hb_font_t *font_,
 		      hb_buffer_t *buffer_) :
 			iter_input (), iter_context (),
 			font (font_), face (font->face), buffer (buffer_),
 			recurse_func (nullptr),
-			gdef (*hb_ot_layout_from_face (face)->gdef),
+			gdef (*hb_ot_layout_from_face (face)->table.GDEF),
 			var_store (gdef.get_var_store ()),
 			direction (buffer_->props.direction),
 			lookup_mask (1),
 			table_index (table_index_),
 			lookup_index ((unsigned int) -1),
 			lookup_props (0),
 			nesting_level_left (HB_MAX_NESTING_LEVEL),
 			debug_depth (0),
@@ -2303,16 +2315,17 @@ struct Extension
 
 
 /*
  * GSUB/GPOS Common
  */
 
 struct GSUBGPOS
 {
+  inline bool has_data (void) const { return version.to_int () != 0; }
   inline unsigned int get_script_count (void) const
   { return (this+scriptList).len; }
   inline const Tag& get_script_tag (unsigned int i) const
   { return (this+scriptList).get_tag (i); }
   inline unsigned int get_script_tags (unsigned int start_offset,
 				       unsigned int *script_count /* IN/OUT */,
 				       hb_tag_t     *script_tags /* OUT */) const
   { return (this+scriptList).get_tags (start_offset, script_count, script_tags); }
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -116,35 +116,16 @@ hb_ot_layout_position_finish_offsets (hb
 				      hb_buffer_t  *buffer);
 
 
 
 /*
  * hb_ot_layout_t
  */
 
-namespace OT {
-  struct BASE;
-  struct COLR;
-  struct CPAL;
-  struct GDEF;
-  struct GSUB;
-  struct GPOS;
-  struct MATH;
-  struct fvar;
-  struct avar;
-}
-
-namespace AAT {
-  struct ankr;
-  struct kerx;
-  struct morx;
-  struct trak;
-}
-
 struct hb_ot_layout_lookup_accelerator_t
 {
   template <typename TLookup>
   inline void init (const TLookup &lookup)
   {
     digest.init ();
     lookup.add_coverage (&digest);
   }
@@ -156,37 +137,83 @@ struct hb_ot_layout_lookup_accelerator_t
   inline bool may_have (hb_codepoint_t g) const {
     return digest.may_have (g);
   }
 
   private:
   hb_set_digest_t digest;
 };
 
+/* Most of these tables are NOT needed for shaping.  But we need to hook them *somewhere*.
+ * This is as good as any place. */
+#define HB_OT_LAYOUT_TABLES \
+    /* OpenType shaping. */ \
+    HB_OT_LAYOUT_TABLE(OT, GDEF) \
+    HB_OT_LAYOUT_TABLE(OT, GSUB) \
+    HB_OT_LAYOUT_TABLE(OT, GPOS) \
+    HB_OT_LAYOUT_TABLE(OT, JSTF) \
+    HB_OT_LAYOUT_TABLE(OT, BASE) \
+    /* AAT shaping. */ \
+    HB_OT_LAYOUT_TABLE(AAT, morx) \
+    HB_OT_LAYOUT_TABLE(AAT, kerx) \
+    HB_OT_LAYOUT_TABLE(AAT, ankr) \
+    HB_OT_LAYOUT_TABLE(AAT, trak) \
+    /* OpenType variations. */ \
+    HB_OT_LAYOUT_TABLE(OT, fvar) \
+    HB_OT_LAYOUT_TABLE(OT, avar) \
+    HB_OT_LAYOUT_TABLE(OT, MVAR) \
+    /* OpenType color. */ \
+    HB_OT_LAYOUT_TABLE(OT, COLR) \
+    HB_OT_LAYOUT_TABLE(OT, CPAL) \
+    HB_OT_LAYOUT_TABLE(OT, CBDT) \
+    HB_OT_LAYOUT_TABLE(OT, CBLC) \
+    HB_OT_LAYOUT_TABLE(OT, sbix) \
+    HB_OT_LAYOUT_TABLE(OT, svg) \
+    /* OpenType math. */ \
+    HB_OT_LAYOUT_TABLE(OT, MATH) \
+    /* OpenType fundamentals. */ \
+    HB_OT_LAYOUT_TABLE(OT, post) \
+    /* */
+
+/* Declare tables. */
+#define HB_OT_LAYOUT_TABLE(Namespace, Type) namespace Namespace { struct Type; }
+HB_OT_LAYOUT_TABLES
+#undef HB_OT_LAYOUT_TABLE
+
 struct hb_ot_layout_t
 {
-  hb_blob_t *gdef_blob;
-  hb_blob_t *gsub_blob;
-  hb_blob_t *gpos_blob;
-
-  const struct OT::GDEF *gdef;
-  const struct OT::GSUB *gsub;
-  const struct OT::GPOS *gpos;
-
-  /* TODO Move the following out of this struct. */
-  OT::hb_table_lazy_loader_t<struct OT::BASE> base;
-  OT::hb_table_lazy_loader_t<struct OT::MATH> math;
-  OT::hb_table_lazy_loader_t<struct OT::fvar> fvar;
-  OT::hb_table_lazy_loader_t<struct OT::avar> avar;
-
   unsigned int gsub_lookup_count;
   unsigned int gpos_lookup_count;
 
   hb_ot_layout_lookup_accelerator_t *gsub_accels;
   hb_ot_layout_lookup_accelerator_t *gpos_accels;
+
+  /* Various non-shaping tables. */
+  struct tables_t
+  {
+    HB_INTERNAL void init0 (hb_face_t *face);
+    HB_INTERNAL void fini (void);
+
+#define HB_OT_LAYOUT_TABLE_ORDER(Namespace, Type) \
+      HB_PASTE (ORDER_, HB_PASTE (Namespace, HB_PASTE (_, Type)))
+    enum order_t
+    {
+      ORDER_ZERO,
+#define HB_OT_LAYOUT_TABLE(Namespace, Type) \
+	HB_OT_LAYOUT_TABLE_ORDER (Namespace, Type),
+      HB_OT_LAYOUT_TABLES
+#undef HB_OT_LAYOUT_TABLE
+    };
+
+    hb_face_t *face; /* MUST be JUST before the lazy loaders. */
+#define HB_OT_LAYOUT_TABLE(Namespace, Type) \
+    hb_table_lazy_loader_t<HB_OT_LAYOUT_TABLE_ORDER (Namespace, Type), struct Namespace::Type> Type;
+    HB_OT_LAYOUT_TABLES
+#undef HB_OT_LAYOUT_TABLE
+  } table;
 };
 
 
 HB_INTERNAL hb_ot_layout_t *
 _hb_ot_layout_create (hb_face_t *face);
 
 HB_INTERNAL void
 _hb_ot_layout_destroy (hb_ot_layout_t *layout);
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -26,176 +26,182 @@
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-open-type-private.hh"
 #include "hb-ot-layout-private.hh"
 #include "hb-ot-map-private.hh"
+#include "hb-map-private.hh"
 
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
 
 // Just so we compile them; unused otherwise:
 #include "hb-ot-layout-base-table.hh"
 #include "hb-ot-layout-jstf-table.hh"
 #include "hb-ot-color-colr-table.hh"
 #include "hb-ot-color-cpal-table.hh"
 #include "hb-ot-color-sbix-table.hh"
 #include "hb-ot-color-svg-table.hh"
 #include "hb-ot-name-table.hh"
-#include "hb-map-private.hh"
+
 
+static bool
+_hb_ot_blacklist_gdef (unsigned int gdef_len,
+		       unsigned int gsub_len,
+		       unsigned int gpos_len)
+{
+  /* The ugly business of blacklisting individual fonts' tables happen here!
+   * See this thread for why we finally had to bend in and do this:
+   * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
+   *
+   * In certain versions of Times New Roman Italic and Bold Italic,
+   * ASCII double quotation mark U+0022 has wrong glyph class 3 (mark)
+   * in GDEF.  Many versions of Tahoma have bad GDEF tables that
+   * incorrectly classify some spacing marks such as certain IPA
+   * symbols as glyph class 3. So do older versions of Microsoft
+   * Himalaya, and the version of Cantarell shipped by Ubuntu 16.04.
+   *
+   * Nuke the GDEF tables of to avoid unwanted width-zeroing.
+   *
+   * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925
+   *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279693
+   *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279875
+   */
+#define ENCODE(x,y,z) ((int64_t) (x) << 32 | (int64_t) (y) << 16 | (z))
+  switch ENCODE(gdef_len, gsub_len, gpos_len)
+  {
+    /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */
+    case ENCODE (442, 2874, 42038):
+    /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */
+    case ENCODE (430, 2874, 40662):
+    /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */
+    case ENCODE (442, 2874, 39116):
+    /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */
+    case ENCODE (430, 2874, 39374):
+    /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */
+    case ENCODE (490, 3046, 41638):
+    /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */
+    case ENCODE (478, 3046, 41902):
+    /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c  tahoma.ttf from Windows 8 */
+    case ENCODE (898, 12554, 46470):
+    /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc  tahomabd.ttf from Windows 8 */
+    case ENCODE (910, 12566, 47732):
+    /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e  tahoma.ttf from Windows 8.1 */
+    case ENCODE (928, 23298, 59332):
+    /* sha1sum:6d400781948517c3c0441ba42acb309584b73033  tahomabd.ttf from Windows 8.1 */
+    case ENCODE (940, 23310, 60732):
+    /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+    case ENCODE (964, 23836, 60072):
+    /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+    case ENCODE (976, 23832, 61456):
+    /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846  tahoma.ttf from Windows 10 */
+    case ENCODE (994, 24474, 60336):
+    /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343  tahomabd.ttf from Windows 10 */
+    case ENCODE (1006, 24470, 61740):
+    /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+    case ENCODE (1006, 24576, 61346):
+    /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+    case ENCODE (1018, 24572, 62828):
+    /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5  tahoma.ttf from Windows 10 AU */
+    case ENCODE (1006, 24576, 61352):
+    /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2  tahomabd.ttf from Windows 10 AU */
+    case ENCODE (1018, 24572, 62834):
+    /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7  Tahoma.ttf from Mac OS X 10.9 */
+    case ENCODE (832, 7324, 47162):
+    /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba  Tahoma Bold.ttf from Mac OS X 10.9 */
+    case ENCODE (844, 7302, 45474):
+    /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc  himalaya.ttf from Windows 7 */
+    case ENCODE (180, 13054, 7254):
+    /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0  himalaya.ttf from Windows 8 */
+    case ENCODE (192, 12638, 7254):
+    /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427  himalaya.ttf from Windows 8.1 */
+    case ENCODE (192, 12690, 7254):
+    /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44  cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */
+    /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371  cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */
+    case ENCODE (188, 248, 3852):
+    /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f  cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */
+    /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b  cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */
+    case ENCODE (188, 264, 3426):
+    /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */
+    case ENCODE (1058, 47032, 11818):
+    /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/
+    case ENCODE (1046, 47030, 12600):
+    /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */
+    case ENCODE (1058, 71796, 16770):
+    /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */
+    case ENCODE (1046, 71790, 17862):
+    /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */
+    case ENCODE (1046, 71788, 17112):
+    /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */
+    case ENCODE (1058, 71794, 17514):
+    /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */
+    case ENCODE (1330, 109904, 57938):
+    /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */
+    case ENCODE (1330, 109904, 58972):
+    /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85  Padauk.ttf
+     *  "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */
+    case ENCODE (1004, 59092, 14836):
+      return true;
+#undef ENCODE
+  }
+  return false;
+}
+
+void hb_ot_layout_t::tables_t::init0 (hb_face_t *face)
+{
+  this->face = face;
+#define HB_OT_LAYOUT_TABLE(Namespace, Type) Type.init0 ();
+  HB_OT_LAYOUT_TABLES
+#undef HB_OT_LAYOUT_TABLE
+}
+void hb_ot_layout_t::tables_t::fini (void)
+{
+#define HB_OT_LAYOUT_TABLE(Namespace, Type) Type.fini ();
+  HB_OT_LAYOUT_TABLES
+#undef HB_OT_LAYOUT_TABLE
+}
 
 hb_ot_layout_t *
 _hb_ot_layout_create (hb_face_t *face)
 {
   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
   if (unlikely (!layout))
     return nullptr;
 
-  layout->gdef_blob = OT::Sanitizer<OT::GDEF>().sanitize (face->reference_table (HB_OT_TAG_GDEF));
-  layout->gdef = layout->gdef_blob->as<OT::GDEF> ();
-
-  layout->gsub_blob = OT::Sanitizer<OT::GSUB>().sanitize (face->reference_table (HB_OT_TAG_GSUB));
-  layout->gsub = layout->gsub_blob->as<OT::GSUB> ();
+  layout->table.init0 (face);
 
-  layout->gpos_blob = OT::Sanitizer<OT::GPOS>().sanitize (face->reference_table (HB_OT_TAG_GPOS));
-  layout->gpos = layout->gpos_blob->as<OT::GPOS> ();
-
-  layout->math.init (face);
-  layout->fvar.init (face);
-  layout->avar.init (face);
+  const OT::GSUB &gsub = *layout->table.GSUB;
+  const OT::GPOS &gpos = *layout->table.GPOS;
 
-  {
-    /*
-     * The ugly business of blacklisting individual fonts' tables happen here!
-     * See this thread for why we finally had to bend in and do this:
-     * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
-     */
-    unsigned int gdef_len = layout->gdef_blob->length;
-    unsigned int gsub_len = layout->gsub_blob->length;
-    unsigned int gpos_len = layout->gpos_blob->length;
-    if (0
-      /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */
-      || (442 == gdef_len && 42038 == gpos_len && 2874 == gsub_len)
-      /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */
-      || (430 == gdef_len && 40662 == gpos_len && 2874 == gsub_len)
-      /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */
-      || (442 == gdef_len && 39116 == gpos_len && 2874 == gsub_len)
-      /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */
-      || (430 == gdef_len && 39374 == gpos_len && 2874 == gsub_len)
-      /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */
-      || (490 == gdef_len && 41638 == gpos_len && 3046 == gsub_len)
-      /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */
-      || (478 == gdef_len && 41902 == gpos_len && 3046 == gsub_len)
-    )
-    {
-      /* In certain versions of Times New Roman Italic and Bold Italic,
-       * ASCII double quotation mark U+0022, mapped to glyph 5, has wrong
-       * glyph class 3 (mark) in GDEF.  Nuke the GDEF to avoid zero-width
-       * double-quote.  See:
-       * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
-       */
-     if (3 == layout->gdef->get_glyph_class (5))
-       layout->gdef = &Null(OT::GDEF);
-    }
-    else if (0
-      /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c  tahoma.ttf from Windows 8 */
-      || (898 == gdef_len && 46470 == gpos_len && 12554 == gsub_len)
-      /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc  tahomabd.ttf from Windows 8 */
-      || (910 == gdef_len && 47732 == gpos_len && 12566 == gsub_len)
-      /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e  tahoma.ttf from Windows 8.1 */
-      || (928 == gdef_len && 59332 == gpos_len && 23298 == gsub_len)
-      /* sha1sum:6d400781948517c3c0441ba42acb309584b73033  tahomabd.ttf from Windows 8.1 */
-      || (940 == gdef_len && 60732 == gpos_len && 23310 == gsub_len)
-      /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
-      || (964 == gdef_len && 60072 == gpos_len && 23836 == gsub_len)
-      /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
-      || (976 == gdef_len && 61456 == gpos_len && 23832 == gsub_len)
-      /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846  tahoma.ttf from Windows 10 */
-      || (994 == gdef_len && 60336 == gpos_len && 24474 == gsub_len)
-      /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343  tahomabd.ttf from Windows 10 */
-      || (1006 == gdef_len && 61740 == gpos_len && 24470 == gsub_len)
-      /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
-      || (1006 == gdef_len && 61346 == gpos_len && 24576 == gsub_len)
-      /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
-      || (1018 == gdef_len && 62828 == gpos_len && 24572 == gsub_len)
-      /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5  tahoma.ttf from Windows 10 AU */
-      || (1006 == gdef_len && 61352 == gpos_len && 24576 == gsub_len)
-      /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2  tahomabd.ttf from Windows 10 AU */
-      || (1018 == gdef_len && 62834 == gpos_len && 24572 == gsub_len)
-      /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7  Tahoma.ttf from Mac OS X 10.9 */
-      || (832 == gdef_len && 47162 == gpos_len && 7324 == gsub_len)
-      /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba  Tahoma Bold.ttf from Mac OS X 10.9 */
-      || (844 == gdef_len && 45474 == gpos_len && 7302 == gsub_len)
-      /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc  himalaya.ttf from Windows 7 */
-      || (180 == gdef_len && 7254 == gpos_len && 13054 == gsub_len)
-      /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0  himalaya.ttf from Windows 8 */
-      || (192 == gdef_len && 7254 == gpos_len && 12638 == gsub_len)
-      /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427  himalaya.ttf from Windows 8.1 */
-      || (192 == gdef_len && 7254 == gpos_len && 12690 == gsub_len)
-      /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44  cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */
-      /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371  cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */
-      || (188 == gdef_len && 3852 == gpos_len && 248 == gsub_len)
-      /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f  cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */
-      /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b  cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */
-      || (188 == gdef_len && 3426 == gpos_len && 264 == gsub_len)
-      /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */
-      || (1058 == gdef_len && 11818 == gpos_len && 47032 == gsub_len)
-      /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/
-      || (1046 == gdef_len && 12600 == gpos_len && 47030 == gsub_len)
-      /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */
-      || (1058 == gdef_len && 16770 == gpos_len && 71796 == gsub_len)
-      /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */
-      || (1046 == gdef_len && 17862 == gpos_len && 71790 == gsub_len)
-      /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */
-      || (1046 == gdef_len && 17112 == gpos_len && 71788 == gsub_len)
-      /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */
-      || (1058 == gdef_len && 17514 == gpos_len && 71794 == gsub_len)
-      /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */
-      || (1330 == gdef_len && 57938 == gpos_len && 109904 == gsub_len)
-      /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */
-      || (1330 == gdef_len && 58972 == gpos_len && 109904 == gsub_len)
-      /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85  Padauk.ttf
-       *  "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */
-      || (1004 == gdef_len && 14836 == gpos_len && 59092 == gsub_len)
-    )
-    {
-      /* Many versions of Tahoma have bad GDEF tables that incorrectly classify some spacing marks
-       * such as certain IPA symbols as glyph class 3. So do older versions of Microsoft Himalaya,
-       * and the version of Cantarell shipped by Ubuntu 16.04.
-       * Nuke the GDEF tables of these fonts to avoid unwanted width-zeroing.
-       * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925
-       *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279693
-       *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279875
-       */
-      layout->gdef = &Null(OT::GDEF);
-    }
-  }
+  if (unlikely (_hb_ot_blacklist_gdef (layout->table.GDEF.get_blob ()->length,
+				       layout->table.GSUB.get_blob ()->length,
+				       layout->table.GPOS.get_blob ()->length)))
+    layout->table.GDEF.set_stored (hb_blob_get_empty ());
 
-  layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
-  layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
+  unsigned int gsub_lookup_count = layout->gsub_lookup_count = gsub.get_lookup_count ();
+  unsigned int gpos_lookup_count = layout->gpos_lookup_count = gpos.get_lookup_count ();
 
-  layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
-  layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
+  layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (gsub_lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t));
+  layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (gpos_lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t));
 
-  if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) ||
-		(layout->gpos_lookup_count && !layout->gpos_accels)))
+  if (unlikely ((gsub_lookup_count && !layout->gsub_accels) ||
+		(gpos_lookup_count && !layout->gpos_accels)))
   {
     _hb_ot_layout_destroy (layout);
     return nullptr;
   }
 
-  for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
-    layout->gsub_accels[i].init (layout->gsub->get_lookup (i));
-  for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
-    layout->gpos_accels[i].init (layout->gpos->get_lookup (i));
+  for (unsigned int i = 0; i < gsub_lookup_count; i++)
+    layout->gsub_accels[i].init (gsub.get_lookup (i));
+  for (unsigned int i = 0; i < gpos_lookup_count; i++)
+    layout->gpos_accels[i].init (gpos.get_lookup (i));
 
   return layout;
 }
 
 void
 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
 {
   if (layout->gsub_accels)
@@ -203,52 +209,46 @@ void
       layout->gsub_accels[i].fini ();
   if (layout->gpos_accels)
     for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
       layout->gpos_accels[i].fini ();
 
   free (layout->gsub_accels);
   free (layout->gpos_accels);
 
-  hb_blob_destroy (layout->gdef_blob);
-  hb_blob_destroy (layout->gsub_blob);
-  hb_blob_destroy (layout->gpos_blob);
-
-  layout->math.fini ();
-  layout->fvar.fini ();
-  layout->avar.fini ();
+  layout->table.fini ();
 
   free (layout);
 }
 
 // static inline const OT::BASE&
 // _get_base (hb_face_t *face)
 // {
 //   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::BASE);
 //   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
 //   return *(layout->base.get ());
 // }
 
 static inline const OT::GDEF&
 _get_gdef (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GDEF);
-  return *hb_ot_layout_from_face (face)->gdef;
+  return *hb_ot_layout_from_face (face)->table.GDEF;
 }
 static inline const OT::GSUB&
 _get_gsub (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GSUB);
-  return *hb_ot_layout_from_face (face)->gsub;
+  return *hb_ot_layout_from_face (face)->table.GSUB;
 }
 static inline const OT::GPOS&
 _get_gpos (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GPOS);
-  return *hb_ot_layout_from_face (face)->gpos;
+  return *hb_ot_layout_from_face (face)->table.GPOS;
 }
 
 /*
  * GDEF
  */
 
 hb_bool_t
 hb_ot_layout_has_glyph_classes (hb_face_t *face)
@@ -513,16 +513,29 @@ hb_ot_layout_language_get_required_featu
 
   unsigned int index = l.get_required_feature_index ();
   if (feature_index) *feature_index = index;
   if (feature_tag) *feature_tag = g.get_feature_tag (index);
 
   return l.has_required_feature ();
 }
 
+static void
+_hb_ot_layout_language_add_feature_indexes_to (hb_face_t    *face,
+                                               hb_tag_t      table_tag,
+                                               unsigned int  script_index,
+                                               unsigned int  language_index,
+                                               hb_set_t     *feature_indexes /* OUT */)
+{
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
+  l.add_feature_indexes_to (feature_indexes);
+}
+
+
 unsigned int
 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
 					   hb_tag_t      table_tag,
 					   unsigned int  script_index,
 					   unsigned int  language_index,
 					   unsigned int  start_offset,
 					   unsigned int *feature_count /* IN/OUT */,
 					   unsigned int *feature_indexes /* OUT */)
@@ -651,125 +664,149 @@ static void
     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 */)
+_hb_ot_layout_collect_features_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       *feature_indexes /* OUT */)
 {
   if (!features)
   {
     unsigned int required_feature_index;
     if (hb_ot_layout_language_get_required_feature (face,
 						    table_tag,
 						    script_index,
 						    language_index,
 						    &required_feature_index,
 						    nullptr))
-      _hb_ot_layout_collect_lookups_lookups (face,
-					     table_tag,
-					     required_feature_index,
-					     lookup_indexes);
+      feature_indexes->add (required_feature_index);
 
     /* All features */
-    unsigned int feature_indices[32];
-    unsigned int offset, len;
-
-    offset = 0;
-    do {
-      len = ARRAY_LENGTH (feature_indices);
-      hb_ot_layout_language_get_feature_indexes (face,
-						 table_tag,
-						 script_index,
-						 language_index,
-						 offset, &len,
-						 feature_indices);
-
-      for (unsigned int i = 0; i < len; i++)
-	_hb_ot_layout_collect_lookups_lookups (face,
-					       table_tag,
-					       feature_indices[i],
-					       lookup_indexes);
-
-      offset += len;
-    } while (len == ARRAY_LENGTH (feature_indices));
+    _hb_ot_layout_language_add_feature_indexes_to (face,
+                                                   table_tag,
+                                                   script_index,
+                                                   language_index,
+                                                   feature_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);
+        feature_indexes->add (feature_index);
     }
   }
 }
 
 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 */)
+_hb_ot_layout_collect_features_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       *feature_indexes /* OUT */)
 {
-  _hb_ot_layout_collect_lookups_features (face,
-					  table_tag,
-					  script_index,
-					  HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
-					  features,
-					  lookup_indexes);
+  _hb_ot_layout_collect_features_features (face,
+                                           table_tag,
+                                           script_index,
+                                           HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
+                                           features,
+                                           feature_indexes);
 
   if (!languages)
   {
     /* All languages */
     unsigned int count = hb_ot_layout_script_get_language_tags (face,
 								table_tag,
 								script_index,
 								0, nullptr, nullptr);
     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);
+      _hb_ot_layout_collect_features_features (face,
+                                               table_tag,
+                                               script_index,
+                                               language_index,
+                                               features,
+                                               feature_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);
+        _hb_ot_layout_collect_features_features (face,
+                                                 table_tag,
+                                                 script_index,
+                                                 language_index,
+                                                 features,
+                                                 feature_indexes);
+    }
+  }
+}
+
+/**
+ * hb_ot_layout_collect_features:
+ *
+ * Since: 1.8.5
+ **/
+void
+hb_ot_layout_collect_features (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       *feature_indexes /* OUT */)
+{
+  if (!scripts)
+  {
+    /* All scripts */
+    unsigned int count = hb_ot_layout_table_get_script_tags (face,
+							     table_tag,
+							     0, nullptr, nullptr);
+    for (unsigned int script_index = 0; script_index < count; script_index++)
+      _hb_ot_layout_collect_features_languages (face,
+                                                table_tag,
+                                                script_index,
+                                                languages,
+                                                features,
+                                                feature_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_features_languages (face,
+                                                  table_tag,
+                                                  script_index,
+                                                  languages,
+                                                  features,
+                                                  feature_indexes);
     }
   }
 }
 
 /**
  * hb_ot_layout_collect_lookups:
  *
  * Since: 0.9.8
@@ -777,47 +814,20 @@ static void
 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, nullptr, nullptr);
-    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);
-    }
-  }
+  hb_auto_t<hb_set_t> feature_indexes;
+  hb_ot_layout_collect_features (face, table_tag, scripts, languages, features, &feature_indexes);
+  for (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID; hb_set_next (&feature_indexes, &feature_index);)
+    _hb_ot_layout_collect_lookups_lookups (face, table_tag, feature_index, lookup_indexes);
 }
 
 /**
  * hb_ot_layout_lookup_collect_glyphs:
  *
  * Since: 0.9.7
  **/
 void
@@ -836,23 +846,23 @@ hb_ot_layout_lookup_collect_glyphs (hb_f
 				     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);
+      const OT::SubstLookup& l = hb_ot_layout_from_face (face)->table.GSUB->get_lookup (lookup_index);
       l.collect_glyphs (&c);
       return;
     }
     case HB_OT_TAG_GPOS:
     {
-      const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
+      const OT::PosLookup& l = hb_ot_layout_from_face (face)->table.GPOS->get_lookup (lookup_index);
       l.collect_glyphs (&c);
       return;
     }
   }
 }
 
 
 /* Variations support */
@@ -889,17 +899,17 @@ hb_ot_layout_feature_with_variations_get
 
 /*
  * OT::GSUB
  */
 
 hb_bool_t
 hb_ot_layout_has_substitution (hb_face_t *face)
 {
-  return &_get_gsub (face) != &Null(OT::GSUB);
+  return _get_gsub (face).has_data ();
 }
 
 /**
  * hb_ot_layout_lookup_would_substitute:
  *
  * Since: 0.9.7
  **/
 hb_bool_t
@@ -918,17 +928,17 @@ hb_ot_layout_lookup_would_substitute_fas
 					   unsigned int          lookup_index,
 					   const hb_codepoint_t *glyphs,
 					   unsigned int          glyphs_length,
 					   hb_bool_t             zero_context)
 {
   if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
   OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
 
-  const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
+  const OT::SubstLookup& l = hb_ot_layout_from_face (face)->table.GSUB->get_lookup (lookup_index);
 
   return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index]);
 }
 
 void
 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
 {
   OT::GSUB::substitute_start (font, buffer);
@@ -964,41 +974,44 @@ void
 hb_ot_layout_lookups_substitute_closure (hb_face_t      *face,
                                          const hb_set_t *lookups,
                                          hb_set_t       *glyphs)
 {
   hb_auto_t<hb_map_t> done_lookups;
   OT::hb_closure_context_t c (face, glyphs, &done_lookups);
   const OT::GSUB& gsub = _get_gsub (face);
 
+  unsigned int iteration_count = 0;
   unsigned int glyphs_length;
   do
   {
     glyphs_length = glyphs->get_population ();
     if (lookups != nullptr)
     {
       for (hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID; hb_set_next (lookups, &lookup_index);)
         gsub.get_lookup (lookup_index).closure (&c, lookup_index);
     }
     else
     {
       for (unsigned int i = 0; i < gsub.get_lookup_count (); i++)
         gsub.get_lookup (i).closure (&c, i);
     }
-  } while (glyphs_length != glyphs->get_population ());
+    iteration_count++;
+  } while (iteration_count <= HB_CLOSURE_MAX_STAGES
+           && glyphs_length != glyphs->get_population ());
 }
 
 /*
  * OT::GPOS
  */
 
 hb_bool_t
 hb_ot_layout_has_positioning (hb_face_t *face)
 {
-  return &_get_gpos (face) != &Null(OT::GPOS);
+  return _get_gpos (face).has_data ();
 }
 
 void
 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
 {
   OT::GPOS::position_start (font, buffer);
 }
 
@@ -1073,40 +1086,40 @@ hb_ot_layout_get_size_params (hb_face_t 
 
 struct GSUBProxy
 {
   static const unsigned int table_index = 0;
   static const bool inplace = false;
   typedef OT::SubstLookup Lookup;
 
   GSUBProxy (hb_face_t *face) :
-    table (*hb_ot_layout_from_face (face)->gsub),
+    table (*hb_ot_layout_from_face (face)->table.GSUB),
     accels (hb_ot_layout_from_face (face)->gsub_accels) {}
 
   const OT::GSUB &table;
   const hb_ot_layout_lookup_accelerator_t *accels;
 };
 
 struct GPOSProxy
 {
   static const unsigned int table_index = 1;
   static const bool inplace = true;
   typedef OT::PosLookup Lookup;
 
   GPOSProxy (hb_face_t *face) :
-    table (*hb_ot_layout_from_face (face)->gpos),
+    table (*hb_ot_layout_from_face (face)->table.GPOS),
     accels (hb_ot_layout_from_face (face)->gpos_accels) {}
 
   const OT::GPOS &table;
   const hb_ot_layout_lookup_accelerator_t *accels;
 };
 
 
 struct hb_get_subtables_context_t :
-       OT::hb_dispatch_context_t<hb_get_subtables_context_t, hb_void_t, HB_DEBUG_APPLY>
+       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_ot_apply_context_t *c)
   {
     const Type *typed_obj = (const Type *) obj;
     return typed_obj->apply (c);
   }
 
@@ -1323,10 +1336,10 @@ hb_ot_layout_substitute_lookup (OT::hb_o
 //  *
 //  * Return value: true if face has a BASE table, false otherwise
 //  *
 //  * Since: XXX
 //  **/
 // hb_bool_t
 // hb_ot_base_has_data (hb_face_t *face)
 // {
-//   return &_get_base (face) != &Null(OT::BASE);
+//   return _get_base (face).has_data ();
 // }
--- a/gfx/harfbuzz/src/hb-ot-layout.h
+++ b/gfx/harfbuzz/src/hb-ot-layout.h
@@ -189,16 +189,23 @@ hb_ot_layout_feature_get_lookups (hb_fac
 				  unsigned int  start_offset,
 				  unsigned int *lookup_count /* IN/OUT */,
 				  unsigned int *lookup_indexes /* OUT */);
 
 HB_EXTERN unsigned int
 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
 				     hb_tag_t      table_tag);
 
+HB_EXTERN void
+hb_ot_layout_collect_features (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       *feature_indexes /* OUT */);
 
 HB_EXTERN 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 */);
--- a/gfx/harfbuzz/src/hb-ot-math-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-math-table.hh
@@ -681,16 +681,18 @@ struct MathVariants
  * MATH -- Mathematical typesetting
  * https://docs.microsoft.com/en-us/typography/opentype/spec/math
  */
 
 struct MATH
 {
   static const hb_tag_t tableTag	= HB_OT_TAG_MATH;
 
+  inline bool has_data (void) const { return version.to_int () != 0; }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
 		  likely (version.major == 1) &&
 		  mathConstants.sanitize (c, this) &&
 		  mathGlyphInfo.sanitize (c, this) &&
 		  mathVariants.sanitize (c, this));
--- a/gfx/harfbuzz/src/hb-ot-math.cc
+++ b/gfx/harfbuzz/src/hb-ot-math.cc
@@ -29,17 +29,17 @@
 #include "hb-ot-layout-private.hh"
 #include "hb-ot-math-table.hh"
 
 static inline const OT::MATH&
 _get_math (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::MATH);
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  return *(layout->math.get ());
+  return *(layout->table.MATH.get ());
 }
 
 /*
  * OT::MATH
  */
 
 /**
  * hb_ot_math_has_data:
@@ -50,17 +50,17 @@ static inline const OT::MATH&
  *
  * Return value: true if face has a MATH table, false otherwise
  *
  * Since: 1.3.3
  **/
 hb_bool_t
 hb_ot_math_has_data (hb_face_t *face)
 {
-  return &_get_math (face) != &Null(OT::MATH);
+  return _get_math (face).has_data ();
 }
 
 /**
  * hb_ot_math_get_constant:
  * @font: #hb_font_t from which to retrieve the value
  * @constant: #hb_ot_math_constant_t the constant to retrieve
  *
  * This function returns the requested math constants as a #hb_position_t.
--- a/gfx/harfbuzz/src/hb-ot-maxp-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-maxp-table.hh
@@ -95,35 +95,35 @@ struct maxp
       const maxpV1Tail &v1 = StructAfter<maxpV1Tail> (*this);
       return v1.sanitize (c);
     }
     return_trace (likely (version.major == 0 && version.minor == 0x5000u));
   }
 
   inline bool subset (hb_subset_plan_t *plan) const
   {
-    hb_blob_t *maxp_blob = OT::Sanitizer<OT::maxp>().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_maxp));
+    hb_blob_t *maxp_blob = hb_sanitize_context_t().reference_table<maxp> (plan->source);
     hb_blob_t *maxp_prime_blob = hb_blob_copy_writable_or_fail (maxp_blob);
     hb_blob_destroy (maxp_blob);
 
     if (unlikely (!maxp_prime_blob)) {
       return false;
     }
-    OT::maxp *maxp_prime = (OT::maxp *) hb_blob_get_data (maxp_prime_blob, nullptr);
+    maxp *maxp_prime = (maxp *) hb_blob_get_data (maxp_prime_blob, nullptr);
 
     maxp_prime->set_num_glyphs (plan->glyphs.len);
     if (plan->drop_hints)
       drop_hint_fields (plan, maxp_prime);
 
     bool result = plan->add_table (HB_OT_TAG_maxp, maxp_prime_blob);
     hb_blob_destroy (maxp_prime_blob);
     return result;
   }
 
-  static inline void drop_hint_fields (hb_subset_plan_t *plan, OT::maxp *maxp_prime)
+  static inline void drop_hint_fields (hb_subset_plan_t *plan, maxp *maxp_prime)
   {
     if (maxp_prime->version.major == 1)
     {
       maxpV1Tail &v1 = StructAfter<maxpV1Tail> (*maxp_prime);
       v1.maxZones.set (1);
       v1.maxTwilightPoints.set (0);
       v1.maxStorage.set (0);
       v1.maxFunctionDefs.set (0);
--- a/gfx/harfbuzz/src/hb-ot-os2-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-os2-table.hh
@@ -46,22 +46,22 @@ struct os2
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
   inline bool subset (hb_subset_plan_t *plan) const
   {
-    hb_blob_t *os2_blob = OT::Sanitizer<OT::os2>().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_os2));
+    hb_blob_t *os2_blob = hb_sanitize_context_t().reference_table<os2> (plan->source);
     hb_blob_t *os2_prime_blob = hb_blob_create_sub_blob (os2_blob, 0, -1);
     // TODO(grieger): move to hb_blob_copy_writable_or_fail
     hb_blob_destroy (os2_blob);
 
-    OT::os2 *os2_prime = (OT::os2 *) hb_blob_get_data_writable (os2_prime_blob, nullptr);
+    os2 *os2_prime = (os2 *) hb_blob_get_data_writable (os2_prime_blob, nullptr);
     if (unlikely (!os2_prime)) {
       hb_blob_destroy (os2_prime_blob);
       return false;
     }
 
     uint16_t min_cp, max_cp;
     find_min_and_max_codepoint (plan->unicodes, &min_cp, &max_cp);
     os2_prime->usFirstCharIndex.set (min_cp);
--- a/gfx/harfbuzz/src/hb-ot-post-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-post-table.hh
@@ -80,17 +80,17 @@ struct post
       return_trace (v2.sanitize (c));
     }
     return_trace (true);
   }
 
   inline bool subset (hb_subset_plan_t *plan) const
   {
     unsigned int post_prime_length;
-    hb_blob_t *post_blob = OT::Sanitizer<post>().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_post));
+    hb_blob_t *post_blob = hb_sanitize_context_t().reference_table<post>(plan->source);
     hb_blob_t *post_prime_blob = hb_blob_create_sub_blob (post_blob, 0, post::static_size);
     post *post_prime = (post *) hb_blob_get_data_writable (post_prime_blob, &post_prime_length);
     hb_blob_destroy (post_blob);
 
     if (unlikely (!post_prime || post_prime_length != post::static_size))
     {
       hb_blob_destroy (post_prime_blob);
       DEBUG_MSG(SUBSET, nullptr, "Invalid source post table with length %d.", post_prime_length);
@@ -105,17 +105,17 @@ struct post
   }
 
   struct accelerator_t
   {
     inline void init (hb_face_t *face)
     {
       index_to_offset.init ();
 
-      blob = Sanitizer<post>().sanitize (face->reference_table (HB_OT_TAG_post));
+      blob = hb_sanitize_context_t().reference_table<post> (face);
       const post *table = blob->as<post> ();
       unsigned int table_length = blob->length;
 
       version = table->version.to_int ();
       if (version != 0x00020000)
         return;
 
       const postV2Tail &v2 = StructAfter<postV2Tail> (*table);
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -74,22 +74,22 @@ arabic_fallback_synthesize_lookup_single
 
   if (!num_glyphs)
     return nullptr;
 
   /* Bubble-sort or something equally good!
    * May not be good-enough for presidential candidate interviews, but good-enough for us... */
   hb_stable_sort (&glyphs[0], num_glyphs, (int(*)(const OT::GlyphID*, const OT::GlyphID *)) OT::GlyphID::cmp, &substitutes[0]);
 
-  OT::Supplier<OT::GlyphID> glyphs_supplier      (glyphs, num_glyphs);
-  OT::Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs);
+  Supplier<OT::GlyphID> glyphs_supplier      (glyphs, num_glyphs);
+  Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs);
 
   /* Each glyph takes four bytes max, and there's some overhead. */
   char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128];
-  OT::hb_serialize_context_t c (buf, sizeof (buf));
+  hb_serialize_context_t c (buf, sizeof (buf));
   OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
   bool ret = lookup->serialize_single (&c,
 				       OT::LookupFlag::IgnoreMarks,
 				       glyphs_supplier,
 				       substitutes_supplier,
 				       num_glyphs);
   c.end_serialize ();
   /* TODO sanitize the results? */
@@ -150,25 +150,25 @@ arabic_fallback_synthesize_lookup_ligatu
       component_list[num_ligatures].set (second_glyph);
       num_ligatures++;
     }
   }
 
   if (!num_ligatures)
     return nullptr;
 
-  OT::Supplier<OT::GlyphID>   first_glyphs_supplier                      (first_glyphs, num_first_glyphs);
-  OT::Supplier<unsigned int > ligature_per_first_glyph_count_supplier    (ligature_per_first_glyph_count_list, num_first_glyphs);
-  OT::Supplier<OT::GlyphID>   ligatures_supplier                         (ligature_list, num_ligatures);
-  OT::Supplier<unsigned int > component_count_supplier                   (component_count_list, num_ligatures);
-  OT::Supplier<OT::GlyphID>   component_supplier                         (component_list, num_ligatures);
+  Supplier<OT::GlyphID>   first_glyphs_supplier                      (first_glyphs, num_first_glyphs);
+  Supplier<unsigned int > ligature_per_first_glyph_count_supplier    (ligature_per_first_glyph_count_list, num_first_glyphs);
+  Supplier<OT::GlyphID>   ligatures_supplier                         (ligature_list, num_ligatures);
+  Supplier<unsigned int > component_count_supplier                   (component_count_list, num_ligatures);
+  Supplier<OT::GlyphID>   component_supplier                         (component_list, num_ligatures);
 
   /* 16 bytes per ligature ought to be enough... */
   char buf[ARRAY_LENGTH_CONST (ligature_list) * 16 + 128];
-  OT::hb_serialize_context_t c (buf, sizeof (buf));
+  hb_serialize_context_t c (buf, sizeof (buf));
   OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
   bool ret = lookup->serialize_ligature (&c,
 					 OT::LookupFlag::IgnoreMarks,
 					 first_glyphs_supplier,
 					 ligature_per_first_glyph_count_supplier,
 					 num_first_glyphs,
 					 ligatures_supplier,
 					 component_count_supplier,
@@ -200,27 +200,26 @@ struct arabic_fallback_plan_t
   unsigned int num_lookups;
   bool free_lookups;
 
   hb_mask_t mask_array[ARABIC_FALLBACK_MAX_LOOKUPS];
   OT::SubstLookup *lookup_array[ARABIC_FALLBACK_MAX_LOOKUPS];
   hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS];
 };
 
-static const arabic_fallback_plan_t arabic_fallback_plan_nil = {};
-
 #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_WIN1256)
 #define HB_WITH_WIN1256
 #endif
 
 #ifdef HB_WITH_WIN1256
 #include "hb-ot-shape-complex-arabic-win1256.hh"
 #endif
 
-struct ManifestLookup {
+struct ManifestLookup
+{
   OT::Tag tag;
   OT::OffsetTo<OT::SubstLookup> lookupOffset;
 };
 typedef OT::ArrayOf<ManifestLookup> Manifest;
 
 static bool
 arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan,
 				   const hb_ot_shape_plan_t *plan,
@@ -294,39 +293,40 @@ arabic_fallback_plan_init_unicode (arabi
 }
 
 static arabic_fallback_plan_t *
 arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan,
 			     hb_font_t *font)
 {
   arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1, sizeof (arabic_fallback_plan_t));
   if (unlikely (!fallback_plan))
-    return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil);
+    return const_cast<arabic_fallback_plan_t *> (&Null(arabic_fallback_plan_t));
 
   fallback_plan->num_lookups = 0;
   fallback_plan->free_lookups = false;
 
   /* Try synthesizing GSUB table using Unicode Arabic Presentation Forms,
    * in case the font has cmap entries for the presentation-forms characters. */
   if (arabic_fallback_plan_init_unicode (fallback_plan, plan, font))
     return fallback_plan;
 
   /* See if this looks like a Windows-1256-encoded font.  If it does, use a
    * hand-coded GSUB table. */
   if (arabic_fallback_plan_init_win1256 (fallback_plan, plan, font))
     return fallback_plan;
 
+  assert (fallback_plan->num_lookups == 0);
   free (fallback_plan);
-  return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil);
+  return const_cast<arabic_fallback_plan_t *> (&Null(arabic_fallback_plan_t));
 }
 
 static void
 arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan)
 {
-  if (!fallback_plan || fallback_plan == &arabic_fallback_plan_nil)
+  if (!fallback_plan || fallback_plan->num_lookups == 0)
     return;
 
   for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
     if (fallback_plan->lookup_array[i])
     {
       fallback_plan->accel_array[i].fini ();
       if (fallback_plan->free_lookups)
 	free (fallback_plan->lookup_array[i]);
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -245,17 +245,17 @@ struct arabic_shape_plan_t
   ASSERT_POD ();
 
   /* The "+ 1" in the next array is to accommodate for the "NONE" command,
    * which is not an OpenType feature, but this simplifies the code by not
    * having to do a "if (... < NONE) ..." and just rely on the fact that
    * mask_array[NONE] == 0. */
   hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1];
 
-  arabic_fallback_plan_t *fallback_plan;
+  mutable arabic_fallback_plan_t *fallback_plan;
 
   unsigned int do_fallback : 1;
   unsigned int has_stch : 1;
 };
 
 void *
 data_create_arabic (const hb_ot_shape_plan_t *plan)
 {
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
@@ -295,17 +295,19 @@ static const hb_codepoint_t ra_chars[] =
   0x0A30u, /* Gurmukhi */	/* No Reph */
   0x0AB0u, /* Gujarati */
   0x0B30u, /* Oriya */
   0x0BB0u, /* Tamil */		/* No Reph */
   0x0C30u, /* Telugu */		/* Reph formed only with ZWJ */
   0x0CB0u, /* Kannada */
   0x0D30u, /* Malayalam */	/* No Reph, Logical Repha */
 
-  0x0DBBu, /* Sinhala */		/* Reph formed only with ZWJ */
+  0x0DBBu, /* Sinhala */	/* Reph formed only with ZWJ */
+
+  0x179Au, /* Khmer */
 };
 
 static inline bool
 is_ra (hb_codepoint_t u)
 {
   for (unsigned int i = 0; i < ARRAY_LENGTH (ra_chars); i++)
     if (u == ra_chars[i])
       return true;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -246,39 +246,39 @@ struct would_substitute_feature_t
   unsigned int count;
   bool zero_context;
 };
 
 struct indic_shape_plan_t
 {
   ASSERT_POD ();
 
-  inline bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
+  inline bool load_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
   {
-    hb_codepoint_t glyph = virama_glyph;
-    if (unlikely (virama_glyph == (hb_codepoint_t) -1))
+    hb_codepoint_t glyph = virama_glyph.get_relaxed ();
+    if (unlikely (glyph == (hb_codepoint_t) -1))
     {
       if (!config->virama || !font->get_nominal_glyph (config->virama, &glyph))
 	glyph = 0;
       /* Technically speaking, the spec says we should apply 'locl' to virama too.
        * Maybe one day... */
 
       /* Our get_nominal_glyph() function needs a font, so we can't get the virama glyph
-       * during shape planning...  Instead, overwrite it here.  It's safe.  Don't worry! */
-      virama_glyph = glyph;
+       * during shape planning...  Instead, overwrite it here. */
+      virama_glyph.set_relaxed ((int) glyph);
     }
 
     *pglyph = glyph;
     return glyph != 0;
   }
 
   const indic_config_t *config;
 
   bool is_old_spec;
-  mutable hb_codepoint_t virama_glyph;
+  mutable hb_atomic_int_t virama_glyph;
 
   would_substitute_feature_t rphf;
   would_substitute_feature_t pref;
   would_substitute_feature_t blwf;
   would_substitute_feature_t pstf;
 
   hb_mask_t mask_array[INDIC_NUM_FEATURES];
 };
@@ -293,17 +293,17 @@ data_create_indic (const hb_ot_shape_pla
   indic_plan->config = &indic_configs[0];
   for (unsigned int i = 1; i < ARRAY_LENGTH (indic_configs); i++)
     if (plan->props.script == indic_configs[i].script) {
       indic_plan->config = &indic_configs[i];
       break;
     }
 
   indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2');
-  indic_plan->virama_glyph = (hb_codepoint_t) -1;
+  indic_plan->virama_glyph.set_relaxed (-1);
 
   /* Use zero-context would_substitute() matching for new-spec of the main
    * Indic scripts, and scripts with one spec only, but not for old-specs.
    * The new-spec for all dual-spec scripts says zero-context matching happens.
    *
    * However, testing with Malayalam shows that old and new spec both allow
    * context.  Testing with Bengali new-spec however shows that it doesn't.
    * So, the heuristic here is the way it is.  It should *only* be changed,
@@ -414,17 +414,17 @@ update_consonant_positions (const hb_ot_
 			    hb_buffer_t       *buffer)
 {
   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
 
   if (indic_plan->config->base_pos != BASE_POS_LAST)
     return;
 
   hb_codepoint_t virama;
-  if (indic_plan->get_virama_glyph (font, &virama))
+  if (indic_plan->load_virama_glyph (font, &virama))
   {
     hb_face_t *face = font->face;
     unsigned int count = buffer->len;
     hb_glyph_info_t *info = buffer->info;
     for (unsigned int i = 0; i < count; i++)
       if (info[i].indic_position() == POS_BASE_C)
       {
 	hb_codepoint_t consonant = info[i].codepoint;
@@ -662,39 +662,43 @@ initial_reordering_consonant_syllable (c
   /* Handle beginning Ra */
   if (has_reph)
     info[start].indic_position() = POS_RA_TO_BECOME_REPH;
 
   /* For old-style Indic script tags, move the first post-base Halant after
    * last consonant.
    *
    * Reports suggest that in some scripts Uniscribe does this only if there
-   * is *not* a Halant after last consonant already (eg. Kannada), while it
-   * does it unconditionally in other scripts (eg. Malayalam, Bengali).  We
-   * don't currently know about other scripts, so we whitelist Malayalam and
-   * Bengali for now.
+   * is *not* a Halant after last consonant already.  We know that is the
+   * case for Kannada, while it reorders unconditionally in other scripts,
+   * eg. Malayalam, Bengali, and Devanagari.  We don't currently know about
+   * other scripts, so we blacklist Kannada.
    *
    * Kannada test case:
    * U+0C9A,U+0CCD,U+0C9A,U+0CCD
    * With some versions of Lohit Kannada.
    * https://bugs.freedesktop.org/show_bug.cgi?id=59118
    *
    * Malayalam test case:
    * U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D
    * With lohit-ttf-20121122/Lohit-Malayalam.ttf
    *
-   * Bengali test case
+   * Bengali test case:
    * U+0998,U+09CD,U+09AF,U+09CD
    * With Windows XP vrinda.ttf
    * https://github.com/harfbuzz/harfbuzz/issues/1073
+   *
+   * Devanagari test case:
+   * U+091F,U+094D,U+0930,U+094D
+   * With chandas.ttf
+   * https://github.com/harfbuzz/harfbuzz/issues/1071
    */
   if (indic_plan->is_old_spec)
   {
-    bool disallow_double_halants = buffer->props.script != HB_SCRIPT_MALAYALAM &&
-				   buffer->props.script != HB_SCRIPT_BENGALI;
+    bool disallow_double_halants = buffer->props.script == HB_SCRIPT_KANNADA;
     for (unsigned int i = base + 1; i < end; i++)
       if (info[i].indic_category() == OT_H)
       {
         unsigned int j;
         for (j = end - 1; j > i; j--)
 	  if (is_consonant (info[j]) ||
 	      (disallow_double_halants && info[j].indic_category() == OT_H))
 	    break;
@@ -1031,19 +1035,21 @@ final_reordering_syllable (const hb_ot_s
   hb_glyph_info_t *info = buffer->info;
 
 
   /* This function relies heavily on halant glyphs.  Lots of ligation
    * and possibly multiple substitutions happened prior to this
    * phase, and that might have messed up our properties.  Recover
    * from a particular case of that where we're fairly sure that a
    * class of OT_H is desired but has been lost. */
-  if (indic_plan->virama_glyph)
+  /* We don't call load_virama_glyph(), since we know it's already
+   * loaded. */
+  hb_codepoint_t virama_glyph = indic_plan->virama_glyph.get_relaxed ();
+  if (virama_glyph)
   {
-    unsigned int virama_glyph = indic_plan->virama_glyph;
     for (unsigned int i = start; i < end; i++)
       if (info[i].codepoint == virama_glyph &&
 	  _hb_glyph_info_ligated (&info[i]) &&
 	  _hb_glyph_info_multiplied (&info[i]))
       {
         /* This will make sure that this glyph passes is_halant() test. */
 	info[i].indic_category() = OT_H;
 	_hb_glyph_info_clear_ligated_and_multiplied (&info[i]);
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc
@@ -37,28 +37,26 @@ struct feature_list_t {
   hb_ot_map_feature_flags_t flags;
 };
 
 static const feature_list_t
 khmer_features[] =
 {
   /*
    * Basic features.
-   * These features are applied in order, one at a time, after initial_reordering.
+   * These features are applied in order, one at a time, after reordering.
    */
   {HB_TAG('p','r','e','f'), F_NONE},
   {HB_TAG('b','l','w','f'), F_NONE},
   {HB_TAG('a','b','v','f'), F_NONE},
   {HB_TAG('p','s','t','f'), F_NONE},
   {HB_TAG('c','f','a','r'), F_NONE},
   /*
    * Other features.
-   * These features are applied all at once, after final_reordering.
-   * Default Bengali font in Windows for example has intermixed
-   * lookups for init,pres,abvs,blws features.
+   * These features are applied all at once.
    */
   {HB_TAG('p','r','e','s'), F_GLOBAL},
   {HB_TAG('a','b','v','s'), F_GLOBAL},
   {HB_TAG('b','l','w','s'), F_GLOBAL},
   {HB_TAG('p','s','t','s'), F_GLOBAL},
   /* Positioning features, though we don't care about the types. */
   {HB_TAG('d','i','s','t'), F_GLOBAL},
   {HB_TAG('a','b','v','m'), F_GLOBAL},
@@ -87,57 +85,60 @@ enum {
   KHMER_BASIC_FEATURES = _PRES /* Don't forget to update this! */
 };
 
 static void
 setup_syllables (const hb_ot_shape_plan_t *plan,
 		 hb_font_t *font,
 		 hb_buffer_t *buffer);
 static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer);
-static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font,
-		  hb_buffer_t *buffer);
+reorder (const hb_ot_shape_plan_t *plan,
+	 hb_font_t *font,
+	 hb_buffer_t *buffer);
 static void
 clear_syllables (const hb_ot_shape_plan_t *plan,
 		 hb_font_t *font,
 		 hb_buffer_t *buffer);
 
 static void
 collect_features_khmer (hb_ot_shape_planner_t *plan)
 {
   hb_ot_map_builder_t *map = &plan->map;
 
   /* Do this before any lookups have been applied. */
   map->add_gsub_pause (setup_syllables);
+  map->add_gsub_pause (reorder);
 
+  /* Testing suggests that Uniscribe does NOT pause between basic
+   * features.  Test with KhmerUI.ttf and the following three
+   * sequences:
+   *
+   *   U+1789,U+17BC
+   *   U+1789,U+17D2,U+1789
+   *   U+1789,U+17D2,U+1789,U+17BC
+   *
+   * https://github.com/harfbuzz/harfbuzz/issues/974
+   */
   map->add_global_bool_feature (HB_TAG('l','o','c','l'));
-  /* The Indic specs do not require ccmp, but we apply it here since if
-   * there is a use of it, it's typically at the beginning. */
   map->add_global_bool_feature (HB_TAG('c','c','m','p'));
 
-
   unsigned int i = 0;
-  map->add_gsub_pause (initial_reordering);
   for (; i < KHMER_BASIC_FEATURES; i++) {
     map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
-    map->add_gsub_pause (nullptr);
   }
-  map->add_gsub_pause (final_reordering);
+
+  map->add_gsub_pause (clear_syllables);
+
   for (; i < KHMER_NUM_FEATURES; i++) {
     map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
   }
 
   map->add_global_bool_feature (HB_TAG('c','a','l','t'));
   map->add_global_bool_feature (HB_TAG('c','l','i','g'));
 
-  map->add_gsub_pause (clear_syllables);
 }
 
 static void
 override_features_khmer (hb_ot_shape_planner_t *plan)
 {
   /* Uniscribe does not apply 'kern' in Khmer. */
   if (hb_options ().uniscribe_bug_compatible)
   {
@@ -259,201 +260,107 @@ setup_syllables (const hb_ot_shape_plan_
 		 hb_font_t *font HB_UNUSED,
 		 hb_buffer_t *buffer)
 {
   find_syllables (buffer);
   foreach_syllable (buffer, start, end)
     buffer->unsafe_to_break (start, end);
 }
 
-static int
-compare_khmer_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
-{
-  int a = pa->khmer_position();
-  int b = pb->khmer_position();
-
-  return a < b ? -1 : a == b ? 0 : +1;
-}
-
 
 /* Rules from:
  * https://docs.microsoft.com/en-us/typography/script-development/devanagari */
 
 static void
-initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
-				       hb_face_t *face,
-				       hb_buffer_t *buffer,
-				       unsigned int start, unsigned int end)
+reorder_consonant_syllable (const hb_ot_shape_plan_t *plan,
+			    hb_face_t *face,
+			    hb_buffer_t *buffer,
+			    unsigned int start, unsigned int end)
 {
   const khmer_shape_plan_t *khmer_plan = (const khmer_shape_plan_t *) plan->data;
   hb_glyph_info_t *info = buffer->info;
 
-  /* 1. Khmer shaping assumes that a syllable will begin with a Cons, IndV, or Number. */
-
-  /* The first consonant is always the base. */
-  unsigned int base = start;
-  info[base].khmer_position() = POS_BASE_C;
-
-  /* Mark all subsequent consonants as below. */
-  for (unsigned int i = base + 1; i < end; i++)
-    if (is_consonant_or_vowel (info[i]))
-      info[i].khmer_position() = POS_BELOW_C;
-
-  /* Mark final consonants.  A final consonant is one appearing after a matra,
-   * like in Khmer. */
-  for (unsigned int i = base + 1; i < end; i++)
-    if (info[i].khmer_category() == OT_M) {
-      for (unsigned int j = i + 1; j < end; j++)
-        if (is_consonant_or_vowel (info[j])) {
-	  info[j].khmer_position() = POS_FINAL_C;
-	  break;
-	}
-      break;
-    }
-
-  /* Attach misc marks to previous char to move with them. */
-  {
-    khmer_position_t last_pos = POS_START;
-    for (unsigned int i = start; i < end; i++)
-    {
-      if ((FLAG_UNSAFE (info[i].khmer_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | FLAG (OT_Coeng))))
-      {
-	info[i].khmer_position() = last_pos;
-	if (unlikely (info[i].khmer_category() == OT_Coeng &&
-		      info[i].khmer_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].khmer_position() != POS_PRE_M) {
-	      info[i].khmer_position() = info[j - 1].khmer_position();
-	      break;
-	    }
-	}
-      } else if (info[i].khmer_position() != POS_SMVD) {
-        last_pos = (khmer_position_t) info[i].khmer_position();
-      }
-    }
-  }
-  /* For post-base consonants let them own anything before them
-   * since the last consonant or matra. */
+  /* Setup masks. */
   {
-    unsigned int last = base;
-    for (unsigned int i = base + 1; i < end; i++)
-      if (is_consonant_or_vowel (info[i]))
-      {
-	for (unsigned int j = last + 1; j < i; j++)
-	  if (info[j].khmer_position() < POS_SMVD)
-	    info[j].khmer_position() = info[i].khmer_position();
-	last = i;
-      } else if (info[i].khmer_category() == OT_M)
-        last = i;
-  }
-
-  {
-    /* Use syllable() for sort accounting temporarily. */
-    unsigned int syllable = info[start].syllable();
-    for (unsigned int i = start; i < end; i++)
-      info[i].syllable() = i - start;
-
-    /* Sit tight, rock 'n roll! */
-    hb_stable_sort (info + start, end - start, compare_khmer_order);
-    /* Find base again */
-    base = end;
-    for (unsigned int i = start; i < end; i++)
-      if (info[i].khmer_position() == POS_BASE_C)
-      {
-	base = i;
-	break;
-      }
-
-    if (unlikely (end - start >= 127))
-      buffer->merge_clusters (start, end);
-    else
-      /* Note!  syllable() is a one-byte field. */
-      for (unsigned int i = base; i < end; i++)
-	if (info[i].syllable() != 255)
-	{
-	  unsigned int max = i;
-	  unsigned int j = start + info[i].syllable();
-	  while (j != i)
-	  {
-	    max = MAX (max, j);
-	    unsigned int next = start + info[j].syllable();
-	    info[j].syllable() = 255; /* So we don't process j later again. */
-	    j = next;
-	  }
-	  if (i != max)
-	    buffer->merge_clusters (i, max + 1);
-	}
-
-    /* Put syllable back in. */
-    for (unsigned int i = start; i < end; i++)
-      info[i].syllable() = syllable;
-  }
-
-  /* Setup masks now */
-
-  {
-    hb_mask_t mask;
-
     /* Post-base */
-    mask = khmer_plan->mask_array[BLWF] | khmer_plan->mask_array[ABVF] | khmer_plan->mask_array[PSTF];
-    for (unsigned int i = base + 1; i < end; i++)
+    hb_mask_t mask = khmer_plan->mask_array[BLWF] | khmer_plan->mask_array[ABVF] | khmer_plan->mask_array[PSTF];
+    for (unsigned int i = start + 1; i < end; i++)
       info[i].mask  |= mask;
   }
 
-  unsigned int pref_len = 2;
-  if (khmer_plan->mask_array[PREF] && base + pref_len < end)
+  unsigned int num_coengs = 0;
+  for (unsigned int i = start + 1; i < end; i++)
   {
-    /* Find a Halant,Ra sequence and mark it for pre-base-reordering processing. */
-    for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) {
-      hb_codepoint_t glyphs[2];
-      for (unsigned int j = 0; j < pref_len; j++)
-        glyphs[j] = info[i + j].codepoint;
-      if (khmer_plan->pref.would_substitute (glyphs, pref_len, face))
+    /* """
+     * When a COENG + (Cons | IndV) combination are found (and subscript count
+     * is less than two) the character combination is handled according to the
+     * subscript type of the character following the COENG.
+     *
+     * ...
+     *
+     * Subscript Type 2 - The COENG + RO characters are reordered to immediately
+     * before the base glyph. Then the COENG + RO characters are assigned to have
+     * the 'pref' OpenType feature applied to them.
+     * """
+     */
+    if (info[i].khmer_category() == OT_Coeng && num_coengs <= 2 && i + 1 < end)
+    {
+      num_coengs++;
+
+      if (info[i + 1].khmer_category() == OT_Ra)
       {
-	for (unsigned int j = 0; j < pref_len; j++)
-	  info[i++].mask |= khmer_plan->mask_array[PREF];
+	for (unsigned int j = 0; j < 2; j++)
+	  info[i + j].mask |= khmer_plan->mask_array[PREF];
+
+	/* Move the Coeng,Ro sequence to the start. */
+	buffer->merge_clusters (start, i + 2);
+	hb_glyph_info_t t0 = info[i];
+	hb_glyph_info_t t1 = info[i + 1];
+	memmove (&info[start + 2], &info[start], (i - start) * sizeof (info[0]));
+	info[start] = t0;
+	info[start + 1] = t1;
 
 	/* Mark the subsequent stuff with 'cfar'.  Used in Khmer.
 	 * Read the feature spec.
 	 * This allows distinguishing the following cases with MS Khmer fonts:
 	 * U+1784,U+17D2,U+179A,U+17D2,U+1782
 	 * U+1784,U+17D2,U+1782,U+17D2,U+179A
 	 */
 	if (khmer_plan->mask_array[CFAR])
-	  for (; i < end; i++)
-	    info[i].mask |= khmer_plan->mask_array[CFAR];
+	  for (unsigned int j = i + 2; j < end; j++)
+	    info[j].mask |= khmer_plan->mask_array[CFAR];
+
+	num_coengs = 2; /* Done. */
+      }
+    }
 
-	break;
-      }
+    /* Reorder left matra piece. */
+    else if (info[i].khmer_position() == POS_PRE_M)
+    {
+      /* Move to the start. */
+      buffer->merge_clusters (start, i + 1);
+      hb_glyph_info_t t = info[i];
+      memmove (&info[start + 1], &info[start], (i - start) * sizeof (info[0]));
+      info[start] = t;
     }
   }
 }
 
 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 broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
     case consonant_syllable:
-     initial_reordering_consonant_syllable (plan, face, buffer, start, end);
+     reorder_consonant_syllable (plan, face, buffer, start, end);
      break;
 
     case non_khmer_cluster:
       break;
   }
 }
 
 static inline void
@@ -513,273 +420,36 @@ insert_dotted_circles (const hb_ot_shape
     else
       buffer->next_glyph ();
   }
 
   buffer->swap_buffers ();
 }
 
 static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer)
+reorder (const hb_ot_shape_plan_t *plan,
+	 hb_font_t *font,
+	 hb_buffer_t *buffer)
 {
   insert_dotted_circles (plan, font, buffer);
 
   foreach_syllable (buffer, start, end)
     initial_reordering_syllable (plan, font->face, buffer, start, end);
-}
-
-static void
-final_reordering_syllable (const hb_ot_shape_plan_t *plan,
-			   hb_buffer_t *buffer,
-			   unsigned int start, unsigned int end)
-{
-  const khmer_shape_plan_t *khmer_plan = (const khmer_shape_plan_t *) plan->data;
-  hb_glyph_info_t *info = buffer->info;
-
-
-  /* This function relies heavily on halant glyphs.  Lots of ligation
-   * and possibly multiple substitutions happened prior to this
-   * phase, and that might have messed up our properties.  Recover
-   * from a particular case of that where we're fairly sure that a
-   * class of OT_Coeng is desired but has been lost. */
-  if (khmer_plan->virama_glyph)
-  {
-    unsigned int virama_glyph = khmer_plan->virama_glyph;
-    for (unsigned int i = start; i < end; i++)
-      if (info[i].codepoint == virama_glyph &&
-	  _hb_glyph_info_ligated (&info[i]) &&
-	  _hb_glyph_info_multiplied (&info[i]))
-      {
-        /* This will make sure that this glyph passes is_coeng() test. */
-	info[i].khmer_category() = OT_Coeng;
-	_hb_glyph_info_clear_ligated_and_multiplied (&info[i]);
-      }
-  }
-
-
-  /* 4. Final reordering:
-   *
-   * After the localized forms and basic shaping forms GSUB features have been
-   * applied (see below), the shaping engine performs some final glyph
-   * reordering before applying all the remaining font features to the entire
-   * syllable.
-   */
-
-  bool try_pref = !!khmer_plan->mask_array[PREF];
-
-  /* Find base again */
-  unsigned int base;
-  for (base = start; base < end; base++)
-    if (info[base].khmer_position() >= POS_BASE_C)
-    {
-      if (try_pref && base + 1 < end)
-      {
-	for (unsigned int i = base + 1; i < end; i++)
-	  if ((info[i].mask & khmer_plan->mask_array[PREF]) != 0)
-	  {
-	    if (!(_hb_glyph_info_substituted (&info[i]) &&
-		  _hb_glyph_info_ligated_and_didnt_multiply (&info[i])))
-	    {
-	      /* Ok, this was a 'pref' candidate but didn't form any.
-	       * Base is around here... */
-	      base = i;
-	      while (base < end && is_coeng (info[base]))
-		base++;
-	      info[base].khmer_position() = POS_BASE_C;
-
-	      try_pref = false;
-	    }
-	    break;
-	  }
-      }
-
-      if (start < base && info[base].khmer_position() > POS_BASE_C)
-        base--;
-      break;
-    }
-  if (base == end && start < base &&
-      is_one_of (info[base - 1], FLAG (OT_ZWJ)))
-    base--;
-  if (base < end)
-    while (start < base &&
-	   is_one_of (info[base], (FLAG (OT_N) | FLAG (OT_Coeng))))
-      base--;
-
-
-  /*   o Reorder matras:
-   *
-   *     If a pre-base matra character had been reordered before applying basic
-   *     features, the glyph can be moved closer to the main consonant based on
-   *     whether half-forms had been formed. Actual position for the matra is
-   *     defined as “after last standalone halant glyph, after initial matra
-   *     position and before the main consonant”. If ZWJ or ZWNJ follow this
-   *     halant, position is moved after it.
-   */
-
-  if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */
-  {
-    /* If we lost track of base, alas, position before last thingy. */
-    unsigned int new_pos = base == end ? base - 2 : base - 1;
-
-    while (new_pos > start &&
-	   !(is_one_of (info[new_pos], (FLAG (OT_M) | FLAG (OT_Coeng)))))
-      new_pos--;
-
-    /* If we found no Halant we are done.
-     * Otherwise only proceed if the Halant does
-     * not belong to the Matra itself! */
-    if (is_coeng (info[new_pos]) &&
-	info[new_pos].khmer_position() != POS_PRE_M)
-    {
-      /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
-      if (new_pos + 1 < end && is_joiner (info[new_pos + 1]))
-	new_pos++;
-    }
-    else
-      new_pos = start; /* No move. */
-
-    if (start < new_pos && info[new_pos].khmer_position () != POS_PRE_M)
-    {
-      /* Now go see if there's actually any matras... */
-      for (unsigned int i = new_pos; i > start; i--)
-	if (info[i - 1].khmer_position () == POS_PRE_M)
-	{
-	  unsigned int old_pos = i - 1;
-	  if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */
-	    base--;
-
-	  hb_glyph_info_t tmp = info[old_pos];
-	  memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0]));
-	  info[new_pos] = tmp;
-
-	  /* Note: this merge_clusters() is intentionally *after* the reordering.
-	   * Indic matra reordering is special and tricky... */
-	  buffer->merge_clusters (new_pos, MIN (end, base + 1));
-
-	  new_pos--;
-	}
-    } else {
-      for (unsigned int i = start; i < base; i++)
-	if (info[i].khmer_position () == POS_PRE_M) {
-	  buffer->merge_clusters (i, MIN (end, base + 1));
-	  break;
-	}
-    }
-  }
-
-
-  /*   o Reorder pre-base-reordering consonants:
-   *
-   *     If a pre-base-reordering consonant is found, reorder it according to
-   *     the following rules:
-   */
-
-  if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base-reordering Ra. */
-  {
-    for (unsigned int i = base + 1; i < end; i++)
-      if ((info[i].mask & khmer_plan->mask_array[PREF]) != 0)
-      {
-	/*       1. Only reorder a glyph produced by substitution during application
-	 *          of the <pref> feature. (Note that a font may shape a Ra consonant with
-	 *          the feature generally but block it in certain contexts.)
-	 */
-        /* Note: We just check that something got substituted.  We don't check that
-	 * the <pref> feature actually did it...
-	 *
-	 * Reorder pref only if it ligated. */
-	if (_hb_glyph_info_ligated_and_didnt_multiply (&info[i]))
-	{
-	  /*
-	   *       2. Try to find a target position the same way as for pre-base matra.
-	   *          If it is found, reorder pre-base consonant glyph.
-	   *
-	   *       3. If position is not found, reorder immediately before main
-	   *          consonant.
-	   */
-
-	  unsigned int new_pos = base;
-	  while (new_pos > start &&
-		 !(is_one_of (info[new_pos - 1], FLAG(OT_M) | FLAG (OT_Coeng))))
-	    new_pos--;
-
-	  /* In Khmer coeng model, a H,Ra can go *after* matras.  If it goes after a
-	   * split matra, it should be reordered to *before* the left part of such matra. */
-	  if (new_pos > start && info[new_pos - 1].khmer_category() == OT_M)
-	  {
-	    unsigned int old_pos = i;
-	    for (unsigned int j = base + 1; j < old_pos; j++)
-	      if (info[j].khmer_category() == OT_M)
-	      {
-		new_pos--;
-		break;
-	      }
-	  }
-
-	  if (new_pos > start && is_coeng (info[new_pos - 1]))
-	  {
-	    /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
-	    if (new_pos < end && is_joiner (info[new_pos]))
-	      new_pos++;
-	  }
-
-	  {
-	    unsigned int old_pos = i;
-
-	    buffer->merge_clusters (new_pos, old_pos + 1);
-	    hb_glyph_info_t tmp = info[old_pos];
-	    memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * sizeof (info[0]));
-	    info[new_pos] = tmp;
-
-	    if (new_pos <= base && base < old_pos)
-	      base++;
-	  }
-	}
-
-        break;
-      }
-  }
-
-
-  /*
-   * Finish off the clusters and go home!
-   */
-  if (hb_options ().uniscribe_bug_compatible)
-  {
-    /* Uniscribe merges the entire syllable into a single cluster... Except for Tamil & Sinhala.
-     * This means, half forms are submerged into the main consonant's cluster.
-     * This is unnecessary, and makes cursor positioning harder, but that's what
-     * Uniscribe does. */
-    buffer->merge_clusters (start, end);
-  }
-}
-
-
-static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font HB_UNUSED,
-		  hb_buffer_t *buffer)
-{
-  unsigned int count = buffer->len;
-  if (unlikely (!count)) return;
-
-  foreach_syllable (buffer, start, end)
-    final_reordering_syllable (plan, buffer, start, end);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_position);
 }
 
-
 static void
 clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		 hb_font_t *font HB_UNUSED,
 		 hb_buffer_t *buffer)
 {
+  /* TODO: In USE, we clear syllables right after reorder.  Figure out
+   * what Uniscribe does. */
   hb_glyph_info_t *info = buffer->info;
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
     info[i].syllable() = 0;
 }
 
 
 static bool
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-private.hh
@@ -274,30 +274,17 @@ hb_ot_shape_complex_categorize (const hb
        * GSUB/GPOS needed, so there may be no scripts found! */
       if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') ||
 	  planner->map.chosen_script[0] == HB_TAG ('l','a','t','n'))
 	return &_hb_ot_complex_shaper_default;
       else
 	return &_hb_ot_complex_shaper_indic;
 
     case HB_SCRIPT_KHMER:
-      /* A number of Khmer fonts in the wild don't have a 'pref' feature,
-       * 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'),
-					      nullptr))
 	return &_hb_ot_complex_shaper_khmer;
-      else
-	return &_hb_ot_complex_shaper_default;
 
     case HB_SCRIPT_MYANMAR:
       if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','2'))
 	return &_hb_ot_complex_shaper_myanmar;