Merge pull request #357 from khaledhosny/graphite-scale

[graphite] Fix shaping with varying font sizes
diff --git a/NEWS b/NEWS
index 8c3ef11..43a3bac 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,34 @@
+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
 ====================================
diff --git a/configure.ac b/configure.ac
index 32dcf18..31fa97d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.4.1],
+        [1.4.2],
         [https://github.com/behdad/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
diff --git a/docs/harfbuzz-docs.xml b/docs/harfbuzz-docs.xml
index 7a42d23..00113e9 100644
--- a/docs/harfbuzz-docs.xml
+++ b/docs/harfbuzz-docs.xml
@@ -180,7 +180,7 @@
         <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">
+      <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>
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index 270c87c..a91eb4c 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -245,6 +245,7 @@
 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
@@ -252,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
@@ -266,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>
@@ -304,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
@@ -328,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
@@ -384,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
@@ -410,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
@@ -423,6 +439,23 @@
 </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
@@ -486,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
@@ -498,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
@@ -552,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/m4/ax_pthread.m4 b/m4/ax_pthread.m4
index d90de34..4c4051e 100644
--- a/m4/ax_pthread.m4
+++ b/m4/ax_pthread.m4
@@ -19,10 +19,10 @@
 #   is necessary on AIX to use the special cc_r compiler alias.)
 #
 #   NOTE: You are assumed to not only compile your program with these flags,
-#   but also link it with them as well. e.g. you should link with
+#   but also to link with them as well. For example, you might link with
 #   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
 #
-#   If you are only building threads programs, you may wish to use these
+#   If you are only building threaded programs, you may wish to use these
 #   variables in your default LIBS, CFLAGS, and CC:
 #
 #     LIBS="$PTHREAD_LIBS $LIBS"
@@ -30,8 +30,8 @@
 #     CC="$PTHREAD_CC"
 #
 #   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
-#   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
-#   (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#   has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
+#   that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
 #
 #   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
 #   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
@@ -82,35 +82,40 @@
 #   modified version of the Autoconf Macro, you may extend this special
 #   exception to the GPL to apply to your modified version as well.
 
-#serial 18
+#serial 23
 
 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
 AC_DEFUN([AX_PTHREAD], [
 AC_REQUIRE([AC_CANONICAL_HOST])
+AC_REQUIRE([AC_PROG_CC])
+AC_REQUIRE([AC_PROG_SED])
 AC_LANG_PUSH([C])
 ax_pthread_ok=no
 
 # We used to check for pthread.h first, but this fails if pthread.h
-# requires special compiler flags (e.g. on True64 or Sequent).
+# requires special compiler flags (e.g. on Tru64 or Sequent).
 # It gets checked for in the link test anyway.
 
 # First of all, check if the user has set any of the PTHREAD_LIBS,
 # etcetera environment variables, and if threads linking works using
 # them:
-if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
-        save_CFLAGS="$CFLAGS"
+if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
+        ax_pthread_save_CC="$CC"
+        ax_pthread_save_CFLAGS="$CFLAGS"
+        ax_pthread_save_LIBS="$LIBS"
+        AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
         CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-        save_LIBS="$LIBS"
         LIBS="$PTHREAD_LIBS $LIBS"
-        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
-        AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
-        AC_MSG_RESULT($ax_pthread_ok)
-        if test x"$ax_pthread_ok" = xno; then
+        AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
+        AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
+        AC_MSG_RESULT([$ax_pthread_ok])
+        if test "x$ax_pthread_ok" = "xno"; then
                 PTHREAD_LIBS=""
                 PTHREAD_CFLAGS=""
         fi
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
+        CC="$ax_pthread_save_CC"
+        CFLAGS="$ax_pthread_save_CFLAGS"
+        LIBS="$ax_pthread_save_LIBS"
 fi
 
 # We must check for the threads library under a number of different
@@ -123,7 +128,7 @@
 # which indicates that we try without any flags at all, and "pthread-config"
 # which is a program returning the flags for the Pth emulation library.
 
-ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
 
 # The ordering *is* (sometimes) important.  Some notes on the
 # individual items follow:
@@ -132,68 +137,225 @@
 # none: in case threads are in libc; should be tried before -Kthread and
 #       other compiler flags to prevent continual compiler warnings
 # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
-# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
-# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
-# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
-# -pthreads: Solaris/gcc
-# -mthreads: Mingw32/gcc, Lynx/gcc
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
+#           (Note: HP C rejects this with "bad form for `-t' option")
+# -pthreads: Solaris/gcc (Note: HP C also rejects)
 # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
-#      doesn't hurt to check since this sometimes defines pthreads too;
-#      also defines -D_REENTRANT)
-#      ... -mt is also the pthreads flag for HP/aCC
+#      doesn't hurt to check since this sometimes defines pthreads and
+#      -D_REENTRANT too), HP C (must be checked before -lpthread, which
+#      is present but should not be used directly; and before -mthreads,
+#      because the compiler interprets this as "-mt" + "-hreads")
+# -mthreads: Mingw32/gcc, Lynx/gcc
 # pthread: Linux, etcetera
 # --thread-safe: KAI C++
 # pthread-config: use pthread-config program (for GNU Pth library)
 
-case ${host_os} in
+case $host_os in
+
+        freebsd*)
+
+        # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+        # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+
+        ax_pthread_flags="-kthread lthread $ax_pthread_flags"
+        ;;
+
+        hpux*)
+
+        # From the cc(1) man page: "[-mt] Sets various -D flags to enable
+        # multi-threading and also sets -lpthread."
+
+        ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
+        ;;
+
+        openedition*)
+
+        # IBM z/OS requires a feature-test macro to be defined in order to
+        # enable POSIX threads at all, so give the user a hint if this is
+        # not set. (We don't define these ourselves, as they can affect
+        # other portions of the system API in unpredictable ways.)
+
+        AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
+            [
+#            if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
+             AX_PTHREAD_ZOS_MISSING
+#            endif
+            ],
+            [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
+        ;;
+
         solaris*)
 
         # On Solaris (at least, for some versions), libc contains stubbed
         # (non-functional) versions of the pthreads routines, so link-based
-        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
-        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
-        # a function called by this macro, so we could check for that, but
-        # who knows whether they'll stub that too in a future libc.)  So,
-        # we'll just look for -pthreads and -lpthread first:
+        # tests will erroneously succeed. (N.B.: The stubs are missing
+        # pthread_cleanup_push, or rather a function called by this macro,
+        # so we could check for that, but who knows whether they'll stub
+        # that too in a future libc.)  So we'll check first for the
+        # standard Solaris way of linking pthreads (-mt -lpthread).
 
-        ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
-        ;;
-
-        darwin*)
-        ax_pthread_flags="-pthread $ax_pthread_flags"
+        ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
         ;;
 esac
 
-if test x"$ax_pthread_ok" = xno; then
-for flag in $ax_pthread_flags; do
+# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
 
