Merge branch 'master' into subset-varstore
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 2ce6a8e..6ad98d2 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -264,7 +264,7 @@
       - image: dockcross/android-arm
     steps:
       - checkout
-      - run: cmake -Bbuild -H. -GNinja
+      - run: cmake -Bbuild -H. -GNinja -DHB_BUILD_TESTS=OFF
       - run: ninja -Cbuild
 
   crosscompile-cmake-notest-browser-asmjs-hb_tiny:
@@ -272,7 +272,7 @@
       - image: dockcross/browser-asmjs
     steps:
       - checkout
-      - run: cmake -Bbuild -H. -GNinja -DCMAKE_CXX_FLAGS="-DHB_TINY"
+      - run: cmake -Bbuild -H. -GNinja -DCMAKE_CXX_FLAGS="-DHB_TINY" -DHB_BUILD_TESTS=OFF
       - run: ninja -Cbuild
 
   crosscompile-cmake-notest-linux-arm64:
@@ -280,7 +280,7 @@
       - image: dockcross/linux-arm64
     steps:
       - checkout
-      - run: cmake -Bbuild -H. -GNinja
+      - run: cmake -Bbuild -H. -GNinja -DHB_BUILD_TESTS=OFF
       - run: ninja -Cbuild
 
   crosscompile-cmake-notest-linux-mips:
@@ -288,7 +288,7 @@
       - image: dockcross/linux-mips
     steps:
       - checkout
-      - run: cmake -Bbuild -H. -GNinja
+      - run: cmake -Bbuild -H. -GNinja -DHB_BUILD_TESTS=OFF
       - run: ninja -Cbuild
 
   #crosscompile-cmake-notest-windows-x64:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c9ba016..66e4a83 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -43,6 +43,7 @@
 endif ()
 if (WIN32)
   option(HB_HAVE_UNISCRIBE "Enable Uniscribe shaper backend on Windows" OFF)
+  option(HB_HAVE_GDI "Enable GDI integration helpers on Windows" OFF)
   option(HB_HAVE_DIRECTWRITE "Enable DirectWrite shaper backend on Windows" OFF)
 endif ()
 option(HB_BUILD_UTILS "Build harfbuzz utils, needs cairo, freetype, and glib properly be installed" OFF)
@@ -77,6 +78,7 @@
   set (HB_HAVE_GRAPHITE2 ON)
   if (WIN32)
     set (HB_HAVE_UNISCRIBE ON)
+    set (HB_HAVE_GDI ON)
     set (HB_HAVE_DIRECTWRITE ON)
   elseif (APPLE)
     set (HB_HAVE_CORETEXT ON)
@@ -140,8 +142,8 @@
 
 ## Extract variables from Makefile files
 function (extract_make_variable variable makefile_source)
-  string(REGEX MATCH "${variable} = ([^$]+)\\$" temp ${makefile_source})
-  string(REGEX MATCHALL "[^ \n\t\\]+" listVar ${CMAKE_MATCH_1})
+  string(REGEX MATCH "${variable} = ([^$]+)\\$" temp "${makefile_source}")
+  string(REGEX MATCHALL "[^ \n\t\\]+" listVar "${CMAKE_MATCH_1}")
   set (${variable} ${listVar} PARENT_SCOPE)
 endfunction ()
 
@@ -157,8 +159,6 @@
 file(READ ${PROJECT_SOURCE_DIR}/src/Makefile.sources SRCSOURCES)
 file(READ ${PROJECT_SOURCE_DIR}/util/Makefile.sources UTILSOURCES)
 
-extract_make_variable(HB_BASE_sources ${SRCSOURCES})
-add_prefix_to_list(HB_BASE_sources "${PROJECT_SOURCE_DIR}/src/")
 extract_make_variable(HB_BASE_headers ${SRCSOURCES})
 add_prefix_to_list(HB_BASE_headers "${PROJECT_SOURCE_DIR}/src/")
 
@@ -192,59 +192,12 @@
 set (HB_VERSION_MINOR ${CMAKE_MATCH_3})
 set (HB_VERSION_MICRO ${CMAKE_MATCH_4})
 
-
-## Define ragel tasks
-# if (NOT IN_HB_DIST)
-#  foreach (ragel_output IN ITEMS ${HB_BASE_RAGEL_GENERATED_sources})
-#    string(REGEX MATCH "([^/]+)\\.hh" temp ${ragel_output})
-#    set (target_name ${CMAKE_MATCH_1})
-#    add_custom_command(OUTPUT ${ragel_output}
-#      COMMAND ${RAGEL} -G2 -o ${ragel_output} ${PROJECT_SOURCE_DIR}/src/${target_name}.rl -I ${PROJECT_SOURCE_DIR} ${ARGN}
-#      DEPENDS ${PROJECT_SOURCE_DIR}/src/${target_name}.rl
-#    )
-#    add_custom_target(harfbuzz_${target_name} DEPENDS ${PROJECT_BINARY_DIR}/src/${target_name})
-#  endforeach ()
-
-#  mark_as_advanced(RAGEL)
-# endif ()
-
-
-## Generate hb-version.h
-# if (NOT IN_HB_DIST)
-#  set (HB_VERSION_H_IN "${PROJECT_SOURCE_DIR}/src/hb-version.h.in")
-#  set (HB_VERSION_H "${PROJECT_BINARY_DIR}/src/hb-version.h")
-#  set_source_files_properties("${HB_VERSION_H}" PROPERTIES GENERATED true)
-#  configure_file("${HB_VERSION_H_IN}" "${HB_VERSION_H}.tmp" @ONLY)
-#  execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different
-#    "${HB_VERSION_H}.tmp"
-#    "${HB_VERSION_H}"
-#  )
-#  file(REMOVE "${HB_VERSION_H}.tmp")
-# endif ()
-
-
 ## Define sources and headers of the project
-set (project_sources
-  ${HB_BASE_sources}
-  ${HB_BASE_RAGEL_GENERATED_sources}
-)
-
-set (subset_project_sources
-  ${HB_SUBSET_sources}
-)
-
+set (project_sources ${PROJECT_SOURCE_DIR}/src/harfbuzz.cc) # use amalgam source
+set (subset_project_sources ${HB_SUBSET_sources})
 set (project_extra_sources)
-
-set (project_headers
-  #${HB_VERSION_H}
-
-  ${HB_BASE_headers}
-)
-
-set (subset_project_headers
-  ${HB_SUBSET_headers}
-)
-
+set (project_headers ${HB_BASE_headers})
+set (subset_project_headers ${HB_SUBSET_headers})
 
 ## Find and include needed header folders and libraries
 if (HB_HAVE_FREETYPE)
@@ -257,7 +210,6 @@
   include_directories(AFTER ${FREETYPE_INCLUDE_DIRS})
   add_definitions(-DHAVE_FREETYPE=1)
 
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-ft.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-ft.h)
 
   # So check_funcs can find its headers
@@ -275,7 +227,6 @@
 
   include_directories(${GRAPHITE2_INCLUDE_DIR})
 
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-graphite2.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-graphite2.h)
 
   list(APPEND THIRD_PARTY_LIBS ${GRAPHITE2_LIBRARY})
@@ -296,7 +247,6 @@
 
   include_directories(${GLIBCONFIG_INCLUDE_DIR} ${GLIB_INCLUDE_DIR})
 
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-glib.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-glib.h)
 
   list(APPEND THIRD_PARTY_LIBS ${GLIB_LIBRARIES})
@@ -316,7 +266,6 @@
 
   include_directories(${ICU_INCLUDE_DIR})
 
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-icu.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-icu.h)
 
   list(APPEND THIRD_PARTY_LIBS ${ICU_LIBRARY})
@@ -328,7 +277,6 @@
   # Apple Advanced Typography
   add_definitions(-DHAVE_CORETEXT)
 
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-coretext.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-coretext.h)
 
   if (HB_IOS)
@@ -359,21 +307,21 @@
   endif ()
 endif ()
 
+if (WIN32 AND HB_HAVE_GDI)
+  add_definitions(-DHAVE_GDI)
+  list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-gdi.h)
+  list(APPEND THIRD_PARTY_LIBS gdi32)
+endif ()
+
 if (WIN32 AND HB_HAVE_UNISCRIBE)
   add_definitions(-DHAVE_UNISCRIBE)
-
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-uniscribe.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-uniscribe.h)
-
   list(APPEND THIRD_PARTY_LIBS usp10 gdi32 rpcrt4)
 endif ()
 
 if (WIN32 AND HB_HAVE_DIRECTWRITE)
   add_definitions(-DHAVE_DIRECTWRITE)
-
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-directwrite.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-directwrite.h)
-
   list(APPEND THIRD_PARTY_LIBS dwrite rpcrt4)
 endif ()
 
@@ -481,7 +429,6 @@
   )
 endif ()
 
-
 ## Atomic ops availability detection
 file(WRITE "${PROJECT_BINARY_DIR}/try_compile_intel_atomic_primitives.c"
 "		void memory_barrier (void) { __sync_synchronize (); }
@@ -590,7 +537,6 @@
 endif ()
 
 if (HB_HAVE_INTROSPECTION)
-
   find_package(PkgConfig)
   pkg_check_modules(PC_GI QUIET gobject-introspection-1.0)
 
@@ -824,7 +770,7 @@
 
 if (HB_BUILD_TESTS)
   ## src/ executables
-  foreach (prog main test test-gsub-would-substitute test-gpos-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges)
+  foreach (prog main test test-gsub-would-substitute test-gpos-size-params test-buffer-serialize test-unicode-ranges) # hb-ot-tag
     set (prog_name ${prog})
     if (${prog_name} STREQUAL "test")
       # test can not be used as a valid executable name on cmake, lets special case it
@@ -833,7 +779,7 @@
     add_executable(${prog_name} ${PROJECT_SOURCE_DIR}/src/${prog}.cc)
     target_link_libraries(${prog_name} harfbuzz ${THIRD_PARTY_LIBS})
   endforeach ()
-  set_target_properties(hb-ot-tag PROPERTIES COMPILE_FLAGS "-DMAIN")
+  # set_target_properties(hb-ot-tag PROPERTIES COMPILE_FLAGS "-DMAIN")
 
   ## Tests
   if (UNIX OR MINGW)
diff --git a/appveyor.yml b/appveyor.yml
index 236bb1b..6daf8d2 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -73,7 +73,7 @@
   - 'if "%compiler%"=="msvc2" cmake --build build --config %configuration%'
 
   - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "curl https://raw.githubusercontent.com/mirror/mingw-w64/023eb04c396d4e8d8fcf604cfababc53dae13398/mingw-w64-headers/include/dwrite_1.h > %MINGW_PREFIX%/%MINGW_CHOST%/include/dwrite_1.h"'
-  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER; PATH=$PATH:/mingw64/bin:/mingw32/bin; ./autogen.sh --with-uniscribe --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 --with-directwrite --build=%MINGW_CHOST% --host=%MINGW_CHOST% --prefix=%MINGW_PREFIX%; make -j3 check || .ci/fail.sh"'
+  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER; PATH=$PATH:/mingw64/bin:/mingw32/bin; ./autogen.sh --with-uniscribe --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 --with-directwrite --with-gdi --build=%MINGW_CHOST% --host=%MINGW_CHOST% --prefix=%MINGW_PREFIX%; make -j3 check || .ci/fail.sh"'
 
 cache:
   - c:\tools\vcpkg\installed\
diff --git a/configure.ac b/configure.ac
index 09ce4e6..cebb10a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -361,6 +361,28 @@
 
 dnl ===========================================================================
 
+AC_ARG_WITH(gdi,
+	[AS_HELP_STRING([--with-gdi=@<:@yes/no/auto@:>@],
+			[Provide GDI integration helpers @<:@default=no@:>@])],,
+	[with_gdi=no])
+have_gdi=false
+if test "x$with_gdi" = "xyes" -o "x$with_gdi" = "xauto"; then
+	AC_CHECK_HEADERS(windows.h, have_gdi=true)
+fi
+if test "x$with_gdi" = "xyes" -a "x$have_gdi" != "xtrue"; then
+	AC_MSG_ERROR([gdi support requested but not found])
+fi
+if $have_gdi; then
+	GDI_CFLAGS=
+	GDI_LIBS="-lgdi32"
+	AC_SUBST(GDI_CFLAGS)
+	AC_SUBST(GDI_LIBS)
+	AC_DEFINE(HAVE_GDI, 1, [Have GDI library])
+fi
+AM_CONDITIONAL(HAVE_GDI, $have_gdi)
+
+dnl ===========================================================================
+
 AC_ARG_WITH(directwrite,
 	[AS_HELP_STRING([--with-directwrite=@<:@yes/no/auto@:>@],
 			[Use the DirectWrite library (experimental) @<:@default=no@:>@])],,
@@ -510,6 +532,7 @@
 Platform shapers (not normally needed):
 	CoreText:		${have_coretext}
 	DirectWrite:		${have_directwrite}
+	GDI:			${have_gdi}
 	Uniscribe:		${have_uniscribe}
 
 Other features:
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index 4be248d..99916eb 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -1,6 +1,7 @@
 <SUBSECTION Private>
 HB_H_IN
 HB_OT_H_IN
+HB_AAT_H_IN
 </SECTION>
 
 <SECTION>
@@ -179,6 +180,7 @@
 HB_SCRIPT_CANADIAN_ABORIGINAL
 hb_font_funcs_set_glyph_func
 hb_font_get_glyph_func_t
+HB_MATH_GLYPH_PART_FLAG_EXTENDER
 hb_ot_layout_table_choose_script
 hb_ot_layout_table_find_script
 hb_ot_tag_from_language
@@ -368,6 +370,11 @@
 </SECTION>
 
 <SECTION>
+<FILE>hb-gdi</FILE>
+hb_gdi_face_create
+</SECTION>
+
+<SECTION>
 <FILE>hb-glib</FILE>
 hb_glib_get_unicode_funcs
 hb_glib_script_from_script
@@ -601,6 +608,22 @@
 </SECTION>
 
 <SECTION>
+<FILE>hb-ot-meta</FILE>
+hb_ot_meta_t
+hb_ot_meta_get_entries
+hb_ot_meta_reference_entry
+</SECTION>
+
+<SECTION>
+<FILE>hb-ot-metrics</FILE>
+hb_ot_metrics_t
+hb_ot_metrics_get_position
+hb_ot_metrics_get_variation
+hb_ot_metrics_get_x_variation
+hb_ot_metrics_get_y_variation
+</SECTION>
+
+<SECTION>
 <FILE>hb-ot-shape</FILE>
 hb_ot_shape_glyphs_closure
 </SECTION>
diff --git a/docs/usermanual-clusters.xml b/docs/usermanual-clusters.xml
index a7b1658..9147ff0 100644
--- a/docs/usermanual-clusters.xml
+++ b/docs/usermanual-clusters.xml
@@ -156,14 +156,16 @@
       order.
     </para>
     <para>
-      For left-to-right scripts (LTR) and top-to-bottom scripts (TTB),
+      For buffers in the left-to-right (LTR)
+      or top-to-bottom (TTB) text flow direction,
       HarfBuzz will preserve the monotonic property: client programs
       are guaranteed that monotonically increasing initial cluster
       values will be returned as monotonically increasing final
       cluster values.
     </para>
     <para>
-      For right-to-left scripts (RTL) and bottom-to-top scripts (BTT),
+      For buffers in the right-to-left (RTL)
+      or bottom-to-top (BTT) text flow direction,
       the directionality of the buffer itself is reversed for final
       output as a matter of design. Therefore, HarfBuzz inverts the
       monotonic property: client programs are guaranteed that
diff --git a/docs/usermanual-fonts-and-faces.xml b/docs/usermanual-fonts-and-faces.xml
index b87f0e5..1258bec 100644
--- a/docs/usermanual-fonts-and-faces.xml
+++ b/docs/usermanual-fonts-and-faces.xml
@@ -86,7 +86,7 @@
       objects. <function>hb_font_set_ppem(font, x_ppem,
       y_ppem)</function> sets the pixels-per-EM value of the font. You
       can also set the point size of the font with
-      <function>hb_font_set_ppem(font, ptem)</function>. HarfBuzz uses the
+      <function>hb_font_set_ptem(font, ptem)</function>. HarfBuzz uses the
       industry standard 72 points per inch.
     </para>
     <para>
diff --git a/src/Makefile.am b/src/Makefile.am
index d4ba39a..7173f4b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,6 +12,8 @@
 TESTS =
 check_PROGRAMS =
 
+EXTRA_DIST += harfbuzz.cc
+
 # Convenience targets:
 lib: $(BUILT_SOURCES) libharfbuzz.la
 libs: $(BUILT_SOURCES) $(lib_LTLIBRARIES)
@@ -80,6 +82,13 @@
 HBHEADERS += $(HB_DIRECTWRITE_headers)
 endif
 
+if HAVE_GDI
+HBCFLAGS += $(GDI_CXXFLAGS)
+HBNONPCLIBS += $(GDI_LIBS)
+HBSOURCES += $(HB_GDI_sources)
+HBHEADERS += $(HB_GDI_headers)
+endif
+
 if HAVE_CORETEXT
 HBCFLAGS += $(CORETEXT_CFLAGS)
 HBNONPCLIBS += $(CORETEXT_LIBS)
@@ -253,31 +262,38 @@
 	$(NULL)
 EXTRA_DIST += $(GENERATORS)
 
-unicode-tables: arabic-table indic-table tag-table use-table emoji-table
+unicode-tables: \
+	arabic-table \
+	emoji-table \
+	indic-table \
+	tag-table \
+	ucd-table \
+	use-table \
+	emoji-table \
+	$(NULL)
 
 arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-arabic-table.hh \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-arabic-table.hh; false)
-
+emoji-table: gen-emoji-table.py emoji-data.txt
+	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-unicode-emoji-table.hh \
+	|| ($(RM) $(srcdir)/hb-unicode-emoji-table.hh; false)
 indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-indic-table.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-indic-table.cc; false)
-
 tag-table: gen-tag-table.py languagetags language-subtag-registry
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-tag-table.hh \
 	|| ($(RM) $(srcdir)/hb-ot-tag-table.hh; false)
-
+ucd-table: gen-ucd-table.py ucd.nounihan.grouped.zip hb-common.h
+	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ucd-table.hh \
+	|| ($(RM) $(srcdir)/hb-ucd-table.hh; false)
 use-table: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-use-table.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-use-table.cc; false)
-
 vowel-constraints: gen-vowel-constraints.py HBIndicVowelConstraints.txt Scripts.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc; false)
 
-emoji-table: gen-emoji-table.py emoji-data.txt
-	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-unicode-emoji-table.hh \
-	|| ($(RM) $(srcdir)/hb-unicode-emoji-table.hh; false)
 
 built-sources: $(BUILT_SOURCES)
 
@@ -296,10 +312,27 @@
 	$(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \
 	|| ($(RM) "$@"; false)
 
+harfbuzz.cc: Makefile.sources
+	$(AM_V_GEN) \
+	for f in \
+		$(HB_BASE_sources) \
+		$(HB_GLIB_sources) \
+		$(HB_FT_sources) \
+		$(HB_GRAPHITE2_sources) \
+		$(HB_UNISCRIBE_sources) \
+		$(HB_GDI_sources) \
+		$(HB_DIRECTWRITE_sources) \
+		$(HB_CORETEXT_sources) \
+		; do echo '#include "'$$f'"'; done | \
+	grep '[.]cc"' > $(srcdir)/harfbuzz.cc \
+	|| ($(RM) $(srcdir)/harfbuzz.cc; false)
+BUILT_SOURCES += harfbuzz.cc
+
 noinst_PROGRAMS = \
 	main \
 	test \
 	test-buffer-serialize \
+	test-ot-meta \
 	test-ot-name \
 	test-gpos-size-params \
 	test-gsub-would-substitute \
@@ -318,6 +351,10 @@
 test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
 test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
 
+test_ot_meta_SOURCES = test-ot-meta.cc
+test_ot_meta_CPPFLAGS = $(HBCFLAGS)
+test_ot_meta_LDADD = libharfbuzz.la $(HBLIBS)
+
 test_ot_name_SOURCES = test-ot-name.cc
 test_ot_name_CPPFLAGS = $(HBCFLAGS)
 test_ot_name_LDADD = libharfbuzz.la $(HBLIBS)
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 6f42ba3..a8369bf 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -88,6 +88,10 @@
 	hb-ot-math-table.hh \
 	hb-ot-math.cc \
 	hb-ot-maxp-table.hh \
+	hb-ot-meta-table.hh \
+	hb-ot-meta.cc \
+	hb-ot-metrics.cc \
+	hb-ot-metrics.hh \
 	hb-ot-name-language-static.hh \
 	hb-ot-name-language.hh \
 	hb-ot-name-table.hh \
@@ -191,6 +195,8 @@
 	hb-ot-font.h \
 	hb-ot-layout.h \
 	hb-ot-math.h \
+	hb-ot-meta.h \
+	hb-ot-metrics.h \
 	hb-ot-name.h \
 	hb-ot-shape.h \
 	hb-ot-var.h \
@@ -222,6 +228,9 @@
 HB_DIRECTWRITE_sources = hb-directwrite.cc
 HB_DIRECTWRITE_headers = hb-directwrite.h
 
+HB_GDI_sources = hb-gdi.cc
+HB_GDI_headers = hb-gdi.h
+
 HB_UNISCRIBE_sources = hb-uniscribe.cc
 HB_UNISCRIBE_headers = hb-uniscribe.h
 
diff --git a/src/gen-ucd-table.py b/src/gen-ucd-table.py
index a152375..552c3c6 100755
--- a/src/gen-ucd-table.py
+++ b/src/gen-ucd-table.py
@@ -6,8 +6,8 @@
 import logging
 logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
 
-if len (sys.argv) != 2:
-	print("usage: ./gen-ucd-table ucd.nounihan.grouped.xml", file=sys.stderr)
+if len (sys.argv) not in (2, 3):
+	print("usage: ./gen-ucd-table ucd.nounihan.grouped.xml [/path/to/hb-common.h]", file=sys.stderr)
 	sys.exit(1)
 
 # https://github.com/harfbuzz/packtab
@@ -18,6 +18,8 @@
 ucdxml = packTab.ucdxml.load_ucdxml(sys.argv[1])
 ucd = packTab.ucdxml.ucdxml_get_repertoire(ucdxml)
 
+hb_common_h = 'hb-common.h' if len (sys.argv) < 3 else sys.argv[2]
+
 logging.info('Preparing data tables...')
 
 gc = [u['gc'] for u in ucd]
@@ -68,7 +70,7 @@
 sc_order = dict()
 sc_array = []
 sc_re = re.compile(r"\b(HB_SCRIPT_[_A-Z]*).*HB_TAG [(]'(.)','(.)','(.)','(.)'[)]")
-for line in open('hb-common.h'):
+for line in open(hb_common_h):
     m = sc_re.search (line)
     if not m: continue
     name = m.group(1)
diff --git a/src/harfbuzz.cc b/src/harfbuzz.cc
new file mode 100644
index 0000000..d338d17
--- /dev/null
+++ b/src/harfbuzz.cc
@@ -0,0 +1,53 @@
+#include "hb-aat-layout.cc"
+#include "hb-aat-map.cc"
+#include "hb-blob.cc"
+#include "hb-buffer-serialize.cc"
+#include "hb-buffer.cc"
+#include "hb-common.cc"
+#include "hb-face.cc"
+#include "hb-fallback-shape.cc"
+#include "hb-font.cc"
+#include "hb-map.cc"
+#include "hb-ot-cff1-table.cc"
+#include "hb-ot-cff2-table.cc"
+#include "hb-ot-color.cc"
+#include "hb-ot-face.cc"
+#include "hb-ot-font.cc"
+#include "hb-ot-layout.cc"
+#include "hb-ot-map.cc"
+#include "hb-ot-math.cc"
+#include "hb-ot-meta.cc"
+#include "hb-ot-metrics.cc"
+#include "hb-ot-name.cc"
+#include "hb-ot-shape-complex-arabic.cc"
+#include "hb-ot-shape-complex-default.cc"
+#include "hb-ot-shape-complex-hangul.cc"
+#include "hb-ot-shape-complex-hebrew.cc"
+#include "hb-ot-shape-complex-indic-table.cc"
+#include "hb-ot-shape-complex-indic.cc"
+#include "hb-ot-shape-complex-khmer.cc"
+#include "hb-ot-shape-complex-myanmar.cc"
+#include "hb-ot-shape-complex-thai.cc"
+#include "hb-ot-shape-complex-use-table.cc"
+#include "hb-ot-shape-complex-use.cc"
+#include "hb-ot-shape-complex-vowel-constraints.cc"
+#include "hb-ot-shape-fallback.cc"
+#include "hb-ot-shape-normalize.cc"
+#include "hb-ot-shape.cc"
+#include "hb-ot-tag.cc"
+#include "hb-ot-var.cc"
+#include "hb-set.cc"
+#include "hb-shape-plan.cc"
+#include "hb-shape.cc"
+#include "hb-shaper.cc"
+#include "hb-static.cc"
+#include "hb-ucd.cc"
+#include "hb-unicode.cc"
+#include "hb-warning.cc"
+#include "hb-glib.cc"
+#include "hb-ft.cc"
+#include "hb-graphite2.cc"
+#include "hb-uniscribe.cc"
+#include "hb-gdi.cc"
+#include "hb-directwrite.cc"
+#include "hb-coretext.cc"
diff --git a/src/hb-aat-layout-feat-table.hh b/src/hb-aat-layout-feat-table.hh
index a20ef86..2345f21 100644
--- a/src/hb-aat-layout-feat-table.hh
+++ b/src/hb-aat-layout-feat-table.hh
@@ -174,9 +174,7 @@
   }
 
   const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const
