Merge remote-tracking branch 'upstream/master' into var-subset
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 6ad98d2..e221943 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -22,15 +22,16 @@
       - run: make -j4
       - run: make check || .ci/fail.sh
 
-  macos-10.14.3-aat-fonts:
+  macos-10.14.4-aat-fonts:
     macos:
-      xcode: "10.2.0"
+      xcode: "11.0.0"
     steps:
       - checkout
-      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo icu4c graphite2
+      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo icu4c graphite2 cmake
       - run: export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig" && ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-coretext --with-graphite2
       - run: make -j4
       - run: make check || .ci/fail.sh
+      - run: cmake -Bbuild -H. -DHB_HAVE_CORETEXT=1 -DHB_BUILD_TESTS=0 && cmake --build build
 
   distcheck:
     docker:
@@ -127,7 +128,7 @@
       - run: apt update || true
       - run: apt install -y clang lld binutils libtool autoconf automake make pkg-config gtk-doc-tools ragel libfreetype6-dev libfontconfig1-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
       - run: pip install fonttools
-      - run: CFLAGS="-Weverything -Wno-reserved-id-macro -Wno-conversion -Wno-padded -Wno-sign-conversion -Wno-cast-qual -Wno-documentation -Wno-documentation-unknown-command" CXXFLAGS="-Weverything -Wno-old-style-cast -Wno-documentation -Wno-documentation-unknown-command -Wno-c++98-compat -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-sign-conversion -Wno-padded -Wno-shorten-64-to-32 -Wno-reserved-id-macro -Wno-float-conversion -Wno-format-pedantic -Wno-shadow -Wno-conversion -Wno-zero-as-null-pointer-constant -Wno-missing-field-initializers -Wno-used-but-marked-unused -Wno-unused-macros -Wno-comma -Wno-float-equal -Wno-disabled-macro-expansion -Wno-weak-vtables -Wno-unused-parameter -Wno-covered-switch-default -Wno-unreachable-code -Wno-unused-template" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 --with-fontconfig
+      - run: CFLAGS="-Weverything -Wno-reserved-id-macro -Wno-conversion -Wno-padded -Wno-sign-conversion -Wno-cast-qual -Wno-documentation -Wno-documentation-unknown-command -DHB_WITH_WIN1256" CXXFLAGS="-Weverything -Wno-old-style-cast -Wno-documentation -Wno-documentation-unknown-command -Wno-c++98-compat -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-sign-conversion -Wno-padded -Wno-shorten-64-to-32 -Wno-reserved-id-macro -Wno-float-conversion -Wno-format-pedantic -Wno-shadow -Wno-conversion -Wno-zero-as-null-pointer-constant -Wno-missing-field-initializers -Wno-used-but-marked-unused -Wno-unused-macros -Wno-comma -Wno-float-equal -Wno-disabled-macro-expansion -Wno-weak-vtables -Wno-unused-parameter -Wno-covered-switch-default -Wno-unreachable-code -Wno-unused-template -DHB_WITH_WIN1256" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 --with-fontconfig
       - run: make -j32 CPPFLAGS="-Werror"
       - run: make check CPPFLAGS="-Werror" || .ci/fail.sh
 
@@ -193,9 +194,9 @@
       - run: apt update || true
       - run: apt install -y clang lld binutils libtool autoconf automake make pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
       - run: pip install fonttools
-      - run: CPPFLAGS="-fsanitize=undefined" LDFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
+      - run: CPPFLAGS="-fsanitize=undefined -fno-sanitize-recover=undefined" LDFLAGS="-fsanitize=undefined -fno-sanitize-recover=undefined -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
       - run: make -j32
-      - run: make check || .ci/fail.sh | asan_symbolize | c++filt
+      - run: UBSAN_OPTIONS=print_stacktrace=1 make check || .ci/fail.sh | asan_symbolize | c++filt
 
   fedora-O0-debug-outoftreebuild-mingw:
     docker:
@@ -203,8 +204,8 @@
     steps:
       - checkout
       - run: dnf install -y pkg-config ragel gcc gcc-c++ automake autoconf libtool make which glib2-devel freetype-devel cairo-devel libicu-devel gobject-introspection-devel graphite2-devel redhat-rpm-config python mingw32-gcc-c++ mingw64-gcc-c++ mingw32-glib2 mingw32-cairo mingw32-freetype mingw64-glib2 mingw64-cairo mingw64-freetype glibc-devel.i686 || true
-      - run: CFLAGS="-O0" CXXFLAGS="-O0" CPPFLAGS="-DHB_DEBUG" NOCONFIGURE=1 ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2
-      - run: mkdir build && cd build && ../configure && make -j32 && (make check || ../.ci/fail.sh)
+      - run: NOCONFIGURE=1 ./autogen.sh
+      - run: mkdir build && cd build && CFLAGS="-O0" CXXFLAGS="-O0" CPPFLAGS="-DHB_DEBUG" ../configure --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 && make -j32 && (make check || ../.ci/fail.sh)
       - run: pip install pefile
       - run: mkdir winbuild32 && cd winbuild32 && ../mingw32.sh && make -j32 && make dist-win && cp harfbuzz-*-win32.zip harfbuzz-win32.zip
       - run: mkdir winbuild64 && cd winbuild64 && ../mingw64.sh && make -j32 && make dist-win && cp harfbuzz-*-win64.zip harfbuzz-win64.zip
@@ -306,7 +307,7 @@
       # macOS
       - macos-10.12.6-aat-fonts
       - macos-10.13.6-aat-fonts
-      - macos-10.14.3-aat-fonts
+      - macos-10.14.4-aat-fonts
 
       # both autotools and cmake
       - distcheck
diff --git a/.editorconfig b/.editorconfig
index 4850f2b..4291676 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,7 +6,7 @@
 end_of_line = lf
 insert_final_newline = true
 
-[*.{c,cc,h,hh}]
+[*.{c,cc,h,hh,rl}]
 tab_width = 8
 indent_size = 2
 indent_style = tab # should be space
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 66e4a83..2a8fd8b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -108,7 +108,7 @@
 if (UNIX)
   list(APPEND CMAKE_REQUIRED_LIBRARIES m)
 endif ()
-check_funcs(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l)
+check_funcs(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l roundf)
 check_include_file(unistd.h HAVE_UNISTD_H)
 if (${HAVE_UNISTD_H})
   add_definitions(-DHAVE_UNISTD_H)
diff --git a/NEWS b/NEWS
index ef5f487..30fa8c3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+Overview of changes leading to 2.6.2
+Monday, September 30, 2019
+====================================
+- Misc small fixes, mostly to build-related issues.
+
+
 Overview of changes leading to 2.6.1
 Thursday, August 22, 2019
 ====================================
diff --git a/README.md b/README.md
index c3cc0c2..e0ef935 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,7 @@
 [![Codacy Code Health](https://api.codacy.com/project/badge/Grade/f17f1708783c447488bc8dd317150eaa)](https://app.codacy.com/app/behdad/harfbuzz)
 [![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/master/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz)
 [![Coverals Code Coverage](https://img.shields.io/coveralls/harfbuzz/harfbuzz.svg)](https://coveralls.io/r/harfbuzz/harfbuzz)
+[![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions)
 [ABI Tracker](http://abi-laboratory.pro/tracker/timeline/harfbuzz/)
 
 This is HarfBuzz, a text shaping library.
@@ -23,3 +24,11 @@
 For test execution, see [TESTING.md](TESTING.md).
 
 Documentation: https://harfbuzz.github.io
+
+
+<details>
+  <summary>Packaging status of HarfBuzz</summary
+
+[![Packaging status](https://repology.org/badge/vertical-allrepos/harfbuzz.svg?header=harfbuzz)](https://repology.org/project/harfbuzz/versions)  
+
+</details>
diff --git a/configure.ac b/configure.ac
index ab079fc..c88cffe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [2.6.1],
+        [2.6.2],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -77,7 +77,7 @@
 ])
 
 # Functions and headers
-AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l)
+AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l roundf)
 AC_CHECK_HEADERS(unistd.h sys/mman.h xlocale.h stdbool.h)
 
 # Compiler flags
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index 52b43a9..c625b92 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -612,14 +612,14 @@
 
 <SECTION>
 <FILE>hb-ot-meta</FILE>
-hb_ot_meta_t
+hb_ot_meta_tag_t
 hb_ot_meta_get_entry_tags
 hb_ot_meta_reference_entry
 </SECTION>
 
 <SECTION>
 <FILE>hb-ot-metrics</FILE>
-hb_ot_metrics_t
+hb_ot_metrics_tag_t
 hb_ot_metrics_get_position
 hb_ot_metrics_get_variation
 hb_ot_metrics_get_x_variation
diff --git a/src/Makefile.am b/src/Makefile.am
index 7173f4b..a76d968 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -411,7 +411,7 @@
 dump_use_data_CPPFLAGS = $(HBCFLAGS)
 dump_use_data_LDADD = libharfbuzz.la $(HBLIBS)
 
-COMPILED_TESTS = test-algs test-iter test-meta test-ot-tag test-unicode-ranges test-bimap
+COMPILED_TESTS = test-algs test-iter test-meta test-number test-ot-tag test-unicode-ranges test-bimap
 COMPILED_TESTS_CPPFLAGS = $(HBCFLAGS) -DMAIN -UNDEBUG
 COMPILED_TESTS_LDADD = libharfbuzz.la $(HBLIBS)
 check_PROGRAMS += $(COMPILED_TESTS)
@@ -429,6 +429,10 @@
 test_meta_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
 test_meta_LDADD = $(COMPILED_TESTS_LDADD)
 
+test_number_SOURCES = test-number.cc hb-number.cc
+test_number_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
+test_number_LDADD = $(COMPILED_TESTS_LDADD)
+
 test_ot_tag_SOURCES = hb-ot-tag.cc
 test_ot_tag_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
 test_ot_tag_LDADD = $(COMPILED_TESTS_LDADD)
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 22e765d..07bbd06 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -49,6 +49,8 @@
 	hb-meta.hh \
 	hb-mutex.hh \
 	hb-null.hh \
+	hb-number.cc \
+	hb-number.hh \
 	hb-object.hh \
 	hb-open-file.hh \
 	hb-open-type.hh \
@@ -169,6 +171,7 @@
 HB_BASE_RAGEL_GENERATED_sources = \
 	hb-buffer-deserialize-json.hh \
 	hb-buffer-deserialize-text.hh \
+	hb-number-parser.hh \
 	hb-ot-shape-complex-indic-machine.hh \
 	hb-ot-shape-complex-khmer-machine.hh \
 	hb-ot-shape-complex-myanmar-machine.hh \
@@ -177,6 +180,7 @@
 HB_BASE_RAGEL_sources = \
 	hb-buffer-deserialize-json.rl \
 	hb-buffer-deserialize-text.rl \
+	hb-number-parser.rl \
 	hb-ot-shape-complex-indic-machine.rl \
 	hb-ot-shape-complex-khmer-machine.rl \
 	hb-ot-shape-complex-myanmar-machine.rl \
@@ -243,6 +247,8 @@
 
 # Sources for libharfbuzz-subset
 HB_SUBSET_sources = \
+	hb-number.cc \
+	hb-number.hh \
 	hb-ot-cff1-table.cc \
 	hb-ot-cff2-table.cc \
 	hb-static.cc \
diff --git a/src/harfbuzz.cc b/src/harfbuzz.cc
index 20940cd..2aa5a79 100644
--- a/src/harfbuzz.cc
+++ b/src/harfbuzz.cc
@@ -8,6 +8,7 @@
 #include "hb-fallback-shape.cc"
 #include "hb-font.cc"
 #include "hb-map.cc"
+#include "hb-number.cc"
 #include "hb-ot-cff1-table.cc"
 #include "hb-ot-cff2-table.cc"
 #include "hb-ot-color.cc"
diff --git a/src/hb-aat-fdsc-table.hh b/src/hb-aat-fdsc-table.hh
index 1188e35..604d5bc 100644
--- a/src/hb-aat-fdsc-table.hh
+++ b/src/hb-aat-fdsc-table.hh
@@ -65,7 +65,7 @@
   protected:
   Tag		tag;		/* The 4-byte table tag name. */
   union {
-  Fixed		value;		/* The value for the descriptor tag. */
+  HBFixed		value;		/* The value for the descriptor tag. */
   HBUINT32	nalfType;	/* If the tag is `nalf`, see non_alphabetic_value_t */
   } u;
   public:
@@ -108,7 +108,7 @@
   }
 
   protected:
-  Fixed		version;	/* Version number of the font descriptors
+  HBFixed		version;	/* Version number of the font descriptors
 				 * table (0x00010000 for the current version). */
   LArrayOf<FontDescriptor>
 		descriptors;	/* List of tagged-coordinate pairs style descriptors
diff --git a/src/hb-aat-layout-bsln-table.hh b/src/hb-aat-layout-bsln-table.hh
index 9139d28..15ef2da 100644
--- a/src/hb-aat-layout-bsln-table.hh
+++ b/src/hb-aat-layout-bsln-table.hh
@@ -82,7 +82,7 @@
   }
 
   protected:
-  GlyphID	stdGlyph;	/* The specific glyph index number in this
+  HBGlyphID	stdGlyph;	/* The specific glyph index number in this
 				 * font that is used to set the baseline values.
 				 * This is the standard glyph.
 				 * This glyph must contain a set of control points
@@ -105,7 +105,7 @@
   }
 
   protected:
-  GlyphID	stdGlyph;	/* ditto */
+  HBGlyphID	stdGlyph;	/* ditto */
   HBUINT16	ctlPoints[32];	/* ditto */
   Lookup<HBUINT16>
 		lookupTable;	/* Lookup table that maps glyphs to their
diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh
index a8c4b76..473f2cd 100644
--- a/src/hb-aat-layout-common.hh
+++ b/src/hb-aat-layout-common.hh
@@ -93,8 +93,8 @@
     return_trace (c->check_struct (this) && value.sanitize (c, base));
   }
 
-  GlyphID	last;		/* Last GlyphID in this segment */
-  GlyphID	first;		/* First GlyphID in this segment */
+  HBGlyphID	last;		/* Last GlyphID in this segment */
+  HBGlyphID	first;		/* First GlyphID in this segment */
   T		value;		/* The lookup value (only one) */
   public:
   DEFINE_SIZE_STATIC (4 + T::static_size);
@@ -162,8 +162,8 @@
 		  valuesZ.sanitize (c, base, last - first + 1, hb_forward<Ts> (ds)...));
   }
 
-  GlyphID	last;		/* Last GlyphID in this segment */
-  GlyphID	first;		/* First GlyphID in this segment */
+  HBGlyphID	last;		/* Last GlyphID in this segment */
+  HBGlyphID	first;		/* First GlyphID in this segment */
   NNOffsetTo<UnsizedArrayOf<T>>
 		valuesZ;	/* A 16-bit offset from the start of
 				 * the table to the data. */
@@ -222,7 +222,7 @@
     return_trace (c->check_struct (this) && value.sanitize (c, base));
   }
 
-  GlyphID	glyph;		/* Last GlyphID */
+  HBGlyphID	glyph;		/* Last GlyphID */
   T		value;		/* The lookup value (only one) */
   public:
   DEFINE_SIZE_STATIC (2 + T::static_size);
@@ -284,7 +284,7 @@
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 8 */
-  GlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
+  HBGlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
   HBUINT16	glyphCount;	/* Total number of glyphs (equivalent to the last
 				 * glyph minus the value of firstGlyph plus 1). */
   UnsizedArrayOf<T>
@@ -326,7 +326,7 @@
   protected:
   HBUINT16	format;		/* Format identifier--format = 8 */
   HBUINT16	valueSize;	/* Byte size of each value. */
-  GlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
+  HBGlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
   HBUINT16	glyphCount;	/* Total number of glyphs (equivalent to the last
 				 * glyph minus the value of firstGlyph plus 1). */
   UnsizedArrayOf<HBUINT8>
@@ -658,7 +658,7 @@
     return_trace (c->check_struct (this) && classArray.sanitize (c));
   }
   protected:
-  GlyphID		firstGlyph;	/* First glyph index included in the trimmed array. */
+  HBGlyphID		firstGlyph;	/* First glyph index included in the trimmed array. */
   ArrayOf<HBUCHAR>	classArray;	/* The class codes (indexed by glyph index minus
 					 * firstGlyph). */
   public:
@@ -678,7 +678,7 @@
 				     const void *base,
 				     const T *array)
   {
-    return (offset - ((const char *) array - (const char *) base)) / sizeof (T);
+    return (offset - ((const char *) array - (const char *) base)) / T::static_size;
   }
   template <typename T>
   static unsigned int byteOffsetToIndex (unsigned int offset,
diff --git a/src/hb-aat-layout-feat-table.hh b/src/hb-aat-layout-feat-table.hh
index c822994..788d408 100644
--- a/src/hb-aat-layout-feat-table.hh
+++ b/src/hb-aat-layout-feat-table.hh
@@ -206,7 +206,7 @@
   SortedUnsizedArrayOf<FeatureName>
 		namesZ;		/* The feature name array. */
   public:
-  DEFINE_SIZE_STATIC (24);
+  DEFINE_SIZE_ARRAY (12, namesZ);
 };
 
 } /* namespace AAT */
diff --git a/src/hb-aat-layout-just-table.hh b/src/hb-aat-layout-just-table.hh
index e15c946..e1787d1 100644
--- a/src/hb-aat-layout-just-table.hh
+++ b/src/hb-aat-layout-just-table.hh
@@ -70,9 +70,9 @@
 
   ActionSubrecordHeader
 		header;
-  Fixed		lowerLimit; 	/* If the distance factor is less than this value,
+  HBFixed		lowerLimit; 	/* If the distance factor is less than this value,
 				 * then the ligature is decomposed. */
-  Fixed		upperLimit; 	/* If the distance factor is greater than this value,
+  HBFixed		upperLimit; 	/* If the distance factor is greater than this value,
 				 * then the ligature is decomposed. */
   HBUINT16 	order;		/* Numerical order in which this ligature will
 				 * be decomposed; you may want infrequent ligatures
@@ -100,7 +100,7 @@
   protected:
   ActionSubrecordHeader
 		header;
-  GlyphID	addGlyph;	/* Glyph that should be added if the distance factor
+  HBGlyphID	addGlyph;	/* Glyph that should be added if the distance factor
 				 * is growing. */
 
   public:
@@ -118,14 +118,14 @@
   protected:
   ActionSubrecordHeader
 		header;
-  Fixed 	substThreshold; /* Distance growth factor (in ems) at which
+  HBFixed 	substThreshold; /* Distance growth factor (in ems) at which
 				 * this glyph is replaced and the growth factor
 				 * recalculated. */
-  GlyphID 	addGlyph; 	/* Glyph to be added as kashida. If this value is
+  HBGlyphID 	addGlyph; 	/* Glyph to be added as kashida. If this value is
 				 * 0xFFFF, no extra glyph will be added. Note that
 				 * generally when a glyph is added, justification
 				 * will need to be redone. */
-  GlyphID 	substGlyph; 	/* Glyph to be substituted for this glyph if the
+  HBGlyphID 	substGlyph; 	/* Glyph to be substituted for this glyph if the
 				 * growth factor equals or exceeds the value of
 				 * substThreshold. */
   public:
@@ -146,13 +146,13 @@
   HBUINT32 	variationAxis;	/* The 4-byte tag identifying the ductile axis.
 				 * This would normally be 0x64756374 ('duct'),
 				 * but you may use any axis the font contains. */
-  Fixed 	minimumLimit; 	/* The lowest value for the ductility axis tha
+  HBFixed 	minimumLimit; 	/* The lowest value for the ductility axis tha
 				 * still yields an acceptable appearance. Normally
 				 * this will be 1.0. */
-  Fixed 	noStretchValue; /* This is the default value that corresponds to
+  HBFixed 	noStretchValue; /* This is the default value that corresponds to
 				 * no change in appearance. Normally, this will
 				 * be 1.0. */
-  Fixed 	maximumLimit; 	/* The highest value for the ductility axis that
+  HBFixed 	maximumLimit; 	/* The highest value for the ductility axis that
 				 * still yields an acceptable appearance. */
   public:
   DEFINE_SIZE_STATIC (22);
@@ -170,7 +170,7 @@
   ActionSubrecordHeader
 		header;
   HBUINT16 	flags;		/* Currently unused; set to 0. */
-  GlyphID 	glyph;		/* Glyph that should be added if the distance factor
+  HBGlyphID 	glyph;		/* Glyph that should be added if the distance factor
 				 * is growing. */
   public:
   DEFINE_SIZE_STATIC (10);
@@ -271,14 +271,14 @@
   };
 
   protected:
-  Fixed		beforeGrowLimit;/* The ratio by which the advance width of the
+  HBFixed		beforeGrowLimit;/* The ratio by which the advance width of the
 				 * glyph is permitted to grow on the left or top side. */
-  Fixed		beforeShrinkLimit;
+  HBFixed		beforeShrinkLimit;
 				/* The ratio by which the advance width of the
 				 * glyph is permitted to shrink on the left or top side. */
-  Fixed		afterGrowLimit;	/* The ratio by which the advance width of the glyph
+  HBFixed		afterGrowLimit;	/* The ratio by which the advance width of the glyph
 				 * is permitted to shrink on the left or top side. */
-  Fixed		afterShrinkLimit;
+  HBFixed		afterShrinkLimit;
 				/* The ratio by which the advance width of the glyph
 				 * is at most permitted to shrink on the right or
 				 * bottom side. */
diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 25de8c6..be1b339 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -82,8 +82,8 @@
   }
 
   protected:
-  GlyphID	left;
-  GlyphID	right;
+  HBGlyphID	left;
+  HBGlyphID	right;
   FWORD		value;
   public:
   DEFINE_SIZE_STATIC (6);
@@ -392,7 +392,7 @@
 
     const UnsizedArrayOf<FWORD> &arrayZ = this+array;
     unsigned int kern_idx = l + r;
-    kern_idx = Types::offsetToIndex (kern_idx, this, &arrayZ);
+    kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ);
     const FWORD *v = &arrayZ[kern_idx];
     if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
 
@@ -733,8 +733,8 @@
 {
   typedef ExtendedTypes Types;
 
-  unsigned int tuple_count () const { return tupleCount; }
-  bool is_horizontal () const       { return !(coverage & Vertical); }
+  unsigned   tuple_count () const { return tupleCount; }
+  bool     is_horizontal () const { return !(coverage & Vertical); }
 
   enum Coverage
   {
diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
index 2925414..d8df579 100644
--- a/src/hb-aat-layout-morx-table.hh
+++ b/src/hb-aat-layout-morx-table.hh
@@ -240,21 +240,21 @@
       if (buffer->idx == buffer->len && !mark_set)
 	return;
 
-      const GlyphID *replacement;
+      const HBGlyphID *replacement;
 
       replacement = nullptr;
       if (Types::extended)
       {
 	if (entry.data.markIndex != 0xFFFF)
 	{
-	  const Lookup<GlyphID> &lookup = subs[entry.data.markIndex];
+	  const Lookup<HBGlyphID> &lookup = subs[entry.data.markIndex];
 	  replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs);
 	}
       }
       else
       {
 	unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint;
-	const UnsizedArrayOf<GlyphID> &subs_old = (const UnsizedArrayOf<GlyphID> &) subs;
+	const UnsizedArrayOf<HBGlyphID> &subs_old = (const UnsizedArrayOf<HBGlyphID> &) subs;
 	replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
 	if (!replacement->sanitize (&c->sanitizer) || !*replacement)
 	  replacement = nullptr;
@@ -272,14 +272,14 @@
       {
 	if (entry.data.currentIndex != 0xFFFF)
 	{
-	  const Lookup<GlyphID> &lookup = subs[entry.data.currentIndex];
+	  const Lookup<HBGlyphID> &lookup = subs[entry.data.currentIndex];
 	  replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs);
 	}
       }
       else
       {
 	unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint;
-	const UnsizedArrayOf<GlyphID> &subs_old = (const UnsizedArrayOf<GlyphID> &) subs;
+	const UnsizedArrayOf<HBGlyphID> &subs_old = (const UnsizedArrayOf<HBGlyphID> &) subs;
 	replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
 	if (!replacement->sanitize (&c->sanitizer) || !*replacement)
 	  replacement = nullptr;
@@ -304,7 +304,7 @@
     bool mark_set;
     unsigned int mark;
     const ContextualSubtable *table;
-    const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT, false> &subs;
+    const UnsizedOffsetListOf<Lookup<HBGlyphID>, HBUINT, false> &subs;
   };
 
   bool apply (hb_aat_apply_context_t *c) const
@@ -348,7 +348,7 @@
   protected:
   StateTable<Types, EntryData>
 		machine;
-  NNOffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT, false>, HBUINT>
+  NNOffsetTo<UnsizedOffsetListOf<Lookup<HBGlyphID>, HBUINT, false>, HBUINT>
 		substitutionTables;
   public:
   DEFINE_SIZE_STATIC (20);
@@ -520,7 +520,7 @@
 	  if (action & (LigActionStore | LigActionLast))
 	  {
 	    ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ);
-	    const GlyphID &ligatureData = ligature[ligature_idx];
+	    const HBGlyphID &ligatureData = ligature[ligature_idx];
 	    if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break;
 	    hb_codepoint_t lig = ligatureData;
 
@@ -554,7 +554,7 @@
     const LigatureSubtable *table;
     const UnsizedArrayOf<HBUINT32> &ligAction;
     const UnsizedArrayOf<HBUINT16> &component;
-    const UnsizedArrayOf<GlyphID> &ligature;
+    const UnsizedArrayOf<HBGlyphID> &ligature;
     unsigned int match_length;
     unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
   };
