Merge branch 'master' into var-subset
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1c901eb..9eeb154 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -235,15 +235,6 @@
       - run: CFLAGS="-Wno-attributes" CXXFLAGS="-Wno-attributes" ./autogen.sh --prefix=/usr/local/djgpp --host=i586-pc-msdosdjgpp
       - run: make -j32
 
-  crosscompile-notest-freebsd9:
-    docker:
-      - image: donbowman/freebsd-cross-build
-    steps:
-      - checkout
-      - run: apt update && apt install -y pkg-config ragel
-      - run: ./autogen.sh --prefix=/freebsd --host=x86_64-pc-freebsd9
-      - run: make -j32
-
   crosscompile-notest-psvita:
     docker:
       - image: dockcross/base
@@ -327,7 +318,6 @@
       # they can't be test thus are without tests
       ## autotools
       - crosscompile-notest-djgpp
-      - crosscompile-notest-freebsd9
       - crosscompile-notest-psvita
 
       ## cmake
diff --git a/.editorconfig b/.editorconfig
index 4991473..4850f2b 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,7 +9,7 @@
 [*.{c,cc,h,hh}]
 tab_width = 8
 indent_size = 2
-indent_style = space
+indent_style = tab # should be space
 
 [*.{py,sh}]
 indent_style = tab
diff --git a/AUTHORS b/AUTHORS
index 0763761..83c0c66 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,11 +1,14 @@
 Behdad Esfahbod
+David Corbett
 David Turner
 Ebrahim Byagowi
+Garret Rieger
 Jonathan Kew
 Khaled Hosny
 Lars Knoll
 Martin Hosken
 Owen Taylor
+Roderick Sheeter
 Roozbeh Pournader
 Simon Hausmann
 Werner Lemberg
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f64f96d..a8d12c1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -844,7 +844,7 @@
 
 if (HB_BUILD_TESTS)
   ## src/ executables
-  foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges)
+  foreach (prog main test test-gsub-would-substitute test-gpos-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges)
     set (prog_name ${prog})
     if (${prog_name} STREQUAL "test")
       # test can not be used as a valid executable name on cmake, lets special case it
diff --git a/COPYING b/COPYING
index 9d1056f..0278e60 100644
--- a/COPYING
+++ b/COPYING
@@ -2,7 +2,8 @@
 For parts of HarfBuzz that are licensed under different licenses see individual
 files names COPYING in subdirectories where applicable.
 
-Copyright © 2010,2011,2012  Google, Inc.
+Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019  Google, Inc.
+Copyright © 2019  Facebook, Inc.
 Copyright © 2012  Mozilla Foundation
 Copyright © 2011  Codethink Limited
 Copyright © 2008,2010  Nokia Corporation and/or its subsidiary(-ies)
diff --git a/THANKS b/THANKS
index 940cfde..88cb7e9 100644
--- a/THANKS
+++ b/THANKS
@@ -1,6 +1,6 @@
 Bradley Grainger
-Khaled Hosny
 Kenichi Ishibashi
+Ivan Kuckir <https://photopea.com/>
 Ryan Lortie
 Jeff Muizelaar
 suzuki toshiya
diff --git a/docs/harfbuzz-docs.xml b/docs/harfbuzz-docs.xml
index f4ad134..e48d175 100644
--- a/docs/harfbuzz-docs.xml
+++ b/docs/harfbuzz-docs.xml
@@ -20,11 +20,7 @@
 
       <para>
 	The canonical source-code tree is available at
-        <ulink
-	    url="https://github.com/harfbuzz/harfbuzz">github.com/harfbuzz/harfbuzz</ulink>
-        and is also available at
-        <ulink
-	    url="http://cgit.freedesktop.org/harfbuzz/">cgit.freedesktop.org/harfbuzz</ulink>.
+        <ulink url="https://github.com/harfbuzz/harfbuzz">github.com/harfbuzz/harfbuzz</ulink>.
 	See <xref linkend="download" endterm="download.title"/> for
 	release tarballs.
       </para>
diff --git a/src/Makefile.am b/src/Makefile.am
index 66a3b31..9b5512f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -310,9 +310,9 @@
 	main \
 	test \
 	test-buffer-serialize \
-	test-name-table \
-	test-size-params \
-	test-would-substitute \
+	test-ot-name \
+	test-gpos-size-params \
+	test-gsub-would-substitute \
 	$(NULL)
 bin_PROGRAMS =
 
@@ -328,17 +328,17 @@
 test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
 test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
 
-test_name_table_SOURCES = test-name-table.cc
-test_name_table_CPPFLAGS = $(HBCFLAGS)
-test_name_table_LDADD = libharfbuzz.la $(HBLIBS)
+test_ot_name_SOURCES = test-ot-name.cc
+test_ot_name_CPPFLAGS = $(HBCFLAGS)
+test_ot_name_LDADD = libharfbuzz.la $(HBLIBS)
 
-test_size_params_SOURCES = test-size-params.cc
-test_size_params_CPPFLAGS = $(HBCFLAGS)
-test_size_params_LDADD = libharfbuzz.la $(HBLIBS)
+test_gpos_size_params_SOURCES = test-gpos-size-params.cc
+test_gpos_size_params_CPPFLAGS = $(HBCFLAGS)
+test_gpos_size_params_LDADD = libharfbuzz.la $(HBLIBS)
 
-test_would_substitute_SOURCES = test-would-substitute.cc
-test_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
-test_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
+test_gsub_would_substitute_SOURCES = test-gsub-would-substitute.cc
+test_gsub_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
+test_gsub_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
 
 if HAVE_FREETYPE
 if HAVE_CAIRO_FT
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 5c2e02b..289e126 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -86,7 +86,7 @@
 	hb-ot-math-table.hh \
 	hb-ot-math.cc \
 	hb-ot-maxp-table.hh \
-	hb-ot-name-language.cc \
+	hb-ot-name-language-static.hh \
 	hb-ot-name-language.hh \
 	hb-ot-name-table.hh \
 	hb-ot-name.cc \
diff --git a/src/gen-use-table.py b/src/gen-use-table.py
index 029e66e..1a33b8a 100755
--- a/src/gen-use-table.py
+++ b/src/gen-use-table.py
@@ -48,6 +48,12 @@
 # TODO Characters that are not in Unicode Indic files, but used in USE
 data[0][0x034F] = defaults[0]
 data[0][0x2060] = defaults[0]
+# TODO https://github.com/harfbuzz/harfbuzz/pull/1685
+data[0][0x1B5B] = 'Consonant_Placeholder'
+data[0][0x1B5C] = 'Consonant_Placeholder'
+data[0][0x1B5F] = 'Consonant_Placeholder'
+data[0][0x1B62] = 'Consonant_Placeholder'
+data[0][0x1B68] = 'Consonant_Placeholder'
 # TODO https://github.com/roozbehp/unicode-data/issues/9
 data[0][0x11C44] = 'Consonant_Placeholder'
 data[0][0x11C45] = 'Consonant_Placeholder'
@@ -171,7 +177,7 @@
 def is_BASE_IND(U, UISC, UGC):
 	#SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
 	return (UISC in [Consonant_Dead, Modifying_Letter] or
-		(UGC == Po and not U in [0x104B, 0x104E, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
+		(UGC == Po and not U in [0x104B, 0x104E, 0x1B5B, 0x1B5C, 0x1B5F, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
 		False # SPEC-DRAFT-OUTDATED! U == 0x002D
 		)
 def is_BASE_NUM(U, UISC, UGC):
@@ -228,7 +234,7 @@
 def is_SYM(U, UISC, UGC):
 	if U == 0x25CC: return False #SPEC-DRAFT
 	#SPEC-DRAFT return UGC in [So, Sc] or UISC == Symbol_Letter
-	return UGC in [So, Sc]
+	return UGC in [So, Sc] and U not in [0x1B62, 0x1B68]
 def is_SYM_MOD(U, UISC, UGC):
 	return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73]
 def is_VARIATION_SELECTOR(U, UISC, UGC):
diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh
index 2508276..7c74d79 100644
--- a/src/hb-aat-layout-common.hh
+++ b/src/hb-aat-layout-common.hh
@@ -153,13 +153,13 @@
 		  first <= last &&
 		  valuesZ.sanitize (c, base, last - first + 1));
   }
-  template <typename T2>
-  bool sanitize (hb_sanitize_context_t *c, const void *base, T2 user_data) const
+  template <typename ...Ts>
+  bool sanitize (hb_sanitize_context_t *c, const void *base, Ts &&...ds) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  first <= last &&
-		  valuesZ.sanitize (c, base, last - first + 1, user_data));
+		  valuesZ.sanitize (c, base, last - first + 1, hb_forward<Ts> (ds)...));
   }
 
   GlyphID	last;		/* Last GlyphID in this segment */
diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc
index 5168a9c..1966ded 100644
--- a/src/hb-aat-layout.cc
+++ b/src/hb-aat-layout.cc
@@ -311,14 +311,6 @@
   trak.apply (&c);
 }
 
-
-hb_language_t
-_hb_aat_language_get (hb_face_t *face,
-		      unsigned int i)
-{
-  return face->table.ltag->get_language (i);
-}
-
 /**
  * hb_aat_layout_get_feature_types:
  * @face: a face object
diff --git a/src/hb-aat-layout.hh b/src/hb-aat-layout.hh
index 6340924..9a0e446 100644
--- a/src/hb-aat-layout.hh
+++ b/src/hb-aat-layout.hh
@@ -30,7 +30,7 @@
 #include "hb.hh"
 
 #include "hb-ot-shape.hh"
-
+#include "hb-aat-ltag-table.hh"
 
 struct hb_aat_feature_mapping_t
 {
@@ -77,9 +77,13 @@
 		     hb_font_t *font,
 		     hb_buffer_t *buffer);
 
-HB_INTERNAL hb_language_t
+
+inline hb_language_t
 _hb_aat_language_get (hb_face_t *face,
-		      unsigned int i);
+                      unsigned int i)
+{
+  return face->table.ltag->get_language (i);
+}
 
 
 #endif /* HB_AAT_LAYOUT_HH */
diff --git a/src/hb-algs.hh b/src/hb-algs.hh
index 9367743..c346780 100644
--- a/src/hb-algs.hh
+++ b/src/hb-algs.hh
@@ -71,28 +71,31 @@
   private:
 
   /* Pointer-to-member-function. */