-  {
-    return namesZ.bsearch (featureNameCount, feature_type);
-  }
+  { return namesZ.bsearch (featureNameCount, feature_type); }
 
   hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const
   { return get_feature (feature).get_feature_name_id (); }
diff --git a/src/hb-aat-layout.h b/src/hb-aat-layout.h
index 760aaae..b617e8b 100644
--- a/src/hb-aat-layout.h
+++ b/src/hb-aat-layout.h
@@ -85,7 +85,7 @@
   HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE			= 39,
   HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE		= 103,
 
-  _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
+  _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
 } hb_aat_layout_feature_type_t;
 
 /**
@@ -424,7 +424,7 @@
   HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN		= 2,
   HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN		= 3,
 
-  _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
+  _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
 } hb_aat_layout_feature_selector_t;
 
 HB_EXTERN unsigned int
diff --git a/src/hb-algs.hh b/src/hb-algs.hh
index c8d83c8..7886894 100644
--- a/src/hb-algs.hh
+++ b/src/hb-algs.hh
@@ -50,31 +50,31 @@
 struct
 {
   /* Note.  This is dangerous in that if it's passed an rvalue, it returns rvalue-reference. */
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (T&& v) const HB_AUTO_RETURN ( hb_forward<T> (v) )
 }
 HB_FUNCOBJ (hb_identity);
 struct
 {
   /* Like identity(), but only retains lvalue-references.  Rvalues are returned as rvalues. */
-  template <typename T> T&
+  template <typename T> constexpr T&
   operator () (T& v) const { return v; }
 
-  template <typename T> hb_remove_reference<T>
+  template <typename T> constexpr hb_remove_reference<T>
   operator () (T&& v) const { return v; }
 }
 HB_FUNCOBJ (hb_lidentity);
 struct
 {
   /* Like identity(), but always returns rvalue. */
-  template <typename T> hb_remove_reference<T>
+  template <typename T> constexpr hb_remove_reference<T>
   operator () (T&& v) const { return v; }
 }
 HB_FUNCOBJ (hb_ridentity);
 
 struct
 {
-  template <typename T> bool
+  template <typename T> constexpr bool
   operator () (T&& v) const { return bool (hb_forward<T> (v)); }
 }
 HB_FUNCOBJ (hb_bool);
@@ -82,11 +82,11 @@
 struct
 {
   private:
-  template <typename T> auto
+  template <typename T> constexpr auto
   impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
 
   template <typename T,
-	    hb_enable_if (hb_is_integral (T))> auto
+	    hb_enable_if (hb_is_integral (T))> constexpr auto
   impl (const T& v, hb_priority<0>) const HB_AUTO_RETURN
   (
     /* Knuth's multiplicative method: */
@@ -95,7 +95,7 @@
 
   public:
 
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (const T& v) const HB_RETURN (uint32_t, impl (v, hb_prioritize))
 }
 HB_FUNCOBJ (hb_hash);
@@ -328,14 +328,14 @@
 
 struct
 {
-  template <typename Pair> typename Pair::first_t
+  template <typename Pair> constexpr typename Pair::first_t
   operator () (const Pair& pair) const { return pair.first; }
 }
 HB_FUNCOBJ (hb_first);
 
 struct
 {
-  template <typename Pair> typename Pair::second_t
+  template <typename Pair> constexpr typename Pair::second_t
   operator () (const Pair& pair) const { return pair.second; }
 }
 HB_FUNCOBJ (hb_second);
@@ -346,14 +346,14 @@
  * comparing integers of different signedness. */
 struct
 {
-  template <typename T, typename T2> auto
+  template <typename T, typename T2> constexpr auto
   operator () (T&& a, T2&& b) const HB_AUTO_RETURN
   (hb_forward<T> (a) <= hb_forward<T2> (b) ? hb_forward<T> (a) : hb_forward<T2> (b))
 }
 HB_FUNCOBJ (hb_min);
 struct
 {
-  template <typename T, typename T2> auto
+  template <typename T, typename T2> constexpr auto
   operator () (T&& a, T2&& b) const HB_AUTO_RETURN
   (hb_forward<T> (a) >= hb_forward<T2> (b) ? hb_forward<T> (a) : hb_forward<T2> (b))
 }
@@ -917,7 +917,7 @@
 { HB_PARTIALIZE(2);
   static constexpr bool passthru_left = false;
   static constexpr bool passthru_right = false;
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & b)
 }
 HB_FUNCOBJ (hb_bitwise_and);
@@ -925,7 +925,7 @@
 { HB_PARTIALIZE(2);
   static constexpr bool passthru_left = true;
   static constexpr bool passthru_right = true;
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | b)
 }
 HB_FUNCOBJ (hb_bitwise_or);
@@ -933,7 +933,7 @@
 { HB_PARTIALIZE(2);
   static constexpr bool passthru_left = true;
   static constexpr bool passthru_right = true;
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (const T &a, const T &b) const HB_AUTO_RETURN (a ^ b)
 }
 HB_FUNCOBJ (hb_bitwise_xor);
@@ -941,56 +941,56 @@
 { HB_PARTIALIZE(2);
   static constexpr bool passthru_left = true;
   static constexpr bool passthru_right = false;
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & ~b)
 }
 HB_FUNCOBJ (hb_bitwise_sub);
 struct
 {
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (const T &a) const HB_AUTO_RETURN (~a)
 }
 HB_FUNCOBJ (hb_bitwise_neg);
 
 struct
 { HB_PARTIALIZE(2);
-  template <typename T, typename T2> auto
+  template <typename T, typename T2> constexpr auto
   operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a + b)
 }
 HB_FUNCOBJ (hb_add);
 struct
 { HB_PARTIALIZE(2);
-  template <typename T, typename T2> auto
+  template <typename T, typename T2> constexpr auto
   operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a - b)
 }
 HB_FUNCOBJ (hb_sub);
 struct
 { HB_PARTIALIZE(2);
-  template <typename T, typename T2> auto
+  template <typename T, typename T2> constexpr auto
   operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a * b)
 }
 HB_FUNCOBJ (hb_mul);
 struct
 { HB_PARTIALIZE(2);
-  template <typename T, typename T2> auto
+  template <typename T, typename T2> constexpr auto
   operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a / b)
 }
 HB_FUNCOBJ (hb_div);
 struct
 { HB_PARTIALIZE(2);
-  template <typename T, typename T2> auto
+  template <typename T, typename T2> constexpr auto
   operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a % b)
 }
 HB_FUNCOBJ (hb_mod);
 struct
 {
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (const T &a) const HB_AUTO_RETURN (+a)
 }
 HB_FUNCOBJ (hb_pos);
 struct
 {
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (const T &a) const HB_AUTO_RETURN (-a)
 }
 HB_FUNCOBJ (hb_neg);
diff --git a/src/hb-cff-interp-common.hh b/src/hb-cff-interp-common.hh
index 948772b..fdc5c68 100644
--- a/src/hb-cff-interp-common.hh
+++ b/src/hb-cff-interp-common.hh
@@ -378,7 +378,7 @@
 
 /* stack */
 template <typename ELEM, int LIMIT>
-struct stack_t
+struct cff_stack_t
 {
   void init ()
   {
@@ -469,7 +469,7 @@
 
 /* argument stack */
 template <typename ARG=number_t>
-struct arg_stack_t : stack_t<ARG, 513>
+struct arg_stack_t : cff_stack_t<ARG, 513>
 {
   void push_int (int v)
   {
@@ -523,7 +523,7 @@
   { return S::elements.sub_array (start); }
 
   private:
-  typedef stack_t<ARG, 513> S;
+  typedef cff_stack_t<ARG, 513> S;
 };
 
 /* an operator prefixed by its operands in a byte string */
diff --git a/src/hb-cff-interp-cs-common.hh b/src/hb-cff-interp-cs-common.hh
index cf9ce4d..d9ad4d0 100644
--- a/src/hb-cff-interp-cs-common.hh
+++ b/src/hb-cff-interp-cs-common.hh
@@ -57,7 +57,7 @@
 
 /* call stack */
 const unsigned int kMaxCallLimit = 10;
-struct call_stack_t : stack_t<call_context_t, kMaxCallLimit> {};
+struct call_stack_t : cff_stack_t<call_context_t, kMaxCallLimit> {};
 
 template <typename SUBRS>
 struct biased_subrs_t
diff --git a/src/hb-config.hh b/src/hb-config.hh
index adf2b33..b6db206 100644
--- a/src/hb-config.hh
+++ b/src/hb-config.hh
@@ -66,6 +66,8 @@
 #define HB_NO_LAYOUT_COLLECT_GLYPHS
 #define HB_NO_LAYOUT_UNUSED
 #define HB_NO_MATH
+#define HB_NO_META
+#define HB_NO_METRICS
 #define HB_NO_MMAP
 #define HB_NO_NAME
 #define HB_NO_OPEN
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc
index a57b970..e8a4fd5 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -75,7 +75,7 @@
 }
 
 static hb_blob_t *
-reference_table  (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+_hb_cg_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
 {
   CGFontRef cg_font = reinterpret_cast<CGFontRef> (user_data);
   CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag);
@@ -299,7 +299,7 @@
 hb_face_t *
 hb_coretext_face_create (CGFontRef cg_font)
 {
-  return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), _hb_cg_font_release);
+  return hb_face_create_for_tables (_hb_cg_reference_table, CGFontRetain (cg_font), _hb_cg_font_release);
 }
 
 /*
diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc
index a625763..c14e9d2 100644
--- a/src/hb-directwrite.cc
+++ b/src/hb-directwrite.cc
@@ -539,11 +539,6 @@
   Run  mRunHead;
 };
 
-static inline uint16_t hb_uint16_swap (const uint16_t v)
-{ return (v >> 8) | (v << 8); }
-static inline uint32_t hb_uint32_swap (const uint32_t v)
-{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
-
 /*
  * shaper
  */
@@ -934,7 +929,7 @@
 }
 
 static hb_blob_t *
-reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+_hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
 {
   IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data);
   const void *data;
@@ -979,7 +974,7 @@
 {
   if (font_face)
     font_face->AddRef ();
-  return hb_face_create_for_tables (reference_table, font_face,
+  return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face,
 				    _hb_directwrite_font_release);
 }
 
diff --git a/src/hb-font.cc b/src/hb-font.cc
index f450d88..9cd5011 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -1300,6 +1300,8 @@
 
   1000, /* x_scale */
   1000, /* y_scale */
+  1<<16, /* x_mult */
+  1<<16, /* y_mult */
 
   0, /* x_ppem */
   0, /* y_ppem */
@@ -1330,6 +1332,7 @@
   font->klass = hb_font_funcs_get_empty ();
   font->data.init0 (font);
   font->x_scale = font->y_scale = hb_face_get_upem (face);
+  font->x_mult = font->y_mult = 1 << 16;
 
   return font;
 }
@@ -1601,7 +1604,9 @@
 
   hb_face_t *old = font->face;
 
+  hb_face_make_immutable (face);
   font->face = hb_face_reference (face);
+  font->mults_changed ();
 
   hb_face_destroy (old);
 }
@@ -1711,6 +1716,7 @@
 
   font->x_scale = x_scale;
   font->y_scale = y_scale;
+  font->mults_changed ();
 }
 
 /**
diff --git a/src/hb-font.hh b/src/hb-font.hh
index 3b917d1..4adf6ae 100644
--- a/src/hb-font.hh
+++ b/src/hb-font.hh
@@ -107,8 +107,10 @@
   hb_font_t *parent;
   hb_face_t *face;
 
-  int x_scale;
-  int y_scale;
+  int32_t x_scale;
+  int32_t y_scale;
+  int64_t x_mult;
+  int64_t y_mult;
 
   unsigned int x_ppem;
   unsigned int y_ppem;
@@ -127,16 +129,16 @@
 
 
   /* Convert from font-space to user-space */
-  int dir_scale (hb_direction_t direction)
-  { return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; }
-  hb_position_t em_scale_x (int16_t v) { return em_scale (v, x_scale); }
-  hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); }
-  hb_position_t em_scalef_x (float v) { return em_scalef (v, this->x_scale); }
-  hb_position_t em_scalef_y (float v) { return em_scalef (v, this->y_scale); }
+  int64_t dir_mult (hb_direction_t direction)
+  { return HB_DIRECTION_IS_VERTICAL(direction) ? y_mult : x_mult; }
+  hb_position_t em_scale_x (int16_t v) { return em_mult (v, x_mult); }
+  hb_position_t em_scale_y (int16_t v) { return em_mult (v, y_mult); }
+  hb_position_t em_scalef_x (float v) { return em_scalef (v, x_scale); }
+  hb_position_t em_scalef_y (float v) { return em_scalef (v, y_scale); }
   float em_fscale_x (int16_t v) { return em_fscale (v, x_scale); }
   float em_fscale_y (int16_t v) { return em_fscale (v, y_scale); }
   hb_position_t em_scale_dir (int16_t v, hb_direction_t direction)
-  { return em_scale (v, dir_scale (direction)); }
+  { return em_mult (v, dir_mult (direction)); }
 
   /* Convert from parent-font user-space to our user-space */
   hb_position_t parent_scale_x_distance (hb_position_t v)
@@ -607,12 +609,16 @@
     return false;
   }
 
-  hb_position_t em_scale (int16_t v, int scale)
+  void mults_changed ()
   {
-    int upem = face->get_upem ();
-    int64_t scaled = v * (int64_t) scale;
-    scaled += scaled >= 0 ? upem/2 : -upem/2; /* Round. */
-    return (hb_position_t) (scaled / upem);
+    signed upem = face->get_upem ();
+    x_mult = ((int64_t) x_scale << 16) / upem;
+    y_mult = ((int64_t) y_scale << 16) / upem;
+  }
+
+  hb_position_t em_mult (int16_t v, int64_t mult)
+  {
+    return (hb_position_t) ((v * mult) >> 16);
   }
   hb_position_t em_scalef (float v, int scale)
   { return (hb_position_t) roundf (v * scale / face->get_upem ()); }
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index c01f029..2d7f2b9 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -564,7 +564,7 @@
 
 
 static hb_blob_t *
-reference_table  (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+_hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
 {
   FT_Face ft_face = (FT_Face) user_data;
   FT_Byte *buffer;
@@ -619,7 +619,7 @@
     face = hb_face_create (blob, ft_face->face_index);
     hb_blob_destroy (blob);
   } else {
-    face = hb_face_create_for_tables (reference_table, ft_face, destroy);
+    face = hb_face_create_for_tables (_hb_ft_reference_table, ft_face, destroy);
   }
 
   hb_face_set_index (face, ft_face->face_index);
diff --git a/src/hb-gdi.cc b/src/hb-gdi.cc
new file mode 100644
index 0000000..526f1cd
--- /dev/null
+++ b/src/hb-gdi.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb.hh"
+
+#ifdef HAVE_GDI
+
+#include "hb-gdi.h"
+
+static hb_blob_t *
+_hb_gdi_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+{
+  char *buffer = nullptr;
+  DWORD length = 0;
+
+  HDC hdc = GetDC (nullptr);
+  if (unlikely (!SelectObject (hdc, (HFONT) user_data))) goto fail;
+
+  length = GetFontData (hdc, hb_uint32_swap (tag), 0, buffer, length);
+  if (unlikely (length == GDI_ERROR)) goto fail_with_releasedc;
+
+  buffer = (char *) malloc (length);
+  if (unlikely (!buffer)) goto fail_with_releasedc;
+  length = GetFontData (hdc, hb_uint32_swap (tag), 0, buffer, length);
+  if (unlikely (length == GDI_ERROR)) goto fail_with_releasedc_and_free;
+  ReleaseDC (nullptr, hdc);
+
+  return hb_blob_create ((const char *) buffer, length, HB_MEMORY_MODE_WRITABLE, buffer, free);
+
+fail_with_releasedc_and_free:
+  free (buffer);
+fail_with_releasedc:
+  ReleaseDC (nullptr, hdc);
+fail:
+  return hb_blob_get_empty ();
+}
+
+/**
+ * hb_gdi_face_create:
+ * @hdc: a HFONT object.
+ *
+ * Return value: #hb_face_t object corresponding to the given input
+ *
+ * Since: REPLACEME
+ **/
+hb_face_t *
+hb_gdi_face_create (HFONT hfont)
+{
+  return hb_face_create_for_tables (_hb_gdi_reference_table, (void *) hfont, nullptr);
+}
+
+#endif
diff --git a/src/hb-gdi.h b/src/hb-gdi.h
new file mode 100644
index 0000000..68cc439
--- /dev/null
+++ b/src/hb-gdi.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_GDI_H
+#define HB_GDI_H
+
+#include "hb.h"
+
+#include <windows.h>
+
+HB_BEGIN_DECLS
+
+HB_EXTERN hb_face_t *
+hb_gdi_face_create (HFONT hfont);
+
+HB_END_DECLS
+
+#endif /* HB_GDI_H */
diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index 9588fa4..40ac906 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -106,32 +106,6 @@
   return d;
 }
 