-        case $flag in
+AS_IF([test "x$GCC" = "xyes"],
+      [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"])
+
+# The presence of a feature test macro requesting re-entrant function
+# definitions is, on some systems, a strong hint that pthreads support is
+# correctly enabled
+
+case $host_os in
+        darwin* | hpux* | linux* | osf* | solaris*)
+        ax_pthread_check_macro="_REENTRANT"
+        ;;
+
+        aix*)
+        ax_pthread_check_macro="_THREAD_SAFE"
+        ;;
+
+        *)
+        ax_pthread_check_macro="--"
+        ;;
+esac
+AS_IF([test "x$ax_pthread_check_macro" = "x--"],
+      [ax_pthread_check_cond=0],
+      [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
+
+# Are we compiling with Clang?
+
+AC_CACHE_CHECK([whether $CC is Clang],
+    [ax_cv_PTHREAD_CLANG],
+    [ax_cv_PTHREAD_CLANG=no
+     # Note that Autoconf sets GCC=yes for Clang as well as GCC
+     if test "x$GCC" = "xyes"; then
+        AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
+            [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
+#            if defined(__clang__) && defined(__llvm__)
+             AX_PTHREAD_CC_IS_CLANG
+#            endif
+            ],
+            [ax_cv_PTHREAD_CLANG=yes])
+     fi
+    ])
+ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
+
+ax_pthread_clang_warning=no
+
+# Clang needs special handling, because older versions handle the -pthread
+# option in a rather... idiosyncratic way
+
+if test "x$ax_pthread_clang" = "xyes"; then
+
+        # Clang takes -pthread; it has never supported any other flag
+
+        # (Note 1: This will need to be revisited if a system that Clang
+        # supports has POSIX threads in a separate library.  This tends not
+        # to be the way of modern systems, but it's conceivable.)
+
+        # (Note 2: On some systems, notably Darwin, -pthread is not needed
+        # to get POSIX threads support; the API is always present and
+        # active.  We could reasonably leave PTHREAD_CFLAGS empty.  But
+        # -pthread does define _REENTRANT, and while the Darwin headers
+        # ignore this macro, third-party headers might not.)
+
+        PTHREAD_CFLAGS="-pthread"
+        PTHREAD_LIBS=
+
+        ax_pthread_ok=yes
+
+        # However, older versions of Clang make a point of warning the user
+        # that, in an invocation where only linking and no compilation is
+        # taking place, the -pthread option has no effect ("argument unused
+        # during compilation").  They expect -pthread to be passed in only
+        # when source code is being compiled.
+        #
+        # Problem is, this is at odds with the way Automake and most other
+        # C build frameworks function, which is that the same flags used in
+        # compilation (CFLAGS) are also used in linking.  Many systems
+        # supported by AX_PTHREAD require exactly this for POSIX threads
+        # support, and in fact it is often not straightforward to specify a
+        # flag that is used only in the compilation phase and not in
+        # linking.  Such a scenario is extremely rare in practice.
+        #
+        # Even though use of the -pthread flag in linking would only print
+        # a warning, this can be a nuisance for well-run software projects
+        # that build with -Werror.  So if the active version of Clang has
+        # this misfeature, we search for an option to squash it.
+
+        AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
+            [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
+            [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
+             # Create an alternate version of $ac_link that compiles and
+             # links in two steps (.c -> .o, .o -> exe) instead of one
+             # (.c -> exe), because the warning occurs only in the second
+             # step
+             ax_pthread_save_ac_link="$ac_link"
+             ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
+             ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"`
+             ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
+             ax_pthread_save_CFLAGS="$CFLAGS"
+             for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
+                AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
+                CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
+                ac_link="$ax_pthread_save_ac_link"
+                AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
+                    [ac_link="$ax_pthread_2step_ac_link"
+                     AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
+                         [break])
+                    ])
+             done
+             ac_link="$ax_pthread_save_ac_link"
+             CFLAGS="$ax_pthread_save_CFLAGS"
+             AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
+             ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
+            ])
+
+        case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
+                no | unknown) ;;
+                *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
+        esac
+
+fi # $ax_pthread_clang = yes
+
+if test "x$ax_pthread_ok" = "xno"; then
+for ax_pthread_try_flag in $ax_pthread_flags; do
+
+        case $ax_pthread_try_flag in
                 none)
                 AC_MSG_CHECKING([whether pthreads work without any flags])
                 ;;
 
+                -mt,pthread)
+                AC_MSG_CHECKING([whether pthreads work with -mt -lpthread])
+                PTHREAD_CFLAGS="-mt"
+                PTHREAD_LIBS="-lpthread"
+                ;;
+
                 -*)
-                AC_MSG_CHECKING([whether pthreads work with $flag])
-                PTHREAD_CFLAGS="$flag"
+                AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
+                PTHREAD_CFLAGS="$ax_pthread_try_flag"
                 ;;
 
                 pthread-config)
-                AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
-                if test x"$ax_pthread_config" = xno; then continue; fi
+                AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
+                AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
                 PTHREAD_CFLAGS="`pthread-config --cflags`"
                 PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
                 ;;
 
                 *)
-                AC_MSG_CHECKING([for the pthreads library -l$flag])
-                PTHREAD_LIBS="-l$flag"
+                AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
+                PTHREAD_LIBS="-l$ax_pthread_try_flag"
                 ;;
         esac
 
-        save_LIBS="$LIBS"
-        save_CFLAGS="$CFLAGS"
-        LIBS="$PTHREAD_LIBS $LIBS"
+        ax_pthread_save_CFLAGS="$CFLAGS"
+        ax_pthread_save_LIBS="$LIBS"
         CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
 
         # Check for various functions.  We must include pthread.h,
         # since some functions may be macros.  (On the Sequent, we
@@ -204,7 +366,11 @@
         # pthread_cleanup_push because it is one of the few pthread
         # functions on Solaris that doesn't have a non-functional libc stub.
         # We try pthread_create on general principles.
+
         AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+#                       if $ax_pthread_check_cond
+#                        error "$ax_pthread_check_macro must be defined"
+#                       endif
                         static void routine(void *a) { a = 0; }
                         static void *start_routine(void *a) { return a; }],
                        [pthread_t th; pthread_attr_t attr;
@@ -213,16 +379,14 @@
                         pthread_attr_init(&attr);
                         pthread_cleanup_push(routine, 0);
                         pthread_cleanup_pop(0) /* ; */])],
-                [ax_pthread_ok=yes],
-                [])
+            [ax_pthread_ok=yes],
+            [])
 
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
+        CFLAGS="$ax_pthread_save_CFLAGS"
+        LIBS="$ax_pthread_save_LIBS"
 
-        AC_MSG_RESULT($ax_pthread_ok)
-        if test "x$ax_pthread_ok" = xyes; then
-                break;
-        fi
+        AC_MSG_RESULT([$ax_pthread_ok])
+        AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
 
         PTHREAD_LIBS=""
         PTHREAD_CFLAGS=""
@@ -230,76 +394,88 @@
 fi
 
 # Various other checks:
-if test "x$ax_pthread_ok" = xyes; then
-        save_LIBS="$LIBS"
-        LIBS="$PTHREAD_LIBS $LIBS"
-        save_CFLAGS="$CFLAGS"
+if test "x$ax_pthread_ok" = "xyes"; then
+        ax_pthread_save_CFLAGS="$CFLAGS"
+        ax_pthread_save_LIBS="$LIBS"
         CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
 
         # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
-        AC_MSG_CHECKING([for joinable pthread attribute])
-        attr_name=unknown
-        for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
-            AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
-                           [int attr = $attr; return attr /* ; */])],
-                [attr_name=$attr; break],
-                [])
-        done
-        AC_MSG_RESULT($attr_name)
-        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
-            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
-                               [Define to necessary symbol if this constant
-                                uses a non-standard name on your system.])
-        fi
+        AC_CACHE_CHECK([for joinable pthread attribute],
+            [ax_cv_PTHREAD_JOINABLE_ATTR],
+            [ax_cv_PTHREAD_JOINABLE_ATTR=unknown
+             for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+                 AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+                                                 [int attr = $ax_pthread_attr; return attr /* ; */])],
+                                [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
+                                [])
+             done
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
+               test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
+               test "x$ax_pthread_joinable_attr_defined" != "xyes"],
+              [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
+                                  [$ax_cv_PTHREAD_JOINABLE_ATTR],
+                                  [Define to necessary symbol if this constant
+                                   uses a non-standard name on your system.])
+               ax_pthread_joinable_attr_defined=yes
+              ])
 
