Bug 1443812 - Update HarfBuzz to 1.7.6. r=jfkthame
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 07 Mar 2018 13:29:23 -0500
changeset 407075 32da8b4abfd7adb3f60b2f138be37e4d43637283
parent 407074 35129c889938eeefd8ccc81cf78a223e045e7f00
child 407076 2df077fac15a16af2befa9a7dd74a9d068d38514
push id33589
push usercsabou@mozilla.com
push dateThu, 08 Mar 2018 03:59:55 +0000
treeherdermozilla-central@888f3dded126 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1443812
milestone60.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 1443812 - Update HarfBuzz to 1.7.6. r=jfkthame
gfx/harfbuzz/NEWS
gfx/harfbuzz/README-mozilla
gfx/harfbuzz/TODO
gfx/harfbuzz/autogen.sh
gfx/harfbuzz/configure.ac
gfx/harfbuzz/src/Makefile.am
gfx/harfbuzz/src/Makefile.sources
gfx/harfbuzz/src/check-defs.sh
gfx/harfbuzz/src/check-libstdc++.sh
gfx/harfbuzz/src/check-symbols.sh
gfx/harfbuzz/src/dev-run.sh
gfx/harfbuzz/src/dump-indic-data.cc
gfx/harfbuzz/src/dump-khmer-data.cc
gfx/harfbuzz/src/dump-myanmar-data.cc
gfx/harfbuzz/src/dump-use-data.cc
gfx/harfbuzz/src/gen-def.py
gfx/harfbuzz/src/gen-unicode-ranges.py
gfx/harfbuzz/src/gen-use-table.py
gfx/harfbuzz/src/harfbuzz-config.cmake.in
gfx/harfbuzz/src/harfbuzz-icu.pc
gfx/harfbuzz/src/harfbuzz-subset.pc
gfx/harfbuzz/src/harfbuzz-subset.pc.in
gfx/harfbuzz/src/harfbuzz.pc
gfx/harfbuzz/src/hb-aat-layout-ankr-table.hh
gfx/harfbuzz/src/hb-aat-layout-common-private.hh
gfx/harfbuzz/src/hb-aat-layout-kerx-table.hh
gfx/harfbuzz/src/hb-aat-layout-morx-table.hh
gfx/harfbuzz/src/hb-aat-layout-private.hh
gfx/harfbuzz/src/hb-aat-layout-trak-table.hh
gfx/harfbuzz/src/hb-aat-layout.cc
gfx/harfbuzz/src/hb-blob.cc
gfx/harfbuzz/src/hb-blob.h
gfx/harfbuzz/src/hb-buffer-deserialize-json.hh
gfx/harfbuzz/src/hb-buffer-deserialize-json.rl
gfx/harfbuzz/src/hb-buffer-deserialize-text.hh
gfx/harfbuzz/src/hb-buffer-deserialize-text.rl
gfx/harfbuzz/src/hb-buffer-private.hh
gfx/harfbuzz/src/hb-buffer.cc
gfx/harfbuzz/src/hb-buffer.h
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-face.h
gfx/harfbuzz/src/hb-ft.cc
gfx/harfbuzz/src/hb-gobject-enums.h.tmpl
gfx/harfbuzz/src/hb-gobject-structs.h
gfx/harfbuzz/src/hb-graphite2.cc
gfx/harfbuzz/src/hb-open-file-private.hh
gfx/harfbuzz/src/hb-open-type-private.hh
gfx/harfbuzz/src/hb-ot-cbdt-table.hh
gfx/harfbuzz/src/hb-ot-cmap-table.hh
gfx/harfbuzz/src/hb-ot-color-cbdt-table.hh
gfx/harfbuzz/src/hb-ot-color-colr-table.hh
gfx/harfbuzz/src/hb-ot-color-cpal-table.hh
gfx/harfbuzz/src/hb-ot-color.cc
gfx/harfbuzz/src/hb-ot-font.cc
gfx/harfbuzz/src/hb-ot-glyf-table.hh
gfx/harfbuzz/src/hb-ot-hdmx-table.hh
gfx/harfbuzz/src/hb-ot-head-table.hh
gfx/harfbuzz/src/hb-ot-hhea-table.hh
gfx/harfbuzz/src/hb-ot-hmtx-table.hh
gfx/harfbuzz/src/hb-ot-layout-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-map-private.hh
gfx/harfbuzz/src/hb-ot-map.cc
gfx/harfbuzz/src/hb-ot-maxp-table.hh
gfx/harfbuzz/src/hb-ot-os2-table.hh
gfx/harfbuzz/src/hb-ot-os2-unicode-ranges.hh
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-private.hh
gfx/harfbuzz/src/hb-ot-shape-complex-khmer.cc
gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh
gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.rl
gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-private.hh
gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh
gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.rl
gfx/harfbuzz/src/hb-ot-shape-complex-use-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-fallback.cc
gfx/harfbuzz/src/hb-ot-shape.cc
gfx/harfbuzz/src/hb-private.hh
gfx/harfbuzz/src/hb-set-private.hh
gfx/harfbuzz/src/hb-set.cc
gfx/harfbuzz/src/hb-set.h
gfx/harfbuzz/src/hb-subset-glyf.cc
gfx/harfbuzz/src/hb-subset-glyf.hh
gfx/harfbuzz/src/hb-subset-input.cc
gfx/harfbuzz/src/hb-subset-plan.cc
gfx/harfbuzz/src/hb-subset-plan.hh
gfx/harfbuzz/src/hb-subset-private.hh
gfx/harfbuzz/src/hb-subset.cc
gfx/harfbuzz/src/hb-subset.h
gfx/harfbuzz/src/hb-uniscribe.cc
gfx/harfbuzz/src/hb-version.h
gfx/harfbuzz/src/main.cc
gfx/harfbuzz/src/moz.build
gfx/harfbuzz/src/test-buffer-serialize.cc
gfx/harfbuzz/src/test-unicode-ranges.cc
--- a/gfx/harfbuzz/NEWS
+++ b/gfx/harfbuzz/NEWS
@@ -1,8 +1,23 @@
+Overview of changes leading to 1.7.6
+Wednesday, March 7, 2018
+====================================
+
+- Fix to hb_set_t binary operations. Ouch.
+- New experimental harfbuzz-subset library. All of hb-subset.h
+  is experimental right now and API WILL change.
+
+- New API:
+hb_blob_copy_writable_or_fail()
+HB_OT_TAG_BASE
+hb_set_previous()
+hb_set_previous_range()
+
+
 Overview of changes leading to 1.7.5
 Tuesday, January 30, 2018
 ====================================
 
 - Separate Khmer shaper from Indic.
 - First stab at AAT morx. Not hooked up.
 - Misc bug fixes.
 
--- a/gfx/harfbuzz/README-mozilla
+++ b/gfx/harfbuzz/README-mozilla
@@ -1,14 +1,14 @@
-gfx/harfbuzz status as of 2018-01-30:
+gfx/harfbuzz status as of 2018-03-07:
 
 This directory contains the HarfBuzz source from the 'master' branch of
 https://github.com/behdad/harfbuzz.
 
-Current version: 1.7.5
+Current version: 1.7.6
 
 UPDATING:
 
 Note that gfx/harfbuzz/src/hb-version.h is not present in the upstream Git
 repository. It is created at build time by the HarfBuzz build system;
 but as we don't use that build system in mozilla, it is necessary to refresh
 this file when updating HarfBuzz, and check it into the mozilla tree.
 
--- a/gfx/harfbuzz/TODO
+++ b/gfx/harfbuzz/TODO
@@ -1,29 +1,19 @@
 General fixes:
 =============
 
-- AAT 'morx' implementation.
-
-- Return "safe-to-break" bit from shaping.
-
 - Implement 'rand' feature.
 
-- mask propagation? (when ligation, "or" the masks).
-
 
 API issues:
 ===========
 
 - API to accept a list of languages?
 
-- Add init_func to font_funcs.  Adjust ft.
-
-- 'const' for getter APIs? (use mutable internally)
-
 - Remove hb_ot_shape_glyphs_closure()?
 
 
 API additions
 =============
 
 - Language to/from script.
 
@@ -34,36 +24,19 @@ API additions
 - Add sanitize API (and a cached version, that saves result on blob user-data)
 
 - BCP 47 language handling / API (language_matches?)
 
 - Add hb_font_create_unscaled()?
 
 - Add query / enumeration API for aalt-like features?
 
-- SFNT api? get_num_faces? get_table_tags? (there's something in stash)
+- SFNT api? get_num_faces?
 
 - Add segmentation API
 
 - Add hb-fribidi glue?
 
 
 hb-view / hb-shape enhancements:
 ===============================
 
 - Add --width, --height, --auto-size, --ink-box, --align, etc?
-
-
-Tests to write:
-==============
-
-- ot-layout enumeration API (needs font)
-
-- Finish test-shape.c, grep for TODO
-
-- Finish test-unicode.c, grep for TODO
-
-- GObject, FreeType, etc
-
-- hb_cache_t and relatives
-
-- hb_feature_to/from_string
-- hb_buffer_[sg]et_contents
--- a/gfx/harfbuzz/autogen.sh
+++ b/gfx/harfbuzz/autogen.sh
@@ -2,21 +2,21 @@
 # Run this to generate all the initial makefiles, etc.
 
 test -n "$srcdir" || srcdir=`dirname "$0"`
 test -n "$srcdir" || srcdir=.
 
 olddir=`pwd`
 cd $srcdir
 
-echo -n "checking for ragel... "
-which ragel || {
-	echo "You need to install ragel... See http://www.complang.org/ragel/"
-	exit 1
-}
+#echo -n "checking for ragel... "
+#which ragel || {
+#	echo "You need to install ragel... See http://www.complang.org/ragel/"
+#	exit 1
+#}
 
 echo -n "checking for pkg-config... "
 which pkg-config || {
 	echo "*** No pkg-config found, please install it ***"
 	exit 1
 }
 
 echo -n "checking for libtoolize... "
--- a/gfx/harfbuzz/configure.ac
+++ b/gfx/harfbuzz/configure.ac
@@ -1,30 +1,32 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.7.5],
+        [1.7.6],
         [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])
 
 AM_INIT_AUTOMAKE([1.13.0 gnits tar-ustar dist-bzip2 no-dist-gzip -Wall no-define color-tests -Wno-portability])
 AM_SILENT_RULES([yes])
+AX_CODE_COVERAGE
 
 # Initialize libtool
 m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
 LT_PREREQ([2.2])
 LT_INIT([disable-static])
 
 # Check for programs
 AC_USE_SYSTEM_EXTENSIONS
 AC_PROG_CC
+AC_PROG_CC_C99
 AM_PROG_CC_C_O
 AC_PROG_CXX
 dnl AX_CXX_COMPILE_STDCXX(11, noext, optional)
 AC_SYS_LARGEFILE
 PKG_PROG_PKG_CONFIG([0.20])
 AM_MISSING_PROG([RAGEL], [ragel])
 AM_MISSING_PROG([GIT], [git])
 
@@ -52,45 +54,46 @@ m4_if(m4_eval(hb_version_minor % 2), [1]
       [m4_define([hb_libtool_revision], hb_version_micro)])
 m4_define([hb_libtool_age],
 	  m4_eval(hb_version_int - hb_libtool_revision))
 m4_define([hb_libtool_current],
 	  m4_eval(hb_libtool_age))
 HB_LIBTOOL_VERSION_INFO=hb_libtool_current:hb_libtool_revision:hb_libtool_age
 AC_SUBST(HB_LIBTOOL_VERSION_INFO)
 
+AC_ARG_WITH([libstdc++],
+	[AS_HELP_STRING([--with-libstdc++=@<:@yes/no@:>@],
+			[Allow linking with libstdc++ @<:@default=no@:>@])],
+	[with_libstdcxx=$withval],
+	[with_libstdcxx=no])
+AM_CONDITIONAL(WITH_LIBSTDCXX, [test "x$with_libstdcxx" = "xyes"])
+
 # Documentation
 have_gtk_doc=false
 m4_ifdef([GTK_DOC_CHECK], [
 GTK_DOC_CHECK([1.15],[--flavour no-tmpl])
 	if test "x$enable_gtk_doc" = xyes; then
 		have_gtk_doc=true
 	fi
 ], [
 	AM_CONDITIONAL([ENABLE_GTK_DOC], false)
 ])
 
-# Functions and headers
-AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l)
+# Functions, and headers
+AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l setlinebuf)
 AC_CHECK_HEADERS(unistd.h sys/mman.h xlocale.h)
 
 # Compiler flags
 AC_CANONICAL_HOST
 AC_CHECK_ALIGNOF([struct{char;}])
 if test "x$GCC" = "xyes"; then
 
 	# Make symbols link locally
 	LDFLAGS="$LDFLAGS -Bsymbolic-functions"
 
-	# Make sure we don't link to libstdc++
-	CXXFLAGS="$CXXFLAGS -fno-rtti -fno-exceptions"
-
-	# No threadsafe statics and C++ as we do it ourselves
-	CXXFLAGS="$CXXFLAGS -fno-threadsafe-statics"
-
 	# Assorted warnings
 	CXXFLAGS="$CXXFLAGS -Wcast-align"
 
 	case "$host" in
 		*-*-mingw*)
 		;;
 		*)
 			# Hide inline methods
@@ -177,16 +180,17 @@ if test "x$with_gobject" = "xyes" -a "x$
 	AC_MSG_ERROR([gobject support requested but gobject-2.0 / glib-2.0 not found])
 fi
 if $have_gobject; then
 	AC_DEFINE(HAVE_GOBJECT, 1, [Have gobject2 library])
 	GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
 	AC_SUBST(GLIB_MKENUMS)
 fi
 AM_CONDITIONAL(HAVE_GOBJECT, $have_gobject)
+AC_SUBST(have_gobject)
 
 dnl ===========================================================================
 
 
 dnl ===========================================================================
 # Gobject-Introspection
 have_introspection=false
 m4_ifdef([GOBJECT_INTROSPECTION_CHECK], [
@@ -350,19 +354,17 @@ if test "x$with_freetype" = "xyes" -o "x
 fi
 if test "x$with_freetype" = "xyes" -a "x$have_freetype" != "xtrue"; then
 	AC_MSG_ERROR([FreeType support requested but libfreetype2 not found])
 fi
 if $have_freetype; then
 	AC_DEFINE(HAVE_FREETYPE, 1, [Have FreeType 2 library])
 	save_libs=$LIBS
 	LIBS="$LIBS $FREETYPE_LIBS"
-	AC_CHECK_FUNCS(FT_Get_Var_Blend_Coordinates)
-	AC_CHECK_FUNCS(FT_Set_Var_Blend_Coordinates)
-	AC_CHECK_FUNCS(FT_Done_MM_Var)
+	AC_CHECK_FUNCS(FT_Get_Var_Blend_Coordinates FT_Set_Var_Blend_Coordinates FT_Done_MM_Var)
 	LIBS=$save_libs
 fi
 AM_CONDITIONAL(HAVE_FREETYPE, $have_freetype)
 
 dnl ===========================================================================
 
 AC_ARG_WITH(uniscribe,
 	[AS_HELP_STRING([--with-uniscribe=@<:@yes/no/auto@:>@],
@@ -486,52 +488,55 @@ if test "$os_win32" = no && ! $have_pthr
 fi
 
 dnl ===========================================================================
 
 AC_CONFIG_FILES([
 Makefile
 src/Makefile
 src/hb-version.h
+src/harfbuzz-config.cmake
 src/hb-ucdn/Makefile
 util/Makefile
 test/Makefile
 test/api/Makefile
 test/fuzzing/Makefile
 test/shaping/Makefile
 test/shaping/data/Makefile
 test/shaping/data/in-house/Makefile
 test/shaping/data/text-rendering-tests/Makefile
+test/subset/Makefile
+test/subset/data/Makefile
 docs/Makefile
 docs/version.xml
 ])
 
 AC_OUTPUT
 
 AC_MSG_NOTICE([
 
 Build configuration:
 
 Unicode callbacks (you want at least one):
 	Builtin (UCDN):		${have_ucdn}
 	Glib:			${have_glib}
 	ICU:			${have_icu}
 
-Font callbacks (the more the better):
+Font callbacks (the more the merrier):
 	FreeType:		${have_freetype}
 
 Tools used for command-line utilities:
 	Cairo:			${have_cairo}
 	Fontconfig:		${have_fontconfig}
 
-Additional shapers (the more the better):
+Additional shapers (the more the merrier):
 	Graphite2:		${have_graphite2}
 
 Platform shapers (not normally needed):
 	CoreText:		${have_coretext}
 	Uniscribe:		${have_uniscribe}
 	DirectWrite:		${have_directwrite}
 
 Other features:
-	Documentation:		${have_gtk_doc}
+	Documentation:		${enable_gtk_doc}
 	GObject bindings:	${have_gobject}
 	Introspection:		${have_introspection}
 ])
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -4,36 +4,46 @@ NULL =
 SUBDIRS =
 DIST_SUBDIRS =
 BUILT_SOURCES =
 EXTRA_DIST =
 CLEANFILES =
 DISTCLEANFILES =
 MAINTAINERCLEANFILES =
 DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
+TESTS =
+check_PROGRAMS =
 
 # The following warning options are useful for debugging: -Wpadded
 #AM_CXXFLAGS =
 
 # Convenience targets:
-lib: $(BUILT_SOURCES) libharfbuzz.la
+lib: $(BUILT_SOURCES) libharfbuzz.la libharfbuzz-subset.la
 fuzzing: $(BUILT_SOURCES) libharfbuzz-fuzzing.la
 
 lib_LTLIBRARIES = libharfbuzz.la
 
 include Makefile.sources
 
 HBCFLAGS =
 HBLIBS =
 HBNONPCLIBS =
 HBDEPS =
 HBSOURCES =  $(HB_BASE_sources)
 HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources)
 HBHEADERS = $(HB_BASE_headers)
-HBNODISTHEADERS = $(HB_NODIST_headers)
+
+if WITH_LIBSTDCXX
+HBNOLIBCXXCFLAGS =
+else
+# Make sure we don't link to libstdc++
+# No threadsafe statics in C++ as we do it ourselves
+HBCFLAGS += -fno-exceptions
+HBNOLIBCXXFLAGS = -fno-threadsafe-statics -fno-rtti
+endif
 
 if HAVE_OT
 HBSOURCES += $(HB_OT_sources)
 HBSOURCES += $(HB_OT_RAGEL_GENERATED_sources)
 HBHEADERS += $(HB_OT_headers)
 endif
 
 if HAVE_FALLBACK
@@ -109,84 +119,115 @@ DIST_SUBDIRS += hb-ucdn
 
 # Put the library together
 
 HBLIBS += $(HBNONPCLIBS)
 
 if OS_WIN32
 export_symbols = -export-symbols harfbuzz.def
 harfbuzz_def_dependency = harfbuzz.def
-libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
+export_symbols_subset = -export-symbols harfbuzz-subset.def
+harfbuzz_subset_def_dependency = harfbuzz-subset.def
+export_symbols_icu = -export-symbols harfbuzz-icu.def
+harfbuzz_icu_def_dependency = harfbuzz-icu.def
+export_symbols_gobject = -export-symbols harfbuzz-gobject.def
+harfbuzz_gobject_def_dependency = harfbuzz-gobject.def
+chosen_linker = $(CXXLINK)
 else
-# Use a C linker for GCC, not C++; Don't link to libstdc++
+if WITH_LIBSTDCXX
+chosen_linker = $(CXXLINK)
+else
 if HAVE_GCC
-libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS)
+# Use a C linker for GCC, not C++; Don't link to libstdc++
+chosen_linker = $(LINK)
 else
-libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
+chosen_linker = $(CXXLINK)
+endif
 endif
 endif
 
-libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) $(HBNODISTHEADERS)
-libharfbuzz_la_CPPFLAGS = $(HBCFLAGS)
-libharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) -lm -version-info $(HB_LIBTOOL_VERSION_INFO) $(export_symbols) -no-undefined
+@CODE_COVERAGE_RULES@
+
+base_link_flags = $(AM_LDFLAGS) -lm -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
+libharfbuzz_la_LINK = $(chosen_linker) $(libharfbuzz_la_LDFLAGS) $(CODE_COVERAGE_LDFLAGS)
+libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS)
+libharfbuzz_la_CPPFLAGS = $(HBCFLAGS) $(HBNOLIBCXXFLAGS) $(CODE_COVERAGE_CFLAGS)
+libharfbuzz_la_LDFLAGS = $(base_link_flags) $(export_symbols)
 libharfbuzz_la_LIBADD = $(HBLIBS)
 EXTRA_libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency)
 pkginclude_HEADERS = $(HBHEADERS)
-nodist_pkginclude_HEADERS = $(HBNODISTHEADERS)
+nodist_pkginclude_HEADERS =
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = harfbuzz.pc
-EXTRA_DIST += harfbuzz.pc.in
+cmakedir = $(libdir)/cmake/harfbuzz
+cmake_DATA = harfbuzz-config.cmake
+EXTRA_DIST += harfbuzz.pc.in harfbuzz-config.cmake.in
+
+lib_LTLIBRARIES += libharfbuzz-subset.la
+libharfbuzz_subset_la_SOURCES = $(HB_SUBSET_sources)
+libharfbuzz_subset_la_CPPFLAGS = $(HBCFLAGS)
+libharfbuzz_subset_la_LDFLAGS = $(base_link_flags) $(export_symbols_subset)
+libharfbuzz_subset_la_LIBADD = libharfbuzz.la
+EXTRA_libharfbuzz_subset_la_DEPENDENCIES = $(harfbuzz_subset_def_dependency)
+pkginclude_HEADERS += $(HB_SUBSET_headers)
+pkgconfig_DATA += harfbuzz-subset.pc
+EXTRA_DIST += harfbuzz-subset.pc.in
 
 FUZZING_CPPFLAGS = \
 	-DHB_NDEBUG \
 	-DHB_MAX_NESTING_LEVEL=3 \
 	-DHB_SANITIZE_MAX_EDITS=3 \
+	-DHB_SANITIZE_MAX_OPS_FACTOR=3 \
+	-DHB_SANITIZE_MAX_OPS_MIN=128 \
 	-DHB_BUFFER_MAX_LEN_FACTOR=3 \
 	-DHB_BUFFER_MAX_LEN_MIN=8 \
 	-DHB_BUFFER_MAX_LEN_DEFAULT=128 \
 	-DHB_BUFFER_MAX_OPS_FACTOR=8 \
 	-DHB_BUFFER_MAX_OPS_MIN=64 \
 	-DHB_BUFFER_MAX_OPS_DEFAULT=1024 \
 	$(NULL)
 EXTRA_LTLIBRARIES = libharfbuzz-fuzzing.la
-libharfbuzz_fuzzing_la_LINK = $(libharfbuzz_la_LINK)
+libharfbuzz_fuzzing_la_LINK = $(chosen_linker) $(libharfbuzz_fuzzing_la_LDFLAGS)
 libharfbuzz_fuzzing_la_SOURCES = $(libharfbuzz_la_SOURCES)
-libharfbuzz_fuzzing_la_CPPFLAGS = $(libharfbuzz_la_CPPFLAGS) $(FUZZING_CPPFLAGS)
-libharfbuzz_fuzzing_la_LDFLAGS = $(libharfbuzz_la_LDFLAGS)
+libharfbuzz_fuzzing_la_CPPFLAGS = $(HBCFLAGS) $(FUZZING_CPPFLAGS)
+libharfbuzz_fuzzing_la_LDFLAGS = $(AM_LDFLAGS)
 libharfbuzz_fuzzing_la_LIBADD = $(libharfbuzz_la_LIBADD)
 EXTRA_libharfbuzz_fuzzing_la_DEPENDENCIES = $(EXTRA_libharfbuzz_la_DEPENDENCIES)
 CLEANFILES += libharfbuzz-fuzzing.la
 
 if HAVE_ICU
 if HAVE_ICU_BUILTIN
 HBCFLAGS += $(ICU_CFLAGS)
 HBLIBS += $(ICU_LIBS)
 HBSOURCES += $(HB_ICU_sources)
 HBHEADERS += $(HB_ICU_headers)
 else
 lib_LTLIBRARIES += libharfbuzz-icu.la
 libharfbuzz_icu_la_SOURCES = $(HB_ICU_sources)
-libharfbuzz_icu_la_CPPFLAGS = $(ICU_CFLAGS)
-libharfbuzz_icu_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
+libharfbuzz_icu_la_CPPFLAGS = $(HBCFLAGS) $(ICU_CFLAGS)
+libharfbuzz_icu_la_LDFLAGS = $(base_link_flags) $(export_symbols_icu)
 libharfbuzz_icu_la_LIBADD = $(ICU_LIBS) libharfbuzz.la
+EXTRA_libharfbuzz_icu_la_DEPENDENCIES = $(harfbuzz_icu_def_dependency)
 pkginclude_HEADERS += $(HB_ICU_headers)
 pkgconfig_DATA += harfbuzz-icu.pc
 endif
 endif
 EXTRA_DIST += harfbuzz-icu.pc.in
 
 if HAVE_GOBJECT
 lib_LTLIBRARIES += libharfbuzz-gobject.la
-libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_sources)
-nodist_libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_ENUM_sources)
-libharfbuzz_gobject_la_CPPFLAGS = $(GOBJECT_CFLAGS)
-libharfbuzz_gobject_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
+libharfbuzz_gobject_la_LINK = $(chosen_linker) $(libharfbuzz_gobject_la_LDFLAGS)
+libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_DIST_sources)
+nodist_libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_NODIST_sources)
+libharfbuzz_gobject_la_CPPFLAGS = $(HBCFLAGS) $(HBNOLIBCXXFLAGS) $(GOBJECT_CFLAGS)
+libharfbuzz_gobject_la_LDFLAGS = $(base_link_flags)
 libharfbuzz_gobject_la_LIBADD = $(GOBJECT_LIBS) libharfbuzz.la
-pkginclude_HEADERS += $(HB_GOBJECT_headers)
-nodist_pkginclude_HEADERS += $(HB_GOBJECT_ENUM_headers)
+EXTRA_libharfbuzz_gobject_la_DEPENDENCIES = $(harfbuzz_gobject_def_dependency)
+pkginclude_HEADERS += $(HB_GOBJECT_DIST_headers)
+nodist_pkginclude_HEADERS += $(HB_GOBJECT_NODIST_headers)
 pkgconfig_DATA += harfbuzz-gobject.pc
 
 BUILT_SOURCES += \
 	$(HB_GOBJECT_ENUM_sources) \
 	$(HB_GOBJECT_ENUM_headers) \
 	$(NULL)
 DISTCLEANFILES += \
 	$(HB_GOBJECT_ENUM_sources) \
@@ -216,19 +257,30 @@ EXTRA_DIST += \
 		-e 's@%requires_private%@$(HBDEPS)@g' \
 		-e 's@%VERSION%@$(VERSION)@g' \
 	"$<" > "$@" \
 	|| ($(RM) "$@"; false)
 
 CLEANFILES += $(pkgconfig_DATA)
 
 
-CLEANFILES += harfbuzz.def
+DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def
+if HAVE_GOBJECT
+DEF_FILES += harfbuzz-gobject.def
+endif
+check: $(DEF_FILES) # For check-symbols.sh
+CLEANFILES += $(DEF_FILES)
 harfbuzz.def: $(HBHEADERS) $(HBNODISTHEADERS)
 	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@
+harfbuzz-subset.def: $(HB_SUBSET_headers)
+	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@
+harfbuzz-icu.def: $(HB_ICU_headers)
+	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@
+harfbuzz-gobject.def: $(HB_GOBJECT_headers)
+	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@
 
 
 GENERATORS = \
 	gen-arabic-table.py \
 	gen-indic-table.py \
 	gen-use-table.py \
 	gen-def.py \
 	$(NULL)
@@ -256,17 +308,18 @@ RAGEL_GENERATED = \
 	$(patsubst %,$(srcdir)/%,$(HB_BASE_RAGEL_GENERATED_sources)) \
 	$(patsubst %,$(srcdir)/%,$(HB_OT_RAGEL_GENERATED_sources)) \
 	$(NULL)
 BUILT_SOURCES += $(RAGEL_GENERATED)
 EXTRA_DIST += \
 	$(HB_BASE_RAGEL_sources) \
 	$(HB_OT_RAGEL_sources) \
 	$(NULL)
-MAINTAINERCLEANFILES += $(RAGEL_GENERATED)
+# We decided to add ragel-generated files to git...
+#MAINTAINERCLEANFILES += $(RAGEL_GENERATED)
 $(srcdir)/%.hh: $(srcdir)/%.rl
 	$(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \
 	|| ($(RM) "$@"; false)
 
 noinst_PROGRAMS = \
 	main \
 	test \
 	test-buffer-serialize \
@@ -290,42 +343,66 @@ test_would_substitute_LDADD = libharfbuz
 test_size_params_SOURCES = test-size-params.cc
 test_size_params_CPPFLAGS = $(HBCFLAGS)
 test_size_params_LDADD = libharfbuzz.la $(HBLIBS)
 
 test_buffer_serialize_SOURCES = test-buffer-serialize.cc
 test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
 test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
 
-check: harfbuzz.def # For check-defs.sh
-
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
-	check-defs.sh \
 	check-externs.sh \
 	check-header-guards.sh \
 	check-includes.sh \
-	check-libstdc++.sh \
 	check-static-inits.sh \
 	check-symbols.sh \
 	$(NULL)
+TESTS += $(dist_check_SCRIPTS)
 
-check_PROGRAMS = \
-	test-ot-tag \
+if !WITH_LIBSTDCXX
+dist_check_SCRIPTS += \
+	check-libstdc++.sh \
+	$(NULL)
+endif
+
+check_PROGRAMS += \
+	dump-indic-data \
+	dump-khmer-data \
+	dump-myanmar-data \
+	dump-use-data \
 	$(NULL)
+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)
+dump_myanmar_data_LDADD = libharfbuzz.la $(HBLIBS)
+dump_use_data_SOURCES = dump-use-data.cc hb-ot-shape-complex-use-table.cc
+dump_use_data_CPPFLAGS = $(HBCFLAGS)
+dump_use_data_LDADD = libharfbuzz.la $(HBLIBS)
+
+check_PROGRAMS += test-ot-tag test-unicode-ranges
+TESTS += test-ot-tag test-unicode-ranges
+
 test_ot_tag_SOURCES = hb-ot-tag.cc
 test_ot_tag_CPPFLAGS = $(HBCFLAGS) -DMAIN
 test_ot_tag_LDADD = libharfbuzz.la $(HBLIBS)
 
-TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS)
+test_unicode_ranges_SOURCES = test-unicode-ranges.cc
+test_unicode_ranges_LDADD = libharfbuzz.la $(HBLIBS)
+
 TESTS_ENVIRONMENT = \
 	srcdir="$(srcdir)" \
 	MAKE="$(MAKE) $(AM_MAKEFLAGS)" \
 	HBSOURCES="$(HBSOURCES)" \
-	HBHEADERS="$(HBHEADERS) $(HBNODISTHEADERS)" \
+	HBHEADERS="$(HBHEADERS)" \
 	$(NULL)
 
 if HAVE_INTROSPECTION
 
 -include $(INTROSPECTION_MAKEFILE)
 INTROSPECTION_GIRS = HarfBuzz-0.0.gir # What does the 0 mean anyway?!
 INTROSPECTION_SCANNER_ARGS = -I$(srcdir) -n hb --identifier-prefix=hb_ --warn-all
 INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
@@ -345,22 +422,19 @@ HarfBuzz_0_0_gir_CFLAGS = \
 	-DHB_EXTERN= \
 	$(NULL)
 HarfBuzz_0_0_gir_LIBS = \
 	libharfbuzz.la \
 	libharfbuzz-gobject.la \
 	$(NULL)
 HarfBuzz_0_0_gir_FILES = \
 	$(HBHEADERS) \
-	$(HBNODISTHEADERS) \
 	$(HBSOURCES) \
-	$(HB_GOBJECT_ENUM_sources) \
-	$(HB_GOBJECT_ENUM_headers) \
 	$(HB_GOBJECT_sources) \