@@ -586,7 +586,7 @@
 		ligAction;	/* Offset to the ligature action table. */
   NNOffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT>
 		component;	/* Offset to the component table. */
-  NNOffsetTo<UnsizedArrayOf<GlyphID>, HBUINT>
+  NNOffsetTo<UnsizedArrayOf<HBGlyphID>, HBUINT>
 		ligature;	/* Offset to the actual ligature lists. */
   public:
   DEFINE_SIZE_STATIC (28);
@@ -606,7 +606,7 @@
     unsigned int count = c->buffer->len;
     for (unsigned int i = 0; i < count; i++)
     {
-      const GlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
+      const HBGlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
       if (replacement)
       {
 	info[i].codepoint = *replacement;
@@ -624,7 +624,7 @@
   }
 
   protected:
-  Lookup<GlyphID>	substitute;
+  Lookup<HBGlyphID>	substitute;
   public:
   DEFINE_SIZE_MIN (2);
 };
@@ -726,7 +726,7 @@
       {
 	unsigned int count = (flags & MarkedInsertCount);
 	unsigned int start = entry.data.markedInsertIndex;
-	const GlyphID *glyphs = &insertionAction[start];
+	const HBGlyphID *glyphs = &insertionAction[start];
 	if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
 
 	bool before = flags & MarkedInsertBefore;
@@ -754,7 +754,7 @@
       {
 	unsigned int count = (flags & CurrentInsertCount) >> 5;
 	unsigned int start = entry.data.currentInsertIndex;
-	const GlyphID *glyphs = &insertionAction[start];
+	const HBGlyphID *glyphs = &insertionAction[start];
 	if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
 
 	bool before = flags & CurrentInsertBefore;
@@ -793,7 +793,7 @@
     private:
     hb_aat_apply_context_t *c;
     unsigned int mark;
-    const UnsizedArrayOf<GlyphID> &insertionAction;
+    const UnsizedArrayOf<HBGlyphID> &insertionAction;
   };
 
   bool apply (hb_aat_apply_context_t *c) const
@@ -819,7 +819,7 @@
   protected:
   StateTable<Types, EntryData>
 		machine;
-  NNOffsetTo<UnsizedArrayOf<GlyphID>, HBUINT>
+  NNOffsetTo<UnsizedArrayOf<HBGlyphID>, HBUINT>
 		insertionAction;	/* Byte offset from stateHeader to the start of
 					 * the insertion glyph table. */
   public:
@@ -1080,10 +1080,10 @@
  * The 'mort'/'morx' Table
  */
 
-template <typename Types>
+template <typename Types, hb_tag_t TAG>
 struct mortmorx
 {
-  static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx;
+  static constexpr hb_tag_t tableTag = TAG;
 
   bool has_data () const { return version != 0; }
 
@@ -1143,14 +1143,8 @@
   DEFINE_SIZE_MIN (8);
 };
 
-struct morx : mortmorx<ExtendedTypes>
-{
-  static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx;
-};
-struct mort : mortmorx<ObsoleteTypes>
-{
-  static constexpr hb_tag_t tableTag = HB_AAT_TAG_mort;
-};
+struct morx : mortmorx<ExtendedTypes, HB_AAT_TAG_morx> {};
+struct mort : mortmorx<ObsoleteTypes, HB_AAT_TAG_mort> {};
 
 
 } /* namespace AAT */
diff --git a/src/hb-aat-layout-trak-table.hh b/src/hb-aat-layout-trak-table.hh
index cd272a5..99dddd8 100644
--- a/src/hb-aat-layout-trak-table.hh
+++ b/src/hb-aat-layout-trak-table.hh
@@ -62,7 +62,7 @@
   }
 
   protected:
-  Fixed		track;		/* Track value for this record. */
+  HBFixed		track;		/* Track value for this record. */
   NameID	trackNameID;	/* The 'name' table index for this track.
 				 * (a short word or phrase like "loose"
 				 * or "very tight") */
@@ -82,7 +82,7 @@
 			const void *base) const
   {
     unsigned int sizes = nSizes;
-    hb_array_t<const Fixed> size_table ((base+sizeTable).arrayZ, sizes);
+    hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
 
     float s0 = size_table[idx].to_float ();
     float s1 = size_table[idx + 1].to_float ();
@@ -120,7 +120,7 @@
     if (!sizes) return 0.;
     if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
 
-    hb_array_t<const Fixed> size_table ((base+sizeTable).arrayZ, sizes);
+    hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
     unsigned int size_index;
     for (size_index = 0; size_index < sizes - 1; size_index++)
       if (size_table[size_index].to_float () >= ptem)
@@ -141,7 +141,7 @@
   protected:
   HBUINT16	nTracks;	/* Number of separate tracks included in this table. */
   HBUINT16	nSizes;		/* Number of point sizes included in this table. */
-  LOffsetTo<UnsizedArrayOf<Fixed>, false>
+  LOffsetTo<UnsizedArrayOf<HBFixed>, false>
 		sizeTable;	/* Offset from start of the tracking table to
 				 * Array[nSizes] of size values.. */
   UnsizedArrayOf<TrackTableEntry>
diff --git a/src/hb-algs.hh b/src/hb-algs.hh
index 7886894..c57481b 100644
--- a/src/hb-algs.hh
+++ b/src/hb-algs.hh
@@ -32,6 +32,7 @@
 #include "hb.hh"
 #include "hb-meta.hh"
 #include "hb-null.hh"
+#include "hb-number.hh"
 
 
 /* Encodes three unsigned integers in one 64-bit number.  If the inputs have more than 21 bits,
@@ -82,6 +83,7 @@
 struct
 {
   private:
+
   template <typename T> constexpr auto
   impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
 
@@ -895,17 +897,12 @@
 static inline hb_bool_t
 hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
 {
-  /* Pain because we don't know whether s is nul-terminated. */
-  char buf[64];
-  len = hb_min (ARRAY_LENGTH (buf) - 1, len);
-  strncpy (buf, s, len);
-  buf[len] = '\0';
+  unsigned int v;
+  const char *p = s;
+  const char *end = p + len;
+  if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, base)))
+    return false;
 
-  char *end;
-  errno = 0;
-  unsigned long v = strtoul (buf, &end, base);
-  if (errno) return false;
-  if (*end) return false;
   *out = v;
   return true;
 }
@@ -994,6 +991,18 @@
   operator () (const T &a) const HB_AUTO_RETURN (-a)
 }
 HB_FUNCOBJ (hb_neg);
+struct
+{
+  template <typename T> constexpr auto
+  operator () (T &a) const HB_AUTO_RETURN (++a)
+}
+HB_FUNCOBJ (hb_inc);
+struct
+{
+  template <typename T> constexpr auto
+  operator () (T &a) const HB_AUTO_RETURN (--a)
+}
+HB_FUNCOBJ (hb_dec);
 
 
 /* Compiler-assisted vectorization. */
diff --git a/src/hb-array.hh b/src/hb-array.hh
index 2e7a4fa..20d3eb7 100644
--- a/src/hb-array.hh
+++ b/src/hb-array.hh
@@ -50,7 +50,7 @@
   template <typename U,
 	    hb_enable_if (hb_is_cr_convertible(U, Type))>
   hb_array_t (const hb_array_t<U> &o) :
-    hb_iter_with_fallback_t<hb_array_t<Type>, Type&> (),
+    hb_iter_with_fallback_t<hb_array_t, Type&> (),
     arrayZ (o.arrayZ), length (o.length), backwards_length (o.backwards_length) {}
   template <typename U,
 	    hb_enable_if (hb_is_cr_convertible(U, Type))>
@@ -106,7 +106,7 @@
    */
 
   /* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */
-  int cmp (const hb_array_t<Type> &a) const
+  int cmp (const hb_array_t &a) const
   {
     if (length != a.length)
       return (int) a.length - (int) length;
@@ -114,8 +114,8 @@
   }
   HB_INTERNAL static int cmp (const void *pa, const void *pb)
   {
-    hb_array_t<Type> *a = (hb_array_t<Type> *) pa;
-    hb_array_t<Type> *b = (hb_array_t<Type> *) pb;
+    hb_array_t *a = (hb_array_t *) pa;
+    hb_array_t *b = (hb_array_t *) pb;
     return b->cmp (*a);
   }
 
@@ -141,13 +141,13 @@
   hb_sorted_array_t<Type> qsort (int (*cmp_)(const void*, const void*))
   {
     if (likely (length))
-      hb_qsort (arrayZ, length, this->item_size, cmp_);
+      hb_qsort (arrayZ, length, this->get_item_size (), cmp_);
     return hb_sorted_array_t<Type> (*this);
   }
   hb_sorted_array_t<Type> qsort ()
   {
     if (likely (length))
-      hb_qsort (arrayZ, length, this->item_size, Type::cmp);
+      hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp);
     return hb_sorted_array_t<Type> (*this);
   }
   void qsort (unsigned int start, unsigned int end)
@@ -155,16 +155,16 @@
     end = hb_min (end, length);
     assert (start <= end);
     if (likely (start < end))
-      hb_qsort (arrayZ + start, end - start, this->item_size, Type::cmp);
+      hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp);
   }
 
   /*
    * Other methods.
    */
 
-  unsigned int get_size () const { return length * this->item_size; }
+  unsigned int get_size () const { return length * this->get_item_size (); }
 
-  hb_array_t<Type> sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
+  hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
   {
     if (!start_offset && !seg_count)
       return *this;
@@ -176,11 +176,13 @@
       count -= start_offset;
     if (seg_count)
       count = *seg_count = hb_min (count, *seg_count);
-    return hb_array_t<Type> (arrayZ + start_offset, count);
+    return hb_array_t (arrayZ + start_offset, count);
   }
-  hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const
+  hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
   { return sub_array (start_offset, &seg_count); }
 
+  hb_array_t truncate (unsigned length) const { return sub_array (0, length); }
+
   template <typename T,
 	    unsigned P = sizeof (Type),
 	    hb_enable_if (P == 1)>
@@ -234,7 +236,7 @@
 	hb_iter_t<hb_sorted_array_t<Type>, Type&>,
 	hb_array_t<Type>
 {
-  typedef hb_iter_t<hb_sorted_array_t<Type>, Type&> iter_base_t;
+  typedef hb_iter_t<hb_sorted_array_t, Type&> iter_base_t;
   HB_ITER_USING (iter_base_t);
   static constexpr bool is_random_access_iterator = true;
   static constexpr bool is_sorted_iterator = true;
@@ -247,7 +249,7 @@
   template <typename U,
 	    hb_enable_if (hb_is_cr_convertible(U, Type))>
   hb_sorted_array_t (const hb_array_t<U> &o) :
-    hb_iter_t<hb_sorted_array_t<Type>, Type&> (),
+    hb_iter_t<hb_sorted_array_t, Type&> (),
     hb_array_t<Type> (o) {}
   template <typename U,
 	    hb_enable_if (hb_is_cr_convertible(U, Type))>
@@ -258,11 +260,13 @@
   bool operator != (const hb_sorted_array_t& o) const
   { return this->arrayZ != o.arrayZ || this->length != o.length; }
 
-  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
+  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
+  { return hb_sorted_array_t (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); }
+  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
   { return sub_array (start_offset, &seg_count); }
 
+  hb_sorted_array_t truncate (unsigned length) const { return sub_array (0, length); }
+
   template <typename T>
   Type *bsearch (const T &x, Type *not_found = nullptr)
   {
@@ -277,8 +281,8 @@
   }
   template <typename T>
   bool bfind (const T &x, unsigned int *i = nullptr,
-		     hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
-		     unsigned int to_store = (unsigned int) -1) const
+	      hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
+	      unsigned int to_store = (unsigned int) -1) const
   {
     int min = 0, max = (int) this->length - 1;
     const Type *array = this->arrayZ;
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index 006de7d..2e72683 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -48,7 +48,6 @@
 #endif /* HAVE_SYS_MMAN_H */
 
 #include <stdio.h>
-#include <errno.h>
 #include <stdlib.h>
 
 
diff --git a/src/hb-buffer-serialize.cc b/src/hb-buffer-serialize.cc
index d1bcf4d..e64eb0e 100644
--- a/src/hb-buffer-serialize.cc
+++ b/src/hb-buffer-serialize.cc
@@ -379,43 +379,24 @@
   }
 }
 
-
-static hb_bool_t
-parse_uint (const char *pp, const char *end, uint32_t *pv)
+static bool
+parse_int (const char *pp, const char *end, int32_t *pv)
 {
-  char buf[32];
-  unsigned int len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp));
-  strncpy (buf, pp, len);
-  buf[len] = '\0';
-
-  char *p = buf;
-  char *pend = p;
-  uint32_t v;
-
-  errno = 0;
-  v = strtol (p, &pend, 10);
-  if (errno || p == pend || pend - p != end - pp)
+  int v;
+  const char *p = pp;
+  if (unlikely (!hb_parse_int (&p, end, &v, true/* whole buffer */)))
     return false;
 
   *pv = v;
   return true;
 }
 
-static hb_bool_t
-parse_int (const char *pp, const char *end, int32_t *pv)
+static bool
+parse_uint (const char *pp, const char *end, uint32_t *pv)
 {
-  char buf[32];
-  unsigned int len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp));
-  strncpy (buf, pp, len);
-  buf[len] = '\0';
-
-  char *p = buf;
-  char *pend = p;
-  int32_t v;
-
-  errno = 0;
-  v = strtol (p, &pend, 10);
-  if (errno || p == pend || pend - p != end - pp)
+  unsigned int v;
+  const char *p = pp;
+  if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */)))
     return false;
 
   *pv = v;
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index bd013a3..bb7f3c7 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -324,7 +324,8 @@
   out_len = 0;
   out_info = info;
 
-  memset (pos, 0, sizeof (pos[0]) * len);
+  if (likely (len))
+    memset (pos, 0, sizeof (pos[0]) * len);
 }
 
 void
diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh
index de0655d..b8ea5ad 100644
--- a/src/hb-buffer.hh
+++ b/src/hb-buffer.hh
@@ -408,8 +408,7 @@
       }
   }
 
-  void unsafe_to_break_all ()
-  { unsafe_to_break_impl (0, len); }
+  void unsafe_to_break_all () { unsafe_to_break_impl (0, len); }
   void safe_to_break_all ()
   {
     for (unsigned int i = 0; i < len; i++)
diff --git a/src/hb-cff-interp-dict-common.hh b/src/hb-cff-interp-dict-common.hh
index ea0db13..1f03d82 100644
--- a/src/hb-cff-interp-dict-common.hh
+++ b/src/hb-cff-interp-dict-common.hh
@@ -94,130 +94,52 @@
     }
   }
 
+  /* Turns CFF's BCD format into strtod understandable string */
   static double parse_bcd (byte_str_ref_t& str_ref)
   {
-    bool    neg = false;
-    double  int_part = 0;
-    uint64_t frac_part = 0;
-    uint32_t  frac_count = 0;
-    bool    exp_neg = false;
-    uint32_t  exp_part = 0;
-    bool    exp_overflow = false;
-    enum Part { INT_PART=0, FRAC_PART, EXP_PART } part = INT_PART;
-    enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END };
-    const uint64_t MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 1^52-1 */
-    const uint32_t MAX_EXP = 0x7FFu; /* 1^11-1 */
+    if (unlikely (str_ref.in_error ())) return .0;
 
-    double  value = 0.0;
+    enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END };
+
+    char buf[32];
     unsigned char byte = 0;
-    for (uint32_t i = 0;; i++)
+    for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count)
     {
-      char d;
-      if ((i & 1) == 0)
+      unsigned nibble;
+      if (!(i & 1))
       {
-	if (!str_ref.avail ())
-	{
-	  str_ref.set_error ();
-	  return 0.0;
-	}
+	if (unlikely (!str_ref.avail ())) break;
+
 	byte = str_ref[0];
 	str_ref.inc ();
-	d = byte >> 4;
+	nibble = byte >> 4;
       }
       else
-	d = byte & 0x0F;
+	nibble = byte & 0x0F;
 
-      switch (d)
+      if (unlikely (nibble == RESERVED)) break;
+      else if (nibble == END)
       {
-	case RESERVED:
-	  str_ref.set_error ();
-	  return value;
-
-	case END:
-	  value = (double) (neg ? -int_part : int_part);
-	  if (frac_count > 0)
-	  {
-	    double frac = (frac_part / pow (10.0, (double) frac_count));
-	    if (neg) frac = -frac;
-	    value += frac;
-	  }
-	  if (unlikely (exp_overflow))
-	  {
-	    if (value == 0.0)
-	      return value;
-	    if (exp_neg)
-	      return neg ? -DBL_MIN : DBL_MIN;
-	    else
-	      return neg ? -DBL_MAX : DBL_MAX;
-	  }
-	  if (exp_part != 0)
-	  {
-	    if (exp_neg)
-	      value /= pow (10.0, (double) exp_part);
-	    else
-	      value *= pow (10.0, (double) exp_part);
-	  }
-	  return value;
-
-	case NEG:
-	  if (i != 0)
-	  {
-	    str_ref.set_error ();
-	    return 0.0;
-	  }
-	  neg = true;
+	const char *p = buf;
+	double pv;
+	if (unlikely (!hb_parse_double (&p, p + count, &pv, true/* whole buffer */)))
 	  break;
-
-	case DECIMAL:
-	  if (part != INT_PART)
-	  {
-	    str_ref.set_error ();
-	    return value;
-	  }
-	  part = FRAC_PART;
-	  break;
-
-	case EXP_NEG:
-	  exp_neg = true;
-	  HB_FALLTHROUGH;
-
-	case EXP_POS:
-	  if (part == EXP_PART)
-	  {
-	    str_ref.set_error ();
-	    return value;
-	  }
-	  part = EXP_PART;
-	  break;
-
-	default:
-	  switch (part) {
-	    default:
-	    case INT_PART:
-	      int_part = (int_part * 10) + d;
-	      break;
-
-	    case FRAC_PART:
-	      if (likely (frac_part <= MAX_FRACT / 10))
-	      {
-		frac_part = (frac_part * 10) + (unsigned)d;
-		frac_count++;
-	      }
-	      break;
-
-	    case EXP_PART:
-	      if (likely (exp_part * 10 + d <= MAX_EXP))
-	      {
-	      	exp_part = (exp_part * 10) + d;
-	      }
-	      else
-	      	exp_overflow = true;
-	      break;
-	  }
+	return pv;
+      }
+      else
+      {
+	buf[count] = "0123456789.EE?-?"[nibble];
+	if (nibble == EXP_NEG)
+	{
+	  ++count;
+	  if (unlikely (count == ARRAY_LENGTH (buf))) break;
+	  buf[count] = '-';
+	}
       }
     }
 
-    return value;
+    str_ref.set_error ();
+    return .0;
   }
 
   static bool is_hint_op (op_code_t op)
diff --git a/src/hb-common.cc b/src/hb-common.cc
index d96f397..0ae0c05 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -27,13 +27,9 @@
  */
 
 #include "hb.hh"
-
 #include "hb-machinery.hh"
 
 #include <locale.h>
-#ifdef HAVE_XLOCALE_H
-#include <xlocale.h>
-#endif
 
 #ifdef HB_NO_SETLOCALE
 #define setlocale(Category, Locale) "C"
@@ -385,7 +381,8 @@
 const char *
 hb_language_to_string (hb_language_t language)
 {
-  /* This is actually nullptr-safe! */
+  if (unlikely (!language)) return nullptr;
+
   return language->s;
 }
 
@@ -722,131 +719,24 @@
 static bool
 parse_uint (const char **pp, const char *end, unsigned int *pv)
 {
-  char buf[32];
-  unsigned int len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
-  strncpy (buf, *pp, len);
-  buf[len] = '\0';
-
-  char *p = buf;
-  char *pend = p;
-  unsigned int v;
-
-  /* Intentionally use strtol instead of strtoul, such that
-   * -1 turns into "big number"... */
-  errno = 0;
-  v = strtol (p, &pend, 10);
-  if (errno || p == pend)
-    return false;
+  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
+   * such that -1 turns into "big number"... */
+  int v;
+  if (unlikely (!hb_parse_int (pp, end, &v))) return false;
 
   *pv = v;
-  *pp += pend - p;
   return true;
 }
 
 static bool
 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
 {
-  char buf[32];
-  unsigned int len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
-  strncpy (buf, *pp, len);
-  buf[len] = '\0';
-
-  char *p = buf;
-  char *pend = p;
-  unsigned int v;
-
-  /* Intentionally use strtol instead of strtoul, such that
-   * -1 turns into "big number"... */
-  errno = 0;
-  v = strtol (p, &pend, 10);
-  if (errno || p == pend)
-    return false;
+  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
+   * such that -1 turns into "big number"... */
+  int v;
+  if (unlikely (!hb_parse_int (pp, end, &v))) return false;
 
   *pv = v;
-  *pp += pend - p;
-  return true;
-}
-
-#if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
-#define USE_XLOCALE 1
-#define HB_LOCALE_T locale_t
-#define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
-#define HB_FREE_LOCALE(loc) freelocale (loc)
-#elif defined(_MSC_VER)
-#define USE_XLOCALE 1
-#define HB_LOCALE_T _locale_t
-#define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
-#define HB_FREE_LOCALE(loc) _free_locale (loc)
-#define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
-#endif
-
-#ifdef USE_XLOCALE
-
-#if HB_USE_ATEXIT
-static void free_static_C_locale ();
-#endif
-
-static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<HB_LOCALE_T>,
-							  hb_C_locale_lazy_loader_t>
-{
-  static HB_LOCALE_T create ()
-  {
-    HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C");
-
-#if HB_USE_ATEXIT
-    atexit (free_static_C_locale);
-#endif
-
-    return C_locale;
-  }
-  static void destroy (HB_LOCALE_T p)
-  {
-    HB_FREE_LOCALE (p);
-  }
-  static HB_LOCALE_T get_null ()
-  {
-    return nullptr;
-  }
-} static_C_locale;
-
-#if HB_USE_ATEXIT
-static
-void free_static_C_locale ()
-{
-  static_C_locale.free_instance ();
-}
-#endif
-
-static HB_LOCALE_T
-get_C_locale ()
-{
-  return static_C_locale.get_unconst ();
-}
-#endif /* USE_XLOCALE */
-
-static bool
-parse_float (const char **pp, const char *end, float *pv)
-{
-  char buf[32];
-  unsigned int len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
-  strncpy (buf, *pp, len);
-  buf[len] = '\0';
-
-  char *p = buf;
-  char *pend = p;
-  float v;
-
-  errno = 0;
-#ifdef USE_XLOCALE
-  v = strtod_l (p, &pend, get_C_locale ());
-#else
-  v = strtod (p, &pend);
-#endif
-  if (errno || p == pend)
-    return false;
-
-  *pv = v;
-  *pp += pend - p;
   return true;
 }
 
