Merge "Merge Harfbuzz 1.4.2 from the upstream" am: bd7ef0ec81 am: a9c46cb26e
am: 45382daa31

Change-Id: I077e1a2bf67b3161f6e507528c0a90ab0ada1a3f
diff --git a/Android.bp b/Android.bp
index f048737..ff6b430 100644
--- a/Android.bp
+++ b/Android.bp
@@ -75,6 +75,7 @@
         "src/hb-ot-font.cc",
         "src/hb-ot-layout.cc",
         "src/hb-ot-map.cc",
+        "src/hb-ot-math.cc",
         "src/hb-ot-shape.cc",
         "src/hb-ot-shape-complex-arabic.cc",
         "src/hb-ot-shape-complex-default.cc",
@@ -89,6 +90,7 @@
         "src/hb-ot-shape-complex-use-table.cc",
         "src/hb-ot-shape-normalize.cc",
         "src/hb-ot-shape-fallback.cc",
+        "src/hb-ot-var.cc",
 
         "src/hb-icu.cc",
     ],
diff --git a/Makefile.am b/Makefile.am
index d56a151..8dc8a4b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,6 @@
 EXTRA_DIST = \
 	autogen.sh \
 	harfbuzz.doap \
-	Android.mk \
 	README.python \
 	BUILD.md \
 	$(NULL)
diff --git a/NEWS b/NEWS
index ae0926e..43a3bac 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,110 @@
+Overview of changes leading to 1.4.2
+Monday, January 23, 2017
+====================================
+
+- Implement OpenType Font Variation tables avar/fvar/HVAR/VVAR.
+- hb-shape and hb-view now accept --variations.
+- New API:
+
+hb_variation_t
+hb_variation_from_string()
+hb_variation_to_string()
+
+hb_font_set_variations()
+hb_font_set_var_coords_design()
+hb_font_get_var_coords_normalized()
+
+hb-ot-var.h:
+hb_ot_var_axis_t
+hb_ot_var_has_data()
+hb_ot_var_get_axis_count()
+hb_ot_var_get_axes()
+hb_ot_var_find_axis()
+hb_ot_var_normalize_variations()
+hb_ot_var_normalize_coords()
+
+- MVAR to be implemented later.  Access to named instances to be
+  implemented later as well.
+
+- Misc fixes.
+
+
+Overview of changes leading to 1.4.1
+Thursday, January 5, 2017
+====================================
+
+- Always build and use UCDN for Unicode data by default.
+  Reduces dependence on version of Unicode data in glib,
+  specially in the Windows bundles we are shipping, which
+  have very old glib.
+
+
+Overview of changes leading to 1.4.0
+Thursday, January 5, 2017
+====================================
+
+- Merged "OpenType GX" branch which adds core of support for
+  OpenType 1.8 Font Variations.  To that extent, the relevant
+  new API is:
+
+New API:
+hb_font_set_var_coords_normalized()
+
+  with supporting API:
+
+New API:
+HB_OT_LAYOUT_NO_VARIATIONS_INDEX
+hb_ot_layout_table_find_feature_variations()
+hb_ot_layout_feature_with_variations_get_lookups()
+hb_shape_plan_create2()
+hb_shape_plan_create_cached2()
+
+  Currently variations in GSUB/GPOS/GDEF are fully supported,
+  and no other tables are supported.  In particular, fvar/avar
+  are NOT supported, hence the hb_font_set_var_coords_normalized()
+  taking normalized coordinates.  API to take design coordinates
+  will be added in the future.
+
+  HVAR/VVAR/MVAR support will also be added to hb-ot-font in the
+  future.
+
+- Fix regression in GDEF glyph class processing.
+- Add decompositions for Chakma, Limbu, and Balinese in USE shaper.
+- Misc fixes.
+
+
+Overview of changes leading to 1.3.4
+Monday, December 5, 2016
+====================================
+
+- Fix vertical glyph origin in hb-ot-font.
+- Implement CBDT/CBLC color font glyph extents in hb-ot-font.
+
+
+Overview of changes leading to 1.3.3
+Wednesday, September 28, 2016
+====================================
+
+- Implement parsing of OpenType MATH table.
+New API:
+HB_OT_TAG_MATH
+HB_OT_MATH_SCRIPT
+hb_ot_math_constant_t
+hb_ot_math_kern_t
+hb_ot_math_glyph_variant_t
+hb_ot_math_glyph_part_flags_t
+hb_ot_math_glyph_part_t
+hb_ot_math_has_data
+hb_ot_math_get_constant
+hb_ot_math_get_glyph_italics_correction
+hb_ot_math_get_glyph_top_accent_attachment
+hb_ot_math_get_glyph_kerning
+hb_ot_math_is_glyph_extended_shape
+hb_ot_math_get_glyph_variants
+hb_ot_math_get_min_connector_overlap
+hb_ot_math_get_glyph_assembly
+
+
 Overview of changes leading to 1.3.2
 Wednesday, September 27, 2016
 ====================================
diff --git a/README.version b/README.version
index 126e130..93200ee 100644
--- a/README.version
+++ b/README.version
@@ -1,3 +1,3 @@
-URL: http://www.freedesktop.org/software/harfbuzz/release/harfbuzz-1.3.2.tar.bz2
-Version: 1.3.2
+URL: http://www.freedesktop.org/software/harfbuzz/release/harfbuzz-1.4.2.tar.bz2
+Version: 1.4.2
 BugComponent: 25699
diff --git a/appveyor.yml b/appveyor.yml
index 4677a27..2a0e7c5 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -4,15 +4,19 @@
   matrix:
     - compiler: msvc
       ARCH: amd64
+      VCPKG_ARCH: x64-windows
       CFG: release
     - compiler: msvc
       ARCH: x86
+      VCPKG_ARCH: x86-windows
       CFG: release
     - compiler: msvc
       ARCH: amd64
+      VCPKG_ARCH: x64-windows
       CFG: debug
     - compiler: msvc
       ARCH: x86
+      VCPKG_ARCH: x86-windows
       CFG: debug
 
     - compiler: msys2
@@ -27,11 +31,19 @@
 install:
   - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-ragel"
 
-build_script:
   - 'if "%compiler%"=="msvc" call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %ARCH%'
+  - 'if "%compiler%"=="msvc" git clone https://github.com/Microsoft/vcpkg'
+  - 'if "%compiler%"=="msvc" cd vcpkg'
+  - 'if "%compiler%"=="msvc" powershell -exec bypass scripts\bootstrap.ps1'
+  - 'if "%compiler%"=="msvc" vcpkg install freetype:%VCPKG_ARCH%'
+  - 'if "%compiler%"=="msvc" copy installed\%VCPKG_ARCH%\debug\lib\freetyped.lib installed\%VCPKG_ARCH%\lib'
+  - 'if "%compiler%"=="msvc" cd ..'
+
+build_script:
   - 'if "%compiler%"=="msvc" C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER; PATH=$PATH:/mingw64/bin:/mingw32/bin; ./autogen.sh; make distdir"'
   - 'if "%compiler%"=="msvc" cd harfbuzz-*\win32'
-  - 'if "%compiler%"=="msvc" nmake /f Makefile.vc CFG=%CFG% DIRECTWRITE=1'
+  - 'if "%compiler%"=="msvc" nmake /f Makefile.vc CFG=%CFG% UNISCRIBE=1 DIRECTWRITE=1 FREETYPE=1 FREETYPE_DIR=..\..\vcpkg\installed\%VCPKG_ARCH%\include ADDITIONAL_LIB_DIR=..\..\vcpkg\installed\%VCPKG_ARCH%\lib'
+  - 'if "%compiler%"=="msvc" nmake /f Makefile.vc CFG=%CFG% UNISCRIBE=1 DIRECTWRITE=1 FREETYPE=1 install'
 
   - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-$MSYS2_ARCH-{freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config}"'
   - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER; PATH=$PATH:/mingw64/bin:/mingw32/bin; ./autogen.sh --with-uniscribe --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 --build=$MINGW_CHOST --host=$MINGW_CHOST --prefix=$MINGW_PREFIX; make; make check"'
diff --git a/configure.ac b/configure.ac
index 57e9e6d..31fa97d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.3.2],
+        [1.4.2],
         [https://github.com/behdad/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -19,9 +19,11 @@
 LT_INIT([disable-static])
 
 # Check for programs
+AC_USE_SYSTEM_EXTENSIONS
 AC_PROG_CC
 AM_PROG_CC_C_O
 AC_PROG_CXX
+AC_SYS_LARGEFILE
 PKG_PROG_PKG_CONFIG([0.20])
 AM_MISSING_PROG([RAGEL], [ragel])
 AM_MISSING_PROG([GIT], [git])
@@ -145,7 +147,7 @@
 			[Use glib @<:@default=auto@:>@])],,
 	[with_glib=auto])
 have_glib=false
-GLIB_DEPS="glib-2.0 >= 2.16"
+GLIB_DEPS="glib-2.0 >= 2.19.1"
 AC_SUBST(GLIB_DEPS)
 if test "x$with_glib" = "xyes" -o "x$with_glib" = "xauto"; then
 	PKG_CHECK_MODULES(GLIB, $GLIB_DEPS, have_glib=true, :)
@@ -287,9 +289,13 @@
 
 dnl ===========================================================================
 
-have_ucdn=true
-if $have_glib || test \( $have_icu -a "x$with_icu" = "xbuiltin" \); then
-	have_ucdn=false
+AC_ARG_WITH(ucdn,
+	[AS_HELP_STRING([--with-ucdn=@<:@yes/no@:>@],
+			[Use builtin UCDN library @<:@default=yes@:>@])],,
+	[with_ucdn=yes])
+have_ucdn=false
+if test "x$with_ucdn" = "xyes"; then
+	have_ucdn=true
 fi
 if $have_ucdn; then
 	AC_DEFINE(HAVE_UCDN, 1, [Have UCDN Unicode functions])
@@ -344,6 +350,10 @@
 fi
 if $have_freetype; then
 	AC_DEFINE(HAVE_FREETYPE, 1, [Have FreeType 2 library])
+	save_libs=$LIBS
+	LIBS="$LIBS $FREETYPE_LIBS"
+	AC_CHECK_FUNCS(FT_Get_Var_Blend_Coordinates)
+	LIBS=$save_libs
 fi
 AM_CONDITIONAL(HAVE_FREETYPE, $have_freetype)
 
@@ -411,7 +421,8 @@
 	else
 		# On iOS CoreText and CoreGraphics are stand-alone frameworks
 		if test "x$have_coretext" != "xtrue"; then