-  template <typename Appl, typename Val> auto
-  impl (Appl&& a, Val &&v, hb_priority<2>) const HB_AUTO_RETURN
-  (hb_forward<Val> (hb_deref_pointer (v)).*a ())
+  template <typename Appl, typename Val1, typename ...Vals> auto
+  impl (Appl&& a, hb_priority<2>, Val1 &&v1, Vals &&...vs) const HB_AUTO_RETURN
+  ((hb_deref_pointer (hb_forward<Val1> (v1)).*hb_forward<Appl> (a)) (hb_forward<Vals> (vs)...))
 
   /* Pointer-to-member. */
   template <typename Appl, typename Val> auto
-  impl (Appl&& a, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
-  (hb_forward<Val> (hb_deref_pointer (v)).*a)
+  impl (Appl&& a, hb_priority<1>, Val &&v) const HB_AUTO_RETURN
+  ((hb_deref_pointer (hb_forward<Val> (v))).*hb_forward<Appl> (a))
 
   /* Operator(). */
-  template <typename Appl, typename Val> auto
-  impl (Appl&& a, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
-  (hb_deref_pointer (a) (hb_forward<Val> (v)))
+  template <typename Appl, typename ...Vals> auto
+  impl (Appl&& a, hb_priority<0>, Vals &&...vs) const HB_AUTO_RETURN
+  (hb_deref_pointer (hb_forward<Appl> (a)) (hb_forward<Vals> (vs)...))
 
   public:
+  template <typename Appl, typename Val1, typename ...Vals> auto
+  impl2 (Appl&& a, hb_priority<2>, Val1 &&v1, Vals &&...vs) const HB_AUTO_RETURN
+  (hb_deref_pointer (hb_forward<Val1> (v1)).*hb_forward<Appl> (a) (hb_forward<Vals> (vs)...))
 
-  template <typename Appl, typename Val> auto
-  operator () (Appl&& a, Val &&v) const HB_AUTO_RETURN
+  template <typename Appl, typename ...Vals> auto
+  operator () (Appl&& a, Vals &&...vs) const HB_AUTO_RETURN
   (
     impl (hb_forward<Appl> (a),
-	  hb_forward<Val> (v),
-	  hb_prioritize)
+	  hb_prioritize,
+	  hb_forward<Vals> (vs)...)
   )
 } HB_FUNCOBJ (hb_invoke);
 
@@ -102,7 +105,7 @@
 
   template <typename Pred, typename Val> auto
   impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
-  (hb_deref_pointer (p).has (v))
+  (hb_deref_pointer (hb_forward<Pred> (p)).has (v))
 
   template <typename Pred, typename Val> auto
   impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
@@ -127,7 +130,7 @@
 
   template <typename Proj, typename Val> auto
   impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
-  (hb_deref_pointer (f).get (hb_forward<Val> (v)))
+  (hb_deref_pointer (hb_forward<Proj> (f)).get (hb_forward<Val> (v)))
 
   template <typename Proj, typename Val> auto
   impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
diff --git a/src/hb-array.hh b/src/hb-array.hh
index 74b6757..b4619ee 100644
--- a/src/hb-array.hh
+++ b/src/hb-array.hh
@@ -43,20 +43,19 @@
    * Constructors.
    */
   hb_array_t () : arrayZ (nullptr), length (0) {}
-  hb_array_t (const hb_array_t<Type> &o) :
-    hb_iter_with_fallback_t<hb_array_t<Type>, Type&> (),
-    arrayZ (o.arrayZ), length (o.length) {}
-  template <typename U = Type, hb_enable_if (hb_is_const (U))>
-  hb_array_t (const hb_array_t<hb_remove_const<Type> > &o) : arrayZ (o.arrayZ), length (o.length) {}
-
   hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {}
   template <unsigned int length_> hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_) {}
 
-  template <typename U = Type, hb_enable_if (hb_is_const (U))>
-  hb_array_t& operator = (const hb_array_t<hb_remove_const<Type> > &o)
+  template <typename U,
+	    hb_enable_if (hb_is_cr_convertible_to(U, Type))>
+  hb_array_t (const hb_array_t<U> &o) :
+    hb_iter_with_fallback_t<hb_array_t<Type>, Type&> (),
+    arrayZ (o.arrayZ), length (o.length) {}
+  template <typename U,
+	    hb_enable_if (hb_is_cr_convertible_to(U, Type))>
+  hb_array_t& operator = (const hb_array_t<U> &o)
   { arrayZ = o.arrayZ; length = o.length; return *this; }
-  hb_array_t& operator = (const hb_array_t &o)
-  { arrayZ = o.arrayZ; length = o.length; return *this; }
+
   /*
    * Iterator implementation.
    */
@@ -212,12 +211,19 @@
   static constexpr bool is_sorted_iterator = true;
 
   hb_sorted_array_t () : hb_array_t<Type> () {}
-  hb_sorted_array_t (const hb_array_t<Type> &o) : hb_array_t<Type> (o) {}
-  template <typename U = Type, hb_enable_if (hb_is_const (U))>
-  hb_sorted_array_t (const hb_sorted_array_t<hb_remove_const<Type> > &o) : hb_array_t<Type> (o) {}
   hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t<Type> (array_, length_) {}
   template <unsigned int length_> hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {}
 
+  template <typename U,
+	    hb_enable_if (hb_is_cr_convertible_to(U, Type))>
+  hb_sorted_array_t (const hb_array_t<U> &o) :
+    hb_iter_t<hb_sorted_array_t<Type>, Type&> (),
+    hb_array_t<Type> (o) {}
+  template <typename U,
+	    hb_enable_if (hb_is_cr_convertible_to(U, Type))>
+  hb_sorted_array_t& operator = (const hb_array_t<U> &o)
+  { hb_array_t<Type> (*this) = o; return *this; }
+
   hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
   { return hb_sorted_array_t<Type> (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); }
   hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const
diff --git a/src/hb-cff-interp-common.hh b/src/hb-cff-interp-common.hh
index 78ef997..c5157c7 100644
--- a/src/hb-cff-interp-common.hh
+++ b/src/hb-cff-interp-common.hh
@@ -691,7 +691,7 @@
 
       case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
       case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
-	env.argStack.push_int ((int16_t)(-(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108));
+	env.argStack.push_int ((-(int16_t)(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108));
 	env.str_ref.inc ();
 	break;
 
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 60d16c8..c453443 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -1160,6 +1160,59 @@
   buf[len] = '\0';
 }
 
+/**
+ * hb_color_get_alpha:
+ *
+ *
+ *
+ * Since: REPLACEME
+ */
+uint8_t
+(hb_color_get_alpha) (hb_color_t color)
+{
+  return hb_color_get_alpha (color);
+}
+
+/**
+ * hb_color_get_red:
+ *
+ *
+ *
+ * Since: REPLACEME
+ */
+uint8_t
+(hb_color_get_red) (hb_color_t color)
+{
+  return hb_color_get_red (color);
+}
+
+/**
+ * hb_color_get_green:
+ *
+ *
+ *
+ * Since: REPLACEME
+ */
+uint8_t
+(hb_color_get_green) (hb_color_t color)
+{
+  return hb_color_get_green (color);
+}
+
+/**
+ * hb_color_get_blue:
+ *
+ *
+ *
+ * Since: REPLACEME
+ */
+uint8_t
+(hb_color_get_blue) (hb_color_t color)
+{
+  return hb_color_get_blue (color);
+}
+
+
 /* If there is no visibility control, then hb-static.cc will NOT
  * define anything.  Instead, we get it to define one set in here
  * only, so only libharfbuzz.so defines them, not other libs. */
diff --git a/src/hb-common.h b/src/hb-common.h
index 371b2bf..edd9ffb 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -467,39 +467,21 @@
 
 #define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a)))
 
-/**
- * hb_color_get_alpha:
- *
- *
- *
- * Since: 2.1.0
- */
+HB_EXTERN uint8_t
+hb_color_get_alpha (hb_color_t color);
 #define hb_color_get_alpha(color)	((color) & 0xFF)
-/**
- * hb_color_get_red:
- *
- *
- *
- * Since: 2.1.0
- */
-#define hb_color_get_red(color)		(((color) >> 8) & 0xFF)
-/**
- * hb_color_get_green:
- *
- *
- *
- * Since: 2.1.0
- */
-#define hb_color_get_green(color)	(((color) >> 16) & 0xFF)
-/**
- * hb_color_get_blue:
- *
- *
- *
- * Since: 2.1.0
- */
-#define hb_color_get_blue(color)	(((color) >> 24) & 0xFF)
 
+HB_EXTERN uint8_t
+hb_color_get_red (hb_color_t color);
+#define hb_color_get_red(color)		(((color) >> 8) & 0xFF)
+
+HB_EXTERN uint8_t
+hb_color_get_green (hb_color_t color);
+#define hb_color_get_green(color)	(((color) >> 16) & 0xFF)
+
+HB_EXTERN uint8_t
+hb_color_get_blue (hb_color_t color);
+#define hb_color_get_blue(color)	(((color) >> 24) & 0xFF)
 
 HB_END_DECLS
 
diff --git a/src/hb-iter.hh b/src/hb-iter.hh
index a4ffbd6..f094728 100644
--- a/src/hb-iter.hh
+++ b/src/hb-iter.hh
@@ -129,8 +129,6 @@
 #define hb_iter_t(Iterable) decltype (hb_declval (Iterable).iter ())
 
 
-/* TODO Change to function-object. */
-
 template <typename> struct hb_array_t;
 
 struct
@@ -209,35 +207,35 @@
 struct hb_is_iterable
 {
   private:
+
   template <typename U>
-  static auto test (int) -> decltype (hb_declval (U).iter (), hb_true_t ());
+  static auto impl (hb_priority<1>) -> decltype (hb_declval (U).iter (), hb_true_t ());
+
   template <typename>
-  static hb_false_t test (...);
+  static hb_false_t impl (hb_priority<0>);
 
   public:
-  enum { value = decltype (test<T> (0))::value };
+
+  enum { value = decltype (impl<T> (hb_prioritize))::value };
 };
 #define hb_is_iterable(Iterable) hb_is_iterable<Iterable>::value
 
 /* TODO Add hb_is_iterable_of().
  * TODO Add random_access / sorted variants. */
 
-
 /* hb_is_iterator() / hb_is_random_access_iterator() / hb_is_sorted_iterator() */
 
-template <typename Iter>
-struct _hb_is_iterator_of
-{
-  char operator () (...) { return 0; }
-  template<typename Item> int operator () (hb_iter_t<Iter, Item> *) { return 0; }
-  template<typename Item> int operator () (hb_iter_t<Iter, const Item> *) { return 0; }
-  template<typename Item> int operator () (hb_iter_t<Iter, Item&> *) { return 0; }
-  template<typename Item> int operator () (hb_iter_t<Iter, const Item&> *) { return 0; }
-  static_assert (sizeof (char) != sizeof (int), "");
-};
+template <typename Iter, typename Item>
+static inline char _hb_is_iterator_of (hb_priority<0>, const void *) { return 0; }
+template <typename Iter,
+	  typename Item,
+	  typename Item2 = typename Iter::item_t,
+	  hb_enable_if (hb_is_cr_convertible_to (Item2, Item))>
+static inline int _hb_is_iterator_of (hb_priority<2>, hb_iter_t<Iter, Item2> *) { return 0; }
+
 template<typename Iter, typename Item>
 struct hb_is_iterator_of { enum {
-  value = sizeof (int) == sizeof (hb_declval (_hb_is_iterator_of<Iter>) (hb_declval (Iter*))) }; };
+  value = sizeof (int) == sizeof (_hb_is_iterator_of<Iter, Item> (hb_prioritize, hb_declval (Iter*))) }; };
 #define hb_is_iterator_of(Iter, Item) hb_is_iterator_of<Iter, Item>::value
 #define hb_is_iterator(Iter) hb_is_iterator_of (Iter, typename Iter::item_t)
 