@@ -1099,7 +989,11 @@
 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
 {
   parse_char (pp, end, '='); /* Optional. */
-  return parse_float (pp, end, &variation->value);
+  double v;
+  if (unlikely (!hb_parse_double (pp, end, &v))) return false;
+
+  variation->value = v;
+  return true;
 }
 
 static bool
diff --git a/src/hb-common.h b/src/hb-common.h
index edd9ffb..9f1764b 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -63,6 +63,8 @@
 typedef unsigned __int32 uint32_t;
 typedef __int64 int64_t;
 typedef unsigned __int64 uint64_t;
+#elif defined (__KERNEL__)
+#  include <linux/types.h>
 #else
 #  include <stdint.h>
 #endif
diff --git a/src/hb-config.hh b/src/hb-config.hh
index b6db206..14c5395 100644
--- a/src/hb-config.hh
+++ b/src/hb-config.hh
@@ -58,6 +58,7 @@
 #define HB_NO_BITMAP
 #define HB_NO_CFF
 #define HB_NO_COLOR
+#define HB_NO_ERRNO
 #define HB_NO_FACE_COLLECT_UNICODES
 #define HB_NO_GETENV
 #define HB_NO_HINTING
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc
index 8c9b49c..8885cfe 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -302,7 +302,7 @@
   if (unlikely (!face_data)) return nullptr;
   CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext;
 
-  CGFloat font_size = font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem;
+  CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem);
   CTFontRef ct_font = create_ct_font (cg_font, font_size);
 
   if (unlikely (!ct_font))
@@ -327,7 +327,7 @@
   const hb_coretext_font_data_t *data = font->data.coretext;
   if (unlikely (!data)) return nullptr;
 
-  if (fabs (CTFontGetSize ((CTFontRef) data) - font->ptem) > .5)
+  if (fabs (CTFontGetSize ((CTFontRef) data) - (CGFloat) font->ptem) > .5)
   {
     /* XXX-MT-bug
      * Note that evaluating condition above can be dangerous if another thread
diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc
index a079563..efb2029 100644
--- a/src/hb-directwrite.cc
+++ b/src/hb-directwrite.cc
@@ -625,7 +625,7 @@
   HB_STMT_START { \
     DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
     return false; \
-  } HB_STMT_END;
+  } HB_STMT_END
 
   if (FAILED (hr))
     FAIL ("Analyzer failed to generate results.");
diff --git a/src/hb-face.cc b/src/hb-face.cc
index 1098adb..0c9949f 100644
--- a/src/hb-face.cc
+++ b/src/hb-face.cc
@@ -367,6 +367,9 @@
 hb_face_reference_table (const hb_face_t *face,
 			 hb_tag_t tag)
 {
+  if (unlikely (tag == HB_TAG_NONE))
+    return hb_blob_get_empty ();
+
   return face->reference_table (tag);
 }
 
diff --git a/src/hb-font.h b/src/hb-font.h
index 9b65187..677be2f 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -649,8 +649,8 @@
 /* Be *very* careful with this function! */
 HB_EXTERN void
 hb_font_set_funcs_data (hb_font_t         *font,
-		        void              *font_data,
-		        hb_destroy_func_t  destroy);
+			void              *font_data,
+			hb_destroy_func_t  destroy);
 
 
 HB_EXTERN void
diff --git a/src/hb-font.hh b/src/hb-font.hh
index 4adf6ae..b1e8e64 100644
--- a/src/hb-font.hh
+++ b/src/hb-font.hh
@@ -216,7 +216,7 @@
   }
 
   hb_bool_t get_nominal_glyph (hb_codepoint_t unicode,
-				      hb_codepoint_t *glyph)
+			       hb_codepoint_t *glyph)
   {
     *glyph = 0;
     return klass->get.f.nominal_glyph (this, user_data,
@@ -286,7 +286,7 @@
   }
 
   hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph,
-				       hb_position_t *x, hb_position_t *y)
+			        hb_position_t *x, hb_position_t *y)
   {
     *x = *y = 0;
     return klass->get.f.glyph_h_origin (this, user_data,
@@ -328,7 +328,7 @@
   }
 
   hb_bool_t get_glyph_extents (hb_codepoint_t glyph,
-				      hb_glyph_extents_t *extents)
+			       hb_glyph_extents_t *extents)
   {
     memset (extents, 0, sizeof (*extents));
     return klass->get.f.glyph_extents (this, user_data,
@@ -499,7 +499,7 @@
   }
 
   void subtract_glyph_h_origin (hb_codepoint_t glyph,
-			        hb_position_t *x, hb_position_t *y)
+				hb_position_t *x, hb_position_t *y)
   {
     hb_position_t origin_x, origin_y;
 
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index ec4748c..e526bf4 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -840,8 +840,8 @@
     return;
   }
 
-  if (FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE))
-    FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL);
+  if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL))
+    FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE);
 
   FT_Set_Char_Size (ft_face,
 		    abs (font->x_scale), abs (font->y_scale),
@@ -866,7 +866,7 @@
     if (ft_coords)
     {
       for (unsigned int i = 0; i < num_coords; i++)
-	ft_coords[i] = coords[i] << 2;
+	ft_coords[i] = coords[i] * 4;
       FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords);
       free (ft_coords);
     }
diff --git a/src/hb-gdi.cc b/src/hb-gdi.cc
index d55085c..f6306ef 100644
--- a/src/hb-gdi.cc
+++ b/src/hb-gdi.cc
@@ -58,7 +58,7 @@
 
 /**
  * hb_gdi_face_create:
- * @hdc: a HFONT object.
+ * @hfont: a HFONT object.
  *
  * Return value: #hb_face_t object corresponding to the given input
  *
diff --git a/src/hb-icu.cc b/src/hb-icu.cc
index e98908d..985ff02 100644
--- a/src/hb-icu.cc
+++ b/src/hb-icu.cc
@@ -41,6 +41,12 @@
 #include <unicode/utf16.h>
 #include <unicode/uversion.h>
 
+/* ICU extra semicolon, fixed since 65, https://github.com/unicode-org/icu/commit/480bec3 */
+#if U_ICU_VERSION_MAJOR_NUM < 65 && (defined(__GNUC__) || defined(__clang__))
+#define HB_ICU_EXTRA_SEMI_IGNORED
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wextra-semi-stmt"
+#endif
 
 /**
  * SECTION:hb-icu
@@ -51,10 +57,6 @@
  * Functions for using HarfBuzz with the ICU library to provide Unicode data.
  **/
 
-/* ICU doesn't do-while(0) around their statements.  Ugh!
- * https://unicode-org.atlassian.net/browse/CLDR-13027 */
-#define HB_ICU_STMT(S) do { S } while (0)
-
 hb_script_t
 hb_icu_script_to_script (UScriptCode script)
 {
@@ -188,9 +190,9 @@
 
   len = 0;
   err = false;
-  HB_ICU_STMT (U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), a, err));
+  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), a, err);
   if (err) return false;
-  HB_ICU_STMT (U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), b, err));
+  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), b, err);
   if (err) return false;
 
   icu_err = U_ZERO_ERROR;
@@ -198,7 +200,7 @@
   if (U_FAILURE (icu_err))
     return false;
   if (u_countChar32 (normalized, len) == 1) {
-    HB_ICU_STMT (U16_GET_UNSAFE (normalized, 0, *ab));
+    U16_GET_UNSAFE (normalized, 0, *ab);
     ret = true;
   } else {
     ret = false;
@@ -226,13 +228,13 @@
 
     len = u_countChar32 (decomposed, len);
     if (len == 1) {
-      HB_ICU_STMT (U16_GET_UNSAFE (decomposed, 0, *a));
+      U16_GET_UNSAFE (decomposed, 0, *a);
       *b = 0;
       return *a != ab;
     } else if (len == 2) {
-      len =0;
-      HB_ICU_STMT (U16_NEXT_UNSAFE (decomposed, len, *a));
-      HB_ICU_STMT (U16_NEXT_UNSAFE (decomposed, len, *b));
+      len = 0;
+      U16_NEXT_UNSAFE (decomposed, len, *a);
+      U16_NEXT_UNSAFE (decomposed, len, *b);
     }
     return true;
   }
@@ -252,7 +254,7 @@
 
   len = 0;
   err = false;
-  HB_ICU_STMT (U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), ab, err));
+  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), ab, err);
   if (err) return false;
 
   icu_err = U_ZERO_ERROR;
@@ -263,13 +265,13 @@
   len = u_countChar32 (normalized, len);
 
   if (len == 1) {
-    HB_ICU_STMT (U16_GET_UNSAFE (normalized, 0, *a));
+    U16_GET_UNSAFE (normalized, 0, *a);
     *b = 0;
     ret = *a != ab;
   } else if (len == 2) {
-    len =0;
-    HB_ICU_STMT (U16_NEXT_UNSAFE (normalized, len, *a));
-    HB_ICU_STMT (U16_NEXT_UNSAFE (normalized, len, *b));
+    len = 0;
+    U16_NEXT_UNSAFE (normalized, len, *a);
+    U16_NEXT_UNSAFE (normalized, len, *b);
 
     /* Here's the ugly part: if ab decomposes to a single character and
      * that character decomposes again, we have to detect that and undo
@@ -280,7 +282,7 @@
     if (U_FAILURE (icu_err))
       return false;
     hb_codepoint_t c;
-    HB_ICU_STMT (U16_GET_UNSAFE (recomposed, 0, c));
+    U16_GET_UNSAFE (recomposed, 0, c);
     if (c != *a && c != ab) {
       *a = c;
       *b = 0;
@@ -289,7 +291,7 @@
   } else {
     /* If decomposed to more than two characters, take the last one,
      * and recompose the rest to get the first component. */
-    HB_ICU_STMT (U16_PREV_UNSAFE (normalized, len, *b)); /* Changes len in-place. */
+    U16_PREV_UNSAFE (normalized, len, *b); /* Changes len in-place. */
     UChar recomposed[18 * 2];
     icu_err = U_ZERO_ERROR;
     len = unorm2_normalize (unorm2_getNFCInstance (&icu_err), normalized, len, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
@@ -298,7 +300,7 @@
     /* We expect that recomposed has exactly one character now. */
     if (unlikely (u_countChar32 (recomposed, len) != 1))
       return false;
-    HB_ICU_STMT (U16_GET_UNSAFE (recomposed, 0, *a));
+    U16_GET_UNSAFE (recomposed, 0, *a);
     ret = true;
   }
 
@@ -354,5 +356,8 @@
   return static_icu_funcs.get_unconst ();
 }
 
+#ifdef HB_ICU_EXTRA_SEMI_IGNORED
+#pragma GCC diagnostic pop
+#endif
 
 #endif
diff --git a/src/hb-iter.hh b/src/hb-iter.hh
index 238663e..981c5c2 100644
--- a/src/hb-iter.hh
+++ b/src/hb-iter.hh
@@ -64,7 +64,7 @@
 struct hb_iter_t
 {
   typedef Item item_t;
-  static constexpr unsigned item_size = hb_static_size (Item);
+  constexpr unsigned get_item_size () const { return hb_static_size (Item); }
   static constexpr bool is_iterator = true;
   static constexpr bool is_random_access_iterator = false;
   static constexpr bool is_sorted_iterator = false;
@@ -130,7 +130,7 @@
   using item_t = typename Name::item_t; \
   using Name::begin; \
   using Name::end; \
-  using Name::item_size; \
+  using Name::get_item_size; \
   using Name::is_iterator; \
   using Name::iter; \
   using Name::operator bool; \
@@ -156,6 +156,7 @@
 
 
 template <typename> struct hb_array_t;
+template <typename> struct hb_sorted_array_t;
 
 struct
 {
@@ -175,6 +176,14 @@
 
 }
 HB_FUNCOBJ (hb_iter);
+struct
+{
+  template <typename T> unsigned
+  operator () (T&& c) const
+  { return c.len (); }
+
+}
+HB_FUNCOBJ (hb_len);
 
 /* Mixin to fill in what the subclass doesn't provide. */
 template <typename iter_t, typename item_t = typename iter_t::__item_t__>
@@ -563,7 +572,7 @@
   B b;
 };
 struct
-{
+{ HB_PARTIALIZE(2);
   template <typename A, typename B,
 	    hb_requires (hb_is_iterable (A) && hb_is_iterable (B))>
   hb_zip_iter_t<hb_iter_type<A>, hb_iter_type<B>>
@@ -602,18 +611,18 @@
 }
 HB_FUNCOBJ (hb_apply);
 
-/* hb_iota()/hb_range() */
+/* hb_range()/hb_iota()/hb_repeat() */
 
 template <typename T, typename S>
-struct hb_counter_iter_t :
-  hb_iter_t<hb_counter_iter_t<T, S>, T>
+struct hb_range_iter_t :
+  hb_iter_t<hb_range_iter_t<T, S>, T>
 {
-  hb_counter_iter_t (T start, T end_, S step) : v (start), end_ (end_for (start, end_, step)), step (step) {}
+  hb_range_iter_t (T start, T end_, S step) : v (start), end_ (end_for (start, end_, step)), step (step) {}
 
   typedef T __item_t__;
   static constexpr bool is_random_access_iterator = true;
   static constexpr bool is_sorted_iterator = true;
-  __item_t__ __item__ () const { return +v; }
+  __item_t__ __item__ () const { return hb_ridentity (v); }
   __item_t__ __item_at__ (unsigned j) const { return v + j * step; }
   bool __more__ () const { return v != end_; }
   unsigned __len__ () const { return !step ? UINT_MAX : (end_ - v) / step; }
@@ -621,8 +630,8 @@
   void __forward__ (unsigned n) { v += n * step; }
   void __prev__ () { v -= step; }
   void __rewind__ (unsigned n) { v -= n * step; }
-  hb_counter_iter_t __end__ () const { return hb_counter_iter_t (end_, end_, step); }
-  bool operator != (const hb_counter_iter_t& o) const
+  hb_range_iter_t __end__ () const { return hb_range_iter_t (end_, end_, step); }
+  bool operator != (const hb_range_iter_t& o) const
   { return v != o.v; }
 
   private:
@@ -644,24 +653,91 @@
 };
 struct
 {
-  template <typename T = unsigned, typename S = unsigned> hb_counter_iter_t<T, S>
-  operator () (T start = 0u, S&& step = 1u) const
-  { return hb_counter_iter_t<T, S> (start, step >= 0 ? hb_int_max (T) : hb_int_min (T), step); }
-}
-HB_FUNCOBJ (hb_iota);
-struct
-{
-  template <typename T = unsigned> hb_counter_iter_t<T, unsigned>
+  template <typename T = unsigned> hb_range_iter_t<T, unsigned>
   operator () (T end = (unsigned) -1) const
-  { return hb_counter_iter_t<T, unsigned> (0, end, 1u); }
+  { return hb_range_iter_t<T, unsigned> (0, end, 1u); }
 
-  template <typename T, typename S = unsigned> hb_counter_iter_t<T, S>
-  operator () (T start, T end, S&& step = 1u) const
-  { return hb_counter_iter_t<T, S> (start, end, step); }
+  template <typename T, typename S = unsigned> hb_range_iter_t<T, S>
+  operator () (T start, T end, S step = 1u) const
+  { return hb_range_iter_t<T, S> (start, end, step); }
 }
 HB_FUNCOBJ (hb_range);
 
-/* hb_enumerate */
+template <typename T, typename S>
+struct hb_iota_iter_t :
+  hb_iter_with_fallback_t<hb_iota_iter_t<T, S>, T>
+{
+  hb_iota_iter_t (T start, S step) : v (start), step (step) {}
+
+  private:
+
+  template <typename S2 = S>
+  auto
+  inc (hb_type_identity<S2> s, hb_priority<1>)
+    -> hb_void_t<decltype (hb_invoke (hb_forward<S2> (s), hb_declval<T&> ()))>
+  { v = hb_invoke (hb_forward<S2> (s), v); }
+
+  void
+  inc (S s, hb_priority<0>)
+  { v += s; }
+
+  public:
+
+  typedef T __item_t__;
+  static constexpr bool is_random_access_iterator = true;
+  static constexpr bool is_sorted_iterator = true;
+  __item_t__ __item__ () const { return hb_ridentity (v); }
+  bool __more__ () const { return true; }
+  unsigned __len__ () const { return UINT_MAX; }
+  void __next__ () { inc (step, hb_prioritize); }
+  void __prev__ () { v -= step; }
+  hb_iota_iter_t __end__ () const { return *this; }
+  bool operator != (const hb_iota_iter_t& o) const { return true; }
+
+  private:
+  T v;
+  S step;
+};
+struct
+{
+  template <typename T = unsigned, typename S = unsigned> hb_iota_iter_t<T, S>
+  operator () (T start = 0u, S step = 1u) const
+  { return hb_iota_iter_t<T, S> (start, step); }
+}
+HB_FUNCOBJ (hb_iota);
+
+template <typename T>
+struct hb_repeat_iter_t :
+  hb_iter_t<hb_repeat_iter_t<T>, T>
+{
+  hb_repeat_iter_t (T value) : v (value) {}
+
+  typedef T __item_t__;
+  static constexpr bool is_random_access_iterator = true;
+  static constexpr bool is_sorted_iterator = true;
+  __item_t__ __item__ () const { return v; }
+  __item_t__ __item_at__ (unsigned j) const { return v; }
+  bool __more__ () const { return true; }
+  unsigned __len__ () const { return UINT_MAX; }
+  void __next__ () {}
+  void __forward__ (unsigned) {}
+  void __prev__ () {}
+  void __rewind__ (unsigned) {}
+  hb_repeat_iter_t __end__ () const { return *this; }
+  bool operator != (const hb_repeat_iter_t& o) const { return true; }
+
+  private:
+  T v;
+};
+struct
+{
+  template <typename T> hb_repeat_iter_t<T>
+  operator () (T value) const
+  { return hb_repeat_iter_t<T> (value); }
+}
+HB_FUNCOBJ (hb_repeat);
+
+/* hb_enumerate()/hb_take() */
 
 struct
 {
@@ -673,6 +749,37 @@
 }
 HB_FUNCOBJ (hb_enumerate);
 
+struct
+{ HB_PARTIALIZE(2);
+  template <typename Iterable,
+	    hb_requires (hb_is_iterable (Iterable))>
+  auto operator () (Iterable&& it, unsigned count) const HB_AUTO_RETURN
+  ( hb_zip (hb_range (count), it) | hb_map (hb_second) )
+
+  /* Specialization arrays. */
+
+  template <typename Type> inline hb_array_t<Type>
+  operator () (hb_array_t<Type> array, unsigned count) const
+  { return array.sub_array (0, count); }
+
+  template <typename Type> inline hb_sorted_array_t<Type>
+  operator () (hb_sorted_array_t<Type> array, unsigned count) const
+  { return array.sub_array (0, count); }
+}
+HB_FUNCOBJ (hb_take);
+
+struct
+{ HB_PARTIALIZE(2);
+  template <typename Iter,
+	    hb_requires (hb_is_iterator (Iter))>
+  auto operator () (Iter it, unsigned count) const HB_AUTO_RETURN
+  (
+    + hb_iota (it, hb_add (count))
+    | hb_map (hb_take (count))
+    | hb_take ((hb_len (it) + count - 1) / count)
+  )
+}
+HB_FUNCOBJ (hb_chop);
 
 /* hb_sink() */
 
diff --git a/src/hb-machinery.hh b/src/hb-machinery.hh
index 383c469..15535d7 100644
--- a/src/hb-machinery.hh
+++ b/src/hb-machinery.hh
@@ -135,7 +135,7 @@
 
 #define DEFINE_SIZE_ARRAY(size, array) \
   DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + VAR * sizeof ((array)[0])) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + HB_VAR_ARRAY * sizeof ((array)[0])) \
   static constexpr unsigned null_size = (size); \
   static constexpr unsigned min_size = (size)
 