-			AC_CHECK_TYPE(CTFontRef, have_coretext=true,, [#include <CoreText/CoreText.h>])
+			# Check for a different symbol to avoid getting cached result.
+			AC_CHECK_TYPE(CTRunRef, have_coretext=true,, [#include <CoreText/CoreText.h>])
 		fi
 
 		if $have_coretext; then
@@ -494,9 +505,9 @@
 Build configuration:
 
 Unicode callbacks (you want at least one):
+	Builtin (UCDN):		${have_ucdn}
 	Glib:			${have_glib}
 	ICU:			${have_icu}
-	UCDN:			${have_ucdn}
 
 Font callbacks (the more the better):
 	FreeType:		${have_freetype}
diff --git a/docs/harfbuzz-docs.xml b/docs/harfbuzz-docs.xml
index 2c43c46..00113e9 100644
--- a/docs/harfbuzz-docs.xml
+++ b/docs/harfbuzz-docs.xml
@@ -80,6 +80,7 @@
         <xi:include href="xml/hb-ot-tag.xml"/>
         <xi:include href="xml/hb-ot-font.xml"/>
         <xi:include href="xml/hb-ot-shape.xml"/>
+        <xi:include href="xml/hb-ot-math.xml"/>
 
         <xi:include href="xml/hb-shape-plan.xml"/>
 
@@ -175,6 +176,14 @@
         <title>Index of new symbols in 1.1.3</title>
         <xi:include href="xml/api-index-1.1.3.xml"><xi:fallback /></xi:include>
       </index>
+      <index id="api-index-1-2-3" role="1.2.3">
+        <title>Index of new symbols in 1.2.3</title>
+        <xi:include href="xml/api-index-1.2.3.xml"><xi:fallback /></xi:include>
+      </index>
+      <index id="api-index-1-3-3" role="1.3.3">
+        <title>Index of new symbols in 1.3.3</title>
+        <xi:include href="xml/api-index-1.3.3.xml"><xi:fallback /></xi:include>
+      </index>
       <index id="deprecated-api-index" role="deprecated">
         <title>Index of deprecated API</title>
         <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index e0dc23d..a91eb4c 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -144,6 +144,8 @@
 HB_BUFFER_FLAGS_DEFAULT
 HB_BUFFER_SERIALIZE_FLAGS_DEFAULT
 HB_SCRIPT_CANADIAN_ABORIGINAL
+hb_font_funcs_set_glyph_func
+hb_font_get_glyph_func_t
 </SECTION>
 
 <SECTION>
@@ -193,7 +195,6 @@
 hb_font_funcs_set_glyph_contour_point_func
 hb_font_funcs_set_glyph_extents_func
 hb_font_funcs_set_glyph_from_name_func
-hb_font_funcs_set_glyph_func
 hb_font_funcs_set_glyph_h_advance_func
 hb_font_funcs_set_glyph_h_kerning_func
 hb_font_funcs_set_glyph_h_origin_func
@@ -201,7 +202,9 @@
 hb_font_funcs_set_glyph_v_advance_func
 hb_font_funcs_set_glyph_v_kerning_func
 hb_font_funcs_set_glyph_v_origin_func
+hb_font_funcs_set_nominal_glyph_func
 hb_font_funcs_set_user_data
+hb_font_funcs_set_variation_glyph_func
 hb_font_funcs_t
 hb_font_get_empty
 hb_font_get_face
@@ -216,7 +219,6 @@
 hb_font_get_glyph_extents_func_t
 hb_font_get_glyph_from_name
 hb_font_get_glyph_from_name_func_t
-hb_font_get_glyph_func_t
 hb_font_get_glyph_h_advance
 hb_font_get_glyph_h_advance_func_t
 hb_font_get_glyph_h_kerning
@@ -235,10 +237,15 @@
 hb_font_get_glyph_v_kerning_func_t
 hb_font_get_glyph_v_origin
 hb_font_get_glyph_v_origin_func_t
+hb_font_get_nominal_glyph
+hb_font_get_nominal_glyph_func_t
 hb_font_get_parent
 hb_font_get_ppem
 hb_font_get_scale
 hb_font_get_user_data
+hb_font_get_variation_glyph
+hb_font_get_variation_glyph_func_t
+hb_font_get_var_coords_normalized
 hb_font_glyph_from_string
 hb_font_glyph_to_string
 hb_font_is_immutable
@@ -246,9 +253,16 @@
 hb_font_reference
 hb_font_set_funcs
 hb_font_set_funcs_data
+hb_font_set_parent
 hb_font_set_ppem
 hb_font_set_scale
 hb_font_set_user_data
+hb_variation_t
+hb_variation_from_string
+hb_variation_to_string
+hb_font_set_variations
+hb_font_set_var_coords_design
+hb_font_set_var_coords_normalized
 hb_font_subtract_glyph_origin_for_direction
 hb_font_t
 hb_reference_table_func_t
@@ -260,7 +274,6 @@
 hb_font_get_font_v_extents_func_t
 hb_font_get_h_extents
 hb_font_get_v_extents
-hb_font_set_parent
 </SECTION>
 
 <SECTION>
@@ -298,6 +311,9 @@
 HB_GOBJECT_TYPE_FONT_FUNCS
 HB_GOBJECT_TYPE_MEMORY_MODE
 HB_GOBJECT_TYPE_OT_LAYOUT_GLYPH_CLASS
+HB_GOBJECT_TYPE_OT_MATH_CONSTANT
+HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART_FLAGS
+HB_GOBJECT_TYPE_OT_MATH_KERN
 HB_GOBJECT_TYPE_SCRIPT
 HB_GOBJECT_TYPE_SHAPE_PLAN
 HB_GOBJECT_TYPE_UNICODE_COMBINING_CLASS
@@ -322,6 +338,9 @@
 hb_gobject_font_get_type
 hb_gobject_memory_mode_get_type
 hb_gobject_ot_layout_glyph_class_get_type
+hb_gobject_ot_math_constant_get_type
+hb_gobject_ot_math_glyph_part_flags_get_type
+hb_gobject_ot_math_kern_get_type
 hb_gobject_script_get_type
 hb_gobject_shape_plan_get_type
 hb_gobject_unicode_combining_class_get_type
@@ -378,12 +397,14 @@
 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX
 HB_OT_LAYOUT_NO_FEATURE_INDEX
 HB_OT_LAYOUT_NO_SCRIPT_INDEX
+HB_OT_LAYOUT_NO_VARIATIONS_INDEX
 HB_OT_TAG_GDEF
 HB_OT_TAG_GPOS
 HB_OT_TAG_GSUB
 HB_OT_TAG_JSTF
 hb_ot_layout_collect_lookups
 hb_ot_layout_feature_get_lookups
+hb_ot_layout_feature_with_variations_get_lookups
 hb_ot_layout_get_attach_points
 hb_ot_layout_get_glyph_class
 hb_ot_layout_get_glyphs_in_class
@@ -404,6 +425,7 @@
 hb_ot_layout_script_find_language
 hb_ot_layout_script_get_language_tags
 hb_ot_layout_table_choose_script
+hb_ot_layout_table_find_feature_variations
 hb_ot_layout_table_find_script
 hb_ot_layout_table_get_feature_tags
 hb_ot_layout_table_get_script_tags
@@ -417,6 +439,43 @@
 </SECTION>
 
 <SECTION>
+<FILE>hb-ot-var</FILE>
+HB_OT_TAG_VAR_AXIS_ITALIC
+HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE
+HB_OT_TAG_VAR_AXIS_SLANT
+HB_OT_TAG_VAR_AXIS_WEIGHT
+HB_OT_TAG_VAR_AXIS_WIDTH
+HB_OT_VAR_NO_AXIS_INDEX
+hb_ot_var_axis_t
+hb_ot_var_has_data
+hb_ot_var_find_axis
+hb_ot_var_get_axis_count
+hb_ot_var_get_axes
+hb_ot_var_normalize_variations
+hb_ot_var_normalize_coords
+</SECTION>
+
+<SECTION>
+<FILE>hb-ot-math</FILE>
+HB_OT_TAG_MATH
+HB_OT_MATH_SCRIPT
+hb_ot_math_constant_t
+hb_ot_math_kern_t
+hb_ot_math_glyph_variant_t
+hb_ot_math_glyph_part_flags_t
+hb_ot_math_glyph_part_t
+hb_ot_math_has_data
+hb_ot_math_get_constant
+hb_ot_math_get_glyph_italics_correction
+hb_ot_math_get_glyph_top_accent_attachment
+hb_ot_math_get_glyph_kerning
+hb_ot_math_is_glyph_extended_shape
+hb_ot_math_get_glyph_variants
+hb_ot_math_get_min_connector_overlap
+hb_ot_math_get_glyph_assembly
+</SECTION>
+
+<SECTION>
 <FILE>hb-ot-tag</FILE>
 HB_OT_TAG_DEFAULT_LANGUAGE
 HB_OT_TAG_DEFAULT_SCRIPT
@@ -460,8 +519,8 @@
 
 <SECTION>
 <FILE>hb-shape</FILE>
-hb_feature_from_string
 hb_feature_t
+hb_feature_from_string
 hb_feature_to_string
 hb_shape
 hb_shape_full
@@ -472,6 +531,8 @@
 <FILE>hb-shape-plan</FILE>
 hb_shape_plan_create
 hb_shape_plan_create_cached
+hb_shape_plan_create2
+hb_shape_plan_create_cached2
 hb_shape_plan_destroy
 hb_shape_plan_execute
 hb_shape_plan_get_empty
@@ -526,6 +587,8 @@
 <FILE>hb-uniscribe</FILE>
 hb_uniscribe_font_get_hfont
 hb_uniscribe_font_get_logfontw
+<SUBSECTION Private>
+hb_directwrite_shape_experimental_width
 </SECTION>
 
 <SECTION>
diff --git a/src/Makefile.am b/src/Makefile.am
index 8cfe4ac..d7420a0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -299,6 +299,8 @@
 test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
 test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
 
+check: harfbuzz.def # For check-defs.sh
+
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
 	check-defs.sh \
diff --git a/src/Makefile.sources b/src/Makefile.sources
index ac80683..8d7f1a0 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -20,6 +20,7 @@
 	hb-object-private.hh \
 	hb-open-file-private.hh \
 	hb-open-type-private.hh \
+	hb-ot-cbdt-table.hh \
 	hb-ot-cmap-table.hh \
 	hb-ot-glyf-table.hh \
 	hb-ot-head-table.hh \
@@ -78,6 +79,8 @@
 	hb-ot-layout-private.hh \
 	hb-ot-map.cc \
 	hb-ot-map-private.hh \
+	hb-ot-math.cc \
+	hb-ot-math-table.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-complex-arabic.cc \
 	hb-ot-shape-complex-arabic-fallback.hh \
@@ -105,14 +108,20 @@
 	hb-ot-shape-fallback-private.hh \
 	hb-ot-shape-fallback.cc \
 	hb-ot-shape-private.hh \
+	hb-ot-var.cc \
+	hb-ot-var-avar-table.hh \
+	hb-ot-var-fvar-table.hh \
+	hb-ot-var-hvar-table.hh \
 	$(NULL)
 
 HB_OT_headers = \
 	hb-ot.h \
 	hb-ot-font.h \
 	hb-ot-layout.h \
+	hb-ot-math.h \
 	hb-ot-shape.h \
 	hb-ot-tag.h \
+	hb-ot-var.h \
 	$(NULL)
 
 # Optional Sources and Headers with external deps
diff --git a/src/check-symbols.sh b/src/check-symbols.sh
index b2bf43f..ba09ba1 100755
--- a/src/check-symbols.sh
+++ b/src/check-symbols.sh
@@ -19,8 +19,8 @@
 for suffix in so dylib; do
 	so=.libs/libharfbuzz.$suffix
 	if ! test -f "$so"; then continue; fi
-	
-	EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| llvm_' | cut -d' ' -f3`"
+
+	EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| ___gcov_flush\>\| llvm_\| _llvm_' | cut -d' ' -f3`"
 
 	prefix=`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
 
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 3564e43..64e77d4 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -605,3 +605,347 @@
 {
   return HB_VERSION_ATLEAST (major, minor, micro);
 }
+
+
+
+/* hb_feature_t and hb_variation_t */
+
+static bool
+parse_space (const char **pp, const char *end)
+{
+  while (*pp < end && ISSPACE (**pp))
+    (*pp)++;
+  return true;
+}
+
+static bool
+parse_char (const char **pp, const char *end, char c)
+{
+  parse_space (pp, end);
+
+  if (*pp == end || **pp != c)
+    return false;
+
+  (*pp)++;
+  return true;
+}
+
+static bool
+parse_uint (const char **pp, const char *end, unsigned int *pv)
+{
+  char buf[32];
+  unsigned int len = 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, 0);
+  if (errno || p == pend)
+    return false;
+
+  *pv = v;
+  *pp += pend - p;
+  return true;
+}
+
+static bool
+parse_float (const char **pp, const char *end, float *pv)
+{
+  char buf[32];
+  unsigned int len = 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;
+  v = strtof (p, &pend);
+  if (errno || p == pend)
+    return false;
+
+  *pv = v;
+  *pp += pend - p;
+  return true;
+}
+
+static bool
+parse_bool (const char **pp, const char *end, unsigned int *pv)
+{
+  parse_space (pp, end);
+
+  const char *p = *pp;
+  while (*pp < end && ISALPHA(**pp))
+    (*pp)++;
+
+  /* CSS allows on/off as aliases 1/0. */
+  if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
+    *pv = 1;
+  else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
+    *pv = 0;
+  else
+    return false;
+
+  return true;
+}
+
+/* hb_feature_t */
+
+static bool
+parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
+{
+  if (parse_char (pp, end, '-'))
+    feature->value = 0;
+  else {
+    parse_char (pp, end, '+');
+    feature->value = 1;
+  }
+
+  return true;
+}
+
+static bool
+parse_tag (const char **pp, const char *end, hb_tag_t *tag)
+{
+  parse_space (pp, end);
+
+  char quote = 0;
+
+  if (*pp < end && (**pp == '\'' || **pp == '"'))
+  {
+    quote = **pp;
+    (*pp)++;
+  }
+
+  const char *p = *pp;
+  while (*pp < end && ISALNUM(**pp))
+    (*pp)++;
+
+  if (p == *pp || *pp - p > 4)
+    return false;
+
+  *tag = hb_tag_from_string (p, *pp - p);
+
+  if (quote)
+  {
+    /* CSS expects exactly four bytes.  And we only allow quotations for
+     * CSS compatibility.  So, enforce the length. */
+     if (*pp - p != 4)
+       return false;
+    if (*pp == end || **pp != quote)
+      return false;
+    (*pp)++;
+  }
+
+  return true;
+}
+
+static bool
+parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
+{
+  parse_space (pp, end);
+
+  bool has_start;
+
+  feature->start = 0;
+  feature->end = (unsigned int) -1;
+
+  if (!parse_char (pp, end, '['))
+    return true;
+
+  has_start = parse_uint (pp, end, &feature->start);
+
+  if (parse_char (pp, end, ':')) {
+    parse_uint (pp, end, &feature->end);
+  } else {
+    if (has_start)
+      feature->end = feature->start + 1;
+  }
+
+  return parse_char (pp, end, ']');
+}
+
+static bool
+parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
+{
+  bool had_equal = parse_char (pp, end, '=');
+  bool had_value = parse_uint (pp, end, &feature->value) ||
+                   parse_bool (pp, end, &feature->value);
+  /* CSS doesn't use equal-sign between tag and value.
+   * If there was an equal-sign, then there *must* be a value.
+   * A value without an eqaul-sign is ok, but not required. */
+  return !had_equal || had_value;
+}
+
+static bool
+parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
+{
+  return parse_feature_value_prefix (pp, end, feature) &&
+	 parse_tag (pp, end, &feature->tag) &&
+	 parse_feature_indices (pp, end, feature) &&
+	 parse_feature_value_postfix (pp, end, feature) &&
+	 parse_space (pp, end) &&
+	 *pp == end;
+}
+
+/**
+ * hb_feature_from_string:
+ * @str: (array length=len) (element-type uint8_t): a string to parse
+ * @len: length of @str, or -1 if string is %NULL terminated
+ * @feature: (out): the #hb_feature_t to initialize with the parsed values
+ *
+ * Parses a string into a #hb_feature_t.
+ *
+ * TODO: document the syntax here.
+ *
+ * Return value:
+ * %true if @str is successfully parsed, %false otherwise.
+ *
+ * Since: 0.9.5
+ **/
+hb_bool_t
+hb_feature_from_string (const char *str, int len,
+			hb_feature_t *feature)
+{
+  hb_feature_t feat;
+
+  if (len < 0)
+    len = strlen (str);
+
+  if (likely (parse_one_feature (&str, str + len, &feat)))
+  {
+    if (feature)
+      *feature = feat;
+    return true;
+  }
+
+  if (feature)
+    memset (feature, 0, sizeof (*feature));
+  return false;
+}
+
+/**
+ * hb_feature_to_string:
+ * @feature: an #hb_feature_t to convert
+ * @buf: (array length=size) (out): output string
+ * @size: the allocated size of @buf
+ *
+ * Converts a #hb_feature_t into a %NULL-terminated string in the format
+ * understood by hb_feature_from_string(). The client in responsible for
+ * allocating big enough size for @buf, 128 bytes is more than enough.
+ *
+ * Since: 0.9.5
+ **/
+void
+hb_feature_to_string (hb_feature_t *feature,
+		      char *buf, unsigned int size)
+{
+  if (unlikely (!size)) return;
+
+  char s[128];
+  unsigned int len = 0;
+  if (feature->value == 0)
+    s[len++] = '-';
+  hb_tag_to_string (feature->tag, s + len);
+  len += 4;
+  while (len && s[len - 1] == ' ')
+    len--;
+  if (feature->start != 0 || feature->end != (unsigned int) -1)
+  {
+    s[len++] = '[';
+    if (feature->start)
+      len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
+    if (feature->end != feature->start + 1) {
+      s[len++] = ':';
+      if (feature->end != (unsigned int) -1)
+	len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
+    }
+    s[len++] = ']';
+  }
+  if (feature->value > 1)
+  {
+    s[len++] = '=';
+    len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
+  }
+  assert (len < ARRAY_LENGTH (s));
+  len = MIN (len, size - 1);
+  memcpy (buf, s, len);
+  buf[len] = '\0';
+}
+
+/* hb_variation_t */
+
+static bool
+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);
+}
+
+static bool
+parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
+{
+  return parse_tag (pp, end, &variation->tag) &&
+	 parse_variation_value (pp, end, variation) &&
+	 parse_space (pp, end) &&
+	 *pp == end;
+}
+
+/**
+ * hb_variation_from_string:
+ *
+ * Since: 1.4.2
+ */
+hb_bool_t
+hb_variation_from_string (const char *str, int len,
+			  hb_variation_t *variation)
+{
+  hb_variation_t var;
+
+  if (len < 0)
+    len = strlen (str);
+
+  if (likely (parse_one_variation (&str, str + len, &var)))
+  {
+    if (variation)
+      *variation = var;
+    return true;
+  }
+
+  if (variation)
+    memset (variation, 0, sizeof (*variation));
+  return false;
+}
+
+/**
+ * hb_variation_to_string:
+ *
+ * Since: 1.4.2
+ */
+void
+hb_variation_to_string (hb_variation_t *variation,
+			char *buf, unsigned int size)
+{
+  if (unlikely (!size)) return;
+
+  char s[128];
+  unsigned int len = 0;
+  hb_tag_to_string (variation->tag, s + len);
+  len += 4;
+  while (len && s[len - 1] == ' ')
+    len--;
+  s[len++] = '=';
+  len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
+
+  assert (len < ARRAY_LENGTH (s));
+  len = MIN (len, size - 1);
+  memcpy (buf, s, len);
+  buf[len] = '\0';
+}
diff --git a/src/hb-common.h b/src/hb-common.h
index 2cbee76..634cb96 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -362,6 +362,42 @@
 typedef void (*hb_destroy_func_t) (void *user_data);
 
 
+/* Font features and variations. */
+
+typedef struct hb_feature_t {
+  hb_tag_t      tag;
+  uint32_t      value;
+  unsigned int  start;
+  unsigned int  end;
+} hb_feature_t;
+
+HB_EXTERN hb_bool_t
+hb_feature_from_string (const char *str, int len,
+			hb_feature_t *feature);
+
+HB_EXTERN void
+hb_feature_to_string (hb_feature_t *feature,
+		      char *buf, unsigned int size);
+
+/**
+ * hb_variation_t:
+ *
+ * Since: 1.4.2
+ */
+typedef struct hb_variation_t {
+  hb_tag_t tag;
+  float    value;
+} hb_variation_t;
+
+HB_EXTERN hb_bool_t
+hb_variation_from_string (const char *str, int len,
+			  hb_variation_t *variation);
+
+HB_EXTERN void
+hb_variation_to_string (hb_variation_t *variation,
+			char *buf, unsigned int size);
+
+
 HB_END_DECLS
 
 #endif /* HB_COMMON_H */
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc
index 507581b..e857dfa 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -288,7 +288,9 @@
 hb_coretext_shaper_shape_plan_data_t *
 _hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					     const hb_feature_t *user_features HB_UNUSED,
-					     unsigned int        num_user_features HB_UNUSED)
+					     unsigned int        num_user_features HB_UNUSED,
+					     const int          *coords HB_UNUSED,
+					     unsigned int        num_coords HB_UNUSED)
 {
   return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
@@ -1280,7 +1282,9 @@
 hb_coretext_aat_shaper_shape_plan_data_t *
 _hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					     const hb_feature_t *user_features HB_UNUSED,
-					     unsigned int        num_user_features HB_UNUSED)
+					     unsigned int        num_user_features HB_UNUSED,
+					     const int          *coords HB_UNUSED,
+					     unsigned int        num_coords HB_UNUSED)
 {
   return (hb_coretext_aat_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc
index 76482ac..d63bc04 100644
--- a/src/hb-directwrite.cc
+++ b/src/hb-directwrite.cc
@@ -209,8 +209,8 @@
     data->fontFile->Release ();
   if (data->dwriteFactory) {
     if (data->fontFileLoader)
-      data->dwriteFactory->UnregisterFontFileLoader(data->fontFileLoader);
-    data->dwriteFactory->Release();
+      data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader);
+    data->dwriteFactory->Release ();
   }
   if (data->fontFileLoader)
     delete data->fontFileLoader;
@@ -258,8 +258,10 @@
 
 hb_directwrite_shaper_shape_plan_data_t *
 _hb_directwrite_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
-               const hb_feature_t *user_features HB_UNUSED,
-               unsigned int        num_user_features HB_UNUSED)
+					       const hb_feature_t *user_features HB_UNUSED,
+					       unsigned int        num_user_features HB_UNUSED,
+					       const int          *coords HB_UNUSED,
+					       unsigned int        num_coords HB_UNUSED)
 {
   return (hb_directwrite_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
@@ -540,12 +542,13 @@
  * shaper
  */
 
-hb_bool_t
-_hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
+static hb_bool_t
+_hb_directwrite_shape_full(hb_shape_plan_t    *shape_plan,
   hb_font_t          *font,
   hb_buffer_t        *buffer,
   const hb_feature_t *features,
-  unsigned int        num_features)
+  unsigned int        num_features,
+  float               lineWidth)
 {
   hb_face_t *face = font->face;
   hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
@@ -668,7 +671,7 @@
   DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties = (DWRITE_SHAPING_GLYPH_PROPERTIES*)
     malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES));
 
-  hr = analyzer->GetGlyphs (textString, textLength, fontFace, FALSE,
+  hr = analyzer->GetGlyphs (textString, textLength, fontFace, false,
     isRightToLeft, &runHead->mScript, localeName, NULL, &dwFeatures,
     featureRangeLengths, 1, maxGlyphCount, clusterMap, textProperties, glyphIndices,
     glyphProperties, &glyphCount);
@@ -716,7 +719,7 @@
   hr = analyzer->GetGlyphPlacements (textString,
     clusterMap, textProperties, textLength, glyphIndices,
     glyphProperties, glyphCount, fontFace, fontEmSize,
-    FALSE, isRightToLeft, &runHead->mScript, localeName,
+    false, isRightToLeft, &runHead->mScript, localeName,
     &dwFeatures, featureRangeLengths, 1,
     glyphAdvances, glyphOffsets);
 
@@ -726,9 +729,6 @@
     return false;
   }
 
-  // TODO: get lineWith from somewhere
-  float lineWidth = 0;
-
   IDWriteTextAnalyzer1* analyzer1;
   analyzer->QueryInterface (&analyzer1);
 
@@ -898,3 +898,37 @@
   /* Wow, done! */
   return true;
 }
+
+hb_bool_t
+_hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
+  hb_font_t          *font,
+  hb_buffer_t        *buffer,
+  const hb_feature_t *features,
+  unsigned int        num_features)
+{
+  return _hb_directwrite_shape_full(shape_plan, font, buffer,
+    features, num_features, 0);
+}
+
+/*
+ * Public [experimental] API
+ */
+
+hb_bool_t
+hb_directwrite_shape_experimental_width(hb_font_t          *font,
+  hb_buffer_t        *buffer,
+  const hb_feature_t *features,
+  unsigned int        num_features,
+  float               width)
+{
+  static char *shapers = "directwrite";
+  hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face,
+    &buffer->props, features, num_features, &shapers);
+  hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
+    features, num_features, width);
+
+  if (res)
+    buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
+
+  return res;
+}
diff --git a/src/hb-directwrite.h b/src/hb-directwrite.h
index 0e1b479..e743af2 100644
--- a/src/hb-directwrite.h
+++ b/src/hb-directwrite.h
@@ -29,6 +29,10 @@
 
 HB_BEGIN_DECLS
 
+HB_EXTERN hb_bool_t
+hb_directwrite_shape_experimental_width(hb_font_t *font, hb_buffer_t *buffer,
+  const hb_feature_t *features, unsigned int num_features, float width);
+
 HB_END_DECLS
 
 #endif /* HB_DIRECTWRITE_H */
diff --git a/src/hb-face-private.hh b/src/hb-face-private.hh
index c4266ff..43e7b1c 100644
--- a/src/hb-face-private.hh
+++ b/src/hb-face-private.hh
@@ -50,12 +50,16 @@
   void                      *user_data;
   hb_destroy_func_t          destroy;
 
-  unsigned int index;
-  mutable unsigned int upem;
-  mutable unsigned int num_glyphs;
+  unsigned int index;			/* Face index in a collection, zero-based. */
+  mutable unsigned int upem;		/* Units-per-EM. */
+  mutable unsigned int num_glyphs;	/* Number of glyphs. */
 
-  struct hb_shaper_data_t shaper_data;
+  struct hb_shaper_data_t shaper_data;	/* Various shaper data. */
 
+  /* Various non-shaping data. */
+  /* ... */
+
+  /* Cache */
   struct plan_node_t {
     hb_shape_plan_t *shape_plan;
     plan_node_t *next;
diff --git a/src/hb-face.cc b/src/hb-face.cc
index 6b563bc..1ba9707 100644
--- a/src/hb-face.cc
+++ b/src/hb-face.cc
@@ -28,15 +28,11 @@
 
 #include "hb-private.hh"
 
-#include "hb-ot-layout-private.hh"
-
-#include "hb-font-private.hh"
+#include "hb-face-private.hh"
 #include "hb-open-file-private.hh"
 #include "hb-ot-head-table.hh"
 #include "hb-ot-maxp-table.hh"
 
-#include <string.h>
-
 
 /*
  * hb_face_t
diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc
index e2ad240..ac6d4b0 100644
--- a/src/hb-fallback-shape.cc
+++ b/src/hb-fallback-shape.cc
@@ -73,7 +73,9 @@
 hb_fallback_shaper_shape_plan_data_t *
 _hb_fallback_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					    const hb_feature_t *user_features HB_UNUSED,
-					    unsigned int        num_user_features HB_UNUSED)
+					    unsigned int        num_user_features HB_UNUSED,
+					    const int          *coords HB_UNUSED,
+					    unsigned int        num_coords HB_UNUSED)
 {
   return (hb_fallback_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
diff --git a/src/hb-font-private.hh b/src/hb-font-private.hh
index 0b75577..53671d7 100644
--- a/src/hb-font-private.hh
+++ b/src/hb-font-private.hh
@@ -108,6 +108,10 @@
   unsigned int x_ppem;
   unsigned int y_ppem;
 
+  /* Font variation coordinates. */
+  unsigned int num_coords;
+  int *coords;
+
   hb_font_funcs_t   *klass;
   void              *user_data;
   hb_destroy_func_t  destroy;
@@ -116,8 +120,14 @@
 
 
   /* Convert from font-space to user-space */
-  inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, this->x_scale); }
-  inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, this->y_scale); }
+  inline int dir_scale (hb_direction_t direction)
+  { return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; }
+  inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, x_scale); }
+  inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); }
+  inline hb_position_t em_scalef_x (float v) { return em_scalef (v, this->x_scale); }
+  inline hb_position_t em_scalef_y (float v) { return em_scalef (v, this->y_scale); }
+  inline hb_position_t em_scale_dir (int16_t v, hb_direction_t direction)
+  { return em_scale (v, dir_scale (direction)); }
 
   /* Convert from parent-font user-space to our user-space */
   inline hb_position_t parent_scale_x_distance (hb_position_t v) {
@@ -292,24 +302,32 @@
 
   /* A bit higher-level, and with fallback */
 
+  inline void get_h_extents_with_fallback (hb_font_extents_t *extents)
+  {
+    if (!get_font_h_extents (extents))
+    {
+      extents->ascender = y_scale * .8;
+      extents->descender = extents->ascender - y_scale;
+      extents->line_gap = 0;
+    }
+  }
+  inline void get_v_extents_with_fallback (hb_font_extents_t *extents)
+  {
+    if (!get_font_v_extents (extents))
+    {
+      extents->ascender = x_scale / 2;
+      extents->descender = extents->ascender - x_scale;
+      extents->line_gap = 0;
+    }
+  }
+
   inline void get_extents_for_direction (hb_direction_t direction,
 					 hb_font_extents_t *extents)
   {
-    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) {
-      if (!get_font_h_extents (extents))
-      {
-	extents->ascender = y_scale * .8;
-	extents->descender = y_scale - extents->ascender;
-	extents->line_gap = 0;
-      }
-    } else {
-      if (!get_font_v_extents (extents))
-      {
-	extents->ascender = x_scale / 2;
-	extents->descender = x_scale - extents->ascender;
-	extents->line_gap = 0;
-      }
-    }
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+      get_h_extents_with_fallback (extents);
+    else
+      get_v_extents_with_fallback (extents);
   }
 
   inline void get_glyph_advance_for_direction (hb_codepoint_t glyph,
@@ -325,14 +343,38 @@
     }
   }
 
-  /* Internal only */
   inline void guess_v_origin_minus_h_origin (hb_codepoint_t glyph,
 					     hb_position_t *x, hb_position_t *y)
   {
     *x = get_glyph_h_advance (glyph) / 2;
 
-    /* TODO use font_extents.ascender */
-    *y = y_scale;
+    /* TODO cache this somehow?! */
+    hb_font_extents_t extents;
+    get_h_extents_with_fallback (&extents);
+    *y = extents.ascender;
+  }
+
+  inline void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph,
+						hb_position_t *x, hb_position_t *y)
+  {
+    if (!get_glyph_h_origin (glyph, x, y) &&
+	 get_glyph_v_origin (glyph, x, y))
+    {
+      hb_position_t dx, dy;
+      guess_v_origin_minus_h_origin (glyph, &dx, &dy);
+      *x -= dx; *y -= dy;
+    }
+  }
+  inline void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph,
+						hb_position_t *x, hb_position_t *y)
+  {
+    if (!get_glyph_v_origin (glyph, x, y) &&
+	 get_glyph_h_origin (glyph, x, y))
+    {
+      hb_position_t dx, dy;
+      guess_v_origin_minus_h_origin (glyph, &dx, &dy);
+      *x += dx; *y += dy;
+    }
   }
 
   inline void get_glyph_origin_for_direction (hb_codepoint_t glyph,
@@ -340,25 +382,9 @@
 					      hb_position_t *x, hb_position_t *y)
   {
     if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
-    {
-      if (!get_glyph_h_origin (glyph, x, y) &&
-	   get_glyph_v_origin (glyph, x, y))
-      {
-	hb_position_t dx, dy;
-	guess_v_origin_minus_h_origin (glyph, &dx, &dy);
-	*x -= dx; *y -= dy;
-      }
-    }
+      get_glyph_h_origin_with_fallback (glyph, x, y);
     else
-    {
-      if (!get_glyph_v_origin (glyph, x, y) &&
-	   get_glyph_h_origin (glyph, x, y))
-      {
-	hb_position_t dx, dy;
-	guess_v_origin_minus_h_origin (glyph, &dx, &dy);
-	*x += dx; *y += dy;
-      }
-    }
+      get_glyph_v_origin_with_fallback (glyph, x, y);
   }
 
   inline void add_glyph_h_origin (hb_codepoint_t glyph,
@@ -366,7 +392,7 @@
   {
     hb_position_t origin_x, origin_y;
 
-    get_glyph_h_origin (glyph, &origin_x, &origin_y);
+    get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y);
 
     *x += origin_x;
     *y += origin_y;
@@ -376,7 +402,7 @@
   {
     hb_position_t origin_x, origin_y;
 
-    get_glyph_v_origin (glyph, &origin_x, &origin_y);
+    get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y);
 
     *x += origin_x;
     *y += origin_y;
@@ -398,7 +424,7 @@
   {
     hb_position_t origin_x, origin_y;
 
-    get_glyph_h_origin (glyph, &origin_x, &origin_y);
+    get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y);
 
     *x -= origin_x;
     *y -= origin_y;
@@ -408,7 +434,7 @@
   {
     hb_position_t origin_x, origin_y;
 
-    get_glyph_v_origin (glyph, &origin_x, &origin_y);
+    get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y);
 
     *x -= origin_x;
     *y -= origin_y;
@@ -504,7 +530,6 @@
     return false;
   }
 
-  private:
   inline hb_position_t em_scale (int16_t v, int scale)
   {
     int upem = face->get_upem ();
@@ -512,6 +537,10 @@
     scaled += scaled >= 0 ? upem/2 : -upem/2; /* Round. */
     return (hb_position_t) (scaled / upem);
   }
+  inline hb_position_t em_scalef (float v, int scale)
+  {
+    return (hb_position_t) (v * scale / face->get_upem ());
+  }
 };
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
diff --git a/src/hb-font.cc b/src/hb-font.cc
index 08fcd64..ea45501 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -28,14 +28,7 @@
 
 #include "hb-private.hh"
 
-#include "hb-ot-layout-private.hh"
-
 #include "hb-font-private.hh"
-#include "hb-open-file-private.hh"
-#include "hb-ot-head-table.hh"
-#include "hb-ot-maxp-table.hh"
-
-#include <string.h>
 
 
 /*
@@ -1165,6 +1158,8 @@
   font->x_ppem = parent->x_ppem;
   font->y_ppem = parent->y_ppem;
 
+  /* TODO: copy variation coordinates. */
+
   return font;
 }
 
@@ -1194,6 +1189,9 @@
     0, /* x_ppem */
     0, /* y_ppem */
 
+    0, /* num_coords */
+    NULL, /* coords */
+
     const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */
     NULL, /* user_data */
     NULL, /* destroy */
@@ -1248,6 +1246,8 @@
   hb_face_destroy (font->face);
   hb_font_funcs_destroy (font->klass);
 
+  free (font->coords);
+
   free (font);
 }
 
@@ -1536,6 +1536,114 @@
   if (y_ppem) *y_ppem = font->y_ppem;
 }
 
+/*
+ * Variations
+ */
+
+static void
+_hb_font_adopt_var_coords_normalized (hb_font_t *font,
+				      int *coords, /* 2.14 normalized */
+				      unsigned int coords_length)
+{
+  free (font->coords);
+
+  font->coords = coords;
+  font->num_coords = coords_length;
+}
+
+/**
+ * hb_font_set_variations:
+ *
+ * Since: 1.4.2
+ */
+void
+hb_font_set_variations (hb_font_t *font,
+			const hb_variation_t *variations,
+			unsigned int variations_length)
+{
+  if (font->immutable)
+    return;
+
+  if (!variations_length)
+  {
+    hb_font_set_var_coords_normalized (font, NULL, 0);
+    return;
+  }
+
+  unsigned int coords_length = hb_ot_var_get_axis_count (font->face);
+
+  int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : NULL;
+  if (unlikely (coords_length && !normalized))
+    return;
+
+  hb_ot_var_normalize_variations (font->face,
+				  variations, variations_length,
+				  normalized, coords_length);
+  _hb_font_adopt_var_coords_normalized (font, normalized, coords_length);
+}
+
+/**
+ * hb_font_set_var_coords_design:
+ *
+ * Since: 1.4.2
+ */
+void
+hb_font_set_var_coords_design (hb_font_t *font,
+			       const float *coords,
+			       unsigned int coords_length)
+{
+  if (font->immutable)
+    return;
+
+  int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : NULL;
+  if (unlikely (coords_length && !normalized))
+    return;
+
+  hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized);
+  _hb_font_adopt_var_coords_normalized (font, normalized, coords_length);
+}
+
+/**
+ * hb_font_set_var_coords_normalized:
+ *
+ * Since: 1.4.2
+ */
+void
+hb_font_set_var_coords_normalized (hb_font_t *font,
+				   const int *coords, /* 2.14 normalized */
+				   unsigned int coords_length)
+{
+  if (font->immutable)
+    return;
+
+  int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : NULL;
+  if (unlikely (coords_length && !copy))
+    return;
+
+  if (coords_length)
+    memcpy (copy, coords, coords_length * sizeof (coords[0]));
+
+  _hb_font_adopt_var_coords_normalized (font, copy, coords_length);
+}
+
+/**
+ * hb_font_set_var_coords_normalized:
+ *
+ * Return value is valid as long as variation coordinates of the font
+ * are not modified.
+ *
+ * Since: 1.4.2
+ */
+const int *
+hb_font_get_var_coords_normalized (hb_font_t *font,
+				   unsigned int *length)
+{
+  if (length)
+    *length = font->num_coords;
+
+  return font->coords;
+}
+
 
 #ifndef HB_DISABLE_DEPRECATED
 
diff --git a/src/hb-font.h b/src/hb-font.h
index 2b6ab50..e2e5979 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -603,6 +603,24 @@
 		  unsigned int *x_ppem,
 		  unsigned int *y_ppem);
 
+HB_EXTERN void
+hb_font_set_variations (hb_font_t *font,
+			const hb_variation_t *variations,
+			unsigned int variations_length);
+
+HB_EXTERN void
+hb_font_set_var_coords_design (hb_font_t *font,
+			       const float *coords,
+			       unsigned int coords_length);
+
+HB_EXTERN void
+hb_font_set_var_coords_normalized (hb_font_t *font,
+				   const int *coords, /* 2.14 normalized */
+				   unsigned int coords_length);
+
+HB_EXTERN const int *
+hb_font_get_var_coords_normalized (hb_font_t *font,
+				   unsigned int *length);
 
 HB_END_DECLS
 
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index 2b06c59..48d6a0e 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -36,6 +36,7 @@
 #include "hb-cache-private.hh" // Maybe use in the future?
 
 #include FT_ADVANCES_H
+#include FT_MULTIPLE_MASTERS_H
 #include FT_TRUETYPE_TABLES_H
 
 
@@ -616,6 +617,28 @@
 		    ft_face->size->metrics.y_ppem);
 #endif
 
+#ifdef HAVE_FT_GET_VAR_BLEND_COORDINATES
+  FT_MM_Var *mm_var = NULL;
+  if (!FT_Get_MM_Var (ft_face, &mm_var))
+  {
+    FT_Fixed *ft_coords = (FT_Fixed *) calloc (mm_var->num_axis, sizeof (FT_Fixed));
+    int *coords = (int *) calloc (mm_var->num_axis, sizeof (int));
+    if (coords && ft_coords)
+    {
+      if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords))
+      {
+	for (unsigned int i = 0; i < mm_var->num_axis; ++i)
+	  coords[i] = ft_coords[i] >>= 2;
+
+	hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis);
+      }
+      free (coords);
+      free (ft_coords);
+    }
+    free (mm_var);
+  }
+#endif
+
   return font;
 }
 
@@ -718,6 +741,20 @@
     FT_Set_Transform (ft_face, &matrix, NULL);
   }
 
+  unsigned int num_coords;
+  const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
+  if (num_coords)
+  {
+    FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed));
+    if (ft_coords)
+    {
+      for (unsigned int i = 0; i < num_coords; i++)
+	ft_coords[i] = coords[i] << 2;
+      FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords);
+      free (ft_coords);
+    }
+  }
+
   ft_face->generic.data = blob;
   ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob;
 
diff --git a/src/hb-glib.cc b/src/hb-glib.cc
index e203524..2b91b5b 100644
--- a/src/hb-glib.cc
+++ b/src/hb-glib.cc
@@ -382,6 +382,7 @@
   return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
 }
 
+#if GLIB_CHECK_VERSION(2,31,10)
 /**
  * hb_glib_blob_create:
  *
@@ -398,3 +399,4 @@
 			 g_bytes_ref (gbytes),
 			 (hb_destroy_func_t) g_bytes_unref);
 }
+#endif
diff --git a/src/hb-glib.h b/src/hb-glib.h
index 12c3e3b..5f04183 100644
--- a/src/hb-glib.h
+++ b/src/hb-glib.h
@@ -46,9 +46,10 @@
 HB_EXTERN hb_unicode_funcs_t *
 hb_glib_get_unicode_funcs (void);
 
+#if GLIB_CHECK_VERSION(2,31,10)
 HB_EXTERN hb_blob_t *
 hb_glib_blob_create (GBytes *gbytes);
-
+#endif
 
 HB_END_DECLS
 
diff --git a/src/hb-gobject-structs.cc b/src/hb-gobject-structs.cc
index 6bd6336..fef0024 100644
--- a/src/hb-gobject-structs.cc
+++ b/src/hb-gobject-structs.cc
@@ -78,3 +78,6 @@
 HB_DEFINE_VALUE_TYPE (glyph_position)
 HB_DEFINE_VALUE_TYPE (segment_properties)
 HB_DEFINE_VALUE_TYPE (user_data_key)
+
+HB_DEFINE_VALUE_TYPE (ot_math_glyph_variant)
+HB_DEFINE_VALUE_TYPE (ot_math_glyph_part)
diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index c32318d..a2d90db 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -195,7 +195,9 @@
 hb_graphite2_shaper_shape_plan_data_t *
 _hb_graphite2_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					     const hb_feature_t *user_features HB_UNUSED,
-					     unsigned int        num_user_features HB_UNUSED)
+					     unsigned int        num_user_features HB_UNUSED,
+					     const int          *coords HB_UNUSED,
+					     unsigned int        num_coords HB_UNUSED)
 {
   return (hb_graphite2_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh
index 5357ddc..f208419 100644
--- a/src/hb-open-file-private.hh
+++ b/src/hb-open-file-private.hh
@@ -142,7 +142,7 @@
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
   FixedVersion<>version;	/* Version of the TTC Header (1.0),
 				 * 0x00010000u */
-  ArrayOf<OffsetTo<OffsetTable, ULONG>, ULONG>
+  ArrayOf<LOffsetTo<OffsetTable>, ULONG>
 		table;		/* Array of offsets to the OffsetTable for each font
 				 * from the beginning of the file */
   public:
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index df683ca..d90d68c 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -30,6 +30,7 @@
 #define HB_OPEN_TYPE_PRIVATE_HH
 
 #include "hb-private.hh"
+#include "hb-face-private.hh"
 
 
 namespace OT {
@@ -105,7 +106,7 @@
   inline unsigned int get_size (void) const { return (size); }
 
 #define DEFINE_SIZE_UNION(size, _member) \
-  DEFINE_INSTANCE_ASSERTION (this->u._member.static_size == (size)); \
+  DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \
   static const unsigned int min_size = (size)
 
 #define DEFINE_SIZE_MIN(size) \
@@ -650,7 +651,9 @@
   DEFINE_SIZE_STATIC (Size);
 };
 
+typedef	IntType<int8_t	, 1> CHAR;	/* 8-bit signed integer. */
 typedef	IntType<uint8_t	, 1> BYTE;	/* 8-bit unsigned integer. */
+typedef	IntType<int8_t	, 1> INT8;	/* 8-bit signed integer. */
 typedef IntType<uint16_t, 2> USHORT;	/* 16-bit unsigned integer. */
 typedef IntType<int16_t,  2> SHORT;	/* 16-bit signed integer. */
 typedef IntType<uint32_t, 4> ULONG;	/* 32-bit unsigned integer. */
@@ -805,6 +808,7 @@
     if (unlikely (!c->check_struct (this))) return_trace (false);
     unsigned int offset = *this;
     if (unlikely (!offset)) return_trace (true);
+    if (unlikely (!c->check_range (base, offset))) return_trace (false);
     const Type &obj = StructAtOffset<Type> (base, offset);
     return_trace (likely (obj.sanitize (c)) || neuter (c));
   }
@@ -815,6 +819,7 @@
     if (unlikely (!c->check_struct (this))) return_trace (false);
     unsigned int offset = *this;
     if (unlikely (!offset)) return_trace (true);
+    if (unlikely (!c->check_range (base, offset))) return_trace (false);
     const Type &obj = StructAtOffset<Type> (base, offset);
     return_trace (likely (obj.sanitize (c, user_data)) || neuter (c));
   }
@@ -825,6 +830,7 @@
   }
   DEFINE_SIZE_STATIC (sizeof(OffsetType));
 };
+template <typename Type> struct LOffsetTo : OffsetTo<Type, ULONG> {};
 template <typename Base, typename OffsetType, typename Type>
 static inline const Type& operator + (const Base &base, const OffsetTo<Type, OffsetType> &offset) { return offset (base); }
 template <typename Base, typename OffsetType, typename Type>
@@ -946,10 +952,11 @@
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), array);
 };
+template <typename Type> struct LArrayOf : ArrayOf<Type, ULONG> {};
 
 /* Array of Offset's */
-template <typename Type>
-struct OffsetArrayOf : ArrayOf<OffsetTo<Type> > {};
+template <typename Type, typename OffsetType=USHORT>
+struct OffsetArrayOf : ArrayOf<OffsetTo<Type, OffsetType> > {};
 
 /* Array of offsets relative to the beginning of the array itself. */
 template <typename Type>
@@ -1057,6 +1064,104 @@
 };
 
 
+/* Lazy struct and blob loaders. */
+
+/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */
+template <typename T>
+struct hb_lazy_loader_t
+{
+  inline void init (hb_face_t *face_)
+  {
+    face = face_;
+    instance = NULL;
+  }
+
+  inline void fini (void)
+  {
+    if (instance && instance != &OT::Null(T))
+    {
+      instance->fini();
+      free (instance);
+    }
+  }
+
+  inline const T* get (void) const
+  {
+  retry:
+    T *p = (T *) hb_atomic_ptr_get (&instance);
+    if (unlikely (!p))
+    {
+      p = (T *) calloc (1, sizeof (T));
+      if (unlikely (!p))
+        p = const_cast<T *> (&OT::Null(T));
+      else
+	p->init (face);
+      if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), NULL, p)))
+      {
+	if (p != &OT::Null(T))
+	  p->fini ();
+	goto retry;
+      }
+    }
+    return p;
+  }
+
+  inline const T* operator-> (void) const
+  {
+    return get ();
+  }
+
+  private:
+  hb_face_t *face;
+  T *instance;
+};
+
+/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */
+template <typename T>
+struct hb_lazy_table_loader_t
+{
+  inline void init (hb_face_t *face_)
+  {
+    face = face_;
+    instance = NULL;
+    blob = NULL;
+  }
+
+  inline void fini (void)
+  {
+    hb_blob_destroy (blob);
+  }
+
+  inline const T* get (void) const
+  {
+  retry:
+    T *p = (T *) hb_atomic_ptr_get (&instance);
+    if (unlikely (!p))
+    {
+      hb_blob_t *blob_ = OT::Sanitizer<T>::sanitize (face->reference_table (T::tableTag));
+      p = const_cast<T *>(OT::Sanitizer<T>::lock_instance (blob_));
+      if (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), NULL, p))
+      {
+	hb_blob_destroy (blob_);
+	goto retry;
+      }
+      blob = blob_;
+    }
+    return p;
+  }
+
+  inline const T* operator-> (void) const
+  {
+    return get();
+  }
+
+  private:
+  hb_face_t *face;
+  T *instance;
+  mutable hb_blob_t *blob;
+};
+
+
 } /* namespace OT */
 
 
diff --git a/src/hb-ot-cbdt-table.hh b/src/hb-ot-cbdt-table.hh
new file mode 100644
index 0000000..0a7fbf5
--- /dev/null
+++ b/src/hb-ot-cbdt-table.hh
@@ -0,0 +1,384 @@
+/*
+ * Copyright © 2016  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Seigo Nonaka
+ */
+
+#ifndef HB_OT_CBDT_TABLE_HH
+#define HB_OT_CBDT_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+namespace OT {
+
+struct SmallGlyphMetrics
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  inline void get_extents (hb_glyph_extents_t *extents) const
+  {
+    extents->x_bearing = bearingX;
+    extents->y_bearing = bearingY;
+    extents->width = width;
+    extents->height = -height;
+  }
+
+  BYTE height;
+  BYTE width;
+  CHAR bearingX;
+  CHAR bearingY;
+  BYTE advance;
+
+  DEFINE_SIZE_STATIC(5);
+};
+
+struct BigGlyphMetrics : SmallGlyphMetrics
+{
+  CHAR vertBearingX;
+  CHAR vertBearingY;
+  BYTE vertAdvance;
+
+  DEFINE_SIZE_STATIC(8);
+};
+
+struct SBitLineMetrics
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  CHAR ascender;
+  CHAR decender;
+  BYTE widthMax;
+  CHAR caretSlopeNumerator;
+  CHAR caretSlopeDenominator;
+  CHAR caretOffset;
+  CHAR minOriginSB;
+  CHAR minAdvanceSB;
+  CHAR maxBeforeBL;
+  CHAR minAfterBL;
+  CHAR padding1;
+  CHAR padding2;
+
+  DEFINE_SIZE_STATIC(12);
+};
+
+
+/*
+ * Index Subtables.
+ */
+
+struct IndexSubtableHeader
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  USHORT indexFormat;
+  USHORT imageFormat;
+  ULONG imageDataOffset;
+
+  DEFINE_SIZE_STATIC(8);
+};
+
+template <typename OffsetType>
+struct IndexSubtableFormat1Or3
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  c->check_array (offsetArrayZ, offsetArrayZ[0].static_size, glyph_count + 1));
+  }
+
+  bool get_image_data (unsigned int idx,
+		       unsigned int *offset,
+		       unsigned int *length) const
+  {
+    if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx]))
+      return false;
+
+    *offset = header.imageDataOffset + offsetArrayZ[idx];
+    *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx];
+    return true;
+  }
+
+  IndexSubtableHeader header;
+  Offset<OffsetType> offsetArrayZ[VAR];
+
+  DEFINE_SIZE_ARRAY(8, offsetArrayZ);
+};
+
+struct IndexSubtableFormat1 : IndexSubtableFormat1Or3<ULONG> {};
+struct IndexSubtableFormat3 : IndexSubtableFormat1Or3<USHORT> {};
+
+struct IndexSubtable
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.header.sanitize (c)) return_trace (false);
+    switch (u.header.indexFormat) {
+    case 1: return_trace (u.format1.sanitize (c, glyph_count));
+    case 3: return_trace (u.format3.sanitize (c, glyph_count));
+    default:return_trace (true);
+    }
+  }
+
+  inline bool get_extents (hb_glyph_extents_t *extents) const
+  {
+    switch (u.header.indexFormat) {
+    case 2: case 5: /* TODO */
+    case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */
+    default:return (false);
+    }
+  }
+
+  bool get_image_data (unsigned int idx,
+		       unsigned int *offset,
+		       unsigned int *length,
+		       unsigned int *format) const
+  {
+    *format = u.header.imageFormat;
+    switch (u.header.indexFormat) {
+    case 1: return u.format1.get_image_data (idx, offset, length);
+    case 3: return u.format3.get_image_data (idx, offset, length);
+    default: return false;
+    }
+  }
+
+  protected:
+  union {
+  IndexSubtableHeader	header;
+  IndexSubtableFormat1	format1;
+  IndexSubtableFormat3	format3;
+  /* TODO: Format 2, 4, 5. */
+  } u;
+  public:
+  DEFINE_SIZE_UNION (8, header);
+};
+
+struct IndexSubtableRecord
+{
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  firstGlyphIndex <= lastGlyphIndex &&
+		  offsetToSubtable.sanitize (c, this, lastGlyphIndex - firstGlyphIndex + 1));
+  }
+
+  inline bool get_extents (hb_glyph_extents_t *extents) const
+  {
+    return (this+offsetToSubtable).get_extents (extents);
+  }
+
+  bool get_image_data (unsigned int gid,
+		       unsigned int *offset,
+		       unsigned int *length,
+		       unsigned int *format) const
+  {
+    if (gid < firstGlyphIndex || gid > lastGlyphIndex)
+    {
+      return false;
+    }
+    return (this+offsetToSubtable).get_image_data (gid - firstGlyphIndex,
+						   offset, length, format);
+  }
+
+  USHORT firstGlyphIndex;
+  USHORT lastGlyphIndex;
+  LOffsetTo<IndexSubtable> offsetToSubtable;
+
+  DEFINE_SIZE_STATIC(8);
+};
+
+struct IndexSubtableArray
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_array (&indexSubtablesZ, indexSubtablesZ[0].static_size, count)))
+      return_trace (false);
+    for (unsigned int i = 0; i < count; i++)
+      if (unlikely (!indexSubtablesZ[i].sanitize (c, this)))
+	return_trace (false);
+    return_trace (true);
+  }
+
+  public:
+  const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const
+  {
+    for (unsigned int i = 0; i < numTables; ++i)
+    {
+      unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex;
+      unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex;
+      if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) {
+        return &indexSubtablesZ[i];
+      }
+    }
+    return NULL;
+  }
+
+  protected:
+  IndexSubtableRecord indexSubtablesZ[VAR];
+
+  public:
+  DEFINE_SIZE_ARRAY(0, indexSubtablesZ);
+};
+
+struct BitmapSizeTable
+{
+  friend struct CBLC;
+
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) &&
+		  c->check_range (&(base+indexSubtableArrayOffset), indexTablesSize) &&
+		  horizontal.sanitize (c) &&
+		  vertical.sanitize (c));
+  }
+
+  const IndexSubtableRecord *find_table (hb_codepoint_t glyph, const void *base) const
+  {
+    return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables);
+  }
+
+  protected:
+  LOffsetTo<IndexSubtableArray> indexSubtableArrayOffset;
+  ULONG indexTablesSize;
+  ULONG numberOfIndexSubtables;
+  ULONG colorRef;
+  SBitLineMetrics horizontal;
+  SBitLineMetrics vertical;
+  USHORT startGlyphIndex;
+  USHORT endGlyphIndex;
+  BYTE ppemX;
+  BYTE ppemY;
+  BYTE bitDepth;
+  CHAR flags;
+
+public:
+  DEFINE_SIZE_STATIC(48);
+};
+
+
+/*
+ * Glyph Bitmap Data Formats.
+ */
+
+struct GlyphBitmapDataFormat17
+{
+  SmallGlyphMetrics glyphMetrics;
+  ULONG dataLen;
+  BYTE dataZ[VAR];
+
+  DEFINE_SIZE_ARRAY(9, dataZ);
+};
+
+
+/*
+ * CBLC -- Color Bitmap Location Table
+ */
+
+#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C')
+
+struct CBLC
+{
+  static const hb_tag_t tableTag = HB_OT_TAG_CBLC;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  likely (version.major == 2 || version.major == 3) &&
+		  sizeTables.sanitize (c, this));
+  }
+
+  public:
+  const IndexSubtableRecord *find_table (hb_codepoint_t glyph,
+					 unsigned int *x_ppem, unsigned int *y_ppem) const
+  {
+    /* TODO: Make it possible to select strike. */
+
+    unsigned int count = sizeTables.len;
+    for (uint32_t i = 0; i < count; ++i)
+    {
+      unsigned int startGlyphIndex = sizeTables.array[i].startGlyphIndex;
+      unsigned int endGlyphIndex = sizeTables.array[i].endGlyphIndex;
+      if (startGlyphIndex <= glyph && glyph <= endGlyphIndex)
+      {
+	*x_ppem = sizeTables[i].ppemX;
+	*y_ppem = sizeTables[i].ppemY;
+	return sizeTables[i].find_table (glyph, this);
+      }
+    }
+
+    return NULL;
+  }
+
+  protected:
+  FixedVersion<>		version;
+  LArrayOf<BitmapSizeTable>	sizeTables;
+
+  public:
+  DEFINE_SIZE_ARRAY(8, sizeTables);
+};
+
+/*
+ * CBDT -- Color Bitmap Data Table
+ */
+#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T')
+
+struct CBDT
+{
+  static const hb_tag_t tableTag = HB_OT_TAG_CBDT;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  likely (version.major == 2 || version.major == 3));
+  }
+
+  protected:
+  FixedVersion<>version;
+  BYTE dataZ[VAR];
+
+  public:
+  DEFINE_SIZE_ARRAY(4, dataZ);
+};
+
+} /* namespace OT */
+
+#endif /* HB_OT_CBDT_TABLE_HH */
diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index d7a94a1..3a53a1c 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -380,9 +380,9 @@
   }
 
   UINT24	varSelector;	/* Variation selector. */
-  OffsetTo<DefaultUVS, ULONG>
+  LOffsetTo<DefaultUVS>
 		defaultUVS;	/* Offset to Default UVS Table. May be 0. */
-  OffsetTo<NonDefaultUVS, ULONG>
+  LOffsetTo<NonDefaultUVS>
 		nonDefaultUVS;	/* Offset to Non-Default UVS Table. May be 0. */
   public:
   DEFINE_SIZE_STATIC (11);
@@ -486,7 +486,7 @@
 
   USHORT	platformID;	/* Platform ID. */
   USHORT	encodingID;	/* Platform-specific encoding ID. */
-  OffsetTo<CmapSubtable, ULONG>
+  LOffsetTo<CmapSubtable>
 		subtable;	/* Byte offset from beginning of table to the subtable for this encoding. */
   public:
   DEFINE_SIZE_STATIC (8);
diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc
index 0b7e31b..009db20 100644
--- a/src/hb-ot-font.cc
+++ b/src/hb-ot-font.cc
@@ -31,11 +31,13 @@
 #include "hb-font-private.hh"
 
 #include "hb-ot-cmap-table.hh"
+#include "hb-ot-cbdt-table.hh"
 #include "hb-ot-glyf-table.hh"
 #include "hb-ot-head-table.hh"
 #include "hb-ot-hhea-table.hh"
 #include "hb-ot-hmtx-table.hh"
 #include "hb-ot-os2-table.hh"
+#include "hb-ot-var-hvar-table.hh"
 //#include "hb-ot-post-table.hh"
 
 
@@ -47,16 +49,22 @@
   unsigned short ascender;
   unsigned short descender;
   unsigned short line_gap;
+  bool has_font_extents;
 
-  const OT::_mtx *table;
+  const OT::hmtxvmtx *table;
   hb_blob_t *blob;
 
+  const OT::HVARVVAR *var;
+  hb_blob_t *var_blob;
+
   inline void init (hb_face_t *face,
 		    hb_tag_t _hea_tag,
 		    hb_tag_t _mtx_tag,
-		    hb_tag_t os2_tag)
+		    hb_tag_t _var_tag,
+		    hb_tag_t os2_tag,
+		    unsigned int default_advance = 0)
   {
-    this->default_advance = face->get_upem ();
+    this->default_advance = default_advance ? default_advance : face->get_upem ();
 
     bool got_font_extents = false;
     if (os2_tag)
@@ -82,10 +90,13 @@
       this->ascender = _hea->ascender;
       this->descender = _hea->descender;
       this->line_gap = _hea->lineGap;
+      got_font_extents = (this->ascender | this->descender) != 0;
     }
     hb_blob_destroy (_hea_blob);
 
-    this->blob = OT::Sanitizer<OT::_mtx>::sanitize (face->reference_table (_mtx_tag));
+    this->has_font_extents = got_font_extents;
+
+    this->blob = OT::Sanitizer<OT::hmtxvmtx>::sanitize (face->reference_table (_mtx_tag));
 
     /* Cap num_metrics() and num_advances() based on table length. */
     unsigned int len = hb_blob_get_length (this->blob);
@@ -101,15 +112,20 @@
       hb_blob_destroy (this->blob);
       this->blob = hb_blob_get_empty ();
     }
-    this->table = OT::Sanitizer<OT::_mtx>::lock_instance (this->blob);
+    this->table = OT::Sanitizer<OT::hmtxvmtx>::lock_instance (this->blob);
+
+    this->var_blob = OT::Sanitizer<OT::HVARVVAR>::sanitize (face->reference_table (_var_tag));
+    this->var = OT::Sanitizer<OT::HVARVVAR>::lock_instance (this->var_blob);
   }
 
   inline void fini (void)
   {
     hb_blob_destroy (this->blob);
+    hb_blob_destroy (this->var_blob);
   }
 
-  inline unsigned int get_advance (hb_codepoint_t glyph) const
+  inline unsigned int get_advance (hb_codepoint_t  glyph,
+				   hb_font_t      *font) const
   {
     if (unlikely (glyph >= this->num_metrics))
     {
@@ -122,10 +138,8 @@
 	return this->default_advance;
     }
 
-    if (glyph >= this->num_advances)
-      glyph = this->num_advances - 1;
-
-    return this->table->longMetric[glyph].advance;
+    return this->table->longMetric[MIN (glyph, this->num_advances - 1)].advance
+	 + this->var->get_advance_var (glyph, font->coords, font->num_coords); // TODO Optimize?!
   }
 };
 
@@ -202,6 +216,91 @@
   }
 };
 
+struct hb_ot_face_cbdt_accelerator_t
+{
+  hb_blob_t *cblc_blob;
+  hb_blob_t *cbdt_blob;
+  const OT::CBLC *cblc;
+  const OT::CBDT *cbdt;
+
+  unsigned int cbdt_len;
+  float upem;
+
+  inline void init (hb_face_t *face)
+  {
+    upem = face->get_upem();
+
+    cblc_blob = OT::Sanitizer<OT::CBLC>::sanitize (face->reference_table (HB_OT_TAG_CBLC));
+    cbdt_blob = OT::Sanitizer<OT::CBDT>::sanitize (face->reference_table (HB_OT_TAG_CBDT));
+    cbdt_len = hb_blob_get_length (cbdt_blob);
+
+    if (hb_blob_get_length (cblc_blob) == 0) {
+      cblc = NULL;
+      cbdt = NULL;
+      return;  /* Not a bitmap font. */
+    }
+    cblc = OT::Sanitizer<OT::CBLC>::lock_instance (cblc_blob);
+    cbdt = OT::Sanitizer<OT::CBDT>::lock_instance (cbdt_blob);
+
+  }
+
+  inline void fini (void)
+  {
+    hb_blob_destroy (this->cblc_blob);
+    hb_blob_destroy (this->cbdt_blob);
+  }
+
+  inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
+  {
+    unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */
+
+    if (cblc == NULL)
+      return false;  // Not a color bitmap font.
+
+    const OT::IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem);
+    if (subtable_record == NULL)
+      return false;
+
+    if (subtable_record->get_extents (extents))
+      return true;
+
+    unsigned int image_offset = 0, image_length = 0, image_format = 0;
+    if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format))
+      return false;
+
+    {
+      /* TODO Move the following into CBDT struct when adding more formats. */
+
+      if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length))
+	return false;
+
+      switch (image_format)
+      {
+	case 17: {
+	  if (unlikely (image_length < OT::GlyphBitmapDataFormat17::min_size))
+	    return false;
+
+	  const OT::GlyphBitmapDataFormat17& glyphFormat17 =
+	      OT::StructAtOffset<OT::GlyphBitmapDataFormat17> (this->cbdt, image_offset);
+	  glyphFormat17.glyphMetrics.get_extents (extents);
+	}
+	break;
+	default:
+	  // TODO: Support other image formats.
+	  return false;
+      }
+    }
+
+    /* Convert to the font units. */
+    extents->x_bearing *= upem / (float) x_ppem;
+    extents->y_bearing *= upem / (float) y_ppem;
+    extents->width *= upem / (float) x_ppem;
+    extents->height *= upem / (float) y_ppem;
+
+    return true;
+  }
+};
+
 typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
 					  hb_codepoint_t codepoint,
 					  hb_codepoint_t *glyph);
@@ -264,7 +363,11 @@
     if (!subtable) subtable = cmap->find_subtable (0, 2);
     if (!subtable) subtable = cmap->find_subtable (0, 1);
     if (!subtable) subtable = cmap->find_subtable (0, 0);
-    if (!subtable)(subtable = cmap->find_subtable (3, 0)) && (symbol = true);
+    if (!subtable)
+    {
+      subtable = cmap->find_subtable (3, 0);
+      if (subtable) symbol = true;
+    }
     /* Meh. */
     if (!subtable) subtable = &OT::Null(OT::CmapSubtable);
 
@@ -326,54 +429,13 @@
   }
 };
 
-template <typename T>
-struct hb_lazy_loader_t
-{
-  inline void init (hb_face_t *face_)
-  {
-    face = face_;
-    instance = NULL;
-  }
-
-  inline void fini (void)
-  {
-    if (instance && instance != &OT::Null(T))
-    {
-      instance->fini();
-      free (instance);
-    }
-  }
-
-  inline const T* operator-> (void) const
-  {
-  retry:
-    T *p = (T *) hb_atomic_ptr_get (&instance);
-    if (unlikely (!p))
-    {
-      p = (T *) calloc (1, sizeof (T));
-      if (unlikely (!p))
-        return &OT::Null(T);
-      p->init (face);
-      if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), NULL, p)))
-      {
-	p->fini ();
-	goto retry;
-      }
-    }
-    return p;
-  }
-
-  private:
-  hb_face_t *face;
-  T *instance;
-};
-
 struct hb_ot_font_t
 {
   hb_ot_face_cmap_accelerator_t cmap;
   hb_ot_face_metrics_accelerator_t h_metrics;
   hb_ot_face_metrics_accelerator_t v_metrics;
-  hb_lazy_loader_t<hb_ot_face_glyf_accelerator_t> glyf;
+  OT::hb_lazy_loader_t<hb_ot_face_glyf_accelerator_t> glyf;
+  OT::hb_lazy_loader_t<hb_ot_face_cbdt_accelerator_t> cbdt;
 };
 
 
@@ -386,9 +448,11 @@
     return NULL;
 
   ot_font->cmap.init (face);
-  ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, HB_OT_TAG_os2);
-  ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, HB_TAG_NONE); /* TODO Can we do this lazily? */
+  ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, HB_OT_TAG_HVAR, HB_OT_TAG_os2);
+  ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, HB_OT_TAG_VVAR, HB_TAG_NONE,
+			   ot_font->h_metrics.ascender - ot_font->h_metrics.descender); /* TODO Can we do this lazily? */
   ot_font->glyf.init (face);
+  ot_font->cbdt.init (face);
 
   return ot_font;
 }
@@ -400,6 +464,7 @@
   ot_font->h_metrics.fini ();
   ot_font->v_metrics.fini ();
   ot_font->glyf.fini ();
+  ot_font->cbdt.fini ();
 
   free (ot_font);
 }
@@ -430,23 +495,23 @@
 }
 
 static hb_position_t
-hb_ot_get_glyph_h_advance (hb_font_t *font HB_UNUSED,
+hb_ot_get_glyph_h_advance (hb_font_t *font,
 			   void *font_data,
 			   hb_codepoint_t glyph,
 			   void *user_data HB_UNUSED)
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return font->em_scale_x (ot_font->h_metrics.get_advance (glyph));
+  return font->em_scale_x (ot_font->h_metrics.get_advance (glyph, font));
 }
 
 static hb_position_t
-hb_ot_get_glyph_v_advance (hb_font_t *font HB_UNUSED,
+hb_ot_get_glyph_v_advance (hb_font_t *font,
 			   void *font_data,
 			   hb_codepoint_t glyph,
 			   void *user_data HB_UNUSED)
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph));
+  return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph, font));
 }
 
 static hb_bool_t
@@ -458,6 +523,9 @@
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
   bool ret = ot_font->glyf->get_extents (glyph, extents);
+  if (!ret)
+    ret = ot_font->cbdt->get_extents (glyph, extents);
+  // TODO Hook up side-bearings variations.
   extents->x_bearing = font->em_scale_x (extents->x_bearing);
   extents->y_bearing = font->em_scale_y (extents->y_bearing);
   extents->width     = font->em_scale_x (extents->width);
@@ -475,7 +543,8 @@
   metrics->ascender = font->em_scale_y (ot_font->h_metrics.ascender);
   metrics->descender = font->em_scale_y (ot_font->h_metrics.descender);
   metrics->line_gap = font->em_scale_y (ot_font->h_metrics.line_gap);
-  return true;
+  // TODO Hook up variations.
+  return ot_font->h_metrics.has_font_extents;
 }
 
 static hb_bool_t
@@ -488,7 +557,8 @@
   metrics->ascender = font->em_scale_x (ot_font->v_metrics.ascender);
   metrics->descender = font->em_scale_x (ot_font->v_metrics.descender);
   metrics->line_gap = font->em_scale_x (ot_font->v_metrics.line_gap);
-  return true;
+  // TODO Hook up variations.
+  return ot_font->v_metrics.has_font_extents;
 }
 
 static hb_font_funcs_t *static_ot_funcs = NULL;
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index a9606b3..30aa625 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -50,10 +50,8 @@
   DEFINE_SIZE_STATIC (4);
 };
 
-struct _mtx
+struct hmtxvmtx
 {
-  static const hb_tag_t tableTag = HB_TAG('_','m','t','x');
-
   static const hb_tag_t hmtxTag	= HB_OT_TAG_hmtx;
   static const hb_tag_t vmtxTag	= HB_OT_TAG_vmtx;
 
@@ -91,10 +89,10 @@
   DEFINE_SIZE_ARRAY2 (0, longMetric, leadingBearingX);
 };
 
-struct hmtx : _mtx {
+struct hmtx : hmtxvmtx {
   static const hb_tag_t tableTag	= HB_OT_TAG_hmtx;
 };
-struct vmtx : _mtx {
+struct vmtx : hmtxvmtx {
   static const hb_tag_t tableTag	= HB_OT_TAG_vmtx;
 };
 
diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index 34fa1b7..180e5f0 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -507,7 +507,7 @@
   { return this+featureParams; }
 
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<Feature>::sanitize_closure_t *closure) const
+			const Record<Feature>::sanitize_closure_t *closure = NULL) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c))))
@@ -731,8 +731,8 @@
     inline void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
     inline bool more (void) { return i < c->glyphArray.len; }
     inline void next (void) { i++; }
-    inline uint16_t get_glyph (void) { return c->glyphArray[i]; }
-    inline uint16_t get_coverage (void) { return i; }
+    inline hb_codepoint_t get_glyph (void) { return c->glyphArray[i]; }
+    inline unsigned int get_coverage (void) { return i; }
 
     private:
     const struct CoverageFormat1 *c;
@@ -829,26 +829,33 @@
 
   public:
   /* Older compilers need this to be public. */
-  struct Iter {
-    inline void init (const CoverageFormat2 &c_) {
+  struct Iter
+  {
+    inline void init (const CoverageFormat2 &c_)
+    {
       c = &c_;
       coverage = 0;
       i = 0;
       j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0;
     }
     inline bool more (void) { return i < c->rangeRecord.len; }
-    inline void next (void) {
-      coverage++;
-      if (j == c->rangeRecord[i].end) {
+    inline void next (void)
+    {
+      if (j >= c->rangeRecord[i].end)
+      {
         i++;
 	if (more ())
+	{
 	  j = c->rangeRecord[i].start;
+	  coverage = c->rangeRecord[i].value;
+	}
 	return;
       }
+      coverage++;
       j++;
     }
-    inline uint16_t get_glyph (void) { return j; }
-    inline uint16_t get_coverage (void) { return coverage; }
+    inline hb_codepoint_t get_glyph (void) { return j; }
+    inline unsigned int get_coverage (void) { return coverage; }
 
     private:
     const struct CoverageFormat2 *c;
@@ -957,14 +964,14 @@
       default:                   break;
       }
     }
-    inline uint16_t get_glyph (void) {
+    inline hb_codepoint_t get_glyph (void) {
       switch (format) {
       case 1: return u.format1.get_glyph ();
       case 2: return u.format2.get_glyph ();
       default:return 0;
       }
     }
-    inline uint16_t get_coverage (void) {
+    inline unsigned int get_coverage (void) {
       switch (format) {
       case 1: return u.format1.get_coverage ();
       case 2: return u.format2.get_coverage ();
@@ -1162,11 +1169,388 @@
 
 
 /*
+ * Item Variation Store
+ */
+
+struct VarRegionAxis
+{
+  inline float evaluate (int coord) const
+  {
+    int start = startCoord, peak = peakCoord, end = endCoord;
+
+    /* TODO Move these to sanitize(). */
+    if (unlikely (start > peak || peak > end))
+      return 1.;
+    if (unlikely (start < 0 && end > 0 && peak != 0))
+      return 1.;
+
+    if (peak == 0 || coord == peak)
+      return 1.;
+
+    if (coord <= start || end <= coord)
+      return 0.;
+
+    /* Interpolate */
+    if (coord < peak)
+      return float (coord - start) / (peak - start);
+    else
+      return float (end - coord) / (end - peak);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+    /* TODO Handle invalid start/peak/end configs, so we don't
+     * have to do that at runtime. */
+  }
+
+  public:
+  F2DOT14	startCoord;
+  F2DOT14	peakCoord;
+  F2DOT14	endCoord;
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct VarRegionList
+{
+  inline float evaluate (unsigned int region_index,
+			 int *coords, unsigned int coord_len) const
+  {
+    if (unlikely (region_index >= regionCount))
+      return 0.;
+
+    const VarRegionAxis *axes = axesZ + (region_index * axisCount);
+
+    float v = 1.;
+    unsigned int count = MIN (coord_len, (unsigned int) axisCount);
+    for (unsigned int i = 0; i < count; i++)
+    {
+      float factor = axes[i].evaluate (coords[i]);
+      if (factor == 0.)
+        return 0.;
+      v *= factor;
+    }
+    return v;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  c->check_array (axesZ, axesZ[0].static_size,
+				  (unsigned int) axisCount * (unsigned int) regionCount));
+  }
+
+  protected:
+  USHORT	axisCount;
+  USHORT	regionCount;
+  VarRegionAxis	axesZ[VAR];
+  public:
+  DEFINE_SIZE_ARRAY (4, axesZ);
+};
+
+struct VarData
+{
+  inline unsigned int get_row_size (void) const
+  { return shortCount + regionIndices.len; }
+
+  inline unsigned int get_size (void) const
+  { return itemCount * get_row_size (); }
+
+  inline float get_delta (unsigned int inner,
+			  int *coords, unsigned int coord_count,
+			  const VarRegionList &regions) const
+  {
+    if (unlikely (inner >= itemCount))
+      return 0.;
+
+   unsigned int count = regionIndices.len;
+   unsigned int scount = shortCount;
+
+   const BYTE *bytes = &StructAfter<BYTE> (regionIndices);
+   const BYTE *row = bytes + inner * (scount + count);
+
+   float delta = 0.;
+   unsigned int i = 0;
+
+   const SHORT *scursor = reinterpret_cast<const SHORT *> (row);
+   for (; i < scount; i++)
+   {
+     float scalar = regions.evaluate (regionIndices.array[i], coords, coord_count);
+     delta += scalar * *scursor++;
+   }
+   const INT8 *bcursor = reinterpret_cast<const INT8 *> (scursor);
+   for (; i < count; i++)
+   {
+     float scalar = regions.evaluate (regionIndices.array[i], coords, coord_count);
+     delta += scalar * *bcursor++;
+   }
+
+   return delta;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  regionIndices.sanitize(c) &&
+		  shortCount <= regionIndices.len &&
+		  c->check_array (&StructAfter<BYTE> (regionIndices),
+				  get_row_size (), itemCount));
+  }
+
+  protected:
+  USHORT		itemCount;
+  USHORT		shortCount;
+  ArrayOf<USHORT>	regionIndices;
+  BYTE			bytesX[VAR];
+  public:
+  DEFINE_SIZE_ARRAY2 (6, regionIndices, bytesX);
+};
+
+struct VariationStore
+{
+  inline float get_delta (unsigned int outer, unsigned int inner,
+			  int *coords, unsigned int coord_count) const
+  {
+    if (unlikely (outer >= dataSets.len))
+      return 0.;
+
+    return (this+dataSets[outer]).get_delta (inner,
+					     coords, coord_count,
+					     this+regions);
+  }
+
+  inline float get_delta (unsigned int index,
+			  int *coords, unsigned int coord_count) const
+  {
+    unsigned int outer = index >> 16;
+    unsigned int inner = index & 0xFFFF;
+    return get_delta (outer, inner, coords, coord_count);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  format == 1 &&
+		  regions.sanitize (c, this) &&
+		  dataSets.sanitize (c, this));
+  }
+
+  protected:
+  USHORT				format;
+  LOffsetTo<VarRegionList>		regions;
+  OffsetArrayOf<VarData, ULONG>		dataSets;
+  public:
+  DEFINE_SIZE_ARRAY (8, dataSets);
+};
+
+/*
+ * Feature Variations
+ */
+
+struct ConditionFormat1
+{
+  friend struct Condition;
+
+  private:
+  inline bool evaluate (const int *coords, unsigned int coord_len) const
+  {
+    int coord = axisIndex < coord_len ? coords[axisIndex] : 0;
+    return filterRangeMinValue <= coord && coord <= filterRangeMaxValue;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  USHORT	format;		/* Format identifier--format = 1 */
+  USHORT	axisIndex;
+  F2DOT14	filterRangeMinValue;
+  F2DOT14	filterRangeMaxValue;
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct Condition
+{
+  inline bool evaluate (const int *coords, unsigned int coord_len) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.evaluate (coords, coord_len);
+    default:return false;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format) {
+    case 1: return_trace (u.format1.sanitize (c));
+    default:return_trace (true);
+    }
+  }
+
+  protected:
+  union {
+  USHORT		format;		/* Format identifier */
+  ConditionFormat1	format1;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+struct ConditionSet
+{
+  inline bool evaluate (const int *coords, unsigned int coord_len) const
+  {
+    unsigned int count = conditions.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+conditions.array[i]).evaluate (coords, coord_len))
+        return false;
+    return true;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (conditions.sanitize (c, this));
+  }
+
+  protected:
+  OffsetArrayOf<Condition, ULONG> conditions;
+  public:
+  DEFINE_SIZE_ARRAY (2, conditions);
+};
+
+struct FeatureTableSubstitutionRecord
+{
+  friend struct FeatureTableSubstitution;
+
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && feature.sanitize (c, base));
+  }
+
+  protected:
+  USHORT		featureIndex;
+  LOffsetTo<Feature>	feature;
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct FeatureTableSubstitution
+{
+  inline const Feature *find_substitute (unsigned int feature_index) const
+  {
+    unsigned int count = substitutions.len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      const FeatureTableSubstitutionRecord &record = substitutions.array[i];
+      if (record.featureIndex == feature_index)
+	return &(this+record.feature);
+    }
+    return NULL;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+		  likely (version.major == 1) &&
+		  substitutions.sanitize (c, this));
+  }
+
+  protected:
+  FixedVersion<>	version;	/* Version--0x00010000u */
+  ArrayOf<FeatureTableSubstitutionRecord>
+			substitutions;
+  public:
+  DEFINE_SIZE_ARRAY (6, substitutions);
+};
+
+struct FeatureVariationRecord
+{
+  friend struct FeatureVariations;
+
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (conditions.sanitize (c, base) &&
+		  substitutions.sanitize (c, base));
+  }
+
+  protected:
+  LOffsetTo<ConditionSet>
+			conditions;
+  LOffsetTo<FeatureTableSubstitution>
+			substitutions;
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct FeatureVariations
+{
+  static const unsigned int NOT_FOUND_INDEX = 0xFFFFFFFFu;
+
+  inline bool find_index (const int *coords, unsigned int coord_len,
+			  unsigned int *index) const
+  {
+    unsigned int count = varRecords.len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      const FeatureVariationRecord &record = varRecords.array[i];
+      if ((this+record.conditions).evaluate (coords, coord_len))
+      {
+	*index = i;
+	return true;
+      }
+    }
+    *index = NOT_FOUND_INDEX;
+    return false;
+  }
+
+  inline const Feature *find_substitute (unsigned int variations_index,
+					 unsigned int feature_index) const
+  {
+    const FeatureVariationRecord &record = varRecords[variations_index];
+    return (this+record.substitutions).find_substitute (feature_index);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+		  likely (version.major == 1) &&
+		  varRecords.sanitize (c, this));
+  }
+
+  protected:
+  FixedVersion<>	version;	/* Version--0x00010000u */
+  LArrayOf<FeatureVariationRecord>
+			varRecords;
+  public:
+  DEFINE_SIZE_ARRAY (8, varRecords);
+};
+
+
+/*
  * Device Tables
  */
 
-struct Device
+struct HintingDevice
 {
+  friend struct Device;
+
+  private:
 
   inline hb_position_t get_x_delta (hb_font_t *font) const
   { return get_delta (font->x_ppem, font->x_scale); }
@@ -1235,6 +1619,101 @@
   DEFINE_SIZE_ARRAY (6, deltaValue);
 };
 
+struct VariationDevice
+{
+  friend struct Device;
+
+  private:
+
+  inline hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store) const
+  { return font->em_scalef_x (get_delta (font, store)); }
+
+  inline hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store) const
+  { return font->em_scalef_y (get_delta (font, store)); }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  private:
+
+  inline float get_delta (hb_font_t *font, const VariationStore &store) const
+  {
+    return store.get_delta (outerIndex, innerIndex, font->coords, font->num_coords);
+  }
+
+  protected:
+  USHORT	outerIndex;
+  USHORT	innerIndex;
+  USHORT	deltaFormat;	/* Format identifier for this table: 0x0x8000 */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct DeviceHeader
+{
+  protected:
+  USHORT		reserved1;
+  USHORT		reserved2;
+  public:
+  USHORT		format;		/* Format identifier */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct Device
+{
+  inline hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store=Null(VariationStore)) const
+  {
+    switch (u.b.format)
+    {
+    case 1: case 2: case 3:
+      return u.hinting.get_x_delta (font);
+    case 0x8000:
+      return u.variation.get_x_delta (font, store);
+    default:
+      return 0;
+    }
+  }
+  inline hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store=Null(VariationStore)) const
+  {
+    switch (u.b.format)
+    {
+    case 1: case 2: case 3:
+      return u.hinting.get_y_delta (font);
+    case 0x8000:
+      return u.variation.get_y_delta (font, store);
+    default:
+      return 0;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.b.format.sanitize (c)) return_trace (false);
+    switch (u.b.format) {
+    case 1: case 2: case 3:
+      return_trace (u.hinting.sanitize (c));
+    case 0x8000:
+      return_trace (u.variation.sanitize (c));
+    default:
+      return_trace (true);
+    }
+  }
+
+  protected:
+  union {
+  DeviceHeader		b;
+  HintingDevice		hinting;
+  VariationDevice	variation;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (6, b);
+};
+
 
 } /* namespace OT */
 
diff --git a/src/hb-ot-layout-gdef-table.hh b/src/hb-ot-layout-gdef-table.hh
index 2b4bc5a..552df0f 100644
--- a/src/hb-ot-layout-gdef-table.hh
+++ b/src/hb-ot-layout-gdef-table.hh
@@ -97,7 +97,7 @@
   friend struct CaretValue;
 
   private:
-  inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id HB_UNUSED) const
+  inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction) const
   {
     return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate);
   }
@@ -146,11 +146,11 @@
 {
   friend struct CaretValue;
 
-  inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id HB_UNUSED) const
+  inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, const VariationStore &var_store) const
   {
     return HB_DIRECTION_IS_HORIZONTAL (direction) ?
-           font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font) :
-           font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font);
+           font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) :
+           font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font, var_store);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -172,12 +172,15 @@
 
 struct CaretValue
 {
-  inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const
+  inline hb_position_t get_caret_value (hb_font_t *font,
+					hb_direction_t direction,
+					hb_codepoint_t glyph_id,
+					const VariationStore &var_store) const
   {
     switch (u.format) {
-    case 1: return u.format1.get_caret_value (font, direction, glyph_id);
+    case 1: return u.format1.get_caret_value (font, direction);
     case 2: return u.format2.get_caret_value (font, direction, glyph_id);
-    case 3: return u.format3.get_caret_value (font, direction, glyph_id);
+    case 3: return u.format3.get_caret_value (font, direction, var_store);
     default:return 0;
     }
   }
@@ -210,6 +213,7 @@
   inline unsigned int get_lig_carets (hb_font_t *font,
 				      hb_direction_t direction,
 				      hb_codepoint_t glyph_id,
+				      const VariationStore &var_store,
 				      unsigned int start_offset,
 				      unsigned int *caret_count /* IN/OUT */,
 				      hb_position_t *caret_array /* OUT */) const
@@ -218,7 +222,7 @@
       const OffsetTo<CaretValue> *array = carets.sub_array (start_offset, caret_count);
       unsigned int count = *caret_count;
       for (unsigned int i = 0; i < count; i++)
-	caret_array[i] = (this+array[i]).get_caret_value (font, direction, glyph_id);
+	caret_array[i] = (this+array[i]).get_caret_value (font, direction, glyph_id, var_store);
     }
 
     return carets.len;
@@ -244,6 +248,7 @@
   inline unsigned int get_lig_carets (hb_font_t *font,
 				      hb_direction_t direction,
 				      hb_codepoint_t glyph_id,
+				      const VariationStore &var_store,
 				      unsigned int start_offset,
 				      unsigned int *caret_count /* IN/OUT */,
 				      hb_position_t *caret_array /* OUT */) const
@@ -256,7 +261,7 @@
       return 0;
     }
     const LigGlyph &lig_glyph = this+ligGlyph[index];
-    return lig_glyph.get_lig_carets (font, direction, glyph_id, start_offset, caret_count, caret_array);
+    return lig_glyph.get_lig_carets (font, direction, glyph_id, var_store, start_offset, caret_count, caret_array);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -290,7 +295,7 @@
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
-  ArrayOf<OffsetTo<Coverage, ULONG> >
+  ArrayOf<LOffsetTo<Coverage> >
 		coverage;		/* Array of long offsets to mark set
 					 * coverage tables */
   public:
@@ -367,11 +372,17 @@
 				      unsigned int start_offset,
 				      unsigned int *caret_count /* IN/OUT */,
 				      hb_position_t *caret_array /* OUT */) const
-  { return (this+ligCaretList).get_lig_carets (font, direction, glyph_id, start_offset, caret_count, caret_array); }
+  { return (this+ligCaretList).get_lig_carets (font,
+					       direction, glyph_id, get_var_store(),
+					       start_offset, caret_count, caret_array); }
 
-  inline bool has_mark_sets (void) const { return version.to_int () >= 0x00010002u && markGlyphSetsDef[0] != 0; }
+  inline bool has_mark_sets (void) const { return version.to_int () >= 0x00010002u && markGlyphSetsDef != 0; }
   inline bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const
-  { return version.to_int () >= 0x00010002u && (this+markGlyphSetsDef[0]).covers (set_index, glyph_id); }
+  { return version.to_int () >= 0x00010002u && (this+markGlyphSetsDef).covers (set_index, glyph_id); }
+
+  inline bool has_var_store (void) const { return version.to_int () >= 0x00010003u && varStore != 0; }
+  inline const VariationStore &get_var_store (void) const
+  { return version.to_int () >= 0x00010003u ? this+varStore : Null(VariationStore); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -382,10 +393,10 @@
 		  attachList.sanitize (c, this) &&
 		  ligCaretList.sanitize (c, this) &&
 		  markAttachClassDef.sanitize (c, this) &&
-		  (version.to_int () < 0x00010002u || markGlyphSetsDef[0].sanitize (c, this)));
+		  (version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) &&
+		  (version.to_int () < 0x00010003u || varStore.sanitize (c, this)));
   }
 
-
   /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing
    * glyph class and other bits, and high 8-bit gthe mark attachment type (if any).
    * Not to be confused with lookup_props which is very similar. */
@@ -410,7 +421,7 @@
 
   protected:
   FixedVersion<>version;		/* Version of the GDEF table--currently
-					 * 0x00010002u */
+					 * 0x00010003u */
   OffsetTo<ClassDef>
 		glyphClassDef;		/* Offset to class definition table
 					 * for glyph type--from beginning of
@@ -428,12 +439,17 @@
 					 * mark attachment type--from beginning
 					 * of GDEF header (may be Null) */
   OffsetTo<MarkGlyphSets>
-		markGlyphSetsDef[VAR];	/* Offset to the table of mark set
+		markGlyphSetsDef;	/* Offset to the table of mark set
 					 * definitions--from beginning of GDEF
 					 * header (may be NULL).  Introduced
-					 * in version 00010002. */
+					 * in version 0x00010002. */
+  LOffsetTo<VariationStore>
+		varStore;		/* Offset to the table of Item Variation
+					 * Store--from beginning of GDEF
+					 * header (may be NULL).  Introduced
+					 * in version 0x00010003. */
   public:
-  DEFINE_SIZE_ARRAY (12, markGlyphSetsDef);
+  DEFINE_SIZE_MIN (12);
 };
 
 
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index bbe390c..952fd60 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -103,18 +103,17 @@
   inline unsigned int get_size (void) const
   { return get_len () * Value::static_size; }
 
-  void apply_value (hb_font_t            *font,
-		    hb_direction_t        direction,
+  void apply_value (hb_apply_context_t   *c,
 		    const void           *base,
 		    const Value          *values,
 		    hb_glyph_position_t  &glyph_pos) const
   {
-    unsigned int x_ppem, y_ppem;
     unsigned int format = *this;
-    hb_bool_t horizontal = HB_DIRECTION_IS_HORIZONTAL (direction);
-
     if (!format) return;
 
+    hb_font_t *font = c->font;
+    hb_bool_t horizontal = HB_DIRECTION_IS_HORIZONTAL (c->direction);
+
     if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++));
     if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++));
     if (format & xAdvance) {
@@ -129,27 +128,29 @@
 
     if (!has_device ()) return;
 
-    x_ppem = font->x_ppem;
-    y_ppem = font->y_ppem;
+    bool use_x_device = font->x_ppem || font->num_coords;
+    bool use_y_device = font->y_ppem || font->num_coords;
 
-    if (!x_ppem && !y_ppem) return;
+    if (!use_x_device && !use_y_device) return;
+
+    const VariationStore &store = c->var_store;
 
     /* pixel -> fractional pixel */
     if (format & xPlaDevice) {
-      if (x_ppem) glyph_pos.x_offset  += (base + get_device (values)).get_x_delta (font);
+      if (use_x_device) glyph_pos.x_offset  += (base + get_device (values)).get_x_delta (font, store);
       values++;
     }
     if (format & yPlaDevice) {
-      if (y_ppem) glyph_pos.y_offset  += (base + get_device (values)).get_y_delta (font);
+      if (use_y_device) glyph_pos.y_offset  += (base + get_device (values)).get_y_delta (font, store);
       values++;
     }
     if (format & xAdvDevice) {
-      if (horizontal && x_ppem) glyph_pos.x_advance += (base + get_device (values)).get_x_delta (font);
+      if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values)).get_x_delta (font, store);
       values++;
     }
     if (format & yAdvDevice) {
       /* y_advance values grow downward but font-space grows upward, hence negation */
-      if (!horizontal && y_ppem) glyph_pos.y_advance -= (base + get_device (values)).get_y_delta (font);
+      if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values)).get_y_delta (font, store);
       values++;
     }
   }
@@ -231,11 +232,12 @@
 
 struct AnchorFormat1
 {
-  inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
+  inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
 			  hb_position_t *x, hb_position_t *y) const
   {
-      *x = font->em_scale_x (xCoordinate);
-      *y = font->em_scale_y (yCoordinate);
+    hb_font_t *font = c->font;
+    *x = font->em_scale_x (xCoordinate);
+    *y = font->em_scale_y (yCoordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -254,18 +256,19 @@
 
 struct AnchorFormat2
 {
-  inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
+  inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id,
 			  hb_position_t *x, hb_position_t *y) const
   {
-      unsigned int x_ppem = font->x_ppem;
-      unsigned int y_ppem = font->y_ppem;
-      hb_position_t cx, cy;
-      hb_bool_t ret;
+    hb_font_t *font = c->font;
+    unsigned int x_ppem = font->x_ppem;
+    unsigned int y_ppem = font->y_ppem;
+    hb_position_t cx, cy;
+    hb_bool_t ret;
 
-      ret = (x_ppem || y_ppem) &&
-             font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
-      *x = ret && x_ppem ? cx : font->em_scale_x (xCoordinate);
-      *y = ret && y_ppem ? cy : font->em_scale_y (yCoordinate);
+    ret = (x_ppem || y_ppem) &&
+	   font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
+    *x = ret && x_ppem ? cx : font->em_scale_x (xCoordinate);
+    *y = ret && y_ppem ? cy : font->em_scale_y (yCoordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -285,16 +288,17 @@
 
 struct AnchorFormat3
 {
-  inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id HB_UNUSED,
+  inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
 			  hb_position_t *x, hb_position_t *y) const
   {
-      *x = font->em_scale_x (xCoordinate);
-      *y = font->em_scale_y (yCoordinate);
+    hb_font_t *font = c->font;
+    *x = font->em_scale_x (xCoordinate);
+    *y = font->em_scale_y (yCoordinate);
 
-      if (font->x_ppem)
-	*x += (this+xDeviceTable).get_x_delta (font);
-      if (font->y_ppem)
-	*y += (this+yDeviceTable).get_x_delta (font);
+    if (font->x_ppem || font->num_coords)
+      *x += (this+xDeviceTable).get_x_delta (font, c->var_store);
+    if (font->y_ppem || font->num_coords)
+      *y += (this+yDeviceTable).get_y_delta (font, c->var_store);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -321,14 +325,14 @@
 
 struct Anchor
 {
-  inline void get_anchor (hb_font_t *font, hb_codepoint_t glyph_id,
+  inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id,
 			  hb_position_t *x, hb_position_t *y) const
   {
     *x = *y = 0;
     switch (u.format) {
-    case 1: u.format1.get_anchor (font, glyph_id, x, y); return;
-    case 2: u.format2.get_anchor (font, glyph_id, x, y); return;
-    case 3: u.format3.get_anchor (font, glyph_id, x, y); return;
+    case 1: u.format1.get_anchor (c, glyph_id, x, y); return;
+    case 2: u.format2.get_anchor (c, glyph_id, x, y); return;
+    case 3: u.format3.get_anchor (c, glyph_id, x, y); return;
     default:						 return;
     }
   }
@@ -370,7 +374,7 @@
   {
     TRACE_SANITIZE (this);
     if (!c->check_struct (this)) return_trace (false);
-    if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return_trace (false);
+    if (unlikely (_hb_unsigned_int_mul_overflows (rows, cols))) return_trace (false);
     unsigned int count = rows * cols;
     if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return_trace (false);
     for (unsigned int i = 0; i < count; i++)
@@ -428,8 +432,8 @@
 
     hb_position_t mark_x, mark_y, base_x, base_y;
 
-    mark_anchor.get_anchor (c->font, buffer->cur().codepoint, &mark_x, &mark_y);
-    glyph_anchor.get_anchor (c->font, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
+    mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
+    glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
 
     hb_glyph_position_t &o = buffer->cur_pos();
     o.x_offset = base_x - mark_x;
@@ -472,8 +476,7 @@
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
-    valueFormat.apply_value (c->font, c->direction, this,
-			     values, buffer->cur_pos());
+    valueFormat.apply_value (c, this, values, buffer->cur_pos());
 
     buffer->idx++;
     return_trace (true);
@@ -523,7 +526,7 @@
 
     if (likely (index >= valueCount)) return_trace (false);
 
-    valueFormat.apply_value (c->font, c->direction, this,
+    valueFormat.apply_value (c, this,
 			     &values[index * valueFormat.get_len ()],
 			     buffer->cur_pos());
 
@@ -640,10 +643,8 @@
         min = mid + 1;
       else
       {
-	valueFormats[0].apply_value (c->font, c->direction, this,
-				     &record->values[0], buffer->cur_pos());
-	valueFormats[1].apply_value (c->font, c->direction, this,
-				     &record->values[len1], buffer->pos[pos]);
+	valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
+	valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
 	if (len2)
 	  pos++;
 	buffer->idx = pos;
@@ -689,7 +690,7 @@
     (this+coverage).add_coverage (c->input);
     unsigned int count = pairSet.len;
     for (unsigned int i = 0; i < count; i++)
-      (this+pairSet[i]).collect_glyphs (c, &valueFormat1);
+      (this+pairSet[i]).collect_glyphs (c, valueFormat);
   }
 
   inline const Coverage &get_coverage (void) const
@@ -708,7 +709,7 @@
     skippy_iter.reset (buffer->idx, 1);
     if (!skippy_iter.next ()) return_trace (false);
 
-    return_trace ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx));
+    return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -717,11 +718,11 @@
 
     if (!c->check_struct (this)) return_trace (false);
 
-    unsigned int len1 = valueFormat1.get_len ();
-    unsigned int len2 = valueFormat2.get_len ();
+    unsigned int len1 = valueFormat[0].get_len ();
+    unsigned int len2 = valueFormat[1].get_len ();
     PairSet::sanitize_closure_t closure = {
       this,
-      &valueFormat1,
+      valueFormat,
       len1,
       1 + len1 + len2
     };
@@ -734,10 +735,10 @@
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
-  ValueFormat	valueFormat1;		/* Defines the types of data in
+  ValueFormat	valueFormat[2];		/* [0] Defines the types of data in
 					 * ValueRecord1--for the first glyph
 					 * in the pair--may be zero (0) */
-  ValueFormat	valueFormat2;		/* Defines the types of data in
+					/* [1] Defines the types of data in
 					 * ValueRecord2--for the second glyph
 					 * in the pair--may be zero (0) */
   OffsetArrayOf<PairSet>
@@ -790,10 +791,8 @@
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
 
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
-    valueFormat1.apply_value (c->font, c->direction, this,
-			      v, buffer->cur_pos());
-    valueFormat2.apply_value (c->font, c->direction, this,
-			      v + len1, buffer->pos[skippy_iter.idx]);
+    valueFormat1.apply_value (c, this, v, buffer->cur_pos());
+    valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
 
     buffer->idx = skippy_iter.idx;
     if (len2)
@@ -931,8 +930,8 @@
     unsigned int j = skippy_iter.idx;
 
     hb_position_t entry_x, entry_y, exit_x, exit_y;
-    (this+this_record.exitAnchor).get_anchor (c->font, buffer->info[i].codepoint, &exit_x, &exit_y);
-    (this+next_record.entryAnchor).get_anchor (c->font, buffer->info[j].codepoint, &entry_x, &entry_y);
+    (this+this_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
+    (this+next_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
 
     hb_glyph_position_t *pos = buffer->pos;
 
@@ -1519,8 +1518,6 @@
     const OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
     return_trace (list.sanitize (c, this));
   }
-  public:
-  DEFINE_SIZE_STATIC (10);
 };
 
 
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 22031f4..66fcb3f 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -41,7 +41,10 @@
   {
     TRACE_CLOSURE (this);
     Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+    for (iter.init (this+coverage); iter.more (); iter.next ())
+    {
+      /* TODO Switch to range-based API to work around malicious fonts.
+       * https://github.com/behdad/harfbuzz/issues/363 */
       hb_codepoint_t glyph_id = iter.get_glyph ();
       if (c->glyphs->has (glyph_id))
 	c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
@@ -52,7 +55,10 @@
   {
     TRACE_COLLECT_GLYPHS (this);
     Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+    for (iter.init (this+coverage); iter.more (); iter.next ())
+    {
+      /* TODO Switch to range-based API to work around malicious fonts.
+       * https://github.com/behdad/harfbuzz/issues/363 */
       hb_codepoint_t glyph_id = iter.get_glyph ();
       c->input->add (glyph_id);
       c->output->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
@@ -120,7 +126,11 @@
   {
     TRACE_CLOSURE (this);
     Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+    unsigned int count = substitute.len;
+    for (iter.init (this+coverage); iter.more (); iter.next ())
+    {
+      if (unlikely (iter.get_coverage () >= count))
+        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
 	c->glyphs->add (substitute[iter.get_coverage ()]);
     }
@@ -130,7 +140,11 @@
   {
     TRACE_COLLECT_GLYPHS (this);
     Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+    unsigned int count = substitute.len;
+    for (iter.init (this+coverage); iter.more (); iter.next ())
+    {
+      if (unlikely (iter.get_coverage () >= count))
+        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
       c->input->add (iter.get_glyph ());
       c->output->add (substitute[iter.get_coverage ()]);
     }
@@ -321,7 +335,11 @@
   {
     TRACE_CLOSURE (this);
     Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+    unsigned int count = sequence.len;
+    for (iter.init (this+coverage); iter.more (); iter.next ())
+    {
+      if (unlikely (iter.get_coverage () >= count))
+        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
 	(this+sequence[iter.get_coverage ()]).closure (c);
     }
@@ -439,7 +457,11 @@
   {
     TRACE_CLOSURE (this);
     Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+    unsigned int count = alternateSet.len;
+    for (iter.init (this+coverage); iter.more (); iter.next ())
+    {
+      if (unlikely (iter.get_coverage () >= count))
+        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ())) {
 	const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
 	unsigned int count = alt_set.len;
@@ -453,7 +475,11 @@
   {
     TRACE_COLLECT_GLYPHS (this);
     Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+    unsigned int count = alternateSet.len;
+    for (iter.init (this+coverage); iter.more (); iter.next ())
+    {
+      if (unlikely (iter.get_coverage () >= count))
+        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
       c->input->add (iter.get_glyph ());
       const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
       unsigned int count = alt_set.len;
@@ -762,7 +788,11 @@
   {
     TRACE_CLOSURE (this);
     Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+    unsigned int count = ligatureSet.len;
+    for (iter.init (this+coverage); iter.more (); iter.next ())
+    {
+      if (unlikely (iter.get_coverage () >= count))
+        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
 	(this+ligatureSet[iter.get_coverage ()]).closure (c);
     }
@@ -772,7 +802,11 @@
   {
     TRACE_COLLECT_GLYPHS (this);
     Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+    unsigned int count = ligatureSet.len;
+    for (iter.init (this+coverage); iter.more (); iter.next ())
+    {
+      if (unlikely (iter.get_coverage () >= count))
+        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
       c->input->add (iter.get_glyph ());
       (this+ligatureSet[iter.get_coverage ()]).collect_glyphs (c);
     }
@@ -923,7 +957,11 @@
 
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
     Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ()) {
+    count = substitute.len;
+    for (iter.init (this+coverage); iter.more (); iter.next ())
+    {
+      if (unlikely (iter.get_coverage () >= count))
+        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
 	c->glyphs->add (substitute[iter.get_coverage ()]);
     }
@@ -1273,8 +1311,6 @@
     const OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
     return_trace (list.sanitize (c, this));
   }
-  public:
-  DEFINE_SIZE_STATIC (10);
 };
 
 
@@ -1285,28 +1321,10 @@
 
   const GDEF &gdef = *hb_ot_layout_from_face (font->face)->gdef;
   unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
   {
-    unsigned int props = gdef.get_glyph_props (info[i].codepoint);
-    if (!props)
-    {
-      /* Never mark default-ignorables as marks.
-       * They won't get in the way of lookups anyway,
-       * but having them as mark will cause them to be skipped
-       * over if the lookup-flag says so, but at least for the
-       * Mongolian variation selectors, looks like Uniscribe
-       * marks them as non-mark.  Some Mongolian fonts without
-       * GDEF rely on this.  Another notable character that
-       * this applies to is COMBINING GRAPHEME JOINER. */
-      props = (_hb_glyph_info_get_general_category (&info[i]) !=
-	       HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK ||
-	       _hb_glyph_info_is_default_ignorable (&info[i])) ?
-	      HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH :
-	      HB_OT_LAYOUT_GLYPH_PROPS_MARK;
-    }
-    _hb_glyph_info_set_glyph_props (&info[i], props);
-    _hb_glyph_info_clear_lig_props (&info[i]);
+    _hb_glyph_info_set_glyph_props (&buffer->info[i], gdef.get_glyph_props (buffer->info[i].codepoint));
+    _hb_glyph_info_clear_lig_props (&buffer->info[i]);
     buffer->info[i].syllable() = 0;
   }
 }
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 997d225..b7a0122 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -469,6 +469,7 @@
   unsigned int lookup_props;
   const GDEF &gdef;
   bool has_glyph_classes;
+  const VariationStore &var_store;
   skipping_iterator_t iter_input, iter_context;
   unsigned int lookup_index;
   unsigned int debug_depth;
@@ -487,6 +488,7 @@
 			lookup_props (0),
 			gdef (*hb_ot_layout_from_face (face)->gdef),
 			has_glyph_classes (gdef.has_glyph_classes ()),
+			var_store (gdef.get_var_store ()),
 			iter_input (),
 			iter_context (),
 			lookup_index ((unsigned int) -1),
@@ -999,8 +1001,12 @@
     end = int (end) + delta;
     if (end <= match_positions[idx])
     {
+      /* End might end up being smaller than match_positions[idx] if the recursed
+       * lookup ended up removing many items, more than we have had matched.
+       * Just never rewind end back and get out of here.
+       * https://bugs.chromium.org/p/chromium/issues/detail?id=659496 */
+      end = match_positions[idx];
       /* There can't be any further changes. */
-      assert (end == match_positions[idx]);
       break;
     }
 
@@ -2269,6 +2275,24 @@
   inline const Lookup& get_lookup (unsigned int i) const
   { return (this+lookupList)[i]; }
 
+  inline bool find_variations_index (const int *coords, unsigned int num_coords,
+				     unsigned int *index) const
+  { return (version.to_int () >= 0x00010001u ? this+featureVars : Null(FeatureVariations))
+	   .find_index (coords, num_coords, index); }
+  inline const Feature& get_feature_variation (unsigned int feature_index,
+					       unsigned int variations_index) const
+  {
+    if (FeatureVariations::NOT_FOUND_INDEX != variations_index &&
+	version.to_int () >= 0x00010001u)
+    {
+      const Feature *feature = (this+featureVars).find_substitute (variations_index,
+								   feature_index);
+      if (feature)
+        return *feature;
+    }
+    return get_feature (feature_index);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -2276,7 +2300,8 @@
 		  likely (version.major == 1) &&
 		  scriptList.sanitize (c, this) &&
 		  featureList.sanitize (c, this) &&
-		  lookupList.sanitize (c, this));
+		  lookupList.sanitize (c, this) &&
+		  (version.to_int () < 0x00010001u || featureVars.sanitize (c, this)));
   }
 
   protected:
@@ -2288,8 +2313,13 @@
 		featureList; 	/* FeatureList table */
   OffsetTo<LookupList>
 		lookupList; 	/* LookupList table */
+  LOffsetTo<FeatureVariations>
+		featureVars;	/* Offset to Feature Variations
+				   table--from beginning of table
+				 * (may be NULL).  Introduced
+				 * in version 0x00010001. */
   public:
-  DEFINE_SIZE_STATIC (10);
+  DEFINE_SIZE_MIN (10);
 };
 
 
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index 778b2c4..8c348be 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -34,6 +34,7 @@
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 #include "hb-set-private.hh"
+#include "hb-open-type-private.hh"
 
 
 /* Private API corresponding to hb-ot-layout.h: */
@@ -124,6 +125,9 @@
   struct GDEF;
   struct GSUB;
   struct GPOS;
+  struct MATH;
+  struct fvar;
+  struct avar;
 }
 
 struct hb_ot_layout_lookup_accelerator_t
@@ -157,6 +161,11 @@
   const struct OT::GSUB *gsub;
   const struct OT::GPOS *gpos;
 
+  /* TODO Move the following out of this struct. */
+  OT::hb_lazy_table_loader_t<struct OT::MATH> math;
+  OT::hb_lazy_table_loader_t<struct OT::fvar> fvar;
+  OT::hb_lazy_table_loader_t<struct OT::avar> avar;
+
   unsigned int gsub_lookup_count;
   unsigned int gpos_lookup_count;
 
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 5cb1491..d7ededd 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -34,13 +34,10 @@
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
-#include "hb-ot-layout-jstf-table.hh"
+#include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise.
 
 #include "hb-ot-map-private.hh"
 
-#include <stdlib.h>
-#include <string.h>
-
 
 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
 
@@ -60,6 +57,10 @@
   layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
   layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
 
+  layout->math.init (face);
+  layout->fvar.init (face);
+  layout->avar.init (face);
+
   {
     /*
      * The ugly business of blacklisting individual fonts' tables happen here!
@@ -178,6 +179,10 @@
   hb_blob_destroy (layout->gsub_blob);
   hb_blob_destroy (layout->gpos_blob);
 
+  layout->math.fini ();
+  layout->fvar.fini ();
+  layout->avar.fini ();
+
   free (layout);
 }
 
@@ -200,7 +205,6 @@
   return *hb_ot_layout_from_face (face)->gpos;
 }
 
-
 /*
  * GDEF
  */
@@ -552,10 +556,13 @@
 				  unsigned int *lookup_count /* IN/OUT */,
 				  unsigned int *lookup_indexes /* OUT */)
 {
-  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
-  const OT::Feature &f = g.get_feature (feature_index);
-
-  return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
+  return hb_ot_layout_feature_with_variations_get_lookups (face,
+							   table_tag,
+							   feature_index,
+							   HB_OT_LAYOUT_NO_VARIATIONS_INDEX,
+							   start_offset,
+							   lookup_count,
+							   lookup_indexes);
 }
 
 /**
@@ -806,6 +813,38 @@
 }
 
 
+/* Variations support */
+
+hb_bool_t
+hb_ot_layout_table_find_feature_variations (hb_face_t    *face,
+					    hb_tag_t      table_tag,
+					    const int    *coords,
+					    unsigned int  num_coords,
+					    unsigned int *variations_index /* out */)
+{
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+
+  return g.find_variations_index (coords, num_coords, variations_index);
+}
+
+unsigned int
+hb_ot_layout_feature_with_variations_get_lookups (hb_face_t    *face,
+						  hb_tag_t      table_tag,
+						  unsigned int  feature_index,
+						  unsigned int  variations_index,
+						  unsigned int  start_offset,
+						  unsigned int *lookup_count /* IN/OUT */,
+						  unsigned int *lookup_indexes /* OUT */)
+{
+  ASSERT_STATIC (OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX);
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+
+  const OT::Feature &f = g.get_feature_variation (feature_index, variations_index);
+
+  return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
+}
+
+
 /*
  * OT::GSUB
  */
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index eb23d45..9861f0f 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -95,6 +95,7 @@
 #define HB_OT_LAYOUT_NO_SCRIPT_INDEX		0xFFFFu
 #define HB_OT_LAYOUT_NO_FEATURE_INDEX		0xFFFFu
 #define HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX	0xFFFFu
+#define HB_OT_LAYOUT_NO_VARIATIONS_INDEX	0xFFFFFFFFu
 
 HB_EXTERN unsigned int
 hb_ot_layout_table_get_script_tags (hb_face_t    *face,
@@ -236,6 +237,24 @@
 					 void         *user_data);
 #endif
 
+/* Variations support */
+
+HB_EXTERN hb_bool_t
+hb_ot_layout_table_find_feature_variations (hb_face_t    *face,
+					    hb_tag_t      table_tag,
+					    const int    *coords,
+					    unsigned int  num_coords,
+					    unsigned int *variations_index /* out */);
+
+HB_EXTERN unsigned int
+hb_ot_layout_feature_with_variations_get_lookups (hb_face_t    *face,
+						  hb_tag_t      table_tag,
+						  unsigned int  feature_index,
+						  unsigned int  variations_index,
+						  unsigned int  start_offset,
+						  unsigned int *lookup_count /* IN/OUT */,
+						  unsigned int *lookup_indexes /* OUT */);
+
 
 /*
  * GSUB
diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh
index 8692caa..0395c9c 100644
--- a/src/hb-ot-map-private.hh
+++ b/src/hb-ot-map-private.hh
@@ -139,12 +139,6 @@
 
   private:
 
-  HB_INTERNAL void add_lookups (hb_face_t    *face,
-				unsigned int  table_index,
-				unsigned int  feature_index,
-				hb_mask_t     mask,
-				bool          auto_zwj);
-
   hb_mask_t global_mask;
 
   hb_prealloced_array_t<feature_map_t, 8> features;
@@ -182,7 +176,9 @@
   inline void add_gpos_pause (hb_ot_map_t::pause_func_t pause_func)
   { add_pause (1, pause_func); }
 
-  HB_INTERNAL void compile (struct hb_ot_map_t &m);
+  HB_INTERNAL void compile (hb_ot_map_t  &m,
+			    const int    *coords,
+			    unsigned int  num_coords);
 
   inline void finish (void) {
     feature_infos.finish ();
@@ -194,6 +190,14 @@
 
   private:
 
+  HB_INTERNAL void add_lookups (hb_ot_map_t  &m,
+				hb_face_t    *face,
+				unsigned int  table_index,
+				unsigned int  feature_index,
+				unsigned int  variations_index,
+				hb_mask_t     mask,
+				bool          auto_zwj);
+
   struct feature_info_t {
     hb_tag_t tag;
     unsigned int seq; /* sequence#, used for stable sorting only */
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index 17e3f40..9b331d5 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -31,44 +31,13 @@
 #include "hb-ot-layout-private.hh"
 
 
-void
-hb_ot_map_t::add_lookups (hb_face_t    *face,
-			  unsigned int  table_index,
-			  unsigned int  feature_index,
-			  hb_mask_t     mask,
-			  bool          auto_zwj)
+void hb_ot_map_t::collect_lookups (unsigned int table_index, hb_set_t *lookups_out) const
 {
-  unsigned int lookup_indices[32];
-  unsigned int offset, len;
-  unsigned int table_lookup_count;
-
-  table_lookup_count = hb_ot_layout_table_get_lookup_count (face, table_tags[table_index]);
-
-  offset = 0;
-  do {
-    len = ARRAY_LENGTH (lookup_indices);
-    hb_ot_layout_feature_get_lookups (face,
-				      table_tags[table_index],
-				      feature_index,
-				      offset, &len,
-				      lookup_indices);
-
-    for (unsigned int i = 0; i < len; i++)
-    {
-      if (lookup_indices[i] >= table_lookup_count)
-	continue;
-      hb_ot_map_t::lookup_map_t *lookup = lookups[table_index].push ();
-      if (unlikely (!lookup))
-        return;
-      lookup->mask = mask;
-      lookup->index = lookup_indices[i];
-      lookup->auto_zwj = auto_zwj;
-    }
-
-    offset += len;
-  } while (len == ARRAY_LENGTH (lookup_indices));
+  for (unsigned int i = 0; i < lookups[table_index].len; i++)
+    hb_set_add (lookups_out, lookups[table_index][i].index);
 }
 
+
 hb_ot_map_builder_t::hb_ot_map_builder_t (hb_face_t *face_,
 					  const hb_segment_properties_t *props_)
 {
@@ -109,13 +78,48 @@
   info->stage[1] = current_stage[1];
 }
 
-
-void hb_ot_map_t::collect_lookups (unsigned int table_index, hb_set_t *lookups_out) const
+void
+hb_ot_map_builder_t::add_lookups (hb_ot_map_t  &m,
+				  hb_face_t    *face,
+				  unsigned int  table_index,
+				  unsigned int  feature_index,
+				  unsigned int  variations_index,
+				  hb_mask_t     mask,
+				  bool          auto_zwj)
 {
-  for (unsigned int i = 0; i < lookups[table_index].len; i++)
-    hb_set_add (lookups_out, lookups[table_index][i].index);
+  unsigned int lookup_indices[32];
+  unsigned int offset, len;
+  unsigned int table_lookup_count;
+
+  table_lookup_count = hb_ot_layout_table_get_lookup_count (face, table_tags[table_index]);
+
+  offset = 0;
+  do {
+    len = ARRAY_LENGTH (lookup_indices);
+    hb_ot_layout_feature_with_variations_get_lookups (face,
+						      table_tags[table_index],
+						      feature_index,
+						      variations_index,
+						      offset, &len,
+						      lookup_indices);
+
+    for (unsigned int i = 0; i < len; i++)
+    {
+      if (lookup_indices[i] >= table_lookup_count)
+	continue;
+      hb_ot_map_t::lookup_map_t *lookup = m.lookups[table_index].push ();
+      if (unlikely (!lookup))
+        return;
+      lookup->mask = mask;
+      lookup->index = lookup_indices[i];
+      lookup->auto_zwj = auto_zwj;
+    }
+
+    offset += len;
+  } while (len == ARRAY_LENGTH (lookup_indices));
 }
 
+
 void hb_ot_map_builder_t::add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func)
 {
   stage_info_t *s = stages[table_index].push ();
@@ -128,7 +132,9 @@
 }
 
 void
-hb_ot_map_builder_t::compile (hb_ot_map_t &m)
+hb_ot_map_builder_t::compile (hb_ot_map_t  &m,
+			      const int    *coords,
+			      unsigned int  num_coords)
 {
   m.global_mask = 1;
 
@@ -262,23 +268,32 @@
   {
     /* Collect lookup indices for features */
 
+    unsigned int variations_index;
+    hb_ot_layout_table_find_feature_variations (face,
+						table_tags[table_index],
+						coords,
+						num_coords,
+						&variations_index);
+
     unsigned int stage_index = 0;
     unsigned int last_num_lookups = 0;
     for (unsigned stage = 0; stage < current_stage[table_index]; stage++)
     {
       if (required_feature_index[table_index] != HB_OT_LAYOUT_NO_FEATURE_INDEX &&
 	  required_feature_stage[table_index] == stage)
-	m.add_lookups (face, table_index,
-		       required_feature_index[table_index],
-		       1 /* mask */,
-		       true /* auto_zwj */);
+	add_lookups (m, face, table_index,
+		     required_feature_index[table_index],
+		     variations_index,
+		     1 /* mask */,
+		     true /* auto_zwj */);
 
       for (unsigned i = 0; i < m.features.len; i++)
         if (m.features[i].stage[table_index] == stage)
-	  m.add_lookups (face, table_index,
-			 m.features[i].index[table_index],
-			 m.features[i].mask,
-			 m.features[i].auto_zwj);
+	  add_lookups (m, face, table_index,
+		       m.features[i].index[table_index],
+		       variations_index,
+		       m.features[i].mask,
+		       m.features[i].auto_zwj);
 
       /* Sort lookups and merge duplicates */
       if (last_num_lookups < m.lookups[table_index].len)
diff --git a/src/hb-ot-math-table.hh b/src/hb-ot-math-table.hh
new file mode 100644
index 0000000..191d79e
--- /dev/null
+++ b/src/hb-ot-math-table.hh
@@ -0,0 +1,722 @@
+/*
+ * Copyright © 2016  Igalia S.L.
+ *
+ *  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.
+ *
+ * Igalia Author(s): Frédéric Wang
+ */
+
+#ifndef HB_OT_MATH_TABLE_HH
+#define HB_OT_MATH_TABLE_HH
+
+#include "hb-open-type-private.hh"
+#include "hb-ot-layout-common-private.hh"
+#include "hb-ot-math.h"
+
+namespace OT {
+
+
+struct MathValueRecord
+{
+  inline hb_position_t get_x_value (hb_font_t *font, const void *base) const
+  { return font->em_scale_x (value) + (base+deviceTable).get_x_delta (font); }
+  inline hb_position_t get_y_value (hb_font_t *font, const void *base) const
+  { return font->em_scale_y (value) + (base+deviceTable).get_y_delta (font); }
+
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && deviceTable.sanitize (c, base));
+  }
+
+  protected:
+  SHORT			value;		/* The X or Y value in design units */
+  OffsetTo<Device>	deviceTable;	/* Offset to the device table - from the
+					 * beginning of parent table. May be NULL.
+					 * Suggested format for device table is 1. */
+
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct MathConstants
+{
+  inline bool sanitize_math_value_records (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+
+    unsigned int count = ARRAY_LENGTH (mathValueRecords);
+    for (unsigned int i = 0; i < count; i++)
+      if (!mathValueRecords[i].sanitize (c, this))
+	return_trace (false);
+
+    return_trace (true);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && sanitize_math_value_records(c));
+  }
+
+  inline hb_position_t get_value (hb_ot_math_constant_t constant,
+				  hb_font_t *font) const
+  {
+    switch (constant) {
+
+    case HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN:
+    case HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN:
+      return percentScaleDown[constant - HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN];
+
+    case HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT:
+    case HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT:
+      return font->em_scale_y (minHeight[constant - HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT]);
+
+    case HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE:
+    case HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE:
+    case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP:
+    case HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT:
+      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_x_value(font, this);
+
+    case HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT:
+    case HB_OT_MATH_CONSTANT_AXIS_HEIGHT:
+    case HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT:
+    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS:
+    case HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN:
+    case HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_MATH_LEADING:
+    case HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER:
+    case HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS:
+    case HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP:
+    case HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP:
+    case HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER:
+    case HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS:
+    case HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP:
+    case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP:
+    case HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_STACK_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN:
+    case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN:
+    case HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN:
+    case HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN:
+    case HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX:
+    case HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN:
+    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX:
+    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT:
+    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN:
+    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP:
+    case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED:
+    case HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER:
+    case HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS:
+    case HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP:
+    case HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN:
+    case HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN:
+      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_y_value(font, this);
+
+    case HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT:
+      return radicalDegreeBottomRaisePercent;
+
+    default:
+      return 0;
+    }
+  }
+
+  protected:
+  SHORT percentScaleDown[2];
+  USHORT minHeight[2];
+  MathValueRecord mathValueRecords[51];
+  SHORT radicalDegreeBottomRaisePercent;
+
+  public:
+  DEFINE_SIZE_STATIC (214);
+};
+
+struct MathItalicsCorrectionInfo
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  coverage.sanitize (c, this) &&
+		  italicsCorrection.sanitize (c, this));
+  }
+
+  inline hb_position_t get_value (hb_codepoint_t glyph,
+				  hb_font_t *font) const
+  {
+    unsigned int index = (this+coverage).get_coverage (glyph);
+    return italicsCorrection[index].get_x_value (font, this);
+  }
+
+  protected:
+  OffsetTo<Coverage>       coverage;		/* Offset to Coverage table -
+						 * from the beginning of
+						 * MathItalicsCorrectionInfo
+						 * table. */
+  ArrayOf<MathValueRecord> italicsCorrection;	/* Array of MathValueRecords
+						 * defining italics correction
+						 * values for each
+						 * covered glyph. */
+
+  public:
+  DEFINE_SIZE_ARRAY (4, italicsCorrection);
+};
+
+struct MathTopAccentAttachment
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  topAccentCoverage.sanitize (c, this) &&
+		  topAccentAttachment.sanitize (c, this));
+  }
+
+  inline hb_position_t get_value (hb_codepoint_t glyph,
+				  hb_font_t *font) const
+  {
+    unsigned int index = (this+topAccentCoverage).get_coverage (glyph);
+    if (index == NOT_COVERED)
+      return font->get_glyph_h_advance (glyph) / 2;
+    return topAccentAttachment[index].get_x_value(font, this);
+  }
+
+  protected:
+  OffsetTo<Coverage>       topAccentCoverage;   /* Offset to Coverage table -
+						 * from the beginning of
+						 * MathTopAccentAttachment
+						 * table. */
+  ArrayOf<MathValueRecord> topAccentAttachment; /* Array of MathValueRecords
+						 * defining top accent
+						 * attachment points for each
+						 * covered glyph. */
+
+  public:
+  DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment);
+};
+
+struct MathKern
+{
+  inline bool sanitize_math_value_records (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    unsigned int count = 2 * heightCount + 1;
+    for (unsigned int i = 0; i < count; i++)
+      if (!mathValueRecords[i].sanitize (c, this)) return_trace (false);
+    return_trace (true);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  c->check_array (mathValueRecords,
+				  mathValueRecords[0].static_size,
+				  2 * heightCount + 1) &&
+		  sanitize_math_value_records (c));
+  }
+
+  inline hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
+  {
+    const MathValueRecord* correctionHeight = mathValueRecords;
+    const MathValueRecord* kernValue = mathValueRecords + heightCount;
+    int sign = font->y_scale < 0 ? -1 : +1;
+
+    /* The description of the MathKern table is a ambiguous, but interpreting
+     * "between the two heights found at those indexes" for 0 < i < len as
+     *
+     *   correctionHeight[i-1] < correction_height <= correctionHeight[i]
+     *
+     * makes the result consistent with the limit cases and we can just use the
+     * binary search algorithm of std::upper_bound:
+     */
+    unsigned int i = 0;
+    unsigned int count = heightCount;
+    while (count > 0)
+    {
+      unsigned int half = count / 2;
+      hb_position_t height = correctionHeight[i + half].get_y_value(font, this);
+      if (sign * height < sign * correction_height)
+      {
+	i += half + 1;
+	count -= half + 1;
+      } else
+	count = half;
+    }
+    return kernValue[i].get_x_value(font, this);
+  }
+
+  protected:
+  USHORT	  heightCount;
+  MathValueRecord mathValueRecords[VAR]; /* Array of correction heights at
+					  * which the kern value changes.
+					  * Sorted by the height value in
+					  * design units (heightCount entries),
+					  * Followed by:
+					  * Array of kern values corresponding
+					  * to heights. (heightCount+1 entries).
+					  */
+
+  public:
+  DEFINE_SIZE_ARRAY (2, mathValueRecords);
+};
+
+struct MathKernInfoRecord
+{
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+
+    unsigned int count = ARRAY_LENGTH (mathKern);
+    for (unsigned int i = 0; i < count; i++)
+      if (unlikely (!mathKern[i].sanitize (c, base)))
+	return_trace (false);
+
+    return_trace (true);
+  }
+
+  inline hb_position_t get_kerning (hb_ot_math_kern_t kern,
+				    hb_position_t correction_height,
+				    hb_font_t *font,
+				    const void *base) const
+  {
+    unsigned int idx = kern;
+    if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0;
+    return (base+mathKern[idx]).get_value (correction_height, font);
+  }
+
+  protected:
+  /* Offset to MathKern table for each corner -
+   * from the beginning of MathKernInfo table. May be NULL. */
+  OffsetTo<MathKern> mathKern[4];
+
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct MathKernInfo
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  mathKernCoverage.sanitize (c, this) &&
+		  mathKernInfoRecords.sanitize (c, this));
+  }
+
+  inline hb_position_t get_kerning (hb_codepoint_t glyph,
+				    hb_ot_math_kern_t kern,
+				    hb_position_t correction_height,
+				    hb_font_t *font) const
+  {
+    unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
+    return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this);
+  }
+
+  protected:
+  OffsetTo<Coverage>		mathKernCoverage;    /* Offset to Coverage table -
+						      * from the beginning of the
+						      * MathKernInfo table. */
+  ArrayOf<MathKernInfoRecord>	mathKernInfoRecords; /* Array of
+						      * MathKernInfoRecords,
+						      * per-glyph information for
+						      * mathematical positioning
+						      * of subscripts and
+						      * superscripts. */
+
+  public:
+  DEFINE_SIZE_ARRAY (4, mathKernInfoRecords);
+};
+
+struct MathGlyphInfo
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  mathItalicsCorrectionInfo.sanitize (c, this) &&
+		  mathTopAccentAttachment.sanitize (c, this) &&
+		  extendedShapeCoverage.sanitize (c, this) &&
+		  mathKernInfo.sanitize(c, this));
+  }
+
+  inline hb_position_t
+  get_italics_correction (hb_codepoint_t  glyph, hb_font_t *font) const
+  { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); }
+
+  inline hb_position_t
+  get_top_accent_attachment (hb_codepoint_t  glyph, hb_font_t *font) const
+  { return (this+mathTopAccentAttachment).get_value (glyph, font); }
+
+  inline bool is_extended_shape (hb_codepoint_t glyph) const
+  { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; }
+
+  inline hb_position_t get_kerning (hb_codepoint_t glyph,
+				    hb_ot_math_kern_t kern,
+				    hb_position_t correction_height,
+				    hb_font_t *font) const
+  { return (this+mathKernInfo).get_kerning (glyph, kern, correction_height, font); }
+
+  protected:
+  /* Offset to MathItalicsCorrectionInfo table -
+   * from the beginning of MathGlyphInfo table. */
+  OffsetTo<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo;
+
+  /* Offset to MathTopAccentAttachment table -
+   * from the beginning of MathGlyphInfo table. */
+  OffsetTo<MathTopAccentAttachment> mathTopAccentAttachment;
+
+  /* Offset to coverage table for Extended Shape glyphs -
+   * from the beginning of MathGlyphInfo table. When the left or right glyph of
+   * a box is an extended shape variant, the (ink) box (and not the default
+   * position defined by values in MathConstants table) should be used for
+   * vertical positioning purposes. May be NULL.. */
+  OffsetTo<Coverage> extendedShapeCoverage;
+
+   /* Offset to MathKernInfo table -
+    * from the beginning of MathGlyphInfo table. */
+  OffsetTo<MathKernInfo> mathKernInfo;
+
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct MathGlyphVariantRecord
+{
+  friend struct MathGlyphConstruction;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  GlyphID variantGlyph;       /* Glyph ID for the variant. */
+  USHORT  advanceMeasurement; /* Advance width/height, in design units, of the
+			       * variant, in the direction of requested
+			       * glyph extension. */
+
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct PartFlags : USHORT
+{
+  enum Flags {
+    Extender	= 0x0001u, /* If set, the part can be skipped or repeated. */
+
+    Defined	= 0x0001u, /* All defined flags. */
+  };
+
+  public:
+  DEFINE_SIZE_STATIC (2);
+};
+
+struct MathGlyphPartRecord
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  inline void extract (hb_ot_math_glyph_part_t &out,
+		       int scale,
+		       hb_font_t *font) const
+  {
+    out.glyph			= glyph;
+
+    out.start_connector_length	= font->em_scale (startConnectorLength, scale);
+    out.end_connector_length	= font->em_scale (endConnectorLength, scale);
+    out.full_advance		= font->em_scale (fullAdvance, scale);
+
+    ASSERT_STATIC ((unsigned int) HB_MATH_GLYPH_PART_FLAG_EXTENDER ==
+		   (unsigned int) PartFlags::Extender);
+
+    out.flags = (hb_ot_math_glyph_part_flags_t)
+		(unsigned int)
+		(partFlags & PartFlags::Defined);
+  }
+
+  protected:
+  GlyphID   glyph;		  /* Glyph ID for the part. */
+  USHORT    startConnectorLength; /* Advance width/ height of the straight bar
+				   * connector material, in design units, is at
+				   * the beginning of the glyph, in the
+				   * direction of the extension. */
+  USHORT    endConnectorLength;   /* Advance width/ height of the straight bar
+				   * connector material, in design units, is at
+				   * the end of the glyph, in the direction of
+				   * the extension. */
+  USHORT    fullAdvance;	  /* Full advance width/height for this part,
+				   * in the direction of the extension.
+				   * In design units. */
+  PartFlags partFlags;		  /* Part qualifiers. */
+
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+struct MathGlyphAssembly
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  italicsCorrection.sanitize(c, this) &&
+		  partRecords.sanitize(c));
+  }
+
+  inline unsigned int get_parts (hb_direction_t direction,
+				 hb_font_t *font,
+				 unsigned int start_offset,
+				 unsigned int *parts_count, /* IN/OUT */
+				 hb_ot_math_glyph_part_t *parts /* OUT */,
+				 hb_position_t *italics_correction /* OUT */) const
+  {
+    if (parts_count)
+    {
+      int scale = font->dir_scale (direction);
+      const MathGlyphPartRecord *arr =
+	    partRecords.sub_array (start_offset, parts_count);
+      unsigned int count = *parts_count;
+      for (unsigned int i = 0; i < count; i++)
+	arr[i].extract (parts[i], scale, font);
+    }
+
+    if (italics_correction)
+      *italics_correction = italicsCorrection.get_x_value (font, this);
+
+    return partRecords.len;
+  }
+
+  protected:
+  MathValueRecord	   italicsCorrection; /* Italics correction of this
+					       * MathGlyphAssembly. Should not
+					       * depend on the assembly size. */
+  ArrayOf<MathGlyphPartRecord> partRecords;   /* Array of part records, from
+					       * left to right and bottom to
+					       * top. */
+
+  public:
+  DEFINE_SIZE_ARRAY (6, partRecords);
+};
+
+struct MathGlyphConstruction
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  glyphAssembly.sanitize(c, this) &&
+		  mathGlyphVariantRecord.sanitize(c));
+  }
+
+  inline const MathGlyphAssembly &get_assembly (void) const
+  { return this+glyphAssembly; }
+
+  inline unsigned int get_variants (hb_direction_t direction,
+				    hb_font_t *font,
+				    unsigned int start_offset,
+				    unsigned int *variants_count, /* IN/OUT */
+				    hb_ot_math_glyph_variant_t *variants /* OUT */) const
+  {
+    if (variants_count)
+    {
+      int scale = font->dir_scale (direction);
+      const MathGlyphVariantRecord *arr =
+	    mathGlyphVariantRecord.sub_array (start_offset, variants_count);
+      unsigned int count = *variants_count;
+      for (unsigned int i = 0; i < count; i++)
+      {
+	variants[i].glyph = arr[i].variantGlyph;
+	variants[i].advance = font->em_scale (arr[i].advanceMeasurement, scale);
+      }
+    }
+    return mathGlyphVariantRecord.len;
+  }
+
+  protected:
+  /* Offset to MathGlyphAssembly table for this shape - from the beginning of
+     MathGlyphConstruction table. May be NULL. */
+  OffsetTo<MathGlyphAssembly>	  glyphAssembly;
+
+  /* MathGlyphVariantRecords for alternative variants of the glyphs. */
+  ArrayOf<MathGlyphVariantRecord> mathGlyphVariantRecord;
+
+  public:
+  DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord);
+};
+
+struct MathVariants
+{
+  inline bool sanitize_offsets (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    unsigned int count = vertGlyphCount + horizGlyphCount;
+    for (unsigned int i = 0; i < count; i++)
+      if (!glyphConstruction[i].sanitize (c, this)) return_trace (false);
+    return_trace (true);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  vertGlyphCoverage.sanitize (c, this) &&
+		  horizGlyphCoverage.sanitize (c, this) &&
+		  c->check_array (glyphConstruction,
+				  glyphConstruction[0].static_size,
+				  vertGlyphCount + horizGlyphCount) &&
+		  sanitize_offsets (c));
+  }
+
+  inline hb_position_t get_min_connector_overlap (hb_direction_t direction,
+						  hb_font_t *font) const
+  { return font->em_scale_dir (minConnectorOverlap, direction); }
+
+  inline unsigned int get_glyph_variants (hb_codepoint_t glyph,
+					  hb_direction_t direction,
+					  hb_font_t *font,
+					  unsigned int start_offset,
+					  unsigned int *variants_count, /* IN/OUT */
+					  hb_ot_math_glyph_variant_t *variants /* OUT */) const
+  { return get_glyph_construction (glyph, direction, font)
+	   .get_variants (direction, font, start_offset, variants_count, variants); }
+
+  inline unsigned int get_glyph_parts (hb_codepoint_t glyph,
+				       hb_direction_t direction,
+				       hb_font_t *font,
+				       unsigned int start_offset,
+				       unsigned int *parts_count, /* IN/OUT */
+				       hb_ot_math_glyph_part_t *parts /* OUT */,
+				       hb_position_t *italics_correction /* OUT */) const
+  { return get_glyph_construction (glyph, direction, font)
+	   .get_assembly ()
+	   .get_parts (direction, font,
+		       start_offset, parts_count, parts,
+		       italics_correction); }
+
+  private:
+  inline const MathGlyphConstruction &
+		get_glyph_construction (hb_codepoint_t glyph,
+					hb_direction_t direction,
+					hb_font_t *font) const
+  {
+    bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
+    unsigned int count = vertical ? vertGlyphCount : horizGlyphCount;
+    const OffsetTo<Coverage> &coverage = vertical ? vertGlyphCoverage
+						  : horizGlyphCoverage;
+
+    unsigned int index = (this+coverage).get_coverage (glyph);
+    if (unlikely (index >= count)) return Null(MathGlyphConstruction);
+
+    if (!vertical)
+      index += vertGlyphCount;
+
+    return this+glyphConstruction[index];
+  }
+
+  protected:
+  USHORT	     minConnectorOverlap; /* Minimum overlap of connecting
+					   * glyphs during glyph construction,
+					   * in design units. */
+  OffsetTo<Coverage> vertGlyphCoverage;   /* Offset to Coverage table -
+					   * from the beginning of MathVariants
+					   * table. */
+  OffsetTo<Coverage> horizGlyphCoverage;  /* Offset to Coverage table -
+					   * from the beginning of MathVariants
+					   * table. */
+  USHORT	     vertGlyphCount;      /* Number of glyphs for which
+					   * information is provided for
+					   * vertically growing variants. */
+  USHORT	     horizGlyphCount;     /* Number of glyphs for which
+					   * information is provided for
+					   * horizontally growing variants. */
+
+  /* Array of offsets to MathGlyphConstruction tables - from the beginning of
+     the MathVariants table, for shapes growing in vertical/horizontal
+     direction. */
+  OffsetTo<MathGlyphConstruction> glyphConstruction[VAR];
+
+  public:
+  DEFINE_SIZE_ARRAY (10, glyphConstruction);
+};
+
+
+/*
+ * MATH -- The MATH Table
+ */
+
+struct MATH
+{
+  static const hb_tag_t tableTag	= HB_OT_TAG_MATH;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+		  likely (version.major == 1) &&
+		  mathConstants.sanitize (c, this) &&
+		  mathGlyphInfo.sanitize (c, this) &&
+		  mathVariants.sanitize (c, this));
+  }
+
+  inline hb_position_t get_constant (hb_ot_math_constant_t  constant,
+				     hb_font_t		   *font) const
+  { return (this+mathConstants).get_value (constant, font); }
+
+  inline const MathGlyphInfo &get_math_glyph_info (void) const
+  { return this+mathGlyphInfo; }
+
+  inline const MathVariants &get_math_variants (void) const
+  { return this+mathVariants; }
+
+  protected:
+  FixedVersion<>version;		/* Version of the MATH table
+					 * initially set to 0x00010000u */
+  OffsetTo<MathConstants> mathConstants;/* MathConstants table */
+  OffsetTo<MathGlyphInfo> mathGlyphInfo;/* MathGlyphInfo table */
+  OffsetTo<MathVariants>  mathVariants;	/* MathVariants table */
+
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_MATH_TABLE_HH */
diff --git a/src/hb-ot-math.cc b/src/hb-ot-math.cc
new file mode 100644
index 0000000..2d7e679
--- /dev/null
+++ b/src/hb-ot-math.cc
@@ -0,0 +1,254 @@
+/*
+ * Copyright © 2016  Igalia S.L.
+ *
+ *  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.
+ *
+ * Igalia Author(s): Frédéric Wang
+ */
+
+#include "hb-open-type-private.hh"
+
+#include "hb-ot-math-table.hh"
+
+HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
+
+static inline const OT::MATH&
+_get_math (hb_face_t *face)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::MATH);
+  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
+  return *(layout->math.get ());
+}
+
+/*
+ * OT::MATH
+ */
+
+/**
+ * hb_ot_math_has_data:
+ * @face: #hb_face_t to test
+ *
+ * This function allows to verify the presence of an OpenType MATH table on the
+ * face.
+ *
+ * Return value: true if face has a MATH table, false otherwise
+ *
+ * Since: 1.3.3
+ **/
+hb_bool_t
+hb_ot_math_has_data (hb_face_t *face)
+{
+  return &_get_math (face) != &OT::Null(OT::MATH);
+}
+
+/**
+ * hb_ot_math_get_constant:
+ * @font: #hb_font_t from which to retrieve the value
+ * @constant: #hb_ot_math_constant_t the constant to retrieve
+ *
+ * This function returns the requested math constants as a #hb_position_t.
+ * If the request constant is HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN,
+ * HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN or
+ * HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN then the return value is
+ * actually an integer between 0 and 100 representing that percentage.
+ *
+ * Return value: the requested constant or 0
+ *
+ * Since: 1.3.3
+ **/
+hb_position_t
+hb_ot_math_get_constant (hb_font_t *font,
+			 hb_ot_math_constant_t constant)
+{
+  const OT::MATH &math = _get_math (font->face);
+  return math.get_constant(constant, font);
+}
+
+/**
+ * hb_ot_math_get_glyph_italics_correction:
+ * @font: #hb_font_t from which to retrieve the value
+ * @glyph: glyph index from which to retrieve the value
+ *
+ * Return value: the italics correction of the glyph or 0
+ *
+ * Since: 1.3.3
+ **/
+hb_position_t
+hb_ot_math_get_glyph_italics_correction (hb_font_t *font,
+					 hb_codepoint_t glyph)
+{
+  const OT::MATH &math = _get_math (font->face);
+  return math.get_math_glyph_info().get_italics_correction (glyph, font);
+}
+
+/**
+ * hb_ot_math_get_glyph_top_accent_attachment:
+ * @font: #hb_font_t from which to retrieve the value
+ * @glyph: glyph index from which to retrieve the value
+ *
+ * Return value: the top accent attachment of the glyph or 0
+ *
+ * Since: 1.3.3
+ **/
+hb_position_t
+hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font,
+					    hb_codepoint_t glyph)
+{
+  const OT::MATH &math = _get_math (font->face);
+  return math.get_math_glyph_info().get_top_accent_attachment (glyph, font);
+}
+
+/**
+ * hb_ot_math_is_glyph_extended_shape:
+ * @face: a #hb_face_t to test
+ * @glyph: a glyph index to test
+ *
+ * Return value: true if the glyph is an extended shape, false otherwise
+ *
+ * Since: 1.3.3
+ **/
+hb_bool_t
+hb_ot_math_is_glyph_extended_shape (hb_face_t *face,
+				    hb_codepoint_t glyph)
+{
+  const OT::MATH &math = _get_math (face);
+  return math.get_math_glyph_info().is_extended_shape (glyph);
+}
+
+/**
+ * hb_ot_math_get_glyph_kerning:
+ * @font: #hb_font_t from which to retrieve the value
+ * @glyph: glyph index from which to retrieve the value
+ * @kern: the #hb_ot_math_kern_t from which to retrieve the value
+ * @correction_height: the correction height to use to determine the kerning.
+ *
+ * This function tries to retrieve the MathKern table for the specified font,
+ * glyph and #hb_ot_math_kern_t. Then it browses the list of heights from the
+ * MathKern table to find one value that is greater or equal to specified
+ * correction_height. If one is found the corresponding value from the list of
+ * kerns is returned and otherwise the last kern value is returned.
+ *
+ * Return value: requested kerning or 0
+ *
+ * Since: 1.3.3
+ **/
+hb_position_t
+hb_ot_math_get_glyph_kerning (hb_font_t *font,
+			      hb_codepoint_t glyph,
+			      hb_ot_math_kern_t kern,
+			      hb_position_t correction_height)
+{
+  const OT::MATH &math = _get_math (font->face);
+  return math.get_math_glyph_info().get_kerning (glyph, kern, correction_height, font);
+}
+
+/**
+ * hb_ot_math_get_glyph_variants:
+ * @font: #hb_font_t from which to retrieve the values
+ * @glyph: index of the glyph to stretch
+ * @direction: direction of the stretching
+ * @start_offset: offset of the first variant to retrieve
+ * @variants_count: maximum number of variants to retrieve after start_offset
+ * (IN) and actual number of variants retrieved (OUT)
+ * @variants: array of size at least @variants_count to store the result
+ *
+ * This function tries to retrieve the MathGlyphConstruction for the specified
+ * font, glyph and direction. Note that only the value of
+ * #HB_DIRECTION_IS_HORIZONTAL is considered. It provides the corresponding list
+ * of size variants as an array of hb_ot_math_glyph_variant_t structs.
+ *
+ * Return value: the total number of size variants available or 0
+ *
+ * Since: 1.3.3
+ **/
+unsigned int
+hb_ot_math_get_glyph_variants (hb_font_t *font,
+			       hb_codepoint_t glyph,
+			       hb_direction_t direction,
+			       unsigned int start_offset,
+			       unsigned int *variants_count, /* IN/OUT */
+			       hb_ot_math_glyph_variant_t *variants /* OUT */)
+{
+  const OT::MATH &math = _get_math (font->face);
+  return math.get_math_variants().get_glyph_variants (glyph, direction, font,
+						      start_offset,
+						      variants_count,
+						      variants);
+}
+
+/**
+ * hb_ot_math_get_min_connector_overlap:
+ * @font: #hb_font_t from which to retrieve the value
+ * @direction: direction of the stretching
+ *
+ * This function tries to retrieve the MathVariants table for the specified
+ * font and returns the minimum overlap of connecting glyphs to draw a glyph
+ * assembly in the specified direction. Note that only the value of
+ * #HB_DIRECTION_IS_HORIZONTAL is considered.
+ *
+ * Return value: requested min connector overlap or 0
+ *
+ * Since: 1.3.3
+ **/
+hb_position_t
+hb_ot_math_get_min_connector_overlap (hb_font_t *font,
+				      hb_direction_t direction)
+{
+  const OT::MATH &math = _get_math (font->face);
+  return math.get_math_variants().get_min_connector_overlap (direction, font);
+}
+
+/**
+ * hb_ot_math_get_glyph_assembly:
+ * @font: #hb_font_t from which to retrieve the values
+ * @glyph: index of the glyph to stretch
+ * @direction: direction of the stretching
+ * @start_offset: offset of the first glyph part to retrieve
+ * @parts_count: maximum number of glyph parts to retrieve after start_offset
+ * (IN) and actual number of parts retrieved (OUT)
+ * @parts: array of size at least @parts_count to store the result
+ * @italics_correction: italic correction of the glyph assembly
+ *
+ * This function tries to retrieve the GlyphAssembly for the specified font,
+ * glyph and direction. Note that only the value of #HB_DIRECTION_IS_HORIZONTAL
+ * is considered. It provides the information necessary to draw the glyph
+ * assembly as an array of #hb_ot_math_glyph_part_t.
+ *
+ * Return value: the total number of parts in the glyph assembly
+ *
+ * Since: 1.3.3
+ **/
+unsigned int
+hb_ot_math_get_glyph_assembly (hb_font_t *font,
+			       hb_codepoint_t glyph,
+			       hb_direction_t direction,
+			       unsigned int start_offset,
+			       unsigned int *parts_count, /* IN/OUT */
+			       hb_ot_math_glyph_part_t *parts, /* OUT */
+			       hb_position_t *italics_correction /* OUT */)
+{
+  const OT::MATH &math = _get_math (font->face);
+  return math.get_math_variants().get_glyph_parts (glyph, direction, font,
+						   start_offset,
+						   parts_count,
+						   parts,
+						   italics_correction);
+}
diff --git a/src/hb-ot-math.h b/src/hb-ot-math.h
new file mode 100644
index 0000000..521a5ca
--- /dev/null
+++ b/src/hb-ot-math.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright © 2016  Igalia S.L.
+ *
+ *  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.
+ *
+ * Igalia Author(s): Frédéric Wang
+ */
+
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
+#ifndef HB_OT_MATH_H
+#define HB_OT_MATH_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * MATH
+ */
+
+#define HB_OT_TAG_MATH HB_TAG('M','A','T','H')
+
+/* Use with hb_buffer_set_script() for math shaping. */
+#define HB_OT_MATH_SCRIPT HB_TAG('m','a','t','h')
+
+/* Types */
+
+/**
+ * hb_ot_math_constant_t:
+ *
+ * Since: 1.3.3
+ */
+typedef enum {
+  HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN = 0,
+  HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN = 1,
+  HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT = 2,
+  HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT = 3,
+  HB_OT_MATH_CONSTANT_MATH_LEADING = 4,
+  HB_OT_MATH_CONSTANT_AXIS_HEIGHT = 5,
+  HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT = 6,
+  HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT = 7,
+  HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN = 8,
+  HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX = 9,
+  HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN = 10,
+  HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP = 11,
+  HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED = 12,
+  HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN = 13,
+  HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX = 14,
+  HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN = 15,
+  HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT = 16,
+  HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT = 17,
+  HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN = 18,
+  HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN = 19,
+  HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN = 20,
+  HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN = 21,
+  HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP = 22,
+  HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP = 23,
+  HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN = 24,
+  HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN = 25,
+  HB_OT_MATH_CONSTANT_STACK_GAP_MIN = 26,
+  HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN = 27,
+  HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP = 28,
+  HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN = 29,
+  HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN = 30,
+  HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN = 31,
+  HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP = 32,
+  HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP = 33,
+  HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN = 34,
+  HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN = 35,
+  HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN = 36,
+  HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN = 37,
+  HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS = 38,
+  HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN = 39,
+  HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN = 40,
+  HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP = 41,
+  HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP = 42,
+  HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP = 43,
+  HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS = 44,
+  HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER = 45,
+  HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP = 46,
+  HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS = 47,
+  HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER = 48,
+  HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP = 49,
+  HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP = 50,
+  HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS = 51,
+  HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER = 52,
+  HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE = 53,
+  HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE = 54,
+  HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT = 55
+} hb_ot_math_constant_t;
+
+/**
+ * hb_ot_math_kern_t:
+ *
+ * Since: 1.3.3
+ */
+typedef enum {
+  HB_OT_MATH_KERN_TOP_RIGHT = 0,
+  HB_OT_MATH_KERN_TOP_LEFT = 1,
+  HB_OT_MATH_KERN_BOTTOM_RIGHT = 2,
+  HB_OT_MATH_KERN_BOTTOM_LEFT = 3
+} hb_ot_math_kern_t;
+
+/**
+ * hb_ot_math_glyph_variant_t:
+ *
+ * Since: 1.3.3
+ */
+typedef struct hb_ot_math_glyph_variant_t {
+  hb_codepoint_t glyph;
+  hb_position_t advance;
+} hb_ot_math_glyph_variant_t;
+
+/**
+ * hb_ot_math_glyph_part_flags_t:
+ *
+ * Since: 1.3.3
+ */
+typedef enum { /*< flags >*/
+  HB_MATH_GLYPH_PART_FLAG_EXTENDER	= 0x00000001u  /* Extender glyph */
+} hb_ot_math_glyph_part_flags_t;
+
+/**
+ * hb_ot_math_glyph_part_t:
+ *
+ * Since: 1.3.3
+ */
+typedef struct hb_ot_math_glyph_part_t {
+  hb_codepoint_t glyph;
+  hb_position_t start_connector_length;
+  hb_position_t end_connector_length;
+  hb_position_t full_advance;
+  hb_ot_math_glyph_part_flags_t flags;
+} hb_ot_math_glyph_part_t;
+
+/* Methods */
+
+HB_EXTERN hb_bool_t
+hb_ot_math_has_data (hb_face_t *face);
+
+HB_EXTERN hb_position_t
+hb_ot_math_get_constant (hb_font_t *font,
+			 hb_ot_math_constant_t constant);
+
+HB_EXTERN hb_position_t
+hb_ot_math_get_glyph_italics_correction (hb_font_t *font,
+					 hb_codepoint_t glyph);
+
+HB_EXTERN hb_position_t
+hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font,
+					    hb_codepoint_t glyph);
+
+HB_EXTERN hb_bool_t
+hb_ot_math_is_glyph_extended_shape (hb_face_t *face,
+				    hb_codepoint_t glyph);
+
+HB_EXTERN hb_position_t
+hb_ot_math_get_glyph_kerning (hb_font_t *font,
+			      hb_codepoint_t glyph,
+			      hb_ot_math_kern_t kern,
+			      hb_position_t correction_height);
+
+HB_EXTERN unsigned int
+hb_ot_math_get_glyph_variants (hb_font_t *font,
+			       hb_codepoint_t glyph,
+			       hb_direction_t direction,
+			       unsigned int start_offset,
+			       unsigned int *variants_count, /* IN/OUT */
+			       hb_ot_math_glyph_variant_t *variants /* OUT */);
+
+HB_EXTERN hb_position_t
+hb_ot_math_get_min_connector_overlap (hb_font_t *font,
+				      hb_direction_t direction);
+
+HB_EXTERN unsigned int
+hb_ot_math_get_glyph_assembly (hb_font_t *font,
+			       hb_codepoint_t glyph,
+			       hb_direction_t direction,
+			       unsigned int start_offset,
+			       unsigned int *parts_count, /* IN/OUT */
+			       hb_ot_math_glyph_part_t *parts, /* OUT */
+			       hb_position_t *italics_correction /* OUT */);
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_MATH_H */
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index 4da8990..56ec5cd 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -618,6 +618,7 @@
   NULL, /* decompose */
   NULL, /* compose */
   setup_masks_arabic,
+  NULL, /* disable_otl */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-default.cc b/src/hb-ot-shape-complex-default.cc
index be60e56..42830ab 100644
--- a/src/hb-ot-shape-complex-default.cc
+++ b/src/hb-ot-shape-complex-default.cc
@@ -40,6 +40,7 @@
   NULL, /* decompose */
   NULL, /* compose */
   NULL, /* setup_masks */
+  NULL, /* disable_otl */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-hangul.cc b/src/hb-ot-shape-complex-hangul.cc
index 5f4d98b..af50565 100644
--- a/src/hb-ot-shape-complex-hangul.cc
+++ b/src/hb-ot-shape-complex-hangul.cc
@@ -32,7 +32,7 @@
 
 /* Same order as the feature array below */
 enum {
-  NONE,
+  _JMO,
 
   LJMO,
   VJMO,
@@ -419,6 +419,7 @@
   NULL, /* decompose */
   NULL, /* compose */
   setup_masks_hangul,
+  NULL, /* disable_otl */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   false, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-hebrew.cc b/src/hb-ot-shape-complex-hebrew.cc
index 3215900..96f2494 100644
--- a/src/hb-ot-shape-complex-hebrew.cc
+++ b/src/hb-ot-shape-complex-hebrew.cc
@@ -154,6 +154,18 @@
   return found;
 }
 
+static bool
+disable_otl_hebrew (const hb_ot_shape_plan_t *plan)
+{
+  /* For Hebrew shaper, use fallback if GPOS does not have 'hebr'
+   * script.  This matches Uniscribe better, and makes fonts like
+   * Arial that have GSUB/GPOS/GDEF but no data for Hebrew work.
+   * See:
+   * https://github.com/behdad/harfbuzz/issues/347#issuecomment-267838368
+   */
+  return plan->map.chosen_script[1] != HB_TAG ('h','e','b','r');
+}
+
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew =
 {
@@ -168,6 +180,7 @@
   NULL, /* decompose */
   compose_hebrew,
   NULL, /* setup_masks */
+  disable_otl_hebrew,
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 94556f6..b48fb56 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -1713,33 +1713,28 @@
   switch (ab)
   {
     /* Don't decompose these. */
-    case 0x0931u  : return false;
-    case 0x0B94u  : return false;
+    case 0x0931u  : return false; /* DEVANAGARI LETTER RRA */
+    case 0x0B94u  : return false; /* TAMIL LETTER AU */
 
 
     /*
      * Decompose split matras that don't have Unicode decompositions.
      */
 
-    case 0x0F77u  : *a = 0x0FB2u; *b= 0x0F81u; return true;
-    case 0x0F79u  : *a = 0x0FB3u; *b= 0x0F81u; return true;
+    /* Khmer */
     case 0x17BEu  : *a = 0x17C1u; *b= 0x17BEu; return true;
     case 0x17BFu  : *a = 0x17C1u; *b= 0x17BFu; return true;
     case 0x17C0u  : *a = 0x17C1u; *b= 0x17C0u; return true;
     case 0x17C4u  : *a = 0x17C1u; *b= 0x17C4u; return true;
     case 0x17C5u  : *a = 0x17C1u; *b= 0x17C5u; return true;
-    case 0x1925u  : *a = 0x1920u; *b= 0x1923u; return true;
-    case 0x1926u  : *a = 0x1920u; *b= 0x1924u; return true;
-    case 0x1B3Cu  : *a = 0x1B42u; *b= 0x1B3Cu; return true;
-    case 0x1112Eu  : *a = 0x11127u; *b= 0x11131u; return true;
-    case 0x1112Fu  : *a = 0x11127u; *b= 0x11132u; return true;
+
 #if 0
+    /* Gujarati */
     /* This one has no decomposition in Unicode, but needs no decomposition either. */
     /* case 0x0AC9u  : return false; */
+
+    /* Oriya */
     case 0x0B57u  : *a = no decomp, -> RIGHT; return true;
-    case 0x1C29u  : *a = no decomp, -> LEFT; return true;
-    case 0xA9C0u  : *a = no decomp, -> RIGHT; return true;
-    case 0x111BuF  : *a = no decomp, -> ABOVE; return true;
 #endif
   }
 
@@ -1819,6 +1814,7 @@
   decompose_indic,
   compose_indic,
   setup_masks_indic,
+  NULL, /* disable_otl */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   false, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc
index 577d790..bb68622 100644
--- a/src/hb-ot-shape-complex-myanmar.cc
+++ b/src/hb-ot-shape-complex-myanmar.cc
@@ -521,6 +521,7 @@
   NULL, /* decompose */
   NULL, /* compose */
   NULL, /* setup_masks */
+  NULL, /* disable_otl */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
 };
@@ -538,6 +539,7 @@
   NULL, /* decompose */
   NULL, /* compose */
   setup_masks_myanmar,
+  NULL, /* disable_otl */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
   false, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index fb0c704..39572df 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -146,6 +146,14 @@
 		       hb_buffer_t              *buffer,
 		       hb_font_t                *font);
 
+  /* disable_otl()
+   * Called during shape().
+   * If set and returns true, GDEF/GSUB/GPOS of the font are ignored
+   * and fallback operations used.
+   * May be NULL.
+   */
+  bool (*disable_otl) (const hb_ot_shape_plan_t *plan);
+
   hb_ot_shape_zero_width_marks_type_t zero_width_marks;
 
   bool fallback_position;
diff --git a/src/hb-ot-shape-complex-thai.cc b/src/hb-ot-shape-complex-thai.cc
index 4322b0d..e6f80f5 100644
--- a/src/hb-ot-shape-complex-thai.cc
+++ b/src/hb-ot-shape-complex-thai.cc
@@ -376,6 +376,7 @@
   NULL, /* decompose */
   NULL, /* compose */
   NULL, /* setup_masks */
+  NULL, /* disable_otl */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   false,/* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-tibetan.cc b/src/hb-ot-shape-complex-tibetan.cc
index a77b531..aadf59f 100644
--- a/src/hb-ot-shape-complex-tibetan.cc
+++ b/src/hb-ot-shape-complex-tibetan.cc
@@ -57,6 +57,7 @@
   NULL, /* decompose */
   NULL, /* compose */
   NULL, /* setup_masks */
+  NULL, /* disable_otl */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc
index 045ead5..af68706 100644
--- a/src/hb-ot-shape-complex-use.cc
+++ b/src/hb-ot-shape-complex-use.cc
@@ -559,6 +559,25 @@
 }
 
 static bool
+decompose_use (const hb_ot_shape_normalize_context_t *c,
+                hb_codepoint_t  ab,
+                hb_codepoint_t *a,
+                hb_codepoint_t *b)
+{
+  switch (ab)
+  {
+    /* Chakma:
+     * Special case where the Unicode decomp gives matras in the wrong order
+     * for cluster validation.
+     */
+    case 0x1112Eu : *a = 0x11127u; *b= 0x11131u; return true;
+    case 0x1112Fu : *a = 0x11127u; *b= 0x11132u; return true;
+  }
+
+  return (bool) c->unicode->decompose (ab, a, b);
+}
+
+static bool
 compose_use (const hb_ot_shape_normalize_context_t *c,
 	     hb_codepoint_t  a,
 	     hb_codepoint_t  b,
@@ -582,9 +601,10 @@
   NULL, /* preprocess_text */
   NULL, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
-  NULL, /* decompose */
+  decompose_use,
   compose_use,
   setup_masks_use,
+  NULL, /* disable_otl */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
   false, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape-private.hh
index 54ac2c3..594e54c 100644
--- a/src/hb-ot-shape-private.hh
+++ b/src/hb-ot-shape-private.hh
@@ -77,11 +77,13 @@
 			 map (face, &props) {}
   ~hb_ot_shape_planner_t (void) { map.finish (); }
 
-  inline void compile (hb_ot_shape_plan_t &plan)
+  inline void compile (hb_ot_shape_plan_t &plan,
+		       const int          *coords,
+		       unsigned int        num_coords)
   {
     plan.props = props;
     plan.shaper = shaper;
-    map.compile (plan.map);
+    map.compile (plan.map, coords, num_coords);
 
     plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m'));
     plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c'));
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 7811cb7..6b38739 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -69,6 +69,9 @@
 {
   hb_ot_map_builder_t *map = &planner->map;
 
+  map->add_global_bool_feature (HB_TAG('r','v','r','n'));
+  map->add_gsub_pause (NULL);
+
   switch (props->direction) {
     case HB_DIRECTION_LTR:
       map->add_global_bool_feature (HB_TAG ('l','t','r','a'));
@@ -163,7 +166,9 @@
 hb_ot_shaper_shape_plan_data_t *
 _hb_ot_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
 				      const hb_feature_t *user_features,
-				      unsigned int        num_user_features)
+				      unsigned int        num_user_features,
+				      const int          *coords,
+				      unsigned int        num_coords)
 {
   hb_ot_shape_plan_t *plan = (hb_ot_shape_plan_t *) calloc (1, sizeof (hb_ot_shape_plan_t));
   if (unlikely (!plan))
@@ -173,9 +178,10 @@
 
   planner.shaper = hb_ot_shape_complex_categorize (&planner);
 
-  hb_ot_shape_collect_features (&planner, &shape_plan->props, user_features, num_user_features);
+  hb_ot_shape_collect_features (&planner, &shape_plan->props,
+				user_features, num_user_features);
 
-  planner.compile (*plan);
+  planner.compile (*plan, coords, num_coords);
 
   if (plan->shaper->data_create) {
     plan->data = plan->shaper->data_create (plan);
@@ -212,6 +218,8 @@
   unsigned int        num_user_features;
 
   /* Transient stuff */
+  bool fallback_positioning;
+  bool fallback_glyph_classes;
   hb_direction_t target_direction;
 };
 
@@ -354,6 +362,18 @@
 
   hb_buffer_t *buffer = c->buffer;
 
+  hb_mask_t pre_mask, post_mask;
+  if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
+  {
+    pre_mask = c->plan->numr_mask | c->plan->frac_mask;
+    post_mask = c->plan->frac_mask | c->plan->dnom_mask;
+  }
+  else
+  {
+    pre_mask = c->plan->frac_mask | c->plan->dnom_mask;
+    post_mask = c->plan->numr_mask | c->plan->frac_mask;
+  }
+
   /* TODO look in pre/post context text also. */
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
@@ -372,10 +392,10 @@
         end++;
 
       for (unsigned int j = start; j < i; j++)
-        info[j].mask |= c->plan->numr_mask | c->plan->frac_mask;
+        info[j].mask |= pre_mask;
       info[i].mask |= c->plan->frac_mask;
       for (unsigned int j = i + 1; j < end; j++)
-        info[j].mask |= c->plan->frac_mask | c->plan->dnom_mask;
+        info[j].mask |= post_mask;
 
       i = end - 1;
     }
@@ -524,6 +544,32 @@
 }
 
 static inline void
+hb_synthesize_glyph_classes (hb_ot_shape_context_t *c)
+{
+  unsigned int count = c->buffer->len;
+  hb_glyph_info_t *info = c->buffer->info;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    hb_ot_layout_glyph_props_flags_t klass;
+
+    /* Never mark default-ignorables as marks.
+     * They won't get in the way of lookups anyway,
+     * but having them as mark will cause them to be skipped
+     * over if the lookup-flag says so, but at least for the
+     * Mongolian variation selectors, looks like Uniscribe
+     * marks them as non-mark.  Some Mongolian fonts without
+     * GDEF rely on this.  Another notable character that
+     * this applies to is COMBINING GRAPHEME JOINER. */
+    klass = (_hb_glyph_info_get_general_category (&info[i]) !=
+	     HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK ||
+	     _hb_glyph_info_is_default_ignorable (&info[i])) ?
+	    HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH :
+	    HB_OT_LAYOUT_GLYPH_PROPS_MARK;
+    _hb_glyph_info_set_glyph_props (&info[i], klass);
+  }
+}
+
+static inline void
 hb_ot_substitute_default (hb_ot_shape_context_t *c)
 {
   hb_buffer_t *buffer = c->buffer;
@@ -539,7 +585,7 @@
   hb_ot_shape_setup_masks (c);
 
   /* This is unfortunate to go here, but necessary... */
-  if (!hb_ot_layout_has_positioning (c->face))
+  if (c->fallback_positioning)
     _hb_ot_shape_fallback_position_recategorize_marks (c->plan, c->font, buffer);
 
   hb_ot_map_glyphs_fast (buffer);
@@ -554,6 +600,9 @@
 
   hb_ot_layout_substitute_start (c->font, buffer);
 
+  if (!hb_ot_layout_has_glyph_classes (c->face))
+    hb_synthesize_glyph_classes (c);
+
   c->plan->substitute (c->font, buffer);
 
   return;
@@ -632,14 +681,12 @@
     _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer);
 }
 
-static inline bool
+static inline void
 hb_ot_position_complex (hb_ot_shape_context_t *c)
 {
   hb_ot_layout_position_start (c->font, c->buffer);
 
-  bool ret = false;
   unsigned int count = c->buffer->len;
-  bool has_positioning = (bool) hb_ot_layout_has_positioning (c->face);
 
   /* If the font has no GPOS, AND, no fallback positioning will
    * happen, AND, direction is forward, then when zeroing mark
@@ -650,8 +697,9 @@
    * If fallback positinoing happens or GPOS is present, we don't
    * care.
    */
-  bool adjust_offsets_when_zeroing = !(has_positioning || c->plan->shaper->fallback_position ||
-                                       HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction));
+  bool adjust_offsets_when_zeroing = c->fallback_positioning &&
+				     !c->plan->shaper->fallback_position &&
+				     HB_DIRECTION_IS_FORWARD (c->buffer->props.direction);
 
   switch (c->plan->shaper->zero_width_marks)
   {
@@ -665,7 +713,7 @@
       break;
   }
 
-  if (has_positioning)
+  if (likely (!c->fallback_positioning))
   {
     hb_glyph_info_t *info = c->buffer->info;
     hb_glyph_position_t *pos = c->buffer->pos;
@@ -688,7 +736,6 @@
 					  &pos[i].x_offset,
 					  &pos[i].y_offset);
 
-    ret = true;
   }
 
   switch (c->plan->shaper->zero_width_marks)
@@ -707,8 +754,6 @@
   hb_ot_layout_position_finish_advances (c->font, c->buffer);
   hb_ot_zero_width_default_ignorables (c);
   hb_ot_layout_position_finish_offsets (c->font, c->buffer);
-
-  return ret;
 }
 
 static inline void
@@ -718,9 +763,9 @@
 
   hb_ot_position_default (c);
 
-  hb_bool_t fallback = !hb_ot_position_complex (c);
+  hb_ot_position_complex (c);
 
-  if (fallback && c->plan->shaper->fallback_position)
+  if (c->fallback_positioning && c->plan->shaper->fallback_position)
     _hb_ot_shape_fallback_position (c->plan, c->font, c->buffer);
 
   if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
@@ -728,7 +773,7 @@
 
   /* Visual fallback goes here. */
 
-  if (fallback)
+  if (c->fallback_positioning)
     _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
 
   _hb_buffer_deallocate_gsubgpos_vars (c->buffer);
@@ -748,6 +793,11 @@
 			      (unsigned) HB_BUFFER_MAX_LEN_MIN);
   }
 
+  bool disable_otl = c->plan->shaper->disable_otl && c->plan->shaper->disable_otl (c->plan);
+  //c->fallback_substitute     = disable_otl || !hb_ot_layout_has_substitution (c->face);
+  c->fallback_positioning    = disable_otl || !hb_ot_layout_has_positioning (c->face);
+  c->fallback_glyph_classes  = disable_otl || !hb_ot_layout_has_glyph_classes (c->face);
+
   /* Save the original direction, we use it later. */
   c->target_direction = c->buffer->props.direction;
 
diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc
index 5f21ac0..9b0db50 100644
--- a/src/hb-ot-tag.cc
+++ b/src/hb-ot-tag.cc
@@ -28,9 +28,6 @@
 
 #include "hb-private.hh"
 
-#include <string.h>
-
-
 
 /* hb_script_t */
 
@@ -201,6 +198,7 @@
   {"alt",	HB_TAG('A','L','T',' ')},	/* [Southern] Altai */
   {"am",	HB_TAG('A','M','H',' ')},	/* Amharic */
   {"amf",	HB_TAG('H','B','N',' ')},	/* Hammer-Banna */
+  {"amw",	HB_TAG('S','Y','R',' ')},	/* Western Neo-Aramaic */
   {"an",	HB_TAG('A','R','G',' ')},	/* Aragonese */
   {"ang",	HB_TAG('A','N','G',' ')},	/* Old English (ca. 450-1100) */
   {"ar",	HB_TAG('A','R','A',' ')},	/* Arabic [macrolanguage] */
@@ -239,6 +237,7 @@
   {"bg",	HB_TAG('B','G','R',' ')},	/* Bulgarian */
   {"bgc",	HB_TAG('B','G','C',' ')},	/* Haryanvi */
   {"bgq",	HB_TAG('B','G','Q',' ')},	/* Bagri */
+  {"bgr",	HB_TAG('Q','I','N',' ')},	/* Bawm Chin */
   {"bhb",	HB_TAG('B','H','I',' ')},	/* Bhili */
   {"bhk",	HB_TAG('B','I','K',' ')},	/* Albay Bicolano (retired code) */
   {"bho",	HB_TAG('B','H','O',' ')},	/* Bhojpuri */
@@ -270,8 +269,10 @@
   {"ca",	HB_TAG('C','A','T',' ')},	/* Catalan */
   {"cak",	HB_TAG('C','A','K',' ')},	/* Kaqchikel */
   {"cbk",	HB_TAG('C','B','K',' ')},	/* Chavacano */
+  {"cbl",	HB_TAG('Q','I','N',' ')},	/* Bualkhaw Chin */
   {"ce",	HB_TAG('C','H','E',' ')},	/* Chechen */
   {"ceb",	HB_TAG('C','E','B',' ')},	/* Cebuano */
+  {"cfm",	HB_TAG('H','A','L',' ')},	/* Halam/Falam Chin */
   {"cgg",	HB_TAG('C','G','G',' ')},	/* Chiga */
   {"ch",	HB_TAG('C','H','A',' ')},	/* Chamorro */
   {"chk",	HB_TAG('C','H','K','0')},	/* Chuukese */
@@ -279,8 +280,17 @@
   {"chp",	HB_TAG('C','H','P',' ')},	/* Chipewyan */
   {"chr",	HB_TAG('C','H','R',' ')},	/* Cherokee */
   {"chy",	HB_TAG('C','H','Y',' ')},	/* Cheyenne */
+  {"cja",	HB_TAG('C','J','A',' ')},	/* Western Cham */
+  {"cjm",	HB_TAG('C','J','M',' ')},	/* Eastern Cham */
+  {"cka",	HB_TAG('Q','I','N',' ')},	/* Khumi Awa Chin */
   {"ckb",	HB_TAG('K','U','R',' ')},	/* Central Kurdish (Sorani) */
   {"ckt",	HB_TAG('C','H','K',' ')},	/* Chukchi */
+  {"cld",	HB_TAG('S','Y','R',' ')},	/* Chaldean Neo-Aramaic */
+  {"cmr",	HB_TAG('Q','I','N',' ')},	/* Mro-Khimi Chin */
+  {"cnb",	HB_TAG('Q','I','N',' ')},	/* Chinbon Chin */
+  {"cnh",	HB_TAG('Q','I','N',' ')},	/* Hakha Chin */
+  {"cnk",	HB_TAG('Q','I','N',' ')},	/* Khumi Chin */
+  {"cnw",	HB_TAG('Q','I','N',' ')},	/* Ngawn Chin */
   {"cop",	HB_TAG('C','O','P',' ')},	/* Coptic */
   {"cpp",	HB_TAG('C','P','P',' ')},	/* Creoles */
   {"cr",	HB_TAG('C','R','E',' ')},	/* Cree */
@@ -293,6 +303,9 @@
   {"crx",	HB_TAG('C','R','R',' ')},	/* Carrier */
   {"cs",	HB_TAG('C','S','Y',' ')},	/* Czech */
   {"csb",	HB_TAG('C','S','B',' ')},	/* Kashubian */
+  {"csh",	HB_TAG('Q','I','N',' ')},	/* Asho Chin */
+  {"csy",	HB_TAG('Q','I','N',' ')},	/* Siyin Chin */
+  {"ctd",	HB_TAG('Q','I','N',' ')},	/* Tedim Chin */
   {"ctg",	HB_TAG('C','T','G',' ')},	/* Chittagonian */
   {"cts",	HB_TAG('B','I','K',' ')},	/* Northern Catanduanes Bikol */
   {"cu",	HB_TAG('C','S','L',' ')},	/* Church Slavic */
@@ -300,7 +313,9 @@
   {"cv",	HB_TAG('C','H','U',' ')},	/* Chuvash */
   {"cwd",	HB_TAG('D','C','R',' ')},	/* Woods Cree */
   {"cy",	HB_TAG('W','E','L',' ')},	/* Welsh */
+  {"czt",	HB_TAG('Q','I','N',' ')},	/* Zotung Chin */
   {"da",	HB_TAG('D','A','N',' ')},	/* Danish */
+  {"dao",	HB_TAG('Q','I','N',' ')},	/* Daai Chin */
   {"dap",	HB_TAG('N','I','S',' ')},	/* Nisi (India) */
   {"dar",	HB_TAG('D','A','R',' ')},	/* Dargwa */
   {"dax",	HB_TAG('D','A','X',' ')},	/* Dayi */
@@ -343,7 +358,7 @@
   {"fi",	HB_TAG('F','I','N',' ')},	/* Finnish */
   {"fil",	HB_TAG('P','I','L',' ')},	/* Filipino */
   {"fj",	HB_TAG('F','J','I',' ')},	/* Fijian */
-  {"flm",	HB_TAG('H','A','L',' ')},	/* Halam */
+  {"flm",	HB_TAG('H','A','L',' ')},	/* Halam/Falam Chin [retired ISO639 code] */
   {"fo",	HB_TAG('F','O','S',' ')},	/* Faroese */
   {"fon",	HB_TAG('F','O','N',' ')},	/* Fon */
   {"fr",	HB_TAG('F','R','A',' ')},	/* French */
@@ -390,6 +405,7 @@
   {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
   {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
   {"hil",	HB_TAG('H','I','L',' ')},	/* Hiligaynon */
+  {"hlt",	HB_TAG('Q','I','N',' ')},	/* Matu Chin */
   {"hmn",	HB_TAG('H','M','N',' ')},	/* Hmong */
   {"hnd",	HB_TAG('H','N','D',' ')},	/* [Southern] Hindko */
   {"hne",	HB_TAG('C','H','H',' ')},	/* Chattisgarhi */
@@ -553,6 +569,7 @@
   {"mos",	HB_TAG('M','O','S',' ')},	/* Mossi */
   {"mpe",	HB_TAG('M','A','J',' ')},	/* Majang */
   {"mr",	HB_TAG('M','A','R',' ')},	/* Marathi */
+  {"mrh",	HB_TAG('Q','I','N',' ')},	/* Mara Chin */
   {"mrj",	HB_TAG('H','M','A',' ')},	/* High Mari */
   {"ms",	HB_TAG('M','L','Y',' ')},	/* Malay [macrolanguage] */
   {"msc",	HB_TAG('M','N','K',' ')},	/* Sankaran Maninka */
@@ -617,6 +634,7 @@
   {"pcc",	HB_TAG('P','C','C',' ')},	/* Bouyei */
   {"pcd",	HB_TAG('P','C','D',' ')},	/* Picard */
   {"pce",	HB_TAG('P','L','G',' ')},	/* [Ruching] Palaung */
+  {"pck",	HB_TAG('Q','I','N',' ')},	/* Paite Chin */
   {"pdc",	HB_TAG('P','D','C',' ')},	/* Pennsylvania German */
   {"pes",	HB_TAG('F','A','R',' ')},	/* Iranian Persian */
   {"phk",	HB_TAG('P','H','K',' ')},	/* Phake */
@@ -674,6 +692,7 @@
   {"se",	HB_TAG('N','S','M',' ')},	/* Northern Sami */
   {"seh",	HB_TAG('S','N','A',' ')},	/* Sena */
   {"sel",	HB_TAG('S','E','L',' ')},	/* Selkup */
+  {"sez",	HB_TAG('Q','I','N',' ')},	/* Senthang Chin */
   {"sg",	HB_TAG('S','G','O',' ')},	/* Sango */
   {"sga",	HB_TAG('S','G','A',' ')},	/* Old Irish (to 900) */
   {"sgs",	HB_TAG('S','G','S',' ')},	/* Samogitian */
@@ -713,12 +732,15 @@
   {"swh",	HB_TAG('S','W','K',' ')},	/* Kiswahili/Swahili */
   {"swv",	HB_TAG('M','A','W',' ')},	/* Shekhawati */
   {"sxu",	HB_TAG('S','X','U',' ')},	/* Upper Saxon */
+  {"syc",	HB_TAG('S','Y','R',' ')},	/* Classical Syriac */
   {"syl",	HB_TAG('S','Y','L',' ')},	/* Sylheti */
   {"syr",	HB_TAG('S','Y','R',' ')},	/* Syriac [macrolanguage] */
   {"szl",	HB_TAG('S','Z','L',' ')},	/* Silesian */
   {"ta",	HB_TAG('T','A','M',' ')},	/* Tamil */
   {"tab",	HB_TAG('T','A','B',' ')},	/* Tabasaran */
+  {"tcp",	HB_TAG('Q','I','N',' ')},	/* Tawr Chin */
   {"tcy",	HB_TAG('T','U','L',' ')},	/* Tulu */
+  {"tcz",	HB_TAG('Q','I','N',' ')},	/* Thado Chin */
   {"tdd",	HB_TAG('T','D','D',' ')},	/* Tai Nüa */
   {"te",	HB_TAG('T','E','L',' ')},	/* Telugu */
   {"tem",	HB_TAG('T','M','N',' ')},	/* Temne */
@@ -786,11 +808,13 @@
   {"yap",	HB_TAG('Y','A','P',' ')},	/* Yapese */
   {"yi",	HB_TAG('J','I','I',' ')},	/* Yiddish [macrolanguage] */
   {"yo",	HB_TAG('Y','B','A',' ')},	/* Yoruba */
+  {"yos",	HB_TAG('Q','I','N',' ')},	/* Yos, deprecated by IANA in favor of Zou [zom] */
   {"yso",	HB_TAG('N','I','S',' ')},	/* Nisi (China) */
   {"za",	HB_TAG('Z','H','A',' ')},	/* Chuang/Zhuang [macrolanguage] */
   {"zea",	HB_TAG('Z','E','A',' ')},	/* Zeeuws */
   {"zgh",	HB_TAG('Z','G','H',' ')},	/* Standard Morrocan Tamazigh */
   {"zne",	HB_TAG('Z','N','D',' ')},	/* Zande */
+  {"zom",	HB_TAG('Q','I','N',' ')},	/* Zou */
   {"zu",	HB_TAG('Z','U','L',' ')}, 	/* Zulu */
   {"zum",	HB_TAG('L','R','C',' ')},	/* Kumzari */
   {"zza",	HB_TAG('Z','Z','A',' ')},	/* Zazaki */
@@ -907,6 +931,30 @@
     return HB_TAG('A','P','P','H');  /* Phonetic transcription—Americanist conventions */
   }
 
+  /*
+   * "Syre" is a BCP-47 script tag, meaning the Estrangela variant of the Syriac script.
+   * It can be applied to any language.
+   */
+  if (strstr (lang_str, "-syre")) {
+    return HB_TAG('S','Y','R','E');  /* Estrangela Syriac */
+  }
+
+  /*
+   * "Syrj" is a BCP-47 script tag, meaning the Western variant of the Syriac script.
+   * It can be applied to any language.
+   */
+  if (strstr (lang_str, "-syrj")) {
+    return HB_TAG('S','Y','R','J');  /* Western Syriac */
+  }
+
+  /*
+   * "Syrn" is a BCP-47 script tag, meaning the Eastern variant of the Syriac script.
+   * It can be applied to any language.
+   */
+  if (strstr (lang_str, "-syrn")) {
+    return HB_TAG('S','Y','R','N');  /* Eastern Syriac */
+  }
+
   /* Find a language matching in the first component */
   {
     const LangTag *lang_tag;
@@ -962,6 +1010,22 @@
   if (tag == HB_OT_TAG_DEFAULT_LANGUAGE)
     return NULL;
 
+  /* struct LangTag has only room for 3-letter language tags. */
+  switch (tag) {
+  case HB_TAG('A','P','P','H'):  /* Phonetic transcription—Americanist conventions */
+    return hb_language_from_string ("und-fonnapa", -1);
+  case HB_TAG('I','P','P','H'):  /* Phonetic transcription—IPA conventions */
+    return hb_language_from_string ("und-fonipa", -1);
+  case HB_TAG('S','Y','R',' '):  /* Syriac [macrolanguage] */
+    return hb_language_from_string ("syr", -1);
+  case HB_TAG('S','Y','R','E'):  /* Estrangela Syriac */
+    return hb_language_from_string ("und-Syre", -1);
+  case HB_TAG('S','Y','R','J'):  /* Western Syriac */
+    return hb_language_from_string ("und-Syrj", -1);
+  case HB_TAG('S','Y','R','N'):  /* Eastern Syriac */
+    return hb_language_from_string ("und-Syrn", -1);
+  }
+
   for (i = 0; i < ARRAY_LENGTH (ot_languages); i++)
     if (ot_languages[i].tag == tag)
       return hb_language_from_string (ot_languages[i].language, -1);
@@ -976,14 +1040,6 @@
     }
   }
 
-  /* struct LangTag has only room for 3-letter language tags. */
-  switch (tag) {
-  case HB_TAG('A','P','P','H'):  /* Phonetic transcription—Americanist conventions */
-    return hb_language_from_string ("und-fonnapa", -1);
-  case HB_TAG('I','P','P','H'):  /* Phonetic transcription—IPA conventions */
-    return hb_language_from_string ("und-fonipa", -1);
-  }
-
   /* Else return a custom language in the form of "x-hbotABCD" */
   {
     unsigned char buf[11] = "x-hbot";
diff --git a/src/hb-ot-var-avar-table.hh b/src/hb-ot-var-avar-table.hh
new file mode 100644
index 0000000..ace0f5f
--- /dev/null
+++ b/src/hb-ot-var-avar-table.hh
@@ -0,0 +1,144 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_VAR_AVAR_TABLE_HH
+#define HB_OT_VAR_AVAR_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+namespace OT {
+
+
+struct AxisValueMap
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  F2DOT14	fromCoord;	/* A normalized coordinate value obtained using
+				 * default normalization. */
+  F2DOT14	toCoord;	/* The modified, normalized coordinate value. */
+
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct SegmentMaps : ArrayOf<AxisValueMap>
+{
+  inline int map (int value) const
+  {
+    /* The following special-cases are not part of OpenType, which requires
+     * that at least -1, 0, and +1 must be mapped. But we include these as
+     * part of a better error recovery scheme. */
+
+    if (!len)
+      return value;
+
+    if (value <= array[0].fromCoord)
+      return value - array[0].fromCoord + array[0].toCoord;
+
+    unsigned int i;
+    unsigned int count = len;
+    for (i = 1; i < count && value > array[i].fromCoord; i++)
+      ;
+
+    if (value >= array[i].fromCoord)
+      return value - array[i].fromCoord + array[i].toCoord;
+
+    if (unlikely (array[i-1].fromCoord == array[i].fromCoord))
+      return array[i-1].toCoord;
+
+    int denom = array[i].fromCoord - array[i-1].fromCoord;
+    return array[i-1].toCoord +
+	   (array[i].toCoord - array[i-1].toCoord) *
+	   (value - array[i-1].fromCoord + denom/2) / denom;
+  }
+
+  DEFINE_SIZE_ARRAY (2, array);
+};
+
+/*
+ * avar — Axis Variations Table
+ */
+
+#define HB_OT_TAG_avar HB_TAG('a','v','a','r')
+
+struct avar
+{
+  static const hb_tag_t tableTag	= HB_OT_TAG_avar;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!(version.sanitize (c) &&
+		    version.major == 1 &&
+		    c->check_struct (this))))
+      return_trace (false);
+
+    const SegmentMaps *map = &axisSegmentMapsZ;
+    unsigned int count = axisCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (unlikely (!map->sanitize (c)))
+        return_trace (false);
+      map = &StructAfter<SegmentMaps> (*map);
+    }
+
+    return_trace (true);
+  }
+
+  inline void map_coords (int *coords, unsigned int coords_length) const
+  {
+    unsigned int count = MIN<unsigned int> (coords_length, axisCount);
+
+    const SegmentMaps *map = &axisSegmentMapsZ;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      coords[i] = map->map (coords[i]);
+      map = &StructAfter<SegmentMaps> (*map);
+    }
+  }
+
+  protected:
+  FixedVersion<>version;	/* Version of the avar table
+				 * initially set to 0x00010000u */
+  USHORT	reserved;	/* This field is permanently reserved. Set to 0. */
+  USHORT	axisCount;	/* The number of variation axes in the font. This
+				 * must be the same number as axisCount in the
+				 * 'fvar' table. */
+  SegmentMaps	axisSegmentMapsZ;
+
+  public:
+  DEFINE_SIZE_MIN (8);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_VAR_AVAR_TABLE_HH */
diff --git a/src/hb-ot-var-fvar-table.hh b/src/hb-ot-var-fvar-table.hh
new file mode 100644
index 0000000..9f6fb32
--- /dev/null
+++ b/src/hb-ot-var-fvar-table.hh
@@ -0,0 +1,209 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_VAR_FVAR_TABLE_HH
+#define HB_OT_VAR_FVAR_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+namespace OT {
+
+
+struct InstanceRecord
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  c->check_array (coordinates, coordinates[0].static_size, axis_count));
+  }
+
+  protected:
+  USHORT	subfamilyNameID;/* The name ID for entries in the 'name' table
+				 * that provide subfamily names for this instance. */
+  USHORT	reserved;	/* Reserved for future use — set to 0. */
+  Fixed		coordinates[VAR];/* The coordinates array for this instance. */
+  //USHORT	postScriptNameIDX;/*Optional. The name ID for entries in the 'name'
+  //				  * table that provide PostScript names for this
+  //				  * instance. */
+
+  public:
+  DEFINE_SIZE_ARRAY (4, coordinates);
+};
+
+struct AxisRecord
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  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. */
+  USHORT	reserved;	/* Reserved for future use — set to 0. */
+  USHORT	axisNameID;	/* The name ID for entries in the 'name' table that
+				 * provide a display name for this axis. */
+
+  public:
+  DEFINE_SIZE_STATIC (20);
+};
+
+
+/*
+ * fvar — Font Variations Table
+ */
+
+#define HB_OT_TAG_fvar HB_TAG('f','v','a','r')
+
+struct fvar
+{
+  static const hb_tag_t tableTag	= HB_OT_TAG_fvar;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+		  likely (version.major == 1) &&
+		  c->check_struct (this) &&
+		  instanceSize >= axisCount * 4 + 4 &&
+		  axisSize <= 1024 && /* Arbitrary, just to simplify overflow checks. */
+		  instanceSize <= 1024 && /* Arbitrary, just to simplify overflow checks. */
+		  c->check_range (this, things) &&
+		  c->check_range (&StructAtOffset<char> (this, things),
+				  axisCount * axisSize + instanceCount * instanceSize));
+  }
+
+  inline unsigned int get_axis_count (void) const
+  { return axisCount; }
+
+  inline bool get_axis (unsigned int index, hb_ot_var_axis_t *info) const
+  {
+    if (unlikely (index >= axisCount))
+      return false;
+
+    if (info)
+    {
+      const AxisRecord &axis = get_axes ()[index];
+      info->tag = axis.axisTag;
+      info->name_id =  axis.axisNameID;
+      info->default_value = axis.defaultValue / 65536.;
+      /* Ensure order, to simplify client math. */
+      info->min_value = MIN<float> (info->default_value, axis.minValue / 65536.);
+      info->max_value = MAX<float> (info->default_value, axis.maxValue / 65536.);
+    }
+
+    return true;
+  }
+
+  inline unsigned int get_axis_infos (unsigned int      start_offset,
+				      unsigned int     *axes_count /* IN/OUT */,
+				      hb_ot_var_axis_t *axes_array /* OUT */) const
+  {
+    if (axes_count)
+    {
+      unsigned int count = axisCount;
+      start_offset = MIN (start_offset, count);
+
+      count -= start_offset;
+      axes_array += start_offset;
+
+      count = MIN (count, *axes_count);
+      *axes_count = count;
+
+      for (unsigned int i = 0; i < count; i++)
+	get_axis (start_offset + i, axes_array + i);
+    }
+    return axisCount;
+  }
+
+  inline bool find_axis (hb_tag_t tag, unsigned int *index, hb_ot_var_axis_t *info) const
+  {
+    const AxisRecord *axes = get_axes ();
+    unsigned int count = get_axis_count ();
+    for (unsigned int i = 0; i < count; i++)
+      if (axes[i].axisTag == tag)
+      {
+        if (index)
+	  *index = i;
+	return get_axis (i, info);
+      }
+    if (index)
+      *index = HB_OT_VAR_NO_AXIS_INDEX;
+    return false;
+  }
+
+  inline int normalize_axis_value (unsigned int axis_index, float v) const
+  {
+    hb_ot_var_axis_t axis;
+    if (!get_axis (axis_index, &axis))
+      return 0;
+
+    v = MAX (MIN (v, axis.max_value), axis.min_value); /* Clamp. */
+
+    if (v == axis.default_value)
+      return 0;
+    else if (v < axis.default_value)
+      v = (v - axis.default_value) / (axis.default_value - axis.min_value);
+    else
+      v = (v - axis.default_value) / (axis.max_value - axis.default_value);
+    return (int) (v * 16384. + (v >= 0. ? .5 : -.5));
+  }
+
+  protected:
+  inline const AxisRecord * get_axes (void) const
+  { return &StructAtOffset<AxisRecord> (this, things); }
+
+  inline const InstanceRecord * get_instances (void) const
+  { return &StructAtOffset<InstanceRecord> (get_axes () + axisCount, 0); }
+
+  protected:
+  FixedVersion<>version;	/* Version of the fvar table
+				 * initially set to 0x00010000u */
+  Offset<>	things;		/* Offset in bytes from the beginning of the table
+				 * to the start of the AxisRecord array. */
+  USHORT	reserved;	/* This field is permanently reserved. Set to 2. */
+  USHORT	axisCount;	/* The number of variation axes in the font (the
+				 * number of records in the axes array). */
+  USHORT	axisSize;	/* The size in bytes of each VariationAxisRecord —
+				 * set to 20 (0x0014) for this version. */
+  USHORT	instanceCount;	/* The number of named instances defined in the font
+				 * (the number of records in the instances array). */
+  USHORT	instanceSize;	/* The size in bytes of each InstanceRecord — set
+				 * to either axisCount * sizeof(Fixed) + 4, or to
+				 * axisCount * sizeof(Fixed) + 6. */
+
+  public:
+  DEFINE_SIZE_STATIC (16);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_VAR_FVAR_TABLE_HH */
diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh
new file mode 100644
index 0000000..3a2a820
--- /dev/null
+++ b/src/hb-ot-var-hvar-table.hh
@@ -0,0 +1,165 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_VAR_HVAR_TABLE_HH
+#define HB_OT_VAR_HVAR_TABLE_HH
+
+#include "hb-ot-layout-common-private.hh"
+
+
+namespace OT {
+
+
+struct DeltaSetIndexMap
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  c->check_array (mapData, get_width (), mapCount));
+  }
+
+  unsigned int map (unsigned int v) const /* Returns 16.16 outer.inner. */
+  {
+    /* If count is zero, pass value unchanged.  This takes
+     * care of direct mapping for advance map. */
+    if (!mapCount)
+      return v;
+
+    if (v >= mapCount)
+      v = mapCount - 1;
+
+    unsigned int u = 0;
+    { /* Fetch it. */
+      unsigned int w = get_width ();
+      const BYTE *p = mapData + w * v;
+      for (; w; w--)
+	u = (u << 8) + *p++;
+    }
+
+    { /* Repack it. */
+      unsigned int n = get_inner_bitcount ();
+      unsigned int outer = u >> n;
+      unsigned int inner = u & ((1 << n) - 1);
+      u = (outer<<16) | inner;
+    }
+
+    return u;
+  }
+
+  protected:
+  inline unsigned int get_width (void) const
+  { return ((format >> 4) & 3) + 1; }
+
+  inline unsigned int get_inner_bitcount (void) const
+  { return (format & 0xF) + 1; }
+
+  protected:
+  USHORT format;		/* A packed field that describes the compressed
+				 * representation of delta-set indices. */
+  USHORT mapCount;		/* The number of mapping entries. */
+  BYTE mapData[VAR];		/* The delta-set index mapping data. */
+
+  public:
+  DEFINE_SIZE_ARRAY (4, mapData);
+};
+
+
+/*
+ * HVAR -- The Horizontal Metrics Variations Table
+ * VVAR -- The Vertical Metrics Variations Table
+ */
+
+#define HB_OT_TAG_HVAR HB_TAG('H','V','A','R')
+#define HB_OT_TAG_VVAR HB_TAG('V','V','A','R')
+
+struct HVARVVAR
+{
+  static const hb_tag_t HVARTag	= HB_OT_TAG_HVAR;
+  static const hb_tag_t VVARTag	= HB_OT_TAG_VVAR;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+		  likely (version.major == 1) &&
+		  varStore.sanitize (c, this) &&
+		  advMap.sanitize (c, this) &&
+		  lsbMap.sanitize (c, this) &&
+		  rsbMap.sanitize (c, this));
+  }
+
+  inline float get_advance_var (hb_codepoint_t glyph,
+				int *coords, unsigned int coord_count) const
+  {
+    unsigned int varidx = (this+advMap).map (glyph);
+    return (this+varStore).get_delta (varidx, coords, coord_count);
+  }
+
+  inline bool has_sidebearing_deltas (void) const
+  { return lsbMap && rsbMap; }
+
+  protected:
+  FixedVersion<>version;	/* Version of the metrics variation table
+				 * initially set to 0x00010000u */
+  LOffsetTo<VariationStore>
+		varStore;	/* Offset to item variation store table. */
+  LOffsetTo<DeltaSetIndexMap>
+		advMap;		/* Offset to advance var-idx mapping. */
+  LOffsetTo<DeltaSetIndexMap>
+		lsbMap;		/* Offset to lsb/tsb var-idx mapping. */
+  LOffsetTo<DeltaSetIndexMap>
+		rsbMap;		/* Offset to rsb/bsb var-idx mapping. */
+
+  public:
+  DEFINE_SIZE_STATIC (20);
+};
+
+struct HVAR : HVARVVAR {
+  static const hb_tag_t tableTag	= HB_OT_TAG_HVAR;
+};
+struct VVAR : HVARVVAR {
+  static const hb_tag_t tableTag	= HB_OT_TAG_VVAR;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (static_cast<const HVARVVAR *> (this)->sanitize (c) &&
+		  vorgMap.sanitize (c, this));
+  }
+
+  protected:
+  LOffsetTo<DeltaSetIndexMap>
+		vorgMap;	/* Offset to vertical-origin var-idx mapping. */
+
+  public:
+  DEFINE_SIZE_STATIC (24);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_VAR_HVAR_TABLE_HH */
diff --git a/src/hb-ot-var.cc b/src/hb-ot-var.cc
new file mode 100644
index 0000000..d4d16df
--- /dev/null
+++ b/src/hb-ot-var.cc
@@ -0,0 +1,160 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-open-type-private.hh"
+
+#include "hb-ot-layout-private.hh"
+#include "hb-ot-var-avar-table.hh"
+#include "hb-ot-var-fvar-table.hh"
+#include "hb-ot-var.h"
+
+HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
+
+/*
+ * fvar/avar
+ */
+
+static inline const OT::fvar&
+_get_fvar (hb_face_t *face)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::fvar);
+  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
+  return *(layout->fvar.get ());
+}
+static inline const OT::avar&
+_get_avar (hb_face_t *face)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::avar);
+  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
+  return *(layout->avar.get ());
+}
+
+/**
+ * hb_ot_var_has_data:
+ * @face: #hb_face_t to test
+ *
+ * This function allows to verify the presence of OpenType variation data on the face.
+ * Alternatively, use hb_ot_var_get_axis_count().
+ *
+ * Return value: true if face has a `fvar' table and false otherwise
+ *
+ * Since: 1.4.2
+ **/
+hb_bool_t
+hb_ot_var_has_data (hb_face_t *face)
+{
+  return &_get_fvar (face) != &OT::Null(OT::fvar);
+}
+
+/**
+ * hb_ot_var_get_axis_count:
+ *
+ * Since: 1.4.2
+ **/
+unsigned int
+hb_ot_var_get_axis_count (hb_face_t *face)
+{
+  const OT::fvar &fvar = _get_fvar (face);
+  return fvar.get_axis_count ();
+}
+
+/**
+ * hb_ot_var_get_axes:
+ *
+ * Since: 1.4.2
+ **/
+unsigned int
+hb_ot_var_get_axes (hb_face_t        *face,
+		    unsigned int      start_offset,
+		    unsigned int     *axes_count /* IN/OUT */,
+		    hb_ot_var_axis_t *axes_array /* OUT */)
+{
+  const OT::fvar &fvar = _get_fvar (face);
+  return fvar.get_axis_infos (start_offset, axes_count, axes_array);
+}
+
+/**
+ * hb_ot_var_find_axis:
+ *
+ * Since: 1.4.2
+ **/
+hb_bool_t
+hb_ot_var_find_axis (hb_face_t        *face,
+		     hb_tag_t          axis_tag,
+		     unsigned int     *axis_index,
+		     hb_ot_var_axis_t *axis_info)
+{
+  const OT::fvar &fvar = _get_fvar (face);
+  return fvar.find_axis (axis_tag, axis_index, axis_info);
+}
+
+
+/**
+ * hb_ot_var_normalize_variations:
+ *
+ * Since: 1.4.2
+ **/
+void
+hb_ot_var_normalize_variations (hb_face_t            *face,
+				const hb_variation_t *variations, /* IN */
+				unsigned int          variations_length,
+				int                  *coords, /* OUT */
+				unsigned int          coords_length)
+{
+  for (unsigned int i = 0; i < coords_length; i++)
+    coords[i] = 0;
+
+  const OT::fvar &fvar = _get_fvar (face);
+  for (unsigned int i = 0; i < variations_length; i++)
+  {
+    unsigned int axis_index;
+    if (hb_ot_var_find_axis (face, variations[i].tag, &axis_index, NULL) &&
+	axis_index < coords_length)
+      coords[axis_index] = fvar.normalize_axis_value (axis_index, variations[i].value);
+  }
+
+  const OT::avar &avar = _get_avar (face);
+  avar.map_coords (coords, coords_length);
+}
+
+/**
+ * hb_ot_var_normalize_coords:
+ *
+ * Since: 1.4.2
+ **/
+void
+hb_ot_var_normalize_coords (hb_face_t    *face,
+			    unsigned int coords_length,
+			    const float *design_coords, /* IN */
+			    int *normalized_coords /* OUT */)
+{
+  const OT::fvar &fvar = _get_fvar (face);
+  for (unsigned int i = 0; i < coords_length; i++)
+    normalized_coords[i] = fvar.normalize_axis_value (i, design_coords[i]);
+
+  const OT::avar &avar = _get_avar (face);
+  avar.map_coords (normalized_coords, coords_length);
+}
diff --git a/src/hb-ot-var.h b/src/hb-ot-var.h
new file mode 100644
index 0000000..a2c0c5f
--- /dev/null
+++ b/src/hb-ot-var.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
+#ifndef HB_OT_VAR_H
+#define HB_OT_VAR_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+
+#define HB_OT_TAG_VAR_AXIS_ITALIC	HB_TAG('i','t','a','l')
+#define HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE	HB_TAG('o','p','s','z')
+#define HB_OT_TAG_VAR_AXIS_SLANT	HB_TAG('s','l','n','t')
+#define HB_OT_TAG_VAR_AXIS_WIDTH	HB_TAG('w','d','t','h')
+#define HB_OT_TAG_VAR_AXIS_WEIGHT	HB_TAG('w','g','h','t')
+
+
+/*
+ * fvar / avar
+ */
+
+/**
+ * hb_ot_var_axis_t:
+ *
+ * Since: 1.4.2
+ */
+typedef struct hb_ot_var_axis_t {
+  hb_tag_t tag;
+  unsigned int name_id;
+  float min_value;
+  float default_value;
+  float max_value;
+} hb_ot_var_axis_t;
+
+HB_EXTERN hb_bool_t
+hb_ot_var_has_data (hb_face_t *face);
+
+/**
+ * HB_OT_VAR_NO_AXIS_INDEX:
+ *
+ * Since: 1.4.2
+ */
+#define HB_OT_VAR_NO_AXIS_INDEX		0xFFFFFFFFu
+
+HB_EXTERN unsigned int
+hb_ot_var_get_axis_count (hb_face_t *face);
+
+HB_EXTERN unsigned int
+hb_ot_var_get_axes (hb_face_t        *face,
+		    unsigned int      start_offset,
+		    unsigned int     *axes_count /* IN/OUT */,
+		    hb_ot_var_axis_t *axes_array /* OUT */);
+
+HB_EXTERN hb_bool_t
+hb_ot_var_find_axis (hb_face_t        *face,
+		     hb_tag_t          axis_tag,
+		     unsigned int     *axis_index,
+		     hb_ot_var_axis_t *axis_info);
+
+
+HB_EXTERN void
+hb_ot_var_normalize_variations (hb_face_t            *face,
+				const hb_variation_t *variations, /* IN */
+				unsigned int          variations_length,
+				int                  *coords, /* OUT */
+				unsigned int          coords_length);
+
+HB_EXTERN void
+hb_ot_var_normalize_coords (hb_face_t    *face,
+			    unsigned int coords_length,
+			    const float *design_coords, /* IN */
+			    int *normalized_coords /* OUT */);
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_VAR_H */
diff --git a/src/hb-ot.h b/src/hb-ot.h
index 47c92a5..2120a3e 100644
--- a/src/hb-ot.h
+++ b/src/hb-ot.h
@@ -32,8 +32,10 @@
 
 #include "hb-ot-font.h"
 #include "hb-ot-layout.h"
+#include "hb-ot-math.h"
 #include "hb-ot-tag.h"
 #include "hb-ot-shape.h"
+#include "hb-ot-var.h"
 
 HB_BEGIN_DECLS
 
diff --git a/src/hb-private.hh b/src/hb-private.hh
index c45be6f..666af62 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -689,17 +689,20 @@
     fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), "");
 
   if (indented) {
-/* One may want to add ASCII version of these.  See:
- * https://bugs.freedesktop.org/show_bug.cgi?id=50970 */
 #define VBAR	"\342\224\202"	/* U+2502 BOX DRAWINGS LIGHT VERTICAL */
 #define VRBAR	"\342\224\234"	/* U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
 #define DLBAR	"\342\225\256"	/* U+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT */
 #define ULBAR	"\342\225\257"	/* U+256F BOX DRAWINGS LIGHT ARC UP AND LEFT */
 #define LBAR	"\342\225\264"	/* U+2574 BOX DRAWINGS LIGHT LEFT */
-    static const char bars[] = VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR;
+    static const char bars[] =
+      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
+      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
+      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
+      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
+      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR;
     fprintf (stderr, "%2u %s" VRBAR "%s",
 	     level,
-	     bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), (unsigned int) (sizeof (VBAR) - 1) * level),
+	     bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level),
 	     level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR);
   } else
     fprintf (stderr, "   " VRBAR LBAR);