-        AC_MSG_CHECKING([if more special flags are required for pthreads])
-        flag=no
-        case ${host_os} in
-            aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
-            osf* | hpux*) flag="-D_REENTRANT";;
-            solaris*)
-            if test "$GCC" = "yes"; then
-                flag="-D_REENTRANT"
-            else
-                flag="-mt -D_REENTRANT"
-            fi
-            ;;
-        esac
-        AC_MSG_RESULT(${flag})
-        if test "x$flag" != xno; then
-            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
-        fi
+        AC_CACHE_CHECK([whether more special flags are required for pthreads],
+            [ax_cv_PTHREAD_SPECIAL_FLAGS],
+            [ax_cv_PTHREAD_SPECIAL_FLAGS=no
+             case $host_os in
+             solaris*)
+             ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
+             ;;
+             esac
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
+               test "x$ax_pthread_special_flags_added" != "xyes"],
+              [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
+               ax_pthread_special_flags_added=yes])
 
         AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
-            ax_cv_PTHREAD_PRIO_INHERIT, [
-                AC_LINK_IFELSE([
-                    AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
-                    [ax_cv_PTHREAD_PRIO_INHERIT=yes],
-                    [ax_cv_PTHREAD_PRIO_INHERIT=no])
+            [ax_cv_PTHREAD_PRIO_INHERIT],
+            [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
+                                             [[int i = PTHREAD_PRIO_INHERIT;]])],
+                            [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+                            [ax_cv_PTHREAD_PRIO_INHERIT=no])
             ])
-        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
-            AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
+        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
+               test "x$ax_pthread_prio_inherit_defined" != "xyes"],
+              [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
+               ax_pthread_prio_inherit_defined=yes
+              ])
 
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
+        CFLAGS="$ax_pthread_save_CFLAGS"
+        LIBS="$ax_pthread_save_LIBS"
 
-        # More AIX lossage: must compile with xlc_r or cc_r
-        if test x"$GCC" != xyes; then
-          AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
-        else
-          PTHREAD_CC=$CC
+        # More AIX lossage: compile with *_r variant
+        if test "x$GCC" != "xyes"; then
+            case $host_os in
+                aix*)
+                AS_CASE(["x/$CC"],
+                    [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
+                    [#handle absolute path differently from PATH based program lookup
+                     AS_CASE(["x$CC"],
+                         [x/*],
+                         [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
+                         [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
+                ;;
+            esac
         fi
-else
-        PTHREAD_CC="$CC"
 fi
 
-AC_SUBST(PTHREAD_LIBS)
-AC_SUBST(PTHREAD_CFLAGS)
-AC_SUBST(PTHREAD_CC)
+test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+
+AC_SUBST([PTHREAD_LIBS])
+AC_SUBST([PTHREAD_CFLAGS])
+AC_SUBST([PTHREAD_CC])
 
 # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
-if test x"$ax_pthread_ok" = xyes; then
-        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+if test "x$ax_pthread_ok" = "xyes"; then
+        ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
         :
 else
         ax_pthread_ok=no
diff --git a/src/Makefile.am b/src/Makefile.am
index 8cfe4ac..e322d56 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -218,7 +218,7 @@
 	(cat $^ || echo 'hb_ERROR ()' ) | \
 	$(EGREP) '^hb_.* \(' | \
 	sed -e 's/ (.*//' | \
-	LANG=C sort; \
+	LC_ALL=C sort; \
 	echo LIBRARY libharfbuzz-0.dll; \
 	) >"$@"
 	@ ! grep -q hb_ERROR "$@" \
@@ -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 e727598..6363cf9 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -76,11 +76,11 @@
 	hb-ot-layout-gsubgpos-private.hh \
 	hb-ot-layout-gsub-table.hh \
 	hb-ot-layout-jstf-table.hh \
-	hb-ot-layout-math-table.hh \
 	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 \
@@ -108,6 +108,11 @@
 	hb-ot-shape-fallback-private.hh \
 	hb-ot-shape-fallback.cc \
 	hb-ot-shape-private.hh \
+	hb-ot-var.cc \
+	hb-ot-var-avar-table.hh \
+	hb-ot-var-fvar-table.hh \
+	hb-ot-var-hvar-table.hh \
+	hb-ot-var-mvar-table.hh \
 	$(NULL)
 
 HB_OT_headers = \
@@ -117,6 +122,7 @@
 	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/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 e857dfa..86de0be 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -69,8 +69,8 @@
 }
 
 
-HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face)
-HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font)
+HB_SHAPER_DATA_ENSURE_DEFINE(coretext, face)
+HB_SHAPER_DATA_ENSURE_DEFINE(coretext, font)
 
 
 /*
diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc
index b5c1113..ab07d8a 100644
--- a/src/hb-directwrite.cc
+++ b/src/hb-directwrite.cc
@@ -34,8 +34,8 @@
 #define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0)
 #endif
 
-HB_SHAPER_DATA_ENSURE_DECLARE(directwrite, face)
-HB_SHAPER_DATA_ENSURE_DECLARE(directwrite, font)
+HB_SHAPER_DATA_ENSURE_DEFINE(directwrite, face)
+HB_SHAPER_DATA_ENSURE_DEFINE(directwrite, font)
 
 
 /*
@@ -671,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);
@@ -719,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);
 
@@ -915,7 +915,7 @@
  */
 
 hb_bool_t
-hb_shape_dwrite_experimental_width(hb_font_t          *font,
+hb_directwrite_shape_experimental_width(hb_font_t          *font,
   hb_buffer_t        *buffer,
   const hb_feature_t *features,
   unsigned int        num_features,
diff --git a/src/hb-directwrite.h b/src/hb-directwrite.h
index 0eb116f..e743af2 100644
--- a/src/hb-directwrite.h
+++ b/src/hb-directwrite.h
@@ -30,7 +30,7 @@
 HB_BEGIN_DECLS
 
 HB_EXTERN hb_bool_t
-hb_shape_dwrite_experimental_width(hb_font_t *font, hb_buffer_t *buffer,
+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
diff --git a/src/hb-face-private.hh b/src/hb-face-private.hh
index c4266ff..eb0e850 100644
--- a/src/hb-face-private.hh
+++ b/src/hb-face-private.hh
@@ -50,12 +50,23 @@
   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;
+  enum dirty_t {
+    NOTHING	= 0x0000,
+    INDEX	= 0x0001,
+    UPEM	= 0x0002,
+    NUM_GLYPHS	= 0x0004,
+  } dirty;
 
+  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;
@@ -95,6 +106,8 @@
   HB_INTERNAL void load_num_glyphs (void) const;
 };
 
+HB_MARK_AS_FLAG_T (hb_face_t::dirty_t);
+
 extern HB_INTERNAL const hb_face_t _hb_face_nil;
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
diff --git a/src/hb-face.cc b/src/hb-face.cc
index 6b563bc..1800c99 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
@@ -55,6 +51,8 @@
   1000, /* upem */
   0,    /* num_glyphs */
 
+  hb_face_t::NOTHING, /* dirty */
+
   {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
 #include "hb-shaper-list.hh"
@@ -175,7 +173,7 @@
 				    closure,
 				    (hb_destroy_func_t) _hb_face_for_data_closure_destroy);
 
-  hb_face_set_index (face, index);
+  face->index = index;
 
   return face;
 }
@@ -369,6 +367,11 @@
   if (face->immutable)
     return;
 
+  if (face->index == index)
+    return;
+
+  face->dirty |= face->INDEX;
+
   face->index = index;
 }
 
@@ -404,6 +407,11 @@
   if (face->immutable)
     return;
 
+  if (face->upem == upem)
+    return;
+
+  face->dirty |= face->UPEM;
+
   face->upem = upem;
 }
 
@@ -448,6 +456,11 @@
   if (face->immutable)
     return;
 
+  if (face->num_glyphs == glyph_count)
+    return;
+
+  face->dirty |= face->NUM_GLYPHS;
+
   face->num_glyphs = glyph_count;
 }
 
diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc
index ac6d4b0..4b60c6c 100644
--- a/src/hb-fallback-shape.cc
+++ b/src/hb-fallback-shape.cc
@@ -28,6 +28,10 @@
 #include "hb-shaper-impl-private.hh"
 
 
+HB_SHAPER_DATA_ENSURE_DEFINE(fallback, face)
+HB_SHAPER_DATA_ENSURE_DEFINE(fallback, font)
+
+
 /*
  * shaper face data
  */
@@ -125,7 +129,7 @@
       pos[i].y_advance = 0;
       continue;
     }
-    font->get_nominal_glyph (info[i].codepoint, &info[i].codepoint);
+    (void) font->get_nominal_glyph (info[i].codepoint, &info[i].codepoint);
     font->get_glyph_advance_for_direction (info[i].codepoint,
 					   direction,
 					   &pos[i].x_advance,
diff --git a/src/hb-font-private.hh b/src/hb-font-private.hh
index 53671d7..fbb16a0 100644
--- a/src/hb-font-private.hh
+++ b/src/hb-font-private.hh
@@ -116,6 +116,16 @@
   void              *user_data;
   hb_destroy_func_t  destroy;
 
+  enum dirty_t {
+    NOTHING	= 0x0000,
+    FACE	= 0x0001,
+    PARENT	= 0x0002,
+    FUNCS	= 0x0004,
+    SCALE	= 0x0008,
+    PPEM	= 0x0010,
+    VARIATIONS	= 0x0020,
+  } dirty;
+
   struct hb_shaper_data_t shaper_data;
 
 
@@ -543,6 +553,8 @@
   }
 };
 
+HB_MARK_AS_FLAG_T (hb_font_t::dirty_t);
+
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font);
 #include "hb-shaper-list.hh"
diff --git a/src/hb-font.cc b/src/hb-font.cc
index 2935c4b..e900bd3 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>
 
 
 /*
@@ -1203,6 +1196,8 @@
     NULL, /* user_data */
     NULL, /* destroy */
 
+    hb_font_t::NOTHING, /* dirty */
+
     {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
 #include "hb-shaper-list.hh"
@@ -1355,6 +1350,11 @@
   if (!parent)
     parent = hb_font_get_empty ();
 
+  if (parent == font->parent)
+    return;
+
+  font->dirty |= font->PARENT;
+
   hb_font_t *old = font->parent;
 
   font->parent = hb_font_reference (parent);
@@ -1379,6 +1379,37 @@
 }
 
 /**
+ * hb_font_set_face:
+ * @font: a font.
+ * @face: new face.
+ *
+ * Sets font-face of @font.
+ *
+ * Since: 1.4.3
+ **/
+void
+hb_font_set_face (hb_font_t *font,
+		  hb_face_t *face)
+{
+  if (font->immutable)
+    return;
+
+  if (unlikely (!face))
+    face = hb_face_get_empty ();
+
+  if (font->face == face)
+    return;
+
+  font->dirty |= font->FACE;
+
+  hb_face_t *old = font->face;
+
+  font->face = hb_face_reference (face);
+
+  hb_face_destroy (old);
+}
+
+/**
  * hb_font_get_face:
  * @font: a font.
  *
@@ -1424,6 +1455,8 @@
   if (!klass)
     klass = hb_font_funcs_get_empty ();
 
+  font->dirty |= font->FUNCS;
+
   hb_font_funcs_reference (klass);
   hb_font_funcs_destroy (font->klass);
   font->klass = klass;
@@ -1479,6 +1512,11 @@
   if (font->immutable)
     return;
 
+  if (font->x_scale == x_scale && font->y_scale == y_scale)
+    return;
+
+  font->dirty |= font->SCALE;
+
   font->x_scale = x_scale;
   font->y_scale = y_scale;
 }
@@ -1520,6 +1558,11 @@
   if (font->immutable)
     return;
 
+  if (font->x_ppem == x_ppem && font->y_ppem == y_ppem)
+    return;
+
+  font->dirty |= font->PPEM;
+
   font->x_ppem = x_ppem;
   font->y_ppem = y_ppem;
 }
@@ -1543,30 +1586,122 @@
   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)
+{
+  if (font->num_coords == coords_length &&
+      (coords_length == 0 ||
+       0 == memcmp (font->coords, coords, coords_length * sizeof (coords[0]))))
+  {
+    free (coords);
+    return;
+  }
+
+  font->dirty |= font->VARIATIONS;
+
+  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,
-				   int *coords, /* XXX 2.14 normalized */
+				   const int *coords, /* 2.14 normalized */
 				   unsigned int coords_length)
 {
   if (font->immutable)
     return;
 
-  /* Skip tail zero entries. */
-  while (coords_length && !coords[coords_length - 1])
-    coords_length--;
-
   int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : NULL;
   if (unlikely (coords_length && !copy))
     return;
 
-  free (font->coords);
-
   if (coords_length)
     memcpy (copy, coords, coords_length * sizeof (coords[0]));
 
-  font->coords = copy;
-  font->num_coords = coords_length;
+  _hb_font_adopt_var_coords_normalized (font, copy, coords_length);
+}
+
+/**
+ * hb_font_get_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;
 }
 
 
diff --git a/src/hb-font.h b/src/hb-font.h
index 8813286..85fb56d 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -563,6 +563,10 @@
 HB_EXTERN hb_font_t *
 hb_font_get_parent (hb_font_t *font);
 
+HB_EXTERN void
+hb_font_set_face (hb_font_t *font,
+		  hb_face_t *face);
+
 HB_EXTERN hb_face_t *
 hb_font_get_face (hb_font_t *font);
 
@@ -603,12 +607,25 @@
 		  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,
-				   int *coords, /* XXX 2.14 normalized */
+				   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
 
 #endif /* HB_FONT_H */
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index f127066..48d6a0e 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -621,17 +621,22 @@
   FT_MM_Var *mm_var = NULL;
   if (!FT_Get_MM_Var (ft_face, &mm_var))
   {
-    FT_Fixed coords[mm_var->num_axis];
-    int hbCoords[mm_var->num_axis];
-    if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, coords))
+    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)
     {
-      for (int i = 0; i < mm_var->num_axis; ++i)
-	hbCoords[i] = coords[i] >> 2;
+      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, hbCoords, mm_var->num_axis);
+	hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis);
+      }
+      free (coords);
+      free (ft_coords);
     }
+    free (mm_var);
   }
-  free (mm_var);
 #endif
 
   return font;
@@ -736,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-graphite2.cc b/src/hb-graphite2.cc
index f253a5f..5b804b8 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -34,8 +34,8 @@
 #include <graphite2/Segment.h>
 
 
-HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, face)
-HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, font)
+HB_SHAPER_DATA_ENSURE_DEFINE(graphite2, face)
+HB_SHAPER_DATA_ENSURE_DEFINE(graphite2, font)
 
 
 /*
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 2cc1fb2..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 {
@@ -829,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>
@@ -950,6 +952,7 @@
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), array);
 };
+template <typename Type> struct LArrayOf : ArrayOf<Type, ULONG> {};
 
 /* Array of Offset's */
 template <typename Type, typename OffsetType=USHORT>