diff --git a/src/hb-number-parser.hh b/src/hb-number-parser.hh
new file mode 100644
index 0000000..c78c850
--- /dev/null
+++ b/src/hb-number-parser.hh
@@ -0,0 +1,240 @@
+
+#line 1 "hb-number-parser.rl"
+/*
+ * Copyright © 2019  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_NUMBER_PARSER_HH
+#define HB_NUMBER_PARSER_HH
+
+#include "hb.hh"
+
+#include <float.h>
+
+
+#line 37 "hb-number-parser.hh"
+static const unsigned char _double_parser_trans_keys[] = {
+	0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, 
+	46u, 101u, 0
+};
+
+static const char _double_parser_key_spans[] = {
+	0, 15, 12, 10, 15, 10, 54, 10, 
+	56
+};
+
+static const unsigned char _double_parser_index_offsets[] = {
+	0, 0, 16, 29, 40, 56, 67, 122, 
+	133
+};
+
+static const char _double_parser_indicies[] = {
+	0, 1, 2, 3, 1, 4, 4, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	1, 3, 1, 4, 4, 4, 4, 4, 
+	4, 4, 4, 4, 4, 1, 5, 5, 
+	5, 5, 5, 5, 5, 5, 5, 5, 
+	1, 6, 1, 7, 1, 1, 8, 8, 
+	8, 8, 8, 8, 8, 8, 8, 8, 
+	1, 8, 8, 8, 8, 8, 8, 8, 
+	8, 8, 8, 1, 5, 5, 5, 5, 
+	5, 5, 5, 5, 5, 5, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 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, 9, 1, 8, 8, 8, 8, 8, 
+	8, 8, 8, 8, 8, 1, 3, 1, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	4, 4, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 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, 9, 1, 0
+};
+
+static const char _double_parser_trans_targs[] = {
+	2, 0, 2, 3, 8, 6, 5, 5, 
+	7, 4
+};
+
+static const char _double_parser_trans_actions[] = {
+	0, 0, 1, 0, 2, 3, 0, 4, 
+	5, 0
+};
+
+static const int double_parser_start = 1;
+static const int double_parser_first_final = 6;
+static const int double_parser_error = 0;
+
+static const int double_parser_en_main = 1;
+
+
+#line 70 "hb-number-parser.rl"
+
+
+/* Works only for n < 512 */
+static inline double
+_pow10 (unsigned int exponent)
+{
+  static const double _powers_of_10[] =
+  {
+    1.0e+256,
+    1.0e+128,
+    1.0e+64,
+    1.0e+32,
+    1.0e+16,
+    1.0e+8,
+    10000.,
+    100.,
+    10.
+  };
+  unsigned int mask = 1 << (ARRAY_LENGTH (_powers_of_10) - 1);
+  double result = 1;
+  for (const double *power = _powers_of_10; mask; ++power, mask >>= 1)
+    if (exponent & mask) result *= *power;
+  return result;
+}
+
+static inline double
+strtod_rl (const char *buf, char **end_ptr)
+{
+  const char *p, *pe;
+  double value = 0;
+  double frac = 0;
+  double frac_count = 0;
+  unsigned int exp = 0;
+  bool neg = false, exp_neg = false, exp_overflow = false;
+  const unsigned long long MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 1^52-1 */
+  const unsigned int MAX_EXP = 0x7FFu; /* 1^11-1 */
+  p = buf;
+  pe = p + strlen (p);
+
+  while (p < pe && ISSPACE (*p))
+    p++;
+
+  int cs;
+  
+#line 142 "hb-number-parser.hh"
+	{
+	cs = double_parser_start;
+	}
+
+#line 147 "hb-number-parser.hh"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+	if ( cs == 0 )
+		goto _out;
+_resume:
+	_keys = _double_parser_trans_keys + (cs<<1);
+	_inds = _double_parser_indicies + _double_parser_index_offsets[cs];
+
+	_slen = _double_parser_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
+		(*p) <= _keys[1] ?
+		(*p) - _keys[0] : _slen ];
+
+	cs = _double_parser_trans_targs[_trans];
+
+	if ( _double_parser_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _double_parser_trans_actions[_trans] ) {
+	case 1:
+#line 39 "hb-number-parser.rl"
+	{ neg = true; }
+	break;
+	case 4:
+#line 40 "hb-number-parser.rl"
+	{ exp_neg = true; }
+	break;
+	case 2:
+#line 42 "hb-number-parser.rl"
+	{
+	value = value * 10. + ((*p) - '0');
+}
+	break;
+	case 3:
+#line 45 "hb-number-parser.rl"
+	{
+	if (likely (frac <= MAX_FRACT / 10))
+	{
+	  frac = frac * 10. + ((*p) - '0');
+	  ++frac_count;
+	}
+}
+	break;
+	case 5:
+#line 52 "hb-number-parser.rl"
+	{
+	if (likely (exp * 10 + ((*p) - '0') <= MAX_EXP))
+	  exp = exp * 10 + ((*p) - '0');
+	else
+	  exp_overflow = true;
+}
+	break;
+#line 205 "hb-number-parser.hh"
+	}
+
+_again:
+	if ( cs == 0 )
+		goto _out;
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	_out: {}
+	}
+
+#line 116 "hb-number-parser.rl"
+
+
+  *end_ptr = (char *) p;
+
+  if (frac_count) value += frac / _pow10 (frac_count);
+  if (neg) value *= -1.;
+
+  if (unlikely (exp_overflow))
+  {
+    if (value == 0) return value;
+    if (exp_neg)    return neg ? -DBL_MIN : DBL_MIN;
+    else            return neg ? -DBL_MAX : DBL_MAX;
+  }
+
+  if (exp)
+  {
+    if (exp_neg) value /= _pow10 (exp);
+    else         value *= _pow10 (exp);
+  }
+
+  return value;
+}
+
+#endif /* HB_NUMBER_PARSER_HH */
diff --git a/src/hb-number-parser.rl b/src/hb-number-parser.rl
new file mode 100644
index 0000000..8445fa2
--- /dev/null
+++ b/src/hb-number-parser.rl
@@ -0,0 +1,139 @@
+/*
+ * Copyright © 2019  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_NUMBER_PARSER_HH
+#define HB_NUMBER_PARSER_HH
+
+#include "hb.hh"
+
+#include <float.h>
+
+%%{
+
+machine double_parser;
+alphtype unsigned char;
+write data;
+
+action see_neg { neg = true; }
+action see_exp_neg { exp_neg = true; }
+
+action add_int  {
+	value = value * 10. + (fc - '0');
+}
+action add_frac {
+	if (likely (frac <= MAX_FRACT / 10))
+	{
+	  frac = frac * 10. + (fc - '0');
+	  ++frac_count;
+	}
+}
+action add_exp  {
+	if (likely (exp * 10 + (fc - '0') <= MAX_EXP))
+	  exp = exp * 10 + (fc - '0');
+	else
+	  exp_overflow = true;
+}
+
+num = [0-9]+;
+
+main := (
+	(
+		(('+'|'-'@see_neg)? num @add_int) ('.' num @add_frac)?
+		|
+		(('+'|'-'@see_neg)? '.' num @add_frac)
+	)
+	(('e'|'E') (('+'|'-'@see_exp_neg)? num @add_exp))?
+);
+
+}%%
+
+/* Works only for n < 512 */
+static inline double
+_pow10 (unsigned int exponent)
+{
+  static const double _powers_of_10[] =
+  {
+    1.0e+256,
+    1.0e+128,
+    1.0e+64,
+    1.0e+32,
+    1.0e+16,
+    1.0e+8,
+    10000.,
+    100.,
+    10.
+  };
+  unsigned int mask = 1 << (ARRAY_LENGTH (_powers_of_10) - 1);
+  double result = 1;
+  for (const double *power = _powers_of_10; mask; ++power, mask >>= 1)
+    if (exponent & mask) result *= *power;
+  return result;
+}
+
+static inline double
+strtod_rl (const char *buf, char **end_ptr)
+{
+  const char *p, *pe;
+  double value = 0;
+  double frac = 0;
+  double frac_count = 0;
+  unsigned int exp = 0;
+  bool neg = false, exp_neg = false, exp_overflow = false;
+  const unsigned long long MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 1^52-1 */
+  const unsigned int MAX_EXP = 0x7FFu; /* 1^11-1 */
+  p = buf;
+  pe = p + strlen (p);
+
+  while (p < pe && ISSPACE (*p))
+    p++;
+
+  int cs;
+  %%{
+    write init;
+    write exec;
+  }%%
+
+  *end_ptr = (char *) p;
+
+  if (frac_count) value += frac / _pow10 (frac_count);
+  if (neg) value *= -1.;
+
+  if (unlikely (exp_overflow))
+  {
+    if (value == 0) return value;
+    if (exp_neg)    return neg ? -DBL_MIN : DBL_MIN;
+    else            return neg ? -DBL_MAX : DBL_MAX;
+  }
+
+  if (exp)
+  {
+    if (exp_neg) value /= _pow10 (exp);
+    else         value *= _pow10 (exp);
+  }
+
+  return value;
+}
+
+#endif /* HB_NUMBER_PARSER_HH */
diff --git a/src/hb-number.cc b/src/hb-number.cc
new file mode 100644
index 0000000..4f84d4a
--- /dev/null
+++ b/src/hb-number.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ */
+
+#include "hb.hh"
+#include "hb-machinery.hh"
+#include "hb-number-parser.hh"
+
+#include <locale.h>
+#ifdef HAVE_XLOCALE_H
+#include <xlocale.h>
+#endif
+
+template<typename T, typename Func>
+static bool
+_parse_number (const char **pp, const char *end, T *pv,
+	       bool whole_buffer, Func f)
+{
+  char buf[32];
+  unsigned int len = hb_min (ARRAY_LENGTH (buf) - 1,
+			     (unsigned int) (end - *pp));
+  strncpy (buf, *pp, len);
+  buf[len] = '\0';
+
+  char *p = buf;
+  char *pend = p;
+
+  errno = 0;
+  *pv = f (p, &pend);
+  if (unlikely (errno || p == pend ||
+		/* Check if consumed whole buffer if is requested */
+		(whole_buffer && pend - p != end - *pp))) return false;
+
+  *pp += pend - p;
+  return true;
+}
+
+bool
+hb_parse_int (const char **pp, const char *end, int *pv, bool whole_buffer)
+{
+  return _parse_number<int> (pp, end, pv, whole_buffer,
+			     [] (const char *p, char **end)
+			     { return strtol (p, end, 10); });
+}
+
+bool
+hb_parse_uint (const char **pp, const char *end, unsigned int *pv,
+	       bool whole_buffer, int base)
+{
+  return _parse_number<unsigned int> (pp, end, pv, whole_buffer,
+				      [base] (const char *p, char **end)
+				      { return strtoul (p, end, base); });
+}
+
+
+#if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
+#define USE_XLOCALE 1
+#define HB_LOCALE_T locale_t
+#define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
+#define HB_FREE_LOCALE(loc) freelocale (loc)
+#elif defined(_MSC_VER)
+#define USE_XLOCALE 1
+#define HB_LOCALE_T _locale_t
+#define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
+#define HB_FREE_LOCALE(loc) _free_locale (loc)
+#define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
+#endif
+
+#ifdef USE_XLOCALE
+
+#if HB_USE_ATEXIT
+static void free_static_C_locale ();
+#endif
+
+static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<HB_LOCALE_T>,
+							  hb_C_locale_lazy_loader_t>
+{
+  static HB_LOCALE_T create ()
+  {
+    HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C");
+
+#if HB_USE_ATEXIT
+    atexit (free_static_C_locale);
+#endif
+
+    return C_locale;
+  }
+  static void destroy (HB_LOCALE_T p)
+  {
+    HB_FREE_LOCALE (p);
+  }
+  static HB_LOCALE_T get_null ()
+  {
+    return nullptr;
+  }
+} static_C_locale;
+
+#if HB_USE_ATEXIT
+static
+void free_static_C_locale ()
+{
+  static_C_locale.free_instance ();
+}
+#endif
+
+static HB_LOCALE_T
+get_C_locale ()
+{
+  return static_C_locale.get_unconst ();
+}
+#endif /* USE_XLOCALE */
+
+bool
+hb_parse_double (const char **pp, const char *end, double *pv,
+		 bool whole_buffer)
+{
+  return _parse_number<double> (pp, end, pv, whole_buffer,
+				[] (const char *p, char **end)
+				{
+#ifdef USE_XLOCALE
+				  return strtod_l (p, end, get_C_locale ());
+#else
+				  return strtod_rl (p, end);
+#endif
+				});
+}
diff --git a/src/hb-number.hh b/src/hb-number.hh
new file mode 100644
index 0000000..14d1260
--- /dev/null
+++ b/src/hb-number.hh
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2019  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_NUMBER_HH
+#define HB_NUMBER_HH
+
+HB_INTERNAL bool
+hb_parse_int (const char **pp, const char *end, int *pv,
+	      bool whole_buffer = false);
+
+HB_INTERNAL bool
+hb_parse_uint (const char **pp, const char *end, unsigned int *pv,
+	       bool whole_buffer = false, int base = 10);
+
+HB_INTERNAL bool
+hb_parse_double (const char **pp, const char *end, double *pv,
+		 bool whole_buffer = false);
+
+#endif /* HB_NUMBER_HH */
diff --git a/src/hb-open-file.hh b/src/hb-open-file.hh
index b053936..cb1fdf1 100644
--- a/src/hb-open-file.hh
+++ b/src/hb-open-file.hh
@@ -287,7 +287,7 @@
   { return CastR<OpenTypeFontFace> ((data_base+offset).arrayZ); }
 
   bool sanitize (hb_sanitize_context_t *c,
-			const void *data_base) const
+		 const void *data_base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
diff --git a/src/hb-open-type.hh b/src/hb-open-type.hh
index 94ee04b..af242ec 100644
--- a/src/hb-open-type.hh
+++ b/src/hb-open-type.hh
@@ -124,9 +124,9 @@
 };
 
 /* 32-bit signed fixed-point number (16.16). */
-struct Fixed : HBINT32
+struct HBFixed : HBINT32
 {
-  Fixed& operator = (uint32_t i) { HBINT32::operator= (i); return *this; }
+  HBFixed& operator = (uint32_t i) { HBINT32::operator= (i); return *this; }
   // 65536 means 1<<16
   float to_float () const  { return ((int32_t) v) / 65536.f; }
   void set_float (float f) { v = roundf (f * 65536.f); }
@@ -163,9 +163,9 @@
 };
 
 /* Glyph index number, same as uint16 (length = 16 bits) */
-struct GlyphID : HBUINT16
+struct HBGlyphID : HBUINT16
 {
-  GlyphID& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; }
+  HBGlyphID& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; }
 };
 
 /* Script/language-system/feature index */
@@ -264,8 +264,8 @@
 template <typename Type>
 struct _hb_has_null<Type, true>
 {
-  static const Type *get_null () { return &Null(Type); }
-  static Type *get_crap ()       { return &Crap(Type); }
+  static const Type *get_null () { return &Null (Type); }
+  static       Type *get_crap () { return &Crap (Type); }
 };
 
 template <typename Type, typename OffsetType=HBUINT16, bool has_null=true>
@@ -423,7 +423,7 @@
   { return hb_array (arrayZ, len); }
   hb_array_t<const Type> as_array (unsigned int len) const
   { return hb_array (arrayZ, len); }
-  operator hb_array_t<Type> ()             { return as_array (); }
+  operator hb_array_t<      Type> ()       { return as_array (); }
   operator hb_array_t<const Type> () const { return as_array (); }
 
   template <typename T>
@@ -483,7 +483,7 @@
   }
 
   public:
-  Type		arrayZ[VAR];
+  Type		arrayZ[HB_VAR_ARRAY];
   public:
   DEFINE_SIZE_UNBOUNDED (0);
 };
@@ -669,7 +669,7 @@
 
   public:
   LenType	len;
-  Type		arrayZ[VAR];
+  Type		arrayZ[HB_VAR_ARRAY];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
 };
@@ -802,7 +802,7 @@
 
   public:
   LenType	lenP1;
-  Type		arrayZ[VAR];
+  Type		arrayZ[HB_VAR_ARRAY];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
 };
@@ -850,7 +850,7 @@
 
   public:
   LenType	lenM1;
-  Type		arrayZ[VAR];
+  Type		arrayZ[HB_VAR_ARRAY];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
 };
@@ -902,8 +902,8 @@
   { return *as_array ().bsearch (x, &not_found); }
   template <typename T>
   bool bfind (const T &x, unsigned int *i = nullptr,
-		     hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
-		     unsigned int to_store = (unsigned int) -1) const
+	      hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
+	      unsigned int to_store = (unsigned int) -1) const
   { return as_array ().bfind (x, i, not_found, to_store); }
 };
 
diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh
index aced77c..6735c74 100644
--- a/src/hb-ot-cff-common.hh
+++ b/src/hb-ot-cff-common.hh
@@ -237,8 +237,8 @@
   public:
   COUNT		count;		/* Number of object data. Note there are (count+1) offsets */
   HBUINT8	offSize;	/* The byte size of each offset in the offsets array. */
-  HBUINT8	offsets[VAR];	/* The array of (count + 1) offsets into objects array (1-base). */
-  /* HBUINT8 data[VAR];	Object data */
+  HBUINT8	offsets[HB_VAR_ARRAY];	/* The array of (count + 1) offsets into objects array (1-base). */
+  /* HBUINT8 data[HB_VAR_ARRAY];	Object data */
   public:
   DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
 };
@@ -514,9 +514,9 @@
   unsigned int get_size (unsigned int num_glyphs) const
   { return HBUINT8::static_size * num_glyphs; }
 
-  HBUINT8     fds[VAR];
+  HBUINT8     fds[HB_VAR_ARRAY];
 
-  DEFINE_SIZE_MIN (1);
+  DEFINE_SIZE_MIN (0);
 };
 
 template <typename GID_TYPE, typename FD_TYPE>
@@ -564,13 +564,13 @@
       if (glyph < ranges[i].first)
 	break;
 
-    return (hb_codepoint_t)ranges[i - 1].fd;
+    return (hb_codepoint_t) ranges[i - 1].fd;
   }
 
-  GID_TYPE &nRanges () { return ranges.len; }
-  GID_TYPE nRanges () const { return ranges.len; }
-  GID_TYPE &sentinel ()  { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }
-  const GID_TYPE &sentinel () const  { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }
+  GID_TYPE        &nRanges ()       { return ranges.len; }
+  GID_TYPE         nRanges () const { return ranges.len; }
+  GID_TYPE       &sentinel ()       { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }
+  const GID_TYPE &sentinel () const { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }
 
   ArrayOf<FDSelect3_4_Range<GID_TYPE, FD_TYPE>, GID_TYPE> ranges;
   /* GID_TYPE sentinel */
@@ -608,8 +608,8 @@
 
   hb_codepoint_t get_fd (hb_codepoint_t glyph) const
   {
-    if (this == &Null (FDSelect))
-      return 0;
+    if (this == &Null (FDSelect)) return 0;
+
     switch (format)
     {
     case 0: return u.format0.get_fd (glyph);
diff --git a/src/hb-ot-cff1-table.cc b/src/hb-ot-cff1-table.cc
index 3e4fc20..5e09af4 100644
--- a/src/hb-ot-cff1-table.cc
+++ b/src/hb-ot-cff1-table.cc
@@ -203,8 +203,7 @@
     }
   }
 
-  bool empty () const
-  { return (min.x >= max.x) || (min.y >= max.y); }
+  bool empty () const { return (min.x >= max.x) || (min.y >= max.y); }
 
   point_t min;
   point_t max;
@@ -219,12 +218,12 @@
     bounds.init ();
   }
 
-  void start_path ()         { path_open = true; }
-  void end_path ()           { path_open = false; }
+  void start_path   ()       { path_open = true; }
+  void end_path     ()       { path_open = false; }
   bool is_path_open () const { return path_open; }
 
-  bool    path_open;
-  bounds_t  bounds;
+  bool path_open;
+  bounds_t bounds;
 
   const OT::cff1::accelerator_t *cff;
 };
diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh
index be6ea52..49bd9b2 100644
--- a/src/hb-ot-cff1-table.hh
+++ b/src/hb-ot-cff1-table.hh
@@ -357,7 +357,7 @@
     return HBUINT16::static_size * (num_glyphs - 1);
   }
 
-  HBUINT16  sids[VAR];
+  HBUINT16  sids[HB_VAR_ARRAY];
 
   DEFINE_SIZE_ARRAY(0, sids);
 };
@@ -439,7 +439,7 @@
     return size;
   }
 
-  Charset_Range<TYPE>   ranges[VAR];
+  Charset_Range<TYPE>   ranges[HB_VAR_ARRAY];
 
   DEFINE_SIZE_ARRAY (0, ranges);
 };
@@ -1155,7 +1155,7 @@
     }
 
     bool is_valid () const { return blob != nullptr; }
-    bool is_CID () const { return topDict.is_CID (); }
+    bool   is_CID () const { return topDict.is_CID (); }
 
     bool is_predef_charset () const { return topDict.CharsetOffset <= ExpertSubsetCharset; }
 
@@ -1225,25 +1225,25 @@
 
     bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; }
 
-    hb_codepoint_t  glyph_to_code (hb_codepoint_t glyph) const
+    hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const
     {
       if (encoding != &Null(Encoding))
 	return encoding->get_code (glyph);
       else
       {
-	hb_codepoint_t  sid = glyph_to_sid (glyph);
+	hb_codepoint_t sid = glyph_to_sid (glyph);
 	if (sid == 0) return 0;
-	hb_codepoint_t  code = 0;
+	hb_codepoint_t code = 0;
 	switch (topDict.EncodingOffset)
 	{
-	  case  StandardEncoding:
-	    code = lookup_standard_encoding_for_code (sid);
-	    break;
-	  case  ExpertEncoding:
-	    code = lookup_expert_encoding_for_code (sid);
-	    break;
-	  default:
-	    break;
+	case StandardEncoding:
+	  code = lookup_standard_encoding_for_code (sid);
+	  break;
+	case ExpertEncoding:
+	  code = lookup_expert_encoding_for_code (sid);
+	  break;
+	default:
+	  break;
 	}
 	return code;
       }
diff --git a/src/hb-ot-cff2-table.cc b/src/hb-ot-cff2-table.cc
index 0251efa..b280550 100644
--- a/src/hb-ot-cff2-table.cc
+++ b/src/hb-ot-cff2-table.cc
@@ -44,8 +44,8 @@
     max_y.set_int (INT_MIN);
   }
 
-  void start_path ()         { path_open = true; }
-  void end_path ()           { path_open = false; }
+  void   start_path ()       { path_open = true; }
+  void     end_path ()       { path_open = false; }
   bool is_path_open () const { return path_open; }
 
   void update_bounds (const point_t &pt)
diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index 48ce881..e664615 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -334,9 +334,7 @@
       return true;
     }
     HB_INTERNAL static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph)
-    {
-      return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph);
-    }
+    { return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); }
     void collect_unicodes (hb_set_t *out) const
     {
       unsigned int count = this->segCount;
@@ -498,7 +496,7 @@
   UINT		length;		/* Byte length of this subtable. */
   UINT		language;	/* Ignore. */
   UINT		startCharCode;	/* First character code covered. */
-  ArrayOf<GlyphID, UINT>
+  ArrayOf<HBGlyphID, UINT>
 		glyphIdArray;	/* Array of glyph index values for character
 				 * codes in the range. */
   public:
@@ -609,11 +607,9 @@
   }
 
   static size_t get_sub_table_size (const hb_sorted_vector_t<CmapSubtableLongGroup> &groups_data)
-  {
-    return 16 + 12 * groups_data.length;
-  }
+  { return 16 + 12 * groups_data.length; }
 
- private:
+  private:
   static bool _is_gid_consecutive (hb_codepoint_t endCharCode,
 				   hb_codepoint_t startCharCode,
 				   hb_codepoint_t glyphID,
@@ -676,6 +672,63 @@
     }
   }
 
+  DefaultUVS* copy (hb_serialize_context_t *c,
+		    const hb_set_t *unicodes) const
+  {
+    DefaultUVS *out = c->start_embed<DefaultUVS> ();
+    if (unlikely (!out)) return nullptr;
+    auto snap = c->snapshot ();
+
+    HBUINT32 len;
+    len = 0;
+    if (unlikely (!c->copy<HBUINT32> (len))) return nullptr;
+    unsigned init_len = c->length ();
+
+    hb_codepoint_t lastCode = HB_MAP_VALUE_INVALID;
+    int count = -1;
+
+    for (const UnicodeValueRange& _ : as_array ())
+    {
+      for (const unsigned addcnt : hb_range ((unsigned) _.additionalCount + 1))
+      {
+	unsigned curEntry = (unsigned) _.startUnicodeValue + addcnt;
+	if (!unicodes->has (curEntry)) continue;
+	count += 1;
+	if (lastCode == HB_MAP_VALUE_INVALID)
+	  lastCode = curEntry;
+	else if (lastCode + count != curEntry)
+	{
+	  UnicodeValueRange rec;
+	  rec.startUnicodeValue = lastCode;
+	  rec.additionalCount = count - 1;
+	  c->copy<UnicodeValueRange> (rec);
+
+	  lastCode = curEntry;
+	  count = 0;
+	}
+      }
+    }
+
+    if (lastCode != HB_MAP_VALUE_INVALID)
+    {
+      UnicodeValueRange rec;
+      rec.startUnicodeValue = lastCode;
+      rec.additionalCount = count;
+      c->copy<UnicodeValueRange> (rec);
+    }
+
+    if (c->length () - init_len == 0)
+    {
+      c->revert (snap);
+      return nullptr;
+    }
+    else
+    {
+      if (unlikely (!c->check_assign (out->len, (c->length () - init_len) / UnicodeValueRange::static_size))) return nullptr;
+      return out;
+    }
+  }
+
   public:
   DEFINE_SIZE_ARRAY (4, *this);
 };
@@ -683,9 +736,7 @@
 struct UVSMapping
 {
   int cmp (const hb_codepoint_t &codepoint) const
-  {
-    return unicodeValue.cmp (codepoint);
-  }
+  { return unicodeValue.cmp (codepoint); }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -694,7 +745,7 @@
   }
 
   HBUINT24	unicodeValue;	/* Base Unicode value of the UVS */
-  GlyphID	glyphID;	/* Glyph ID of the UVS */
+  HBGlyphID	glyphID;	/* Glyph ID of the UVS */
   public:
   DEFINE_SIZE_STATIC (5);
 };
@@ -708,6 +759,49 @@
       out->add (arrayZ[i].glyphID);
   }
 
+  void closure_glyphs (const hb_set_t      *unicodes,
+		       hb_set_t            *glyphset) const
+  {
+    + as_array ()
+    | hb_filter (unicodes, &UVSMapping::unicodeValue)
+    | hb_map (&UVSMapping::glyphID)
+    | hb_sink (glyphset)
+    ;
+  }
+
+  NonDefaultUVS* copy (hb_serialize_context_t *c,
+		       const hb_set_t *unicodes,
+		       const hb_set_t *glyphs,
+		       const hb_map_t *glyph_map) const
+  {
+    NonDefaultUVS *out = c->start_embed<NonDefaultUVS> ();
+    if (unlikely (!out)) return nullptr;
+
+    auto it =
+    + as_array ()
+    | hb_filter ([&] (const UVSMapping& _)
+		 {
+		   return unicodes->has (_.unicodeValue) || glyphs->has (_.glyphID);
+		 })
+    ;
+
+    if (!it) return nullptr;
+
+    HBUINT32 len;
+    len = it.len ();
+    if (unlikely (!c->copy<HBUINT32> (len))) return nullptr;
+
+    for (const UVSMapping& _ : it)
+    {
+      UVSMapping mapping;
+      mapping.unicodeValue = _.unicodeValue;
+      mapping.glyphID = glyph_map->get (_.glyphID);
+      c->copy<UVSMapping> (mapping);
+    }
+
+    return out;
+  }
+
   public:
   DEFINE_SIZE_ARRAY (4, *this);
 };