diff --git a/src/hb-set.cc b/src/hb-set.cc
index cb7fcdb..f3fe1ba 100644
--- a/src/hb-set.cc
+++ b/src/hb-set.cc
@@ -105,7 +105,7 @@
  * @set: a set.
  * @key:
  * @data:
- * @destroy (closure data):
+ * @destroy:
  * @replace:
  *
  * Return value:
diff --git a/src/hb-shape-plan-private.hh b/src/hb-shape-plan-private.hh
index 607da5e..aa0413a 100644
--- a/src/hb-shape-plan-private.hh
+++ b/src/hb-shape-plan-private.hh
@@ -47,12 +47,17 @@
   hb_feature_t *user_features;
   unsigned int num_user_features;
 
+  int *coords;
+  unsigned int num_coords;
+
   struct hb_shaper_data_t shaper_data;
 };
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS \
-	, const hb_feature_t            *user_features \
-	, unsigned int                   num_user_features
+	, const hb_feature_t *user_features \
+	, unsigned int        num_user_features \
+	, const int          *coords \
+	, unsigned int        num_coords
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, shape_plan);
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
diff --git a/src/hb-shape-plan.cc b/src/hb-shape-plan.cc
index 87231fb..600faae 100644
--- a/src/hb-shape-plan.cc
+++ b/src/hb-shape-plan.cc
@@ -46,11 +46,14 @@
 hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
 		    const hb_feature_t *user_features,
 		    unsigned int        num_user_features,