-static void hb_graphite2_release_table(const void *data, const void *table_buffer)
-{
-  hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data;
-  hb_graphite2_tablelist_t *tlist = face_data->tlist;
-
-  hb_graphite2_tablelist_t *prev = nullptr;
-  hb_graphite2_tablelist_t *curr = tlist;
-  while (curr)
-  {
-    if (hb_blob_get_data(curr->blob, nullptr) == table_buffer)
-    {
-      if (prev == nullptr)
-        face_data->tlist.cmpexch(tlist, curr->next);
-      else
-        prev->next = curr->next;
-      hb_blob_destroy(curr->blob);
-      free(curr);
-      break;
-    }
-    prev = curr;
-    curr = curr->next;
-  }
-}
-
-static gr_face_ops hb_graphite2_face_ops = { sizeof(gr_face_ops), hb_graphite2_get_table, hb_graphite2_release_table };
-
 hb_graphite2_face_data_t *
 _hb_graphite2_shaper_face_data_create (hb_face_t *face)
 {
@@ -150,7 +124,7 @@
     return nullptr;
 
   data->face = face;
-  data->grface = gr_make_face_with_ops (data, &hb_graphite2_face_ops, gr_face_preloadAll);
+  data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll);
 
   if (unlikely (!data->grface)) {
     free (data);
diff --git a/src/hb-iter.hh b/src/hb-iter.hh
index 5df4333..8d2ff80 100644
--- a/src/hb-iter.hh
+++ b/src/hb-iter.hh
@@ -480,7 +480,7 @@
 
   template <typename Iter,
 	    hb_requires (hb_is_iterator (Iter)),
-	    typename AccuT = decltype (hb_declval (Redu) (hb_declval (InitT), hb_declval (typename Iter::item_t)))>
+	    typename AccuT = hb_decay<decltype (hb_declval (Redu) (hb_declval (InitT), hb_declval (typename Iter::item_t)))>>
   AccuT
   operator () (Iter it)
   {
diff --git a/src/hb-meta.hh b/src/hb-meta.hh
index df8ebd1..2dfaeb7 100644
--- a/src/hb-meta.hh
+++ b/src/hb-meta.hh
@@ -80,8 +80,8 @@
 
 struct
 {
-  template <typename T>
-  T* operator () (T& arg) const
+  template <typename T> constexpr T*
+  operator () (T& arg) const
   {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wcast-align"
@@ -171,29 +171,29 @@
 /* std::move and std::forward */
 
 template <typename T>
-static hb_remove_reference<T>&& hb_move (T&& t) { return (hb_remove_reference<T>&&) (t); }
+static constexpr hb_remove_reference<T>&& hb_move (T&& t) { return (hb_remove_reference<T>&&) (t); }
 
 template <typename T>
-static T&& hb_forward (hb_remove_reference<T>& t) { return (T&&) t; }
+static constexpr T&& hb_forward (hb_remove_reference<T>& t) { return (T&&) t; }
 template <typename T>
-static T&& hb_forward (hb_remove_reference<T>&& t) { return (T&&) t; }
+static constexpr T&& hb_forward (hb_remove_reference<T>&& t) { return (T&&) t; }
 
 struct
 {
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (T&& v) const HB_AUTO_RETURN (hb_forward<T> (v))
 
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (T *v) const HB_AUTO_RETURN (*v)
 }
 HB_FUNCOBJ (hb_deref);
 
 struct
 {
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (T&& v) const HB_AUTO_RETURN (hb_forward<T> (v))
 
-  template <typename T> auto
+  template <typename T> constexpr auto
   operator () (T& v) const HB_AUTO_RETURN (hb_addressof (v))
 }
 HB_FUNCOBJ (hb_ref);
diff --git a/src/hb-open-file.hh b/src/hb-open-file.hh
index 5318c7f..f3f4dc0 100644
--- a/src/hb-open-file.hh
+++ b/src/hb-open-file.hh
@@ -141,14 +141,15 @@
       TableRecord &rec = tables.arrayZ[i];
       hb_blob_t *blob = items[i].blob;
       rec.tag = items[i].tag;
-      rec.length = hb_blob_get_length (blob);
+      rec.length = blob->length;
       rec.offset.serialize (c, this);
 
       /* Allocate room for the table and copy it. */
       char *start = (char *) c->allocate_size<void> (rec.length);
-      if (unlikely (!start)) {return false;}
+      if (unlikely (!start)) return false;
 
-      memcpy (start, hb_blob_get_data (blob, nullptr), rec.length);
+      if (likely (rec.length))
+	memcpy (start, blob->data, rec.length);
 
       /* 4-byte alignment. */
       c->align (4);
diff --git a/src/hb-open-type.hh b/src/hb-open-type.hh
index e235a97..ad99575 100644
--- a/src/hb-open-type.hh
+++ b/src/hb-open-type.hh
@@ -576,13 +576,13 @@
   operator writer_t ()       { return writer (); }
 
   hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
   hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
   hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
   hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
 
   bool serialize (hb_serialize_context_t *c, unsigned int items_len)
   {
@@ -826,13 +826,13 @@
   operator writer_t ()       { return writer (); }
 
   hb_sorted_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
   hb_sorted_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
   hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
   hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
 
   bool serialize (hb_serialize_context_t *c, unsigned int items_len)
   {
diff --git a/src/hb-ot-cff1-table.cc b/src/hb-ot-cff1-table.cc
index 3238ad7..3e4fc20 100644
--- a/src/hb-ot-cff1-table.cc
+++ b/src/hb-ot-cff1-table.cc
@@ -210,7 +210,7 @@
   point_t max;
 };
 
-struct extents_param_t
+struct cff1_extents_param_t
 {
   void init (const OT::cff1::accelerator_t *_cff)
   {
@@ -229,15 +229,15 @@
   const OT::cff1::accelerator_t *cff;
 };
 
-struct cff1_path_procs_extents_t : path_procs_t<cff1_path_procs_extents_t, cff1_cs_interp_env_t, extents_param_t>
+struct cff1_path_procs_extents_t : path_procs_t<cff1_path_procs_extents_t, cff1_cs_interp_env_t, cff1_extents_param_t>
 {
-  static void moveto (cff1_cs_interp_env_t &env, extents_param_t& param, const point_t &pt)
+  static void moveto (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt)
   {
     param.end_path ();
     env.moveto (pt);
   }
 
-  static void line (cff1_cs_interp_env_t &env, extents_param_t& param, const point_t &pt1)
+  static void line (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1)
   {
     if (!param.is_path_open ())
     {
@@ -248,7 +248,7 @@
     param.bounds.update (env.get_pt ());
   }
 
-  static void curve (cff1_cs_interp_env_t &env, extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
+  static void curve (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
   {
     if (!param.is_path_open ())
     {
@@ -265,9 +265,9 @@
 
 static bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, bounds_t &bounds, bool in_seac=false);
 
-struct cff1_cs_opset_extents_t : cff1_cs_opset_t<cff1_cs_opset_extents_t, extents_param_t, cff1_path_procs_extents_t>
+struct cff1_cs_opset_extents_t : cff1_cs_opset_t<cff1_cs_opset_extents_t, cff1_extents_param_t, cff1_path_procs_extents_t>
 {
-  static void process_seac (cff1_cs_interp_env_t &env, extents_param_t& param)
+  static void process_seac (cff1_cs_interp_env_t &env, cff1_extents_param_t& param)
   {
     unsigned int  n = env.argStack.get_count ();
     point_t delta;
@@ -296,11 +296,11 @@
   if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false;
 
   unsigned int fd = cff->fdSelect->get_fd (glyph);
-  cff1_cs_interpreter_t<cff1_cs_opset_extents_t, extents_param_t> interp;
+  cff1_cs_interpreter_t<cff1_cs_opset_extents_t, cff1_extents_param_t> interp;
   const byte_str_t str = (*cff->charStrings)[glyph];
   interp.env.init (str, *cff, fd);
   interp.env.set_in_seac (in_seac);
-  extents_param_t  param;
+  cff1_extents_param_t  param;
   param.init (cff);
   if (unlikely (!interp.interpret (param))) return false;
   bounds = param.bounds;
diff --git a/src/hb-ot-cff2-table.cc b/src/hb-ot-cff2-table.cc
index 9c9e37b..33b51fe 100644
--- a/src/hb-ot-cff2-table.cc
+++ b/src/hb-ot-cff2-table.cc
@@ -33,7 +33,7 @@
 
 using namespace CFF;
 
-struct extents_param_t
+struct cff2_extents_param_t
 {
   void init ()
   {
@@ -63,15 +63,15 @@
   number_t max_y;
 };
 
-struct cff2_path_procs_extents_t : path_procs_t<cff2_path_procs_extents_t, cff2_cs_interp_env_t, extents_param_t>
+struct cff2_path_procs_extents_t : path_procs_t<cff2_path_procs_extents_t, cff2_cs_interp_env_t, cff2_extents_param_t>
 {
-  static void moveto (cff2_cs_interp_env_t &env, extents_param_t& param, const point_t &pt)
+  static void moveto (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt)
   {
     param.end_path ();
     env.moveto (pt);
   }
 
-  static void line (cff2_cs_interp_env_t &env, extents_param_t& param, const point_t &pt1)
+  static void line (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1)
   {
     if (!param.is_path_open ())
     {
@@ -82,7 +82,7 @@
     param.update_bounds (env.get_pt ());
   }
 
-  static void curve (cff2_cs_interp_env_t &env, extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
+  static void curve (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
   {
     if (!param.is_path_open ())
     {
@@ -97,7 +97,7 @@
   }
 };
 
-struct cff2_cs_opset_extents_t : cff2_cs_opset_t<cff2_cs_opset_extents_t, extents_param_t, cff2_path_procs_extents_t> {};
+struct cff2_cs_opset_extents_t : cff2_cs_opset_t<cff2_cs_opset_extents_t, cff2_extents_param_t, cff2_path_procs_extents_t> {};
 
 bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
 					   hb_codepoint_t glyph,
@@ -113,10 +113,10 @@
   unsigned int num_coords;
   const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
   unsigned int fd = fdSelect->get_fd (glyph);
-  cff2_cs_interpreter_t<cff2_cs_opset_extents_t, extents_param_t> interp;
+  cff2_cs_interpreter_t<cff2_cs_opset_extents_t, cff2_extents_param_t> interp;
   const byte_str_t str = (*charStrings)[glyph];
   interp.env.init (str, *this, fd, coords, num_coords);
-  extents_param_t  param;
+  cff2_extents_param_t  param;
   param.init ();
   if (unlikely (!interp.interpret (param))) return false;
 
diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index ac75bd9..d79b549 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -878,7 +878,7 @@
     cmap_plan->has_ms_bmp = find_subtable (3, 1);
     cmap_plan->has_ms_ucs4 = find_subtable (3, 10);
     cmap_plan->num_enc_records = cmap_plan->has_unicode_bmp + cmap_plan->has_unicode_ucs4 + cmap_plan->has_ms_bmp + cmap_plan->has_ms_ucs4;
-  
+
     if (unlikely (!CmapSubtableFormat4::create_sub_table_plan (plan, &cmap_plan->format4_segments)))
       return false;
 
@@ -979,6 +979,14 @@
       if (unlikely (!format12.serialize (&c, cmap_subset_plan.format12_groups)))
 	return false;
     }
+    else
+    {
+      // FIXME: Merge this with above or, remove and tweak #final_size
+      // and rebase all the tests expectations
+      HBUINT32 empty;
+      empty = 0;
+      for (unsigned int i = 0; i < 4; ++i) c.copy (empty);
+    }
 
     c.end_serialize ();
 
diff --git a/src/hb-ot-color-sbix-table.hh b/src/hb-ot-color-sbix-table.hh
index 9b725c4..8a915a6 100644
--- a/src/hb-ot-color-sbix-table.hh
+++ b/src/hb-ot-color-sbix-table.hh
@@ -235,9 +235,9 @@
       const PNGHeader &png = *blob->as<PNGHeader>();
 
       extents->x_bearing = x_offset;
-      extents->y_bearing = y_offset;
+      extents->y_bearing = png.IHDR.height + y_offset;
       extents->width     = png.IHDR.width;
-      extents->height    = png.IHDR.height;
+      extents->height    = -png.IHDR.height;
 
       /* Convert to font units. */
       if (strike_ppem)
diff --git a/src/hb-ot-face-table-list.hh b/src/hb-ot-face-table-list.hh
index ac70527..97fb765 100644
--- a/src/hb-ot-face-table-list.hh
+++ b/src/hb-ot-face-table-list.hh
@@ -50,9 +50,10 @@
 #if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT)
 HB_OT_ACCELERATOR (OT, cmap)
 #endif
+HB_OT_TABLE (OT, hhea)
 HB_OT_ACCELERATOR (OT, hmtx)
 HB_OT_TABLE (OT, OS2)
-#ifndef HB_NO_OT_FONT_GLYPH_NAMES
+#if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS)
 HB_OT_ACCELERATOR (OT, post)
 #endif
 #ifndef HB_NO_NAME
@@ -61,8 +62,12 @@
 #ifndef HB_NO_STAT
 HB_OT_TABLE (OT, STAT)
 #endif
+#ifndef HB_NO_META
+HB_OT_ACCELERATOR (OT, meta)
+#endif
 
 /* Vertical layout. */
+HB_OT_TABLE (OT, vhea)
 HB_OT_ACCELERATOR (OT, vmtx)
 
 /* TrueType outlines. */
diff --git a/src/hb-ot-face.cc b/src/hb-ot-face.cc
index f54d0b6..5ef8df4 100644
--- a/src/hb-ot-face.cc
+++ b/src/hb-ot-face.cc
@@ -32,6 +32,7 @@
 #include "hb-ot-cff2-table.hh"
 #include "hb-ot-hmtx-table.hh"
 #include "hb-ot-kern-table.hh"
+#include "hb-ot-meta-table.hh"
 #include "hb-ot-name-table.hh"
 #include "hb-ot-post-table.hh"
 #include "hb-ot-color-cbdt-table.hh"
diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc
index 999bfac..56e4685 100644
--- a/src/hb-ot-font.cc
+++ b/src/hb-ot-font.cc
@@ -230,32 +230,24 @@
 
 static hb_bool_t
 hb_ot_get_font_h_extents (hb_font_t *font,
-			  void *font_data,
+			  void *font_data HB_UNUSED,
 			  hb_font_extents_t *metrics,
 			  void *user_data HB_UNUSED)
 {
-  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
-  const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx;
-  metrics->ascender = font->em_scale_y (hmtx.ascender);
-  metrics->descender = font->em_scale_y (hmtx.descender);
-  metrics->line_gap = font->em_scale_y (hmtx.line_gap);
-  // TODO Hook up variations.
-  return hmtx.has_font_extents;
+  return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_HORIZONTAL_ASCENDER, &metrics->ascender) &&
+	 _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_HORIZONTAL_DESCENDER, &metrics->descender) &&
+	 _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_HORIZONTAL_LINE_GAP, &metrics->line_gap);
 }
 
 static hb_bool_t
 hb_ot_get_font_v_extents (hb_font_t *font,
-			  void *font_data,
+			  void *font_data HB_UNUSED,
 			  hb_font_extents_t *metrics,
 			  void *user_data HB_UNUSED)
 {
-  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
-  const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
-  metrics->ascender = font->em_scale_x (vmtx.ascender);
-  metrics->descender = font->em_scale_x (vmtx.descender);
-  metrics->line_gap = font->em_scale_x (vmtx.line_gap);
-  // TODO Hook up variations.
-  return vmtx.has_font_extents;
+  return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_VERTICAL_ASCENDER, &metrics->ascender) &&
+	 _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_VERTICAL_DESCENDER, &metrics->descender) &&
+	 _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_VERTICAL_LINE_GAP, &metrics->line_gap);
 }
 
 #if HB_USE_ATEXIT
diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh
index c3155b7..778b6c5 100644
--- a/src/hb-ot-hhea-table.hh
+++ b/src/hb-ot-hhea-table.hh
@@ -45,6 +45,8 @@
 template <typename T>
 struct _hea
 {
+  bool has_data () const { return version.major; }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index 754a376..11a588e 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -29,8 +29,8 @@
 
 #include "hb-open-type.hh"
 #include "hb-ot-hhea-table.hh"
-#include "hb-ot-os2-table.hh"
 #include "hb-ot-var-hvar-table.hh"
+#include "hb-ot-metrics.hh"
 
 /*
  * hmtx -- Horizontal Metrics
@@ -88,22 +88,22 @@
 
   template<typename Iterator,
            hb_requires (hb_is_iterator (Iterator))>
-  void serialize (hb_serialize_context_t *c, 
-                  Iterator it, 
+  void serialize (hb_serialize_context_t *c,
+                  Iterator it,
                   unsigned num_advances)
   {
     unsigned idx = 0;
     + it
     | hb_apply ([c, &idx, num_advances] (const hb_item_type<Iterator>& _)
                 {
-                  if (idx < num_advances) 
+                  if (idx < num_advances)
                   {
                     LongMetric lm;
                     lm.advance = _.first;
                     lm.sb = _.second;
                     if (unlikely (!c->embed<LongMetric> (&lm))) return;
-                  } 
-                  else 
+                  }
+                  else
                   {
                     FWORD *sb = c->allocate_size<FWORD> (FWORD::static_size);
                     if (unlikely (!sb)) return;
@@ -120,12 +120,12 @@
 
     T *table_prime = c->serializer->start_embed <T> ();
     if (unlikely (!table_prime)) return_trace (false);
-    
+
     accelerator_t _mtx;
     _mtx.init (c->plan->source);
     unsigned num_advances = _mtx.num_advances_for_subset (c->plan);
-    
-    auto it = 
+
+    auto it =
     + hb_range (c->plan->num_output_glyphs ())
     | hb_map ([c, &_mtx] (unsigned _)
 	{
@@ -162,28 +162,7 @@
     {
       default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);
 
-      bool got_font_extents = false;
-      if (T::os2Tag != HB_TAG_NONE && face->table.OS2->is_typo_metrics ())
-      {
-	ascender = abs (face->table.OS2->sTypoAscender);
-	descender = -abs (face->table.OS2->sTypoDescender);
-	line_gap = face->table.OS2->sTypoLineGap;
-	got_font_extents = (ascender | descender) != 0;
-      }
-
-      hb_blob_t *_hea_blob = hb_sanitize_context_t().reference_table<H> (face);
-      const H *_hea_table = _hea_blob->as<H> ();
-      num_advances = _hea_table->numberOfLongMetrics;
-      if (!got_font_extents)
-      {
-	ascender = abs (_hea_table->ascender);
-	descender = -abs (_hea_table->descender);
-	line_gap = _hea_table->lineGap;
-	got_font_extents = (ascender | descender) != 0;
-      }
-      hb_blob_destroy (_hea_blob);
-
-      has_font_extents = got_font_extents;
+      num_advances = T::is_horizontal ? face->table.hhea->numberOfLongMetrics : face->table.vhea->numberOfLongMetrics;
 
       table = hb_sanitize_context_t().reference_table<hmtxvmtx> (face, T::tableTag);
 
@@ -277,12 +256,6 @@
       return get_advance (old_gid);
     }
 
-    public:
-    bool has_font_extents;
-    int ascender;
-    int descender;
-    int line_gap;
-
     protected:
     unsigned int num_metrics;
     unsigned int num_advances;
@@ -322,12 +295,12 @@
 struct hmtx : hmtxvmtx<hmtx, hhea> {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_hmtx;
   static constexpr hb_tag_t variationsTag = HB_OT_TAG_HVAR;
-  static constexpr hb_tag_t os2Tag = HB_OT_TAG_OS2;
+  static constexpr bool is_horizontal = true;
 };
 struct vmtx : hmtxvmtx<vmtx, vhea> {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_vmtx;
   static constexpr hb_tag_t variationsTag = HB_OT_TAG_VVAR;
-  static constexpr hb_tag_t os2Tag = HB_TAG_NONE;
+  static constexpr bool is_horizontal = false;
 };
 
 struct hmtx_accelerator_t : hmtx::accelerator_t {};
diff --git a/src/hb-ot-layout-base-table.hh b/src/hb-ot-layout-base-table.hh
index 0912362..12cc163 100644
--- a/src/hb-ot-layout-base-table.hh
+++ b/src/hb-ot-layout-base-table.hh
@@ -1,7 +1,7 @@
 /*
- * Copyright © 2016 Elie Roux <elie.roux@telecom-bretagne.eu>
+ * Copyright © 2016  Elie Roux <elie.roux@telecom-bretagne.eu>
  * Copyright © 2018  Google, Inc.
- * Copyright © 2018  Ebrahim Byagowi
+ * Copyright © 2018-2019  Ebrahim Byagowi
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -116,6 +116,8 @@
 
 struct BaseCoord
 {
+  bool has_data () const { return u.format; }
+
   hb_position_t get_coord (hb_font_t *font,
 			   const VariationStore &var_store,
 			   hb_direction_t direction) const
@@ -142,10 +144,10 @@
 
   protected:
   union {
-    HBUINT16		format;
-    BaseCoordFormat1	format1;
-    BaseCoordFormat2	format2;
-    BaseCoordFormat3	format3;
+  HBUINT16		format;
+  BaseCoordFormat1	format1;
+  BaseCoordFormat2	format2;
+  BaseCoordFormat3	format3;
   } u;
   public:
   DEFINE_SIZE_UNION (2, format);
@@ -153,14 +155,9 @@
 
 struct FeatMinMaxRecord
 {
-  HB_INTERNAL static int cmp (const void *key_, const void *entry_)
-  {
-    hb_tag_t key = * (hb_tag_t *) key_;
-    const FeatMinMaxRecord &entry = * (const FeatMinMaxRecord *) entry_;
-    return key < (unsigned int) entry.tag ? -1 :
-	   key > (unsigned int) entry.tag ? 1 :
-	   0;
-  }
+  int cmp (hb_tag_t key) const { return tag.cmp (key); }
+
+  bool has_data () const { return tag; }
 
   void get_min_max (const BaseCoord **min, const BaseCoord **max) const
   {
@@ -195,17 +192,12 @@
 struct MinMax
 {
   void get_min_max (hb_tag_t          feature_tag,
-			   const BaseCoord **min,
-			   const BaseCoord **max) const
+		    const BaseCoord **min,
+		    const BaseCoord **max) const
   {
-    /* TODO Replace hb_bsearch() with .bsearch(). */
-    const FeatMinMaxRecord *minMaxCoord = (const FeatMinMaxRecord *)
-					  hb_bsearch (&feature_tag, featMinMaxRecords.arrayZ,
-						      featMinMaxRecords.len,
-						      FeatMinMaxRecord::static_size,
-						      FeatMinMaxRecord::cmp);
-    if (minMaxCoord)
-      minMaxCoord->get_min_max (min, max);
+    const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag);
+    if (minMaxCoord.has_data ())
+      minMaxCoord.get_min_max (min, max);
     else
     {
       if (likely (min)) *min = &(this+minCoord);
@@ -271,17 +263,11 @@
 
 struct BaseLangSysRecord
 {
-  HB_INTERNAL static int cmp (const void *key_, const void *entry_)
-  {
-    hb_tag_t key = * (hb_tag_t *) key_;
-    const BaseLangSysRecord &entry = * (const BaseLangSysRecord *) entry_;
-    return key < (unsigned int) entry.baseLangSysTag ? -1 :
-	   key > (unsigned int) entry.baseLangSysTag ? 1 :
-	   0;
-  }
+  int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); }
 
-  const MinMax &get_min_max () const
-  { return this+minMax; }
+  bool has_data () const { return baseLangSysTag; }
+
+  const MinMax &get_min_max () const { return this+minMax; }
 
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
@@ -303,19 +289,14 @@
 {
   const MinMax &get_min_max (hb_tag_t language_tag) const
   {
-    /* TODO Replace hb_bsearch() with .bsearch(). */
-    const BaseLangSysRecord* record = (const BaseLangSysRecord *)
-				      hb_bsearch (&language_tag, baseLangSysRecords.arrayZ,
-						  baseLangSysRecords.len,
-						  BaseLangSysRecord::static_size,
-						  BaseLangSysRecord::cmp);
-    return record ? record->get_min_max () : this+defaultMinMax;
+    const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag);
+    return record.has_data () ? record.get_min_max () : this+defaultMinMax;
   }
 
   const BaseCoord &get_base_coord (int baseline_tag_index) const
   { return (this+baseValues).get_base_coord (baseline_tag_index); }
 
-  bool is_empty () const { return !baseValues; }
+  bool has_data () const { return baseValues; }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -345,14 +326,9 @@
 struct BaseScriptList;
 struct BaseScriptRecord
 {
-  HB_INTERNAL static int cmp (const void *key_, const void *entry_)
-  {
-    hb_tag_t key = * (hb_tag_t *) key_;
-    const BaseScriptRecord &entry = * (const BaseScriptRecord *) entry_;
-    return key < (unsigned int) entry.baseScriptTag ? -1 :
-	   key > (unsigned int) entry.baseScriptTag ? 1 :
-	   0;
-  }
+  int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); }
+
+  bool has_data () const { return baseScriptTag; }
 
   const BaseScript &get_base_script (const BaseScriptList *list) const
   { return list+baseScript; }
@@ -376,22 +352,11 @@
 
 struct BaseScriptList
 {
-  const BaseScriptRecord *find_record (hb_tag_t script) const
-  {
-    /* TODO Replace hb_bsearch() with .bsearch(). */
-    return (const BaseScriptRecord *) hb_bsearch (&script, baseScriptRecords.arrayZ,
-						  baseScriptRecords.len,
-						  BaseScriptRecord::static_size,
-						  BaseScriptRecord::cmp);
-  }
-
-  /* TODO: Or client should handle fallback? */
   const BaseScript &get_base_script (hb_tag_t script) const
   {
-    const BaseScriptRecord *record = find_record (script);
-    if (!record) record = find_record ((hb_script_t) HB_TAG ('D','F','L','T'));
-
-    return record ? record->get_base_script (this) : Null (BaseScript);
+    const BaseScriptRecord *record = &baseScriptRecords.bsearch (script);
+    if (!record->has_data ()) record = &baseScriptRecords.bsearch (HB_TAG ('D','F','L','T'));
+    return record->has_data () ? record->get_base_script (this) : Null (BaseScript);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -412,14 +377,19 @@
 struct Axis
 {
   bool get_baseline (hb_ot_layout_baseline_t   baseline,
-			    hb_tag_t                  script_tag,
-			    hb_tag_t                  language_tag,
-			    const BaseCoord         **coord) const
+		     hb_tag_t                  script_tag,
+		     hb_tag_t                  language_tag,
+		     const BaseCoord         **coord) const
   {
     const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
-    if (base_script.is_empty ()) return false;
+    if (!base_script.has_data ()) return false;
 
-    if (likely (coord)) *coord = &base_script.get_base_coord ((this+baseTagList).bsearch (baseline));
+    if (likely (coord))
+    {
+      unsigned int tag_index = 0;
+      (this+baseTagList).bfind (baseline, &tag_index);
+      *coord = &base_script.get_base_coord (tag_index);
+    }
 
     return true;
   }
@@ -431,7 +401,7 @@
 		    const BaseCoord **max_coord) const
   {
     const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
-    if (base_script.is_empty ()) return false;
+    if (!base_script.has_data ()) return false;
 
     base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord);
 
@@ -479,13 +449,14 @@
 		     hb_tag_t                 language_tag,
 		     hb_position_t           *base) const
   {
-    const BaseCoord *base_coord;
-    if (!get_axis (direction).get_baseline (baseline, script_tag, language_tag, &base_coord))
+    const BaseCoord *base_coord = nullptr;
+    if (unlikely (!get_axis (direction).get_baseline (baseline, script_tag, language_tag, &base_coord) ||
+		  !base_coord || !base_coord->has_data ()))
       return false;
 
-    if (likely (base && base_coord)) *base = base_coord->get_coord (font,
-								    get_var_store (),
-								    direction);
+    if (likely (base))
+      *base = base_coord->get_coord (font, get_var_store (), direction);
+
     return true;
   }
 
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index f4be42c..5c21980 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -733,7 +733,7 @@
     + hb_zip (this+coverage, pairSet)
     | hb_filter (*glyphs, hb_first)
     | hb_map (hb_second)
-    | hb_map ([=] (const OffsetTo<PairSet> &_)
+    | hb_map ([glyphs, this] (const OffsetTo<PairSet> &_)
 	      { return (this+_).intersects (glyphs, valueFormat); })
     | hb_any
     ;
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index a6cc1a2..84e1b15 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -248,7 +248,7 @@
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned format = 2;
     unsigned delta = 0;
-    if (glyphs.len ())
+    if (glyphs)
     {
       format = 1;
       auto get_delta = [=] (hb_codepoint_pair_t _) {
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 98cd109..4cd65b4 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -43,7 +43,6 @@
 #include "hb-map.hh"
 
 #include "hb-ot-kern-table.hh"
-#include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
@@ -109,7 +108,7 @@
  *
  * Tests whether a face has any cross-stream kerning (i.e., kerns
  * that make adjustments perpendicular to the direction of the text
- * flow: Y adjustments in horizontal text or X adjustments in 
+ * flow: Y adjustments in horizontal text or X adjustments in
  * vertical text) in the 'kern' table.
  *
  * Does NOT examine the GPOS table.
@@ -286,7 +285,7 @@
  *
  * Fetches the GDEF class of the requested glyph in the specified face.
  *
- * Return value: The #hb_ot_layout_glyph_class_t glyph class of the given code 
+ * Return value: The #hb_ot_layout_glyph_class_t glyph class of the given code
  * point in the GDEF table of the face.
  *
  * Since: 0.9.7
@@ -330,7 +329,7 @@
  * @point_array: (out) (array length=point_count): The array of attachment points found for the query
  *
  * Fetches a list of all attachment points for the specified glyph in the GDEF
- * table of the face. The list returned will begin at the offset provided. 
+ * table of the face. The list returned will begin at the offset provided.
  *
  * Useful if the client program wishes to cache the list.
  *
@@ -980,7 +979,7 @@
  * @face: #hb_face_t to work upon
  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
  *
- * Fetches the total number of lookups enumerated in the specified 
+ * Fetches the total number of lookups enumerated in the specified
  * face's GSUB table or GPOS table.
  *
  * Since: 0.9.22
@@ -1188,7 +1187,7 @@
  * table or GPOS table, underneath the specified scripts, languages, and
  * features. If no list of scripts is provided, all scripts will be queried.
  * If no list of languages is provided, all languages will be queried. If no
- * list of features is provided, all features will be queried. 
+ * list of features is provided, all features will be queried.
  *
  * Since: 0.9.8
  **/
@@ -1582,7 +1581,7 @@
  * as used here are defined as pertaining only to fonts within a font family that differ
  * specifically in their respective size ranges; other ways to differentiate fonts within
  * a subfamily are not covered by the `size` feature.
- * 
+ *
  * For more information on this distinction, see the `size` documentation at
  * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-39size39
  *
@@ -1724,7 +1723,7 @@
  *       returned. This function can be called with incrementally larger start_offset
  *       until the char_count output value is lower than its input value, or the size
  *       of the characters array can be increased.</note>
- * 
+ *
  * Return value: Number of total sample characters in the cvXX feature.
  *
  * Since: 2.0.0
@@ -1980,7 +1979,9 @@
   HB_OT_LAYOUT_BASELINE_IDEO = HB_TAG('i','d','e','o'),
   HB_OT_LAYOUT_BASELINE_IDTB = HB_TAG('i','d','t','b'),
   HB_OT_LAYOUT_BASELINE_MATH = HB_TAG('m','a','t','h'),
-  HB_OT_LAYOUT_BASELINE_ROMN = HB_TAG('r','o','m','n')
+  HB_OT_LAYOUT_BASELINE_ROMN = HB_TAG('r','o','m','n'),
+
+  _HB_OT_LAYOUT_BASELINE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
 } hb_ot_layout_baseline_t;
 
 
diff --git a/src/hb-ot-layout.hh b/src/hb-ot-layout.hh
index be7ef02..f3bb155 100644
--- a/src/hb-ot-layout.hh
+++ b/src/hb-ot-layout.hh
@@ -168,6 +168,17 @@
   return start;
 }
 
+static inline void
+_hb_clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		     hb_font_t *font HB_UNUSED,
+		     hb_buffer_t *buffer)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    info[i].syllable() = 0;
+}
+
 
 /* unicode_props */
 
@@ -551,6 +562,17 @@
   info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED);
 }
 
+static inline void
+_hb_clear_substitution_flags (const hb_ot_shape_plan_t *plan HB_UNUSED,
+			      hb_font_t *font HB_UNUSED,
+			      hb_buffer_t *buffer)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    _hb_glyph_info_clear_substituted (&info[i]);
+}
+
 
 /* Allocation / deallocation. */
 
diff --git a/src/hb-ot-map.hh b/src/hb-ot-map.hh
index dd67786..0a4827d 100644
--- a/src/hb-ot-map.hh
+++ b/src/hb-ot-map.hh
@@ -154,8 +154,8 @@
 
   HB_INTERNAL void collect_lookups (unsigned int table_index, hb_set_t *lookups) const;
   template <typename Proxy>
-  HB_INTERNAL inline void apply (const Proxy &proxy,
-				 const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
+  HB_INTERNAL void apply (const Proxy &proxy,
+			  const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
   HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
   HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
 
diff --git a/src/hb-ot-math-table.hh b/src/hb-ot-math-table.hh
index 0b16e0b..b4592cc 100644
--- a/src/hb-ot-math-table.hh
+++ b/src/hb-ot-math-table.hh
@@ -453,14 +453,14 @@
   }
 
   void extract (hb_ot_math_glyph_part_t &out,
-		int scale,
+		int64_t mult,
 		hb_font_t *font) const
   {
     out.glyph			= glyph;
 
-    out.start_connector_length	= font->em_scale (startConnectorLength, scale);
-    out.end_connector_length	= font->em_scale (endConnectorLength, scale);
-    out.full_advance		= font->em_scale (fullAdvance, scale);
+    out.start_connector_length	= font->em_mult (startConnectorLength, mult);
+    out.end_connector_length	= font->em_mult (endConnectorLength, mult);
+    out.full_advance		= font->em_mult (fullAdvance, mult);
 
     static_assert ((unsigned int) HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER ==
 		   (unsigned int) PartFlags::Extender, "");
@@ -508,11 +508,11 @@
   {
     if (parts_count)
     {
-      int scale = font->dir_scale (direction);
+      int64_t mult = font->dir_mult (direction);
       hb_array_t<const MathGlyphPartRecord> arr = partRecords.sub_array (start_offset, parts_count);
       unsigned int count = arr.length;
       for (unsigned int i = 0; i < count; i++)
-	arr[i].extract (parts[i], scale, font);
+	arr[i].extract (parts[i], mult, font);
     }
 
     if (italics_correction)
@@ -553,13 +553,13 @@
   {
     if (variants_count)
     {
-      int scale = font->dir_scale (direction);
+      int64_t mult = font->dir_mult (direction);
       hb_array_t<const MathGlyphVariantRecord> arr = mathGlyphVariantRecord.sub_array (start_offset, variants_count);
       unsigned int count = arr.length;
       for (unsigned int i = 0; i < count; i++)
       {
 	variants[i].glyph = arr[i].variantGlyph;
-	variants[i].advance = font->em_scale (arr[i].advanceMeasurement, scale);
+	variants[i].advance = font->em_mult (arr[i].advanceMeasurement, mult);
       }
     }
     return mathGlyphVariantRecord.len;
diff --git a/src/hb-ot-math.h b/src/hb-ot-math.h
index 7b2befb..ad864a7 100644
--- a/src/hb-ot-math.h
+++ b/src/hb-ot-math.h
@@ -158,7 +158,7 @@
  * hb_ot_math_glyph_part_t:
  * @glyph: The glyph index of the variant part
  * @start_connector_length: The length of the connector on the starting side of the variant part
- * @end_connection_length: The length of the connector on the ending side of the variant part
+ * @end_connector_length: The length of the connector on the ending side of the variant part
  * @full_advance: The total advance of the part
  * @flags: #hb_ot_math_glyph_part_flags_t flags for the part
  * 
diff --git a/src/hb-ot-meta-table.hh b/src/hb-ot-meta-table.hh
new file mode 100644
index 0000000..f0842e4
--- /dev/null
+++ b/src/hb-ot-meta-table.hh
@@ -0,0 +1,124 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_OT_META_TABLE_HH
+#define HB_OT_META_TABLE_HH
+
+#include "hb-open-type.hh"
+
+/*
+ * meta -- Metadata Table
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/meta
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html
+ */
+#define HB_OT_TAG_meta HB_TAG ('m','e','t','a')
+
+
+namespace OT {
+
+
+struct DataMap
+{
+  int cmp (hb_tag_t a) const { return tag.cmp (a); }
+
+  hb_tag_t get_tag () const { return tag; }
+
+  hb_blob_t *reference_entry (hb_blob_t *meta_blob) const
+  { return hb_blob_create_sub_blob (meta_blob, dataZ, dataLength); }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  dataZ.sanitize (c, base, dataLength)));
+  }
+
+  protected:
+  Tag		tag;		/* A tag indicating the type of metadata. */
+  LOffsetTo<UnsizedArrayOf<HBUINT8>>
+		dataZ;		/* Offset in bytes from the beginning of the
+				 * metadata table to the data for this tag. */
+  HBUINT32	dataLength;	/* Length of the data. The data is not required to
+				 * be padded to any byte boundary. */
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+struct meta
+{
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_meta;
+
+  struct accelerator_t
+  {
+    void init (hb_face_t *face)
+    { table = hb_sanitize_context_t ().reference_table<meta> (face); }
+    void fini () { table.destroy (); }
+
+    hb_blob_t *reference_entry (hb_tag_t tag) const
+    { return table->dataMaps.lsearch (tag).reference_entry (table.get_blob ()); }
+
+    unsigned int get_entries (unsigned int      start_offset,
+			      unsigned int     *count,
+			      hb_ot_meta_tag_t *entries) const
+    {
+      if (count)
+      {
+	hb_array_t<const DataMap> arr = table->dataMaps.sub_array (start_offset, count);
+	for (unsigned int i = 0; i < arr.length; i++)
+	  entries[i] = (hb_ot_meta_tag_t) arr[i].get_tag ();
+      }
+      return table->dataMaps.len;
+    }
+
+    private:
+    hb_blob_ptr_t<meta> table;
+  };
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  version == 1 &&
+			  dataMaps.sanitize (c, this)));
+  }
+
+  protected:
+  HBUINT32	version;	/* Version number of the metadata table — set to 1. */
+  HBUINT32	flags;		/* Flags — currently unused; set to 0. */
+  HBUINT32	dataOffset;	/* Per Apple specification:
+				 * Offset from the beginning of the table to the data.
+				 * Per OT specification:
+				 * Reserved. Not used; should be set to 0. */
+  LArrayOf<DataMap>
+		dataMaps;	/* Array of data map records. */
+  public:
+  DEFINE_SIZE_ARRAY (16, dataMaps);
+};
+
+struct meta_accelerator_t : meta::accelerator_t {};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_META_TABLE_HH */
diff --git a/src/hb-ot-meta.cc b/src/hb-ot-meta.cc
new file mode 100644
index 0000000..a5ce148
--- /dev/null
+++ b/src/hb-ot-meta.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb.hh"
+
+#ifndef HB_NO_META
+
+#include "hb-ot-meta-table.hh"
+
+/**
+ * SECTION:hb-ot-meta
+ * @title: hb-ot-meta
+ * @short_description: OpenType Metadata
+ * @include: hb-ot.h
+ *
+ * Functions for fetching metadata from fonts.
+ **/
+
+/**
+ * hb_ot_meta_reference_entry:
+ * @face: a face object
+ * @start_offset: iteration's start offset
+ * @entries_count:(inout) (allow-none): buffer size as input, filled size as output
+ * @entries: (out caller-allocates) (array length=entries_count): entries tags buffer
+ *
+ * Return value: Number of all available feature types.
+ *
+ * Since: REPLACEME
+ **/
+unsigned int
+hb_ot_meta_get_entries (hb_face_t        *face,
+			unsigned int      start_offset,
+			unsigned int     *entries_count, /* IN/OUT.  May be NULL. */
+			hb_ot_meta_tag_t *entries        /* OUT.     May be NULL. */)
+{
+  return face->table.meta->get_entries (start_offset, entries_count, entries);
+}
+
+/**
+ * hb_ot_meta_reference_entry:
+ * @face: a #hb_face_t object.
+ * @meta_tag: tag of metadata you like to have.
+ *
+ * It fetches metadata entry of a given tag from a font.
+ *
+ * Returns: (transfer full): A blob containing the blob.
+ *
+ * Since: REPLACEME
+ **/
+hb_blob_t *
+hb_ot_meta_reference_entry (hb_face_t *face, hb_ot_meta_tag_t meta_tag)
+{
+  return face->table.meta->reference_entry (meta_tag);
+}
+
+#endif
diff --git a/src/hb-ot-meta.h b/src/hb-ot-meta.h
new file mode 100644
index 0000000..5267728
--- /dev/null
+++ b/src/hb-ot-meta.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
+#ifndef HB_OT_META_H
+#define HB_OT_META_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+/**
+ * hb_ot_meta_tag_t:
+ *
+ * From https://docs.microsoft.com/en-us/typography/opentype/spec/meta
+ *
+ * Since: REPLACEME
+ **/
+typedef enum {
+/*
+   HB_OT_META_APPL		= HB_TAG ('a','p','p','l'),
+   HB_OT_META_BILD		= HB_TAG ('b','i','l','d'),
+*/
+  HB_OT_META_DESIGN_LANGUAGES	= HB_TAG ('d','l','n','g'),
+  HB_OT_META_SUPPORTED_LANGUAGES= HB_TAG ('s','l','n','g'),
+
+  _HB_OT_META_MAX_VALUE		= HB_TAG_MAX_SIGNED /*< skip >*/
+} hb_ot_meta_tag_t;
+
+HB_EXTERN unsigned int
+hb_ot_meta_get_entries (hb_face_t        *face,
+			unsigned int      start_offset,
+			unsigned int     *entries_count, /* IN/OUT.  May be NULL. */
+			hb_ot_meta_tag_t *entries        /* OUT.     May be NULL. */);
+
+HB_EXTERN hb_blob_t *
+hb_ot_meta_reference_entry (hb_face_t *face, hb_ot_meta_tag_t meta_tag);
+
+HB_END_DECLS
+
+#endif /* HB_OT_META_H */
diff --git a/src/hb-ot-metrics.cc b/src/hb-ot-metrics.cc
new file mode 100644
index 0000000..d211d7e
--- /dev/null
+++ b/src/hb-ot-metrics.cc
@@ -0,0 +1,231 @@
+/*
+ * Copyright © 2018-2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb.hh"
+
+#include "hb-ot-var-mvar-table.hh"
+#include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise.
+#include "hb-ot-os2-table.hh"
+#include "hb-ot-post-table.hh"
+#include "hb-ot-hhea-table.hh"
+#include "hb-ot-metrics.hh"
+#include "hb-ot-face.hh"
+
+
+static float
+_fix_ascender_descender (float value, hb_ot_metrics_tag_t metrics_tag)
+{
+  if (metrics_tag == HB_OT_METRICS_HORIZONTAL_ASCENDER ||
+      metrics_tag == HB_OT_METRICS_VERTICAL_ASCENDER)
+    return fabs ((double) value);
+  if (metrics_tag == HB_OT_METRICS_HORIZONTAL_DESCENDER ||
+      metrics_tag == HB_OT_METRICS_VERTICAL_DESCENDER)
+    return -fabs ((double) value);
+  return value;
+}
+
+/* The common part of _get_position logic needed on hb-ot-font and here
+   to be able to have slim builds without the not always needed parts */
+bool
+_hb_ot_metrics_get_position_common (hb_font_t           *font,
+				    hb_ot_metrics_tag_t  metrics_tag,
+				    hb_position_t       *position     /* OUT.  May be NULL. */)
+{
+  hb_face_t *face = font->face;
+  switch ((unsigned) metrics_tag)
+  {
+#ifndef HB_NO_VAR
+#define GET_VAR face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords)
+#else
+#define GET_VAR .0f
+#endif
+#define GET_METRIC_X(TABLE, ATTR) \
+  (face->table.TABLE->has_data () && \
+    (position && (*position = font->em_scalef_x (_fix_ascender_descender ( \
+      face->table.TABLE->ATTR + GET_VAR, metrics_tag))), true))
+#define GET_METRIC_Y(TABLE, ATTR) \
+  (face->table.TABLE->has_data () && \
+    (position && (*position = font->em_scalef_y (_fix_ascender_descender ( \
+      face->table.TABLE->ATTR + GET_VAR, metrics_tag))), true))
+  case HB_OT_METRICS_HORIZONTAL_ASCENDER:
+    return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoAscender)) ||
+	   GET_METRIC_Y (hhea, ascender);
+  case HB_OT_METRICS_HORIZONTAL_DESCENDER:
+    return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoDescender)) ||
+	   GET_METRIC_Y (hhea, descender);
+  case HB_OT_METRICS_HORIZONTAL_LINE_GAP:
+    return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoLineGap)) ||
+	   GET_METRIC_Y (hhea, lineGap);
+  case HB_OT_METRICS_VERTICAL_ASCENDER:  return GET_METRIC_X (vhea, ascender);
+  case HB_OT_METRICS_VERTICAL_DESCENDER: return GET_METRIC_X (vhea, descender);
+  case HB_OT_METRICS_VERTICAL_LINE_GAP:  return GET_METRIC_X (vhea, lineGap);
+#undef GET_METRIC_Y
+#undef GET_METRIC_X
+#undef GET_VAR
+  default:                               assert (0); return false;
+  }
+}
+
+#ifndef HB_NO_METRICS
+
+#if 0
+static bool
+_get_gasp (hb_face_t *face, float *result, hb_ot_metrics_tag_t metrics_tag)
+{
+  const OT::GaspRange& range = face->table.gasp->get_gasp_range (metrics_tag - HB_TAG ('g','s','p','0'));
+  if (&range == &Null (OT::GaspRange)) return false;
+  if (result) *result = range.rangeMaxPPEM + font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords);
+  return true;
+}
+#endif
+
+/* Private tags for https://github.com/harfbuzz/harfbuzz/issues/1866 */
+#define _HB_OT_METRICS_HORIZONTAL_ASCENDER_OS2   HB_TAG ('O','a','s','c')
+#define _HB_OT_METRICS_HORIZONTAL_ASCENDER_HHEA  HB_TAG ('H','a','s','c')
+#define _HB_OT_METRICS_HORIZONTAL_DESCENDER_OS2  HB_TAG ('O','d','s','c')
+#define _HB_OT_METRICS_HORIZONTAL_DESCENDER_HHEA HB_TAG ('H','d','s','c')
+#define _HB_OT_METRICS_HORIZONTAL_LINE_GAP_OS2   HB_TAG ('O','l','g','p')
+#define _HB_OT_METRICS_HORIZONTAL_LINE_GAP_HHEA  HB_TAG ('H','l','g','p')
+
+/**
+ * hb_ot_metrics_get_position:
+ * @font: a #hb_font_t object.
+ * @metrics_tag: tag of metrics value you like to fetch.
+ * @position: (out) (optional): result of metrics value from the font.
+ *
+ * It fetches metrics value corresponding to a given tag from a font.
+ *
+ * Returns: Whether found the requested metrics in the font.
+ * Since: REPLACEME
+ **/
+hb_bool_t
+hb_ot_metrics_get_position (hb_font_t           *font,
+			    hb_ot_metrics_tag_t  metrics_tag,
+			    hb_position_t       *position     /* OUT.  May be NULL. */)
+{
+  hb_face_t *face = font->face;
+  switch ((unsigned) metrics_tag)
+  {
+  case HB_OT_METRICS_HORIZONTAL_ASCENDER:
+  case HB_OT_METRICS_HORIZONTAL_DESCENDER:
+  case HB_OT_METRICS_HORIZONTAL_LINE_GAP:
+  case HB_OT_METRICS_VERTICAL_ASCENDER:
+  case HB_OT_METRICS_VERTICAL_DESCENDER:
+  case HB_OT_METRICS_VERTICAL_LINE_GAP:           return _hb_ot_metrics_get_position_common (font, metrics_tag, position);
+#ifndef HB_NO_VAR
+#define GET_VAR hb_ot_metrics_get_variation (font, metrics_tag)
+#else
+#define GET_VAR 0
+#endif
+#define GET_METRIC_X(TABLE, ATTR) \
+  (face->table.TABLE->has_data () && \
+    (position && (*position = font->em_scalef_x (face->table.TABLE->ATTR + GET_VAR)), true))
+#define GET_METRIC_Y(TABLE, ATTR) \
+  (face->table.TABLE->has_data () && \
+    (position && (*position = font->em_scalef_y (face->table.TABLE->ATTR + GET_VAR)), true))
+  case HB_OT_METRICS_HORIZONTAL_CLIPPING_ASCENT:  return GET_METRIC_Y (OS2, usWinAscent);
+  case HB_OT_METRICS_HORIZONTAL_CLIPPING_DESCENT: return GET_METRIC_Y (OS2, usWinDescent);
+  case HB_OT_METRICS_HORIZONTAL_CARET_RISE:       return GET_METRIC_Y (hhea, caretSlopeRise);
+  case HB_OT_METRICS_HORIZONTAL_CARET_RUN:        return GET_METRIC_X (hhea, caretSlopeRun);
+  case HB_OT_METRICS_HORIZONTAL_CARET_OFFSET:     return GET_METRIC_X (hhea, caretOffset);
+  case HB_OT_METRICS_VERTICAL_CARET_RISE:         return GET_METRIC_X (vhea, caretSlopeRise);
+  case HB_OT_METRICS_VERTICAL_CARET_RUN:          return GET_METRIC_Y (vhea, caretSlopeRun);
+  case HB_OT_METRICS_VERTICAL_CARET_OFFSET:       return GET_METRIC_Y (vhea, caretOffset);
+  case HB_OT_METRICS_X_HEIGHT:                    return GET_METRIC_Y (OS2->v2 (), sxHeight);
+  case HB_OT_METRICS_CAP_HEIGHT:                  return GET_METRIC_Y (OS2->v2 (), sCapHeight);
+  case HB_OT_METRICS_SUBSCRIPT_EM_X_SIZE:         return GET_METRIC_X (OS2, ySubscriptXSize);
+  case HB_OT_METRICS_SUBSCRIPT_EM_Y_SIZE:         return GET_METRIC_Y (OS2, ySubscriptYSize);
+  case HB_OT_METRICS_SUBSCRIPT_EM_X_OFFSET:       return GET_METRIC_X (OS2, ySubscriptXOffset);
+  case HB_OT_METRICS_SUBSCRIPT_EM_Y_OFFSET:       return GET_METRIC_Y (OS2, ySubscriptYOffset);
+  case HB_OT_METRICS_SUPERSCRIPT_EM_X_SIZE:       return GET_METRIC_X (OS2, ySuperscriptXSize);
+  case HB_OT_METRICS_SUPERSCRIPT_EM_Y_SIZE:       return GET_METRIC_Y (OS2, ySuperscriptYSize);
+  case HB_OT_METRICS_SUPERSCRIPT_EM_X_OFFSET:     return GET_METRIC_X (OS2, ySuperscriptXOffset);
+  case HB_OT_METRICS_SUPERSCRIPT_EM_Y_OFFSET:     return GET_METRIC_Y (OS2, ySuperscriptYOffset);
+  case HB_OT_METRICS_STRIKEOUT_SIZE:              return GET_METRIC_Y (OS2, yStrikeoutSize);
+  case HB_OT_METRICS_STRIKEOUT_OFFSET:            return GET_METRIC_Y (OS2, yStrikeoutPosition);
+  case HB_OT_METRICS_UNDERLINE_SIZE:              return GET_METRIC_Y (post->table, underlineThickness);
+  case HB_OT_METRICS_UNDERLINE_OFFSET:            return GET_METRIC_Y (post->table, underlinePosition);
+
+  /* Private tags */
+  case _HB_OT_METRICS_HORIZONTAL_ASCENDER_OS2:    return GET_METRIC_Y (OS2, sTypoAscender);
+  case _HB_OT_METRICS_HORIZONTAL_ASCENDER_HHEA:   return GET_METRIC_Y (hhea, ascender);
+  case _HB_OT_METRICS_HORIZONTAL_DESCENDER_OS2:   return GET_METRIC_Y (OS2, sTypoDescender);
+  case _HB_OT_METRICS_HORIZONTAL_DESCENDER_HHEA:  return GET_METRIC_Y (hhea, descender);
+  case _HB_OT_METRICS_HORIZONTAL_LINE_GAP_OS2:    return GET_METRIC_Y (OS2, sTypoLineGap);
+  case _HB_OT_METRICS_HORIZONTAL_LINE_GAP_HHEA:   return GET_METRIC_Y (hhea, lineGap);
+#undef GET_METRIC_Y
+#undef GET_METRIC_X
+#undef GET_VAR
+  default:                                        return false;
+  }
+}
+
+#ifndef HB_NO_VAR
+/**
+ * hb_ot_metrics_get_variation:
+ * @font:
+ * @metrics_tag:
+ *
+ * Returns:
+ *
+ * Since: REPLACEME
+ **/
+float
+hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag)
+{
+  return font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords);
+}
+
+/**
+ * hb_ot_metrics_get_x_variation:
+ * @font:
+ * @metrics_tag:
+ *
+ * Returns:
+ *
+ * Since: REPLACEME
+ **/
+hb_position_t
+hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag)
+{
+  return font->em_scalef_x (hb_ot_metrics_get_variation (font, metrics_tag));
+}
+
+/**
+ * hb_ot_metrics_get_y_variation:
+ * @font:
+ * @metrics_tag:
+ *
+ * Returns:
+ *
+ * Since: REPLACEME
+ **/
+hb_position_t
+hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag)
+{
+  return font->em_scalef_y (hb_ot_metrics_get_variation (font, metrics_tag));
+}
+#endif
+
+#endif
diff --git a/src/hb-ot-metrics.h b/src/hb-ot-metrics.h
new file mode 100644
index 0000000..6b2747b
--- /dev/null
+++ b/src/hb-ot-metrics.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
+#ifndef HB_OT_METRICS_H
+#define HB_OT_METRICS_H
+
+#include "hb.h"
+#include "hb-ot-name.h"
+
+HB_BEGIN_DECLS
+
+
+/**
+ * hb_ot_metrics_tag_t:
+ *
+ * From https://docs.microsoft.com/en-us/typography/opentype/spec/mvar#value-tags
+ *
+ * Since: REPLACEME
+ **/
+typedef enum {
+  HB_OT_METRICS_HORIZONTAL_ASCENDER		= HB_TAG ('h','a','s','c'),
+  HB_OT_METRICS_HORIZONTAL_DESCENDER		= HB_TAG ('h','d','s','c'),
+  HB_OT_METRICS_HORIZONTAL_LINE_GAP		= HB_TAG ('h','l','g','p'),
+  HB_OT_METRICS_HORIZONTAL_CLIPPING_ASCENT	= HB_TAG ('h','c','l','a'),
+  HB_OT_METRICS_HORIZONTAL_CLIPPING_DESCENT	= HB_TAG ('h','c','l','d'),
+  HB_OT_METRICS_VERTICAL_ASCENDER		= HB_TAG ('v','a','s','c'),
+  HB_OT_METRICS_VERTICAL_DESCENDER		= HB_TAG ('v','d','s','c'),
+  HB_OT_METRICS_VERTICAL_LINE_GAP		= HB_TAG ('v','l','g','p'),
+  HB_OT_METRICS_HORIZONTAL_CARET_RISE		= HB_TAG ('h','c','r','s'),
+  HB_OT_METRICS_HORIZONTAL_CARET_RUN		= HB_TAG ('h','c','r','n'),
+  HB_OT_METRICS_HORIZONTAL_CARET_OFFSET		= HB_TAG ('h','c','o','f'),
+  HB_OT_METRICS_VERTICAL_CARET_RISE		= HB_TAG ('v','c','r','s'),
+  HB_OT_METRICS_VERTICAL_CARET_RUN		= HB_TAG ('v','c','r','n'),
+  HB_OT_METRICS_VERTICAL_CARET_OFFSET		= HB_TAG ('v','c','o','f'),
+  HB_OT_METRICS_X_HEIGHT			= HB_TAG ('x','h','g','t'),
+  HB_OT_METRICS_CAP_HEIGHT			= HB_TAG ('c','p','h','t'),
+  HB_OT_METRICS_SUBSCRIPT_EM_X_SIZE		= HB_TAG ('s','b','x','s'),
+  HB_OT_METRICS_SUBSCRIPT_EM_Y_SIZE		= HB_TAG ('s','b','y','s'),
+  HB_OT_METRICS_SUBSCRIPT_EM_X_OFFSET		= HB_TAG ('s','b','x','o'),
+  HB_OT_METRICS_SUBSCRIPT_EM_Y_OFFSET		= HB_TAG ('s','b','y','o'),
+  HB_OT_METRICS_SUPERSCRIPT_EM_X_SIZE		= HB_TAG ('s','p','x','s'),
+  HB_OT_METRICS_SUPERSCRIPT_EM_Y_SIZE		= HB_TAG ('s','p','y','s'),
+  HB_OT_METRICS_SUPERSCRIPT_EM_X_OFFSET		= HB_TAG ('s','p','x','o'),
+  HB_OT_METRICS_SUPERSCRIPT_EM_Y_OFFSET		= HB_TAG ('s','p','y','o'),
+  HB_OT_METRICS_STRIKEOUT_SIZE			= HB_TAG ('s','t','r','s'),
+  HB_OT_METRICS_STRIKEOUT_OFFSET		= HB_TAG ('s','t','r','o'),
+  HB_OT_METRICS_UNDERLINE_SIZE			= HB_TAG ('u','n','d','s'),
+  HB_OT_METRICS_UNDERLINE_OFFSET		= HB_TAG ('u','n','d','o'),
+
+  _HB_OT_METRICS_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
+} hb_ot_metrics_tag_t;
+
+HB_EXTERN hb_bool_t
+hb_ot_metrics_get_position (hb_font_t           *font,
+			    hb_ot_metrics_tag_t  metrics_tag,
+			    hb_position_t       *position     /* OUT.  May be NULL. */);
+
+HB_EXTERN float
+hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag);
+
+HB_EXTERN hb_position_t
+hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag);
+
+HB_EXTERN hb_position_t
+hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag);
+
+HB_END_DECLS
+
+#endif /* HB_OT_METRICS_H */
diff --git a/src/hb-ot-metrics.hh b/src/hb-ot-metrics.hh
new file mode 100644
index 0000000..19a5e9e
--- /dev/null
+++ b/src/hb-ot-metrics.hh
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_OT_METRICS_HH
+#define HB_OT_METRICS_HH
+
+#include "hb.hh"
+
+HB_INTERNAL bool
+_hb_ot_metrics_get_position_common (hb_font_t           *font,
+				    hb_ot_metrics_tag_t  metrics_tag,
+				    hb_position_t       *position     /* OUT.  May be NULL. */);
+
+#endif /* HB_OT_METRICS_HH */
diff --git a/src/hb-ot-os2-table.hh b/src/hb-ot-os2-table.hh
index 16e29ca..67f21eb 100644
--- a/src/hb-ot-os2-table.hh
+++ b/src/hb-ot-os2-table.hh
@@ -59,6 +59,10 @@
 
 struct OS2V2Tail
 {
+  bool has_data () const { return this != &Null (OS2V2Tail); }
+
+  const OS2V2Tail * operator -> () const { return this; }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -113,9 +117,9 @@
     OBLIQUE		= 1u<<9
   };
 