diff --git a/src/hb-map.hh b/src/hb-map.hh
index b99fb8f..bbb1bef 100644
--- a/src/hb-map.hh
+++ b/src/hb-map.hh
@@ -34,11 +34,9 @@
  * hb_hashmap_t
  */
 
-/* TODO if K/V is signed integer, -1 is not a good default.
- * Don't know how to get to -MAX using bit work. */
 template <typename K, typename V,
-	  K kINVALID = hb_is_pointer (K) ? 0 : (K) -1,
-	  V vINVALID = hb_is_pointer (V) ? 0 : (V) -1>
+	  K kINVALID = hb_is_pointer (K) ? 0 : hb_is_signed (K) ? hb_int_min (K) : (K) -1,
+	  V vINVALID = hb_is_pointer (V) ? 0 : hb_is_signed (V) ? hb_int_min (V) : (V) -1>
 struct hb_hashmap_t
 {
   HB_DELETE_COPY_ASSIGN (hb_hashmap_t);
@@ -122,7 +120,7 @@
       return false;
     }
     + hb_iter (new_items, new_size)
-    | hb_apply ([] (item_t &_) { _.clear (); }) /* TODO make pointer-to-methods invokable. */
+    | hb_apply (&item_t::clear)
     ;
 
     unsigned int old_size = mask + 1;
@@ -193,7 +191,7 @@
       return;
     if (items)
       + hb_iter (items, mask + 1)
-      | hb_apply ([] (item_t &_) { _.clear (); }) /* TODO make pointer-to-methods invokable. */
+      | hb_apply (&item_t::clear)
       ;
 
     population = occupancy = 0;
diff --git a/src/hb-meta.hh b/src/hb-meta.hh
index 0dcf793..b80358c 100644
--- a/src/hb-meta.hh
+++ b/src/hb-meta.hh
@@ -65,6 +65,9 @@
 #define HB_FUNCOBJ(x) static_const x HB_UNUSED
 
 
+template <typename T> struct hb_match_identity { typedef T type; };
+template <typename T> using hb_type_identity = typename hb_match_identity<T>::type;
+
 struct
 {
   template <typename T>
@@ -96,6 +99,14 @@
 template <typename T> using hb_remove_pointer = typename hb_match_pointer<T>::type;
 #define hb_is_pointer(T) hb_match_pointer<T>::value
 
+/* TODO Add feature-parity to std::decay. */
+template <typename T> using hb_decay = hb_remove_const<hb_remove_reference<T>>;
+
+#define hb_is_cr_convertible_to(A, B) ( \
+	hb_is_same (hb_decay<A>, hb_decay<B>) && \
+	hb_is_const (A) <= hb_is_const (B) && \
+	hb_is_reference (A) >= hb_is_reference (B))
+
 
 /* std::move and std::forward */
 
@@ -127,17 +138,25 @@
 #define hb_is_same(T, T2) hb_is_same<T, T2>::value
 
 template <typename T> struct hb_is_signed;
-/* https://github.com/harfbuzz/harfbuzz/issues/1535 */
-template <> struct hb_is_signed<int8_t> { enum { value = true }; };
-template <> struct hb_is_signed<int16_t> { enum { value = true }; };
-template <> struct hb_is_signed<int32_t> { enum { value = true }; };
-template <> struct hb_is_signed<int64_t> { enum { value = true }; };
-template <> struct hb_is_signed<uint8_t> { enum { value = false }; };
-template <> struct hb_is_signed<uint16_t> { enum { value = false }; };
-template <> struct hb_is_signed<uint32_t> { enum { value = false }; };
-template <> struct hb_is_signed<uint64_t> { enum { value = false }; };
+template <> struct hb_is_signed<char> { enum { value = CHAR_MIN < 0 }; };
+template <> struct hb_is_signed<signed char> { enum { value = true }; };
+template <> struct hb_is_signed<unsigned char> { enum { value = false }; };
+template <> struct hb_is_signed<signed short> { enum { value = true }; };
+template <> struct hb_is_signed<unsigned short> { enum { value = false }; };
+template <> struct hb_is_signed<signed int> { enum { value = true }; };
+template <> struct hb_is_signed<unsigned int> { enum { value = false }; };
+template <> struct hb_is_signed<signed long> { enum { value = true }; };
+template <> struct hb_is_signed<unsigned long> { enum { value = false }; };
+template <> struct hb_is_signed<signed long long> { enum { value = true }; };
+template <> struct hb_is_signed<unsigned long long> { enum { value = false }; };
 #define hb_is_signed(T) hb_is_signed<T>::value
 
+template <typename T> struct hb_int_min { static constexpr T value = 0; };
+template <> struct hb_int_min<char> { static constexpr char value = CHAR_MIN; };
+template <> struct hb_int_min<int>  { static constexpr int  value = INT_MIN;  };
+template <> struct hb_int_min<long> { static constexpr long value = LONG_MIN; };
+#define hb_int_min(T) hb_int_min<T>::value
+
 template <bool is_signed> struct hb_signedness_int;
 template <> struct hb_signedness_int<false> { typedef unsigned int value; };
 template <> struct hb_signedness_int<true>  { typedef   signed int value; };
diff --git a/src/hb-mutex.hh b/src/hb-mutex.hh
index 35f1fde..a760e73 100644
--- a/src/hb-mutex.hh
+++ b/src/hb-mutex.hh
@@ -127,8 +127,6 @@
 
 struct hb_mutex_t
 {
-  /* TODO Add tracing. */
-
   hb_mutex_impl_t m;
 
   void init   () { hb_mutex_impl_init   (&m); }
diff --git a/src/hb-open-type.hh b/src/hb-open-type.hh
index 23f7511..041b984 100644
--- a/src/hb-open-type.hh
+++ b/src/hb-open-type.hh
@@ -182,7 +182,7 @@
   void *serialize (hb_serialize_context_t *c, const void *base)
   {
     void *t = c->start_embed<void> ();
-    *this = (char *) t - (char *) base; /* TODO(serialize) Overflow? */
+    c->check_assign (*this, (unsigned) ((char *) t - (char *) base));
     return t;
   }
 
@@ -284,8 +284,8 @@
     return * (Type *) Offset<OffsetType>::serialize (c, base);
   }
 
-  template <typename T>
-  bool serialize_subset (hb_subset_context_t *c, const T &src, const void *base)
+  template <typename T, typename ...Ts>
+  bool serialize_subset (hb_subset_context_t *c, const T &src, const void *base, Ts &&...ds)
   {
     *this = 0;
     if (has_null && &src == &Null (T))
@@ -295,7 +295,7 @@
 
     s->push ();
 
-    bool ret = src.subset (c);
+    bool ret = src.subset (c, hb_forward<Ts> (ds)...);
 
     if (ret || !has_null)
       s->add_link (*this, s->pop_pack (), base);
@@ -314,39 +314,13 @@
     return_trace (true);
   }
 
-  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  template <typename ...Ts>
+  bool sanitize (hb_sanitize_context_t *c, const void *base, Ts &&...ds) const
   {
     TRACE_SANITIZE (this);
     return_trace (sanitize_shallow (c, base) &&
 		  (this->is_null () ||
-		   StructAtOffset<Type> (base, *this).sanitize (c) ||
-		   neuter (c)));
-  }
-  template <typename T1>
-  bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (sanitize_shallow (c, base) &&
-		  (this->is_null () ||
-		   StructAtOffset<Type> (base, *this).sanitize (c, d1) ||
-		   neuter (c)));
-  }
-  template <typename T1, typename T2>
-  bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (sanitize_shallow (c, base) &&
-		  (this->is_null () ||
-		   StructAtOffset<Type> (base, *this).sanitize (c, d1, d2) ||
-		   neuter (c)));
-  }
-  template <typename T1, typename T2, typename T3>
-  bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2, T3 d3) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (sanitize_shallow (c, base) &&
-		  (this->is_null () ||
-		   StructAtOffset<Type> (base, *this).sanitize (c, d1, d2, d3) ||
+		   StructAtOffset<Type> (base, *this).sanitize (c, hb_forward<Ts> (ds)...) ||
 		   neuter (c)));
   }
 
@@ -430,29 +404,26 @@
      * we do not need to call their sanitize() as we already did
      * a bound check on the aggregate array size.  We just include
      * a small unreachable expression to make sure the structs
-     * pointed to do have a simple sanitize(), ie. they do not
+     * pointed to do have a simple sanitize() as well as an
+     * assignment opreator.  This ensures that they do not
      * reference other structs via offsets.
      */
-    (void) (false && arrayZ[0].sanitize (c));
+    if (false)
+    {
+      arrayZ[0].sanitize (c);
+      Type v;
+      v = arrayZ[0];
+    }
 
     return_trace (true);
   }