+		    const int          *coords,
+		    unsigned int        num_coords,
 		    const char * const *shaper_list)
 {
   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
-		  "num_features=%d shaper_list=%p",
+		  "num_features=%d num_coords=%d shaper_list=%p",
 		  num_user_features,
+		  num_coords,
 		  shaper_list);
 
   const hb_shaper_pair_t *shapers = _hb_shapers_get ();
@@ -59,7 +62,9 @@
 	HB_STMT_START { \
 	  if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) { \
 	    HB_SHAPER_DATA (shaper, shape_plan) = \
-	      HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, user_features, num_user_features); \
+	      HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, \
+							       user_features, num_user_features, \
+							       coords, num_coords); \
 	    shape_plan->shaper_func = _hb_##shaper##_shape; \
 	    shape_plan->shaper_name = #shaper; \
 	    return; \
@@ -115,14 +120,31 @@
 		      unsigned int                   num_user_features,
 		      const char * const            *shaper_list)
 {
+  return hb_shape_plan_create2 (face, props,
+				user_features, num_user_features,
+				NULL, 0,
+				shaper_list);
+}
+
+hb_shape_plan_t *
+hb_shape_plan_create2 (hb_face_t                     *face,
+		       const hb_segment_properties_t *props,
+		       const hb_feature_t            *user_features,
+		       unsigned int                   num_user_features,
+		       const int                     *orig_coords,
+		       unsigned int                   num_coords,
+		       const char * const            *shaper_list)
+{
   DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
-		  "face=%p num_features=%d shaper_list=%p",
+		  "face=%p num_features=%d num_coords=%d shaper_list=%p",
 		  face,
 		  num_user_features,
+		  num_coords,
 		  shaper_list);
 
   hb_shape_plan_t *shape_plan;
   hb_feature_t *features = NULL;