-	$(HB_GOBJECT_STRUCTS_headers) \
+	$(HB_GOBJECT_headers) \
 	$(NULL)
 
 girdir = $(datadir)/gir-1.0
 gir_DATA = $(INTROSPECTION_GIRS)
 
 typelibdir = $(libdir)/girepository-1.0
 typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
 
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/Makefile.sources
@@ -0,0 +1,214 @@
+# Base and default-included sources and headers
+
+HB_BASE_sources = \
+	hb-atomic-private.hh \
+	hb-blob.cc \
+	hb-buffer-private.hh \
+	hb-buffer-serialize.cc \
+	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-mutex-private.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 \
+	hb-ot-hhea-table.hh \
+	hb-ot-hmtx-table.hh \
+	hb-ot-kern-table.hh \
+	hb-ot-maxp-table.hh \
+	hb-ot-name-table.hh \
+	hb-ot-os2-table.hh \
+	hb-ot-os2-unicode-ranges.hh \
+	hb-ot-post-macroman.hh \
+	hb-ot-post-table.hh \
+	hb-ot-tag.cc \
+	hb-private.hh \
+	hb-set-digest-private.hh \
+	hb-set-private.hh \
+	hb-set.cc \
+	hb-shape.cc \
+	hb-shape-plan-private.hh \
+	hb-shape-plan.cc \
+	hb-shaper-list.hh \
+	hb-shaper-impl-private.hh \
+	hb-shaper-private.hh \
+	hb-shaper.cc \
+	hb-string-array.hh \
+	hb-unicode-private.hh \
+	hb-unicode.cc \
+	hb-utf-private.hh \
+	hb-warning.cc \
+	$(NULL)
+
+HB_BASE_RAGEL_GENERATED_sources = \
+	hb-buffer-deserialize-json.hh \
+	hb-buffer-deserialize-text.hh \
+	$(NULL)
+HB_BASE_RAGEL_sources = \
+	hb-buffer-deserialize-json.rl \
+	hb-buffer-deserialize-text.rl \
+	$(NULL)
+
+HB_BASE_headers = \
+	hb.h \
+	hb-blob.h \
+	hb-buffer.h \
+	hb-common.h \
+	hb-deprecated.h \
+	hb-face.h \
+	hb-font.h \
+	hb-set.h \
+	hb-shape.h \
+	hb-shape-plan.h \
+	hb-unicode.h \
+	hb-version.h \
+	$(NULL)
+
+HB_FALLBACK_sources = \
+	hb-fallback-shape.cc	\
+	$(NULL)
+
+HB_OT_sources = \
+	hb-aat-layout.cc \
+	hb-aat-layout-common-private.hh \
+	hb-aat-layout-ankr-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-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 \
+	hb-ot-layout-gsub-table.hh \
+	hb-ot-layout-jstf-table.hh \
+	hb-ot-layout-private.hh \
+	hb-ot-color.cc \
+	hb-ot-color-colr-table.hh \
+	hb-ot-color-cpal-table.hh \
+	hb-ot-map.cc \
+	hb-ot-map-private.hh \
+	hb-ot-math.cc \
+	hb-ot-math-table.hh \
+	hb-ot-shape.cc \
+	hb-ot-shape-complex-arabic.cc \
+	hb-ot-shape-complex-arabic-fallback.hh \
+	hb-ot-shape-complex-arabic-private.hh \
+	hb-ot-shape-complex-arabic-table.hh \
+	hb-ot-shape-complex-arabic-win1256.hh \
+	hb-ot-shape-complex-default.cc \
+	hb-ot-shape-complex-hangul.cc \
+	hb-ot-shape-complex-hebrew.cc \
+	hb-ot-shape-complex-indic.cc \
+	hb-ot-shape-complex-indic-private.hh \
+	hb-ot-shape-complex-indic-table.cc \
+	hb-ot-shape-complex-khmer-private.hh \
+	hb-ot-shape-complex-khmer.cc \
+	hb-ot-shape-complex-myanmar-private.hh \
+	hb-ot-shape-complex-myanmar.cc \
+	hb-ot-shape-complex-thai.cc \
+	hb-ot-shape-complex-tibetan.cc \
+	hb-ot-shape-complex-use.cc \
+	hb-ot-shape-complex-use-private.hh \
+	hb-ot-shape-complex-use-table.cc \
+	hb-ot-shape-complex-private.hh \
+	hb-ot-shape-normalize-private.hh \
+	hb-ot-shape-normalize.cc \
+	hb-ot-shape-fallback-private.hh \
+	hb-ot-shape-fallback.cc \
+	hb-ot-shape-private.hh \
+	hb-ot-var.cc \
+	hb-ot-var-avar-table.hh \
+	hb-ot-var-fvar-table.hh \
+	hb-ot-var-hvar-table.hh \
+	hb-ot-var-mvar-table.hh \
+	$(NULL)
+
+HB_OT_RAGEL_GENERATED_sources = \
+	hb-ot-shape-complex-indic-machine.hh \
+	hb-ot-shape-complex-khmer-machine.hh \
+	hb-ot-shape-complex-myanmar-machine.hh \
+	hb-ot-shape-complex-use-machine.hh \
+	$(NULL)
+HB_OT_RAGEL_sources = \
+	hb-ot-shape-complex-indic-machine.rl \
+	hb-ot-shape-complex-khmer-machine.rl \
+	hb-ot-shape-complex-myanmar-machine.rl \
+	hb-ot-shape-complex-use-machine.rl \
+	$(NULL)
+
+HB_OT_headers = \
+	hb-ot.h \
+	hb-ot-font.h \
+	hb-ot-layout.h \
+	hb-ot-math.h \
+	hb-ot-shape.h \
+	hb-ot-tag.h \
+	hb-ot-var.h \
+	$(NULL)
+
+# Optional Sources and Headers with external deps
+
+HB_FT_sources = hb-ft.cc
+HB_FT_headers = hb-ft.h
+
+HB_GLIB_sources = hb-glib.cc
+HB_GLIB_headers = hb-glib.h
+
+HB_GRAPHITE2_sources = hb-graphite2.cc
+HB_GRAPHITE2_headers = hb-graphite2.h
+
+# System-dependent sources and headers
+
+HB_CORETEXT_sources = hb-coretext.cc
+HB_CORETEXT_headers = hb-coretext.h
+
+HB_DIRECTWRITE_sources = hb-directwrite.cc
+HB_DIRECTWRITE_headers = hb-directwrite.h
+
+HB_UNISCRIBE_sources = hb-uniscribe.cc
+HB_UNISCRIBE_headers = hb-uniscribe.h
+
+# Additional supplemental sources
+HB_UCDN_sources  = hb-ucdn.cc
+
+# Sources for libharfbuzz-gobject and libharfbuzz-icu
+HB_ICU_sources = hb-icu.cc
+HB_ICU_headers = hb-icu.h
+
+# Sources for libharfbuzz-subset
+HB_SUBSET_sources = \
+	hb-subset.cc \
+	hb-subset-glyf.cc \
+	hb-subset-input.cc \
+	hb-subset-plan.cc \
+	$(NULL)
+
+HB_SUBSET_headers = \
+	hb-subset.h \
+	hb-subset-glyf.hh \
+	hb-subset-plan.hh \
+	hb-subset-private.hh \
+	$(NULL)
+
+HB_GOBJECT_DIST_sources = hb-gobject-structs.cc
+HB_GOBJECT_DIST_headers = hb-gobject.h hb-gobject-structs.h
+HB_GOBJECT_ENUM_sources = hb-gobject-enums.cc
+HB_GOBJECT_ENUM_headers = hb-gobject-enums.h
+HB_GOBJECT_NODIST_sources = $(HB_GOBJECT_ENUM_sources)
+HB_GOBJECT_NODIST_headers = $(HB_GOBJECT_ENUM_headers)
+HB_GOBJECT_sources = $(HB_GOBJECT_DIST_sources) $(HB_GOBJECT_NODIST_sources)
+HB_GOBJECT_headers = $(HB_GOBJECT_DIST_headers) $(HB_GOBJECT_NODIST_headers)
deleted file mode 100755
--- a/gfx/harfbuzz/src/check-defs.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/sh
-
-LC_ALL=C
-export LC_ALL
-
-test -z "$srcdir" && srcdir=.
-test -z "$libs" && libs=.libs
-stat=0
-
-if which nm 2>/dev/null >/dev/null; then
-	:
-else
-	echo "check-defs.sh: 'nm' not found; skipping test"
-	exit 77
-fi
-
-defs="harfbuzz.def"
-if ! test -f "$defs"; then
-	echo "check-defs.sh: '$defs' not found; skipping test"
-	exit 77
-fi
-
-tested=false
-for def in $defs; do
-	lib=`echo "$def" | sed 's/[.]def$//;s@.*/@@'`
-	for suffix in so dylib; do
-		so=$libs/lib${lib}.$suffix
-		if ! test -f "$so"; then continue; fi
-
-		# On mac, C symbols are prefixed with _
-		if test $suffix = dylib; then prefix="_"; fi
-
-		EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] .' | grep -v " $prefix"'\(_fini\>\|_init\>\|_fdata\>\|_ftext\>\|_fbss\>\|__bss_start\>\|__bss_start__\>\|__bss_end__\>\|_edata\>\|_end\>\|_bss_end__\>\|__end__\>\|__gcov_flush\>\|llvm_\)' | cut -d' ' -f3`"
-
-		if test -f "$so"; then
-
-			echo "Checking that $so has the same symbol list as $def"
-			{
-				echo EXPORTS
-				echo "$EXPORTED_SYMBOLS" | sed -e "s/^${prefix}hb/hb/g"
-				# cheat: copy the last line from the def file!
-				tail -n1 "$def"
-			} | diff "$def" - >&2 || stat=1
-
-			tested=true
-		fi
-	done
-done
-if ! $tested; then
-	echo "check-defs.sh: libharfbuzz shared library not found; skipping test"
-	exit 77
-fi
-
-exit $stat
--- a/gfx/harfbuzz/src/check-libstdc++.sh
+++ b/gfx/harfbuzz/src/check-libstdc++.sh
@@ -16,25 +16,29 @@ else
 		LDD="otool -L"
 	else
 		echo "check-libstdc++.sh: 'ldd' not found; skipping test"
 		exit 77
 	fi
 fi
 
 tested=false
-for suffix in so dylib; do
-	so=$libs/libharfbuzz.$suffix
-	if ! test -f "$so"; then continue; fi
+# harfbuzz-icu links to libstdc++ because icu does.
+# harfbuzz-subset uses libstdc++.
+for soname in harfbuzz harfbuzz-gobject; do
+	for suffix in so dylib; do
+		so=$libs/lib$soname.$suffix
+		if ! test -f "$so"; then continue; fi
 
-	echo "Checking that we are not linking to libstdc++ or libc++"
-	if $LDD $so | grep 'libstdc[+][+]\|libc[+][+]'; then
-		echo "Ouch, linked to libstdc++ or libc++"
-		stat=1
-	fi
-	tested=true
+		echo "Checking that we are not linking to libstdc++ or libc++ in $so"
+		if $LDD $so | grep 'libstdc[+][+]\|libc[+][+]'; then
+			echo "Ouch, linked to libstdc++ or libc++"
+			stat=1
+		fi
+		tested=true
+	done
 done
 if ! $tested; then
 	echo "check-libstdc++.sh: libharfbuzz shared library not found; skipping test"
 	exit 77
 fi
 
 exit $stat
--- a/gfx/harfbuzz/src/check-symbols.sh
+++ b/gfx/harfbuzz/src/check-symbols.sh
@@ -2,43 +2,61 @@
 
 LC_ALL=C
 export LC_ALL
 
 test -z "$srcdir" && srcdir=.
 test -z "$libs" && libs=.libs
 stat=0
 
+IGNORED_SYMBOLS='_fini\|_init\|_fdata\|_ftext\|_fbss\|__bss_start\|__bss_start__\|__bss_end__\|_edata\|_end\|_bss_end__\|__end__\|__gcov_flush\|llvm_.*'
 
 if which nm 2>/dev/null >/dev/null; then
 	:
 else
 	echo "check-symbols.sh: 'nm' not found; skipping test"
 	exit 77
 fi
 
-echo "Checking that we are not exposing internal symbols"
 tested=false
-for suffix in so dylib; do
-	so=$libs/libharfbuzz.$suffix
-	if ! test -f "$so"; then continue; fi
+for soname in harfbuzz harfbuzz-subset harfbuzz-icu harfbuzz-gobject; do
+	for suffix in so dylib; do
+		so=$libs/lib$soname.$suffix
+		if ! test -f "$so"; then continue; fi
 
-	EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] .' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| ___gcov_flush\>\| llvm_\| _llvm_' | cut -d' ' -f3`"
+		# On macOS, C symbols are prefixed with _
+		symprefix=
+		if test $suffix = dylib; then symprefix=_; fi
 
-	prefix=`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
+		EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`"
+
+		prefix=$symprefix`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
 
-	# On mac, C symbols are prefixed with _
-	if test $suffix = dylib; then prefix="_$prefix"; fi
+		echo
+		echo "Checking that $so does not expose internal symbols"
+		if echo "$EXPORTED_SYMBOLS" | grep -v "^${prefix}\(_\|$\)"; then
+			echo "Ouch, internal symbols exposed"
+			stat=1
+		fi
 
-	echo "Processing $so"
-	if echo "$EXPORTED_SYMBOLS" | grep -v "^${prefix}_"; then
-		echo "Ouch, internal symbols exposed"
-		stat=1
-	fi
+		def=$soname.def
+		if ! test -f "$def"; then
+			echo "'$def' not found; skipping"
+		else
+			echo
+			echo "Checking that $so has the same symbol list as $def"
+			{
+				echo EXPORTS
+				echo "$EXPORTED_SYMBOLS" | sed -e "s/^${symprefix}hb/hb/g"
+				# cheat: copy the last line from the def file!
+				tail -n1 "$def"
+			} | c++filt | diff "$def" - >&2 || stat=1
+		fi
 
-	tested=true
+		tested=true
+	done
 done
 if ! $tested; then
-	echo "check-symbols.sh: no shared library found; skipping test"
+	echo "check-symbols.sh: no shared libraries found; skipping test"
 	exit 77
 fi
 
 exit $stat
new file mode 100755
--- /dev/null
+++ b/gfx/harfbuzz/src/dev-run.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+# Suggested setup to use the script:
+#  (on the root of the project)
+#  $ NOCONFIGURE=1 ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
+#  $ mkdir build && cd build && ../configure && make -j5 && cd ..
+#  $ src/dev-run.sh [FONT-FILE] [TEXT]
+#
+# Or, using cmake:
+#  $ cmake -DHB_CHECK=ON -Bbuild -H. -GNinja && ninja -Cbuild
+#  $ src/dev-run.sh [FONT-FILE] [TEXT]
+#
+# If you want to open the result rendering using a GUI app,
+#  $ src/dev-run.sh open [FONT-FILE] [TEXT]
+#
+# And if you are using iTerm2, you can use the script like this,
+#  $ src/dev-run.sh img [FONT-FILE] [TEXT]
+#
+
+[ $# = 0 ] && echo Usage: "src/dev-run.sh [FONT-FILE] [TEXT]" && exit
+command -v entr >/dev/null 2>&1 || { echo >&2 "This script needs `entr` be installed"; exit 1; }
+
+
+GDB=gdb
+# if gdb doesn't exist, hopefully lldb exist
+command -v $GDB >/dev/null 2>&1 || export GDB="lldb"
+
+
+[ $1 = "open" ] && openimg=1 && shift
+OPEN=xdg-open
+[ "$(uname)" == "Darwin" ] && OPEN=open
+
+
+[ $1 = "img" ] && img=1 && shift
+# http://iterm2.com/documentation-images.html
+osc="\033]"
+if [[ $TERM == screen* ]]; then osc="\033Ptmux;\033\033]"; fi
+st="\a"
+if [[ $TERM == screen* ]]; then st="\a"; fi
+
+
+tmp=tmp.png
+[ -f 'build/build.ninja' ] && CMAKENINJA=TRUE
+# or "fswatch -0 . -e build/ -e .git"
+find src/ | entr printf '\0' | while read -d ""; do
+	clear
+	echo '===================================================='
+	if [[ $CMAKENINJA ]]; then
+		ninja -Cbuild hb-shape hb-view && {
+			build/hb-shape $@
+			if [ $openimg ]; then
+				build/hb-view $@ -O png -o $tmp
+				$OPEN $tmp
+			elif [ $img ]; then
+				build/hb-view $@ -O png -o $tmp
+				printf "\n${osc}1337;File=;inline=1:`cat $tmp | base64`${st}\n"
+			else
+				build/hb-view $@
+			fi
+		}
+	else
+		make -Cbuild/src -j5 -s lib && {
+			build/util/hb-shape $@
+			if [ $openimg ]; then
+				build/util/hb-view $@ -O png -o $tmp
+				$OPEN $tmp
+			elif [ $img ]; then
+				build/util/hb-view $@ -O png -o $tmp
+				printf "\n${osc}1337;File=;inline=1:`cat $tmp | base64`${st}\n"
+			else
+				build/util/hb-view $@
+			fi
+		}
+	fi
+done
+
+read -n 1 -p "[C]heck, [D]ebug, [R]estart, [Q]uit? " answer
+case "$answer" in
+c|C )
+	if [[ $CMAKENINJA ]]; then
+		CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=5 ninja -Cbuild test
+	else
+		make -Cbuild -j5 check && .ci/fail.sh
+	fi
+;;
+d|D )
+	if [[ $CMAKENINJA ]]; then
+		echo "Not supported on cmake builds yet"
+	else
+		build/libtool --mode=execute $GDB -- build/util/hb-shape $@
+	fi
+;;
+r|R )
+	src/dev-run.sh $@
+;;
+* )
+	exit
+;;
+esac
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/dump-indic-data.cc
@@ -0,0 +1,43 @@
+/*
+ * 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
+ */
+
+#include "hb-ot-shape-complex-indic-private.hh"
+
+int
+main (void)
+{
+  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
+  {
+    hb_glyph_info_t info;
+    info.codepoint = u;
+    set_indic_properties (info);
+    if (info.indic_category() != INDIC_SYLLABIC_CATEGORY_OTHER ||
+	info.indic_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE)
+      printf("U+%04X	%u	%u\n", u,
+	     info.indic_category(),
+	     info.indic_position());
+  }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/dump-khmer-data.cc
@@ -0,0 +1,43 @@
+/*
+ * 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
+ */
+
+#include "hb-ot-shape-complex-khmer-private.hh"
+
+int
+main (void)
+{
+  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
+  {
+    hb_glyph_info_t info;
+    info.codepoint = u;
+    set_khmer_properties (info);
+    if (info.khmer_category() != INDIC_SYLLABIC_CATEGORY_OTHER ||
+	info.khmer_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE)
+      printf("U+%04X	%u	%u\n", u,
+	     info.khmer_category(),
+	     info.khmer_position());
+  }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/dump-myanmar-data.cc
@@ -0,0 +1,43 @@
+/*
+ * 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
+ */
+
+#include "hb-ot-shape-complex-myanmar-private.hh"
+
+int
+main (void)
+{
+  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
+  {
+    hb_glyph_info_t info;
+    info.codepoint = u;
+    set_myanmar_properties (info);
+    if (info.myanmar_category() != INDIC_SYLLABIC_CATEGORY_OTHER ||
+	info.myanmar_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE)
+      printf("U+%04X	%u	%u\n", u,
+	     info.myanmar_category(),
+	     info.myanmar_position());
+  }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/dump-use-data.cc
@@ -0,0 +1,38 @@
+/*
+ * 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
+ */
+
+#include "hb-ot-shape-complex-use-private.hh"
+
+int
+main (void)
+{
+  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
+  {
+    unsigned int category = hb_use_get_category (u);
+    if (category != USE_O)
+      printf("U+%04X	%u\n", u, category);
+  }
+}
--- a/gfx/harfbuzz/src/gen-def.py
+++ b/gfx/harfbuzz/src/gen-def.py
@@ -4,13 +4,16 @@ from __future__ import print_function
 
 import io, os, re, sys
 
 headers_content = []
 for h in os.environ["headers"].split (' '):
 	if h.endswith (".h"):
 		with io.open(h, encoding='utf8') as f: headers_content.append (f.read ())
 
-result = ("EXPORTS\n" +
-	"\n".join (sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M))) +
-	"\nLIBRARY libharfbuzz-0.dll")
+result = """EXPORTS
+%s
+LIBRARY lib%s-0.dll""" % (
+	"\n".join (sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M))),
+	sys.argv[1].replace ('.def', '')
+)
 
 with open (sys.argv[1], "w") as f: f.write (result)
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/gen-unicode-ranges.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+
+# Generates the code for a sorted unicode range array as used in hb-ot-os2-unicode-ranges.hh
+# Input is a tab seperated list of unicode ranges from the otspec
+# (https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ulunicoderange1).
+
+import io
+import re
+import sys
+
+reload(sys)
+sys.setdefaultencoding('utf-8')
+
+print (u"""static Range os2UnicodeRangesSorted[] =
+{""")
+
+args = sys.argv[1:]
+input_file = args[0]
+
+with io.open(input_file, mode="r", encoding="utf-8") as f:
+
+  all_ranges = [];
+  current_bit = 0
+  while True:
+    line = f.readline().strip()
+    if not line:
+      break
+    fields = re.split(r'\t+', line)
+    if len(fields) == 3:
+      current_bit = fields[0]
+      fields = fields[1:]
+    elif len(fields) > 3:
+      raise Error("bad input :(.")
+
+    name = fields[0]
+    ranges = re.split("-", fields[1])
+    if len(ranges) != 2:
+      raise Error("bad input :(.")
+
+    v = tuple((int(ranges[0], 16), int(ranges[1], 16), int(current_bit), name))
+    all_ranges.append(v)
+
+all_ranges = sorted(all_ranges, key=lambda t: t[0])
+
+for ranges in all_ranges:
+  start = ("0x%X" % ranges[0]).rjust(8)
+  end = ("0x%X" % ranges[1]).rjust(8)
+  bit = ("%s" % ranges[2]).rjust(3)
+
+  print "  {%s, %s, %s}, // %s" % (start, end, bit, ranges[3])
+
+print (u"""};""");
--- a/gfx/harfbuzz/src/gen-use-table.py
+++ b/gfx/harfbuzz/src/gen-use-table.py
@@ -444,17 +444,17 @@ ends.append (last + 1)
 offset += ends[-1] - starts[-1]
 print
 print
 occupancy = used * 100. / total
 page_bits = 12
 print "}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy)
 print
 print "USE_TABLE_ELEMENT_TYPE"