@@ -736,9 +830,7 @@
   }
 
   int cmp (const hb_codepoint_t &variation_selector) const
-  {
-    return varSelector.cmp (variation_selector);
-  }
+  { return varSelector.cmp (variation_selector); }
 
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
@@ -748,6 +840,52 @@
 		  nonDefaultUVS.sanitize (c, base));
   }
 
+  VariationSelectorRecord* copy (hb_serialize_context_t *c,
+				 const hb_set_t *unicodes,
+				 const hb_set_t *glyphs,
+				 const hb_map_t *glyph_map,
+				 const void *src_base,
+				 const void *dst_base) const
+  {
+    auto snap = c->snapshot ();
+    auto *out = c->embed<VariationSelectorRecord> (*this);
+    if (unlikely (!out)) return nullptr;
+
+    out->defaultUVS = 0;
+    out->nonDefaultUVS = 0;
+
+    bool drop = true;
+
+    if (defaultUVS != 0)
+    {
+      c->push ();
+      if (c->copy (src_base+defaultUVS, unicodes))
+      {
+	c->add_link (out->defaultUVS, c->pop_pack (), dst_base);
+	drop = false;
+      }
+      else c->pop_discard ();
+    }
+
+    if (nonDefaultUVS != 0)
+    {
+      c->push ();
+      if (c->copy (src_base+nonDefaultUVS, unicodes, glyphs, glyph_map))
+      {
+	c->add_link (out->nonDefaultUVS, c->pop_pack (), dst_base);
+	drop = false;
+      }
+      else c->pop_discard ();
+    }
+
+    if (drop)
+    {
+      c->revert (snap);
+      return nullptr;
+    }
+    else return out;
+  }
+
   HBUINT24	varSelector;	/* Variation selector. */
   LOffsetTo<DefaultUVS>
 		defaultUVS;	/* Offset to Default UVS Table.  May be 0. */
@@ -762,9 +900,7 @@
   glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint,
 				     hb_codepoint_t variation_selector,
 				     hb_codepoint_t *glyph) const
-  {
-    return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this);
-  }
+  { return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); }
 
   void collect_variation_selectors (hb_set_t *out) const
   {
@@ -774,8 +910,44 @@
   }
   void collect_variation_unicodes (hb_codepoint_t variation_selector,
 				   hb_set_t *out) const
+  { record.bsearch (variation_selector).collect_unicodes (out, this); }
+
+  void serialize (hb_serialize_context_t *c,
+		  const hb_set_t *unicodes,
+		  const hb_set_t *glyphs,
+		  const hb_map_t *glyph_map,
+		  const void *src_base)
   {
-    record.bsearch (variation_selector).collect_unicodes (out, this);
+    auto snap = c->snapshot ();
+    unsigned table_initpos = c->length ();
+    const char* init_tail = c->tail;
+
+    if (unlikely (!c->extend_min (*this))) return;
+    this->format = 14;
+
+    const CmapSubtableFormat14 *src_tbl = reinterpret_cast<const CmapSubtableFormat14*> (src_base);
+    for (const VariationSelectorRecord& _ : src_tbl->record)
+      c->copy (_, unicodes, glyphs, glyph_map, src_base, this);
+
+    if (c->length () - table_initpos == CmapSubtableFormat14::min_size)
+      c->revert (snap);
+    else
+    {
+      int tail_len = init_tail - c->tail;
+      c->check_assign (this->length, c->length () - table_initpos + tail_len);
+      c->check_assign (this->record.len, (c->length () - table_initpos - CmapSubtableFormat14::min_size) / VariationSelectorRecord::static_size);
+    }
+  }
+
+  void closure_glyphs (const hb_set_t      *unicodes,
+		       hb_set_t            *glyphset) const
+  {
+    + hb_iter (record)
+    | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS)
+    | hb_map (&VariationSelectorRecord::nonDefaultUVS)
+    | hb_map (hb_add (this))
+    | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); })
+    ;
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -831,11 +1003,14 @@
 	   hb_requires (hb_is_iterator (Iterator))>
   void serialize (hb_serialize_context_t *c,
 		  Iterator it,
-		  unsigned format)
+		  unsigned format,
+		  const hb_subset_plan_t *plan,
+		  const void *src_base)
   {
     switch (format) {
     case  4: u.format4.serialize (c, it);  return;
     case 12: u.format12.serialize (c, it); return;
+    case 14: u.format14.serialize (c, plan->unicodes, plan->_glyphset, plan->glyph_map, src_base); return;
     default: return;
     }
   }
@@ -896,10 +1071,13 @@
   EncodingRecord* copy (hb_serialize_context_t *c,
 			Iterator it,
 			unsigned format,
-			void *base,
+			const void *src_base,
+			const void *dst_base,
+			const hb_subset_plan_t *plan,
 			/* INOUT */ unsigned *objidx) const
   {
     TRACE_SERIALIZE (this);
+    auto snap = c->snapshot ();
     auto *out = c->embed (this);
     if (unlikely (!out)) return_trace (nullptr);
     out->subtable = 0;
@@ -908,12 +1086,18 @@
     {
       CmapSubtable *cmapsubtable = c->push<CmapSubtable> ();
       unsigned origin_length = c->length ();
-      cmapsubtable->serialize (c, it, format);
+      cmapsubtable->serialize (c, it, format, plan, &(src_base+subtable));
       if (c->length () - origin_length > 0) *objidx = c->pop_pack ();
       else c->pop_discard ();
     }
 
-    c->add_link (out->subtable, *objidx, base);
+    if (*objidx == 0)
+    {
+      c->revert (snap);
+      return_trace (nullptr);
+    }
+
+    c->add_link (out->subtable, *objidx, dst_base);
     return_trace (out);
   }
 
@@ -929,27 +1113,40 @@
 {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap;
 
-  template<typename Iterator,
+  template<typename Iterator, typename EncodingRecIter,
 	   hb_requires (hb_is_iterator (Iterator))>
   void serialize (hb_serialize_context_t *c,
 		  Iterator it,
-		  const EncodingRecord *unicode_bmp,
-		  const EncodingRecord *unicode_ucs4,
-		  const EncodingRecord *ms_bmp,
-		  const EncodingRecord *ms_ucs4)
+		  EncodingRecIter encodingrec_iter,
+		  const void *src_base,
+		  const hb_subset_plan_t *plan)
   {
     if (unlikely (!c->extend_min ((*this))))  return;
     this->version = 0;
 
-    unsigned numTables = (unicode_bmp ? 1 : 0) + (unicode_ucs4 ? 1 : 0) + (ms_bmp ? 1 : 0) + (ms_ucs4 ? 1 : 0);
-    if (unlikely (!c->check_assign(this->encodingRecord.len, numTables))) return;
+    unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0;
 
-    unsigned format4objidx = 0, format12objidx = 0;
-    if (unicode_bmp) c->copy (unicode_bmp, it, 4u, this, &format4objidx);
-    if (unicode_ucs4) c->copy (unicode_ucs4, it, 12u, this, &format12objidx);
-    if (ms_bmp) c->copy (ms_bmp, it, 4u, this, &format4objidx);
-    if (ms_ucs4) c->copy (ms_ucs4, it, 12u, this, &format12objidx);
+    for (const EncodingRecord& _ : encodingrec_iter)
+    {
+      unsigned format = (src_base+_.subtable).u.format;
 
+      if (format == 4) c->copy (_, it, 4u, src_base, this, plan, &format4objidx);
+      else if (format == 12) c->copy (_, it, 12u, src_base, this, plan, &format12objidx);
+      else if (format == 14) c->copy (_, it, 14u, src_base, this, plan, &format14objidx);
+    }
+
+    c->check_assign(this->encodingRecord.len, (c->length () - cmap::min_size)/EncodingRecord::static_size);
+  }
+
+  void closure_glyphs (const hb_set_t      *unicodes,
+		       hb_set_t            *glyphset) const
+  {
+    + hb_iter (encodingRecord)
+    | hb_map (&EncodingRecord::subtable)
+    | hb_map (hb_add (this))
+    | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; })
+    | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); })
+    ;
   }
 
   bool subset (hb_subset_context_t *c) const
@@ -959,31 +1156,55 @@
     cmap *cmap_prime = c->serializer->start_embed<cmap> ();
     if (unlikely (!c->serializer->check_success (cmap_prime))) return_trace (false);
 
-    const EncodingRecord *unicode_bmp = find_encodingrec (0, 3);
-    const EncodingRecord *unicode_ucs4 = find_encodingrec (0, 4);
-    const EncodingRecord *ms_bmp = find_encodingrec (3, 1);
-    const EncodingRecord *ms_ucs4 = find_encodingrec (3, 10);
-    bool has_format12 = find_subtable (12);
+    auto encodingrec_iter =
+    + hb_iter (encodingRecord)
+    | hb_filter ([&] (const EncodingRecord& _)
+		{
+		  if ((_.platformID == 0 && _.encodingID == 3) ||
+		      (_.platformID == 0 && _.encodingID == 4) ||
+		      (_.platformID == 3 && _.encodingID == 1) ||
+		      (_.platformID == 3 && _.encodingID == 10) ||
+		      (this + _.subtable).u.format == 14)
+		    return true;
+
+		  return false;
+		})
+    ;
+
+
+    if (unlikely (!encodingrec_iter.len ())) return_trace (false);
+
+    const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr;
+    bool has_format12 = false;
+
+    for (const EncodingRecord& _ : encodingrec_iter)
+    {
+      unsigned format = (this + _.subtable).u.format;
+      if (format == 12) has_format12 = true;
+
+      const EncodingRecord *table = hb_addressof (_);
+      if      (_.platformID == 0 && _.encodingID ==  3) unicode_bmp = table;
+      else if (_.platformID == 0 && _.encodingID ==  4) unicode_ucs4 = table;
+      else if (_.platformID == 3 && _.encodingID ==  1) ms_bmp = table;
+      else if (_.platformID == 3 && _.encodingID == 10) ms_ucs4 = table;
+    }
 
     if (unlikely (!unicode_bmp && !ms_bmp)) return_trace (false);
     if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false);
 
-
     auto it =
     + hb_iter (c->plan->unicodes)
     | hb_map ([&] (hb_codepoint_t _)
-		 {
-		   hb_codepoint_t new_gid = HB_MAP_VALUE_INVALID;
-		   c->plan->new_gid_for_codepoint (_, &new_gid);
-		   return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, new_gid);
-		 })
-    | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _)
 	      {
-		return (_.second != HB_MAP_VALUE_INVALID);
+		hb_codepoint_t new_gid = HB_MAP_VALUE_INVALID;
+		c->plan->new_gid_for_codepoint (_, &new_gid);
+		return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, new_gid);
 	      })
+    | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _)
+		 { return (_.second != HB_MAP_VALUE_INVALID); })
     ;
 
-    cmap_prime->serialize (c->serializer, it, unicode_bmp, unicode_ucs4, ms_bmp, ms_ucs4);
+    cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan);
     return_trace (true);
   }
 
@@ -1034,9 +1255,9 @@
 
       this->get_glyph_data = subtable;
       if (unlikely (symbol))
-      {
 	this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable>;
-      } else {
+      else
+      {
 	switch (subtable->u.format) {
 	/* Accelerate format 4 and format 12. */
 	default:
@@ -1046,20 +1267,20 @@
 	  this->get_glyph_funcZ = get_glyph_from<CmapSubtableFormat12>;
 	  break;
 	case  4:
-	  {
-	    this->format4_accel.init (&subtable->u.format4);
-	    this->get_glyph_data = &this->format4_accel;
-	    this->get_glyph_funcZ = this->format4_accel.get_glyph_func;
-	  }
+	{
+	  this->format4_accel.init (&subtable->u.format4);
+	  this->get_glyph_data = &this->format4_accel;
+	  this->get_glyph_funcZ = this->format4_accel.get_glyph_func;
 	  break;
 	}
+	}
       }
     }
 
     void fini () { this->table.destroy (); }
 
     bool get_nominal_glyph (hb_codepoint_t  unicode,
-				   hb_codepoint_t *glyph) const
+			    hb_codepoint_t *glyph) const
     {
       if (unlikely (!this->get_glyph_funcZ)) return false;
       return this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph);
@@ -1103,18 +1324,12 @@
     }
 
     void collect_unicodes (hb_set_t *out) const
-    {
-      subtable->collect_unicodes (out);
-    }
+    { subtable->collect_unicodes (out); }
     void collect_variation_selectors (hb_set_t *out) const
-    {
-      subtable_uvs->collect_variation_selectors (out);
-    }
+    { subtable_uvs->collect_variation_selectors (out); }
     void collect_variation_unicodes (hb_codepoint_t variation_selector,
 				     hb_set_t *out) const
-    {
-      subtable_uvs->collect_variation_unicodes (variation_selector, out);
-    }
+    { subtable_uvs->collect_variation_unicodes (variation_selector, out); }
 
     protected:
     typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
@@ -1161,6 +1376,7 @@
 
     CmapSubtableFormat4::accelerator_t format4_accel;
 
+    public:
     hb_blob_ptr_t<cmap> table;
   };
 
diff --git a/src/hb-ot-color-cbdt-table.hh b/src/hb-ot-color-cbdt-table.hh
index e85e4ad..06da556 100644
--- a/src/hb-ot-color-cbdt-table.hh
+++ b/src/hb-ot-color-cbdt-table.hh
@@ -226,8 +226,8 @@
 						   offset, length, format);
   }
 
-  GlyphID			firstGlyphIndex;
-  GlyphID			lastGlyphIndex;
+  HBGlyphID			firstGlyphIndex;
+  HBGlyphID			lastGlyphIndex;
   LOffsetTo<IndexSubtable>	offsetToSubtable;
   public:
   DEFINE_SIZE_STATIC(8);
@@ -290,8 +290,8 @@
   HBUINT32		colorRef;
   SBitLineMetrics	horizontal;
   SBitLineMetrics	vertical;
-  GlyphID		startGlyphIndex;
-  GlyphID		endGlyphIndex;
+  HBGlyphID		startGlyphIndex;
+  HBGlyphID		endGlyphIndex;
   HBUINT8		ppemX;
   HBUINT8		ppemY;
   HBUINT8		bitDepth;
@@ -453,7 +453,7 @@
     }
 
     hb_blob_t* reference_png (hb_font_t      *font,
-				     hb_codepoint_t  glyph) const
+			      hb_codepoint_t  glyph) const
     {
       const void *base;
       const BitmapSizeTable &strike = this->cblc->choose_strike (font);
diff --git a/src/hb-ot-color-colr-table.hh b/src/hb-ot-color-colr-table.hh
index 3447b14..e2ed7c6 100644
--- a/src/hb-ot-color-colr-table.hh
+++ b/src/hb-ot-color-colr-table.hh
@@ -48,7 +48,7 @@
   }
 
   protected:
-  GlyphID	glyphId;	/* Glyph ID of layer glyph */
+  HBGlyphID	glyphId;	/* Glyph ID of layer glyph */
   Index		colorIdx;	/* Index value to use with a
 				 * selected color palette.
 				 * An index value of 0xFFFF
@@ -75,7 +75,7 @@
   }
 
   public:
-  GlyphID	glyphId;	/* Glyph ID of reference glyph */
+  HBGlyphID	glyphId;	/* Glyph ID of reference glyph */
   HBUINT16	firstLayerIdx;	/* Index (from beginning of
 				 * the Layer Records) to the
 				 * layer record. There will be
diff --git a/src/hb-ot-color-cpal-table.hh b/src/hb-ot-color-cpal-table.hh
index 76df889..1b3c7fc 100644
--- a/src/hb-ot-color-cpal-table.hh
+++ b/src/hb-ot-color-cpal-table.hh
@@ -115,7 +115,7 @@
   { return min_size + numPalettes * sizeof (colorRecordIndicesZ[0]); }
 
   unsigned int get_palette_count () const { return numPalettes; }
-  unsigned int get_color_count () const   { return numColors; }
+  unsigned int   get_color_count () const { return numColors; }
 
   hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette_index) const
   { return v1 ().get_palette_flags (this, palette_index, numPalettes); }
diff --git a/src/hb-ot-color-sbix-table.hh b/src/hb-ot-color-sbix-table.hh
index 824b960..6089027 100644
--- a/src/hb-ot-color-sbix-table.hh
+++ b/src/hb-ot-color-sbix-table.hh
@@ -125,7 +125,7 @@
 		imageOffsetsZ;	/* Offset from the beginning of the strike data header
 				 * to bitmap data for an individual glyph ID. */
   public:
-  DEFINE_SIZE_STATIC (8);
+  DEFINE_SIZE_ARRAY (4, imageOffsetsZ);
 };
 
 struct sbix
diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh
index 2d4505c..5ee3c27 100644
--- a/src/hb-ot-glyf-table.hh
+++ b/src/hb-ot-glyf-table.hh
@@ -147,11 +147,7 @@
 		  const hb_subset_plan_t *plan)
   {
     TRACE_SERIALIZE (this);
-
-    + it
-    | hb_apply ([=] (const SubsetGlyph& _) { _.serialize (c, plan); })
-    ;
-
+    for (const auto &_ : it) _.serialize (c, plan);
     return_trace (true);
   }
 
@@ -326,7 +322,7 @@
     };
 
     HBUINT16 flags;
-    GlyphID  glyphIndex;
+    HBGlyphID  glyphIndex;
 
     unsigned int get_size () const
     {
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index 687c1f1..700381a 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -197,8 +197,6 @@
       var_table.destroy ();
     }
 
-    bool has_data () const { return table.get () != nullptr; }
-
     int get_side_bearing (hb_codepoint_t glyph) const
     {
       if (glyph < num_advances)
@@ -214,17 +212,14 @@
     int get_side_bearing (hb_font_t *font, hb_codepoint_t glyph) const
     {
       int side_bearing = get_side_bearing (glyph);
-      if (likely (glyph < num_metrics))
-      {
-	if (font->num_coords)
-	{
-	  if (var_table.get_blob () != hb_blob_get_empty ())
-	    side_bearing += var_table->get_side_bearing_var (glyph, font->coords, font->num_coords); // TODO Optimize?!
-	  else
-	    side_bearing = get_side_bearing_var_tt (font, glyph, T::tableTag==HB_OT_TAG_vmtx);
-	}
-      }
-      return side_bearing;
+
+      if (unlikely (glyph >= num_metrics) || !font->num_coords)
+	return side_bearing;
+
+      if (var_table.get_blob () == &Null (hb_blob_t))
+	return get_side_bearing_var_tt (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
+
+      return side_bearing + var_table->get_side_bearing_var (glyph, font->coords, font->num_coords); // TODO Optimize?!
     }
 
     unsigned int get_advance (hb_codepoint_t glyph) const
@@ -247,17 +242,14 @@
 			      hb_font_t      *font) const
     {
       unsigned int advance = get_advance (glyph);
-      if (likely (glyph < num_metrics))
-      {
-      	if (font->num_coords)
-      	{
-	  if (var_table.get_blob () != hb_blob_get_empty ())
-	    advance += roundf (var_table->get_advance_var (glyph, font->coords, font->num_coords)); // TODO Optimize?!
-	  else
-	    advance = get_advance_var_tt (font, glyph, T::tableTag==HB_OT_TAG_vmtx);
-	}
-      }
-      return advance;
+
+      if (unlikely (glyph >= num_metrics) || !font->num_coords)
+	return advance;
+
+      if (var_table.get_blob () == &Null (hb_blob_t))
+	return get_advance_var_tt (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
+
+      return advance + roundf (var_table->get_advance_var (glyph, font->coords, font->num_coords)); // TODO Optimize?!
     }
 
     unsigned int num_advances_for_subset (const hb_subset_plan_t *plan) const
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 7dfb207..36e5a35 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -167,8 +167,8 @@
   static constexpr bool apple = false;
   typedef AAT::ObsoleteTypes Types;
 
-  unsigned int tuple_count () const { return 0; }
-  bool is_horizontal () const { return (coverage & Horizontal); }
+  unsigned   tuple_count () const { return 0; }
+  bool     is_horizontal () const { return (coverage & Horizontal); }
 
   enum Coverage
   {
@@ -222,8 +222,8 @@
   static constexpr bool apple = true;
   typedef AAT::ObsoleteTypes Types;
 
-  unsigned int tuple_count () const { return 0; }
-  bool is_horizontal () const       { return !(coverage & Vertical); }
+  unsigned   tuple_count () const { return 0; }
+  bool     is_horizontal () const { return !(coverage & Vertical); }
 
   enum Coverage
   {
@@ -275,8 +275,8 @@
 {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_kern;
 
-  bool has_data () const { return u.version32; }
-  unsigned int get_type () const { return u.major; }
+  bool     has_data () const { return u.version32; }
+  unsigned get_type () const { return u.major; }
 
   bool has_state_machine () const
   {
diff --git a/src/hb-ot-layout-base-table.hh b/src/hb-ot-layout-base-table.hh
index 7c00a13..02fe14f 100644
--- a/src/hb-ot-layout-base-table.hh
+++ b/src/hb-ot-layout-base-table.hh
@@ -73,7 +73,7 @@
   protected:
   HBUINT16	format;		/* Format identifier--format = 2 */
   FWORD		coordinate;	/* X or Y value, in design units */
-  GlyphID	referenceGlyph;	/* Glyph ID of control glyph */
+  HBGlyphID	referenceGlyph;	/* Glyph ID of control glyph */
   HBUINT16	coordPoint;	/* Index of contour point on the
 				 * reference glyph */
   public:
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh
index 7e08436..051bfda 100644
--- a/src/hb-ot-layout-common.hh
+++ b/src/hb-ot-layout-common.hh
@@ -173,8 +173,8 @@
   bool add_coverage (set_t *glyphs) const
   { return glyphs->add_range (start, end); }
 
-  GlyphID	start;		/* First GlyphID in the range */
-  GlyphID	end;		/* Last GlyphID in the range */
+  HBGlyphID	start;		/* First GlyphID in the range */
+  HBGlyphID	end;		/* Last GlyphID in the range */
   HBUINT16	value;		/* Value */
   public:
   DEFINE_SIZE_STATIC (6);
@@ -541,7 +541,7 @@
   FeatureParamsCharacterVariants	characterVariants;
   } u;
   public:
-  DEFINE_SIZE_STATIC (17);
+  DEFINE_SIZE_MIN (0);
 };
 
 struct Feature
@@ -781,7 +781,7 @@
   HBUINT16	lookupFlag;		/* Lookup qualifiers */
   ArrayOf<Offset16>
 		subTable;		/* Array of SubTables */
-/*HBUINT16	markFilteringSetX[VAR];*//* Index (base 0) into GDEF mark glyph sets
+/*HBUINT16	markFilteringSetX[HB_VAR_ARRAY];*//* Index (base 0) into GDEF mark glyph sets
 					 * structure. This field is only present if bit
 					 * UseMarkFilteringSet of lookup flags is set. */
   public:
@@ -857,7 +857,7 @@
 
   protected:
   HBUINT16	coverageFormat;	/* Format identifier--format = 1 */
-  SortedArrayOf<GlyphID>
+  SortedArrayOf<HBGlyphID>
 		glyphArray;	/* Array of GlyphIDs--in numerical order */
   public:
   DEFINE_SIZE_ARRAY (4, glyphArray);
@@ -1063,7 +1063,7 @@
       last = g;
       count++;
     }
-    u.format = count * 2 < num_ranges * 3 ? 1 : 2;
+    u.format = count <= num_ranges * 3 ? 1 : 2;
 
     switch (u.format)
     {
@@ -1197,7 +1197,7 @@
  */
 
 static inline void ClassDef_serialize (hb_serialize_context_t *c,
-				       hb_array_t<const GlyphID> glyphs,
+				       hb_array_t<const HBGlyphID> glyphs,
 				       hb_array_t<const HBUINT16> klasses);
 
 struct ClassDefFormat1
@@ -1211,7 +1211,7 @@
   }
 
   bool serialize (hb_serialize_context_t *c,
-		  hb_array_t<const GlyphID> glyphs,
+		  hb_array_t<const HBGlyphID> glyphs,
 		  hb_array_t<const HBUINT16> klasses)
   {
     TRACE_SERIALIZE (this);
@@ -1242,7 +1242,7 @@
     TRACE_SUBSET (this);
     const hb_set_t &glyphset = *c->plan->glyphset ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
-    hb_sorted_vector_t<GlyphID> glyphs;
+    hb_sorted_vector_t<HBGlyphID> glyphs;
     hb_vector_t<HBUINT16> klasses;
 
     hb_codepoint_t start = startGlyph;
@@ -1329,7 +1329,7 @@
 
   protected:
   HBUINT16	classFormat;	/* Format identifier--format = 1 */
-  GlyphID	startGlyph;	/* First GlyphID of the classValueArray */
+  HBGlyphID	startGlyph;	/* First GlyphID of the classValueArray */
   ArrayOf<HBUINT16>
 		classValue;	/* Array of Class Values--one per GlyphID */
   public:
@@ -1347,7 +1347,7 @@
   }
 
   bool serialize (hb_serialize_context_t *c,
-		  hb_array_t<const GlyphID> glyphs,
+		  hb_array_t<const HBGlyphID> glyphs,
 		  hb_array_t<const HBUINT16> klasses)
   {
     TRACE_SERIALIZE (this);
@@ -1391,7 +1391,7 @@
     TRACE_SUBSET (this);
     const hb_set_t &glyphset = *c->plan->glyphset ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
-    hb_vector_t<GlyphID> glyphs;
+    hb_vector_t<HBGlyphID> glyphs;
     hb_vector_t<HBUINT16> klasses;
 
     unsigned int count = rangeRecord.len;
@@ -1507,7 +1507,7 @@
   }
 
   bool serialize (hb_serialize_context_t *c,
-		  hb_array_t<const GlyphID> glyphs,
+		  hb_array_t<const HBGlyphID> glyphs,
 		  hb_array_t<const HBUINT16> klasses)
   {
     TRACE_SERIALIZE (this);
@@ -1612,7 +1612,7 @@
 };
 
 static inline void ClassDef_serialize (hb_serialize_context_t *c,
-				       hb_array_t<const GlyphID> glyphs,
+				       hb_array_t<const HBGlyphID> glyphs,
 				       hb_array_t<const HBUINT16> klasses)
 { c->start_embed<ClassDef> ()->serialize (c, glyphs, klasses); }
 
@@ -2218,6 +2218,8 @@
   hb_position_t get_y_delta (hb_font_t *font) const
   { return get_delta (font->y_ppem, font->y_scale); }
 
+  public:
+
   unsigned int get_size () const
   {
     unsigned int f = deltaFormat;
@@ -2231,6 +2233,12 @@
     return_trace (c->check_struct (this) && c->check_range (this, this->get_size ()));
   }
 
+  HintingDevice* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (c->embed<HintingDevice> (this));
+  }
+
   private:
 
   int get_delta (unsigned int ppem, int scale) const
@@ -2292,6 +2300,12 @@
   hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store) const
   { return font->em_scalef_y (get_delta (font, store)); }
 
+  VariationDevice* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (c->embed<VariationDevice> (this));
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -2377,6 +2391,25 @@
     }
   }
 