+  int *coords = NULL;
 
   if (unlikely (!face))
     face = hb_face_get_empty ();
@@ -130,7 +152,14 @@
     return hb_shape_plan_get_empty ();
   if (num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t))))
     return hb_shape_plan_get_empty ();
-  if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) {
+  if (num_coords && !(coords = (int *) calloc (num_coords, sizeof (int))))
+  {
+    free (features);
+    return hb_shape_plan_get_empty ();
+  }
+  if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
+  {
+    free (coords);
     free (features);
     return hb_shape_plan_get_empty ();
   }
@@ -145,8 +174,15 @@
   shape_plan->user_features = features;
   if (num_user_features)
     memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
+  shape_plan->num_coords = num_coords;
+  shape_plan->coords = coords;
+  if (num_coords)
+    memcpy (coords, orig_coords, num_coords * sizeof (int));
 
-  hb_shape_plan_plan (shape_plan, user_features, num_user_features, shaper_list);
+  hb_shape_plan_plan (shape_plan,
+		      user_features, num_user_features,
+		      coords, num_coords,
+		      shaper_list);
 
   return shape_plan;
 }
@@ -176,6 +212,9 @@
     NULL, /* user_features */
     0,    /* num_user_featurs */
 
+    NULL, /* coords */
+    0,    /* num_coords */
+
     {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
 #include "hb-shaper-list.hh"
@@ -220,6 +259,7 @@
 #undef HB_SHAPER_IMPLEMENT
 
   free (shape_plan->user_features);
+  free (shape_plan->coords);
 
   free (shape_plan);
 }