-print "hb_use_get_categories (hb_codepoint_t u)"
+print "hb_use_get_category (hb_codepoint_t u)"
 print "{"
 print "  switch (u >> %d)" % page_bits
 print "  {"
 pages = set([u>>page_bits for u in starts+ends])
 for p in sorted(pages):
 	print "    case 0x%0Xu:" % p
 	for (start,end) in zip (starts, ends):
 		if p not in [start>>page_bits, end>>page_bits]: continue
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/harfbuzz-config.cmake.in
@@ -0,0 +1,82 @@
+# Set these variables so that the `${prefix}/lib` expands to something we can
+# remove.
+set(_harfbuzz_remove_string "REMOVE_ME")
+set(exec_prefix "${_harfbuzz_remove_string}")
+set(prefix "${_harfbuzz_remove_string}")
+
+# Compute the installation prefix by stripping components from our current
+# location.
+get_filename_component(_harfbuzz_prefix "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY)
+get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY)
+set(_harfbuzz_libdir "@libdir@")
+string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_libdir "${_harfbuzz_libdir}")
+set(_harfbuzz_libdir_iter "${_harfbuzz_libdir}")
+while (_harfbuzz_libdir_iter)
+  get_filename_component(_harfbuzz_libdir_iter "${_harfbuzz_libdir_iter}" DIRECTORY)
+  get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY)
+endwhile ()
+unset(_harfbuzz_libdir_iter)
+
+# Get the include subdir.
+set(_harfbuzz_includedir "@includedir@")
+string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_includedir "${_harfbuzz_includedir}")
+
+# Extract version information from libtool.
+set(_harfbuzz_version_info "@HB_LIBTOOL_VERSION_INFO@")
+string(REPLACE ":" ";" _harfbuzz_version_info "${_harfbuzz_version_info}")
+list(GET _harfbuzz_version_info 0
+  _harfbuzz_current)
+list(GET _harfbuzz_version_info 1
+  _harfbuzz_revision)
+list(GET _harfbuzz_version_info 2
+  _harfbuzz_age)
+unset(_harfbuzz_version_info)
+
+if (APPLE)
+  set(_harfbuzz_lib_suffix ".0${CMAKE_SHARED_LIBRARY_SUFFIX}")
+elseif (UNIX)
+  set(_harfbuzz_lib_suffix "${CMAKE_SHARED_LIBRARY_SUFFIX}.0.${_harfbuzz_current}.${_harfbuzz_revision}")
+else ()
+  # Unsupported.
+  set(harfbuzz_FOUND 0)
+endif ()
+
+# Add the libraries.
+add_library(harfbuzz::harfbuzz SHARED IMPORTED)
+set_target_properties(harfbuzz::harfbuzz PROPERTIES
+  INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
+  IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz${_harfbuzz_lib_suffix}")
+
+add_library(harfbuzz::icu SHARED IMPORTED)
+set_target_properties(harfbuzz::icu PROPERTIES
+  INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
+  INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
+  IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-icu${_harfbuzz_lib_suffix}")
+
+add_library(harfbuzz::subset SHARED IMPORTED)
+set_target_properties(harfbuzz::subset PROPERTIES
+  INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
+  INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
+  IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-subset${_harfbuzz_lib_suffix}")
+
+# Only add the gobject library if it was built.
+set(_harfbuzz_have_gobject "@have_gobject@")
+if (_harfbuzz_have_gobject)
+  add_library(harfbuzz::gobject SHARED IMPORTED)
+  set_target_properties(harfbuzz::gobject PROPERTIES
+    INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
+    INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
+    IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-gobject${_harfbuzz_lib_suffix}")
+endif ()
+
+# Clean out variables we used in our scope.
+unset(_harfbuzz_lib_suffix)
+unset(_harfbuzz_current)
+unset(_harfbuzz_revision)
+unset(_harfbuzz_age)
+unset(_harfbuzz_includedir)
+unset(_harfbuzz_libdir)
+unset(_harfbuzz_prefix)
+unset(exec_prefix)
+unset(prefix)
+unset(_harfbuzz_remove_string)
--- a/gfx/harfbuzz/src/harfbuzz-icu.pc
+++ b/gfx/harfbuzz/src/harfbuzz-icu.pc
@@ -1,13 +1,13 @@
 prefix=/usr/local
 exec_prefix=/usr/local
 libdir=/usr/local/lib
 includedir=/usr/local/include
 
 Name: harfbuzz
 Description: HarfBuzz text shaping library ICU integration
-Version: 1.7.5
+Version: 1.7.6
 
 Requires: harfbuzz
 Requires.private: icu-uc
 Libs: -L${libdir} -lharfbuzz-icu
 Cflags: -I${includedir}/harfbuzz
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/harfbuzz-subset.pc
@@ -0,0 +1,12 @@
+prefix=/usr/local
+exec_prefix=/usr/local
+libdir=/usr/local/lib
+includedir=/usr/local/include
+
+Name: harfbuzz
+Description: HarfBuzz font subsetter
+Version: 1.7.6
+
+Requires: harfbuzz
+Libs: -L${libdir} -lharfbuzz-subset
+Cflags: -I${includedir}/harfbuzz
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/harfbuzz-subset.pc.in
@@ -0,0 +1,12 @@
+prefix=%prefix%
+exec_prefix=%exec_prefix%
+libdir=%libdir%
+includedir=%includedir%
+
+Name: harfbuzz
+Description: HarfBuzz font subsetter
+Version: %VERSION%
+
+Requires: harfbuzz
+Libs: -L${libdir} -lharfbuzz-subset
+Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/harfbuzz.pc
+++ b/gfx/harfbuzz/src/harfbuzz.pc
@@ -1,13 +1,13 @@
 prefix=/usr/local
 exec_prefix=/usr/local
 libdir=/usr/local/lib
 includedir=/usr/local/include
 
 Name: harfbuzz
 Description: HarfBuzz text shaping library
-Version: 1.7.5
+Version: 1.7.6
 
 Libs: -L${libdir} -lharfbuzz
 Libs.private: -lm    
 Requires.private: glib-2.0 >= 2.19.1 
 Cflags: -I${includedir}/harfbuzz
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-aat-layout-ankr-table.hh
@@ -0,0 +1,80 @@
+/*
+ * 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_LAYOUT_ANKR_TABLE_HH
+#define HB_AAT_LAYOUT_ANKR_TABLE_HH
+
+#include "hb-aat-layout-common-private.hh"
+
+#define HB_AAT_TAG_ankr HB_TAG('a','n','k','r')
+
+
+namespace AAT {
+
+
+/*
+ * ankr -- Anchor point
+ */
+
+struct Anchor
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  FWORD		xCoordinate;
+  FWORD		yCoordinate;
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct ankr
+{
+  static const hb_tag_t tableTag = HB_AAT_TAG_ankr;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && version == 0 &&
+		  lookupTable.sanitize (c, this) &&
+		  anchors.sanitize (c, this));
+  }
+
+  protected:
+  HBUINT16			version; 	/* Version number (set to zero) */
+  HBUINT16			flags;		/* Flags (currently unused; set to zero) */
+  LOffsetTo<Lookup<HBUINT16> >	lookupTable;	/* Offset to the table's lookup table */
+  LOffsetTo<ArrayOf<Anchor, HBUINT32> >
+				anchors;	/* Offset to the glyph data table */
+
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_ANKR_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-aat-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-aat-layout-common-private.hh
@@ -115,17 +115,17 @@ struct BinSearchArrayOf
       int c = p->cmp (key);
       if (c < 0)
 	max = mid - 1;
       else if (c > 0)
 	min = mid + 1;
       else
 	return p;
     }
-    return NULL;
+    return nullptr;
   }
 
   private:
   inline bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (header.sanitize (c) &&
 		  Type::static_size >= header.unitSize &&
@@ -558,30 +558,30 @@ struct StateTable
 
     unsigned int num_states = 1;
     unsigned int num_entries = 0;
 
     unsigned int state = 0;
     unsigned int entry = 0;
     while (state < num_states)
     {
-      if (unlikely (!c->check_array (states + state * nClasses,
-				     states[0].static_size,
-				     nClasses * (num_states - state))))
+      if (unlikely (!c->check_array (states,
+				     states[0].static_size * nClasses,
+				     num_states)))
 	return_trace (false);
       { /* Sweep new states. */
 	const HBUINT16 *stop = &states[num_states * nClasses];
 	for (const HBUINT16 *p = &states[state * nClasses]; p < stop; p++)
 	  num_entries = MAX<unsigned int> (num_entries, *p + 1);
 	state = num_states;
       }
 
-      if (unlikely (!c->check_array (entries + entry,
+      if (unlikely (!c->check_array (entries,
 				     entries[0].static_size,
-				     num_entries - entry)))
+				     num_entries)))
 	return_trace (false);
       { /* Sweep new entries. */
 	const Entry<Extra> *stop = &entries[num_entries];
 	for (const Entry<Extra> *p = &entries[entry]; p < stop; p++)
 	  num_states = MAX<unsigned int> (num_states, p->newState + 1);
 	entry = num_entries;
       }
     }
@@ -609,45 +609,60 @@ struct StateTable
 template <typename EntryData>
 struct StateTableDriver
 {
   inline StateTableDriver (const StateTable<EntryData> &machine_,
 			   hb_buffer_t *buffer_,
 			   hb_face_t *face_) :
 	      machine (machine_),
 	      buffer (buffer_),
-	      num_glyphs (face_->get_num_glyphs ()),
-	      last_zero (0) {}
+	      num_glyphs (face_->get_num_glyphs ()) {}
 
   template <typename context_t>
   inline void drive (context_t *c)
   {
     hb_glyph_info_t *info = buffer->info;
 
     if (!c->in_place)
       buffer->clear_output ();
 
     unsigned int state = 0;
     bool last_was_dont_advance = false;
     for (buffer->idx = 0;;)
     {
-      if (!state)
-	last_zero = buffer->idx;
-
       unsigned int klass = buffer->idx < buffer->len ?
 			   machine.get_class (info[buffer->idx].codepoint, num_glyphs) :
 			   0 /* End of text */;
       const Entry<EntryData> *entry = machine.get_entryZ (state, klass);
       if (unlikely (!entry))
 	break;
 
+      /* Unsafe-to-break before this if not in state 0, as things might
+       * go differently if we start from state 0 here. */
+      if (state && buffer->idx)
+      {
+	/* If there's no action and we're just epsilon-transitioning to state 0,
+	 * safe to break. */
+	if (c->is_actionable (this, entry) ||
+	    !(entry->newState == 0 && entry->flags == context_t::DontAdvance))
+	  buffer->unsafe_to_break (buffer->idx - 1, buffer->idx + 1);
+      }
+
+      /* Unsafe-to-break if end-of-text would kick in here. */
+      if (buffer->idx + 2 <= buffer->len)
+      {
+	const Entry<EntryData> *end_entry = machine.get_entryZ (state, 0);
+	if (c->is_actionable (this, end_entry))
+	  buffer->unsafe_to_break (buffer->idx, buffer->idx + 2);
+      }
+
       if (unlikely (!c->transition (this, entry)))
         break;
 
-      last_was_dont_advance = (entry->flags & context_t::DontAdvance) && buffer->max_ops--;
+      last_was_dont_advance = (entry->flags & context_t::DontAdvance) && buffer->max_ops-- > 0;
 
       state = entry->newState;
 
       if (buffer->idx == buffer->len)
         break;
 
       if (!last_was_dont_advance)
         buffer->next_glyph ();
@@ -660,17 +675,16 @@ struct StateTableDriver
       buffer->swap_buffers ();
     }
   }
 
   public:
   const StateTable<EntryData> &machine;
   hb_buffer_t *buffer;
   unsigned int num_glyphs;
-  unsigned int last_zero;
 };
 
 
 
 struct hb_aat_apply_context_t :
        hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
 {
   inline const char *get_name (void) { return "APPLY"; }
@@ -679,27 +693,33 @@ struct hb_aat_apply_context_t :
   static return_t default_return_value (void) { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
 
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t *buffer;
   hb_sanitize_context_t sanitizer;
 
+  /* Unused. For debug tracing only. */
+  unsigned int lookup_index;
+  unsigned int debug_depth;
+
   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 ()
+		sanitizer (), lookup_index (0), debug_depth (0)
   {
     sanitizer.init (table);
     sanitizer.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 ();
   }
 };
 
 
 } /* namespace AAT */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-aat-layout-kerx-table.hh
@@ -0,0 +1,339 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ * 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_AAT_LAYOUT_KERX_TABLE_HH
+#define HB_AAT_LAYOUT_KERX_TABLE_HH
+
+#include "hb-open-type-private.hh"
+#include "hb-aat-layout-common-private.hh"
+
+#define HB_AAT_TAG_KERX HB_TAG('k','e','r','x')
+
+
+namespace AAT {
+
+using namespace OT;
+
+
+struct KerxFormat0Records
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  GlyphID	left;
+  GlyphID	right;
+  FWORD		value;
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct KerxSubTableFormat0
+{
+  // TODO(ebraminio) Enable when we got suitable BinSearchArrayOf
+  // inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+  // {
+  //   hb_glyph_pair_t pair = {left, right};
+  //   int i = pairs.bsearch (pair);
+  //   if (i == -1)
+  //     return 0;
+  //   return pairs[i].get_kerning ();
+  // }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+      c->check_array (records, records[0].static_size, nPairs));
+  }
+
+  protected:
+  // TODO(ebraminio): A custom version of "BinSearchArrayOf<KerxPair> pairs;" is
+  // needed here to use HBUINT32 instead
+  HBUINT32 nPairs;	/* The number of kerning pairs in this subtable */
+  HBUINT32 searchRange; /* The largest power of two less than or equal to the value of nPairs,
+                         * multiplied by the size in bytes of an entry in the subtable. */
+  HBUINT32 entrySelector; /* This is calculated as log2 of the largest power of two less
+                           * than or equal to the value of nPairs. */
+  HBUINT32 rangeShift;	/* The value of nPairs minus the largest power of two less than or equal to nPairs. */
+  KerxFormat0Records records[VAR]; /* VAR=nPairs */
+  public:
+  DEFINE_SIZE_ARRAY (16, records);
+};
+
+struct KerxSubTableFormat1
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+      stateHeader.sanitize (c));
+  }
+
+  protected:
+  StateTable<HBUINT16>		stateHeader;
+  LOffsetTo<ArrayOf<HBUINT16> >	valueTable;
+  public:
+  DEFINE_SIZE_STATIC (20);
+};
+
+// TODO(ebraminio): Maybe this can be replaced with Lookup<HBUINT16>?
+struct KerxClassTable
+{
+  inline unsigned int get_class (hb_codepoint_t g) const { return classes[g - firstGlyph]; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (firstGlyph.sanitize (c) && classes.sanitize (c));
+  }
+
+  protected:
+  HBUINT16		firstGlyph;	/* First glyph in class range. */
+  ArrayOf<HBUINT16>	classes;	/* Glyph classes. */
+  public:
+  DEFINE_SIZE_ARRAY (4, classes);
+};
+
+struct KerxSubTableFormat2
+{
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
+  {
+    unsigned int l = (this+leftClassTable).get_class (left);
+    unsigned int r = (this+leftClassTable).get_class (left);
+    unsigned int offset = l * rowWidth + r * sizeof (FWORD);
+    const FWORD *arr = &(this+array);
+    if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end))
+      return 0;
+    const FWORD *v = &StructAtOffset<FWORD> (arr, offset);
+    if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end))
+      return 0;
+    return *v;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+      rowWidth.sanitize (c) &&
+		  leftClassTable.sanitize (c, this) &&
+		  rightClassTable.sanitize (c, this) &&
+		  array.sanitize (c, this));
+  }
+
+  protected:
+  HBUINT32	rowWidth;	/* The width, in bytes, of a row in the table. */
+  LOffsetTo<KerxClassTable>
+		leftClassTable;	/* Offset from beginning of this subtable to
+				 * left-hand class table. */
+  LOffsetTo<KerxClassTable>
+		rightClassTable;/* Offset from beginning of this subtable to
+				 * right-hand class table. */
+  LOffsetTo<FWORD>
+		array;		/* Offset from beginning of this subtable to
+				 * the start of the kerning array. */
+  public:
+  DEFINE_SIZE_STATIC (16);
+};
+
+struct KerxSubTableFormat4
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+      rowWidth.sanitize (c) &&
+		  leftClassTable.sanitize (c, this) &&
+		  rightClassTable.sanitize (c, this) &&
+		  array.sanitize (c, this));
+  }
+
+  protected:
+  HBUINT32	rowWidth;	/* The width, in bytes, of a row in the table. */
+  LOffsetTo<KerxClassTable>
+		leftClassTable;	/* Offset from beginning of this subtable to
+				 * left-hand class table. */
+  LOffsetTo<KerxClassTable>
+		rightClassTable;/* Offset from beginning of this subtable to
+				 * right-hand class table. */
+  LOffsetTo<FWORD>
+		array;		/* Offset from beginning of this subtable to
+				 * the start of the kerning array. */
+  public:
+  DEFINE_SIZE_STATIC (16);
+};
+
+struct KerxSubTableFormat6
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+      rowIndexTable.sanitize (c, this) &&
+      columnIndexTable.sanitize (c, this) &&
+      kerningArray.sanitize (c, this) &&
+      kerningVector.sanitize (c, this));
+  }
+
+  protected:
+  HBUINT32	flags;
+  HBUINT16	rowCount;
+  HBUINT16	columnCount;
+  LOffsetTo<Lookup<HBUINT16> >	rowIndexTable;
+  LOffsetTo<Lookup<HBUINT16> >	columnIndexTable;
+  LOffsetTo<Lookup<HBUINT16> >	kerningArray;
+  LOffsetTo<Lookup<HBUINT16> >	kerningVector;
+  public:
+  DEFINE_SIZE_STATIC (24);
+};
+
+enum coverage_flags_t
+{
+  COVERAGE_VERTICAL_FLAG	= 0x80u,
+  COVERAGE_CROSSSTREAM_FLAG	= 0x40u,
+  COVERAGE_VARIATION_FLAG	= 0x20u,
+  COVERAGE_PROCESS_DIRECTION	= 0x10u,
+};
+
+struct KerxTable
+{
+  inline bool apply (hb_aat_apply_context_t *c, const AAT::ankr *ankr) const
+  {
+    TRACE_APPLY (this);
+    /* TODO */
+    return_trace (false);
+  }
+
+  inline unsigned int get_size (void) const { return length; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!c->check_struct (this))
+      return_trace (false);
+
+    switch (format) {
+    case 0: return u.format0.sanitize (c);
+    case 1: return u.format1.sanitize (c);
+    case 2: return u.format2.sanitize (c);
+    case 4: return u.format4.sanitize (c);
+    case 6: return u.format6.sanitize (c);
+    default:return_trace (false);
+    }
+  }
+
+protected:
+  HBUINT32	length;
+  HBUINT8	coverage;
+  HBUINT16	unused;
+  HBUINT8	format;
+  HBUINT32	tupleIndex;
+  union {
+  KerxSubTableFormat0	format0;
+  KerxSubTableFormat1	format1;
+  KerxSubTableFormat2	format2;
+  KerxSubTableFormat4	format4;
+  KerxSubTableFormat6	format6;
+  } u;
+public:
+  DEFINE_SIZE_MIN (12);
+};
+
+struct SubtableGlyphCoverageArray
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  HBUINT32	length;
+  HBUINT32	coverage;
+  HBUINT32	tupleCount;
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+struct kerx
+{
+  static const hb_tag_t tableTag = HB_AAT_TAG_KERX;
+
+  inline bool apply (hb_aat_apply_context_t *c, const AAT::ankr *ankr) const
+  {
+    TRACE_APPLY (this);
+    const KerxTable &table = StructAfter<KerxTable> (*this);
+    return_trace (table.apply (c, ankr));
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!(c->check_struct (this)))
+     return_trace (false);
+
+    /* TODO: Something like `morx`s ChainSubtable should be done here instead */
+    const KerxTable *table = &StructAfter<KerxTable> (*this);
+    if (!(table->sanitize (c)))
+      return_trace (false);
+
+    for (unsigned int i = 0; i < nTables - 1; ++i)
+    {
+      table = &StructAfter<KerxTable> (*table);
+      if (!(table->sanitize (c)))
+        return_trace (false);
+    }
+
+    // If version is less than 3, we are done here; otherwise better to check footer also
+    if (version < 3)
+      return_trace (true);
+
+    // TODO: Investigate why this just work on some fonts no matter of version
+    // const SubtableGlyphCoverageArray &footer =
+    //   StructAfter<SubtableGlyphCoverageArray> (*table);
+    // return_trace (footer.sanitize (c));
+
+    return_trace (true);
+  }
+
+  protected:
+  HBUINT16		version;
+  HBUINT16		padding;
+  HBUINT32		nTables;
+/*KerxTable tables[VAR];*/
+/*SubtableGlyphCoverageArray coverage_array;*/
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-aat-layout-morx-table.hh
+++ b/gfx/harfbuzz/src/hb-aat-layout-morx-table.hh
@@ -35,16 +35,18 @@
 
 namespace AAT {
 
 using namespace OT;
 
 
 struct RearrangementSubtable
 {
+  typedef void EntryData;
+
   struct driver_context_t
   {
     static const bool in_place = true;
     enum Flags {
       MarkFirst		= 0x8000,	/* If set, make the current glyph the first
 					 * glyph to be rearranged. */
       DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph
 					 * before going to the new state. This means
@@ -53,30 +55,31 @@ struct RearrangementSubtable
       MarkLast		= 0x2000,	/* If set, make the current glyph the last
 					 * glyph to be rearranged. */
       Reserved		= 0x1FF0,	/* These bits are reserved and should be set to 0. */
       Verb		= 0x000F,	/* The type of rearrangement specified. */
     };
 
     inline driver_context_t (const RearrangementSubtable *table) :
 	ret (false),
-	start (0), end (0),
-	last_zero_before_start (0) {}
+	start (0), end (0) {}
 
-    inline bool transition (StateTableDriver<void> *driver,
-			    const Entry<void> *entry)
+    inline bool is_actionable (StateTableDriver<EntryData> *driver,
+			       const Entry<EntryData> *entry)
+    {
+      return (entry->flags & Verb) && start < end;
+    }
+    inline bool transition (StateTableDriver<EntryData> *driver,
+			    const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
       unsigned int flags = entry->flags;
 
       if (flags & MarkFirst)
-      {
 	start = buffer->idx;
-	last_zero_before_start = driver->last_zero;
-      }
 
       if (flags & MarkLast)
 	end = MIN (buffer->idx + 1, buffer->len);
 
       if ((flags & Verb) && start < end)
       {
 	/* The following map has two nibbles, for start-side
 	 * and end-side. Values of 0,1,2 mean move that many
@@ -105,17 +108,17 @@ struct RearrangementSubtable
 	unsigned int m = map[flags & Verb];
 	unsigned int l = MIN<unsigned int> (2, m >> 4);
 	unsigned int r = MIN<unsigned int> (2, m & 0x0F);
 	bool reverse_l = 3 == (m >> 4);
 	bool reverse_r = 3 == (m & 0x0F);
 
 	if (end - start >= l + r)
 	{
-	  buffer->unsafe_to_break (last_zero_before_start, MIN (buffer->idx + 1, buffer->len));
+	  buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len));
 	  buffer->merge_clusters (start, end);
 
 	  hb_glyph_info_t *info = buffer->info;
 	  hb_glyph_info_t buf[4];
 
 	  memcpy (buf, info + start, l * sizeof (buf[0]));
 	  memcpy (buf + 2, info + end - r, r * sizeof (buf[0]));
 
@@ -142,17 +145,16 @@ struct RearrangementSubtable
       return true;
     }
 
     public:
     bool ret;
     private:
     unsigned int start;
     unsigned int end;
-    unsigned int last_zero_before_start;
   };
 
   inline bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
     driver_context_t dc (this);
 
@@ -164,17 +166,17 @@ struct RearrangementSubtable
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (machine.sanitize (c));
   }
 
   protected:
-  StateTable<void>	machine;
+  StateTable<EntryData>	machine;
   public:
   DEFINE_SIZE_STATIC (16);
 };
 
 struct ContextualSubtable
 {
   struct EntryData
   {
@@ -193,64 +195,79 @@ struct ContextualSubtable
       SetMark		= 0x8000,	/* If set, make the current glyph the marked glyph. */
       DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
 					 * going to the new state. */
       Reserved		= 0x3FFF,	/* These bits are reserved and should be set to 0. */
     };
 
     inline driver_context_t (const ContextualSubtable *table) :
 	ret (false),
+	mark_set (false),
 	mark (0),
-	last_zero_before_mark (0),
 	subs (table+table->substitutionTables) {}
 
+    inline bool is_actionable (StateTableDriver<EntryData> *driver,
+			       const Entry<EntryData> *entry)
+    {
+      hb_buffer_t *buffer = driver->buffer;
+
+      if (buffer->idx == buffer->len && !mark_set)
+        return false;
+
+      return entry->data.markIndex != 0xFFFF || entry->data.currentIndex != 0xFFFF;
+    }
     inline bool transition (StateTableDriver<EntryData> *driver,
 			    const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
 
-      if (entry->flags & SetMark)
-      {
-	mark = buffer->idx;
-	last_zero_before_mark = driver->last_zero;
-      }
+      /* Looks like CoreText applies neither mark nor current substitution for
+       * end-of-text if mark was not explicitly set. */
+      if (buffer->idx == buffer->len && !mark_set)
+        return true;
 
       if (entry->data.markIndex != 0xFFFF)
       {
 	const Lookup<GlyphID> &lookup = subs[entry->data.markIndex];
 	hb_glyph_info_t *info = buffer->info;
 	const GlyphID *replacement = lookup.get_value (info[mark].codepoint, driver->num_glyphs);
 	if (replacement)
 	{
-	  buffer->unsafe_to_break (last_zero_before_mark, MIN (buffer->idx + 1, buffer->len));
+	  buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
 	  info[mark].codepoint = *replacement;
 	  ret = true;
 	}
       }
       if (entry->data.currentIndex != 0xFFFF)
       {
+        unsigned int idx = MIN (buffer->idx, buffer->len - 1);
 	const Lookup<GlyphID> &lookup = subs[entry->data.currentIndex];
 	hb_glyph_info_t *info = buffer->info;
-	const GlyphID *replacement = lookup.get_value (info[buffer->idx].codepoint, driver->num_glyphs);
+	const GlyphID *replacement = lookup.get_value (info[idx].codepoint, driver->num_glyphs);
 	if (replacement)
 	{
-	  buffer->unsafe_to_break (driver->last_zero, MIN (buffer->idx + 1, buffer->len));
-	  info[buffer->idx].codepoint = *replacement;
+	  info[idx].codepoint = *replacement;
 	  ret = true;
 	}
       }
 
+      if (entry->flags & SetMark)
+      {
+	mark_set = true;
+	mark = buffer->idx;
+      }
+
       return true;
     }
 
     public:
     bool ret;
     private:
+    bool mark_set;
     unsigned int mark;
-    unsigned int last_zero_before_mark;
     const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> &subs;
   };
 
   inline bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
     driver_context_t dc (this);
@@ -260,28 +277,30 @@ struct ContextualSubtable
 
     return_trace (dc.ret);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
 
-    unsigned int num_entries;
-    if (unlikely (!machine.sanitize (c, &num_entries))) return false;
+    unsigned int num_entries = 0;
+    if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false);
 
     unsigned int num_lookups = 0;
 
     const Entry<EntryData> *entries = machine.get_entries ();
     for (unsigned int i = 0; i < num_entries; i++)
     {
       const EntryData &data = entries[i].data;
 
-      num_lookups = MAX<unsigned int> (num_lookups, 1 + data.markIndex);
-      num_lookups = MAX<unsigned int> (num_lookups, 1 + data.currentIndex);
+      if (data.markIndex != 0xFFFF)
+	num_lookups = MAX<unsigned int> (num_lookups, 1 + data.markIndex);
+      if (data.currentIndex != 0xFFFF)
+	num_lookups = MAX<unsigned int> (num_lookups, 1 + data.currentIndex);
     }
 
     return_trace (substitutionTables.sanitize (c, this, num_lookups));
   }
 
   protected:
   StateTable<EntryData>	machine;
   OffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32>, HBUINT32>
@@ -328,16 +347,21 @@ struct LigatureSubtable
 			     hb_aat_apply_context_t *c_) :
 	ret (false),
 	c (c_),
 	ligAction (table+table->ligAction),
 	component (table+table->component),
 	ligature (table+table->ligature),
 	match_length (0) {}
 
+    inline bool is_actionable (StateTableDriver<EntryData> *driver,
+			       const Entry<EntryData> *entry)
+    {
+      return !!(entry->flags & PerformAction);
+    }
     inline bool transition (StateTableDriver<EntryData> *driver,
 			    const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
       unsigned int flags = entry->flags;
 
       if (flags & SetComponent)
       {
@@ -427,19 +451,18 @@ struct LigatureSubtable
 
     return_trace (dc.ret);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* The rest of array sanitizations are done at run-time. */
-    return c->check_struct (this) && machine.sanitize (c) &&
-	   ligAction && component && ligature;
-    return_trace (true);
+    return_trace (c->check_struct (this) && machine.sanitize (c) &&
+		  ligAction && component && ligature);
   }
 
   protected:
   StateTable<EntryData>	machine;
   OffsetTo<UnsizedArrayOf<HBUINT32>, HBUINT32>
 		ligAction;	/* Offset to the ligature action table. */
   OffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT32>
 		component;	/* Offset to the component table. */
@@ -588,18 +611,28 @@ struct ChainSubtable
 struct Chain
 {
   inline void apply (hb_aat_apply_context_t *c) const
   {
     const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
     unsigned int count = subtableCount;
     for (unsigned int i = 0; i < count; i++)
     {
+      if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
+      {
+	c->set_lookup_index (c->lookup_index + 1);
+	continue;
+      }
+
       subtable->apply (c);
       subtable = &StructAfter<ChainSubtable> (*subtable);
+
+      (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);
+
+      c->set_lookup_index (c->lookup_index + 1);
     }
   }
 
   inline unsigned int get_size (void) const { return length; }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int major) const
   {
     TRACE_SANITIZE (this);
@@ -643,16 +676,17 @@ struct Chain
  */
 
 struct morx
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_MORX;
 
   inline void apply (hb_aat_apply_context_t *c) const
   {
+    c->set_lookup_index (0);
     const Chain *chain = chains;
     unsigned int count = chainCount;
     for (unsigned int i = 0; i < count; i++)
     {
       chain->apply (c);
       chain = &StructAfter<Chain> (*chain);
     }
   }
--- a/gfx/harfbuzz/src/hb-aat-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-aat-layout-private.hh
@@ -32,9 +32,12 @@
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 #include "hb-open-type-private.hh"
 
 
 HB_INTERNAL void
 hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer);
 
+HB_INTERNAL void
+hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer);
+
 #endif /* HB_AAT_LAYOUT_PRIVATE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh
@@ -0,0 +1,201 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ * 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_AAT_LAYOUT_TRAK_TABLE_HH
+#define HB_AAT_LAYOUT_TRAK_TABLE_HH
+
+#include "hb-aat-layout-common-private.hh"
+#include "hb-open-type-private.hh"
+
+#define HB_AAT_TAG_trak HB_TAG('t','r','a','k')
+
+
+namespace AAT {
+
+
+struct TrackTableEntry
+{
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base, unsigned int size) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && (values.sanitize (c, base, size)));
+  }
+
+  inline float get_track_value () const
+  {
+    return track.to_float ();
+  }
+
+  inline int get_value (const void *base, unsigned int index) const
+  {
+    return (base+values)[index];
+  }
+
+  protected:
+  Fixed			track;		/* Track value for this record. */
+  HBUINT16		trackNameID;	/* The 'name' table index for this track */
+  OffsetTo<UnsizedArrayOf<FWORD> >
+			values;		/* Offset from start of tracking table to
+					 * per-size tracking values for this track. */
+
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct TrackData
+{
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  sizeTable.sanitize (c, base, nSizes) &&
+		  trackTable.sanitize (c, nTracks, base, nSizes));
+  }
+
+  inline float get_tracking (const void *base, float ptem) const
+  {
+    /* CoreText points are CSS pixels (96 per inch),
+     * NOT typographic points (72 per inch).
+     *
+     * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
+     */
+    float csspx = ptem * 96.f / 72.f;
+    Fixed fixed_size;
+    fixed_size.set_float (csspx);
+
+    /* XXX Clean this up. Make it work with nSizes==1 and 0. */
+
+    unsigned int sizes = nSizes;
+
+    const TrackTableEntry *trackTableEntry = nullptr;
+    for (unsigned int i = 0; i < sizes; ++i)
+      // For now we only seek for track entries with zero tracking value
+      if (trackTable[i].get_track_value () == 0.)
+        trackTableEntry = &trackTable[0];
+
+    // We couldn't match any, exit
+    if (!trackTableEntry) return 0.;
+
+    /* TODO bfind() */
+    unsigned int size_index;
+    UnsizedArrayOf<Fixed> size_table = base+sizeTable;
+    for (size_index = 0; size_index < sizes; ++size_index)
+      if (size_table[size_index] >= fixed_size)
+        break;
+
+    // TODO(ebraminio): We don't attempt to extrapolate to larger or
+    // smaller values for now but we should do, per spec
+    if (size_index == sizes)
+      return trackTableEntry->get_value (base, sizes - 1);
+    if (size_index == 0 || size_table[size_index] == fixed_size)
+      return trackTableEntry->get_value (base, size_index);
+
+    float s0 = size_table[size_index - 1].to_float ();
+    float s1 = size_table[size_index].to_float ();
+    float t = (csspx - s0) / (s1 - s0);
+    return t * trackTableEntry->get_value (base, size_index) +
+      (1.0 - t) * trackTableEntry->get_value (base, size_index - 1);
+  }
+
+  protected:
+  HBUINT16		nTracks;	/* Number of separate tracks included in this table. */
+  HBUINT16		nSizes;		/* Number of point sizes included in this table. */
+  LOffsetTo<UnsizedArrayOf<Fixed> >	/* Offset to array[nSizes] of size values. */
+			sizeTable;
+  UnsizedArrayOf<TrackTableEntry>
+			trackTable;	/* Array[nTracks] of TrackTableEntry records. */
+
+  public:
+  DEFINE_SIZE_ARRAY (8, trackTable);
+};
+
+struct trak
+{
+  static const hb_tag_t tableTag = HB_AAT_TAG_trak;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+
+    return_trace (c->check_struct (this) &&
+		  horizData.sanitize (c, this, this) &&
+		  vertData.sanitize (c, this, this));
+  }
+
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    const float ptem = c->font->ptem;
+    if (ptem <= 0.f)
+      return_trace (false);
+
+    hb_buffer_t *buffer = c->buffer;
+    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
+    {
+      const TrackData &trackData = this+horizData;
+      float tracking = trackData.get_tracking (this, ptem);
+      hb_position_t advance_to_add = c->font->em_scalef_x (tracking / 2);
+      foreach_grapheme (buffer, start, end)
+      {
+	/* TODO This is wrong. */
+	buffer->pos[start].x_advance += advance_to_add;
+	buffer->pos[end].x_advance += advance_to_add;
+      }
+    }
+    else
+    {
+      const TrackData &trackData = this+vertData;
+      float tracking = trackData.get_tracking (this, ptem);
+      hb_position_t advance_to_add = c->font->em_scalef_y (tracking / 2);
+      foreach_grapheme (buffer, start, end)
+      {
+	/* TODO This is wrong. */
+	buffer->pos[start].y_advance += advance_to_add;
+	buffer->pos[end].y_advance += advance_to_add;
+      }
+    }
+
+    return_trace (true);
+  }
+
+  protected:
+  FixedVersion<>	version;	/* Version of the tracking table--currently
+					 * 0x00010000u for version 1.0. */
+  HBUINT16		format; 	/* Format of the tracking table */
+  OffsetTo<TrackData>	horizData;	/* TrackData for horizontal text */
+  OffsetTo<TrackData>	vertData;	/* TrackData for vertical text */
+  HBUINT16		reserved;	/* Reserved. Set to 0. */
+
+  public:
+  DEFINE_SIZE_MIN (12);
+};
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_TRAK_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-aat-layout.cc
+++ b/gfx/harfbuzz/src/hb-aat-layout.cc
@@ -25,22 +25,58 @@
  */
 
 #include "hb-open-type-private.hh"
 
 #include "hb-ot-layout-private.hh"
 #include "hb-ot-layout-gsubgpos-private.hh"
 
 #include "hb-aat-layout-private.hh"
+#include "hb-aat-layout-ankr-table.hh"
+#include "hb-aat-layout-kerx-table.hh"
 #include "hb-aat-layout-morx-table.hh"
+#include "hb-aat-layout-trak-table.hh"
 
 /*
- * mort/morx
+ * morx/kerx/trak
  */
 
+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 OT::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 OT::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 OT::Null(AAT::morx);
@@ -48,31 +84,60 @@ static inline const 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 ());
   if (blob)
     *blob = layout->morx.blob;
   return morx;
 }
 
-static inline void
-_hb_aat_layout_create (hb_face_t *face)
+static inline const AAT::trak&
+_get_trak (hb_face_t *face, hb_blob_t **blob = nullptr)
 {
-  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));
-  OT::Sanitizer<AAT::morx>::lock_instance (morx_blob);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
+  {
+    if (blob)
+      *blob = hb_blob_get_empty ();
+    return OT::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;
+}
 
-  if (0)
-  {
-    OT::Sanitizer<AAT::Lookup<OT::GlyphID> >::lock_instance (morx_blob)->get_value (1, face->get_num_glyphs ());
-  }
-}
+// 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));
+//   OT::Sanitizer<AAT::morx>::lock_instance (morx_blob);
+
+//   if (0)
+//   {
+//     OT::Sanitizer<AAT::Lookup<OT::GlyphID> >::lock_instance (morx_blob)->get_value (1, face->get_num_glyphs ());
+//   }
+// }
 
 void
 hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer)
 {
   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);
 }
+
+void
+hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer)
+{
+  hb_blob_t *blob;
+  const AAT::ankr& ankr = _get_ankr (font->face, &blob);
+  const AAT::kerx& kerx = _get_kerx (font->face, &blob);
+  const AAT::trak& trak = _get_trak (font->face, &blob);
+
+  AAT::hb_aat_apply_context_t c (font, buffer, blob);
+  kerx.apply (&c, &ankr);
+  trak.apply (&c);
+}
--- a/gfx/harfbuzz/src/hb-blob.cc
+++ b/gfx/harfbuzz/src/hb-blob.cc
@@ -166,16 +166,41 @@ hb_blob_create_sub_blob (hb_blob_t    *p
 			 HB_MEMORY_MODE_READONLY,
 			 hb_blob_reference (parent),
 			 _hb_blob_destroy);
 
   return blob;
 }
 
 /**
+ * hb_blob_copy_writable_or_fail:
+ * @blob: A blob.
+ *
+ * Makes a writable copy of @blob.
+ *
+ * Return value: New blob, or nullptr if allocation failed.
+ *
+ * Since: 1.8.0
+ **/
+hb_blob_t *
+hb_blob_copy_writable_or_fail (hb_blob_t *blob)
+{
+  blob = hb_blob_create (blob->data,
+			 blob->length,
+			 HB_MEMORY_MODE_DUPLICATE,
+			 nullptr,
+			 nullptr);
+
+  if (unlikely (blob == hb_blob_get_empty ()))
+    blob = nullptr;
+
+  return blob;
+}
+
+/**
  * hb_blob_get_empty:
  *
  * Returns the singleton empty blob.
  *
  * See TODO:link object types for more information.
  *
  * Return value: (transfer full): the empty blob.
  *
@@ -217,17 +242,17 @@ hb_blob_reference (hb_blob_t *blob)
 {
   return hb_object_reference (blob);
 }
 
 /**
  * hb_blob_destroy: (skip)
  * @blob: a blob.
  *
- * Descreases the reference count on @blob, and if it reaches zero, destroys
+ * Decreases the reference count on @blob, and if it reaches zero, destroys
  * @blob, freeing all memory, possibly calling the destroy-callback the blob
  * was created for if it has not been called already.
  *
  * See TODO:link object types for more information.
  *
  * Since: 0.9.2
  **/
 void