-  bool is_italic () const       { return fsSelection & ITALIC; }
-  bool is_oblique () const      { return fsSelection & OBLIQUE; }
-  bool is_typo_metrics () const { return fsSelection & USE_TYPO_METRICS; }
+  bool is_italic () const        { return fsSelection & ITALIC; }
+  bool is_oblique () const       { return fsSelection & OBLIQUE; }
+  bool use_typo_metrics () const { return fsSelection & USE_TYPO_METRICS; }
 
   enum width_class_t {
     FWIDTH_ULTRA_CONDENSED	= 1, /* 50% */
@@ -192,13 +196,14 @@
   }
 
   static void find_min_and_max_codepoint (const hb_set_t *codepoints,
-						 uint16_t *min_cp, /* OUT */
-						 uint16_t *max_cp  /* OUT */)
+					  uint16_t *min_cp, /* OUT */
+					  uint16_t *max_cp  /* OUT */)
   {
     *min_cp = codepoints->get_min ();
     *max_cp = codepoints->get_max ();
   }
 
+  /* https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681 */
   enum font_page_t {
     HEBREW_FONT_PAGE		= 0xB100, // Hebrew Windows 3.1 font page
     SIMP_ARABIC_FONT_PAGE	= 0xB200, // Simplified Arabic Windows 3.1 font page
@@ -208,8 +213,6 @@
     TRAD_FARSI_FONT_PAGE	= 0xBB00, // Traditional Farsi Windows 3.1 font page
     THAI_FONT_PAGE		= 0xDE00  // Thai Windows 3.1 font page
   };