@@ -351,6 +391,8 @@
   const char * const            *shaper_list;
   const hb_feature_t            *user_features;
   unsigned int                   num_user_features;
+  const int                     *coords;
+  unsigned int                   num_coords;
   hb_shape_func_t               *shaper_func;
 };
 
@@ -358,12 +400,26 @@
 hb_shape_plan_user_features_match (const hb_shape_plan_t          *shape_plan,
 				   const hb_shape_plan_proposal_t *proposal)
 {
-  if (proposal->num_user_features != shape_plan->num_user_features) return false;
+  if (proposal->num_user_features != shape_plan->num_user_features)
+    return false;
   for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++)
     if (proposal->user_features[i].tag   != shape_plan->user_features[i].tag   ||
         proposal->user_features[i].value != shape_plan->user_features[i].value ||
         proposal->user_features[i].start != shape_plan->user_features[i].start ||
-        proposal->user_features[i].end   != shape_plan->user_features[i].end) return false;
+        proposal->user_features[i].end   != shape_plan->user_features[i].end)
+      return false;
+  return true;
+}
+
+static inline hb_bool_t
+hb_shape_plan_coords_match (const hb_shape_plan_t          *shape_plan,
+			    const hb_shape_plan_proposal_t *proposal)
+{
+  if (proposal->num_coords != shape_plan->num_coords)
+    return false;
+  for (unsigned int i = 0, n = proposal->num_coords; i < n; i++)
+    if (proposal->coords[i] != shape_plan->coords[i])
+      return false;
   return true;
 }
 
@@ -373,6 +429,7 @@
 {
   return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
 	 hb_shape_plan_user_features_match (shape_plan, proposal) &&
+	 hb_shape_plan_coords_match (shape_plan, proposal) &&
 	 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
 	  (shape_plan->shaper_func == proposal->shaper_func));
 }
@@ -389,6 +446,13 @@
   return false;
 }
 
+static inline hb_bool_t
+hb_coords_present (const int *coords,
+		   unsigned int num_coords)
+{
+  return num_coords != 0;
+}
+
 /**
  * hb_shape_plan_create_cached:
  * @face: 
@@ -410,6 +474,21 @@
 			     unsigned int                   num_user_features,
 			     const char * const            *shaper_list)
 {
+  return hb_shape_plan_create_cached2 (face, props,
+				       user_features, num_user_features,
+				       NULL, 0,
+				       shaper_list);
+}
+
+hb_shape_plan_t *
+hb_shape_plan_create_cached2 (hb_face_t                     *face,
+			      const hb_segment_properties_t *props,
+			      const hb_feature_t            *user_features,
+			      unsigned int                   num_user_features,
+			      const int                     *coords,
+			      unsigned int                   num_coords,
+			      const char * const            *shaper_list)
+{
   DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
 		  "face=%p num_features=%d shaper_list=%p",
 		  face,
@@ -456,16 +535,21 @@
 
   /* Not found. */
 
-  hb_shape_plan_t *shape_plan = hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list);
+  hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props,
+						       user_features, num_user_features,
+						       coords, num_coords,
+						       shaper_list);
 
   /* Don't add to the cache if face is inert. */
   if (unlikely (hb_object_is_inert (face)))
     return shape_plan;
 
   /* Don't add the plan to the cache if there were user features with non-global ranges */
-
   if (hb_non_global_user_features_present (user_features, num_user_features))
     return shape_plan;
+  /* Don't add the plan to the cache if there were variation coordinates XXX Fix me. */
+  if (hb_coords_present (coords, num_coords))
+    return shape_plan;
 
   hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
   if (unlikely (!node))
diff --git a/src/hb-shape-plan.h b/src/hb-shape-plan.h
index aa5e0c7..b62ae7c 100644
--- a/src/hb-shape-plan.h
+++ b/src/hb-shape-plan.h
@@ -53,6 +53,25 @@
 			     const char * const            *shaper_list);
 
 HB_EXTERN hb_shape_plan_t *
+hb_shape_plan_create2 (hb_face_t                     *face,
+		       const hb_segment_properties_t *props,
+		       const hb_feature_t            *user_features,
+		       unsigned int                   num_user_features,
+		       const int                     *coords,
+		       unsigned int                   num_coords,
+		       const char * const            *shaper_list);
+
+HB_EXTERN hb_shape_plan_t *
+hb_shape_plan_create_cached2 (hb_face_t                     *face,
+			      const hb_segment_properties_t *props,
+			      const hb_feature_t            *user_features,
+			      unsigned int                   num_user_features,
+			      const int                     *coords,
+			      unsigned int                   num_coords,
+			      const char * const            *shaper_list);
+
+
+HB_EXTERN hb_shape_plan_t *
 hb_shape_plan_get_empty (void);
 
 HB_EXTERN hb_shape_plan_t *
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 41a4fc5..f080a15 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -45,254 +45,6 @@
  * contains the output glyphs and their positions.
  **/
 
-static bool
-parse_space (const char **pp, const char *end)
-{
-  while (*pp < end && ISSPACE (**pp))
-    (*pp)++;
-  return true;
-}
-
-static bool
-parse_char (const char **pp, const char *end, char c)
-{
-  parse_space (pp, end);
-
-  if (*pp == end || **pp != c)
-    return false;
-
-  (*pp)++;
-  return true;
-}
-
-static bool
-parse_uint (const char **pp, const char *end, unsigned int *pv)
-{
-  char buf[32];
-  unsigned int len = 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, 0);
-  if (errno || p == pend)
-    return false;
-
-  *pv = v;
-  *pp += pend - p;
-  return true;
-}
-
-static bool
-parse_bool (const char **pp, const char *end, unsigned int *pv)
-{
-  parse_space (pp, end);
-
-  const char *p = *pp;
-  while (*pp < end && ISALPHA(**pp))
-    (*pp)++;
-
-  /* CSS allows on/off as aliases 1/0. */
-  if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
-    *pv = 1;
-  else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
-    *pv = 0;
-  else
-    return false;
-
-  return true;
-}
-
-static bool
-parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
-{
-  if (parse_char (pp, end, '-'))
-    feature->value = 0;
-  else {
-    parse_char (pp, end, '+');
-    feature->value = 1;
-  }
-
-  return true;
-}
-
-static bool
-parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature)
-{
-  parse_space (pp, end);
-
-  char quote = 0;
-
-  if (*pp < end && (**pp == '\'' || **pp == '"'))
-  {
-    quote = **pp;
-    (*pp)++;
-  }
-
-  const char *p = *pp;
-  while (*pp < end && ISALNUM(**pp))
-    (*pp)++;
-
-  if (p == *pp || *pp - p > 4)
-    return false;
-
-  feature->tag = hb_tag_from_string (p, *pp - p);
-
-  if (quote)
-  {
-    /* CSS expects exactly four bytes.  And we only allow quotations for
-     * CSS compatibility.  So, enforce the length. */
-     if (*pp - p != 4)
-       return false;
-    if (*pp == end || **pp != quote)
-      return false;
-    (*pp)++;
-  }
-
-  return true;
-}
-
-static bool
-parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
-{
-  parse_space (pp, end);
-
-  bool has_start;
-
-  feature->start = 0;
-  feature->end = (unsigned int) -1;
-
-  if (!parse_char (pp, end, '['))
-    return true;
-
-  has_start = parse_uint (pp, end, &feature->start);
-
-  if (parse_char (pp, end, ':')) {
-    parse_uint (pp, end, &feature->end);
-  } else {
-    if (has_start)
-      feature->end = feature->start + 1;
-  }
-
-  return parse_char (pp, end, ']');
-}
-
-static bool
-parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
-{
-  bool had_equal = parse_char (pp, end, '=');
-  bool had_value = parse_uint (pp, end, &feature->value) ||
-                   parse_bool (pp, end, &feature->value);
-  /* CSS doesn't use equal-sign between tag and value.
-   * If there was an equal-sign, then there *must* be a value.
-   * A value without an eqaul-sign is ok, but not required. */
-  return !had_equal || had_value;
-}
-
-
-static bool
-parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
-{
-  return parse_feature_value_prefix (pp, end, feature) &&
-	 parse_feature_tag (pp, end, feature) &&
-	 parse_feature_indices (pp, end, feature) &&
-	 parse_feature_value_postfix (pp, end, feature) &&
-	 parse_space (pp, end) &&
-	 *pp == end;
-}
-
-/**
- * hb_feature_from_string:
- * @str: (array length=len) (element-type uint8_t): a string to parse
- * @len: length of @str, or -1 if string is %NULL terminated
- * @feature: (out): the #hb_feature_t to initialize with the parsed values
- *
- * Parses a string into a #hb_feature_t.
- *
- * TODO: document the syntax here.
- *
- * Return value:
- * %true if @str is successfully parsed, %false otherwise.
- *
- * Since: 0.9.5
- **/
-hb_bool_t
-hb_feature_from_string (const char *str, int len,
-			hb_feature_t *feature)
-{
-  hb_feature_t feat;
-
-  if (len < 0)
-    len = strlen (str);
-
-  if (likely (parse_one_feature (&str, str + len, &feat)))
-  {
-    if (feature)
-      *feature = feat;
-    return true;
-  }
-
-  if (feature)
-    memset (feature, 0, sizeof (*feature));
-  return false;
-}
-
-/**
- * hb_feature_to_string:
- * @feature: an #hb_feature_t to convert
- * @buf: (array length=size) (out): output string
- * @size: the allocated size of @buf
- *
- * Converts a #hb_feature_t into a %NULL-terminated string in the format
- * understood by hb_feature_from_string(). The client in responsible for
- * allocating big enough size for @buf, 128 bytes is more than enough.
- *
- * Since: 0.9.5
- **/
-void
-hb_feature_to_string (hb_feature_t *feature,
-		      char *buf, unsigned int size)
-{
-  if (unlikely (!size)) return;
-
-  char s[128];
-  unsigned int len = 0;
-  if (feature->value == 0)
-    s[len++] = '-';
-  hb_tag_to_string (feature->tag, s + len);
-  len += 4;
-  while (len && s[len - 1] == ' ')
-    len--;
-  if (feature->start != 0 || feature->end != (unsigned int) -1)
-  {
-    s[len++] = '[';
-    if (feature->start)
-      len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
-    if (feature->end != feature->start + 1) {
-      s[len++] = ':';
-      if (feature->end != (unsigned int) -1)
-	len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
-    }
-    s[len++] = ']';
-  }
-  if (feature->value > 1)
-  {
-    s[len++] = '=';
-    len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
-  }
-  assert (len < ARRAY_LENGTH (s));
-  len = MIN (len, size - 1);
-  memcpy (buf, s, len);
-  buf[len] = '\0';
-}
-
-
 static const char **static_shaper_list;
 
 #ifdef HB_USE_ATEXIT
@@ -362,7 +114,7 @@
  * shapers will be used in the given order, otherwise the default shapers list
  * will be used.
  *
- * Return value: %FALSE if all shapers failed, %TRUE otherwise
+ * Return value: false if all shapers failed, true otherwise
  *
  * Since: 0.9.2
  **/
@@ -373,7 +125,10 @@
 	       unsigned int        num_features,
 	       const char * const *shaper_list)
 {
-  hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list);
+  hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached2 (font->face, &buffer->props,
+							      features, num_features,
+							      font->coords, font->num_coords,
+							      shaper_list);
   hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features);
   hb_shape_plan_destroy (shape_plan);
 
diff --git a/src/hb-shape.h b/src/hb-shape.h
index 53bb845..39507ff 100644
--- a/src/hb-shape.h
+++ b/src/hb-shape.h
@@ -40,22 +40,6 @@
 HB_BEGIN_DECLS
 
 