--- a/gfx/harfbuzz/src/hb-blob.h
+++ b/gfx/harfbuzz/src/hb-blob.h
@@ -78,16 +78,19 @@ hb_blob_create (const char        *data,
  * shared among multiple sub-blobs.
  */
 HB_EXTERN hb_blob_t *
 hb_blob_create_sub_blob (hb_blob_t    *parent,
 			 unsigned int  offset,
 			 unsigned int  length);
 
 HB_EXTERN hb_blob_t *
+hb_blob_copy_writable_or_fail (hb_blob_t *blob);
+
+HB_EXTERN hb_blob_t *
 hb_blob_get_empty (void);
 
 HB_EXTERN hb_blob_t *
 hb_blob_reference (hb_blob_t *blob);
 
 HB_EXTERN void
 hb_blob_destroy (hb_blob_t *blob);
 
--- a/gfx/harfbuzz/src/hb-buffer-deserialize-json.hh
+++ b/gfx/harfbuzz/src/hb-buffer-deserialize-json.hh
@@ -29,407 +29,407 @@
 #ifndef HB_BUFFER_DESERIALIZE_JSON_HH
 #define HB_BUFFER_DESERIALIZE_JSON_HH
 
 #include "hb-private.hh"
 
 
 #line 36 "hb-buffer-deserialize-json.hh"
 static const unsigned char _deserialize_json_trans_keys[] = {
-	0u, 0u, 9u, 123u, 9u, 34u, 97u, 103u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 
-	48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 
-	9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 
-	120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 
-	9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 
+	0u, 0u, 9u, 123u, 9u, 34u, 97u, 103u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u,
+	48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u,
+	9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u,
+	120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u,
+	9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u,
 	65u, 122u, 34u, 122u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0
 };
 
 static const char _deserialize_json_key_spans[] = {
-	0, 115, 26, 7, 2, 1, 50, 49, 
-	10, 117, 117, 117, 1, 50, 49, 10, 
-	117, 117, 1, 1, 50, 49, 117, 117, 
-	2, 1, 50, 49, 10, 117, 117, 1, 
-	50, 49, 10, 117, 117, 1, 50, 49, 
+	0, 115, 26, 7, 2, 1, 50, 49,
+	10, 117, 117, 117, 1, 50, 49, 10,
+	117, 117, 1, 1, 50, 49, 117, 117,
+	2, 1, 50, 49, 10, 117, 117, 1,
+	50, 49, 10, 117, 117, 1, 50, 49,
 	58, 89, 117, 117, 85, 115, 0
 };
 
 static const short _deserialize_json_index_offsets[] = {
-	0, 0, 116, 143, 151, 154, 156, 207, 
-	257, 268, 386, 504, 622, 624, 675, 725, 
-	736, 854, 972, 974, 976, 1027, 1077, 1195, 
-	1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666, 
-	1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069, 
+	0, 0, 116, 143, 151, 154, 156, 207,
+	257, 268, 386, 504, 622, 624, 675, 725,
+	736, 854, 972, 974, 976, 1027, 1077, 1195,
+	1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666,
+	1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069,
 	2119, 2178, 2268, 2386, 2504, 2590, 2706
 };
 
 static const char _deserialize_json_indicies[] = {
-	0, 0, 0, 0, 0, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	0, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 2, 1, 3, 3, 3, 
-	3, 3, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 3, 1, 4, 1, 
-	5, 1, 6, 7, 1, 1, 8, 1, 
-	9, 10, 1, 11, 1, 11, 11, 11, 
-	11, 11, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 11, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 12, 1, 
-	12, 12, 12, 12, 12, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 12, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 13, 1, 1, 14, 
-	15, 15, 15, 15, 15, 15, 15, 15, 
-	15, 1, 16, 17, 17, 17, 17, 17, 
-	17, 17, 17, 17, 1, 18, 18, 18, 
-	18, 18, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 18, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	19, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 20, 1, 21, 21, 21, 21, 21, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 21, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 3, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 22, 
-	1, 18, 18, 18, 18, 18, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	18, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 19, 1, 1, 1, 
-	17, 17, 17, 17, 17, 17, 17, 17, 
-	17, 17, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 20, 1, 23, 
-	1, 23, 23, 23, 23, 23, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	23, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 24, 1, 24, 24, 24, 24, 
-	24, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 24, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	25, 1, 1, 26, 27, 27, 27, 27, 
-	27, 27, 27, 27, 27, 1, 28, 29, 
-	29, 29, 29, 29, 29, 29, 29, 29, 
-	1, 30, 30, 30, 30, 30, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	30, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 31, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 32, 1, 30, 
-	30, 30, 30, 30, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 30, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 31, 1, 1, 1, 29, 29, 
-	29, 29, 29, 29, 29, 29, 29, 29, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 32, 1, 33, 1, 34, 
-	1, 34, 34, 34, 34, 34, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	34, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 35, 1, 35, 35, 35, 35, 
-	35, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 35, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 36, 37, 37, 37, 37, 
-	37, 37, 37, 37, 37, 1, 38, 38, 
-	38, 38, 38, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 38, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 39, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 40, 1, 38, 38, 38, 38, 
-	38, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 38, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 39, 
-	1, 1, 1, 41, 41, 41, 41, 41, 
-	41, 41, 41, 41, 41, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	40, 1, 42, 43, 1, 44, 1, 44, 
-	44, 44, 44, 44, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 44, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	45, 1, 45, 45, 45, 45, 45, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 45, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 46, 1, 
-	1, 47, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 1, 49, 50, 50, 50, 
-	50, 50, 50, 50, 50, 50, 1, 51, 
-	51, 51, 51, 51, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 51, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 52, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 53, 1, 51, 51, 51, 
-	51, 51, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 51, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	52, 1, 1, 1, 50, 50, 50, 50, 
-	50, 50, 50, 50, 50, 50, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 53, 1, 54, 1, 54, 54, 54, 
-	54, 54, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 54, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 55, 1, 
-	55, 55, 55, 55, 55, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 55, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 56, 1, 1, 57, 
-	58, 58, 58, 58, 58, 58, 58, 58, 
-	58, 1, 59, 60, 60, 60, 60, 60, 
-	60, 60, 60, 60, 1, 61, 61, 61, 
-	61, 61, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 61, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	62, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 63, 1, 61, 61, 61, 61, 61, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 61, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 62, 1, 
-	1, 1, 60, 60, 60, 60, 60, 60, 
-	60, 60, 60, 60, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 63, 
-	1, 64, 1, 64, 64, 64, 64, 64, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 64, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 65, 1, 65, 65, 
-	65, 65, 65, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 65, 1, 66, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 67, 68, 68, 
-	68, 68, 68, 68, 68, 68, 68, 1, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 1, 1, 1, 1, 1, 1, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 1, 70, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 71, 71, 
-	1, 71, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 1, 1, 1, 1, 1, 
-	1, 1, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 1, 1, 1, 1, 
-	71, 1, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 1, 72, 72, 72, 
-	72, 72, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 72, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	73, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 74, 1, 72, 72, 72, 72, 72, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 72, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 73, 1, 
-	1, 1, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 75, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 74, 
-	1, 76, 76, 76, 76, 76, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	76, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 77, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 78, 1, 0, 
-	0, 0, 0, 0, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 0, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
+	0, 0, 0, 0, 0, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	0, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 2, 1, 3, 3, 3,
+	3, 3, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 3, 1, 4, 1,
+	5, 1, 6, 7, 1, 1, 8, 1,
+	9, 10, 1, 11, 1, 11, 11, 11,
+	11, 11, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 11, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 12, 1,
+	12, 12, 12, 12, 12, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 12,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 13, 1, 1, 14,
+	15, 15, 15, 15, 15, 15, 15, 15,
+	15, 1, 16, 17, 17, 17, 17, 17,
+	17, 17, 17, 17, 1, 18, 18, 18,
+	18, 18, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 18, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	19, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 20, 1, 21, 21, 21, 21, 21,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 21, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 3, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 22,
+	1, 18, 18, 18, 18, 18, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	18, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 19, 1, 1, 1,
+	17, 17, 17, 17, 17, 17, 17, 17,
+	17, 17, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 20, 1, 23,
+	1, 23, 23, 23, 23, 23, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	23, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 24, 1, 24, 24, 24, 24,
+	24, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 24, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	25, 1, 1, 26, 27, 27, 27, 27,
+	27, 27, 27, 27, 27, 1, 28, 29,
+	29, 29, 29, 29, 29, 29, 29, 29,
+	1, 30, 30, 30, 30, 30, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	30, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 31, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 32, 1, 30,
+	30, 30, 30, 30, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 30, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 31, 1, 1, 1, 29, 29,
+	29, 29, 29, 29, 29, 29, 29, 29,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 32, 1, 33, 1, 34,
+	1, 34, 34, 34, 34, 34, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	34, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 35, 1, 35, 35, 35, 35,
+	35, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 35, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 36, 37, 37, 37, 37,
+	37, 37, 37, 37, 37, 1, 38, 38,
+	38, 38, 38, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 38, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 39, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 40, 1, 38, 38, 38, 38,
+	38, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 38, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 39,
+	1, 1, 1, 41, 41, 41, 41, 41,
+	41, 41, 41, 41, 41, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	40, 1, 42, 43, 1, 44, 1, 44,
+	44, 44, 44, 44, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 44, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	45, 1, 45, 45, 45, 45, 45, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 45, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 46, 1,
+	1, 47, 48, 48, 48, 48, 48, 48,
+	48, 48, 48, 1, 49, 50, 50, 50,
+	50, 50, 50, 50, 50, 50, 1, 51,
+	51, 51, 51, 51, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 51, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 52, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 53, 1, 51, 51, 51,
+	51, 51, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 51, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	52, 1, 1, 1, 50, 50, 50, 50,
+	50, 50, 50, 50, 50, 50, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 53, 1, 54, 1, 54, 54, 54,
+	54, 54, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 54, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 55, 1,
+	55, 55, 55, 55, 55, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 55,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 56, 1, 1, 57,
+	58, 58, 58, 58, 58, 58, 58, 58,
+	58, 1, 59, 60, 60, 60, 60, 60,
+	60, 60, 60, 60, 1, 61, 61, 61,
+	61, 61, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 61, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	62, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 63, 1, 61, 61, 61, 61, 61,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 61, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 62, 1,
+	1, 1, 60, 60, 60, 60, 60, 60,
+	60, 60, 60, 60, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 63,
+	1, 64, 1, 64, 64, 64, 64, 64,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 64, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 65, 1, 65, 65,
+	65, 65, 65, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 65, 1, 66,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 67, 68, 68,
+	68, 68, 68, 68, 68, 68, 68, 1,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 1, 1, 1, 1, 1, 1,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 1, 70, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 71, 71,
+	1, 71, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 1, 1, 1, 1, 1,
+	1, 1, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 1, 1, 1, 1,
+	71, 1, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 1, 72, 72, 72,
+	72, 72, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 72, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	73, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 74, 1, 72, 72, 72, 72, 72,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 72, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 73, 1,
+	1, 1, 75, 75, 75, 75, 75, 75,
+	75, 75, 75, 75, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 74,
+	1, 76, 76, 76, 76, 76, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	76, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 77, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 78, 1, 0,
+	0, 0, 0, 0, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 0, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
 	1, 2, 1, 1, 0
 };
 
 static const char _deserialize_json_trans_targs[] = {
-	1, 0, 2, 2, 3, 4, 18, 24, 
-	37, 5, 12, 6, 7, 8, 9, 11, 
-	9, 11, 10, 2, 44, 10, 44, 13, 
-	14, 15, 16, 17, 16, 17, 10, 2, 
-	44, 19, 20, 21, 22, 23, 10, 2, 
-	44, 23, 25, 31, 26, 27, 28, 29, 
-	30, 29, 30, 10, 2, 44, 32, 33, 
-	34, 35, 36, 35, 36, 10, 2, 44, 
-	38, 39, 40, 42, 43, 41, 10, 41, 
+	1, 0, 2, 2, 3, 4, 18, 24,
+	37, 5, 12, 6, 7, 8, 9, 11,
+	9, 11, 10, 2, 44, 10, 44, 13,
+	14, 15, 16, 17, 16, 17, 10, 2,
+	44, 19, 20, 21, 22, 23, 10, 2,
+	44, 23, 25, 31, 26, 27, 28, 29,
+	30, 29, 30, 10, 2, 44, 32, 33,
+	34, 35, 36, 35, 36, 10, 2, 44,
+	38, 39, 40, 42, 43, 41, 10, 41,
 	10, 2, 44, 43, 44, 45, 46
 };
 
 static const char _deserialize_json_trans_actions[] = {
-	0, 0, 1, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 2, 2, 2, 
-	0, 0, 3, 3, 4, 0, 5, 0, 
-	0, 2, 2, 2, 0, 0, 6, 6, 
-	7, 0, 0, 0, 2, 2, 8, 8, 
-	9, 0, 0, 0, 0, 0, 2, 2, 
-	2, 0, 0, 10, 10, 11, 0, 0, 
-	2, 2, 2, 0, 0, 12, 12, 13, 
-	0, 0, 0, 2, 2, 2, 14, 0, 
+	0, 0, 1, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 2, 2, 2,
+	0, 0, 3, 3, 4, 0, 5, 0,
+	0, 2, 2, 2, 0, 0, 6, 6,
+	7, 0, 0, 0, 2, 2, 8, 8,
+	9, 0, 0, 0, 0, 0, 2, 2,
+	2, 0, 0, 10, 10, 11, 0, 0,
+	2, 2, 2, 0, 0, 12, 12, 13,
+	0, 0, 0, 2, 2, 2, 14, 0,
 	15, 15, 16, 0, 0, 0, 0
 };
 
 static const int deserialize_json_start = 1;
 static const int deserialize_json_first_final = 44;
 static const int deserialize_json_error = 0;
 
 static const int deserialize_json_en_main = 1;
@@ -443,30 +443,30 @@ static hb_bool_t
 				    const char *buf,
 				    unsigned int buf_len,
 				    const char **end_ptr,
 				    hb_font_t *font)
 {
   const char *p = buf, *pe = buf + buf_len;
 
   /* Ensure we have positions. */
-  (void) hb_buffer_get_glyph_positions (buffer, NULL);
+  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
 
   while (p < pe && ISSPACE (*p))
     p++;
   if (p < pe && *p == (buffer->len ? ',' : '['))
   {
     *end_ptr = ++p;
   }
 
-  const char *tok = NULL;
+  const char *tok = nullptr;
   int cs;
   hb_glyph_info_t info = {0};
   hb_glyph_position_t pos = {0};
-  
+
 #line 466 "hb-buffer-deserialize-json.hh"
 	{
 	cs = deserialize_json_start;
 	}
 
 #line 471 "hb-buffer-deserialize-json.hh"
 	{
 	int _slen;
--- a/gfx/harfbuzz/src/hb-buffer-deserialize-json.rl
+++ b/gfx/harfbuzz/src/hb-buffer-deserialize-json.rl
@@ -101,26 +101,26 @@ static hb_bool_t
 				    const char *buf,
 				    unsigned int buf_len,
 				    const char **end_ptr,
 				    hb_font_t *font)
 {
   const char *p = buf, *pe = buf + buf_len;
 
   /* Ensure we have positions. */
-  (void) hb_buffer_get_glyph_positions (buffer, NULL);
+  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
 
   while (p < pe && ISSPACE (*p))
     p++;
   if (p < pe && *p == (buffer->len ? ',' : '['))
   {
     *end_ptr = ++p;
   }
 
-  const char *tok = NULL;
+  const char *tok = nullptr;
   int cs;
   hb_glyph_info_t info = {0};
   hb_glyph_position_t pos = {0};
   %%{
     write init;
     write exec;
   }%%
 
--- a/gfx/harfbuzz/src/hb-buffer-deserialize-text.hh
+++ b/gfx/harfbuzz/src/hb-buffer-deserialize-text.hh
@@ -29,284 +29,284 @@
 #ifndef HB_BUFFER_DESERIALIZE_TEXT_HH
 #define HB_BUFFER_DESERIALIZE_TEXT_HH
 
 #include "hb-private.hh"
 
 
 #line 36 "hb-buffer-deserialize-text.hh"
 static const unsigned char _deserialize_text_trans_keys[] = {
-	0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, 
-	48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, 
-	9u, 122u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 
+	0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u,
+	48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u,
+	9u, 122u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u,
 	9u, 124u, 9u, 124u, 9u, 124u, 0
 };
 
 static const char _deserialize_text_key_spans[] = {
-	0, 114, 13, 10, 13, 10, 10, 13, 
-	10, 1, 13, 10, 14, 116, 116, 0, 
-	114, 116, 116, 116, 116, 116, 116, 116, 
+	0, 114, 13, 10, 13, 10, 10, 13,
+	10, 1, 13, 10, 14, 116, 116, 0,
+	114, 116, 116, 116, 116, 116, 116, 116,
 	116, 116, 116
 };
 
 static const short _deserialize_text_index_offsets[] = {
-	0, 0, 115, 129, 140, 154, 165, 176, 
-	190, 201, 203, 217, 228, 243, 360, 477, 
-	478, 593, 710, 827, 944, 1061, 1178, 1295, 
+	0, 0, 115, 129, 140, 154, 165, 176,
+	190, 201, 203, 217, 228, 243, 360, 477,
+	478, 593, 710, 827, 944, 1061, 1178, 1295,
 	1412, 1529, 1646
 };
 
 static const char _deserialize_text_indicies[] = {
-	0, 0, 0, 0, 0, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	0, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	2, 3, 3, 3, 3, 3, 3, 3, 
-	3, 3, 1, 1, 1, 1, 1, 1, 
-	1, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 1, 1, 1, 1, 1, 
-	1, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 1, 5, 1, 1, 6, 
-	7, 7, 7, 7, 7, 7, 7, 7, 
-	7, 1, 8, 9, 9, 9, 9, 9, 
-	9, 9, 9, 9, 1, 10, 1, 1, 
-	11, 12, 12, 12, 12, 12, 12, 12, 
-	12, 12, 1, 13, 14, 14, 14, 14, 
-	14, 14, 14, 14, 14, 1, 15, 16, 
-	16, 16, 16, 16, 16, 16, 16, 16, 
-	1, 17, 1, 1, 18, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 1, 20, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 1, 22, 1, 23, 1, 1, 24, 
-	25, 25, 25, 25, 25, 25, 25, 25, 
-	25, 1, 26, 27, 27, 27, 27, 27, 
-	27, 27, 27, 27, 1, 22, 1, 1, 
-	1, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 1, 28, 28, 28, 28, 
-	28, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 28, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 29, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	30, 1, 1, 31, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	32, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 33, 
-	1, 34, 34, 34, 34, 34, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	34, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 35, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 36, 1, 1, 0, 
-	0, 0, 0, 0, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 0, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 2, 3, 
-	3, 3, 3, 3, 3, 3, 3, 3, 
-	1, 1, 1, 1, 1, 1, 1, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 1, 1, 1, 1, 1, 1, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 1, 28, 28, 28, 28, 28, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 28, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 29, 1, 1, 1, 
-	1, 37, 37, 37, 37, 37, 37, 37, 
-	37, 37, 37, 1, 1, 1, 30, 1, 
-	1, 31, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 32, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 33, 1, 38, 
-	38, 38, 38, 38, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 38, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 39, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 40, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 41, 1, 42, 42, 42, 42, 
-	42, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 42, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	43, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 44, 
-	1, 42, 42, 42, 42, 42, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	42, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	14, 14, 14, 14, 14, 14, 14, 14, 
-	14, 14, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 43, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 44, 1, 38, 38, 
-	38, 38, 38, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 38, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 39, 1, 1, 1, 9, 9, 9, 
-	9, 9, 9, 9, 9, 9, 9, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 40, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 41, 1, 45, 45, 45, 45, 45, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 45, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 46, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 47, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 48, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 49, 1, 
-	50, 50, 50, 50, 50, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 50, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 51, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 52, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 53, 1, 50, 50, 50, 
-	50, 50, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 50, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 51, 
-	1, 1, 1, 1, 27, 27, 27, 27, 
-	27, 27, 27, 27, 27, 27, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 52, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	53, 1, 45, 45, 45, 45, 45, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 45, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 46, 1, 1, 1, 
-	1, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 1, 1, 1, 1, 1, 
-	1, 47, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 48, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 49, 1, 28, 
-	28, 28, 28, 28, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 28, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 29, 1, 55, 55, 1, 55, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	1, 1, 1, 30, 1, 1, 31, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 1, 1, 32, 1, 55, 1, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
+	0, 0, 0, 0, 0, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	0, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	2, 3, 3, 3, 3, 3, 3, 3,
+	3, 3, 1, 1, 1, 1, 1, 1,
+	1, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 1, 1, 1, 1, 1,
+	1, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 1, 5, 1, 1, 6,
+	7, 7, 7, 7, 7, 7, 7, 7,
+	7, 1, 8, 9, 9, 9, 9, 9,
+	9, 9, 9, 9, 1, 10, 1, 1,
+	11, 12, 12, 12, 12, 12, 12, 12,
+	12, 12, 1, 13, 14, 14, 14, 14,
+	14, 14, 14, 14, 14, 1, 15, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	1, 17, 1, 1, 18, 19, 19, 19,
+	19, 19, 19, 19, 19, 19, 1, 20,
+	21, 21, 21, 21, 21, 21, 21, 21,
+	21, 1, 22, 1, 23, 1, 1, 24,
+	25, 25, 25, 25, 25, 25, 25, 25,
+	25, 1, 26, 27, 27, 27, 27, 27,
+	27, 27, 27, 27, 1, 22, 1, 1,
+	1, 21, 21, 21, 21, 21, 21, 21,
+	21, 21, 21, 1, 28, 28, 28, 28,
+	28, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 28, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 29, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	30, 1, 1, 31, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	32, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 33,
+	1, 34, 34, 34, 34, 34, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	34, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 35, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 36, 1, 1, 0,
+	0, 0, 0, 0, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 0, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 2, 3,
+	3, 3, 3, 3, 3, 3, 3, 3,
+	1, 1, 1, 1, 1, 1, 1, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 1, 1, 1, 1, 1, 1, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 1, 28, 28, 28, 28, 28, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 28, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 29, 1, 1, 1,
+	1, 37, 37, 37, 37, 37, 37, 37,
+	37, 37, 37, 1, 1, 1, 30, 1,
+	1, 31, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 32, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 33, 1, 38,
+	38, 38, 38, 38, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 38, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 39, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 40, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 41, 1, 42, 42, 42, 42,
+	42, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 42, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	43, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 44,
+	1, 42, 42, 42, 42, 42, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	42, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	14, 14, 14, 14, 14, 14, 14, 14,
+	14, 14, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 43, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 44, 1, 38, 38,
+	38, 38, 38, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 38, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 39, 1, 1, 1, 9, 9, 9,
+	9, 9, 9, 9, 9, 9, 9, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 40, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 41, 1, 45, 45, 45, 45, 45,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 45, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 46, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 47, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 48,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 49, 1,
+	50, 50, 50, 50, 50, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 50,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 51, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 52, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 53, 1, 50, 50, 50,
+	50, 50, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 50, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 51,
+	1, 1, 1, 1, 27, 27, 27, 27,
+	27, 27, 27, 27, 27, 27, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 52, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	53, 1, 45, 45, 45, 45, 45, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 45, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 46, 1, 1, 1,
+	1, 54, 54, 54, 54, 54, 54, 54,
+	54, 54, 54, 1, 1, 1, 1, 1,
+	1, 47, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 48, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 49, 1, 28,
+	28, 28, 28, 28, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 28, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 29, 1, 55, 55, 1, 55, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	1, 1, 1, 30, 1, 1, 31, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	55, 1, 1, 32, 1, 55, 1, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
 	55, 1, 33, 1, 0
 };
 
 static const char _deserialize_text_trans_targs[] = {
-	1, 0, 13, 17, 26, 3, 18, 21, 
-	18, 21, 5, 19, 20, 19, 20, 22, 
-	25, 8, 9, 12, 9, 12, 10, 11, 
-	23, 24, 23, 24, 14, 2, 6, 7, 
-	15, 16, 14, 15, 16, 17, 14, 4, 
-	15, 16, 14, 15, 16, 14, 2, 7, 
+	1, 0, 13, 17, 26, 3, 18, 21,
+	18, 21, 5, 19, 20, 19, 20, 22,
+	25, 8, 9, 12, 9, 12, 10, 11,
+	23, 24, 23, 24, 14, 2, 6, 7,
+	15, 16, 14, 15, 16, 17, 14, 4,
+	15, 16, 14, 15, 16, 14, 2, 7,
 	15, 16, 14, 2, 15, 16, 25, 26
 };
 
 static const char _deserialize_text_trans_actions[] = {
-	0, 0, 1, 1, 1, 2, 2, 2, 
-	0, 0, 2, 2, 2, 0, 0, 2, 
-	2, 2, 2, 2, 0, 0, 3, 2, 
-	2, 2, 0, 0, 4, 5, 5, 5, 
-	4, 4, 0, 0, 0, 0, 6, 7, 
-	6, 6, 8, 8, 8, 9, 10, 10, 
+	0, 0, 1, 1, 1, 2, 2, 2,
+	0, 0, 2, 2, 2, 0, 0, 2,
+	2, 2, 2, 2, 0, 0, 3, 2,
+	2, 2, 0, 0, 4, 5, 5, 5,
+	4, 4, 0, 0, 0, 0, 6, 7,
+	6, 6, 8, 8, 8, 9, 10, 10,
 	9, 9, 11, 12, 11, 11, 0, 0
 };
 
 static const char _deserialize_text_eof_actions[] = {
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 4, 0, 0, 
-	0, 4, 6, 8, 8, 6, 9, 11, 
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 4, 0, 0,
+	0, 4, 6, 8, 8, 6, 9, 11,
 	11, 9, 4
 };
 
 static const int deserialize_text_start = 1;
 static const int deserialize_text_first_final = 13;
 static const int deserialize_text_error = 0;
 
 static const int deserialize_text_en_main = 1;
@@ -320,30 +320,30 @@ static hb_bool_t
 				    const char *buf,
 				    unsigned int buf_len,
 				    const char **end_ptr,
 				    hb_font_t *font)
 {
   const char *p = buf, *pe = buf + buf_len;
 
   /* Ensure we have positions. */
-  (void) hb_buffer_get_glyph_positions (buffer, NULL);
+  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
 
   while (p < pe && ISSPACE (*p))
     p++;
   if (p < pe && *p == (buffer->len ? '|' : '['))
   {
     *end_ptr = ++p;
   }
 
-  const char *eof = pe, *tok = NULL;
+  const char *eof = pe, *tok = nullptr;
   int cs;
   hb_glyph_info_t info = {0};
   hb_glyph_position_t pos = {0};
-  
+
 #line 343 "hb-buffer-deserialize-text.hh"
 	{
 	cs = deserialize_text_start;
 	}
 
 #line 348 "hb-buffer-deserialize-text.hh"
 	{
 	int _slen;
--- a/gfx/harfbuzz/src/hb-buffer-deserialize-text.rl
+++ b/gfx/harfbuzz/src/hb-buffer-deserialize-text.rl
@@ -95,26 +95,26 @@ static hb_bool_t
 				    const char *buf,
 				    unsigned int buf_len,
 				    const char **end_ptr,
 				    hb_font_t *font)
 {
   const char *p = buf, *pe = buf + buf_len;
 
   /* Ensure we have positions. */
-  (void) hb_buffer_get_glyph_positions (buffer, NULL);
+  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
 
   while (p < pe && ISSPACE (*p))
     p++;
   if (p < pe && *p == (buffer->len ? '|' : '['))
   {
     *end_ptr = ++p;
   }
 
-  const char *eof = pe, *tok = NULL;
+  const char *eof = pe, *tok = nullptr;
   int cs;
   hb_glyph_info_t info = {0};
   hb_glyph_position_t pos = {0};
   %%{
     write init;
     write exec;
   }%%
 
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -88,17 +88,17 @@ 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. */
-  hb_buffer_scratch_flags_t scratch_flags; /* Have space-flallback, etc. */
+  hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */
   unsigned int max_len; /* Maximum allowed len. */
   int max_ops; /* Maximum allowed operations. */
 
   /* Buffer contents */
   hb_buffer_content_type_t content_type;
   hb_segment_properties_t props; /* Script, language, direction */
 
   bool in_error; /* Allocation failed */
@@ -339,18 +339,17 @@ struct hb_buffer_t {
 	scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK;
 	infos[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
       }
   }
 
   inline void
   unsafe_to_break_all (void)
   {
-    for (unsigned int i = 0; i < len; i++)
-      info[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+    unsafe_to_break_impl (0, len);
   }
   inline void
   safe_to_break_all (void)
   {
     for (unsigned int i = 0; i < len; i++)
       info[i].mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
   }
 };
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -1928,17 +1928,17 @@ hb_buffer_diff (hb_buffer_t *buffer,
   const hb_glyph_info_t *buf_info = buffer->info;
   const hb_glyph_info_t *ref_info = reference->info;
   for (unsigned int i = 0; i < count; i++)
   {
     if (buf_info->codepoint != ref_info->codepoint)
       result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH;
     if (buf_info->cluster != ref_info->cluster)
       result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH;
-    if ((buf_info->mask & HB_GLYPH_FLAG_DEFINED) != (ref_info->mask & HB_GLYPH_FLAG_DEFINED))
+    if ((buf_info->mask & ~ref_info->mask & HB_GLYPH_FLAG_DEFINED))
       result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH;
     if (contains && ref_info->codepoint == dottedcircle_glyph)
       result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
     if (contains && ref_info->codepoint == 0)
       result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT;
     buf_info++;
     ref_info++;
   }
--- a/gfx/harfbuzz/src/hb-buffer.h
+++ b/gfx/harfbuzz/src/hb-buffer.h
@@ -66,16 +66,34 @@ typedef struct hb_glyph_info_t {
   hb_mask_t      mask; /* Holds hb_glyph_flags_t after hb_shape(), plus other things. */
   uint32_t       cluster;
 
   /*< private >*/
   hb_var_int_t   var1;
   hb_var_int_t   var2;
 } hb_glyph_info_t;
 
+/**
+ * hb_glyph_flags_t:
+ * @HB_GLYPH_FLAG_UNSAFE_TO_BREAK: Indicates that if input text is broken at the
+ * 				   beginning of the cluster this glyph is part of,
+ * 				   then both sides need to be re-shaped, as the
+ * 				   result might be different.  On the flip side,
+ * 				   it means that when this flag is not present,
+ * 				   then it's safe to break the glyph-run at the
+ * 				   beginning of this cluster, and the two sides
+ * 				   represent the exact same result one would get
+ * 				   if breaking input text at the beginning of
+ * 				   this cluster and shaping the two sides
+ * 				   separately.  This can be used to optimize
+ * 				   paragraph layout, by avoiding re-shaping
+ * 				   of each line after line-breaking, or limiting
+ * 				   the reshaping to a small piece around the
+ * 				   breaking point only.
+ */
 typedef enum { /*< flags >*/
   HB_GLYPH_FLAG_UNSAFE_TO_BREAK		= 0x00000001,
 
   HB_GLYPH_FLAG_DEFINED			= 0x00000001 /* OR of all defined flags */
 } hb_glyph_flags_t;
 
 HB_EXTERN hb_glyph_flags_t
 hb_glyph_info_get_glyph_flags (const hb_glyph_info_t *info);
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -874,17 +874,17 @@ parse_feature_indices (const char **pp, 
 static bool
 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
 {
   bool had_equal = parse_char (pp, end, '=');
   bool had_value = parse_uint32 (pp, end, &feature->value) ||
                    parse_bool (pp, end, &feature->value);
   /* CSS doesn't use equal-sign between tag and value.
    * If there was an equal-sign, then there *must* be a value.
-   * A value without an eqaul-sign is ok, but not required. */
+   * A value without an equal-sign is ok, but not required. */
   return !had_equal || had_value;
 }
 
 static bool
 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
 {
   return parse_feature_value_prefix (pp, end, feature) &&
 	 parse_tag (pp, end, &feature->tag) &&
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -69,17 +69,20 @@ reference_table  (hb_face_t *face HB_UNU
   CGFontRef cg_font = reinterpret_cast<CGFontRef> (user_data);
   CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag);
   if (unlikely (!cf_data))
     return nullptr;
 
   const char *data = reinterpret_cast<const char*> (CFDataGetBytePtr (cf_data));
   const size_t length = CFDataGetLength (cf_data);
   if (!data || !length)
+  {
+    CFRelease (cf_data);
     return nullptr;
+  }
 
   return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY,
 			 reinterpret_cast<void *> (const_cast<__CFData *> (cf_data)),
 			 release_table_data);
 }
 
 static void
 _hb_cg_font_release (void *data)
@@ -306,17 +309,17 @@ void
 }
 
 /*
  * Since: 1.7.2
  */
 hb_font_t *
 hb_coretext_font_create (CTFontRef ct_font)
 {
-  CGFontRef cg_font = CTFontCopyGraphicsFont (ct_font, 0);
+  CGFontRef cg_font = CTFontCopyGraphicsFont (ct_font, nullptr);
   hb_face_t *face = hb_coretext_face_create (cg_font);
   CFRelease (cg_font);
   hb_font_t *font = hb_font_create (face);
   hb_face_destroy (face);
 
   if (unlikely (hb_object_is_inert (font)))
     return font;
 
@@ -872,17 +875,20 @@ resize_and_retry:
 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
 #  define kCTLanguageAttributeName CFSTR ("NSLanguage")
 #endif
         CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault,
 							    hb_language_to_string (buffer->props.language),
 							    kCFStringEncodingUTF8,
 							    kCFAllocatorNull);
 	if (unlikely (!lang))
+        {
+	  CFRelease (attr_string);
 	  FAIL ("CFStringCreateWithCStringNoCopy failed");
+        }
 	CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
 					kCTLanguageAttributeName, lang);
 	CFRelease (lang);
       }
       CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
 				      kCTFontAttributeName, ct_font);
 
       if (num_features && range_records.len)
@@ -941,17 +947,20 @@ resize_and_retry:
       CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault,
 						    (const void **) &kCTTypesetterOptionForcedEmbeddingLevel,
 						    (const void **) &level_number,
 						    1,
 						    &kCFTypeDictionaryKeyCallBacks,
 						    &kCFTypeDictionaryValueCallBacks);
       CFRelease (level_number);
       if (unlikely (!options))
+      {
+        CFRelease (attr_string);
         FAIL ("CFDictionaryCreate failed");
+      }
 
       CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options);
       CFRelease (options);
       CFRelease (attr_string);
       if (unlikely (!typesetter))
 	FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed");
 
       line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0));
@@ -1031,17 +1040,17 @@ resize_and_retry:
 	for (unsigned int i = 0; i < range_records.len; i++)
 	  if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font))
 	  {
 	    matched = true;
 	    break;
 	  }
 	if (!matched)
 	{
-	  CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0);
+	  CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, nullptr);
 	  if (run_cg_font)
 	  {
 	    matched = CFEqual (run_cg_font, cg_font);
 	    CFRelease (run_cg_font);
 	  }
 	}
 	if (!matched)
 	{
@@ -1204,17 +1213,17 @@ resize_and_retry:
 #undef SCRATCH_SAVE
 #undef USE_PTR
 #undef ALLOCATE_ARRAY
 
       buffer->len += num_glyphs;
     }
 
     /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel,