+  Device* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    switch (u.b.format) {
+#ifndef HB_NO_HINTING
+    case 1:
+    case 2:
+    case 3:
+      return_trace (reinterpret_cast<Device *> (u.hinting.copy (c)));
+#endif
+#ifndef HB_NO_VAR
+    case 0x8000:
+      return_trace (reinterpret_cast<Device *> (u.variation.copy (c)));
+#endif
+    default:
+      return_trace (nullptr);
+    }
+  }
+
   protected:
   union {
   DeviceHeader		b;
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index a9ec962..2b535af 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -101,10 +101,10 @@
   unsigned int get_len () const  { return hb_popcount ((unsigned int) *this); }
   unsigned int get_size () const { return get_len () * Value::static_size; }
 
-  bool apply_value (hb_ot_apply_context_t   *c,
-		    const void           *base,
-		    const Value          *values,
-		    hb_glyph_position_t  &glyph_pos) const
+  bool apply_value (hb_ot_apply_context_t *c,
+		    const void            *base,
+		    const Value           *values,
+		    hb_glyph_position_t   &glyph_pos) const
   {
     bool ret = false;
     unsigned int format = *this;
@@ -258,6 +258,12 @@
     return_trace (c->check_struct (this));
   }
 
+  AnchorFormat1* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (c->embed<AnchorFormat1> (this));
+  }
+
   protected:
   HBUINT16	format;			/* Format identifier--format = 1 */
   FWORD		xCoordinate;		/* Horizontal value--in design units */
@@ -296,6 +302,12 @@
     return_trace (c->check_struct (this));
   }
 
+  AnchorFormat2* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (c->embed<AnchorFormat2> (this));
+  }
+
   protected:
   HBUINT16	format;			/* Format identifier--format = 2 */
   FWORD		xCoordinate;		/* Horizontal value--in design units */
@@ -326,6 +338,17 @@
     return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
   }
 
+  AnchorFormat3* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->embed<AnchorFormat3> (this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    out->xDeviceTable.serialize_copy (c, xDeviceTable, this, out);
+    out->yDeviceTable.serialize_copy (c, yDeviceTable, this, out);
+    return_trace (out);
+  }
+
   protected:
   HBUINT16	format;			/* Format identifier--format = 3 */
   FWORD		xCoordinate;		/* Horizontal value--in design units */
@@ -368,6 +391,17 @@
     }
   }
 
+  Anchor* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    switch (u.format) {
+    case 1: return_trace (reinterpret_cast<Anchor *> (u.format1.copy (c)));
+    case 2: return_trace (reinterpret_cast<Anchor *> (u.format2.copy (c)));
+    case 3: return_trace (reinterpret_cast<Anchor *> (u.format3.copy (c)));
+    default:return_trace (nullptr);
+    }
+  }
+
   protected:
   union {
   HBUINT16		format;		/* Format identifier */
@@ -510,11 +544,8 @@
     if (unlikely (!c->extend_min (*this))) return;
     if (unlikely (!c->check_assign (valueFormat, valFormat))) return;
 
-    auto vals = hb_second (*it);
-
-    + vals
-    | hb_apply ([=] (const Value& _) { c->copy (_); })
-    ;
+    for (const auto &_ : hb_second (*it))
+      c->copy (_);
 
     auto glyphs =
     + it
@@ -530,15 +561,11 @@
     const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
-    unsigned length = valueFormat.get_len ();
-
     auto it =
     + hb_iter (this+coverage)
     | hb_filter (glyphset)
-    | hb_map_retains_sorting ([&] (hb_codepoint_t p)
-			      {
-				return hb_pair (glyph_map[p], values.as_array (length));
-			      })
+    | hb_map_retains_sorting (glyph_map)
+    | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ())))
     ;
 
     bool ret = bool (it);
@@ -605,15 +632,9 @@
     if (unlikely (!c->check_assign (valueFormat, valFormat))) return;
     if (unlikely (!c->check_assign (valueCount, it.len ()))) return;
 
-    + it
-    | hb_map (hb_second)
-    | hb_apply ([=] (hb_array_t<const Value> val_iter)
-		{
-		  + val_iter
-		  | hb_apply ([=] (const Value& _) { c->copy (_); })
-		  ;
-		})
-    ;
+    for (const auto iter : it)
+      for (const auto &_ : iter.second)
+	c->copy (_);
 
     auto glyphs =
     + it
@@ -630,14 +651,16 @@
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     unsigned sub_length = valueFormat.get_len ();
-    unsigned total_length = (unsigned)valueCount * sub_length;
+    auto values_array = values.as_array (valueCount * sub_length);
 
     auto it =
     + hb_zip (this+coverage, hb_range ((unsigned) valueCount))
     | hb_filter (glyphset, hb_first)
     | hb_map_retains_sorting ([&] (const hb_pair_t<hb_codepoint_t, unsigned>& _)
 			      {
-				return hb_pair (glyph_map[_.first], values.as_array (total_length).sub_array (_.second * sub_length, sub_length));
+				return hb_pair (glyph_map[_.first],
+						values_array.sub_array (_.second * sub_length,
+									sub_length));
 			      })
     ;
 
@@ -674,27 +697,14 @@
 	   hb_requires (hb_is_iterator (Iterator))>
   unsigned get_format (Iterator glyph_val_iter_pairs)
   {
-    unsigned subset_format = 1;
     hb_array_t<const Value> first_val_iter = hb_second (*glyph_val_iter_pairs);
 
-    + glyph_val_iter_pairs
-    | hb_map (hb_second)
-    | hb_apply ([&] (hb_array_t<const Value> val_iter)
-		{
-		  + hb_zip (val_iter, first_val_iter)
-		  | hb_apply ([&] (const hb_pair_t<Value, Value>& _)
-			      {
-				if (_.first != _.second)
-				{
-				  subset_format = 2;
-				  return;
-				}
-			      })
-		  ;
-		})
-    ;
+    for (const auto iter : glyph_val_iter_pairs)
+      for (const auto _ : hb_zip (iter.second, first_val_iter))
+	if (_.first != _.second)
+	  return 2;
 
-    return subset_format;
+    return 1;
   }
 
 
@@ -752,7 +762,7 @@
   friend struct PairSet;
 
   protected:
-  GlyphID	secondGlyph;		/* GlyphID of second glyph in the
+  HBGlyphID	secondGlyph;		/* GlyphID of second glyph in the
 					 * pair--first glyph is listed in the
 					 * Coverage table */
   ValueRecord	values;			/* Positioning data for the first glyph
@@ -1088,6 +1098,19 @@
     return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
   }
 
+  EntryExitRecord* copy (hb_serialize_context_t *c,
+			 const void *src_base,
+			 const void *dst_base) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    out->entryAnchor.serialize_copy (c, entryAnchor, src_base, dst_base);
+    out->exitAnchor.serialize_copy (c, exitAnchor, src_base, dst_base);
+    return_trace (out);
+  }
+
   protected:
   OffsetTo<Anchor>
 		entryAnchor;		/* Offset to EntryAnchor table--from
@@ -1215,11 +1238,47 @@
     return_trace (true);
   }
 
+  template <typename Iterator,
+	    hb_requires (hb_is_iterator (Iterator))>
+  void serialize (hb_serialize_context_t *c,
+		  Iterator it,
+		  const void *src_base)
+  {
+    if (unlikely (!c->extend_min ((*this)))) return;
+    this->format = 1;
+    this->entryExitRecord.len = it.len ();
+
+    for (const EntryExitRecord& entry_record : + it
+					       | hb_map (hb_second))
+      c->copy (entry_record, src_base, this);
+
+    auto glyphs =
+    + it
+    | hb_map_retains_sorting (hb_first)
+    ;
+
+    coverage.serialize (c, this).serialize (c, glyphs);
+  }
+
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    // TODO(subset)
-    return_trace (false);
+    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    auto it =
+    + hb_zip (this+coverage, entryExitRecord)
+    | hb_filter (glyphset, hb_first)
+    | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const EntryExitRecord&> p) -> hb_pair_t<hb_codepoint_t, const EntryExitRecord&>
+			      { return hb_pair (glyph_map[p.first], p.second);})
+    ;
+
+    bool ret = bool (it);
+    out->serialize (c->serializer, it, this);
+    return_trace (ret);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 91aee11..a2722ad 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -111,8 +111,11 @@
     + hb_iter (this+coverage)
     | hb_filter (glyphset)
     | hb_map_retains_sorting ([&] (hb_codepoint_t g) {
-				return hb_codepoint_pair_t (glyph_map[g],
-							    glyph_map[(g + delta) & 0xFFFF]); })
+				return hb_codepoint_pair_t (g,
+							    (g + delta) & 0xFFFF); })
+    | hb_filter (glyphset, hb_second)
+    | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t
+			      { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
     ;
 
     bool ret = bool (it);
@@ -208,7 +211,8 @@
     auto it =
     + hb_zip (this+coverage, substitute)
     | hb_filter (glyphset, hb_first)
-    | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const GlyphID &> p) -> hb_codepoint_pair_t
+    | hb_filter (glyphset, hb_second)
+    | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const HBGlyphID &> p) -> hb_codepoint_pair_t
 			      { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
     ;
 
@@ -228,7 +232,7 @@
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
-  ArrayOf<GlyphID>
+  ArrayOf<HBGlyphID>
 		substitute;		/* Array of substitute
 					 * GlyphIDs--ordered by Coverage Index */
   public:
@@ -370,7 +374,7 @@
   }
 
   protected:
-  ArrayOf<GlyphID>
+  ArrayOf<HBGlyphID>
 		substitute;		/* String of GlyphIDs to substitute */
   public:
   DEFINE_SIZE_ARRAY (2, substitute);
@@ -417,9 +421,9 @@
   }
 
   bool serialize (hb_serialize_context_t *c,
-		  hb_sorted_array_t<const GlyphID> glyphs,
+		  hb_sorted_array_t<const HBGlyphID> glyphs,
 		  hb_array_t<const unsigned int> substitute_len_list,
-		  hb_array_t<const GlyphID> substitute_glyphs_list)
+		  hb_array_t<const HBGlyphID> substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
@@ -492,9 +496,9 @@
 struct MultipleSubst
 {
   bool serialize (hb_serialize_context_t *c,
-		  hb_sorted_array_t<const GlyphID> glyphs,
+		  hb_sorted_array_t<const HBGlyphID> glyphs,
 		  hb_array_t<const unsigned int> substitute_len_list,
-		  hb_array_t<const GlyphID> substitute_glyphs_list)
+		  hb_array_t<const HBGlyphID> substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
@@ -593,7 +597,7 @@
   }
 
   protected:
-  ArrayOf<GlyphID>
+  ArrayOf<HBGlyphID>
 		alternates;		/* Array of alternate GlyphIDs--in
 					 * arbitrary order */
   public:
@@ -640,9 +644,9 @@
   }
 
   bool serialize (hb_serialize_context_t *c,
-		  hb_sorted_array_t<const GlyphID> glyphs,
+		  hb_sorted_array_t<const HBGlyphID> glyphs,
 		  hb_array_t<const unsigned int> alternate_len_list,
-		  hb_array_t<const GlyphID> alternate_glyphs_list)
+		  hb_array_t<const HBGlyphID> alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
@@ -715,9 +719,9 @@
 struct AlternateSubst
 {
   bool serialize (hb_serialize_context_t *c,
-		  hb_sorted_array_t<const GlyphID> glyphs,
+		  hb_sorted_array_t<const HBGlyphID> glyphs,
 		  hb_array_t<const unsigned int> alternate_len_list,
-		  hb_array_t<const GlyphID> alternate_glyphs_list)
+		  hb_array_t<const HBGlyphID> alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
@@ -856,8 +860,8 @@
   }
 
   protected:
-  GlyphID	ligGlyph;		/* GlyphID of ligature to substitute */
-  HeadlessArrayOf<GlyphID>
+  HBGlyphID	ligGlyph;		/* GlyphID of ligature to substitute */
+  HeadlessArrayOf<HBGlyphID>
 		component;		/* Array of component GlyphIDs--start
 					 * with the second  component--ordered
 					 * in writing direction */
@@ -917,9 +921,9 @@
   }
 
   bool serialize (hb_serialize_context_t *c,
-		  hb_array_t<const GlyphID> ligatures,
+		  hb_array_t<const HBGlyphID> ligatures,
 		  hb_array_t<const unsigned int> component_count_list,
-		  hb_array_t<const GlyphID> &component_list /* Starting from second for each ligature */)
+		  hb_array_t<const HBGlyphID> &component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
@@ -1034,11 +1038,11 @@
   }
 
   bool serialize (hb_serialize_context_t *c,
-		  hb_sorted_array_t<const GlyphID> first_glyphs,
+		  hb_sorted_array_t<const HBGlyphID> first_glyphs,
 		  hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
-		  hb_array_t<const GlyphID> ligatures_list,
+		  hb_array_t<const HBGlyphID> ligatures_list,
 		  hb_array_t<const unsigned int> component_count_list,
-		  hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
+		  hb_array_t<const HBGlyphID> component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
@@ -1114,11 +1118,11 @@
 struct LigatureSubst
 {
   bool serialize (hb_serialize_context_t *c,
-		  hb_sorted_array_t<const GlyphID> first_glyphs,
+		  hb_sorted_array_t<const HBGlyphID> first_glyphs,
 		  hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
-		  hb_array_t<const GlyphID> ligatures_list,
+		  hb_array_t<const HBGlyphID> ligatures_list,
 		  hb_array_t<const unsigned int> component_count_list,
-		  hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
+		  hb_array_t<const HBGlyphID> component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
@@ -1195,7 +1199,7 @@
     if (!intersects (c->glyphs)) return;
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
-    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID>> (lookahead);
+    const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (lookahead);
 
     + hb_zip (this+coverage, substitute)
     | hb_filter (*c->glyphs, hb_first)
@@ -1219,7 +1223,7 @@
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!(this+lookahead[i]).add_coverage (c->after))) return;
 
-    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID>> (lookahead);
+    const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (lookahead);
     count = substitute.len;
     c->output->add_array (substitute.arrayZ, substitute.len);
   }
@@ -1239,7 +1243,7 @@
     if (likely (index == NOT_COVERED)) return_trace (false);
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
-    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID>> (lookahead);
+    const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (lookahead);
 
   unsigned int start_index = 0, end_index = 0;
     if (match_backtrack (c,
@@ -1277,7 +1281,7 @@
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
     if (!lookahead.sanitize (c, this))
       return_trace (false);
-    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID>> (lookahead);
+    const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (lookahead);
     return_trace (substitute.sanitize (c));
   }
 
@@ -1294,7 +1298,7 @@
 		lookaheadX;		/* Array of coverage tables
 					 * in lookahead sequence, in glyph
 					 * sequence order */
-  ArrayOf<GlyphID>
+  ArrayOf<HBGlyphID>
 		substituteX;		/* Array of substitute
 					 * GlyphIDs--ordered by Coverage Index */
   public:
@@ -1449,8 +1453,8 @@
 
   bool serialize_single (hb_serialize_context_t *c,
 			 uint32_t lookup_props,
-			 hb_sorted_array_t<const GlyphID> glyphs,
-			 hb_array_t<const GlyphID> substitutes)
+			 hb_sorted_array_t<const HBGlyphID> glyphs,
+			 hb_array_t<const HBGlyphID> substitutes)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
@@ -1460,9 +1464,9 @@
 
   bool serialize_multiple (hb_serialize_context_t *c,
 			   uint32_t lookup_props,
-			   hb_sorted_array_t<const GlyphID> glyphs,
+			   hb_sorted_array_t<const HBGlyphID> glyphs,
 			   hb_array_t<const unsigned int> substitute_len_list,
-			   hb_array_t<const GlyphID> substitute_glyphs_list)
+			   hb_array_t<const HBGlyphID> substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
@@ -1475,9 +1479,9 @@
 
   bool serialize_alternate (hb_serialize_context_t *c,
 			    uint32_t lookup_props,
-			    hb_sorted_array_t<const GlyphID> glyphs,
+			    hb_sorted_array_t<const HBGlyphID> glyphs,
 			    hb_array_t<const unsigned int> alternate_len_list,
-			    hb_array_t<const GlyphID> alternate_glyphs_list)
+			    hb_array_t<const HBGlyphID> alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
@@ -1490,11 +1494,11 @@
 
   bool serialize_ligature (hb_serialize_context_t *c,
 			   uint32_t lookup_props,
-			   hb_sorted_array_t<const GlyphID> first_glyphs,
+			   hb_sorted_array_t<const HBGlyphID> first_glyphs,
 			   hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
-			   hb_array_t<const GlyphID> ligatures_list,
+			   hb_array_t<const HBGlyphID> ligatures_list,
 			   hb_array_t<const unsigned int> component_count_list,
-			   hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
+			   hb_array_t<const HBGlyphID> component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
diff --git a/src/hb-ot-layout-jstf-table.hh b/src/hb-ot-layout-jstf-table.hh
index 1dd31d5..53eb623 100644
--- a/src/hb-ot-layout-jstf-table.hh
+++ b/src/hb-ot-layout-jstf-table.hh
@@ -136,7 +136,7 @@
  * ExtenderGlyphs -- Extender Glyph Table
  */
 
-typedef SortedArrayOf<GlyphID> ExtenderGlyphs;
+typedef SortedArrayOf<HBGlyphID> ExtenderGlyphs;
 
 
 /*
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 8172614..fba3ad1 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -1227,7 +1227,7 @@
  * @glyphs_output: (out): Array of glyphs that would be the substitued output of the lookup
  *
  * Fetches a list of all glyphs affected by the specified lookup in the
- * specified face's GSUB table of GPOS table.
+ * specified face's GSUB table or GPOS table.
  *
  * Since: 0.9.7
  **/
@@ -1949,7 +1949,7 @@
 /**
  * hb_ot_layout_get_baseline:
  * @font: a font
- * @baseline: a baseline tag
+ * @baseline_tag: a baseline tag
  * @direction: text direction.
  * @script_tag:  script tag.
  * @language_tag: language tag.
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index 6d623f5..d40c67a 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -191,7 +191,8 @@
 	  feature_infos[j].max_value = feature_infos[i].max_value;
 	  feature_infos[j].default_value = feature_infos[i].default_value;
 	} else {
-	  feature_infos[j].flags &= ~F_GLOBAL;
+	  if (feature_infos[j].flags & F_GLOBAL)
+	    feature_infos[j].flags ^= F_GLOBAL;
 	  feature_infos[j].max_value = hb_max (feature_infos[j].max_value, feature_infos[i].max_value);
 	  /* Inherit default_value from j */
 	}
diff --git a/src/hb-ot-math-table.hh b/src/hb-ot-math-table.hh
index b4592cc..7529e0c 100644
--- a/src/hb-ot-math-table.hh
+++ b/src/hb-ot-math-table.hh
@@ -423,7 +423,7 @@
   }
 
   protected:
-  GlyphID variantGlyph;       /* Glyph ID for the variant. */
+  HBGlyphID variantGlyph;       /* Glyph ID for the variant. */
   HBUINT16  advanceMeasurement; /* Advance width/height, in design units, of the
 				 * variant, in the direction of requested
 				 * glyph extension. */
@@ -471,7 +471,7 @@
   }
 
   protected:
-  GlyphID   glyph;		  /* Glyph ID for the part. */
+  HBGlyphID   glyph;		  /* Glyph ID for the part. */
   HBUINT16    startConnectorLength; /* Advance width/ height of the straight bar
 				   * connector material, in design units, is at
 				   * the beginning of the glyph, in the
diff --git a/src/hb-ot-maxp-table.hh b/src/hb-ot-maxp-table.hh
index 9945ea6..1c25eda 100644
--- a/src/hb-ot-maxp-table.hh
+++ b/src/hb-ot-maxp-table.hh
@@ -129,7 +129,7 @@
   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]; */
+/*maxpV1Tail	v1Tail[HB_VAR_ARRAY]; */
   public:
   DEFINE_SIZE_STATIC (6);
 };
diff --git a/src/hb-ot-name-table.hh b/src/hb-ot-name-table.hh
index 4a8b57f..84be04c 100644
--- a/src/hb-ot-name-table.hh
+++ b/src/hb-ot-name-table.hh
@@ -191,9 +191,7 @@
 
     const void *dst_string_pool = &(this + this->stringOffset);
 