@@ -1061,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
index 52897ab..0a7fbf5 100644
--- a/src/hb-ot-cbdt-table.hh
+++ b/src/hb-ot-cbdt-table.hh
@@ -216,7 +216,7 @@
 
   USHORT firstGlyphIndex;
   USHORT lastGlyphIndex;
-  OffsetTo<IndexSubtable, ULONG> offsetToSubtable;
+  LOffsetTo<IndexSubtable> offsetToSubtable;
 
   DEFINE_SIZE_STATIC(8);
 };
@@ -275,7 +275,7 @@
   }
 
   protected:
-  OffsetTo<IndexSubtableArray, ULONG> indexSubtableArrayOffset;
+  LOffsetTo<IndexSubtableArray> indexSubtableArrayOffset;
   ULONG indexTablesSize;
   ULONG numberOfIndexSubtables;
   ULONG colorRef;
@@ -348,8 +348,8 @@
   }
 
   protected:
-  FixedVersion<>version;
-  ArrayOf<BitmapSizeTable, ULONG> sizeTables;
+  FixedVersion<>		version;
+  LArrayOf<BitmapSizeTable>	sizeTables;
 
   public:
   DEFINE_SIZE_ARRAY(8, sizeTables);
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 5be055d..009db20 100644
--- a/src/hb-ot-font.cc
+++ b/src/hb-ot-font.cc
@@ -37,6 +37,7 @@
 #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"
 
 
@@ -50,12 +51,16 @@
   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 _var_tag,
 		    hb_tag_t os2_tag,
 		    unsigned int default_advance = 0)
   {
@@ -91,7 +96,7 @@
 
     this->has_font_extents = got_font_extents;
 
-    this->blob = OT::Sanitizer<OT::_mtx>::sanitize (face->reference_table (_mtx_tag));
+    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);
@@ -107,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))
     {
@@ -128,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?!
   }
 };
 
@@ -421,55 +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;
-  hb_lazy_loader_t<hb_ot_face_cbdt_accelerator_t> cbdt;
+  OT::hb_lazy_loader_t<hb_ot_face_glyf_accelerator_t> glyf;
+  OT::hb_lazy_loader_t<hb_ot_face_cbdt_accelerator_t> cbdt;
 };
 
 
@@ -482,8 +448,8 @@
     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,
+  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);
@@ -529,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
@@ -559,6 +525,7 @@
   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);
@@ -576,6 +543,7 @@
   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);
+  // TODO Hook up variations.
   return ot_font->h_metrics.has_font_extents;
 }
 
@@ -589,6 +557,7 @@
   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);
+  // TODO Hook up variations.
   return ot_font->v_metrics.has_font_extents;
 }
 
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 62ca7a3..180e5f0 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -1323,6 +1323,14 @@
 					     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);
@@ -1334,7 +1342,7 @@
 
   protected:
   USHORT				format;
-  OffsetTo<VarRegionList, ULONG>	regions;
+  LOffsetTo<VarRegionList>		regions;
   OffsetArrayOf<VarData, ULONG>		dataSets;
   public:
   DEFINE_SIZE_ARRAY (8, dataSets);
@@ -1433,8 +1441,8 @@
   }
 
   protected:
-  USHORT			featureIndex;
-  OffsetTo<Feature, ULONG>	feature;
+  USHORT		featureIndex;
+  LOffsetTo<Feature>	feature;
   public:
   DEFINE_SIZE_STATIC (6);
 };
@@ -1481,9 +1489,9 @@
   }
 
   protected:
-  OffsetTo<ConditionSet, ULONG>
+  LOffsetTo<ConditionSet>
 			conditions;
-  OffsetTo<FeatureTableSubstitution, ULONG>
+  LOffsetTo<FeatureTableSubstitution>
 			substitutions;
   public:
   DEFINE_SIZE_STATIC (8);
@@ -1527,7 +1535,7 @@
 
   protected:
   FixedVersion<>	version;	/* Version--0x00010000u */
-  ArrayOf<FeatureVariationRecord, ULONG>
+  LArrayOf<FeatureVariationRecord>
 			varRecords;
   public:
   DEFINE_SIZE_ARRAY (8, varRecords);
diff --git a/src/hb-ot-layout-gdef-table.hh b/src/hb-ot-layout-gdef-table.hh
index b70cbb7..552df0f 100644
--- a/src/hb-ot-layout-gdef-table.hh
+++ b/src/hb-ot-layout-gdef-table.hh
@@ -295,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:
@@ -443,7 +443,7 @@
 					 * definitions--from beginning of GDEF
 					 * header (may be NULL).  Introduced
 					 * in version 0x00010002. */
-  OffsetTo<VariationStore, ULONG>
+  LOffsetTo<VariationStore>
 		varStore;		/* Offset to the table of Item Variation
 					 * Store--from beginning of GDEF
 					 * header (may be NULL).  Introduced
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index fd75c54..b7a0122 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -2313,7 +2313,7 @@
 		featureList; 	/* FeatureList table */
   OffsetTo<LookupList>
 		lookupList; 	/* LookupList table */
-  OffsetTo<FeatureVariations, ULONG>
+  LOffsetTo<FeatureVariations>
 		featureVars;	/* Offset to Feature Variations
 				   table--from beginning of table
 				 * (may be NULL).  Introduced
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index a4272de..071a439 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: */
@@ -125,6 +126,8 @@
   struct GSUB;
   struct GPOS;
   struct MATH;
+  struct fvar;
+  struct avar;
 }
 
 struct hb_ot_layout_lookup_accelerator_t
@@ -153,12 +156,15 @@
   hb_blob_t *gdef_blob;
   hb_blob_t *gsub_blob;
   hb_blob_t *gpos_blob;
-  hb_blob_t *math_blob;
 
   const struct OT::GDEF *gdef;
   const struct OT::GSUB *gsub;
   const struct OT::GPOS *gpos;
-  const struct OT::MATH *math;
+
+  /* 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;
@@ -617,5 +623,4 @@
 #undef lig_props
 #undef glyph_props
 
-
 #endif /* HB_OT_LAYOUT_PRIVATE_HH */
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 145ec76..a1682a5 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -34,15 +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)
 
 hb_ot_layout_t *
 _hb_ot_layout_create (hb_face_t *face)
@@ -60,9 +55,9 @@
   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);
 
-  /* The MATH table is rarely used, so only try and load it in _get_math. */
-  layout->math_blob = NULL;
-  layout->math = NULL;
+  layout->math.init (face);
+  layout->fvar.init (face);
+  layout->avar.init (face);
 
   {
     /*
@@ -110,10 +105,16 @@
       || (994 == gdef_len && 60336 == gpos_len && 24474 == gsub_len)
       /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343  tahomabd.ttf from Windows 10 */
       || (1006 == gdef_len && 61740 == gpos_len && 24470 == gsub_len)
+      /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5  tahoma.ttf from Windows 10 AU */
+      || (1006 == gdef_len && 61352 == gpos_len && 24576 == gsub_len)
+      /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2  tahomabd.ttf from Windows 10 AU */
+      || (1018 == gdef_len && 62834 == gpos_len && 24572 == gsub_len)
       /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7  Tahoma.ttf from Mac OS X 10.9 */
       || (832 == gdef_len && 47162 == gpos_len && 7324 == gsub_len)
       /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba  Tahoma Bold.ttf from Mac OS X 10.9 */
       || (844 == gdef_len && 45474 == gpos_len && 7302 == gsub_len)
+      /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc  himalaya.ttf from Windows 7 */
+      || (180 == gdef_len && 7254 == gpos_len && 13054 == gsub_len)
       /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0  himalaya.ttf from Windows 8 */
       || (192 == gdef_len && 7254 == gpos_len && 12638 == gsub_len)
       /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427  himalaya.ttf from Windows 8.1 */
@@ -181,7 +182,10 @@
   hb_blob_destroy (layout->gdef_blob);
   hb_blob_destroy (layout->gsub_blob);
   hb_blob_destroy (layout->gpos_blob);
-  hb_blob_destroy (layout->math_blob);
+
+  layout->math.fini ();
+  layout->fvar.fini ();
+  layout->avar.fini ();
 
   free (layout);
 }