-     * or if it does, it doesn't resepct it.  So we get runs with wrong
+     * or if it does, it doesn't respect it.  So we get runs with wrong
      * directions.  As such, disable the assert...  It wouldn't crash, but
      * cursoring will be off...
      *
      * http://crbug.com/419769
      */
     if (0)
     {
       /* Make sure all runs had the expected direction. */
@@ -1230,29 +1239,25 @@ resize_and_retry:
     hb_glyph_position_t *pos = buffer->pos;
     if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
       for (unsigned int i = 0; i < count; i++)
       {
 	pos->x_advance = info->mask;
 	pos->x_offset = info->var1.i32;
 	pos->y_offset = info->var2.i32;
 
-	info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
-
 	info++, pos++;
       }
     else
       for (unsigned int i = 0; i < count; i++)
       {
 	pos->y_advance = info->mask;
 	pos->x_offset = info->var1.i32;
 	pos->y_offset = info->var2.i32;
 
-	info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
-
 	info++, pos++;
       }
 
     /* Fix up clusters so that we never return out-of-order indices;
      * if core text has reordered glyphs, we'll merge them to the
      * beginning of the reordered cluster.  CoreText is nice enough
      * to tell us whenever it has produced nonmonotonic results...
      * Note that we assume the input clusters were nonmonotonic to
--- a/gfx/harfbuzz/src/hb-debug.hh
+++ b/gfx/harfbuzz/src/hb-debug.hh
@@ -391,16 +391,28 @@ struct hb_no_trace_t {
 #define TRACE_SERIALIZE(this) \
 	hb_auto_trace_t<HB_DEBUG_SERIALIZE, bool> trace \
 	(&c->debug_depth, "SERIALIZE", c, HB_FUNC, \
 	 " ");
 #else
 #define TRACE_SERIALIZE(this) hb_no_trace_t<bool> trace
 #endif
 
+#ifndef HB_DEBUG_SUBSET
+#define HB_DEBUG_SUBSET (HB_DEBUG+0)
+#endif
+#if HB_DEBUG_SUBSET
+#define TRACE_SUBSET(this) \
+  hb_auto_trace_t<HB_DEBUG_SUBSET, bool> trace \
+  (&c->debug_depth, c->get_name (), this, HB_FUNC, \
+   " ");
+#else
+#define TRACE_SUBSET(this) hb_no_trace_t<bool> trace
+#endif
+
 #ifndef HB_DEBUG_WOULD_APPLY
 #define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
 #endif
 #if HB_DEBUG_WOULD_APPLY
 #define TRACE_WOULD_APPLY(this) \
 	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY, bool> trace \
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
 	 "%d glyphs", c->len);
@@ -410,16 +422,17 @@ struct hb_no_trace_t {
 
 #ifndef HB_DEBUG_DISPATCH
 #define HB_DEBUG_DISPATCH ( \
 	HB_DEBUG_APPLY + \
 	HB_DEBUG_CLOSURE + \
 	HB_DEBUG_COLLECT_GLYPHS + \
 	HB_DEBUG_SANITIZE + \
 	HB_DEBUG_SERIALIZE + \
+  HB_DEBUG_SUBSET + \
 	HB_DEBUG_WOULD_APPLY + \
 	0)
 #endif
 #if HB_DEBUG_DISPATCH
 #define TRACE_DISPATCH(this, format) \
 	hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
 	 "format %d", (int) format);
--- a/gfx/harfbuzz/src/hb-directwrite.cc
+++ b/gfx/harfbuzz/src/hb-directwrite.cc
@@ -365,17 +365,17 @@ public:
   }
 
   IFACEMETHODIMP GetTextBeforePosition(uint32_t textPosition,
     OUT wchar_t const** textString,
     OUT uint32_t* textLength)
   {
     if (textPosition == 0 || textPosition > mTextLength) {
       // Either there is no text before here (== 0), or this
-      // is an invalid position. The query is considered valid thouh.
+      // is an invalid position. The query is considered valid though.
       *textString = nullptr;
       *textLength = 0;
     }
     else {
       *textString = mText;
       *textLength = textPosition;
     }
     return S_OK;
@@ -873,18 +873,16 @@ retry_getglyphs:
     hb_glyph_info_t *info = &buffer->info[i];
     hb_glyph_position_t *pos = &buffer->pos[i];
 
     /* TODO vertical */
     pos->x_advance = x_mult * (int32_t) info->mask;
     pos->x_offset =
       x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
     pos->y_offset = y_mult * info->var2.i32;
-
-    info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
   }
 
   if (isRightToLeft)
     hb_buffer_reverse (buffer);
 
   free (clusterMap);
   free (glyphIndices);
   free (textProperties);
--- a/gfx/harfbuzz/src/hb-face.h
+++ b/gfx/harfbuzz/src/hb-face.h
@@ -66,17 +66,16 @@ 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_user_data_key_t *key);
 
 HB_EXTERN void
 hb_face_make_immutable (hb_face_t *face);
 
 HB_EXTERN hb_bool_t
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -53,17 +53,17 @@
  *     for that, such that no rounding etc happens.  As such, we don't set ppem, and
  *     pass NO_HINTING as load_flags.  Would be much better to use NO_SCALE, and scale
  *     ourselves, like we do in uniscribe, etc.
  *
  *   - We don't handle / allow for emboldening / obliqueing.
  *
  *   - In the future, we should add constructors to create fonts in font space?
  *
- *   - FT_Load_Glyph() is exteremely costly.  Do something about it?
+ *   - FT_Load_Glyph() is extremely costly.  Do something about it?
  */
 
 
 struct hb_ft_font_t
 {
   FT_Face ft_face;
   int load_flags;
   bool symbol; /* Whether selected cmap is symbol cmap. */
--- a/gfx/harfbuzz/src/hb-gobject-enums.h.tmpl
+++ b/gfx/harfbuzz/src/hb-gobject-enums.h.tmpl
@@ -37,17 +37,18 @@
 #include <glib-object.h>
 
 HB_BEGIN_DECLS
 
 
 /*** END file-header ***/
 
 /*** BEGIN value-header ***/
-HB_EXTERN GType @enum_name@_get_type (void) G_GNUC_CONST;
+HB_EXTERN GType
+@enum_name@_get_type (void) G_GNUC_CONST;
 #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
 
 /*** END value-header ***/
 
 /*** BEGIN file-tail ***/
 
 HB_END_DECLS
 
--- a/gfx/harfbuzz/src/hb-gobject-structs.h
+++ b/gfx/harfbuzz/src/hb-gobject-structs.h
@@ -40,78 +40,99 @@ HB_BEGIN_DECLS
 
 /* Object types */
 
 /**
  * hb_gobject_blob_get_type:
  *
  * Since: 0.9.2
  **/
-HB_EXTERN GType hb_gobject_blob_get_type (void);
+HB_EXTERN GType
+hb_gobject_blob_get_type (void);
 #define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ())
 
 /**
  * hb_gobject_buffer_get_type:
  *
  * Since: 0.9.2
  **/
-HB_EXTERN GType hb_gobject_buffer_get_type (void);
+HB_EXTERN GType
+hb_gobject_buffer_get_type (void);
 #define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ())
 
 /**
  * hb_gobject_face_get_type:
  *
  * Since: 0.9.2
  **/
-HB_EXTERN GType hb_gobject_face_get_type (void);
+HB_EXTERN GType
+hb_gobject_face_get_type (void);
 #define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ())
 
 /**
  * hb_gobject_font_get_type:
  *
  * Since: 0.9.2
  **/
-HB_EXTERN GType hb_gobject_font_get_type (void);
+HB_EXTERN GType
+hb_gobject_font_get_type (void);
 #define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ())
 
 /**
  * hb_gobject_font_funcs_get_type:
  *
  * Since: 0.9.2
  **/
-HB_EXTERN GType hb_gobject_font_funcs_get_type (void);
+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);
+HB_EXTERN GType
+hb_gobject_set_get_type (void);
 #define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ())
 
-HB_EXTERN GType hb_gobject_shape_plan_get_type (void);
+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
  **/
-HB_EXTERN GType hb_gobject_unicode_funcs_get_type (void);
+HB_EXTERN GType
+hb_gobject_unicode_funcs_get_type (void);
 #define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ())
 
 /* Value types */
 
-HB_EXTERN GType hb_gobject_feature_get_type (void);
+HB_EXTERN GType
+hb_gobject_feature_get_type (void);
 #define HB_GOBJECT_TYPE_FEATURE (hb_gobject_feature_get_type ())
 
-HB_EXTERN GType hb_gobject_glyph_info_get_type (void);
+HB_EXTERN GType
+hb_gobject_glyph_info_get_type (void);
 #define HB_GOBJECT_TYPE_GLYPH_INFO (hb_gobject_glyph_info_get_type ())
 
-HB_EXTERN GType hb_gobject_glyph_position_get_type (void);
+HB_EXTERN GType
+hb_gobject_glyph_position_get_type (void);
 #define HB_GOBJECT_TYPE_GLYPH_POSITION (hb_gobject_glyph_position_get_type ())
 
-HB_EXTERN GType hb_gobject_segment_properties_get_type (void);
+HB_EXTERN GType
+hb_gobject_segment_properties_get_type (void);
 #define HB_GOBJECT_TYPE_SEGMENT_PROPERTIES (hb_gobject_segment_properties_get_type ())
 
-HB_EXTERN GType hb_gobject_user_data_key_get_type (void);
+HB_EXTERN GType
+hb_gobject_user_data_key_get_type (void);
 #define HB_GOBJECT_TYPE_USER_DATA_KEY (hb_gobject_user_data_key_get_type ())
 
+HB_EXTERN GType
+hb_gobject_ot_math_glyph_variant_get_type (void);
+#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_VARIANT (hb_gobject_ot_math_glyph_variant_get_type ())
+
+HB_EXTERN GType
+hb_gobject_ot_math_glyph_part_get_type (void);
+#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART (hb_gobject_ot_math_glyph_part_get_type ())
+
 
 HB_END_DECLS
 
 #endif /* HB_GOBJECT_H */
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -75,17 +75,17 @@ static const void *hb_graphite2_get_tabl
     if (unlikely (!p)) {
       hb_blob_destroy (blob);
       return nullptr;
     }
     p->blob = blob;
     p->tag = tag;
 
     /* TODO Not thread-safe, but fairly harmless.
-     * We can do the double-chcked pointer cmpexch thing here. */
+     * We can do the double-checked pointer cmpexch thing here. */
     p->next = face_data->tlist;
     face_data->tlist = p;
   }
 
   unsigned int tlen;
   const char *d = hb_blob_get_data (blob, &tlen);
   *len = tlen;
   return d;
@@ -355,17 +355,16 @@ hb_bool_t
 
   for (unsigned int i = 0; i < ci; ++i)
   {
     for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j)
     {
       hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j];
       info->codepoint = gids[clusters[i].base_glyph + j];
       info->cluster = clusters[i].cluster;
-      info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
       info->var1.i32 = clusters[i].advance;     // all glyphs in the cluster get the same advance
     }
   }
   buffer->len = glyph_count;
 
   unsigned int upem = hb_face_get_upem (face);
   float xscale = (float) font->x_scale / upem;
   float yscale = (float) font->y_scale / upem;
--- a/gfx/harfbuzz/src/hb-open-file-private.hh
+++ b/gfx/harfbuzz/src/hb-open-file-private.hh
@@ -25,16 +25,17 @@
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OPEN_FILE_PRIVATE_HH
 #define HB_OPEN_FILE_PRIVATE_HH
 
 #include "hb-open-type-private.hh"
+#include "hb-ot-head-table.hh"
 
 
 namespace OT {
 
 
 /*
  *
  * The OpenType Font File
@@ -49,44 +50,51 @@ namespace OT {
 struct OpenTypeFontFile;
 struct OffsetTable;
 struct TTCHeader;
 
 
 typedef struct TableRecord
 {
   int cmp (Tag t) const
-  { return t.cmp (tag); }
+  { return -t.cmp (tag); }
+
+  static int cmp (const void *pa, const void *pb)
+  {
+    const TableRecord *a = (const TableRecord *) pa;
+    const TableRecord *b = (const TableRecord *) pb;
+    return b->cmp (a->tag);
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
   Tag		tag;		/* 4-byte identifier. */
   CheckSum	checkSum;	/* CheckSum for this table. */
-  HBUINT32		offset;		/* Offset from beginning of TrueType font
+  Offset32	offset;		/* Offset from beginning of TrueType font
 				 * file. */
-  HBUINT32		length;		/* Length of this table. */
+  HBUINT32	length;		/* Length of this table. */
   public:
   DEFINE_SIZE_STATIC (16);
 } OpenTypeTable;
 
 typedef struct OffsetTable
 {
   friend struct OpenTypeFontFile;
 
   inline unsigned int get_table_count (void) const
   { return tables.len; }
   inline const TableRecord& get_table (unsigned int i) const
   {
     return tables[i];
   }
-  inline unsigned int get_table_tags (unsigned int start_offset,
+  inline unsigned int get_table_tags (unsigned int  start_offset,
 				      unsigned int *table_count, /* IN/OUT */
 				      hb_tag_t     *table_tags /* OUT */) const
   {
     if (table_count)
     {
       if (start_offset >= tables.len)
         *table_count = 0;
       else
@@ -113,16 +121,88 @@ typedef struct OffsetTable
   inline const TableRecord& get_table_by_tag (hb_tag_t tag) const
   {
     unsigned int table_index;
     find_table_index (tag, &table_index);
     return get_table (table_index);
   }
 
   public:
+
+  inline bool serialize (hb_serialize_context_t *c,
+			 hb_tag_t sfnt_tag,
+			 Supplier<hb_tag_t> &tags,
+			 Supplier<hb_blob_t *> &blobs,
+			 unsigned int table_count)
+  {
+    TRACE_SERIALIZE (this);
+    /* Alloc 12 for the OTHeader. */
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    /* Write sfntVersion (bytes 0..3). */
+    sfnt_version.set (sfnt_tag);
+    /* Take space for numTables, searchRange, entrySelector, RangeShift
+     * and the TableRecords themselves.  */
+    if (unlikely (!tables.serialize (c, table_count))) return_trace (false);
+
+    const char *dir_end = (const char *) c->head;
+    HBUINT32 *checksum_adjustment = nullptr;
+
+    /* Write OffsetTables, alloc for and write actual table blobs. */
+    for (unsigned int i = 0; i < table_count; i++)
+    {
+      TableRecord &rec = tables.array[i];
+      hb_blob_t *blob = blobs[i];
+      rec.tag.set (tags[i]);
+      rec.length.set (hb_blob_get_length (blob));
+      rec.offset.serialize (c, this);
+
+      /* Allocate room for the table and copy it. */
+      char *start = (char *) c->allocate_size<void> (rec.length);
+      if (unlikely (!start)) {return false;}
+
+      memcpy (start, hb_blob_get_data (blob, nullptr), rec.length);
+
+      /* 4-byte allignment. */
+      if (rec.length % 4)
+	c->allocate_size<void> (4 - rec.length % 4);
+      const char *end = (const char *) c->head;
+
+      if (tags[i] == HB_OT_TAG_head && end - start >= head::static_size)
+      {
+	head *h = (head *) start;
+	checksum_adjustment = &h->checkSumAdjustment;
+	checksum_adjustment->set (0);
+      }
+
+      rec.checkSum.set_for_data (start, end - start);
+    }
+    tags += table_count;
+    blobs += table_count;
+
+    tables.qsort ();
+
+    if (checksum_adjustment)
+    {
+      CheckSum checksum;
+
+      /* The following line is a slower version of the following block. */
+      //checksum.set_for_data (this, (const char *) c->head - (const char *) this);
+      checksum.set_for_data (this, dir_end - (const char *) this);
+      for (unsigned int i = 0; i < table_count; i++)
+      {
+	TableRecord &rec = tables.array[i];
+	checksum.set (checksum + rec.checkSum);
+      }
+
+      checksum_adjustment->set (0xB1B0AFBAu - checksum);
+    }
+
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && tables.sanitize (c));
   }
 
   protected:
   Tag		sfnt_version;	/* '\0\001\0\00' if TrueType / 'OTTO' if CFF */
@@ -244,16 +324,28 @@ struct OpenTypeFontFile
     case TrueTag:
     case Typ1Tag:
     case TrueTypeTag:	return u.fontFace;
     case TTCTag:	return u.ttcHeader.get_face (i);
     default:		return Null(OpenTypeFontFace);
     }
   }
 
+  inline bool serialize_single (hb_serialize_context_t *c,
+				hb_tag_t sfnt_tag,
+			        Supplier<hb_tag_t> &tags,
+			        Supplier<hb_blob_t *> &blobs,
+			        unsigned int table_count)
+  {
+    TRACE_SERIALIZE (this);
+    assert (sfnt_tag != TTCTag);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    return_trace (u.fontFace.serialize (c, sfnt_tag, tags, blobs, table_count));
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!u.tag.sanitize (c))) return_trace (false);
     switch (u.tag) {
     case CFFTag:	/* All the non-collection tags */
     case TrueTag:
     case Typ1Tag:
--- a/gfx/harfbuzz/src/hb-open-type-private.hh
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -129,17 +129,27 @@ static inline Type& StructAfter(TObject 
 /*
  * Null objects
  */
 
 /* Global nul-content Null pool.  Enlarge as necessary. */
 
 #define HB_NULL_POOL_SIZE 264
 static_assert (HB_NULL_POOL_SIZE % sizeof (void *) == 0, "Align HB_NULL_POOL_SIZE.");
-extern HB_INTERNAL const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)];
+
+#ifdef HB_NO_VISIBILITY
+static
+#else
+extern HB_INTERNAL
+#endif
+const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)]
+#ifdef HB_NO_VISIBILITY
+= {}
+#endif
+;
 
 /* Generic nul-content Null objects. */
 template <typename Type>
 static inline const Type& Null (void) {
   static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
   return *CastP<Type> (_hb_NullPool);
 }
 
@@ -174,24 +184,30 @@ struct hb_dispatch_context_t
 /*
  * 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),
+	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>
@@ -206,16 +222,18 @@ struct hb_sanitize_context_t :
     this->writable = false;
   }
 
   inline void start_processing (void)
   {
     this->start = hb_blob_get_data (this->blob, nullptr);
     this->end = this->start + hb_blob_get_length (this->blob);
     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));
   }
@@ -229,17 +247,20 @@ struct hb_sanitize_context_t :
     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->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len;
+    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);
@@ -293,16 +314,17 @@ struct hb_sanitize_context_t :
     }
     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>
@@ -494,40 +516,43 @@ struct hb_serialize_context_t
   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_)
+  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 head[i];
+    return * (const Type *) (const void *) ((const char *) head + stride * i);
   }
 
-  inline void advance (unsigned int count)
+  inline Supplier<Type> & operator += (unsigned int count)
   {
     if (unlikely (count > len))
       count = len;
     len -= count;
-    head += 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
  */
@@ -662,18 +687,18 @@ struct F2DOT14 : HBINT16
   //inline void set_float (float f) { v.set (f * ???); }
   public:
   DEFINE_SIZE_STATIC (2);
 };
 
 /* 32-bit signed fixed-point number (16.16). */
 struct Fixed: HBINT32
 {
-  //inline float to_float (void) const { return ???; }
-  //inline void set_float (float f) { v.set (f * ???); }
+  inline float to_float (void) const { return ((int32_t) v) / 65536.0; }
+  inline void set_float (float f) { v.set (round (f * 65536.0)); }
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
 /* Date represented in number of seconds since 12:00 midnight, January 1,
  * 1904. The value is represented as a signed 64-bit integer. */
 struct LONGDATETIME
 {
@@ -710,32 +735,41 @@ struct Index : HBUINT16 {
 };
 DEFINE_NULL_DATA (Index, "\xff\xff");
 
 /* 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)
+  {
+    void *t = c->start_embed<void> ();
+    this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */
+    return t;
+  }
+
   public:
   DEFINE_SIZE_STATIC (sizeof(Type));
 };
 
 typedef Offset<HBUINT16> Offset16;
 typedef Offset<HBUINT32> Offset32;
 
 
 /* CheckSum */
 struct CheckSum : HBUINT32
 {
   /* This is reference implementation from the spec. */
   static inline uint32_t CalcTableChecksum (const HBUINT32 *Table, uint32_t Length)
   {
     uint32_t Sum = 0L;
-    const HBUINT32 *EndPtr = Table+((Length+3) & ~3) / HBUINT32::static_size;
+    assert (0 == (Length & 3));
+    const HBUINT32 *EndPtr = Table + Length / HBUINT32::static_size;
 
     while (Table < EndPtr)
       Sum += *Table++;
     return Sum;
   }
 
   /* Note: data should be 4byte aligned and have 4byte padding at the end. */
   inline void set_for_data (const void *data, unsigned int length)
@@ -781,19 +815,17 @@ struct OffsetTo : Offset<OffsetType>
   {
     unsigned int offset = *this;
     if (unlikely (!offset)) return Null(Type);
     return StructAtOffset<Type> (base, offset);
   }
 
   inline Type& serialize (hb_serialize_context_t *c, const void *base)
   {
-    Type *t = c->start_embed<Type> ();
-    this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */
-    return *t;
+    return * (Type *) Offset<OffsetType>::serialize (c, base);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this))) return_trace (false);
     unsigned int offset = *this;
     if (unlikely (!offset)) return_trace (true);
@@ -871,17 +903,17 @@ struct ArrayOf
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<Type> &items,
 			 unsigned int items_len)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!serialize (c, items_len))) return_trace (false);
     for (unsigned int i = 0; i < items_len; i++)
       array[i] = items[i];
-    items.advance (items_len);
+    items += items_len;
     return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
 
@@ -923,16 +955,21 @@ struct ArrayOf
   {
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (!this->array[i].cmp (x))
         return i;
     return -1;
   }
 
+  inline void qsort (void)
+  {
+    ::qsort (array, len, sizeof (Type), Type::cmp);
+  }
+
   private:
   inline bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (len.sanitize (c) && c->check_array (array, Type::static_size, len));
   }
 
   public:
@@ -989,17 +1026,17 @@ struct HeadlessArrayOf
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
     len.set (items_len); /* TODO(serialize) Overflow? */
     if (unlikely (!items_len)) return_trace (true);
     if (unlikely (!c->extend (*this))) return_trace (false);
     for (unsigned int i = 0; i < items_len - 1; i++)
       array[i] = items[i];
-    items.advance (items_len - 1);
+    items += items_len - 1;
     return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
 
@@ -1067,16 +1104,27 @@ 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));
   }
 
+  inline void set (unsigned int v)
+  {
+    len.set (v);
+    assert (len == v);
+    entrySelectorZ.set (MAX (1u, _hb_bit_storage (v)) - 1);
+    searchRangeZ.set (16 * (1u << entrySelectorZ));
+    rangeShiftZ.set (v * 16 > searchRangeZ
+                     ? 16 * v - searchRangeZ
+                     : 0);
+  }
+
   protected:
   HBUINT16	len;
   HBUINT16	searchRangeZ;
   HBUINT16	entrySelectorZ;
   HBUINT16	rangeShiftZ;
 
   public:
   DEFINE_SIZE_STATIC (8);
deleted file mode 100644
--- a/gfx/harfbuzz/src/hb-ot-cbdt-table.hh
+++ /dev/null
@@ -1,471 +0,0 @@
-/*
- * Copyright © 2016  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): Seigo Nonaka
- */
-
-#ifndef HB_OT_CBDT_TABLE_HH
-#define HB_OT_CBDT_TABLE_HH
-
-#include "hb-open-type-private.hh"
-
-namespace OT {
-
-struct SmallGlyphMetrics
-{
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
-  }
-
-  inline void get_extents (hb_glyph_extents_t *extents) const
-  {
-    extents->x_bearing = bearingX;
-    extents->y_bearing = bearingY;
-    extents->width = width;
-    extents->height = -height;
-  }
-
-  HBUINT8 height;
-  HBUINT8 width;
-  HBINT8 bearingX;
-  HBINT8 bearingY;
-  HBUINT8 advance;
-
-  DEFINE_SIZE_STATIC(5);
-};
-
-struct BigGlyphMetrics : SmallGlyphMetrics
-{
-  HBINT8 vertBearingX;
-  HBINT8 vertBearingY;
-  HBUINT8 vertAdvance;
-
-  DEFINE_SIZE_STATIC(8);
-};
-
-struct SBitLineMetrics
-{
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
-  }
-
-  HBINT8 ascender;
-  HBINT8 decender;
-  HBUINT8 widthMax;
-  HBINT8 caretSlopeNumerator;
-  HBINT8 caretSlopeDenominator;
-  HBINT8 caretOffset;
-  HBINT8 minOriginSB;
-  HBINT8 minAdvanceSB;
-  HBINT8 maxBeforeBL;
-  HBINT8 minAfterBL;
-  HBINT8 padding1;
-  HBINT8 padding2;
-
-  DEFINE_SIZE_STATIC(12);
-};
-
-
-/*
- * Index Subtables.
- */
-
-struct IndexSubtableHeader
-{
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
-  }
-
-  HBUINT16 indexFormat;
-  HBUINT16 imageFormat;
-  HBUINT32 imageDataOffset;
-
-  DEFINE_SIZE_STATIC(8);
-};
-
-template <typename OffsetType>
-struct IndexSubtableFormat1Or3
-{
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  c->check_array (offsetArrayZ, offsetArrayZ[0].static_size, glyph_count + 1));
-  }
-
-  bool get_image_data (unsigned int idx,
-		       unsigned int *offset,
-		       unsigned int *length) const
-  {
-    if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx]))
-      return false;
-
-    *offset = header.imageDataOffset + offsetArrayZ[idx];
-    *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx];
-    return true;
-  }
-
-  IndexSubtableHeader header;
-  Offset<OffsetType> offsetArrayZ[VAR];
-
-  DEFINE_SIZE_ARRAY(8, offsetArrayZ);
-};
-
-struct IndexSubtableFormat1 : IndexSubtableFormat1Or3<HBUINT32> {};
-struct IndexSubtableFormat3 : IndexSubtableFormat1Or3<HBUINT16> {};
-
-struct IndexSubtable
-{
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
-  {
-    TRACE_SANITIZE (this);
-    if (!u.header.sanitize (c)) return_trace (false);
-    switch (u.header.indexFormat) {
-    case 1: return_trace (u.format1.sanitize (c, glyph_count));
-    case 3: return_trace (u.format3.sanitize (c, glyph_count));
-    default:return_trace (true);
-    }
-  }
-
-  inline bool get_extents (hb_glyph_extents_t *extents) const
-  {
-    switch (u.header.indexFormat) {
-    case 2: case 5: /* TODO */
-    case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */
-    default:return (false);
-    }
-  }
-
-  bool get_image_data (unsigned int idx,
-		       unsigned int *offset,
-		       unsigned int *length,
-		       unsigned int *format) const
-  {
-    *format = u.header.imageFormat;
-    switch (u.header.indexFormat) {
-    case 1: return u.format1.get_image_data (idx, offset, length);
-    case 3: return u.format3.get_image_data (idx, offset, length);
-    default: return false;
-    }
-  }
-
-  protected:
-  union {
-  IndexSubtableHeader	header;
-  IndexSubtableFormat1	format1;
-  IndexSubtableFormat3	format3;
-  /* TODO: Format 2, 4, 5. */
-  } u;
-  public:
-  DEFINE_SIZE_UNION (8, header);
-};
-
-struct IndexSubtableRecord
-{
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  firstGlyphIndex <= lastGlyphIndex &&
-		  offsetToSubtable.sanitize (c, this, lastGlyphIndex - firstGlyphIndex + 1));
-  }
-
-  inline bool get_extents (hb_glyph_extents_t *extents) const
-  {
-    return (this+offsetToSubtable).get_extents (extents);
-  }
-
-  bool get_image_data (unsigned int gid,
-		       unsigned int *offset,
-		       unsigned int *length,
-		       unsigned int *format) const
-  {
-    if (gid < firstGlyphIndex || gid > lastGlyphIndex)
-    {
-      return false;
-    }
-    return (this+offsetToSubtable).get_image_data (gid - firstGlyphIndex,
-						   offset, length, format);
-  }
-
-  HBUINT16 firstGlyphIndex;
-  HBUINT16 lastGlyphIndex;
-  LOffsetTo<IndexSubtable> offsetToSubtable;
-
-  DEFINE_SIZE_STATIC(8);
-};
-
-struct IndexSubtableArray
-{
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!c->check_array (&indexSubtablesZ, indexSubtablesZ[0].static_size, count)))
-      return_trace (false);
-    for (unsigned int i = 0; i < count; i++)
-      if (unlikely (!indexSubtablesZ[i].sanitize (c, this)))
-	return_trace (false);
-    return_trace (true);
-  }
-
-  public:
-  const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const
-  {
-    for (unsigned int i = 0; i < numTables; ++i)
-    {
-      unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex;
-      unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex;
-      if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) {
-        return &indexSubtablesZ[i];
-      }
-    }
-    return nullptr;
-  }
-
-  protected:
-  IndexSubtableRecord indexSubtablesZ[VAR];
-
-  public:
-  DEFINE_SIZE_ARRAY(0, indexSubtablesZ);
-};
-
-struct BitmapSizeTable
-{
-  friend struct CBLC;
-
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) &&
-		  c->check_range (&(base+indexSubtableArrayOffset), indexTablesSize) &&
-		  horizontal.sanitize (c) &&
-		  vertical.sanitize (c));
-  }
-
-  const IndexSubtableRecord *find_table (hb_codepoint_t glyph, const void *base) const
-  {
-    return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables);
-  }
-
-  protected:
-  LOffsetTo<IndexSubtableArray> indexSubtableArrayOffset;
-  HBUINT32 indexTablesSize;
-  HBUINT32 numberOfIndexSubtables;
-  HBUINT32 colorRef;
-  SBitLineMetrics horizontal;
-  SBitLineMetrics vertical;
-  HBUINT16 startGlyphIndex;
-  HBUINT16 endGlyphIndex;
-  HBUINT8 ppemX;
-  HBUINT8 ppemY;
-  HBUINT8 bitDepth;
-  HBINT8 flags;
-
-  public:
-  DEFINE_SIZE_STATIC(48);
-};
-
-
-/*
- * Glyph Bitmap Data Formats.
- */
-
-struct GlyphBitmapDataFormat17
-{
-  SmallGlyphMetrics glyphMetrics;
-  HBUINT32 dataLen;
-  HBUINT8 dataZ[VAR];
-
-  DEFINE_SIZE_ARRAY(9, dataZ);
-};
-
-
-/*
- * CBLC -- Color Bitmap Location Table
- */
-
-#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C')
-
-struct CBLC
-{
-  friend struct CBDT;
-
-  static const hb_tag_t tableTag = HB_OT_TAG_CBLC;
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  likely (version.major == 2 || version.major == 3) &&
-		  sizeTables.sanitize (c, this));
-  }
-
-  protected:
-  const IndexSubtableRecord *find_table (hb_codepoint_t glyph,
-					 unsigned int *x_ppem, unsigned int *y_ppem) const
-  {
-    /* TODO: Make it possible to select strike. */
-
-    unsigned int count = sizeTables.len;
-    for (uint32_t i = 0; i < count; ++i)
-    {
-      unsigned int startGlyphIndex = sizeTables.array[i].startGlyphIndex;
-      unsigned int endGlyphIndex = sizeTables.array[i].endGlyphIndex;
-      if (startGlyphIndex <= glyph && glyph <= endGlyphIndex)
-      {
-	*x_ppem = sizeTables[i].ppemX;
-	*y_ppem = sizeTables[i].ppemY;
-	return sizeTables[i].find_table (glyph, this);
-      }
-    }
-
-    return nullptr;
-  }
-
-  protected:
-  FixedVersion<>		version;
-  LArrayOf<BitmapSizeTable>	sizeTables;
-
-  public:
-  DEFINE_SIZE_ARRAY(8, sizeTables);
-};
-
-/*
- * CBDT -- Color Bitmap Data Table
- */
-#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T')
-
-struct CBDT
-{
-  static const hb_tag_t tableTag = HB_OT_TAG_CBDT;
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  likely (version.major == 2 || version.major == 3));
-  }
-
-  struct accelerator_t
-  {
-    inline void init (hb_face_t *face)
-    {
-      upem = face->get_upem();
-
-      cblc_blob = Sanitizer<CBLC>().sanitize (face->reference_table (HB_OT_TAG_CBLC));
-      cbdt_blob = Sanitizer<CBDT>().sanitize (face->reference_table (HB_OT_TAG_CBDT));
-      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 = Sanitizer<CBLC>::lock_instance (cblc_blob);
-      cbdt = Sanitizer<CBDT>::lock_instance (cbdt_blob);
-
-    }
-
-    inline void fini (void)
-    {
-      hb_blob_destroy (this->cblc_blob);
-      hb_blob_destroy (this->cbdt_blob);
-    }
-
-    inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
-    {
-      unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */
-
-      if (!cblc)
-	return false;  // Not a color bitmap font.
-
-      const IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem);
-      if (!subtable_record || !x_ppem || !y_ppem)
-	return false;
-
-      if (subtable_record->get_extents (extents))
-	return true;
-
-      unsigned int image_offset = 0, image_length = 0, image_format = 0;
-      if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format))
-	return false;
-
-      {
-	if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length))
-	  return false;
-
-	switch (image_format)
-	{
-	  case 17: {
-	    if (unlikely (image_length < GlyphBitmapDataFormat17::min_size))
-	      return false;
-
-	    const GlyphBitmapDataFormat17& glyphFormat17 =
-		StructAtOffset<GlyphBitmapDataFormat17> (this->cbdt, image_offset);
-	    glyphFormat17.glyphMetrics.get_extents (extents);
-	  }
-	  break;
-	  default:
-	    // TODO: Support other image formats.
-	    return false;
-	}
-      }
-
-      /* Convert to the font units. */
-      extents->x_bearing *= upem / (float) x_ppem;
-      extents->y_bearing *= upem / (float) y_ppem;
-      extents->width *= upem / (float) x_ppem;
-      extents->height *= upem / (float) y_ppem;
-
-      return true;
-    }
-
-    private:
-    hb_blob_t *cblc_blob;
-    hb_blob_t *cbdt_blob;
-    const CBLC *cblc;
-    const CBDT *cbdt;
-
-    unsigned int cbdt_len;
-    unsigned int upem;
-  };
-
-
-  protected:
-  FixedVersion<>version;
-  HBUINT8 dataZ[VAR];
-
-  public:
-  DEFINE_SIZE_ARRAY(4, dataZ);
-};
-
-} /* namespace OT */
-
-#endif /* HB_OT_CBDT_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-cmap-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-cmap-table.hh
@@ -23,17 +23,17 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_CMAP_TABLE_HH
 #define HB_OT_CMAP_TABLE_HH
 
 #include "hb-open-type-private.hh"