-    + it
-    | hb_apply ([=] (const NameRecord& _) { c->copy (_, src_string_pool, dst_string_pool); })
-    ;
+    for (const auto &_ : it) c->copy (_, src_string_pool, dst_string_pool);
 
     if (unlikely (c->ran_out_of_room)) return_trace (false);
 
@@ -330,6 +328,9 @@
   DEFINE_SIZE_ARRAY (6, nameRecordZ);
 };
 
+#undef entry_index
+#undef entry_score
+
 struct name_accelerator_t : name::accelerator_t {};
 
 } /* namespace OT */
diff --git a/src/hb-ot-os2-table.hh b/src/hb-ot-os2-table.hh
index 037fcae..f6b1503 100644
--- a/src/hb-ot-os2-table.hh
+++ b/src/hb-ot-os2-table.hh
@@ -134,8 +134,8 @@
     OBLIQUE		= 1u<<9
   };
 
-  bool is_italic () const        { return fsSelection & ITALIC; }
-  bool is_oblique () const       { return fsSelection & OBLIQUE; }
+  bool        is_italic () const { return fsSelection & ITALIC; }
+  bool       is_oblique () const { return fsSelection & OBLIQUE; }
   bool use_typo_metrics () const { return fsSelection & USE_TYPO_METRICS; }
 
   enum width_class_t {
diff --git a/src/hb-ot-post-table.hh b/src/hb-ot-post-table.hh
index fb826cd..38302f5 100644
--- a/src/hb-ot-post-table.hh
+++ b/src/hb-ot-post-table.hh
@@ -262,7 +262,7 @@
 					 * 0x00020000 for version 2.0
 					 * 0x00025000 for version 2.5 (deprecated)
 					 * 0x00030000 for version 3.0 */
-  Fixed		italicAngle;		/* Italic angle in counter-clockwise degrees
+  HBFixed		italicAngle;		/* Italic angle in counter-clockwise degrees
 					 * from the vertical. Zero for upright text,
 					 * negative for text that leans to the right
 					 * (forward). */
diff --git a/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh
index ecf3450..2a7a8eb 100644
--- a/src/hb-ot-shape-complex-arabic-fallback.hh
+++ b/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -49,8 +49,8 @@
 					  hb_font_t *font,
 					  unsigned int feature_index)
 {
-  OT::GlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1];
-  OT::GlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1];
+  OT::HBGlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1];
+  OT::HBGlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1];
   unsigned int num_glyphs = 0;
 
   /* Populate arrays */
@@ -78,7 +78,7 @@
   /* Bubble-sort or something equally good!
    * May not be good-enough for presidential candidate interviews, but good-enough for us... */
   hb_stable_sort (&glyphs[0], num_glyphs,
-		  (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::GlyphID::cmp,
+		  (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::HBGlyphID::cmp,
 		  &substitutes[0]);
 
 
@@ -99,15 +99,15 @@
 arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UNUSED,
 					    hb_font_t *font)
 {
-  OT::GlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)];
+  OT::HBGlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)];
   unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)];
   unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)];
   unsigned int num_first_glyphs = 0;
 
   /* We know that all our ligatures are 2-component */
-  OT::GlyphID ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)];
+  OT::HBGlyphID ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)];
   unsigned int component_count_list[ARRAY_LENGTH_CONST (ligature_list)];
-  OT::GlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */];
+  OT::HBGlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */];
   unsigned int num_ligatures = 0;
 
   /* Populate arrays */
@@ -125,7 +125,7 @@
     num_first_glyphs++;
   }
   hb_stable_sort (&first_glyphs[0], num_first_glyphs,
-		  (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::GlyphID::cmp,
+		  (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::HBGlyphID::cmp,
 		  &first_glyphs_indirection[0]);
 
   /* Now that the first-glyphs are sorted, walk again, populate ligatures. */
@@ -230,8 +230,8 @@
     return false;
 
   const Manifest &manifest = reinterpret_cast<const Manifest&> (arabic_win1256_gsub_lookups.manifest);
-  static_assert (sizeof (arabic_win1256_gsub_lookups.manifestData) / sizeof (ManifestLookup)
-		 <= ARABIC_FALLBACK_MAX_LOOKUPS, "");
+  static_assert (sizeof (arabic_win1256_gsub_lookups.manifestData) ==
+		 ARABIC_FALLBACK_MAX_LOOKUPS * sizeof (ManifestLookup), "");
   /* TODO sanitize the table? */
 
   unsigned j = 0;
diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc
index dad1dba..fc3490d 100644
--- a/src/hb-ot-shape-complex-myanmar.cc
+++ b/src/hb-ot-shape-complex-myanmar.cc
@@ -116,8 +116,8 @@
 
 static void
 setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		   hb_buffer_t              *buffer,
-		   hb_font_t                *font HB_UNUSED)
+		     hb_buffer_t              *buffer,
+		     hb_font_t                *font HB_UNUSED)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_category);
   HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_position);
diff --git a/src/hb-ot-stat-table.hh b/src/hb-ot-stat-table.hh
index b5ea604..2cdd3a4 100644
--- a/src/hb-ot-stat-table.hh
+++ b/src/hb-ot-stat-table.hh
@@ -77,7 +77,7 @@
   NameID	valueNameID;	/* The name ID for entries in the 'name' table
 				 * that provide a display string for this
 				 * attribute value. */
-  Fixed		value;		/* A numeric value for this attribute value. */
+  HBFixed		value;		/* A numeric value for this attribute value. */
   public:
   DEFINE_SIZE_STATIC (12);
 };
@@ -102,10 +102,10 @@
   NameID	valueNameID;	/* The name ID for entries in the 'name' table
 				 * that provide a display string for this
 				 * attribute value. */
-  Fixed		nominalValue;	/* A numeric value for this attribute value. */
-  Fixed		rangeMinValue;	/* The minimum value for a range associated
+  HBFixed		nominalValue;	/* A numeric value for this attribute value. */
+  HBFixed		rangeMinValue;	/* The minimum value for a range associated
 				 * with the specified name ID. */
-  Fixed		rangeMaxValue;	/* The maximum value for a range associated
+  HBFixed		rangeMaxValue;	/* The maximum value for a range associated
 				 * with the specified name ID. */
   public:
   DEFINE_SIZE_STATIC (20);
@@ -131,8 +131,8 @@
   NameID	valueNameID;	/* The name ID for entries in the 'name' table
 				 * that provide a display string for this
 				 * attribute value. */
-  Fixed		value;		/* A numeric value for this attribute value. */
-  Fixed		linkedValue;	/* The numeric value for a style-linked mapping
+  HBFixed		value;		/* A numeric value for this attribute value. */
+  HBFixed		linkedValue;	/* The numeric value for a style-linked mapping
 				 * from this value. */
   public:
   DEFINE_SIZE_STATIC (16);
@@ -150,7 +150,7 @@
   HBUINT16	axisIndex;	/* Zero-base index into the axis record array
 				 * identifying the axis to which this value
 				 * applies. Must be less than designAxisCount. */
-  Fixed		value;		/* A numeric value for this attribute value. */
+  HBFixed		value;		/* A numeric value for this attribute value. */
   public:
   DEFINE_SIZE_STATIC (6);
 };
diff --git a/src/hb-ot-var-fvar-table.hh b/src/hb-ot-var-fvar-table.hh
index 3d30d7a..7ce3123 100644
--- a/src/hb-ot-var-fvar-table.hh
+++ b/src/hb-ot-var-fvar-table.hh
@@ -44,7 +44,7 @@
 {
   friend struct fvar;
 
-  hb_array_t<const Fixed> get_coordinates (unsigned int axis_count) const
+  hb_array_t<const HBFixed> get_coordinates (unsigned int axis_count) const
   { return coordinatesZ.as_array (axis_count); }
 
   bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const
@@ -58,7 +58,7 @@
   NameID	subfamilyNameID;/* The name ID for entries in the 'name' table
 				 * that provide subfamily names for this instance. */
   HBUINT16	flags;		/* Reserved for future use — set to 0. */
-  UnsizedArrayOf<Fixed>
+  UnsizedArrayOf<HBFixed>
 		coordinatesZ;	/* The coordinates array for this instance. */
   //NameID	postScriptNameIDX;/*Optional. The name ID for entries in the 'name'
   //				  * table that provide PostScript names for this
@@ -83,9 +83,9 @@
 
   public:
   Tag		axisTag;	/* Tag identifying the design variation for the axis. */
-  Fixed		minValue;	/* The minimum coordinate value for the axis. */
-  Fixed		defaultValue;	/* The default coordinate value for the axis. */
-  Fixed		maxValue;	/* The maximum coordinate value for the axis. */
+  HBFixed		minValue;	/* The minimum coordinate value for the axis. */
+  HBFixed		defaultValue;	/* The default coordinate value for the axis. */
+  HBFixed		maxValue;	/* The maximum coordinate value for the axis. */
   HBUINT16	flags;		/* Axis flags. */
   NameID	axisNameID;	/* The name ID for entries in the 'name' table that
 				 * provide a display name for this axis. */
@@ -286,7 +286,7 @@
 
     if (coords_length && *coords_length)
     {
-      hb_array_t<const Fixed> instanceCoords = instance->get_coordinates (axisCount)
+      hb_array_t<const HBFixed> instanceCoords = instance->get_coordinates (axisCount)
 							 .sub_array (0, *coords_length);
       for (unsigned int i = 0; i < instanceCoords.length; i++)
 	coords[i] = instanceCoords.arrayZ[i].to_float ();
@@ -340,8 +340,8 @@
   HBUINT16	instanceCount;	/* The number of named instances defined in the font
 				 * (the number of records in the instances array). */
   HBUINT16	instanceSize;	/* The size in bytes of each InstanceRecord — set
-				 * to either axisCount * sizeof(Fixed) + 4, or to
-				 * axisCount * sizeof(Fixed) + 6. */
+				 * to either axisCount * sizeof(HBFixed) + 4, or to
+				 * axisCount * sizeof(HBFixed) + 6. */
 
   public:
   DEFINE_SIZE_STATIC (16);
diff --git a/src/hb-ot-vorg-table.hh b/src/hb-ot-vorg-table.hh
index e1b25f9..a4d6b06 100644
--- a/src/hb-ot-vorg-table.hh
+++ b/src/hb-ot-vorg-table.hh
@@ -48,7 +48,7 @@
   }
 
   public:
-  GlyphID	glyph;
+  HBGlyphID	glyph;
   FWORD		vertOriginY;
 
   public:
@@ -84,9 +84,7 @@
     this->defaultVertOriginY = defaultVertOriginY;
     this->vertYOrigins.len = it.len ();
 
-    + it
-    | hb_apply ([c] (const VertOriginMetric& _) { c->copy (_); })
-    ;
+    for (const auto _ : it) c->copy (_);
   }
 
   bool subset (hb_subset_context_t *c) const
diff --git a/src/hb-serialize.hh b/src/hb-serialize.hh
index 5764a90..4c674b1 100644
--- a/src/hb-serialize.hh
+++ b/src/hb-serialize.hh
@@ -91,9 +91,7 @@
 
   void fini ()
   {
-    ++ hb_iter (packed)
-    | hb_apply ([] (object_t *_) { _->fini (); })
-    ;
+    for (object_t *_ : ++hb_iter (packed)) _->fini ();
     packed.fini ();
     this->packed_map.fini ();
 
@@ -194,6 +192,7 @@
     if (unlikely (!obj)) return;
     current = current->next;
     revert (*obj);
+    obj->fini ();
     object_pool.free (obj);
   }
   objidx_t pop_pack ()
@@ -291,7 +290,6 @@
     assert (packed.length > 1);
 
     for (const object_t* parent : ++hb_iter (packed))
-    {
       for (const object_t::link_t &link : parent->links)
       {
 	const object_t* child = packed[link.objidx];
@@ -311,7 +309,6 @@
 	  check_assign (off, offset);
 	}
       }
-    }
   }
 
   unsigned int length () const { return this->head - current->head; }
@@ -353,9 +350,7 @@
 
   template <typename Type>
   Type *allocate_min ()
-  {
-    return this->allocate_size<Type> (Type::min_size);
-  }
+  { return this->allocate_size<Type> (Type::min_size); }
 
   template <typename Type>
   Type *embed (const Type *obj)
@@ -427,14 +422,12 @@
     /* Copy both items from head side and tail side... */
     unsigned int len = (this->head - this->start)
 		     + (this->end  - this->tail);
+
     char *p = (char *) malloc (len);
-    if (p)
-    {
-      memcpy (p, this->start, this->head - this->start);
-      memcpy (p + (this->head - this->start), this->tail, this->end - this->tail);
-    }
-    else
-      return hb_bytes_t ();
+    if (unlikely (!p)) return hb_bytes_t ();
+
+    memcpy (p, this->start, this->head - this->start);
+    memcpy (p + (this->head - this->start), this->tail, this->end - this->tail);
     return hb_bytes_t (p, len);
   }
   template <typename Type>
diff --git a/src/hb-set.hh b/src/hb-set.hh
index 6e1295d..36d11c0 100644
--- a/src/hb-set.hh
+++ b/src/hb-set.hh
@@ -136,12 +136,17 @@
       unsigned int j = m & ELT_MASK;
 
       const elt_t vv = v[i] & ((elt_t (1) << (j + 1)) - 1);
-      for (const elt_t *p = &vv; (int) i >= 0; p = &v[--i])
+      const elt_t *p = &vv;
+      while (true)
+      {
 	if (*p)
 	{
 	  *codepoint = i * ELT_BITS + elt_get_max (*p);
 	  return true;
 	}
+	if ((int) i <= 0) break;
+	p = &v[--i];
+      }
 
       *codepoint = INVALID;
       return false;
diff --git a/src/hb-string-array.hh b/src/hb-string-array.hh
index c4cf666..1c67ab4 100644
--- a/src/hb-string-array.hh
+++ b/src/hb-string-array.hh
@@ -48,7 +48,7 @@
 #include HB_STRING_ARRAY_LIST
 #undef _S
   } st;
-  char str[VAR];
+  char str[HB_VAR_ARRAY];
 }
 HB_STRING_ARRAY_POOL_NAME =
 {
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 1387e0a..6559113 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -39,7 +39,7 @@
 		       hb_codepoint_t gid,
 		       hb_set_t *gids_to_retain)
 {
-  if (hb_set_has (gids_to_retain, gid))
+  if (gids_to_retain->has (gid))
     // Already visited this gid, ignore.
     return;
 
@@ -88,6 +88,14 @@
 #endif
 
 static inline void
+_cmap_closure (hb_face_t           *face,
+	       const hb_set_t      *unicodes,
+	       hb_set_t            *glyphset)
+{
+  face->table.cmap->table->closure_glyphs (unicodes, glyphset);
+}
+
+static inline void
 _remove_invalid_gids (hb_set_t *glyphs,
 		      unsigned int num_glyphs)
 {
@@ -129,6 +137,8 @@
     plan->_glyphset_gsub->add (gid);
   }
 
+  _cmap_closure (plan->source, plan->unicodes, plan->_glyphset_gsub);
+
 #ifndef HB_NO_SUBSET_LAYOUT
   if (close_over_gsub)
     // Add all glyphs needed for GSUB substitutions.
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 827784f..f812444 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -249,11 +249,6 @@
 static bool
 _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag)
 {
-  if (!tag)
-    /* Drop tables with no tag as that means table header in
-       _hb_face_builder_reference_table */
-    return true;
-
   if (plan->drop_tables->has (tag))
     return true;
 
@@ -301,17 +296,19 @@
   hb_tag_t table_tags[32];
   unsigned int offset = 0, count;
   bool success = true;
+  hb_set_t tags_set;
   do {
     count = ARRAY_LENGTH (table_tags);
     hb_face_get_table_tags (source, offset, &count, table_tags);
     for (unsigned int i = 0; i < count; i++)
     {
       hb_tag_t tag = table_tags[i];
-      if (_should_drop_table (plan, tag))
+      if (_should_drop_table (plan, tag) && !tags_set.has (tag))
       {
 	DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG (tag));
 	continue;
       }
+      tags_set.add (tag);
       success = success && _subset_table (plan, tag);
     }
     offset += count;
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index 9a81840..08a4054 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -270,9 +270,9 @@
  **/
 hb_bool_t
 hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs,
-			        hb_user_data_key_t *key,
-			        void *              data,
-			        hb_destroy_func_t   destroy,
+				hb_user_data_key_t *key,
+				void *              data,
+				hb_destroy_func_t   destroy,
 				hb_bool_t           replace)
 {
   return hb_object_set_user_data (ufuncs, key, data, destroy, replace);
@@ -291,7 +291,7 @@
  **/
 void *
 hb_unicode_funcs_get_user_data (hb_unicode_funcs_t *ufuncs,
-			        hb_user_data_key_t *key)
+				hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (ufuncs, key);
 }
diff --git a/src/hb-unicode.h b/src/hb-unicode.h
index df0b91f..61b1b0b 100644
--- a/src/hb-unicode.h
+++ b/src/hb-unicode.h
@@ -200,15 +200,15 @@
 
 HB_EXTERN hb_bool_t
 hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs,
-			        hb_user_data_key_t *key,
-			        void *              data,
-			        hb_destroy_func_t   destroy,
+				hb_user_data_key_t *key,
+				void *              data,
+				hb_destroy_func_t   destroy,
 				hb_bool_t           replace);
 
 
 HB_EXTERN void *
 hb_unicode_funcs_get_user_data (hb_unicode_funcs_t *ufuncs,
-			        hb_user_data_key_t *key);
+				hb_user_data_key_t *key);
 
 
 HB_EXTERN void
@@ -260,7 +260,7 @@
  * @user_data:
  * @destroy:
  *
- * 
+ *
  *
  * Since: 0.9.2
  **/
@@ -276,7 +276,7 @@
  * @user_data:
  * @destroy:
  *
- * 
+ *
  *
  * Since: 0.9.2
  **/
@@ -292,7 +292,7 @@
  * @user_data:
  * @destroy:
  *
- * 
+ *
  *
  * Since: 0.9.2
  **/
@@ -308,7 +308,7 @@
  * @user_data:
  * @destroy:
  *
- * 
+ *
  *
  * Since: 0.9.2
  **/
@@ -324,7 +324,7 @@
  * @user_data:
  * @destroy:
  *
- * 
+ *
  *
  * Since: 0.9.2
  **/
@@ -340,7 +340,7 @@
  * @user_data:
  * @destroy:
  *
- * 
+ *
  *
  * Since: 0.9.2
  **/
diff --git a/src/hb-vector.hh b/src/hb-vector.hh
index 288a541..7b150fb 100644
--- a/src/hb-vector.hh
+++ b/src/hb-vector.hh
@@ -302,8 +302,8 @@
   { return as_array ().bsearch (x, not_found); }
   template <typename T>
   bool bfind (const T &x, unsigned int *i = nullptr,
-		     hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
-		     unsigned int to_store = (unsigned int) -1) const
+	      hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
+	      unsigned int to_store = (unsigned int) -1) const
   { return as_array ().bfind (x, i, not_found, to_store); }
 };
 
diff --git a/src/hb-version.h b/src/hb-version.h
index 18b3c2c..4081ca8 100644
--- a/src/hb-version.h
+++ b/src/hb-version.h
@@ -38,9 +38,9 @@
 
 #define HB_VERSION_MAJOR 2
 #define HB_VERSION_MINOR 6
-#define HB_VERSION_MICRO 1
+#define HB_VERSION_MICRO 2
 
-#define HB_VERSION_STRING "2.6.1"
+#define HB_VERSION_STRING "2.6.2"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
diff --git a/src/hb.hh b/src/hb.hh
index 0e7b890..f316512 100644
--- a/src/hb.hh
+++ b/src/hb.hh
@@ -180,7 +180,6 @@
 #include <stddef.h>
 #include <string.h>
 #include <assert.h>
-#include <errno.h>
 #include <stdio.h>
 #include <stdarg.h>
 
@@ -242,7 +241,7 @@
 #define HB_CONST_FUNC
 #define HB_PRINTF_FUNC(format_idx, arg_idx)
 #endif
-#if defined(__GNUC__) && (__GNUC__ >= 4)
+#if defined(__GNUC__) && (__GNUC__ >= 4) || (__clang__)
 #define HB_UNUSED	__attribute__((unused))
 #elif defined(_MSC_VER) /* https://github.com/harfbuzz/harfbuzz/issues/635 */
 #define HB_UNUSED __pragma(warning(suppress: 4100 4101))
@@ -355,7 +354,7 @@
 #    endif
 #    if _WIN32_WCE < 0x800
 #      define HB_NO_SETLOCALE
-static int errno = 0; /* Use something better? */
+#      define HB_NO_ERRNO
 #    endif
 #  elif defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
 #    ifndef HB_NO_GETENV
@@ -371,6 +370,12 @@
 #define getenv(Name) nullptr
 #endif
 
+#ifdef HB_NO_ERRNO
+static int errno = 0; /* Use something better? */
+#else
+#include <errno.h>
+#endif
+
 #if defined(HAVE_ATEXIT) && !defined(HB_USE_ATEXIT)
 /* atexit() is only safe to be called from shared libraries on certain
  * platforms.  Whitelist.
@@ -475,7 +480,18 @@
 
 
 /* Size signifying variable-sized array */
-#define VAR 1
+#ifndef HB_VAR_ARRAY
+#define HB_VAR_ARRAY 1
+#endif
+
+static inline double
+_hb_roundf (float x)
+{
+  return x >= 0 ? floor ((double) x + .5) : ceil ((double) x - .5);
+}
+#ifndef HAVE_ROUNDF
+#define roundf(x) _hb_roundf(x)
+#endif
 
 /* Endian swap, used in Windows related backends */
 static inline uint16_t hb_uint16_swap (const uint16_t v)
@@ -585,9 +601,10 @@
  * them directly.*/
 #include "hb-meta.hh"
 #include "hb-mutex.hh"
+#include "hb-number.hh"
 #include "hb-atomic.hh"	// Requires: hb-meta
 #include "hb-null.hh"	// Requires: hb-meta
-#include "hb-algs.hh"	// Requires: hb-meta hb-null
+#include "hb-algs.hh"	// Requires: hb-meta hb-null hb-number
 #include "hb-iter.hh"	// Requires: hb-algs hb-meta
 #include "hb-debug.hh"	// Requires: hb-algs hb-atomic
 #include "hb-array.hh"	// Requires: hb-algs hb-iter hb-null
diff --git a/src/main.cc b/src/main.cc
index 0aa4a5b..983cb55 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -45,7 +45,8 @@
 int
 main (int argc, char **argv)
 {
-  if (argc != 2) {
+  if (argc != 2)
+  {
     fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]);
     exit (1);
   }
@@ -65,7 +66,8 @@
   const OpenTypeFontFile& ot = *sanitized;
 
 