-typedef struct hb_feature_t {
-  hb_tag_t      tag;
-  uint32_t      value;
-  unsigned int  start;
-  unsigned int  end;
-} hb_feature_t;
-
-HB_EXTERN hb_bool_t
-hb_feature_from_string (const char *str, int len,
-			hb_feature_t *feature);
-
-HB_EXTERN void
-hb_feature_to_string (hb_feature_t *feature,
-		      char *buf, unsigned int size);
-
-
 HB_EXTERN void
 hb_shape (hb_font_t           *font,
 	  hb_buffer_t         *buffer,
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index 487d10b..d553a71 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -131,12 +131,12 @@
 #define HB_UNICODE_FUNCS_IMPLEMENT(set) \
   return hb_##set##_get_unicode_funcs ();
 
-#ifdef HAVE_GLIB
+#if defined(HAVE_UCDN)
+  HB_UNICODE_FUNCS_IMPLEMENT(ucdn)
+#elif defined(HAVE_GLIB)
   HB_UNICODE_FUNCS_IMPLEMENT(glib)
 #elif defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN)
   HB_UNICODE_FUNCS_IMPLEMENT(icu)
-#elif defined(HAVE_UCDN)
-  HB_UNICODE_FUNCS_IMPLEMENT(ucdn)
 #else
 #define HB_UNICODE_FUNCS_NIL 1
   HB_UNICODE_FUNCS_IMPLEMENT(nil)
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 07007a6..6e4db01 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -587,7 +587,9 @@
 hb_uniscribe_shaper_shape_plan_data_t *
 _hb_uniscribe_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					     const hb_feature_t *user_features HB_UNUSED,
-					     unsigned int        num_user_features HB_UNUSED)
+					     unsigned int        num_user_features HB_UNUSED,
+					     const int          *coords HB_UNUSED,
+					     unsigned int        num_coords HB_UNUSED)
 {
   return (hb_uniscribe_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
diff --git a/src/hb-version.h b/src/hb-version.h
index bf199ce..df83a8a 100644
--- a/src/hb-version.h
+++ b/src/hb-version.h
@@ -37,10 +37,10 @@
 
 
 #define HB_VERSION_MAJOR 1
-#define HB_VERSION_MINOR 3
+#define HB_VERSION_MINOR 4
 #define HB_VERSION_MICRO 2
 
-#define HB_VERSION_STRING "1.3.2"
+#define HB_VERSION_STRING "1.4.2"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index d7d40af..530bf3e 100644
--- a/test/api/Makefile.am
+++ b/test/api/Makefile.am
@@ -43,10 +43,29 @@
 
 
 if HAVE_OT
+
 TEST_PROGS += \
 	test-ot-tag \
 	$(NULL)
-endif
+
+if HAVE_FREETYPE
+TEST_PROGS += \
+	test-ot-math \
+	$(NULL)
+test_ot_math_LDADD = $(LDADD) $(FREETYPE_LIBS)
+test_ot_math_CPPFLAGS = $(AM_CPPFLAGS) $(FREETYPE_CFLAGS)
+EXTRA_DIST += \
+	fonts/MathTestFontEmpty.otf \
+	fonts/MathTestFontFull.otf \
+	fonts/MathTestFontNone.otf \
+	fonts/MathTestFontPartial1.otf \
+	fonts/MathTestFontPartial2.otf \
+	fonts/MathTestFontPartial3.otf \
+	fonts/MathTestFontPartial4.otf \
+	$(NULL)
+endif # HAVE_FREETYPE
+
+endif # HAVE_OT
 
 # Tests for header compilation
 TEST_PROGS += \
@@ -73,6 +92,8 @@
 	G_DEBUG=gc-friendly \
 	G_SLICE=always-malloc \
 	srcdir=$(srcdir) \
+	G_TEST_SRCDIR=$(abs_srcdir) \
+	G_TEST_BUILDDIR=$(abs_builddir) \
 	$(NULL)
 
 
diff --git a/test/api/fonts/MathTestFontEmpty.otf b/test/api/fonts/MathTestFontEmpty.otf
new file mode 100644
index 0000000..6b50d66
--- /dev/null
+++ b/test/api/fonts/MathTestFontEmpty.otf
Binary files differ
diff --git a/test/api/fonts/MathTestFontFull.otf b/test/api/fonts/MathTestFontFull.otf
new file mode 100644
index 0000000..6c7c9a9
--- /dev/null
+++ b/test/api/fonts/MathTestFontFull.otf
Binary files differ
diff --git a/test/api/fonts/MathTestFontNone.otf b/test/api/fonts/MathTestFontNone.otf
new file mode 100644
index 0000000..52984ee
--- /dev/null
+++ b/test/api/fonts/MathTestFontNone.otf
Binary files differ
diff --git a/test/api/fonts/MathTestFontPartial1.otf b/test/api/fonts/MathTestFontPartial1.otf
new file mode 100644
index 0000000..b3bf36e
--- /dev/null
+++ b/test/api/fonts/MathTestFontPartial1.otf
Binary files differ
diff --git a/test/api/fonts/MathTestFontPartial2.otf b/test/api/fonts/MathTestFontPartial2.otf
new file mode 100644
index 0000000..4607c11
--- /dev/null
+++ b/test/api/fonts/MathTestFontPartial2.otf
Binary files differ
diff --git a/test/api/fonts/MathTestFontPartial3.otf b/test/api/fonts/MathTestFontPartial3.otf
new file mode 100644
index 0000000..ca18a9a
--- /dev/null
+++ b/test/api/fonts/MathTestFontPartial3.otf
Binary files differ
diff --git a/test/api/fonts/MathTestFontPartial4.otf b/test/api/fonts/MathTestFontPartial4.otf
new file mode 100644
index 0000000..cda3057
--- /dev/null
+++ b/test/api/fonts/MathTestFontPartial4.otf
Binary files differ
diff --git a/test/api/test-ot-math.c b/test/api/test-ot-math.c
new file mode 100644
index 0000000..0ca5566
--- /dev/null
+++ b/test/api/test-ot-math.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright © 2016  Igalia S.L.
+ *
+ *  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.
+ *
+ * Igalia Author(s): Frédéric Wang
+ */
+
+
+#include "hb-test.h"
+
+#include "hb-ft.h"
+#include "hb-ot.h"
+
+/* Unit tests for hb-ot-math.h - OpenType MATH table  */
+
+static FT_Library ft_library;
+static FT_Face ft_face;
+static hb_font_t *hb_font;
+static hb_face_t *hb_face;
+
+static inline void
+initFreeType (void)
+{
+  FT_Error ft_error;
+  if ((ft_error = FT_Init_FreeType (&ft_library)))
+    abort();
+}
+
+static inline void
+cleanupFreeType (void)
+{
+  FT_Done_FreeType (ft_library);
+}
+
+static void
+openFont(const char* fontFile)
+{
+#if GLIB_CHECK_VERSION(2,37,2)
+  gchar* path = g_test_build_filename(G_TEST_DIST, fontFile, NULL);
+#else
+  gchar* path = g_strdup(fontFile);
+#endif
+
+  FT_Error ft_error;
+  if ((ft_error = FT_New_Face (ft_library, path, 0, &ft_face))) {
+    g_free(path);
+    abort();
+  }
+  g_free(path);
+
+  if ((ft_error = FT_Set_Char_Size (ft_face, 2000, 1000, 0, 0)))
+    abort();
+  hb_font = hb_ft_font_create (ft_face, NULL);
+  hb_face = hb_face_reference (hb_font_get_face (hb_font));
+}
+
+static inline void
+closeFont (void)
+{
+  hb_face_destroy (hb_face);
+  hb_font_destroy (hb_font);
+  FT_Done_Face (ft_face);
+  hb_face = NULL;
+  hb_font = NULL;
+  ft_face = NULL;
+}
+
+static void
+test_has_data (void)
+{
+  initFreeType();
+
+  openFont("fonts/MathTestFontNone.otf");
+  g_assert(!hb_ot_math_has_data (hb_face)); // MATH table not available
+  closeFont();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_ot_math_has_data (hb_face)); // MATH table available
+  closeFont();
+
+  hb_face = hb_face_get_empty ();
+  hb_font = hb_font_create (hb_face);
+  g_assert(!hb_ot_math_has_data (hb_face)); // MATH table not available
+
+  hb_font = hb_font_get_empty ();
+  hb_face = hb_font_get_face (hb_font);
+  g_assert(!hb_ot_math_has_data (hb_face)); // MATH table not available
+
+  cleanupFreeType();
+}
+
+static void
+test_get_constant (void)
+{
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert_cmpint(hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT), ==, 0); // MathConstants not available
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT)), ==, 100);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT)), ==, 200);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_MATH_LEADING)), ==, 300);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_AXIS_HEIGHT)), ==, 400);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT)), ==, 500);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT)), ==, 600);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN)), ==, 700);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX)), ==, 800);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN)), ==, 900);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP)), ==, 1100);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED)), ==, 1200);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN)), ==, 1300);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX)), ==, 1400);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN)), ==, 1500);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT)), ==, 1600);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT)), ==, 3400);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN)), ==, 1800);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN)), ==, 1900);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN)), ==, 2200);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN)), ==, 2300);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP)), ==, 2400);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP)), ==, 2500);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN)), ==, 2600);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN)), ==, 2700);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_GAP_MIN)), ==, 2800);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN)), ==, 2900);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP)), ==, 3000);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN)), ==, 3100);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN)), ==, 3200);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN)), ==, 3300);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP)), ==, 3400);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP)), ==, 3500);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN)), ==, 3600);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN)), ==, 3700);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN)), ==, 3800);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN)), ==, 3900);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS)), ==, 4000);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN)), ==, 4100);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN)), ==, 4200);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP)), ==, 8600);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP)), ==, 4400);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP)), ==, 4500);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS)), ==, 4600);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER)), ==, 4700);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP)), ==, 4800);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS)), ==, 4900);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER)), ==, 5000);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP)), ==, 5100);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP)), ==, 5200);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS)), ==, 5300);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER)), ==, 5400);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE)), ==, 11000);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE)), ==, 11200);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN)), ==, 87);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN)), ==, 76);
+  g_assert_cmpint((hb_ot_math_get_constant (hb_font, HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT)), ==, 65);
+  closeFont();
+
+  cleanupFreeType();
+}
+
+static void
+test_get_glyph_italics_correction (void)
+{
+  hb_codepoint_t glyph;
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_italics_correction (hb_font, glyph), ==, 0); // MathGlyphInfo not available
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial1.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_italics_correction (hb_font, glyph), ==, 0); // MathGlyphInfo empty
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial2.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_italics_correction (hb_font, glyph), ==, 0); // MathItalicsCorrectionInfo empty
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_italics_correction (hb_font, glyph), ==, 0); // Glyph without italic correction.
+  g_assert(hb_font_get_glyph_from_name (hb_font, "A", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_italics_correction (hb_font, glyph), ==, 394);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "B", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_italics_correction (hb_font, glyph), ==, 300);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "C", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_italics_correction (hb_font, glyph), ==, 904);
+  closeFont();
+
+  cleanupFreeType();
+}
+
+static void
+test_get_glyph_top_accent_attachment (void)
+{
+  hb_codepoint_t glyph;
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 1000); // MathGlyphInfo not available
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial1.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 1000); // MathGlyphInfo empty
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial2.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 1000); // MathTopAccentAttachment empty
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 1000); // Glyph without top accent attachment.
+  g_assert(hb_font_get_glyph_from_name (hb_font, "D", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 748);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "E", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 692);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "F", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_top_accent_attachment (hb_font, glyph), ==, 636);
+  closeFont();
+
+  cleanupFreeType();
+}
+
+static void
+test_is_glyph_extended_shape (void)
+{
+  hb_codepoint_t glyph;
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert(!hb_ot_math_is_glyph_extended_shape (hb_face, glyph)); // MathGlyphInfo not available
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial1.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert(!hb_ot_math_is_glyph_extended_shape (hb_face, glyph)); // MathGlyphInfo empty
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "G", -1, &glyph));
+  g_assert(!hb_ot_math_is_glyph_extended_shape (hb_face, glyph));
+  g_assert(hb_font_get_glyph_from_name (hb_font, "H", -1, &glyph));
+  g_assert(hb_ot_math_is_glyph_extended_shape (hb_face, glyph));
+  closeFont();
+
+  cleanupFreeType();
+}
+
+static void
+test_get_glyph_kerning (void)
+{
+  hb_codepoint_t glyph;
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 0), ==, 0); // MathGlyphInfo not available
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 0), ==, 0); // MathGlyphInfo not available
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 0), ==, 0); // MathGlyphInfo not available
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_LEFT, 0), ==, 0); // MathGlyphInfo not available
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial2.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 0), ==, 0); // MathKernInfo empty
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 0), ==, 0); // MathKernInfo empty
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 0), ==, 0); // MathKernInfo empty
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_LEFT, 0), ==, 0); // MathKernInfo empty
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial3.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 0), ==, 0); // MathKernInfoRecords empty
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 0), ==, 0); // MathKernInfoRecords empty
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 0), ==, 0); // MathKernInfoRecords empty
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_LEFT, 0), ==, 0); // MathKernInfoRecords empty
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "I", -1, &glyph));
+
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 7), ==, 62); // lower than min heigth
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 14), ==, 62); // equal to min height
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 20), ==, 104);
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 23), ==, 104);
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 31), ==, 146);
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 32), ==, 146);
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 86), ==, 398); // equal to max height
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 91), ==, 440); // larger than max height
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 96), ==, 440); // larger than max height
+
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_RIGHT, 39), ==, 188); // top right
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_TOP_LEFT, 39), ==, 110); // top left
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_RIGHT, 39), ==, 44); // bottom right
+  g_assert_cmpint(hb_ot_math_get_glyph_kerning (hb_font, glyph, HB_OT_MATH_KERN_BOTTOM_LEFT, 39), ==, 100); // bottom left
+
+  closeFont();
+
+  cleanupFreeType();
+}
+
+
+static hb_position_t
+get_glyph_assembly_italics_correction (hb_font_t *font,
+				       hb_codepoint_t glyph,
+				       hb_bool_t horizontal)
+{
+  hb_position_t corr;
+  hb_ot_math_get_glyph_assembly (font, glyph,
+				 horizontal ? HB_DIRECTION_LTR : HB_DIRECTION_TTB,
+				 0, NULL, NULL,
+				 &corr);
+  return corr;
+}
+
+static void
+test_get_glyph_assembly_italics_correction (void)
+{
+  hb_codepoint_t glyph;
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0); // MathVariants not available
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0); // MathVariants not available
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial1.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0); // VertGlyphCoverage and HorizGlyphCoverage absent
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0); // VertGlyphCoverage and HorizGlyphCoverage absent
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial2.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0); // VertGlyphCoverage and HorizGlyphCoverage empty
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0); // VertGlyphCoverage and HorizGlyphCoverage empty
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial3.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0); // HorizGlyphConstruction and VertGlyphConstruction empty
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0);  // HorizGlyphConstruction and VertGlyphConstruction empty
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial4.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0);
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0);
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "arrowleft", -1, &glyph));
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 248);
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 0);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "arrowup", -1, &glyph));
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, TRUE), ==, 0);
+  g_assert_cmpint(get_glyph_assembly_italics_correction (hb_font, glyph, FALSE), ==, 662);
+  closeFont();
+
+  cleanupFreeType();
+}
+
+static void
+test_get_min_connector_overlap (void)
+{
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, FALSE), ==, 0); // MathVariants not available
+  g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, TRUE), ==, 0); // MathVariants not available
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial1.otf");
+  g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, HB_DIRECTION_LTR), ==, 108);
+  g_assert_cmpint(hb_ot_math_get_min_connector_overlap(hb_font, HB_DIRECTION_TTB), ==, 54);
+  closeFont();
+
+  cleanupFreeType();
+}
+
+static void
+test_get_glyph_variants (void)
+{
+  hb_codepoint_t glyph;
+  hb_ot_math_glyph_variant_t variants[20];
+  unsigned variantsSize = sizeof (variants) / sizeof (variants[0]);
+  unsigned int count;
+  unsigned int offset = 0;
+
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL), ==, 0);
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial1.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL), ==, 0);
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial2.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL), ==, 0);
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial3.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL), ==, 0);
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial4.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL), ==, 0);
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+
+  g_assert(hb_font_get_glyph_from_name (hb_font, "arrowleft", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font,
+                                                 glyph,
+                                                 HB_DIRECTION_BTT,
+                                                 0,
+                                                 NULL,
+                                                 NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font,
+                                                 glyph,
+                                                 HB_DIRECTION_RTL,
+                                                 0,
+                                                 NULL,
+                                                 NULL), ==, 3);
+
+  g_assert(hb_font_get_glyph_from_name (hb_font, "arrowup", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font,
+                                                 glyph,
+                                                 HB_DIRECTION_BTT,
+                                                 0,
+                                                 NULL,
+                                                 NULL), ==, 4);
+  g_assert_cmpint(hb_ot_math_get_glyph_variants (hb_font,
+                                                 glyph,
+                                                 HB_DIRECTION_RTL,
+                                                 0,
+                                                 NULL,
+                                                 NULL), ==, 0);
+
+  g_assert(hb_font_get_glyph_from_name (hb_font, "arrowleft", -1, &glyph));
+  do {
+    count = variantsSize;
+    hb_ot_math_get_glyph_variants (hb_font,
+                                   glyph,
+                                   HB_DIRECTION_RTL,
+                                   offset,
+                                   &count,
+                                   variants);
+    offset += count;
+  } while (count == variantsSize);
+  g_assert_cmpint(offset, ==, 3);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "uni2190_size2", -1, &glyph));
+  g_assert_cmpint(variants[0].glyph, ==, glyph);
+  g_assert_cmpint(variants[0].advance, ==, 4302);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "uni2190_size3", -1, &glyph));
+  g_assert_cmpint(variants[1].glyph, ==, glyph);
+  g_assert_cmpint(variants[1].advance, ==, 4802);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "uni2190_size4", -1, &glyph));
+  g_assert_cmpint(variants[2].glyph, ==, glyph);
+  g_assert_cmpint(variants[2].advance, ==, 5802);
+
+  g_assert(hb_font_get_glyph_from_name (hb_font, "arrowup", -1, &glyph));
+  offset = 0;
+  do {
+    count = variantsSize;
+    hb_ot_math_get_glyph_variants (hb_font,
+                                   glyph,
+                                   HB_DIRECTION_BTT,
+                                   offset,
+                                   &count,
+                                   variants);
+    offset += count;
+  } while (count == variantsSize);
+  g_assert_cmpint(offset, ==, 4);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "uni2191_size2", -1, &glyph));
+  g_assert_cmpint(variants[0].glyph, ==, glyph);
+  g_assert_cmpint(variants[0].advance, ==, 2251);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "uni2191_size3", -1, &glyph));
+  g_assert_cmpint(variants[1].glyph, ==, glyph);
+  g_assert_cmpint(variants[1].advance, ==, 2501);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "uni2191_size4", -1, &glyph));
+  g_assert_cmpint(variants[2].glyph, ==, glyph);
+  g_assert_cmpint(variants[2].advance, ==, 3001);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "uni2191_size5", -1, &glyph));
+  g_assert_cmpint(variants[3].glyph, ==, glyph);
+  g_assert_cmpint(variants[3].advance, ==, 3751);
+
+  closeFont();
+
+  cleanupFreeType();
+}
+
+static void
+test_get_glyph_assembly (void)
+{
+  hb_codepoint_t glyph;
+  hb_ot_math_glyph_part_t parts[20];
+  unsigned partsSize = sizeof (parts) / sizeof (parts[0]);
+  unsigned int count;
+  unsigned int offset = 0;
+
+  initFreeType();
+
+  openFont("fonts/MathTestFontEmpty.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL, NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL, NULL), ==, 0);
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial1.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL, NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL, NULL), ==, 0);
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial2.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL, NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL, NULL), ==, 0);
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial3.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL, NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL, NULL), ==, 0);
+  closeFont();
+
+  openFont("fonts/MathTestFontPartial4.otf");
+  g_assert(hb_font_get_glyph_from_name (hb_font, "space", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_RTL, 0, NULL, NULL, NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font, glyph, HB_DIRECTION_BTT, 0, NULL, NULL, NULL), ==, 0);
+  closeFont();
+
+  openFont("fonts/MathTestFontFull.otf");
+
+  g_assert(hb_font_get_glyph_from_name (hb_font, "arrowright", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font,
+                                                 glyph,
+                                                 HB_DIRECTION_BTT,
+                                                 0,
+                                                 NULL,
+                                                 NULL,
+                                                 NULL), ==, 0);
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font,
+                                                 glyph,
+                                                 HB_DIRECTION_RTL,
+                                                 0,
+                                                 NULL,
+                                                 NULL,
+                                                 NULL), ==, 3);
+
+  g_assert(hb_font_get_glyph_from_name (hb_font, "arrowdown", -1, &glyph));
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font,
+                                                 glyph,
+                                                 HB_DIRECTION_BTT,
+                                                 0,
+                                                 NULL,
+                                                 NULL,
+                                                 NULL), ==, 5);
+  g_assert_cmpint(hb_ot_math_get_glyph_assembly (hb_font,
+                                                 glyph,
+                                                 HB_DIRECTION_RTL,
+                                                 0,
+                                                 NULL,
+                                                 NULL,
+                                                 NULL), ==, 0);
+
+  g_assert(hb_font_get_glyph_from_name (hb_font, "arrowright", -1, &glyph));
+  do {
+    count = partsSize;
+    hb_ot_math_get_glyph_assembly (hb_font,
+                                   glyph,
+                                   HB_DIRECTION_RTL,
+                                   offset,
+                                   &count,
+                                   parts,
+                                   NULL);
+    offset += count;
+  } while (count == partsSize);
+  g_assert_cmpint(offset, ==, 3);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "left", -1, &glyph));
+  g_assert_cmpint(parts[0].glyph, ==, glyph);
+  g_assert_cmpint(parts[0].start_connector_length, ==, 800);
+  g_assert_cmpint(parts[0].end_connector_length, ==, 384);
+  g_assert_cmpint(parts[0].full_advance, ==, 2000);
+  g_assert(!(parts[0].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER));
+  g_assert(hb_font_get_glyph_from_name (hb_font, "horizontal", -1, &glyph));
+  g_assert_cmpint(parts[1].glyph, ==, glyph);
+  g_assert_cmpint(parts[1].start_connector_length, ==, 524);
+  g_assert_cmpint(parts[1].end_connector_length, ==, 800);
+  g_assert_cmpint(parts[1].full_advance, ==, 2000);
+  g_assert(parts[1].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "right", -1, &glyph));
+  g_assert_cmpint(parts[2].glyph, ==, glyph);
+  g_assert_cmpint(parts[2].start_connector_length, ==, 316);
+  g_assert_cmpint(parts[2].end_connector_length, ==, 454);
+  g_assert_cmpint(parts[2].full_advance, ==, 2000);
+  g_assert(!(parts[2].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER));
+
+  g_assert(hb_font_get_glyph_from_name (hb_font, "arrowdown", -1, &glyph));
+  offset = 0;
+  do {
+    count = partsSize;
+    hb_ot_math_get_glyph_assembly (hb_font,
+                                   glyph,
+                                   HB_DIRECTION_BTT,
+                                   offset,
+                                   &count,
+                                   parts,
+                                   NULL);
+    offset += count;
+  } while (count == partsSize);
+  g_assert_cmpint(offset, ==, 5);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "bottom", -1, &glyph));
+  g_assert_cmpint(parts[0].glyph, ==, glyph);
+  g_assert_cmpint(parts[0].start_connector_length, ==, 365);
+  g_assert_cmpint(parts[0].end_connector_length, ==, 158);
+  g_assert_cmpint(parts[0].full_advance, ==, 1000);
+  g_assert(!(parts[0].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER));
+  g_assert(hb_font_get_glyph_from_name (hb_font, "vertical", -1, &glyph));
+  g_assert_cmpint(parts[1].glyph, ==, glyph);
+  g_assert_cmpint(parts[1].glyph, ==, glyph);
+  g_assert_cmpint(parts[1].start_connector_length, ==, 227);
+  g_assert_cmpint(parts[1].end_connector_length, ==, 365);
+  g_assert_cmpint(parts[1].full_advance, ==, 1000);
+  g_assert(parts[1].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "center", -1, &glyph));
+  g_assert_cmpint(parts[2].glyph, ==, glyph);
+  g_assert_cmpint(parts[2].start_connector_length, ==, 54);
+  g_assert_cmpint(parts[2].end_connector_length, ==, 158);
+  g_assert_cmpint(parts[2].full_advance, ==, 1000);
+  g_assert(!(parts[2].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER));
+  g_assert(hb_font_get_glyph_from_name (hb_font, "vertical", -1, &glyph));
+  g_assert_cmpint(parts[3].glyph, ==, glyph);
+  g_assert_cmpint(parts[3].glyph, ==, glyph);
+  g_assert_cmpint(parts[3].glyph, ==, glyph);
+  g_assert_cmpint(parts[3].start_connector_length, ==, 400);
+  g_assert_cmpint(parts[3].end_connector_length, ==, 296);
+  g_assert_cmpint(parts[3].full_advance, ==, 1000);
+  g_assert(parts[1].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER);
+  g_assert(hb_font_get_glyph_from_name (hb_font, "top", -1, &glyph));
+  g_assert_cmpint(parts[4].glyph, ==, glyph);
+  g_assert_cmpint(parts[4].start_connector_length, ==, 123);
+  g_assert_cmpint(parts[4].end_connector_length, ==, 192);
+  g_assert_cmpint(parts[4].full_advance, ==, 1000);
+  g_assert(!(parts[4].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER));
+
+  closeFont();
+
+  cleanupFreeType();
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_has_data);
+  hb_test_add (test_get_constant);
+  hb_test_add (test_get_glyph_italics_correction);
+  hb_test_add (test_get_glyph_top_accent_attachment);
+  hb_test_add (test_is_glyph_extended_shape);
+  hb_test_add (test_get_glyph_kerning);
+  hb_test_add (test_get_glyph_assembly_italics_correction);
+  hb_test_add (test_get_min_connector_overlap);
+  hb_test_add (test_get_glyph_variants);
+  hb_test_add (test_get_glyph_assembly);
+
+  return hb_test_run();
+}
diff --git a/test/api/test-ot-tag.c b/test/api/test-ot-tag.c
index e54e552..f5cbd9d 100644
--- a/test/api/test-ot-tag.c
+++ b/test/api/test-ot-tag.c
@@ -188,11 +188,48 @@
   test_language_two_way ("ENG", "en");
   test_tag_from_language ("ENG", "en_US");
 
+  test_language_two_way ("CJA", "cja"); /* Western Cham */
+  test_language_two_way ("CJM", "cjm"); /* Eastern Cham */
   test_language_two_way ("EVN", "eve");
 
+  test_language_two_way ("HAL", "cfm"); /* BCP47 and current ISO639-3 code for Halam/Falam Chin */
+  test_tag_from_language ("HAL", "flm"); /* Retired ISO639-3 code for Halam/Falam Chin */
+
+  test_tag_from_language ("QIN", "bgr"); /* Bawm Chin */
+  test_tag_from_language ("QIN", "cbl"); /* Bualkhaw Chin */
+  test_tag_from_language ("QIN", "cka"); /* Khumi Awa Chin */
+  test_tag_from_language ("QIN", "cmr"); /* Mro-Khimi Chin */
+  test_tag_from_language ("QIN", "cnb"); /* Chinbon Chin */
+  test_tag_from_language ("QIN", "cnh"); /* Hakha Chin */
+  test_tag_from_language ("QIN", "cnk"); /* Khumi Chin */
+  test_tag_from_language ("QIN", "cnw"); /* Ngawn Chin */
+  test_tag_from_language ("QIN", "csh"); /* Asho Chin */
+  test_tag_from_language ("QIN", "csy"); /* Siyin Chin */
+  test_tag_from_language ("QIN", "ctd"); /* Tedim Chin */
+  test_tag_from_language ("QIN", "czt"); /* Zotung Chin */
+  test_tag_from_language ("QIN", "dao"); /* Daai Chin */
+  test_tag_from_language ("QIN", "hlt"); /* Matu Chin */
+  test_tag_from_language ("QIN", "mrh"); /* Mara Chin */
+  test_tag_from_language ("QIN", "pck"); /* Paite Chin */
+  test_tag_from_language ("QIN", "sez"); /* Senthang Chin */
+  test_tag_from_language ("QIN", "tcp"); /* Tawr Chin */
+  test_tag_from_language ("QIN", "tcz"); /* Thado Chin */
+  test_tag_from_language ("QIN", "yos"); /* Yos, deprecated by IANA in favor of Zou [zom] */
+  test_tag_from_language ("QIN", "zom"); /* Zou */
+  test_tag_to_language ("QIN", "bgr");   /* no single BCP47 tag for Chin; picking Bawm Chin */
+
   test_language_two_way ("FAR", "fa");
   test_tag_from_language ("FAR", "fa_IR");
 
+  test_language_two_way ("SWA", "aii"); /* Swadaya Aramaic */
+
+  test_language_two_way ("SYR", "syr"); /* Syriac [macrolanguage] */
+  test_tag_from_language ("SYR", "amw"); /* Western Neo-Aramaic */
+  test_tag_from_language ("SYR", "cld"); /* Chaldean Neo-Aramaic */
+  test_tag_from_language ("SYR", "syc"); /* Classical Syriac */
+
+  test_language_two_way ("TUA", "tru"); /* Turoyo Aramaic */
+
   test_language_two_way ("ZHH", "zh-hk"); /* Chinese (Hong Kong) */
 
   test_tag_from_language ("ZHS", "zh"); /* Chinese */
@@ -238,6 +275,27 @@
   test_tag_from_language ("APPH", "und-fonnapa");
   test_tag_to_language ("APPH", "und-fonnapa");
 
+  /* Estrangela Syriac */
+  test_tag_from_language ("SYRE", "aii-Syre");
+  test_tag_from_language ("SYRE", "de-Syre");
+  test_tag_from_language ("SYRE", "syr-Syre");
+  test_tag_from_language ("SYRE", "und-Syre");
+  test_tag_to_language ("SYRE", "und-Syre");
+
+  /* Western Syriac */
+  test_tag_from_language ("SYRJ", "aii-Syrj");
+  test_tag_from_language ("SYRJ", "de-Syrj");
+  test_tag_from_language ("SYRJ", "syr-Syrj");
+  test_tag_from_language ("SYRJ", "und-Syrj");
+  test_tag_to_language ("SYRJ", "und-Syrj");
+
+  /* Eastern Syriac */
+  test_tag_from_language ("SYRN", "aii-Syrn");
+  test_tag_from_language ("SYRN", "de-Syrn");
+  test_tag_from_language ("SYRN", "syr-Syrn");
+  test_tag_from_language ("SYRN", "und-Syrn");
+  test_tag_to_language ("SYRN", "und-Syrn");
+
   /* Test that x-hbot overrides the base language */
   test_tag_from_language ("ABC", "fa-x-hbotabc-zxc");
   test_tag_from_language ("ABC", "fa-ir-x-hbotabc-zxc");
diff --git a/test/fuzzing/Makefile.am b/test/fuzzing/Makefile.am
index 7b0eb94..3ea8605 100644
--- a/test/fuzzing/Makefile.am
+++ b/test/fuzzing/Makefile.am
@@ -30,14 +30,15 @@
 	$(NULL)
 
 hb_fuzzer_SOURCES = \
+	hb-fuzzer.hh \
 	hb-fuzzer.cc \
+	main.cc \
 	$(NULL)
 hb_fuzzer_LDADD = \
 	$(LDADD) \
 	$(NULL)
 hb_fuzzer_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
-	-DMAIN \
 	$(NULL)
 hb_fuzzer_DEPENDENCIES = \
 	lib \
diff --git a/test/fuzzing/hb-fuzzer.cc b/test/fuzzing/hb-fuzzer.cc
index b319a71..79f3222 100644
--- a/test/fuzzing/hb-fuzzer.cc
+++ b/test/fuzzing/hb-fuzzer.cc
@@ -1,10 +1,10 @@
-#include <stddef.h>
-#include <hb.h>
+#include "hb-fuzzer.hh"
+
 #include <hb-ot.h>
 #include <string.h>
 
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
-
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
   hb_blob_t *blob = hb_blob_create((const char *)data, size,
                                    HB_MEMORY_MODE_READONLY, NULL, NULL);
   hb_face_t *face = hb_face_create(blob, 0);
@@ -28,6 +28,19 @@
     hb_buffer_add_utf32(buffer, text32, sizeof(text32)/sizeof(text32[0]), 0, -1);
     hb_buffer_guess_segment_properties(buffer);
     hb_shape(font, buffer, NULL, 0);
+
+    unsigned int len = hb_buffer_get_length (buffer);
+    hb_glyph_info_t *infos = hb_buffer_get_glyph_infos (buffer, NULL);
+    //hb_glyph_position_t *positions = hb_buffer_get_glyph_positions (buffer, NULL);
+    for (unsigned int i = 0; i < len; i++)
+    {
+      hb_glyph_info_t info = infos[i];
+      //hb_glyph_position_t pos = positions[i];
+
+      hb_glyph_extents_t extents;
+      hb_font_get_glyph_extents (font, info.codepoint, &extents);
+    }
+
     hb_buffer_destroy(buffer);
   }
 
@@ -37,25 +50,3 @@
   hb_blob_destroy(blob);
   return 0;
 }
-
-#ifdef MAIN
-#include <iostream>
-#include <iterator>
-#include <fstream>
-#include <assert.h>
-
-std::string FileToString(const std::string &Path) {
-  /* TODO This silently passes if file does not exist.  Fix it! */
-  std::ifstream T(Path.c_str());
-  return std::string((std::istreambuf_iterator<char>(T)),
-                     std::istreambuf_iterator<char>());
-}
-
-int main(int argc, char **argv) {
-  for (int i = 1; i < argc; i++) {
-    std::string s = FileToString(argv[i]);
-    std::cout << argv[i] << std::endl;
-    LLVMFuzzerTestOneInput((const unsigned char*)s.data(), s.size());
-  }
-}
-#endif
diff --git a/test/fuzzing/hb-fuzzer.hh b/test/fuzzing/hb-fuzzer.hh
new file mode 100644
index 0000000..d0c617e
--- /dev/null
+++ b/test/fuzzing/hb-fuzzer.hh
@@ -0,0 +1,4 @@
+#include <hb.h>
+#include <stddef.h>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
diff --git a/test/fuzzing/main.cc b/test/fuzzing/main.cc
new file mode 100644
index 0000000..4692f7b
--- /dev/null
+++ b/test/fuzzing/main.cc
@@ -0,0 +1,21 @@
+#include "hb-fuzzer.hh"
+
+#include <iostream>
+#include <iterator>
+#include <fstream>
+#include <assert.h>
+
+std::string FileToString(const std::string &Path) {
+  /* TODO This silently passes if file does not exist.  Fix it! */
+  std::ifstream T(Path.c_str());
+  return std::string((std::istreambuf_iterator<char>(T)),
+                     std::istreambuf_iterator<char>());
+}
+
+int main(int argc, char **argv) {
+  for (int i = 1; i < argc; i++) {
+    std::string s = FileToString(argv[i]);
+    std::cout << argv[i] << std::endl;
+    LLVMFuzzerTestOneInput((const unsigned char*)s.data(), s.size());
+  }
+}
diff --git a/test/shaping/Makefile.am b/test/shaping/Makefile.am
index c5efe62..ea0b28a 100644
--- a/test/shaping/Makefile.am
+++ b/test/shaping/Makefile.am
@@ -43,7 +43,9 @@
 TESTS = \
 	tests/arabic-fallback-shaping.tests \
 	tests/arabic-feature-order.tests \
+	tests/automatic-fractions.tests \
 	tests/cluster.tests \
+	tests/color-fonts.tests \
 	tests/context-matching.tests \
 	tests/cursive-positioning.tests \
 	tests/default-ignorables.tests \
@@ -61,6 +63,7 @@
 	tests/spaces.tests \
 	tests/simple.tests \
 	tests/use.tests \
+	tests/use-marchen.tests \
 	tests/vertical.tests \
 	tests/zero-width-marks.tests \
 	$(NULL)