-
-  // https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681
   font_page_t get_font_page () const
   { return (font_page_t) (version == 0 ? fsSelection & 0xFF00 : 0); }
 
diff --git a/src/hb-ot-post-table.hh b/src/hb-ot-post-table.hh
index 720e03b..fb826cd 100644
--- a/src/hb-ot-post-table.hh
+++ b/src/hb-ot-post-table.hh
@@ -178,6 +178,8 @@
       return false;
     }
 
+    hb_blob_ptr_t<post> table;
+
     protected:
 
     unsigned int get_glyph_count () const
@@ -237,7 +239,6 @@
     }
 
     private:
-    hb_blob_ptr_t<post> table;
     uint32_t version;
     const ArrayOf<HBUINT16> *glyphNameIndex;
     hb_vector_t<uint32_t> index_to_offset;
@@ -245,6 +246,8 @@
     hb_atomic_ptr_t<uint16_t *> gids_sorted_by_name;
   };
 
+  bool has_data () const { return version.to_int (); }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
diff --git a/src/hb-ot-shape-complex-indic-machine.hh b/src/hb-ot-shape-complex-indic-machine.hh
index ca26ecb..670b6bf 100644
--- a/src/hb-ot-shape-complex-indic-machine.hh
+++ b/src/hb-ot-shape-complex-indic-machine.hh
@@ -395,13 +395,13 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | indic_##syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
 static void
-find_syllables (hb_buffer_t *buffer)
+find_syllables_indic (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act;
   int cs;
@@ -569,4 +569,6 @@
 
 }
 
+#undef found_syllable
+
 #endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index a2a88af..5f819bd 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -96,13 +96,13 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | indic_##syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
 static void
-find_syllables (hb_buffer_t *buffer)
+find_syllables_indic (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act;
   int cs;
@@ -121,4 +121,6 @@
   }%%
 }
 
+#undef found_syllable
+
 #endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 6405d9c..fd099ca 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -144,49 +144,45 @@
  * Must be in the same order as the indic_features array.
  */
 enum {
-  _NUKT,
-  _AKHN,
-  RPHF,
-  _RKRF,
-  PREF,
-  BLWF,
-  ABVF,
-  HALF,
-  PSTF,
-  _VATU,
-  _CJCT,
+  _INDIC_NUKT,
+  _INDIC_AKHN,
+  INDIC_RPHF,
+  _INDIC_RKRF,
+  INDIC_PREF,
+  INDIC_BLWF,
+  INDIC_ABVF,
+  INDIC_HALF,
+  INDIC_PSTF,
+  _INDIC_VATU,
+  _INDIC_CJCT,
 
-  INIT,
-  _PRES,
-  _ABVS,
-  _BLWS,
-  _PSTS,
-  _HALN,
+  INDIC_INIT,
+  _INDIC_PRES,
+  _INDIC_ABVS,
+  _INDIC_BLWS,
+  _INDIC_PSTS,
+  _INDIC_HALN,
 
-  _DIST,
-  _ABVM,
-  _BLWM,
+  _INDIC_DIST,
+  _INDIC_ABVM,
+  _INDIC_BLWM,
 
   INDIC_NUM_FEATURES,
-  INDIC_BASIC_FEATURES = INIT, /* Don't forget to update this! */
+  INDIC_BASIC_FEATURES = INDIC_INIT, /* Don't forget to update this! */
 };
 
 static void
-setup_syllables (const hb_ot_shape_plan_t *plan,
-		 hb_font_t *font,
-		 hb_buffer_t *buffer);
+setup_syllables_indic (const hb_ot_shape_plan_t *plan,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer);
 static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer);
+initial_reordering_indic (const hb_ot_shape_plan_t *plan,
+			  hb_font_t *font,
+			  hb_buffer_t *buffer);
 static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font,
-		  hb_buffer_t *buffer);
-static void
-clear_syllables (const hb_ot_shape_plan_t *plan,
-		 hb_font_t *font,
-		 hb_buffer_t *buffer);
+final_reordering_indic (const hb_ot_shape_plan_t *plan,
+			hb_font_t *font,
+			hb_buffer_t *buffer);
 
 static void
 collect_features_indic (hb_ot_shape_planner_t *plan)
@@ -194,7 +190,7 @@
   hb_ot_map_builder_t *map = &plan->map;
 
   /* Do this before any lookups have been applied. */
-  map->add_gsub_pause (setup_syllables);
+  map->add_gsub_pause (setup_syllables_indic);
 
   map->enable_feature (HB_TAG('l','o','c','l'));
   /* The Indic specs do not require ccmp, but we apply it here since if
@@ -203,14 +199,14 @@
 
 
   unsigned int i = 0;
-  map->add_gsub_pause (initial_reordering);
+  map->add_gsub_pause (initial_reordering_indic);
 
   for (; i < INDIC_BASIC_FEATURES; i++) {
     map->add_feature (indic_features[i]);
     map->add_gsub_pause (nullptr);
   }
 
-  map->add_gsub_pause (final_reordering);
+  map->add_gsub_pause (final_reordering_indic);
 
   for (; i < INDIC_NUM_FEATURES; i++)
     map->add_feature (indic_features[i]);
@@ -218,7 +214,7 @@
   map->enable_feature (HB_TAG('c','a','l','t'));
   map->enable_feature (HB_TAG('c','l','i','g'));
 
-  map->add_gsub_pause (clear_syllables);
+  map->add_gsub_pause (_hb_clear_syllables);
 }
 
 static void
@@ -228,32 +224,6 @@
 }
 
 
-struct would_substitute_feature_t
-{
-  void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
-  {
-    zero_context = zero_context_;
-    map->get_stage_lookups (0/*GSUB*/,
-			    map->get_feature_stage (0/*GSUB*/, feature_tag),
-			    &lookups, &count);
-  }
-
-  bool would_substitute (const hb_codepoint_t *glyphs,
-			 unsigned int          glyphs_count,
-			 hb_face_t            *face) const
-  {
-    for (unsigned int i = 0; i < count; i++)
-      if (hb_ot_layout_lookup_would_substitute (face, lookups[i].index, glyphs, glyphs_count, zero_context))
-	return true;
-    return false;
-  }
-
-  private:
-  const hb_ot_map_t::lookup_map_t *lookups;
-  unsigned int count;
-  bool zero_context;
-};
-
 struct indic_shape_plan_t
 {
   bool load_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
@@ -285,10 +255,10 @@
 #endif
   mutable hb_atomic_int_t virama_glyph;
 
-  would_substitute_feature_t rphf;
-  would_substitute_feature_t pref;
-  would_substitute_feature_t blwf;
-  would_substitute_feature_t pstf;
+  hb_indic_would_substitute_feature_t rphf;
+  hb_indic_would_substitute_feature_t pref;
+  hb_indic_would_substitute_feature_t blwf;
+  hb_indic_would_substitute_feature_t pstf;
 
   hb_mask_t mask_array[INDIC_NUM_FEATURES];
 };
@@ -371,13 +341,13 @@
 }
 
 
-enum syllable_type_t {
-  consonant_syllable,
-  vowel_syllable,
-  standalone_cluster,
-  symbol_cluster,
-  broken_cluster,
-  non_indic_cluster,
+enum indic_syllable_type_t {
+  indic_consonant_syllable,
+  indic_vowel_syllable,
+  indic_standalone_cluster,
+  indic_symbol_cluster,
+  indic_broken_cluster,
+  indic_non_indic_cluster,
 };
 
 #include "hb-ot-shape-complex-indic-machine.hh"
@@ -401,11 +371,11 @@
 }
 
 static void
-setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		 hb_font_t *font HB_UNUSED,
-		 hb_buffer_t *buffer)
+setup_syllables_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		       hb_font_t *font HB_UNUSED,
+		       hb_buffer_t *buffer)
 {
-  find_syllables (buffer);
+  find_syllables_indic (buffer);
   foreach_syllable (buffer, start, end)
     buffer->unsafe_to_break (start, end);
 }
@@ -422,9 +392,9 @@
 
 
 static void
-update_consonant_positions (const hb_ot_shape_plan_t *plan,
-			    hb_font_t         *font,
-			    hb_buffer_t       *buffer)
+update_consonant_positions_indic (const hb_ot_shape_plan_t *plan,
+				  hb_font_t         *font,
+				  hb_buffer_t       *buffer)
 {
   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
 
@@ -497,7 +467,7 @@
      *    and has more than one consonant, Ra is excluded from candidates for
      *    base consonants. */
     unsigned int limit = start;
-    if (indic_plan->mask_array[RPHF] &&
+    if (indic_plan->mask_array[INDIC_RPHF] &&
 	start + 3 <= end &&
 	(
 	 (indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) ||
@@ -833,13 +803,13 @@
 
     /* Reph */
     for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++)
-      info[i].mask |= indic_plan->mask_array[RPHF];
+      info[i].mask |= indic_plan->mask_array[INDIC_RPHF];
 
     /* Pre-base */
-    mask = indic_plan->mask_array[HALF];
+    mask = indic_plan->mask_array[INDIC_HALF];
     if (!indic_plan->is_old_spec &&
 	indic_plan->config->blwf_mode == BLWF_MODE_PRE_AND_POST)
-      mask |= indic_plan->mask_array[BLWF];
+      mask |= indic_plan->mask_array[INDIC_BLWF];
     for (unsigned int i = start; i < base; i++)
       info[i].mask  |= mask;
     /* Base */
@@ -847,7 +817,9 @@
     if (base < end)
       info[base].mask |= mask;
     /* Post-base */
-    mask = indic_plan->mask_array[BLWF] | indic_plan->mask_array[ABVF] | indic_plan->mask_array[PSTF];
+    mask = indic_plan->mask_array[INDIC_BLWF] |
+	   indic_plan->mask_array[INDIC_ABVF] |
+	   indic_plan->mask_array[INDIC_PSTF];
     for (unsigned int i = base + 1; i < end; i++)
       info[i].mask  |= mask;
   }
@@ -879,13 +851,13 @@
 	  (i + 2 == base ||
 	   info[i+2].indic_category() != OT_ZWJ))
       {
-	info[i  ].mask |= indic_plan->mask_array[BLWF];
-	info[i+1].mask |= indic_plan->mask_array[BLWF];
+	info[i  ].mask |= indic_plan->mask_array[INDIC_BLWF];
+	info[i+1].mask |= indic_plan->mask_array[INDIC_BLWF];
       }
   }
 
   unsigned int pref_len = 2;
-  if (indic_plan->mask_array[PREF] && base + pref_len < end)
+  if (indic_plan->mask_array[INDIC_PREF] && base + pref_len < end)
   {
     /* Find a Halant,Ra sequence and mark it for pre-base-reordering processing. */
     for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) {
@@ -895,7 +867,7 @@
       if (indic_plan->pref.would_substitute (glyphs, pref_len, face))
       {
 	for (unsigned int j = 0; j < pref_len; j++)
-	  info[i++].mask |= indic_plan->mask_array[PREF];
+	  info[i++].mask |= indic_plan->mask_array[INDIC_PREF];
 	break;
       }
     }
@@ -916,7 +888,7 @@
 
 	/* A ZWNJ disables HALF. */
 	if (non_joiner)
-	  info[j].mask &= ~indic_plan->mask_array[HALF];
+	  info[j].mask &= ~indic_plan->mask_array[INDIC_HALF];
 
       } while (j > start && !is_consonant (info[j]));
     }
@@ -945,34 +917,34 @@
 }
 
 static void
-initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
-			     hb_face_t *face,
-			     hb_buffer_t *buffer,
-			     unsigned int start, unsigned int end)
+initial_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
+				   hb_face_t *face,
+				   hb_buffer_t *buffer,
+				   unsigned int start, unsigned int end)
 {
-  syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
+  indic_syllable_type_t syllable_type = (indic_syllable_type_t) (buffer->info[start].syllable() & 0x0F);
   switch (syllable_type)
   {
-    case vowel_syllable: /* We made the vowels look like consonants.  So let's call the consonant logic! */
-    case consonant_syllable:
+    case indic_vowel_syllable: /* We made the vowels look like consonants.  So let's call the consonant logic! */
+    case indic_consonant_syllable:
      initial_reordering_consonant_syllable (plan, face, buffer, start, end);
      break;
 
-    case broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */
-    case standalone_cluster:
+    case indic_broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */
+    case indic_standalone_cluster:
      initial_reordering_standalone_cluster (plan, face, buffer, start, end);
      break;
 
-    case symbol_cluster:
-    case non_indic_cluster:
+    case indic_symbol_cluster:
+    case indic_non_indic_cluster:
       break;
   }
 }
 
 static inline void
-insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		       hb_font_t *font,
-		       hb_buffer_t *buffer)
+insert_dotted_circles_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
+			     hb_font_t *font,
+			     hb_buffer_t *buffer)
 {
   if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
     return;
@@ -983,7 +955,7 @@
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
-    if ((info[i].syllable() & 0x0F) == broken_cluster)
+    if ((info[i].syllable() & 0x0F) == indic_broken_cluster)
     {
       has_broken_syllables = true;
       break;
@@ -1008,8 +980,8 @@
   while (buffer->idx < buffer->len && buffer->successful)
   {
     unsigned int syllable = buffer->cur().syllable();
-    syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
-    if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
+    indic_syllable_type_t syllable_type = (indic_syllable_type_t) (syllable & 0x0F);
+    if (unlikely (last_syllable != syllable && syllable_type == indic_broken_cluster))
     {
       last_syllable = syllable;
 
@@ -1033,21 +1005,21 @@
 }
 
 static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer)
+initial_reordering_indic (const hb_ot_shape_plan_t *plan,
+			  hb_font_t *font,
+			  hb_buffer_t *buffer)
 {
-  update_consonant_positions (plan, font, buffer);
-  insert_dotted_circles (plan, font, buffer);
+  update_consonant_positions_indic (plan, font, buffer);
+  insert_dotted_circles_indic (plan, font, buffer);
 
   foreach_syllable (buffer, start, end)
-    initial_reordering_syllable (plan, font->face, buffer, start, end);
+    initial_reordering_syllable_indic (plan, font->face, buffer, start, end);
 }
 
 static void
-final_reordering_syllable (const hb_ot_shape_plan_t *plan,
-			   hb_buffer_t *buffer,
-			   unsigned int start, unsigned int end)
+final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan,
+				 hb_buffer_t *buffer,
+				 unsigned int start, unsigned int end)
 {
   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
   hb_glyph_info_t *info = buffer->info;
@@ -1083,7 +1055,7 @@
    * syllable.
    */
 
-  bool try_pref = !!indic_plan->mask_array[PREF];
+  bool try_pref = !!indic_plan->mask_array[INDIC_PREF];
 
   /* Find base again */
   unsigned int base;
@@ -1093,7 +1065,7 @@
       if (try_pref && base + 1 < end)
       {
 	for (unsigned int i = base + 1; i < end; i++)
-	  if ((info[i].mask & indic_plan->mask_array[PREF]) != 0)
+	  if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0)
 	  {
 	    if (!(_hb_glyph_info_substituted (&info[i]) &&
 		  _hb_glyph_info_ligated_and_didnt_multiply (&info[i])))
@@ -1415,7 +1387,7 @@
   if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base-reordering Ra. */
   {
     for (unsigned int i = base + 1; i < end; i++)
-      if ((info[i].mask & indic_plan->mask_array[PREF]) != 0)
+      if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0)
       {
 	/*       1. Only reorder a glyph produced by substitution during application
 	 *          of the <pref> feature. (Note that a font may shape a Ra consonant with
@@ -1478,7 +1450,7 @@
     if (!start ||
 	!(FLAG_UNSAFE (_hb_glyph_info_get_general_category (&info[start - 1])) &
 	 FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
-      info[start].mask |= indic_plan->mask_array[INIT];
+      info[start].mask |= indic_plan->mask_array[INDIC_INIT];
     else
       buffer->unsafe_to_break (start - 1, start + 1);
   }
@@ -1508,15 +1480,15 @@
 
 
 static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font HB_UNUSED,
-		  hb_buffer_t *buffer)
+final_reordering_indic (const hb_ot_shape_plan_t *plan,
+			hb_font_t *font HB_UNUSED,
+			hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   if (unlikely (!count)) return;
 
   foreach_syllable (buffer, start, end)
-    final_reordering_syllable (plan, buffer, start, end);
+    final_reordering_syllable_indic (plan, buffer, start, end);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
@@ -1524,18 +1496,6 @@
 
 
 static void
-clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		 hb_font_t *font HB_UNUSED,
-		 hb_buffer_t *buffer)
-{
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    info[i].syllable() = 0;
-}
-
-
-static void
 preprocess_text_indic (const hb_ot_shape_plan_t *plan,
 		       hb_buffer_t              *buffer,
 		       hb_font_t                *font)
diff --git a/src/hb-ot-shape-complex-indic.hh b/src/hb-ot-shape-complex-indic.hh
index d207728..1eeed68 100644
--- a/src/hb-ot-shape-complex-indic.hh
+++ b/src/hb-ot-shape-complex-indic.hh
@@ -64,7 +64,14 @@
   OT_Ra = 16,
   OT_CM = 17,  /* Consonant-Medial. */
   OT_Symbol = 18, /* Avagraha, etc that take marks (SM,A,VD). */
-  OT_CS = 19
+  OT_CS = 19,
+
+  /* The following are used by Khmer & Myanmar shapers.  Defined
+   * here for them to share. */
+  OT_VAbv    = 26,
+  OT_VBlw    = 27,
+  OT_VPre    = 28,
+  OT_VPst    = 29,
 };
 
 #define MEDIAL_FLAGS (FLAG (OT_CM))
@@ -398,5 +405,31 @@
   info.indic_position() = pos;
 }
 
+struct hb_indic_would_substitute_feature_t
+{
+  void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
+  {
+    zero_context = zero_context_;
+    map->get_stage_lookups (0/*GSUB*/,
+			    map->get_feature_stage (0/*GSUB*/, feature_tag),
+			    &lookups, &count);
+  }
+
+  bool would_substitute (const hb_codepoint_t *glyphs,
+			 unsigned int          glyphs_count,
+			 hb_face_t            *face) const
+  {
+    for (unsigned int i = 0; i < count; i++)
+      if (hb_ot_layout_lookup_would_substitute (face, lookups[i].index, glyphs, glyphs_count, zero_context))
+	return true;
+    return false;
+  }
+
+  private:
+  const hb_ot_map_t::lookup_map_t *lookups;
+  unsigned int count;
+  bool zero_context;
+};
+
 
 #endif /* HB_OT_SHAPE_COMPLEX_INDIC_HH */
diff --git a/src/hb-ot-shape-complex-khmer-machine.hh b/src/hb-ot-shape-complex-khmer-machine.hh
index 65e0ffc..a040318 100644
--- a/src/hb-ot-shape-complex-khmer-machine.hh
+++ b/src/hb-ot-shape-complex-khmer-machine.hh
@@ -226,13 +226,13 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | khmer_##syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
 static void
-find_syllables (hb_buffer_t *buffer)
+find_syllables_khmer (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act HB_UNUSED;
   int cs;
@@ -367,4 +367,6 @@
 
 }
 
+#undef found_syllable
+
 #endif /* HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-khmer-machine.rl b/src/hb-ot-shape-complex-khmer-machine.rl
index 1076a08..e7f1453 100644
--- a/src/hb-ot-shape-complex-khmer-machine.rl
+++ b/src/hb-ot-shape-complex-khmer-machine.rl
@@ -83,13 +83,13 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | khmer_##syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
 static void
-find_syllables (hb_buffer_t *buffer)
+find_syllables_khmer (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act HB_UNUSED;
   int cs;
@@ -108,4 +108,6 @@
   }%%
 }
 
+#undef found_syllable
+
 #endif /* HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-khmer.cc b/src/hb-ot-shape-complex-khmer.cc
index eba7d86..b1fa015 100644
--- a/src/hb-ot-shape-complex-khmer.cc
+++ b/src/hb-ot-shape-complex-khmer.cc
@@ -69,37 +69,33 @@
  * Must be in the same order as the khmer_features array.
  */
 enum {
-  PREF,
-  BLWF,
-  ABVF,
-  PSTF,
-  CFAR,
+  KHMER_PREF,
+  KHMER_BLWF,
+  KHMER_ABVF,
+  KHMER_PSTF,
+  KHMER_CFAR,
 
-  _PRES,
-  _ABVS,
-  _BLWS,
-  _PSTS,
+  _KHMER_PRES,
+  _KHMER_ABVS,
+  _KHMER_BLWS,
+  _KHMER_PSTS,
 
-  _DIST,
-  _ABVM,
-  _BLWM,
+  _KHMER_DIST,
+  _KHMER_ABVM,
+  _KHMER_BLWM,
 
   KHMER_NUM_FEATURES,
-  KHMER_BASIC_FEATURES = _PRES, /* Don't forget to update this! */
+  KHMER_BASIC_FEATURES = _KHMER_PRES, /* Don't forget to update this! */
 };
 
 static void
-setup_syllables (const hb_ot_shape_plan_t *plan,
-		 hb_font_t *font,
-		 hb_buffer_t *buffer);
+setup_syllables_khmer (const hb_ot_shape_plan_t *plan,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer);
 static void
-reorder (const hb_ot_shape_plan_t *plan,
-	 hb_font_t *font,
-	 hb_buffer_t *buffer);
-static void
-clear_syllables (const hb_ot_shape_plan_t *plan,
-		 hb_font_t *font,
-		 hb_buffer_t *buffer);
+reorder_khmer (const hb_ot_shape_plan_t *plan,
+	       hb_font_t *font,
+	       hb_buffer_t *buffer);
 
 static void
 collect_features_khmer (hb_ot_shape_planner_t *plan)
@@ -107,8 +103,8 @@
   hb_ot_map_builder_t *map = &plan->map;
 
   /* Do this before any lookups have been applied. */
-  map->add_gsub_pause (setup_syllables);
-  map->add_gsub_pause (reorder);
+  map->add_gsub_pause (setup_syllables_khmer);
+  map->add_gsub_pause (reorder_khmer);
 
   /* Testing suggests that Uniscribe does NOT pause between basic
    * features.  Test with KhmerUI.ttf and the following three
@@ -127,7 +123,7 @@
   for (; i < KHMER_BASIC_FEATURES; i++)
     map->add_feature (khmer_features[i]);
 
-  map->add_gsub_pause (clear_syllables);
+  map->add_gsub_pause (_hb_clear_syllables);
 
   for (; i < KHMER_NUM_FEATURES; i++)
     map->add_feature (khmer_features[i]);
@@ -153,32 +149,6 @@
 }
 
 
-struct would_substitute_feature_t
-{
-  void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
-  {
-    zero_context = zero_context_;
-    map->get_stage_lookups (0/*GSUB*/,
-			    map->get_feature_stage (0/*GSUB*/, feature_tag),
-			    &lookups, &count);
-  }
-
-  bool would_substitute (const hb_codepoint_t *glyphs,
-			 unsigned int          glyphs_count,
-			 hb_face_t            *face) const
-  {
-    for (unsigned int i = 0; i < count; i++)
-      if (hb_ot_layout_lookup_would_substitute (face, lookups[i].index, glyphs, glyphs_count, zero_context))
-	return true;
-    return false;
-  }
-
-  private:
-  const hb_ot_map_t::lookup_map_t *lookups;
-  unsigned int count;
-  bool zero_context;
-};
-
 struct khmer_shape_plan_t
 {
   bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
@@ -202,7 +172,7 @@
 
   mutable hb_codepoint_t virama_glyph;
 
-  would_substitute_feature_t pref;
+  hb_indic_would_substitute_feature_t pref;
 
   hb_mask_t mask_array[KHMER_NUM_FEATURES];
 };
@@ -232,10 +202,10 @@
 }
 
 
-enum syllable_type_t {
-  consonant_syllable,
-  broken_cluster,
-  non_khmer_cluster,
+enum khmer_syllable_type_t {
+  khmer_consonant_syllable,
+  khmer_broken_cluster,
+  khmer_non_khmer_cluster,
 };
 
 #include "hb-ot-shape-complex-khmer-machine.hh"
@@ -257,11 +227,11 @@
 }
 
 static void
-setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		 hb_font_t *font HB_UNUSED,
-		 hb_buffer_t *buffer)
+setup_syllables_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		       hb_font_t *font HB_UNUSED,
+		       hb_buffer_t *buffer)
 {
-  find_syllables (buffer);
+  find_syllables_khmer (buffer);
   foreach_syllable (buffer, start, end)
     buffer->unsafe_to_break (start, end);
 }
@@ -282,7 +252,9 @@
   /* Setup masks. */
   {
     /* Post-base */
-    hb_mask_t mask = khmer_plan->mask_array[BLWF] | khmer_plan->mask_array[ABVF] | khmer_plan->mask_array[PSTF];
+    hb_mask_t mask = khmer_plan->mask_array[KHMER_BLWF] |
+		     khmer_plan->mask_array[KHMER_ABVF] |
+		     khmer_plan->mask_array[KHMER_PSTF];
     for (unsigned int i = start + 1; i < end; i++)
       info[i].mask  |= mask;
   }
@@ -309,7 +281,7 @@
       if (info[i + 1].khmer_category() == OT_Ra)
       {
 	for (unsigned int j = 0; j < 2; j++)
-	  info[i + j].mask |= khmer_plan->mask_array[PREF];
+	  info[i + j].mask |= khmer_plan->mask_array[KHMER_PREF];
 
 	/* Move the Coeng,Ro sequence to the start. */
 	buffer->merge_clusters (start, i + 2);
@@ -325,9 +297,9 @@
 	 * U+1784,U+17D2,U+179A,U+17D2,U+1782
 	 * U+1784,U+17D2,U+1782,U+17D2,U+179A
 	 */
-	if (khmer_plan->mask_array[CFAR])
+	if (khmer_plan->mask_array[KHMER_CFAR])
 	  for (unsigned int j = i + 2; j < end; j++)
-	    info[j].mask |= khmer_plan->mask_array[CFAR];
+	    info[j].mask |= khmer_plan->mask_array[KHMER_CFAR];
 
 	num_coengs = 2; /* Done. */
       }
@@ -346,28 +318,28 @@
 }
 
 static void
-initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
-			     hb_face_t *face,
-			     hb_buffer_t *buffer,
-			     unsigned int start, unsigned int end)
+reorder_syllable_khmer (const hb_ot_shape_plan_t *plan,
+			hb_face_t *face,
+			hb_buffer_t *buffer,
+			unsigned int start, unsigned int end)
 {
-  syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
+  khmer_syllable_type_t syllable_type = (khmer_syllable_type_t) (buffer->info[start].syllable() & 0x0F);
   switch (syllable_type)
   {
-    case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
-    case consonant_syllable:
+    case khmer_broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
+    case khmer_consonant_syllable:
      reorder_consonant_syllable (plan, face, buffer, start, end);
      break;
 
-    case non_khmer_cluster:
+    case khmer_non_khmer_cluster:
       break;
   }
 }
 
 static inline void
-insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		       hb_font_t *font,
-		       hb_buffer_t *buffer)
+insert_dotted_circles_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED,
+			     hb_font_t *font,
+			     hb_buffer_t *buffer)
 {
   if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
     return;
@@ -378,7 +350,7 @@
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
-    if ((info[i].syllable() & 0x0F) == broken_cluster)
+    if ((info[i].syllable() & 0x0F) == khmer_broken_cluster)
     {
       has_broken_syllables = true;
       break;
@@ -403,8 +375,8 @@
   while (buffer->idx < buffer->len && buffer->successful)
   {
     unsigned int syllable = buffer->cur().syllable();
-    syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
-    if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
+    khmer_syllable_type_t syllable_type = (khmer_syllable_type_t) (syllable & 0x0F);
+    if (unlikely (last_syllable != syllable && syllable_type == khmer_broken_cluster))
     {
       last_syllable = syllable;
 
@@ -428,29 +400,18 @@
 }
 
 static void
-reorder (const hb_ot_shape_plan_t *plan,
-	 hb_font_t *font,
-	 hb_buffer_t *buffer)
+reorder_khmer (const hb_ot_shape_plan_t *plan,
+	       hb_font_t *font,
+	       hb_buffer_t *buffer)
 {
-  insert_dotted_circles (plan, font, buffer);
+  insert_dotted_circles_khmer (plan, font, buffer);
 
   foreach_syllable (buffer, start, end)
-    initial_reordering_syllable (plan, font->face, buffer, start, end);
+    reorder_syllable_khmer (plan, font->face, buffer, start, end);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category);
 }
 
-static void
-clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		 hb_font_t *font HB_UNUSED,
-		 hb_buffer_t *buffer)
-{
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    info[i].syllable() = 0;
-}
-
 
 static bool
 decompose_khmer (const hb_ot_shape_normalize_context_t *c,
diff --git a/src/hb-ot-shape-complex-khmer.hh b/src/hb-ot-shape-complex-khmer.hh
index 21015c7..11a77bf 100644
--- a/src/hb-ot-shape-complex-khmer.hh
+++ b/src/hb-ot-shape-complex-khmer.hh
@@ -43,11 +43,10 @@
   OT_Robatic = 20,
   OT_Xgroup  = 21,
   OT_Ygroup  = 22,
-
-  OT_VAbv    = 26,
-  OT_VBlw    = 27,
-  OT_VPre    = 28,
-  OT_VPst    = 29,
+  //OT_VAbv = 26,
+  //OT_VBlw = 27,
+  //OT_VPre = 28,
+  //OT_VPst = 29,
 };
 
 static inline void
@@ -100,10 +99,10 @@
   if (cat == (khmer_category_t) OT_M)
     switch ((int) pos)
     {
-      case POS_PRE_C:	cat = OT_VPre; break;
-      case POS_BELOW_C:	cat = OT_VBlw; break;
-      case POS_ABOVE_C:	cat = OT_VAbv; break;
-      case POS_POST_C:	cat = OT_VPst; break;
+      case POS_PRE_C:	cat = (khmer_category_t) OT_VPre; break;
+      case POS_BELOW_C:	cat = (khmer_category_t) OT_VBlw; break;
+      case POS_ABOVE_C:	cat = (khmer_category_t) OT_VAbv; break;
+      case POS_POST_C:	cat = (khmer_category_t) OT_VPst; break;
       default: assert (0);
     }
 
diff --git a/src/hb-ot-shape-complex-myanmar-machine.hh b/src/hb-ot-shape-complex-myanmar-machine.hh
index b7b04cb..c2f4c00 100644
--- a/src/hb-ot-shape-complex-myanmar-machine.hh
+++ b/src/hb-ot-shape-complex-myanmar-machine.hh
@@ -304,13 +304,13 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | myanmar_##syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
 static void
-find_syllables (hb_buffer_t *buffer)
+find_syllables_myanmar (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act HB_UNUSED;
   int cs;
diff --git a/src/hb-ot-shape-complex-myanmar-machine.rl b/src/hb-ot-shape-complex-myanmar-machine.rl
index 6659989..67133cd 100644
--- a/src/hb-ot-shape-complex-myanmar-machine.rl
+++ b/src/hb-ot-shape-complex-myanmar-machine.rl
@@ -97,13 +97,13 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | myanmar_##syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
 static void
-find_syllables (hb_buffer_t *buffer)
+find_syllables_myanmar (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act HB_UNUSED;
   int cs;
diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc
index 5b819cf..14d215e 100644
--- a/src/hb-ot-shape-complex-myanmar.cc
+++ b/src/hb-ot-shape-complex-myanmar.cc
@@ -36,7 +36,7 @@
  */
 
 static const hb_tag_t
-basic_features[] =
+myanmar_basic_features[] =
 {
   /*
    * Basic features.
@@ -48,7 +48,7 @@
   HB_TAG('p','s','t','f'),
 };
 static const hb_tag_t
-other_features[] =
+myanmar_other_features[] =
 {
   /*
    * Other features.
@@ -60,7 +60,7 @@
   HB_TAG('p','s','t','s'),
 };
 static const hb_tag_t
-positioning_features[] =
+myanmar_positioning_features[] =
 {
   /*
    * Positioning features.
@@ -80,15 +80,11 @@
 };
 
 static void
-setup_syllables (const hb_ot_shape_plan_t *plan,
-		 hb_font_t *font,
-		 hb_buffer_t *buffer);
+setup_syllables_myanmar (const hb_ot_shape_plan_t *plan,
+			 hb_font_t *font,
+			 hb_buffer_t *buffer);
 static void
-reorder (const hb_ot_shape_plan_t *plan,
-	 hb_font_t *font,
-	 hb_buffer_t *buffer);
-static void
-clear_syllables (const hb_ot_shape_plan_t *plan,
+reorder_myanmar (const hb_ot_shape_plan_t *plan,
 		 hb_font_t *font,
 		 hb_buffer_t *buffer);
 
@@ -98,7 +94,7 @@
   hb_ot_map_builder_t *map = &plan->map;
 
   /* Do this before any lookups have been applied. */
-  map->add_gsub_pause (setup_syllables);
+  map->add_gsub_pause (setup_syllables_myanmar);
 
   map->enable_feature (HB_TAG('l','o','c','l'));
   /* The Indic specs do not require ccmp, but we apply it here since if
@@ -106,21 +102,21 @@
   map->enable_feature (HB_TAG('c','c','m','p'));
 
 
-  map->add_gsub_pause (reorder);
+  map->add_gsub_pause (reorder_myanmar);
 
-  for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
+  for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_basic_features); i++)
   {
-    map->enable_feature (basic_features[i], F_MANUAL_ZWJ);
+    map->enable_feature (myanmar_basic_features[i], F_MANUAL_ZWJ);
     map->add_gsub_pause (nullptr);
   }
 
-  map->add_gsub_pause (clear_syllables);
+  map->add_gsub_pause (_hb_clear_syllables);
 
-  for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
-    map->enable_feature (other_features[i], F_MANUAL_ZWJ);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_other_features); i++)
+    map->enable_feature (myanmar_other_features[i], F_MANUAL_ZWJ);
 
-  for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++)
-    map->enable_feature (positioning_features[i]);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_positioning_features); i++)
+    map->enable_feature (myanmar_positioning_features[i]);
 }
 
 static void
@@ -130,11 +126,11 @@
 }
 
 
-enum syllable_type_t {
-  consonant_syllable,
-  punctuation_cluster,
-  broken_cluster,
-  non_myanmar_cluster,
+enum myanmar_syllable_type_t {
+  myanmar_consonant_syllable,
+  myanmar_punctuation_cluster,
+  myanmar_broken_cluster,
+  myanmar_non_myanmar_cluster,
 };
 
 #include "hb-ot-shape-complex-myanmar-machine.hh"
@@ -158,11 +154,11 @@
 }
 
 static void
-setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		 hb_font_t *font HB_UNUSED,
-		 hb_buffer_t *buffer)
+setup_syllables_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
+			 hb_font_t *font HB_UNUSED,
+			 hb_buffer_t *buffer)
 {
-  find_syllables (buffer);
+  find_syllables_myanmar (buffer);
   foreach_syllable (buffer, start, end)
     buffer->unsafe_to_break (start, end);
 }
@@ -278,29 +274,29 @@
 }
 
 static void
-initial_reordering_syllable (const hb_ot_shape_plan_t *plan HB_UNUSED,
-			     hb_face_t *face HB_UNUSED,
-			     hb_buffer_t *buffer,
-			     unsigned int start, unsigned int end)
+reorder_syllable_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
+			  hb_face_t *face HB_UNUSED,
+			  hb_buffer_t *buffer,
+			  unsigned int start, unsigned int end)
 {
-  syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
+  myanmar_syllable_type_t syllable_type = (myanmar_syllable_type_t) (buffer->info[start].syllable() & 0x0F);
   switch (syllable_type) {
 
-    case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
-    case consonant_syllable:
+    case myanmar_broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
+    case myanmar_consonant_syllable:
       initial_reordering_consonant_syllable  (buffer, start, end);
       break;
 
-    case punctuation_cluster:
-    case non_myanmar_cluster:
+    case myanmar_punctuation_cluster:
+    case myanmar_non_myanmar_cluster:
       break;
   }
 }
 
 static inline void
-insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		       hb_font_t *font,
-		       hb_buffer_t *buffer)
+insert_dotted_circles_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
+			       hb_font_t *font,
+			       hb_buffer_t *buffer)
 {
   if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
     return;
@@ -311,7 +307,7 @@
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
-    if ((info[i].syllable() & 0x0F) == broken_cluster)
+    if ((info[i].syllable() & 0x0F) == myanmar_broken_cluster)
     {
       has_broken_syllables = true;
       break;
@@ -336,8 +332,8 @@
   while (buffer->idx < buffer->len && buffer->successful)
   {
     unsigned int syllable = buffer->cur().syllable();
-    syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
-    if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
+    myanmar_syllable_type_t syllable_type = (myanmar_syllable_type_t) (syllable & 0x0F);
+    if (unlikely (last_syllable != syllable && syllable_type == myanmar_broken_cluster))
     {
       last_syllable = syllable;
 
@@ -355,30 +351,19 @@
 }
 
 static void
-reorder (const hb_ot_shape_plan_t *plan,
-	 hb_font_t *font,
-	 hb_buffer_t *buffer)
+reorder_myanmar (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font,
+		 hb_buffer_t *buffer)
 {
-  insert_dotted_circles (plan, font, buffer);
+  insert_dotted_circles_myanmar (plan, font, buffer);
 
   foreach_syllable (buffer, start, end)
-    initial_reordering_syllable (plan, font->face, buffer, start, end);
+    reorder_syllable_myanmar (plan, font->face, buffer, start, end);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position);
 }
 
-static void
-clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		 hb_font_t *font HB_UNUSED,
-		 hb_buffer_t *buffer)
-{
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    info[i].syllable() = 0;
-}
-
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
 {
diff --git a/src/hb-ot-shape-complex-myanmar.hh b/src/hb-ot-shape-complex-myanmar.hh
index 9ec78ef..7b9821e 100644
--- a/src/hb-ot-shape-complex-myanmar.hh
+++ b/src/hb-ot-shape-complex-myanmar.hh
@@ -49,10 +49,10 @@
   OT_MW  = 23, /* Various consonant medial types */
   OT_MY  = 24, /* Various consonant medial types */
   OT_PT  = 25, /* Pwo and other tones */
-  OT_VAbv = 26,
-  OT_VBlw = 27,
-  OT_VPre = 28,
-  OT_VPst = 29,
+  //OT_VAbv = 26,
+  //OT_VBlw = 27,
+  //OT_VPre = 28,
+  //OT_VPst = 29,
   OT_VS   = 30, /* Variation selectors */
   OT_P    = 31, /* Punctuation */
   OT_D    = 32, /* Digits except zero */
@@ -155,11 +155,11 @@
   {
     switch ((int) pos)
     {
-      case POS_PRE_C:	cat = OT_VPre;
+      case POS_PRE_C:	cat = (myanmar_category_t) OT_VPre;
 			pos = POS_PRE_M; break;
-      case POS_ABOVE_C:	cat = OT_VAbv;   break;
-      case POS_BELOW_C:	cat = OT_VBlw;   break;
-      case POS_POST_C:	cat = OT_VPst;   break;
+      case POS_ABOVE_C:	cat = (myanmar_category_t) OT_VAbv;   break;
+      case POS_BELOW_C:	cat = (myanmar_category_t) OT_VBlw;   break;
+      case POS_POST_C:	cat = (myanmar_category_t) OT_VPst;   break;
     }
   }
 
diff --git a/src/hb-ot-shape-complex-use-machine.hh b/src/hb-ot-shape-complex-use-machine.hh
index 01fe3d7..462342c 100644
--- a/src/hb-ot-shape-complex-use-machine.hh
+++ b/src/hb-ot-shape-complex-use-machine.hh
@@ -380,13 +380,13 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | use_##syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
 static void
-find_syllables (hb_buffer_t *buffer)
+find_syllables_use (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act;
   int cs;
diff --git a/src/hb-ot-shape-complex-use-machine.rl b/src/hb-ot-shape-complex-use-machine.rl
index aca7ea6..9b75b5c 100644
--- a/src/hb-ot-shape-complex-use-machine.rl
+++ b/src/hb-ot-shape-complex-use-machine.rl
@@ -165,13 +165,13 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | use_##syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
 static void
-find_syllables (hb_buffer_t *buffer)
+find_syllables_use (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act;
   int cs;
diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc
index 91c0b8c..1ea2957 100644
--- a/src/hb-ot-shape-complex-use.cc
+++ b/src/hb-ot-shape-complex-use.cc
@@ -44,7 +44,7 @@
  */
 
 static const hb_tag_t
-basic_features[] =
+use_basic_features[] =
 {
   /*
    * Basic features.
@@ -59,28 +59,23 @@
   HB_TAG('c','j','c','t'),
 };
 static const hb_tag_t
-arabic_features[] =
+use_topographical_features[] =
 {
   HB_TAG('i','s','o','l'),
   HB_TAG('i','n','i','t'),
   HB_TAG('m','e','d','i'),
   HB_TAG('f','i','n','a'),
-  /* The spec doesn't specify these but we apply anyway, since our Arabic shaper
-   * does.  These are only used in Syriac spec. */
-  HB_TAG('m','e','d','2'),
-  HB_TAG('f','i','n','2'),
-  HB_TAG('f','i','n','3'),
 };
-/* Same order as arabic_features.  Don't need Syriac stuff.*/
+/* Same order as use_topographical_features. */
 enum joining_form_t {
-  ISOL,
-  INIT,
-  MEDI,
-  FINA,
-  _NONE
+  USE_ISOL,
+  USE_INIT,
+  USE_MEDI,
+  USE_FINA,
+  _USE_NONE
 };
 static const hb_tag_t
-other_features[] =
+use_other_features[] =
 {
   /*
    * Other features.
@@ -94,7 +89,7 @@
   HB_TAG('p','s','t','s'),
 };
 static const hb_tag_t
-positioning_features[] =
+use_positioning_features[] =
 {
   /*
    * Positioning features.
@@ -106,29 +101,21 @@
 };
 
 static void
-setup_syllables (const hb_ot_shape_plan_t *plan,
+setup_syllables_use (const hb_ot_shape_plan_t *plan,
+		     hb_font_t *font,
+		     hb_buffer_t *buffer);
+static void
+record_rphf_use (const hb_ot_shape_plan_t *plan,
 		 hb_font_t *font,
 		 hb_buffer_t *buffer);
 static void
-clear_substitution_flags (const hb_ot_shape_plan_t *plan,
-			  hb_font_t *font,
-			  hb_buffer_t *buffer);
-static void
-record_rphf (const hb_ot_shape_plan_t *plan,
-	     hb_font_t *font,
-	     hb_buffer_t *buffer);
-static void
-record_pref (const hb_ot_shape_plan_t *plan,
-	     hb_font_t *font,
-	     hb_buffer_t *buffer);
-static void
-reorder (const hb_ot_shape_plan_t *plan,
-	 hb_font_t *font,
-	 hb_buffer_t *buffer);
-static void
-clear_syllables (const hb_ot_shape_plan_t *plan,
+record_pref_use (const hb_ot_shape_plan_t *plan,
 		 hb_font_t *font,
 		 hb_buffer_t *buffer);
+static void
+reorder_use (const hb_ot_shape_plan_t *plan,
+	     hb_font_t *font,
+	     hb_buffer_t *buffer);
 
 static void
 collect_features_use (hb_ot_shape_planner_t *plan)
@@ -136,7 +123,7 @@
   hb_ot_map_builder_t *map = &plan->map;
 
   /* Do this before any lookups have been applied. */
-  map->add_gsub_pause (setup_syllables);
+  map->add_gsub_pause (setup_syllables_use);
 
   /* "Default glyph pre-processing group" */
   map->enable_feature (HB_TAG('l','o','c','l'));
@@ -145,32 +132,32 @@
   map->enable_feature (HB_TAG('a','k','h','n'), F_MANUAL_ZWJ);
 
   /* "Reordering group" */
-  map->add_gsub_pause (clear_substitution_flags);
+  map->add_gsub_pause (_hb_clear_substitution_flags);
   map->add_feature (HB_TAG('r','p','h','f'), F_MANUAL_ZWJ);
-  map->add_gsub_pause (record_rphf);
-  map->add_gsub_pause (clear_substitution_flags);
+  map->add_gsub_pause (record_rphf_use);
+  map->add_gsub_pause (_hb_clear_substitution_flags);
   map->enable_feature (HB_TAG('p','r','e','f'), F_MANUAL_ZWJ);
-  map->add_gsub_pause (record_pref);
+  map->add_gsub_pause (record_pref_use);
 
   /* "Orthographic unit shaping group" */
-  for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
-    map->enable_feature (basic_features[i], F_MANUAL_ZWJ);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (use_basic_features); i++)
+    map->enable_feature (use_basic_features[i], F_MANUAL_ZWJ);
 
-  map->add_gsub_pause (reorder);
-  map->add_gsub_pause (clear_syllables);
+  map->add_gsub_pause (reorder_use);
+  map->add_gsub_pause (_hb_clear_syllables);
 
   /* "Topographical features" */
-  for (unsigned int i = 0; i < ARRAY_LENGTH (arabic_features); i++)
-    map->add_feature (arabic_features[i]);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (use_topographical_features); i++)
+    map->add_feature (use_topographical_features[i]);
   map->add_gsub_pause (nullptr);
 
   /* "Standard typographic presentation" */
-  for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
-    map->enable_feature (other_features[i], F_MANUAL_ZWJ);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (use_other_features); i++)
+    map->enable_feature (use_other_features[i], F_MANUAL_ZWJ);
 
   /* "Positional feature application" */
-  for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++)
-    map->enable_feature (positioning_features[i]);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (use_positioning_features); i++)
+    map->enable_feature (use_positioning_features[i]);
 }
 
 struct use_shape_plan_t
@@ -247,16 +234,16 @@
   free (data);
 }
 