diff --git a/src/hb-ot-layout-math-table.hh b/src/hb-ot-math-table.hh
similarity index 99%
rename from src/hb-ot-layout-math-table.hh
rename to src/hb-ot-math-table.hh
index b52b121..191d79e 100644
--- a/src/hb-ot-layout-math-table.hh
+++ b/src/hb-ot-math-table.hh
@@ -24,8 +24,8 @@
  * Igalia Author(s): Frédéric Wang
  */
 
-#ifndef HB_OT_LAYOUT_MATH_TABLE_HH
-#define HB_OT_LAYOUT_MATH_TABLE_HH
+#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"
@@ -716,7 +716,7 @@
   DEFINE_SIZE_STATIC (10);
 };
 
-} /* mathspace OT */
+} /* namespace OT */
 
 
-#endif /* HB_OT_LAYOUT_MATH_TABLE_HH */
+#endif /* HB_OT_MATH_TABLE_HH */
diff --git a/src/hb-ot-math.cc b/src/hb-ot-math.cc
index 9ef21d2..f82a073 100644
--- a/src/hb-ot-math.cc
+++ b/src/hb-ot-math.cc
@@ -26,33 +26,15 @@
 
 #include "hb-open-type-private.hh"
 
-#include "hb-ot-layout-math-table.hh"
-
-HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
+#include "hb-ot-layout-private.hh"
+#include "hb-ot-math-table.hh"
 
 static inline const OT::MATH&
 _get_math (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::MATH);
-
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-
-retry:
-  const OT::MATH *math = (const OT::MATH *) hb_atomic_ptr_get (&layout->math);
-
-  if (unlikely (!math))
-  {
-    hb_blob_t *blob = OT::Sanitizer<OT::MATH>::sanitize (face->reference_table (HB_OT_TAG_MATH));
-    math = OT::Sanitizer<OT::MATH>::lock_instance (blob);
-    if (!hb_atomic_ptr_cmpexch (&layout->math, NULL, math))
-    {
-      hb_blob_destroy (blob);
-      goto retry;
-    }
-    layout->math_blob = blob;
-  }
-
-  return *math;
+  return *(layout->math.get ());
 }
 
 /*
@@ -64,10 +46,9 @@
  * @face: #hb_face_t to test
  *
  * This function allows to verify the presence of an OpenType MATH table on the
- * face. If so, such a table will be loaded into memory and sanitized. You can
- * then safely call other functions for math layout and shaping.
+ * face.
  *
- * Return value: #TRUE if face has a MATH table and #FALSE otherwise
+ * Return value: true if face has a MATH table, false otherwise
  *
  * Since: 1.3.3
  **/
@@ -136,10 +117,10 @@
 
 /**
  * hb_ot_math_is_glyph_extended_shape:
- * @font: a #hb_font_t to test
+ * @face: a #hb_face_t to test
  * @glyph: a glyph index to test
  *
- * Return value: #TRUE if the glyph is an extended shape and #FALSE otherwise
+ * Return value: true if the glyph is an extended shape, false otherwise
  *
  * Since: 1.3.3
  **/
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index 56ec5cd..57ffc1d 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -540,7 +540,7 @@
       /* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */
       hb_position_t extra_repeat_overlap = 0;
       hb_position_t shortfall = sign * w_remaining - sign * w_repeating * (n_copies + 1);
-      if (shortfall > 0)
+      if (shortfall > 0 && n_repeating > 0)
       {
         ++n_copies;
         hb_position_t excess = (n_copies + 1) * sign * w_repeating - sign * w_remaining;
diff --git a/src/hb-ot-shape-complex-hangul.cc b/src/hb-ot-shape-complex-hangul.cc
index eb95a28..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,
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index 39572df..952441b 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -191,6 +191,9 @@
     case HB_SCRIPT_MANICHAEAN:
     case HB_SCRIPT_PSALTER_PAHLAVI:
 
+    /* Unicode-9.0 additions */
+    case HB_SCRIPT_ADLAM:
+
       /* For Arabic script, use the Arabic shaper even if no OT script tag was found.
        * This is because we do fallback shaping for Arabic script (and not others).
        * But note that Arabic shaping is applicable only to horizontal layout; for
diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc
index 5b19d5d..af68706 100644
--- a/src/hb-ot-shape-complex-use.cc
+++ b/src/hb-ot-shape-complex-use.cc
@@ -572,28 +572,6 @@
      */
     case 0x1112Eu : *a = 0x11127u; *b= 0x11131u; return true;
     case 0x1112Fu : *a = 0x11127u; *b= 0x11132u; return true;
-
-    /*
-     * Decompose split matras that don't have Unicode decompositions.
-     */
-
-    /* Limbu */
-    case 0x1925u  : *a = 0x1920u; *b= 0x1923u; return true;
-    case 0x1926u  : *a = 0x1920u; *b= 0x1924u; return true;
-
-    /* Balinese */
-    case 0x1B3Cu  : *a = 0x1B42u; *b= 0x1B3Cu; return true;
-
-#if 0
-    /* Lepcha */
-    case 0x1C29u  : *a = no decomp, -> LEFT; return true;
-
-    /* Javanese */
-    case 0xA9C0u  : *a = no decomp, -> RIGHT; return true;
-
-    /* Sharada */
-    case 0x111BFu  : *a = no decomp, -> ABOVE; return true;
-#endif
   }
 
   return (bool) c->unicode->decompose (ab, a, b);
diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index 107617e..94a3d7d 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -91,7 +91,7 @@
 static inline void
 set_glyph (hb_glyph_info_t &info, hb_font_t *font)
 {
-  font->get_nominal_glyph (info.codepoint, &info.glyph_index());
+  (void) font->get_nominal_glyph (info.codepoint, &info.glyph_index());
 }
 
 static inline void
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index ddd6662..2eacb34 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -128,6 +128,8 @@
  * shaper face data
  */
 
+HB_SHAPER_DATA_ENSURE_DEFINE(ot, face)
+
 hb_ot_shaper_face_data_t *
 _hb_ot_shaper_face_data_create (hb_face_t *face)
 {
@@ -145,6 +147,8 @@
  * shaper font data
  */
 
+HB_SHAPER_DATA_ENSURE_DEFINE(ot, font)
+
 struct hb_ot_shaper_font_data_t {};
 
 hb_ot_shaper_font_data_t *
@@ -362,6 +366,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;
@@ -380,10 +396,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;
     }
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..f9d801e
--- /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-mvar-table.hh b/src/hb-ot-var-mvar-table.hh
new file mode 100644
index 0000000..3cb7498
--- /dev/null
+++ b/src/hb-ot-var-mvar-table.hh
@@ -0,0 +1,110 @@
+/*
+ * 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_MVAR_TABLE_HH
+#define HB_OT_VAR_MVAR_TABLE_HH
+
+#include "hb-ot-layout-common-private.hh"
+
+
+namespace OT {
+
+
+struct VariationValueRecord
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  Tag		valueTag;	/* Four-byte tag identifying a font-wide measure. */
+  ULONG		varIdx;		/* Outer/inner index into VariationStore item. */
+
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+
+/*
+ * MVAR -- Metrics Variations Table
+ */
+
+#define HB_OT_TAG_MVAR HB_TAG('M','V','A','R')
+
+struct MVAR
+{
+  static const hb_tag_t tableTag	= HB_OT_TAG_MVAR;
+
+  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) &&
+		  valueRecordSize >= VariationValueRecord::static_size &&
+		  varStore.sanitize (c, this) &&
+		  c->check_array (values, valueRecordSize, valueRecordCount));
+  }
+
+  inline float get_var (hb_tag_t tag,
+			int *coords, unsigned int coord_count) const
+  {
+    const VariationValueRecord *record;
+    record = (VariationValueRecord *) bsearch (&tag, values,
+					       valueRecordCount, valueRecordSize,
+					       (hb_compare_func_t) tag_compare);
+    if (!record)
+      return 0.;
+
+    return (this+varStore).get_delta (record->varIdx, coords, coord_count);
+  }
+
+protected:
+  static inline int tag_compare (const hb_tag_t *a, const Tag *b)
+  { return b->cmp (*a); }
+
+  protected:
+  FixedVersion<>version;	/* Version of the metrics variation table
+				 * initially set to 0x00010000u */
+  USHORT	reserved;	/* Not used; set to 0. */
+  USHORT	valueRecordSize;/* The size in bytes of each value record —
+				 * must be greater than zero. */
+  USHORT	valueRecordCount;/* The number of value records — may be zero. */
+  OffsetTo<VariationStore>
+		varStore;	/* Offset to item variation store table. */
+  BYTE		values[VAR];	/* Array of value records. The records must be
+				 * in binary order of their valueTag field. */
+
+  public:
+  DEFINE_SIZE_ARRAY (12, values);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_VAR_MVAR_TABLE_HH */
diff --git a/src/hb-ot-var.cc b/src/hb-ot-var.cc
new file mode 100644
index 0000000..691196d
--- /dev/null
+++ b/src/hb-ot-var.cc
@@ -0,0 +1,159 @@
+/*
+ * 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-mvar-table.hh"
+#include "hb-ot-var.h"
+
+/*
+ * 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 113e37b..2120a3e 100644
--- a/src/hb-ot.h
+++ b/src/hb-ot.h
@@ -35,6 +35,7 @@
 #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-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.cc b/src/hb-shape-plan.cc
index 600faae..1ac77be 100644
--- a/src/hb-shape-plan.cc
+++ b/src/hb-shape-plan.cc
@@ -35,13 +35,6 @@
 #endif
 
 
-#define HB_SHAPER_IMPLEMENT(shaper) \
-	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, face) \
-	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, font)
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-
-
 static void
 hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
 		    const hb_feature_t *user_features,
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 706f144..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
  **/
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-shaper-private.hh b/src/hb-shaper-private.hh
index d1d1146..381398a 100644
--- a/src/hb-shaper-private.hh
+++ b/src/hb-shaper-private.hh
@@ -65,27 +65,31 @@
 #define HB_SHAPER_DATA_INVALID ((void *) -1)
 #define HB_SHAPER_DATA_IS_INVALID(data) ((void *) (data) == HB_SHAPER_DATA_INVALID)
 
-#define HB_SHAPER_DATA_TYPE(shaper, object)		struct hb_##shaper##_shaper_##object##_data_t
+#define HB_SHAPER_DATA_TYPE_NAME(shaper, object)	hb_##shaper##_shaper_##object##_data_t
+#define HB_SHAPER_DATA_TYPE(shaper, object)		struct HB_SHAPER_DATA_TYPE_NAME(shaper, object)
 #define HB_SHAPER_DATA_INSTANCE(shaper, object, instance)	(* (HB_SHAPER_DATA_TYPE(shaper, object) **) &(instance)->shaper_data.shaper)
-#define HB_SHAPER_DATA(shaper, object)			HB_SHAPER_DATA_INSTANCE (shaper, object, object)
+#define HB_SHAPER_DATA(shaper, object)			HB_SHAPER_DATA_INSTANCE(shaper, object, object)
 #define HB_SHAPER_DATA_CREATE_FUNC(shaper, object)	_hb_##shaper##_shaper_##object##_data_create
 #define HB_SHAPER_DATA_DESTROY_FUNC(shaper, object)	_hb_##shaper##_shaper_##object##_data_destroy