-  bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base) const
+  template <typename ...Ts>
+  bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts &&...ds) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
     for (unsigned int i = 0; i < count; i++)
-      if (unlikely (!arrayZ[i].sanitize (c, base)))
-	return_trace (false);
-    return_trace (true);
-  }
-  template <typename T>
-  bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base, T user_data) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
-    for (unsigned int i = 0; i < count; i++)
-      if (unlikely (!arrayZ[i].sanitize (c, base, user_data)))
+      if (unlikely (!arrayZ[i].sanitize (c, hb_forward<Ts> (ds)...)))
 	return_trace (false);
     return_trace (true);
   }
@@ -492,17 +463,12 @@
     return this+*p;
   }
 
-
-  bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  template <typename ...Ts>
+  bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts &&...ds) const
   {
     TRACE_SANITIZE (this);
-    return_trace ((UnsizedOffsetArrayOf<Type, OffsetType, has_null>::sanitize (c, count, this)));
-  }
-  template <typename T>
-  bool sanitize (hb_sanitize_context_t *c, unsigned int count, T user_data) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace ((UnsizedOffsetArrayOf<Type, OffsetType, has_null>::sanitize (c, count, this, user_data)));
+    return_trace ((UnsizedOffsetArrayOf<Type, OffsetType, has_null>
+		   ::sanitize (c, count, this, hb_forward<Ts> (ds)...)));
   }
 };
 
@@ -582,7 +548,7 @@
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    len = items_len; /* TODO(serialize) Overflow? */
+    c->check_assign (len, items_len);
     if (unlikely (!c->extend (*this))) return_trace (false);
     return_trace (true);
   }
@@ -622,24 +588,14 @@
 
     return_trace (true);
   }
-  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  template <typename ...Ts>
+  bool sanitize (hb_sanitize_context_t *c, Ts &&...ds) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
-      if (unlikely (!arrayZ[i].sanitize (c, base)))
-	return_trace (false);
-    return_trace (true);
-  }
-  template <typename T>
-  bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!sanitize_shallow (c))) return_trace (false);
-    unsigned int count = len;
-    for (unsigned int i = 0; i < count; i++)
-      if (unlikely (!arrayZ[i].sanitize (c, base, user_data)))
+      if (unlikely (!arrayZ[i].sanitize (c, hb_forward<Ts> (ds)...)))
 	return_trace (false);
     return_trace (true);
   }
@@ -706,16 +662,11 @@
     return_trace (true);
   }
 
-  bool sanitize (hb_sanitize_context_t *c) const
+  template <typename ...Ts>
+  bool sanitize (hb_sanitize_context_t *c, Ts &&...ds) const
   {
     TRACE_SANITIZE (this);
-    return_trace (OffsetArrayOf<Type>::sanitize (c, this));
-  }
-  template <typename T>
-  bool sanitize (hb_sanitize_context_t *c, T user_data) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (OffsetArrayOf<Type>::sanitize (c, this, user_data));
+    return_trace (OffsetArrayOf<Type>::sanitize (c, this, hb_forward<Ts> (ds)...));
   }
 };
 
@@ -747,7 +698,7 @@
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    lenP1 = items.length + 1; /* TODO(serialize) Overflow? */
+    c->check_assign (lenP1, items.length + 1);
     if (unlikely (!c->extend (*this))) return_trace (false);
     for (unsigned int i = 0; i < items.length; i++)
       arrayZ[i] = items[i];
@@ -763,10 +714,16 @@
      * we do not need to call their sanitize() as we already did
      * a bound check on the aggregate array size.  We just include
      * a small unreachable expression to make sure the structs
-     * pointed to do have a simple sanitize(), ie. they do not
+     * pointed to do have a simple sanitize() as well as an
+     * assignment opreator.  This ensures that they do not
      * reference other structs via offsets.
      */
-    (void) (false && arrayZ[0].sanitize (c));
+    if (false)
+    {
+      arrayZ[0].sanitize (c);
+      Type v;
+      v = arrayZ[0];
+    }
 
     return_trace (true);
   }
@@ -807,14 +764,14 @@
   unsigned int get_size () const
   { return lenM1.static_size + (lenM1 + 1) * Type::static_size; }
 
-  template <typename T>
-  bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
+  template <typename ...Ts>
+  bool sanitize (hb_sanitize_context_t *c, Ts &&...ds) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
     unsigned int count = lenM1 + 1;
     for (unsigned int i = 0; i < count; i++)
-      if (unlikely (!arrayZ[i].sanitize (c, base, user_data)))
+      if (unlikely (!arrayZ[i].sanitize (c, hb_forward<Ts> (ds)...)))
 	return_trace (false);
     return_trace (true);
   }
@@ -999,31 +956,27 @@
      * we do not need to call their sanitize() as we already did
      * a bound check on the aggregate array size.  We just include
      * a small unreachable expression to make sure the structs
-     * pointed to do have a simple sanitize(), ie. they do not
+     * pointed to do have a simple sanitize() as well as an
+     * assignment opreator.  This ensures that they do not
      * reference other structs via offsets.
      */
-    (void) (false && StructAtOffset<Type> (&bytesZ, 0).sanitize (c));
+    if (false)
+    {
+      (*this)[0].sanitize (c);
+      Type v;
+      v = (*this)[0];
+    }
 
     return_trace (true);
   }
-  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  template <typename ...Ts>
+  bool sanitize (hb_sanitize_context_t *c, Ts &&...ds) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
     unsigned int count = get_length ();
     for (unsigned int i = 0; i < count; i++)
-      if (unlikely (!(*this)[i].sanitize (c, base)))
-	return_trace (false);
-    return_trace (true);
-  }
-  template <typename T>
-  bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!sanitize_shallow (c))) return_trace (false);
-    unsigned int count = get_length ();
-    for (unsigned int i = 0; i < count; i++)
-      if (unlikely (!(*this)[i].sanitize (c, base, user_data)))
+      if (unlikely (!(*this)[i].sanitize (c, hb_forward<Ts> (ds)...)))
 	return_trace (false);
     return_trace (true);
   }
diff --git a/src/hb-ot-cff1-table.cc b/src/hb-ot-cff1-table.cc
index 8773c05..1d97e54 100644
--- a/src/hb-ot-cff1-table.cc
+++ b/src/hb-ot-cff1-table.cc
@@ -165,8 +165,8 @@
 {
   void init ()
   {
-    min.set_int (0x7FFFFFFF, 0x7FFFFFFF);
-    max.set_int (-0x80000000, -0x80000000);
+    min.set_int (INT_MAX, INT_MAX);
+    max.set_int (INT_MIN, INT_MIN);
   }
 
   void update (const point_t &pt)
diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh
index 18cbafa..3957d5d 100644
--- a/src/hb-ot-cff1-table.hh
+++ b/src/hb-ot-cff1-table.hh
@@ -110,7 +110,8 @@
     {
       if (glyph <= ranges[i].nLeft)
       {
-	return (hb_codepoint_t)ranges[i].first + glyph;
+	hb_codepoint_t code = (hb_codepoint_t) ranges[i].first + glyph;
+	return (likely (code < 0x100) ? code: CFF_UNDEF_CODE);
       }
       glyph -= (ranges[i].nLeft + 1);
     }
diff --git a/src/hb-ot-cff2-table.cc b/src/hb-ot-cff2-table.cc
index 7daa536..d2463d7 100644
--- a/src/hb-ot-cff2-table.cc
+++ b/src/hb-ot-cff2-table.cc
@@ -34,10 +34,10 @@
   void init ()
   {
     path_open = false;
-    min_x.set_int (0x7FFFFFFF);
-    min_y.set_int (0x7FFFFFFF);
-    max_x.set_int (-0x80000000);
-    max_y.set_int (-0x80000000);
+    min_x.set_int (INT_MAX);
+    min_y.set_int (INT_MAX);
+    max_x.set_int (INT_MIN);
+    max_y.set_int (INT_MIN);
   }
 
   void start_path ()         { path_open = true; }
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index e664e06..10c35e3 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -85,12 +85,12 @@
 
   bool serialize (hb_serialize_context_t *c,
 		  hb_sorted_array_t<const GlyphID> glyphs,
-		  int delta)
+		  unsigned delta)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
     if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
-    deltaGlyphID = delta; /* TODO(serialize) overflow? */
+    c->check_assign (deltaGlyphID, delta);
     return_trace (true);
   }
 
@@ -127,8 +127,8 @@
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
-  HBINT16	deltaGlyphID;		/* Add to original GlyphID to get
-					 * substitute GlyphID */
+  HBUINT16	deltaGlyphID;		/* Add to original GlyphID to get
+					 * substitute GlyphID, modulo 0x10000 */
   public:
   DEFINE_SIZE_STATIC (6);
 };