diff --git a/test/shaping/fonts/sha1sum/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf b/test/shaping/fonts/sha1sum/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf
new file mode 100644
index 0000000..4b80f80
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/217a934cfe15c548b572c203dceb2befdf026462.ttf b/test/shaping/fonts/sha1sum/217a934cfe15c548b572c203dceb2befdf026462.ttf
new file mode 100644
index 0000000..12b91a0
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/217a934cfe15c548b572c203dceb2befdf026462.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf b/test/shaping/fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf
new file mode 100644
index 0000000..18881fe
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf b/test/shaping/fonts/sha1sum/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf
new file mode 100644
index 0000000..fa2d0e1
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf b/test/shaping/fonts/sha1sum/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf
new file mode 100644
index 0000000..ed2fab9
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf
Binary files differ
diff --git a/test/shaping/tests/automatic-fractions.tests b/test/shaping/tests/automatic-fractions.tests
new file mode 100644
index 0000000..f9510e2
--- /dev/null
+++ b/test/shaping/tests/automatic-fractions.tests
@@ -0,0 +1,3 @@
+fonts/sha1sum/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf::U+0031,U+0032,U+0033,U+2044,U+0034,U+0035,U+0036:[one.numr=0+600|two.numr=1+600|three.numr=2+600|fraction=3+252|four.small=4+600|five.small=5+600|six.small=6+600]
+fonts/sha1sum/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf:--direction=l --script=arab:U+0031,U+0032,U+0033,U+2044,U+0034,U+0035,U+0036:[one.numr=0+600|two.numr=1+600|three.numr=2+600|fraction=3+252|four.small=4+600|five.small=5+600|six.small=6+600]
+fonts/sha1sum/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf:--direction=l:U+0661,U+0662,U+0663,U+2044,U+0664,U+0665,U+0666:[uni0661.numr=0+600|uni0662.numr=1+600|uni0663.numr=2+600|fraction=3+252|uni0664.small=4+600|uni0665.small=5+600|uni0666.small=6+600]
diff --git a/test/shaping/tests/color-fonts.tests b/test/shaping/tests/color-fonts.tests
new file mode 100644
index 0000000..397796a
--- /dev/null
+++ b/test/shaping/tests/color-fonts.tests
@@ -0,0 +1 @@
+fonts/sha1sum/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf:--font-funcs=ot --show-extents:U+1F42F:[gid1=0+2963<0,2178,2963,-2788>]
diff --git a/test/shaping/tests/fuzzed.tests b/test/shaping/tests/fuzzed.tests
index 7a5d395..771ac2b 100644
--- a/test/shaping/tests/fuzzed.tests
+++ b/test/shaping/tests/fuzzed.tests
@@ -9,3 +9,4 @@
 fonts/sha1sum/3511ff5c1647150595846ac414c595cccac34f18.ttf:--font-funcs=ot:U+0041:[gid0=0+1000|gid512=0+1000|gid15104=0+1000|gid11004=0+1000|gid3408=0+1000|gid18244=0+1000|gid17872=0+1000|gid17961=0+1000|gid0=0+1000|gid992=0+1000|gid15616=0+1000|gid0=0+1000|gid14151=0+1000|gid20559=0+1000|gid20992=0+1000|gid5440=0+1000|gid256=0+1000|gid0=0+1000|gid10=0+1000|gid8960=0+1000|gid256=0+1000|gid1024=0+1000|gid1490=0+1000|gid0=0+1000|gid768=0+1000|gid4096=0+1000|gid256=0+1000|gid2216=0+1000|gid0=0+1000|gid256=0+1000|gid256=0+1000|gid0=0+1000|gid768=0+1000|gid10752=0+1000|gid11004=0+1000|gid3408=0+1000|gid18244=0+1000|gid17734=0+1000|gid53248=0+1000|gid256=0+1000|gid0=0+1000|gid512=0+1000|gid14848=0+1000|gid10793=0+1000|gid57344=0+1000|gid768=0+1000|gid18227=0+1000|gid20285=0+1000|gid20480=0+1000|gid0=0+1000|gid256=0+1000|gid0=0+1000|gid810=0+1000|gid0=0+1000|gid11004=0+1000|gid3408=0+1000|gid18244=0+1000|gid17734=0+1000|gid53289=0+1000|gid57344=0+1000|gid768=0+1000|gid15667=0+1000|gid71=0+1000|gid0=0+1000|gid20559=0+1000|gid21248=0+1000|gid256=0+1000|gid0=0+1000|gid2816=0+1000|gid2776=0+1000|gid0=0+1000|gid51516=0+1000|gid0=0+1000|gid32=0+1000|gid26209=0+1000|gid28005=0+1000|gid65249=0+1000|gid29690=0+1000|gid0=0+1000|gid51548=0+1000|gid0=0+1000|gid2454=0+1000|gid28783=0+1000|gid29556=0+1000|gid1291=0+1000|gid3458=0+1000|gid80=0+1000|gid0=0+1000|gid2804=0+1000|gid210=0+1000|gid28786=0+1000|gid25968=0+1000|gid45763=0+1000|gid50546=0+1000|gid0=0+1000|gid59136=0+1000|gid0=0+1000|gid38144=0+1000|gid256=0+1000|gid0=0+1000|gid2560=0+1000|gid30208=0+1000|gid52224=0+1000|gid580=0+1000|gid17996=0+1000|gid21504=0+1000|gid6734=0+1000|gid108=0+1000|gid116=0+1000|gid24846=0+1000|gid1024=0+1000|gid0=0+1000|gid255=0+1000|gid65280=0+1000|gid256=0+1000|gid0=0+1000|gid8704=0+1000|gid1345=0+1000|gid23109=0+1000|gid8192=0+1000|gid10823=0+1000|gid21076=0+1000|gid8192=0+1000|gid12877=0+1000|gid20300=0+1000|gid8192=0+1000|gid6738=0+1000|gid20301=0+1000|gid8192=0+1000|gid16980=0+1000|gid21067=0+1000|gid8251=0+1000|gid18944=0+1000|gid255=0+1000|gid65280=0+1000|gid15360=0+1000|gid256=0+1000|gid255=0+1000|gid65280=0+1000|gid256=0+1000|gid768=0+1000|gid255=0+1000|gid65280=0+1000|gid256=0+1000|gid768=0+1000|gid255=0+1000|gid65280=0+1000|gid256=0+1000|gid1024=0+1000|gid12=0+1000|gid65280=0+1000|gid256=0+1000|gid1280=0+1000|gid255=0+1000|gid65280=0+1000|gid256=0+1000|gid1536=0+1000|gid1899=0+1000|gid25970=0+1000|gid110=0+1000|gid11264=0+1000|gid27502=0+1000|gid29285=0+1000|gid12907=0+1000|gid25974=0+1000|gid28160=0+1000|gid14443=0+1000|gid25970=0+1000|gid28288=0+1000|gid3=0+1000|gid118=0+1000|gid18259=0+1000|gid21826=0+1000|gid45716=0+1000|gid46369=0+1000|gid0=0+1000|gid0=0+1000|gid1=0+1000|gid16=0+1000|gid17=0+1000|gid256=0+1000|gid4=0+1000|gid16=0+1000|gid18244=0+1000|gid17734=0+1000|gid28=0+1000|gid12=0+1000|gid0=0+1000|gid284=0+1000|gid0=0+1000|gid28=0+1000|gid18256=0+1000|gid20307=0+1000|gid45114=0+1000|gid47616=0+1000|gid226=0+1000|gid10296=0+1000|gid0=0+1000|gid57927=0+1000|gid1=0+1000|gid0=0+1000|gid0=0+1000|gid21248=0+1000|gid5440=0+1000|gid256=0+1000|gid0=0+1000|gid10=0+1000|gid768=0+1000|gid256=0+1000|gid1024=0+1000|gid512=0+1000|gid0=0+1000|gid297=0+1000|gid16=0+1000|gid24833=0+1000|gid28774=0+1000|gid10794=0+1000|gid2304=0+1000|gid29=0+1000|gid32=0+1000|gid42=0+1000|gid64515=0+1000|gid42=0+1000|gid42=0+1000|gid64525=0+1000|gid20551=0+1000|gid17477=0+1000|gid18128=0+1000|gid10720=0+1000|gid3=0+1000|gid61=0+1000|gid3408=0+1000|gid18244=0+1000|gid17734=0+1000|gid53289=0+1000|gid57344=0+1000|gid768=0+1000|gid15616=0+1000|gid512=0+1000|gid55=0+1000|gid10576=0+1000|gid20307=0+1000|gid0=0+1000|gid255=0+1000|gid56063=0+1000|gid53504=0+1000|gid42=0+1000|gid42=0+1000|gid64525=0+1000|gid12288=0+1000|gid18176=0+1000|gid80=0+1000|gid20307=0+1000|gid1=0+1000|gid0=0+1000|gid62=0+1000]
 fonts/sha1sum/fab39d60d758cb586db5a504f218442cd1395725.ttf:--font-funcs=ot:U+0041,U+0041:[gid0=0+1000|gid0=1+1000]
 fonts/sha1sum/205edd09bd3d141cc9580f650109556cc28b22cb.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
+fonts/sha1sum/217a934cfe15c548b572c203dceb2befdf026462.ttf:--font-funcs=ot:U+0061,U+0061,U+0061:[]
diff --git a/test/shaping/tests/use-marchen.tests b/test/shaping/tests/use-marchen.tests
new file mode 100644
index 0000000..6497178
--- /dev/null
+++ b/test/shaping/tests/use-marchen.tests
@@ -0,0 +1,35 @@
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8F:[u11C8F=0+3000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C71:[u11C71=0+1600]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11CB5:[u11C8A=0+2000|u11CB5=0@-2000,0+0]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C84,U+11C71:[u11C84=0+2200|u11C71=1+1600]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C7E,U+11C8A:[u11C7E=0+2600|u11C8A=1+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11C92,U+11CA9:[u11C8A.11C92.11CA9=0+2600]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11C94,U+11CA9:[u11C8A.11C94.11CA9=0+2600]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C92,U+11CA9:[u11C8D.11C92.11CA9=0+2600]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C94,U+11CA9:[u11C8D.11C94.11CA9=0+2600]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C9E,U+11CA9:[u11C8D.11C9E.11CA9=0+3200]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA0,U+11CA9:[u11C8D.11CA0.11CA9=0+3000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C92,U+11CAA:[u11C8D.11C92.11CAA=0+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C94,U+11CAA:[u11C8D.11C94.11CAA=0+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C9D,U+11CAA:[u11C8D.11C9D.11CAA=0+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C9E,U+11CAA:[u11C8D.11C9E.11CAA=0+2600]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA0,U+11CAA:[u11C8D.11CA0.11CAA=0+2400]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C72,U+11CAA:[u11C80=0+2400|u11C72.11CAA=1+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8C,U+11CB1,U+11C8D:[u11C8C.11CB1=0+2793|u11C8D=2+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C7C,U+11CB3:[u11C80=0+2400|u11C7C.11CB3=1+2200]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C7F,U+11CB2,U+11C7D:[u11C7F.11CB2=0+2400|u11C7D=2+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CB2,U+11C81:[u11C8D.11CB2=0+2000|u11C81=2+2400]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8C,U+11CB4,U+11C74:[u11C8C.11CB4=0+2800|u11C74=2+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11CA1,U+11CA9,U+11C71:[u11C8A.11CA1.11CA9=0+3000|u11C71=3+1600]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA1,U+11CA9,U+11C71:[u11C8D.11CA1.11CA9=0+3000|u11C71=3+1600]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA1,U+11CAA,U+11C71:[u11C8D.11CA1.11CAA=0+2400|u11C71=3+1600]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8F,U+11CB0,U+11CB4,U+11CB6:[u11C8F.11CB0.11CB4=0+3600|u11CB6=0@-3200,0+0]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8E,U+11CB0,U+11CB2,U+11CB5:[u11C8E.11CB0.11CB2=0+2000|u11CB5=0@-2000,0+0]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C74,U+11C89,U+11CB2,U+11C75:[u11C74=0+2000|u11C89.11CB2=1+2000|u11C75=3+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C7C,U+11CAA,U+11CB2,U+11C75:[u11C7C.11CAA.11CB2=0+2200|u11C75=3+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C81,U+11C74,U+11CB2,U+11C8B:[u11C81=0+2400|u11C74.11CB2=1+2000|u11C8B=3+2400]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8B,U+11CB3,U+11C74,U+11C8D:[u11C8B.11CB3=0+2400|u11C74=2+2000|u11C8D=3+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C83,U+11CB4,U+11C74,U+11C8D:[u11C83.11CB4=0+2800|u11C74=2+2000|u11C8D=3+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8B,U+11CB3,U+11C74,U+11C8D,U+11C71:[u11C8B.11CB3=0+2400|u11C74=2+2000|u11C8D=3+2000|u11C71=4+1600]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C76,U+11CB1,U+11C75,U+11C8D:[u11C80=0+2400|u11C76.11CB1=1+3200|u11C75=3+2000|u11C8D=4+2000]
+fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C8D,U+11C94,U+11CAA,U+11CB1,U+11C74,U+11C8D:[u11C80=0+2400|u11C8D.11C94.11CAA.11CB1.shorti=1+2600|u11C74=5+2000|u11C8D=6+2000]
diff --git a/test/shaping/tests/vertical.tests b/test/shaping/tests/vertical.tests
index 8276890..3586080 100644
--- a/test/shaping/tests/vertical.tests
+++ b/test/shaping/tests/vertical.tests
@@ -1 +1,3 @@
 fonts/sha1sum/191826b9643e3f124d865d617ae609db6a2ce203.ttf:--direction=t:U+300C:[uni300C.vert=0@-512,-578+0,-1024]
+fonts/sha1sum/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf:--direction=t --font-funcs=ft:U+0041,U+0042:[gid1=0@-654,-2128+0,-2789|gid2=1@-665,-2125+0,-2789]
+fonts/sha1sum/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf:--direction=t --font-funcs=ot:U+0041,U+0042:[gid1=0@-654,-2189+0,-2789|gid2=1@-665,-2189+0,-2789]
diff --git a/util/helper-cairo.cc b/util/helper-cairo.cc
index 8f30eea..2e2952b 100644
--- a/util/helper-cairo.cc
+++ b/util/helper-cairo.cc
@@ -28,6 +28,7 @@
 
 #include <cairo-ft.h>
 #include <hb-ft.h>
+#include FT_MULTIPLE_MASTERS_H
 
 #include "helper-cairo-ansi.hh"
 #ifdef CAIRO_HAS_SVG_SURFACE
@@ -76,7 +77,8 @@
 
   cairo_font_face_t *cairo_face;
   /* We cannot use the FT_Face from hb_font_t, as doing so will confuse hb_font_t because
-   * cairo will reset the face size.  As such, create new face... */
+   * cairo will reset the face size.  As such, create new face...
+   * TODO Perhaps add API to hb-ft to encapsulate this code. */
   FT_Face ft_face = NULL;//hb_ft_font_get_face (font);
   if (!ft_face)
   {
@@ -100,7 +102,23 @@
 					     CAIRO_FONT_WEIGHT_NORMAL);
   }
   else
+  {
+    unsigned int num_coords;
+    const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
+    if (num_coords)
+    {
+      FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed));
+      if (ft_coords)
+      {
+	for (unsigned int i = 0; i < num_coords; i++)
+	  ft_coords[i] = coords[i] << 2;
+	FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords);
+	free (ft_coords);
+      }
+    }
+
     cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
+  }
   cairo_matrix_t ctm, font_matrix;
   cairo_font_options_t *font_options;
 
diff --git a/util/options.cc b/util/options.cc
index bc699c1..0f2e207 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -254,6 +254,47 @@
   return true;
 }
 
+static gboolean
+parse_variations (const char *name G_GNUC_UNUSED,
+	        const char *arg,
+	        gpointer    data,
+	        GError    **error G_GNUC_UNUSED)
+{
+  font_options_t *font_opts = (font_options_t *) data;
+  char *s = (char *) arg;
+  char *p;
+
+  font_opts->num_variations = 0;
+  g_free (font_opts->variations);
+  font_opts->variations = NULL;
+
+  if (!*s)
+    return true;
+
+  /* count the variations first, so we can allocate memory */
+  p = s;
+  do {
+    font_opts->num_variations++;
+    p = strchr (p, ',');
+    if (p)
+      p++;
+  } while (p);
+
+  font_opts->variations = (hb_variation_t *) calloc (font_opts->num_variations, sizeof (*font_opts->variations));
+
+  /* now do the actual parsing */
+  p = s;
+  font_opts->num_variations = 0;
+  while (p && *p) {
+    char *end = strchr (p, ',');
+    if (hb_variation_from_string (p, end ? end - p : -1, &font_opts->variations[font_opts->num_variations]))
+      font_opts->num_variations++;
+    p = end ? end + 1 : NULL;
+  }
+
+  return true;
+}
+
 
 void
 view_options_t::add_options (option_parser_t *parser)
@@ -270,7 +311,7 @@
   parser->add_group (entries,
 		     "view",
 		     "View options:",
-		     "Options controlling output rendering",
+		     "Options for output rendering",
 		     this);
 }
 
@@ -299,7 +340,7 @@
   parser->add_group (entries,
 		     "shape",
 		     "Shape options:",
-		     "Options controlling the shaping process",
+		     "Options for the shaping process",
 		     this);
 
   const gchar *features_help = "Comma-separated list of font features\n"
@@ -346,7 +387,7 @@
   parser->add_group (entries2,
 		     "features",
 		     "Features options:",
-		     "Options controlling font features used",
+		     "Options for font features used",
 		     this);
 }
 
@@ -413,7 +454,30 @@
   parser->add_group (entries,
 		     "font",
 		     "Font options:",
-		     "Options controlling the font",
+		     "Options for the font",
+		     this);
+
+  const gchar *variations_help = "Comma-separated list of font variations\n"
+    "\n"
+    "    Variations are set globally. The format for specifying variation settings\n"
+    "    follows.  All valid CSS font-variation-settings values other than 'normal'\n"
+    "    and 'inherited' are also accepted, though, not documented below.\n"
+    "\n"
+    "    The format is a tag, optionally followed by an equals sign, followed by a\n"
+    "    number. For example:\n"
+    "\n"
+    "      \"wght=500\"\n"
+    "      \"slnt=-7.5\"\n";
+
+  GOptionEntry entries2[] =
+  {
+    {"variations",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_variations,	variations_help,	"list"},
+    {NULL}
+  };
+  parser->add_group (entries2,
+		     "variations",
+		     "Varitions options:",
+		     "Options for font variations used",
 		     this);
 }
 
@@ -431,7 +495,7 @@
   parser->add_group (entries,
 		     "text",
 		     "Text options:",
-		     "Options controlling the input text",
+		     "Options for the input text",
 		     this);
 }
 
@@ -459,7 +523,7 @@
   parser->add_group (entries,
 		     "output",
 		     "Output destination & format options:",
-		     "Options controlling the destination and form of the output",
+		     "Options for the destination & form of the output",
 		     this);
 }
 
@@ -561,6 +625,8 @@
   hb_font_set_scale (font, scale_x, scale_y);
   hb_face_destroy (face);
 
+  hb_font_set_variations (font, variations, num_variations);
+
   void (*set_font_funcs) (hb_font_t *) = NULL;
   if (!font_funcs)
   {
@@ -719,7 +785,7 @@
          "    text: [<glyph name or index>=<glyph cluster index within input>@<horizontal displacement>,<vertical displacement>+<horizontal advance>,<vertical advance>|...]\n"
          "    json: [{\"g\": <glyph name or index>, \"ax\": <horizontal advance>, \"ay\": <vertical advance>, \"dx\": <horizontal displacement>, \"dy\": <vertical displacement>, \"cl\": <glyph cluster index within input>}, ...]\n"
          "\nOutput syntax options:",
-		     "Options controlling the syntax of the output",
+		     "Options for the syntax of the output",
 		     this);
 }
 
diff --git a/util/options.hh b/util/options.hh
index 919e4f8..9ed4fd0 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -285,7 +285,10 @@
 {
   font_options_t (option_parser_t *parser,
 		  int default_font_size_,
-		  unsigned int subpixel_bits_) {
+		  unsigned int subpixel_bits_)
+  {
+    variations = NULL;
+    num_variations = 0;
     default_font_size = default_font_size_;
     subpixel_bits = subpixel_bits_;
     font_file = NULL;
@@ -299,6 +302,7 @@
   }
   ~font_options_t (void) {
     g_free (font_file);
+    free (variations);
     g_free (font_funcs);
     hb_font_destroy (font);
   }
@@ -309,6 +313,8 @@
 
   char *font_file;
   int face_index;
+  hb_variation_t *variations;
+  unsigned int num_variations;
   int default_font_size;
   unsigned int subpixel_bits;
   mutable double font_size_x;
diff --git a/win32/README.txt b/win32/README.txt
index e2ead01..af0dc15 100644
--- a/win32/README.txt
+++ b/win32/README.txt
@@ -7,11 +7,10 @@
 The following are instructions for performing such a build, as there is a

 number of build configurations supported for the build.  Note that for all

 build configurations, the OpenType and Simple TrueType layout (fallback)

-backends are enabled, as well as the Uniscribe platform shaper, and this

-is the base configuration that is built if no options (see below) are

-specified.  A 'clean' target is provided-it is recommended that one cleans

-the build and redo the build if any configuration option changed.  An

-'install' target is also provided to copy the built items in their appropriate

+backends are enabled, and this is the base configuration that is built if no

+options (see below) are specified.  A 'clean' target is provided-it is recommended

+that one cleans the build and redo the build if any configuration option changed.

+An 'install' target is also provided to copy the built items in their appropriate

 locations under $(PREFIX), which is described below.

 

 Invoke the build by issuing the command:

@@ -64,11 +63,11 @@
 

 GRAPHITE2: Enable the Graphite2 shaper, requires the SIL Graphite2 library.

 

-ICU: Enables the build HarfBuzz-ICU, which is now the recommended layout engine

-     for ICU (International Components for Unicode), which deprecated ICU LE.

-     Requires the ICU libraries.

+ICU: Enables the build of ICU Unicode functions. Requires the ICU libraries.
 

-DIRECTWRITE: Enable (experimental) DirectWrite platform shaper support,

+UNISCRIBE: Enable Uniscribe platform shaper support.

+

+DIRECTWRITE: Enable DirectWrite platform shaper support,

              requires a rather recent Windows SDK, and at least Windows Vista/

              Server 2008 with SP2 and platform update.

 

diff --git a/win32/build-rules-msvc.mak b/win32/build-rules-msvc.mak
index 03b3833..bfe0286 100644
--- a/win32/build-rules-msvc.mak
+++ b/win32/build-rules-msvc.mak
@@ -23,11 +23,6 @@
 $<
 <<
 
-{..\src\}.cc{$(CFG)\$(PLAT)\harfbuzz-icu\}.obj::
-	$(CXX) $(CFLAGS) $(HB_LIB_CFLAGS) $(HB_ICU_CFLAGS) /Fo$(CFG)\$(PLAT)\harfbuzz-icu\ /c @<<
-$<
-<<
-
 {..\util\}.cc{$(CFG)\$(PLAT)\util\}.obj::
 	$(CXX) $(CFLAGS) $(HB_DEFINES) $(HB_CFLAGS) /Fo$(CFG)\$(PLAT)\util\ /c @<<
 $<
@@ -48,7 +43,6 @@
 
 # Rules for building .lib files
 $(CFG)\$(PLAT)\harfbuzz.lib: $(HARFBUZZ_DLL_FILENAME).dll
-$(CFG)\$(PLAT)\harfbuzz-icu.lib: $(HARFBUZZ_ICU_DLL_FILENAME).dll
 $(CFG)\$(PLAT)\harfbuzz-gobject.lib: $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll
 
 # Rules for linking DLLs
@@ -64,12 +58,6 @@
 <<
 	@-if exist $@.manifest mt /manifest $@.manifest /outputresource:$@;2
 
-$(HARFBUZZ_ICU_DLL_FILENAME).dll: $(CFG)\$(PLAT)\harfbuzz.lib $(harfbuzz_icu_OBJS) $(CFG)\$(PLAT)\harfbuzz-icu
-	link /DLL $(LDFLAGS) $(CFG)\$(PLAT)\harfbuzz.lib $(HB_ICU_DEP_LIBS) /implib:$(CFG)\$(PLAT)\harfbuzz-icu.lib -out:$@ @<<
-$(harfbuzz_icu_OBJS)
-<<
-	@-if exist $@.manifest mt /manifest $@.manifest /outputresource:$@;2
-
 $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll: $(CFG)\$(PLAT)\harfbuzz.lib $(harfbuzz_gobject_OBJS) $(CFG)\$(PLAT)\harfbuzz-gobject
 	link /DLL $(LDFLAGS) $(CFG)\$(PLAT)\harfbuzz.lib $(HB_GOBJECT_DEP_LIBS) /implib:$(CFG)\$(PLAT)\harfbuzz-gobject.lib -out:$@ @<<
 $(harfbuzz_gobject_OBJS)
@@ -131,7 +119,6 @@
 	@-del /f /q $(CFG)\$(PLAT)\*.obj
 	@-if exist $(CFG)\$(PLAT)\util del /f /q $(CFG)\$(PLAT)\util\*.obj
 	@-if exist $(CFG)\$(PLAT)\harfbuzz-gobject del /f /q $(CFG)\$(PLAT)\harfbuzz-gobject\*.obj
-	@-if exist $(CFG)\$(PLAT)\harfbuzz-icu del /f /q $(CFG)\$(PLAT)\harfbuzz-icu\*.obj
 	@-del /f /q $(CFG)\$(PLAT)\harfbuzz\*.obj
 	@-rmdir /s /q $(CFG)\$(PLAT)
 	@-if exist $(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.h del $(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.h
diff --git a/win32/config-msvc.mak b/win32/config-msvc.mak
index e0c6468..4cffaea 100644
--- a/win32/config-msvc.mak
+++ b/win32/config-msvc.mak
@@ -12,7 +12,11 @@
 HB_GOBJECT_DEP_LIBS = gobject-2.0.lib $(HB_GLIB_LIBS)
 
 # Freetype is needed for building FreeType support and hb-view
+!if "$(CFG)" == "debug"
+FREETYPE_LIB = freetyped.lib
+!else
 FREETYPE_LIB = freetype.lib
+!endif
 
 # Cairo is needed for building hb-view
 CAIRO_LIB = cairo.lib
@@ -20,6 +24,9 @@
 # Graphite2 is needed for building SIL Graphite2 support
 GRAPHITE2_LIB = graphite2.lib
 
+# Uniscribe is needed for Uniscribe shaping support
+UNISCRIBE_LIB = usp10.lib gdi32.lib rpcrt4.lib user32.lib
+
 # Directwrite is needed for DirectWrite shaping support
 DIRECTWRITE_LIB = dwrite.lib
 
@@ -31,17 +38,15 @@
 HB_SOURCES =	\
 	$(HB_BASE_sources)		\
 	$(HB_FALLBACK_sources)	\
-	$(HB_OT_sources)		\
-	$(HB_UNISCRIBE_sources)	\
+	$(HB_OT_sources)
 
 HB_HEADERS =	\
 	$(HB_BASE_headers)		\
 	$(HB_NODIST_headers)	\
-	$(HB_OT_headers)		\
-	$(HB_UNISCRIBE_headers)
+	$(HB_OT_headers)
 
 # Minimal set of (system) libraries needed for the HarfBuzz DLL
-HB_DEP_LIBS = usp10.lib gdi32.lib rpcrt4.lib user32.lib
+HB_DEP_LIBS =
 
 # We build the HarfBuzz DLL/LIB at least
 HB_LIBS = $(CFG)\$(PLAT)\harfbuzz.lib
@@ -55,29 +60,12 @@
 # Use libtool-style DLL names, if desired
 !if "$(LIBTOOL_DLL_NAME)" == "1"
 HARFBUZZ_DLL_FILENAME = $(CFG)\$(PLAT)\libharfbuzz-0
-HARFBUZZ_ICU_DLL_FILENAME = $(CFG)\$(PLAT)\libharfbuzz-icu-0
 HARFBUZZ_GOBJECT_DLL_FILENAME = $(CFG)\$(PLAT)\libharfbuzz-gobject-0
 !else
 HARFBUZZ_DLL_FILENAME = $(CFG)\$(PLAT)\harfbuzz-vs$(VSVER)
-HARFBUZZ_ICU_DLL_FILENAME = $(CFG)\$(PLAT)\harfbuzz-icu-vs$(VSVER)
 HARFBUZZ_GOBJECT_DLL_FILENAME = $(CFG)\$(PLAT)\harfbuzz-gobject-vs$(VSVER)
 !endif
 
-# Enable HarfBuzz-ICU, if desired
-!if "$(ICU)" == "1"
-HB_ICU_CFLAGS =
-HB_LIBS =	\
-	$(HB_LIBS)	\
-	$(CFG)\$(PLAT)\harfbuzz-icu.lib
-
-# We don't want to re-define int8_t Visual Studio 2008, will cause build breakage
-# as we define it in hb-common.h, and we ought to use the definitions there.
-!if "$(VSVER)" == "9"
-HB_ICU_CFLAGS = /DU_HAVE_INT8_T
-!endif
-
-!endif
-
 # Enable Introspection (enables HarfBuzz-Gobject as well)
 !if "$(INTROSPECTION)" == "1"
 GOBJECT = 1
@@ -124,6 +112,9 @@
 
 # Enable freetype if desired
 !if "$(FREETYPE)" == "1"
+!if "$(FREETYPE_DIR)" != ""
+HB_CFLAGS = $(HB_CFLAGS) /I$(FREETYPE_DIR)
+!endif
 HB_DEFINES = $(HB_DEFINES) /DHAVE_FREETYPE=1
 HB_SOURCES = $(HB_SOURCES) $(HB_FT_sources)
 HB_HEADERS = $(HB_HEADERS) $(HB_FT_headers)
@@ -173,10 +164,31 @@
 	$(CFG)\$(PLAT)\test-unicode.exe				\
 	$(CFG)\$(PLAT)\test-version.exe
 
-!else
-# If there is no GLib support, use the built-in UCDN
+!elseif "$(ICU)" == "1"
+# use ICU for Unicode functions
 # and define some of the macros in GLib's msvc_recommended_pragmas.h
 # to reduce some unneeded build-time warnings
+HB_DEFINES = $(HB_DEFINES) /DHAVE_ICU=1 /DHAVE_ICU_BUILTIN=1
+HB_CFLAGS =	\
+	$(HB_CFLAGS)					\
+	/wd4244							\
+	/D_CRT_SECURE_NO_WARNINGS		\
+	/D_CRT_NONSTDC_NO_WARNINGS
+
+# We don't want ICU to re-define int8_t in VS 2008, will cause build breakage
+# as we define it in hb-common.h, and we ought to use the definitions there.
+!if "$(VSVER)" == "9"
+HB_CFLAGS =	$(HB_CFLAGS) /DU_HAVE_INT8_T
+!endif
+
+HB_SOURCES = $(HB_SOURCES) $(HB_ICU_sources)
+HB_HEADERS = $(HB_HEADERS) $(HB_ICU_headers)
+HB_DEP_LIBS = $(HB_DEP_LIBS) $(HB_ICU_DEP_LIBS)
+!endif
+
+!if "$(UCDN)" != "0"
+# Define some of the macros in GLib's msvc_recommended_pragmas.h
+# to reduce some unneeded build-time warnings
 HB_DEFINES = $(HB_DEFINES) /DHAVE_UCDN=1
 HB_CFLAGS =	\
 	$(HB_CFLAGS)					\
@@ -188,6 +200,13 @@
 HB_SOURCES = $(HB_SOURCES) $(LIBHB_UCDN_sources) $(HB_UCDN_sources)
 !endif
 
+!if "$(UNISCRIBE)" == "1"
+HB_CFLAGS = $(HB_CFLAGS) /DHAVE_UNISCRIBE
+HB_SOURCES = $(HB_SOURCES) $(HB_UNISCRIBE_sources)
+HB_HEADERS = $(HB_HEADERS) $(HB_UNISCRIBE_headers)
+HB_DEP_LIBS = $(HB_DEP_LIBS) $(UNISCRIBE_LIB)
+!endif
+
 !if "$(DIRECTWRITE)" == "1"
 HB_CFLAGS = $(HB_CFLAGS) /DHAVE_DIRECTWRITE
 HB_SOURCES = $(HB_SOURCES) $(HB_DIRECTWRITE_sources)
diff --git a/win32/config.h.win32.in b/win32/config.h.win32.in
index 73ad205..d45cefb 100644
--- a/win32/config.h.win32.in
+++ b/win32/config.h.win32.in
@@ -115,7 +115,7 @@
 #define HAVE_UCDN 1
 
 /* Have Uniscribe library */
-#define HAVE_UNISCRIBE 1
+/* #undef HAVE_UNISCRIBE */
 
 /* Define to 1 if you have the <unistd.h> header file. */
 #ifndef _MSC_VER
diff --git a/win32/create-lists-msvc.mak b/win32/create-lists-msvc.mak
index 9b5574b..dbd2a57 100644
--- a/win32/create-lists-msvc.mak
+++ b/win32/create-lists-msvc.mak
@@ -59,19 +59,6 @@
 !endif
 !endif
 
-# For HarfBuzz-ICU
-!if "$(ICU)" == "1"
-
-!if [call create-lists.bat header hb_objs.mak harfbuzz_icu_OBJS]
-!endif
-
-!if [for %c in ($(HB_ICU_sources)) do @if "%~xc" == ".cc" @call create-lists.bat file hb_objs.mak ^$(CFG)\^$(PLAT)\harfbuzz-icu\%~nc.obj]
-!endif
-
-!if [call create-lists.bat footer hb_objs.mak]
-!endif
-!endif
-
 # For the utility programs (GLib support is required)
 !if "$(GLIB)" == "1"
 
diff --git a/win32/detectenv-msvc.mak b/win32/detectenv-msvc.mak
index 83d8786..ca09793 100644
--- a/win32/detectenv-msvc.mak
+++ b/win32/detectenv-msvc.mak
@@ -110,7 +110,9 @@
 # the resulting binaries
 !if "$(CFG)" == "release"
 CFLAGS_ADD = /MD /O2 /GL /MP
-!if "$(VSVER)" != "9"
+!if $(VSVER) > 9 && $(VSVER) < 14
+# Undocumented "enhance optimized debugging" switch. Became documented
+# as "/Zo" in VS 2013 Update 3, and is turned on by default in VS 2015.
 CFLAGS_ADD = $(CFLAGS_ADD) /d2Zi+
 !endif
 !else
@@ -129,7 +131,10 @@
 !if "$(VALID_CFGSET)" == "TRUE"
 CFLAGS = $(CFLAGS_ADD) /W3 /Zi /I.. /I..\src /I. /I$(PREFIX)\include
 
-LDFLAGS_BASE = $(LDFLAGS_ARCH) /libpath:$(PREFIX)\lib /DEBUG
+!if "$(ADDITIONAL_LIB_DIR)" != ""
+ADDITIONAL_LIB_ARG = /libpath:$(ADDITIONAL_LIB_DIR)
+!endif
+LDFLAGS_BASE = $(LDFLAGS_ARCH) /libpath:$(PREFIX)\lib $(ADDITIONAL_LIB_ARG) /DEBUG
 
 !if "$(CFG)" == "debug"
 LDFLAGS = $(LDFLAGS_BASE)
diff --git a/win32/generate-msvc.mak b/win32/generate-msvc.mak
index 7c17a94..32214eb 100644
--- a/win32/generate-msvc.mak
+++ b/win32/generate-msvc.mak
@@ -22,5 +22,5 @@
 !endif
 
 # Create the build directories
-$(CFG)\$(PLAT)\harfbuzz $(CFG)\$(PLAT)\harfbuzz-icu $(CFG)\$(PLAT)\harfbuzz-gobject $(CFG)\$(PLAT)\util:
-	@-mkdir -p $@
+$(CFG)\$(PLAT)\harfbuzz $(CFG)\$(PLAT)\harfbuzz-gobject $(CFG)\$(PLAT)\util:
+	@-md $@
diff --git a/win32/info-msvc.mak b/win32/info-msvc.mak
index bc85dc9..3ec11d4 100644
--- a/win32/info-msvc.mak
+++ b/win32/info-msvc.mak
@@ -1,6 +1,6 @@
 # NMake Makefile portion for displaying config info
 
-INC_FEATURES = Uniscribe Fallback OT
+INC_FEATURES = Fallback OT
 BUILT_TOOLS =
 BUILT_LIBRARIES = HarfBuzz
 
@@ -11,6 +11,8 @@
 !if "$(CAIRO_FT)" == "1"
 BUILT_TOOLS = hb-view.exe $(BUILT_TOOLS)
 !endif
+!elseif "$(ICU)" == "1"
+UNICODE_IMPL = ICU
 !else
 UNICODE_IMPL = ucdn
 !endif
@@ -23,12 +25,12 @@
 INC_FEATURES = $(INC_FEATURES) Graphite2
 !endif
 
-!if "$(DIRECTWRITE)" == "1"
-INC_FEATURES = $(INC_FEATURES) DirectWrite
+!if "$(UNISCRIBE)" == "1"
+INC_FEATURES = $(INC_FEATURES) Uniscribe
 !endif
 
-!if "$(ICU)" == "1"
-BUILT_LIBRARIES = $(BUILT_LIBRARIES) HarfBuzz-ICU
+!if "$(DIRECTWRITE)" == "1"
+INC_FEATURES = $(INC_FEATURES) DirectWrite
 !endif
 
 !if "$(GOBJECT)" == "1"
@@ -77,9 +79,12 @@
 	@echo.
 	@echo OPTION: Optional, may be any of the following, use OPTION=1 to enable;
 	@echo multiple OPTION's may be used.  If no OPTION is specified, a default
-	@echo HarfBuzz DLL is built with OpenType, fallback and Uniscribe support
+	@echo HarfBuzz DLL is built with OpenType and fallback support
 	@echo with a bundled Unicode implementation (UCDN).
 	@echo ======
+	@echo UNISCRIBE:
+	@echo Enable Uniscribe support.
+	@echo.
 	@echo DIRECTWRITE:
 	@echo Enable DirectWrite support, requires a recent enough Windows SDK.
 	@echo.
@@ -94,20 +99,20 @@
 	@echo library.  Enables the build of utility programs.
 	@echo.
 	@echo ICU:
-	@echo Enable the HarfBuzz-ICU layout library, requires the International
+	@echo Enable build with ICU Unicode functions, requires the International
 	@echo Components for Unicode (ICU) libraries.
 	@echo.
 	@echo GOBJECT:
 	@echo Enable the HarfBuzz-GObject library, also implies GLib2 support,
 	@echo requires the GNOME GLib2 libraries and tools, notably the glib-mkenums
-	@echo tool script, which will require a PERL interpretor (use
+	@echo tool script, which will require a PERL interpreter (use
 	@echo PERL=^$(PATH_TO_PERL_INTERPRETOR)) if it is not already in your PATH).
 	@echo.
 	@echo INTROSPECTION:
 	@echo Enable the build of introspection files, also implies GObject/GLib2 support,
 	@echo requires the GNOME gobject-introspection libraries and tools.  You will need
 	@echo to ensure the pkg-config (.pc) files can be found for GObject-2.0 and the
-	@echo Python interpretor (that was used to build the gobject-introsoection tools)
+	@echo Python interpreter (that was used to build the gobject-introspection tools)
 	@echo can be found by setting PKG_CONFIG_PATH beforehand, and passing in PYTHON=
 	@echo ^$(PATH_TO_PYTHON_INTERPRETOR) respectively, if python.exe is not already
 	@echo in your PATH.
diff --git a/win32/install.mak b/win32/install.mak
index fa239ea..e0a38e3 100644
--- a/win32/install.mak
+++ b/win32/install.mak
@@ -8,9 +8,6 @@
 	@copy /b $(HARFBUZZ_DLL_FILENAME).dll $(PREFIX)\bin
 	@copy /b $(HARFBUZZ_DLL_FILENAME).pdb $(PREFIX)\bin
 	@copy /b $(CFG)\$(PLAT)\harfbuzz.lib $(PREFIX)\lib
-	@if exist $(HARFBUZZ_ICU_DLL_FILENAME).dll copy /b $(HARFBUZZ_ICU_DLL_FILENAME).dll $(PREFIX)\bin
-	@if exist $(HARFBUZZ_ICU_DLL_FILENAME).dll copy /b $(HARFBUZZ_ICU_DLL_FILENAME).pdb $(PREFIX)\bin
-	@if exist $(HARFBUZZ_ICU_DLL_FILENAME).dll copy /b $(CFG)\$(PLAT)\harfbuzz-icu.lib $(PREFIX)\lib
 	@if exist $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll copy /b $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll $(PREFIX)\bin
 	@if exist $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll copy /b $(HARFBUZZ_GOBJECT_DLL_FILENAME).pdb $(PREFIX)\bin
 	@if exist $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll copy /b $(CFG)\$(PLAT)\harfbuzz-gobject.lib $(PREFIX)\lib
@@ -21,7 +18,6 @@
 	@if exist $(CFG)\$(PLAT)\hb-shape.exe copy /b $(CFG)\$(PLAT)\hb-shape.exe $(PREFIX)\bin
 	@if exist $(CFG)\$(PLAT)\hb-shape.exe copy /b $(CFG)\$(PLAT)\hb-shape.pdb $(PREFIX)\bin
 	@for %h in ($(HB_ACTUAL_HEADERS)) do @copy %h $(PREFIX)\include\harfbuzz
-	@if exist $(HARFBUZZ_ICU_DLL_FILENAME).dll for %h in ($(HB_ICU_headers)) do @copy ..\src\%h $(PREFIX)\include\harfbuzz
 	@if exist $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll for %h in ($(HB_GOBJECT_headers)) do @copy ..\src\%h $(PREFIX)\include\harfbuzz
 	@if exist $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll copy $(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.h $(PREFIX)\include\harfbuzz
 	@rem Copy the generated introspection files