+#define HB_SHAPER_DATA_ENSURE_FUNC(shaper, object)	hb_##shaper##_shaper_##object##_data_ensure
 
 #define HB_SHAPER_DATA_PROTOTYPE(shaper, object) \
 	HB_SHAPER_DATA_TYPE (shaper, object); /* Type forward declaration. */ \
 	extern "C" HB_INTERNAL HB_SHAPER_DATA_TYPE (shaper, object) * \
 	HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (hb_##object##_t *object HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS); \
 	extern "C" HB_INTERNAL void \
-	HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (HB_SHAPER_DATA_TYPE (shaper, object) *data)
+	HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (HB_SHAPER_DATA_TYPE (shaper, object) *data); \
+	extern "C" HB_INTERNAL bool \
+	HB_SHAPER_DATA_ENSURE_FUNC (shaper, object) (hb_##object##_t *object)
 
 #define HB_SHAPER_DATA_DESTROY(shaper, object) \
     if (HB_SHAPER_DATA_TYPE (shaper, object) *data = HB_SHAPER_DATA (shaper, object)) \
       if (data != HB_SHAPER_DATA_INVALID && data != HB_SHAPER_DATA_SUCCEEDED) \
         HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data);
 
-#define HB_SHAPER_DATA_ENSURE_DECLARE(shaper, object) \
-static inline bool \
-hb_##shaper##_shaper_##object##_data_ensure (hb_##object##_t *object) \
+#define HB_SHAPER_DATA_ENSURE_DEFINE(shaper, object) \
+bool \
+HB_SHAPER_DATA_ENSURE_FUNC(shaper, object) (hb_##object##_t *object) \
 {\
   retry: \
   HB_SHAPER_DATA_TYPE (shaper, object) *data = (HB_SHAPER_DATA_TYPE (shaper, object) *) hb_atomic_ptr_get (&HB_SHAPER_DATA (shaper, object)); \
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 6e4db01..58f983d 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -293,8 +293,8 @@
   unsigned int index_last;  /* == end - 1 */
 };
 
-HB_SHAPER_DATA_ENSURE_DECLARE(uniscribe, face)
-HB_SHAPER_DATA_ENSURE_DECLARE(uniscribe, font)
+HB_SHAPER_DATA_ENSURE_DEFINE(uniscribe, face)
+HB_SHAPER_DATA_ENSURE_DEFINE(uniscribe, font)
 
 
 /*
diff --git a/test/api/test-font.c b/test/api/test-font.c
index 34f6c74..527dfcd 100644
--- a/test/api/test-font.c
+++ b/test/api/test-font.c
@@ -35,13 +35,23 @@
 static void
 test_face_empty (void)
 {
+  hb_face_t *created_from_empty;
+  hb_face_t *created_from_null;
+
   g_assert (hb_face_get_empty ());
-  g_assert (hb_face_get_empty () != hb_face_create (hb_blob_get_empty (), 0));
-  g_assert (hb_face_get_empty () != hb_face_create (NULL, 0));
+
+  created_from_empty = hb_face_create (hb_blob_get_empty (), 0);
+  g_assert (hb_face_get_empty () != created_from_empty);
+
+  created_from_null = hb_face_create (NULL, 0);
+  g_assert (hb_face_get_empty () != created_from_null);
 
   g_assert (hb_face_reference_table (hb_face_get_empty (), HB_TAG ('h','e','a','d')) == hb_blob_get_empty ());
 
   g_assert_cmpint (hb_face_get_upem (hb_face_get_empty ()), ==, 1000);
+
+  hb_face_destroy (created_from_null);
+  hb_face_destroy (created_from_empty);
 }
 
 static void
@@ -357,14 +367,29 @@
 static void
 test_font_empty (void)
 {
+  hb_font_t *created_from_empty;
+  hb_font_t *created_from_null;
+  hb_font_t *created_sub_from_null;
+
   g_assert (hb_font_get_empty ());
-  g_assert (hb_font_get_empty () != hb_font_create (hb_face_get_empty ()));
-  g_assert (hb_font_get_empty () != hb_font_create (NULL));
-  g_assert (hb_font_get_empty () != hb_font_create_sub_font (NULL));
+
+  created_from_empty = hb_font_create (hb_face_get_empty ());
+  g_assert (hb_font_get_empty () != created_from_empty);
+
+  created_from_null = hb_font_create (NULL);
+  g_assert (hb_font_get_empty () != created_from_null);
+
+  created_sub_from_null = hb_font_create_sub_font (NULL);
+  g_assert (hb_font_get_empty () != created_sub_from_null);
+
   g_assert (hb_font_is_immutable (hb_font_get_empty ()));
 
   g_assert (hb_font_get_face (hb_font_get_empty ()) == hb_face_get_empty ());
   g_assert (hb_font_get_parent (hb_font_get_empty ()) == NULL);
+
+  hb_font_destroy (created_sub_from_null);
+  hb_font_destroy (created_from_null);
+  hb_font_destroy (created_from_empty);
 }
 
 static void
diff --git a/test/api/test-ot-math.c b/test/api/test-ot-math.c
index 049656a..0ca5566 100644
--- a/test/api/test-ot-math.c
+++ b/test/api/test-ot-math.c
@@ -70,14 +70,18 @@
   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_ft_face_create_cached(ft_face);
+  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
@@ -93,6 +97,14 @@
   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();
 }
 
@@ -400,6 +412,11 @@
 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");
@@ -463,10 +480,6 @@
                                                  NULL), ==, 0);
 
   g_assert(hb_font_get_glyph_from_name (hb_font, "arrowleft", -1, &glyph));
-  hb_ot_math_glyph_variant_t variants[20];
-  unsigned variantsSize = sizeof (variants) / sizeof (variants[0]);
-  unsigned int count;
-  unsigned int offset = 0;
   do {
     count = variantsSize;
     hb_ot_math_get_glyph_variants (hb_font,
@@ -523,6 +536,11 @@
 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");
@@ -590,10 +608,6 @@
                                                  NULL), ==, 0);
 
   g_assert(hb_font_get_glyph_from_name (hb_font, "arrowright", -1, &glyph));
-  hb_ot_math_glyph_part_t parts[20];
-  unsigned partsSize = sizeof (parts) / sizeof (parts[0]);
-  unsigned int count;
-  unsigned int offset = 0;
   do {
     count = partsSize;
     hb_ot_math_get_glyph_assembly (hb_font,
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/shaping/Makefile.am b/test/shaping/Makefile.am
index f2bb95b..89578e1 100644
--- a/test/shaping/Makefile.am
+++ b/test/shaping/Makefile.am
@@ -43,6 +43,8 @@
 TESTS = \
 	tests/arabic-fallback-shaping.tests \
 	tests/arabic-feature-order.tests \
+	tests/arabic-like-joining.tests \
+	tests/automatic-fractions.tests \
 	tests/cluster.tests \
 	tests/color-fonts.tests \
 	tests/context-matching.tests \
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/5dfad7735c6a67085f1b90d4d497e32907db4c78.ttf b/test/shaping/fonts/sha1sum/5dfad7735c6a67085f1b90d4d497e32907db4c78.ttf
new file mode 100644
index 0000000..45f16fc
--- /dev/null
+++ b/test/shaping/fonts/sha1sum/5dfad7735c6a67085f1b90d4d497e32907db4c78.ttf
Binary files differ
diff --git a/test/shaping/tests/arabic-like-joining.tests b/test/shaping/tests/arabic-like-joining.tests
new file mode 100644
index 0000000..ec994d0
--- /dev/null
+++ b/test/shaping/tests/arabic-like-joining.tests
@@ -0,0 +1 @@
+fonts/sha1sum/5dfad7735c6a67085f1b90d4d497e32907db4c78.ttf::U+1E922,U+1E923,U+1E924,U+1E925,U+1E926,U+1E927,U+1E928,U+1E929,U+1E92A,U+1E92B,U+1E92C,U+1E92D,U+1E92E,U+1E92F,U+1E930,U+1E931,U+1E932,U+1E933,U+1E934,U+1E935,U+1E936,U+1E937,U+1E938,U+1E939,U+1E93A,U+1E93B,U+1E93C,U+1E93D,U+1E93E,U+1E93F,U+1E940,U+1E941,U+1E942,U+1E943:[sha_adlam.fina=33+711|kpo_adlam.medi=32+573|zal_adlam.medi=31+773|gbe_adlam.medi=30+594|kha_adlam.medi=29+686|va_adlam.medi=28+621|nha_adlam.medi=27+587|tu_adlam.medi=26+772|nya_adlam.medi=25+577|ga_adlam.medi=24+552|qaaf_adlam.medi=23+694|ha_adlam.medi=22+600|chi_adlam.medi=21+662|jiim_adlam.medi=20+781|u_adlam.medi=19+678|ya_adlam.medi=18+553|kaf_adlam.medi=17+808|nun_adlam.medi=16+561|waw_adlam.medi=15+651|yhe_adlam.medi=14+674|dha_adlam.medi=13+674|o_adlam.medi=12+640|i_adlam.medi=11+657|fa_adlam.medi=10+590|e_adlam.medi=9+628|ra_adlam.medi=8+599|bhe_adlam.medi=7+594|pe_adlam.medi=6+492|sinnyiiyhe_adlam.medi=5+777|ba_adlam.medi=4+655|miim_adlam.medi=3+525|laam_adlam.medi=2+554|daali_adlam.medi=1+600|alif_adlam.init=0+597]
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/util/hb-shape.cc b/util/hb-shape.cc
index 3bd2184..75c3793 100644
--- a/util/hb-shape.cc
+++ b/util/hb-shape.cc
@@ -35,7 +35,9 @@
 		    format (parser),
 		    gs (NULL),
 		    line_no (0),
-		    font (NULL) {}
+		    font (NULL),
+		    output_format (HB_BUFFER_SERIALIZE_FORMAT_INVALID),
+		    format_flags (HB_BUFFER_SERIALIZE_FLAG_DEFAULT) {}
 
   void init (const font_options_t *font_opts)
   {
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/config-msvc.mak b/win32/config-msvc.mak
index ecbbe92..4cffaea 100644
--- a/win32/config-msvc.mak
+++ b/win32/config-msvc.mak
@@ -184,9 +184,10 @@
 HB_SOURCES = $(HB_SOURCES) $(HB_ICU_sources)
 HB_HEADERS = $(HB_HEADERS) $(HB_ICU_headers)
 HB_DEP_LIBS = $(HB_DEP_LIBS) $(HB_ICU_DEP_LIBS)
-!else
-# If there is no GLib support, use the built-in UCDN
-# and define some of the macros in GLib's msvc_recommended_pragmas.h
+!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 =	\