@@ -231,15 +231,14 @@
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
-    unsigned int format = 2;
-    int delta = 0;
+    unsigned format = 2;
+    unsigned delta = 0;
     if (glyphs.length)
     {
       format = 1;
-      /* TODO(serialize) check for wrap-around */
-      delta = substitutes[0] - glyphs[0];
+      delta = (unsigned) (substitutes[0] - glyphs[0]) & 0xFFFF;
       for (unsigned int i = 1; i < glyphs.length; i++)
-	if (delta != (int) (substitutes[i] - glyphs[i])) {
+	if (delta != ((unsigned) (substitutes[i] - glyphs[i]) & 0xFFFF)) {
 	  format = 2;
 	  break;
 	}
diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh
index 2e9165b..840c142 100644
--- a/src/hb-ot-layout-gsubgpos.hh
+++ b/src/hb-ot-layout-gsubgpos.hh
@@ -286,7 +286,7 @@
     };
 
     may_match_t may_match (const hb_glyph_info_t &info,
-				  const HBUINT16        *glyph_data) const
+			   const HBUINT16        *glyph_data) const
     {
       if (!(info.mask & mask) ||
 	  (syllable && syllable != info.syllable ()))
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index ef0bcc7..846414c 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -219,28 +219,28 @@
       continue; /* Feature disabled, or not enough bits. */
 
 
-    hb_bool_t found = false;
+    bool found = false;
     unsigned int feature_index[2];
     for (unsigned int table_index = 0; table_index < 2; table_index++)
     {
       if (required_feature_tag[table_index] == info->tag)
 	required_feature_stage[table_index] = info->stage[table_index];
 
-      found |= hb_ot_layout_language_find_feature (face,
-						   table_tags[table_index],
-						   script_index[table_index],
-						   language_index[table_index],
-						   info->tag,
-						   &feature_index[table_index]);
+      found |= (bool) hb_ot_layout_language_find_feature (face,
+							  table_tags[table_index],
+							  script_index[table_index],
+							  language_index[table_index],
+							  info->tag,
+							  &feature_index[table_index]);
     }
     if (!found && (info->flags & F_GLOBAL_SEARCH))
     {
       for (unsigned int table_index = 0; table_index < 2; table_index++)
       {
-	found |= hb_ot_layout_table_find_feature (face,
-						  table_tags[table_index],
-						  info->tag,
-						  &feature_index[table_index]);
+	found |= (bool) hb_ot_layout_table_find_feature (face,
+							 table_tags[table_index],
+							 info->tag,
+							 &feature_index[table_index]);
       }
     }
     if (!found && !(info->flags & F_HAS_FALLBACK))
diff --git a/src/hb-ot-name-language.cc b/src/hb-ot-name-language-static.hh
similarity index 98%
rename from src/hb-ot-name-language.cc
rename to src/hb-ot-name-language-static.hh
index 0e37e0a..fac3178 100644
--- a/src/hb-ot-name-language.cc
+++ b/src/hb-ot-name-language-static.hh
@@ -24,6 +24,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
+#ifndef HB_OT_NAME_LANGUAGE_STATIC_HH
+#define HB_OT_NAME_LANGUAGE_STATIC_HH
+
 #include "hb-ot-name-language.hh"
 
 /* Following two tables were generated by joining FreeType, FontConfig,
@@ -455,3 +458,5 @@
 				   hb_mac_language_map,
 				   ARRAY_LENGTH (hb_mac_language_map));
 }
+
+#endif /* HB_OT_NAME_LANGUAGE_STATIC_HH */
diff --git a/src/hb-ot-name-table.hh b/src/hb-ot-name-table.hh
index fca5091..72deb10 100644
--- a/src/hb-ot-name-table.hh
+++ b/src/hb-ot-name-table.hh
@@ -158,6 +158,150 @@
   unsigned int get_size () const
   { return min_size + count * nameRecordZ.item_size; }
 
+  void get_subsetted_ids (const name *source_name,
+                             const hb_subset_plan_t *plan,
+                             hb_vector_t<unsigned int>& name_record_idx_to_retain) const
+  {
+    for(unsigned int i = 0; i < count; i++)
+    {
+      if (format == 0 && (unsigned int) source_name->nameRecordZ[i].nameID > 25)
+        continue;
+      if (!hb_set_is_empty (plan->name_ids) &&
+          !hb_set_has (plan->name_ids, source_name->nameRecordZ[i].nameID))
+        continue;
+      name_record_idx_to_retain.push (i);
+    }
+  }
+
+  bool serialize_name_record (hb_serialize_context_t *c,
+                              const name *source_name,
+                              const hb_vector_t<unsigned int>& name_record_idx_to_retain)
+  {
+    for (unsigned int i = 0; i < name_record_idx_to_retain.length; i++)
+    {
+      unsigned int idx = name_record_idx_to_retain[i];
+      if (unlikely (idx >= source_name->count))
+      {
+        DEBUG_MSG (SUBSET, nullptr, "Invalid index: %d.", idx);
+        return false;
+      }
+
+      c->push<NameRecord> ();
+
+      NameRecord *p = c->embed<NameRecord> (source_name->nameRecordZ[idx]);
+      if (!p)
+        return false;
+      p->offset = 0;
+    }
+
+    return true;
+  }
+
+  bool serialize_strings (hb_serialize_context_t *c,
+                          const name *source_name,
+                          const hb_subset_plan_t *plan,
+                          const hb_vector_t<unsigned int>& name_record_idx_to_retain)
+  {
+    hb_face_t *face = plan->source;
+    accelerator_t acc;
+    acc.init (face);
+
+    for (unsigned int i = 0; i < name_record_idx_to_retain.length; i++)
+    {
+      unsigned int idx = name_record_idx_to_retain[i];
+      unsigned int size = acc.get_name (idx).get_size ();
+
+      c->push<char> ();
+      char *new_pos = c->allocate_size<char> (size);
+      
+      if (unlikely (new_pos == nullptr))
+      {
+        acc.fini ();
+        DEBUG_MSG (SUBSET, nullptr, "Couldn't allocate enough space for Name string: %u.",
+                   size);
+        return false;
+      }
+
+      const HBUINT8* source_string_pool = (source_name + source_name->stringOffset).arrayZ;
+      unsigned int name_record_offset = source_name->nameRecordZ[idx].offset;
+
+      memcpy (new_pos, source_string_pool + name_record_offset, size);
+    }
+
+    acc.fini ();
+    return true;
+  }
+
+  bool pack_record_and_strings (name *dest_name_unpacked,
+                                hb_serialize_context_t *c, 
+                                unsigned length)
+  {
+    hb_hashmap_t<unsigned, unsigned> id_str_idx_map;
+    for (int i = length-1; i >= 0; i--)
+    {
+      unsigned objidx = c->pop_pack ();
+      id_str_idx_map.set ((unsigned)i, objidx);
+    }
+
+    const void *base = & (dest_name_unpacked->nameRecordZ[length]); 
+    for (int i = length-1; i >= 0; i--)
+    {
+      unsigned str_idx = id_str_idx_map.get ((unsigned)i);
+      NameRecord& namerecord = dest_name_unpacked->nameRecordZ[i];
+      c->add_link<HBUINT16> (namerecord.offset, str_idx, base);
+      c->pop_pack ();
+    }
+
+    if (c->in_error ())
+      return false;
+
+    return true;
+  }
+
+  bool serialize (hb_serialize_context_t *c,
+                  const name *source_name,
+                  const hb_subset_plan_t *plan,
+                  const hb_vector_t<unsigned int>& name_record_idx_to_retain)
+  {
+    TRACE_SERIALIZE (this);
+
+    if (unlikely (!c->extend_min ((*this))))  return_trace (false);
+
+    this->format = source_name->format;
+    this->count = name_record_idx_to_retain.length;
+    this->stringOffset = min_size + name_record_idx_to_retain.length * NameRecord::static_size;
+
+
+    if (!serialize_name_record (c, source_name, name_record_idx_to_retain))
+      return_trace (false);
+
+    if (!serialize_strings (c, source_name, plan, name_record_idx_to_retain))
+      return_trace (false);
+
+    if (!pack_record_and_strings (this, c, name_record_idx_to_retain.length))
+      return_trace (false);
+
+    return_trace (true);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    hb_subset_plan_t *plan = c->plan;
+    hb_vector_t<unsigned int> name_record_idx_to_retain;
+
+    get_subsetted_ids (this, plan, name_record_idx_to_retain);
+
+    hb_serialize_context_t *serializer = c->serializer;
+    name *name_prime = serializer->start_embed<name> ();
+    if (!name_prime || !name_prime->serialize (serializer, this, plan, name_record_idx_to_retain))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "Failed to serialize write new name.");
+      return false;
+    }
+    
+    return true;
+  }
+
   bool sanitize_records (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 1fd8fc6..6d46fe3 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -1008,7 +1008,6 @@
       ginfo.cluster = buffer->cur().cluster;
       ginfo.mask = buffer->cur().mask;
       ginfo.syllable() = buffer->cur().syllable();
-      /* TODO Set glyph_props? */
 
       /* Insert dottedcircle after possible Repha. */
       while (buffer->idx < buffer->len && buffer->successful &&
diff --git a/src/hb-ot-shape-complex-khmer.cc b/src/hb-ot-shape-complex-khmer.cc
index 5746651..4c88477 100644
--- a/src/hb-ot-shape-complex-khmer.cc
+++ b/src/hb-ot-shape-complex-khmer.cc
@@ -368,7 +368,8 @@
   if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
     return;
 
-  /* Note: This loop is extra overhead, but should not be measurable. */
+  /* Note: This loop is extra overhead, but should not be measurable.
+   * TODO Use a buffer scratch flag to remove the loop. */
   bool has_broken_syllables = false;
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
@@ -407,7 +408,6 @@
       ginfo.cluster = buffer->cur().cluster;
       ginfo.mask = buffer->cur().mask;
       ginfo.syllable() = buffer->cur().syllable();
-      /* TODO Set glyph_props? */
 
       /* Insert dottedcircle after possible Repha. */
       while (buffer->idx < buffer->len && buffer->successful &&
diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc
index 70ab972..b1f6b65 100644
--- a/src/hb-ot-shape-complex-myanmar.cc
+++ b/src/hb-ot-shape-complex-myanmar.cc
@@ -301,7 +301,8 @@
   if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
     return;
 
-  /* Note: This loop is extra overhead, but should not be measurable. */
+  /* Note: This loop is extra overhead, but should not be measurable.
+   * TODO Use a buffer scratch flag to remove the loop. */
   bool has_broken_syllables = false;
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
diff --git a/src/hb-ot-shape-complex-use-table.cc b/src/hb-ot-shape-complex-use-table.cc
index cb5c358..ddf7053 100644
--- a/src/hb-ot-shape-complex-use-table.cc
+++ b/src/hb-ot-shape-complex-use-table.cc
@@ -318,8 +318,8 @@
   /* 1B20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 1B30 */     B,     B,     B,     B, CMAbv,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VPre,  VPre,
   /* 1B40 */  VPst,  VPst,  VAbv,  VAbv,     H,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,
-  /* 1B50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
-  /* 1B60 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv,
+  /* 1B50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,    GB,    GB,     O,     O,    GB,
+  /* 1B60 */     O,     O,    GB,     O,     O,     O,     O,     O,    GB,     O,     O, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv,
   /* 1B70 */ SMAbv, SMAbv, SMAbv, SMAbv,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
 
   /* Sundanese */
diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc
index eecde6e..ec4d4aa 100644
--- a/src/hb-ot-shape-complex-use.cc
+++ b/src/hb-ot-shape-complex-use.cc
@@ -526,7 +526,8 @@
   if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
     return;
 
-  /* Note: This loop is extra overhead, but should not be measurable. */
+  /* Note: This loop is extra overhead, but should not be measurable.
+   * TODO Use a buffer scratch flag to remove the loop. */
   bool has_broken_syllables = false;
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
@@ -560,7 +561,6 @@
       ginfo.cluster = buffer->cur().cluster;
       ginfo.mask = buffer->cur().mask;
       ginfo.syllable() = buffer->cur().syllable();
-      /* TODO Set glyph_props? */
 
       /* Insert dottedcircle after possible Repha. */
       while (buffer->idx < buffer->len && buffer->successful &&
diff --git a/src/hb-serialize.hh b/src/hb-serialize.hh
index 0d908bd..7566881 100644
--- a/src/hb-serialize.hh
+++ b/src/hb-serialize.hh
@@ -120,17 +120,23 @@
     this->packed.push (nullptr);
   }
 
-  bool propagate_error (bool e)
-  { return this->successful = this->successful && e; }
-  template <typename T> bool propagate_error (const T &obj)
-  { return this->successful = this->successful && !obj.in_error (); }
-  template <typename T> bool propagate_error (const T *obj)
-  { return this->successful = this->successful && !obj->in_error (); }
-  template <typename T1, typename T2> bool propagate_error (T1 &&o1, T2 &&o2)
-  { return propagate_error (o1) && propagate_error (o2); }
-  template <typename T1, typename T2, typename T3>
-  bool propagate_error (T1 &&o1, T2 &&o2, T3 &&o3)
-  { return propagate_error (o1) && propagate_error (o2, o3); }
+  bool check_success (bool success)
+  { return this->successful && (success || (err_other_error (), false)); }
+
+  template <typename T1, typename T2>
+  bool check_equal (T1 &&v1, T2 &&v2)
+  { return check_success (v1 == v2); }
+
+  template <typename T1, typename T2>
+  bool check_assign (T1 &v1, T2 &&v2)
+  { return check_equal (v1 = v2, v2); }
+
+  template <typename T> bool propagate_error (T &&obj)
+  { return check_success (!hb_deref_pointer (obj).in_error ()); }
+
+  template <typename T1, typename... Ts> bool propagate_error (T1 &&o1, Ts &&...os)
+  { return propagate_error (hb_forward<T1> (o1)) &&
+	   propagate_error (hb_forward<Ts> (os)...); }
 
   /* To be called around main operation. */
   template <typename Type>
@@ -172,7 +178,7 @@
   {
     object_t *obj = object_pool.alloc ();
     if (unlikely (!obj))
-      propagate_error (false);
+      check_success (false);
     else
     {
       obj->head = head;
@@ -272,7 +278,7 @@
 
     auto& link = *current->links.push ();
     link.is_wide = sizeof (T) == 4;
-    link.position = (const char *) &ofs - (const char *) base;
+    link.position = (const char *) &ofs - current->head;
     link.bias = (const char *) base - current->head;
     link.objidx = objidx;
   }
@@ -294,14 +300,14 @@
 	if (link.is_wide)
 	{
 	  auto &off = * ((BEInt<uint32_t, 4> *) (parent.head + link.position));
-	  off = offset;
-	  propagate_error (off == offset);
+	  assert (0 == off);
+	  check_assign (off, offset);
 	}
 	else
 	{
 	  auto &off = * ((BEInt<uint16_t, 2> *) (parent.head + link.position));
-	  off = offset;
-	  propagate_error (off == offset);
+	  assert (0 == off);
+	  check_assign (off, offset);
 	}
       }
     }
@@ -323,8 +329,9 @@
     return ret;
   }
 
-  void
-  err_ran_out_of_room () { this->ran_out_of_room = true; }
+  /* Following two functions exist to allow setting breakpoint on. */
+  void err_ran_out_of_room () { this->ran_out_of_room = true; }
+  void err_other_error () { this->successful = false; }
 
   template <typename Type>
   Type *allocate_size (unsigned int size)
@@ -358,6 +365,24 @@
     memcpy (ret, &obj, size);
     return ret;
   }