-
+#include "hb-subset-plan.hh"
 
 namespace OT {
 
 
 /*
  * cmap -- Character To Glyph Index Mapping Table
  */
 
@@ -56,17 +56,17 @@ struct CmapSubtableFormat0
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
   protected:
   HBUINT16	format;		/* Format number is set to 0. */
   HBUINT16	lengthZ;	/* Byte length of this subtable. */
   HBUINT16	languageZ;	/* Ignore. */
-  HBUINT8		glyphIdArray[256];/* An array that maps character
+  HBUINT8	glyphIdArray[256];/* An array that maps character
 				 * code to glyph index values. */
   public:
   DEFINE_SIZE_STATIC (6 + 256);
 };
 
 struct CmapSubtableFormat4
 {
   struct accelerator_t
@@ -188,16 +188,17 @@ struct CmapSubtableFormat4
   public:
   DEFINE_SIZE_ARRAY (14, values);
 };
 
 struct CmapSubtableLongGroup
 {
   friend struct CmapSubtableFormat12;
   friend struct CmapSubtableFormat13;
+  friend struct cmap;
 
   int cmp (hb_codepoint_t codepoint) const
   {
     if (codepoint < startCharCode) return -1;
     if (codepoint > endCharCode)   return +1;
     return 0;
   }
 
@@ -248,31 +249,43 @@ struct CmapSubtableTrimmed
 };
 
 struct CmapSubtableFormat6  : CmapSubtableTrimmed<HBUINT16> {};
 struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32 > {};
 
 template <typename T>
 struct CmapSubtableLongSegmented
 {
+  friend struct cmap;
+
   inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
     int i = groups.bsearch (codepoint);
     if (i == -1)
       return false;
     *glyph = T::group_get_glyph (groups[i], codepoint);
     return true;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && groups.sanitize (c));
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+                         hb_prealloced_array_t<CmapSubtableLongGroup> &group_data)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    Supplier<CmapSubtableLongGroup> supplier (group_data.array, group_data.len);
+    if (unlikely (!groups.serialize (c, supplier, group_data.len))) return_trace (false);
+    return true;
+  }
+
   protected:
   HBUINT16	format;		/* Subtable format; set to 12. */
   HBUINT16	reservedZ;	/* Reserved; set to 0. */
   HBUINT32		lengthZ;	/* Byte length of this subtable. */
   HBUINT32		languageZ;	/* Ignore. */
   SortedArrayOf<CmapSubtableLongGroup, HBUINT32>
 		groups;		/* Groupings. */
   public:
@@ -417,22 +430,22 @@ struct CmapSubtableFormat14
 struct CmapSubtable
 {
   /* Note: We intentionally do NOT implement subtable formats 2 and 8. */
 
   inline bool get_glyph (hb_codepoint_t codepoint,
 			 hb_codepoint_t *glyph) const
   {
     switch (u.format) {
-    case  0: return u.format0 .get_glyph(codepoint, glyph);
-    case  4: return u.format4 .get_glyph(codepoint, glyph);
-    case  6: return u.format6 .get_glyph(codepoint, glyph);
-    case 10: return u.format10.get_glyph(codepoint, glyph);
-    case 12: return u.format12.get_glyph(codepoint, glyph);
-    case 13: return u.format13.get_glyph(codepoint, glyph);
+    case  0: return u.format0 .get_glyph (codepoint, glyph);
+    case  4: return u.format4 .get_glyph (codepoint, glyph);
+    case  6: return u.format6 .get_glyph (codepoint, glyph);
+    case 10: return u.format10.get_glyph (codepoint, glyph);
+    case 12: return u.format12.get_glyph (codepoint, glyph);
+    case 13: return u.format13.get_glyph (codepoint, glyph);
     case 14:
     default: return false;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -499,16 +512,124 @@ struct cmap
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  likely (version == 0) &&
 		  encodingRecord.sanitize (c, this));
   }
 
+  inline bool populate_groups (hb_subset_plan_t *plan,
+			       hb_prealloced_array_t<CmapSubtableLongGroup> *groups) const
+  {
+    CmapSubtableLongGroup *group = nullptr;
+    for (unsigned int i = 0; i < plan->codepoints.len; i++) {
+
+      hb_codepoint_t cp = plan->codepoints[i];
+      if (!group || cp - 1 != group->endCharCode)
+      {
+        group = groups->push ();
+        group->startCharCode.set (cp);
+        group->endCharCode.set (cp);
+        hb_codepoint_t new_gid;
+        if (unlikely (!hb_subset_plan_new_gid_for_codepoint (plan, cp, &new_gid)))
+        {
+          DEBUG_MSG(SUBSET, nullptr, "Unable to find new gid for %04x", cp);
+          return false;
+        }
+        group->glyphID.set (new_gid);
+      } else
+      {
+        group->endCharCode.set (cp);
+      }
+    }
+
+    DEBUG_MSG(SUBSET, nullptr, "cmap");
+    for (unsigned int i = 0; i < groups->len; i++) {
+      CmapSubtableLongGroup& group = (*groups)[i];
+      DEBUG_MSG(SUBSET, nullptr, "  %d: U+%04X-U+%04X, gid %d-%d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID, (uint32_t) group.glyphID + ((uint32_t) group.endCharCode - (uint32_t) group.startCharCode));
+    }
+
+    return true;
+  }
+
+  inline bool _subset (hb_prealloced_array_t<CmapSubtableLongGroup> &groups,
+		       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)))
+    {
+      return false;
+    }
+
+    cmap->version.set (0);
+
+    if (unlikely (!cmap->encodingRecord.serialize (&c, /* numTables */ 1))) return false;
+
+    EncodingRecord &rec = cmap->encodingRecord[0];
+    rec.platformID.set (3); // Windows
+    rec.encodingID.set (10); // Unicode UCS-4
+
+    /* capture offset to subtable */
+    CmapSubtable &subtable = rec.subtable.serialize (&c, cmap);
+
+    subtable.u.format.set (12);
+
+    CmapSubtableFormat12 &format12 = subtable.u.format12;
+    if (unlikely (!c.extend_min (format12))) return false;
+
+    format12.format.set (12);
+    format12.reservedZ.set (0);
+    format12.lengthZ.set (16 + 12 * groups.len);
+
+    if (unlikely (!format12.serialize (&c, groups))) return false;
+
+    c.end_serialize ();
+
+    return true;
+  }
+
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_auto_array_t<CmapSubtableLongGroup> groups;
+
+    if (unlikely (!populate_groups (plan, &groups))) return false;
+
+    // We now know how big our blob needs to be
+    // TODO use APIs from the structs to get size?
+    size_t dest_sz = 4 // header
+                   + 8 // 1 EncodingRecord
+                   + 16 // Format 12 header
+                   + 12 * groups.len; // SequentialMapGroup records
+    void *dest = malloc (dest_sz);
+    if (unlikely (!dest)) {
+      DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for cmap subset output", (unsigned long) dest_sz);
+      return false;
+    }
+
+    if (unlikely (!_subset (groups, dest_sz, dest)))
+    {
+      free (dest);
+      return false;
+    }
+
+    // all done, write the blob into dest
+    hb_blob_t *cmap_prime = hb_blob_create ((const char *)dest,
+                                            dest_sz,
+                                            HB_MEMORY_MODE_READONLY,
+                                            dest,
+                                            free);
+    bool result =  hb_subset_plan_add_table (plan, HB_OT_TAG_cmap, cmap_prime);
+    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 = OT::Sanitizer<OT::cmap>::lock_instance (this->blob);
       const OT::CmapSubtable *subtable = nullptr;
       const OT::CmapSubtableFormat14 *subtable_uvs = nullptr;
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-color-cbdt-table.hh
@@ -0,0 +1,471 @@
+/*
+ * Copyright © 2016  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): Seigo Nonaka
+ */
+
+#ifndef HB_OT_COLOR_CBDT_TABLE_HH
+#define HB_OT_COLOR_CBDT_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+namespace OT {
+
+struct SmallGlyphMetrics
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  inline void get_extents (hb_glyph_extents_t *extents) const
+  {
+    extents->x_bearing = bearingX;
+    extents->y_bearing = bearingY;
+    extents->width = width;
+    extents->height = -height;
+  }
+
+  HBUINT8 height;
+  HBUINT8 width;
+  HBINT8 bearingX;
+  HBINT8 bearingY;
+  HBUINT8 advance;
+
+  DEFINE_SIZE_STATIC(5);
+};
+
+struct BigGlyphMetrics : SmallGlyphMetrics
+{
+  HBINT8 vertBearingX;
+  HBINT8 vertBearingY;
+  HBUINT8 vertAdvance;
+
+  DEFINE_SIZE_STATIC(8);
+};
+
+struct SBitLineMetrics
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  HBINT8 ascender;
+  HBINT8 decender;
+  HBUINT8 widthMax;
+  HBINT8 caretSlopeNumerator;
+  HBINT8 caretSlopeDenominator;
+  HBINT8 caretOffset;
+  HBINT8 minOriginSB;
+  HBINT8 minAdvanceSB;
+  HBINT8 maxBeforeBL;
+  HBINT8 minAfterBL;
+  HBINT8 padding1;
+  HBINT8 padding2;
+
+  DEFINE_SIZE_STATIC(12);
+};
+
+
+/*
+ * Index Subtables.
+ */
+
+struct IndexSubtableHeader
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  HBUINT16 indexFormat;
+  HBUINT16 imageFormat;
+  HBUINT32 imageDataOffset;
+
+  DEFINE_SIZE_STATIC(8);
+};
+
+template <typename OffsetType>
+struct IndexSubtableFormat1Or3
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  c->check_array (offsetArrayZ, offsetArrayZ[0].static_size, glyph_count + 1));
+  }
+
+  bool get_image_data (unsigned int idx,
+		       unsigned int *offset,
+		       unsigned int *length) const
+  {
+    if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx]))
+      return false;
+
+    *offset = header.imageDataOffset + offsetArrayZ[idx];
+    *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx];
+    return true;
+  }
+
+  IndexSubtableHeader header;
+  Offset<OffsetType> offsetArrayZ[VAR];
+
+  DEFINE_SIZE_ARRAY(8, offsetArrayZ);
+};
+
+struct IndexSubtableFormat1 : IndexSubtableFormat1Or3<HBUINT32> {};
+struct IndexSubtableFormat3 : IndexSubtableFormat1Or3<HBUINT16> {};
+
+struct IndexSubtable
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.header.sanitize (c)) return_trace (false);
+    switch (u.header.indexFormat) {
+    case 1: return_trace (u.format1.sanitize (c, glyph_count));
+    case 3: return_trace (u.format3.sanitize (c, glyph_count));
+    default:return_trace (true);
+    }
+  }
+
+  inline bool get_extents (hb_glyph_extents_t *extents) const
+  {
+    switch (u.header.indexFormat) {
+    case 2: case 5: /* TODO */
+    case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */
+    default:return (false);
+    }
+  }
+
+  bool get_image_data (unsigned int idx,
+		       unsigned int *offset,
+		       unsigned int *length,
+		       unsigned int *format) const
+  {
+    *format = u.header.imageFormat;
+    switch (u.header.indexFormat) {
+    case 1: return u.format1.get_image_data (idx, offset, length);
+    case 3: return u.format3.get_image_data (idx, offset, length);
+    default: return false;
+    }
+  }
+
+  protected:
+  union {
+  IndexSubtableHeader	header;
+  IndexSubtableFormat1	format1;
+  IndexSubtableFormat3	format3;
+  /* TODO: Format 2, 4, 5. */
+  } u;
+  public:
+  DEFINE_SIZE_UNION (8, header);
+};
+
+struct IndexSubtableRecord
+{
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  firstGlyphIndex <= lastGlyphIndex &&
+		  offsetToSubtable.sanitize (c, this, lastGlyphIndex - firstGlyphIndex + 1));
+  }
+
+  inline bool get_extents (hb_glyph_extents_t *extents) const
+  {
+    return (this+offsetToSubtable).get_extents (extents);
+  }
+
+  bool get_image_data (unsigned int gid,
+		       unsigned int *offset,
+		       unsigned int *length,
+		       unsigned int *format) const
+  {
+    if (gid < firstGlyphIndex || gid > lastGlyphIndex)
+    {
+      return false;
+    }
+    return (this+offsetToSubtable).get_image_data (gid - firstGlyphIndex,
+						   offset, length, format);
+  }
+
+  HBUINT16 firstGlyphIndex;
+  HBUINT16 lastGlyphIndex;
+  LOffsetTo<IndexSubtable> offsetToSubtable;
+
+  DEFINE_SIZE_STATIC(8);
+};
+
+struct IndexSubtableArray
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_array (&indexSubtablesZ, indexSubtablesZ[0].static_size, count)))
+      return_trace (false);
+    for (unsigned int i = 0; i < count; i++)
+      if (unlikely (!indexSubtablesZ[i].sanitize (c, this)))
+	return_trace (false);
+    return_trace (true);
+  }
+
+  public:
+  const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const
+  {
+    for (unsigned int i = 0; i < numTables; ++i)
+    {
+      unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex;
+      unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex;
+      if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) {
+        return &indexSubtablesZ[i];
+      }
+    }
+    return nullptr;
+  }
+
+  protected:
+  IndexSubtableRecord indexSubtablesZ[VAR];
+
+  public:
+  DEFINE_SIZE_ARRAY(0, indexSubtablesZ);
+};
+
+struct BitmapSizeTable
+{
+  friend struct CBLC;
+
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) &&
+		  c->check_range (&(base+indexSubtableArrayOffset), indexTablesSize) &&
+		  horizontal.sanitize (c) &&
+		  vertical.sanitize (c));
+  }
+
+  const IndexSubtableRecord *find_table (hb_codepoint_t glyph, const void *base) const
+  {
+    return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables);
+  }
+
+  protected:
+  LOffsetTo<IndexSubtableArray> indexSubtableArrayOffset;
+  HBUINT32 indexTablesSize;
+  HBUINT32 numberOfIndexSubtables;
+  HBUINT32 colorRef;
+  SBitLineMetrics horizontal;
+  SBitLineMetrics vertical;
+  HBUINT16 startGlyphIndex;
+  HBUINT16 endGlyphIndex;
+  HBUINT8 ppemX;
+  HBUINT8 ppemY;
+  HBUINT8 bitDepth;
+  HBINT8 flags;
+
+  public:
+  DEFINE_SIZE_STATIC(48);
+};
+
+
+/*
+ * Glyph Bitmap Data Formats.
+ */
+
+struct GlyphBitmapDataFormat17
+{
+  SmallGlyphMetrics glyphMetrics;
+  HBUINT32 dataLen;
+  HBUINT8 dataZ[VAR];
+
+  DEFINE_SIZE_ARRAY(9, dataZ);
+};
+
+
+/*
+ * CBLC -- Color Bitmap Location Table
+ */
+
+#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C')
+
+struct CBLC
+{
+  friend struct CBDT;
+
+  static const hb_tag_t tableTag = HB_OT_TAG_CBLC;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  likely (version.major == 2 || version.major == 3) &&
+		  sizeTables.sanitize (c, this));
+  }
+
+  protected:
+  const IndexSubtableRecord *find_table (hb_codepoint_t glyph,
+					 unsigned int *x_ppem, unsigned int *y_ppem) const
+  {
+    /* TODO: Make it possible to select strike. */
+
+    unsigned int count = sizeTables.len;
+    for (uint32_t i = 0; i < count; ++i)
+    {
+      unsigned int startGlyphIndex = sizeTables.array[i].startGlyphIndex;
+      unsigned int endGlyphIndex = sizeTables.array[i].endGlyphIndex;
+      if (startGlyphIndex <= glyph && glyph <= endGlyphIndex)
+      {
+	*x_ppem = sizeTables[i].ppemX;
+	*y_ppem = sizeTables[i].ppemY;
+	return sizeTables[i].find_table (glyph, this);
+      }
+    }
+
+    return nullptr;
+  }
+
+  protected:
+  FixedVersion<>		version;
+  LArrayOf<BitmapSizeTable>	sizeTables;
+
+  public:
+  DEFINE_SIZE_ARRAY(8, sizeTables);
+};
+
+/*
+ * CBDT -- Color Bitmap Data Table
+ */
+#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T')
+
+struct CBDT
+{
+  static const hb_tag_t tableTag = HB_OT_TAG_CBDT;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  likely (version.major == 2 || version.major == 3));
+  }
+
+  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));
+      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 = Sanitizer<CBLC>::lock_instance (cblc_blob);
+      cbdt = Sanitizer<CBDT>::lock_instance (cbdt_blob);
+
+    }
+
+    inline void fini (void)
+    {
+      hb_blob_destroy (this->cblc_blob);
+      hb_blob_destroy (this->cbdt_blob);
+    }
+
+    inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
+    {
+      unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */
+
+      if (!cblc)
+	return false;  // Not a color bitmap font.
+
+      const IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem);
+      if (!subtable_record || !x_ppem || !y_ppem)
+	return false;
+
+      if (subtable_record->get_extents (extents))
+	return true;
+
+      unsigned int image_offset = 0, image_length = 0, image_format = 0;
+      if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format))
+	return false;
+
+      {
+	if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length))
+	  return false;
+
+	switch (image_format)
+	{
+	  case 17: {
+	    if (unlikely (image_length < GlyphBitmapDataFormat17::min_size))
+	      return false;
+
+	    const GlyphBitmapDataFormat17& glyphFormat17 =
+		StructAtOffset<GlyphBitmapDataFormat17> (this->cbdt, image_offset);
+	    glyphFormat17.glyphMetrics.get_extents (extents);
+	  }
+	  break;
+	  default:
+	    // TODO: Support other image formats.
+	    return false;
+	}
+      }
+
+      /* Convert to the font units. */
+      extents->x_bearing *= upem / (float) x_ppem;
+      extents->y_bearing *= upem / (float) y_ppem;
+      extents->width *= upem / (float) x_ppem;
+      extents->height *= upem / (float) y_ppem;
+
+      return true;
+    }
+
+    private:
+    hb_blob_t *cblc_blob;
+    hb_blob_t *cbdt_blob;
+    const CBLC *cblc;
+    const CBDT *cbdt;
+
+    unsigned int cbdt_len;
+    unsigned int upem;
+  };
+
+
+  protected:
+  FixedVersion<>version;
+  HBUINT8 dataZ[VAR];
+
+  public:
+  DEFINE_SIZE_ARRAY(4, dataZ);
+};
+
+} /* namespace OT */
+
+#endif /* HB_OT_COLOR_CBDT_TABLE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-color-colr-table.hh
@@ -0,0 +1,142 @@
+/*
+ * 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_OT_COLOR_COLR_TABLE_HH
+#define HB_OT_COLOR_COLR_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+/*
+ * Color Palette
+ * http://www.microsoft.com/typography/otspec/colr.htm
+ */
+
+#define HB_OT_TAG_COLR HB_TAG('C','O','L','R')
+
+namespace OT {
+
+
+struct LayerRecord
+{
+  friend struct COLR;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  GlyphID gID;			/* Glyph ID of layer glyph */
+  HBUINT16 paletteIndex;	/* Index value to use with a selected color palette */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct BaseGlyphRecord
+{
+  friend struct COLR;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  GlyphID gID;			/* Glyph ID of reference glyph */
+  HBUINT16 firstLayerIndex;	/* Index to the layer record */
+  HBUINT16 numLayers;		/* Number of color layers associated with this glyph */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct COLR
+{
+  static const hb_tag_t tableTag = HB_OT_TAG_COLR;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!(c->check_struct (this) &&
+        c->check_array ((const void*) &layerRecordsOffsetZ, sizeof (LayerRecord), numLayerRecords) &&
+        c->check_array ((const void*) &baseGlyphRecordsZ, sizeof (BaseGlyphRecord), numBaseGlyphRecords)))
+      return_trace (false);
+
+    const BaseGlyphRecord* base_glyph_records = &baseGlyphRecordsZ (this);
+    for (unsigned int i = 0; i < numBaseGlyphRecords; ++i)
+      if (base_glyph_records[i].firstLayerIndex +
+          base_glyph_records[i].numLayers > numLayerRecords)
+        return_trace (false);
+
+    return_trace (true);
+  }
+
+  inline bool get_base_glyph_record (
+    hb_codepoint_t glyph_id, unsigned int &first_layer, unsigned int &num_layers) const
+  {
+    const BaseGlyphRecord* base_glyph_records = &baseGlyphRecordsZ (this);
+    unsigned int min = 0, max = numBaseGlyphRecords - 1;
+    while (min <= max)
+    {
+      unsigned int mid = (min + max) / 2;
+      hb_codepoint_t gID = base_glyph_records[mid].gID;
+      if (gID > glyph_id)
+        max = mid - 1;
+      else if (gID < glyph_id)
+        min = mid + 1;
+      else
+      {
+        first_layer = base_glyph_records[mid].firstLayerIndex;
+        num_layers = base_glyph_records[mid].numLayers;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  inline void get_layer_record (int layer,
+    hb_codepoint_t &glyph_id, unsigned int &palette_index) const
+  {
+    const LayerRecord* records = &layerRecordsOffsetZ (this);
+    glyph_id = records[layer].gID;
+    palette_index = records[layer].paletteIndex;
+  }
+
+  protected:
+  HBUINT16	version;		/* Table version number */
+  HBUINT16	numBaseGlyphRecords;	/* Number of Base Glyph Records */
+  LOffsetTo<BaseGlyphRecord>
+		baseGlyphRecordsZ;	/* Offset to Base Glyph records. */
+  LOffsetTo<LayerRecord>
+		layerRecordsOffsetZ;	/* Offset to Layer Records */
+  HBUINT16	numLayerRecords;	/* Number of Layer Records */
+  public:
+  DEFINE_SIZE_STATIC (14);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_COLOR_COLR_TABLE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-color-cpal-table.hh
@@ -0,0 +1,208 @@
+/*
+ * Copyright © 2016  Google, Inc.
+ * 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.
+ *
+ * Google Author(s): Sascha Brawer
+ */
+
+#ifndef HB_OT_COLOR_CPAL_TABLE_HH
+#define HB_OT_COLOR_CPAL_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+
+/*
+ * Following parts to be moved to a public header.
+ */
+
+/**
+ * hb_ot_color_t:
+ * ARGB data type for holding color values.
+ *
+ * Since: REPLACEME
+ */
+typedef uint32_t hb_ot_color_t;
+
+
+/**
+ * hb_ot_color_palette_flags_t:
+ * @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: default indicating that there is nothing special to note about a color palette.
+ * @HB_OT_COLOR_PALETTE_FLAG_FOR_LIGHT_BACKGROUND: flag indicating that the color palette is suitable for rendering text on light background.
+ * @HB_OT_COLOR_PALETTE_FLAG_FOR_DARK_BACKGROUND: flag indicating that the color palette is suitable for rendering text on dark background.
+ *
+ * Since: REPLACEME
+ */
+typedef enum { /*< flags >*/
+  HB_OT_COLOR_PALETTE_FLAG_DEFAULT = 0x00000000u,
+  HB_OT_COLOR_PALETTE_FLAG_FOR_LIGHT_BACKGROUND = 0x00000001u,
+  HB_OT_COLOR_PALETTE_FLAG_FOR_DARK_BACKGROUND = 0x00000002u,
+} hb_ot_color_palette_flags_t;
+
+// HB_EXTERN unsigned int
+// hb_ot_color_get_palette_count (hb_face_t *face);
+
+// HB_EXTERN unsigned int
+// hb_ot_color_get_palette_name_id (hb_face_t *face, unsigned int palette);
+
+// HB_EXTERN hb_ot_color_palette_flags_t
+// hb_ot_color_get_palette_flags (hb_face_t *face, unsigned int palette);
+
+// HB_EXTERN unsigned int
+// hb_ot_color_get_palette_colors (hb_face_t       *face,
+// 				unsigned int     palette, /* default=0 */
+// 				unsigned int     start_offset,
+// 				unsigned int    *color_count /* IN/OUT */,
+// 				hb_ot_color_t   *colors /* OUT */);
+
+
+
+
+
+/*
+ * Color Palette
+ * http://www.microsoft.com/typography/otspec/cpal.htm
+ */
+
+#define HB_OT_TAG_CPAL HB_TAG('C','P','A','L')
+
+namespace OT {
+
+
+struct CPALV1Tail
+{
+  friend struct CPAL;
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int palettes) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (
+      c->check_struct (this) &&
+      c->check_array ((const void*) &paletteFlags, sizeof (HBUINT32), palettes) &&
+      c->check_array ((const void*) &paletteLabel, sizeof (HBUINT16), palettes) &&
+      c->check_array ((const void*) &paletteEntryLabel, sizeof (HBUINT16), palettes));
+  }
+
+  private:
+  inline hb_ot_color_palette_flags_t
+  get_palette_flags (const void *base, unsigned int palette) const
+  {
+    const HBUINT32* flags = &paletteFlags (base);
+    return (hb_ot_color_palette_flags_t) (uint32_t) flags[palette];
+  }
+
+  inline unsigned int
+  get_palette_name_id (const void *base, unsigned int palette) const
+  {
+    const HBUINT16* name_ids = &paletteLabel (base);
+    return name_ids[palette];
+  }
+
+  protected:
+  LOffsetTo<HBUINT32> paletteFlags;
+  LOffsetTo<HBUINT16> paletteLabel;
+  LOffsetTo<HBUINT16> paletteEntryLabel;
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+typedef HBUINT32 BGRAColor;
+
+struct CPAL
+{
+  static const hb_tag_t tableTag = HB_OT_TAG_CPAL;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!(c->check_struct (this) && // This checks colorRecordIndicesX sanity also, see #get_size
+        c->check_array ((const void*) &colorRecordsZ, sizeof (BGRAColor), numColorRecords)))
+      return_trace (false);
+
+    // Check for indices sanity so no need for doing it runtime
+    for (unsigned int i = 0; i < numPalettes; ++i)
+      if (colorRecordIndicesX[i] + numPaletteEntries > numColorRecords)
+        return_trace (false);
+
+    // If version is zero, we are done here; otherwise we need to check tail also
+    if (version == 0)
+      return_trace (true);
+
+    const CPALV1Tail &v1 = StructAfter<CPALV1Tail> (*this);
+    return_trace (v1.sanitize (c, numPalettes));
+  }
+
+  inline unsigned int get_size (void) const
+  {
+    return min_size + numPalettes * sizeof (HBUINT16);
+  }
+
+  inline hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette) const
+  {
+    if (version == 0 || palette >= numPalettes)
+      return HB_OT_COLOR_PALETTE_FLAG_DEFAULT;
+
+    const CPALV1Tail& cpal1 = StructAfter<CPALV1Tail> (*this);
+    return cpal1.get_palette_flags (this, palette);
+  }
+
+  inline unsigned int get_palette_name_id (unsigned int palette) const
+  {
+    if (version == 0 || palette >= numPalettes)
+      return 0xFFFF;
+
+    const CPALV1Tail& cpal1 = StructAfter<CPALV1Tail> (*this);
+    return cpal1.get_palette_name_id (this, palette);
+  }
+
+  inline unsigned int get_palette_count () const
+  {
+    return numPalettes;
+  }
+
+  inline hb_ot_color_t get_color_record_argb (unsigned int color_index, unsigned int palette) const
+  {
+    if (color_index >= numPaletteEntries || palette >= numPalettes)
+      return 0;
+
+    const BGRAColor* records = &colorRecordsZ(this);
+    // No need for more range check as it is already done on #sanitize
+    return records[colorRecordIndicesX[palette] + color_index];
+  }
+
+  protected:
+  HBUINT16	version;
+  /* Version 0 */
+  HBUINT16	numPaletteEntries;
+  HBUINT16	numPalettes;
+  HBUINT16	numColorRecords;
+  LOffsetTo<HBUINT32>	colorRecordsZ;
+  HBUINT16	colorRecordIndicesX[VAR];  // VAR=numPalettes
+/*CPALV1Tail	v1[VAR];*/
+  public:
+  DEFINE_SIZE_ARRAY (12, colorRecordIndicesX);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_COLOR_CPAL_TABLE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-color.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright © 2016  Google, Inc.
+ * 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.
+ *
+ * Google Author(s): Sascha Brawer
+ */
+
+#include "hb-open-type-private.hh"
+#include "hb-ot-color-colr-table.hh"
+#include "hb-ot-color-cpal-table.hh"
+#include "hb-ot.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "hb-ot-layout-private.hh"
+#include "hb-shaper-private.hh"
+
+#if 0
+HB_MARK_AS_FLAG_T (hb_ot_color_palette_flags_t)
+//HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) Hmm?
+
+
+static inline const OT::COLR&
+_get_colr (hb_face_t *face)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::COLR);
+  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
+  return *(layout->colr.get ());
+}
+
+static inline const OT::CPAL&
+_get_cpal (hb_face_t *face)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::CPAL);
+  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
+  return *(layout->cpal.get ());
+}
+
+
+/**
+ * hb_ot_color_get_palette_count:
+ * @face: a font face.
+ *
+ * Returns: the number of color palettes in @face, or zero if @face has
+ * no colors.
+ *
+ * Since: REPLACEME
+ */
+unsigned int
+hb_ot_color_get_palette_count (hb_face_t *face)
+{
+  const OT::CPAL& cpal = _get_cpal (face);
+  return cpal.get_palette_count ();
+}
+
+
+/**
+ * hb_ot_color_get_palette_name_id:
+ * @face: a font face.
+ * @palette: the index of the color palette whose name is being requested.
+ *
+ * Retrieves the name id of a color palette. For example, a color font can
+ * have themed palettes like "Spring", "Summer", "Fall", and "Winter".
+ *
+ * Returns: an identifier within @face's `name` table.
+ * If the requested palette has no name, or if @face has no colors,
+ * or if @palette is not between 0 and hb_ot_color_get_palette_count(),
+ * the result is 0xFFFF. The implementation does not check whether
+ * the returned palette name id is actually in @face's `name` table.
+ *
+ * Since: REPLACEME
+ */
+unsigned int
+hb_ot_color_get_palette_name_id (hb_face_t *face, unsigned int palette)
+{
+  const OT::CPAL& cpal = _get_cpal (face);
+  return cpal.get_palette_name_id (palette);
+}
+
+
+/**
+ * hb_ot_color_get_palette_flags:
+ * @face: a font face
+ * @palette: the index of the color palette whose flags are being requested
+ *
+ * Returns: the flags for the requested color palette.  If @face has no colors,
+ * or if @palette is not between 0 and hb_ot_color_get_palette_count(),
+ * the result is #HB_OT_COLOR_PALETTE_FLAG_DEFAULT.
+ *
+ * Since: REPLACEME
+ */
+hb_ot_color_palette_flags_t
+hb_ot_color_get_palette_flags (hb_face_t *face, unsigned int palette)
+{
+  const OT::CPAL& cpal = _get_cpal(face);
+  return cpal.get_palette_flags (palette);
+}
+
+
+/**
+ * hb_ot_color_get_palette_colors:
+ * @face:         a font face.
+ * @palette:      the index of the color palette whose colors
+ *                are being requested.
+ * @start_offset: the index of the first color being requested.
+ * @color_count:  (inout) (optional): on input, how many colors
+ *                can be maximally stored into the @colors array;
+ *                on output, how many colors were actually stored.
+ * @colors: (array length=color_count) (optional):
+ *                an array of #hb_ot_color_t records. After calling
+ *                this function, @colors will be filled with
+ *                the palette colors. If @colors is NULL, the function
+ *                will just return the number of total colors
+ *                without storing any actual colors; this can be used
+ *                for allocating a buffer of suitable size before calling
+ *                hb_ot_color_get_palette_colors() a second time.
+ *
+ * Retrieves the colors in a color palette.
+ *
+ * Returns: the total number of colors in the palette. All palettes in
+ * a font have the same number of colors. If @face has no colors, or if
+ * @palette is not between 0 and hb_ot_color_get_palette_count(),
+ * the result is zero.
+ *
+ * Since: REPLACEME
+ */
+unsigned int
+hb_ot_color_get_palette_colors (hb_face_t       *face,
+				unsigned int     palette, /* default=0 */
+				unsigned int     start_offset,
+				unsigned int    *color_count /* IN/OUT */,
+				hb_ot_color_t   *colors /* OUT */)
+{
+  const OT::CPAL& cpal = _get_cpal(face);
+  if (unlikely (palette >= cpal.numPalettes))
+  {
+    if (color_count) *color_count = 0;
+    return 0;
+  }
+
+  const OT::ColorRecord* crec = &cpal.offsetFirstColorRecord (&cpal);
+  crec += cpal.colorRecordIndices[palette];
+
+  unsigned int num_results = 0;
+  if (likely (color_count && colors))
+  {
+    for (unsigned int i = start_offset;
+	 i < cpal.numPaletteEntries && num_results < *color_count; ++i)
+    {
+      hb_ot_color_t* result = &colors[num_results];
+      result->red = crec[i].red;
+      result->green = crec[i].green;
+      result->blue = crec[i].blue;
+      result->alpha = crec[i].alpha;
+      ++num_results;
+    }
+  }
+
+  if (likely (color_count)) *color_count = num_results;
+  return cpal.numPaletteEntries;
+}
+#endif
--- a/gfx/harfbuzz/src/hb-ot-font.cc
+++ b/gfx/harfbuzz/src/hb-ot-font.cc
@@ -26,22 +26,23 @@
 
 #include "hb-private.hh"
 
 #include "hb-ot.h"
 
 #include "hb-font-private.hh"
 
 #include "hb-ot-cmap-table.hh"
-#include "hb-ot-cbdt-table.hh"
 #include "hb-ot-glyf-table.hh"
 #include "hb-ot-hmtx-table.hh"
 #include "hb-ot-kern-table.hh"
 #include "hb-ot-post-table.hh"
 
+#include "hb-ot-color-cbdt-table.hh"
+
 
 struct hb_ot_font_t
 {
   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;
--- a/gfx/harfbuzz/src/hb-ot-glyf-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-glyf-table.hh
@@ -24,17 +24,19 @@
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_GLYF_TABLE_HH
 #define HB_OT_GLYF_TABLE_HH
 
 #include "hb-open-type-private.hh"
 #include "hb-ot-head-table.hh"
-
+#include "hb-subset-glyf.hh"
+#include "hb-subset-plan.hh"
+#include "hb-subset-private.hh"
 
 namespace OT {
 
 
 /*
  * loca -- Index to Location
  */
 
@@ -73,30 +75,164 @@ struct glyf
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* We don't check for anything specific here.  The users of the
      * struct do all the hard work... */
     return_trace (true);
   }
 
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_blob_t *glyf_prime = nullptr;
+    hb_blob_t *loca_prime = nullptr;
+
+    bool success = true;
+    bool use_short_loca = false;
+    if (hb_subset_glyf_and_loca (plan, &use_short_loca, &glyf_prime, &loca_prime)) {
+      success = success && hb_subset_plan_add_table (plan, HB_OT_TAG_glyf, glyf_prime);
+      success = success && hb_subset_plan_add_table (plan, HB_OT_TAG_loca, loca_prime);
+      success = success && _add_head_and_set_loca_version (plan->source, use_short_loca, plan->dest);
+    } else {
+      success = false;
+    }
+    hb_blob_destroy (loca_prime);
+    hb_blob_destroy (glyf_prime);
+
+    return success;
+  }
+
+  static bool
+  _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_t *dest)
+  {
+    hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (hb_face_reference_table (source, HB_OT_TAG_head));
+    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_prime->indexToLocFormat.set (use_short_loca ? 0 : 1);
+    bool success = hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob);
+
+    hb_blob_destroy (head_prime_blob);
+    return success;
+  }
+
   struct GlyphHeader
   {
     HBINT16		numberOfContours;	/* If the number of contours is
-					   * greater than or equal to zero,
-					   * this is a simple glyph; if negative,
-					   * this is a composite glyph. */
+                                                 * greater than or equal to zero,
+                                                 * this is a simple glyph; if negative,
+                                                 * this is a composite glyph. */
     FWORD		xMin;			/* Minimum x for coordinate data. */
     FWORD		yMin;			/* Minimum y for coordinate data. */
     FWORD		xMax;			/* Maximum x for coordinate data. */
     FWORD		yMax;			/* Maximum y for coordinate data. */
 
     DEFINE_SIZE_STATIC (10);
   };
 
+  struct CompositeGlyphHeader
+  {
+    static const uint16_t ARG_1_AND_2_ARE_WORDS =      0x0001;
+    static const uint16_t ARGS_ARE_XY_VALUES =         0x0002;
+    static const uint16_t ROUND_XY_TO_GRID =           0x0004;
+    static const uint16_t WE_HAVE_A_SCALE =            0x0008;
+    static const uint16_t MORE_COMPONENTS =            0x0020;
+    static const uint16_t WE_HAVE_AN_X_AND_Y_SCALE =   0x0040;
+    static const uint16_t WE_HAVE_A_TWO_BY_TWO =       0x0080;
+    static const uint16_t WE_HAVE_INSTRUCTIONS =       0x0100;
+    static const uint16_t USE_MY_METRICS =             0x0200;
+    static const uint16_t OVERLAP_COMPOUND =           0x0400;
+    static const uint16_t SCALED_COMPONENT_OFFSET =    0x0800;
+    static const uint16_t UNSCALED_COMPONENT_OFFSET =  0x1000;
+
+    HBUINT16 flags;
+    HBUINT16 glyphIndex;
+
+    inline unsigned int get_size (void) const
+    {
+      unsigned int size = min_size;
+      if (flags & ARG_1_AND_2_ARE_WORDS) {
+        // arg1 and 2 are int16
+        size += 4;
+      } else {
+        // arg1 and 2 are int8
+        size += 2;
+      }
+      if (flags & WE_HAVE_A_SCALE) {
+        // One x 16 bit (scale)
+        size += 2;
+      } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
+        // Two x 16 bit (xscale, yscale)
+        size += 4;
+      } else if (flags & WE_HAVE_A_TWO_BY_TWO) {
+        // Four x 16 bit (xscale, scale01, scale10, yscale)
+        size += 8;
+      }
+      return size;
+    }
+
+    struct Iterator
+    {
+      const char *glyph_start;
+      const char *glyph_end;
+      const CompositeGlyphHeader *current;
+
+      inline bool move_to_next ()
+      {
+	if (current->flags & CompositeGlyphHeader::MORE_COMPONENTS)
+	{
+	  const CompositeGlyphHeader *possible =
+	    &StructAfter<CompositeGlyphHeader, CompositeGlyphHeader> (*current);
+	  if (!in_range (possible))
+	    return false;
+	  current = possible;
+	  return true;
+	}
+	return false;
+      }
+
+      inline bool in_range (const CompositeGlyphHeader *composite) const
+      {
+	return (const char *) composite >= glyph_start
+	  && ((const char *) composite + CompositeGlyphHeader::min_size) <= glyph_end
+	  && ((const char *) composite + composite->get_size()) <= glyph_end;
+      }
+    };
+
+    static inline bool get_iterator (const char * glyph_data,
+				     unsigned int length,
+				     CompositeGlyphHeader::Iterator *iterator /* OUT */)
+    {
+      if (length < GlyphHeader::static_size)
+	return false; /* Empty glyph; zero extents. */
+
+      const GlyphHeader &glyph_header = StructAtOffset<GlyphHeader> (glyph_data, 0);
+      if (glyph_header.numberOfContours < 0)
+      {
+        const CompositeGlyphHeader *possible =
+	  &StructAfter<CompositeGlyphHeader, GlyphHeader> (glyph_header);
+
+	iterator->glyph_start = glyph_data;
+	iterator->glyph_end = (const char *) glyph_data + length;
+	if (!iterator->in_range (possible))
+          return false;
+        iterator->current = possible;
+        return true;
+      }
+
+      return false;
+    }
+
+    DEFINE_SIZE_MIN (4);
+  };
+
   struct accelerator_t
   {
     inline void init (hb_face_t *face)
     {
       hb_blob_t *head_blob = Sanitizer<head>().sanitize (face->reference_table (HB_OT_TAG_head));
       const head *head_table = Sanitizer<head>::lock_instance (head_blob);
       if ((unsigned int) head_table->indexToLocFormat > 1 || head_table->glyphDataFormat != 0)
       {
@@ -117,39 +253,196 @@ struct glyf
     }
 
     inline void fini (void)
     {
       hb_blob_destroy (loca_blob);
       hb_blob_destroy (glyf_blob);
     }
 
-    inline bool get_extents (hb_codepoint_t glyph,
-			     hb_glyph_extents_t *extents) const
+    /*
+     * 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
+    {
+      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,
+						 composite);
+    }
+
+    /* based on FontTools _g_l_y_f.py::trim */
+    inline bool remove_padding(unsigned int start_offset,
+                               unsigned int *end_offset) const
+    {
+      static const int FLAG_X_SHORT = 0x02;
+      static const int FLAG_Y_SHORT = 0x04;
+      static const int FLAG_REPEAT = 0x08;
+      static const int FLAG_X_SAME = 0x10;
+      static const int FLAG_Y_SAME = 0x20;
+
+      if (*end_offset - start_offset < GlyphHeader::static_size)
+        return true;
+
+      const char *glyph = ((const char *) glyf_table) + start_offset;
+      const char * const glyph_end = glyph + (*end_offset - start_offset);
+      const GlyphHeader &glyph_header = StructAtOffset<GlyphHeader> (glyph, 0);
+      int16_t num_contours = (int16_t) glyph_header.numberOfContours;
+
+      if (num_contours < 0)
+        /* Trimming for composites not implemented.
+         * If removing hints it falls out of that. */
+        return true;
+      else if (num_contours > 0)
+      {
+        /* simple glyph w/contours, possibly trimmable */
+        glyph += GlyphHeader::static_size + 2 * num_contours;
+
+        if (unlikely (glyph + 2 >= glyph_end)) return false;
+        uint16_t nCoordinates = (uint16_t) StructAtOffset<HBUINT16>(glyph - 2, 0) + 1;
+        uint16_t nInstructions = (uint16_t) StructAtOffset<HBUINT16>(glyph, 0);
+
+        glyph += 2 + nInstructions;
+        if (unlikely (glyph + 2 >= glyph_end)) return false;
+
+        unsigned int coordBytes = 0;
+        unsigned int coordsWithFlags = 0;
+        while (glyph < glyph_end)
+        {
+          uint8_t flag = (uint8_t) *glyph;
+          glyph++;
+
+          unsigned int repeat = 1;
+          if (flag & FLAG_REPEAT)
+          {
+            if (glyph >= glyph_end)
+            {
+              DEBUG_MSG(SUBSET, nullptr, "Bad flag");
+              return false;
+            }
+            repeat = ((uint8_t) *glyph) + 1;
+            glyph++;
+          }
+
+          unsigned int xBytes, yBytes;
+          xBytes = yBytes = 0;
+          if (flag & FLAG_X_SHORT)
+            xBytes = 1;
+          else if ((flag & FLAG_X_SAME) == 0)
+            xBytes = 2;
+
+          if (flag & FLAG_Y_SHORT)
+            yBytes = 1;
+          else if ((flag & FLAG_Y_SAME) == 0)
+            yBytes = 2;
+
+          coordBytes += (xBytes + yBytes) * repeat;
+          coordsWithFlags += repeat;
+          if (coordsWithFlags >= nCoordinates)
+            break;
+        }
+
+        if (coordsWithFlags != nCoordinates)
+        {
+          DEBUG_MSG(SUBSET, nullptr, "Expect %d coords to have flags, got flags for %d", nCoordinates, coordsWithFlags);
+          return false;
+        }
+        glyph += coordBytes;
+
+        if (glyph < glyph_end)
+          *end_offset -= glyph_end - glyph;
+      }
+      return true;
+    }
+
+    inline bool get_offsets (hb_codepoint_t  glyph,
+                             unsigned int   *start_offset /* OUT */,
+                             unsigned int   *end_offset   /* OUT */) const
     {
       if (unlikely (glyph >= num_glyphs))
 	return false;
 
-      unsigned int start_offset, end_offset;
       if (short_offset)
       {
         const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataX;
-	start_offset = 2 * offsets[glyph];
-	end_offset   = 2 * offsets[glyph + 1];
+	*start_offset = 2 * offsets[glyph];
+	*end_offset   = 2 * offsets[glyph + 1];
       }
       else
       {
         const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataX;
-	start_offset = offsets[glyph];
-	end_offset   = offsets[glyph + 1];
+
+	*start_offset = offsets[glyph];
+	*end_offset   = offsets[glyph + 1];
       }
 
-      if (start_offset > end_offset || end_offset > glyf_len)
+      if (*start_offset > *end_offset || *end_offset > glyf_len)
 	return false;
 
+      return true;
+    }
+
+    inline bool get_instruction_offsets(unsigned int start_offset,
+                                        unsigned int end_offset,
+                                        unsigned int *instruction_start /* OUT */,
+                                        unsigned int *instruction_end /* OUT */) const
+    {
+      if (end_offset - start_offset < GlyphHeader::static_size)
+      {
+        *instruction_start = 0;
+        *instruction_end = 0;
+        return true; /* Empty glyph; no instructions. */
+      }
+      const GlyphHeader &glyph_header = StructAtOffset<GlyphHeader> (glyf_table, start_offset);
+      int16_t num_contours = (int16_t) glyph_header.numberOfContours;
+      if (num_contours < 0)
+      {
+        CompositeGlyphHeader::Iterator composite_it;
+        if (unlikely (!CompositeGlyphHeader::get_iterator (
+            (const char*) this->glyf_table + start_offset,
+             end_offset - start_offset, &composite_it))) return false;
+        const CompositeGlyphHeader *last;
+        do {
+          last = composite_it.current;
+        } while (composite_it.move_to_next());
+
+        if ( (uint16_t) last->flags & CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS)
+          *instruction_start = ((char *) last - (char *) glyf_table->dataX) + last->get_size();
+        else
+          *instruction_start = end_offset;
+        *instruction_end = end_offset;
+        if (unlikely (*instruction_start > *instruction_end))
+        {
+          DEBUG_MSG(SUBSET, nullptr, "Invalid instruction offset, %d is outside [%d, %d]", *instruction_start, start_offset, end_offset);
+          return false;
+        }
+      }
+      else
+      {
+        unsigned int instruction_length_offset = start_offset + GlyphHeader::static_size + 2 * num_contours;
+        const HBUINT16 &instruction_length = StructAtOffset<HBUINT16> (glyf_table, instruction_length_offset);
+        *instruction_start = instruction_length_offset + 2;
+        *instruction_end = *instruction_start + (uint16_t) instruction_length;
+      }
+      return true;
+    }
+
+    inline bool get_extents (hb_codepoint_t glyph,
+			     hb_glyph_extents_t *extents) const
+    {
+      unsigned int start_offset, end_offset;
+      if (!get_offsets (glyph, &start_offset, &end_offset))
+        return false;
+
       if (end_offset - start_offset < GlyphHeader::static_size)
 	return true; /* Empty glyph; zero extents. */
 
       const GlyphHeader &glyph_header = StructAtOffset<GlyphHeader> (glyf_table, start_offset);
 
       extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax);
       extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax);
       extents->width     = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing;
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-hdmx-table.hh
@@ -0,0 +1,198 @@
+/*
+ * 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): Garret Rieger
+ */
+
+#ifndef HB_OT_HDMX_TABLE_HH
+#define HB_OT_HDMX_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+namespace OT {
+
+
+/*
+ * hdmx - Horizontal Device Metric
+ */
+
+#define HB_OT_TAG_hdmx HB_TAG('h','d','m','x')
+
+struct DeviceRecord
+{
+  struct SubsetView
+  {
+    const DeviceRecord *source_device_record;
+    hb_subset_plan_t *subset_plan;
+
+    inline void init(const DeviceRecord *source_device_record,
+		     hb_subset_plan_t   *subset_plan)
+    {
+      this->source_device_record = source_device_record;
+      this->subset_plan = subset_plan;
+    }
+
+    inline unsigned int len () const
+    {
+      return this->subset_plan->gids_to_retain_sorted.len;
+    }
+
+    inline const HBUINT8& operator [] (unsigned int i) const
+    {
+      if (unlikely (i >= len())) return Null(HBUINT8);
+      hb_codepoint_t gid = this->subset_plan->gids_to_retain_sorted [i];
+      return this->source_device_record->widths[gid];
+    }
+  };
+
+  static inline unsigned int get_size (unsigned int count)
+  {
+    unsigned int raw_size = min_size + count * HBUINT8::static_size;
+    if (raw_size % 4)
+      /* Align to 32 bits */
+      return raw_size + (4 - (raw_size % 4));
+    return raw_size;
+  }
+
+  inline bool serialize (hb_serialize_context_t *c, const SubsetView &subset_view)
+  {
+    TRACE_SERIALIZE (this);
+
+    if (unlikely (!c->allocate_size<DeviceRecord> (get_size (subset_view.len()))))
+      return_trace (false);
+
+    this->pixel_size.set (subset_view.source_device_record->pixel_size);
+    this->max_width.set (subset_view.source_device_record->max_width);
+
+    for (unsigned int i = 0; i < subset_view.len(); i++)
+      widths[i].set (subset_view[i]);
+
+    return_trace (true);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int size_device_record) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  c->check_range (this, size_device_record)));
+  }
+
+  HBUINT8 pixel_size;   /* Pixel size for following widths (as ppem). */
+  HBUINT8 max_width;    /* Maximum width. */
+  HBUINT8 widths[VAR];  /* Array of widths (numGlyphs is from the 'maxp' table). */
+  public:
+  DEFINE_SIZE_ARRAY (2, widths);
+};
+
+
+struct hdmx
+{
+  static const hb_tag_t tableTag = HB_OT_TAG_hdmx;
+
+  inline unsigned int get_size (void) const
+  {
+    return min_size + num_records * size_device_record;
+  }
+
+  inline const DeviceRecord& operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= num_records)) return Null(DeviceRecord);
+    return StructAtOffset<DeviceRecord> (this, min_size + i * size_device_record);
+  }
+
+  inline bool serialize (hb_serialize_context_t *c, const hdmx *source_hdmx, hb_subset_plan_t *plan)
+  {
+    TRACE_SERIALIZE (this);
+
+    if (unlikely (!c->extend_min ((*this))))  return_trace (false);
+
+    this->version.set (source_hdmx->version);
+    this->num_records.set (source_hdmx->num_records);
+    this->size_device_record.set (DeviceRecord::get_size (plan->gids_to_retain_sorted.len));
+
+    for (unsigned int i = 0; i < source_hdmx->num_records; i++)
+    {
+      DeviceRecord::SubsetView subset_view;
+      subset_view.init (&(*source_hdmx)[i], plan);
+
+      c->start_embed<DeviceRecord> ()->serialize (c, subset_view);
+    }
+
+    return_trace (true);
+  }
+
+  static inline size_t get_subsetted_size (hb_subset_plan_t *plan)
+  {
+    return min_size + DeviceRecord::get_size (plan->gids_to_retain_sorted.len);
+  }
+
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    size_t dest_size = get_subsetted_size (plan);
+    hdmx *dest = (hdmx *) malloc (dest_size);
+    if (unlikely (!dest))
+    {
+      DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for hdmx subset output.", (unsigned long) dest_size);
+      return false;
+    }
+
+    hb_serialize_context_t c (dest, dest_size);
+    hdmx *hdmx_prime = c.start_serialize<hdmx> ();
+    if (!hdmx_prime || !hdmx_prime->serialize (&c, this, plan)) {
+      free (dest);
+      return false;
+    }
+    c.end_serialize ();
+
+    hb_blob_t *hdmx_prime_blob = hb_blob_create ((const char *) dest,
+						 dest_size,
+						 HB_MEMORY_MODE_READONLY,
+						 dest,
+						 free);
+    bool result = hb_subset_plan_add_table (plan, HB_OT_TAG_hdmx, hdmx_prime_blob);
+    hb_blob_destroy (hdmx_prime_blob);
+
+    return result;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && version == 0 &&
+		  !_hb_unsigned_int_mul_overflows (num_records, size_device_record) &&
+		  c->check_range (this, get_size()));
+  }
+
+  protected:
+  HBUINT16	version;		/* Table version number (0) */
+  HBUINT16	num_records;		/* Number of device records. */
+  HBUINT32	size_device_record;	/* Size of a device record, 32-bit aligned. */
+  HBUINT8	data[VAR];		/* Array of device records. */
+  public:
+  DEFINE_SIZE_ARRAY (8, data);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_HDMX_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-head-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-head-table.hh
@@ -38,16 +38,18 @@ namespace OT {
 /*
  * head -- Font Header
  */
 
 #define HB_OT_TAG_head HB_TAG('h','e','a','d')
 
 struct head
 {
+  friend struct OffsetTable;
+
   static const hb_tag_t tableTag	= HB_OT_TAG_head;
 
   inline unsigned int get_upem (void) const
   {
     unsigned int upem = unitsPerEm;
     /* If no valid head table found, assume 1000, which matches typical Type1 usage. */
     return 16 <= upem && upem <= 16384 ? upem : 1000;
   }
--- a/gfx/harfbuzz/src/hb-ot-hhea-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hhea-table.hh
@@ -36,17 +36,17 @@ namespace OT {
 /*
  * hhea -- The Horizontal Header Table
  * vhea -- The Vertical Header Table
  */
 
 #define HB_OT_TAG_hhea HB_TAG('h','h','e','a')
 #define HB_OT_TAG_vhea HB_TAG('v','h','e','a')
 
-
+template <typename T>
 struct _hea
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && likely (version.major == 1));
   }
 
@@ -79,20 +79,20 @@ struct _hea
   HBINT16		reserved4;		/* Set to 0. */
   HBINT16		metricDataFormat;	/* 0 for current format. */
   HBUINT16	numberOfLongMetrics;	/* Number of LongMetric entries in metric
 					 * table. */
   public:
   DEFINE_SIZE_STATIC (36);
 };
 
-struct hhea : _hea {
+struct hhea : _hea<hhea> {
   static const hb_tag_t tableTag	= HB_OT_TAG_hhea;
 };
-struct vhea : _hea {
+struct vhea : _hea<vhea> {
   static const hb_tag_t tableTag	= HB_OT_TAG_vhea;
 };
 
 
 } /* namespace OT */
 
 
 #endif /* HB_OT_HHEA_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
@@ -16,17 +16,17 @@
  * 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
+ * Google Author(s): Behdad Esfahbod, Roderick Sheeter
  */
 
 #ifndef HB_OT_HMTX_TABLE_HH
 #define HB_OT_HMTX_TABLE_HH
 
 #include "hb-open-type-private.hh"
 #include "hb-ot-hhea-table.hh"
 #include "hb-ot-os2-table.hh"
@@ -48,65 +48,176 @@ namespace OT {
 struct LongMetric
 {
   UFWORD	advance; /* Advance width/height. */
   FWORD		lsb; /* Leading (left/top) side bearing. */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
-template <typename T>
+template <typename T, typename H>
 struct hmtxvmtx
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* We don't check for anything specific here.  The users of the
      * 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 *dest_blob = hb_blob_copy_writable_or_fail(src_blob);
+    hb_blob_destroy (src_blob);
+
+    if (unlikely (!dest_blob)) {
+      return false;
+    }
+
+    unsigned int length;
+    H *table = (H *) hb_blob_get_data (dest_blob, &length);
+    table->numberOfLongMetrics.set (num_hmetrics);
+
+    bool result = hb_subset_plan_add_table (plan, H::tableTag, dest_blob);
+    hb_blob_destroy (dest_blob);
+
+    return result;
+  }
+
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    typename T::accelerator_t _mtx;
+    _mtx.init (plan->source);
+
+    /* All the trailing glyphs with the same advance can use one LongMetric
+     * and just keep LSB */
+    hb_prealloced_array_t<hb_codepoint_t> &gids = plan->gids_to_retain_sorted;
+    unsigned int num_advances = gids.len;
+    unsigned int last_advance = _mtx.get_advance (gids[num_advances - 1]);
+    while (num_advances > 1
+        && last_advance == _mtx.get_advance (gids[num_advances - 2]))
+    {
+      num_advances--;
+    }
+
+    /* alloc the new table */
+    size_t dest_sz = num_advances * 4
+                  + (gids.len - num_advances) * 2;
+    void *dest = (void *) malloc (dest_sz);
+    if (unlikely (!dest))
+    {
+      return false;
+    }
+    DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in src has %d advances, %d lsbs", HB_UNTAG(T::tableTag), _mtx.num_advances, _mtx.num_metrics - _mtx.num_advances);
+    DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %u bytes", HB_UNTAG(T::tableTag), num_advances, gids.len - num_advances, (unsigned int) dest_sz);
+
+    const char *source_table = hb_blob_get_data (_mtx.blob, nullptr);
+    // Copy everything over
+    LongMetric * old_metrics = (LongMetric *) source_table;
+    FWORD *lsbs = (FWORD *) (old_metrics + _mtx.num_advances);
+    char * dest_pos = (char *) dest;
+    for (unsigned int i = 0; i < gids.len; i++)
+    {
+      /* the last metric or the one for gids[i] */
+      LongMetric *src_metric = old_metrics + MIN ((hb_codepoint_t) _mtx.num_advances - 1, gids[i]);
+      if (gids[i] < _mtx.num_advances)
+      {
+        /* src is a LongMetric */
+        if (i < num_advances)
+        {
+          /* dest is a LongMetric, copy it */
+          *((LongMetric *) dest_pos) = *src_metric;
+        }
+        else
+        {
+          /* dest just lsb */
+          *((FWORD *) dest_pos) = src_metric->lsb;
+        }
+      }
+      else
+      {
+        FWORD src_lsb = *(lsbs + gids[i] - _mtx.num_advances);
+        if (i < num_advances)
+        {
+          /* dest needs a full LongMetric */
+          LongMetric *metric = (LongMetric *)dest_pos;
+          metric->advance = src_metric->advance;
+          metric->lsb = src_lsb;
+        }
+        else
+        {
+          /* dest just needs an lsb */
+          *((FWORD *) dest_pos) = src_lsb;
+        }
+      }
+      dest_pos += (i < num_advances ? 4 : 2);
+    }
+    _mtx.fini ();
+
+    // Amend header num hmetrics
+    if (unlikely (!subset_update_header (plan, num_advances)))
+    {
+      free (dest);
+      return false;
+    }
+
+    hb_blob_t *result = hb_blob_create ((const char *)dest,
+                                        dest_sz,
+                                        HB_MEMORY_MODE_READONLY,
+                                        dest,
+                                        free);
+    bool success = hb_subset_plan_add_table (plan, T::tableTag, result);
+    hb_blob_destroy (result);
+    return success;
+  }
+
   struct accelerator_t
   {
+    friend struct hmtxvmtx;
+
     inline void init (hb_face_t *face,
 		      unsigned int default_advance_ = 0)
     {
-      default_advance = default_advance_ ? default_advance_ : face->get_upem ();
+      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 = Sanitizer<os2> ().sanitize (face->reference_table (T::os2Tag));
 	const os2 *os2_table = Sanitizer<os2>::lock_instance (os2_blob);
 #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<_hea>().sanitize (face->reference_table (T::headerTag));
-      const _hea *_hea_table = Sanitizer<_hea>::lock_instance (_hea_blob);
+      hb_blob_t *_hea_blob = Sanitizer<H> ().sanitize (face->reference_table (H::tableTag));
+      const H *_hea_table = Sanitizer<H>::lock_instance (_hea_blob);
       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 = Sanitizer<hmtxvmtx> ().sanitize (face->reference_table (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.
@@ -114,96 +225,104 @@ struct hmtxvmtx
       if (unlikely (!num_advances))
       {
 	num_metrics = num_advances = 0;
 	hb_blob_destroy (blob);
 	blob = hb_blob_get_empty ();
       }
       table = Sanitizer<hmtxvmtx>::lock_instance (blob);
 
-      var_blob = Sanitizer<HVARVVAR>().sanitize (face->reference_table (T::variationsTag));
+      var_blob = Sanitizer<HVARVVAR> ().sanitize (face->reference_table (T::variationsTag));
       var_table = Sanitizer<HVARVVAR>::lock_instance (var_blob);
     }
 
     inline void fini (void)
     {
       hb_blob_destroy (blob);
       hb_blob_destroy (var_blob);
     }
 
-    inline unsigned int get_advance (hb_codepoint_t  glyph,
-				     hb_font_t      *font) const
+    inline unsigned int get_advance (hb_codepoint_t  glyph) const
     {
       if (unlikely (glyph >= num_metrics))
       {
-	/* If num_metrics is zero, it means we don't have the metrics table
-	 * for this direction: return default advance.  Otherwise, it means that the
-	 * glyph index is out of bound: return zero. */
-	if (num_metrics)
-	  return 0;
-	else
-	  return default_advance;
+        /* If num_metrics is zero, it means we don't have the metrics table
+         * for this direction: return default advance.  Otherwise, it means that the
+         * glyph index is out of bound: return zero. */
+        if (num_metrics)
+          return 0;
+        else
+          return default_advance;
       }
 
-      return table->longMetric[MIN (glyph, (uint32_t) num_advances - 1)].advance
-	   + (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?!
+      return table->longMetric[MIN (glyph, (uint32_t) num_advances - 1)].advance;
+    }
+
+    inline unsigned int get_advance (hb_codepoint_t  glyph,
+                                     hb_font_t      *font) const
+    {
+      unsigned int advance = get_advance (glyph);
+      if (likely(glyph < num_metrics))
+      {
+        advance += (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?!
+      }
+      return advance;	        
     }
 
     public:
     bool has_font_extents;
     unsigned short ascender;
     unsigned short descender;
     unsigned short line_gap;
 
-    private:
+    protected:
     unsigned int num_metrics;
     unsigned int num_advances;
     unsigned int default_advance;
 
+    private:
     const hmtxvmtx *table;
     hb_blob_t *blob;
     const HVARVVAR *var_table;
     hb_blob_t *var_blob;
   };
 
   protected:
   LongMetric	longMetric[VAR];	/* Paired advance width and leading
 					 * bearing values for each glyph. The
 					 * value numOfHMetrics comes from
 					 * the 'hhea' table. If the font is
 					 * monospaced, only one entry need
 					 * be in the array, but that entry is
 					 * required. The last entry applies to
 					 * all subsequent glyphs. */
-  FWORD		leadingBearingX[VAR];	/* Here the advance is assumed
+/*FWORD		leadingBearingX[VAR];*/	/* Here the advance is assumed
 					 * to be the same as the advance
 					 * for the last entry above. The
 					 * number of entries in this array is
 					 * derived from numGlyphs (from 'maxp'
 					 * table) minus numberOfLongMetrics.
 					 * This generally is used with a run
 					 * of monospaced glyphs (e.g., Kanji
 					 * fonts or Courier fonts). Only one
 					 * run is allowed and it must be at
 					 * the end. This allows a monospaced
 					 * font to vary the side bearing
 					 * values for each glyph. */
   public:
-  DEFINE_SIZE_ARRAY2 (0, longMetric, leadingBearingX);
+  DEFINE_SIZE_ARRAY (0, longMetric);
 };
 
-struct hmtx : hmtxvmtx<hmtx> {
+struct hmtx : hmtxvmtx<hmtx, hhea> {
   static const hb_tag_t tableTag	= HB_OT_TAG_hmtx;
-  static const hb_tag_t headerTag	= HB_OT_TAG_hhea;
   static const hb_tag_t variationsTag	= HB_OT_TAG_HVAR;
   static const hb_tag_t os2Tag		= HB_OT_TAG_os2;
 };
-struct vmtx : hmtxvmtx<vmtx> {
+struct vmtx : hmtxvmtx<vmtx, vhea> {
   static const hb_tag_t tableTag	= HB_OT_TAG_vmtx;
-  static const hb_tag_t headerTag	= HB_OT_TAG_vhea;
   static const hb_tag_t variationsTag	= HB_OT_TAG_VVAR;
   static const hb_tag_t os2Tag		= HB_TAG_NONE;
 };
 
 } /* namespace OT */
 
 
 #endif /* HB_OT_HMTX_TABLE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-layout-base-table.hh
@@ -0,0 +1,655 @@
+/*
+ * Copyright © 2016 Elie Roux <elie.roux@telecom-bretagne.eu>
+ * 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_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 -- The BASE Table
+ */
+
+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 */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct BaseCoordFormat2
+{
+  inline int get_coord (void) const
+  {
+    /* TODO */
+    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 = 2 */
+  HBINT16	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
+{
+  inline int get_coord (void) const
+  {
+    /* TODO */
+    return coordinate;
+  }
+
+  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 */
+  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
+  {
+    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;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format) {
+    case 1: return_trace (u.format1.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    case 3: return_trace (u.format3.sanitize (c));
+    default:return_trace (false);
+    }
+  }
+
+  protected:
+  union {
+    HBUINT16		format;
+    BaseCoordFormat1	format1;
+    BaseCoordFormat2	format2;
+    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_max_value (void) const
+  { return (this+maxCoord).get_coord(); }
+
+  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));
+  }
+
+  protected:
+  Tag                   tag;		/* 4-byte feature identification tag--must
+					 * match feature tag in FeatureList */
+  OffsetTo<BaseCoord>   minCoord;	/* Offset to BaseCoord table that defines
+					 * the minimum extent value, from beginning
+					 * of MinMax table (may be NULL) */
+  OffsetTo<BaseCoord>   maxCoord;	/* Offset to BaseCoord table that defines
+					 * the maximum extent value, from beginning
+					 * of MinMax table (may be NULL) */
+  public:
+  DEFINE_SIZE_STATIC (8);
+
+};
+
+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();
+      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
+  {
+    if (featureTableTagIndex == NOT_INDEXED)
+      return (this+minCoord).get_coord();
+    return featMinMaxRecords[featureTableTagIndex].get_min_value();
+  }
+
+  inline int get_max_value (unsigned int featureTableTagIndex) const
+  {
+    if (featureTableTagIndex == NOT_INDEXED)
+      return (this+maxCoord).get_coord();
+    return featMinMaxRecords[featureTableTagIndex].get_max_value();
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  minCoord.sanitize (c, this) &&
+		  maxCoord.sanitize (c, this) &&
+		  featMinMaxRecords.sanitize (c, this));
+  }
+
+  protected:
+  OffsetTo<BaseCoord>	minCoord;	/* Offset to BaseCoord table that defines
+					 * minimum extent value, from the beginning
+					 * of MinMax table (may be NULL) */
+  OffsetTo<BaseCoord>	maxCoord;	/* Offset to BaseCoord table that defines
+					 * maximum extent value, from the beginning
+					 * of MinMax table (may be NULL) */
+  ArrayOf<FeatMinMaxRecord>
+		featMinMaxRecords;	/* Array of FeatMinMaxRecords, in alphabetical
+					 * order by featureTableTag */
+  public:
+  DEFINE_SIZE_ARRAY (6, featMinMaxRecords);
+};
+
+/* 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); }
+
+  inline int get_min_value (unsigned int featureTableTagIndex) const
+  { return (this+minMax).get_min_value(featureTableTagIndex); }
+
+  inline int get_max_value (unsigned int featureTableTagIndex) const
+  { 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));
+  }
+
+  protected:
+  Tag			baseLangSysTag;
+  OffsetTo<MinMax>	minMax;
+  public:
+  DEFINE_SIZE_STATIC (6);
+
+};
+
+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();
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+      defaultIndex <= baseCoordCount &&
+      baseCoords.sanitize (c, this));
+  }
+
+  protected:
+  Index				defaultIndex;
+  HBUINT16			baseCoordCount;
+  OffsetArrayOf<BaseCoord>	baseCoords;
+  public:
+  DEFINE_SIZE_ARRAY (6, baseCoords);
+
+};
+
+struct BaseScript {
+
+  inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const
+  {
+    Tag tag;
+    int cmp;
+    for (unsigned int i = 0; i < baseLangSysCount; 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 (unlikely(defaultMinMax)) return NOT_INDEXED;
+      return (this+defaultMinMax).get_feature_tag_index(featureTableTag);
+    }
+    if (unlikely(baseLangSysIndex >= baseLangSysCount)) return NOT_INDEXED;
+    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);
+  }
+
+  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);
+  }
+
+  inline unsigned int get_default_base_tag_index (void) const
+  { return (this+baseValues).get_default_base_tag_index(); }
+
+  inline int get_base_coord (unsigned int baselineTagIndex) const
+  { 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));
+  }
+
+  protected:
+  OffsetTo<BaseValues>        baseValues;
+  OffsetTo<MinMax>            defaultMinMax;
+  HBUINT16                      baseLangSysCount;
+  ArrayOf<BaseLangSysRecord>  baseLangSysRecords;
+
+  public:
+    DEFINE_SIZE_ARRAY (8, 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(); }
+
+  inline int get_base_coord(unsigned int baselineTagIndex) const
+  { 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); }
+
+  inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const
+  { 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); }
+
+  inline int get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  { 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));
+  }
+
+  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++)
+      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);
+  }
+
+  inline int get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  {
+    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));
+  }
+
+  protected:
+  HBUINT16                    baseScriptCount;
+  ArrayOf<BaseScriptRecord> baseScriptRecords;
+
+  public:
+  DEFINE_SIZE_ARRAY (4, baseScriptRecords);
+
+};
+
+struct BaseTagList
+{
+
+  inline unsigned int get_tag_index(Tag baselineTag) const
+  {
+    for (unsigned int i = 0; i < baseTagCount; 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);
+};
+
+struct Axis
+{
+
+  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
+  {
+    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);
+  }
+
+  inline int get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  {
+    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));
+  }
+
+  protected:
+  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_horiz_axis(void)
+  { return horizAxis != Null(OffsetTo<Axis>); }
+
+  // horizontal axis base coords:
+
+  inline unsigned int get_horiz_base_tag_index(Tag baselineTag) const
+  {
+    if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
+    return (this+horizAxis).get_base_tag_index(baselineTag);
+  }
+
+  inline unsigned int get_horiz_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);
+  }
+
+  inline int get_horiz_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  {
+    return (this+horizAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
+  }
+
+  // vertical axis base coords:
+
+  inline unsigned int get_vert_base_tag_index(Tag baselineTag) const
+  {
+    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
+    return (this+vertAxis).get_base_tag_index(baselineTag);
+  }
+
+  inline unsigned int get_vert_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);
+  }
+
+  inline int get_vert_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  {
+    return (this+vertAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
+  }
+
+  // horizontal axis min/max coords:
+
+  inline unsigned int get_horiz_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);
+  }
+
+  inline unsigned int get_horiz_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);
+  }
+
+  inline int get_horiz_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  {
+    return (this+horizAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+  }
+
+  inline int get_horiz_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  {
+    return (this+horizAxis).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
+  {
+    if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NOT_INDEXED;
+    return (this+vertAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
+  }
+
+  inline unsigned int get_vert_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);
+  }
+
+  inline int get_vert_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  {
+    return (this+vertAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
+  }
+
+  inline int get_vert_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  {
+    return (this+vertAxis).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) &&
+		  (version.to_int () < 0x00010001u || varStore.sanitize (c, this)));
+  }
+
+  protected:
+  FixedVersion<>  version;
+  OffsetTo<Axis>  horizAxis;
+  OffsetTo<Axis>  vertAxis;
+  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);
+};
+
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_LAYOUT_BASE_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -294,17 +294,17 @@ struct FeatureParamsSize
      * Registry". You can see a copy of this at:
      * http://partners.adobe.com/public/developer/opentype/index_tag8.html#size
      *
      * Here is one set of rules to determine if the 'size' feature is built
      * correctly, or as by the older versions of MakeOTF. You may be able to do
      * better.
      *
      * Assume that the offset to the size feature is according to specification,
-     * and make the following value checks. If it fails, assume the the size
+     * and make the following value checks. If it fails, assume the size
      * feature is calculated as versions of MakeOTF before the AFDKO 2.0 built it.
      * If this fails, reject the 'size' feature. The older makeOTF's calculated the
      * offset from the beginning of the FeatureList table, rather than from the
      * beginning of the 'size' Feature table.
      *
      * If "design size" == 0:
      *     fails check
      *
@@ -695,17 +695,17 @@ struct CoverageFormat1
 			 unsigned int num_glyphs)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
     glyphArray.len.set (num_glyphs);
     if (unlikely (!c->extend (glyphArray))) return_trace (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
       glyphArray[i] = glyphs[i];
-    glyphs.advance (num_glyphs);
+    glyphs += num_glyphs;
     return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (glyphArray.sanitize (c));
   }
@@ -784,17 +784,17 @@ struct CoverageFormat2
       if (glyphs[i - 1] + 1 != glyphs[i]) {
 	range++;
 	rangeRecord[range].start = glyphs[i];
 	rangeRecord[range].value.set (i);
         rangeRecord[range].end = glyphs[i];
       } else {
         rangeRecord[range].end = glyphs[i];
       }
-    glyphs.advance (num_glyphs);
+    glyphs += num_glyphs;
     return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (rangeRecord.sanitize (c));
   }
@@ -1052,17 +1052,17 @@ struct ClassDefFormat1
     return true;
   }
 
   inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
     unsigned int count = classValue.len;
     if (klass == 0)
     {
       /* Match if there's any glyph that is not listed! */
-      hb_codepoint_t g = -1;
+      hb_codepoint_t g = HB_SET_VALUE_INVALID;
       if (!hb_set_next (glyphs, &g))
         return false;
       if (g < startGlyph)
         return true;
       g = startGlyph + count - 1;
       if (hb_set_next (glyphs, &g))
         return true;
       /* Fall through. */
@@ -1123,26 +1123,26 @@ struct ClassDefFormat2
     return true;
   }
 
   inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
     unsigned int count = rangeRecord.len;
     if (klass == 0)
     {
       /* Match if there's any glyph that is not listed! */
-      hb_codepoint_t g = (hb_codepoint_t) -1;
+      hb_codepoint_t g = HB_SET_VALUE_INVALID;
       for (unsigned int i = 0; i < count; i++)
       {
 	if (!hb_set_next (glyphs, &g))
 	  break;
 	if (g < rangeRecord[i].start)
 	  return true;
 	g = rangeRecord[i].end;
       }
-      if (g != (hb_codepoint_t) -1 && hb_set_next (glyphs, &g))
+      if (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g))
         return true;
       /* Fall through. */
     }
     for (unsigned int i = 0; i < count; i++)
       if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs))
         return true;
     return false;
   }