-enum syllable_type_t {
-  independent_cluster,
-  virama_terminated_cluster,
-  sakot_terminated_cluster,
-  standard_cluster,
-  number_joiner_terminated_cluster,
-  numeral_cluster,
-  symbol_cluster,
-  broken_cluster,
-  non_cluster,
+enum use_syllable_type_t {
+  use_independent_cluster,
+  use_virama_terminated_cluster,
+  use_sakot_terminated_cluster,
+  use_standard_cluster,
+  use_number_joiner_terminated_cluster,
+  use_numeral_cluster,
+  use_symbol_cluster,
+  use_broken_cluster,
+  use_non_cluster,
 };
 
 #include "hb-ot-shape-complex-use-machine.hh"
@@ -313,11 +300,11 @@
   if (use_plan->arabic_plan)
     return;
 
-  static_assert ((INIT < 4 && ISOL < 4 && MEDI < 4 && FINA < 4), "");
+  static_assert ((USE_INIT < 4 && USE_ISOL < 4 && USE_MEDI < 4 && USE_FINA < 4), "");
   hb_mask_t masks[4], all_masks = 0;
   for (unsigned int i = 0; i < 4; i++)
   {
-    masks[i] = plan->map.get_1_mask (arabic_features[i]);
+    masks[i] = plan->map.get_1_mask (use_topographical_features[i]);
     if (masks[i] == plan->map.get_global_mask ())
       masks[i] = 0;
     all_masks |= masks[i];
@@ -327,39 +314,39 @@
   hb_mask_t other_masks = ~all_masks;
 
   unsigned int last_start = 0;
-  joining_form_t last_form = _NONE;
+  joining_form_t last_form = _USE_NONE;
   hb_glyph_info_t *info = buffer->info;
   foreach_syllable (buffer, start, end)
   {
-    syllable_type_t syllable_type = (syllable_type_t) (info[start].syllable() & 0x0F);
+    use_syllable_type_t syllable_type = (use_syllable_type_t) (info[start].syllable() & 0x0F);
     switch (syllable_type)
     {
-      case independent_cluster:
-      case symbol_cluster:
-      case non_cluster:
+      case use_independent_cluster:
+      case use_symbol_cluster:
+      case use_non_cluster:
 	/* These don't join.  Nothing to do. */
-	last_form = _NONE;
+	last_form = _USE_NONE;
 	break;
 
-      case virama_terminated_cluster:
-      case sakot_terminated_cluster:
-      case standard_cluster:
-      case number_joiner_terminated_cluster:
-      case numeral_cluster:
-      case broken_cluster:
+      case use_virama_terminated_cluster:
+      case use_sakot_terminated_cluster:
+      case use_standard_cluster:
+      case use_number_joiner_terminated_cluster:
+      case use_numeral_cluster:
+      case use_broken_cluster:
 
-	bool join = last_form == FINA || last_form == ISOL;
+	bool join = last_form == USE_FINA || last_form == USE_ISOL;
 
 	if (join)
 	{
 	  /* Fixup previous syllable's form. */
-	  last_form = last_form == FINA ? MEDI : INIT;
+	  last_form = last_form == USE_FINA ? USE_MEDI : USE_INIT;
 	  for (unsigned int i = last_start; i < start; i++)
 	    info[i].mask = (info[i].mask & other_masks) | masks[last_form];
 	}
 
 	/* Form for this syllable. */
-	last_form = join ? FINA : ISOL;
+	last_form = join ? USE_FINA : USE_ISOL;
 	for (unsigned int i = start; i < end; i++)
 	  info[i].mask = (info[i].mask & other_masks) | masks[last_form];
 
@@ -371,11 +358,11 @@
 }
 
 static void
-setup_syllables (const hb_ot_shape_plan_t *plan,
-		 hb_font_t *font HB_UNUSED,
-		 hb_buffer_t *buffer)
+setup_syllables_use (const hb_ot_shape_plan_t *plan,
+		     hb_font_t *font HB_UNUSED,
+		     hb_buffer_t *buffer)
 {
-  find_syllables (buffer);
+  find_syllables_use (buffer);
   foreach_syllable (buffer, start, end)
     buffer->unsafe_to_break (start, end);
   setup_rphf_mask (plan, buffer);
@@ -383,20 +370,9 @@
 }
 
 static void
-clear_substitution_flags (const hb_ot_shape_plan_t *plan HB_UNUSED,
-			  hb_font_t *font HB_UNUSED,
-			  hb_buffer_t *buffer)
-{
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    _hb_glyph_info_clear_substituted (&info[i]);
-}
-
-static void
-record_rphf (const hb_ot_shape_plan_t *plan,
-	     hb_font_t *font HB_UNUSED,
-	     hb_buffer_t *buffer)
+record_rphf_use (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font HB_UNUSED,
+		 hb_buffer_t *buffer)
 {
   const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
 
@@ -417,9 +393,9 @@
 }
 
 static void
-record_pref (const hb_ot_shape_plan_t *plan HB_UNUSED,
-	     hb_font_t *font HB_UNUSED,
-	     hb_buffer_t *buffer)
+record_pref_use (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		 hb_font_t *font HB_UNUSED,
+		 hb_buffer_t *buffer)
 {
   hb_glyph_info_t *info = buffer->info;
 
@@ -436,22 +412,22 @@
 }
 
 static inline bool
-is_halant (const hb_glyph_info_t &info)
+is_halant_use (const hb_glyph_info_t &info)
 {
   return (info.use_category() == USE_H || info.use_category() == USE_HVM) &&
 	 !_hb_glyph_info_ligated (&info);
 }
 
 static void
-reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end)
+reorder_syllable_use (hb_buffer_t *buffer, unsigned int start, unsigned int end)
 {
-  syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
+  use_syllable_type_t syllable_type = (use_syllable_type_t) (buffer->info[start].syllable() & 0x0F);
   /* Only a few syllable types need reordering. */
   if (unlikely (!(FLAG_UNSAFE (syllable_type) &
-		  (FLAG (virama_terminated_cluster) |
-		   FLAG (sakot_terminated_cluster) |
-		   FLAG (standard_cluster) |
-		   FLAG (broken_cluster) |
+		  (FLAG (use_virama_terminated_cluster) |
+		   FLAG (use_sakot_terminated_cluster) |
+		   FLAG (use_standard_cluster) |
+		   FLAG (use_broken_cluster) |
 		   0))))
     return;
 
@@ -482,7 +458,7 @@
     for (unsigned int i = start + 1; i < end; i++)
     {
       bool is_post_base_glyph = (FLAG64_UNSAFE (info[i].use_category()) & POST_BASE_FLAGS64) ||
-				is_halant (info[i]);
+				is_halant_use (info[i]);
       if (is_post_base_glyph || i == end - 1)
       {
 	/* If we hit a post-base glyph, move before it; otherwise move to the
@@ -506,7 +482,7 @@
   for (unsigned int i = start; i < end; i++)
   {
     uint32_t flag = FLAG_UNSAFE (info[i].use_category());
-    if (is_halant (info[i]))
+    if (is_halant_use (info[i]))
     {
       /* If we hit a halant, move after it; otherwise move to the beginning, and
        * shift things in between forward. */
@@ -526,9 +502,9 @@
 }
 
 static inline void
-insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		       hb_font_t *font,
-		       hb_buffer_t *buffer)
+insert_dotted_circles_use (const hb_ot_shape_plan_t *plan HB_UNUSED,
+			   hb_font_t *font,
+			   hb_buffer_t *buffer)
 {
   if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
     return;
@@ -539,7 +515,7 @@
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
-    if ((info[i].syllable() & 0x0F) == broken_cluster)
+    if ((info[i].syllable() & 0x0F) == use_broken_cluster)
     {
       has_broken_syllables = true;
       break;
@@ -559,8 +535,8 @@
   while (buffer->idx < buffer->len && buffer->successful)
   {
     unsigned int syllable = buffer->cur().syllable();
-    syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
-    if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
+    use_syllable_type_t syllable_type = (use_syllable_type_t) (syllable & 0x0F);
+    if (unlikely (last_syllable != syllable && syllable_type == use_broken_cluster))
     {
       last_syllable = syllable;
 
@@ -584,29 +560,18 @@
 }
 
 static void
-reorder (const hb_ot_shape_plan_t *plan,
-	 hb_font_t *font,
-	 hb_buffer_t *buffer)
+reorder_use (const hb_ot_shape_plan_t *plan,
+	     hb_font_t *font,
+	     hb_buffer_t *buffer)
 {
-  insert_dotted_circles (plan, font, buffer);
+  insert_dotted_circles_use (plan, font, buffer);
 
   foreach_syllable (buffer, start, end)
-    reorder_syllable (buffer, start, end);
+    reorder_syllable_use (buffer, start, end);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, use_category);
 }
 
-static void
-clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
-		 hb_font_t *font HB_UNUSED,
-		 hb_buffer_t *buffer)
-{
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    info[i].syllable() = 0;
-}
-
 
 static void
 preprocess_text_use (const hb_ot_shape_plan_t *plan,
diff --git a/src/hb-ot-var.h b/src/hb-ot-var.h
index cf6f0c9..df89bc5 100644
--- a/src/hb-ot-var.h
+++ b/src/hb-ot-var.h
@@ -68,7 +68,7 @@
 typedef enum { /*< flags >*/
   HB_OT_VAR_AXIS_FLAG_HIDDEN	= 0x00000001u,
 
-  _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= 0x7FFFFFFFu /*< skip >*/
+  _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= HB_TAG_MAX_SIGNED /*< skip >*/
 } hb_ot_var_axis_flags_t;
 
 /**
diff --git a/src/hb-ot-vorg-table.hh b/src/hb-ot-vorg-table.hh
index 19e08eb..d9002f3 100644
--- a/src/hb-ot-vorg-table.hh
+++ b/src/hb-ot-vorg-table.hh
@@ -85,7 +85,7 @@
     this->vertYOrigins.len = it.len ();
 
     + it
-    | hb_apply ([c] (const VertOriginMetric& _) { c->copy (_);})
+    | hb_apply ([c] (const VertOriginMetric& _) { c->copy (_); })
     ;
   }
 
diff --git a/src/hb-ot.h b/src/hb-ot.h
index db78469..f2dbaa1 100644
--- a/src/hb-ot.h
+++ b/src/hb-ot.h
@@ -35,6 +35,8 @@
 #include "hb-ot-font.h"
 #include "hb-ot-layout.h"
 #include "hb-ot-math.h"
+#include "hb-ot-meta.h"
+#include "hb-ot-metrics.h"
 #include "hb-ot-name.h"
 #include "hb-ot-shape.h"
 #include "hb-ot-var.h"
diff --git a/src/hb-set.cc b/src/hb-set.cc
index fa98688..10638a7 100644
--- a/src/hb-set.cc
+++ b/src/hb-set.cc
@@ -479,7 +479,7 @@
  * @set: a set.
  * @codepoint: (inout):
  *
- * Gets the previous number in @set that is slower than current value of @codepoint.
+ * Gets the previous number in @set that is lower than current value of @codepoint.
  *
  * Set @codepoint to %HB_SET_VALUE_INVALID to get started.
  *
@@ -524,7 +524,7 @@
  * @last: (out): output last codepoint in the range.
  *
  * Gets the previous consecutive range of numbers in @set that
- * are greater than current value of @last.
+ * are less than current value of @first.
  *
  * Set @first to %HB_SET_VALUE_INVALID to get started.
  *
diff --git a/src/hb-shape-plan.hh b/src/hb-shape-plan.hh
index 8e8d763..6da7edb 100644
--- a/src/hb-shape-plan.hh
+++ b/src/hb-shape-plan.hh
@@ -46,16 +46,16 @@
   hb_shape_func_t         *shaper_func;
   const char              *shaper_name;
 
-  HB_INTERNAL inline bool init (bool                           copy,
-				hb_face_t                     *face,
-				const hb_segment_properties_t *props,
-				const hb_feature_t            *user_features,
-				unsigned int                   num_user_features,
-				const int                     *coords,
-				unsigned int                   num_coords,
-				const char * const            *shaper_list);
+  HB_INTERNAL bool init (bool                           copy,
+			 hb_face_t                     *face,
+			 const hb_segment_properties_t *props,
+			 const hb_feature_t            *user_features,
+			 unsigned int                   num_user_features,
+			 const int                     *coords,
+			 unsigned int                   num_coords,
+			 const char * const            *shaper_list);
 
-  HB_INTERNAL inline void free () { ::free ((void *) user_features); }
+  HB_INTERNAL void free () { ::free ((void *) user_features); }
 
   HB_INTERNAL bool user_features_match (const hb_shape_plan_key_t *other);
 
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 6b33c17..e244da1 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -179,7 +179,7 @@
 
     unsigned max_glyph =
     + hb_iter (all_gids_to_retain)
-    | hb_reduce (hb_max, 0)
+    | hb_reduce (hb_max, 0u)
     ;
     *num_glyphs = max_glyph + 1;
   }
diff --git a/src/hb-ucd.cc b/src/hb-ucd.cc
index 69949a2..b29f2a9 100644
--- a/src/hb-ucd.cc
+++ b/src/hb-ucd.cc
@@ -15,6 +15,7 @@
  */
 
 #include "hb.hh"
+#include "hb-unicode.hh"
 #include "hb-machinery.hh"
 
 #include "hb-ucd-table.hh"
@@ -235,10 +236,6 @@
 }
 #endif
 
-extern "C" HB_INTERNAL
-hb_unicode_funcs_t *
-hb_ucd_get_unicode_funcs ();
-
 hb_unicode_funcs_t *
 hb_ucd_get_unicode_funcs ()
 {
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index ed4fb77..9a81840 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -126,10 +126,12 @@
 }
 #endif
 
-
-extern "C" hb_unicode_funcs_t *hb_ucd_get_unicode_funcs ();
-extern "C" hb_unicode_funcs_t *hb_glib_get_unicode_funcs ();
-extern "C" hb_unicode_funcs_t *hb_icu_get_unicode_funcs ();
+#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_GLIB)
+#include "hb-glib.h"
+#endif
+#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN)
+#include "hb-icu.h"
+#endif
 
 hb_unicode_funcs_t *
 hb_unicode_funcs_get_default ()
diff --git a/src/hb-unicode.hh b/src/hb-unicode.hh
index 021fa46..0c355f1 100644
--- a/src/hb-unicode.hh
+++ b/src/hb-unicode.hh
@@ -105,9 +105,6 @@
   unsigned int
   modified_combining_class (hb_codepoint_t u)
   {
-    /* XXX This hack belongs to the Myanmar shaper. */
-    if (unlikely (u == 0x1037u)) u = 0x103Au;
-
     /* XXX This hack belongs to the USE shaper (for Tai Tham):
      * Reorder SAKOT to ensure it comes after any tone marks. */
     if (unlikely (u == 0x1A60u)) return 254;
@@ -395,4 +392,7 @@
 _hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp);
 
 
+extern "C" HB_INTERNAL hb_unicode_funcs_t *hb_ucd_get_unicode_funcs ();
+
+
 #endif /* HB_UNICODE_HH */
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 5c7ff87..289a347 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -58,13 +58,6 @@
  * Functions for using HarfBuzz with the Windows fonts.
  **/
 
-
-static inline uint16_t hb_uint16_swap (const uint16_t v)
-{ return (v >> 8) | (v << 8); }
-static inline uint32_t hb_uint32_swap (const uint32_t v)
-{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
-
-
 typedef HRESULT (WINAPI *SIOT) /*ScriptItemizeOpenType*/(
   const WCHAR *pwcInChars,
   int cInChars,
@@ -245,8 +238,9 @@
   }
 };
 
-
+#if HB_USE_ATEXIT
 static void free_static_uniscribe_shaper_funcs ();
+#endif
 
 static struct hb_uniscribe_shaper_funcs_lazy_loader_t : hb_lazy_loader_t<hb_uniscribe_shaper_funcs_t,
 									 hb_uniscribe_shaper_funcs_lazy_loader_t>
diff --git a/src/hb-vector.hh b/src/hb-vector.hh
index de16c97..035c6d0 100644
--- a/src/hb-vector.hh
+++ b/src/hb-vector.hh
@@ -143,13 +143,13 @@
   operator writer_t ()       { return writer (); }
 
   hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
   hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
   hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
   hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
-  { return as_array ().sub_array (start_offset, count);}
+  { return as_array ().sub_array (start_offset, count); }
 
   hb_sorted_array_t<Type> as_sorted_array ()
   { return hb_sorted_array (arrayZ, length); }
diff --git a/src/hb.hh b/src/hb.hh
index bee39cd..8dcd5aa 100644
--- a/src/hb.hh
+++ b/src/hb.hh
@@ -98,6 +98,7 @@
 #ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_WARNING
 #pragma GCC diagnostic warning "-Wbuiltin-macro-redefined"
 #pragma GCC diagnostic warning "-Wdeprecated"
+#pragma GCC diagnostic warning "-Wdeprecated-declarations"
 #pragma GCC diagnostic warning "-Wdisabled-optimization"
 #pragma GCC diagnostic warning "-Wdouble-promotion"
 #pragma GCC diagnostic warning "-Wformat=2"
@@ -317,7 +318,8 @@
 #  define HB_FALLTHROUGH /* FALLTHROUGH */
 #endif
 
-#ifdef __clang__
+/* https://github.com/harfbuzz/harfbuzz/issues/1852 */
+#if defined(__clang__) && !(defined(_AIX) && (defined(__IBMCPP__) || defined(__ibmxl__)))
 /* Disable certain sanitizer errors. */
 /* https://github.com/harfbuzz/harfbuzz/issues/1247 */
 #define HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW __attribute__((no_sanitize("signed-integer-overflow")))
@@ -475,6 +477,11 @@
 /* Size signifying variable-sized array */
 #define VAR 1
 
+/* Endian swap, used in Windows related backends */
+static inline uint16_t hb_uint16_swap (const uint16_t v)
+{ return (v >> 8) | (v << 8); }
+static inline uint32_t hb_uint32_swap (const uint32_t v)
+{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
 
 /*
  * Big-endian integers.  Here because fundamental.
diff --git a/src/test-ot-meta.cc b/src/test-ot-meta.cc
new file mode 100644
index 0000000..512df5a
--- /dev/null
+++ b/src/test-ot-meta.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb.hh"
+#include "hb-ot.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef HB_NO_OPEN
+#define hb_blob_create_from_file(x)  hb_blob_get_empty ()
+#endif
+
+int
+main (int argc, char **argv)
+{
+  if (argc != 2) {
+    fprintf (stderr, "usage: %s font-file\n", argv[0]);
+    exit (1);
+  }
+
+  hb_blob_t *blob = hb_blob_create_from_file (argv[1]);
+  hb_face_t *face = hb_face_create (blob, 0 /* first face */);
+  hb_blob_destroy (blob);
+  blob = nullptr;
+
+  unsigned int count = 0;
+
+#ifndef HB_NO_META
+  count = hb_ot_meta_get_entries (face, 0, nullptr, nullptr);
+
+  hb_ot_meta_tag_t *tags = (hb_ot_meta_tag_t *)
+			   malloc (sizeof (hb_ot_meta_tag_t) * count);
+  hb_ot_meta_get_entries (face, 0, &count, tags);
+  for (unsigned i = 0; i < count; ++i)
+  {
+    hb_blob_t *entry = hb_ot_meta_reference_entry (face, tags[i]);
+    printf ("%c%c%c%c, size: %d: %.*s\n",
+	    HB_UNTAG (tags[i]), hb_blob_get_length (entry),
+	    hb_blob_get_length (entry), hb_blob_get_data (entry, nullptr));
+    hb_blob_destroy (entry);
+  }
+  free (tags);
+#endif
+
+  hb_face_destroy (face);
+
+  return !count;
+}
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index b9d4d79..2386e53 100644
--- a/test/api/Makefile.am
+++ b/test/api/Makefile.am
@@ -84,6 +84,8 @@
 	test-ot-color \
 	test-ot-ligature-carets \
 	test-ot-name \
+	test-ot-meta \
+	test-ot-metrics \
 	test-ot-tag \
 	test-ot-extents-cff \
 	$(NULL)
diff --git a/test/api/fonts/TestCFF2VF.otf b/test/api/fonts/TestCFF2VF.otf
new file mode 100644
index 0000000..a9e48e3
--- /dev/null
+++ b/test/api/fonts/TestCFF2VF.otf
Binary files differ
diff --git a/test/api/fonts/meta.ttf b/test/api/fonts/meta.ttf
new file mode 100644
index 0000000..414d7e6
--- /dev/null
+++ b/test/api/fonts/meta.ttf
Binary files differ
diff --git a/test/api/test-ot-color.c b/test/api/test-ot-color.c
index c0cbd77..d646f73 100644
--- a/test/api/test-ot-color.c
+++ b/test/api/test-ot-color.c
@@ -426,9 +426,9 @@
   g_assert (strncmp (data + 1, "PNG", 3) == 0);
   hb_font_get_glyph_extents (sbix_font, 1, &extents);
   g_assert_cmpint (extents.x_bearing, ==, 0);
-  g_assert_cmpint (extents.y_bearing, ==, 0);
+  g_assert_cmpint (extents.y_bearing, ==, 800);
   g_assert_cmpint (extents.width, ==, 800);
-  g_assert_cmpint (extents.height, ==, 800);
+  g_assert_cmpint (extents.height, ==, -800);
   hb_blob_destroy (blob);
   hb_font_destroy (sbix_font);
 
diff --git a/test/api/test-ot-meta.c b/test/api/test-ot-meta.c
new file mode 100644
index 0000000..b8be5a3
--- /dev/null
+++ b/test/api/test-ot-meta.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb-test.h"
+
+#include <hb-ot.h>
+
+/* Unit tests for hb-ot-meta.h */
+
+static void
+test_ot_meta_get_entries (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/meta.ttf");
+  hb_ot_meta_tag_t entries[2];
+
+  unsigned int entries_count = 2;
+  g_assert_cmpint (hb_ot_meta_get_entries (face, 0, &entries_count, entries), ==, 5);
+  g_assert_cmpint (entries_count, ==, 2);
+  g_assert_cmpint (entries[0], ==, HB_TAG ('a','p','p','l'));
+  g_assert_cmpint (entries[1], ==, HB_TAG ('b','i','l','d'));
+
+  entries_count = 1;
+  g_assert_cmpint (hb_ot_meta_get_entries (face, 2, &entries_count, entries), ==, 5);
+  g_assert_cmpint (entries_count, ==, 1);
+  g_assert_cmpint (entries[0], ==, HB_TAG ('d','l','n','g'));
+
+  entries_count = 2;
+  g_assert_cmpint (hb_ot_meta_get_entries (face, 4, &entries_count, entries), ==, 5);
+  g_assert_cmpint (entries_count, ==, 1);
+  g_assert_cmpint (entries[0], ==, HB_TAG ('s','l','n','g'));
+
+  hb_face_destroy (face);
+}
+
+static void
+test_ot_meta_reference_entry (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/meta.ttf");
+  hb_blob_t *dlng = hb_ot_meta_reference_entry (face, HB_OT_META_DESIGN_LANGUAGES);
+  g_assert_cmpint (hb_blob_get_length (dlng), ==, 8);
+  g_assert_cmpmem (hb_blob_get_data (dlng, NULL), 8, "ar,de,fa", 8);
+  hb_blob_destroy (dlng);
+  hb_blob_t *fslf = hb_ot_meta_reference_entry (face, (hb_ot_meta_tag_t) HB_TAG ('f','s','l','f'));
+  g_assert_cmpint (hb_blob_get_length (fslf), ==, 12);
+  hb_blob_destroy (fslf);
+  hb_blob_t *nacl = hb_ot_meta_reference_entry (face, (hb_ot_meta_tag_t) HB_TAG ('n','a','c','l'));
+  g_assert_cmpint (hb_blob_get_length (nacl), ==, 0);
+  hb_blob_destroy (nacl);
+  hb_blob_t *slng = hb_ot_meta_reference_entry (face, HB_OT_META_SUPPORTED_LANGUAGES);
+  g_assert_cmpint (hb_blob_get_length (slng), ==, 11);
+  g_assert_cmpmem (hb_blob_get_data (slng, NULL), 11, "ar,de,en,fa", 11);
+  hb_blob_destroy (slng);
+  hb_face_destroy (face);
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+  hb_test_add (test_ot_meta_get_entries);
+  hb_test_add (test_ot_meta_reference_entry);
+  return hb_test_run ();
+}
diff --git a/test/api/test-ot-metrics.c b/test/api/test-ot-metrics.c
new file mode 100644
index 0000000..9712c93
--- /dev/null
+++ b/test/api/test-ot-metrics.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb-test.h"
+
+#include <hb-ot.h>
+
+#include <math.h>
+
+/* Unit tests for hb-ot-metrics.h */
+
+static void
+test_ot_metrics_get_no_var (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/cpal-v0.ttf");
+  hb_font_t *font = hb_font_create (face);
+  hb_position_t value;
+  g_assert (hb_ot_metrics_get_position (font, HB_OT_METRICS_HORIZONTAL_ASCENDER, &value));
+  g_assert_cmpint (value, ==, 1000);
+  g_assert_cmpint (hb_ot_metrics_get_x_variation (font, HB_OT_METRICS_HORIZONTAL_ASCENDER), ==, 0);
+  g_assert_cmpint (hb_ot_metrics_get_y_variation (font, HB_OT_METRICS_HORIZONTAL_ASCENDER), ==, 0);
+  g_assert_cmpint (hb_ot_metrics_get_x_variation (font, HB_OT_METRICS_X_HEIGHT), ==, 0);
+  // g_assert_cmpint ((int) hb_ot_metrics_get_variation (font, HB_OT_METRICS_HORIZONTAL_ASCENDER), ==, 0);
+  hb_font_destroy (font);
+  hb_face_destroy (face);
+}
+
+static void
+test_ot_metrics_get_var (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/TestCFF2VF.otf");
+  hb_font_t *font = hb_font_create (face);
+  hb_position_t value;
+  g_assert (hb_ot_metrics_get_position (font, HB_OT_METRICS_X_HEIGHT, &value));
+  g_assert_cmpint (value, ==, 486);
+  g_assert_cmpint (hb_ot_metrics_get_x_variation (font, HB_OT_METRICS_HORIZONTAL_ASCENDER), ==, 0);
+  g_assert_cmpint (hb_ot_metrics_get_y_variation (font, HB_OT_METRICS_HORIZONTAL_ASCENDER), ==, 0);
+  g_assert_cmpint (hb_ot_metrics_get_x_variation (font, HB_OT_METRICS_X_HEIGHT), ==, 0);
+  float coords[] = {100.f};
+  hb_font_set_var_coords_design (font, coords, 1);
+  g_assert (hb_ot_metrics_get_position (font, HB_OT_METRICS_X_HEIGHT, &value));
+  g_assert_cmpint (value, ==,  478);
+  g_assert_cmpint (hb_ot_metrics_get_x_variation (font, HB_OT_METRICS_HORIZONTAL_ASCENDER), ==, 0);
+  g_assert_cmpint (hb_ot_metrics_get_y_variation (font, HB_OT_METRICS_HORIZONTAL_ASCENDER), ==, 0);
+  g_assert_cmpint (hb_ot_metrics_get_x_variation (font, HB_OT_METRICS_X_HEIGHT), ==, -8);
+  hb_font_destroy (font);
+  hb_face_destroy (face);
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+  hb_test_add (test_ot_metrics_get_no_var);
+  hb_test_add (test_ot_metrics_get_var);
+  return hb_test_run ();
+}
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5738978499624960 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5738978499624960
new file mode 100644
index 0000000..0264a15
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5738978499624960
Binary files differ
diff --git a/test/fuzzing/hb-subset-fuzzer.cc b/test/fuzzing/hb-subset-fuzzer.cc
index 73c95b2..428765e 100644
--- a/test/fuzzing/hb-subset-fuzzer.cc
+++ b/test/fuzzing/hb-subset-fuzzer.cc
@@ -3,6 +3,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <assert.h>
 
 #include "hb-subset.h"
 
@@ -32,6 +33,19 @@
   }
 
   hb_face_t *result = hb_subset (face, input);
+  {
+    hb_blob_t *blob = hb_face_reference_blob (result);
+    unsigned int length;
+    const char *data = hb_blob_get_data (blob, &length);
+
+    // Something not optimizable just to access all the blob data
+    unsigned int bytes_count = 0;
+    for (unsigned int i = 0; i < length; ++i)
+      if (data[i]) ++bytes_count;
+    assert (bytes_count || !length);
+
+    hb_blob_destroy (blob);
+  }
   hb_face_destroy (result);
 
   hb_subset_input_destroy (input);
diff --git a/test/fuzzing/run-shape-fuzzer-tests.py b/test/fuzzing/run-shape-fuzzer-tests.py
index ba480dd..6d36c2c 100755
--- a/test/fuzzing/run-shape-fuzzer-tests.py
+++ b/test/fuzzing/run-shape-fuzzer-tests.py
@@ -5,41 +5,41 @@
 import sys, os, subprocess, tempfile, threading
 
 
-def which(program):
+def which (program):
 	# https://stackoverflow.com/a/377028
-	def is_exe(fpath):
-		return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+	def is_exe (fpath):
+		return os.path.isfile (fpath) and os.access (fpath, os.X_OK)
 
-	fpath, _ = os.path.split(program)
+	fpath, _ = os.path.split (program)
 	if fpath:
-		if is_exe(program):
+		if is_exe (program):
 			return program
 	else:
-		for path in os.environ["PATH"].split(os.pathsep):
-			exe_file = os.path.join(path, program)
-			if is_exe(exe_file):
+		for path in os.environ["PATH"].split (os.pathsep):
+			exe_file = os.path.join (path, program)
+			if is_exe (exe_file):
 				return exe_file
 
 	return None
 
 
-def cmd(command):
+def cmd (command):
 	# https://stackoverflow.com/a/4408409
 	# https://stackoverflow.com/a/10012262
-	with tempfile.TemporaryFile() as tempf:
+	with tempfile.TemporaryFile () as tempf:
 		p = subprocess.Popen (command, stderr=tempf)
 		is_killed = {'value': False}
 
-		def timeout(p, is_killed):
+		def timeout (p, is_killed):
 			is_killed['value'] = True
-			p.kill()
+			p.kill ()
 		timer = threading.Timer (2, timeout, [p, is_killed])
 
 		try:
 			timer.start()
 			p.wait ()
 			tempf.seek (0)
-			text = tempf.read().decode ("utf-8").strip ()
+			text = tempf.read ().decode ("utf-8").strip ()
 			returncode = p.returncode
 		finally:
 			timer.cancel()
@@ -67,9 +67,9 @@
 print ('hb_shape_fuzzer:', hb_shape_fuzzer)
 fails = 0
 
-libtool = os.environ.get('LIBTOOL')
+libtool = os.environ.get ('LIBTOOL')
 valgrind = None
-if os.environ.get('RUN_VALGRIND', ''):
+if os.environ.get ('RUN_VALGRIND', ''):
 	valgrind = which ('valgrind')
 	if valgrind is None:
 		print ("""Valgrind requested but not found.""")
@@ -80,7 +80,7 @@
 
 parent_path = os.path.join (srcdir, "fonts")
 for file in os.listdir (parent_path):
-	path = os.path.join(parent_path, file)
+	path = os.path.join (parent_path, file)
 
 	if valgrind:
 		text, returncode = cmd (libtool.split(' ') + ['--mode=execute', valgrind + ' --leak-check=full --error-exitcode=1', '--', hb_shape_fuzzer, path])
@@ -89,7 +89,7 @@
 		if 'error' in text:
 			returncode = 1
 
-	if not valgrind and text.strip ():
+	if (not valgrind or returncode) and text.strip ():
 		print (text)
 
 	if returncode != 0:
diff --git a/test/fuzzing/run-subset-fuzzer-tests.py b/test/fuzzing/run-subset-fuzzer-tests.py
index 3ac2288..f94c13f 100755
--- a/test/fuzzing/run-subset-fuzzer-tests.py
+++ b/test/fuzzing/run-subset-fuzzer-tests.py
@@ -33,7 +33,7 @@
 		def timeout(p, is_killed):
 			is_killed['value'] = True
 			p.kill()
-		timer = threading.Timer (2, timeout, [p, is_killed])
+		timer = threading.Timer (8, timeout, [p, is_killed])
 
 		try:
 			timer.start()
@@ -82,6 +82,8 @@
 	global fails
 	for file in os.listdir (parent_path):
 		path = os.path.join(parent_path, file)
+		# TODO: Run on all the fonts not just subset related ones
+		if "subset" not in path: continue
 
 		print ("running subset fuzzer against %s" % path)
 		if valgrind:
@@ -91,7 +93,7 @@
 			if 'error' in text:
 				returncode = 1
 
-		if not valgrind and text.strip ():
+		if (not valgrind or returncode) and text.strip ():
 			print (text)
 
 		if returncode != 0:
@@ -100,8 +102,7 @@
 
 
 run_dir (os.path.join (srcdir, "..", "subset", "data", "fonts"))
-# TODO running these tests very slow tests.  Fix and re-enable
-#run_dir (os.path.join (srcdir, "fonts"))
+run_dir (os.path.join (srcdir, "fonts"))
 
 if fails:
         print ("%i subset fuzzer related tests failed." % fails)
diff --git a/test/shaping/data/in-house/fonts/fcbaa518d3cce441ed37ae3b1fed6a19e9b54efd.ttf b/test/shaping/data/in-house/fonts/fcbaa518d3cce441ed37ae3b1fed6a19e9b54efd.ttf
new file mode 100644
index 0000000..b1a8a33
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/fcbaa518d3cce441ed37ae3b1fed6a19e9b54efd.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/tests/color-fonts.tests b/test/shaping/data/in-house/tests/color-fonts.tests
index b325d78..bf0005c 100644
--- a/test/shaping/data/in-house/tests/color-fonts.tests
+++ b/test/shaping/data/in-house/tests/color-fonts.tests
@@ -1 +1,2 @@
 ../fonts/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf:--font-funcs=ot --show-extents:U+1F42F:[gid1=0+2963<0,2179,2963,-2789>]
+../fonts/fcbaa518d3cce441ed37ae3b1fed6a19e9b54efd.ttf:--font-funcs=ot --show-extents:U+1F600:[gid4=0+2550<0,1898,2555,-2405>]
diff --git a/test/subset/data/expected/basics/Comfortaa-Regular-new.default.retain-all-codepoint.ttf b/test/subset/data/expected/basics/Comfortaa-Regular-new.default.retain-all-codepoint.ttf
new file mode 100644
index 0000000..5de8d89
--- /dev/null
+++ b/test/subset/data/expected/basics/Comfortaa-Regular-new.default.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Comfortaa-Regular-new.drop-hints-retain-gids.retain-all-codepoint.ttf b/test/subset/data/expected/basics/Comfortaa-Regular-new.drop-hints-retain-gids.retain-all-codepoint.ttf
new file mode 100644
index 0000000..e3c0727
--- /dev/null
+++ b/test/subset/data/expected/basics/Comfortaa-Regular-new.drop-hints-retain-gids.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Comfortaa-Regular-new.drop-hints.retain-all-codepoint.ttf b/test/subset/data/expected/basics/Comfortaa-Regular-new.drop-hints.retain-all-codepoint.ttf
new file mode 100644
index 0000000..6425ecf
--- /dev/null
+++ b/test/subset/data/expected/basics/Comfortaa-Regular-new.drop-hints.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Comfortaa-Regular-new.name-ids.retain-all-codepoint.ttf b/test/subset/data/expected/basics/Comfortaa-Regular-new.name-ids.retain-all-codepoint.ttf
new file mode 100644
index 0000000..fbb8c33
--- /dev/null
+++ b/test/subset/data/expected/basics/Comfortaa-Regular-new.name-ids.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Comfortaa-Regular-new.retain-gids.retain-all-codepoint.ttf b/test/subset/data/expected/basics/Comfortaa-Regular-new.retain-gids.retain-all-codepoint.ttf
new file mode 100644
index 0000000..cc2805a
--- /dev/null
+++ b/test/subset/data/expected/basics/Comfortaa-Regular-new.retain-gids.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.default.retain-all-codepoint.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.default.retain-all-codepoint.ttf
new file mode 100644
index 0000000..12d9208
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.default.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.retain-all-codepoint.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.retain-all-codepoint.ttf
new file mode 100644
index 0000000..52dc474
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints-retain-gids.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints.retain-all-codepoint.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints.retain-all-codepoint.ttf
new file mode 100644
index 0000000..52dc474
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.drop-hints.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.retain-all-codepoint.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.retain-all-codepoint.ttf
new file mode 100644
index 0000000..12d9208
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.name-ids.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.retain-all-codepoint.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.retain-all-codepoint.ttf
new file mode 100644
index 0000000..12d9208
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.retain-gids.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/tests/basics.tests b/test/subset/data/tests/basics.tests
index c310722..772f33c 100644
--- a/test/subset/data/tests/basics.tests
+++ b/test/subset/data/tests/basics.tests
@@ -15,3 +15,4 @@
 c
 ac
 a
+*
diff --git a/test/subset/subset_test_suite.py b/test/subset/subset_test_suite.py
index ad438ee..47664d0 100644
--- a/test/subset/subset_test_suite.py
+++ b/test/subset/subset_test_suite.py
@@ -12,7 +12,10 @@
 		self.subset = subset
 
 	def unicodes(self):
-		return ",".join("%X" % ord(c) for (i, c) in enumerate(self.subset))
+		if self.subset == '*':
+			return self.subset[0]
+		else:
+			return ",".join("%X" % ord(c) for (i, c) in enumerate(self.subset))
 
 	def get_profile_flags(self):
 		with io.open(self.profile_path, mode="r", encoding="utf-8") as f:
@@ -23,7 +26,12 @@
 		font_base_name_parts = os.path.splitext(font_base_name)
 		profile_name = os.path.splitext(os.path.basename(self.profile_path))[0]
 
-		return "%s.%s.%s%s" % (font_base_name_parts[0],
+		if self.unicodes() == "*":
+			return "%s.%s.retain-all-codepoint%s" % (font_base_name_parts[0],
+				       profile_name,
+				       font_base_name_parts[1])
+		else:
+			return "%s.%s.%s%s" % (font_base_name_parts[0],
 				       profile_name,
 				       self.unicodes(),
 				       font_base_name_parts[1])
@@ -39,9 +47,9 @@
 
 	def __init__(self, test_path, definition):
 		self.test_path = test_path
-		self.fonts = set()
-		self.profiles = set()
-		self.subsets = set()
+		self.fonts = []
+		self.profiles = []
+		self.subsets = []
 		self._parse(definition)
 
 	def get_output_directory(self):
@@ -87,6 +95,6 @@
 			if line in destinations:
 				current_destination = destinations[line]
 			elif current_destination is not None:
-				current_destination.add(line)
+				current_destination.append(line)
 			else:
 				raise Exception("Failed to parse test suite file.")
diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index 4b7af8e..6d87c56 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -53,6 +53,13 @@
   {
     // TODO(Q1) does this only get called with at least 1 codepoint?
     hb_set_t *codepoints = hb_subset_input_unicode_set (input);
+    if (0 == strcmp (text, "*"))
+    {
+      hb_face_t *face = hb_font_get_face (font);
+      hb_face_collect_unicodes (face, codepoints);
+      return;
+    }
+
     gchar *c = (gchar *)text;
     do {
       gunichar cp = g_utf8_get_char(c);
diff --git a/util/options.cc b/util/options.cc
index 4e6ca57..f71a5a3 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -349,28 +349,37 @@
   }
 
   GString *gs = g_string_new (nullptr);
-  char *s = (char *) arg;
-  char *p;
-
-  while (s && *s)
+  if (0 == strcmp (arg, "*")) 
   {
-    while (*s && strchr (DELIMITERS, *s))
-      s++;
-    if (!*s)
-      break;
+    g_string_append_c (gs, '*');
+  }
+  else
+  {
 
-    errno = 0;
-    hb_codepoint_t u = strtoul (s, &p, 16);
-    if (errno || s == p)
+    char *s = (char *) arg;
+    char *p;
+  
+    while (s && *s)
     {
-      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
-		   "Failed parsing Unicode values at: '%s'", s);
-      return false;
+      while (*s && strchr (DELIMITERS, *s))
+        s++;
+      if (!*s)
+        break;
+  
+      errno = 0;
+      hb_codepoint_t u = strtoul (s, &p, 16);
+      if (errno || s == p)
+      {
+        g_string_free (gs, TRUE);
+        g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+  		   "Failed parsing Unicode values at: '%s'", s);
+        return false;
+      }
+  
+      g_string_append_unichar (gs, u);
+  
+      s = p;
     }
-
-    g_string_append_unichar (gs, u);
-
-    s = p;
   }
 
   text_opts->text_len = gs->len;