+
+  template <typename Type> auto
+  _copy (const Type &obj, hb_priority<1>) const HB_RETURN (Type *, obj.copy (this))
+
+  template <typename Type> auto
+  _copy (const Type &obj, hb_priority<0>) const -> decltype (&(obj = obj))
+  {
+    Type *ret = this->allocate_size<Type> (sizeof (Type));
+    if (unlikely (!ret)) return nullptr;
+    *ret = obj;
+    return ret;
+  }
+
+  /* Like embed, but active: calls obj.operator=() or obj.copy() to transfer data
+   * instead of memcpy(). */
+  template <typename Type>
+  Type *copy (const Type &obj) { return _copy (obj, hb_prioritize); }
+
   template <typename Type>
   hb_serialize_context_t &operator << (const Type &obj) { embed (obj); return *this; }
 
diff --git a/src/hb-static.cc b/src/hb-static.cc
index 4c51588..6b89183 100644
--- a/src/hb-static.cc
+++ b/src/hb-static.cc
@@ -37,6 +37,7 @@
 #include "hb-ot-maxp-table.hh"
 
 #ifndef HB_NO_VISIBILITY
+#include "hb-ot-name-language-static.hh"
 
 hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {};
 /*thread_local*/ hb_vector_size_impl_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {};
diff --git a/src/hb-subset-input.cc b/src/hb-subset-input.cc
index 4d203b5..b3b27d4 100644
--- a/src/hb-subset-input.cc
+++ b/src/hb-subset-input.cc
@@ -44,6 +44,7 @@
 
   input->unicodes = hb_set_create ();
   input->glyphs = hb_set_create ();
+  input->name_ids = hb_set_create ();
   input->drop_hints = false;
   input->drop_layout = true;
   input->desubroutinize = false;
@@ -81,6 +82,7 @@
 
   hb_set_destroy (subset_input->unicodes);
   hb_set_destroy (subset_input->glyphs);
+  hb_set_destroy (subset_input->name_ids);
 
   free (subset_input);
 }
@@ -109,6 +111,12 @@
   return subset_input->glyphs;
 }
 
+HB_EXTERN hb_set_t *
+hb_subset_input_nameid_set (hb_subset_input_t *subset_input)
+{
+  return subset_input->name_ids;
+}
+
 HB_EXTERN void
 hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input,
 				hb_bool_t drop_hints)
diff --git a/src/hb-subset-input.hh b/src/hb-subset-input.hh
index 04d6e12..d01fece 100644
--- a/src/hb-subset-input.hh
+++ b/src/hb-subset-input.hh
@@ -40,6 +40,7 @@
 
   hb_set_t *unicodes;
   hb_set_t *glyphs;
+  hb_set_t *name_ids;
 
   bool drop_hints : 1;
   bool drop_layout : 1;
@@ -49,7 +50,7 @@
    *
    * features
    * lookups
-   * nameIDs
+   * name_ids
    * ...
    */
 };
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 8b72314..fe636b1 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -204,12 +204,14 @@
   plan->drop_hints = input->drop_hints;
   plan->drop_layout = input->drop_layout;
   plan->desubroutinize = input->desubroutinize;
-  plan->unicodes = hb_set_create();
+  plan->retain_gids = input->retain_gids;
+  plan->unicodes = hb_set_create ();
+  plan->name_ids = hb_set_reference (input->name_ids);
   plan->source = hb_face_reference (face);
   plan->dest = hb_face_builder_create ();
-  plan->codepoint_to_glyph = hb_map_create();
-  plan->glyph_map = hb_map_create();
-  plan->reverse_glyph_map = hb_map_create();
+  plan->codepoint_to_glyph = hb_map_create ();
+  plan->glyph_map = hb_map_create ();
+  plan->reverse_glyph_map = hb_map_create ();
   plan->_glyphset = _populate_gids_to_retain (face,
                                               input->unicodes,
                                               input->glyphs,
@@ -238,6 +240,7 @@
   if (!hb_object_destroy (plan)) return;
 
   hb_set_destroy (plan->unicodes);
+  hb_set_destroy (plan->name_ids);
   hb_face_destroy (plan->source);
   hb_face_destroy (plan->dest);
   hb_map_destroy (plan->codepoint_to_glyph);
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh
index 56726d4..abbab5e 100644
--- a/src/hb-subset-plan.hh
+++ b/src/hb-subset-plan.hh
@@ -42,10 +42,14 @@
   bool drop_hints : 1;
   bool drop_layout : 1;
   bool desubroutinize : 1;
+  bool retain_gids : 1;
 
   // For each cp that we'd like to retain maps to the corresponding gid.
   hb_set_t *unicodes;
 
+  //name_ids we would like to retain
+  hb_set_t *name_ids;
+
   // The glyph subset
   hb_map_t *codepoint_to_glyph;
 
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index a7066cd..80d1628 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -43,6 +43,7 @@
 #include "hb-ot-cff1-table.hh"
 #include "hb-ot-cff2-table.hh"
 #include "hb-ot-vorg-table.hh"
+#include "hb-ot-name-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
 #include "hb-ot-var-gvar-table.hh"
@@ -69,11 +70,11 @@
 static bool
 _subset2 (hb_subset_plan_t *plan)
 {
+  bool result = true;
   hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
   const TableType *table = source_blob->as<TableType> ();
 
   hb_tag_t tag = TableType::tableTag;
-  hb_bool_t result = false;
   if (source_blob->data)
   {
     hb_vector_t<char> buf;
@@ -88,8 +89,7 @@
     hb_serialize_context_t serializer ((void *) buf, buf_size);
     serializer.start_serialize<TableType> ();
     hb_subset_context_t c (plan, &serializer);
-    result = table->subset (&c);
-    serializer.end_serialize ();
+    bool needed = table->subset (&c);
     if (serializer.ran_out_of_room)
     {
       buf_size += (buf_size >> 1) + 32;
@@ -101,22 +101,23 @@
       }
       goto retry;
     }
-    if (serializer.in_error ())
-    {
-      abort ();
-    }
+    serializer.end_serialize ();
+
+    result = !serializer.in_error ();
 
     if (result)
     {
-      hb_blob_t *dest_blob = serializer.copy_blob ();
-      DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length);
-      result = c.plan->add_table (tag, dest_blob);
-      hb_blob_destroy (dest_blob);
-    }
-    else
-    {
-      DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag));
-      result = true;
+      if (needed)
+      {
+	hb_blob_t *dest_blob = serializer.copy_blob ();
+	DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length);
+	result = c.plan->add_table (tag, dest_blob);
+	hb_blob_destroy (dest_blob);
+      }
+      else
+      {
+	DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag));
+      }
     }
   }
   else