--- a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
@@ -105,17 +105,17 @@ struct CaretValueFormat1
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
   protected:
   HBUINT16	caretValueFormat;	/* 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 CaretValueFormat2
 {
   friend struct CaretValue;
 
@@ -156,17 +156,17 @@ struct CaretValueFormat3
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && deviceTable.sanitize (c, this));
   }
 
   protected:
   HBUINT16	caretValueFormat;	/* 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 CaretValue
 					 * table */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -94,17 +94,17 @@ struct ValueFormat : HBUINT16
 					 * horizontal advance--measured from
 					 * beginning of PosTable (may be NULL) */
   Offset	yAdvDevice;		/* Offset to Device table for vertical
 					 * advance--measured from beginning of
 					 * PosTable (may be NULL) */
 #endif
 
   inline unsigned int get_len (void) const
-  { return _hb_popcount32 ((unsigned int) *this); }
+  { return _hb_popcount ((unsigned int) *this); }
   inline unsigned int get_size (void) const
   { return get_len () * Value::static_size; }
 
   void apply_value (hb_ot_apply_context_t   *c,
 		    const void           *base,
 		    const Value          *values,
 		    hb_glyph_position_t  &glyph_pos) const
   {
@@ -243,18 +243,18 @@ struct AnchorFormat1
   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		xCoordinate;		/* Horizontal value--in design units */
-  HBINT16		yCoordinate;		/* Vertical value--in design units */
+  FWORD		xCoordinate;		/* Horizontal value--in design units */
+  FWORD		yCoordinate;		/* Vertical value--in design units */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 struct AnchorFormat2
 {
   inline void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
 			  float *x, float *y) const
@@ -274,18 +274,18 @@ struct AnchorFormat2
   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		xCoordinate;		/* Horizontal value--in design units */
-  HBINT16		yCoordinate;		/* Vertical value--in design units */
+  FWORD		xCoordinate;		/* Horizontal value--in design units */
+  FWORD		yCoordinate;		/* Vertical value--in design units */
   HBUINT16	anchorPoint;		/* Index to glyph contour point */
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
 struct AnchorFormat3
 {
   inline void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
@@ -304,18 +304,18 @@ struct AnchorFormat3
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
   }
 
   protected:
   HBUINT16	format;			/* Format identifier--format = 3 */
-  HBINT16		xCoordinate;		/* Horizontal value--in design units */
-  HBINT16		yCoordinate;		/* Vertical value--in design units */
+  FWORD		xCoordinate;		/* Horizontal value--in design units */
+  FWORD		yCoordinate;		/* Vertical value--in design units */
   OffsetTo<Device>
 		xDeviceTable;		/* Offset to Device table for X
 					 * coordinate-- from beginning of
 					 * Anchor table (may be NULL) */
   OffsetTo<Device>
 		yDeviceTable;		/* Offset to Device table for Y
 					 * coordinate-- from beginning of
 					 * Anchor table (may be NULL) */
@@ -1067,19 +1067,27 @@ struct MarkBasePosFormat1
     if (likely (mark_index == NOT_COVERED)) return_trace (false);
 
     /* Now we search backwards for a non-mark glyph */
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
     do {
       if (!skippy_iter.prev ()) return_trace (false);
-      /* We only want to attach to the first of a MultipleSubst sequence.  Reject others. */
+      /* We only want to attach to the first of a MultipleSubst sequence.
+       * https://github.com/harfbuzz/harfbuzz/issues/740
+       * Reject others. */
       if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
-	  0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]))
+	  0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
+	  (skippy_iter.idx == 0 ||
+	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
+	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
+	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
+	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
+	   ))
 	break;
       skippy_iter.reject ();
     } while (1);
 
     /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
     //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
 
     unsigned int base_index = (this+baseCoverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint);
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -109,17 +109,17 @@ struct SingleSubstFormat1
     return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
   }
 
   protected:
   HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
-  HBINT16		deltaGlyphID;		/* Add to original GlyphID to get
+  HBINT16	deltaGlyphID;		/* Add to original GlyphID to get
 					 * substitute GlyphID */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 struct SingleSubstFormat2
 {
   inline void closure (hb_closure_context_t *c) const
@@ -381,17 +381,17 @@ struct MultipleSubstFormat1
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
     if (unlikely (!sequence.serialize (c, num_glyphs))) return_trace (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
       if (unlikely (!sequence[i].serialize (c, this).serialize (c,
 								substitute_glyphs_list,
 								substitute_len_list[i]))) return_trace (false);
-    substitute_len_list.advance (num_glyphs);
+    substitute_len_list += num_glyphs;
     if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
     return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
@@ -531,17 +531,17 @@ struct AlternateSubstFormat1
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
     if (unlikely (!alternateSet.serialize (c, num_glyphs))) return_trace (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
       if (unlikely (!alternateSet[i].serialize (c, this).serialize (c,
 								    alternate_glyphs_list,
 								    alternate_len_list[i]))) return_trace (false);
-    alternate_len_list.advance (num_glyphs);
+    alternate_len_list += num_glyphs;
     if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
     return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
@@ -752,18 +752,18 @@ struct LigatureSet
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
     if (unlikely (!ligature.serialize (c, num_ligatures))) return_trace (false);
     for (unsigned int i = 0; i < num_ligatures; i++)
       if (unlikely (!ligature[i].serialize (c, this).serialize (c,
 								ligatures[i],
 								component_list,
 								component_count_list[i]))) return_trace (false);
-    ligatures.advance (num_ligatures);
-    component_count_list.advance (num_ligatures);
+    ligatures += num_ligatures;
+    component_count_list += num_ligatures;
     return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (ligature.sanitize (c, this));
   }
@@ -845,17 +845,17 @@ struct LigatureSubstFormat1
     if (unlikely (!c->extend_min (*this))) return_trace (false);
     if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return_trace (false);
     for (unsigned int i = 0; i < num_first_glyphs; i++)
       if (unlikely (!ligatureSet[i].serialize (c, this).serialize (c,
 								   ligatures_list,
 								   component_count_list,
 								   ligature_per_first_glyph_count_list[i],
 								   component_list))) return_trace (false);
-    ligature_per_first_glyph_count_list.advance (num_first_glyphs);
+    ligature_per_first_glyph_count_list += num_first_glyphs;
     if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return_trace (false);
     return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -1207,30 +1207,30 @@ struct Rule
   public:
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (inputCount.sanitize (c) &&
 		  lookupCount.sanitize (c) &&
 		  c->check_range (inputZ,
 				  inputZ[0].static_size * inputCount +
-				  lookupRecordX[0].static_size * lookupCount));
+				  LookupRecord::static_size * lookupCount));
   }
 
   protected:
   HBUINT16	inputCount;		/* Total number of glyphs in input
 					 * glyph sequence--includes the first
 					 * glyph */
   HBUINT16	lookupCount;		/* Number of LookupRecords */
   HBUINT16	inputZ[VAR];		/* Array of match inputs--start with
 					 * second glyph */
-  LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
+/*LookupRecord	lookupRecordX[VAR];*/	/* Array of LookupRecords--in
 					 * design order */
   public:
-  DEFINE_SIZE_ARRAY2 (4, inputZ, lookupRecordX);
+  DEFINE_SIZE_ARRAY (4, inputZ);
 };
 
 struct RuleSet
 {
   inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
     unsigned int num_rules = rule.len;
@@ -1551,20 +1551,20 @@ struct ContextFormat3
   protected:
   HBUINT16	format;			/* Format identifier--format = 3 */
   HBUINT16	glyphCount;		/* Number of glyphs in the input glyph
 					 * sequence */
   HBUINT16	lookupCount;		/* Number of LookupRecords */
   OffsetTo<Coverage>
 		coverageZ[VAR];		/* Array of offsets to Coverage
 					 * table in glyph sequence order */
-  LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
+/*LookupRecord	lookupRecordX[VAR];*/	/* Array of LookupRecords--in
 					 * design order */
   public:
-  DEFINE_SIZE_ARRAY2 (6, coverageZ, lookupRecordX);
+  DEFINE_SIZE_ARRAY (6, coverageZ);
 };
 
 struct Context
 {
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -117,26 +117,32 @@ hb_ot_layout_position_finish_offsets (hb
 
 
 
 /*
  * 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 ();
@@ -161,20 +167,26 @@ struct hb_ot_layout_t
   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_lazy_table_loader_t<struct OT::BASE> base;
+  OT::hb_lazy_table_loader_t<struct OT::COLR> colr;
+  OT::hb_lazy_table_loader_t<struct OT::CPAL> cpal;
   OT::hb_lazy_table_loader_t<struct OT::MATH> math;
   OT::hb_lazy_table_loader_t<struct OT::fvar> fvar;
   OT::hb_lazy_table_loader_t<struct OT::avar> avar;
+  OT::hb_lazy_table_loader_t<struct AAT::ankr> ankr;
+  OT::hb_lazy_table_loader_t<struct AAT::kerx> kerx;
   OT::hb_lazy_table_loader_t<struct AAT::morx> morx;
+  OT::hb_lazy_table_loader_t<struct AAT::trak> trak;
 
   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;
 };
 
@@ -354,16 +366,38 @@ static inline void
   info->unicode_props() = (modified_class<<8) | (info->unicode_props() & 0xFF);
 }
 static inline unsigned int
 _hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info)
 {
   return _hb_glyph_info_is_unicode_mark (info) ? info->unicode_props()>>8 : 0;
 }
 
+
+/* Loop over grapheme. Based on foreach_cluster(). */
+#define foreach_grapheme(buffer, start, end) \
+  for (unsigned int \
+       _count = buffer->len, \
+       start = 0, end = _count ? _next_grapheme (buffer, 0) : 0; \
+       start < _count; \
+       start = end, end = _next_grapheme (buffer, start))
+
+static inline unsigned int
+_next_grapheme (hb_buffer_t *buffer, unsigned int start)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+
+  while (++start < count && _hb_glyph_info_is_unicode_mark (&info[start]))
+    ;
+
+  return start;
+}
+
+
 #define info_cc(info) (_hb_glyph_info_get_modified_combining_class (&(info)))
 
 static inline bool
 _hb_glyph_info_is_unicode_space (const hb_glyph_info_t *info)
 {
   return _hb_glyph_info_get_general_category (info) ==
 	 HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR;
 }
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -26,26 +26,31 @@
  *
  * 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-layout-base-table.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
 #include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-ot-name-table.hh" // Just so we compile it; unused otherwise.
+#include "hb-ot-color-colr-table.hh"
+#include "hb-ot-color-cpal-table.hh"
 
 #include "hb-ot-map-private.hh"
 
 
+#ifndef HB_NO_VISIBILITY
 const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
+#endif
 
 
 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;
@@ -54,20 +59,26 @@ hb_ot_layout_t *
   layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
 
   layout->gsub_blob = OT::Sanitizer<OT::GSUB>().sanitize (face->reference_table (HB_OT_TAG_GSUB));
   layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
 
   layout->gpos_blob = OT::Sanitizer<OT::GPOS>().sanitize (face->reference_table (HB_OT_TAG_GPOS));
   layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
 
+  layout->base.init (face);
+  layout->colr.init (face);
+  layout->cpal.init (face);
   layout->math.init (face);
   layout->fvar.init (face);
   layout->avar.init (face);
+  layout->ankr.init (face);
+  layout->kerx.init (face);
   layout->morx.init (face);
+  layout->trak.init (face);
 
   {
     /*
      * 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 = hb_blob_get_length (layout->gdef_blob);
@@ -190,36 +201,52 @@ hb_ot_layout_t *
     layout->gpos_accels[i].init (layout->gpos->get_lookup (i));
 
   return layout;
 }
 
 void
 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
 {
-  for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
-    layout->gsub_accels[i].fini ();
-  for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
-    layout->gpos_accels[i].fini ();
+  if (layout->gsub_accels)
+    for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
+      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->base.fini ();
+  layout->colr.fini ();
+  layout->cpal.fini ();
   layout->math.fini ();
   layout->fvar.fini ();
   layout->avar.fini ();
+  layout->ankr.fini ();
+  layout->kerx.fini ();
   layout->morx.fini ();
+  layout->trak.fini ();
 
   free (layout);
 }
 
+// static inline const OT::BASE&
+// _get_base (hb_face_t *face)
+// {
+//   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::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 OT::Null(OT::GDEF);
   return *hb_ot_layout_from_face (face)->gdef;
 }
 static inline const OT::GSUB&
 _get_gsub (hb_face_t *face)
@@ -1248,15 +1275,39 @@ void hb_ot_map_t::substitute (const hb_o
 }
 
 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
 {
   GPOSProxy proxy (font->face);
   apply (proxy, plan, font, buffer);
 }
 
-HB_INTERNAL void
+void
 hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c,
 				const OT::SubstLookup &lookup,
 				const hb_ot_layout_lookup_accelerator_t &accel)
 {
   apply_string<GSUBProxy> (c, lookup, accel);
 }
+
+
+
+
+/*
+ * OT::BASE
+ */
+
+// /**
+//  * hb_ot_base_has_data:
+//  * @face: #hb_face_t to test
+//  *
+//  * This function allows to verify the presence of an OpenType BASE table on the
+//  * face.
+//  *
+//  * 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) != &OT::Null(OT::BASE);
+// }
--- a/gfx/harfbuzz/src/hb-ot-layout.h
+++ b/gfx/harfbuzz/src/hb-ot-layout.h
@@ -33,16 +33,17 @@
 
 #include "hb.h"
 
 #include "hb-ot-tag.h"
 
 HB_BEGIN_DECLS
 
 
+#define HB_OT_TAG_BASE HB_TAG('B','A','S','E')
 #define HB_OT_TAG_GDEF HB_TAG('G','D','E','F')
 #define HB_OT_TAG_GSUB HB_TAG('G','S','U','B')
 #define HB_OT_TAG_GPOS HB_TAG('G','P','O','S')
 #define HB_OT_TAG_JSTF HB_TAG('J','S','T','F')
 
 
 /*
  * GDEF
@@ -311,11 +312,27 @@ HB_EXTERN hb_bool_t
 hb_ot_layout_get_size_params (hb_face_t    *face,
 			      unsigned int *design_size,       /* OUT.  May be NULL */
 			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
 			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
 			      unsigned int *range_start,       /* OUT.  May be NULL */
 			      unsigned int *range_end          /* OUT.  May be NULL */);
 
 
+/*
+ * BASE
+ */
+#if 0
+
+#define HB_OT_TAG_BASE_HANG HB_TAG('h','a','n','g')
+#define HB_OT_TAG_BASE_ICFB HB_TAG('i','c','f','b')
+#define HB_OT_TAG_BASE_ICFT HB_TAG('i','c','f','t')
+#define HB_OT_TAG_BASE_IDEO HB_TAG('i','d','e','o')
+#define HB_OT_TAG_BASE_IDTB HB_TAG('i','d','t','b')
+#define HB_OT_TAG_BASE_MATH HB_TAG('m','a','t','h')
+#define HB_OT_TAG_BASE_ROMN HB_TAG('r','o','m','n')
+
+#endif
+
+
 HB_END_DECLS
 
 #endif /* HB_OT_LAYOUT_H */
--- a/gfx/harfbuzz/src/hb-ot-map-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-map-private.hh
@@ -79,38 +79,38 @@ struct hb_ot_map_t
   };
 
 
   hb_ot_map_t (void) { memset (this, 0, sizeof (*this)); }
 
   inline hb_mask_t get_global_mask (void) const { return global_mask; }
 
   inline hb_mask_t get_mask (hb_tag_t feature_tag, unsigned int *shift = nullptr) const {
-    const feature_map_t *map = features.bsearch (&feature_tag);
+    const feature_map_t *map = features.bsearch (feature_tag);
     if (shift) *shift = map ? map->shift : 0;
     return map ? map->mask : 0;
   }
 
   inline bool needs_fallback (hb_tag_t feature_tag) const {
-    const feature_map_t *map = features.bsearch (&feature_tag);
+    const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->needs_fallback : false;
   }
 
   inline hb_mask_t get_1_mask (hb_tag_t feature_tag) const {
-    const feature_map_t *map = features.bsearch (&feature_tag);
+    const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->_1_mask : 0;
   }
 
   inline unsigned int get_feature_index (unsigned int table_index, hb_tag_t feature_tag) const {
-    const feature_map_t *map = features.bsearch (&feature_tag);
+    const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->index[table_index] : HB_OT_LAYOUT_NO_FEATURE_INDEX;
   }
 
   inline unsigned int get_feature_stage (unsigned int table_index, hb_tag_t feature_tag) const {
-    const feature_map_t *map = features.bsearch (&feature_tag);
+    const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->stage[table_index] : (unsigned int) -1;
   }
 
   inline void get_stage_lookups (unsigned int table_index, unsigned int stage,
 				 const struct lookup_map_t **plookups, unsigned int *lookup_count) const {
     if (unlikely (stage == (unsigned int) -1)) {
       *plookups = nullptr;
       *lookup_count = 0;
--- a/gfx/harfbuzz/src/hb-ot-map.cc
+++ b/gfx/harfbuzz/src/hb-ot-map.cc
@@ -134,17 +134,17 @@ void hb_ot_map_builder_t::add_pause (uns
 
 void
 hb_ot_map_builder_t::compile (hb_ot_map_t  &m,
 			      const int    *coords,
 			      unsigned int  num_coords)
 {
   static_assert ((!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1))), "");
   unsigned int global_bit_mask = HB_GLYPH_FLAG_DEFINED + 1;
-  unsigned int global_bit_shift = _hb_popcount32 (HB_GLYPH_FLAG_DEFINED);
+  unsigned int global_bit_shift = _hb_popcount (HB_GLYPH_FLAG_DEFINED);
 
   m.global_mask = global_bit_mask;
 
   unsigned int required_feature_index[2];
   hb_tag_t required_feature_tag[2];
   /* We default to applying required feature in stage 0.  If the required
    * feature has a tag that is known to the shaper, we apply required feature
    * in the stage for that tag.
--- a/gfx/harfbuzz/src/hb-ot-maxp-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-maxp-table.hh
@@ -23,49 +23,125 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_MAXP_TABLE_HH
 #define HB_OT_MAXP_TABLE_HH
 
 #include "hb-open-type-private.hh"
-
+#include "hb-subset-plan.hh"
 
 namespace OT {
 
 
 /*
  * maxp -- The Maximum Profile Table
  */
 
 #define HB_OT_TAG_maxp HB_TAG('m','a','x','p')
 
+struct maxpV1Tail
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  HBUINT16 maxPoints;		  /* Maximum points in a non-composite glyph. */
+  HBUINT16 maxContours;		  /* Maximum contours in a non-composite glyph. */
+  HBUINT16 maxCompositePoints;	  /* Maximum points in a composite glyph. */
+  HBUINT16 maxCompositeContours;  /* Maximum contours in a composite glyph. */
+  HBUINT16 maxZones;		  /* 1 if instructions do not use the twilight zone (Z0),
+				   * or 2 if instructions do use Z0; should be set to 2 in
+				   * most cases. */
+  HBUINT16 maxTwilightPoints;	  /* Maximum points used in Z0. */
+  HBUINT16 maxStorage;		  /* Number of Storage Area locations. */
+  HBUINT16 maxFunctionDefs;	  /* Number of FDEFs, equal to the highest function number + 1. */
+  HBUINT16 maxInstructionDefs;	  /* Number of IDEFs. */
+  HBUINT16 maxStackElements;	  /* Maximum stack depth. (This includes Font and CVT
+				   * Programs, as well as the instructions for each glyph.) */
+  HBUINT16 maxSizeOfInstructions; /* Maximum byte count for glyph instructions. */
+  HBUINT16 maxComponentElements;  /* Maximum number of components referenced at
+				   * "top level" for any composite glyph. */
+  HBUINT16 maxComponentDepth;	  /* Maximum levels of recursion; 1 for simple components. */
+ public:
+  DEFINE_SIZE_STATIC (26);
+};
+
+
 struct maxp
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_maxp;
+  static const hb_tag_t tableTag = HB_OT_TAG_maxp;
 
   inline unsigned int get_num_glyphs (void) const
   {
     return numGlyphs;
   }
 
+  inline void set_num_glyphs (unsigned int count)
+  {
+    numGlyphs.set (count);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  likely (version.major == 1 ||
-			  (version.major == 0 && version.minor == 0x5000u)));
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+
+    if (version.major == 1)
+    {
+      const maxpV1Tail &v1 = StructAfter<maxpV1Tail> (*this);
+      return v1.sanitize (c);
+    }
+    return_trace (likely (version.major == 0 && version.minor == 0x5000u));
   }
 
-  /* We only implement version 0.5 as none of the extra fields in version 1.0 are useful. */
+  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_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_prime->set_num_glyphs (plan->gids_to_retain_sorted.len);
+    if (plan->drop_hints)
+      drop_hint_fields (plan, maxp_prime);
+
+    bool result = hb_subset_plan_add_table(plan, 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)
+  {
+    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);
+      v1.maxInstructionDefs.set (0);
+      v1.maxStackElements.set (0);
+      v1.maxSizeOfInstructions.set (0);
+    }
+  }
+
   protected:
   FixedVersion<>version;		/* Version of the maxp table (0.5 or 1.0),
 					 * 0x00005000u or 0x00010000u. */
   HBUINT16	numGlyphs;		/* The number of glyphs in the font. */
+/*maxpV1Tail v1Tail[VAR]; */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 
 } /* namespace OT */
 
 
--- a/gfx/harfbuzz/src/hb-ot-os2-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-os2-table.hh
@@ -23,17 +23,17 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_OS2_TABLE_HH
 #define HB_OT_OS2_TABLE_HH
 
 #include "hb-open-type-private.hh"
-
+#include "hb-ot-os2-unicode-ranges.hh"
 
 namespace OT {
 
 /*
  * OS/2 and Windows Metrics
  * http://www.microsoft.com/typography/otspec/os2.htm
  */
 
@@ -44,61 +44,137 @@ struct os2
   static const hb_tag_t tableTag = HB_OT_TAG_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_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);
+    if (unlikely (!os2_prime)) {
+      hb_blob_destroy (os2_prime_blob);
+      return false;
+    }
+
+    uint16_t min_cp, max_cp;
+    find_min_and_max_codepoint (plan->codepoints, &min_cp, &max_cp);
+    os2_prime->usFirstCharIndex.set (min_cp);
+    os2_prime->usLastCharIndex.set (max_cp);
+
+    _update_unicode_ranges (plan->codepoints, os2_prime->ulUnicodeRange);
+    bool result = hb_subset_plan_add_table(plan, HB_OT_TAG_os2, os2_prime_blob);
+
+    hb_blob_destroy (os2_prime_blob);
+    return result;
+  }
+
+  inline void _update_unicode_ranges (const hb_prealloced_array_t<hb_codepoint_t> &codepoints,
+                                      HBUINT32 ulUnicodeRange[4]) const
+  {
+    for (unsigned int i = 0; i < 4; i++)
+      ulUnicodeRange[i].set (0);
+
+    for (unsigned int i = 0; i < codepoints.len; i++)
+    {
+      hb_codepoint_t cp = codepoints[i];
+      unsigned int bit = hb_get_unicode_range_bit (cp);
+      if (bit < 128)
+      {
+        unsigned int block = bit / 32;
+        unsigned int bit_in_block = bit % 32;
+        unsigned int mask = 1 << bit_in_block;
+        ulUnicodeRange[block].set (ulUnicodeRange[block] | mask);
+      }
+      if (cp >= 0x10000 && cp <= 0x110000)
+      {
+        /* the spec says that bit 57 ("Non Plane 0") implies that there's
+           at least one codepoint beyond the BMP; so I also include all
+           the non-BMP codepoints here */
+        ulUnicodeRange[1].set (ulUnicodeRange[1] | (1 << 25));
+      }
+    }
+  }
+
+  static inline void find_min_and_max_codepoint (const hb_prealloced_array_t<hb_codepoint_t> &codepoints,
+                                                 uint16_t *min_cp, /* OUT */
+                                                 uint16_t *max_cp  /* OUT */)
+  {
+    hb_codepoint_t min = -1, max = 0;
+
+    for (unsigned int i = 0; i < codepoints.len; i++)
+    {
+      hb_codepoint_t cp = codepoints[i];
+      if (cp < min)
+        min = cp;
+      if (cp > max)
+        max = cp;
+    }
+