-  switch (ot.get_tag ()) {
+  switch (ot.get_tag ())
+  {
   case OpenTypeFontFile::TrueTypeTag:
     printf ("OpenType font with TrueType outlines\n");
     break;
@@ -91,20 +93,23 @@
 
   int num_fonts = ot.get_face_count ();
   printf ("%d font(s) found in file\n", num_fonts);
-  for (int n_font = 0; n_font < num_fonts; n_font++) {
+  for (int n_font = 0; n_font < num_fonts; n_font++)
+  {
     const OpenTypeFontFace &font = ot.get_face (n_font);
     printf ("Font %d of %d:\n", n_font, num_fonts);
 
     int num_tables = font.get_table_count ();
     printf ("  %d table(s) found in font\n", num_tables);
-    for (int n_table = 0; n_table < num_tables; n_table++) {
+    for (int n_table = 0; n_table < num_tables; n_table++)
+    {
       const OpenTypeTable &table = font.get_table (n_table);
       printf ("  Table %2d of %2d: %.4s (0x%08x+0x%08x)\n", n_table, num_tables,
 	      (const char *) table.tag,
 	      (unsigned int) table.offset,
 	      (unsigned int) table.length);
 
-      switch (table.tag) {
+      switch (table.tag)
+      {
 
       case HB_OT_TAG_GSUB:
       case HB_OT_TAG_GPOS:
@@ -114,10 +119,11 @@
 
 	int num_scripts = g.get_script_count ();
 	printf ("    %d script(s) found in table\n", num_scripts);
-	for (int n_script = 0; n_script < num_scripts; n_script++) {
+	for (int n_script = 0; n_script < num_scripts; n_script++)
+	{
 	  const Script &script = g.get_script (n_script);
 	  printf ("    Script %2d of %2d: %.4s\n", n_script, num_scripts,
-	          (const char *)g.get_script_tag(n_script));
+		  (const char *)g.get_script_tag(n_script));
 
 	  if (!script.has_default_lang_sys())
 	    printf ("      No default language system\n");
@@ -140,34 +146,37 @@
 
 	    int num_features = langsys.get_feature_count ();
 	    printf ("        %d feature(s) found in language system\n", num_features);
-	    for (int n_feature = 0; n_feature < num_features; n_feature++) {
+	    for (int n_feature = 0; n_feature < num_features; n_feature++)
+	    {
 	      printf ("        Feature index %2d of %2d: %d\n", n_feature, num_features,
-	              langsys.get_feature_index (n_feature));
+		      langsys.get_feature_index (n_feature));
 	    }
 	  }
 	}
 
 	int num_features = g.get_feature_count ();
 	printf ("    %d feature(s) found in table\n", num_features);
-	for (int n_feature = 0; n_feature < num_features; n_feature++) {
+	for (int n_feature = 0; n_feature < num_features; n_feature++)
+	{
 	  const Feature &feature = g.get_feature (n_feature);
 	  int num_lookups = feature.get_lookup_count ();
 	  printf ("    Feature %2d of %2d: %c%c%c%c\n", n_feature, num_features,
-	          HB_UNTAG(g.get_feature_tag(n_feature)));
+		  HB_UNTAG(g.get_feature_tag(n_feature)));
 
 	  printf ("        %d lookup(s) found in feature\n", num_lookups);
 	  for (int n_lookup = 0; n_lookup < num_lookups; n_lookup++) {
 	    printf ("        Lookup index %2d of %2d: %d\n", n_lookup, num_lookups,
-	            feature.get_lookup_index (n_lookup));
+		    feature.get_lookup_index (n_lookup));
 	  }
 	}
 
 	int num_lookups = g.get_lookup_count ();
 	printf ("    %d lookup(s) found in table\n", num_lookups);
-	for (int n_lookup = 0; n_lookup < num_lookups; n_lookup++) {
+	for (int n_lookup = 0; n_lookup < num_lookups; n_lookup++)
+	{
 	  const Lookup &lookup = g.get_lookup (n_lookup);
 	  printf ("    Lookup %2d of %2d: type %d, props 0x%04X\n", n_lookup, num_lookups,
-	          lookup.get_type(), lookup.get_props());
+		  lookup.get_type(), lookup.get_props());
 	}
 
 	}
diff --git a/src/test-algs.cc b/src/test-algs.cc
index 333a892..f8b8ff6 100644
--- a/src/test-algs.cc
+++ b/src/test-algs.cc
@@ -87,5 +87,9 @@
   assert (hb_add (2) (5) == 7);
   assert (hb_add (5) (2) == 7);
 
+  x = 1;
+  assert (++hb_inc (x) == 3);
+  assert (x == 3);
+
   return 0;
 }
diff --git a/src/test-iter.cc b/src/test-iter.cc
index 156a5fc..9c83171 100644
--- a/src/test-iter.cc
+++ b/src/test-iter.cc
@@ -234,7 +234,7 @@
 	    {
 	      hb_set_t *set = hb_set_create ();
 	      for (unsigned int i = 0; i < temp1; ++i)
-	        hb_set_add (set, i);
+		hb_set_add (set, i);
 	      temp1++;
 	      return set;
 	    })
@@ -267,7 +267,12 @@
   hb_iota ();
   hb_iota (3);
   hb_iota (3, 2);
+  assert ((&vl) + 1 == *++hb_iota (&vl, hb_inc));
   hb_range ();
+  hb_repeat (7u);
+  hb_repeat (nullptr);
+  hb_repeat (vl) | hb_chop (3);
+  assert (hb_len (hb_range (10) | hb_take (3)) == 3);
   assert (hb_range (9).len () == 9);
   assert (hb_range (2, 9).len () == 7);
   assert (hb_range (2, 9, 3).len () == 3);
diff --git a/src/test-number.cc b/src/test-number.cc
new file mode 100644
index 0000000..3591b13
--- /dev/null
+++ b/src/test-number.cc
@@ -0,0 +1,253 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ */
+
+#include "hb.hh"
+#include "hb-number.hh"
+#include "hb-number-parser.hh"
+
+
+int
+main (int argc, char **argv)
+{
+  {
+    const char str[] = "123";
+    const char *pp = str;
+    const char *end = str + 3;
+
+    int pv;
+    assert (hb_parse_int (&pp, end, &pv));
+    assert (pv == 123);
+    assert (pp - str == 3);
+    assert (end - pp == 0);
+    assert (!*end);
+  }
+
+  {
+    const char str[] = "123";
+    const char *pp = str;
+    const char *end = str + strlen (str);
+
+    unsigned int pv;
+    assert (hb_parse_uint (&pp, end, &pv));
+    assert (pv == 123);
+    assert (pp - str == 3);
+    assert (end - pp == 0);
+    assert (!*end);
+  }
+
+  {
+    const char str[] = "12F";
+    const char *pp = str;
+    const char *end = str + 3;
+
+    unsigned int pv;
+    assert (hb_parse_uint (&pp, end, &pv, true, 16));
+    assert (pv == 0x12F);
+    assert (pp - str == 3);
+    assert (end - pp == 0);
+    assert (!*end);
+  }
+
+  {
+    const char str[] = "12Fq";
+    const char *pp = str;
+    const char *end = str + 4;
+
+    unsigned int pv;
+    assert (!hb_parse_uint (&pp, end, &pv, true, 16));
+    assert (hb_parse_uint (&pp, end, &pv, false, 16));
+    assert (pv == 0x12F);
+    assert (pp - str == 3);
+    assert (end - pp == 1);
+    assert (!*end);
+  }
+
+  {
+    const char str[] = "-123";
+    const char *pp = str;
+    const char *end = str + 4;
+
+    int pv;
+    assert (hb_parse_int (&pp, end, &pv));
+    assert (pv == -123);
+    assert (pp - str == 4);
+    assert (end - pp == 0);
+    assert (!*end);
+  }
+
+  {
+    const char str[] = "123";
+    const char *pp = str;
+    assert (ARRAY_LENGTH (str) == 4);
+    const char *end = str + ARRAY_LENGTH (str);
+
+    unsigned int pv;
+    assert (hb_parse_uint (&pp, end, &pv));
+    assert (pv == 123);
+    assert (pp - str == 3);
+    assert (end - pp == 1);
+  }
+
+  {
+    const char str[] = "123\0";
+    const char *pp = str;
+    assert (ARRAY_LENGTH (str) == 5);
+    const char *end = str + ARRAY_LENGTH (str);
+
+    unsigned int pv;
+    assert (hb_parse_uint (&pp, end, &pv));
+    assert (pv == 123);
+    assert (pp - str == 3);
+    assert (end - pp == 2);
+  }
+
+  {
+    const char str[] = "123V";
+    const char *pp = str;
+    assert (ARRAY_LENGTH (str) == 5);
+    const char *end = str + ARRAY_LENGTH (str);
+
+    unsigned int pv;
+    assert (hb_parse_uint (&pp, end, &pv));
+    assert (pv == 123);
+    assert (pp - str == 3);
+    assert (end - pp == 2);
+  }
+
+  {
+    const char str[] = ".123";
+    const char *pp = str;
+    const char *end = str + ARRAY_LENGTH (str);
+
+    double pv;
+    assert (hb_parse_double (&pp, end, &pv));
+    assert ((int) roundf (pv * 1000.) == 123);
+    assert (pp - str == 4);
+    assert (end - pp == 1);
+
+    /* Test strtod_rl even if libc's strtod_l is used */
+    char *pend;
+    assert ((int) roundf (strtod_rl (str, &pend) * 1000.) == 123);
+    assert (pend - str == 4);
+  }
+
+  {
+    const char str[] = "0.123";
+    const char *pp = str;
+    const char *end = str + ARRAY_LENGTH (str) - 1;
+
+    double pv;
+    assert (hb_parse_double (&pp, end, &pv));
+    assert ((int) roundf (pv * 1000.) == 123);
+    assert (pp - str == 5);
+    assert (end - pp == 0);
+
+    char *pend;
+    assert ((int) roundf (strtod_rl (str, &pend) * 1000.) == 123);
+    assert (pend - str == 5);
+  }
+
+  {
+    const char str[] = "0.123e0";
+    const char *pp = str;
+    const char *end = str + ARRAY_LENGTH (str) - 1;
+
+    double pv;
+    assert (hb_parse_double (&pp, end, &pv));
+    assert ((int) roundf (pv * 1000.) == 123);
+    assert (pp - str == 7);
+    assert (end - pp == 0);
+
+    char *pend;
+    assert ((int) roundf (strtod_rl (str, &pend) * 1000.) == 123);
+    assert (pend - str == 7);
+  }
+
+  {
+    const char str[] = "123e-3";
+    const char *pp = str;
+    const char *end = str + ARRAY_LENGTH (str) - 1;
+
+    double pv;
+    assert (hb_parse_double (&pp, end, &pv));
+    assert ((int) roundf (pv * 1000.) == 123);
+    assert (pp - str == 6);
+    assert (end - pp == 0);
+
+    char *pend;
+    assert ((int) roundf (strtod_rl (str, &pend) * 1000.) == 123);
+    assert (pend - str == 6);
+  }
+
+  {
+    const char str[] = ".000123e+3";
+    const char *pp = str;
+    const char *end = str + ARRAY_LENGTH (str) - 1;
+
+    double pv;
+    assert (hb_parse_double (&pp, end, &pv));
+    assert ((int) roundf (pv * 1000.) == 123);
+    assert (pp - str == 10);
+    assert (end - pp == 0);
+
+    char *pend;
+    assert ((int) roundf (strtod_rl (str, &pend) * 1000.) == 123);
+    assert (pend - str == 10);
+  }
+
+  {
+    const char str[] = "-.000000123e6";
+    const char *pp = str;
+    const char *end = str + ARRAY_LENGTH (str) - 1;
+
+    double pv;
+    assert (hb_parse_double (&pp, end, &pv));
+    assert ((int) roundf (pv * 1000.) == -123);
+    assert (pp - str == 13);
+    assert (end - pp == 0);
+
+    char *pend;
+    assert ((int) roundf (strtod_rl (str, &pend) * 1000.) == -123);
+    assert (pend - str == 13);
+  }
+
+  {
+    const char str[] = "-1.23E-1";
+    const char *pp = str;
+    const char *end = str + ARRAY_LENGTH (str) - 1;
+
+    double pv;
+    assert (hb_parse_double (&pp, end, &pv));
+    assert ((int) roundf (pv * 1000.) == -123);
+    assert (pp - str == 8);
+    assert (end - pp == 0);
+
+    char *pend;
+    assert ((int) roundf (strtod_rl (str, &pend) * 1000.) == -123);
+    assert (pend - str == 8);
+  }
+
+  return 0;
+}
diff --git a/test/api/test-ot-tag.c b/test/api/test-ot-tag.c
index d0334cd..958fd6b 100644
--- a/test/api/test-ot-tag.c
+++ b/test/api/test-ot-tag.c
@@ -351,9 +351,6 @@
   test_tag_from_language ("ZHH", "yue-Hant");
   test_tag_from_language ("ZHS", "yue-Hans");
 
-  test_tag_from_language ("ZHS", "zh"); /* Chinese */
-  test_tag_from_language ("ZHS", "zh-xx");
-
   test_language_two_way ("ABC", "abc");
   test_language_two_way ("ABCD", "x-hbotabcd");
   test_tag_from_language ("ABC", "asdf-asdf-wer-x-hbotabc-zxc");
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5728664968232960 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5728664968232960
new file mode 100644
index 0000000..e099413
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5728664968232960
Binary files differ
diff --git a/test/fuzzing/run-shape-fuzzer-tests.py b/test/fuzzing/run-shape-fuzzer-tests.py
index 6d36c2c..a2f8b5c 100755
--- a/test/fuzzing/run-shape-fuzzer-tests.py
+++ b/test/fuzzing/run-shape-fuzzer-tests.py
@@ -39,7 +39,12 @@
 			timer.start()
 			p.wait ()
 			tempf.seek (0)
-			text = tempf.read ().decode ("utf-8").strip ()
+			text = tempf.read ()
+
+			#TODO: Detect debug mode with a better way
+			is_debug_mode = b"SANITIZE" in text
+
+			text = "" if is_debug_mode else text.decode ("utf-8").strip ()
 			returncode = p.returncode
 		finally:
 			timer.cancel()
diff --git a/test/fuzzing/run-subset-fuzzer-tests.py b/test/fuzzing/run-subset-fuzzer-tests.py
index 820519f..297398c 100755
--- a/test/fuzzing/run-subset-fuzzer-tests.py
+++ b/test/fuzzing/run-subset-fuzzer-tests.py
@@ -39,7 +39,12 @@
 			timer.start()
 			p.wait ()
 			tempf.seek (0)
-			text = tempf.read().decode ("utf-8").strip ()
+			text = tempf.read ()
+
+			#TODO: Detect debug mode with a better way
+			is_debug_mode = b"SANITIZE" in text
+
+			text = "" if is_debug_mode else text.decode ("utf-8").strip ()
 			returncode = p.returncode
 		finally:
 			timer.cancel()
diff --git a/test/subset/Makefile.am b/test/subset/Makefile.am
index 1673cfb..da4b3d4 100644
--- a/test/subset/Makefile.am
+++ b/test/subset/Makefile.am
@@ -6,7 +6,8 @@
 SUBDIRS = data
 
 # Convenience targets:
-lib:
+lib: libs # Always build subsetter lib in this subdir
+libs:
 	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src libs
 
 EXTRA_DIST += \
diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am
index 1e90367..0b27452 100644
--- a/test/subset/data/Makefile.am
+++ b/test/subset/data/Makefile.am
@@ -14,6 +14,8 @@
 	expected/cff-japanese \
 	expected/layout \
 	expected/layout.gpos \
+	expected/layout.gpos3 \
+	expected/cmap14 \
 	fonts \
 	profiles \
 	$(NULL)
diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources
index 8bacc51..c6a3042 100644
--- a/test/subset/data/Makefile.sources
+++ b/test/subset/data/Makefile.sources
@@ -6,6 +6,8 @@
 	tests/cff-japanese.tests \
 	tests/layout.tests \
 	tests/layout.gpos.tests \
+	tests/layout.gpos3.tests \
+	tests/cmap14.tests \
 	$(NULL)
 
 XFAIL_TESTS = \
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.default.4E00,4E02,4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.default.4E00,4E02,4E03.otf
new file mode 100644
index 0000000..fb41408
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.default.4E00,4E02,4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.default.4E00,4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.default.4E00,4E03.otf
new file mode 100644
index 0000000..e50256d
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.default.4E00,4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.default.4E00,4E05,4E07.otf b/test/subset/data/expected/cmap14/cmap14_font1.default.4E00,4E05,4E07.otf
new file mode 100644
index 0000000..24f3871
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.default.4E00,4E05,4E07.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.default.4E02,4E03,4E08.otf b/test/subset/data/expected/cmap14/cmap14_font1.default.4E02,4E03,4E08.otf
new file mode 100644
index 0000000..38672ba
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.default.4E02,4E03,4E08.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.default.4E02.otf b/test/subset/data/expected/cmap14/cmap14_font1.default.4E02.otf
new file mode 100644
index 0000000..c5f898e
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.default.4E02.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.default.4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.default.4E03.otf
new file mode 100644
index 0000000..03cae07
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.default.4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.default.4E05,4E07,4E08,4E09.otf b/test/subset/data/expected/cmap14/cmap14_font1.default.4E05,4E07,4E08,4E09.otf
new file mode 100644
index 0000000..2506a41
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.default.4E05,4E07,4E08,4E09.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.default.4E08,4E09.otf b/test/subset/data/expected/cmap14/cmap14_font1.default.4E08,4E09.otf
new file mode 100644
index 0000000..e8ebeb4
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.default.4E08,4E09.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.default.4E08.otf b/test/subset/data/expected/cmap14/cmap14_font1.default.4E08.otf
new file mode 100644
index 0000000..910cc0f
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.default.4E08.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.default.retain-all-codepoint.otf b/test/subset/data/expected/cmap14/cmap14_font1.default.retain-all-codepoint.otf
new file mode 100644
index 0000000..d7d6972
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.default.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E00,4E02,4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E00,4E02,4E03.otf
new file mode 100644
index 0000000..a1c001c
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E00,4E02,4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E00,4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E00,4E03.otf
new file mode 100644
index 0000000..5b41802
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E00,4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E00,4E05,4E07.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E00,4E05,4E07.otf
new file mode 100644
index 0000000..b88e288
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E00,4E05,4E07.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E02,4E03,4E08.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E02,4E03,4E08.otf
new file mode 100644
index 0000000..6d95272
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E02,4E03,4E08.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E02.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E02.otf
new file mode 100644
index 0000000..251568f
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E02.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E03.otf
new file mode 100644
index 0000000..2b1d7a7
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E05,4E07,4E08,4E09.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E05,4E07,4E08,4E09.otf
new file mode 100644
index 0000000..dce7f14
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E05,4E07,4E08,4E09.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E08,4E09.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E08,4E09.otf
new file mode 100644
index 0000000..e1e2245
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E08,4E09.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E08.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E08.otf
new file mode 100644
index 0000000..f72cdc9
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.4E08.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.retain-all-codepoint.otf
new file mode 100644
index 0000000..4efa2e2
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E00,4E02,4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E00,4E02,4E03.otf
new file mode 100644
index 0000000..a440b96
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E00,4E02,4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E00,4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E00,4E03.otf
new file mode 100644
index 0000000..c503e38
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E00,4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E00,4E05,4E07.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E00,4E05,4E07.otf
new file mode 100644
index 0000000..d36d155
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E00,4E05,4E07.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E02,4E03,4E08.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E02,4E03,4E08.otf
new file mode 100644
index 0000000..34a8469
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E02,4E03,4E08.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E02.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E02.otf
new file mode 100644
index 0000000..d695329
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E02.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E03.otf
new file mode 100644
index 0000000..1c4d2b5
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E05,4E07,4E08,4E09.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E05,4E07,4E08,4E09.otf
new file mode 100644
index 0000000..e5981f0
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E05,4E07,4E08,4E09.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E08,4E09.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E08,4E09.otf
new file mode 100644
index 0000000..4b76f1c
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E08,4E09.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E08.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E08.otf
new file mode 100644
index 0000000..cf60215
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.4E08.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.retain-all-codepoint.otf b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.retain-all-codepoint.otf
new file mode 100644
index 0000000..bf353ed
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.drop-hints.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E00,4E02,4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E00,4E02,4E03.otf
new file mode 100644
index 0000000..a7b67bf
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E00,4E02,4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E00,4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E00,4E03.otf
new file mode 100644
index 0000000..7c6805d
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E00,4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E00,4E05,4E07.otf b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E00,4E05,4E07.otf
new file mode 100644
index 0000000..b1876b6
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E00,4E05,4E07.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E02,4E03,4E08.otf b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E02,4E03,4E08.otf
new file mode 100644
index 0000000..b07778f
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E02,4E03,4E08.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E02.otf b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E02.otf
new file mode 100644
index 0000000..fb77632
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E02.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E03.otf
new file mode 100644
index 0000000..4ec322c
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E05,4E07,4E08,4E09.otf b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E05,4E07,4E08,4E09.otf
new file mode 100644
index 0000000..ec20755
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E05,4E07,4E08,4E09.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E08,4E09.otf b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E08,4E09.otf
new file mode 100644
index 0000000..bf2c086
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E08,4E09.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E08.otf b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E08.otf
new file mode 100644
index 0000000..0a3721e
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.4E08.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.name-ids.retain-all-codepoint.otf b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.retain-all-codepoint.otf
new file mode 100644
index 0000000..eaaa56d
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.name-ids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E00,4E02,4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E00,4E02,4E03.otf
new file mode 100644
index 0000000..5c5ce5c
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E00,4E02,4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E00,4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E00,4E03.otf
new file mode 100644
index 0000000..3b87f54
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E00,4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E00,4E05,4E07.otf b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E00,4E05,4E07.otf
new file mode 100644
index 0000000..e06a24d
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E00,4E05,4E07.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E02,4E03,4E08.otf b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E02,4E03,4E08.otf
new file mode 100644
index 0000000..aabdc5e
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E02,4E03,4E08.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E02.otf b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E02.otf
new file mode 100644
index 0000000..4183c9f
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E02.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E03.otf b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E03.otf
new file mode 100644
index 0000000..66ef901
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E03.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E05,4E07,4E08,4E09.otf b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E05,4E07,4E08,4E09.otf
new file mode 100644
index 0000000..4bee46f
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E05,4E07,4E08,4E09.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E08,4E09.otf b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E08,4E09.otf
new file mode 100644
index 0000000..6e8baa9
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E08,4E09.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E08.otf b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E08.otf
new file mode 100644
index 0000000..f6191da
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.4E08.otf
Binary files differ
diff --git a/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.retain-all-codepoint.otf
new file mode 100644
index 0000000..bf2746b
--- /dev/null
+++ b/test/subset/data/expected/cmap14/cmap14_font1.retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,29.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,29.otf
new file mode 100644
index 0000000..17aa6d8
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,29.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,2B.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,2B.otf
new file mode 100644
index 0000000..9e6f2eb
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,2B.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.29,2B.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.29,2B.otf
new file mode 100644
index 0000000..0187ed7
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.29,2B.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.retain-all-codepoint.otf
new file mode 100644
index 0000000..d9b5dfb
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,29.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,29.otf
new file mode 100644
index 0000000..f3ca19a
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,29.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,2B.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,2B.otf
new file mode 100644
index 0000000..2a8114a
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,2B.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.29,2B.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.29,2B.otf
new file mode 100644
index 0000000..1426d50
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.29,2B.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.retain-all-codepoint.otf
new file mode 100644
index 0000000..d9b5dfb
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/fonts/cmap14_font1.otf b/test/subset/data/fonts/cmap14_font1.otf
new file mode 100644
index 0000000..a4283c6
--- /dev/null
+++ b/test/subset/data/fonts/cmap14_font1.otf
Binary files differ
diff --git a/test/subset/data/fonts/gpos3_font3.otf b/test/subset/data/fonts/gpos3_font3.otf
new file mode 100644
index 0000000..69c74a3
--- /dev/null
+++ b/test/subset/data/fonts/gpos3_font3.otf
Binary files differ
diff --git a/test/subset/data/tests/cmap14.tests b/test/subset/data/tests/cmap14.tests
new file mode 100644
index 0000000..6575870
--- /dev/null
+++ b/test/subset/data/tests/cmap14.tests
@@ -0,0 +1,21 @@
+FONTS:
+cmap14_font1.otf
+
+PROFILES:
+default.txt
+drop-hints.txt
+drop-hints-retain-gids.txt
+retain-gids.txt
+name-ids.txt
+
+SUBSETS:
+一丂七
+丂
+七
+一七
+一丅万
+丅万丈三
+丈
+丈三
+丂七丈
+*
diff --git a/test/subset/data/tests/layout.gpos3.tests b/test/subset/data/tests/layout.gpos3.tests
new file mode 100644
index 0000000..409272f
--- /dev/null
+++ b/test/subset/data/tests/layout.gpos3.tests
@@ -0,0 +1,12 @@
+FONTS:
+gpos3_font3.otf
+
+PROFILES:
+keep-layout.txt
+keep-layout-retain-gids.txt
+
+SUBSETS:
+()
+(+
+)+
+*