@@ -160,6 +161,9 @@
     case HB_OT_TAG_hdmx:
       result = _subset<const OT::hdmx> (plan);
       break;
+    case HB_OT_TAG_name:
+      result = _subset2<const OT::name> (plan);
+      break;
     case HB_OT_TAG_head:
       // TODO that won't work well if there is no glyf
       DEBUG_MSG(SUBSET, nullptr, "skip head, handled by glyf");
diff --git a/src/hb-subset.h b/src/hb-subset.h
index 657709e..5034506 100644
--- a/src/hb-subset.h
+++ b/src/hb-subset.h
@@ -54,6 +54,9 @@
 HB_EXTERN hb_set_t *
 hb_subset_input_glyph_set (hb_subset_input_t *subset_input);
 
+HB_EXTERN hb_set_t *
+hb_subset_input_nameid_set (hb_subset_input_t *subset_input);
+
 HB_EXTERN void
 hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input,
 				hb_bool_t drop_hints);
diff --git a/src/hb.hh b/src/hb.hh
index ee35c4d..0336ed5 100644
--- a/src/hb.hh
+++ b/src/hb.hh
@@ -167,8 +167,7 @@
 #include "hb-aat.h"
 #define HB_AAT_H_IN
 
-#include "hb-aat.h"
-
+#include <limits.h>
 #include <math.h>
 #include <stdlib.h>
 #include <stddef.h>
diff --git a/src/test-algs.cc b/src/test-algs.cc
index 42a9538..163a79a 100644
--- a/src/test-algs.cc
+++ b/src/test-algs.cc
@@ -28,6 +28,17 @@
 #include "hb-algs.hh"
 
 
+static char *
+test_func (int a, char **b)
+{
+  return b ? b[a] : nullptr;
+}
+
+struct A
+{
+  void a () {}
+};
+
 int
 main (int argc, char **argv)
 {
@@ -46,5 +57,10 @@
   q.second = 4;
   assert (i == 4);
 
+  hb_invoke (test_func, 0, nullptr);
+
+  A a;
+  hb_invoke (&A::a, a);
+
   return 0;
 }
diff --git a/src/test-size-params.cc b/src/test-gpos-size-params.cc
similarity index 100%
rename from src/test-size-params.cc
rename to src/test-gpos-size-params.cc
diff --git a/src/test-would-substitute.cc b/src/test-gsub-would-substitute.cc
similarity index 100%
rename from src/test-would-substitute.cc
rename to src/test-gsub-would-substitute.cc
diff --git a/src/test-iter.cc b/src/test-iter.cc
index 675bbe3..3de3401 100644
--- a/src/test-iter.cc
+++ b/src/test-iter.cc
@@ -91,7 +91,7 @@
 }
 
 template <typename Iterable,
-	 hb_enable_if (hb_is_iterable (Iterable))>
+	  hb_enable_if (hb_is_iterable (Iterable))>
 static void
 test_iterable (const Iterable &lst = Null(Iterable))
 {
@@ -127,6 +127,11 @@
   hb_set_t st;
   test_iterable (st);
   hb_sorted_array_t<int> sa;
+  (void) static_cast<hb_iter_t<hb_sorted_array_t<int>, hb_sorted_array_t<int>::item_t>&> (sa);
+  (void) static_cast<hb_iter_t<hb_sorted_array_t<int>, hb_sorted_array_t<int>::__item_t__>&> (sa);
+  (void) static_cast<hb_iter_t<hb_sorted_array_t<int>, int&>&>(sa);
+  (void) static_cast<hb_iter_t<hb_sorted_array_t<int>>&>(sa);
+  (void) static_cast<hb_iter_t<hb_array_t<int>, int&>&> (sa);
   test_iterable (sa);
 
   test_iterable<hb_array_t<int> > ();
@@ -181,7 +186,7 @@
   ;
   /* The result should be something like 0->10, 1->11, ..., 9->19 */
   assert (hb_map_get (result, 9) == 19);
-  
+
   unsigned int temp3 = 0;
   + hb_iter(src)
   | hb_map([&] (int i) -> int { return ++temp3; })
diff --git a/src/test-name-table.cc b/src/test-ot-name.cc
similarity index 100%
rename from src/test-name-table.cc
rename to src/test-ot-name.cc
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index eb03381..70a9fda 100644
--- a/test/api/Makefile.am
+++ b/test/api/Makefile.am
@@ -55,6 +55,7 @@
 	test-subset-vvar \
 	test-unicode \
 	test-version \
+	test-subset-nameids \
 	$(NULL)
 
 test_subset_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
@@ -70,6 +71,7 @@
 test_subset_gvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
 test_subset_hvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
 test_subset_vvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
+test_subset_nameids_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
 
 test_unicode_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
diff --git a/test/api/fonts/nameID.dup.expected.ttf b/test/api/fonts/nameID.dup.expected.ttf
new file mode 100644
index 0000000..e9e7ff5
--- /dev/null
+++ b/test/api/fonts/nameID.dup.expected.ttf
Binary files differ
diff --git a/test/api/fonts/nameID.dup.origin.ttf b/test/api/fonts/nameID.dup.origin.ttf
new file mode 100644
index 0000000..aad75d4
--- /dev/null
+++ b/test/api/fonts/nameID.dup.origin.ttf
Binary files differ
diff --git a/test/api/fonts/nameID.expected.ttf b/test/api/fonts/nameID.expected.ttf
new file mode 100644
index 0000000..ccd4b8b
--- /dev/null
+++ b/test/api/fonts/nameID.expected.ttf
Binary files differ
diff --git a/test/api/fonts/nameID.origin.ttf b/test/api/fonts/nameID.origin.ttf
new file mode 100644
index 0000000..aec973a
--- /dev/null
+++ b/test/api/fonts/nameID.origin.ttf
Binary files differ
diff --git a/test/api/hb-subset-test.h b/test/api/hb-subset-test.h
index 3e759a8..8f32aee 100644
--- a/test/api/hb-subset-test.h
+++ b/test/api/hb-subset-test.h
@@ -65,6 +65,15 @@
   return input;
 }
 
+static inline hb_subset_input_t *
+hb_subset_test_create_input_from_nameids (const hb_set_t *name_ids)
+{
+  hb_subset_input_t *input = hb_subset_input_create_or_fail ();
+  hb_set_t * input_name_ids  = hb_subset_input_nameid_set (input);
+  hb_set_union (input_name_ids, name_ids);
+  return input;
+}
+
 static inline hb_face_t *
 hb_subset_test_create_subset (hb_face_t *source,
                               hb_subset_input_t *input)
diff --git a/test/api/test-ot-face.c b/test/api/test-ot-face.c
index 9ebcb4e..12ac666 100644
--- a/test/api/test-ot-face.c
+++ b/test/api/test-ot-face.c
@@ -111,7 +111,7 @@
 }
 
 static void
-test_ot_var_axis_on_zero_named_instance ()
+test_ot_var_axis_on_zero_named_instance (void)
 {
   hb_face_t *face = hb_test_open_font_file ("fonts/Zycon.ttf");
   g_assert (hb_ot_var_get_axis_count (face));
diff --git a/test/api/test-subset-nameids.c b/test/api/test-subset-nameids.c
new file mode 100644
index 0000000..b58a86c
--- /dev/null
+++ b/test/api/test-subset-nameids.c
@@ -0,0 +1,79 @@
+/*
+ * 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
+ */
+
+#include "hb-test.h"
+#include "hb-subset-test.h"
+
+static void
+test_subset_nameids (void)
+{
+  hb_face_t *face_origin = hb_test_open_font_file ("fonts/nameID.origin.ttf");
+  hb_face_t *face_expected = hb_test_open_font_file ("fonts/nameID.expected.ttf");
+
+  hb_set_t *name_ids = hb_set_create();
+  hb_face_t *face_subset;
+  hb_set_add (name_ids, 0);
+  hb_set_add (name_ids, 9);
+  face_subset = hb_subset_test_create_subset (face_origin, hb_subset_test_create_input_from_nameids (name_ids));
+  hb_set_destroy (name_ids);
+
+  hb_subset_test_check (face_expected, face_subset, HB_TAG ('n','a','m','e'));
+
+  hb_face_destroy (face_subset);
+  hb_face_destroy (face_origin);
+  hb_face_destroy (face_expected);
+}
+
+static void
+test_subset_nameids_with_dup_strs (void)
+{
+  hb_face_t *face_origin = hb_test_open_font_file ("fonts/nameID.dup.origin.ttf");
+  hb_face_t *face_expected = hb_test_open_font_file ("fonts/nameID.dup.expected.ttf");
+
+  hb_set_t *name_ids = hb_set_create();
+  hb_face_t *face_subset;
+  hb_set_add (name_ids, 1);
+  hb_set_add (name_ids, 3);
+  face_subset = hb_subset_test_create_subset (face_origin, hb_subset_test_create_input_from_nameids (name_ids));
+  hb_set_destroy (name_ids);
+
+  hb_subset_test_check (face_expected, face_subset, HB_TAG ('n','a','m','e'));
+
+  hb_face_destroy (face_subset);
+  hb_face_destroy (face_origin);
+  hb_face_destroy (face_expected);
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_subset_nameids);
+  hb_test_add (test_subset_nameids_with_dup_strs);
+
+  return hb_test_run();
+}
diff --git a/test/fuzzing/Makefile.am b/test/fuzzing/Makefile.am
index a77df70..5bd2d7e 100644
--- a/test/fuzzing/Makefile.am
+++ b/test/fuzzing/Makefile.am
@@ -55,8 +55,8 @@
 hb_subset_fuzzer_DEPENDENCIES = $(top_builddir)/src/libharfbuzz-subset.la
 
 check:
-	EXEEXT="$(EXEEXT)" srcdir="$(srcdir)" builddir="$(builddir)" $(srcdir)/run-shape-fuzzer-tests.py
-	EXEEXT="$(EXEEXT)" srcdir="$(srcdir)" builddir="$(builddir)" $(srcdir)/run-subset-fuzzer-tests.py
+	EXEEXT="$(EXEEXT)" srcdir="$(srcdir)" builddir="$(builddir)" LIBTOOL="$(LIBTOOL)" $(srcdir)/run-shape-fuzzer-tests.py
+	EXEEXT="$(EXEEXT)" srcdir="$(srcdir)" builddir="$(builddir)" LIBTOOL="$(LIBTOOL)" $(srcdir)/run-subset-fuzzer-tests.py
 check-valgrind:
 	$(AM_V_at)RUN_VALGRIND=1 $(MAKE) $(AM_MAKEFLGS) check
 
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5716947896893440 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5716947896893440
new file mode 100644
index 0000000..6391320
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5716947896893440
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5923632099885056 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5923632099885056
new file mode 100644
index 0000000..0a3c6df
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5923632099885056
Binary files differ
diff --git a/test/fuzzing/run-shape-fuzzer-tests.py b/test/fuzzing/run-shape-fuzzer-tests.py
index 90ed509..ba480dd 100755
--- a/test/fuzzing/run-shape-fuzzer-tests.py
+++ b/test/fuzzing/run-shape-fuzzer-tests.py
@@ -67,36 +67,36 @@
 print ('hb_shape_fuzzer:', hb_shape_fuzzer)
 fails = 0
 
+libtool = os.environ.get('LIBTOOL')
 valgrind = None
 if os.environ.get('RUN_VALGRIND', ''):
 	valgrind = which ('valgrind')
 	if valgrind is None:
 		print ("""Valgrind requested but not found.""")
 		sys.exit (1)
+	if libtool is None:
+		print ("""Valgrind support is currently autotools only and needs libtool but not found.""")
+
 
 parent_path = os.path.join (srcdir, "fonts")
 for file in os.listdir (parent_path):
 	path = os.path.join(parent_path, file)
 
-	text, returncode = cmd ([hb_shape_fuzzer, path])
-	if text.strip ():
+	if valgrind:
+		text, returncode = cmd (libtool.split(' ') + ['--mode=execute', valgrind + ' --leak-check=full --error-exitcode=1', '--', hb_shape_fuzzer, path])
+	else:
+		text, returncode = cmd ([hb_shape_fuzzer, path])
+		if 'error' in text:
+			returncode = 1
+
+	if not valgrind and text.strip ():
 		print (text)
 
-	failed = False
-	if returncode != 0 or 'error' in text:
+	if returncode != 0:
 		print ('failure on %s' % file)
-		failed = True
-
-	if valgrind:
-		text, returncode = cmd ([valgrind, '--error-exitcode=1', '--leak-check=full', hb_shape_fuzzer, path])
-		if returncode:
-			print (text)
-			print ('failure on %s' % file)
-			failed = True
-
-	if failed:
 		fails = fails + 1
 
+
 if fails:
 	print ("%i shape fuzzer related tests failed." % fails)
 	sys.exit (1)
diff --git a/test/fuzzing/run-subset-fuzzer-tests.py b/test/fuzzing/run-subset-fuzzer-tests.py
index 7392a92..3ac2288 100755
--- a/test/fuzzing/run-subset-fuzzer-tests.py
+++ b/test/fuzzing/run-subset-fuzzer-tests.py
@@ -2,7 +2,54 @@
 
 from __future__ import print_function, division, absolute_import
 
-import sys, os, subprocess
+import sys, os, subprocess, tempfile, threading
+
+
+def which(program):
+	# https://stackoverflow.com/a/377028
+	def is_exe(fpath):
+		return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+	fpath, _ = os.path.split(program)
+	if fpath:
+		if is_exe(program):
+			return program
+	else:
+		for path in os.environ["PATH"].split(os.pathsep):
+			exe_file = os.path.join(path, program)
+			if is_exe(exe_file):
+				return exe_file
+
+	return None
+
+
+def cmd(command):
+	# https://stackoverflow.com/a/4408409
+	# https://stackoverflow.com/a/10012262
+	with tempfile.TemporaryFile() as tempf:
+		p = subprocess.Popen (command, stderr=tempf)
+		is_killed = {'value': False}
+
+		def timeout(p, is_killed):
+			is_killed['value'] = True
+			p.kill()
+		timer = threading.Timer (2, timeout, [p, is_killed])
+
+		try:
+			timer.start()
+			p.wait ()
+			tempf.seek (0)
+			text = tempf.read().decode ("utf-8").strip ()
+			returncode = p.returncode
+		finally:
+			timer.cancel()
+
+		if is_killed['value']:
+			text = 'error: timeout, ' + text
+			returncode = 1
+
+		return text, returncode
+
 
 srcdir = os.environ.get ("srcdir", ".")
 EXEEXT = os.environ.get ("EXEEXT", "")
@@ -20,21 +67,37 @@
 print ('hb_subset_fuzzer:', hb_subset_fuzzer)
 fails = 0
 
+libtool = os.environ.get('LIBTOOL')
+valgrind = None
+if os.environ.get('RUN_VALGRIND', ''):
+	valgrind = which ('valgrind')
+	if valgrind is None:
+		print ("""Valgrind requested but not found.""")
+		sys.exit (1)
+	if libtool is None:
+		print ("""Valgrind support is currently autotools only and needs libtool but not found.""")
+
+
 def run_dir (parent_path):
 	global fails
 	for file in os.listdir (parent_path):
 		path = os.path.join(parent_path, file)
 
 		print ("running subset fuzzer against %s" % path)
-		p = subprocess.Popen ([hb_subset_fuzzer, path])
+		if valgrind:
+			text, returncode = cmd (libtool.split(' ') + ['--mode=execute', valgrind + ' --leak-check=full --show-leak-kinds=all --error-exitcode=1', '--', hb_subset_fuzzer, path])
+		else:
+			text, returncode = cmd ([hb_subset_fuzzer, path])
+			if 'error' in text:
+				returncode = 1
 
-		if p.wait () != 0:
+		if not valgrind and text.strip ():
+			print (text)
+
+		if returncode != 0:
 			print ("failed for %s" % path)
 			fails = fails + 1
 
-		if p.wait () != 0:
-			print ("failed for %s" % path)
-			fails = fails + 1
 
 run_dir (os.path.join (srcdir, "..", "subset", "data", "fonts"))
 # TODO running these tests very slow tests.  Fix and re-enable
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.61,62,63.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.61,62,63.ttf
new file mode 100644
index 0000000..12d9208
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.61,62,63.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.61,63.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.61,63.ttf
new file mode 100644
index 0000000..1af233f
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.61,63.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.61.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.61.ttf
new file mode 100644
index 0000000..a699eea
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.61.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.62.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.62.ttf
new file mode 100644
index 0000000..52706dc
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.62.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.63.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.63.ttf
new file mode 100644
index 0000000..3de7c77
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.63.ttf
Binary files differ
diff --git a/test/subset/data/profiles/name-ids.txt b/test/subset/data/profiles/name-ids.txt
new file mode 100644
index 0000000..db42c09
--- /dev/null
+++ b/test/subset/data/profiles/name-ids.txt
@@ -0,0 +1 @@
+--name-IDs=0,1,2
diff --git a/test/subset/data/tests/basics.tests b/test/subset/data/tests/basics.tests
index 4fc3f4e..794510d 100644
--- a/test/subset/data/tests/basics.tests
+++ b/test/subset/data/tests/basics.tests
@@ -6,6 +6,7 @@
 drop-hints.txt
 drop-hints-retain-gids.txt
 retain-gids.txt
+name-ids.txt
 
 SUBSETS:
 abc
diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index 33e584b..682ca4c 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -93,6 +93,7 @@
     hb_subset_input_set_drop_hints (input, subset_options.drop_hints);
     hb_subset_input_set_retain_gids (input, subset_options.retain_gids);
     hb_subset_input_set_desubroutinize (input, subset_options.desubroutinize);
+    hb_set_set (hb_subset_input_nameid_set (input), subset_options.name_ids);
 
     hb_face_t *face = hb_font_get_face (font);
 
diff --git a/util/options.cc b/util/options.cc
index c5a4f0f..a9b3fc7 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -971,6 +971,49 @@
   g_string_append_c (gs, '\n');
 }
 
+static gboolean
+parse_nameids (const char *name G_GNUC_UNUSED,
+               const char *arg,
+               gpointer    data,
+               GError    **error G_GNUC_UNUSED)
+{
+  subset_options_t *subset_opts = (subset_options_t *) data;
+
+  hb_set_t *name_ids = hb_set_create ();
+  char *s = (char *) arg;
+  char *p;
+
+  while (s && *s)
+  {
+    while (*s && strchr ("<+>{},;&#\\xXuUnNiI\n\t\v\f\r ", *s))
+      s++;
+    if (!*s)
+      break;
+
+    errno = 0;
+    hb_codepoint_t u = strtoul (s, &p, 10);
+    if (errno || s == p)
+    {
+      hb_set_destroy (name_ids);
+      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                   "Failed parsing nameID values at: '%s'", s);
+      return false;
+    }
+
+    hb_set_add (name_ids, u);
+
+    s = p;
+  }
+
+  hb_set_t *prev = subset_opts->name_ids;
+  subset_opts->name_ids = hb_set_reference (name_ids);
+  hb_set_destroy (prev);
+  hb_set_destroy (name_ids);
+
+  return true;
+}
+
+
 void
 subset_options_t::add_options (option_parser_t *parser)
 {
@@ -980,6 +1023,7 @@
     {"no-hinting", 0, 0, G_OPTION_ARG_NONE,  &this->drop_hints,   "Whether to drop hints",   nullptr},
     {"retain-gids", 0, 0, G_OPTION_ARG_NONE,  &this->retain_gids,   "If set don't renumber glyph ids in the subset.",   nullptr},
     {"desubroutinize", 0, 0, G_OPTION_ARG_NONE,  &this->desubroutinize,   "Remove CFF/CFF2 use of subroutines",   nullptr},
+    {"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK,  (gpointer) &parse_nameids,  "Subset specified nameids", "list of int numbers"},
 
     {nullptr}
   };
@@ -989,3 +1033,4 @@
          "Options subsetting",
          this);
 }
+
diff --git a/util/options.hh b/util/options.hh
index 84139f5..2691e22 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -677,16 +677,24 @@
     drop_hints = false;
     retain_gids = false;
     desubroutinize = false;
+    name_ids = hb_set_create ();
 
     add_options (parser);
   }
 
+  virtual ~subset_options_t ()
+  {
+    hb_set_destroy (name_ids);
+  }
+
+
   void add_options (option_parser_t *parser);
 
   hb_bool_t keep_layout;
   hb_bool_t drop_hints;
   hb_bool_t retain_gids;
   hb_bool_t desubroutinize;
+  hb_set_t *name_ids;
 };
 
 /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */