Update harfbuzz to 2.1.0, update BUILD.gn and README

Change-Id: I0dc25cb9b342b76dacd0579a389198c50104d342
diff --git a/AUTHORS b/AUTHORS
index 81cdc4c..0763761 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,9 +1,11 @@
 Behdad Esfahbod
-Simon Hausmann
-Martin Hosken
-Jonathan Kew
-Lars Knoll
-Werner Lemberg
-Roozbeh Pournader
-Owen Taylor
 David Turner
+Ebrahim Byagowi
+Jonathan Kew
+Khaled Hosny
+Lars Knoll
+Martin Hosken
+Owen Taylor
+Roozbeh Pournader
+Simon Hausmann
+Werner Lemberg
diff --git a/BUILD.gn b/BUILD.gn
index c3afd3d..2ce33cf 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -22,44 +22,62 @@
     "src/hb-aat-layout-ankr-table.hh",
     "src/hb-aat-layout-bsln-table.hh",
     "src/hb-aat-layout-common-private.hh",
+    "src/hb-aat-layout-common.hh",
     "src/hb-aat-layout-feat-table.hh",
     "src/hb-aat-layout-kerx-table.hh",
     "src/hb-aat-layout-morx-table.hh",
     "src/hb-aat-layout-private.hh",
     "src/hb-aat-layout-trak-table.hh",
     "src/hb-aat-layout.cc",
+    "src/hb-aat-layout.hh",
+    "src/hb-aat-map.cc",
+    "src/hb-aat-map.hh",
     "src/hb-aat-ltag-table.hh",
     "src/hb-atomic-private.hh",
-    "src/hb-blob-private.hh",
+    "src/hb-atomic.hh",
     "src/hb-blob.cc",
+    "src/hb-blob-private.hh",
     "src/hb-blob.h",
+    "src/hb-blob.hh",
     "src/hb-buffer-deserialize-json.hh",
     "src/hb-buffer-deserialize-text.hh",
     "src/hb-buffer-private.hh",
     "src/hb-buffer-serialize.cc",
     "src/hb-buffer.cc",
     "src/hb-buffer.h",
+    "src/hb-buffer.hh",
     "src/hb-common.cc",
     "src/hb-common.h",
+    "src/hb-cache.hh",
     "src/hb-debug.hh",
     "src/hb-deprecated.h",
     "src/hb-dsalgs.hh",
-    "src/hb-face-private.hh",
     "src/hb-face.cc",
+    "src/hb-face-private.hh",
     "src/hb-face.h",
-    "src/hb-font-private.hh",
+    "src/hb-face.hh",
     "src/hb-font.cc",
+    "src/hb-font-private.hh",
     "src/hb-font.h",
+    "src/hb-font.hh",
     "src/hb-icu.cc",
     "src/hb-icu.h",
+    "src/hb-iter.hh",
     "src/hb-iter-private.hh",
     "src/hb-machinery-private.hh",
-    "src/hb-map-private.hh",
+    "src/hb-machinery.hh",
     "src/hb-map.cc",
+    "src/hb-map-private.hh",
     "src/hb-map.h",
+    "src/hb-map.hh",
+    "src/hb-mutex.hh",
     "src/hb-mutex-private.hh",
+    "src/hb-null.hh",
+    "src/hb-object.hh",
     "src/hb-object-private.hh",
+    "src/hb-open-file.hh",
     "src/hb-open-file-private.hh",
+    "src/hb-open-type.hh",
     "src/hb-open-type-private.hh",
     "src/hb-ot-cmap-table.hh",
     "src/hb-ot-color-cbdt-table.hh",
@@ -68,6 +86,9 @@
     "src/hb-ot-color-sbix-table.hh",
     "src/hb-ot-color-svg-table.hh",
     "src/hb-ot-color.cc",
+    "src/hb-ot-color.h",
+    "src/hb-ot-face.cc",
+    "src/hb-ot-face.hh",
     "src/hb-ot-font.cc",
     "src/hb-ot-font.h",
     "src/hb-ot-glyf-table.hh",
@@ -86,12 +107,20 @@
     "src/hb-ot-layout-private.hh",
     "src/hb-ot-layout.cc",
     "src/hb-ot-layout.h",
+    "src/hb-ot-layout.hh",
+    "src/hb-ot-layout-common.hh",
+    "src/hb-ot-layout-gsubpos.hh",
     "src/hb-ot-map-private.hh",
     "src/hb-ot-map.cc",
+    "src/hb-ot-map.hh",
     "src/hb-ot-math-table.hh",
     "src/hb-ot-math.cc",
     "src/hb-ot-math.h",
     "src/hb-ot-maxp-table.hh",
+    "src/hb-ot-name.cc",
+    "src/hb-ot-name.h",
+    "src/hb-ot-name-language.cc",
+    "src/hb-ot-name-language.hh",
     "src/hb-ot-name-table.hh",
     "src/hb-ot-os2-table.hh",
     "src/hb-ot-os2-unicode-ranges.hh",
@@ -102,6 +131,7 @@
     "src/hb-ot-shape-complex-arabic-table.hh",
     "src/hb-ot-shape-complex-arabic-win1256.hh",
     "src/hb-ot-shape-complex-arabic.cc",
+    "src/hb-ot-shape-complex-arabic.hh",
     "src/hb-ot-shape-complex-default.cc",
     "src/hb-ot-shape-complex-hangul.cc",
     "src/hb-ot-shape-complex-hebrew.cc",
@@ -109,49 +139,65 @@
     "src/hb-ot-shape-complex-indic-private.hh",
     "src/hb-ot-shape-complex-indic-table.cc",
     "src/hb-ot-shape-complex-indic.cc",
+    "src/hb-ot-shape-complex-indic.hh",
     "src/hb-ot-shape-complex-khmer-machine.hh",
     "src/hb-ot-shape-complex-khmer-private.hh",
     "src/hb-ot-shape-complex-khmer.cc",
+    "src/hb-ot-shape-complex-khmer.hh",
     "src/hb-ot-shape-complex-myanmar-machine.hh",
     "src/hb-ot-shape-complex-myanmar-private.hh",
     "src/hb-ot-shape-complex-myanmar.cc",
+    "src/hb-ot-shape-complex-myanmar.hh",
     "src/hb-ot-shape-complex-private.hh",
     "src/hb-ot-shape-complex-thai.cc",
-    "src/hb-ot-shape-complex-tibetan.cc",
     "src/hb-ot-shape-complex-use-machine.hh",
     "src/hb-ot-shape-complex-use-private.hh",
     "src/hb-ot-shape-complex-use-table.cc",
     "src/hb-ot-shape-complex-use.cc",
+    "src/hb-ot-shape-complex-use.hh",
+    "src/hb-ot-shape-complex-vowel-constraints.cc",
+    "src/hb-ot-shape-complex-vowel-constraints.hh",
+    "src/hb-ot-shape-complex.hh",
     "src/hb-ot-shape-fallback-private.hh",
     "src/hb-ot-shape-fallback.cc",
+    "src/hb-ot-shape-fallback.hh",
     "src/hb-ot-shape-normalize-private.hh",
     "src/hb-ot-shape-normalize.cc",
+    "src/hb-ot-shape-normalize.hh",
     "src/hb-ot-shape-private.hh",
     "src/hb-ot-shape.cc",
     "src/hb-ot-shape.h",
+    "src/hb-ot-shape.hh",
     "src/hb-ot-tag.cc",
     "src/hb-ot-tag.h",
+    "src/hb-ot-tag-table.hh",
     "src/hb-ot-var-avar-table.hh",
     "src/hb-ot-var-fvar-table.hh",
     "src/hb-ot-var-hvar-table.hh",
     "src/hb-ot-var-mvar-table.hh",
     "src/hb-ot-var.cc",
     "src/hb-ot-var.h",
+    "src/hb-ot-vorg-table.hh",
     "src/hb-ot.h",
     "src/hb-private.hh",
     "src/hb-set-digest-private.hh",
     "src/hb-set-private.hh",
     "src/hb-set.cc",
     "src/hb-set.h",
+    "src/hb-set.hh",
+    "src/hb-set-digest.hh",
     "src/hb-shape-plan-private.hh",
     "src/hb-shape-plan.cc",
     "src/hb-shape-plan.h",
+    "src/hb-shape-plan.hh",
     "src/hb-shape.cc",
     "src/hb-shape.h",
+    "src/hb-shaper-impl.hh",
     "src/hb-shaper-impl-private.hh",
     "src/hb-shaper-list.hh",
     "src/hb-shaper-private.hh",
     "src/hb-shaper.cc",
+    "src/hb-shaper.hh",
     "src/hb-static.cc",
     "src/hb-string-array.hh",
     "src/hb-subset-glyf.cc",
@@ -162,6 +208,8 @@
     "src/hb-subset-private.hh",
     "src/hb-subset.cc",
     "src/hb-subset.h",
+    "src/hb-subset.hh",
+    "src/hb-subset-input.hh",
     "src/hb-unicode-private.hh",
     "src/hb-unicode.cc",
     "src/hb-unicode.h",
@@ -169,6 +217,7 @@
     "src/hb-version.h",
     "src/hb-warning.cc",
     "src/hb.h",
+    "src/hb.hh",
   ]
 
   defines = [
diff --git a/BUILD.md b/BUILD.md
index 8a6b569..4c1c306 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -26,7 +26,7 @@
 library in `src/`, and a few utility programs including `hb-view` and `hb-shape`
 under `util/`.
 
-If you are bootstraping from git, you need a few more tools before you can
+If you are bootstrapping from git, you need a few more tools before you can
 run `autogen.sh` for the first time. Namely, `pkg-config` and `ragel`.
 
 Again, on Ubuntu / Debian:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e881dbd..019e205 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -52,6 +52,9 @@
   set (HB_HAVE_FREETYPE ON)
 endif ()
 
+option(HB_BUILD_SUBSET "Build harfbuzz-subset" ON)
+option(HB_BUILD_TESTS "Build harfbuzz tests" ON)
+
 option(HB_HAVE_GOBJECT "Enable GObject Bindings" OFF)
 if (HB_HAVE_GOBJECT)
   set (HB_HAVE_GLIB ON)
@@ -87,7 +90,6 @@
   ${PROJECT_BINARY_DIR}/src
 )
 
-add_definitions(-DHAVE_OT)
 add_definitions(-DHAVE_FALLBACK)
 
 # We need PYTHON_EXECUTABLE to be set for running the tests...
@@ -98,10 +100,10 @@
 include (CheckIncludeFile)
 macro (check_funcs) # Similar to AC_CHECK_FUNCS of autotools
   foreach (func_name ${ARGN})
-    string(TOUPPER ${func_name} definiton_to_add)
-    check_function_exists(${func_name} HAVE_${definiton_to_add})
-    if (${HAVE_${definiton_to_add}})
-      add_definitions(-DHAVE_${definiton_to_add})
+    string(TOUPPER ${func_name} definition_to_add)
+    check_function_exists(${func_name} HAVE_${definition_to_add})
+    if (${HAVE_${definition_to_add}})
+      add_definitions(-DHAVE_${definition_to_add})
     endif ()
   endforeach ()
 endmacro ()
@@ -359,12 +361,32 @@
   list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-coretext.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-coretext.h)
 
-  find_library(APPLICATION_SERVICES_FRAMEWORK ApplicationServices)
-  if (APPLICATION_SERVICES_FRAMEWORK)
-    list(APPEND THIRD_PARTY_LIBS ${APPLICATION_SERVICES_FRAMEWORK})
-  endif (APPLICATION_SERVICES_FRAMEWORK)
+  if (HB_IOS)
+    find_library(COREFOUNDATION CoreFoundation)
+    if (COREFOUNDATION)
+      list(APPEND THIRD_PARTY_LIBS ${COREFOUNDATION})
+    endif ()
+    mark_as_advanced(COREFOUNDATION)
 
-  mark_as_advanced(APPLICATION_SERVICES_FRAMEWORK)
+    find_library(CORETEXT CoreText)
+    if (CORETEXT)
+      list(APPEND THIRD_PARTY_LIBS ${CORETEXT})
+    endif ()
+    mark_as_advanced(CORETEXT)
+
+    find_library(COREGRAPHICS CoreGraphics)
+    if (COREGRAPHICS)
+      list(APPEND THIRD_PARTY_LIBS ${COREGRAPHICS})
+    endif ()
+    mark_as_advanced(COREGRAPHICS)
+  else ()
+    find_library(APPLICATION_SERVICES_FRAMEWORK ApplicationServices)
+    if (APPLICATION_SERVICES_FRAMEWORK)
+      list(APPEND THIRD_PARTY_LIBS ${APPLICATION_SERVICES_FRAMEWORK})
+    endif ()
+
+    mark_as_advanced(APPLICATION_SERVICES_FRAMEWORK)
+  endif ()
 endif ()
 
 if (WIN32 AND HB_HAVE_UNISCRIBE)
@@ -527,12 +549,14 @@
 target_link_libraries(harfbuzz ${THIRD_PARTY_LIBS})
 
 ## Define harfbuzz-subset library
-add_library(harfbuzz-subset ${subset_project_sources} ${subset_project_headers})
-add_dependencies(harfbuzz-subset harfbuzz)
-target_link_libraries(harfbuzz-subset harfbuzz ${THIRD_PARTY_LIBS})
+if (HB_BUILD_SUBSET)
+  add_library(harfbuzz-subset ${subset_project_sources} ${subset_project_headers})
+  add_dependencies(harfbuzz-subset harfbuzz)
+  target_link_libraries(harfbuzz-subset harfbuzz ${THIRD_PARTY_LIBS})
 
-if (BUILD_SHARED_LIBS)
-  set_target_properties(harfbuzz harfbuzz-subset PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
+  if (BUILD_SHARED_LIBS)
+    set_target_properties(harfbuzz harfbuzz-subset PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
+  endif ()
 endif ()
 
 if (UNIX OR MINGW)
@@ -549,7 +573,9 @@
     set (CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "m") # libm
     set (CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "")
     set_target_properties(harfbuzz PROPERTIES LINKER_LANGUAGE C)
-    set_target_properties(harfbuzz-subset PROPERTIES LINKER_LANGUAGE C)
+    if (HB_BUILD_SUBSET)
+      set_target_properties(harfbuzz-subset PROPERTIES LINKER_LANGUAGE C)
+    endif ()
 
     # No threadsafe statics as we do it ourselves
     set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics")
@@ -574,7 +600,7 @@
 endif ()
 
 if (BUILD_SHARED_LIBS AND WIN32 AND NOT MINGW)
-  add_definitions("-DHB_EXTERN=__declspec(dllexport) extern")
+  add_definitions("-DHB_DLL_EXPORT")
 endif ()
 
 # On Windows, g-ir-scanner requires a DLL build in order for it to work
@@ -761,12 +787,22 @@
 
 if (NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL)
   install(TARGETS harfbuzz
+    EXPORT harfbuzzConfig
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     FRAMEWORK DESTINATION Library/Frameworks
   )
+  install(EXPORT harfbuzzConfig
+      NAMESPACE harfbuzz::
+      DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/harfbuzz
+  )
   if (HB_BUILD_UTILS)
+    if (WIN32 AND BUILD_SHARED_LIBS)
+      install(TARGETS harfbuzz-subset
+        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+      )
+    endif ()
     install(TARGETS hb-view
       RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     )
@@ -818,51 +854,53 @@
 endif ()
 
 
-## src/ executables
-foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges)
-  set (prog_name ${prog})
-  if (${prog_name} STREQUAL "test")
-    # test can not be used as a valid executable name on cmake, lets special case it
-    set (prog_name test-test)
-  endif ()
-  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")
+if (HB_BUILD_TESTS)
+  ## src/ executables
+  foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges)
+    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
+      set (prog_name test-test)
+    endif ()
+    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")
 
-## Tests
-if (UNIX OR MINGW)
-  if (BUILD_SHARED_LIBS)
-    # generate harfbuzz.def after build completion
-    add_custom_command(TARGET harfbuzz POST_BUILD
-      COMMAND "${PYTHON_EXECUTABLE}" ${PROJECT_SOURCE_DIR}/src/gen-def.py ${PROJECT_BINARY_DIR}/harfbuzz.def ${project_headers}
-      WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src)
+  ## Tests
+  if (UNIX OR MINGW)
+    if (BUILD_SHARED_LIBS)
+      # generate harfbuzz.def after build completion
+      add_custom_command(TARGET harfbuzz POST_BUILD
+        COMMAND "${PYTHON_EXECUTABLE}" ${PROJECT_SOURCE_DIR}/src/gen-def.py ${PROJECT_BINARY_DIR}/harfbuzz.def ${project_headers}
+        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src)
 
-    add_test(NAME check-static-inits.sh
-      COMMAND ${PROJECT_SOURCE_DIR}/src/check-static-inits.sh
-      WORKING_DIRECTORY ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/harfbuzz.dir/src # ugly hack
-    )
-    add_test(NAME check-libstdc++.sh COMMAND ${PROJECT_SOURCE_DIR}/src/check-libstdc++.sh)
-    add_test(NAME check-symbols.sh COMMAND ${PROJECT_SOURCE_DIR}/src/check-symbols.sh)
+      add_test(NAME check-static-inits.sh
+        COMMAND ${PROJECT_SOURCE_DIR}/src/check-static-inits.sh
+        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/harfbuzz.dir/src # ugly hack
+      )
+      add_test(NAME check-libstdc++.sh COMMAND ${PROJECT_SOURCE_DIR}/src/check-libstdc++.sh)
+      add_test(NAME check-symbols.sh COMMAND ${PROJECT_SOURCE_DIR}/src/check-symbols.sh)
 
+      set_tests_properties(
+        check-static-inits.sh check-libstdc++.sh check-symbols.sh
+        PROPERTIES
+          ENVIRONMENT "libs=.;srcdir=${PROJECT_SOURCE_DIR}/src"
+          SKIP_RETURN_CODE 77)
+    endif ()
+
+    add_test(NAME check-c-linkage-decls.sh COMMAND ./check-c-linkage-decls.sh)
+    add_test(NAME check-header-guards.sh COMMAND ./check-header-guards.sh)
+    add_test(NAME check-externs.sh COMMAND ./check-externs.sh)
+    add_test(NAME check-includes.sh COMMAND ./check-includes.sh)
     set_tests_properties(
-      check-static-inits.sh check-libstdc++.sh check-symbols.sh
+      check-c-linkage-decls.sh check-header-guards.sh check-externs.sh check-includes.sh
       PROPERTIES
-        ENVIRONMENT "libs=.;srcdir=${PROJECT_SOURCE_DIR}/src"
+        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src
         SKIP_RETURN_CODE 77)
   endif ()
 
-  add_test(NAME check-c-linkage-decls.sh COMMAND ./check-c-linkage-decls.sh)
-  add_test(NAME check-header-guards.sh COMMAND ./check-header-guards.sh)
-  add_test(NAME check-externs.sh COMMAND ./check-externs.sh)
-  add_test(NAME check-includes.sh COMMAND ./check-includes.sh)
-  set_tests_properties(
-    check-c-linkage-decls.sh check-header-guards.sh check-externs.sh check-includes.sh
-    PROPERTIES
-      WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src
-      SKIP_RETURN_CODE 77)
+  # Needs to come last so that variables defined above are passed to
+  # subdirectories.
+  add_subdirectory(test)
 endif ()
-
-# Needs to come last so that variables defined above are passed to
-# subdirectories.
-add_subdirectory(test)
diff --git a/NEWS b/NEWS
index fce43a3..cb8a28f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,237 @@
+Overview of changes leading to 2.1.0
+Tuesday, October 30, 2018
+====================================
+- AAT shaping improvements:
+  o Allow user controlling AAT features, for whole buffer only currently.
+  o Several 'morx' fixes.
+  o Implement tuple-kerns in 'kerx'; Fixes kerning with Apple default
+    San Francisco fonts.
+- Support for color fonts:
+  o COLR/CPAL API to fetch color layers.
+  o SVG table to fetch SVG documents.
+  o CBDT/sbix API to fetch PNG images.
+- New 'name' table API.
+- hb-ot-font now uses 'VORG' table to correctly position CFF glyphs
+  in vertical layout.
+- Various fuzzer-found bug fixes.
+
+Changed API:
+
+A type and a macro added in 2.0.0 were renamed:
+
+hb_name_id_t -> hb_ot_name_id_t
+HB_NAME_ID_INVALID -> HB_OT_NAME_ID_INVALID
+
+New API:
+
++hb_color_t
++HB_COLOR
++hb_color_get_alpha()
++hb_color_get_red()
++hb_color_get_green()
++hb_color_get_blue()
++hb_ot_color_has_palettes()
++hb_ot_color_palette_get_count()
++hb_ot_color_palette_get_name_id()
++hb_ot_color_palette_color_get_name_id()
++hb_ot_color_palette_flags_t
++hb_ot_color_palette_get_flags()
++hb_ot_color_palette_get_colors()
++hb_ot_color_has_layers()
++hb_ot_color_layer_t
++hb_ot_color_glyph_get_layers()
++hb_ot_color_has_svg()
++hb_ot_color_glyph_reference_svg()
++hb_ot_color_has_png()
++hb_ot_color_glyph_reference_png()
+
++hb_ot_name_id_t
++HB_OT_NAME_ID_INVALID
++HB_OT_NAME_ID_COPYRIGHT
++HB_OT_NAME_ID_FONT_FAMILY
++HB_OT_NAME_ID_FONT_SUBFAMILY
++HB_OT_NAME_ID_UNIQUE_ID
++HB_OT_NAME_ID_FULL_NAME
++HB_OT_NAME_ID_VERSION_STRING
++HB_OT_NAME_ID_POSTSCRIPT_NAME
++HB_OT_NAME_ID_TRADEMARK
++HB_OT_NAME_ID_MANUFACTURER
++HB_OT_NAME_ID_DESIGNER
++HB_OT_NAME_ID_DESCRIPTION
++HB_OT_NAME_ID_VENDOR_URL
++HB_OT_NAME_ID_DESIGNER_URL
++HB_OT_NAME_ID_LICENSE
++HB_OT_NAME_ID_LICENSE_URL
++HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY
++HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY
++HB_OT_NAME_ID_MAC_FULL_NAME
++HB_OT_NAME_ID_SAMPLE_TEXT
++HB_OT_NAME_ID_CID_FINDFONT_NAME
++HB_OT_NAME_ID_WWS_FAMILY
++HB_OT_NAME_ID_WWS_SUBFAMILY
++HB_OT_NAME_ID_LIGHT_BACKGROUND
++HB_OT_NAME_ID_DARK_BACKGROUND
++HB_OT_NAME_ID_VARIATIONS_PS_PREFIX
++hb_ot_name_entry_t
++hb_ot_name_list_names()
++hb_ot_name_get_utf8()
++hb_ot_name_get_utf16()
++hb_ot_name_get_utf32()
+
+
+Overview of changes leading to 2.0.2
+Saturday, October 20, 2018
+====================================
+- Fix two minor memory access issues in AAT tables.
+
+
+Overview of changes leading to 2.0.1
+Friday, October 19, 2018
+====================================
+- Fix hb-version.h reported release version that went wrong (1.8.0)
+  with previous release.
+- Fix extrapolation in 'trak' table.
+- Fix hb-font infinite-recursion issue with some font funcs and
+  subclassed fonts.
+- Implement variation-kerning format in kerx table, although without
+  variation.
+- Fix return value of hb_map_is_empty().
+
+
+Overview of changes leading to 2.0.0
+Thursday, October 18, 2018
+====================================
+- Added AAT shaping support (morx/kerx/trak).
+  Automatically used if GSUB/GPOS are not available respectively.
+  Set HB_OPTIONS=aat env var to have morx/kerx preferred over
+  GSUB/GPOS.
+- Apply TrueType kern table internally, instead of relying on
+  hb_font_t callbacks.
+- Khmer shaper significantly rewritten to better match Uniscribe.
+- Indic3 tags ('dev3', etc) are passed to USE shaper.
+- .dfont Mac font containers implemented.
+- Script- and language-mapping revamped to better use BCP 47.
+- Misc USE and Indic fixes.
+- Misc everything fixes.
+- Too many things to list.  Biggest release since 0.9.1, with
+  over 500 commits in just over 5 weeks!  Didn't intend it to
+  be a big release.  Just happened to become.
+- hb-ft now locks underlying FT_Face during use.
+
+API changes:
+
+- Newly-created hb_font_t's now have our internal "hb-ot-font"
+  callbacks set on them, so they should work out of the box
+  without any callbacks set.  If callbacks are set, everything
+  is back to what it was before, the fallback callbacks are
+  null.  If you to get the internal implementation modified,
+  sub_font it.
+
+- New hb_font_funcs_set_nominal_glyphs_func() allows speeding
+  up character to glyph mapping.
+
+New API:
++HB_FEATURE_GLOBAL_START
++HB_FEATURE_GLOBAL_END
++hb_buffer_set_invisible_glyph()
++hb_buffer_get_invisible_glyph()
++hb_font_funcs_set_nominal_glyphs_func()
++hb_ot_layout_table_select_script()
++hb_ot_layout_script_select_language()
++hb_ot_layout_feature_get_name_ids()
++hb_ot_layout_feature_get_characters()
++hb_name_id_t
++HB_NAME_ID_INVALID
++HB_OT_MAX_TAGS_PER_SCRIPT
++hb_ot_tags_from_script_and_language()
++hb_ot_tags_to_script_and_language()
+
+Deprecated API:
+-hb_font_funcs_set_glyph_func()
+-hb_unicode_eastasian_width_func_t
+-hb_unicode_funcs_set_eastasian_width_func()
+-hb_unicode_eastasian_width()
+-hb_unicode_decompose_compatibility_func_t
+-HB_UNICODE_MAX_DECOMPOSITION_LEN
+-hb_unicode_funcs_set_decompose_compatibility_func()
+-hb_unicode_decompose_compatibility()
+-hb_font_funcs_set_glyph_h_kerning_func()
+-hb_font_funcs_set_glyph_v_kerning_func()
+-hb_font_get_glyph_h_kerning()
+-hb_font_get_glyph_v_kerning()
+-hb_font_get_glyph_kerning_for_direction()
+-hb_ot_layout_table_choose_script()
+-hb_ot_layout_script_find_language()
+-hb_ot_tags_from_script()
+-hb_ot_tag_from_language()
+
+
+Overview of changes leading to 1.9.0
+Monday, September 10, 2018
+====================================
+- Added 'cmap' API to hb_face_t.
+- Face-builder API.
+- hb-ot-font re-creation should be much leaner now, as the
+  font tables it uses are cached on hb_face_t now.
+- Internal source header file name changes:
+  hb-*-private.hh is renamed to hb-*.hh.
+
+New API:
++HB_UNICODE_MAX
++hb_face_collect_unicodes()
++hb_face_collect_variation_selectors()
++hb_face_collect_variation_unicodes()
++hb_face_builder_create()
++hb_face_builder_add_table()
+
+
+Overview of changes leading to 1.8.8
+Tuesday, August 14, 2018
+====================================
+- Fix hb-icu crash on architectures where compare_exchange_weak() can
+  fail falsely.  This bug was introduced in 1.8.4.
+  https://bugs.chromium.org/p/chromium/issues/detail?id=873568
+- More internal refactoring of atomic operations and singletons.
+- API changes:
+  The following functions do NOT reference their return value before
+  returning:
+  * hb_unicode_funcs_get_default()
+  * hb_glib_get_unicode_funcs()
+  * hb_icu_get_unicode_funcs()
+  This is consistent with their naming ("get", instead of "reference")
+  as well as how they are used in the wild (ie. no one calls destroy()
+  on their return value.)
+
+
+Overview of changes leading to 1.8.7
+Wednesday, August 8, 2018
+====================================
+- Fix assertion failure with GDEF-blacklisted fonts.
+
+
+Overview of changes leading to 1.8.6
+Tuesday, August 7, 2018
+====================================
+- Internal code shuffling.
+- New API to speed up getting advance widths for implementations
+  that have heavy overhead in get_h_advance callback:
++hb_font_funcs_set_glyph_h_advances_func
++hb_font_funcs_set_glyph_v_advances_func
++hb_font_get_glyph_advances_for_direction
++hb_font_get_glyph_h_advances
++hb_font_get_glyph_h_advances_func_t
++hb_font_get_glyph_v_advances
++hb_font_get_glyph_v_advances_func_t
+
+
+Overview of changes leading to 1.8.5
+Wednesday, August 1, 2018
+====================================
+- Major Khmer shaper improvements to better match Microsoft.
+- Indic bug fixes.
+- Internal improvements to atomic operations.
+
+
 Overview of changes leading to 1.8.4
 Tuesday, July 17, 2018
 ====================================
diff --git a/README.fuchsia b/README.fuchsia
index 34ae1e1..b179037 100644
--- a/README.fuchsia
+++ b/README.fuchsia
@@ -1,9 +1,9 @@
 Name: harfbuzz
 Short Name: harfbuzz
 URL: http://harfbuzz.org
-Version: 1.8.4
+Version: 2.1.0
 Upstream git: https://github.com/harfbuzz/harfbuzz
-Date: 20180731
+Date: 20181102
 Security Critical: yes
 License: MIT
 License File: COPYING
@@ -14,4 +14,4 @@
 To upgrade this repo:
 * Pull in the latest updates from https://github.com/harfbuzz/harfbuzz.
 * Modify the BUILD.gn file to update the harfbuzz target for new sources.
-* Update version numbers and date in this document.
+* Update version numbers and date in this document.
\ No newline at end of file
diff --git a/README.python.md b/README.python.md
index 4c0ba9b..7cf091a 100644
--- a/README.python.md
+++ b/README.python.md
@@ -23,7 +23,7 @@
 $prefix/lib/girepository-* directory.
 
 Make sure you have pygobject installed.  Then check that the following
-import works in your Python interpretter:
+import works in your Python interpreter:
 
 ```python
 from gi.repository import HarfBuzz
diff --git a/README.wine.md b/README.wine.md
index 851d2bf..799eb63 100644
--- a/README.wine.md
+++ b/README.wine.md
@@ -1,6 +1,6 @@
 For the development of HarfBuzz, the Microsoft shaping technology, Uniscribe,
 as a widely used and tested shaper is used as more-or-less OpenType reference
-implemenetation and that specially is important where OpenType specification
+implementation and that specially is important where OpenType specification
 is or wasn't that clear. For having access to Uniscribe on Linux/macOS these
 steps are recommended:
 
@@ -27,8 +27,8 @@
 Now you can use hb-shape using `wine winbuild/util/hb-shape.exe` but if you like to
 to use the original Uniscribe,
 
-8. Bring a 32bit version of `usp10.dll` for youself from `C:\Windows\SysWOW64\usp10.dll` of your
-   Windows installation (asuming you have a 64-bit installation, otherwise `C:\Windows\System32\usp10.dll`)
+8. Bring a 32bit version of `usp10.dll` for yourself from `C:\Windows\SysWOW64\usp10.dll` of your
+   Windows installation (assuming you have a 64-bit installation, otherwise `C:\Windows\System32\usp10.dll`)
    that it is not a DirectWrite proxy ([for more info](https://en.wikipedia.org/wiki/Uniscribe)).
    Rule of thumb, your `usp10.dll` should have a size more than 500kb, otherwise
    it is designed to work with DirectWrite which Wine can't work with its original one.
diff --git a/RELEASING.md b/RELEASING.md
index c57f846..1fd8365 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -8,7 +8,8 @@
      Document them in NEWS.  All API and API semantic changes should be clearly
      marked as API additions, API changes, or API deletions.  Document
      deprecations.  Ensure all new API / deprecations are in listed correctly in
-     docs/harfbuzz-sections.txt
+     docs/harfbuzz-sections.txt.  If release added new API, add entry for new
+     API index at the end of docs/harfbuzz-docs.xml.
 
      If there's a backward-incompatible API change (including deletions for API
      used anywhere), that's a release blocker.  Do NOT release.
@@ -25,6 +26,12 @@
 
 6. Do "make distcheck", if it passes, you get a tarball.
    Otherwise, fix things and commit them separately before making release,
+   Note: Check src/hb-version.h and make sure the new version number is
+   there.  Sometimes, it does not get updated.  If that's the case,
+   "touch configure.ac" and rebuild.  Also check that there is no hb-version.h
+   in your build/src file. Typically it will fail the distcheck if there is.
+   That's what happened to 2.0.0 going out with 1.8.0 hb-version.h...  So, that's
+   a clue.
 
 7. "make release-files".  Enter your GPG password.  This creates a sha256 hash
    and signs it.
diff --git a/TODO b/TODO
index 6dac0be..d8e4105 100644
--- a/TODO
+++ b/TODO
@@ -1,9 +1,3 @@
-General fixes:
-=============
-
-- Implement 'rand' feature.
-
-
 API issues:
 ===========
 
@@ -19,11 +13,7 @@
 
 - Add hb-cairo glue
 
-- Add sanitize API (and a cached version, that saves result on blob user-data)
-
-- BCP 47 language handling / API (language_matches?)
-
-- Add hb_font_create_unscaled()?
+- Add sanitize API.
 
 - Add query / enumeration API for aalt-like features?
 
diff --git a/appveyor.yml b/appveyor.yml
index 6250a91..21d4ea7 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -28,15 +28,19 @@
       MINGW_CHOST: i686-w64-mingw32
       MSYS2_ARCH: i686
 
+
 install:
-  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman -Syu --noconfirm"'
+# - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm --force -Sy && pacman --noconfirm --force -S pacman-mirrors && pacman --force -Syu --noconfirm"'
+  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm --force -S --needed mingw-w64-$MSYS2_ARCH-{gcc,freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config,python2}"'
   - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-ragel"
+  - set PATH=%PATH%;C:\msys64\mingw64\bin # msys2 is added just for having "ragel" on PATH
+  - 'if "%compiler%"=="cygwin" %CYGWIN_PREFIX%\setup-%CYGWIN_ARCH%.exe -g -q -P cygwin-devel,libfreetype-devel,libcairo-devel,libicu-devel,gcc,gcc-g++,gobject-introspection,libglib2.0-devel,libgraphite2-devel,pkg-config,python2'
+  - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" vcpkg install glib:%triplet% freetype:%triplet% cairo:%triplet%'
 
 build_script:
-  - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" vcpkg install glib:%triplet% freetype:%triplet% cairo:%triplet%'
   - 'if "%compiler%"=="msvc" md build'
   - 'if "%compiler%"=="msvc" cd build'
-  - 'if "%compiler%"=="msvc" set PATH=%PATH%;C:\Program Files (x86)\MSBuild\14.0\Bin;c:\msys64\mingw64\bin' # msys2 is added just for having "ragel" on PATH
+  - 'if "%compiler%"=="msvc" set PATH=%PATH%;C:\Program Files (x86)\MSBuild\14.0\Bin'
 
   - 'if "%compiler%"=="msvc" if "%platform%"=="ARM" cmake -DHB_HAVE_UNISCRIBE=ON -DHB_HAVE_DIRECTWRITE=ON -G "%generator%" ../'
   - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" cmake -DHB_HAVE_UNISCRIBE=ON -DHB_HAVE_DIRECTWRITE=ON -DHB_HAVE_GLIB=ON -DHB_HAVE_FREETYPE=ON -DHB_BUILD_UTILS=ON -G "%generator%" -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake ../'
@@ -44,12 +48,12 @@
   - 'if "%compiler%"=="msvc" msbuild harfbuzz.sln /p:Configuration=%configuration% /p:Platform=%platform%'
   - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" ctest --output-on-failure -C %configuration%'
 
-  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S --needed mingw-w64-$MSYS2_ARCH-{freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config,python2}"'
   - '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; make 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 --build=%MINGW_CHOST% --host=%MINGW_CHOST% --prefix=%MINGW_PREFIX%; make -j3 check || .ci/fail.sh"'
 
 cache:
   - c:\tools\vcpkg\installed\
+  - '%CYGWIN_PREFIX%\var\cache\setup'
 
 notifications:
   - provider: Email
@@ -59,5 +63,8 @@
     on_build_failure: true
     on_build_status_changed: true
 
+# Do not build feature branch with open Pull Requests
+skip_branch_with_pr: true
+
 # disable automatic tests
 test: off
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 0000000..88c0a98
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,21 @@
+pool:
+  vmImage: 'VS2017-Win2016'
+
+variables:
+  buildPlatform: 'x86'
+  buildConfiguration: 'Debug'
+  triplet: 'x86-windows'
+
+steps:
+- script: |
+    git clone https://github.com/Microsoft/vcpkg
+    cd vcpkg
+    .\bootstrap-vcpkg.bat
+    .\vcpkg integrate install
+    .\vcpkg install glib:x86-windows freetype:x86-windows cairo:x86-windows
+    cd ..
+    cmake -Bbuild -H. -DHB_HAVE_UNISCRIBE=ON -DHB_HAVE_DIRECTWRITE=ON -DHB_HAVE_GLIB=ON -DHB_HAVE_FREETYPE=ON -DHB_BUILD_UTILS=ON -G "%generator%" -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake ../
+    msbuild harfbuzz.sln /p:Configuration=Debug /p:Platform=Win32
+    cd build
+    ctest --output-on-failure -C Debug
+  displayName: Build and test
diff --git a/configure.ac b/configure.ac
index 0dd70e6..21d48f3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.8.4],
+        [2.1.0],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -12,6 +12,7 @@
 AM_INIT_AUTOMAKE([1.13.0 gnits tar-ustar dist-bzip2 no-dist-gzip -Wall no-define color-tests -Wno-portability])
 AM_SILENT_RULES([yes])
 AX_CODE_COVERAGE
+AC_USE_SYSTEM_EXTENSIONS
 
 # Initialize libtool
 m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
@@ -19,7 +20,6 @@
 LT_INIT([disable-static])
 
 # Check for programs
-AC_USE_SYSTEM_EXTENSIONS
 AC_PROG_CC
 AC_PROG_CC_C99
 AM_PROG_CC_C_O
@@ -148,12 +148,6 @@
 
 dnl ==========================================================================
 
-have_ot=true
-if $have_ot; then
-	AC_DEFINE(HAVE_OT, 1, [Have native OpenType Layout backend])
-fi
-AM_CONDITIONAL(HAVE_OT, $have_ot)
-
 have_fallback=true
 if $have_fallback; then
 	AC_DEFINE(HAVE_FALLBACK, 1, [Have simple TrueType Layout backend])
@@ -330,7 +324,7 @@
 			[Use the graphite2 library @<:@default=no@:>@])],,
 	[with_graphite2=no])
 have_graphite2=false
-GRAPHITE2_DEPS="graphite2"
+GRAPHITE2_DEPS="graphite2 >= 1.2.0"
 AC_SUBST(GRAPHITE2_DEPS)
 if test "x$with_graphite2" = "xyes" -o "x$with_graphite2" = "xauto"; then
 	PKG_CHECK_MODULES(GRAPHITE2, $GRAPHITE2_DEPS, have_graphite2=true, :)
diff --git a/docs/Makefile.am b/docs/Makefile.am
index a993538..9b54b40 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -62,7 +62,6 @@
 
 # Extra header to include when scanning, which are not under DOC_SOURCE_DIR
 # e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
-EXTRA_HFILES=$(top_builddir)/src/hb-version.h
 
 # Images to copy into HTML directory.
 # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
@@ -73,14 +72,15 @@
 # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
 # e.g. content_files=running.sgml building.sgml changes-2.0.sgml
 content_files=	\
-	usermanual-buffers-language-script-and-direction.xml \
-	usermanual-clusters.xml \
-	usermanual-fonts-and-faces.xml \
-	usermanual-glyph-information.xml \
-	usermanual-hello-harfbuzz.xml \
-	usermanual-install-harfbuzz.xml \
-	usermanual-opentype-features.xml \
 	usermanual-what-is-harfbuzz.xml \
+	usermanual-install-harfbuzz.xml \
+	usermanual-getting-started.xml \
+	usermanual-shaping-concepts.xml \
+	usermanual-buffers-language-script-and-direction.xml \
+	usermanual-fonts-and-faces.xml \
+	usermanual-clusters.xml \
+	usermanual-opentype-features.xml \
+	usermanual-glyph-information.xml \
 	version.xml
 
 # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
diff --git a/docs/harfbuzz-docs.xml b/docs/harfbuzz-docs.xml
index 9452a92..66a64d8 100644
--- a/docs/harfbuzz-docs.xml
+++ b/docs/harfbuzz-docs.xml
@@ -12,28 +12,21 @@
       <graphic fileref="HarfBuzz.png" format="PNG" align="center"/>
       <para>
         HarfBuzz is an <ulink url="http://www.microsoft.com/typography/otspec/">OpenType</ulink>
-        text shaping engine.
+        text shaping engine. Using the HarfBuzz library allows
+	programs to convert a sequence of Unicode input into
+	properly formatted and positioned glyph output&mdash;for any writing
+	system and language.
       </para>
+
       <para>
-        The current HarfBuzz codebase, formerly known as harfbuzz-ng, is
-        versioned 1.x.x and is stable and under active maintenance. This is
-        what is used in latest versions of Firefox, GNOME, ChromeOS, Chrome,
-        LibreOffice, XeTeX, Android, and KDE, among other places. The canonical
-        source tree is available
-        <ulink url="http://cgit.freedesktop.org/harfbuzz/">here</ulink>.
-        Also available on
-        <ulink url="https://github.com/harfbuzz/harfbuzz">github</ulink>.
-        See <xref linkend="download" endterm="download.title"/> for release tarballs.
-      </para>
-      <para>
-        The old HarfBuzz codebase, these days known as harfbuzz-old, was
-        derived from <ulink url="http://freetype.org/">FreeType</ulink>,
-        <ulink url="http://pango.org/">Pango</ulink>, and
-        <ulink url="http://qt-project.org/">Qt</ulink> and is available
-        <ulink url="http://cgit.freedesktop.org/harfbuzz.old/">here</ulink>.
-        It is not actively developed or maintained, and is extremely buggy. All
-        users are encouraged to switch over to the new HarfBuzz as soon as
-        possible.  There are no release tarballs of old HarfBuzz whatsoever.
+	The canonical source-code tree is available at
+        <ulink
+	    url="https://github.com/harfbuzz/harfbuzz">github.com/harfbuzz/harfbuzz</ulink>
+        and is also available at
+        <ulink
+	    url="http://cgit.freedesktop.org/harfbuzz/">cgit.freedesktop.org/harfbuzz</ulink>.
+	See <xref linkend="download" endterm="download.title"/> for
+	release tarballs.
       </para>
     </abstract>
   </bookinfo>
@@ -42,7 +35,8 @@
     <title>User's manual</title>
       <xi:include href="usermanual-what-is-harfbuzz.xml"/>
       <xi:include href="usermanual-install-harfbuzz.xml"/>
-      <xi:include href="usermanual-hello-harfbuzz.xml"/>
+      <xi:include href="usermanual-getting-started.xml"/>
+      <xi:include href="usermanual-shaping-concepts.xml"/>
       <xi:include href="usermanual-buffers-language-script-and-direction.xml"/>
       <xi:include href="usermanual-fonts-and-faces.xml"/>
       <xi:include href="usermanual-clusters.xml"/>
@@ -58,152 +52,119 @@
         <ulink role="online-location" url="http://[SERVER]/libharfbuzz/index.html">http://[SERVER]/libharfbuzz/</ulink>.-->
       </releaseinfo>
     </partinfo>
+
+    <note>
+      <para>
+        The current HarfBuzz codebase is versioned 2.x.x and is stable
+	and under active maintenance. This is what is used in latest
+	versions of Firefox, GNOME, ChromeOS, Chrome, LibreOffice,
+	XeTeX, Android, and KDE, among other places. 
+      </para>
+      <para>
+        Prior to 2012, the original HarfBuzz codebase (which, these
+	days, is referred to as <emphasis>harfbuzz-old</emphasis>) was 
+        derived from code in <ulink
+	url="http://freetype.org/">FreeType</ulink>, <ulink
+	url="http://pango.org/">Pango</ulink>, and 
+        <ulink url="http://qt-project.org/">Qt</ulink>.
+        It is <emphasis>not</emphasis> actively developed or
+	maintained, and is extremely buggy. All users of harfbuzz-old
+	are encouraged to switch over to the new HarfBuzz as soon as possible.
+      </para>
+      <para>
+	To make this distinction clearer in discussions, the current
+	HarfBuzz codebase is sometimes referred to as
+	<emphasis>harfbuzz-ng</emphasis>.
+      </para>
+      <para>
+	For reference purposes, the harfbuzz-old source tree is archived 
+        <ulink
+	    url="http://cgit.freedesktop.org/harfbuzz.old/">here</ulink>. There
+	are no release tarballs of harfbuzz-old whatsoever.
+      </para>
+    </note>
+      
     <title>Reference manual</title>
       <chapter>
-        <title>HarfBuzz API</title>
-        <xi:include href="xml/hb.xml"/>
-        <xi:include href="xml/hb-common.xml"/>
-        <xi:include href="xml/hb-unicode.xml"/>
-        <xi:include href="xml/hb-buffer.xml"/>
+        <title>Core API</title>
         <xi:include href="xml/hb-blob.xml"/>
+        <xi:include href="xml/hb-buffer.xml"/>
+        <xi:include href="xml/hb-common.xml"/>
+        <xi:include href="xml/hb-deprecated.xml"/>
         <xi:include href="xml/hb-face.xml"/>
         <xi:include href="xml/hb-font.xml"/>
-        <xi:include href="xml/hb-shape.xml"/>
-
-        <xi:include href="xml/hb-version.xml"/>
-        <xi:include href="xml/hb-deprecated.xml"/>
-
+        <xi:include href="xml/hb-map.xml"/>
         <xi:include href="xml/hb-set.xml"/>
-
-        <xi:include href="xml/hb-ot.xml"/>
-        <xi:include href="xml/hb-ot-layout.xml"/>
-        <xi:include href="xml/hb-ot-tag.xml"/>
-        <xi:include href="xml/hb-ot-font.xml"/>
-        <xi:include href="xml/hb-ot-shape.xml"/>
-        <xi:include href="xml/hb-ot-math.xml"/>
-
         <xi:include href="xml/hb-shape-plan.xml"/>
-
-        <xi:include href="xml/hb-glib.xml"/>
-        <xi:include href="xml/hb-icu.xml"/>
-
-        <xi:include href="xml/hb-ft.xml"/>
-
-        <xi:include href="xml/hb-graphite2.xml"/>
-        <xi:include href="xml/hb-uniscribe.xml"/>
-        <xi:include href="xml/hb-coretext.xml"/>
-
-        <xi:include href="xml/hb-gobject.xml"/>
-
+        <xi:include href="xml/hb-shape.xml"/>
+        <xi:include href="xml/hb-unicode.xml"/>
+        <xi:include href="xml/hb-version.xml"/>
       </chapter>
-      <chapter id="object-tree">
+
+      <chapter>
+        <title>OpenType API</title>
+        <xi:include href="xml/hb-ot-color.xml"/>
+        <xi:include href="xml/hb-ot-font.xml"/>
+        <xi:include href="xml/hb-ot-layout.xml"/>
+        <xi:include href="xml/hb-ot-math.xml"/>
+        <xi:include href="xml/hb-ot-name.xml"/>
+        <xi:include href="xml/hb-ot-shape.xml"/>
+        <xi:include href="xml/hb-ot-var.xml"/>
+      </chapter>
+
+      <chapter>
+        <title>Integration API</title>
+        <xi:include href="xml/hb-coretext.xml"/>
+        <xi:include href="xml/hb-ft.xml"/>
+        <xi:include href="xml/hb-glib.xml"/>
+        <xi:include href="xml/hb-gobject.xml"/>
+        <xi:include href="xml/hb-graphite2.xml"/>
+        <xi:include href="xml/hb-icu.xml"/>
+        <xi:include href="xml/hb-uniscribe.xml"/>
+      </chapter>
+
+      <!--chapter id="object-tree">
         <title>Object Hierarchy</title>
          <xi:include href="xml/tree_index.sgml"/>
-      </chapter>
-      <index id="api-index-full">
-        <title>API Index</title>
-        <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-2" role="0.9.2">
-        <title>Index of new symbols in 0.9.2</title>
-        <xi:include href="xml/api-index-0.9.2.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-5" role="0.9.5">
-        <title>Index of new symbols in 0.9.5</title>
-        <xi:include href="xml/api-index-0.9.5.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-7" role="0.9.7">
-        <title>Index of new symbols in 0.9.7</title>
-        <xi:include href="xml/api-index-0.9.7.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-8" role="0.9.8">
-        <title>Index of new symbols in 0.9.8</title>
-        <xi:include href="xml/api-index-0.9.8.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-10" role="0.9.10">
-        <title>Index of new symbols in 0.9.10</title>
-        <xi:include href="xml/api-index-0.9.10.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-11" role="0.9.11">
-        <title>Index of new symbols in 0.9.11</title>
-        <xi:include href="xml/api-index-0.9.11.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-20" role="0.9.20">
-        <title>Index of new symbols in 0.9.20</title>
-        <xi:include href="xml/api-index-0.9.20.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-22" role="0.9.22">
-        <title>Index of new symbols in 0.9.22</title>
-        <xi:include href="xml/api-index-0.9.22.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-28" role="0.9.28">
-        <title>Index of new symbols in 0.9.28</title>
-        <xi:include href="xml/api-index-0.9.28.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-30" role="0.9.30">
-        <title>Index of new symbols in 0.9.30</title>
-        <xi:include href="xml/api-index-0.9.30.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-31" role="0.9.31">
-        <title>Index of new symbols in 0.9.31</title>
-        <xi:include href="xml/api-index-0.9.31.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-38" role="0.9.38">
-        <title>Index of new symbols in 0.9.38</title>
-        <xi:include href="xml/api-index-0.9.38.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-39" role="0.9.39">
-        <title>Index of new symbols in 0.9.39</title>
-        <xi:include href="xml/api-index-0.9.39.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-41" role="0.9.41">
-        <title>Index of new symbols in 0.9.41</title>
-        <xi:include href="xml/api-index-0.9.41.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-0-9-42" role="0.9.42">
-        <title>Index of new symbols in 0.9.42</title>
-        <xi:include href="xml/api-index-0.9.42.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-1-0-5" role="1.0.5">
-        <title>Index of new symbols in 1.0.5</title>
-        <xi:include href="xml/api-index-1.0.5.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-1-1-2" role="1.1.2">
-        <title>Index of new symbols in 1.1.2</title>
-        <xi:include href="xml/api-index-1.1.2.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-1-1-3" role="1.1.3">
-        <title>Index of new symbols in 1.1.3</title>
-        <xi:include href="xml/api-index-1.1.3.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-1-2-3" role="1.2.3">
-        <title>Index of new symbols in 1.2.3</title>
-        <xi:include href="xml/api-index-1.2.3.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-1-3-3" role="1.3.3">
-        <title>Index of new symbols in 1.3.3</title>
-        <xi:include href="xml/api-index-1.3.3.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-1-4-2" role="1.4.2">
-        <title>Index of new symbols in 1.4.2</title>
-        <xi:include href="xml/api-index-1.4.2.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-1-4-3" role="1.4.3">
-        <title>Index of new symbols in 1.4.3</title>
-        <xi:include href="xml/api-index-1.4.3.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-1-5-0" role="1.5.0">
-        <title>Index of new symbols in 1.5.0</title>
-        <xi:include href="xml/api-index-1.5.0.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="api-index-1-6-0" role="1.6.0">
-        <title>Index of new symbols in 1.6.0</title>
-        <xi:include href="xml/api-index-1.6.0.xml"><xi:fallback /></xi:include>
-      </index>
-      <index id="deprecated-api-index" role="deprecated">
-        <title>Index of deprecated API</title>
-        <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
-      </index>
+      </chapter-->
+
+      <index id="api-index-full"><title>API Index</title><xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include></index>
+      <index id="deprecated-api-index" role="deprecated"><title>Index of deprecated API</title><xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include></index>
+
+      <index id="api-index-2-1-0" role="2.1.0"><title>Index of new symbols in 2.1.0</title><xi:include href="xml/api-index-2.1.0.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-2-0-0" role="2.0.0"><title>Index of new symbols in 2.0.0</title><xi:include href="xml/api-index-2.0.0.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-9-0" role="1.9.0"><title>Index of new symbols in 1.9.0</title><xi:include href="xml/api-index-1.9.0.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-8-6" role="1.8.6"><title>Index of new symbols in 1.8.6</title><xi:include href="xml/api-index-1.8.6.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-8-5" role="1.8.5"><title>Index of new symbols in 1.8.5</title><xi:include href="xml/api-index-1.8.5.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-8-1" role="1.8.1"><title>Index of new symbols in 1.8.1</title><xi:include href="xml/api-index-1.8.1.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-8-0" role="1.8.0"><title>Index of new symbols in 1.8.0</title><xi:include href="xml/api-index-1.8.0.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-7-7" role="1.7.7"><title>Index of new symbols in 1.7.7</title><xi:include href="xml/api-index-1.7.7.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-7-5" role="1.7.5"><title>Index of new symbols in 1.7.5</title><xi:include href="xml/api-index-1.7.5.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-6-0" role="1.6.0"><title>Index of new symbols in 1.6.0</title><xi:include href="xml/api-index-1.6.0.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-5-0" role="1.5.0"><title>Index of new symbols in 1.5.0</title><xi:include href="xml/api-index-1.5.0.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-4-3" role="1.4.3"><title>Index of new symbols in 1.4.3</title><xi:include href="xml/api-index-1.4.3.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-4-2" role="1.4.2"><title>Index of new symbols in 1.4.2</title><xi:include href="xml/api-index-1.4.2.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-4-0" role="1.4.0"><title>Index of new symbols in 1.4.0</title><xi:include href="xml/api-index-1.4.0.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-3-3" role="1.3.3"><title>Index of new symbols in 1.3.3</title><xi:include href="xml/api-index-1.3.3.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-2-3" role="1.2.3"><title>Index of new symbols in 1.2.3</title><xi:include href="xml/api-index-1.2.3.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-1-3" role="1.1.3"><title>Index of new symbols in 1.1.3</title><xi:include href="xml/api-index-1.1.3.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-1-2" role="1.1.2"><title>Index of new symbols in 1.1.2</title><xi:include href="xml/api-index-1.1.2.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-0-5" role="1.0.5"><title>Index of new symbols in 1.0.5</title><xi:include href="xml/api-index-1.0.5.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-42" role="0.9.42"><title>Index of new symbols in 0.9.42</title><xi:include href="xml/api-index-0.9.42.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-41" role="0.9.41"><title>Index of new symbols in 0.9.41</title><xi:include href="xml/api-index-0.9.41.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-39" role="0.9.39"><title>Index of new symbols in 0.9.39</title><xi:include href="xml/api-index-0.9.39.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-38" role="0.9.38"><title>Index of new symbols in 0.9.38</title><xi:include href="xml/api-index-0.9.38.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-31" role="0.9.31"><title>Index of new symbols in 0.9.31</title><xi:include href="xml/api-index-0.9.31.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-30" role="0.9.30"><title>Index of new symbols in 0.9.30</title><xi:include href="xml/api-index-0.9.30.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-28" role="0.9.28"><title>Index of new symbols in 0.9.28</title><xi:include href="xml/api-index-0.9.28.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-22" role="0.9.22"><title>Index of new symbols in 0.9.22</title><xi:include href="xml/api-index-0.9.22.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-20" role="0.9.20"><title>Index of new symbols in 0.9.20</title><xi:include href="xml/api-index-0.9.20.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-11" role="0.9.11"><title>Index of new symbols in 0.9.11</title><xi:include href="xml/api-index-0.9.11.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-10" role="0.9.10"><title>Index of new symbols in 0.9.10</title><xi:include href="xml/api-index-0.9.10.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-8" role="0.9.8"><title>Index of new symbols in 0.9.8</title><xi:include href="xml/api-index-0.9.8.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-7" role="0.9.7"><title>Index of new symbols in 0.9.7</title><xi:include href="xml/api-index-0.9.7.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-5" role="0.9.5"><title>Index of new symbols in 0.9.5</title><xi:include href="xml/api-index-0.9.5.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-2" role="0.9.2"><title>Index of new symbols in 0.9.2</title><xi:include href="xml/api-index-0.9.2.xml"><xi:fallback /></xi:include></index>
 
       <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
   </part>
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index b93cd1d..6a498e9 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -1,8 +1,6 @@
-<SECTION>
-<FILE>hb</FILE>
 <SUBSECTION Private>
 HB_H_IN
-HB_EXTERN
+HB_OT_H_IN
 </SECTION>
 
 <SECTION>
@@ -67,6 +65,8 @@
 hb_buffer_get_user_data
 hb_buffer_get_glyph_infos
 hb_buffer_get_glyph_positions
+hb_buffer_get_invisible_glyph
+hb_buffer_set_invisible_glyph
 hb_buffer_set_replacement_codepoint
 hb_buffer_get_replacement_codepoint
 hb_buffer_normalize_glyphs
@@ -144,6 +144,10 @@
 uint32_t
 uint64_t
 uint8_t
+<SUBSECTION Private>
+HB_EXTERN
+HB_DEPRECATED
+HB_DEPRECATED_FOR
 </SECTION>
 
 <SECTION>
@@ -153,7 +157,26 @@
 HB_SCRIPT_CANADIAN_ABORIGINAL
 hb_font_funcs_set_glyph_func
 hb_font_get_glyph_func_t
+hb_ot_layout_table_choose_script
+hb_ot_layout_table_find_script
+hb_ot_tag_from_language
+hb_ot_tags_from_script
 hb_set_invert
+hb_unicode_eastasian_width_func_t
+hb_unicode_eastasian_width
+hb_unicode_funcs_set_eastasian_width_func
+HB_UNICODE_MAX_DECOMPOSITION_LEN
+hb_unicode_decompose_compatibility_func_t
+hb_unicode_decompose_compatibility
+hb_unicode_funcs_set_decompose_compatibility_func
+hb_font_funcs_set_glyph_h_kerning_func
+hb_font_funcs_set_glyph_v_kerning_func
+hb_font_get_glyph_h_kerning
+hb_font_get_glyph_h_kerning_func_t
+hb_font_get_glyph_kerning_for_direction
+hb_font_get_glyph_kerning_func_t
+hb_font_get_glyph_v_kerning
+hb_font_get_glyph_v_kerning_func_t
 </SECTION>
 
 <SECTION>
@@ -170,6 +193,7 @@
 <SECTION>
 <FILE>hb-face</FILE>
 hb_face_count
+hb_face_t
 hb_face_create
 hb_face_create_for_tables
 hb_face_destroy
@@ -188,7 +212,11 @@
 hb_face_set_index
 hb_face_set_upem
 hb_face_set_user_data
-hb_face_t
+hb_face_collect_unicodes
+hb_face_collect_variation_selectors
+hb_face_collect_variation_unicodes
+hb_face_builder_create
+hb_face_builder_add_table
 </SECTION>
 
 <SECTION>
@@ -208,13 +236,14 @@
 hb_font_funcs_set_glyph_extents_func
 hb_font_funcs_set_glyph_from_name_func
 hb_font_funcs_set_glyph_h_advance_func
-hb_font_funcs_set_glyph_h_kerning_func
+hb_font_funcs_set_glyph_h_advances_func
 hb_font_funcs_set_glyph_h_origin_func
 hb_font_funcs_set_glyph_name_func
 hb_font_funcs_set_glyph_v_advance_func
-hb_font_funcs_set_glyph_v_kerning_func
+hb_font_funcs_set_glyph_v_advances_func
 hb_font_funcs_set_glyph_v_origin_func
 hb_font_funcs_set_nominal_glyph_func
+hb_font_funcs_set_nominal_glyphs_func
 hb_font_funcs_set_user_data
 hb_font_funcs_set_variation_glyph_func
 hb_font_funcs_t
@@ -223,6 +252,8 @@
 hb_font_get_glyph
 hb_font_get_glyph_advance_for_direction
 hb_font_get_glyph_advance_func_t
+hb_font_get_glyph_advances_for_direction
+hb_font_get_glyph_advances_func_t
 hb_font_get_glyph_contour_point
 hb_font_get_glyph_contour_point_for_origin
 hb_font_get_glyph_contour_point_func_t
@@ -233,24 +264,24 @@
 hb_font_get_glyph_from_name_func_t
 hb_font_get_glyph_h_advance
 hb_font_get_glyph_h_advance_func_t
-hb_font_get_glyph_h_kerning
-hb_font_get_glyph_h_kerning_func_t
+hb_font_get_glyph_h_advances
+hb_font_get_glyph_h_advances_func_t
 hb_font_get_glyph_h_origin
 hb_font_get_glyph_h_origin_func_t
-hb_font_get_glyph_kerning_for_direction
-hb_font_get_glyph_kerning_func_t
 hb_font_get_glyph_name
 hb_font_get_glyph_name_func_t
 hb_font_get_glyph_origin_for_direction
 hb_font_get_glyph_origin_func_t
 hb_font_get_glyph_v_advance
 hb_font_get_glyph_v_advance_func_t
-hb_font_get_glyph_v_kerning
-hb_font_get_glyph_v_kerning_func_t
+hb_font_get_glyph_v_advances
+hb_font_get_glyph_v_advances_func_t
 hb_font_get_glyph_v_origin
 hb_font_get_glyph_v_origin_func_t
 hb_font_get_nominal_glyph
 hb_font_get_nominal_glyph_func_t
+hb_font_get_nominal_glyphs
+hb_font_get_nominal_glyphs_func_t
 hb_font_get_parent
 hb_font_get_ppem
 hb_font_get_ptem
@@ -327,7 +358,9 @@
 HB_GOBJECT_TYPE_FONT
 HB_GOBJECT_TYPE_FONT_FUNCS
 HB_GOBJECT_TYPE_GLYPH_FLAGS
+HB_GOBJECT_TYPE_MAP
 HB_GOBJECT_TYPE_MEMORY_MODE
+HB_GOBJECT_TYPE_OT_COLOR_PALETTE_FLAGS
 HB_GOBJECT_TYPE_OT_LAYOUT_GLYPH_CLASS
 HB_GOBJECT_TYPE_OT_MATH_CONSTANT
 HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART
@@ -358,7 +391,9 @@
 hb_gobject_font_funcs_get_type
 hb_gobject_font_get_type
 hb_gobject_glyph_flags_get_type
+hb_gobject_map_get_type
 hb_gobject_memory_mode_get_type
+hb_gobject_ot_color_palette_flags_get_type
 hb_gobject_ot_layout_glyph_class_get_type
 hb_gobject_ot_math_constant_get_type
 hb_gobject_ot_math_glyph_part_get_type
@@ -382,11 +417,6 @@
 </SECTION>
 
 <SECTION>
-<FILE>hb-gobject</FILE>
-
-</SECTION>
-
-<SECTION>
 <FILE>hb-graphite2</FILE>
 HB_GRAPHITE2_TAG_SILF
 hb_graphite2_face_get_gr_face
@@ -421,9 +451,27 @@
 </SECTION>
 
 <SECTION>
-<FILE>hb-ot</FILE>
-<SUBSECTION Private>
-HB_OT_H_IN
+<FILE>hb-ot-color</FILE>
+hb_color_t
+HB_COLOR
+hb_color_get_alpha
+hb_color_get_blue
+hb_color_get_green
+hb_color_get_red
+hb_ot_color_glyph_get_layers
+hb_ot_color_glyph_reference_png
+hb_ot_color_glyph_reference_svg
+hb_ot_color_has_layers
+hb_ot_color_has_palettes
+hb_ot_color_has_png
+hb_ot_color_has_svg
+hb_ot_color_layer_t
+hb_ot_color_palette_color_get_name_id
+hb_ot_color_palette_flags_t
+hb_ot_color_palette_get_colors
+hb_ot_color_palette_get_count
+hb_ot_color_palette_get_flags
+hb_ot_color_palette_get_name_id
 </SECTION>
 
 <SECTION>
@@ -432,25 +480,42 @@
 </SECTION>
 
 <SECTION>
-<FILE>hb-ot-shape</FILE>
-hb_ot_shape_glyphs_closure
+<FILE>hb-ot-name</FILE>
+hb_ot_name_id_t
+HB_OT_NAME_ID_INVALID
+hb_ot_name_entry_t
+hb_ot_name_list_names
+hb_ot_name_get_utf16
+hb_ot_name_get_utf32
+hb_ot_name_get_utf8
 </SECTION>
 
 <SECTION>
 <FILE>hb-ot-layout</FILE>
+HB_OT_MAX_TAGS_PER_LANGUAGE
+HB_OT_MAX_TAGS_PER_SCRIPT
+HB_OT_TAG_DEFAULT_LANGUAGE
+HB_OT_TAG_DEFAULT_SCRIPT
+hb_ot_tag_to_language
+hb_ot_tag_to_script
+hb_ot_tags_from_script_and_language
+hb_ot_tags_to_script_and_language
 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX
 HB_OT_LAYOUT_NO_FEATURE_INDEX
 HB_OT_LAYOUT_NO_SCRIPT_INDEX
 HB_OT_LAYOUT_NO_VARIATIONS_INDEX
+HB_OT_TAG_BASE
 HB_OT_TAG_GDEF
 HB_OT_TAG_GPOS
 HB_OT_TAG_GSUB
 HB_OT_TAG_JSTF
 hb_ot_layout_collect_lookups
+hb_ot_layout_collect_features
+hb_ot_layout_feature_get_characters
 hb_ot_layout_feature_get_lookups
+hb_ot_layout_feature_get_name_ids
 hb_ot_layout_feature_with_variations_get_lookups
 hb_ot_layout_get_attach_points
-hb_ot_layout_get_feature_name_ids
 hb_ot_layout_get_glyph_class
 hb_ot_layout_get_glyphs_in_class
 hb_ot_layout_get_ligature_carets
@@ -470,12 +535,12 @@
 hb_ot_layout_lookup_would_substitute
 hb_ot_layout_script_find_language
 hb_ot_layout_script_get_language_tags
-hb_ot_layout_table_choose_script
+hb_ot_layout_script_select_language
 hb_ot_layout_table_find_feature_variations
-hb_ot_layout_table_find_script
 hb_ot_layout_table_get_feature_tags
 hb_ot_layout_table_get_script_tags
 hb_ot_layout_table_get_lookup_count
+hb_ot_layout_table_select_script
 hb_ot_shape_plan_collect_lookups
 hb_ot_layout_language_get_required_feature_index
 <SUBSECTION Private>
@@ -485,23 +550,6 @@
 </SECTION>
 
 <SECTION>
-<FILE>hb-ot-var</FILE>
-HB_OT_TAG_VAR_AXIS_ITALIC
-HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE
-HB_OT_TAG_VAR_AXIS_SLANT
-HB_OT_TAG_VAR_AXIS_WEIGHT
-HB_OT_TAG_VAR_AXIS_WIDTH
-HB_OT_VAR_NO_AXIS_INDEX
-hb_ot_var_axis_t
-hb_ot_var_has_data
-hb_ot_var_find_axis
-hb_ot_var_get_axis_count
-hb_ot_var_get_axes
-hb_ot_var_normalize_variations
-hb_ot_var_normalize_coords
-</SECTION>
-
-<SECTION>
 <FILE>hb-ot-math</FILE>
 HB_OT_TAG_MATH
 HB_OT_MATH_SCRIPT
@@ -522,13 +570,25 @@
 </SECTION>
 
 <SECTION>
-<FILE>hb-ot-tag</FILE>
-HB_OT_TAG_DEFAULT_LANGUAGE
-HB_OT_TAG_DEFAULT_SCRIPT
-hb_ot_tag_from_language
-hb_ot_tag_to_language
-hb_ot_tag_to_script
-hb_ot_tags_from_script
+<FILE>hb-ot-shape</FILE>
+hb_ot_shape_glyphs_closure
+</SECTION>
+
+<SECTION>
+<FILE>hb-ot-var</FILE>
+HB_OT_TAG_VAR_AXIS_ITALIC
+HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE
+HB_OT_TAG_VAR_AXIS_SLANT
+HB_OT_TAG_VAR_AXIS_WEIGHT
+HB_OT_TAG_VAR_AXIS_WIDTH
+HB_OT_VAR_NO_AXIS_INDEX
+hb_ot_var_axis_t
+hb_ot_var_has_data
+hb_ot_var_find_axis
+hb_ot_var_get_axis_count
+hb_ot_var_get_axes
+hb_ot_var_normalize_variations
+hb_ot_var_normalize_coords
 </SECTION>
 
 <SECTION>
@@ -567,6 +627,8 @@
 
 <SECTION>
 <FILE>hb-shape</FILE>
+HB_FEATURE_GLOBAL_END
+HB_FEATURE_GLOBAL_START
 hb_feature_t
 hb_feature_from_string
 hb_feature_to_string
@@ -593,16 +655,14 @@
 
 <SECTION>
 <FILE>hb-unicode</FILE>
-HB_UNICODE_MAX_DECOMPOSITION_LEN
+HB_UNICODE_MAX
 hb_unicode_combining_class
 hb_unicode_combining_class_func_t
 hb_unicode_combining_class_t
 hb_unicode_compose
 hb_unicode_compose_func_t
 hb_unicode_decompose
-hb_unicode_decompose_compatibility
 hb_unicode_decompose_func_t
-hb_unicode_eastasian_width
 hb_unicode_funcs_create
 hb_unicode_funcs_destroy
 hb_unicode_funcs_get_default
@@ -614,9 +674,7 @@
 hb_unicode_funcs_reference
 hb_unicode_funcs_set_combining_class_func
 hb_unicode_funcs_set_compose_func
-hb_unicode_funcs_set_decompose_compatibility_func
 hb_unicode_funcs_set_decompose_func
-hb_unicode_funcs_set_eastasian_width_func
 hb_unicode_funcs_set_general_category_func
 hb_unicode_funcs_set_mirroring_func
 hb_unicode_funcs_set_script_func
diff --git a/docs/usermanual-buffers-language-script-and-direction.xml b/docs/usermanual-buffers-language-script-and-direction.xml
index 9eddb71..68ce9bd 100644
--- a/docs/usermanual-buffers-language-script-and-direction.xml
+++ b/docs/usermanual-buffers-language-script-and-direction.xml
@@ -1,3 +1,9 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
 <chapter id="buffers-language-script-and-direction">
   <title>Buffers, language, script and direction</title>
   <para>
@@ -74,4 +80,4 @@
     <para>
     </para>
   </section>
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/docs/usermanual-clusters.xml b/docs/usermanual-clusters.xml
index 608371b..7b2c7ad 100644
--- a/docs/usermanual-clusters.xml
+++ b/docs/usermanual-clusters.xml
@@ -1,3 +1,9 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
 <chapter id="clusters">
 <sect1 id="clusters">
   <title>Clusters</title>
diff --git a/docs/usermanual-fonts-and-faces.xml b/docs/usermanual-fonts-and-faces.xml
index 7de0f05..5536004 100644
--- a/docs/usermanual-fonts-and-faces.xml
+++ b/docs/usermanual-fonts-and-faces.xml
@@ -1,3 +1,9 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
 <chapter id="fonts-and-faces">
   <title>Fonts and faces</title>
   <section id="using-freetype">
@@ -15,4 +21,4 @@
     <para>
     </para>
   </section>
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/docs/usermanual-getting-started.xml b/docs/usermanual-getting-started.xml
new file mode 100644
index 0000000..9e16ecb
--- /dev/null
+++ b/docs/usermanual-getting-started.xml
@@ -0,0 +1,222 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
+<chapter id="getting-started">
+  <title>Getting started with HarfBuzz</title>
+  <section>
+    <title>An overview of the HarfBuzz shaping API</title>
+    <para>
+      The core of the HarfBuzz shaping API is the function
+      <function>hb_shape()</function>. This function takes a font, a
+      buffer containing a string of Unicode codepoints and
+      (optionally) a list of font features as its input. It replaces
+      the codepoints in the buffer with the corresponding glyphs from
+      the font, correctly ordered and positioned, and with any of the
+      optional font features applied.
+    </para>
+    <para>
+      In addition to holding the pre-shaping input (the Unicode
+      codepoints that comprise the input string) and the post-shaping
+      output (the glyphs and positions), a HarfBuzz buffer has several
+      properties that affect shaping. The most important are the
+      text-flow direction (e.g., left-to-right, right-to-left,
+      top-to-bottom, or bottom-to-top), the script tag, and the
+      language tag.
+    </para>
+
+    <para>
+      For input string buffers, flags are available to denote when the
+      buffer represents the beginning or end of a paragraph, to
+      indicate whether or not to visibly render Unicode <literal>Default
+      Ignorable</literal> codepoints, and to modify the cluster-merging
+      behavior for the buffer. For shaped output buffers, the
+      individual X and Y offsets and <literal>advances</literal>
+      (the logical dimensions) of each glyph are 
+      accessible. HarfBuzz also flags glyphs as
+      <literal>UNSAFE_TO_BREAK</literal> if breaking the string at
+      that glyph (e.g., in a line-breaking or hyphenation process)
+      would require re-shaping the text.
+    </para>
+    
+    <para>
+      HarfBuzz also provides methods to compare the contents of
+      buffers, join buffers, normalize buffer contents, and handle
+      invalid codepoints, as well as to determine the state of a
+      buffer (e.g., input codepoints or output glyphs). Buffer
+      lifecycles are managed and all buffers are reference-counted.
+    </para>
+
+    <para>
+      Although the default <function>hb_shape()</function> function is
+      sufficient for most use cases, a variant is also provide that
+      lets you specify which of HarfBuzz's shapers to use on a buffer. 
+    </para>
+
+    <para>
+      HarfBuzz can read TrueType fonts, TrueType collections, OpenType
+      fonts, and OpenType collections. Functions are provided to query
+      font objects about metrics, Unicode coverage, available tables and
+      features, and variation selectors. Individual glyphs can also be
+      queried for metrics, variations, and glyph names. OpenType
+      variable fonts are supported, and HarfBuzz allows you to set
+      variation-axis coordinates on font objects.
+    </para>
+    
+    <para>
+      HarfBuzz provides glue code to integrate with various other
+      libraries, including FreeType, GObject, and CoreText. Support
+      for integrating with Uniscribe and DirectWrite is experimental
+      at present.
+    </para>
+  </section>
+
+  <section>
+    <title>Terminology</title>
+      <variablelist>
+	<varlistentry>
+	  <term>shaper</term>
+	  <listitem>
+	    <para>
+	      In HarfBuzz, a <emphasis>shaper</emphasis> is a
+	      handler for a specific script shaping model. HarfBuzz
+	      implements separate shapers for Indic, Arabic, Thai and
+	      Lao, Khmer, Myanmar, Tibetan, Hangul, Hebrew, the
+	      Universal Shaping Engine (USE), and a default shaper for
+	      non-complex scripts. 
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>cluster</term>
+	  <listitem>
+	    <para>
+	      In text shaping, a <emphasis>cluster</emphasis> is a
+	      sequence of codepoints that must be handled as an
+	      indivisible unit. Clusters can include codepoint
+	      sequences that form a ligature or base-and-mark
+	      sequences. Tracking and preserving clusters is important
+	      when shaping operations might separate or reorder
+	      codepoints.
+	    </para>
+	    <para>
+	      HarfBuzz provides three cluster
+	      <emphasis>levels</emphasis> that implement different
+	      approaches to the problem of preserving clusters during
+	      shaping operations.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+
+      </variablelist>
+    
+  </section>
+
+
+  <section>
+    <title>A simple shaping example</title>
+
+    <para>
+      Below is the simplest HarfBuzz shaping example possible.
+    </para>
+    <orderedlist numeration="arabic">
+      <listitem>
+	<para>
+          Create a buffer and put your text in it.
+	</para>
+      </listitem>
+    </orderedlist>
+    <programlisting language="C">
+      #include &lt;hb.h&gt;
+      hb_buffer_t *buf;
+      buf = hb_buffer_create();
+      hb_buffer_add_utf8(buf, text, -1, 0, -1);
+    </programlisting>
+    <orderedlist numeration="arabic">
+      <listitem override="2">
+	<para>
+          Guess the script, language and direction of the buffer.
+	</para>
+      </listitem>
+    </orderedlist>
+    <programlisting language="C">
+      hb_buffer_set_direction(buf, HB_DIRECTION_LTR);
+      hb_buffer_set_script(buf, HB_SCRIPT_LATIN);
+      hb_buffer_set_language(buf, hb_language_from_string("en", -1));
+    </programlisting>
+    <orderedlist numeration="arabic">
+      <listitem override="3">
+	<para>
+          Create a face and a font, using FreeType for now.
+	</para>
+      </listitem>
+    </orderedlist>
+    <programlisting language="C">
+      #include &lt;hb-ft.h&gt;
+      FT_New_Face(ft_library, font_path, index, &amp;face)
+      hb_font_t *font = hb_ft_font_create(face);
+    </programlisting>
+    <orderedlist numeration="arabic">
+      <listitem override="4">
+	<para>
+          Shape!
+	</para>
+      </listitem>
+    </orderedlist>
+    <programlisting>
+      hb_shape(font, buf, NULL, 0);
+    </programlisting>
+    <orderedlist numeration="arabic">
+      <listitem override="5">
+	<para>
+          Get the glyph and position information.
+	</para>
+      </listitem>
+    </orderedlist>
+    <programlisting language="C">
+      hb_glyph_info_t *glyph_info    = hb_buffer_get_glyph_infos(buf, &amp;glyph_count);
+      hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &amp;glyph_count);
+    </programlisting>
+    <orderedlist numeration="arabic">
+      <listitem override="6">
+	<para>
+          Iterate over each glyph.
+	</para>
+      </listitem>
+    </orderedlist>
+    <programlisting language="C">
+      for (i = 0; i &lt; glyph_count; ++i) {
+      glyphid = glyph_info[i].codepoint;
+      x_offset = glyph_pos[i].x_offset / 64.0;
+      y_offset = glyph_pos[i].y_offset / 64.0;
+      x_advance = glyph_pos[i].x_advance / 64.0;
+      y_advance = glyph_pos[i].y_advance / 64.0;
+      draw_glyph(glyphid, cursor_x + x_offset, cursor_y + y_offset);
+      cursor_x += x_advance;
+      cursor_y += y_advance;
+      }
+    </programlisting>
+    <orderedlist numeration="arabic">
+      <listitem override="7">
+	<para>
+          Tidy up.
+	</para>
+      </listitem>
+    </orderedlist>
+    <programlisting language="C">
+      hb_buffer_destroy(buf);
+      hb_font_destroy(hb_ft_font);
+    </programlisting>
+    
+    <para>
+      This example shows enough to get us started using HarfBuzz. In
+      the sections that follow, we will use the remainder of
+      HarfBuzz's API to refine and extend the example and improve its
+      text-shaping capabilities.
+    </para>
+  </section>
+</chapter>
diff --git a/docs/usermanual-glyph-information.xml b/docs/usermanual-glyph-information.xml
index ca674c0..78f06c7 100644
--- a/docs/usermanual-glyph-information.xml
+++ b/docs/usermanual-glyph-information.xml
@@ -1,3 +1,9 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
 <sect1 id="glyph-information">
   <title>Glyph information</title>
   <sect2 id="names-and-numbers">
@@ -5,4 +11,4 @@
     <para>
     </para>
   </sect2>
-</sect1>
\ No newline at end of file
+</sect1>
diff --git a/docs/usermanual-install-harfbuzz.xml b/docs/usermanual-install-harfbuzz.xml
index 899cc5b..a6484fc 100644
--- a/docs/usermanual-install-harfbuzz.xml
+++ b/docs/usermanual-install-harfbuzz.xml
@@ -1,70 +1,431 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
 <chapter id="install-harfbuzz">
-  <title>Install HarfBuzz</title>
+  <title>Installing HarfBuzz</title>
+  
   <section id="download">
-    <title id="download.title">Download</title>
+    <title id="download.title">Downloading HarfBuzz</title>
     <para>
-      For tarball releases of HarfBuzz, look
-      <ulink url="http://www.freedesktop.org/software/harfbuzz/release/">here</ulink>.
-      At the same place you will
-      also find Win32 binary bundles that include libharfbuzz DLL, hb-view.exe,
-      hb-shape.exe, and all dependencies.
+      The HarfBuzz source code is hosted at <ulink
+      url="https://github.com/harfbuzz/harfbuzz">github.com/harfbuzz/harfbuzz</ulink>. The
+      same source tree is also available at the
+      <ulink
+	  url="http://cgit.freedesktop.org/harfbuzz/">Freedesktop.org</ulink>
+      site.
     </para>
     <para>
-      The canonical source tree is available
-      <ulink url="http://cgit.freedesktop.org/harfbuzz/">here</ulink>.
-      Also available on <ulink url="https://github.com/harfbuzz/harfbuzz">github</ulink>.
+      Tarball releases and Win32 binary bundles (which include the
+      libharfbuzz DLL, hb-view.exe, hb-shape.exe, and all
+      dependencies) of HarfBuzz can be downloaded from <ulink
+      url="https://github.com/harfbuzz/harfbuzz">github.com/harfbuzz/harfbuzz/releases</ulink>
+      or from 
+      <ulink url="http://www.freedesktop.org/software/harfbuzz/release/">Freedesktop.org</ulink>.
     </para>
     <para>
-      The API that comes with <filename class='headerfile'>hb.h</filename> will
-      not change incompatibly. Other, peripheral, headers are more likely to go
-      through minor modifications, but again, will do our best to never change
-      API in an incompatible way. We will never break the ABI.
+      Release notes are posted with each new release to provide an
+      overview of the changes. The project <ulink url="https://github.com/harfbuzz/harfbuzz/issues">tracks bug
+      reports and other issues</ulink> on GitHub. Discussion and
+      questions are welcome on the <ulink
+      url="http://freedesktop.org/mailman/listinfo/harfbuzz/">HarfBuzz
+      mailing list</ulink>.
     </para>
     <para>
-      If you are not sure whether Pango or HarfBuzz is right for you, read
-      <ulink url="http://mces.blogspot.in/2009/11/pango-vs-harfbuzz.html">this</ulink>.
+      The API included in the <filename
+      class='headerfile'>hb.h</filename> file will not change in a
+      compatibility-breaking way in any release. However, other,
+      peripheral headers are more likely to go through minor
+      modifications. We will do our best to never change APIs in an
+      incompatible way. We will <emphasis>never</emphasis> break the ABI. 
     </para>
   </section>
+  
   <section id="building">
-    <title>Building</title>
+    <title>Building HarfBuzz</title>
+
+    <section id="building.linux">
+      <title>Building on Linux</title>
     <para>
-      On Linux, install the development packages for FreeType, Cairo, and GLib.
-      For example, on Ubuntu / Debian, you would do:
-      <programlisting>
-<command>sudo apt-get install</command> <package>gcc g++ libfreetype6-dev libglib2.0-dev libcairo2-dev</package>
-      </programlisting>
-      whereas on Fedora, RHEL, CentOS, and other Red Hat based systems you would do:
-      <programlisting>
-<command>sudo yum install</command> <package>gcc gcc-c++ freetype-devel glib2-devel cairo-devel</package>
-      </programlisting>
-      or using MacPorts:
-      <programlisting>
-<command>sudo port install</command> <package>freetype glib2 cairo</package>
-      </programlisting>
+      <emphasis>(1)</emphasis> To build HarfBuzz on Linux, you must first install the
+      development packages for FreeType, Cairo, and GLib. The exact
+      commands required for this step will vary depending on
+      the Linux distribution you use.
     </para>
     <para>
-      If you are using a tarball, you can now proceed to running
-      <command>configure</command> and <command>make</command> as with any
-      other standard package. That should leave you with a shared library in
-      <filename>src/</filename>, and a few utility programs including hb-view
-      and hb-shape under <filename>util/</filename>.
+      For example, on an Ubuntu or Debian system, you would run:
+      <programlisting>
+	<command>sudo apt install</command> <package>gcc g++
+	libfreetype6-dev libglib2.0-dev libcairo2-dev</package>
+      </programlisting>
+      On Fedora, RHEL, CentOS, or other Red-Hat&ndash;based systems, you would run:
+      <programlisting>
+	<command>sudo yum install</command> <package>gcc gcc-c++ freetype-devel glib2-devel cairo-devel</package>
+      </programlisting>
+
+    </para>
+    
+    <para>
+      <emphasis>(2)</emphasis> The next step depends on whether you
+      are building from the source in a downloaded release tarball or
+      from the source directly from the git repository.
     </para>
     <para>
-      If you are bootstrapping from git, you need a few more tools before you
-      can run <filename>autogen.sh</filename> for the first time. Namely,
-      pkg-config and <ulink url="http://www.complang.org/ragel/">ragel</ulink>.
-      Again, on Ubuntu / Debian:
-      <programlisting>
-<command>sudo apt-get install</command> <package>autoconf automake libtool pkg-config ragel gtk-doc-tools</package>
-      </programlisting>
-      and on Fedora, RHEL, CentOS:
-      <programlisting>
-<command>sudo yum install</command> <package>autoconf automake libtool pkgconfig ragel gtk-doc</package>
-      </programlisting>
-      or using MacPorts:
-      <programlisting>
-<command>sudo port install</command> <package>autoconf automake libtool pkgconfig ragel gtk-doc</package>
-      </programlisting>
+      <emphasis>(2)(a)</emphasis> If you downloaded the HarfBuzz
+      source code in a tarball, you can now extract the source.
     </para>
+    <para>
+      From a shell in the top-level directory of the extracted source
+      code, you can run <command>./configure</command> followed by
+      <command>make</command> as with any other standard package.
+    </para>
+    <para>
+      This should leave you with a shared
+      library in the <filename>src/</filename> directory, and a few
+      utility programs including <command>hb-view</command> and
+      <command>hb-shape</command> under the <filename>util/</filename>
+      directory.
+    </para>
+    <para>
+      <emphasis>(2)(b)</emphasis> If you are building from the source in the HarfBuzz git
+      repository, rather than installing from a downloaded tarball
+      release, then you must install two more auxiliary tools before you 
+      can build for the first time: <package>pkg-config</package> and
+      <ulink url="http://www.complang.org/ragel/">ragel</ulink>.
+    </para>
+    <para>
+      On Ubuntu or Debian, run:
+      <programlisting>
+	<command>sudo apt-get install</command> <package>autoconf automake libtool pkg-config ragel gtk-doc-tools</package>
+      </programlisting>
+      On Fedora, RHEL, CentOS, run:
+      <programlisting>
+	<command>sudo yum install</command> <package>autoconf automake libtool pkgconfig ragel gtk-doc</package>
+      </programlisting>
+      
+    </para>
+    <para>
+      With <package>pkg-config</package> and <package>ragel</package>
+      installed, you can now run <command>./autogen.sh</command>,
+      followed by <command>./configure</command> and
+      <command>make</command> to build HarfBuzz.
+    </para>
+    </section>
+
+    
+    <section id="building.windows">
+      <title>Building on Windows</title>
+
+      <para>
+	On Windows, consider using Microsoft's free <ulink
+	url="https://github.com/Microsoft/vcpkg">vcpkg</ulink> utility
+	to build HarfBuzz, its dependencies, and other open-source
+	libraries. 
+      </para>
+      <para>
+	If you need to build HarfBuzz from source, first put the
+	<program>ragel</program> binary on your
+	<literal>PATH</literal>, then follow the appveyor CI cmake
+	<ulink
+	    url="https://github.com/harfbuzz/harfbuzz/blob/master/appveyor.yml">build
+	instructions</ulink>. 
+      </para>
+    </section>
+
+    
+    <section id="building.macos">
+      <title>Building on macOS</title>
+
+      <para>
+	There are two ways to build HarfBuzz on Mac systems: MacPorts
+	and Homebrew. The process is similar to the process used on a
+	Linux system.
+      </para>
+      <para>
+	<emphasis>(1)</emphasis> You must first install the
+	development packages for FreeType, Cairo, and GLib. If you are
+	using MacPorts, you should run:
+      <programlisting>
+	<command>sudo port install</command> <package>freetype glib2 cairo</package>
+      </programlisting>
+      </para>
+      <para>
+	If you are using Homebrew, you should run:
+	<programlisting>	
+	  <command>brew install</command> <package>freetype glib cairo</package>
+	</programlisting>
+      </para>
+      <para>
+	<emphasis>(2)</emphasis> The next step depends on whether you are building from the
+	source in a downloaded release tarball or from the source directly
+	from the git repository.
+      </para>
+      <para>
+	<emphasis>(2)(a)</emphasis> If you are installing HarfBuzz
+	from a downloaded tarball release, extract the tarball and
+	open a Terminal in the extracted source-code directory. Run:
+	<programlisting>
+	  <command>./configure</command>
+	</programlisting>
+	followed by:
+	<programlisting>	
+	  <command>make</command>
+	</programlisting>
+	to build HarfBuzz.
+      </para>
+      <para>
+	<emphasis>(2)(b)</emphasis> Alternatively, if you are building
+	HarfBuzz from the source in the HarfBuzz git repository, then
+	you must install several built-time dependencies before
+	proceeding.
+      </para>
+      <para>If you are
+	using MacPorts, you should run:
+      <programlisting>
+	<command>sudo port install</command> <package>autoconf
+	automake libtool pkgconfig ragel gtk-doc</package> 
+      </programlisting>
+      to install the build dependencies.
+      </para>
+      <para>If you are using Homebrew, you should run:
+	<programlisting>	
+	  <command>brew install</command> <package>autoconf automake libtool pkgconfig ragel gtk-doc</package>
+	</programlisting>
+      	Finally, you can run:
+	<programlisting>
+	  <command>./autogen.sh</command>
+	</programlisting>
+      </para>
+      <para>
+	<emphasis>(3)</emphasis> You can now build HarfBuzz (on either
+	a MacPorts or a Homebrew system) by running:
+	<programlisting>
+	  <command>./configure</command>
+	</programlisting>
+	followed by:
+	<programlisting>
+	  <command>make</command>
+	</programlisting>
+      </para>
+      <para>
+	This should leave you with a shared
+	library in the <filename>src/</filename> directory, and a few
+	utility programs including <command>hb-view</command> and
+	<command>hb-shape</command> under the <filename>util/</filename>
+	directory.
+      </para>      
+	
+    </section>
+
+    <section id="configuration">
+      <title>Configuration options</title>
+
+      <para>
+	The instructions in the "Building HarfBuzz" section will build
+	the source code under its default configuration. If needed,
+	the following additional configuration options are available.
+      </para>
+
+      <variablelist>
+	<varlistentry>
+	  <term>--with-libstdc++</term>
+	  <listitem>
+	    <para>
+	      Allow linking with libstdc++. <emphasis>(Default = no)</emphasis>
+	    </para>
+	    <para>
+	      This option enables or disables linking HarfBuzz to the
+	      system's libstdc++ library.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>--with-glib</term>
+	  <listitem>
+	    <para>
+	     Use <ulink url="https://developer.gnome.org/glib/">GLib</ulink>. <emphasis>(Default = auto)</emphasis>
+	    </para>	    
+	    <para>
+	      This option enables or disables usage of the GLib
+	      library.  The default setting is to check for the
+	      presence of GLib and, if it is found, build with
+	      GLib support. GLib is native to GNU/Linux systems but is
+	      available on other operating system as well.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>--with-gobject</term>
+	  <listitem>
+	    <para>
+	      Use <ulink url="https://developer.gnome.org/gobject/stable/">GObject</ulink>. <emphasis>(Default = no)</emphasis>
+	    </para>	   
+	    <para>
+	      This option enables or disables usage of the GObject
+	      library. The default setting is to check for the
+	      presence of GObject and, if it is found, build with
+	      GObject support. GObject is native to GNU/Linux systems but is
+	      available on other operating system as well.
+	    </para> 
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>--with-cairo</term>
+	  <listitem>
+	    <para>
+	      Use <ulink url="https://cairographics.org/">Cairo</ulink>. <emphasis>(Default = auto)</emphasis>
+	    </para>	   
+	    <para>
+	      This option enables or disables usage of the Cairo
+	      graphics-rendering library. The default setting is to
+	      check for the presence of Cairo and, if it is found,
+	      build with Cairo support.
+	    </para>
+	    <para>
+	      Note: Cairo is used only by the HarfBuzz
+	      command-line utilities, and not by the HarfBuzz library.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>--with-fontconfig</term>
+	  <listitem>
+	    <para>
+	      Use <ulink url="https://www.freedesktop.org/wiki/Software/fontconfig/">Fontconfig</ulink>. <emphasis>(Default = auto)</emphasis>
+	    </para>	    
+	    <para>
+	      This option enables or disables usage of the Fontconfig
+	      library, which provides font-matching functions and
+	      provides access to font properties. The default setting
+	      is to check for the presence of Fontconfig and, if it is
+	      found, build with Fontconfig support.
+	    </para>
+	    <para>
+	      Note: Fontconfig is used only by the HarfBuzz
+	      command-line utilities, and not by the HarfBuzz library.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>--with-icu</term>
+	  <listitem>
+	    <para>
+	      Use the <ulink url="http://site.icu-project.org/home">ICU</ulink> library. <emphasis>(Default = auto)</emphasis>
+	    </para>	    
+	    <para>
+	      This option enables or disables usage of the
+	      <emphasis>International Components for
+	      Unicode</emphasis> (ICU) library, which provides access
+	      to Unicode Character Database (UCD) properties as well
+	      as normalization and conversion functions. The default
+	      setting is to check for the presence of ICU and, if it
+	      is found, build with ICU support.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>--with-ucdn</term>
+	  <listitem>
+	    <para>
+	      Use HarfBuzz's <ulink url="https://github.com/harfbuzz/harfbuzz/tree/master/src/hb-ucdn">built-in UCDN library</ulink>. <emphasis>(Default = auto)</emphasis>
+	    </para>	    
+	    <para>
+	      The HarfBuzz source tree includes a <emphasis>Unicode
+	      Database and Normalization</emphasis> (UCDN) library
+	      that provides access to basic character properties in
+	      the Unicode Character Database (UCD) as well as low-level
+	      normalization functions. HarfBuzz can be built without
+	      this UCDN support if the usage of a different UCDN
+	      library is desired.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>--with-graphite2</term>
+	  <listitem>
+	    <para>
+	      Use the <ulink url="http://graphite.sil.org/">Graphite2</ulink> library. <emphasis>(Default = no)</emphasis>
+	    </para>	    
+	    <para>
+	      This option enables or disables usage of the Graphite2
+	      library, which provides support for the Graphite shaping
+	      model. 
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>--with-freetype</term>
+	  <listitem>
+	    <para>
+	      Use the <ulink url="https://www.freetype.org/">FreeType</ulink> library. <emphasis>(Default = auto)</emphasis>
+	    </para>	    
+	    <para>
+	      This option enables or disables usage of the FreeType
+	      font-rendering library. The default setting is to check for the
+	      presence of FreeType and, if it is found, build with
+	      FreeType support.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>--with-uniscribe</term>
+	  <listitem>
+	    <para>
+	      Use the <ulink
+	      url="https://docs.microsoft.com/en-us/windows/desktop/intl/uniscribe">Uniscribe</ulink>
+	      library (experimental). <emphasis>(Default = no)</emphasis>
+	    </para>	    
+	    <para>
+	      This option enables or disables usage of the Uniscribe
+	      font-rendering library. Uniscribe is available on
+	      Windows systems. Uniscribe support is used only for
+	      testing purposes and does not need to be enabled for
+	      HarfBuzz to run on Windows systems.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>--with-directwrite</term>
+	  <listitem>
+	    <para>
+	      Use the <ulink url="https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal">DirectWrite</ulink> library (experimental). <emphasis>(Default = no)</emphasis>
+	    </para>	    
+	    <para>
+	      This option enables or disables usage of the DirectWrite
+	      font-rendering library. DirectWrite is available on
+	      Windows systems. DirectWrite support is used only for
+	      testing purposes and does not need to be enabled for
+	      HarfBuzz to run on Windows systems.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>--with-coretext</term>
+	  <listitem>
+	    <para>
+	      Use the <ulink url="https://developer.apple.com/documentation/coretext">CoreText</ulink> library. <emphasis>(Default = no)</emphasis>
+	    </para>	    
+	    <para>
+	      This option enables or disables usage of the CoreText
+	      library. CoreText is available on macOS and iOS systems.
+	    </para>
+	  </listitem>
+	</varlistentry>	
+      </variablelist>
+    </section>
+    
   </section>
 </chapter>
diff --git a/docs/usermanual-opentype-features.xml b/docs/usermanual-opentype-features.xml
index 470bab8..51ff55a 100644
--- a/docs/usermanual-opentype-features.xml
+++ b/docs/usermanual-opentype-features.xml
@@ -1,3 +1,9 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
 <chapter id="shaping-and-shape-plans">
   <title>Shaping and shape plans</title>
   <section id="opentype-features">
@@ -10,4 +16,4 @@
     <para>
     </para>
   </section>
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/docs/usermanual-shaping-concepts.xml b/docs/usermanual-shaping-concepts.xml
new file mode 100644
index 0000000..bc9f1b8
--- /dev/null
+++ b/docs/usermanual-shaping-concepts.xml
@@ -0,0 +1,374 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
+<chapter id="shaping-concepts">
+  <title>Shaping concepts</title>
+  <section id="text-shaping-concepts">
+    <title>Text shaping</title>
+    <para>
+      Text shaping is the process of transforming a sequence of Unicode
+      codepoints that represent individual characters (letters,
+      diacritics, tone marks, numbers, symbols, etc.) into the
+      orthographically and linguistically correct two-dimensional layout
+      of glyph shapes taken from a specified font.
+    </para>
+    <para>
+      For some writing systems (or <emphasis>scripts</emphasis>) and
+      languages, the process is simple, requiring the shaper to do
+      little more than advance the horizontal position forward by the
+      correct amount for each successive glyph.
+    </para>
+    <para>
+      But, for <emphasis>complex scripts</emphasis>, any combination of
+      several shaping operations may be required, and the rules for how
+      and when they are applied vary from script to script. HarfBuzz and
+      other shaping engines implement these rules.
+    </para>
+    <para>
+      The exact rules and necessary operations for a particular script
+      constitute a shaping <emphasis>model</emphasis>. OpenType
+      specifies a set of shaping models that covers all of
+      Unicode. Other shaping models are available, however, including
+      Graphite and Apple Advanced Typography (AAT). 
+    </para>
+  </section>
+  
+  <section id="complex-scripts">
+    <title>Complex scripts</title>
+    <para>
+      In text-shaping terminology, scripts are generally classified as
+      either <emphasis>complex</emphasis> or <emphasis>non-complex</emphasis>.
+    </para>
+    <para>
+      Complex scripts are those for which transforming the input
+      sequence into the final layout requires some combination of
+      operations&mdash;such as context-dependent substitutions,
+      context-dependent mark positioning, glyph-to-glyph joining,
+      glyph reordering, or glyph stacking.
+    </para>
+    <para>
+      In some complex scripts, the shaping rules require that a text
+      run be divided into syllables before the operations can be
+      applied. Other complex scripts may apply shaping operations over
+      entire words or over the entire text run, with no subdivision
+      required.
+    </para>
+    <para>
+      Non-complex scripts, by definition, do not require these
+      operations. However, correctly shaping a text run in a
+      non-complex script may still involve Unicode normalization,
+      ligature substitutions, mark positioning, kerning, and applying
+      other font features. The key difference is that a text run in a
+      non-complex script can be processed sequentially and in the same
+      order as the input sequence of Unicode codepoints, without
+      requiring an analysis stage.
+    </para>
+  </section>
+  
+  <section id="shaping-operations">
+    <title>Shaping operations</title>
+    <para>
+      Shaping a complex-script text run involves transforming the
+      input sequence of Unicode codepoints with some combination of
+      operations that is specified in the shaping model for the
+      script.
+    </para>
+    <para>
+      The specific conditions that trigger a given operation for a
+      text run varies from script to script, as do the order that the
+      operations are performed in and which codepoints are
+      affected. However, the same general set of shaping operations is
+      common to all of the complex-script shaping models. 
+    </para>
+    
+    <itemizedlist>
+      <listitem>
+	<para>
+	  A <emphasis>reordering</emphasis> operation moves a glyph
+	  from its original ("logical") position in the sequence to
+	  some other ("visual") position.
+	</para>
+	<para>
+	  The shaping model for a given complex script might involve
+	  more than one reordering step.
+	</para>
+      </listitem>
+      
+      <listitem>
+	<para>
+	  A <emphasis>joining</emphasis> operation replaces a glyph
+	  with an alternate form that is designed to connect with one
+	  or more of the adjacent glyphs in the sequence.
+	</para>
+      </listitem>
+      
+      <listitem>
+	<para>
+	  A contextual <emphasis>substitution</emphasis> operation
+	  replaces either a single glyph or a subsequence of several
+	  glyphs with an alternate glyph. This substitution is
+	  performed when the original glyph or subsequence of glyphs
+	  occurs in a specified position with respect to the
+	  surrounding sequence. For example, one substitution might be
+	  performed only when the target glyph is the first glyph in
+	  the sequence, while another substitution is performed only
+	  when a different target glyph occurs immediately after a
+	  particular string pattern.
+	</para>
+	<para>
+	  The shaping model for a given complex script might involve
+	  multiple contextual-substitution operations, each applying
+	  to different target glyphs and patterns, and which are
+	  performed in separate steps.
+	</para>
+      </listitem>
+      
+      <listitem>
+	<para>
+	  A contextual <emphasis>positioning</emphasis> operation
+	  moves the horizontal and/or vertical position of a
+	  glyph. This positioning move is performed when the glyph
+	  occurs in a specified position with respect to the
+	  surrounding sequence.
+	</para>
+	<para>
+	  Many contextual positioning operations are used to place
+	  <emphasis>mark</emphasis> glyphs (such as diacritics, vowel
+	  signs, and tone markers) with respect to
+	  <emphasis>base</emphasis> glyphs. However, some complex
+	  scripts may use contextual positioning operations to
+	  correctly place base glyphs as well, such as
+	  when the script uses <emphasis>stacking</emphasis> characters.
+	</para>
+      </listitem>
+      
+    </itemizedlist>
+  </section>
+  
+  <section id="unicode-character-categories">
+    <title>Unicode character categories</title>
+    <para>
+      Shaping models are typically specified with respect to how
+      scripts are defined in the Unicode standard.
+    </para>
+    <para>
+      Every codepoint in the Unicode Character Database (UCD) is
+      assigned a <emphasis>Unicode General Category</emphasis> (UGC),
+      which provides the most fundamental information about the
+      codepoint: whether the codepoint represents a
+      <emphasis>Letter</emphasis>, a <emphasis>Mark</emphasis>, a
+      <emphasis>Number</emphasis>, <emphasis>Punctuation</emphasis>, a
+      <emphasis>Symbol</emphasis>, a <emphasis>Separator</emphasis>,
+      or something else (<emphasis>Other</emphasis>).
+    </para>
+    <para>
+      These UGC properties are "Major" categories. Each codepoint is
+      further assigned to a "minor" category within its Major
+      category, such as "Letter, uppercase" (<literal>Lu</literal>) or
+      "Letter, modifier" (<literal>Lm</literal>).
+    </para>
+    <para>
+      Shaping models are concerned primarily with Letter and Mark
+      codepoints. The minor categories of Mark codepoints are
+      particularly important for shaping. Marks can be nonspacing
+      (<literal>Mn</literal>), spacing combining
+      (<literal>Mc</literal>), or enclosing (<literal>Me</literal>).
+    </para>
+    <para>
+      In addition to the UGC property, codepoints in the Indic and
+      Southeast Asian scripts are also assigned
+      <emphasis>Unicode Indic Syllabic Category</emphasis> (UISC) and
+      <emphasis>Unicode Indic Positional Category</emphasis> (UIPC)
+      property that provides more detailed information needed for
+      shaping.
+    </para>
+    <para>
+      The UISC property sub-categorizes Letters and Marks according to
+      common script-shaping behaviors. For example, UISC distinguishes
+      between consonant letters, vowel letters, and vowel marks. The
+      UIPC property sub-categorizes Mark codepoints by the visual
+      position that they occupy (above, below, right, left, or in
+      multiple positions).
+    </para>
+    <para>
+      Some complex scripts require that the text run be split into
+      syllables, and what constitutes a valid syllable in these
+      scripts is specified in regular expressions of the Letter and
+      Mark codepoints that take the UISC and UIPC properties into account.
+    </para>
+
+  </section>
+  
+  <section id="text-runs">
+    <title>Text runs</title>
+    <para>
+      Real-world text usually contains codepoints from a mixture of
+      different Unicode scripts (including punctuation, numbers, symbols,
+      white-space characters, and other codepoints that do not belong
+      to any script). Real-world text may also be marked up with
+      formatting that changes font properties (including the font,
+      font style, and font size).
+    </para>
+    <para>
+      For shaping purposes, all real-world text streams must be first
+      segmented into runs that have a uniform set of properties. 
+    </para>
+    <para>
+      In particular, shaping models always assume that every codepoint
+      in a text run has the same <emphasis>direction</emphasis>,
+      <emphasis>script</emphasis> tag, and
+      <emphasis>language</emphasis> tag.
+    </para>
+  </section>
+  
+  <section id="opentype-shaping-models">
+    <title>OpenType shaping models</title>
+    <para>
+      OpenType provides shaping models for the following scripts:
+    </para>
+
+    <itemizedlist>
+      <listitem>
+	<para>
+	  The <emphasis>default</emphasis> shaping model handles all
+	  non-complex scripts, and may also be used as a fallback for
+	  handling unrecognized scripts.
+	</para>
+      </listitem>
+
+      <listitem>
+	<para>
+	  The <emphasis>Indic</emphasis> shaping model handles the Indic
+	  scripts Bengali, Devanagari, Gujarati, Gurmukhi, Kannada,
+	  Malayalam, Oriya, Tamil, Telugu, and Sinhala.
+	</para>
+	<para>
+	  The Indic shaping model was revised significantly in
+	  2005. To denote the change, a new set of <emphasis>script
+	  tags</emphasis> was assigned for Bengali, Devanagari,
+	  Gujarati, Gurmukhi, Kannada, Malayalam, Oriya, Tamil, and
+	  Telugu. For the sake of clarity, the term "Indic2" is
+	  sometimes used to refer to the current, revised shaping
+	  model.
+	</para>
+      </listitem>
+
+      <listitem>
+	<para>
+	  The <emphasis>Arabic</emphasis> shaping model supports
+	  Arabic, Mongolian, N'Ko, Syriac, and several other connected
+	  or cursive scripts.
+	</para>
+      </listitem>
+
+      <listitem>
+	<para>
+	  The <emphasis>Thai/Lao</emphasis> shaping model supports
+	  the Thai and Lao scripts.
+	</para>
+      </listitem>
+
+      <listitem>
+	<para>
+	  The <emphasis>Khmer</emphasis> shaping model supports the
+	  Khmer script.
+	</para>
+      </listitem>
+
+      <listitem>
+	<para>
+	  The <emphasis>Myanmar</emphasis> shaping model supports the
+	  Myanmar (or Burmese) script.
+	</para>
+      </listitem>
+
+      <listitem>
+	<para>
+	  The <emphasis>Tibetan</emphasis> shaping model supports the
+	  Tibetan script.
+	</para>
+      </listitem>
+
+      <listitem>
+	<para>
+	  The <emphasis>Hangul</emphasis> shaping model supports the
+	  Hangul script.
+	</para>
+      </listitem>
+
+      <listitem>
+	<para>
+	  The <emphasis>Hebrew</emphasis> shaping model supports the
+	  Hebrew script.
+	</para>
+      </listitem>
+
+      <listitem>
+	<para>
+	  The <emphasis>Universal Shaping Engine</emphasis> (USE)
+	  shaping model supports complex scripts not covered by one of
+	  the above, script-specific shaping models, including
+	  Javanese, Balinese, Buginese, Batak, Chakma, Lepcha, Modi,
+	  Phags-pa, Tagalog, Siddham, Sundanese, Tai Le, Tai Tham, Tai
+	  Viet, and many others. 
+	</para>
+      </listitem>
+
+      <listitem>
+	<para>
+	  Text runs that do not fall under one of the above shaping
+	  models may still require processing by a shaping engine. Of
+	  particular note is <emphasis>Emoji</emphasis> shaping, which
+	  may involve variation-selector sequences and glyph
+	  substitution. Emoji shaping is handled by the default
+	  shaping model.
+	</para>
+      </listitem>
+
+    </itemizedlist>
+    
+  </section>
+  
+  <section id="graphite-shaping">
+    <title>Graphite shaping</title>
+    <para>
+      In contrast to OpenType shaping, Graphite shaping does not
+      specify a predefined set of shaping models or a set of supported
+      scripts.
+    </para>
+    <para>
+      Instead, each Graphite font contains a complete set of rules that
+      implement the required shaping model for the intended
+      script. These rules include finite-state machines to match
+      sequences of codepoints to the shaping operations to perform.
+    </para>
+    <para>
+      Graphite shaping can perform the same shaping operations used in
+      OpenType shaping, as well as other functions that have not been
+      defined for OpenType shaping.
+    </para>
+  </section>
+  
+  <section id="aat-shaping">
+    <title>AAT shaping</title>
+    <para>
+      In contrast to OpenType shaping, AAT shaping does not specify a 
+      predefined set of shaping models or a set of supported scripts.
+    </para>
+    <para>
+      Instead, each AAT font includes a complete set of rules that
+      implement the desired shaping model for the intended
+      script. These rules include finite-state machines to match glyph
+      sequences and the shaping operations to perform.
+    </para>
+    <para>
+      Notably, AAT shaping rules are expressed for glyphs in the font,
+      not for Unicode codepoints. AAT shaping can perform the same
+      shaping operations used in OpenType shaping, as well as other
+      functions that have not been defined for OpenType shaping.
+    </para>
+  </section>
+</chapter>
diff --git a/docs/usermanual-what-is-harfbuzz.xml b/docs/usermanual-what-is-harfbuzz.xml
index 38f40cf..8532d7c 100644
--- a/docs/usermanual-what-is-harfbuzz.xml
+++ b/docs/usermanual-what-is-harfbuzz.xml
@@ -1,115 +1,441 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
 <chapter id="what-is-harfbuzz">
   <title>What is HarfBuzz?</title>
   <para>
-    HarfBuzz is a <emphasis>text shaping engine</emphasis>. It solves
-    the problem of selecting and positioning glyphs from a font given a
-    Unicode string.
+    HarfBuzz is a <emphasis>text-shaping engine</emphasis>. If you
+    give HarfBuzz a font and a string containing a sequence of Unicode
+    codepoints, HarfBuzz selects and positions the corresponding
+    glyphs from the font, applying all of the necessary layout rules
+    and font features. HarfBuzz then returns the string to you in the
+    form that is correctly arranged for the language and writing
+    system. 
   </para>
-  <section id="why-do-i-need-it">
-    <title>Why do I need it?</title>
+  <para>
+    HarfBuzz can properly shape all of the world's major writing
+    systems. It runs on all major operating systems and software
+    platforms and it supports the modern font formats in use
+    today.
+  </para>
+  <section id="what-is-text-shaping">
+    <title>What is text shaping?</title>
     <para>
-      Text shaping is an integral part of preparing text for display. It
-      is a fairly low level operation; HarfBuzz is used directly by
-      graphic rendering libraries such as Pango, and the layout engines
-      in Firefox, LibreOffice and Chromium. Unless you are
-      <emphasis>writing</emphasis> one of these layout engines yourself,
-      you will probably not need to use HarfBuzz - normally higher level
-      libraries will turn text into glyphs for you.
+      Text shaping is the process of translating a string of character
+      codes (such as Unicode codepoints) into a properly arranged
+      sequence of glyphs that can be rendered onto a screen or into
+      final output form for inclusion in a document.
+    </para>
+    <para>
+      The shaping process is dependent on the input string, the active
+      font, the script (or writing system) that the string is in, and
+      the language that the string is in.
+    </para>
+    <para>
+      Modern software systems generally only deal with strings in the
+      Unicode encoding scheme (although legacy systems and documents may
+      involve other encodings).
+    </para>
+    <para>
+      There are several font formats that a program might
+      encounter, each of which has a set of standard text-shaping
+      rules.
+    </para>
+    <para>The dominant format is <ulink
+      url="http://www.microsoft.com/typography/otspec/">OpenType</ulink>. The
+    OpenType specification defines a series of <ulink url="https://github.com/n8willis/opentype-shaping-documents">shaping models</ulink> for
+    various scripts from around the world. These shaping models depend on
+    the font including certain features in its <literal>GSUB</literal>
+    and <literal>GPOS</literal> tables.
+    </para>
+    <para>
+      Alternatively, OpenType fonts can include shaping features for
+      the <ulink url="https://graphite.sil.org/">Graphite</ulink> shaping model.
+    </para>
+    <para>
+      TrueType fonts can also include OpenType shaping
+      features. Alternatively, TrueType fonts can also include <ulink url="https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html">Apple
+      Advanced Typography</ulink> (AAT) tables to implement shaping
+      support. AAT fonts are generally only found on macOS and iOS systems.
+    </para>
+    <para>
+      Text strings will usually be tagged with a script and language
+      tag that provide the context needed to perform text shaping
+      correctly.  The necessary <ulink
+      url="https://docs.microsoft.com/en-us/typography/opentype/spec/scripttags">Script</ulink> 
+      and <ulink
+      url="https://docs.microsoft.com/en-us/typography/opentype/spec/languagetags">language</ulink>
+      tags are defined by OpenType.
+    </para>
+  </section>
+  
+  <section id="why-do-i-need-a-shaping-engine">
+    <title>Why do I need a shaping engine?</title>
+    <para>
+      Text shaping is an integral part of preparing text for
+      display. Before a Unicode sequence can be rendered, the
+      codepoints in the sequence must be mapped to the corresponding
+      glyphs provided in the font, and those glyphs must be positioned
+      correctly relative to each other. For many of the scripts
+      supported in Unicode, these steps involve script-specific layout
+      rules, including complex joining, reordering, and positioning
+      behavior. Implementing these rules is the job of the shaping engine.
+    </para>
+    <para>
+      Text shaping is a fairly low-level operation. HarfBuzz is
+      used directly by text-handling libraries like <ulink
+      url="https://www.pango.org/">Pango</ulink>, as well as by the layout
+      engines in Firefox, LibreOffice, and Chromium. Unless you are
+      <emphasis>writing</emphasis> one of these layout engines
+      yourself, you will probably not need to use HarfBuzz: normally,
+      a layout engine, toolkit, or other library will turn text into
+      glyphs for you.
     </para>
     <para>
       However, if you <emphasis>are</emphasis> writing a layout engine
-      or graphics library yourself, you will need to perform text
-      shaping, and this is where HarfBuzz can help you. Here are some
-      reasons why you need it:
+      or graphics library yourself, then you will need to perform text
+      shaping, and this is where HarfBuzz can help you.
+    </para>
+    <para>
+      Here are some specific scenarios where a text-shaping engine
+      like HarfBuzz helps you:
     </para>
     <itemizedlist>
       <listitem>
         <para>
-          OpenType fonts contain a set of glyphs, indexed by glyph ID.
-          The glyph ID within the font does not necessarily relate to a
-          Unicode codepoint. For instance, some fonts have the letter
-          &quot;a&quot; as glyph ID 1. To pull the right glyph out of
-          the font in order to display it, you need to consult a table
-          within the font (the &quot;cmap&quot; table) which maps
-          Unicode codepoints to glyph IDs. Text shaping turns codepoints
-          into glyph IDs.
+          OpenType fonts contain a set of glyphs (that is, shapes
+	  to represent the letters, numbers, punctuation marks, and
+	  all other symbols), which are indexed by a <literal>glyph ID</literal>.
+	</para>
+	<para>
+          A particular glyph ID within the font does not necessarily
+	  correlate to a predictable Unicode codepoint. For instance,
+	  some fonts have the letter &quot;a&quot; as glyph ID 1, but
+	  many others do not. In order to retrieve the right glyph
+	  from the font to display &quot;a&quot;, you need to consult
+	  the table inside the font (the <literal>cmap</literal>
+	  table) that maps Unicode codepoints to glyph IDs. In other
+	  words, <emphasis>text shaping turns codepoints into glyph
+	  IDs</emphasis>.
         </para>
       </listitem>
       <listitem>
         <para>
           Many OpenType fonts contain ligatures: combinations of
-          characters which are rendered together. For instance, it's
-          common for the <literal>fi</literal> combination to appear in
-          print as the single ligature &quot;fi&quot;. Whether you should
-          render text as <literal>fi</literal> or &quot;fi&quot; does not
-          depend on the input text, but on the capabilities of the font
-          and the level of ligature application you wish to perform.
-          Text shaping involves querying the font's ligature tables and
-          determining what substitutions should be made.
+          characters that are rendered as a single unit. For instance,
+	  it is common for the <literal>fi</literal> letter
+	  combination to appear in print as the single ligature glyph
+	  &quot;fi&quot;.
+	</para>
+	<para>
+	  Whether you should render an &quot;f, i&quot; sequence
+	  as <literal>fi</literal> or as &quot;fi&quot; does not
+          depend on the input text. Instead, it depends on the whether
+	  or not the font includes an &quot;fi&quot; glyph and on the
+	  level of ligature application you wish to perform. The font
+	  and the amount of ligature application used are under your
+	  control. In other words, <emphasis>text shaping involves
+	  querying the font's ligature tables and determining what
+	  substitutions should be made</emphasis>. 
         </para>
       </listitem>
       <listitem>
         <para>
-          While ligatures like &quot;fi&quot; are typographic
-          refinements, some languages <emphasis>require</emphasis> such
+          While ligatures like &quot;fi&quot; are optional typographic
+          refinements, some languages <emphasis>require</emphasis> certain
           substitutions to be made in order to display text correctly.
-          In Tamil, when the letter &quot;TTA&quot; (ட) letter is
-          followed by &quot;U&quot; (உ), the combination should appear
-          as the single glyph &quot;டு&quot;. The sequence of Unicode
-          characters &quot;டஉ&quot; needs to be rendered as a single
-          glyph from the font - text shaping chooses the correct glyph
-          from the sequence of characters provided.
+        </para>
+	<para>
+	  For example, in Tamil, when the letter &quot;TTA&quot; (ட)
+	  letter is followed by &quot;U&quot; (உ), the pair
+	  must be replaced by the single glyph &quot;டு&quot;. The
+	  sequence of Unicode characters &quot;டஉ&quot; needs to be
+	  substituted with a single &quot;டு&quot; glyph from the
+	  font.
+	</para>
+	<para>
+	  But &quot;டு&quot; does not have a Unicode codepoint. To
+	  find this glyph, you need to consult the table inside 
+	  the font (the <literal>GSUB</literal> table) that contains
+	  substitution information. In other words, <emphasis>text shaping 
+	  chooses the correct glyph for a sequence of characters
+	  provided</emphasis>.
         </para>
       </listitem>
       <listitem>
         <para>
-          Similarly, each Arabic character has four different variants:
-          within a font, there will be glyphs for the initial, medial,
-          final, and isolated forms of each letter. Unicode only encodes
-          one codepoint per character, and so a Unicode string will not
-          tell you which glyph to use. Text shaping chooses the correct
-          form of the letter and returns the correct glyph from the font
-          that you need to render.
+          Similarly, each Arabic character has four different variants
+	  corresponding to the different positions it might appear in
+	  within a sequence. Inside a font, there will be separate
+	  glyphs for the initial, medial, final, and isolated forms of
+	  each letter, each at a different glyph ID.
+	</para>
+	<para>
+	  Unicode only assigns one codepoint per character, so a
+	  Unicode string will not tell you which glyph variant to use
+	  for each character. To decide, you need to analyze the whole
+	  string and determine the appropriate glyph for each character
+	  based on its position. In other words, <emphasis>text
+	  shaping chooses the correct form of the letter by its
+	  position and returns the correct glyph from the font</emphasis>.
         </para>
       </listitem>
       <listitem>
         <para>
-          Other languages have marks and accents which need to be
-          rendered in certain positions around a base character. For
-          instance, the Moldovan language has the Cyrillic letter
-          &quot;zhe&quot; (ж) with a breve accent, like so: ӂ. Some
-          fonts will contain this character as an individual glyph,
-          whereas other fonts will not contain a zhe-with-breve glyph
-          but expect the rendering engine to form the character by
-          overlaying the two glyphs ж and ˘. Where you should draw the
-          combining breve depends on the height of the preceding glyph.
-          Again, for Arabic, the correct positioning of vowel marks
-          depends on the height of the character on which you are
-          placing the mark. Text shaping tells you whether you have a
-          precomposed glyph within your font or if you need to compose a
-          glyph yourself out of combining marks, and if so, where to
-          position those marks.
+          Other languages involve marks and accents that need to be
+          rendered in specific positions relative a base character. For
+          instance, the Moldovan language includes the Cyrillic letter
+          &quot;zhe&quot; (ж) with a breve accent, like so: &quot;ӂ&quot;.
+	</para>
+	<para>
+	  Some fonts will provide this character as a single
+	  zhe-with-breve glyph, but other fonts will not and, instead,
+	  will expect the rendering engine to form the character by 
+          superimposing the separate &quot;ж&quot; and &quot;˘&quot;
+	  glyphs.
+	</para>
+	<para>
+	  But exactly where you should draw the breve depends on the
+	  height and width of the preceding zhe glyph. To find the
+	  right position, you need to consult the table inside
+	  the font (the <literal>GPOS</literal> table) that contains
+	  positioning information.
+          In other words, <emphasis>text shaping tells you whether you
+	  have a precomposed glyph within your font or if you need to
+	  compose a glyph yourself out of combining marks&mdash;and,
+	  if so, where to position those marks.</emphasis>
         </para>
       </listitem>
     </itemizedlist>
     <para>
-      If this is something that you need to do, then you need a text
-      shaping engine: you could use Uniscribe if you are using Windows;
-      you could use CoreText on OS X; or you could use HarfBuzz. In the
-      rest of this manual, we are going to assume that you are the
-      implementor of a text layout engine.
+      If tasks like these are something that you need to do, then you
+      need a text shaping engine. You could use Uniscribe if you are
+      writing Windows software; you could use CoreText on macOS; or
+      you could use HarfBuzz.
+    </para>
+    <note>
+      <para>
+	In the rest of this manual, the text will assume that the reader
+	is that implementor of a text-layout engine.
+      </para>
+    </note>
+  </section>
+  
+
+  <section>
+    <title>What does HarfBuzz do?</title>
+    <para>
+      HarfBuzz provides text shaping through a cross-platform
+      C API that accepts sequences of Unicode codepoints as input. Currently,
+      the following OpenType shaping models are supported:
+    </para>
+    <itemizedlist>
+      <listitem>
+	<para>
+	  Indic (covering Devanagari, Bengali, Gujarati,
+	  Gurmukhi, Kannada, Malayalam, Oriya, Tamil, Telugu, and
+	  Sinhala)
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  Arabic (covering Arabic, N'Ko, Syriac, and Mongolian)
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  Thai and Lao
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  Khmer
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  Myanmar
+	</para>
+      </listitem>
+      
+      <listitem>
+	<para>
+	  Tibetan
+	</para>
+      </listitem>
+      
+      <listitem>
+	<para>
+	  Hangul
+	</para>
+      </listitem>
+      
+      <listitem>
+	<para>
+	  Hebrew
+	</para>
+      </listitem>      
+      <listitem>
+	<para>
+	  The Universal Shaping Engine or <emphasis>USE</emphasis>
+	  (covering complex scripts not covered by the above shaping
+	  models)
+	</para>
+      </listitem>      
+      <listitem>
+	<para>
+	  A default shaping model for non-complex scripts
+	  (covering Latin, Cyrillic, Greek, Armenian, Georgian, Tifinagh,
+	  and many others)
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  Emoji (including emoji modifier sequences, flag sequences,
+	  and ZWJ sequences)
+	</para>
+      </listitem>
+    </itemizedlist>
+
+    <para>
+      In addition to OpenType shaping, HarfBuzz supports the latest
+      version of Graphite shaping (the "Graphite 2" model) and AAT
+      shaping.
+    </para>
+    
+    <para>
+      HarfBuzz can read and understand TrueType fonts (.ttf), TrueType
+      collections (.ttc), and OpenType fonts (.otf, including those
+      fonts that contain TrueType-style outlines and those that
+      contain PostScript CFF or CFF2 outlines).
+    </para>
+
+    <para>
+      HarfBuzz is designed and tested to run on top of the FreeType
+      font renderer. It can run on Linux, Android, Windows, macOS, and
+      iOS systems.
+    </para>
+    
+    <para>
+      In addition to its core shaping functionality, HarfBuzz provides
+      functions for accessing other font features, including optional
+      GSUB and GPOS OpenType features, as well as
+      all color-font formats (<literal>CBDT</literal>,
+      <literal>sbix</literal>, <literal>COLR/CPAL</literal>, and
+      <literal>SVG-OT</literal>) and OpenType variable fonts. HarfBuzz
+      also includes a font-subsetting feature. HarfBuzz can perform
+      some low-level math-shaping operations, although it does not
+      currently perform full shaping for mathematical typesetting.
+    </para>
+    
+    <para>
+      A suite of command-line utilities is also provided in the
+      source-code tree, designed to help users test and debug
+      HarfBuzz's features on real-world fonts and input.
     </para>
   </section>
+
+  <section id="what-harfbuzz-doesnt-do">
+    <title>What HarfBuzz doesn't do</title>
+    <para>
+      HarfBuzz will take a Unicode string, shape it, and give you the
+      information required to lay it out correctly on a single
+      horizontal (or vertical) line using the font provided. That is the
+      extent of HarfBuzz's responsibility.
+    </para>
+    <para>
+      It is important to note that if you are implementing a complete
+      text-layout engine you may have other responsibilities that
+      HarfBuzz will <emphasis>not</emphasis> help you with. For example:
+    </para>
+    <itemizedlist>
+      <listitem>
+        <para>
+          HarfBuzz won't help you with bidirectionality. If you want to
+          lay out text that includes a mix of Hebrew and English, you
+	  will need to ensure that each buffer provided to HarfBuzz
+	  has all of its characters in the same order and that the
+	  directionality of the buffer is set correctly. This may mean
+	  segmenting the text before it is placed into HarfBuzz buffers. In
+          other words, the user will hit the keys in the following
+          sequence:
+        </para>
+        <programlisting>
+	  A B C [space] ג ב א [space] D E F
+        </programlisting>
+        <para>
+          but will expect to see in the output:
+        </para>
+        <programlisting>
+	  ABC אבג DEF
+        </programlisting>
+        <para>
+          This reordering is called <emphasis>bidi processing</emphasis>
+          (&quot;bidi&quot; is short for bidirectional), and there's an
+          algorithm as an annex to the Unicode Standard which tells you how
+          to process a string of mixed directionality.
+          Before sending your string to HarfBuzz, you may need to apply the
+          bidi algorithm to it. Libraries such as <ulink
+	  url="http://icu-project.org/">ICU</ulink> and <ulink
+	  url="http://fribidi.org/">fribidi</ulink> can do this for you.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          HarfBuzz won't help you with text that contains different font
+          properties. For instance, if you have the string &quot;a
+          <emphasis>huge</emphasis> breakfast&quot;, and you expect
+          &quot;huge&quot; to be italic, then you will need to send three
+          strings to HarfBuzz: <literal>a</literal>, in your Roman font;
+          <literal>huge</literal> using your italic font; and
+          <literal>breakfast</literal> using your Roman font again.
+	</para>
+	<para>
+          Similarly, if you change the font, font size, script,
+	  language, or direction within your string, then you will
+	  need to shape each run independently and output them
+	  independently. HarfBuzz expects to shape a run of characters
+	  that all share the same properties.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          HarfBuzz won't help you with line breaking, hyphenation, or
+          justification. As mentioned above, HarfBuzz lays out the string
+          along a <emphasis>single line</emphasis> of, notionally,
+          infinite length. If you want to find out where the potential
+          word, sentence and line break points are in your text, you
+          could use the ICU library's break iterator functions.
+        </para>
+        <para>
+          HarfBuzz can tell you how wide a shaped piece of text is, which is
+          useful input to a justification algorithm, but it knows nothing
+          about paragraphs, lines or line lengths. Nor will it adjust the
+          space between words to fit them proportionally into a line.
+        </para>
+      </listitem>
+    </itemizedlist>
+    <para>
+      As a layout-engine implementor, HarfBuzz will help you with the
+      interface between your text and your font, and that's something
+      that you'll need&mdash;what you then do with the glyphs that your font
+      returns is up to you. 
+    </para>
+  </section>
+    
   <section id="why-is-it-called-harfbuzz">
     <title>Why is it called HarfBuzz?</title>
     <para>
-      HarfBuzz began its life as text shaping code within the FreeType
-      project, (and you will see references to the FreeType authors
-      within the source code copyright declarations) but was then
-      abstracted out to its own project. This project is maintained by
-      Behdad Esfahbod, and named HarfBuzz. Originally, it was a shaping
-      engine for OpenType fonts - &quot;HarfBuzz&quot; is the Persian
-      for &quot;open type&quot;.
+      HarfBuzz began its life as text-shaping code within the FreeType
+      project (and you will see references to the FreeType authors
+      within the source code copyright declarations), but was then
+      extracted out to its own project. This project is maintained by
+      Behdad Esfahbod, who named it HarfBuzz. Originally, it was a
+      shaping engine for OpenType fonts&mdash;&quot;HarfBuzz&quot; is
+      the Persian for &quot;open type&quot;.
     </para>
   </section>
-</chapter>
\ No newline at end of file
+</chapter>
diff --git a/src/HBIndicVowelConstraints.txt b/src/HBIndicVowelConstraints.txt
new file mode 100644
index 0000000..146ae1c
--- /dev/null
+++ b/src/HBIndicVowelConstraints.txt
@@ -0,0 +1,97 @@
+# Copied from https://docs.microsoft.com/en-us/typography/script-development/use
+# On October 23, 2018; with documentd dated 02/07/2018.
+
+  0905 0946       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN SHORT E
+  0905 093E       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AA
+  0930 094D 0907  ; # DEVANAGARI LETTER RA, DEVANAGARI SIGN VIRAMA, DEVANAGARI LETTER I
+  0909 0941       ; # DEVANAGARI LETTER U, DEVANAGARI VOWEL SIGN U
+  090F 0945       ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN CANDRA E
+  090F 0946       ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN SHORT E
+  090F 0947       ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN E
+  0905 0949       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN CANDRA O
+  0906 0945       ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN CANDRA E
+  0905 094A       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN SHORT O
+  0906 0946       ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN SHORT E
+  0905 094B       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN O
+  0906 0947       ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN E
+  0905 094C       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AU
+  0906 0948       ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN AI
+  0905 0945       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN CANDRA E
+  0905 093A       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN OE
+  0905 093B       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN OOE
+  0906 093A       ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN OE
+  0905 094F       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AW
+  0905 0956       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN UE
+  0905 0957       ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN UUE
+  0985 09BE       ; # BENGALI LETTER A, BENGALI VOWEL SIGN AA
+  098B 09C3       ; # BENGALI LETTER VOCALIC R, BENGALI VOWEL SIGN VOCALIC R
+  098C 09E2       ; # BENGALI LETTER VOCALIC L, BENGALI VOWEL SIGN VOCALIC L
+  0A05 0A3E       ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AA
+  0A72 0A3F       ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN I
+  0A72 0A40       ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN II
+  0A73 0A41       ; # GURMUKHI URA, GURMUKHI VOWEL SIGN U
+  0A73 0A42       ; # GURMUKHI URA, GURMUKHI VOWEL SIGN UU
+  0A72 0A47       ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN EE
+  0A05 0A48       ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AI
+  0A73 0A4B       ; # GURMUKHI URA, GURMUKHI VOWEL SIGN OO
+  0A05 0A4C       ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AU
+  0A85 0ABE       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA
+  0A85 0AC5       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN CANDRA E
+  0A85 0AC7       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN E
+  0A85 0AC8       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AI
+  0A85 0AC9       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN CANDRA O
+  0A85 0ACB       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN O
+  0A85 0ABE 0AC5  ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA, GUJARATI VOWEL SIGN CANDRA E
+  0A85 0ACC       ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AU
+  0A85 0ABE 0AC8  ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA, GUJARATI VOWEL SIGN AI
+  0AC5 0ABE       ; # GUJARATI VOWEL SIGN CANDRA E, GUJARATI VOWEL SIGN AA
+  0B05 0B3E       ; # ORIYA LETTER A, ORIYA VOWEL SIGN AA
+  0B0F 0B57       ; # ORIYA LETTER E, ORIYA AU LENGTH MARK
+  0B13 0B57       ; # ORIYA LETTER O, ORIYA AU LENGTH MARK
+  0C12 0C55       ; # TELUGU LETTER O, TELUGU LENGTH MARK
+  0C12 0C4C       ; # TELUGU LETTER O, TELUGU VOWEL SIGN AU
+  0C3F 0C55       ; # TELUGU VOWEL SIGN I, TELUGU LENGTH MARK
+  0C46 0C55       ; # TELUGU VOWEL SIGN E, TELUGU LENGTH MARK
+  0C4A 0C55       ; # TELUGU VOWEL SIGN O, TELUGU LENGTH MARK
+  0C89 0CBE       ; # KANNADA LETTER U, KANNADA VOWEL SIGN AA
+  0C92 0CCC       ; # KANNADA LETTER O, KANNADA VOWEL SIGN AU
+  0C8B 0CBE       ; # KANNADA LETTER VOCALIC R, KANNADA VOWEL SIGN AA
+  0D07 0D57       ; # MALAYALAM LETTER I, MALAYALAM AU LENGTH MARK
+  0D09 0D57       ; # MALAYALAM LETTER U, MALAYALAM AU LENGTH MARK
+  0D0E 0D46       ; # MALAYALAM LETTER E, MALAYALAM VOWEL SIGN E
+  0D12 0D3E       ; # MALAYALAM LETTER O, MALAYALAM VOWEL SIGN AA
+  0D12 0D57       ; # MALAYALAM LETTER O, MALAYALAM AU LENGTH MARK
+  0D85 0DCF       ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN AELA-PILLA
+  0D85 0DD0       ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN KETTI AEDA-PILLA
+  0D85 0DD1       ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN DIGA AEDA-PILLA
+  0D8B 0DDF       ; # SINHALA LETTER UYANNA, SINHALA VOWEL SIGN GAYANUKITTA
+  0D8D 0DD8       ; # SINHALA LETTER IRUYANNA, SINHALA VOWEL SIGN GAETTA-PILLA
+  0D8F 0DDF       ; # SINHALA LETTER ILUYANNA, SINHALA VOWEL SIGN GAYANUKITTA
+  0D91 0DCA       ; # SINHALA LETTER EYANNA, SINHALA SIGN AL-LAKUNA
+  0D91 0DD9       ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA
+  0D91 0DDA       ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN DIGA KOMBUVA
+  0D91 0DDC       ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA
+  0D91 0DDD       ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA
+  0D91 0DDD       ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA
+  0D94 0DDF       ; # SINHALA LETTER OYANNA, SINHALA VOWEL SIGN GAYANUKITTA
+  11005 11038     ; # BRAHMI LETTER A, BRAHMI VOWEL SIGN AA
+  1100B 1103E     ; # BRAHMI LETTER VOCALIC R, BRAHMI VOWEL SIGN VOCALIC R
+  1100F 11042     ; # BRAHMI LETTER E, BRAHMI VOWEL SIGN E
+  11680 116AD     ; # TAKRI LETTER A, TAKRI VOWEL SIGN AA
+  11686 116B2     ; # TAKRI LETTER E, TAKRI VOWEL SIGN E
+  11680 116B4     ; # TAKRI LETTER A, TAKRI VOWEL SIGN O
+  11680 116B5     ; # TAKRI LETTER A, TAKRI VOWEL SIGN AU
+  112B0 112E0     ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AA
+  112B0 112E5     ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN E
+  112B0 112E6     ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AI
+  112B0 112E7     ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN O
+  112B0 112E8     ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AU
+  11481 114B0     ; # TIRHUTA LETTER A, TIRHUTA VOWEL SIGN AA
+  114AA 114B5     ; # TIRHUTA LETTER LA, TIRHUTA VOWEL SIGN VOCALIC R
+  114AA 114B6     ; # TIRHUTA LETTER LA, TIRHUTA VOWEL SIGN VOCALIC RR
+  1148B 114BA     ; # TIRHUTA LETTER E, TIRHUTA VOWEL SIGN SHORT E
+  1148D 114BA     ; # TIRHUTA LETTER O, TIRHUTA VOWEL SIGN SHORT E
+  11600 11639     ; # MODI LETTER A, MODI VOWEL SIGN E
+  11600 1163A     ; # MODI LETTER A, MODI VOWEL SIGN AI
+  11601 11639     ; # MODI LETTER AA, MODI VOWEL SIGN E
+  11601 1163A     ; # MODI LETTER AA, MODI VOWEL SIGN AI
diff --git a/src/Makefile.am b/src/Makefile.am
index 9e7fd29..fbd8f60 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,7 +13,7 @@
 check_PROGRAMS =
 
 # Convenience targets:
-lib: $(BUILT_SOURCES) libharfbuzz.la libharfbuzz-subset.la
+lib: $(BUILT_SOURCES) libharfbuzz.la
 libs: $(BUILT_SOURCES) $(lib_LTLIBRARIES)
 fuzzing: $(BUILT_SOURCES) libharfbuzz-fuzzing.la libharfbuzz-subset-fuzzing.la
 
@@ -29,11 +29,9 @@
 HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources)
 HBHEADERS = $(HB_BASE_headers)
 
-if HAVE_OT
 HBSOURCES += $(HB_OT_sources)
 HBSOURCES += $(HB_OT_RAGEL_GENERATED_sources)
 HBHEADERS += $(HB_OT_headers)
-endif
 
 if HAVE_FALLBACK
 HBSOURCES += $(HB_FALLBACK_sources)
@@ -172,7 +170,6 @@
 EXTRA_DIST += harfbuzz-subset.pc.in
 
 FUZZING_CPPFLAGS = \
-	-DHB_NDEBUG \
 	-DHB_MAX_NESTING_LEVEL=3 \
 	-DHB_SANITIZE_MAX_EDITS=3 \
 	-DHB_SANITIZE_MAX_OPS_FACTOR=3 \
@@ -271,7 +268,7 @@
 CLEANFILES += $(pkgconfig_DATA)
 
 
-DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def
+DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def harfbuzz-deprecated-symbols.txt
 if HAVE_GOBJECT
 DEF_FILES += harfbuzz-gobject.def
 endif
@@ -285,17 +282,23 @@
 	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
 harfbuzz-gobject.def: $(HB_GOBJECT_headers)
 	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
+harfbuzz-deprecated-symbols.txt: $(srcdir)/hb-deprecated.h
+	$(AM_V_GEN) PLAIN_LIST=1 $(srcdir)/gen-def.py "$@" $^
 
 
 GENERATORS = \
 	gen-arabic-table.py \
-	gen-indic-table.py \
-	gen-use-table.py \
 	gen-def.py \
+	gen-emoji-table.py \
+	gen-indic-table.py \
+	gen-os2-unicode-ranges.py \
+	gen-tag-table.py \
+	gen-use-table.py \
+	gen-vowel-constraints.py \
 	$(NULL)
 EXTRA_DIST += $(GENERATORS)
 
-unicode-tables: arabic-table indic-table use-table
+unicode-tables: arabic-table indic-table tag-table use-table emoji-table
 
 arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-arabic-table.hh \
@@ -305,13 +308,25 @@
 	$(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)
+
 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)
 
-.PHONY: unicode-tables arabic-table indic-table use-table built-sources
+.PHONY: unicode-tables arabic-table indic-table tag-table use-table vowel-constraints emoji-table built-sources
 
 RAGEL_GENERATED = \
 	$(patsubst %,$(srcdir)/%,$(HB_BASE_RAGEL_GENERATED_sources)) \
@@ -332,6 +347,7 @@
 	main \
 	test \
 	test-buffer-serialize \
+	test-name-table \
 	test-size-params \
 	test-would-substitute \
 	$(NULL)
@@ -345,17 +361,30 @@
 test_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
 test_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
 
-test_would_substitute_SOURCES = test-would-substitute.cc
-test_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
-test_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
+test_buffer_serialize_SOURCES = test-buffer-serialize.cc
+test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
+test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
+
+test_name_table_SOURCES = test-name-table.cc
+test_name_table_CPPFLAGS = $(HBCFLAGS)
+test_name_table_LDADD = libharfbuzz.la $(HBLIBS)
 
 test_size_params_SOURCES = test-size-params.cc
 test_size_params_CPPFLAGS = $(HBCFLAGS)
 test_size_params_LDADD = libharfbuzz.la $(HBLIBS)
 
-test_buffer_serialize_SOURCES = test-buffer-serialize.cc
-test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
-test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
+test_would_substitute_SOURCES = test-would-substitute.cc
+test_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
+test_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
+
+if HAVE_FREETYPE
+if HAVE_CAIRO_FT
+noinst_PROGRAMS += test-ot-color
+test_ot_color_SOURCES = test-ot-color.cc
+test_ot_color_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) $(CAIRO_FT_CFLAGS)
+test_ot_color_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) $(CAIRO_LIBS) $(CAIRO_FT_LIBS)
+endif # HAVE_CAIRO_FT
+endif # HAVE_FREETYPE
 
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
@@ -392,15 +421,6 @@
 dump_use_data_CPPFLAGS = $(HBCFLAGS)
 dump_use_data_LDADD = libharfbuzz.la $(HBLIBS)
 
-if HAVE_FREETYPE
-if HAVE_CAIRO_FT
-check_PROGRAMS += dump-emoji
-dump_emoji_SOURCES = dump-emoji.cc
-dump_emoji_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) $(CAIRO_FT_CFLAGS)
-dump_emoji_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) $(CAIRO_LIBS) $(CAIRO_FT_LIBS)
-endif # HAVE_CAIRO_FT
-endif # HAVE_FREETYPE
-
 check_PROGRAMS += test-ot-tag test-unicode-ranges
 TESTS += test-ot-tag test-unicode-ranges
 
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 80b8e26..561fbed 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -1,27 +1,29 @@
 # Base and default-included sources and headers
 
 HB_BASE_sources = \
-	hb-atomic-private.hh \
-	hb-blob-private.hh \
+	hb-atomic.hh \
+	hb-blob.hh \
 	hb-blob.cc \
-	hb-buffer-private.hh \
+	hb-buffer.hh \
 	hb-buffer-serialize.cc \
 	hb-buffer.cc \
+	hb-cache.hh \
 	hb-common.cc \
 	hb-debug.hh \
 	hb-dsalgs.hh \
-	hb-face-private.hh \
+	hb-face.hh \
 	hb-face.cc \
-	hb-font-private.hh \
+	hb-font.hh \
 	hb-font.cc \
-	hb-iter-private.hh \
-	hb-map-private.hh \
+	hb-iter.hh \
+	hb-map.hh \
 	hb-map.cc \
-	hb-machinery-private.hh \
-	hb-mutex-private.hh \
-	hb-object-private.hh \
-	hb-open-file-private.hh \
-	hb-open-type-private.hh \
+	hb-machinery.hh \
+	hb-mutex.hh \
+	hb-null.hh \
+	hb-object.hh \
+	hb-open-file.hh \
+	hb-open-type.hh \
 	hb-ot-color-cbdt-table.hh \
 	hb-ot-cmap-table.hh \
 	hb-ot-glyf-table.hh \
@@ -31,28 +33,34 @@
 	hb-ot-hmtx-table.hh \
 	hb-ot-kern-table.hh \
 	hb-ot-maxp-table.hh \
+	hb-ot-name-language.cc \
+	hb-ot-name-language.hh \
 	hb-ot-name-table.hh \
+	hb-ot-name.cc \
 	hb-ot-os2-table.hh \
 	hb-ot-os2-unicode-ranges.hh \
 	hb-ot-post-macroman.hh \
 	hb-ot-post-table.hh \
+	hb-ot-tag-table.hh \
 	hb-ot-tag.cc \
-	hb-private.hh \
-	hb-set-digest-private.hh \
-	hb-set-private.hh \
+	hb.hh \
+	hb-set-digest.hh \
+	hb-set.hh \
 	hb-set.cc \
 	hb-shape.cc \
-	hb-shape-plan-private.hh \
+	hb-shape-plan.hh \
 	hb-shape-plan.cc \
 	hb-shaper-list.hh \
-	hb-shaper-impl-private.hh \
-	hb-shaper-private.hh \
+	hb-shaper-impl.hh \
+	hb-shaper.hh \
 	hb-shaper.cc \
 	hb-static.cc \
 	hb-string-array.hh \
-	hb-unicode-private.hh \
+	hb-unicode.hh \
+	hb-unicode-emoji-table.hh \
 	hb-unicode.cc \
-	hb-utf-private.hh \
+	hb-vector.hh \
+	hb-utf.hh \
 	hb-warning.cc \
 	$(NULL)
 
@@ -87,66 +95,72 @@
 
 HB_OT_sources = \
 	hb-aat-layout.cc \
-	hb-aat-layout-common-private.hh \
+	hb-aat-layout-common.hh \
 	hb-aat-layout-ankr-table.hh \
 	hb-aat-layout-bsln-table.hh \
 	hb-aat-layout-feat-table.hh \
 	hb-aat-layout-kerx-table.hh \
 	hb-aat-layout-morx-table.hh \
 	hb-aat-layout-trak-table.hh \
-	hb-aat-layout-private.hh \
+	hb-aat-layout.hh \
 	hb-aat-ltag-table.hh \
+	hb-aat-map.cc \
+	hb-aat-map.hh \
+	hb-ot-face.hh \
+	hb-ot-face.cc \
 	hb-ot-font.cc \
 	hb-ot-layout.cc \
 	hb-ot-layout-base-table.hh \
-	hb-ot-layout-common-private.hh \
+	hb-ot-layout-common.hh \
 	hb-ot-layout-gdef-table.hh \
 	hb-ot-layout-gpos-table.hh \
-	hb-ot-layout-gsubgpos-private.hh \
+	hb-ot-layout-gsubgpos.hh \
 	hb-ot-layout-gsub-table.hh \
 	hb-ot-layout-jstf-table.hh \
-	hb-ot-layout-private.hh \
+	hb-ot-layout.hh \
 	hb-ot-color.cc \
 	hb-ot-color-colr-table.hh \
 	hb-ot-color-cpal-table.hh \
 	hb-ot-color-sbix-table.hh \
 	hb-ot-color-svg-table.hh \
 	hb-ot-map.cc \
-	hb-ot-map-private.hh \
+	hb-ot-map.hh \
 	hb-ot-math.cc \
 	hb-ot-math-table.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-complex-arabic.cc \
 	hb-ot-shape-complex-arabic-fallback.hh \
-	hb-ot-shape-complex-arabic-private.hh \
+	hb-ot-shape-complex-arabic.hh \
 	hb-ot-shape-complex-arabic-table.hh \
 	hb-ot-shape-complex-arabic-win1256.hh \
 	hb-ot-shape-complex-default.cc \
 	hb-ot-shape-complex-hangul.cc \
 	hb-ot-shape-complex-hebrew.cc \
 	hb-ot-shape-complex-indic.cc \
-	hb-ot-shape-complex-indic-private.hh \
+	hb-ot-shape-complex-indic.hh \
 	hb-ot-shape-complex-indic-table.cc \
-	hb-ot-shape-complex-khmer-private.hh \
+	hb-ot-shape-complex-khmer.hh \
 	hb-ot-shape-complex-khmer.cc \
-	hb-ot-shape-complex-myanmar-private.hh \
+	hb-ot-shape-complex-myanmar.hh \
 	hb-ot-shape-complex-myanmar.cc \
 	hb-ot-shape-complex-thai.cc \
-	hb-ot-shape-complex-tibetan.cc \
 	hb-ot-shape-complex-use.cc \
-	hb-ot-shape-complex-use-private.hh \
+	hb-ot-shape-complex-use.hh \
 	hb-ot-shape-complex-use-table.cc \
-	hb-ot-shape-complex-private.hh \
-	hb-ot-shape-normalize-private.hh \
+	hb-ot-shape-complex-vowel-constraints.cc \
+	hb-ot-shape-complex-vowel-constraints.hh \
+	hb-ot-shape-complex.hh \
+	hb-ot-shape-normalize.hh \
 	hb-ot-shape-normalize.cc \
-	hb-ot-shape-fallback-private.hh \
+	hb-ot-shape-fallback.hh \
 	hb-ot-shape-fallback.cc \
-	hb-ot-shape-private.hh \
+	hb-ot-shape.hh \
 	hb-ot-var.cc \
 	hb-ot-var-avar-table.hh \
 	hb-ot-var-fvar-table.hh \
 	hb-ot-var-hvar-table.hh \
 	hb-ot-var-mvar-table.hh \
+	hb-ot-vorg-table.hh \
 	$(NULL)
 
 HB_OT_RAGEL_GENERATED_sources = \
@@ -164,11 +178,12 @@
 
 HB_OT_headers = \
 	hb-ot.h \
+	hb-ot-color.h \
 	hb-ot-font.h \
 	hb-ot-layout.h \
 	hb-ot-math.h \
+	hb-ot-name.h \
 	hb-ot-shape.h \
-	hb-ot-tag.h \
 	hb-ot-var.h \
 	$(NULL)
 
@@ -205,16 +220,17 @@
 HB_SUBSET_sources = \
 	hb-static.cc \
 	hb-subset.cc \
+	hb-subset.hh \
 	hb-subset-glyf.cc \
+	hb-subset-glyf.hh \
 	hb-subset-input.cc \
+	hb-subset-input.hh \
 	hb-subset-plan.cc \
+	hb-subset-plan.hh \
 	$(NULL)
 
 HB_SUBSET_headers = \
 	hb-subset.h \
-	hb-subset-glyf.hh \
-	hb-subset-plan.hh \
-	hb-subset-private.hh \
 	$(NULL)
 
 HB_GOBJECT_DIST_sources = hb-gobject-structs.cc
diff --git a/src/check-includes.sh b/src/check-includes.sh
index fd565da..f938f70 100755
--- a/src/check-includes.sh
+++ b/src/check-includes.sh
@@ -23,14 +23,14 @@
 grep . >&2 && stat=1
 
 
-echo 'Checking that source files #include "hb-*private.hh" first (or none)'
+echo 'Checking that source files #include a private header first (or none)'
 
 for x in $HBSOURCES; do
 	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
-	grep '#.*\<include\>' "$x" /dev/null | grep -v 'include _' | head -n 1
+	grep '#.*\<include\>' "$x" /dev/null | head -n 1
 done |
-grep -v '"hb-.*private[.]hh"' |
-grep -v 'hb-private[.]hh:' |
+grep -v '"hb-.*[.]hh"' |
+grep -v 'hb[.]hh' |
 grep . >&2 && stat=1
 
 
diff --git a/src/check-static-inits.sh b/src/check-static-inits.sh
index 71551cb..def25c7 100755
--- a/src/check-static-inits.sh
+++ b/src/check-static-inits.sh
@@ -7,7 +7,6 @@
 test -z "$libs" && libs=.libs
 stat=0
 
-
 if which objdump 2>/dev/null >/dev/null; then
 	:
 else
@@ -31,7 +30,8 @@
 
 echo "Checking that no object file has lazy static C++ constructors/destructors or other such stuff"
 for obj in $OBJS; do
-	if objdump -t "$obj" | grep '__cxa_'; then
+	if objdump -t "$obj" | grep -q '__cxa_' && ! objdump -t "$obj" | grep -q __ubsan_handle; then
+		objdump -t "$obj" | grep '__cxa_'
 		echo "Ouch, $obj has lazy static C++ constructors/destructors or other such stuff"
 		stat=1
 	fi
diff --git a/src/check-symbols.sh b/src/check-symbols.sh
index d4eca50..cea8684 100755
--- a/src/check-symbols.sh
+++ b/src/check-symbols.sh
@@ -26,7 +26,7 @@
 		symprefix=
 		if test $suffix = dylib; then symprefix=_; fi
 
-		EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`"
+		EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRST] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`"
 
 		prefix=$symprefix`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
 
diff --git a/src/dump-indic-data.cc b/src/dump-indic-data.cc
index d574138..a506889 100644
--- a/src/dump-indic-data.cc
+++ b/src/dump-indic-data.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-indic-private.hh"
+#include "hb-ot-shape-complex-indic.hh"
 
 int
 main (void)
diff --git a/src/dump-khmer-data.cc b/src/dump-khmer-data.cc
index 7dd09b2..1e79a97 100644
--- a/src/dump-khmer-data.cc
+++ b/src/dump-khmer-data.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-khmer-private.hh"
+#include "hb-ot-shape-complex-khmer.hh"
 
 int
 main (void)
@@ -34,10 +34,8 @@
     hb_glyph_info_t info;
     info.codepoint = u;
     set_khmer_properties (info);
-    if (info.khmer_category() != INDIC_SYLLABIC_CATEGORY_OTHER ||
-	info.khmer_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE)
-      printf("U+%04X	%u	%u\n", u,
-	     info.khmer_category(),
-	     info.khmer_position());
+    if (info.khmer_category() != INDIC_SYLLABIC_CATEGORY_OTHER)
+      printf("U+%04X	%u\n", u,
+	     info.khmer_category());
   }
 }
diff --git a/src/dump-myanmar-data.cc b/src/dump-myanmar-data.cc
index 2df9cd9..9f8b12e 100644
--- a/src/dump-myanmar-data.cc
+++ b/src/dump-myanmar-data.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-myanmar-private.hh"
+#include "hb-ot-shape-complex-myanmar.hh"
 
 int
 main (void)
diff --git a/src/dump-use-data.cc b/src/dump-use-data.cc
index 0e64688..4a8b258 100644
--- a/src/dump-use-data.cc
+++ b/src/dump-use-data.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-use-private.hh"
+#include "hb-ot-shape-complex-use.hh"
 
 int
 main (void)
diff --git a/src/gen-def.py b/src/gen-def.py
index ba39eaa..9111c69 100755
--- a/src/gen-def.py
+++ b/src/gen-def.py
@@ -15,11 +15,10 @@
 	if h.endswith (".h"):
 		with io.open (h, encoding='utf-8') as f: headers_content.append (f.read ())
 
-result = """EXPORTS
+symbols = "\n".join (sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M)))
+
+result = symbols if os.environ.get('PLAIN_LIST', '') else """EXPORTS
 %s
-LIBRARY lib%s-0.dll""" % (
-	"\n".join (sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M))),
-	output_file.replace ('.def', '')
-)
+LIBRARY lib%s-0.dll""" % (symbols, output_file.replace ('.def', ''))
 
 with open (output_file, "w") as f: f.write (result)
diff --git a/src/gen-emoji-table.py b/src/gen-emoji-table.py
new file mode 100755
index 0000000..278e0b2
--- /dev/null
+++ b/src/gen-emoji-table.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+
+from __future__ import print_function, division, absolute_import
+import sys
+import os.path
+from collections import OrderedDict
+
+if len (sys.argv) != 2:
+	print("usage: ./gen-emoji-table.py emoji-data.txt", file=sys.stderr)
+	sys.exit (1)
+
+f = open(sys.argv[1])
+header = [f.readline () for _ in range(10)]
+
+sets = OrderedDict()
+for line in f.readlines():
+	line = line.strip()
+	if not line or line[0] == '#':
+		continue
+	rang, typ = [s.strip() for s in line.split('#')[0].split(';')[:2]]
+
+	rang = [int(s, 16) for s in rang.split('..')]
+	if len(rang) > 1:
+		start, end = rang
+	else:
+		start = end = rang[0]
+
+	if typ not in sets:
+		sets[typ] = set()
+	sets[typ].add((start, end))
+
+
+
+print ("/* == Start of generated table == */")
+print ("/*")
+print (" * The following tables are generated by running:")
+print (" *")
+print (" *   ./gen-emoji-table.py emoji-data.txt")
+print (" *")
+print (" * on file with this header:")
+print (" *")
+for l in header:
+	print (" * %s" % (l.strip()))
+print (" */")
+print ()
+print ("#ifndef HB_UNICODE_EMOJI_TABLE_HH")
+print ("#define HB_UNICODE_EMOJI_TABLE_HH")
+print ()
+print ('#include "hb-unicode.hh"')
+print ()
+
+for typ,s in sets.items():
+	if typ != "Extended_Pictographic": continue
+	print()
+	print("static const struct hb_unicode_range_t _hb_unicode_emoji_%s_table[] =" % typ)
+	print("{")
+	for pair in sorted(s):
+		print("  {0x%04X, 0x%04X}," % pair)
+	print("};")
+
+print ()
+print ("#endif /* HB_UNICODE_EMOJI_TABLE_HH */")
+print ()
+print ("/* == End of generated table == */")
diff --git a/src/gen-indic-table.py b/src/gen-indic-table.py
index 6252664..e65b981 100755
--- a/src/gen-indic-table.py
+++ b/src/gen-indic-table.py
@@ -102,7 +102,7 @@
 		print (" * %s" % (l.strip()))
 print (" */")
 print ()
-print ('#include "hb-ot-shape-complex-indic-private.hh"')
+print ('#include "hb-ot-shape-complex-indic.hh"')
 print ()
 
 # Shorten values
diff --git a/src/gen-os2-unicode-ranges.py b/src/gen-os2-unicode-ranges.py
new file mode 100644
index 0000000..d768313
--- /dev/null
+++ b/src/gen-os2-unicode-ranges.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+
+# Generates the code for a sorted unicode range array as used in hb-ot-os2-unicode-ranges.hh
+# Input is a tab seperated list of unicode ranges from the otspec
+# (https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ulunicoderange1).
+
+from __future__ import print_function, division, absolute_import
+
+import io
+import re
+import sys
+
+reload(sys)
+sys.setdefaultencoding('utf-8')
+
+print ("""static OS2Range _hb_os2_unicode_ranges[] =
+{""")
+
+args = sys.argv[1:]
+input_file = args[0]
+
+with io.open(input_file, mode="r", encoding="utf-8") as f:
+
+  all_ranges = [];
+  current_bit = 0
+  while True:
+    line = f.readline().strip()
+    if not line:
+      break
+    fields = re.split(r'\t+', line)
+    if len(fields) == 3:
+      current_bit = fields[0]
+      fields = fields[1:]
+    elif len(fields) > 3:
+      raise Error("bad input :(.")
+
+    name = fields[0]
+    ranges = re.split("-", fields[1])
+    if len(ranges) != 2:
+      raise Error("bad input :(.")
+
+    v = tuple((int(ranges[0], 16), int(ranges[1], 16), int(current_bit), name))
+    all_ranges.append(v)
+
+all_ranges = sorted(all_ranges, key=lambda t: t[0])
+
+for ranges in all_ranges:
+  start = ("0x%X" % ranges[0]).rjust(8)
+  end = ("0x%X" % ranges[1]).rjust(8)
+  bit = ("%s" % ranges[2]).rjust(3)
+
+  print ("  {%s, %s, %s}, // %s" % (start, end, bit, ranges[3]))
+
+print ("""};""")
diff --git a/src/gen-tag-table.py b/src/gen-tag-table.py
new file mode 100755
index 0000000..1300462
--- /dev/null
+++ b/src/gen-tag-table.py
@@ -0,0 +1,1126 @@
+#!/usr/bin/python
+
+"""Generator of the mapping from OpenType tags to BCP 47 tags and vice
+versa.
+
+It creates a ``const LangTag[]``, matching the tags from the OpenType
+languages system tag list to the language subtags of the BCP 47 language
+subtag registry, with some manual adjustments. The mappings are
+supplemented with macrolanguages' sublanguages and retired codes'
+replacements, according to BCP 47 and some manual additions where BCP 47
+omits a retired code entirely.
+
+Also generated is a function, ``hb_ot_ambiguous_tag_to_language``,
+intended for use by ``hb_ot_tag_to_language``. It maps OpenType tags
+back to BCP 47 tags. Ambiguous OpenType tags (those that correspond to
+multiple BCP 47 tags) are listed here, except when the alphabetically
+first BCP 47 tag happens to be the chosen disambiguated tag. In that
+case, the fallback behavior will choose the right tag anyway.
+"""
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import collections
+try:
+	from HTMLParser import HTMLParser
+	def write (s):
+		print (s.encode ('utf-8'), end='')
+except ImportError:
+	from html.parser import HTMLParser
+	def write (s):
+		sys.stdout.flush ()
+		sys.stdout.buffer.write (s.encode ('utf-8'))
+import io
+import itertools
+import re
+import sys
+import unicodedata
+
+if len (sys.argv) != 3:
+	print ('usage: ./gen-tag-table.py languagetags language-subtag-registry', file=sys.stderr)
+	sys.exit (1)
+
+try:
+	from html import unescape
+	def html_unescape (parser, entity):
+		return unescape (entity)
+except ImportError:
+	def html_unescape (parser, entity):
+		return parser.unescape (entity)
+
+def expect (condition, message=None):
+	if not condition:
+		if message is None:
+			raise AssertionError
+		raise AssertionError (message)
+
+# from http://www-01.sil.org/iso639-3/iso-639-3.tab
+ISO_639_3_TO_1 = {
+	'aar': 'aa',
+	'abk': 'ab',
+	'afr': 'af',
+	'aka': 'ak',
+	'amh': 'am',
+	'ara': 'ar',
+	'arg': 'an',
+	'asm': 'as',
+	'ava': 'av',
+	'ave': 'ae',
+	'aym': 'ay',
+	'aze': 'az',
+	'bak': 'ba',
+	'bam': 'bm',
+	'bel': 'be',
+	'ben': 'bn',
+	'bis': 'bi',
+	'bod': 'bo',
+	'bos': 'bs',
+	'bre': 'br',
+	'bul': 'bg',
+	'cat': 'ca',
+	'ces': 'cs',
+	'cha': 'ch',
+	'che': 'ce',
+	'chu': 'cu',
+	'chv': 'cv',
+	'cor': 'kw',
+	'cos': 'co',
+	'cre': 'cr',
+	'cym': 'cy',
+	'dan': 'da',
+	'deu': 'de',
+	'div': 'dv',
+	'dzo': 'dz',
+	'ell': 'el',
+	'eng': 'en',
+	'epo': 'eo',
+	'est': 'et',
+	'eus': 'eu',
+	'ewe': 'ee',
+	'fao': 'fo',
+	'fas': 'fa',
+	'fij': 'fj',
+	'fin': 'fi',
+	'fra': 'fr',
+	'fry': 'fy',
+	'ful': 'ff',
+	'gla': 'gd',
+	'gle': 'ga',
+	'glg': 'gl',
+	'glv': 'gv',
+	'grn': 'gn',
+	'guj': 'gu',
+	'hat': 'ht',
+	'hau': 'ha',
+	'hbs': 'sh',
+	'heb': 'he',
+	'her': 'hz',
+	'hin': 'hi',
+	'hmo': 'ho',
+	'hrv': 'hr',
+	'hun': 'hu',
+	'hye': 'hy',
+	'ibo': 'ig',
+	'ido': 'io',
+	'iii': 'ii',
+	'iku': 'iu',
+	'ile': 'ie',
+	'ina': 'ia',
+	'ind': 'id',
+	'ipk': 'ik',
+	'isl': 'is',
+	'ita': 'it',
+	'jav': 'jv',
+	'jpn': 'ja',
+	'kal': 'kl',
+	'kan': 'kn',
+	'kas': 'ks',
+	'kat': 'ka',
+	'kau': 'kr',
+	'kaz': 'kk',
+	'khm': 'km',
+	'kik': 'ki',
+	'kin': 'rw',
+	'kir': 'ky',
+	'kom': 'kv',
+	'kon': 'kg',
+	'kor': 'ko',
+	'kua': 'kj',
+	'kur': 'ku',
+	'lao': 'lo',
+	'lat': 'la',
+	'lav': 'lv',
+	'lim': 'li',
+	'lin': 'ln',
+	'lit': 'lt',
+	'ltz': 'lb',
+	'lub': 'lu',
+	'lug': 'lg',
+	'mah': 'mh',
+	'mal': 'ml',
+	'mar': 'mr',
+	'mkd': 'mk',
+	'mlg': 'mg',
+	'mlt': 'mt',
+	'mol': 'mo',
+	'mon': 'mn',
+	'mri': 'mi',
+	'msa': 'ms',
+	'mya': 'my',
+	'nau': 'na',
+	'nav': 'nv',
+	'nbl': 'nr',
+	'nde': 'nd',
+	'ndo': 'ng',
+	'nep': 'ne',
+	'nld': 'nl',
+	'nno': 'nn',
+	'nob': 'nb',
+	'nor': 'no',
+	'nya': 'ny',
+	'oci': 'oc',
+	'oji': 'oj',
+	'ori': 'or',
+	'orm': 'om',
+	'oss': 'os',
+	'pan': 'pa',
+	'pli': 'pi',
+	'pol': 'pl',
+	'por': 'pt',
+	'pus': 'ps',
+	'que': 'qu',
+	'roh': 'rm',
+	'ron': 'ro',
+	'run': 'rn',
+	'rus': 'ru',
+	'sag': 'sg',
+	'san': 'sa',
+	'sin': 'si',
+	'slk': 'sk',
+	'slv': 'sl',
+	'sme': 'se',
+	'smo': 'sm',
+	'sna': 'sn',
+	'snd': 'sd',
+	'som': 'so',
+	'sot': 'st',
+	'spa': 'es',
+	'sqi': 'sq',
+	'srd': 'sc',
+	'srp': 'sr',
+	'ssw': 'ss',
+	'sun': 'su',
+	'swa': 'sw',
+	'swe': 'sv',
+	'tah': 'ty',
+	'tam': 'ta',
+	'tat': 'tt',
+	'tel': 'te',
+	'tgk': 'tg',
+	'tgl': 'tl',
+	'tha': 'th',
+	'tir': 'ti',
+	'ton': 'to',
+	'tsn': 'tn',
+	'tso': 'ts',
+	'tuk': 'tk',
+	'tur': 'tr',
+	'twi': 'tw',
+	'uig': 'ug',
+	'ukr': 'uk',
+	'urd': 'ur',
+	'uzb': 'uz',
+	'ven': 've',
+	'vie': 'vi',
+	'vol': 'vo',
+	'wln': 'wa',
+	'wol': 'wo',
+	'xho': 'xh',
+	'yid': 'yi',
+	'yor': 'yo',
+	'zha': 'za',
+	'zho': 'zh',
+	'zul': 'zu',
+}
+
+class LanguageTag (object):
+	"""A BCP 47 language tag.
+
+	Attributes:
+		subtags (List[str]): The list of subtags in this tag.
+		grandfathered (bool): Whether this tag is grandfathered. If
+			``true``, the entire lowercased tag is the ``language``
+			and the other subtag fields are empty.
+		language (str): The language subtag.
+		script (str): The script subtag.
+		region (str): The region subtag.
+		variant (str): The variant subtag.
+
+	Args:
+		tag (str): A BCP 47 language tag.
+
+	"""
+	def __init__ (self, tag):
+		global bcp_47
+		self.subtags = tag.lower ().split ('-')
+		self.grandfathered = tag.lower () in bcp_47.grandfathered
+		if self.grandfathered:
+			self.language = tag.lower ()
+			self.script = ''
+			self.region = ''
+			self.variant = ''
+		else:
+			self.language = self.subtags[0]
+			self.script = self._find_first (lambda s: len (s) == 4 and s[0] > '9', self.subtags)
+			self.region = self._find_first (lambda s: len (s) == 2 and s[0] > '9' or len (s) == 3 and s[0] <= '9', self.subtags[1:])
+			self.variant = self._find_first (lambda s: len (s) > 4 or len (s) == 4 and s[0] <= '9', self.subtags)
+
+	def __str__(self):
+		return '-'.join(self.subtags)
+
+	def __repr__ (self):
+		return 'LanguageTag(%r)' % str(self)
+
+	@staticmethod
+	def _find_first (function, sequence):
+		try:
+			return next (iter (filter (function, sequence)))
+		except StopIteration:
+			return None
+
+	def is_complex (self):
+		"""Return whether this tag is too complex to represent as a
+		``LangTag`` in the generated code.
+
+		Complex tags need to be handled in
+		``hb_ot_tags_from_complex_language``.
+
+		Returns:
+			Whether this tag is complex.
+		"""
+		return not (len (self.subtags) == 1
+			or self.grandfathered
+			and len (self.subtags[1]) != 3
+			and ot.from_bcp_47[self.subtags[0]] == ot.from_bcp_47[self.language])
+
+	def get_group (self):
+		"""Return the group into which this tag should be categorized in
+		``hb_ot_tags_from_complex_language``.
+
+		The group is the first letter of the tag, or ``'und'`` if this tag
+		should not be matched in a ``switch`` statement in the generated
+		code.
+
+		Returns:
+			This tag's group.
+		"""
+		return ('und'
+			if (self.language == 'und'
+				or self.variant in bcp_47.prefixes and len (bcp_47.prefixes[self.variant]) == 1)
+			else self.language[0])
+
+class OpenTypeRegistryParser (HTMLParser):
+	"""A parser for the OpenType language system tag registry.
+
+	Attributes:
+		header (str): The "last updated" line of the registry.
+		names (Mapping[str, str]): A map of language system tags to the
+			names they are given in the registry.
+		ranks (DefaultDict[str, int]): A map of language system tags to
+			numbers. If a single BCP 47 tag corresponds to multiple
+			OpenType tags, the tags are ordered in increasing order by
+			rank. The rank is based on the number of BCP 47 tags
+			associated with a tag, though it may be manually modified.
+		to_bcp_47 (DefaultDict[str, AbstractSet[str]]): A map of
+			OpenType language system tags to sets of BCP 47 tags.
+		from_bcp_47 (DefaultDict[str, AbstractSet[str]]): ``to_bcp_47``
+			inverted. Its values start as unsorted sets;
+			``sort_languages`` converts them to sorted lists.
+
+	"""
+	def __init__ (self):
+		HTMLParser.__init__ (self)
+		self.header = ''
+		self.names = {}
+		self.ranks = collections.defaultdict (int)
+		self.to_bcp_47 = collections.defaultdict (set)
+		self.from_bcp_47 = collections.defaultdict (set)
+		# Whether the parser is in a <td> element
+		self._td = False
+		# The text of the <td> elements of the current <tr> element.
+		self._current_tr = []
+
+	def handle_starttag (self, tag, attrs):
+		if tag == 'meta':
+			for attr, value in attrs:
+				if attr == 'name' and value == 'updated_at':
+					self.header = self.get_starttag_text ()
+					break
+		elif tag == 'td':
+			self._td = True
+			self._current_tr.append ('')
+		elif tag == 'tr':
+			self._current_tr = []
+
+	def handle_endtag (self, tag):
+		if tag == 'td':
+			self._td = False
+		elif tag == 'tr' and self._current_tr:
+			expect (2 <= len (self._current_tr) <= 3)
+			name = self._current_tr[0].strip ()
+			tag = self._current_tr[1].strip ("\t\n\v\f\r '")
+			rank = 0
+			if len (tag) > 4:
+				expect (tag.endswith (' (deprecated)'), 'ill-formed OpenType tag: %s' % tag)
+				name += ' (deprecated)'
+				tag = tag.split (' ')[0]
+				rank = 1
+			self.names[tag] = re.sub (' languages$', '', name)
+			if not self._current_tr[2]:
+				return
+			iso_codes = self._current_tr[2].strip ()
+			self.to_bcp_47[tag].update (ISO_639_3_TO_1.get (code, code) for code in iso_codes.replace (' ', '').split (','))
+			rank += 2 * len (self.to_bcp_47[tag])
+			self.ranks[tag] = rank
+
+	def handle_data (self, data):
+		if self._td:
+			self._current_tr[-1] += data
+
+	def handle_charref (self, name):
+		self.handle_data (html_unescape (self, '&#%s;' % name))
+
+	def handle_entityref (self, name):
+		self.handle_data (html_unescape (self, '&%s;' % name))
+
+	def parse (self, filename):
+		"""Parse the OpenType language system tag registry.
+
+		Args:
+			filename (str): The file name of the registry.
+		"""
+		with io.open (filename, encoding='utf-8') as f:
+			self.feed (f.read ())
+		expect (self.header)
+		for tag, iso_codes in self.to_bcp_47.items ():
+			for iso_code in iso_codes:
+				self.from_bcp_47[iso_code].add (tag)
+
+	def add_language (self, bcp_47_tag, ot_tag):
+		"""Add a language as if it were in the registry.
+
+		Args:
+			bcp_47_tag (str): A BCP 47 tag. If the tag is more than just
+				a language subtag, and if the language subtag is a
+				macrolanguage, then new languages are added corresponding
+				to the macrolanguages' individual languages with the
+				remainder of the tag appended.
+			ot_tag (str): An OpenType language system tag.
+		"""
+		global bcp_47
+		self.to_bcp_47[ot_tag].add (bcp_47_tag)
+		self.from_bcp_47[bcp_47_tag].add (ot_tag)
+		if bcp_47_tag.lower () not in bcp_47.grandfathered:
+			try:
+				[macrolanguage, suffix] = bcp_47_tag.split ('-', 1)
+				if macrolanguage in bcp_47.macrolanguages:
+					s = set ()
+					for language in bcp_47.macrolanguages[macrolanguage]:
+						if language.lower () not in bcp_47.grandfathered:
+							s.add ('%s-%s' % (language, suffix))
+					bcp_47.macrolanguages['%s-%s' % (macrolanguage, suffix)] = s
+			except ValueError:
+				pass
+
+	@staticmethod
+	def _remove_language (tag_1, dict_1, dict_2):
+		for tag_2 in dict_1.pop (tag_1):
+			dict_2[tag_2].remove (tag_1)
+			if not dict_2[tag_2]:
+				del dict_2[tag_2]
+
+	def remove_language_ot (self, ot_tag):
+		"""Remove an OpenType tag from the registry.
+
+		Args:
+			ot_tag (str): An OpenType tag.
+		"""
+		self._remove_language (ot_tag, self.to_bcp_47, self.from_bcp_47)
+
+	def remove_language_bcp_47 (self, bcp_47_tag):
+		"""Remove a BCP 47 tag from the registry.
+
+		Args:
+			bcp_47_tag (str): A BCP 47 tag.
+		"""
+		self._remove_language (bcp_47_tag, self.from_bcp_47, self.to_bcp_47)
+
+	def inherit_from_macrolanguages (self):
+		"""Copy mappings from macrolanguages to individual languages.
+
+		If a BCP 47 tag for an individual mapping has no OpenType
+		mapping but its macrolanguage does, the mapping is copied to
+		the individual language. For example, als (Tosk Albanian) has no
+		explicit mapping, so it inherits from sq (Albanian) the mapping
+		to SQI.
+
+		If a BCP 47 tag for a macrolanguage has no OpenType mapping but
+		all of its individual languages do and they all map to the same
+		tags, the mapping is copied to the macrolanguage.
+		"""
+		global bcp_47
+		original_ot_from_bcp_47 = dict (self.from_bcp_47)
+		for macrolanguage, languages in dict (bcp_47.macrolanguages).items ():
+			ot_macrolanguages = set (original_ot_from_bcp_47.get (macrolanguage, set ()))
+			if ot_macrolanguages:
+				for ot_macrolanguage in ot_macrolanguages:
+					for language in languages:
+						# Remove the following condition if e.g. nn should map to NYN,NOR
+						# instead of just NYN.
+						if language not in original_ot_from_bcp_47:
+							self.add_language (language, ot_macrolanguage)
+							self.ranks[ot_macrolanguage] += 1
+			else:
+				for language in languages:
+					if language in original_ot_from_bcp_47:
+						if ot_macrolanguages:
+							ml = original_ot_from_bcp_47[language]
+							if ml:
+								ot_macrolanguages &= ml
+							else:
+								pass
+						else:
+							ot_macrolanguages |= original_ot_from_bcp_47[language]
+					else:
+						ot_macrolanguages.clear ()
+					if not ot_macrolanguages:
+						break
+				for ot_macrolanguage in ot_macrolanguages:
+					self.add_language (macrolanguage, ot_macrolanguage)
+
+	def sort_languages (self):
+		"""Sort the values of ``from_bcp_47`` in ascending rank order."""
+		for language, tags in self.from_bcp_47.items ():
+			self.from_bcp_47[language] = sorted (tags,
+					key=lambda t: (self.ranks[t] + rank_delta (language, t), t))
+
+ot = OpenTypeRegistryParser ()
+
+class BCP47Parser (object):
+	"""A parser for the BCP 47 subtag registry.
+
+	Attributes:
+		header (str): The "File-Date" line of the registry.
+		names (Mapping[str, str]): A map of subtags to the names they
+			are given in the registry. Each value is a
+			``'\\n'``-separated list of names.
+		scopes (Mapping[str, str]): A map of language subtags to strings
+			suffixed to language names, including suffixes to explain
+			language scopes.
+		macrolanguages (DefaultDict[str, AbstractSet[str]]): A map of
+			language subtags to the sets of language subtags which
+			inherit from them. See
+			``OpenTypeRegistryParser.inherit_from_macrolanguages``.
+		prefixes (DefaultDict[str, AbstractSet[str]]): A map of variant
+			subtags to their prefixes.
+		grandfathered (AbstractSet[str]): The set of grandfathered tags,
+			normalized to lowercase.
+
+	"""
+	def __init__ (self):
+		self.header = ''
+		self.names = {}
+		self.scopes = {}
+		self.macrolanguages = collections.defaultdict (set)
+		self.prefixes = collections.defaultdict (set)
+		self.grandfathered = set ()
+
+	def parse (self, filename):
+		"""Parse the BCP 47 subtag registry.
+
+		Args:
+			filename (str): The file name of the registry.
+		"""
+		with io.open (filename, encoding='utf-8') as f:
+			subtag_type = None
+			subtag = None
+			deprecated = False
+			has_preferred_value = False
+			line_buffer = ''
+			for line in itertools.chain (f, ['']):
+				line = line.rstrip ()
+				if line.startswith (' '):
+					line_buffer += line[1:]
+					continue
+				line, line_buffer = line_buffer, line
+				if line.startswith ('Type: '):
+					subtag_type = line.split (' ')[1]
+					deprecated = False
+					has_preferred_value = False
+				elif line.startswith ('Subtag: ') or line.startswith ('Tag: '):
+					subtag = line.split (' ')[1]
+					if subtag_type == 'grandfathered':
+						self.grandfathered.add (subtag.lower ())
+				elif line.startswith ('Description: '):
+					description = line.split (' ', 1)[1].replace (' (individual language)', '')
+					description = re.sub (' (\((individual |macro)language\)|languages)$', '',
+							description)
+					if subtag in self.names:
+						self.names[subtag] += '\n' + description
+					else:
+						self.names[subtag] = description
+				elif subtag_type == 'language' or subtag_type == 'grandfathered':
+					if line.startswith ('Scope: '):
+						scope = line.split (' ')[1]
+						if scope == 'macrolanguage':
+							scope = ' [macrolanguage]'
+						elif scope == 'collection':
+							scope = ' [family]'
+						else:
+							continue
+						self.scopes[subtag] = scope
+					elif line.startswith ('Deprecated: '):
+						self.scopes[subtag] = ' (retired code)' + self.scopes.get (subtag, '')
+						deprecated = True
+					elif deprecated and line.startswith ('Comments: see '):
+						# If a subtag is split into multiple replacement subtags,
+						# it essentially represents a macrolanguage.
+						for language in line.replace (',', '').split (' ')[2:]:
+							self._add_macrolanguage (subtag, language)
+					elif line.startswith ('Preferred-Value: '):
+						# If a subtag is deprecated in favor of a single replacement subtag,
+						# it is either a dialect or synonym of the preferred subtag. Either
+						# way, it is close enough to the truth to consider the replacement
+						# the macrolanguage of the deprecated language.
+						has_preferred_value = True
+						macrolanguage = line.split (' ')[1]
+						self._add_macrolanguage (macrolanguage, subtag)
+					elif not has_preferred_value and line.startswith ('Macrolanguage: '):
+						self._add_macrolanguage (line.split (' ')[1], subtag)
+				elif subtag_type == 'variant':
+					if line.startswith ('Prefix: '):
+						self.prefixes[subtag].add (line.split (' ')[1])
+				elif line.startswith ('File-Date: '):
+					self.header = line
+		expect (self.header)
+
+	def _add_macrolanguage (self, macrolanguage, language):
+		global ot
+		if language not in ot.from_bcp_47:
+			for l in self.macrolanguages.get (language, set ()):
+				self._add_macrolanguage (macrolanguage, l)
+		if macrolanguage not in ot.from_bcp_47:
+			for ls in list (self.macrolanguages.values ()):
+				if macrolanguage in ls:
+					ls.add (language)
+					return
+		self.macrolanguages[macrolanguage].add (language)
+
+	def remove_extra_macrolanguages (self):
+		"""Make every language have at most one macrolanguage."""
+		inverted = collections.defaultdict (list)
+		for macrolanguage, languages in self.macrolanguages.items ():
+			for language in languages:
+				inverted[language].append (macrolanguage)
+		for language, macrolanguages in inverted.items ():
+			if len (macrolanguages) > 1:
+				macrolanguages.sort (key=lambda ml: len (self.macrolanguages[ml]))
+				biggest_macrolanguage = macrolanguages.pop ()
+				for macrolanguage in macrolanguages:
+					self._add_macrolanguage (biggest_macrolanguage, macrolanguage)
+
+	def get_name (self, lt):
+		"""Return the names of the subtags in a language tag.
+
+		Args:
+			lt (LanguageTag): A BCP 47 language tag.
+
+		Returns:
+			The name form of ``lt``.
+		"""
+		name = self.names[lt.language].split ('\n')[0]
+		if lt.script:
+			name += '; ' + self.names[lt.script.title ()].split ('\n')[0]
+		if lt.region:
+			name += '; ' + self.names[lt.region.upper ()].split ('\n')[0]
+		if lt.variant:
+			name += '; ' + self.names[lt.variant].split ('\n')[0]
+		return name
+
+bcp_47 = BCP47Parser ()
+
+ot.parse (sys.argv[1])
+bcp_47.parse (sys.argv[2])
+
+ot.add_language ('ary', 'MOR')
+
+ot.add_language ('ath', 'ATH')
+
+ot.add_language ('bai', 'BML')
+
+ot.ranks['BAL'] = ot.ranks['KAR'] + 1
+
+ot.add_language ('ber', 'BBR')
+
+ot.remove_language_ot ('PGR')
+ot.add_language ('el-polyton', 'PGR')
+
+bcp_47.macrolanguages['et'] = {'ekk'}
+
+bcp_47.names['flm'] = 'Falam Chin'
+bcp_47.scopes['flm'] = ' (retired code)'
+bcp_47.macrolanguages['flm'] = {'cfm'}
+
+ot.ranks['FNE'] = ot.ranks['TNE'] + 1
+
+ot.add_language ('und-fonipa', 'IPPH')
+
+ot.add_language ('und-fonnapa', 'APPH')
+
+ot.remove_language_ot ('IRT')
+ot.add_language ('ga-Latg', 'IRT')
+
+ot.remove_language_ot ('KGE')
+ot.add_language ('und-Geok', 'KGE')
+
+ot.add_language ('guk', 'GUK')
+ot.names['GUK'] = 'Gumuz (SIL fonts)'
+ot.ranks['GUK'] = ot.ranks['GMZ'] + 1
+
+bcp_47.macrolanguages['id'] = {'in'}
+
+bcp_47.macrolanguages['ijo'] = {'ijc'}
+
+ot.add_language ('kht', 'KHN')
+ot.names['KHN'] = ot.names['KHT'] + ' (Microsoft fonts)'
+ot.names['KHT'] = ot.names['KHT'] + ' (OpenType spec and SIL fonts)'
+ot.ranks['KHN'] = ot.ranks['KHT']
+ot.ranks['KHT'] += 1
+
+ot.ranks['LCR'] = ot.ranks['MCR'] + 1
+
+ot.names['MAL'] = 'Malayalam Traditional'
+ot.ranks['MLR'] += 1
+
+bcp_47.names['mhv'] = 'Arakanese'
+bcp_47.scopes['mhv'] = ' (retired code)'
+
+ot.add_language ('no', 'NOR')
+
+ot.add_language ('oc-provenc', 'PRO')
+
+ot.add_language ('qu', 'QUZ')
+ot.add_language ('qub', 'QWH')
+ot.add_language ('qud', 'QVI')
+ot.add_language ('qug', 'QVI')
+ot.add_language ('qup', 'QVI')
+ot.add_language ('qur', 'QWH')
+ot.add_language ('qus', 'QUH')
+ot.add_language ('quw', 'QVI')
+ot.add_language ('qux', 'QWH')
+ot.add_language ('qva', 'QWH')
+ot.add_language ('qvh', 'QWH')
+ot.add_language ('qvj', 'QVI')
+ot.add_language ('qvl', 'QWH')
+ot.add_language ('qvm', 'QWH')
+ot.add_language ('qvn', 'QWH')
+ot.add_language ('qvo', 'QVI')
+ot.add_language ('qvp', 'QWH')
+ot.add_language ('qvw', 'QWH')
+ot.add_language ('qvz', 'QVI')
+ot.add_language ('qwa', 'QWH')
+ot.add_language ('qws', 'QWH')
+ot.add_language ('qxa', 'QWH')
+ot.add_language ('qxc', 'QWH')
+ot.add_language ('qxh', 'QWH')
+ot.add_language ('qxl', 'QVI')
+ot.add_language ('qxn', 'QWH')
+ot.add_language ('qxo', 'QWH')
+ot.add_language ('qxr', 'QVI')
+ot.add_language ('qxt', 'QWH')
+ot.add_language ('qxw', 'QWH')
+
+bcp_47.macrolanguages['ro'].remove ('mo')
+bcp_47.macrolanguages['ro-MD'].add ('mo')
+
+ot.add_language ('sgw', 'SGW')
+ot.names['SGW'] = ot.names['CHG'] + ' (SIL fonts)'
+ot.ranks['SGW'] = ot.ranks['CHG'] + 1
+
+ot.remove_language_ot ('SYRE')
+ot.remove_language_ot ('SYRJ')
+ot.remove_language_ot ('SYRN')
+ot.add_language ('und-Syre', 'SYRE')
+ot.add_language ('und-Syrj', 'SYRJ')
+ot.add_language ('und-Syrn', 'SYRN')
+
+bcp_47.names['xst'] = u"Silt'e"
+bcp_47.scopes['xst'] = ' (retired code)'
+bcp_47.macrolanguages['xst'] = {'stv', 'wle'}
+
+ot.add_language ('xwo', 'TOD')
+
+ot.remove_language_ot ('ZHH')
+ot.remove_language_ot ('ZHP')
+ot.remove_language_ot ('ZHT')
+bcp_47.macrolanguages['zh'].remove ('lzh')
+bcp_47.macrolanguages['zh'].remove ('yue')
+ot.add_language ('zh-Hant-MO', 'ZHH')
+ot.add_language ('zh-Hant-HK', 'ZHH')
+ot.add_language ('zh-Hans', 'ZHS')
+ot.add_language ('zh-Hant', 'ZHT')
+ot.add_language ('zh-HK', 'ZHH')
+ot.add_language ('zh-MO', 'ZHH')
+ot.add_language ('zh-TW', 'ZHT')
+ot.add_language ('lzh', 'ZHT')
+ot.add_language ('lzh-Hans', 'ZHS')
+ot.add_language ('yue', 'ZHH')
+ot.add_language ('yue-Hans', 'ZHS')
+
+bcp_47.macrolanguages['zom'] = {'yos'}
+
+def rank_delta (bcp_47, ot):
+	"""Return a delta to apply to a BCP 47 tag's rank.
+
+	Most OpenType tags have a constant rank, but a few have ranks that
+	depend on the BCP 47 tag.
+
+	Args:
+		bcp_47 (str): A BCP 47 tag.
+		ot (str): An OpenType tag to.
+
+	Returns:
+		A number to add to ``ot``'s rank when sorting ``bcp_47``'s
+		OpenType equivalents.
+	"""
+	if bcp_47 == 'ak' and ot == 'AKA':
+		return -1
+	if bcp_47 == 'tw' and ot == 'TWI':
+		return -1
+	return 0
+
+disambiguation = {
+	'ALT': 'alt',
+	'ARK': 'rki',
+	'BHI': 'bhb',
+	'BLN': 'bjt',
+	'BTI': 'beb',
+	'CCHN': 'cco',
+	'CMR': 'swb',
+	'CPP': 'crp',
+	'CRR': 'crx',
+	'DUJ': 'dwu',
+	'ECR': 'crj',
+	'HAL': 'cfm',
+	'HND': 'hnd',
+	'KIS': 'kqs',
+	'LRC': 'bqi',
+	'NDB': 'nd',
+	'NIS': 'njz',
+	'PLG': 'pce',
+	'PRO': 'pro',
+	'QIN': 'bgr',
+	'QUH': 'quh',
+	'QVI': 'qvi',
+	'QWH': 'qwh',
+	'SIG': 'stv',
+	'TNE': 'yrk',
+	'ZHH': 'zh-HK',
+	'ZHS': 'zh-Hans',
+	'ZHT': 'zh-Hant',
+}
+
+ot.inherit_from_macrolanguages ()
+bcp_47.remove_extra_macrolanguages ()
+ot.inherit_from_macrolanguages ()
+ot.sort_languages ()
+
+print ('/* == Start of generated table == */')
+print ('/*')
+print (' * The following table is generated by running:')
+print (' *')
+print (' *   %s languagetags language-subtag-registry' % sys.argv[0])
+print (' *')
+print (' * on files with these headers:')
+print (' *')
+print (' * %s' % ot.header.strip ())
+print (' * %s' % bcp_47.header)
+print (' */')
+print ()
+print ('#ifndef HB_OT_TAG_TABLE_HH')
+print ('#define HB_OT_TAG_TABLE_HH')
+print ()
+print ('static const LangTag ot_languages[] = {')
+
+def hb_tag (tag):
+	"""Convert a tag to ``HB_TAG`` form.
+
+	Args:
+		tag (str): An OpenType tag.
+
+	Returns:
+		A snippet of C++ representing ``tag``.
+	"""
+	return u"HB_TAG('%s','%s','%s','%s')" % tuple (('%-4s' % tag)[:4])
+
+def get_variant_set (name):
+	"""Return a set of variant language names from a name.
+
+	Args:
+		name (str): A list of language names from the BCP 47 registry,
+			joined on ``'\\n'``.
+
+	Returns:
+		A set of normalized language names.
+	"""
+	return set (unicodedata.normalize ('NFD', n.replace ('\u2019', u"'"))
+			.encode ('ASCII', 'ignore')
+			.strip ()
+			for n in re.split ('[\n(),]', name) if n)
+
+def language_name_intersection (a, b):
+	"""Return the names in common between two language names.
+
+	Args:
+		a (str): A list of language names from the BCP 47 registry,
+			joined on ``'\\n'``.
+		b (str): A list of language names from the BCP 47 registry,
+			joined on ``'\\n'``.
+
+	Returns:
+		The normalized language names shared by ``a`` and ``b``.
+	"""
+	return get_variant_set (a).intersection (get_variant_set (b))
+
+def get_matching_language_name (intersection, candidates):
+	return next (iter (c for c in candidates if not intersection.isdisjoint (get_variant_set (c))))
+
+maximum_tags = 0
+for language, tags in sorted (ot.from_bcp_47.items ()):
+	if language == '' or '-' in language:
+		continue
+	print ('  {\"%s\",\t{' % language, end='')
+	maximum_tags = max (maximum_tags, len (tags))
+	tag_count = len (tags)
+	for i, tag in enumerate (tags, start=1):
+		if i > 1:
+			print ('\t\t ', end='')
+		print (hb_tag (tag), end='')
+		if i == tag_count:
+			print ('}}', end='')
+		print (',\t/* ', end='')
+		bcp_47_name = bcp_47.names.get (language, '')
+		bcp_47_name_candidates = bcp_47_name.split ('\n')
+		intersection = language_name_intersection (bcp_47_name, ot.names[tag])
+		scope = bcp_47.scopes.get (language, '')
+		if not intersection:
+			write ('%s%s -> %s' % (bcp_47_name_candidates[0], scope, ot.names[tag]))
+		else:
+			name = get_matching_language_name (intersection, bcp_47_name_candidates)
+			bcp_47.names[language] = name
+			write ('%s%s' % (name if len (name) > len (ot.names[tag]) else ot.names[tag], scope))
+		print (' */')
+
+print ('};')
+print ()
+print ('static_assert (HB_OT_MAX_TAGS_PER_LANGUAGE == %iu, "");' % maximum_tags)
+print ()
+
+print ('/**')
+print (' * hb_ot_tags_from_complex_language:')
+print (' * @lang_str: a BCP 47 language tag to convert.')
+print (' * @limit: a pointer to the end of the substring of @lang_str to consider for')
+print (' * conversion.')
+print (' * @count: maximum number of language tags to retrieve (IN) and actual number of')
+print (' * language tags retrieved (OUT). If no tags are retrieved, it is not modified.')
+print (' * @tags: array of size at least @language_count to store the language tag')
+print (' * results')
+print (' *')
+print (' * Converts a multi-subtag BCP 47 language tag to language tags.')
+print (' *')
+print (' * Return value: Whether any language systems were retrieved.')
+print (' **/')
+print ('static bool')
+print ('hb_ot_tags_from_complex_language (const char   *lang_str,')
+print ('\t\t\t\t  const char   *limit,')
+print ('\t\t\t\t  unsigned int *count /* IN/OUT */,')
+print ('\t\t\t\t  hb_tag_t     *tags /* OUT */)')
+print ('{')
+
+def print_subtag_matches (subtag, new_line):
+	if subtag:
+		if new_line:
+			print ()
+			print ('\t&& ', end='')
+		print ('subtag_matches (lang_str, limit, "-%s")' % subtag, end='')
+
+complex_tags = collections.defaultdict (list)
+for initial, group in itertools.groupby ((lt_tags for lt_tags in [
+			(LanguageTag (language), tags)
+			for language, tags in sorted (ot.from_bcp_47.items (),
+				key=lambda i: (-len (i[0]), i[0]))
+		] if lt_tags[0].is_complex ()),
+		key=lambda lt_tags: lt_tags[0].get_group ()):
+	complex_tags[initial] += group
+
+for initial, items in sorted (complex_tags.items ()):
+	if initial != 'und':
+		continue
+	for lt, tags in items:
+		if lt.variant in bcp_47.prefixes:
+			expect (next (iter (bcp_47.prefixes[lt.variant])) == lt.language,
+					'%s is not a valid prefix of %s' % (lt.language, lt.variant))
+		print ('  if (', end='')
+		print_subtag_matches (lt.script, False)
+		print_subtag_matches (lt.region, False)
+		print_subtag_matches (lt.variant, False)
+		print (')')
+		print ('  {')
+		write ('    /* %s */' % bcp_47.get_name (lt))
+		print ()
+		if len (tags) == 1:
+			write ('    tags[0] = %s;  /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]]))
+			print ()
+			print ('    *count = 1;')
+		else:
+			print ('    hb_tag_t possible_tags[] = {')
+			for tag in tags:
+				write ('      %s,  /* %s */' % (hb_tag (tag), ot.names[tag]))
+				print ()
+			print ('    };')
+			print ('    for (i = 0; i < %s && i < *count; i++)' % len (tags))
+			print ('      tags[i] = possible_tags[i];')
+			print ('    *count = i;')
+		print ('    return true;')
+		print ('  }')
+
+print ('  switch (lang_str[0])')
+print ('  {')
+for initial, items in sorted (complex_tags.items ()):
+	if initial == 'und':
+		continue
+	print ("  case '%s':" % initial)
+	for lt, tags in items:
+		print ('    if (', end='')
+		if lt.grandfathered:
+			print ('0 == strcmp (&lang_str[1], "%s")' % lt.language[1:], end='')
+		else:
+			string_literal = lt.language[1:] + '-'
+			if lt.script:
+				string_literal += lt.script
+				lt.script = None
+				if lt.region:
+					string_literal += '-' + lt.region
+					lt.region = None
+			if string_literal[-1] == '-':
+				print ('0 == strncmp (&lang_str[1], "%s", %i)' % (string_literal, len (string_literal)), end='')
+			else:
+				print ('lang_matches (&lang_str[1], "%s")' % string_literal, end='')
+		print_subtag_matches (lt.script, True)
+		print_subtag_matches (lt.region, True)
+		print_subtag_matches (lt.variant, True)
+		print (')')
+		print ('    {')
+		write ('      /* %s */' % bcp_47.get_name (lt))
+		print ()
+		if len (tags) == 1:
+			write ('      tags[0] = %s;  /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]]))
+			print ()
+			print ('      *count = 1;')
+		else:
+			print ('      unsigned int i;')
+			print ('      hb_tag_t possible_tags[] = {')
+			for tag in tags:
+				write ('\t%s,  /* %s */' % (hb_tag (tag), ot.names[tag]))
+				print ()
+			print ('      };')
+			print ('      for (i = 0; i < %s && i < *count; i++)' % len (tags))
+			print ('\ttags[i] = possible_tags[i];')
+			print ('      *count = i;')
+		print ('      return true;')
+		print ('    }')
+	print ('    break;')
+
+print ('  }')
+print ('  return false;')
+print ('}')
+print ()
+print ('/**')
+print (' * hb_ot_ambiguous_tag_to_language')
+print (' * @tag: A language tag.')
+print (' *')
+print (' * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to')
+print (' * many language tags) and the best tag is not the alphabetically first, or if')
+print (' * the best tag consists of multiple subtags.')
+print (' *')
+print (' * Return value: The #hb_language_t corresponding to the BCP 47 language tag,')
+print (' * or #HB_LANGUAGE_INVALID if @tag is not ambiguous.')
+print (' **/')
+print ('static hb_language_t')
+print ('hb_ot_ambiguous_tag_to_language (hb_tag_t tag)')
+print ('{')
+print ('  switch (tag)')
+print ('  {')
+
+def verify_disambiguation_dict ():
+	"""Verify and normalize ``disambiguation``.
+
+	``disambiguation`` is a map of ambiguous OpenType language system
+	tags to the particular BCP 47 tags they correspond to. This function
+	checks that all its keys really are ambiguous and that each key's
+	value is valid for that key. It checks that no ambiguous tag is
+	missing, except when it can figure out which BCP 47 tag is the best
+	by itself.
+
+	It modifies ``disambiguation`` to remove keys whose values are the
+	same as those that the fallback would return anyway, and to add
+	ambiguous keys whose disambiguations it determined automatically.
+
+	Raises:
+		AssertionError: Verification failed.
+	"""
+	global bcp_47
+	global disambiguation
+	global ot
+	for ot_tag, bcp_47_tags in ot.to_bcp_47.items ():
+		primary_tags = list (t for t in bcp_47_tags if t not in bcp_47.grandfathered and ot.from_bcp_47.get (t)[0] == ot_tag)
+		if len (primary_tags) == 1:
+			expect (ot_tag not in disambiguation, 'unnecessary disambiguation for OT tag: %s' % ot_tag)
+			if '-' in primary_tags[0]:
+				disambiguation[ot_tag] = primary_tags[0]
+		elif len (primary_tags) == 0:
+			expect (ot_tag not in disambiguation, 'There is no possible valid disambiguation for %s' % ot_tag)
+		else:
+			macrolanguages = list (t for t in primary_tags if bcp_47.scopes.get (t) == ' [macrolanguage]')
+			if len (macrolanguages) != 1:
+				macrolanguages = list (t for t in primary_tags if bcp_47.scopes.get (t) == ' [family]')
+			if len (macrolanguages) != 1:
+				macrolanguages = list (t for t in primary_tags if 'retired code' not in bcp_47.scopes.get (t, ''))
+			if len (macrolanguages) != 1:
+				expect (ot_tag in disambiguation, 'ambiguous OT tag: %s %s' % (ot_tag, str (macrolanguages)))
+				expect (disambiguation[ot_tag] in bcp_47_tags,
+						'%s is not a valid disambiguation for %s' % (disambiguation[ot_tag], ot_tag))
+			elif ot_tag not in disambiguation:
+				disambiguation[ot_tag] = macrolanguages[0]
+			if disambiguation[ot_tag] == sorted (primary_tags)[0] and '-' not in disambiguation[ot_tag]:
+				del disambiguation[ot_tag]
+	for ot_tag in disambiguation.keys ():
+		expect (ot_tag in ot.to_bcp_47, 'unknown OT tag: %s' % ot_tag)
+
+verify_disambiguation_dict ()
+for ot_tag, bcp_47_tag in sorted (disambiguation.items ()):
+	write ('  case %s:  /* %s */' % (hb_tag (ot_tag), ot.names[ot_tag]))
+	print ()
+	write ('    return hb_language_from_string (\"%s\", -1);  /* %s */' % (bcp_47_tag, bcp_47.get_name (LanguageTag (bcp_47_tag))))
+	print ()
+
+print ('  default:')
+print ('    return HB_LANGUAGE_INVALID;')
+print ('  }')
+print ('}')
+
+print ()
+print ('#endif /* HB_OT_TAG_TABLE_HH */')
+print ()
+print ('/* == End of generated table == */')
+
diff --git a/src/gen-use-table.py b/src/gen-use-table.py
index c742eba..ebfae6f 100755
--- a/src/gen-use-table.py
+++ b/src/gen-use-table.py
@@ -8,7 +8,7 @@
 	print ("usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt", file=sys.stderr)
 	sys.exit (1)
 
-BLACKLISTED_BLOCKS = ["Thai", "Lao", "Tibetan"]
+BLACKLISTED_BLOCKS = ["Thai", "Lao"]
 
 files = [io.open (x, encoding='utf-8') for x in sys.argv[1:]]
 
@@ -197,7 +197,10 @@
 def is_CONS_WITH_STACKER(U, UISC, UGC):
 	return UISC == Consonant_With_Stacker
 def is_HALANT(U, UISC, UGC):
-	return UISC in [Virama, Invisible_Stacker]
+	return UISC in [Virama, Invisible_Stacker] and not is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC)
+def is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC):
+	# https://github.com/harfbuzz/harfbuzz/issues/1102
+	return U == 0x11046
 def is_HALANT_NUM(U, UISC, UGC):
 	return UISC == Number_Joiner
 def is_ZWNJ(U, UISC, UGC):
@@ -248,6 +251,7 @@
 	'SUB':	is_CONS_SUB,
 	'CS':	is_CONS_WITH_STACKER,
 	'H':	is_HALANT,
+	'HVM':	is_HALANT_OR_VOWEL_MODIFIER,
 	'HN':	is_HALANT_NUM,
 	'ZWNJ':	is_ZWNJ,
 	'ZWJ':	is_ZWJ,
@@ -281,8 +285,8 @@
 	'V': {
 		'Abv': [Top, Top_And_Bottom, Top_And_Bottom_And_Right, Top_And_Right],
 		'Blw': [Bottom, Overstruck, Bottom_And_Right],
-		'Pst': [Right],
-		'Pre': [Left, Top_And_Left, Top_And_Left_And_Right, Left_And_Right],
+		'Pst': [Right, Top_And_Left, Top_And_Left_And_Right, Left_And_Right],
+		'Pre': [Left],
 	},
 	'VM': {
 		'Abv': [Top],
@@ -295,6 +299,7 @@
 		'Blw': [Bottom],
 	},
 	'H': None,
+	'HVM': None,
 	'B': None,
 	'FM': None,
 	'SUB': None,
@@ -307,11 +312,28 @@
 
 		# Resolve Indic_Syllabic_Category
 
-		# TODO: These don't have UISC assigned in Unicode 8.0, but
-		# have UIPC
+		# TODO: These don't have UISC assigned in Unicode 8.0, but have UIPC
 		if U == 0x17DD: UISC = Vowel_Dependent
 		if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark
 
+		# Tibetan:
+		# TODO: These don't have UISC assigned in Unicode 11.0, but have UIPC
+		if 0x0F18 <= U <= 0x0F19 or 0x0F3E <= U <= 0x0F3F: UISC = Vowel_Dependent
+		if 0x0F86 <= U <= 0x0F87: UISC = Tone_Mark
+		# Overrides to allow NFC order matching syllable
+		# https://github.com/harfbuzz/harfbuzz/issues/1012
+		if UBlock == 'Tibetan' and is_VOWEL (U, UISC, UGC):
+			if UIPC == Top:
+				UIPC = Bottom
+
+		# TODO: https://github.com/harfbuzz/harfbuzz/pull/982
+		# also  https://github.com/harfbuzz/harfbuzz/issues/1012
+		if UBlock == 'Chakma' and is_VOWEL (U, UISC, UGC):
+			if UIPC == Top:
+				UIPC = Bottom
+			elif UIPC == Bottom:
+				UIPC = Top
+
 		# TODO: https://github.com/harfbuzz/harfbuzz/pull/627
 		if 0x1BF2 <= U <= 0x1BF3: UISC = Nukta; UIPC = Bottom
 
@@ -359,13 +381,6 @@
 		# https://github.com/roozbehp/unicode-data/issues/8
 		if U == 0x0A51: UIPC = Bottom
 
-		# TODO: https://github.com/harfbuzz/harfbuzz/pull/982
-		if UBlock == 'Chakma' and is_VOWEL (U, UISC, UGC):
-			if UIPC == Top:
-				UIPC = Bottom
-			elif UIPC == Bottom:
-				UIPC = Top
-
 		assert (UIPC in [Not_Applicable, Visual_Order_Left] or
 			USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC)
 
@@ -394,7 +409,7 @@
 		print (" * %s" % (l.strip()))
 print (" */")
 print ()
-print ('#include "hb-ot-shape-complex-use-private.hh"')
+print ('#include "hb-ot-shape-complex-use.hh"')
 print ()
 
 total = 0
diff --git a/src/gen-vowel-constraints.py b/src/gen-vowel-constraints.py
new file mode 100755
index 0000000..c4e75aa
--- /dev/null
+++ b/src/gen-vowel-constraints.py
@@ -0,0 +1,221 @@
+#!/usr/bin/python
+
+"""Generator of the function to prohibit certain vowel sequences.
+
+It creates ``_hb_preprocess_text_vowel_constraints``, which inserts dotted
+circles into sequences prohibited by the USE script development spec.
+This function should be used as the ``preprocess_text`` of an
+``hb_ot_complex_shaper_t``.
+"""
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import collections
+try:
+	from HTMLParser import HTMLParser
+	def write (s):
+		print (s.encode ('utf-8'), end='')
+except ImportError:
+	from html.parser import HTMLParser
+	def write (s):
+		sys.stdout.flush ()
+		sys.stdout.buffer.write (s.encode ('utf-8'))
+import itertools
+import io
+import sys
+
+if len (sys.argv) != 3:
+	print ('usage: ./gen-vowel-constraints.py HBIndicVowelConstraints.txt Scripts.txt', file=sys.stderr)
+	sys.exit (1)
+
+with io.open (sys.argv[2], encoding='utf-8') as f:
+	scripts_header = [f.readline () for i in range (2)]
+	scripts = {}
+	script_order = {}
+	for line in f:
+		j = line.find ('#')
+		if j >= 0:
+			line = line[:j]
+		fields = [x.strip () for x in line.split (';')]
+		if len (fields) == 1:
+			continue
+		uu = fields[0].split ('..')
+		start = int (uu[0], 16)
+		if len (uu) == 1:
+			end = start
+		else:
+			end = int (uu[1], 16)
+		script = fields[1]
+		for u in range (start, end + 1):
+			scripts[u] = script
+		if script not in script_order:
+			script_order[script] = start
+
+class ConstraintSet (object):
+	"""A set of prohibited code point sequences.
+
+	Args:
+		constraint (List[int]): A prohibited code point sequence.
+
+	"""
+	def __init__ (self, constraint):
+		# Either a list or a dictionary. As a list of code points, it
+		# represents a prohibited code point sequence. As a dictionary,
+		# it represents a set of prohibited sequences, where each item
+		# represents the set of prohibited sequences starting with the
+		# key (a code point) concatenated with any of the values
+		# (ConstraintSets).
+		self._c = constraint
+
+	def add (self, constraint):
+		"""Add a constraint to this set."""
+		if not constraint:
+			return
+		first = constraint[0]
+		rest = constraint[1:]
+		if isinstance (self._c, list):
+			if constraint == self._c[:len (constraint)]:
+				self._c = constraint
+			elif self._c != constraint[:len (self._c)]:
+				self._c = {self._c[0]: ConstraintSet (self._c[1:])}
+		if isinstance (self._c, dict):
+			if first in self._c:
+				self._c[first].add (rest)
+			else:
+				self._c[first] = ConstraintSet (rest)
+
+	def _indent (self, depth):
+		return ('  ' * depth).replace ('        ', '\t')
+
+	def __str__ (self, index=0, depth=4):
+		s = []
+		indent = self._indent (depth)
+		if isinstance (self._c, list):
+			if len (self._c) == 0:
+				s.append ('{}matched = true;\n'.format (indent))
+			elif len (self._c) == 1:
+				s.append ('{}matched = 0x{:04X}u == buffer->cur ({}).codepoint;\n'.format (indent, next (iter (self._c)), index or ''))
+			else:
+				s.append ('{}if (0x{:04X}u == buffer->cur ({}).codepoint &&\n'.format (indent, self._c[0], index))
+				s.append ('{}buffer->idx + {} < count &&\n'.format (self._indent (depth + 2), len (self._c)))
+				for i, cp in enumerate (self._c[1:], start=1):
+					s.append ('{}0x{:04X}u == buffer->cur ({}).codepoint{}\n'.format (
+						self._indent (depth + 2), cp, index + i, ')' if i == len (self._c) - 1 else ' &&'))
+				s.append ('{}{{\n'.format (indent))
+				for i in range (len (self._c)):
+					s.append ('{}buffer->next_glyph ();\n'.format (self._indent (depth + 1)))
+				s.append ('{}_output_dotted_circle (buffer);\n'.format (self._indent (depth + 1)))
+				s.append ('{}}}\n'.format (indent))
+		else:
+			s.append ('{}switch (buffer->cur ({}).codepoint)\n'.format(indent, index or ''))
+			s.append ('{}{{\n'.format (indent))
+			cases = collections.defaultdict (set)
+			for first, rest in sorted (self._c.items ()):
+				cases[rest.__str__ (index + 1, depth + 2)].add (first)
+			for body, labels in sorted (cases.items (), key=lambda b_ls: sorted (b_ls[1])[0]):
+				for i, cp in enumerate (sorted (labels)):
+					if i % 4 == 0:
+						s.append (self._indent (depth + 1))
+					else:
+						s.append (' ')
+					s.append ('case 0x{:04X}u:{}'.format (cp, '\n' if i % 4 == 3 else ''))
+				if len (labels) % 4 != 0:
+					s.append ('\n')
+				s.append (body)
+				s.append ('{}break;\n'.format (self._indent (depth + 2)))
+			s.append ('{}}}\n'.format (indent))
+		return ''.join (s)
+
+constraints = {}
+with io.open (sys.argv[1], encoding='utf-8') as f:
+	constraints_header = [f.readline ().strip () for i in range (2)]
+	for line in f:
+		j = line.find ('#')
+		if j >= 0:
+			line = line[:j]
+		constraint = [int (cp, 16) for cp in line.split (';')[0].split ()]
+		if not constraint: continue
+		assert 2 <= len (constraint), 'Prohibited sequence is too short: {}'.format (constraint)
+		script = scripts[constraint[0]]
+		if script in constraints:
+			constraints[script].add (constraint)
+		else:
+			constraints[script] = ConstraintSet (constraint)
+		assert constraints, 'No constraints found'
+
+print ('/* == Start of generated functions == */')
+print ('/*')
+print (' * The following functions are generated by running:')
+print (' *')
+print (' *   %s use Scripts.txt' % sys.argv[0])
+print (' *')
+print (' * on files with these headers:')
+print (' *')
+for line in constraints_header:
+	print (' * %s' % line.strip ())
+print (' *')
+for line in scripts_header:
+	print (' * %s' % line.strip ())
+print (' */')
+print ()
+print ('#include "hb-ot-shape-complex-vowel-constraints.hh"')
+print ()
+print ('static void')
+print ('_output_dotted_circle (hb_buffer_t *buffer)')
+print ('{')
+print ('  hb_glyph_info_t &dottedcircle = buffer->output_glyph (0x25CCu);')
+print ('  _hb_glyph_info_reset_continuation (&dottedcircle);')
+print ('}')
+print ()
+print ('static void')
+print ('_output_with_dotted_circle (hb_buffer_t *buffer)')
+print ('{')
+print ('  _output_dotted_circle (buffer);')
+print ('  buffer->next_glyph ();')
+print ('}')
+print ()
+
+print ('void')
+print ('_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED,')
+print ('\t\t\t\t       hb_buffer_t              *buffer,')
+print ('\t\t\t\t       hb_font_t                *font HB_UNUSED)')
+print ('{')
+print ('  /* UGLY UGLY UGLY business of adding dotted-circle in the middle of')
+print ('   * vowel-sequences that look like another vowel.  Data for each script')
+print ('   * collected from the USE script development spec.')
+print ('   *')
+print ('   * https://github.com/harfbuzz/harfbuzz/issues/1019')
+print ('   */')
+print ('  bool processed = false;')
+print ('  buffer->clear_output ();')
+print ('  unsigned int count = buffer->len;')
+print ('  switch ((unsigned) buffer->props.script)')
+print ('  {')
+
+for script, constraints in sorted (constraints.items (), key=lambda s_c: script_order[s_c[0]]):
+	print ('    case HB_SCRIPT_{}:'.format (script.upper ()))
+	print ('      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)')
+	print ('      {')
+	print ('\tbool matched = false;')
+	write (str (constraints))
+	print ('\tbuffer->next_glyph ();')
+	print ('\tif (matched) _output_with_dotted_circle (buffer);')
+	print ('      }')
+	print ('      processed = true;')
+	print ('      break;')
+	print ()
+
+print ('    default:')
+print ('      break;')
+print ('  }')
+print ('  if (processed)')
+print ('  {')
+print ('    if (buffer->idx < count)')
+print ('     buffer->next_glyph ();')
+print ('    if (likely (buffer->successful))')
+print ('      buffer->swap_buffers ();')
+print ('  }')
+print ('}')
+
+print ()
+print ('/* == End of generated functions == */')
diff --git a/src/harfbuzz-config.cmake.in b/src/harfbuzz-config.cmake.in
index 87b1572..304410d 100644
--- a/src/harfbuzz-config.cmake.in
+++ b/src/harfbuzz-config.cmake.in
@@ -12,7 +12,11 @@
 string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_libdir "${_harfbuzz_libdir}")
 set(_harfbuzz_libdir_iter "${_harfbuzz_libdir}")
 while (_harfbuzz_libdir_iter)
+  set(_harfbuzz_libdir_prev_iter "${_harfbuzz_libdir_iter}")
   get_filename_component(_harfbuzz_libdir_iter "${_harfbuzz_libdir_iter}" DIRECTORY)
+  if (_harfbuzz_libdir_prev_iter STREQUAL _harfbuzz_libdir_iter)
+    break()
+  endif ()
   get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY)
 endwhile ()
 unset(_harfbuzz_libdir_iter)
diff --git a/src/hb-aat-layout-ankr-table.hh b/src/hb-aat-layout-ankr-table.hh
index 3b7912b..5f7656d 100644
--- a/src/hb-aat-layout-ankr-table.hh
+++ b/src/hb-aat-layout-ankr-table.hh
@@ -25,7 +25,7 @@
 #ifndef HB_AAT_LAYOUT_ANKR_TABLE_HH
 #define HB_AAT_LAYOUT_ANKR_TABLE_HH
 
-#include "hb-aat-layout-common-private.hh"
+#include "hb-aat-layout-common.hh"
 
 /*
  * ankr -- Anchor Point
@@ -45,32 +45,49 @@
     return_trace (c->check_struct (this));
   }
 
+  public:
   FWORD		xCoordinate;
   FWORD		yCoordinate;
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
+typedef LArrayOf<Anchor> GlyphAnchors;
+
 struct ankr
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_ankr;
 
+  inline const Anchor &get_anchor (hb_codepoint_t glyph_id,
+				   unsigned int i,
+				   unsigned int num_glyphs,
+				   const char *end) const
+  {
+    const Offset<HBUINT16, false> *offset = (this+lookupTable).get_value (glyph_id, num_glyphs);
+    if (!offset)
+      return Null(Anchor);
+    const GlyphAnchors &anchors = StructAtOffset<GlyphAnchors> (&(this+anchorData), *offset);
+    /* TODO Use sanitizer; to avoid overflows and more. */
+    if (unlikely ((const char *) &anchors + anchors.get_size () > end))
+      return Null(Anchor);
+    return anchors[i];
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
 			  version == 0 &&
-			  lookupTable.sanitize (c, this) &&
-			  anchors.sanitize (c, this)));
+			  lookupTable.sanitize (c, this)));
   }
 
   protected:
   HBUINT16	version; 	/* Version number (set to zero) */
   HBUINT16	flags;		/* Flags (currently unused; set to zero) */
-  LOffsetTo<Lookup<HBUINT16> >
+  LOffsetTo<Lookup<Offset<HBUINT16, false> >, false>
 		lookupTable;	/* Offset to the table's lookup table */
-  LOffsetTo<LArrayOf<Anchor> >
-		anchors;	/* Offset to the glyph data table */
+  LOffsetTo<HBUINT8, false>
+		anchorData;	/* Offset to the glyph data table */
 
   public:
   DEFINE_SIZE_STATIC (12);
diff --git a/src/hb-aat-layout-bsln-table.hh b/src/hb-aat-layout-bsln-table.hh
index df2bf5b..b864086 100644
--- a/src/hb-aat-layout-bsln-table.hh
+++ b/src/hb-aat-layout-bsln-table.hh
@@ -25,7 +25,7 @@
 #ifndef HB_AAT_LAYOUT_BSLN_TABLE_HH
 #define HB_AAT_LAYOUT_BSLN_TABLE_HH
 
-#include "hb-aat-layout-common-private.hh"
+#include "hb-aat-layout-common.hh"
 
 /*
  * bsln -- Baseline
diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh
new file mode 100644
index 0000000..ce40abd
--- /dev/null
+++ b/src/hb-aat-layout-common.hh
@@ -0,0 +1,651 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_AAT_LAYOUT_COMMON_HH
+#define HB_AAT_LAYOUT_COMMON_HH
+
+#include "hb-aat-layout.hh"
+
+
+namespace AAT {
+
+using namespace OT;
+
+
+/*
+ * Lookup Table
+ */
+
+template <typename T> struct Lookup;
+
+template <typename T>
+struct LookupFormat0
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  {
+    if (unlikely (glyph_id >= num_glyphs)) return nullptr;
+    return &arrayZ[glyph_id];
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (arrayZ.sanitize (c, c->get_num_glyphs ()));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 0 */
+  UnsizedArrayOf<T>
+		arrayZ;		/* Array of lookup values, indexed by glyph index. */
+  public:
+  DEFINE_SIZE_ARRAY (2, arrayZ);
+};
+
+
+template <typename T>
+struct LookupSegmentSingle
+{
+  inline int cmp (hb_codepoint_t g) const {
+    return g < first ? -1 : g <= last ? 0 : +1 ;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && value.sanitize (c));
+  }
+
+  GlyphID	last;		/* Last GlyphID in this segment */
+  GlyphID	first;		/* First GlyphID in this segment */
+  T		value;		/* The lookup value (only one) */
+  public:
+  DEFINE_SIZE_STATIC (4 + T::static_size);
+};
+
+template <typename T>
+struct LookupFormat2
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const T* get_value (hb_codepoint_t glyph_id) const
+  {
+    const LookupSegmentSingle<T> *v = segments.bsearch (glyph_id);
+    return v ? &v->value : nullptr;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (segments.sanitize (c));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 2 */
+  VarSizedBinSearchArrayOf<LookupSegmentSingle<T> >
+		segments;	/* The actual segments. These must already be sorted,
+				 * according to the first word in each one (the last
+				 * glyph in each segment). */
+  public:
+  DEFINE_SIZE_ARRAY (8, segments);
+};
+
+template <typename T>
+struct LookupSegmentArray
+{
+  inline const T* get_value (hb_codepoint_t glyph_id, const void *base) const
+  {
+    return first <= glyph_id && glyph_id <= last ? &(base+valuesZ)[glyph_id - first] : nullptr;
+  }
+
+  inline int cmp (hb_codepoint_t g) const {
+    return g < first ? -1 : g <= last ? 0 : +1 ;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  first <= last &&
+		  valuesZ.sanitize (c, base, last - first + 1));
+  }
+
+  GlyphID	last;		/* Last GlyphID in this segment */
+  GlyphID	first;		/* First GlyphID in this segment */
+  OffsetTo<UnsizedArrayOf<T>, HBUINT16, false>
+		valuesZ;	/* A 16-bit offset from the start of
+				 * the table to the data. */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+template <typename T>
+struct LookupFormat4
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const T* get_value (hb_codepoint_t glyph_id) const
+  {
+    const LookupSegmentArray<T> *v = segments.bsearch (glyph_id);
+    return v ? v->get_value (glyph_id, this) : nullptr;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (segments.sanitize (c, this));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 4 */
+  VarSizedBinSearchArrayOf<LookupSegmentArray<T> >
+		segments;	/* The actual segments. These must already be sorted,
+				 * according to the first word in each one (the last
+				 * glyph in each segment). */
+  public:
+  DEFINE_SIZE_ARRAY (8, segments);
+};
+
+template <typename T>
+struct LookupSingle
+{
+  inline int cmp (hb_codepoint_t g) const { return glyph.cmp (g); }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && value.sanitize (c));
+  }
+
+  GlyphID	glyph;		/* Last GlyphID */
+  T		value;		/* The lookup value (only one) */
+  public:
+  DEFINE_SIZE_STATIC (2 + T::static_size);
+};
+
+template <typename T>
+struct LookupFormat6
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const T* get_value (hb_codepoint_t glyph_id) const
+  {
+    const LookupSingle<T> *v = entries.bsearch (glyph_id);
+    return v ? &v->value : nullptr;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (entries.sanitize (c));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 6 */
+  VarSizedBinSearchArrayOf<LookupSingle<T> >
+		entries;	/* The actual entries, sorted by glyph index. */
+  public:
+  DEFINE_SIZE_ARRAY (8, entries);
+};
+
+template <typename T>
+struct LookupFormat8
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const T* get_value (hb_codepoint_t glyph_id) const
+  {
+    return firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount ?
+	   &valueArrayZ[glyph_id - firstGlyph] : nullptr;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 8 */
+  GlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
+  HBUINT16	glyphCount;	/* Total number of glyphs (equivalent to the last
+				 * glyph minus the value of firstGlyph plus 1). */
+  UnsizedArrayOf<T>
+		valueArrayZ;	/* The lookup values (indexed by the glyph index
+				 * minus the value of firstGlyph). */
+  public:
+  DEFINE_SIZE_ARRAY (6, valueArrayZ);
+};
+
+template <typename T>
+struct LookupFormat10
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const typename T::type get_value_or_null (hb_codepoint_t glyph_id) const
+  {
+    if (!(firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount))
+      return Null(T);
+
+    const HBUINT8 *p = &valueArrayZ[(glyph_id - firstGlyph) * valueSize];
+
+    unsigned int v = 0;
+    unsigned int count = valueSize;
+    for (unsigned int i = 0; i < count; i++)
+      v = (v << 8) | *p++;
+
+    return v;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  valueSize <= 4 &&
+		  valueArrayZ.sanitize (c, glyphCount * valueSize));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 8 */
+  HBUINT16	valueSize;	/* Byte size of each value. */
+  GlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
+  HBUINT16	glyphCount;	/* Total number of glyphs (equivalent to the last
+				 * glyph minus the value of firstGlyph plus 1). */
+  UnsizedArrayOf<HBUINT8>
+		valueArrayZ;	/* The lookup values (indexed by the glyph index
+				 * minus the value of firstGlyph). */
+  public:
+  DEFINE_SIZE_ARRAY (8, valueArrayZ);
+};
+
+template <typename T>
+struct Lookup
+{
+  inline const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  {
+    switch (u.format) {
+    case 0: return u.format0.get_value (glyph_id, num_glyphs);
+    case 2: return u.format2.get_value (glyph_id);
+    case 4: return u.format4.get_value (glyph_id);
+    case 6: return u.format6.get_value (glyph_id);
+    case 8: return u.format8.get_value (glyph_id);
+    default:return nullptr;
+    }
+  }
+
+  inline const typename T::type get_value_or_null (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  {
+    switch (u.format) {
+      /* Format 10 cannot return a pointer. */
+      case 10: return u.format10.get_value_or_null (glyph_id);
+      default:
+      const T *v = get_value (glyph_id, num_glyphs);
+      return v ? *v : Null(T);
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format) {
+    case 0: return_trace (u.format0.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    case 4: return_trace (u.format4.sanitize (c));
+    case 6: return_trace (u.format6.sanitize (c));
+    case 8: return_trace (u.format8.sanitize (c));
+    case 10: return_trace (u.format10.sanitize (c));
+    default:return_trace (true);
+    }
+  }
+
+  protected:
+  union {
+  HBUINT16		format;		/* Format identifier */
+  LookupFormat0<T>	format0;
+  LookupFormat2<T>	format2;
+  LookupFormat4<T>	format4;
+  LookupFormat6<T>	format6;
+  LookupFormat8<T>	format8;
+  LookupFormat10<T>	format10;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+/* Lookup 0 has unbounded size (dependant on num_glyphs).  So we need to defined
+ * special NULL objects for Lookup<> objects, but since it's template our macros
+ * don't work.  So we have to hand-code them here.  UGLY. */
+} /* Close namespace. */
+/* Ugly hand-coded null objects for template Lookup<> :(. */
+extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2];
+template <>
+/*static*/ inline const AAT::Lookup<OT::HBUINT16>& Null<AAT::Lookup<OT::HBUINT16> > (void) {
+  return *reinterpret_cast<const AAT::Lookup<OT::HBUINT16> *> (_hb_Null_AAT_Lookup);
+}
+template <>
+/*static*/ inline const AAT::Lookup<OT::HBUINT32>& Null<AAT::Lookup<OT::HBUINT32> > (void) {
+  return *reinterpret_cast<const AAT::Lookup<OT::HBUINT32> *> (_hb_Null_AAT_Lookup);
+}
+template <>
+/*static*/ inline const AAT::Lookup<OT::Offset<OT::HBUINT16, false> >& Null<AAT::Lookup<OT::Offset<OT::HBUINT16, false> > > (void) {
+  return *reinterpret_cast<const AAT::Lookup<OT::Offset<OT::HBUINT16, false> > *> (_hb_Null_AAT_Lookup);
+}
+namespace AAT {
+
+enum { DELETED_GLYPH = 0xFFFF };
+
+/*
+ * Extended State Table
+ */
+
+template <typename T>
+struct Entry
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  {
+    TRACE_SANITIZE (this);
+    /* Note, we don't recurse-sanitize data because we don't access it.
+     * That said, in our DEFINE_SIZE_STATIC we access T::static_size,
+     * which ensures that data has a simple sanitize(). To be determined
+     * if I need to remove that as well.
+     *
+     * XXX Because we are a template, our DEFINE_SIZE_STATIC assertion
+     * wouldn't be checked. */
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  HBUINT16	newState;	/* Byte offset from beginning of state table
+				 * to the new state. Really?!?! Or just state
+				 * number?  The latter in morx for sure. */
+  HBUINT16	flags;		/* Table specific. */
+  T		data;		/* Optional offsets to per-glyph tables. */
+  public:
+  DEFINE_SIZE_STATIC (4 + T::static_size);
+};
+
+template <>
+struct Entry<void>
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count /*XXX Unused?*/) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  HBUINT16	newState;	/* Byte offset from beginning of state table to the new state. */
+  HBUINT16	flags;		/* Table specific. */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+template <typename Extra>
+struct StateTable
+{
+  enum State
+  {
+    STATE_START_OF_TEXT = 0,
+    STATE_START_OF_LINE = 1,
+  };
+  enum Class
+  {
+    CLASS_END_OF_TEXT = 0,
+    CLASS_OUT_OF_BOUNDS = 1,
+    CLASS_DELETED_GLYPH = 2,
+    CLASS_END_OF_LINE = 3,
+  };
+
+  inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  {
+    if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH;
+    const HBUINT16 *v = (this+classTable).get_value (glyph_id, num_glyphs);
+    return v ? (unsigned) *v : (unsigned) CLASS_OUT_OF_BOUNDS;
+  }
+
+  inline const Entry<Extra> *get_entries () const
+  {
+    return (this+entryTable).arrayZ;
+  }
+
+  inline const Entry<Extra> *get_entryZ (unsigned int state, unsigned int klass) const
+  {
+    if (unlikely (klass >= nClasses)) return nullptr;
+
+    const HBUINT16 *states = (this+stateArrayTable).arrayZ;
+    const Entry<Extra> *entries = (this+entryTable).arrayZ;
+
+    unsigned int entry = states[state * nClasses + klass];
+
+    return &entries[entry];
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c,
+			unsigned int *num_entries_out = nullptr) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!(c->check_struct (this) &&
+		    classTable.sanitize (c, this)))) return_trace (false);
+
+    const HBUINT16 *states = (this+stateArrayTable).arrayZ;
+    const Entry<Extra> *entries = (this+entryTable).arrayZ;
+
+    unsigned int num_classes = nClasses;
+
+    unsigned int num_states = 1;
+    unsigned int num_entries = 0;
+
+    unsigned int state = 0;
+    unsigned int entry = 0;
+    while (state < num_states)
+    {
+      if (unlikely (hb_unsigned_mul_overflows (num_classes, states[0].static_size)))
+	return_trace (false);
+
+      if (unlikely (!c->check_array (states,
+				     num_states,
+				     num_classes * states[0].static_size)))
+	return_trace (false);
+      if ((c->max_ops -= num_states - state) < 0)
+	return_trace (false);
+      { /* Sweep new states. */
+	const HBUINT16 *stop = &states[num_states * num_classes];
+	for (const HBUINT16 *p = &states[state * num_classes]; p < stop; p++)
+	  num_entries = MAX<unsigned int> (num_entries, *p + 1);
+	state = num_states;
+      }
+
+      if (unlikely (!c->check_array (entries, num_entries)))
+	return_trace (false);
+      if ((c->max_ops -= num_entries - entry) < 0)
+	return_trace (false);
+      { /* Sweep new entries. */
+	const Entry<Extra> *stop = &entries[num_entries];
+	for (const Entry<Extra> *p = &entries[entry]; p < stop; p++)
+	  num_states = MAX<unsigned int> (num_states, p->newState + 1);
+	entry = num_entries;
+      }
+    }
+
+    if (num_entries_out)
+      *num_entries_out = num_entries;
+
+    return_trace (true);
+  }
+
+  protected:
+  HBUINT32	nClasses;	/* Number of classes, which is the number of indices
+				 * in a single line in the state array. */
+  LOffsetTo<Lookup<HBUINT16>, false>
+		classTable;	/* Offset to the class table. */
+  LOffsetTo<UnsizedArrayOf<HBUINT16>, false>
+		stateArrayTable;/* Offset to the state array. */
+  LOffsetTo<UnsizedArrayOf<Entry<Extra> >, false>
+		entryTable;	/* Offset to the entry array. */
+
+  public:
+  DEFINE_SIZE_STATIC (16);
+};
+
+template <typename EntryData>
+struct StateTableDriver
+{
+  inline StateTableDriver (const StateTable<EntryData> &machine_,
+			   hb_buffer_t *buffer_,
+			   hb_face_t *face_) :
+	      machine (machine_),
+	      buffer (buffer_),
+	      num_glyphs (face_->get_num_glyphs ()) {}
+
+  template <typename context_t>
+  inline void drive (context_t *c)
+  {
+    if (!c->in_place)
+      buffer->clear_output ();
+
+    unsigned int state = StateTable<EntryData>::STATE_START_OF_TEXT;
+    bool last_was_dont_advance = false;
+    for (buffer->idx = 0; buffer->successful;)
+    {
+      unsigned int klass = buffer->idx < buffer->len ?
+			   machine.get_class (buffer->info[buffer->idx].codepoint, num_glyphs) :
+			   (unsigned) StateTable<EntryData>::CLASS_END_OF_TEXT;
+      const Entry<EntryData> *entry = machine.get_entryZ (state, klass);
+      if (unlikely (!entry))
+	break;
+
+      /* Unsafe-to-break before this if not in state 0, as things might
+       * go differently if we start from state 0 here.
+       *
+       * Ugh.  The indexing here is ugly... */
+      if (state && buffer->backtrack_len () && buffer->idx < buffer->len)
+      {
+	/* If there's no action and we're just epsilon-transitioning to state 0,
+	 * safe to break. */
+	if (c->is_actionable (this, entry) ||
+	    !(entry->newState == StateTable<EntryData>::STATE_START_OF_TEXT &&
+	      entry->flags == context_t::DontAdvance))
+	  buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
+      }
+
+      /* Unsafe-to-break if end-of-text would kick in here. */
+      if (buffer->idx + 2 <= buffer->len)
+      {
+	const Entry<EntryData> *end_entry = machine.get_entryZ (state, 0);
+	if (c->is_actionable (this, end_entry))
+	  buffer->unsafe_to_break (buffer->idx, buffer->idx + 2);
+      }
+
+      if (unlikely (!c->transition (this, entry)))
+        break;
+
+      last_was_dont_advance = (entry->flags & context_t::DontAdvance) && buffer->max_ops-- > 0;
+
+      state = entry->newState;
+
+      if (buffer->idx == buffer->len)
+        break;
+
+      if (!last_was_dont_advance)
+        buffer->next_glyph ();
+    }
+
+    if (!c->in_place)
+    {
+      for (; buffer->successful && buffer->idx < buffer->len;)
+	buffer->next_glyph ();
+      if (likely (buffer->successful))
+	buffer->swap_buffers ();
+    }
+  }
+
+  public:
+  const StateTable<EntryData> &machine;
+  hb_buffer_t *buffer;
+  unsigned int num_glyphs;
+};
+
+
+struct ankr;
+
+struct hb_aat_apply_context_t :
+       hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
+{
+  inline const char *get_name (void) { return "APPLY"; }
+  template <typename T>
+  inline return_t dispatch (const T &obj) { return obj.apply (this); }
+  static return_t default_return_value (void) { return false; }
+  bool stop_sublookup_iteration (return_t r) const { return r; }
+
+  hb_ot_shape_plan_t *plan;
+  hb_font_t *font;
+  hb_face_t *face;
+  hb_buffer_t *buffer;
+  hb_sanitize_context_t sanitizer;
+  const ankr &ankr_table;
+  const char *ankr_end;
+
+  /* Unused. For debug tracing only. */
+  unsigned int lookup_index;
+  unsigned int debug_depth;
+
+  inline hb_aat_apply_context_t (hb_ot_shape_plan_t *plan_,
+				 hb_font_t *font_,
+				 hb_buffer_t *buffer_,
+				 hb_blob_t *blob = const_cast<hb_blob_t *> (&Null(hb_blob_t)),
+				 const ankr &ankr_table_ = Null(ankr),
+				 const char *ankr_end_ = nullptr) :
+		plan (plan_), font (font_), face (font->face), buffer (buffer_),
+		sanitizer (),
+		ankr_table (ankr_table_), ankr_end (ankr_end_),
+		lookup_index (0), debug_depth (0)
+  {
+    sanitizer.init (blob);
+    sanitizer.set_num_glyphs (face->get_num_glyphs ());
+    sanitizer.start_processing ();
+    sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
+  }
+
+  inline void set_lookup_index (unsigned int i) { lookup_index = i; }
+
+  inline ~hb_aat_apply_context_t (void)
+  {
+    sanitizer.end_processing ();
+  }
+};
+
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_COMMON_HH */
diff --git a/src/hb-aat-layout-feat-table.hh b/src/hb-aat-layout-feat-table.hh
index 3e070d7..b670caa 100644
--- a/src/hb-aat-layout-feat-table.hh
+++ b/src/hb-aat-layout-feat-table.hh
@@ -25,7 +25,7 @@
 #ifndef HB_AAT_LAYOUT_FEAT_TABLE_HH
 #define HB_AAT_LAYOUT_FEAT_TABLE_HH
 
-#include "hb-aat-layout-common-private.hh"
+#include "hb-aat-layout-common.hh"
 
 /*
  * feat -- Feature Name
@@ -78,7 +78,7 @@
   protected:
   HBUINT16	feature;	/* Feature type. */
   HBUINT16	nSettings;	/* The number of records in the setting name array. */
-  LOffsetTo<UnsizedArrayOf<SettingName> >
+  LOffsetTo<UnsizedArrayOf<SettingName>, false>
 		settingTable;	/* Offset in bytes from the beginning of this table to
 				 * this feature's setting name array. The actual type of
 				 * record this offset refers to will depend on the
diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index cc03d62..a883125 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -28,9 +28,10 @@
 #ifndef HB_AAT_LAYOUT_KERX_TABLE_HH
 #define HB_AAT_LAYOUT_KERX_TABLE_HH
 
-#include "hb-open-type-private.hh"
-#include "hb-aat-layout-common-private.hh"
-#include "hb-aat-layout-ankr-table.hh"
+#include "hb-open-type.hh"
+#include "hb-aat-layout-common.hh"
+#include "hb-ot-layout-gpos-table.hh"
+#include "hb-ot-kern-table.hh"
 
 /*
  * kerx -- Extended Kerning
@@ -44,7 +45,22 @@
 using namespace OT;
 
 
-struct KerxFormat0Records
+static inline int
+kerxTupleKern (int value,
+	       unsigned int tupleCount,
+	       const void *base,
+	       hb_aat_apply_context_t *c)
+{
+  if (likely (!tupleCount)) return value;
+
+  unsigned int offset = value;
+  const FWORD *pv = &StructAtOffset<FWORD> (base, offset);
+  if (unlikely (!pv->sanitize (&c->sanitizer))) return 0;
+  return *pv;
+}
+
+
+struct KerxSubTableHeader
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -52,217 +68,589 @@
     return_trace (likely (c->check_struct (this)));
   }
 
-  protected:
-  GlyphID	left;
-  GlyphID	right;
-  FWORD		value;
   public:
-  DEFINE_SIZE_STATIC (6);
+  HBUINT32	length;
+  HBUINT32	coverage;
+  HBUINT32	tupleCount;
+  public:
+  DEFINE_SIZE_STATIC (12);
 };
 
 struct KerxSubTableFormat0
 {
-  // TODO(ebraminio) Enable when we got suitable BinSearchArrayOf
-  // inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
-  // {
-  //   hb_glyph_pair_t pair = {left, right};
-  //   int i = pairs.bsearch (pair);
-  //   if (i == -1)
-  //     return 0;
-  //   return pairs[i].get_kerning ();
-  // }
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
+			  hb_aat_apply_context_t *c) const
+  {
+    hb_glyph_pair_t pair = {left, right};
+    int i = pairs.bsearch (pair);
+    if (i == -1) return 0;
+    int v = pairs[i].get_kerning ();
+    return kerxTupleKern (v, header.tupleCount, this, c);
+  }
+
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    if (!c->plan->requested_kerning)
+      return false;
+
+    accelerator_t accel (*this, c);
+    hb_kern_machine_t<accelerator_t> machine (accel);
+    machine.kern (c->font, c->buffer, c->plan->kern_mask);
+
+    return_trace (true);
+  }
+
+  struct accelerator_t
+  {
+    const KerxSubTableFormat0 &table;
+    hb_aat_apply_context_t *c;
+
+    inline accelerator_t (const KerxSubTableFormat0 &table_,
+			  hb_aat_apply_context_t *c_) :
+			    table (table_), c (c_) {}
+
+    inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+    { return table.get_kerning (left, right, c); }
+  };
+
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
-			  recordsZ.sanitize (c, nPairs)));
+			  pairs.sanitize (c)));
   }
 
   protected:
-  // TODO(ebraminio): A custom version of "BinSearchArrayOf<KerxPair> pairs;" is
-  // needed here to use HBUINT32 instead
-  HBUINT32	nPairs;		/* The number of kerning pairs in this subtable */
-  HBUINT32	searchRange;	/* The largest power of two less than or equal to the value of nPairs,
-				 * multiplied by the size in bytes of an entry in the subtable. */
-  HBUINT32	entrySelector;	/* This is calculated as log2 of the largest power of two less
-				 * than or equal to the value of nPairs. */
-  HBUINT32	rangeShift;	/* The value of nPairs minus the largest power of two less than or equal to nPairs. */
-  UnsizedArrayOf<KerxFormat0Records>
-		recordsZ;	/* VAR=nPairs */
+  KerxSubTableHeader	header;
+  BinSearchArrayOf<KernPair, HBUINT32>
+			pairs;	/* Sorted kern records. */
   public:
-  DEFINE_SIZE_ARRAY (16, recordsZ);
+  DEFINE_SIZE_ARRAY (28, pairs);
 };
 
 struct KerxSubTableFormat1
 {
+  struct EntryData
+  {
+    HBUINT16	kernActionIndex;/* Index into the kerning value array. If
+				 * this index is 0xFFFF, then no kerning
+				 * is to be performed. */
+    public:
+    DEFINE_SIZE_STATIC (2);
+  };
+
+  struct driver_context_t
+  {
+    static const bool in_place = true;
+    enum Flags
+    {
+      Push		= 0x8000,	/* If set, push this glyph on the kerning stack. */
+      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph
+					 * before going to the new state. */
+      Reset		= 0x2000,	/* If set, reset the kerning data (clear the stack) */
+      Reserved		= 0x1FFF,	/* Not used; set to 0. */
+    };
+
+    inline driver_context_t (const KerxSubTableFormat1 *table,
+			     hb_aat_apply_context_t *c_) :
+	c (c_),
+	/* Apparently the offset kernAction is from the beginning of the state-machine,
+	 * similar to offsets in morx table, NOT from beginning of this table, like
+	 * other subtables in kerx.  Discovered via testing. */
+	kernAction (&table->machine + table->kernAction),
+	depth (0) {}
+
+    inline bool is_actionable (StateTableDriver<EntryData> *driver HB_UNUSED,
+			       const Entry<EntryData> *entry)
+    {
+      return entry->data.kernActionIndex != 0xFFFF;
+    }
+    inline bool transition (StateTableDriver<EntryData> *driver,
+			    const Entry<EntryData> *entry)
+    {
+      hb_buffer_t *buffer = driver->buffer;
+      unsigned int flags = entry->flags;
+
+      if (flags & Reset)
+      {
+	depth = 0;
+      }
+
+      if (flags & Push)
+      {
+	if (likely (depth < ARRAY_LENGTH (stack)))
+	  stack[depth++] = buffer->idx;
+	else
+	  depth = 0; /* Probably not what CoreText does, but better? */
+      }
+
+      if (entry->data.kernActionIndex != 0xFFFF)
+      {
+	const FWORD *actions = &kernAction[entry->data.kernActionIndex];
+	if (!c->sanitizer.check_array (actions, depth))
+	{
+	  depth = 0;
+	  return false;
+	}
+
+	hb_mask_t kern_mask = c->plan->kern_mask;
+	for (unsigned int i = 0; i < depth; i++)
+	{
+	  /* Apparently, when spec says "Each pops one glyph from the kerning stack
+	   * and applies the kerning value to it.", it doesn't mean it in that order.
+	   * The deepest item in the stack corresponds to the first item in the action
+	   * list.  Discovered by testing. */
+	  unsigned int idx = stack[i];
+	  int v = *actions++;
+	  if (idx < buffer->len && buffer->info[idx].mask & kern_mask)
+	  {
+	    /* XXX Non-forward direction... */
+	    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
+	      buffer->pos[idx].x_advance += c->font->em_scale_x (v);
+	    else
+	      buffer->pos[idx].y_advance += c->font->em_scale_y (v);
+	  }
+	}
+	depth = 0;
+      }
+
+      return true;
+    }
+
+    private:
+    hb_aat_apply_context_t *c;
+    const UnsizedArrayOf<FWORD> &kernAction;
+    unsigned int stack[8];
+    unsigned int depth;
+  };
+
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    if (!c->plan->requested_kerning)
+      return false;
+
+    if (header.tupleCount)
+      return_trace (false); /* TODO kerxTupleKern */
+
+    driver_context_t dc (this, c);
+
+    StateTableDriver<EntryData> driver (machine, c->buffer, c->font->face);
+    driver.drive (&dc);
+
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
+    /* The rest of array sanitizations are done at run-time. */
     return_trace (likely (c->check_struct (this) &&
-			  stateHeader.sanitize (c)));
+			  machine.sanitize (c)));
   }
 
   protected:
-  StateTable<HBUINT16>		stateHeader;
-  LOffsetTo<ArrayOf<HBUINT16> >	valueTable;
+  KerxSubTableHeader				header;
+  StateTable<EntryData>				machine;
+  LOffsetTo<UnsizedArrayOf<FWORD>, false>	kernAction;
   public:
-  DEFINE_SIZE_STATIC (20);
-};
-
-// TODO(ebraminio): Maybe this can be replaced with Lookup<HBUINT16>?
-struct KerxClassTable
-{
-  inline unsigned int get_class (hb_codepoint_t g) const { return classes[g - firstGlyph]; }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (firstGlyph.sanitize (c) &&
-			  classes.sanitize (c)));
-  }
-
-  protected:
-  HBUINT16		firstGlyph;	/* First glyph in class range. */
-  ArrayOf<HBUINT16>	classes;	/* Glyph classes. */
-  public:
-  DEFINE_SIZE_ARRAY (4, classes);
+  DEFINE_SIZE_STATIC (32);
 };
 
 struct KerxSubTableFormat2
 {
-  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
+			  hb_aat_apply_context_t *c) const
   {
-    unsigned int l = (this+leftClassTable).get_class (left);
-    unsigned int r = (this+leftClassTable).get_class (left);
-    unsigned int offset = l * rowWidth + r * sizeof (FWORD);
-    const FWORD *arr = &(this+array);
-    if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end))
-      return 0;
-    const FWORD *v = &StructAtOffset<FWORD> (arr, offset);
-    if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end))
-      return 0;
-    return *v;
+    unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
+    unsigned int l = (this+leftClassTable).get_value_or_null (left, num_glyphs);
+    unsigned int r = (this+rightClassTable).get_value_or_null (right, num_glyphs);
+    unsigned int offset = l + r;
+    const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
+    if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
+    return kerxTupleKern (*v, header.tupleCount, this, c);
   }
 
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    if (!c->plan->requested_kerning)
+      return false;
+
+    accelerator_t accel (*this, c);
+    hb_kern_machine_t<accelerator_t> machine (accel);
+    machine.kern (c->font, c->buffer, c->plan->kern_mask);
+
+    return_trace (true);
+  }
+
+  struct accelerator_t
+  {
+    const KerxSubTableFormat2 &table;
+    hb_aat_apply_context_t *c;
+
+    inline accelerator_t (const KerxSubTableFormat2 &table_,
+			  hb_aat_apply_context_t *c_) :
+			    table (table_), c (c_) {}
+
+    inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+    { return table.get_kerning (left, right, c); }
+  };
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
-			  rowWidth.sanitize (c) &&
 			  leftClassTable.sanitize (c, this) &&
 			  rightClassTable.sanitize (c, this) &&
-			  array.sanitize (c, this)));
+			  c->check_range (this, array)));
   }
 
   protected:
-  HBUINT32	rowWidth;	/* The width, in bytes, of a row in the table. */
-  LOffsetTo<KerxClassTable>
-		leftClassTable;	/* Offset from beginning of this subtable to
-				 * left-hand class table. */
-  LOffsetTo<KerxClassTable>
-		rightClassTable;/* Offset from beginning of this subtable to
-				 * right-hand class table. */
-  LOffsetTo<FWORD>
-		array;		/* Offset from beginning of this subtable to
-				 * the start of the kerning array. */
+  KerxSubTableHeader	header;
+  HBUINT32		rowWidth;	/* The width, in bytes, of a row in the table. */
+  LOffsetTo<Lookup<HBUINT16>, false>
+			leftClassTable;	/* Offset from beginning of this subtable to
+					 * left-hand class table. */
+  LOffsetTo<Lookup<HBUINT16>, false>
+			rightClassTable;/* Offset from beginning of this subtable to
+					 * right-hand class table. */
+  LOffsetTo<UnsizedArrayOf<FWORD>, false>
+			 array;		/* Offset from beginning of this subtable to
+					 * the start of the kerning array. */
   public:
-  DEFINE_SIZE_STATIC (16);
+  DEFINE_SIZE_STATIC (28);
 };
 
 struct KerxSubTableFormat4
 {
+  struct EntryData
+  {
+    HBUINT16	ankrActionIndex;/* Either 0xFFFF (for no action) or the index of
+				 * the action to perform. */
+    public:
+    DEFINE_SIZE_STATIC (2);
+  };
+
+  struct driver_context_t
+  {
+    static const bool in_place = true;
+    enum Flags
+    {
+      Mark		= 0x8000,	/* If set, remember this glyph as the marked glyph. */
+      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
+					 * going to the new state. */
+      Reserved		= 0x3FFF,	/* Not used; set to 0. */
+    };
+
+    enum SubTableFlags
+    {
+      ActionType	= 0xC0000000,	/* A two-bit field containing the action type. */
+      Unused		= 0x3F000000,	/* Unused - must be zero. */
+      Offset		= 0x00FFFFFF,	/* Masks the offset in bytes from the beginning
+					 * of the subtable to the beginning of the control
+					 * point table. */
+    };
+
+    inline driver_context_t (const KerxSubTableFormat4 *table,
+			     hb_aat_apply_context_t *c_) :
+	c (c_),
+	action_type ((table->flags & ActionType) >> 30),
+	ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
+	mark_set (false),
+	mark (0) {}
+
+    inline bool is_actionable (StateTableDriver<EntryData> *driver HB_UNUSED,
+			       const Entry<EntryData> *entry)
+    {
+      return entry->data.ankrActionIndex != 0xFFFF;
+    }
+    inline bool transition (StateTableDriver<EntryData> *driver,
+			    const Entry<EntryData> *entry)
+    {
+      hb_buffer_t *buffer = driver->buffer;
+      unsigned int flags = entry->flags;
+
+      if (mark_set && entry->data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
+      {
+	hb_glyph_position_t &o = buffer->cur_pos();
+	switch (action_type)
+	{
+	  case 0: /* Control Point Actions.*/
+	  {
+	    /* indexed into glyph outline. */
+	    const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
+	    if (!c->sanitizer.check_array (data, 2))
+	      return false;
+	    HB_UNUSED unsigned int markControlPoint = *data++;
+	    HB_UNUSED unsigned int currControlPoint = *data++;
+	    hb_position_t markX = 0;
+	    hb_position_t markY = 0;
+	    hb_position_t currX = 0;
+	    hb_position_t currY = 0;
+	    if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint,
+							      markControlPoint,
+							      HB_DIRECTION_LTR /*XXX*/,
+							      &markX, &markY) ||
+		!c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint,
+							      currControlPoint,
+							      HB_DIRECTION_LTR /*XXX*/,
+							      &currX, &currY))
+	      return true; /* True, such that the machine continues. */
+
+	    o.x_offset = markX - currX;
+	    o.y_offset = markY - currY;
+	  }
+	  break;
+
+	  case 1: /* Anchor Point Actions. */
+	  {
+	   /* Indexed into 'ankr' table. */
+	    const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
+	    if (!c->sanitizer.check_array (data, 2))
+	      return false;
+	    unsigned int markAnchorPoint = *data++;
+	    unsigned int currAnchorPoint = *data++;
+	    const Anchor markAnchor = c->ankr_table.get_anchor (c->buffer->info[mark].codepoint,
+								markAnchorPoint,
+								c->sanitizer.get_num_glyphs (),
+								c->ankr_end);
+	    const Anchor currAnchor = c->ankr_table.get_anchor (c->buffer->cur ().codepoint,
+								currAnchorPoint,
+								c->sanitizer.get_num_glyphs (),
+								c->ankr_end);
+
+	    o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate);
+	    o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate);
+	  }
+	  break;
+
+	  case 2: /* Control Point Coordinate Actions. */
+	  {
+	    const FWORD *data = (const FWORD *) &ankrData[entry->data.ankrActionIndex];
+	    if (!c->sanitizer.check_array (data, 4))
+	      return false;
+	    int markX = *data++;
+	    int markY = *data++;
+	    int currX = *data++;
+	    int currY = *data++;
+
+	    o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX);
+	    o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY);
+	  }
+	  break;
+	}
+	o.attach_type() = ATTACH_TYPE_MARK;
+	o.attach_chain() = (int) mark - (int) buffer->idx;
+	buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+      }
+
+      if (flags & Mark)
+      {
+	mark_set = true;
+	mark = buffer->idx;
+      }
+
+      return true;
+    }
+
+    private:
+    hb_aat_apply_context_t *c;
+    unsigned int action_type;
+    const HBUINT16 *ankrData;
+    bool mark_set;
+    unsigned int mark;
+  };
+
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    driver_context_t dc (this, c);
+
+    StateTableDriver<EntryData> driver (machine, c->buffer, c->font->face);
+    driver.drive (&dc);
+
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
+    /* The rest of array sanitizations are done at run-time. */
     return_trace (likely (c->check_struct (this) &&
-			  rowWidth.sanitize (c) &&
-			  leftClassTable.sanitize (c, this) &&
-			  rightClassTable.sanitize (c, this) &&
-			  array.sanitize (c, this)));
+			  machine.sanitize (c)));
   }
 
   protected:
-  HBUINT32	rowWidth;	/* The width, in bytes, of a row in the table. */
-  LOffsetTo<KerxClassTable>
-		leftClassTable;	/* Offset from beginning of this subtable to
-				 * left-hand class table. */
-  LOffsetTo<KerxClassTable>
-		rightClassTable;/* Offset from beginning of this subtable to
-				 * right-hand class table. */
-  LOffsetTo<FWORD>
-		array;		/* Offset from beginning of this subtable to
-				 * the start of the kerning array. */
+  KerxSubTableHeader	header;
+  StateTable<EntryData>	machine;
+  HBUINT32		flags;
   public:
-  DEFINE_SIZE_STATIC (16);
+  DEFINE_SIZE_STATIC (32);
 };
 
 struct KerxSubTableFormat6
 {
+  enum Flags
+  {
+    ValuesAreLong	= 0x00000001,
+  };
+
+  inline bool is_long (void) const { return flags & ValuesAreLong; }
+
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
+			  hb_aat_apply_context_t *c) const
+  {
+    unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
+    if (is_long ())
+    {
+      const U::Long &t = u.l;
+      unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
+      unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
+      unsigned int offset = l + r;
+      if (unlikely (offset < l)) return 0; /* Addition overflow. */
+      if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0;
+      const FWORD32 *v = &StructAtOffset<FWORD32> (&(this+t.array), offset * sizeof (FWORD32));
+      if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
+      return kerxTupleKern (*v, header.tupleCount, &(this+vector), c);
+    }
+    else
+    {
+      const U::Short &t = u.s;
+      unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
+      unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
+      unsigned int offset = l + r;
+      const FWORD *v = &StructAtOffset<FWORD> (&(this+t.array), offset * sizeof (FWORD));
+      if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
+      return kerxTupleKern (*v, header.tupleCount, &(this+vector), c);
+    }
+  }
+
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    if (!c->plan->requested_kerning)
+      return false;
+
+    accelerator_t accel (*this, c);
+    hb_kern_machine_t<accelerator_t> machine (accel);
+    machine.kern (c->font, c->buffer, c->plan->kern_mask);
+
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
-			  rowIndexTable.sanitize (c, this) &&
-			  columnIndexTable.sanitize (c, this) &&
-			  kerningArray.sanitize (c, this) &&
-			  kerningVector.sanitize (c, this)));
+			  (is_long () ?
+			   (
+			     u.l.rowIndexTable.sanitize (c, this) &&
+			     u.l.columnIndexTable.sanitize (c, this) &&
+			     c->check_range (this, u.l.array)
+			   ) : (
+			     u.s.rowIndexTable.sanitize (c, this) &&
+			     u.s.columnIndexTable.sanitize (c, this) &&
+			     c->check_range (this, u.s.array)
+			   )) &&
+			  (header.tupleCount == 0 ||
+			   c->check_range (this, vector))));
   }
 
-  protected:
-  HBUINT32	flags;
-  HBUINT16	rowCount;
-  HBUINT16	columnCount;
-  LOffsetTo<Lookup<HBUINT16> >	rowIndexTable;
-  LOffsetTo<Lookup<HBUINT16> >	columnIndexTable;
-  LOffsetTo<Lookup<HBUINT16> >	kerningArray;
-  LOffsetTo<Lookup<HBUINT16> >	kerningVector;
-  public:
-  DEFINE_SIZE_STATIC (24);
-};
+  struct accelerator_t
+  {
+    const KerxSubTableFormat6 &table;
+    hb_aat_apply_context_t *c;
 
-enum coverage_flags_t
-{
-  COVERAGE_VERTICAL_FLAG	= 0x80u,
-  COVERAGE_CROSSSTREAM_FLAG	= 0x40u,
-  COVERAGE_VARIATION_FLAG	= 0x20u,
-  COVERAGE_PROCESS_DIRECTION	= 0x10u,
+    inline accelerator_t (const KerxSubTableFormat6 &table_,
+			  hb_aat_apply_context_t *c_) :
+			    table (table_), c (c_) {}
+
+    inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+    { return table.get_kerning (left, right, c); }
+  };
+
+  protected:
+  KerxSubTableHeader		header;
+  HBUINT32			flags;
+  HBUINT16			rowCount;
+  HBUINT16			columnCount;
+  union U
+  {
+    struct Long
+    {
+      LOffsetTo<Lookup<HBUINT32>, false>	rowIndexTable;
+      LOffsetTo<Lookup<HBUINT32>, false>	columnIndexTable;
+      LOffsetTo<UnsizedArrayOf<FWORD32>, false>	array;
+    } l;
+    struct Short
+    {
+      LOffsetTo<Lookup<HBUINT16>, false>	rowIndexTable;
+      LOffsetTo<Lookup<HBUINT16>, false>	columnIndexTable;
+      LOffsetTo<UnsizedArrayOf<FWORD>, false>	array;
+    } s;
+  } u;
+  LOffsetTo<UnsizedArrayOf<FWORD>, false>	vector;
+  public:
+  DEFINE_SIZE_STATIC (36);
 };
 
 struct KerxTable
 {
-  inline bool apply (hb_aat_apply_context_t *c, const AAT::ankr *ankr) const
-  {
-    TRACE_APPLY (this);
-    /* TODO */
-    return_trace (false);
-  }
+  friend struct kerx;
 
-  inline unsigned int get_size (void) const { return length; }
+  inline unsigned int get_size (void) const { return u.header.length; }
+  inline unsigned int get_type (void) const { return u.header.coverage & SubtableType; }
+
+  enum Coverage
+  {
+    Vertical		= 0x80000000,	/* Set if table has vertical kerning values. */
+    CrossStream		= 0x40000000,	/* Set if table has cross-stream kerning values. */
+    Variation		= 0x20000000,	/* Set if table has variation kerning values. */
+    Backwards		= 0x10000000,	/* If clear, process the glyphs forwards, that
+					 * is, from first to last in the glyph stream.
+					 * If we, process them from last to first.
+					 * This flag only applies to state-table based
+					 * 'kerx' subtables (types 1 and 4). */
+    Reserved		= 0x0FFFFF00,	/* Reserved, set to zero. */
+    SubtableType	= 0x000000FF,	/* Subtable type. */
+  };
+
+  template <typename context_t>
+  inline typename context_t::return_t dispatch (context_t *c) const
+  {
+    unsigned int subtable_type = get_type ();
+    TRACE_DISPATCH (this, subtable_type);
+    switch (subtable_type) {
+    case 0	:		return_trace (c->dispatch (u.format0));
+    case 1	:		return_trace (c->dispatch (u.format1));
+    case 2	:		return_trace (c->dispatch (u.format2));
+    case 4	:		return_trace (c->dispatch (u.format4));
+    case 6	:		return_trace (c->dispatch (u.format6));
+    default:			return_trace (c->default_return_value ());
+    }
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (this)))
+    if (!u.header.sanitize (c) ||
+	!c->check_range (this, u.header.length))
       return_trace (false);
 
-    switch (format) {
-    case 0: return u.format0.sanitize (c);
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    case 4: return u.format4.sanitize (c);
-    case 6: return u.format6.sanitize (c);
-    default:return_trace (false);
-    }
+    return_trace (dispatch (c));
   }
 
 protected:
-  HBUINT32	length;
-  HBUINT8	coverage;
-  HBUINT16	unused;
-  HBUINT8	format;
-  HBUINT32	tupleIndex;
   union {
+  KerxSubTableHeader	header;
   KerxSubTableFormat0	format0;
   KerxSubTableFormat1	format1;
   KerxSubTableFormat2	format2;
@@ -273,71 +661,90 @@
   DEFINE_SIZE_MIN (12);
 };
 
-struct SubtableGlyphCoverageArray
-{
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
-  }
 
-  protected:
-  HBUINT32	length;
-  HBUINT32	coverage;
-  HBUINT32	tupleCount;
-  public:
-  DEFINE_SIZE_STATIC (12);
-};
+/*
+ * The 'kerx' Table
+ */
 
 struct kerx
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
 
-  inline bool apply (hb_aat_apply_context_t *c, const AAT::ankr *ankr) const
+  inline bool has_data (void) const { return version != 0; }
+
+  inline void apply (hb_aat_apply_context_t *c) const
   {
-    TRACE_APPLY (this);
-    const KerxTable &table = StructAfter<KerxTable> (*this);
-    return_trace (table.apply (c, ankr));
+    c->set_lookup_index (0);
+    const KerxTable *table = &firstTable;
+    unsigned int count = tableCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      bool reverse;
+
+      if (table->u.header.coverage & (KerxTable::CrossStream))
+	goto skip; /* We do NOT handle cross-stream. */
+
+      if (HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
+	  bool (table->u.header.coverage & KerxTable::Vertical))
+	goto skip;
+
+      reverse = bool (table->u.header.coverage & KerxTable::Backwards) !=
+		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
+
+      if (!c->buffer->message (c->font, "start kerx subtable %d", c->lookup_index))
+	goto skip;
+
+      if (reverse)
+	c->buffer->reverse ();
+
+      c->sanitizer.set_object (*table);
+
+      /* XXX Reverse-kern is not working yet...
+       * hb_kern_machine_t would need to know that it's reverse-kerning.
+       * Or better yet, make it work in reverse as well, so we don't have
+       * to reverse and reverse back? */
+      table->dispatch (c);
+
+      if (reverse)
+	c->buffer->reverse ();
+
+      (void) c->buffer->message (c->font, "end kerx subtable %d", c->lookup_index);
+
+    skip:
+      table = &StructAfter<KerxTable> (*table);
+    }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!(c->check_struct (this))))
-     return_trace (false);
-
-    /* TODO: Something like `morx`s ChainSubtable should be done here instead */
-    const KerxTable *table = &StructAfter<KerxTable> (*this);
-    if (unlikely (!(table->sanitize (c))))
+    if (!version.sanitize (c) || version < 2 ||
+	!tableCount.sanitize (c))
       return_trace (false);
 
-    for (unsigned int i = 0; i < nTables - 1; ++i)
+    const KerxTable *table = &firstTable;
+    unsigned int count = tableCount;
+    for (unsigned int i = 0; i < count; i++)
     {
+      if (!table->sanitize (c))
+	return_trace (false);
       table = &StructAfter<KerxTable> (*table);
-      if (unlikely (!(table->sanitize (c))))
-        return_trace (false);
     }
 
-    // If version is less than 3, we are done here; otherwise better to check footer also
-    if (version < 3)
-      return_trace (true);
-
-    // TODO: Investigate why this just work on some fonts no matter of version
-    // const SubtableGlyphCoverageArray &footer =
-    //   StructAfter<SubtableGlyphCoverageArray> (*table);
-    // return_trace (footer.sanitize (c));
-
     return_trace (true);
   }
 
   protected:
-  HBUINT16		version;
-  HBUINT16		padding;
-  HBUINT32		nTables;
-/*KerxTable tablesZ[VAR]; XXX ArrayOf??? */
-/*SubtableGlyphCoverageArray coverage_array;*/
+  HBUINT16	version;	/* The version number of the extended kerning table
+				 * (currently 2, 3, or 4). */
+  HBUINT16	unused;		/* Set to 0. */
+  HBUINT32	tableCount;	/* The number of subtables included in the extended kerning
+				 * table. */
+  KerxTable	firstTable;	/* Subtables. */
+/*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */
+
   public:
-  DEFINE_SIZE_STATIC (8);
+  DEFINE_SIZE_MIN (8);
 };
 
 } /* namespace AAT */
diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
index f258424..a562091 100644
--- a/src/hb-aat-layout-morx-table.hh
+++ b/src/hb-aat-layout-morx-table.hh
@@ -27,9 +27,10 @@
 #ifndef HB_AAT_LAYOUT_MORX_TABLE_HH
 #define HB_AAT_LAYOUT_MORX_TABLE_HH
 
-#include "hb-open-type-private.hh"
-#include "hb-aat-layout-common-private.hh"
-#include "hb-ot-layout-common-private.hh"
+#include "hb-open-type.hh"
+#include "hb-aat-layout-common.hh"
+#include "hb-ot-layout-common.hh"
+#include "hb-aat-map.hh"
 
 /*
  * morx -- Extended Glyph Metamorphosis
@@ -50,7 +51,8 @@
   struct driver_context_t
   {
     static const bool in_place = true;
-    enum Flags {
+    enum Flags
+    {
       MarkFirst		= 0x8000,	/* If set, make the current glyph the first
 					 * glyph to be rearranged. */
       DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph
@@ -63,11 +65,11 @@
       Verb		= 0x000F,	/* The type of rearrangement specified. */
     };
 
-    inline driver_context_t (const RearrangementSubtable *table) :
+    inline driver_context_t (const RearrangementSubtable *table HB_UNUSED) :
 	ret (false),
 	start (0), end (0) {}
 
-    inline bool is_actionable (StateTableDriver<EntryData> *driver,
+    inline bool is_actionable (StateTableDriver<EntryData> *driver HB_UNUSED,
 			       const Entry<EntryData> *entry)
     {
       return (entry->flags & Verb) && start < end;
@@ -163,7 +165,7 @@
 
     driver_context_t dc (this);
 
-    StateTableDriver<void> driver (machine, c->buffer, c->face);
+    StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
     driver.drive (&dc);
 
     return_trace (dc.ret);
@@ -196,7 +198,8 @@
   struct driver_context_t
   {
     static const bool in_place = true;
-    enum Flags {
+    enum Flags
+    {
       SetMark		= 0x8000,	/* If set, make the current glyph the marked glyph. */
       DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
 					 * going to the new state. */
@@ -268,7 +271,7 @@
     private:
     bool mark_set;
     unsigned int mark;
-    const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> &subs;
+    const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32, false> &subs;
   };
 
   inline bool apply (hb_aat_apply_context_t *c) const
@@ -309,7 +312,7 @@
   protected:
   StateTable<EntryData>
 		machine;
-  LOffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> >
+  LOffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32, false>, false>
 		substitutionTables;
   public:
   DEFINE_SIZE_STATIC (20);
@@ -329,7 +332,8 @@
   struct driver_context_t
   {
     static const bool in_place = false;
-    enum Flags {
+    enum Flags
+    {
       SetComponent	= 0x8000,	/* Push this glyph onto the component stack for
 					 * eventual processing. */
       DontAdvance	= 0x4000,	/* Leave the glyph pointer at this glyph for the
@@ -338,7 +342,8 @@
 					 * group. */
       Reserved		= 0x1FFF,	/* These bits are reserved and should be set to 0. */
     };
-    enum LigActionFlags {
+    enum LigActionFlags
+    {
       LigActionLast	= 0x80000000,	/* This is the last action in the list. This also
 					 * implies storage. */
       LigActionStore	= 0x40000000,	/* Store the ligature at the current cumulated index
@@ -358,10 +363,10 @@
 	ligature (table+table->ligature),
 	match_length (0) {}
 
-    inline bool is_actionable (StateTableDriver<EntryData> *driver,
+    inline bool is_actionable (StateTableDriver<EntryData> *driver HB_UNUSED,
 			       const Entry<EntryData> *entry)
     {
-      return !!(entry->flags & PerformAction);
+      return entry->flags & PerformAction;
     }
     inline bool transition (StateTableDriver<EntryData> *driver,
 			    const Entry<EntryData> *entry)
@@ -369,6 +374,7 @@
       hb_buffer_t *buffer = driver->buffer;
       unsigned int flags = entry->flags;
 
+      DEBUG_MSG (APPLY, nullptr, "Ligature transition at %d", buffer->idx);
       if (flags & SetComponent)
       {
         if (unlikely (match_length >= ARRAY_LENGTH (match_positions)))
@@ -379,20 +385,36 @@
 	  match_length--;
 
 	match_positions[match_length++] = buffer->out_len;
+	DEBUG_MSG (APPLY, nullptr, "Set component at %d", buffer->out_len);
       }
 
       if (flags & PerformAction)
       {
+	DEBUG_MSG (APPLY, nullptr, "Perform action with %d", match_length);
 	unsigned int end = buffer->out_len;
 	unsigned int action_idx = entry->data.ligActionIndex;
 	unsigned int action;
 	unsigned int ligature_idx = 0;
+
+	if (unlikely (!match_length))
+	  return true;
+
+	if (buffer->idx >= buffer->len)
+	  return false; // TODO Work on previous instead?
+
+	unsigned int cursor = match_length;
         do
 	{
-	  if (unlikely (!match_length))
-	    return false;
+	  if (unlikely (!cursor))
+	  {
+	    /* Stack underflow.  Clear the stack. */
+	    DEBUG_MSG (APPLY, nullptr, "Stack underflow");
+	    match_length = 0;
+	    break;
+	  }
 
-	  buffer->move_to (match_positions[--match_length]);
+	  DEBUG_MSG (APPLY, nullptr, "Moving to stack position %d", cursor - 1);
+	  buffer->move_to (match_positions[--cursor]);
 
 	  const HBUINT32 &actionData = ligAction[action_idx];
 	  if (unlikely (!actionData.sanitize (&c->sanitizer))) return false;
@@ -400,7 +422,7 @@
 
 	  uint32_t uoffset = action & LigActionOffset;
 	  if (uoffset & 0x20000000)
-	    uoffset += 0xC0000000;
+	    uoffset |= 0xC0000000; /* Sign-extend. */
 	  int32_t offset = (int32_t) uoffset;
 	  unsigned int component_idx = buffer->cur().codepoint + offset;
 
@@ -408,23 +430,31 @@
 	  if (unlikely (!componentData.sanitize (&c->sanitizer))) return false;
 	  ligature_idx += componentData;
 
+	  DEBUG_MSG (APPLY, nullptr, "Action store %d last %d",
+		     bool (action & LigActionStore),
+		     bool (action & LigActionLast));
 	  if (action & (LigActionStore | LigActionLast))
 	  {
+
 	    const GlyphID &ligatureData = ligature[ligature_idx];
 	    if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false;
 	    hb_codepoint_t lig = ligatureData;
 
-	    match_positions[match_length++] = buffer->out_len;
+	    DEBUG_MSG (APPLY, nullptr, "Produced ligature %d", lig);
 	    buffer->replace_glyph (lig);
 
-	    //ligature_idx = 0; // XXX Yes or no?
+	    unsigned int lig_end = match_positions[match_length - 1] + 1;
+	    /* Now go and delete all subsequent components. */
+	    while (match_length - 1 > cursor)
+	    {
+	      DEBUG_MSG (APPLY, nullptr, "Skipping ligature component");
+	      buffer->move_to (match_positions[--match_length]);
+	      buffer->replace_glyph (DELETED_GLYPH);
+	    }
+
+	    buffer->move_to (lig_end);
+	    buffer->merge_out_clusters (match_positions[cursor], buffer->out_len);
 	  }
-	  else
-	  {
-	    buffer->skip_glyph ();
-	    end--;
-	  }
-	  /* TODO merge_clusters / unsafe_to_break */
 
 	  action_idx++;
 	}
@@ -469,11 +499,11 @@
   protected:
   StateTable<EntryData>
 		machine;
-  LOffsetTo<UnsizedArrayOf<HBUINT32> >
+  LOffsetTo<UnsizedArrayOf<HBUINT32>, false>
 		ligAction;	/* Offset to the ligature action table. */
-  LOffsetTo<UnsizedArrayOf<HBUINT16> >
+  LOffsetTo<UnsizedArrayOf<HBUINT16>, false>
 		component;	/* Offset to the component table. */
-  LOffsetTo<UnsizedArrayOf<GlyphID> >
+  LOffsetTo<UnsizedArrayOf<GlyphID>, false>
 		ligature;	/* Offset to the actual ligature lists. */
   public:
   DEFINE_SIZE_STATIC (28);
@@ -517,19 +547,201 @@
 
 struct InsertionSubtable
 {
+  struct EntryData
+  {
+    HBUINT16	currentInsertIndex;	/* Zero-based index into the insertion glyph table.
+					 * The number of glyphs to be inserted is contained
+					 * in the currentInsertCount field in the flags.
+					 * A value of 0xFFFF indicates no insertion is to
+					 * be done. */
+    HBUINT16	markedInsertIndex;	/* Zero-based index into the insertion glyph table.
+					 * The number of glyphs to be inserted is contained
+					 * in the markedInsertCount field in the flags.
+					 * A value of 0xFFFF indicates no insertion is to
+					 * be done. */
+    public:
+    DEFINE_SIZE_STATIC (4);
+  };
+
+  struct driver_context_t
+  {
+    static const bool in_place = false;
+    enum Flags
+    {
+      SetMark		= 0x8000,	/* If set, mark the current glyph. */
+      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
+					 * going to the new state.  This does not mean
+					 * that the glyph pointed to is the same one as
+					 * before. If you've made insertions immediately
+					 * downstream of the current glyph, the next glyph
+					 * processed would in fact be the first one
+					 * inserted. */
+      CurrentIsKashidaLike= 0x2000,	/* If set, and the currentInsertList is nonzero,
+					 * then the specified glyph list will be inserted
+					 * as a kashida-like insertion, either before or
+					 * after the current glyph (depending on the state
+					 * of the currentInsertBefore flag). If clear, and
+					 * the currentInsertList is nonzero, then the
+					 * specified glyph list will be inserted as a
+					 * split-vowel-like insertion, either before or
+					 * after the current glyph (depending on the state
+					 * of the currentInsertBefore flag). */
+      MarkedIsKashidaLike= 0x1000,	/* If set, and the markedInsertList is nonzero,
+					 * then the specified glyph list will be inserted
+					 * as a kashida-like insertion, either before or
+					 * after the marked glyph (depending on the state
+					 * of the markedInsertBefore flag). If clear, and
+					 * the markedInsertList is nonzero, then the
+					 * specified glyph list will be inserted as a
+					 * split-vowel-like insertion, either before or
+					 * after the marked glyph (depending on the state
+					 * of the markedInsertBefore flag). */
+      CurrentInsertBefore= 0x0800,	/* If set, specifies that insertions are to be made
+					 * to the left of the current glyph. If clear,
+					 * they're made to the right of the current glyph. */
+      MarkedInsertBefore= 0x0400,	/* If set, specifies that insertions are to be
+					 * made to the left of the marked glyph. If clear,
+					 * they're made to the right of the marked glyph. */
+      CurrentInsertCount= 0x3E0,	/* This 5-bit field is treated as a count of the
+					 * number of glyphs to insert at the current
+					 * position. Since zero means no insertions, the
+					 * largest number of insertions at any given
+					 * current location is 31 glyphs. */
+      MarkedInsertCount= 0x001F,	/* This 5-bit field is treated as a count of the
+					 * number of glyphs to insert at the marked
+					 * position. Since zero means no insertions, the
+					 * largest number of insertions at any given
+					 * marked location is 31 glyphs. */
+    };
+
+    inline driver_context_t (const InsertionSubtable *table,
+			     hb_aat_apply_context_t *c_) :
+	ret (false),
+	c (c_),
+	mark_set (false),
+	mark (0),
+	insertionAction (table+table->insertionAction) {}
+
+    inline bool is_actionable (StateTableDriver<EntryData> *driver HB_UNUSED,
+			       const Entry<EntryData> *entry)
+    {
+      return (entry->flags & (CurrentInsertCount | MarkedInsertCount)) &&
+	     (entry->data.currentInsertIndex != 0xFFFF ||entry->data.markedInsertIndex != 0xFFFF);
+    }
+    inline bool transition (StateTableDriver<EntryData> *driver,
+			    const Entry<EntryData> *entry)
+    {
+      hb_buffer_t *buffer = driver->buffer;
+      unsigned int flags = entry->flags;
+
+      if (entry->data.markedInsertIndex != 0xFFFF && mark_set)
+      {
+	unsigned int count = (flags & MarkedInsertCount);
+	unsigned int start = entry->data.markedInsertIndex;
+	const GlyphID *glyphs = &insertionAction[start];
+	if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false;
+
+	bool before = flags & MarkedInsertBefore;
+
+	unsigned int end = buffer->out_len;
+	buffer->move_to (mark);
+
+	if (buffer->idx < buffer->len && !before)
+	  buffer->copy_glyph ();
+	/* TODO We ignore KashidaLike setting. */
+	for (unsigned int i = 0; i < count; i++)
+	  buffer->output_glyph (glyphs[i]);
+	if (buffer->idx < buffer->len && !before)
+	  buffer->skip_glyph ();
+
+	buffer->move_to (end + count);
+
+	buffer->unsafe_to_break_from_outbuffer (mark, MIN (buffer->idx + 1, buffer->len));
+      }
+
+      if (entry->data.currentInsertIndex != 0xFFFF)
+      {
+	unsigned int count = (flags & CurrentInsertCount) >> 5;
+	unsigned int start = entry->data.currentInsertIndex;
+	const GlyphID *glyphs = &insertionAction[start];
+	if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false;
+
+	bool before = flags & CurrentInsertBefore;
+
+	unsigned int end = buffer->out_len;
+
+	if (buffer->idx < buffer->len && !before)
+	  buffer->copy_glyph ();
+	/* TODO We ignore KashidaLike setting. */
+	for (unsigned int i = 0; i < count; i++)
+	  buffer->output_glyph (glyphs[i]);
+	if (buffer->idx < buffer->len && !before)
+	  buffer->skip_glyph ();
+
+	/* Humm. Not sure where to move to.  There's this wording under
+	 * DontAdvance flag:
+	 *
+	 * "If set, don't update the glyph index before going to the new state.
+	 * This does not mean that the glyph pointed to is the same one as
+	 * before. If you've made insertions immediately downstream of the
+	 * current glyph, the next glyph processed would in fact be the first
+	 * one inserted."
+	 *
+	 * This suggests that if DontAdvance is NOT set, we should move to
+	 * end+count.  If it *was*, then move to end, such that newly inserted
+	 * glyphs are now visible.
+	 *
+	 * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417
+	 */
+	buffer->move_to ((flags & DontAdvance) ? end : end + count);
+      }
+
+      if (flags & SetMark)
+      {
+	mark_set = true;
+	mark = buffer->out_len;
+      }
+
+      return true;
+    }
+
+    public:
+    bool ret;
+    private:
+    hb_aat_apply_context_t *c;
+    bool mark_set;
+    unsigned int mark;
+    const UnsizedArrayOf<GlyphID> &insertionAction;
+  };
+
   inline bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    /* TODO */
-    return_trace (false);
+
+    driver_context_t dc (this, c);
+
+    StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
+    driver.drive (&dc);
+
+    return_trace (dc.ret);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    /* TODO */
-    return_trace (true);
+    /* The rest of array sanitizations are done at run-time. */
+    return_trace (c->check_struct (this) && machine.sanitize (c) &&
+		  insertionAction);
   }
+
+  protected:
+  StateTable<EntryData>
+		machine;
+  LOffsetTo<UnsizedArrayOf<GlyphID>, false>
+		insertionAction;	/* Byte offset from stateHeader to the start of
+					 * the insertion glyph table. */
+  public:
+  DEFINE_SIZE_STATIC (20);
 };
 
 
@@ -559,9 +771,27 @@
   friend struct Chain;
 
   inline unsigned int get_size (void) const { return length; }
-  inline unsigned int get_type (void) const { return coverage & 0xFF; }
+  inline unsigned int get_type (void) const { return coverage & SubtableType; }
 
-  enum Type {
+  enum Coverage
+  {
+    Vertical		= 0x80000000,	/* If set, this subtable will only be applied
+					 * to vertical text. If clear, this subtable
+					 * will only be applied to horizontal text. */
+    Backwards		= 0x40000000,	/* If set, this subtable will process glyphs
+					 * in descending order. If clear, it will
+					 * process the glyphs in ascending order. */
+    AllDirections	= 0x20000000,	/* If set, this subtable will be applied to
+					 * both horizontal and vertical text (i.e.
+					 * the state of bit 0x80000000 is ignored). */
+    Logical		= 0x10000000,	/* If set, this subtable will process glyphs
+					 * in logical order (or reverse logical order,
+					 * depending on the value of bit 0x80000000). */
+    Reserved		= 0x0FFFFF00,	/* Reserved, set to zero. */
+    SubtableType	= 0x000000FF,	/* Subtable type; see following table. */
+  };
+  enum Type
+  {
     Rearrangement	= 0,
     Contextual		= 1,
     Ligature		= 2,
@@ -569,11 +799,6 @@
     Insertion		= 5
   };
 
-  inline void apply (hb_aat_apply_context_t *c) const
-  {
-    dispatch (c);
-  }
-
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
@@ -617,30 +842,104 @@
 
 struct Chain
 {
-  inline void apply (hb_aat_apply_context_t *c) const
+  inline hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const
   {
-    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
+    hb_mask_t flags = defaultFlags;
+    {
+      /* Compute applicable flags.  TODO Should move this to planning
+       * stage and take user-requested features into account. */
+      unsigned int count = featureCount;
+      for (unsigned i = 0; i < count; i++)
+      {
+        const Feature &feature = featureZ[i];
+        uint16_t type = feature.featureType;
+	uint16_t setting = feature.featureSetting;
+	const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch (type);
+	if (info && info->setting == setting)
+	{
+	  flags &= feature.disableFlags;
+	  flags |= feature.enableFlags;
+	}
+      }
+    }
+    return flags;
+  }
+
+  inline void apply (hb_aat_apply_context_t *c,
+		     hb_mask_t flags) const
+  {
+    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (&featureZ, featureZ[0].static_size * featureCount);
     unsigned int count = subtableCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
-      {
-	c->set_lookup_index (c->lookup_index + 1);
-	continue;
-      }
+      bool reverse;
 
-      subtable->apply (c);
-      subtable = &StructAfter<ChainSubtable> (*subtable);
+      if (!(subtable->subFeatureFlags & flags))
+        goto skip;
+
+      if (!(subtable->coverage & ChainSubtable::AllDirections) &&
+	  HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
+	  bool (subtable->coverage & ChainSubtable::Vertical))
+        goto skip;
+
+      /* Buffer contents is always in logical direction.  Determine if
+       * we need to reverse before applying this subtable.  We reverse
+       * back after if we did reverse indeed.
+       *
+       * Quoting the spac:
+       * """
+       * Bits 28 and 30 of the coverage field control the order in which
+       * glyphs are processed when the subtable is run by the layout engine.
+       * Bit 28 is used to indicate if the glyph processing direction is
+       * the same as logical order or layout order. Bit 30 is used to
+       * indicate whether glyphs are processed forwards or backwards within
+       * that order.
+
+		Bit 30	Bit 28	Interpretation for Horizontal Text
+		0	0	The subtable is processed in layout order
+				(the same order as the glyphs, which is
+				always left-to-right).
+		1	0	The subtable is processed in reverse layout order
+				(the order opposite that of the glyphs, which is
+				always right-to-left).
+		0	1	The subtable is processed in logical order
+				(the same order as the characters, which may be
+				left-to-right or right-to-left).
+		1	1	The subtable is processed in reverse logical order
+				(the order opposite that of the characters, which
+				may be right-to-left or left-to-right).
+       */
+      reverse = subtable->coverage & ChainSubtable::Logical ?
+		bool (subtable->coverage & ChainSubtable::Backwards) :
+		bool (subtable->coverage & ChainSubtable::Backwards) !=
+		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
+
+      if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
+        goto skip;
+
+      if (reverse)
+        c->buffer->reverse ();
+
+      c->sanitizer.set_object (*subtable);
+
+      subtable->dispatch (c);
+
+      if (reverse)
+        c->buffer->reverse ();
 
       (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);
 
+      if (unlikely (!c->buffer->successful)) return;
+
+    skip:
+      subtable = &StructAfter<ChainSubtable> (*subtable);
       c->set_lookup_index (c->lookup_index + 1);
     }
   }
 
   inline unsigned int get_size (void) const { return length; }
 
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int major) const
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const
   {
     TRACE_SANITIZE (this);
     if (!length.sanitize (c) ||
@@ -648,10 +947,10 @@
 	!c->check_range (this, length))
       return_trace (false);
 
-    if (!c->check_array (featureZ, featureZ[0].static_size, featureCount))
+    if (!c->check_array (featureZ.arrayZ, featureCount))
       return_trace (false);
 
-    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
+    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (&featureZ, featureZ[0].static_size * featureCount);
     unsigned int count = subtableCount;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -669,9 +968,9 @@
   HBUINT32	featureCount;	/* Number of feature subtable entries. */
   HBUINT32	subtableCount;	/* The number of subtables in the chain. */
 
-  Feature	featureZ[VAR];	/* Features. */
-/*ChainSubtable	subtableX[VAR];*//* Subtables. */
-/*subtableGlyphCoverageArray*/	/* Only if major == 3. */
+  UnsizedArrayOf<Feature>	featureZ;	/* Features. */
+/*ChainSubtable	firstSubtable;*//* Subtables. */
+/*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */
 
   public:
   DEFINE_SIZE_MIN (16);
@@ -679,38 +978,70 @@
 
 
 /*
- * The 'mort'/'morx' Tables
+ * The 'morx' Table
  */
 
 struct morx
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_morx;
 
-  inline void apply (hb_aat_apply_context_t *c) const
+  inline bool has_data (void) const { return version != 0; }
+
+  inline void compile_flags (const hb_aat_map_builder_t *mapper,
+			     hb_aat_map_t *map) const
   {
-    c->set_lookup_index (0);
-    const Chain *chain = chainsZ;
+    const Chain *chain = &firstChain;
     unsigned int count = chainCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      chain->apply (c);
+      map->chain_flags.push (chain->compile_flags (mapper));
       chain = &StructAfter<Chain> (*chain);
     }
   }
 
+  inline static void remove_deleted_glyphs (hb_buffer_t *buffer)
+  {
+    if (unlikely (!buffer->successful)) return;
+
+    buffer->clear_output ();
+    for (buffer->idx = 0; buffer->idx < buffer->len && buffer->successful;)
+    {
+      if (unlikely (buffer->cur().codepoint == DELETED_GLYPH))
+        buffer->skip_glyph ();
+      else
+        buffer->next_glyph ();
+    }
+    if (likely (buffer->successful))
+      buffer->swap_buffers ();
+  }
+
+  inline void apply (hb_aat_apply_context_t *c) const
+  {
+    if (unlikely (!c->buffer->successful)) return;
+    c->set_lookup_index (0);
+    const Chain *chain = &firstChain;
+    unsigned int count = chainCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      chain->apply (c, c->plan->aat_map.chain_flags[i]);
+      if (unlikely (!c->buffer->successful)) return;
+      chain = &StructAfter<Chain> (*chain);
+    }
+    remove_deleted_glyphs (c->buffer);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!version.sanitize (c) ||
-	(version.major >> (sizeof (HBUINT32) == 4 ? 1 : 0)) != 1 ||
+    if (!version.sanitize (c) || version < 2 ||
 	!chainCount.sanitize (c))
       return_trace (false);
 
-    const Chain *chain = chainsZ;
+    const Chain *chain = &firstChain;
     unsigned int count = chainCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (!chain->sanitize (c, version.major))
+      if (!chain->sanitize (c, version))
 	return_trace (false);
       chain = &StructAfter<Chain> (*chain);
     }
@@ -719,11 +1050,12 @@
   }
 
   protected:
-  FixedVersion<>version;	/* Version number of the glyph metamorphosis table.
-				 * 1 for mort, 2 or 3 for morx. */
+  HBUINT16	version;	/* Version number of the glyph metamorphosis table.
+				 * 2 or 3. */
+  HBUINT16	unused;		/* Set to 0. */
   HBUINT32	chainCount;	/* Number of metamorphosis chains contained in this
 				 * table. */
-  Chain		chainsZ[VAR];	/* Chains. */
+  Chain		firstChain;	/* Chains. */
 
   public:
   DEFINE_SIZE_MIN (8);
diff --git a/src/hb-aat-layout-trak-table.hh b/src/hb-aat-layout-trak-table.hh
index f5dc558..de2071a 100644
--- a/src/hb-aat-layout-trak-table.hh
+++ b/src/hb-aat-layout-trak-table.hh
@@ -28,9 +28,9 @@
 #ifndef HB_AAT_LAYOUT_TRAK_TABLE_HH
 #define HB_AAT_LAYOUT_TRAK_TABLE_HH
 
-#include "hb-aat-layout-common-private.hh"
-#include "hb-ot-layout-private.hh"
-#include "hb-open-type-private.hh"
+#include "hb-aat-layout-common.hh"
+#include "hb-ot-layout.hh"
+#include "hb-open-type.hh"
 
 /*
  * trak -- Tracking
@@ -46,29 +46,33 @@
 {
   friend struct TrackData;
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base,
-			unsigned int size) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) &&
-			  (valuesZ.sanitize (c, base, size))));
-  }
-
-  private:
   inline float get_track_value () const
   {
     return track.to_float ();
   }
 
-  inline int get_value (const void *base, unsigned int index) const
+  inline int get_value (const void *base,
+			unsigned int index,
+			unsigned int nSizes) const
   {
-    return (base+valuesZ)[index];
+    return hb_array (base+valuesZ, nSizes)[index];
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base,
+			unsigned int nSizes) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  (valuesZ.sanitize (c, base, nSizes))));
   }
 
   protected:
   Fixed		track;		/* Track value for this record. */
-  NameID	trackNameID;	/* The 'name' table index for this track */
-  OffsetTo<UnsizedArrayOf<FWORD> >
+  NameID	trackNameID;	/* The 'name' table index for this track.
+				 * (a short word or phrase like "loose"
+				 * or "very tight") */
+  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT16, false>
 		valuesZ;	/* Offset from start of tracking table to
 				 * per-size tracking values for this track. */
 
@@ -78,6 +82,68 @@
 
 struct TrackData
 {
+  inline float interpolate_at (unsigned int idx,
+			       float target_size,
+			       const TrackTableEntry &trackTableEntry,
+			       const void *base) const
+  {
+    unsigned int sizes = nSizes;
+    hb_array_t<const Fixed> size_table ((base+sizeTable).arrayZ, sizes);
+
+    float s0 = size_table[idx].to_float ();
+    float s1 = size_table[idx + 1].to_float ();
+    float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0);
+    return t * trackTableEntry.get_value (base, idx + 1, sizes) +
+	   (1.f - t) * trackTableEntry.get_value (base, idx, sizes);
+  }
+
+  inline int get_tracking (const void *base, float ptem) const
+  {
+    /* CoreText points are CSS pixels (96 per inch),
+     * NOT typographic points (72 per inch).
+     *
+     * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
+     */
+    float csspx = ptem * 96.f / 72.f;
+
+    /*
+     * Choose track.
+     */
+    const TrackTableEntry *trackTableEntry = nullptr;
+    unsigned int count = nTracks;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      /* Note: Seems like the track entries are sorted by values.  But the
+       * spec doesn't explicitly say that.  It just mentions it in the example. */
+
+      /* For now we only seek for track entries with zero tracking value */
+
+      if (trackTable[i].get_track_value () == 0.f)
+      {
+	trackTableEntry = &trackTable[i];
+	break;
+      }
+    }
+    if (!trackTableEntry) return 0.;
+
+    /*
+     * Choose size.
+     */
+    unsigned int sizes = nSizes;
+    if (!sizes) return 0.;
+    if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
+
+    /* TODO bfind() */
+    hb_array_t<const Fixed> size_table ((base+sizeTable).arrayZ, sizes);
+    unsigned int size_index;
+    for (size_index = 0; size_index < sizes - 1; size_index++)
+      if (size_table[size_index].to_float () >= csspx)
+        break;
+
+    return round (interpolate_at (size_index ? size_index - 1 : 0, csspx,
+				  *trackTableEntry, base));
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
@@ -86,56 +152,12 @@
 		  trackTable.sanitize (c, nTracks, base, nSizes));
   }
 
-  inline float get_tracking (const void *base, float ptem) const
-  {
-    /* CoreText points are CSS pixels (96 per inch),
-     * NOT typographic points (72 per inch).
-     *
-     * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
-     */
-    float csspx = ptem * 96.f / 72.f;
-    Fixed fixed_size;
-    fixed_size.set_float (csspx);
-
-    /* XXX Clean this up. Make it work with nSizes==1 and 0. */
-
-    unsigned int sizes = nSizes;
-
-    const TrackTableEntry *trackTableEntry = nullptr;
-    for (unsigned int i = 0; i < sizes; ++i)
-      // For now we only seek for track entries with zero tracking value
-      if (trackTable[i].get_track_value () == 0.f)
-        trackTableEntry = &trackTable[0];
-
-    // We couldn't match any, exit
-    if (!trackTableEntry) return 0.;
-
-    /* TODO bfind() */
-    unsigned int size_index;
-    UnsizedArrayOf<Fixed> size_table = base+sizeTable;
-    for (size_index = 0; size_index < sizes; ++size_index)
-      if (size_table[size_index] >= fixed_size)
-        break;
-
-    // TODO(ebraminio): We don't attempt to extrapolate to larger or
-    // smaller values for now but we should do, per spec
-    if (size_index == sizes)
-      return trackTableEntry->get_value (base, sizes - 1);
-    if (size_index == 0 || size_table[size_index] == fixed_size)
-      return trackTableEntry->get_value (base, size_index);
-
-    float s0 = size_table[size_index - 1].to_float ();
-    float s1 = size_table[size_index].to_float ();
-    float t = (csspx - s0) / (s1 - s0);
-    return (float) t * trackTableEntry->get_value (base, size_index) +
-	   ((float) 1.0 - t) * trackTableEntry->get_value (base, size_index - 1);
-  }
-
   protected:
   HBUINT16	nTracks;	/* Number of separate tracks included in this table. */
   HBUINT16	nSizes;		/* Number of point sizes included in this table. */
-  LOffsetTo<UnsizedArrayOf<Fixed> >
-		sizeTable;	/* Offset to array[nSizes] of size values. */
+  LOffsetTo<UnsizedArrayOf<Fixed>, false>
+		sizeTable;	/* Offset from start of the tracking table to
+				 * Array[nSizes] of size values.. */
   UnsizedArrayOf<TrackTableEntry>
 		trackTable;	/* Array[nTracks] of TrackTableEntry records. */
 
@@ -147,6 +169,8 @@
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_trak;
 
+  inline bool has_data (void) const { return version.to_int () != 0; }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -160,6 +184,8 @@
   {
     TRACE_APPLY (this);
 
+    hb_mask_t trak_mask = c->plan->trak_mask;
+
     const float ptem = c->font->ptem;
     if (unlikely (ptem <= 0.f))
       return_trace (false);
@@ -168,25 +194,27 @@
     if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
     {
       const TrackData &trackData = this+horizData;
-      float tracking = trackData.get_tracking (this, ptem);
-      hb_position_t advance_to_add = c->font->em_scalef_x (tracking / 2);
+      int tracking = trackData.get_tracking (this, ptem);
+      hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2);
+      hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
       foreach_grapheme (buffer, start, end)
       {
-	buffer->pos[start].x_offset += advance_to_add;
+        if (!(buffer->info[start].mask & trak_mask)) continue;
 	buffer->pos[start].x_advance += advance_to_add;
-	buffer->pos[end].x_advance += advance_to_add;
+	buffer->pos[start].x_offset += offset_to_add;
       }
     }
     else
     {
       const TrackData &trackData = this+vertData;
-      float tracking = trackData.get_tracking (this, ptem);
-      hb_position_t advance_to_add = c->font->em_scalef_y (tracking / 2);
+      int tracking = trackData.get_tracking (this, ptem);
+      hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2);
+      hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
       foreach_grapheme (buffer, start, end)
       {
-	buffer->pos[start].y_offset += advance_to_add;
+        if (!(buffer->info[start].mask & trak_mask)) continue;
 	buffer->pos[start].y_advance += advance_to_add;
-	buffer->pos[end].y_advance += advance_to_add;
+	buffer->pos[start].y_offset += offset_to_add;
       }
     }
 
@@ -194,15 +222,17 @@
   }
 
   protected:
-  FixedVersion<>	version;	/* Version of the tracking table--currently
-					 * 0x00010000u for version 1.0. */
-  HBUINT16		format; 	/* Format of the tracking table */
-  OffsetTo<TrackData>	horizData;	/* TrackData for horizontal text */
-  OffsetTo<TrackData>	vertData;	/* TrackData for vertical text */
+  FixedVersion<>	version;	/* Version of the tracking table
+					 * (0x00010000u for version 1.0). */
+  HBUINT16		format; 	/* Format of the tracking table (set to 0). */
+  OffsetTo<TrackData>	horizData;	/* Offset from start of tracking table to TrackData
+					 * for horizontal text (or 0 if none). */
+  OffsetTo<TrackData>	vertData;	/* Offset from start of tracking table to TrackData
+					 * for vertical text (or 0 if none). */
   HBUINT16		reserved;	/* Reserved. Set to 0. */
 
   public:
-  DEFINE_SIZE_MIN (12);
+  DEFINE_SIZE_STATIC (12);
 };
 
 } /* namespace AAT */
diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc
index 405aafc..ec05393 100644
--- a/src/hb-aat-layout.cc
+++ b/src/hb-aat-layout.cc
@@ -24,22 +24,113 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
-#include "hb-ot-layout-private.hh"
-#include "hb-ot-layout-gsubgpos-private.hh"
-
-#include "hb-aat-layout-private.hh"
+#include "hb-ot-face.hh"
+#include "hb-aat-layout.hh"
 #include "hb-aat-layout-ankr-table.hh"
 #include "hb-aat-layout-bsln-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-layout-feat-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-layout-kerx-table.hh"
 #include "hb-aat-layout-morx-table.hh"
 #include "hb-aat-layout-trak-table.hh"
-#include "hb-aat-ltag-table.hh" // Just so we compile it; unused otherwise.
+#include "hb-aat-ltag-table.hh"
+
+
+/* Table data courtesy of Apple.  Converted from mnemonics to integers
+ * when moving to this file. */
+static const hb_aat_feature_mapping_t feature_mappings[] =
+{
+  {HB_TAG ('c','2','p','c'),	38/*kUpperCaseType*/,			2/*kUpperCasePetiteCapsSelector*/,		0/*kDefaultUpperCaseSelector*/},
+  {HB_TAG ('c','2','s','c'),	38/*kUpperCaseType*/,			1/*kUpperCaseSmallCapsSelector*/,		0/*kDefaultUpperCaseSelector*/},
+  {HB_TAG ('c','a','l','t'),	36/*kContextualAlternatesType*/,	0/*kContextualAlternatesOnSelector*/,		1/*kContextualAlternatesOffSelector*/},
+  {HB_TAG ('c','a','s','e'),	33/*kCaseSensitiveLayoutType*/,		0/*kCaseSensitiveLayoutOnSelector*/,		1/*kCaseSensitiveLayoutOffSelector*/},
+  {HB_TAG ('c','l','i','g'),	1/*kLigaturesType*/,			18/*kContextualLigaturesOnSelector*/,		19/*kContextualLigaturesOffSelector*/},
+  {HB_TAG ('c','p','s','p'),	33/*kCaseSensitiveLayoutType*/,		2/*kCaseSensitiveSpacingOnSelector*/,		3/*kCaseSensitiveSpacingOffSelector*/},
+  {HB_TAG ('c','s','w','h'),	36/*kContextualAlternatesType*/,	4/*kContextualSwashAlternatesOnSelector*/,	5/*kContextualSwashAlternatesOffSelector*/},
+  {HB_TAG ('d','l','i','g'),	1/*kLigaturesType*/,			4/*kRareLigaturesOnSelector*/,			5/*kRareLigaturesOffSelector*/},
+  {HB_TAG ('e','x','p','t'),	20/*kCharacterShapeType*/,		10/*kExpertCharactersSelector*/,		16},
+  {HB_TAG ('f','r','a','c'),	11/*kFractionsType*/,			2/*kDiagonalFractionsSelector*/,		0/*kNoFractionsSelector*/},
+  {HB_TAG ('f','w','i','d'),	22/*kTextSpacingType*/,			1/*kMonospacedTextSelector*/,			7},
+  {HB_TAG ('h','a','l','t'),	22/*kTextSpacingType*/,			6/*kAltHalfWidthTextSelector*/,			7},
+  {HB_TAG ('h','i','s','t'),	1/*kLigaturesType*/,			20/*kHistoricalLigaturesOnSelector*/,		21/*kHistoricalLigaturesOffSelector*/},
+  {HB_TAG ('h','k','n','a'),	34/*kAlternateKanaType*/,		0/*kAlternateHorizKanaOnSelector*/,		1/*kAlternateHorizKanaOffSelector*/,	},
+  {HB_TAG ('h','l','i','g'),	1/*kLigaturesType*/,			20/*kHistoricalLigaturesOnSelector*/,		21/*kHistoricalLigaturesOffSelector*/},
+  {HB_TAG ('h','n','g','l'),	23/*kTransliterationType*/,		1/*kHanjaToHangulSelector*/,			0/*kNoTransliterationSelector*/},
+  {HB_TAG ('h','o','j','o'),	20/*kCharacterShapeType*/,		12/*kHojoCharactersSelector*/,			16},
+  {HB_TAG ('h','w','i','d'),	22/*kTextSpacingType*/,			2/*kHalfWidthTextSelector*/,			7},
+  {HB_TAG ('i','t','a','l'),	32/*kItalicCJKRomanType*/,		2/*kCJKItalicRomanOnSelector*/,			3/*kCJKItalicRomanOffSelector*/},
+  {HB_TAG ('j','p','0','4'),	20/*kCharacterShapeType*/,		11/*kJIS2004CharactersSelector*/,		16},
+  {HB_TAG ('j','p','7','8'),	20/*kCharacterShapeType*/,		2/*kJIS1978CharactersSelector*/,		16},
+  {HB_TAG ('j','p','8','3'),	20/*kCharacterShapeType*/,		3/*kJIS1983CharactersSelector*/,		16},
+  {HB_TAG ('j','p','9','0'),	20/*kCharacterShapeType*/,		4/*kJIS1990CharactersSelector*/,		16},
+  {HB_TAG ('l','i','g','a'),	1/*kLigaturesType*/,			2/*kCommonLigaturesOnSelector*/,		3/*kCommonLigaturesOffSelector*/},
+  {HB_TAG ('l','n','u','m'),	21/*kNumberCaseType*/,			1/*kUpperCaseNumbersSelector*/,			2},
+  {HB_TAG ('m','g','r','k'),	15/*kMathematicalExtrasType*/,		10/*kMathematicalGreekOnSelector*/,		11/*kMathematicalGreekOffSelector*/},
+  {HB_TAG ('n','l','c','k'),	20/*kCharacterShapeType*/,		13/*kNLCCharactersSelector*/,			16},
+  {HB_TAG ('o','n','u','m'),	21/*kNumberCaseType*/,			0/*kLowerCaseNumbersSelector*/,			2},
+  {HB_TAG ('o','r','d','n'),	10/*kVerticalPositionType*/,		3/*kOrdinalsSelector*/,				0/*kNormalPositionSelector*/},
+  {HB_TAG ('p','a','l','t'),	22/*kTextSpacingType*/,			5/*kAltProportionalTextSelector*/,		7},
+  {HB_TAG ('p','c','a','p'),	37/*kLowerCaseType*/,			2/*kLowerCasePetiteCapsSelector*/,		0/*kDefaultLowerCaseSelector*/},
+  {HB_TAG ('p','k','n','a'),	22/*kTextSpacingType*/,			0/*kProportionalTextSelector*/,			7},
+  {HB_TAG ('p','n','u','m'),	6/*kNumberSpacingType*/,		1/*kProportionalNumbersSelector*/,		4},
+  {HB_TAG ('p','w','i','d'),	22/*kTextSpacingType*/,			0/*kProportionalTextSelector*/,			7},
+  {HB_TAG ('q','w','i','d'),	22/*kTextSpacingType*/,			4/*kQuarterWidthTextSelector*/,			7},
+  {HB_TAG ('r','u','b','y'),	28/*kRubyKanaType*/,			2/*kRubyKanaOnSelector*/,			3/*kRubyKanaOffSelector*/},
+  {HB_TAG ('s','i','n','f'),	10/*kVerticalPositionType*/,		4/*kScientificInferiorsSelector*/,		0/*kNormalPositionSelector*/},
+  {HB_TAG ('s','m','c','p'),	37/*kLowerCaseType*/,			1/*kLowerCaseSmallCapsSelector*/,		0/*kDefaultLowerCaseSelector*/},
+  {HB_TAG ('s','m','p','l'),	20/*kCharacterShapeType*/,		1/*kSimplifiedCharactersSelector*/,		16},
+  {HB_TAG ('s','s','0','1'),	35/*kStylisticAlternativesType*/,	2/*kStylisticAltOneOnSelector*/,		3/*kStylisticAltOneOffSelector*/},
+  {HB_TAG ('s','s','0','2'),	35/*kStylisticAlternativesType*/,	4/*kStylisticAltTwoOnSelector*/,		5/*kStylisticAltTwoOffSelector*/},
+  {HB_TAG ('s','s','0','3'),	35/*kStylisticAlternativesType*/,	6/*kStylisticAltThreeOnSelector*/,		7/*kStylisticAltThreeOffSelector*/},
+  {HB_TAG ('s','s','0','4'),	35/*kStylisticAlternativesType*/,	8/*kStylisticAltFourOnSelector*/,		9/*kStylisticAltFourOffSelector*/},
+  {HB_TAG ('s','s','0','5'),	35/*kStylisticAlternativesType*/,	10/*kStylisticAltFiveOnSelector*/,		11/*kStylisticAltFiveOffSelector*/},
+  {HB_TAG ('s','s','0','6'),	35/*kStylisticAlternativesType*/,	12/*kStylisticAltSixOnSelector*/,		13/*kStylisticAltSixOffSelector*/},
+  {HB_TAG ('s','s','0','7'),	35/*kStylisticAlternativesType*/,	14/*kStylisticAltSevenOnSelector*/,		15/*kStylisticAltSevenOffSelector*/},
+  {HB_TAG ('s','s','0','8'),	35/*kStylisticAlternativesType*/,	16/*kStylisticAltEightOnSelector*/,		17/*kStylisticAltEightOffSelector*/},
+  {HB_TAG ('s','s','0','9'),	35/*kStylisticAlternativesType*/,	18/*kStylisticAltNineOnSelector*/,		19/*kStylisticAltNineOffSelector*/},
+  {HB_TAG ('s','s','1','0'),	35/*kStylisticAlternativesType*/,	20/*kStylisticAltTenOnSelector*/,		21/*kStylisticAltTenOffSelector*/},
+  {HB_TAG ('s','s','1','1'),	35/*kStylisticAlternativesType*/,	22/*kStylisticAltElevenOnSelector*/,		23/*kStylisticAltElevenOffSelector*/},
+  {HB_TAG ('s','s','1','2'),	35/*kStylisticAlternativesType*/,	24/*kStylisticAltTwelveOnSelector*/,		25/*kStylisticAltTwelveOffSelector*/},
+  {HB_TAG ('s','s','1','3'),	35/*kStylisticAlternativesType*/,	26/*kStylisticAltThirteenOnSelector*/,		27/*kStylisticAltThirteenOffSelector*/},
+  {HB_TAG ('s','s','1','4'),	35/*kStylisticAlternativesType*/,	28/*kStylisticAltFourteenOnSelector*/,		29/*kStylisticAltFourteenOffSelector*/},
+  {HB_TAG ('s','s','1','5'),	35/*kStylisticAlternativesType*/,	30/*kStylisticAltFifteenOnSelector*/,		31/*kStylisticAltFifteenOffSelector*/},
+  {HB_TAG ('s','s','1','6'),	35/*kStylisticAlternativesType*/,	32/*kStylisticAltSixteenOnSelector*/,		33/*kStylisticAltSixteenOffSelector*/},
+  {HB_TAG ('s','s','1','7'),	35/*kStylisticAlternativesType*/,	34/*kStylisticAltSeventeenOnSelector*/,		35/*kStylisticAltSeventeenOffSelector*/},
+  {HB_TAG ('s','s','1','8'),	35/*kStylisticAlternativesType*/,	36/*kStylisticAltEighteenOnSelector*/,		37/*kStylisticAltEighteenOffSelector*/},
+  {HB_TAG ('s','s','1','9'),	35/*kStylisticAlternativesType*/,	38/*kStylisticAltNineteenOnSelector*/,		39/*kStylisticAltNineteenOffSelector*/},
+  {HB_TAG ('s','s','2','0'),	35/*kStylisticAlternativesType*/,	40/*kStylisticAltTwentyOnSelector*/,		41/*kStylisticAltTwentyOffSelector*/},
+  {HB_TAG ('s','u','b','s'),	10/*kVerticalPositionType*/,		2/*kInferiorsSelector*/,			0/*kNormalPositionSelector*/},
+  {HB_TAG ('s','u','p','s'),	10/*kVerticalPositionType*/,		1/*kSuperiorsSelector*/,			0/*kNormalPositionSelector*/},
+  {HB_TAG ('s','w','s','h'),	36/*kContextualAlternatesType*/,	2/*kSwashAlternatesOnSelector*/,		3/*kSwashAlternatesOffSelector*/},
+  {HB_TAG ('t','i','t','l'),	19/*kStyleOptionsType*/,		4/*kTitlingCapsSelector*/,			0/*kNoStyleOptionsSelector*/},
+  {HB_TAG ('t','n','a','m'),	20/*kCharacterShapeType*/,		14/*kTraditionalNamesCharactersSelector*/,	16},
+  {HB_TAG ('t','n','u','m'),	6/*kNumberSpacingType*/,		0/*kMonospacedNumbersSelector*/,		4},
+  {HB_TAG ('t','r','a','d'),	20/*kCharacterShapeType*/,		0/*kTraditionalCharactersSelector*/,		16},
+  {HB_TAG ('t','w','i','d'),	22/*kTextSpacingType*/,			3/*kThirdWidthTextSelector*/,			7},
+  {HB_TAG ('u','n','i','c'),	3/*kLetterCaseType*/,			14,						15},
+  {HB_TAG ('v','a','l','t'),	22/*kTextSpacingType*/,			5/*kAltProportionalTextSelector*/,		7},
+  {HB_TAG ('v','e','r','t'),	4/*kVerticalSubstitutionType*/,		0/*kSubstituteVerticalFormsOnSelector*/,	1/*kSubstituteVerticalFormsOffSelector*/},
+  {HB_TAG ('v','h','a','l'),	22/*kTextSpacingType*/,			6/*kAltHalfWidthTextSelector*/,			7},
+  {HB_TAG ('v','k','n','a'),	34/*kAlternateKanaType*/,		2/*kAlternateVertKanaOnSelector*/,		3/*kAlternateVertKanaOffSelector*/},
+  {HB_TAG ('v','p','a','l'),	22/*kTextSpacingType*/,			5/*kAltProportionalTextSelector*/,		7},
+  {HB_TAG ('v','r','t','2'),	4/*kVerticalSubstitutionType*/,		0/*kSubstituteVerticalFormsOnSelector*/,	1/*kSubstituteVerticalFormsOffSelector*/},
+  {HB_TAG ('z','e','r','o'),	14/*kTypographicExtrasType*/,		4/*kSlashedZeroOnSelector*/,			5/*kSlashedZeroOffSelector*/},
+};
+
+const hb_aat_feature_mapping_t *
+hb_aat_layout_find_feature_mapping (hb_tag_t tag)
+{
+  return (const hb_aat_feature_mapping_t *) bsearch (&tag,
+						     feature_mappings,
+						     ARRAY_LENGTH (feature_mappings),
+						     sizeof (feature_mappings[0]),
+						     hb_aat_feature_mapping_t::cmp);
+}
+
 
 /*
- * morx/kerx/trak/ankr
+ * morx/kerx/trak
  */
 
 static inline const AAT::morx&
@@ -51,46 +142,122 @@
       *blob = hb_blob_get_empty ();
     return Null(AAT::morx);
   }
-  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  const AAT::morx& morx = *(layout->morx.get ());
+  const AAT::morx& morx = *(hb_ot_face_data (face)->morx.get ());
   if (blob)
-    *blob = layout->morx.get_blob ();
+    *blob = hb_ot_face_data (face)->morx.get_blob ();
   return morx;
 }
+static inline const AAT::kerx&
+_get_kerx (hb_face_t *face, hb_blob_t **blob = nullptr)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
+  {
+    if (blob)
+      *blob = hb_blob_get_empty ();
+    return Null(AAT::kerx);
+  }
+  const AAT::kerx& kerx = *(hb_ot_face_data (face)->kerx.get ());
+  if (blob)
+    *blob = hb_ot_face_data (face)->kerx.get_blob ();
+  return kerx;
+}
+static inline const AAT::ankr&
+_get_ankr (hb_face_t *face, hb_blob_t **blob = nullptr)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
+  {
+    if (blob)
+      *blob = hb_blob_get_empty ();
+    return Null(AAT::ankr);
+  }
+  const AAT::ankr& ankr = *(hb_ot_face_data (face)->ankr.get ());
+  if (blob)
+    *blob = hb_ot_face_data (face)->ankr.get_blob ();
+  return ankr;
+}
+static inline const AAT::trak&
+_get_trak (hb_face_t *face)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(AAT::trak);
+  return *(hb_ot_face_data (face)->trak.get ());
+}
+static inline const AAT::ltag&
+_get_ltag (hb_face_t *face)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(AAT::ltag);
+  return *(hb_ot_face_data (face)->ltag.get ());
+}
 
-// static inline void
-// _hb_aat_layout_create (hb_face_t *face)
-// {
-//   hb_blob_t *morx_blob = OT::hb_sanitize_context_t().reference_table<AAT::morx> (face);
-//   morx_blob->as<AAT::morx> ();
-
-//   if (0)
-//   {
-//     morx_blob->as<AAT::Lookup<OT::GlyphID> > ()->get_value (1, face->get_num_glyphs ());
-//   }
-// }
 
 void
-hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer)
+hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
+			   hb_aat_map_t *map)
+{
+  _get_morx (mapper->face).compile_flags (mapper, map);
+}
+
+
+hb_bool_t
+hb_aat_layout_has_substitution (hb_face_t *face)
+{
+  return _get_morx (face).has_data ();
+}
+
+void
+hb_aat_layout_substitute (hb_ot_shape_plan_t *plan,
+			  hb_font_t *font,
+			  hb_buffer_t *buffer)
 {
   hb_blob_t *blob;
   const AAT::morx& morx = _get_morx (font->face, &blob);
 
-  AAT::hb_aat_apply_context_t c (font, buffer, blob);
+  AAT::hb_aat_apply_context_t c (plan, font, buffer, blob);
   morx.apply (&c);
 }
 
-void
-hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer)
-{
-#if 0
-  hb_blob_t *blob;
-  const AAT::ankr& ankr = _get_ankr (font->face, &blob);
-  const AAT::kerx& kerx = _get_kerx (font->face, &blob);
-  const AAT::trak& trak = _get_trak (font->face, &blob);
 
-  AAT::hb_aat_apply_context_t c (font, buffer, blob);
-  kerx.apply (&c, &ankr);
+hb_bool_t
+hb_aat_layout_has_positioning (hb_face_t *face)
+{
+  return _get_kerx (face).has_data ();
+}
+
+void
+hb_aat_layout_position (hb_ot_shape_plan_t *plan,
+			hb_font_t *font,
+			hb_buffer_t *buffer)
+{
+  hb_blob_t *blob;
+  const AAT::kerx& kerx = _get_kerx (font->face, &blob);
+
+  hb_blob_t *ankr_blob;
+  const AAT::ankr& ankr = _get_ankr (font->face, &ankr_blob);
+
+  AAT::hb_aat_apply_context_t c (plan, font, buffer, blob,
+				 ankr, ankr_blob->data + ankr_blob->length);
+  kerx.apply (&c);
+}
+
+hb_bool_t
+hb_aat_layout_has_tracking (hb_face_t *face)
+{
+  return _get_trak (face).has_data ();
+}
+
+void
+hb_aat_layout_track (hb_ot_shape_plan_t *plan,
+		     hb_font_t *font,
+		     hb_buffer_t *buffer)
+{
+  const AAT::trak& trak = _get_trak (font->face);
+
+  AAT::hb_aat_apply_context_t c (plan, font, buffer);
   trak.apply (&c);
-#endif
+}
+
+hb_language_t
+_hb_aat_language_get (hb_face_t *face,
+		      unsigned int i)
+{
+  return _get_ltag (face).get_language (i);
 }
diff --git a/src/hb-aat-layout.hh b/src/hb-aat-layout.hh
new file mode 100644
index 0000000..8a558e6
--- /dev/null
+++ b/src/hb-aat-layout.hh
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_AAT_LAYOUT_HH
+#define HB_AAT_LAYOUT_HH
+
+#include "hb.hh"
+
+#include "hb-ot-shape.hh"
+
+
+struct hb_aat_feature_mapping_t
+{
+  hb_tag_t otFeatureTag;
+  uint16_t aatFeatureType;
+  uint16_t selectorToEnable;
+  uint16_t selectorToDisable;
+
+  static inline int cmp (const void *key_, const void *entry_)
+  {
+    hb_tag_t key = * (unsigned int *) key_;
+    const hb_aat_feature_mapping_t * entry = (const hb_aat_feature_mapping_t *) entry_;
+    return key < entry->otFeatureTag ? -1 :
+	   key > entry->otFeatureTag ? 1 :
+	   0;
+  }
+};
+
+HB_INTERNAL const hb_aat_feature_mapping_t *
+hb_aat_layout_find_feature_mapping (hb_tag_t tag);
+
+HB_INTERNAL void
+hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
+			   hb_aat_map_t *map);
+
+HB_INTERNAL hb_bool_t
+hb_aat_layout_has_substitution (hb_face_t *face);
+
+HB_INTERNAL void
+hb_aat_layout_substitute (hb_ot_shape_plan_t *plan,
+			  hb_font_t *font,
+			  hb_buffer_t *buffer);
+
+HB_INTERNAL hb_bool_t
+hb_aat_layout_has_positioning (hb_face_t *face);
+
+HB_INTERNAL void
+hb_aat_layout_position (hb_ot_shape_plan_t *plan,
+			hb_font_t *font,
+			hb_buffer_t *buffer);
+
+HB_INTERNAL hb_bool_t
+hb_aat_layout_has_tracking (hb_face_t *face);
+
+HB_INTERNAL void
+hb_aat_layout_track (hb_ot_shape_plan_t *plan,
+		     hb_font_t *font,
+		     hb_buffer_t *buffer);
+
+HB_INTERNAL hb_language_t
+_hb_aat_language_get (hb_face_t *face,
+		      unsigned int i);
+
+
+#endif /* HB_AAT_LAYOUT_HH */
diff --git a/src/hb-aat-ltag-table.hh b/src/hb-aat-ltag-table.hh
index 15c4e89..8a42b35 100644
--- a/src/hb-aat-ltag-table.hh
+++ b/src/hb-aat-ltag-table.hh
@@ -25,7 +25,7 @@
 #ifndef HB_AAT_LTAG_TABLE_HH
 #define HB_AAT_LTAG_TABLE_HH
 
-#include "hb-aat-layout-common-private.hh"
+#include "hb-open-type.hh"
 
 /*
  * ltag -- Language Tag
@@ -36,9 +36,13 @@
 
 namespace AAT {
 
+using namespace OT;
+
 
 struct FTStringRange
 {
+  friend struct ltag;
+
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
@@ -46,7 +50,7 @@
   }
 
   protected:
-  OffsetTo<UnsizedArrayOf<HBUINT8> >
+  OffsetTo<UnsizedArrayOf<HBUINT8>, HBUINT16, false>
 		tag;		/* Offset from the start of the table to
 				 * the beginning of the string */
   HBUINT16	length;		/* String length (in bytes) */
@@ -58,10 +62,19 @@
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_ltag;
 
+  inline hb_language_t get_language (unsigned int i) const
+  {
+    const FTStringRange &range = tagRanges[i];
+    return hb_language_from_string ((const char *) (this+range.tag).arrayZ,
+				    range.length);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) && tagRanges.sanitize (c, this)));
+    return_trace (likely (c->check_struct (this) &&
+			  version >= 1 &&
+			  tagRanges.sanitize (c, this)));
   }
 
   protected:
diff --git a/src/hb-aat-map.cc b/src/hb-aat-map.cc
new file mode 100644
index 0000000..f2736bf
--- /dev/null
+++ b/src/hb-aat-map.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2009,2010  Red Hat, Inc.
+ * Copyright © 2010,2011,2013  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-aat-map.hh"
+
+#include "hb-aat-layout.hh"
+
+
+void hb_aat_map_builder_t::add_feature (hb_tag_t tag,
+					unsigned int value)
+{
+  const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag);
+  if (!mapping) return;
+
+  feature_info_t *info = features.push();
+  info->type = mapping->aatFeatureType;
+  info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable;
+}
+
+void
+hb_aat_map_builder_t::compile (hb_aat_map_t  &m,
+			       const int    *coords HB_UNUSED,
+			       unsigned int  num_coords HB_UNUSED)
+{
+  /* Sort features and merge duplicates */
+  if (features.len)
+  {
+    features.qsort ();
+    unsigned int j = 0;
+    for (unsigned int i = 1; i < features.len; i++)
+      if (features[i].type != features[j].type)
+	features[++j] = features[i];
+    features.shrink (j + 1);
+  }
+
+  hb_aat_layout_compile_map (this, &m);
+}
diff --git a/src/hb-aat-map.hh b/src/hb-aat-map.hh
new file mode 100644
index 0000000..846cdc1
--- /dev/null
+++ b/src/hb-aat-map.hh
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2018  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_AAT_MAP_HH
+#define HB_AAT_MAP_HH
+
+#include "hb.hh"
+
+
+struct hb_ot_shape_plan_t;
+
+struct hb_aat_map_t
+{
+  friend struct hb_aat_map_builder_t;
+
+  public:
+
+  inline void init (void)
+  {
+    memset (this, 0, sizeof (*this));
+    chain_flags.init ();
+  }
+  inline void fini (void)
+  {
+    chain_flags.fini ();
+  }
+
+  public:
+  hb_vector_t<hb_mask_t, 1> chain_flags;
+};
+
+struct hb_aat_map_builder_t
+{
+  public:
+
+  HB_INTERNAL hb_aat_map_builder_t (hb_face_t *face_,
+				    const hb_segment_properties_t *props_ HB_UNUSED) :
+				      face (face_)
+  {
+    features.init ();
+  }
+
+  ~hb_aat_map_builder_t (void)
+  {
+    features.fini ();
+  }
+
+  HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value=1);
+
+  HB_INTERNAL void compile (hb_aat_map_t  &m,
+			    const int    *coords,
+			    unsigned int  num_coords);
+
+  public:
+  struct feature_info_t
+  {
+    uint16_t  type;
+    uint16_t  setting;
+    unsigned  seq; /* For stable sorting only. */
+
+    static int cmp (const void *pa, const void *pb)
+    {
+      const feature_info_t *a = (const feature_info_t *) pa;
+      const feature_info_t *b = (const feature_info_t *) pb;
+      return (a->type != b->type) ? (a->type < b->type ? -1 : 1) :
+	     (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
+    }
+
+    int cmp (const short unsigned int *ty) const
+    {
+      return (type != *ty) ? (type < *ty ? -1 : 1) : 0;
+    }
+  };
+
+  public:
+  hb_face_t *face;
+
+  public:
+  hb_vector_t<feature_info_t, 32> features;
+};
+
+
+#endif /* HB_AAT_MAP_HH */
diff --git a/src/hb-atomic.hh b/src/hb-atomic.hh
new file mode 100644
index 0000000..697de19
--- /dev/null
+++ b/src/hb-atomic.hh
@@ -0,0 +1,297 @@
+/*
+ * Copyright © 2007  Chris Wilson
+ * Copyright © 2009,2010  Red Hat, Inc.
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris@chris-wilson.co.uk>
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_ATOMIC_HH
+#define HB_ATOMIC_HH
+
+#include "hb.hh"
+
+
+/*
+ * Atomic integers and pointers.
+ */
+
+
+/* We need external help for these */
+
+#if defined(hb_atomic_int_impl_add) \
+ && defined(hb_atomic_ptr_impl_get) \
+ && defined(hb_atomic_ptr_impl_cmpexch)
+
+/* Defined externally, i.e. in config.h. */
+
+
+#elif !defined(HB_NO_MT) && defined(__ATOMIC_ACQUIRE)
+
+/* C++11-style GCC primitives. */
+
+#define _hb_memory_barrier()			__sync_synchronize ()
+
+#define hb_atomic_int_impl_add(AI, V)		__atomic_fetch_add ((AI), (V), __ATOMIC_ACQ_REL)
+#define hb_atomic_int_impl_set_relaxed(AI, V)	__atomic_store_n ((AI), (V), __ATOMIC_RELAXED)
+#define hb_atomic_int_impl_set(AI, V)		__atomic_store_n ((AI), (V), __ATOMIC_RELEASE)
+#define hb_atomic_int_impl_get_relaxed(AI)	__atomic_load_n ((AI), __ATOMIC_RELAXED)
+#define hb_atomic_int_impl_get(AI)		__atomic_load_n ((AI), __ATOMIC_ACQUIRE)
+
+#define hb_atomic_ptr_impl_set_relaxed(P, V)	__atomic_store_n ((P), (V), __ATOMIC_RELAXED)
+#define hb_atomic_ptr_impl_get_relaxed(P)	__atomic_load_n ((P), __ATOMIC_RELAXED)
+#define hb_atomic_ptr_impl_get(P)		__atomic_load_n ((P), __ATOMIC_ACQUIRE)
+static inline bool
+_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
+{
+  const void *O = O_; // Need lvalue
+  return __atomic_compare_exchange_n ((void **) P, (void **) &O, (void *) N, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
+}
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	_hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N))
+
+#elif !defined(HB_NO_MT) && __cplusplus >= 201103L
+
+/* C++11 atomics. */
+
+#include <atomic>
+
+#define _hb_memory_barrier()			std::atomic_thread_fence(std::memory_order_ack_rel)
+#define _hb_memory_r_barrier()			std::atomic_thread_fence(std::memory_order_acquire)
+#define _hb_memory_w_barrier()			std::atomic_thread_fence(std::memory_order_release)
+
+#define hb_atomic_int_impl_add(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
+#define hb_atomic_int_impl_set_relaxed(AI, V)	(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_relaxed))
+#define hb_atomic_int_impl_set(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_release))
+#define hb_atomic_int_impl_get_relaxed(AI)	(reinterpret_cast<std::atomic<int> *> (AI)->load (std::memory_order_relaxed))
+#define hb_atomic_int_impl_get(AI)		(reinterpret_cast<std::atomic<int> *> (AI)->load (std::memory_order_acquire))
+
+#define hb_atomic_ptr_impl_set_relaxed(P, V)	(reinterpret_cast<std::atomic<void*> *> (P)->store ((V), std::memory_order_relaxed))
+#define hb_atomic_ptr_impl_get_relaxed(P)	(reinterpret_cast<std::atomic<void*> *> (P)->load (std::memory_order_relaxed))
+#define hb_atomic_ptr_impl_get(P)		(reinterpret_cast<std::atomic<void*> *> (P)->load (std::memory_order_acquire))
+static inline bool
+_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
+{
+  const void *O = O_; // Need lvalue
+  return reinterpret_cast<std::atomic<const void*> *> (P)->compare_exchange_weak (O, N, std::memory_order_acq_rel, std::memory_order_relaxed);
+}
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	_hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N))
+
+
+#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__))
+
+#include <windows.h>
+
+static inline void _hb_memory_barrier (void)
+{
+#if !defined(MemoryBarrier)
+  /* MinGW has a convoluted history of supporting MemoryBarrier. */
+  LONG dummy = 0;
+  InterlockedExchange (&dummy, 1);
+#else
+  MemoryBarrier ();
+#endif
+}
+#define _hb_memory_barrier()			_hb_memory_barrier ()
+
+#define hb_atomic_int_impl_add(AI, V)		InterlockedExchangeAdd ((LONG *) (AI), (V))
+static_assert ((sizeof (LONG) == sizeof (int)), "");
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	(InterlockedCompareExchangePointer ((P), (N), (O)) == (O))
+
+
+#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
+
+#define _hb_memory_barrier()			__sync_synchronize ()
+
+#define hb_atomic_int_impl_add(AI, V)		__sync_fetch_and_add ((AI), (V))
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	__sync_bool_compare_and_swap ((P), (O), (N))
+
+
+#elif !defined(HB_NO_MT) && defined(HAVE_SOLARIS_ATOMIC_OPS)
+
+#include <atomic.h>
+#include <mbarrier.h>
+
+#define _hb_memory_r_barrier()			__machine_r_barrier ()
+#define _hb_memory_w_barrier()			__machine_w_barrier ()
+#define _hb_memory_barrier()			__machine_rw_barrier ()
+
+static inline int _hb_fetch_and_add (int *AI, int V)
+{
+  _hb_memory_w_barrier ();
+  int result = atomic_add_int_nv ((uint_t *) AI, V);
+  _hb_memory_r_barrier ();
+  return result;
+}
+static inline bool _hb_compare_and_swap_ptr (void **P, void *O, void *N)
+{
+  _hb_memory_w_barrier ();
+  bool result = atomic_cas_ptr (P, O, N) == O;
+  _hb_memory_r_barrier ();
+  return result;
+}
+
+#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add ((AI), (V))
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swap_ptr ((P), (O), (N))
+
+
+#elif !defined(HB_NO_MT) && defined(__APPLE__)
+
+#include <libkern/OSAtomic.h>
+#ifdef __MAC_OS_X_MIN_REQUIRED
+#include <AvailabilityMacros.h>
+#elif defined(__IPHONE_OS_MIN_REQUIRED)
+#include <Availability.h>
+#endif
+
+#define _hb_memory_barrier()			OSMemoryBarrier ()
+
+#define hb_atomic_int_impl_add(AI, V)		(OSAtomicAdd32Barrier ((V), (AI)) - (V))
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100)
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwapPtrBarrier ((O), (N), (P))
+#else
+#if __ppc64__ || __x86_64__ || __aarch64__
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P))
+#else
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P))
+#endif
+#endif
+
+
+#elif !defined(HB_NO_MT) && defined(_AIX) && defined(__IBMCPP__)
+
+#include <builtins.h>
+
+#define _hb_memory_barrier()			__lwsync ()
+
+static inline int _hb_fetch_and_add (int *AI, int V)
+{
+  _hb_memory_barrier ();
+  int result = __fetch_and_add (AI, V);
+  _hb_memory_barrier ();
+  return result;
+}
+static inline bool _hb_compare_and_swaplp (long *P, long O, long N)
+{
+  _hb_memory_barrier ();
+  bool result = __compare_and_swaplp (P, &O, N);
+  _hb_memory_barrier ();
+  return result;
+}
+
+#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add ((AI), (V))
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swaplp ((long *) (P), (long) (O), (long) (N))
+static_assert ((sizeof (long) == sizeof (void *)), "");
+
+
+#elif !defined(HB_NO_MT)
+
+#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
+
+#define _hb_memory_barrier()
+
+#define hb_atomic_int_impl_add(AI, V)		((*(AI) += (V)) - (V))
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	(* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
+
+
+#else /* HB_NO_MT */
+
+#define hb_atomic_int_impl_add(AI, V)		((*(AI) += (V)) - (V))
+
+#define _hb_memory_barrier()
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	(* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
+
+
+#endif
+
+
+#ifndef _hb_memory_r_barrier
+#define _hb_memory_r_barrier()			_hb_memory_barrier ()
+#endif
+#ifndef _hb_memory_w_barrier
+#define _hb_memory_w_barrier()			_hb_memory_barrier ()
+#endif
+#ifndef hb_atomic_int_impl_set_relaxed
+#define hb_atomic_int_impl_set_relaxed(AI, V)	(*(AI) = (V))
+#endif
+#ifndef hb_atomic_int_impl_get_relaxed
+#define hb_atomic_int_impl_get_relaxed(AI)	(*(AI))
+#endif
+
+#ifndef hb_atomic_ptr_impl_set_relaxed
+#define hb_atomic_ptr_impl_set_relaxed(P, V)	(*(P) = (V))
+#endif
+#ifndef hb_atomic_ptr_impl_get_relaxed
+#define hb_atomic_ptr_impl_get_relaxed(P)	(*(P))
+#endif
+#ifndef hb_atomic_int_impl_set
+inline void hb_atomic_int_impl_set (int *AI, int v)	{ _hb_memory_w_barrier (); *AI = v; }
+#endif
+#ifndef hb_atomic_int_impl_get
+inline int hb_atomic_int_impl_get (int *AI)	{ int v = *AI; _hb_memory_r_barrier (); return v; }
+#endif
+#ifndef hb_atomic_ptr_impl_get
+inline void *hb_atomic_ptr_impl_get (void **P)	{ void *v = *P; _hb_memory_r_barrier (); return v; }
+#endif
+
+
+#define HB_ATOMIC_INT_INIT(V)          {V}
+struct hb_atomic_int_t
+{
+  inline void set_relaxed (int v_) const { hb_atomic_int_impl_set_relaxed (&v, v_); }
+  inline void set (int v_) const { hb_atomic_int_impl_set (&v, v_); }
+  inline int get_relaxed (void) const { return hb_atomic_int_impl_get_relaxed (&v); }
+  inline int get (void) const { return hb_atomic_int_impl_get (&v); }
+  inline int inc (void) { return hb_atomic_int_impl_add (&v,  1); }
+  inline int dec (void) { return hb_atomic_int_impl_add (&v, -1); }
+
+  mutable int v;
+};
+
+
+#define HB_ATOMIC_PTR_INIT(V)          {V}
+template <typename P>
+struct hb_atomic_ptr_t
+{
+  typedef typename hb_remove_pointer<P>::value T;
+
+  inline void init (T* v_ = nullptr) { set_relaxed (v_); }
+  inline void set_relaxed (T* v_) const { hb_atomic_ptr_impl_set_relaxed (&v, v_); }
+  inline T *get_relaxed (void) const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); }
+  inline T *get (void) const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); }
+  inline bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }
+
+  mutable T *v;
+};
+
+
+#endif /* HB_ATOMIC_HH */
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index 5bab1ab..51f22ce 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -25,13 +25,8 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-/* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */
-#ifndef _POSIX_C_SOURCE
-#define _POSIX_C_SOURCE 200809L
-#endif
-
-#include "hb-private.hh"
-#include "hb-blob-private.hh"
+#include "hb.hh"
+#include "hb-blob.hh"
 
 #ifdef HAVE_SYS_MMAN_H
 #ifdef HAVE_UNISTD_H
@@ -46,6 +41,33 @@
 
 
 /**
+ * SECTION: hb-blob
+ * @title: hb-blob
+ * @short_description: Binary data containers
+ * @include: hb.h
+ *
+ * Blobs wrap a chunk of binary data to handle lifecycle management of data
+ * while it is passed between client and HarfBuzz.  Blobs are primarily used
+ * to create font faces, but also to access font face tables, as well as
+ * pass around other binary data.
+ **/
+
+
+DEFINE_NULL_INSTANCE (hb_blob_t) =
+{
+  HB_OBJECT_HEADER_STATIC,
+
+  true, /* immutable */
+
+  nullptr, /* data */
+  0, /* length */
+  HB_MEMORY_MODE_READONLY, /* mode */
+
+  nullptr, /* user_data */
+  nullptr  /* destroy */
+};
+
+/**
  * hb_blob_create: (skip)
  * @data: Pointer to blob data.
  * @length: Length of @data in bytes.
@@ -182,20 +204,7 @@
 hb_blob_t *
 hb_blob_get_empty (void)
 {
-  static const hb_blob_t _hb_blob_nil = {
-    HB_OBJECT_HEADER_STATIC,
-
-    true, /* immutable */
-
-    nullptr, /* data */
-    0, /* length */
-    HB_MEMORY_MODE_READONLY, /* mode */
-
-    nullptr, /* user_data */
-    nullptr  /* destroy */
-  };
-
-  return const_cast<hb_blob_t *> (&_hb_blob_nil);
+  return const_cast<hb_blob_t *> (&Null(hb_blob_t));
 }
 
 /**
@@ -292,6 +301,8 @@
 {
   if (hb_object_is_inert (blob))
     return;
+  if (blob->immutable)
+    return;
 
   blob->immutable = true;
 }
@@ -489,8 +500,8 @@
 #if defined(_WIN32) || defined(__CYGWIN__)
 # include <windows.h>
 #else
-# ifndef _O_BINARY
-#  define _O_BINARY 0
+# ifndef O_BINARY
+#  define O_BINARY 0
 # endif
 #endif
 
@@ -509,8 +520,9 @@
 
 #if (defined(HAVE_MMAP) || defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_MMAP)
 static void
-_hb_mapped_file_destroy (hb_mapped_file_t *file)
+_hb_mapped_file_destroy (void *file_)
 {
+  hb_mapped_file_t *file = (hb_mapped_file_t *) file_;
 #ifdef HAVE_MMAP
   munmap (file->contents, file->length);
 #elif defined(_WIN32) || defined(__CYGWIN__)
@@ -541,7 +553,7 @@
   hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t));
   if (unlikely (!file)) return hb_blob_get_empty ();
 
-  int fd = open (file_name, O_RDONLY | _O_BINARY, 0);
+  int fd = open (file_name, O_RDONLY | O_BINARY, 0);
   if (unlikely (fd == -1)) goto fail_without_close;
 
   struct stat st;
@@ -573,18 +585,45 @@
   wchar_t * wchar_file_name = (wchar_t *) malloc (sizeof (wchar_t) * size);
   if (unlikely (wchar_file_name == nullptr)) goto fail_without_close;
   mbstowcs (wchar_file_name, file_name, size);
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
+  {
+    CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 };
+    ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
+    ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF;
+    ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000;
+    ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000;
+    ceparams.lpSecurityAttributes = nullptr;
+    ceparams.hTemplateFile = nullptr;
+    fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ,
+                      OPEN_EXISTING, &ceparams);
+  }
+#else
   fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr,
 		    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
 		    nullptr);
+#endif
   free (wchar_file_name);
 
   if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close;
 
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
+  {
+    LARGE_INTEGER length;
+    GetFileSizeEx (fd, &length);
+    file->length = length.LowPart;
+    file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr);
+  }
+#else
   file->length = (unsigned long) GetFileSize (fd, nullptr);
   file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr);
+#endif
   if (unlikely (file->mapping == nullptr)) goto fail;
 
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
+  file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0);
+#else
   file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0);
+#endif
   if (unlikely (file->contents == nullptr)) goto fail;
 
   CloseHandle (fd);
diff --git a/src/hb-blob.hh b/src/hb-blob.hh
new file mode 100644
index 0000000..26e2dd7
--- /dev/null
+++ b/src/hb-blob.hh
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2009  Red Hat, Inc.
+ * Copyright © 2018  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_BLOB_HH
+#define HB_BLOB_HH
+
+#include "hb.hh"
+
+
+/*
+ * hb_blob_t
+ */
+
+struct hb_blob_t
+{
+  inline void fini_shallow (void)
+  {
+    destroy_user_data ();
+  }
+
+  inline void destroy_user_data (void)
+  {
+    if (destroy)
+    {
+      destroy (user_data);
+      user_data = nullptr;
+      destroy = nullptr;
+    }
+  }
+
+  HB_INTERNAL bool try_make_writable (void);
+  HB_INTERNAL bool try_make_writable_inplace (void);
+  HB_INTERNAL bool try_make_writable_inplace_unix (void);
+
+  template <typename Type>
+  inline const Type* as (void) const
+  {
+    return unlikely (!data) ? &Null(Type) : reinterpret_cast<const Type *> (data);
+  }
+  inline hb_bytes_t as_bytes (void) const
+  {
+    return hb_bytes_t (data, length);
+  }
+
+  public:
+  hb_object_header_t header;
+
+  bool immutable;
+
+  const char *data;
+  unsigned int length;
+  hb_memory_mode_t mode;
+
+  void *user_data;
+  hb_destroy_func_t destroy;
+};
+DECLARE_NULL_INSTANCE (hb_blob_t);
+
+
+#endif /* HB_BLOB_HH */
diff --git a/src/hb-buffer-deserialize-json.hh b/src/hb-buffer-deserialize-json.hh
index 380f3c5..1f9e2e9 100644
--- a/src/hb-buffer-deserialize-json.hh
+++ b/src/hb-buffer-deserialize-json.hh
@@ -29,7 +29,7 @@
 #ifndef HB_BUFFER_DESERIALIZE_JSON_HH
 #define HB_BUFFER_DESERIALIZE_JSON_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 #line 36 "hb-buffer-deserialize-json.hh"
diff --git a/src/hb-buffer-deserialize-json.rl b/src/hb-buffer-deserialize-json.rl
index ec9bc7c..f3abb02 100644
--- a/src/hb-buffer-deserialize-json.rl
+++ b/src/hb-buffer-deserialize-json.rl
@@ -27,7 +27,7 @@
 #ifndef HB_BUFFER_DESERIALIZE_JSON_HH
 #define HB_BUFFER_DESERIALIZE_JSON_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 %%{
 
diff --git a/src/hb-buffer-deserialize-text.hh b/src/hb-buffer-deserialize-text.hh
index 5bca369..67f0a12 100644
--- a/src/hb-buffer-deserialize-text.hh
+++ b/src/hb-buffer-deserialize-text.hh
@@ -29,7 +29,7 @@
 #ifndef HB_BUFFER_DESERIALIZE_TEXT_HH
 #define HB_BUFFER_DESERIALIZE_TEXT_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 #line 36 "hb-buffer-deserialize-text.hh"
diff --git a/src/hb-buffer-deserialize-text.rl b/src/hb-buffer-deserialize-text.rl
index 1d90979..6268a6c 100644
--- a/src/hb-buffer-deserialize-text.rl
+++ b/src/hb-buffer-deserialize-text.rl
@@ -27,7 +27,7 @@
 #ifndef HB_BUFFER_DESERIALIZE_TEXT_HH
 #define HB_BUFFER_DESERIALIZE_TEXT_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 %%{
 
diff --git a/src/hb-buffer-serialize.cc b/src/hb-buffer-serialize.cc
index 1147194..1bd603d 100644
--- a/src/hb-buffer-serialize.cc
+++ b/src/hb-buffer-serialize.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-buffer-private.hh"
+#include "hb-buffer.hh"
 
 
 static const char *serialize_formats[] = {
@@ -58,7 +58,7 @@
  * @str is a valid buffer serialization format, use
  * hb_buffer_serialize_list_formats() to get the list of supported formats.
  *
- * Return value: 
+ * Return value:
  * The parsed #hb_buffer_serialize_format_t.
  *
  * Since: 0.9.7
@@ -319,7 +319,7 @@
  * ## json
  * TODO.
  *
- * Return value: 
+ * Return value:
  * The number of serialized items.
  *
  * Since: 0.9.7
@@ -425,14 +425,14 @@
  * hb_buffer_deserialize_glyphs:
  * @buffer: an #hb_buffer_t buffer.
  * @buf: (array length=buf_len):
- * @buf_len: 
+ * @buf_len:
  * @end_ptr: (out):
- * @font: 
- * @format: 
+ * @font:
+ * @format:
  *
- * 
  *
- * Return value: 
+ *
+ * Return value:
  *
  * Since: 0.9.7
  **/
@@ -440,8 +440,8 @@
 hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
 			      const char *buf,
 			      int buf_len, /* -1 means nul-terminated */
-			      const char **end_ptr, /* May be nullptr */
-			      hb_font_t *font, /* May be nullptr */
+			      const char **end_ptr, /* May be NULL */
+			      hb_font_t *font, /* May be NULL */
 			      hb_buffer_serialize_format_t format)
 {
   const char *end;
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index d04e1e8..5a8152e 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -27,20 +27,21 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-buffer-private.hh"
-#include "hb-utf-private.hh"
+#include "hb-buffer.hh"
+#include "hb-utf.hh"
 
 
 /**
  * SECTION: hb-buffer
- * @title: Buffers
+ * @title: hb-buffer
  * @short_description: Input and output buffers
  * @include: hb.h
  *
  * Buffers serve dual role in HarfBuzz; they hold the input characters that are
- * passed hb_shape(), and after shaping they hold the output glyphs.
+ * passed to hb_shape(), and after shaping they hold the output glyphs.
  **/
 
+
 /**
  * hb_segment_properties_equal:
  * @a: first #hb_segment_properties_t to compare.
@@ -182,7 +183,11 @@
   if (idx + count > len)
   {
     /* Under memory failure we might expose this area.  At least
-     * clean it up.  Oh well... */
+     * clean it up.  Oh well...
+     *
+     * Ideally, we should at least set Default_Ignorable bits on
+     * these, as well as consistent cluster values.  But the former
+     * is layering violation... */
     memset (info + len, 0, (idx + count - len) * sizeof (info[0]));
   }
   len += count;
@@ -216,9 +221,10 @@
     return;
 
   hb_unicode_funcs_destroy (unicode);
-  unicode = hb_unicode_funcs_get_default ();
+  unicode = hb_unicode_funcs_reference (hb_unicode_funcs_get_default ());
   flags = HB_BUFFER_FLAG_DEFAULT;
   replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
+  invisible = 0;
 
   clear ();
 }
@@ -354,6 +360,8 @@
 {
   if (unlikely (!make_room_for (num_in, num_out))) return;
 
+  assert (idx + num_in <= len);
+
   merge_clusters (idx, idx + num_in);
 
   hb_glyph_info_t orig_info = info[idx];
@@ -369,37 +377,6 @@
   out_len += num_out;
 }
 
-void
-hb_buffer_t::output_glyph (hb_codepoint_t glyph_index)
-{
-  if (unlikely (!make_room_for (0, 1))) return;
-
-  out_info[out_len] = info[idx];
-  out_info[out_len].codepoint = glyph_index;
-
-  out_len++;
-}
-
-void
-hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info)
-{
-  if (unlikely (!make_room_for (0, 1))) return;
-
-  out_info[out_len] = glyph_info;
-
-  out_len++;
-}
-
-void
-hb_buffer_t::copy_glyph (void)
-{
-  if (unlikely (!make_room_for (0, 1))) return;
-
-  out_info[out_len] = info[idx];
-
-  out_len++;
-}
-
 bool
 hb_buffer_t::move_to (unsigned int i)
 {
@@ -429,8 +406,14 @@
     unsigned int count = out_len - i;
 
     /* This will blow in our face if memory allocation fails later
-     * in this same lookup... */
-    if (unlikely (idx < count && !shift_forward (count + 32))) return false;
+     * in this same lookup...
+     *
+     * We used to shift with extra 32 items, instead of the 0 below.
+     * But that would leave empty slots in the buffer in case of allocation
+     * failures.  Setting to zero for now to avoid other problems (see
+     * comments in shift_forward().  This can cause O(N^2) behavior more
+     * severely than adding 32 empty slots can... */
+    if (unlikely (idx < count && !shift_forward (count + 0))) return false;
 
     assert (idx >= count);
 
@@ -442,19 +425,6 @@
   return true;
 }
 
-void
-hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index)
-{
-  if (unlikely (out_info != info || out_len != idx)) {
-    if (unlikely (!make_room_for (1, 1))) return;
-    out_info[out_len] = info[idx];
-  }
-  out_info[out_len].codepoint = glyph_index;
-
-  idx++;
-  out_len++;
-}
-
 
 void
 hb_buffer_t::set_masks (hb_mask_t    value,
@@ -701,6 +671,29 @@
 
 /* Public API */
 
+DEFINE_NULL_INSTANCE (hb_buffer_t) =
+{
+  HB_OBJECT_HEADER_STATIC,
+
+  const_cast<hb_unicode_funcs_t *> (&_hb_Null_hb_unicode_funcs_t),
+  HB_BUFFER_FLAG_DEFAULT,
+  HB_BUFFER_CLUSTER_LEVEL_DEFAULT,
+  HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
+  0, /* invisible */
+  HB_BUFFER_SCRATCH_FLAG_DEFAULT,
+  HB_BUFFER_MAX_LEN_DEFAULT,
+  HB_BUFFER_MAX_OPS_DEFAULT,
+
+  HB_BUFFER_CONTENT_TYPE_INVALID,
+  HB_SEGMENT_PROPERTIES_DEFAULT,
+  false, /* successful */
+  true, /* have_output */
+  true  /* have_positions */
+
+  /* Zero is good enough for everything else. */
+};
+
+
 /**
  * hb_buffer_create: (Xconstructor)
  *
@@ -743,27 +736,7 @@
 hb_buffer_t *
 hb_buffer_get_empty (void)
 {
-  static const hb_buffer_t _hb_buffer_nil = {
-    HB_OBJECT_HEADER_STATIC,
-
-    const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
-    HB_BUFFER_FLAG_DEFAULT,
-    HB_BUFFER_CLUSTER_LEVEL_DEFAULT,
-    HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
-    HB_BUFFER_SCRATCH_FLAG_DEFAULT,
-    HB_BUFFER_MAX_LEN_DEFAULT,
-    HB_BUFFER_MAX_OPS_DEFAULT,
-
-    HB_BUFFER_CONTENT_TYPE_INVALID,
-    HB_SEGMENT_PROPERTIES_DEFAULT,
-    false, /* successful */
-    true, /* have_output */
-    true  /* have_positions */
-
-    /* Zero is good enough for everything else. */
-  };
-
-  return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
+  return const_cast<hb_buffer_t *> (&Null(hb_buffer_t));
 }
 
 /**
@@ -906,7 +879,6 @@
   if (!unicode_funcs)
     unicode_funcs = hb_unicode_funcs_get_default ();
 
-
   hb_unicode_funcs_reference (unicode_funcs);
   hb_unicode_funcs_destroy (buffer->unicode);
   buffer->unicode = unicode_funcs;
@@ -1027,7 +999,7 @@
  * are orthogonal to the scripts, and though they are related, they are
  * different concepts and should not be confused with each other.
  *
- * Use hb_language_from_string() to convert from ISO 639 language codes to
+ * Use hb_language_from_string() to convert from BCP 47 language tags to
  * #hb_language_t.
  *
  * Since: 0.9.2
@@ -1210,6 +1182,46 @@
 
 
 /**
+ * hb_buffer_set_invisible_glyph:
+ * @buffer: an #hb_buffer_t.
+ * @invisible: the invisible #hb_codepoint_t
+ *
+ * Sets the #hb_codepoint_t that replaces invisible characters in
+ * the shaping result.  If set to zero (default), the glyph for the
+ * U+0020 SPACE character is used.  Otherwise, this value is used
+ * verbatim.
+ *
+ * Since: 2.0.0
+ **/
+void
+hb_buffer_set_invisible_glyph (hb_buffer_t    *buffer,
+			       hb_codepoint_t  invisible)
+{
+  if (unlikely (hb_object_is_inert (buffer)))
+    return;
+
+  buffer->invisible = invisible;
+}
+
+/**
+ * hb_buffer_get_invisible_glyph:
+ * @buffer: an #hb_buffer_t.
+ *
+ * See hb_buffer_set_invisible_glyph().
+ *
+ * Return value: 
+ * The @buffer invisible #hb_codepoint_t.
+ *
+ * Since: 2.0.0
+ **/
+hb_codepoint_t
+hb_buffer_get_invisible_glyph (hb_buffer_t    *buffer)
+{
+  return buffer->invisible;
+}
+
+
+/**
  * hb_buffer_reset:
  * @buffer: an #hb_buffer_t.
  *
@@ -1498,6 +1510,8 @@
  * it will be set to the process's default language as returned by
  * hb_language_get_default().  This may change in the future by
  * taking buffer script into consideration when choosing a language.
+ * Note that hb_language_get_default() is NOT threadsafe the first time
+ * it is called.  See documentation for that function for details.
  *
  * Since: 0.9.7
  **/
@@ -1652,7 +1666,7 @@
 		     unsigned int    item_offset,
 		     int             item_length)
 {
-  hb_buffer_add_utf<hb_utf32_t<> > (buffer, text, text_length, item_offset, item_length);
+  hb_buffer_add_utf<hb_utf32_t> (buffer, text, text_length, item_offset, item_length);
 }
 
 /**
@@ -1713,7 +1727,7 @@
 			  unsigned int          item_offset,
 			  int                   item_length)
 {
-  hb_buffer_add_utf<hb_utf32_t<false> > (buffer, text, text_length, item_offset, item_length);
+  hb_buffer_add_utf<hb_utf32_novalidate_t> (buffer, text, text_length, item_offset, item_length);
 }
 
 
@@ -1886,6 +1900,10 @@
 
 /**
  * hb_buffer_diff:
+ * @buffer: a buffer.
+ * @reference: other buffer to compare to.
+ * @dottedcircle_glyph: glyph id of U+25CC DOTTED CIRCLE, or (hb_codepont_t) -1.
+ * @position_fuzz: allowed absolute difference in position values.
  *
  * If dottedcircle_glyph is (hb_codepoint_t) -1 then %HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
  * and %HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned.  This should be used by most
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index 8a2d3e8..99e0171 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -44,7 +44,6 @@
  * hb_glyph_info_t:
  * @codepoint: either a Unicode code point (before shaping) or a glyph index
  *             (after shaping).
- * @mask: 
  * @cluster: the index of the character in the original text that corresponds
  *           to this #hb_glyph_info_t, or whatever the client passes to
  *           hb_buffer_add(). More than one #hb_glyph_info_t can have the same
@@ -59,11 +58,13 @@
  *
  * The #hb_glyph_info_t is the structure that holds information about the
  * glyphs and their relation to input text.
- *
  */
-typedef struct hb_glyph_info_t {
+typedef struct hb_glyph_info_t
+{
   hb_codepoint_t codepoint;
-  hb_mask_t      mask; /* Holds hb_glyph_flags_t after hb_shape(), plus other things. */
+  /*< private >*/
+  hb_mask_t      mask;
+  /*< public >*/
   uint32_t       cluster;
 
   /*< private >*/
@@ -88,6 +89,7 @@
  * 				   of each line after line-breaking, or limiting
  * 				   the reshaping to a small piece around the
  * 				   breaking point only.
+ * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags.
  */
 typedef enum { /*< flags >*/
   HB_GLYPH_FLAG_UNSAFE_TO_BREAK		= 0x00000001,
@@ -298,7 +300,15 @@
 HB_EXTERN hb_buffer_flags_t
 hb_buffer_get_flags (hb_buffer_t *buffer);
 
-/*
+/**
+ * hb_buffer_cluster_level_t:
+ * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES: Return cluster values grouped by graphemes into
+ *   monotone order.
+ * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS: Return cluster values grouped into monotone order.
+ * @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: Don't group cluster values.
+ * @HB_BUFFER_CLUSTER_LEVEL_DEFAULT: Default cluster level,
+ *   equal to @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES.
+ *
  * Since: 0.9.42
  */
 typedef enum {
@@ -332,6 +342,13 @@
 HB_EXTERN hb_codepoint_t
 hb_buffer_get_replacement_codepoint (hb_buffer_t    *buffer);
 
+HB_EXTERN void
+hb_buffer_set_invisible_glyph (hb_buffer_t    *buffer,
+			       hb_codepoint_t  invisible);
+
+HB_EXTERN hb_codepoint_t
+hb_buffer_get_invisible_glyph (hb_buffer_t    *buffer);
+
 
 HB_EXTERN void
 hb_buffer_reset (hb_buffer_t *buffer);
diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh
new file mode 100644
index 0000000..37adeb0
--- /dev/null
+++ b/src/hb-buffer.hh
@@ -0,0 +1,446 @@
+/*
+ * Copyright © 1998-2004  David Turner and Werner Lemberg
+ * Copyright © 2004,2007,2009,2010  Red Hat, Inc.
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_BUFFER_HH
+#define HB_BUFFER_HH
+
+#include "hb.hh"
+#include "hb-unicode.hh"
+
+
+#ifndef HB_BUFFER_MAX_LEN_FACTOR
+#define HB_BUFFER_MAX_LEN_FACTOR 32
+#endif
+#ifndef HB_BUFFER_MAX_LEN_MIN
+#define HB_BUFFER_MAX_LEN_MIN 8192
+#endif
+#ifndef HB_BUFFER_MAX_LEN_DEFAULT
+#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */
+#endif
+
+#ifndef HB_BUFFER_MAX_OPS_FACTOR
+#define HB_BUFFER_MAX_OPS_FACTOR 64
+#endif
+#ifndef HB_BUFFER_MAX_OPS_MIN
+#define HB_BUFFER_MAX_OPS_MIN 1024
+#endif
+#ifndef HB_BUFFER_MAX_OPS_DEFAULT
+#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */
+#endif
+
+static_assert ((sizeof (hb_glyph_info_t) == 20), "");
+static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), "");
+
+HB_MARK_AS_FLAG_T (hb_buffer_flags_t);
+HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t);
+HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t);
+
+enum hb_buffer_scratch_flags_t {
+  HB_BUFFER_SCRATCH_FLAG_DEFAULT			= 0x00000000u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII			= 0x00000001u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES		= 0x00000002u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK		= 0x00000004u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT		= 0x00000008u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK		= 0x00000010u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_CGJ			= 0x00000020u,
+
+  /* Reserved for complex shapers' internal use. */
+  HB_BUFFER_SCRATCH_FLAG_COMPLEX0			= 0x01000000u,
+  HB_BUFFER_SCRATCH_FLAG_COMPLEX1			= 0x02000000u,
+  HB_BUFFER_SCRATCH_FLAG_COMPLEX2			= 0x04000000u,
+  HB_BUFFER_SCRATCH_FLAG_COMPLEX3			= 0x08000000u,
+};
+HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t);
+
+
+/*
+ * hb_buffer_t
+ */
+
+struct hb_buffer_t
+{
+  hb_object_header_t header;
+
+  /* Information about how the text in the buffer should be treated */
+  hb_unicode_funcs_t *unicode; /* Unicode functions */
+  hb_buffer_flags_t flags; /* BOT / EOT / etc. */
+  hb_buffer_cluster_level_t cluster_level;
+  hb_codepoint_t replacement; /* U+FFFD or something else. */
+  hb_codepoint_t invisible; /* 0 or something else. */
+  hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */
+  unsigned int max_len; /* Maximum allowed len. */
+  int max_ops; /* Maximum allowed operations. */
+
+  /* Buffer contents */
+  hb_buffer_content_type_t content_type;
+  hb_segment_properties_t props; /* Script, language, direction */
+
+  bool successful; /* Allocations successful */
+  bool have_output; /* Whether we have an output buffer going on */
+  bool have_positions; /* Whether we have positions */
+
+  unsigned int idx; /* Cursor into ->info and ->pos arrays */
+  unsigned int len; /* Length of ->info and ->pos arrays */
+  unsigned int out_len; /* Length of ->out array if have_output */
+
+  unsigned int allocated; /* Length of allocated arrays */
+  hb_glyph_info_t     *info;
+  hb_glyph_info_t     *out_info;
+  hb_glyph_position_t *pos;
+
+  unsigned int serial;
+
+  /* Text before / after the main buffer contents.
+   * Always in Unicode, and ordered outward.
+   * Index 0 is for "pre-context", 1 for "post-context". */
+  enum { CONTEXT_LENGTH = 5 };
+  hb_codepoint_t context[2][CONTEXT_LENGTH];
+  unsigned int context_len[2];
+
+  /* Debugging API */
+  hb_buffer_message_func_t message_func;
+  void *message_data;
+  hb_destroy_func_t message_destroy;
+
+  /* Internal debugging. */
+  /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */
+#ifndef HB_NDEBUG
+  uint8_t allocated_var_bits;
+#endif
+
+
+  /* Methods */
+
+  inline void allocate_var (unsigned int start, unsigned int count)
+  {
+#ifndef HB_NDEBUG
+    unsigned int end = start + count;
+    assert (end <= 8);
+    unsigned int bits = (1u<<end) - (1u<<start);
+    assert (0 == (allocated_var_bits & bits));
+    allocated_var_bits |= bits;
+#endif
+  }
+  inline void deallocate_var (unsigned int start, unsigned int count)
+  {
+#ifndef HB_NDEBUG
+    unsigned int end = start + count;
+    assert (end <= 8);
+    unsigned int bits = (1u<<end) - (1u<<start);
+    assert (bits == (allocated_var_bits & bits));
+    allocated_var_bits &= ~bits;
+#endif
+  }
+  inline void assert_var (unsigned int start, unsigned int count)
+  {
+#ifndef HB_NDEBUG
+    unsigned int end = start + count;
+    assert (end <= 8);
+    unsigned int bits = (1u<<end) - (1u<<start);
+    assert (bits == (allocated_var_bits & bits));
+#endif
+  }
+  inline void deallocate_var_all (void)
+  {
+#ifndef HB_NDEBUG
+    allocated_var_bits = 0;
+#endif
+  }
+
+  inline hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
+  inline hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; }
+
+  inline hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
+  inline hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
+
+  inline hb_glyph_info_t &prev (void) { return out_info[out_len ? out_len - 1 : 0]; }
+  inline hb_glyph_info_t prev (void) const { return out_info[out_len ? out_len - 1 : 0]; }
+
+  inline bool has_separate_output (void) const { return info != out_info; }
+
+
+  HB_INTERNAL void reset (void);
+  HB_INTERNAL void clear (void);
+
+  inline unsigned int backtrack_len (void) const
+  { return have_output? out_len : idx; }
+  inline unsigned int lookahead_len (void) const
+  { return len - idx; }
+  inline unsigned int next_serial (void) { return serial++; }
+
+  HB_INTERNAL void add (hb_codepoint_t  codepoint,
+			unsigned int    cluster);
+  HB_INTERNAL void add_info (const hb_glyph_info_t &glyph_info);
+
+  HB_INTERNAL void reverse_range (unsigned int start, unsigned int end);
+  HB_INTERNAL void reverse (void);
+  HB_INTERNAL void reverse_clusters (void);
+  HB_INTERNAL void guess_segment_properties (void);
+
+  HB_INTERNAL void swap_buffers (void);
+  HB_INTERNAL void remove_output (void);
+  HB_INTERNAL void clear_output (void);
+  HB_INTERNAL void clear_positions (void);
+
+  HB_INTERNAL void replace_glyphs (unsigned int num_in,
+				   unsigned int num_out,
+				   const hb_codepoint_t *glyph_data);
+
+  inline void replace_glyph (hb_codepoint_t glyph_index)
+  {
+    if (unlikely (out_info != info || out_len != idx)) {
+      if (unlikely (!make_room_for (1, 1))) return;
+      out_info[out_len] = info[idx];
+    }
+    out_info[out_len].codepoint = glyph_index;
+
+    idx++;
+    out_len++;
+  }
+  /* Makes a copy of the glyph at idx to output and replace glyph_index */
+  inline hb_glyph_info_t & output_glyph (hb_codepoint_t glyph_index)
+  {
+    if (unlikely (!make_room_for (0, 1))) return Crap(hb_glyph_info_t);
+
+    if (unlikely (idx == len && !out_len))
+      return Crap(hb_glyph_info_t);
+
+    out_info[out_len] = idx < len ? info[idx] : out_info[out_len - 1];
+    out_info[out_len].codepoint = glyph_index;
+
+    out_len++;
+
+    return out_info[out_len - 1];
+  }
+  inline void output_info (const hb_glyph_info_t &glyph_info)
+  {
+    if (unlikely (!make_room_for (0, 1))) return;
+
+    out_info[out_len] = glyph_info;
+
+    out_len++;
+  }
+  /* Copies glyph at idx to output but doesn't advance idx */
+  inline void copy_glyph (void)
+  {
+    if (unlikely (!make_room_for (0, 1))) return;
+
+    out_info[out_len] = info[idx];
+
+    out_len++;
+  }
+  /* Copies glyph at idx to output and advance idx.
+   * If there's no output, just advance idx. */
+  inline void
+  next_glyph (void)
+  {
+    if (have_output)
+    {
+      if (out_info != info || out_len != idx)
+      {
+	if (unlikely (!make_room_for (1, 1))) return;
+	out_info[out_len] = info[idx];
+      }
+      out_len++;
+    }
+
+    idx++;
+  }
+  /* Copies n glyphs at idx to output and advance idx.
+   * If there's no output, just advance idx. */
+  inline void
+  next_glyphs (unsigned int n)
+  {
+    if (have_output)
+    {
+      if (out_info != info || out_len != idx)
+      {
+	if (unlikely (!make_room_for (n, n))) return;
+	memmove (out_info + out_len, info + idx, n * sizeof (out_info[0]));
+      }
+      out_len += n;
+    }
+
+    idx += n;
+  }
+  /* Advance idx without copying to output. */
+  inline void skip_glyph (void)
+  {
+    idx++;
+  }
+  inline void reset_masks (hb_mask_t mask)
+  {
+    for (unsigned int j = 0; j < len; j++)
+      info[j].mask = mask;
+  }
+  inline void add_masks (hb_mask_t mask)
+  {
+    for (unsigned int j = 0; j < len; j++)
+      info[j].mask |= mask;
+  }
+  HB_INTERNAL void set_masks (hb_mask_t value, hb_mask_t mask,
+			      unsigned int cluster_start, unsigned int cluster_end);
+
+  inline void merge_clusters (unsigned int start, unsigned int end)
+  {
+    if (end - start < 2)
+      return;
+    merge_clusters_impl (start, end);
+  }
+  HB_INTERNAL void merge_clusters_impl (unsigned int start, unsigned int end);
+  HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end);
+  /* Merge clusters for deleting current glyph, and skip it. */
+  HB_INTERNAL void delete_glyph (void);
+
+  inline void unsafe_to_break (unsigned int start,
+			       unsigned int end)
+  {
+    if (end - start < 2)
+      return;
+    unsafe_to_break_impl (start, end);
+  }
+  HB_INTERNAL void unsafe_to_break_impl (unsigned int start, unsigned int end);
+  HB_INTERNAL void unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end);
+
+
+  /* Internal methods */
+  HB_INTERNAL bool move_to (unsigned int i); /* i is output-buffer index. */
+
+  HB_INTERNAL bool enlarge (unsigned int size);
+
+  inline bool ensure (unsigned int size)
+  { return likely (!size || size < allocated) ? true : enlarge (size); }
+
+  inline bool ensure_inplace (unsigned int size)
+  { return likely (!size || size < allocated); }
+
+  HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out);
+  HB_INTERNAL bool shift_forward (unsigned int count);
+
+  typedef long scratch_buffer_t;
+  HB_INTERNAL scratch_buffer_t *get_scratch_buffer (unsigned int *size);
+
+  inline void clear_context (unsigned int side) { context_len[side] = 0; }
+
+  HB_INTERNAL void sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *));
+
+  inline bool messaging (void) { return unlikely (message_func); }
+  inline bool message (hb_font_t *font, const char *fmt, ...) HB_PRINTF_FUNC(3, 4)
+  {
+    if (!messaging ())
+      return true;
+    va_list ap;
+    va_start (ap, fmt);
+    bool ret = message_impl (font, fmt, ap);
+    va_end (ap);
+    return ret;
+  }
+  HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0);
+
+  static inline void
+  set_cluster (hb_glyph_info_t &inf, unsigned int cluster, unsigned int mask = 0)
+  {
+    if (inf.cluster != cluster)
+    {
+      if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
+	inf.mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+      else
+	inf.mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+    }
+    inf.cluster = cluster;
+  }
+
+  inline int
+  _unsafe_to_break_find_min_cluster (const hb_glyph_info_t *infos,
+				     unsigned int start, unsigned int end,
+				     unsigned int cluster) const
+  {
+    for (unsigned int i = start; i < end; i++)
+      cluster = MIN<unsigned int> (cluster, infos[i].cluster);
+    return cluster;
+  }
+  inline void
+  _unsafe_to_break_set_mask (hb_glyph_info_t *infos,
+			     unsigned int start, unsigned int end,
+			     unsigned int cluster)
+  {
+    for (unsigned int i = start; i < end; i++)
+      if (cluster != infos[i].cluster)
+      {
+	scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK;
+	infos[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+      }
+  }
+
+  inline void
+  unsafe_to_break_all (void)
+  {
+    unsafe_to_break_impl (0, len);
+  }
+  inline void
+  safe_to_break_all (void)
+  {
+    for (unsigned int i = 0; i < len; i++)
+      info[i].mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+  }
+};
+DECLARE_NULL_INSTANCE (hb_buffer_t);
+
+
+/* Loop over clusters. Duplicated in foreach_syllable(). */
+#define foreach_cluster(buffer, start, end) \
+  for (unsigned int \
+       _count = buffer->len, \
+       start = 0, end = _count ? _next_cluster (buffer, 0) : 0; \
+       start < _count; \
+       start = end, end = _next_cluster (buffer, start))
+
+static inline unsigned int
+_next_cluster (hb_buffer_t *buffer, unsigned int start)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+
+  unsigned int cluster = info[start].cluster;
+  while (++start < count && cluster == info[start].cluster)
+    ;
+
+  return start;
+}
+
+
+#define HB_BUFFER_XALLOCATE_VAR(b, func, var) \
+  b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \
+	   sizeof (b->info[0].var))
+#define HB_BUFFER_ALLOCATE_VAR(b, var)		HB_BUFFER_XALLOCATE_VAR (b, allocate_var,   var ())
+#define HB_BUFFER_DEALLOCATE_VAR(b, var)	HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ())
+#define HB_BUFFER_ASSERT_VAR(b, var)		HB_BUFFER_XALLOCATE_VAR (b, assert_var,     var ())
+
+
+#endif /* HB_BUFFER_HH */
diff --git a/src/hb-cache.hh b/src/hb-cache.hh
new file mode 100644
index 0000000..eb48f18
--- /dev/null
+++ b/src/hb-cache.hh
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_CACHE_HH
+#define HB_CACHE_HH
+
+#include "hb.hh"
+
+
+/* Implements a lock-free cache for int->int functions. */
+
+template <unsigned int key_bits, unsigned int value_bits, unsigned int cache_bits>
+struct hb_cache_t
+{
+  static_assert ((key_bits >= cache_bits), "");
+  static_assert ((key_bits + value_bits - cache_bits <= 8 * sizeof (hb_atomic_int_t)), "");
+  static_assert (sizeof (hb_atomic_int_t) == sizeof (unsigned int), "");
+
+  inline void init (void) { clear (); }
+  inline void fini (void) {}
+
+  inline void clear (void)
+  {
+    for (unsigned i = 0; i < ARRAY_LENGTH (values); i++)
+      values[i].set_relaxed (-1);
+  }
+
+  inline bool get (unsigned int key, unsigned int *value) const
+  {
+    unsigned int k = key & ((1u<<cache_bits)-1);
+    unsigned int v = values[k].get_relaxed ();
+    if ((key_bits + value_bits - cache_bits == 8 * sizeof (hb_atomic_int_t) && v == (unsigned int) -1) ||
+	(v >> value_bits) != (key >> cache_bits))
+      return false;
+    *value = v & ((1u<<value_bits)-1);
+    return true;
+  }
+
+  inline bool set (unsigned int key, unsigned int value)
+  {
+    if (unlikely ((key >> key_bits) || (value >> value_bits)))
+      return false; /* Overflows */
+    unsigned int k = key & ((1u<<cache_bits)-1);
+    unsigned int v = ((key>>cache_bits)<<value_bits) | value;
+    values[k].set_relaxed (v);
+    return true;
+  }
+
+  private:
+  hb_atomic_int_t values[1u<<cache_bits];
+};
+
+typedef hb_cache_t<21, 16, 8> hb_cmap_cache_t;
+typedef hb_cache_t<16, 24, 8> hb_advance_cache_t;
+
+
+#endif /* HB_CACHE_HH */
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 8ec269e..d7c1921 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -26,8 +26,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
+#include "hb-machinery.hh"
 
 #include <locale.h>
 #ifdef HAVE_XLOCALE_H
@@ -35,22 +36,51 @@
 #endif
 
 
+/**
+ * SECTION:hb-common
+ * @title: hb-common
+ * @short_description: Common data types
+ * @include: hb.h
+ *
+ * Common data types used across HarfBuzz are defined here.
+ **/
+
+
 /* hb_options_t */
 
-hb_options_union_t _hb_options;
+hb_atomic_int_t _hb_options;
 
 void
 _hb_options_init (void)
 {
   hb_options_union_t u;
   u.i = 0;
-  u.opts.initialized = 1;
+  u.opts.initialized = true;
 
-  char *c = getenv ("HB_OPTIONS");
-  u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
+  const char *c = getenv ("HB_OPTIONS");
+  if (c)
+  {
+    while (*c)
+    {
+      const char *p = strchr (c, ':');
+      if (!p)
+        p = c + strlen (c);
+
+#define OPTION(name, symbol) \
+	if (0 == strncmp (c, name, p - c) && strlen (name) == p - c) u.opts.symbol = true;
+
+      OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
+      OPTION ("aat", aat);
+
+#undef OPTION
+
+      c = *p ? p + 1 : p;
+    }
+
+  }
 
   /* This is idempotent and threadsafe. */
-  _hb_options = u;
+  _hb_options.set_relaxed (u.i);
 }
 
 
@@ -244,15 +274,15 @@
 
 /* Thread-safe lock-free language list */
 
-static hb_language_item_t *langs;
+static hb_atomic_ptr_t <hb_language_item_t> langs;
 
 #ifdef HB_USE_ATEXIT
 static void
 free_langs (void)
 {
 retry:
-  hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
-  if (!hb_atomic_ptr_cmpexch (&langs, first_lang, nullptr))
+  hb_language_item_t *first_lang = langs.get ();
+  if (unlikely (!langs.cmpexch (first_lang, nullptr)))
     goto retry;
 
   while (first_lang) {
@@ -268,7 +298,7 @@
 lang_find_or_insert (const char *key)
 {
 retry:
-  hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
+  hb_language_item_t *first_lang = langs.get ();
 
   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
     if (*lang == key)
@@ -286,7 +316,8 @@
     return nullptr;
   }
 
-  if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
+  if (unlikely (!langs.cmpexch (first_lang, lang)))
+  {
     lang->fini ();
     free (lang);
     goto retry;
@@ -304,14 +335,14 @@
 /**
  * hb_language_from_string:
  * @str: (array length=len) (element-type uint8_t): a string representing
- *       ISO 639 language code
+ *       a BCP 47 language tag
  * @len: length of the @str, or -1 if it is %NULL-terminated.
  *
- * Converts @str representing an ISO 639 language code to the corresponding
+ * Converts @str representing a BCP 47 language tag to the corresponding
  * #hb_language_t.
  *
  * Return value: (transfer none):
- * The #hb_language_t corresponding to the ISO 639 language code.
+ * The #hb_language_t corresponding to the BCP 47 language tag.
  *
  * Since: 0.9.2
  **/
@@ -359,7 +390,14 @@
 /**
  * hb_language_get_default:
  *
+ * Get default language from current locale.
  *
+ * Note that the first time this function is called, it calls
+ * "setlocale (LC_CTYPE, nullptr)" to fetch current locale.  The underlying
+ * setlocale function is, in many implementations, NOT threadsafe.  To avoid
+ * problems, call this function once before multiple threads can call it.
+ * This function is only used from hb_buffer_guess_segment_properties() by
+ * HarfBuzz itself.
  *
  * Return value: (transfer none):
  *
@@ -368,15 +406,16 @@
 hb_language_t
 hb_language_get_default (void)
 {
-  static hb_language_t default_language = HB_LANGUAGE_INVALID;
+  static hb_atomic_ptr_t <hb_language_t> default_language;
 
-  hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
-  if (unlikely (language == HB_LANGUAGE_INVALID)) {
+  hb_language_t language = default_language.get ();
+  if (unlikely (language == HB_LANGUAGE_INVALID))
+  {
     language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
-    (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
+    (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
   }
 
-  return default_language;
+  return language;
 }
 
 
@@ -528,7 +567,6 @@
 
     /* Unicode-8.0 additions */
     case HB_SCRIPT_HATRAN:
-    case HB_SCRIPT_OLD_HUNGARIAN:
 
     /* Unicode-9.0 additions */
     case HB_SCRIPT_ADLAM:
@@ -542,6 +580,7 @@
 
 
     /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
+    case HB_SCRIPT_OLD_HUNGARIAN:
     case HB_SCRIPT_OLD_ITALIC:
     case HB_SCRIPT_RUNIC:
 
@@ -586,6 +625,19 @@
 
 /* hb_version */
 
+
+/**
+ * SECTION:hb-version
+ * @title: hb-version
+ * @short_description: Information about the version of HarfBuzz in use
+ * @include: hb.h
+ *
+ * These functions and macros allow accessing version of the HarfBuzz
+ * library used at compile- as well as run-time, and to direct code
+ * conditionally based on those versions, again, at compile- or run-time.
+ **/
+
+
 /**
  * hb_version:
  * @major: (out): Library major version component.
@@ -728,47 +780,47 @@
 
 #ifdef USE_XLOCALE
 
-static HB_LOCALE_T C_locale;
+#ifdef HB_USE_ATEXIT
+static void free_static_C_locale (void);
+#endif
+
+static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<HB_LOCALE_T>::value,
+							  hb_C_locale_lazy_loader_t>
+{
+  static inline HB_LOCALE_T create (void)
+  {
+    HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C");
 
 #ifdef HB_USE_ATEXIT
-static void
-free_C_locale (void)
+    atexit (free_static_C_locale);
+#endif
+
+    return C_locale;
+  }
+  static inline void destroy (HB_LOCALE_T p)
+  {
+    HB_FREE_LOCALE (p);
+  }
+  static inline HB_LOCALE_T get_null (void)
+  {
+    return nullptr;
+  }
+} static_C_locale;
+
+#ifdef HB_USE_ATEXIT
+static
+void free_static_C_locale (void)
 {
-retry:
-  HB_LOCALE_T locale = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
-
-  if (!hb_atomic_ptr_cmpexch (&C_locale, locale, nullptr))
-    goto retry;
-
-  if (locale)
-    HB_FREE_LOCALE (locale);
+  static_C_locale.free_instance ();
 }
 #endif
 
 static HB_LOCALE_T
 get_C_locale (void)
 {
-retry:
-  HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
-
-  if (unlikely (!C))
-  {
-    C = HB_CREATE_LOCALE ("C");
-
-    if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C))
-    {
-      HB_FREE_LOCALE (C_locale);
-      goto retry;
-    }
-
-#ifdef HB_USE_ATEXIT
-    atexit (free_C_locale); /* First person registers atexit() callback. */
-#endif
-  }
-
-  return C;
+  return static_C_locale.get_unconst ();
 }
-#endif
+#endif /* USE_XLOCALE */
 
 static bool
 parse_float (const char **pp, const char *end, float *pv)
@@ -845,7 +897,7 @@
   }
 
   const char *p = *pp;
-  while (*pp < end && ISALNUM(**pp))
+  while (*pp < end && (ISALNUM(**pp) || **pp == '_'))
     (*pp)++;
 
   if (p == *pp || *pp - p > 4)
@@ -874,15 +926,15 @@
 
   bool has_start;
 
-  feature->start = 0;
-  feature->end = (unsigned int) -1;
+  feature->start = HB_FEATURE_GLOBAL_START;
+  feature->end = HB_FEATURE_GLOBAL_END;
 
   if (!parse_char (pp, end, '['))
     return true;
 
   has_start = parse_uint (pp, end, &feature->start);
 
-  if (parse_char (pp, end, ':')) {
+  if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
     parse_uint (pp, end, &feature->end);
   } else {
     if (has_start)
@@ -1062,7 +1114,7 @@
   while (len && s[len - 1] == ' ')
     len--;
   s[len++] = '=';
-  len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
+  len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
 
   assert (len < ARRAY_LENGTH (s));
   len = MIN (len, size - 1);
diff --git a/src/hb-common.h b/src/hb-common.h
index 5dc1ebc..ae23698 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -33,6 +33,10 @@
 #ifndef HB_COMMON_H
 #define HB_COMMON_H
 
+#ifndef HB_EXTERN
+#define HB_EXTERN extern
+#endif
+
 #ifndef HB_BEGIN_DECLS
 # ifdef __cplusplus
 #  define HB_BEGIN_DECLS	extern "C" {
@@ -63,6 +67,23 @@
 #  include <stdint.h>
 #endif
 
+#if    __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+#define HB_DEPRECATED __attribute__((__deprecated__))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1300)
+#define HB_DEPRECATED __declspec(deprecated)
+#else
+#define HB_DEPRECATED
+#endif
+
+#if    __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+#define HB_DEPRECATED_FOR(f) __attribute__((__deprecated__("Use '" #f "' instead")))
+#elif defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320)
+#define HB_DEPRECATED_FOR(f) __declspec(deprecated("is deprecated. Use '" #f "' instead"))
+#else
+#define HB_DEPRECATED_FOR(f) HB_DEPRECATED
+#endif
+
+
 HB_BEGIN_DECLS
 
 
@@ -86,8 +107,8 @@
 
 typedef uint32_t hb_tag_t;
 
-#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4))))
-#define HB_UNTAG(tag)   ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag))
+#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint32_t)(c1)&0xFF)<<24)|(((uint32_t)(c2)&0xFF)<<16)|(((uint32_t)(c3)&0xFF)<<8)|((uint32_t)(c4)&0xFF)))
+#define HB_UNTAG(tag)   (((tag)>>24)&0xFF), (((tag)>>16)&0xFF), (((tag)>>8)&0xFF), ((tag)&0xFF)
 
 #define HB_TAG_NONE HB_TAG(0,0,0,0)
 #define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff)
@@ -340,13 +361,15 @@
   HB_SCRIPT_INVALID				= HB_TAG_NONE,
 
   /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t
-   * without risking undefined behavior.  Include both a signed and unsigned max,
-   * since technically enums are int, and indeed, hb_script_t ends up being signed.
+   * without risking undefined behavior.  We have two, for historical reasons.
+   * HB_TAG_MAX used to be unsigned, but that was invalid Ansi C, so was changed
+   * to _HB_SCRIPT_MAX_VALUE to be equal to HB_TAG_MAX_SIGNED as well.
+   *
    * See this thread for technicalities:
    *
    *   https://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html
    */
-  _HB_SCRIPT_MAX_VALUE				= HB_TAG_MAX, /*< skip >*/
+  _HB_SCRIPT_MAX_VALUE				= HB_TAG_MAX_SIGNED, /*< skip >*/
   _HB_SCRIPT_MAX_VALUE_SIGNED			= HB_TAG_MAX_SIGNED /*< skip >*/
 
 } hb_script_t;
@@ -379,6 +402,19 @@
 
 /* Font features and variations. */
 
+/**
+ * HB_FEATURE_GLOBAL_START
+ *
+ * Since: 2.0.0
+ */
+#define HB_FEATURE_GLOBAL_START	0
+/**
+ * HB_FEATURE_GLOBAL_END
+ *
+ * Since: 2.0.0
+ */
+#define HB_FEATURE_GLOBAL_END	((unsigned int) -1)
+
 typedef struct hb_feature_t {
   hb_tag_t      tag;
   uint32_t      value;
@@ -412,6 +448,50 @@
 hb_variation_to_string (hb_variation_t *variation,
 			char *buf, unsigned int size);
 
+/**
+ * hb_color_t:
+ *
+ * Data type for holding color values.
+ *
+ * Since: 2.1.0
+ */
+typedef uint32_t hb_color_t;
+
+#define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a)))
+
+/**
+ * hb_color_get_alpha:
+ *
+ *
+ *
+ * Since: 2.1.0
+ */
+#define hb_color_get_alpha(color)	((color) & 0xFF)
+/**
+ * hb_color_get_red:
+ *
+ *
+ *
+ * Since: 2.1.0
+ */
+#define hb_color_get_red(color)		(((color) >> 8) & 0xFF)
+/**
+ * hb_color_get_green:
+ *
+ *
+ *
+ * Since: 2.1.0
+ */
+#define hb_color_get_green(color)	(((color) >> 16) & 0xFF)
+/**
+ * hb_color_get_blue:
+ *
+ *
+ *
+ * Since: 2.1.0
+ */
+#define hb_color_get_blue(color)	(((color) >> 24) & 0xFF)
+
 
 HB_END_DECLS
 
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc
index 3bdc3f7..184db49 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -28,12 +28,23 @@
 
 #define HB_SHAPER coretext
 
-#include "hb-private.hh"
-#include "hb-shaper-impl-private.hh"
+#include "hb.hh"
+#include "hb-shaper-impl.hh"
 
 #include "hb-coretext.h"
+#include "hb-aat-layout.hh"
 #include <math.h>
 
+
+/**
+ * SECTION:hb-coretext
+ * @title: hb-coretext
+ * @short_description: CoreText integration
+ * @include: hb-coretext.h
+ *
+ * Functions for using HarfBuzz with the CoreText fonts.
+ **/
+
 /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
 #define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
 
@@ -210,7 +221,7 @@
   }
 
   CFURLRef original_url = nullptr;
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+#if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   ATSFontRef atsFont;
   FSRef fsref;
   OSStatus status;
@@ -240,7 +251,7 @@
        * process in Blink. This can be detected by the new file URL location
        * that the newly found font points to. */
       CFURLRef new_url = nullptr;
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+#if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
       atsFont = CTFontGetPlatformFont (new_ct_font, NULL);
       status = ATSFontGetFileReference (atsFont, &fsref);
       if (status == noErr)
@@ -269,7 +280,7 @@
   return ct_font;
 }
 
-hb_coretext_shaper_face_data_t *
+hb_coretext_face_data_t *
 _hb_coretext_shaper_face_data_create (hb_face_t *face)
 {
   CGFontRef cg_font = create_cg_font (face);
@@ -280,11 +291,11 @@
     return nullptr;
   }
 
-  return (hb_coretext_shaper_face_data_t *) cg_font;
+  return (hb_coretext_face_data_t *) cg_font;
 }
 
 void
-_hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data)
+_hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t *data)
 {
   CFRelease ((CGFontRef) data);
 }
@@ -306,7 +317,7 @@
 }
 
 
-hb_coretext_shaper_font_data_t *
+hb_coretext_font_data_t *
 _hb_coretext_shaper_font_data_create (hb_font_t *font)
 {
   hb_face_t *face = font->face;
@@ -321,11 +332,11 @@
     return nullptr;
   }
 
-  return (hb_coretext_shaper_font_data_t *) ct_font;
+  return (hb_coretext_font_data_t *) ct_font;
 }
 
 void
-_hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data)
+_hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data)
 {
   CFRelease ((CTFontRef) data);
 }
@@ -348,7 +359,7 @@
   hb_font_set_ptem (font, coretext_font_size_to_ptem (CTFontGetSize(ct_font)));
 
   /* Let there be dragons here... */
-  HB_SHAPER_DATA_GET (font) = (hb_coretext_shaper_font_data_t *) CFRetain (ct_font);
+  HB_SHAPER_DATA (HB_SHAPER, font).set_relaxed ((hb_coretext_font_data_t *) CFRetain (ct_font));
 
   return font;
 }
@@ -366,20 +377,20 @@
  * shaper shape_plan data
  */
 
-struct hb_coretext_shaper_shape_plan_data_t {};
+struct hb_coretext_shape_plan_data_t {};
 
-hb_coretext_shaper_shape_plan_data_t *
+hb_coretext_shape_plan_data_t *
 _hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					     const hb_feature_t *user_features HB_UNUSED,
 					     unsigned int        num_user_features HB_UNUSED,
 					     const int          *coords HB_UNUSED,
 					     unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_coretext_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
@@ -431,183 +442,6 @@
 };
 
 
-/* The following enum members are added in OS X 10.8. */
-#define kAltHalfWidthTextSelector		6
-#define kAltProportionalTextSelector		5
-#define kAlternateHorizKanaOffSelector		1
-#define kAlternateHorizKanaOnSelector		0
-#define kAlternateKanaType			34
-#define kAlternateVertKanaOffSelector		3
-#define kAlternateVertKanaOnSelector		2
-#define kCaseSensitiveLayoutOffSelector		1
-#define kCaseSensitiveLayoutOnSelector		0
-#define kCaseSensitiveLayoutType		33
-#define kCaseSensitiveSpacingOffSelector	3
-#define kCaseSensitiveSpacingOnSelector		2
-#define kContextualAlternatesOffSelector	1
-#define kContextualAlternatesOnSelector		0
-#define kContextualAlternatesType		36
-#define kContextualLigaturesOffSelector		19
-#define kContextualLigaturesOnSelector		18
-#define kContextualSwashAlternatesOffSelector	5
-#define kContextualSwashAlternatesOnSelector	4
-#define kDefaultLowerCaseSelector		0
-#define kDefaultUpperCaseSelector		0
-#define kHistoricalLigaturesOffSelector		21
-#define kHistoricalLigaturesOnSelector		20
-#define kHojoCharactersSelector			12
-#define kJIS2004CharactersSelector		11
-#define kLowerCasePetiteCapsSelector		2
-#define kLowerCaseSmallCapsSelector		1
-#define kLowerCaseType				37
-#define kMathematicalGreekOffSelector		11
-#define kMathematicalGreekOnSelector		10
-#define kNLCCharactersSelector			13
-#define kQuarterWidthTextSelector		4
-#define kScientificInferiorsSelector		4
-#define kStylisticAltEightOffSelector		17
-#define kStylisticAltEightOnSelector		16
-#define kStylisticAltEighteenOffSelector	37
-#define kStylisticAltEighteenOnSelector		36
-#define kStylisticAltElevenOffSelector		23
-#define kStylisticAltElevenOnSelector		22
-#define kStylisticAltFifteenOffSelector		31
-#define kStylisticAltFifteenOnSelector		30
-#define kStylisticAltFiveOffSelector		11
-#define kStylisticAltFiveOnSelector		10
-#define kStylisticAltFourOffSelector		9
-#define kStylisticAltFourOnSelector		8
-#define kStylisticAltFourteenOffSelector	29
-#define kStylisticAltFourteenOnSelector		28
-#define kStylisticAltNineOffSelector		19
-#define kStylisticAltNineOnSelector		18
-#define kStylisticAltNineteenOffSelector	39
-#define kStylisticAltNineteenOnSelector		38
-#define kStylisticAltOneOffSelector		3
-#define kStylisticAltOneOnSelector		2
-#define kStylisticAltSevenOffSelector		15
-#define kStylisticAltSevenOnSelector		14
-#define kStylisticAltSeventeenOffSelector	35
-#define kStylisticAltSeventeenOnSelector	34
-#define kStylisticAltSixOffSelector		13
-#define kStylisticAltSixOnSelector		12
-#define kStylisticAltSixteenOffSelector		33
-#define kStylisticAltSixteenOnSelector		32
-#define kStylisticAltTenOffSelector		21
-#define kStylisticAltTenOnSelector		20
-#define kStylisticAltThirteenOffSelector	27
-#define kStylisticAltThirteenOnSelector		26
-#define kStylisticAltThreeOffSelector		7
-#define kStylisticAltThreeOnSelector		6
-#define kStylisticAltTwelveOffSelector		25
-#define kStylisticAltTwelveOnSelector		24
-#define kStylisticAltTwentyOffSelector		41
-#define kStylisticAltTwentyOnSelector		40
-#define kStylisticAltTwoOffSelector		5
-#define kStylisticAltTwoOnSelector		4
-#define kStylisticAlternativesType		35
-#define kSwashAlternatesOffSelector		3
-#define kSwashAlternatesOnSelector		2
-#define kThirdWidthTextSelector			3
-#define kTraditionalNamesCharactersSelector	14
-#define kUpperCasePetiteCapsSelector		2
-#define kUpperCaseSmallCapsSelector		1
-#define kUpperCaseType				38
-
-/* Table data courtesy of Apple. */
-static const struct feature_mapping_t {
-    FourCharCode otFeatureTag;
-    uint16_t aatFeatureType;
-    uint16_t selectorToEnable;
-    uint16_t selectorToDisable;
-} feature_mappings[] = {
-    { 'c2pc',   kUpperCaseType,             kUpperCasePetiteCapsSelector,           kDefaultUpperCaseSelector },
-    { 'c2sc',   kUpperCaseType,             kUpperCaseSmallCapsSelector,            kDefaultUpperCaseSelector },
-    { 'calt',   kContextualAlternatesType,  kContextualAlternatesOnSelector,        kContextualAlternatesOffSelector },
-    { 'case',   kCaseSensitiveLayoutType,   kCaseSensitiveLayoutOnSelector,         kCaseSensitiveLayoutOffSelector },
-    { 'clig',   kLigaturesType,             kContextualLigaturesOnSelector,         kContextualLigaturesOffSelector },
-    { 'cpsp',   kCaseSensitiveLayoutType,   kCaseSensitiveSpacingOnSelector,        kCaseSensitiveSpacingOffSelector },
-    { 'cswh',   kContextualAlternatesType,  kContextualSwashAlternatesOnSelector,   kContextualSwashAlternatesOffSelector },
-    { 'dlig',   kLigaturesType,             kRareLigaturesOnSelector,               kRareLigaturesOffSelector },
-    { 'expt',   kCharacterShapeType,        kExpertCharactersSelector,              16 },
-    { 'frac',   kFractionsType,             kDiagonalFractionsSelector,             kNoFractionsSelector },
-    { 'fwid',   kTextSpacingType,           kMonospacedTextSelector,                7 },
-    { 'halt',   kTextSpacingType,           kAltHalfWidthTextSelector,              7 },
-    { 'hist',   kLigaturesType,             kHistoricalLigaturesOnSelector,         kHistoricalLigaturesOffSelector },
-    { 'hkna',   kAlternateKanaType,         kAlternateHorizKanaOnSelector,          kAlternateHorizKanaOffSelector, },
-    { 'hlig',   kLigaturesType,             kHistoricalLigaturesOnSelector,         kHistoricalLigaturesOffSelector },
-    { 'hngl',   kTransliterationType,       kHanjaToHangulSelector,                 kNoTransliterationSelector },
-    { 'hojo',   kCharacterShapeType,        kHojoCharactersSelector,                16 },
-    { 'hwid',   kTextSpacingType,           kHalfWidthTextSelector,                 7 },
-    { 'ital',   kItalicCJKRomanType,        kCJKItalicRomanOnSelector,              kCJKItalicRomanOffSelector },
-    { 'jp04',   kCharacterShapeType,        kJIS2004CharactersSelector,             16 },
-    { 'jp78',   kCharacterShapeType,        kJIS1978CharactersSelector,             16 },
-    { 'jp83',   kCharacterShapeType,        kJIS1983CharactersSelector,             16 },
-    { 'jp90',   kCharacterShapeType,        kJIS1990CharactersSelector,             16 },
-    { 'liga',   kLigaturesType,             kCommonLigaturesOnSelector,             kCommonLigaturesOffSelector },
-    { 'lnum',   kNumberCaseType,            kUpperCaseNumbersSelector,              2 },
-    { 'mgrk',   kMathematicalExtrasType,    kMathematicalGreekOnSelector,           kMathematicalGreekOffSelector },
-    { 'nlck',   kCharacterShapeType,        kNLCCharactersSelector,                 16 },
-    { 'onum',   kNumberCaseType,            kLowerCaseNumbersSelector,              2 },
-    { 'ordn',   kVerticalPositionType,      kOrdinalsSelector,                      kNormalPositionSelector },
-    { 'palt',   kTextSpacingType,           kAltProportionalTextSelector,           7 },
-    { 'pcap',   kLowerCaseType,             kLowerCasePetiteCapsSelector,           kDefaultLowerCaseSelector },
-    { 'pkna',   kTextSpacingType,           kProportionalTextSelector,              7 },
-    { 'pnum',   kNumberSpacingType,         kProportionalNumbersSelector,           4 },
-    { 'pwid',   kTextSpacingType,           kProportionalTextSelector,              7 },
-    { 'qwid',   kTextSpacingType,           kQuarterWidthTextSelector,              7 },
-    { 'ruby',   kRubyKanaType,              kRubyKanaOnSelector,                    kRubyKanaOffSelector },
-    { 'sinf',   kVerticalPositionType,      kScientificInferiorsSelector,           kNormalPositionSelector },
-    { 'smcp',   kLowerCaseType,             kLowerCaseSmallCapsSelector,            kDefaultLowerCaseSelector },
-    { 'smpl',   kCharacterShapeType,        kSimplifiedCharactersSelector,          16 },
-    { 'ss01',   kStylisticAlternativesType, kStylisticAltOneOnSelector,             kStylisticAltOneOffSelector },
-    { 'ss02',   kStylisticAlternativesType, kStylisticAltTwoOnSelector,             kStylisticAltTwoOffSelector },
-    { 'ss03',   kStylisticAlternativesType, kStylisticAltThreeOnSelector,           kStylisticAltThreeOffSelector },
-    { 'ss04',   kStylisticAlternativesType, kStylisticAltFourOnSelector,            kStylisticAltFourOffSelector },
-    { 'ss05',   kStylisticAlternativesType, kStylisticAltFiveOnSelector,            kStylisticAltFiveOffSelector },
-    { 'ss06',   kStylisticAlternativesType, kStylisticAltSixOnSelector,             kStylisticAltSixOffSelector },
-    { 'ss07',   kStylisticAlternativesType, kStylisticAltSevenOnSelector,           kStylisticAltSevenOffSelector },
-    { 'ss08',   kStylisticAlternativesType, kStylisticAltEightOnSelector,           kStylisticAltEightOffSelector },
-    { 'ss09',   kStylisticAlternativesType, kStylisticAltNineOnSelector,            kStylisticAltNineOffSelector },
-    { 'ss10',   kStylisticAlternativesType, kStylisticAltTenOnSelector,             kStylisticAltTenOffSelector },
-    { 'ss11',   kStylisticAlternativesType, kStylisticAltElevenOnSelector,          kStylisticAltElevenOffSelector },
-    { 'ss12',   kStylisticAlternativesType, kStylisticAltTwelveOnSelector,          kStylisticAltTwelveOffSelector },
-    { 'ss13',   kStylisticAlternativesType, kStylisticAltThirteenOnSelector,        kStylisticAltThirteenOffSelector },
-    { 'ss14',   kStylisticAlternativesType, kStylisticAltFourteenOnSelector,        kStylisticAltFourteenOffSelector },
-    { 'ss15',   kStylisticAlternativesType, kStylisticAltFifteenOnSelector,         kStylisticAltFifteenOffSelector },
-    { 'ss16',   kStylisticAlternativesType, kStylisticAltSixteenOnSelector,         kStylisticAltSixteenOffSelector },
-    { 'ss17',   kStylisticAlternativesType, kStylisticAltSeventeenOnSelector,       kStylisticAltSeventeenOffSelector },
-    { 'ss18',   kStylisticAlternativesType, kStylisticAltEighteenOnSelector,        kStylisticAltEighteenOffSelector },
-    { 'ss19',   kStylisticAlternativesType, kStylisticAltNineteenOnSelector,        kStylisticAltNineteenOffSelector },
-    { 'ss20',   kStylisticAlternativesType, kStylisticAltTwentyOnSelector,          kStylisticAltTwentyOffSelector },
-    { 'subs',   kVerticalPositionType,      kInferiorsSelector,                     kNormalPositionSelector },
-    { 'sups',   kVerticalPositionType,      kSuperiorsSelector,                     kNormalPositionSelector },
-    { 'swsh',   kContextualAlternatesType,  kSwashAlternatesOnSelector,             kSwashAlternatesOffSelector },
-    { 'titl',   kStyleOptionsType,          kTitlingCapsSelector,                   kNoStyleOptionsSelector },
-    { 'tnam',   kCharacterShapeType,        kTraditionalNamesCharactersSelector,    16 },
-    { 'tnum',   kNumberSpacingType,         kMonospacedNumbersSelector,             4 },
-    { 'trad',   kCharacterShapeType,        kTraditionalCharactersSelector,         16 },
-    { 'twid',   kTextSpacingType,           kThirdWidthTextSelector,                7 },
-    { 'unic',   kLetterCaseType,            14,                                     15 },
-    { 'valt',   kTextSpacingType,           kAltProportionalTextSelector,           7 },
-    { 'vert',   kVerticalSubstitutionType,  kSubstituteVerticalFormsOnSelector,     kSubstituteVerticalFormsOffSelector },
-    { 'vhal',   kTextSpacingType,           kAltHalfWidthTextSelector,              7 },
-    { 'vkna',   kAlternateKanaType,         kAlternateVertKanaOnSelector,           kAlternateVertKanaOffSelector },
-    { 'vpal',   kTextSpacingType,           kAltProportionalTextSelector,           7 },
-    { 'vrt2',   kVerticalSubstitutionType,  kSubstituteVerticalFormsOnSelector,     kSubstituteVerticalFormsOffSelector },
-    { 'zero',   kTypographicExtrasType,     kSlashedZeroOnSelector,                 kSlashedZeroOffSelector },
-};
-
-static int
-_hb_feature_mapping_cmp (const void *key_, const void *entry_)
-{
-  unsigned int key = * (unsigned int *) key_;
-  const feature_mapping_t * entry = (const feature_mapping_t *) entry_;
-  return key < entry->otFeatureTag ? -1 :
-	 key > entry->otFeatureTag ? 1 :
-	 0;
-}
-
 hb_bool_t
 _hb_coretext_shape (hb_shape_plan_t    *shape_plan,
 		    hb_font_t          *font,
@@ -624,7 +458,7 @@
   CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size;
 
   /* Attach marks to their bases, to match the 'ot' shaper.
-   * Adapted from hb-ot-shape:hb_form_clusters().
+   * Adapted from a very old version of hb-ot-shape:hb_form_clusters().
    * Note that this only makes us be closer to the 'ot' shaper,
    * but by no means the same.  For example, if there's
    * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will
@@ -640,8 +474,8 @@
 	buffer->merge_clusters (i - 1, i + 1);
   }
 
-  hb_auto_t<hb_vector_t<feature_record_t> > feature_records;
-  hb_auto_t<hb_vector_t<range_record_t> > range_records;
+  hb_vector_t<feature_record_t> feature_records;
+  hb_vector_t<range_record_t> range_records;
 
   /*
    * Set up features.
@@ -650,14 +484,10 @@
   if (num_features)
   {
     /* Sort features by start/end events. */
-    hb_auto_t<hb_vector_t<feature_event_t> > feature_events;
+    hb_vector_t<feature_event_t> feature_events;
     for (unsigned int i = 0; i < num_features; i++)
     {
-      const feature_mapping_t * mapping = (const feature_mapping_t *) bsearch (&features[i].tag,
-									       feature_mappings,
-									       ARRAY_LENGTH (feature_mappings),
-									       sizeof (feature_mappings[0]),
-									       _hb_feature_mapping_cmp);
+      const hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag);
       if (!mapping)
         continue;
 
@@ -693,7 +523,7 @@
     }
 
     /* Scan events and save features for each range. */
-    hb_auto_t<hb_vector_t<active_feature_t> > active_features;
+    hb_vector_t<active_feature_t> active_features;
     unsigned int last_index = 0;
     for (unsigned int i = 0; i < feature_events.len; i++)
     {
@@ -766,7 +596,7 @@
       } else {
         active_feature_t *feature = active_features.find (&event->feature);
 	if (feature)
-	  active_features.remove (feature - active_features.arrayZ);
+	  active_features.remove (feature - active_features);
       }
     }
   }
@@ -823,7 +653,7 @@
   CFStringRef string_ref = nullptr;
   CTLineRef line = nullptr;
 
-  if (0)
+  if (false)
   {
 resize_and_retry:
     DEBUG_MSG (CORETEXT, buffer, "Buffer resize");
@@ -1234,7 +1064,7 @@
      *
      * https://crbug.com/419769
      */
-    if (0)
+    if (false)
     {
       /* Make sure all runs had the expected direction. */
       bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
@@ -1329,9 +1159,9 @@
  * shaper face data
  */
 
-struct hb_coretext_aat_shaper_face_data_t {};
+struct hb_coretext_aat_face_data_t {};
 
-hb_coretext_aat_shaper_face_data_t *
+hb_coretext_aat_face_data_t *
 _hb_coretext_aat_shaper_face_data_create (hb_face_t *face)
 {
   static const hb_tag_t tags[] = {HB_CORETEXT_TAG_MORX, HB_CORETEXT_TAG_MORT, HB_CORETEXT_TAG_KERX};
@@ -1342,7 +1172,7 @@
     if (hb_blob_get_length (blob))
     {
       hb_blob_destroy (blob);
-      return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
+      return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
     }
     hb_blob_destroy (blob);
   }
@@ -1351,7 +1181,7 @@
 }
 
 void
-_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_shaper_face_data_t *data HB_UNUSED)
+_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_face_data_t *data HB_UNUSED)
 {
 }
 
@@ -1360,16 +1190,16 @@
  * shaper font data
  */
 
-struct hb_coretext_aat_shaper_font_data_t {};
+struct hb_coretext_aat_font_data_t {};
 
-hb_coretext_aat_shaper_font_data_t *
+hb_coretext_aat_font_data_t *
 _hb_coretext_aat_shaper_font_data_create (hb_font_t *font)
 {
-  return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
+  return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
 }
 
 void
-_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_shaper_font_data_t *data HB_UNUSED)
+_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_font_data_t *data HB_UNUSED)
 {
 }
 
@@ -1378,20 +1208,20 @@
  * shaper shape_plan data
  */
 
-struct hb_coretext_aat_shaper_shape_plan_data_t {};
+struct hb_coretext_aat_shape_plan_data_t {};
 
-hb_coretext_aat_shaper_shape_plan_data_t *
+hb_coretext_aat_shape_plan_data_t *
 _hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					     const hb_feature_t *user_features HB_UNUSED,
 					     unsigned int        num_user_features HB_UNUSED,
 					     const int          *coords HB_UNUSED,
 					     unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_coretext_aat_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_coretext_aat_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
diff --git a/src/hb-debug.hh b/src/hb-debug.hh
index ae0b677..d218e43 100644
--- a/src/hb-debug.hh
+++ b/src/hb-debug.hh
@@ -27,7 +27,8 @@
 #ifndef HB_DEBUG_HH
 #define HB_DEBUG_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
+#include "hb-atomic.hh"
 #include "hb-dsalgs.hh"
 
 
@@ -35,6 +36,51 @@
 #define HB_DEBUG 0
 #endif
 
+
+/*
+ * Global runtime options.
+ */
+
+struct hb_options_t
+{
+  bool unused : 1; /* In-case sign bit is here. */
+  bool initialized : 1;
+  bool uniscribe_bug_compatible : 1;
+  bool aat : 1;
+};
+
+union hb_options_union_t {
+  int i;
+  hb_options_t opts;
+};
+static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), "");
+
+HB_INTERNAL void
+_hb_options_init (void);
+
+extern HB_INTERNAL hb_atomic_int_t _hb_options;
+
+static inline hb_options_t
+hb_options (void)
+{
+  /* Make a local copy, so we can access bitfield threadsafely. */
+  hb_options_union_t u;
+  u.i = _hb_options.get_relaxed ();
+
+  if (unlikely (!u.i))
+  {
+    _hb_options_init ();
+    u.i = _hb_options.get_relaxed ();
+  }
+
+  return u.opts;
+}
+
+
+/*
+ * Debug output (needs enabling at compile time.)
+ */
+
 static inline bool
 _hb_debug (unsigned int level,
 	   unsigned int max_level)
@@ -127,7 +173,7 @@
 
   fprintf (stderr, "\n");
 }
-template <> inline void
+template <> inline void HB_PRINTF_FUNC(7, 0)
 _hb_debug_msg_va<0> (const char *what HB_UNUSED,
 		     const void *obj HB_UNUSED,
 		     const char *func HB_UNUSED,
@@ -146,7 +192,7 @@
 	       int level_dir,
 	       const char *message,
 	       ...) HB_PRINTF_FUNC(7, 8);
-template <int max_level> static inline void
+template <int max_level> static inline void HB_PRINTF_FUNC(7, 8)
 _hb_debug_msg (const char *what,
 	       const void *obj,
 	       const char *func,
@@ -170,7 +216,7 @@
 		  int level_dir HB_UNUSED,
 		  const char *message HB_UNUSED,
 		  ...) HB_PRINTF_FUNC(7, 8);
-template <> inline void
+template <> inline void HB_PRINTF_FUNC(7, 8)
 _hb_debug_msg<0> (const char *what HB_UNUSED,
 		  const void *obj HB_UNUSED,
 		  const char *func HB_UNUSED,
diff --git a/src/hb-deprecated.h b/src/hb-deprecated.h
index eac7efb..e39b79f 100644
--- a/src/hb-deprecated.h
+++ b/src/hb-deprecated.h
@@ -36,6 +36,18 @@
 #include "hb-font.h"
 #include "hb-set.h"
 
+
+/**
+ * SECTION:hb-deprecated
+ * @title: hb-deprecated
+ * @short_description: Deprecated API
+ * @include: hb.h
+ *
+ * These API have been deprecated in favor of newer API, or because they
+ * were deemed unnecessary.
+ **/
+
+
 HB_BEGIN_DECLS
 
 #ifndef HB_DISABLE_DEPRECATED
@@ -50,14 +62,185 @@
 					       hb_codepoint_t *glyph,
 					       void *user_data);
 
-HB_EXTERN void
+HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func or hb_font_funcs_set_variation_glyph_func) void
 hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
 			      hb_font_get_glyph_func_t func,
 			      void *user_data, hb_destroy_func_t destroy);
 
-HB_EXTERN void
+HB_EXTERN HB_DEPRECATED void
 hb_set_invert (hb_set_t *set);
 
+/**
+ * hb_unicode_eastasian_width_func_t:
+ *
+ * Deprecated: 2.0.0
+ */
+typedef unsigned int			(*hb_unicode_eastasian_width_func_t)	(hb_unicode_funcs_t *ufuncs,
+										 hb_codepoint_t      unicode,
+										 void               *user_data);
+
+/**
+ * hb_unicode_funcs_set_eastasian_width_func:
+ * @ufuncs: a Unicode function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 0.9.2
+ * Deprecated: 2.0.0
+ **/
+HB_EXTERN HB_DEPRECATED void
+hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs,
+					   hb_unicode_eastasian_width_func_t func,
+					   void *user_data, hb_destroy_func_t destroy);
+
+/**
+ * hb_unicode_eastasian_width:
+ *
+ * Since: 0.9.2
+ * Deprecated: 2.0.0
+ **/
+HB_EXTERN HB_DEPRECATED unsigned int
+hb_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs,
+			    hb_codepoint_t unicode);
+
+
+/**
+ * hb_unicode_decompose_compatibility_func_t:
+ * @ufuncs: a Unicode function structure
+ * @u: codepoint to decompose
+ * @decomposed: address of codepoint array (of length %HB_UNICODE_MAX_DECOMPOSITION_LEN) to write decomposition into
+ * @user_data: user data pointer as passed to hb_unicode_funcs_set_decompose_compatibility_func()
+ *
+ * Fully decompose @u to its Unicode compatibility decomposition. The codepoints of the decomposition will be written to @decomposed.
+ * The complete length of the decomposition will be returned.
+ *
+ * If @u has no compatibility decomposition, zero should be returned.
+ *
+ * The Unicode standard guarantees that a buffer of length %HB_UNICODE_MAX_DECOMPOSITION_LEN codepoints will always be sufficient for any
+ * compatibility decomposition plus an terminating value of 0.  Consequently, @decompose must be allocated by the caller to be at least this length.  Implementations
+ * of this function type must ensure that they do not write past the provided array.
+ *
+ * Return value: number of codepoints in the full compatibility decomposition of @u, or 0 if no decomposition available.
+ *
+ * Deprecated: 2.0.0
+ */
+typedef unsigned int			(*hb_unicode_decompose_compatibility_func_t)	(hb_unicode_funcs_t *ufuncs,
+											 hb_codepoint_t      u,
+											 hb_codepoint_t     *decomposed,
+											 void               *user_data);
+
+/**
+ * HB_UNICODE_MAX_DECOMPOSITION_LEN:
+ *
+ * See Unicode 6.1 for details on the maximum decomposition length.
+ *
+ * Deprecated: 2.0.0
+ */
+#define HB_UNICODE_MAX_DECOMPOSITION_LEN (18+1) /* codepoints */
+
+/**
+ * hb_unicode_funcs_set_decompose_compatibility_func:
+ * @ufuncs: a Unicode function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 0.9.2
+ * Deprecated: 2.0.0
+ **/
+HB_EXTERN HB_DEPRECATED void
+hb_unicode_funcs_set_decompose_compatibility_func (hb_unicode_funcs_t *ufuncs,
+						   hb_unicode_decompose_compatibility_func_t func,
+						   void *user_data, hb_destroy_func_t destroy);
+
+HB_EXTERN HB_DEPRECATED unsigned int
+hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
+				    hb_codepoint_t      u,
+				    hb_codepoint_t     *decomposed);
+
+
+typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data,
+							   hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
+							   void *user_data);
+typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t;
+typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t;
+
+/**
+ * hb_font_funcs_set_glyph_h_kerning_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 0.9.2
+ * Deprecated: 2.0.0
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_h_kerning_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
+/**
+ * hb_font_funcs_set_glyph_v_kerning_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 0.9.2
+ * Deprecated: 2.0.0
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_v_kerning_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
+HB_EXTERN hb_position_t
+hb_font_get_glyph_h_kerning (hb_font_t *font,
+			     hb_codepoint_t left_glyph, hb_codepoint_t right_glyph);
+HB_EXTERN hb_position_t
+hb_font_get_glyph_v_kerning (hb_font_t *font,
+			     hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph);
+
+HB_EXTERN void
+hb_font_get_glyph_kerning_for_direction (hb_font_t *font,
+					 hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
+					 hb_direction_t direction,
+					 hb_position_t *x, hb_position_t *y);
+
+/* Like hb_ot_layout_table_find_script, but takes zero-terminated array of scripts to test */
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) hb_bool_t
+hb_ot_layout_table_choose_script (hb_face_t      *face,
+				  hb_tag_t        table_tag,
+				  const hb_tag_t *script_tags,
+				  unsigned int   *script_index,
+				  hb_tag_t       *chosen_script);
+
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_script_select_language) hb_bool_t
+hb_ot_layout_script_find_language (hb_face_t    *face,
+				   hb_tag_t      table_tag,
+				   unsigned int  script_index,
+				   hb_tag_t      language_tag,
+				   unsigned int *language_index);
+
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) void
+hb_ot_tags_from_script (hb_script_t  script,
+			hb_tag_t    *script_tag_1,
+			hb_tag_t    *script_tag_2);
+
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) hb_tag_t
+hb_ot_tag_from_language (hb_language_t language);
+
+
 #endif
 
 HB_END_DECLS
diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc
index 7c68849..35197c2 100644
--- a/src/hb-directwrite.cc
+++ b/src/hb-directwrite.cc
@@ -22,9 +22,9 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 #define HB_SHAPER directwrite
-#include "hb-shaper-impl-private.hh"
+#include "hb-shaper-impl.hh"
 
 #include <DWrite_1.h>
 
@@ -133,7 +133,7 @@
 * shaper face data
 */
 
-struct hb_directwrite_shaper_face_data_t
+struct hb_directwrite_face_data_t
 {
   IDWriteFactory *dwriteFactory;
   IDWriteFontFile *fontFile;
@@ -143,10 +143,10 @@
   hb_blob_t *faceBlob;
 };
 
-hb_directwrite_shaper_face_data_t *
+hb_directwrite_face_data_t *
 _hb_directwrite_shaper_face_data_create (hb_face_t *face)
 {
-  hb_directwrite_shaper_face_data_t *data = new hb_directwrite_shaper_face_data_t;
+  hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t;
   if (unlikely (!data))
     return nullptr;
 
@@ -206,7 +206,7 @@
 }
 
 void
-_hb_directwrite_shaper_face_data_destroy (hb_directwrite_shaper_face_data_t *data)
+_hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data)
 {
   if (data->fontFace)
     data->fontFace->Release ();
@@ -233,16 +233,16 @@
  * shaper font data
  */
 
-struct hb_directwrite_shaper_font_data_t
+struct hb_directwrite_font_data_t
 {
 };
 
-hb_directwrite_shaper_font_data_t *
+hb_directwrite_font_data_t *
 _hb_directwrite_shaper_font_data_create (hb_font_t *font)
 {
   if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return nullptr;
 
-  hb_directwrite_shaper_font_data_t *data = new hb_directwrite_shaper_font_data_t;
+  hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t;
   if (unlikely (!data))
     return nullptr;
 
@@ -250,7 +250,7 @@
 }
 
 void
-_hb_directwrite_shaper_font_data_destroy (hb_directwrite_shaper_font_data_t *data)
+_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
 {
   delete data;
 }
@@ -260,20 +260,20 @@
  * shaper shape_plan data
  */
 
-struct hb_directwrite_shaper_shape_plan_data_t {};
+struct hb_directwrite_shape_plan_data_t {};
 
-hb_directwrite_shaper_shape_plan_data_t *
+hb_directwrite_shape_plan_data_t *
 _hb_directwrite_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					       const hb_feature_t *user_features HB_UNUSED,
 					       unsigned int        num_user_features HB_UNUSED,
 					       const int          *coords HB_UNUSED,
 					       unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_directwrite_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_directwrite_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
@@ -555,8 +555,8 @@
   float               lineWidth)
 {
   hb_face_t *face = font->face;
-  hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-  hb_directwrite_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
+  hb_directwrite_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
+  hb_directwrite_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
   IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
   IDWriteFontFace *fontFace = face_data->fontFace;
 
diff --git a/src/hb-dsalgs.hh b/src/hb-dsalgs.hh
index fc7d1f0..e8bd95c 100644
--- a/src/hb-dsalgs.hh
+++ b/src/hb-dsalgs.hh
@@ -27,7 +27,9 @@
 #ifndef HB_DSALGS_HH
 #define HB_DSALGS_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
+
+#include "hb-null.hh"
 
 
 /* Void! For when we need a expression-type of void. */
@@ -232,6 +234,18 @@
  * Tiny stuff.
  */
 
+/* ASCII tag/character handling */
+static inline bool ISALPHA (unsigned char c)
+{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
+static inline bool ISALNUM (unsigned char c)
+{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); }
+static inline bool ISSPACE (unsigned char c)
+{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; }
+static inline unsigned char TOUPPER (unsigned char c)
+{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; }
+static inline unsigned char TOLOWER (unsigned char c)
+{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; }
+
 #undef MIN
 template <typename Type>
 static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
@@ -262,12 +276,64 @@
   return ((v - 1) | 3) + 1;
 }
 
+template <typename T> class hb_assert_unsigned_t;
+template <> class hb_assert_unsigned_t<unsigned char> {};
+template <> class hb_assert_unsigned_t<unsigned short> {};
+template <> class hb_assert_unsigned_t<unsigned int> {};
+template <> class hb_assert_unsigned_t<unsigned long> {};
+
+template <typename T> static inline bool
+hb_in_range (T u, T lo, T hi)
+{
+  /* The sizeof() is here to force template instantiation.
+   * I'm sure there are better ways to do this but can't think of
+   * one right now.  Declaring a variable won't work as HB_UNUSED
+   * is unusable on some platforms and unused types are less likely
+   * to generate a warning than unused variables. */
+  static_assert ((sizeof (hb_assert_unsigned_t<T>) >= 0), "");
+
+  /* The casts below are important as if T is smaller than int,
+   * the subtract results will become a signed int! */
+  return (T)(u - lo) <= (T)(hi - lo);
+}
+template <typename T> static inline bool
+hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2)
+{
+  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2);
+}
+template <typename T> static inline bool
+hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
+{
+  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
+}
+
 
 /*
  * Sort and search.
  */
 
 static inline void *
+hb_bsearch (const void *key, const void *base,
+	    size_t nmemb, size_t size,
+	    int (*compar)(const void *_key, const void *_item))
+{
+  int min = 0, max = (int) nmemb - 1;
+  while (min <= max)
+  {
+    int mid = (min + max) / 2;
+    const void *p = (const void *) (((const char *) base) + (mid * size));
+    int c = compar (key, p);
+    if (c < 0)
+      max = mid - 1;
+    else if (c > 0)
+      min = mid + 1;
+    else
+      return (void *) p;
+  }
+  return nullptr;
+}
+
+static inline void *
 hb_bsearch_r (const void *key, const void *base,
 	      size_t nmemb, size_t size,
 	      int (*compar)(const void *_key, const void *_item, void *_arg),
@@ -276,7 +342,7 @@
   int min = 0, max = (int) nmemb - 1;
   while (min <= max)
   {
-    int mid = (min + max) / 2;
+    int mid = ((unsigned int) min + (unsigned int) max) / 2;
     const void *p = (const void *) (((const char *) base) + (mid * size));
     int c = compar (key, p, arg);
     if (c < 0)
@@ -445,324 +511,22 @@
 }
 
 
-#define HB_VECTOR_INIT {0, 0, false, nullptr}
-template <typename Type, unsigned int StaticSize=8>
-struct hb_vector_t
-{
-  unsigned int len;
-  unsigned int allocated; /* == 0 means allocation failed. */
-  Type *arrayZ;
-  Type static_array[StaticSize];
-
-  void init (void)
-  {
-    len = 0;
-    allocated = ARRAY_LENGTH (static_array);
-    arrayZ = static_array;
-  }
-
-  inline Type& operator [] (unsigned int i)
-  {
-    if (unlikely (i >= len))
-      return Crap (Type);
-    return arrayZ[i];
-  }
-  inline const Type& operator [] (unsigned int i) const
-  {
-    if (unlikely (i >= len))
-      return Null(Type);
-    return arrayZ[i];
-  }
-
-  inline Type *push (void)
-  {
-    if (unlikely (!resize (len + 1)))
-      return &Crap(Type);
-    return &arrayZ[len - 1];
-  }
-  inline Type *push (const Type& v)
-  {
-    Type *p = push ();
-    *p = v;
-    return p;
-  }
-
-  /* Allocate for size but don't adjust len. */
-  inline bool alloc (unsigned int size)
-  {
-    if (unlikely (!allocated))
-      return false;
-
-    if (likely (size <= allocated))
-      return true;
-
-    /* Reallocate */
-
-    unsigned int new_allocated = allocated;
-    while (size >= new_allocated)
-      new_allocated += (new_allocated >> 1) + 8;
-
-    Type *new_array = nullptr;
-
-    if (arrayZ == static_array)
-    {
-      new_array = (Type *) calloc (new_allocated, sizeof (Type));
-      if (new_array)
-        memcpy (new_array, arrayZ, len * sizeof (Type));
-    }
-    else
-    {
-      bool overflows = (new_allocated < allocated) || hb_unsigned_mul_overflows (new_allocated, sizeof (Type));
-      if (likely (!overflows))
-        new_array = (Type *) realloc (arrayZ, new_allocated * sizeof (Type));
-    }
-
-    if (unlikely (!new_array))
-    {
-      allocated = 0;
-      return false;
-    }
-
-    arrayZ = new_array;
-    allocated = new_allocated;
-
-    return true;
-  }
-
-  inline bool resize (int size_)
-  {
-    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
-    if (!alloc (size))
-      return false;
-
-    if (size > len)
-      memset (arrayZ + len, 0, (size - len) * sizeof (*arrayZ));
-
-    len = size;
-    return true;
-  }
-
-  inline void pop (void)
-  {
-    if (!len) return;
-    len--;
-  }
-
-  inline void remove (unsigned int i)
-  {
-     if (unlikely (i >= len))
-       return;
-     memmove (static_cast<void *> (&arrayZ[i]),
-	      static_cast<void *> (&arrayZ[i + 1]),
-	      (len - i - 1) * sizeof (Type));
-     len--;
-  }
-
-  inline void shrink (int size_)
-  {
-    unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
-     if (size < len)
-       len = size;
-  }
-
-  template <typename T>
-  inline Type *find (T v) {
-    for (unsigned int i = 0; i < len; i++)
-      if (arrayZ[i] == v)
-	return &arrayZ[i];
-    return nullptr;
-  }
-  template <typename T>
-  inline const Type *find (T v) const {
-    for (unsigned int i = 0; i < len; i++)
-      if (arrayZ[i] == v)
-	return &arrayZ[i];
-    return nullptr;
-  }
-
-  inline void qsort (int (*cmp)(const void*, const void*))
-  {
-    ::qsort (arrayZ, len, sizeof (Type), cmp);
-  }
-
-  inline void qsort (void)
-  {
-    ::qsort (arrayZ, len, sizeof (Type), Type::cmp);
-  }
-
-  inline void qsort (unsigned int start, unsigned int end)
-  {
-    ::qsort (arrayZ + start, end - start, sizeof (Type), Type::cmp);
-  }
-
-  template <typename T>
-  inline Type *lsearch (const T &x)
-  {
-    for (unsigned int i = 0; i < len; i++)
-      if (0 == this->arrayZ[i].cmp (&x))
-	return &arrayZ[i];
-    return nullptr;
-  }
-
-  template <typename T>
-  inline Type *bsearch (const T &x)
-  {
-    unsigned int i;
-    return bfind (x, &i) ? &arrayZ[i] : nullptr;
-  }
-  template <typename T>
-  inline const Type *bsearch (const T &x) const
-  {
-    unsigned int i;
-    return bfind (x, &i) ? &arrayZ[i] : nullptr;
-  }
-  template <typename T>
-  inline bool bfind (const T &x, unsigned int *i) const
-  {
-    int min = 0, max = (int) this->len - 1;
-    while (min <= max)
-    {
-      int mid = (min + max) / 2;
-      int c = this->arrayZ[mid].cmp (&x);
-      if (c < 0)
-        max = mid - 1;
-      else if (c > 0)
-        min = mid + 1;
-      else
-      {
-        *i = mid;
-	return true;
-      }
-    }
-    if (max < 0 || (max < (int) this->len && this->arrayZ[max].cmp (&x) > 0))
-      max++;
-    *i = max;
-    return false;
-  }
-
-  inline void fini (void)
-  {
-    if (arrayZ != static_array)
-      free (arrayZ);
-    arrayZ = nullptr;
-    allocated = len = 0;
-  }
-};
-
-
-#define HB_LOCKABLE_SET_INIT {HB_VECTOR_INIT}
-template <typename item_t, typename lock_t>
-struct hb_lockable_set_t
-{
-  hb_vector_t <item_t, 1> items;
-
-  inline void init (void) { items.init (); }
-
-  template <typename T>
-  inline item_t *replace_or_insert (T v, lock_t &l, bool replace)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item) {
-      if (replace) {
-	item_t old = *item;
-	*item = v;
-	l.unlock ();
-	old.fini ();
-      }
-      else {
-        item = nullptr;
-	l.unlock ();
-      }
-    } else {
-      item = items.push (v);
-      l.unlock ();
-    }
-    return item;
-  }
-
-  template <typename T>
-  inline void remove (T v, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item) {
-      item_t old = *item;
-      *item = items[items.len - 1];
-      items.pop ();
-      l.unlock ();
-      old.fini ();
-    } else {
-      l.unlock ();
-    }
-  }
-
-  template <typename T>
-  inline bool find (T v, item_t *i, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (item)
-      *i = *item;
-    l.unlock ();
-    return !!item;
-  }
-
-  template <typename T>
-  inline item_t *find_or_insert (T v, lock_t &l)
-  {
-    l.lock ();
-    item_t *item = items.find (v);
-    if (!item) {
-      item = items.push (v);
-    }
-    l.unlock ();
-    return item;
-  }
-
-  inline void fini (lock_t &l)
-  {
-    if (!items.len) {
-      /* No need for locking. */
-      items.fini ();
-      return;
-    }
-    l.lock ();
-    while (items.len) {
-      item_t old = items[items.len - 1];
-	items.pop ();
-	l.unlock ();
-	old.fini ();
-	l.lock ();
-    }
-    items.fini ();
-    l.unlock ();
-  }
-
-};
-
-
-template <typename Type>
-struct hb_auto_t : Type
-{
-  hb_auto_t (void) { Type::init (); }
-  ~hb_auto_t (void) { Type::fini (); }
-  private: /* Hide */
-  void init (void) {}
-  void fini (void) {}
-};
-
 struct hb_bytes_t
 {
-  inline hb_bytes_t (void) : bytes (nullptr), len (0) {}
-  inline hb_bytes_t (const char *bytes_, unsigned int len_) : bytes (bytes_), len (len_) {}
+  inline hb_bytes_t (void) : arrayZ (nullptr), len (0) {}
+  inline hb_bytes_t (const char *bytes_, unsigned int len_) : arrayZ (bytes_), len (len_) {}
+  inline hb_bytes_t (const void *bytes_, unsigned int len_) : arrayZ ((const char *) bytes_), len (len_) {}
+  template <typename T>
+  inline hb_bytes_t (const T& array) : arrayZ ((const char *) array.arrayZ), len (array.len) {}
+
+  inline void free (void) { ::free ((void *) arrayZ); arrayZ = nullptr; len = 0; }
 
   inline int cmp (const hb_bytes_t &a) const
   {
     if (len != a.len)
       return (int) a.len - (int) len;
 
-    return memcmp (a.bytes, bytes, len);
+    return memcmp (a.arrayZ, arrayZ, len);
   }
   static inline int cmp (const void *pa, const void *pb)
   {
@@ -771,10 +535,46 @@
     return b->cmp (*a);
   }
 
-  const char *bytes;
+  const char *arrayZ;
   unsigned int len;
 };
 
+template <typename T>
+struct hb_array_t
+{
+  inline hb_array_t (void) : arrayZ (nullptr), len (0) {}
+  inline hb_array_t (T *array_, unsigned int len_) : arrayZ (array_), len (len_) {}
+
+  inline T& operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= len)) return Null(T);
+    return arrayZ[i];
+  }
+
+  inline hb_array_t<T> sub_array (unsigned int start_offset, unsigned int seg_count) const
+  {
+    unsigned int count = len;
+    if (unlikely (start_offset > count))
+      count = 0;
+    else
+      count -= start_offset;
+    count = MIN (count, seg_count);
+    return hb_array_t<T> (arrayZ + start_offset, count);
+  }
+
+  inline hb_bytes_t as_bytes (void) const
+  {
+    return hb_bytes_t (arrayZ, len * sizeof (T));
+  }
+
+  inline void free (void) { ::free ((void *) arrayZ); arrayZ = nullptr; len = 0; }
+
+  T *arrayZ;
+  unsigned int len;
+};
+template <typename T> static inline
+hb_array_t<T> hb_array (T *array, unsigned int len) { return hb_array_t<T> (array, len); }
+
 
 struct HbOpOr
 {
diff --git a/src/hb-face.cc b/src/hb-face.cc
index fab2aa3..7ca4b1b 100644
--- a/src/hb-face.cc
+++ b/src/hb-face.cc
@@ -26,21 +26,35 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-face-private.hh"
-#include "hb-blob-private.hh"
-#include "hb-open-file-private.hh"
-
+#include "hb-face.hh"
+#include "hb-blob.hh"
+#include "hb-open-file.hh"
+#include "hb-ot-face.hh"
+#include "hb-ot-cmap-table.hh"
 
 
 /**
- * hb_face_count: Get number of faces on the blob
- * @blob:
+ * SECTION:hb-face
+ * @title: hb-face
+ * @short_description: Font face objects
+ * @include: hb.h
  *
+ * Font face is objects represent a single face in a font family.
+ * More exactly, a font face represents a single face in a binary font file.
+ * Font faces are typically built from a binary blob and a face index.
+ * Font faces are used to create fonts.
+ **/
+
+
+/**
+ * hb_face_count:
+ * @blob: a blob.
  *
+ * Get number of faces in a blob.
  *
- * Return value: Number of faces on the blob
+ * Return value: Number of faces in @blob
  *
  * Since: 1.7.7
  **/
@@ -51,7 +65,8 @@
     return 0;
 
   /* TODO We shouldn't be sanitizing blob.  Port to run sanitizer and return if not sane. */
-  hb_blob_t *sanitized = OT::hb_sanitize_context_t().sanitize_blob<OT::OpenTypeFontFile> (hb_blob_reference (blob));
+  /* Make API signature const after. */
+  hb_blob_t *sanitized = hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (hb_blob_reference (blob));
   const OT::OpenTypeFontFile& ot = *sanitized->as<OT::OpenTypeFontFile> ();
   unsigned int ret = ot.get_face_count ();
   hb_blob_destroy (sanitized);
@@ -63,7 +78,8 @@
  * hb_face_t
  */
 
-const hb_face_t _hb_face_nil = {
+DEFINE_NULL_INSTANCE (hb_face_t) =
+{
   HB_OBJECT_HEADER_STATIC,
 
   true, /* immutable */
@@ -77,12 +93,12 @@
   0,    /* num_glyphs */
 
   {
-#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
+#define HB_SHAPER_IMPLEMENT(shaper) HB_ATOMIC_PTR_INIT (HB_SHAPER_DATA_INVALID),
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
   },
 
-  nullptr, /* shape_plans */
+  HB_ATOMIC_PTR_INIT (nullptr), /* shape_plans */
 };
 
 
@@ -160,11 +176,12 @@
     return hb_blob_reference (data->blob);
 
   const OT::OpenTypeFontFile &ot_file = *data->blob->as<OT::OpenTypeFontFile> ();
-  const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
+  unsigned int base_offset;
+  const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index, &base_offset);
 
   const OT::OpenTypeTable &table = ot_face.get_table_by_tag (tag);
 
-  hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, table.offset, table.length);
+  hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, base_offset + table.offset, table.length);
 
   return blob;
 }
@@ -189,7 +206,7 @@
   if (unlikely (!blob))
     blob = hb_blob_get_empty ();
 
-  hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::hb_sanitize_context_t().sanitize_blob<OT::OpenTypeFontFile> (hb_blob_reference (blob)), index);
+  hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (hb_blob_reference (blob)), index);
 
   if (unlikely (!closure))
     return hb_face_get_empty ();
@@ -215,7 +232,7 @@
 hb_face_t *
 hb_face_get_empty (void)
 {
-  return const_cast<hb_face_t *> (&_hb_face_nil);
+  return const_cast<hb_face_t *> (&Null(hb_face_t));
 }
 
 
@@ -248,7 +265,7 @@
 {
   if (!hb_object_destroy (face)) return;
 
-  for (hb_face_t::plan_node_t *node = face->shape_plans; node; )
+  for (hb_face_t::plan_node_t *node = face->shape_plans.get (); node; )
   {
     hb_face_t::plan_node_t *next = node->next;
     hb_shape_plan_destroy (node->shape_plan);
@@ -302,7 +319,7 @@
  * Since: 0.9.2
  **/
 void *
-hb_face_get_user_data (hb_face_t          *face,
+hb_face_get_user_data (const hb_face_t    *face,
 		       hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (face, key);
@@ -321,6 +338,8 @@
 {
   if (unlikely (hb_object_is_inert (face)))
     return;
+  if (face->immutable)
+    return;
 
   face->immutable = true;
 }
@@ -336,7 +355,7 @@
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_face_is_immutable (hb_face_t *face)
+hb_face_is_immutable (const hb_face_t *face)
 {
   return face->immutable;
 }
@@ -354,8 +373,8 @@
  * Since: 0.9.2
  **/
 hb_blob_t *
-hb_face_reference_table (hb_face_t *face,
-			 hb_tag_t   tag)
+hb_face_reference_table (const hb_face_t *face,
+			 hb_tag_t tag)
 {
   return face->reference_table (tag);
 }
@@ -406,7 +425,7 @@
  * Since: 0.9.2
  **/
 unsigned int
-hb_face_get_index (hb_face_t    *face)
+hb_face_get_index (const hb_face_t *face)
 {
   return face->index;
 }
@@ -441,7 +460,7 @@
  * Since: 0.9.2
  **/
 unsigned int
-hb_face_get_upem (hb_face_t *face)
+hb_face_get_upem (const hb_face_t *face)
 {
   return face->get_upem ();
 }
@@ -476,7 +495,7 @@
  * Since: 0.9.7
  **/
 unsigned int
-hb_face_get_glyph_count (hb_face_t *face)
+hb_face_get_glyph_count (const hb_face_t *face)
 {
   return face->get_num_glyphs ();
 }
@@ -484,6 +503,9 @@
 /**
  * hb_face_get_table_tags:
  * @face: a face.
+ * @start_offset: index of first tag to return.
+ * @table_count: input length of @table_tags array, output number of items written.
+ * @table_tags: array to write tags into.
  *
  * Retrieves table tags for a face, if possible.
  *
@@ -492,7 +514,7 @@
  * Since: 1.6.0
  **/
 unsigned int
-hb_face_get_table_tags (hb_face_t    *face,
+hb_face_get_table_tags (const hb_face_t *face,
 			unsigned int  start_offset,
 			unsigned int *table_count, /* IN/OUT */
 			hb_tag_t     *table_tags /* OUT */)
@@ -511,3 +533,208 @@
 
   return ot_face.get_table_tags (start_offset, table_count, table_tags);
 }
+
+
+/*
+ * Character set.
+ */
+
+
+/**
+ * hb_face_collect_unicodes:
+ * @face: font face.
+ * @out: set to add Unicode characters covered by @face to.
+ *
+ * Since: 1.9.0
+ */
+void
+hb_face_collect_unicodes (hb_face_t *face,
+			  hb_set_t  *out)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
+  hb_ot_face_data (face)->cmap->collect_unicodes (out);
+}
+
+/**
+ * hb_face_collect_variation_selectors:
+ * @face: font face.
+ * @out: set to add Variation Selector characters covered by @face to.
+ *
+ *
+ *
+ * Since: 1.9.0
+ */
+void
+hb_face_collect_variation_selectors (hb_face_t *face,
+				     hb_set_t  *out)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
+  hb_ot_face_data (face)->cmap->collect_variation_selectors (out);
+}
+
+/**
+ * hb_face_collect_variation_unicodes:
+ * @face: font face.
+ * @out: set to add Unicode characters for @variation_selector covered by @face to.
+ *
+ *
+ *
+ * Since: 1.9.0
+ */
+void
+hb_face_collect_variation_unicodes (hb_face_t *face,
+				    hb_codepoint_t variation_selector,
+				    hb_set_t  *out)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
+  hb_ot_face_data (face)->cmap->collect_variation_unicodes (variation_selector, out);
+}
+
+
+
+/*
+ * face-builder: A face that has add_table().
+ */
+
+struct hb_face_builder_data_t
+{
+  struct table_entry_t
+  {
+    inline int cmp (const hb_tag_t *t) const
+    {
+      if (*t < tag) return -1;
+      if (*t > tag) return -1;
+      return 0;
+    }
+
+    hb_tag_t   tag;
+    hb_blob_t *blob;
+  };
+
+  hb_vector_t<table_entry_t, 32> tables;
+};
+
+static hb_face_builder_data_t *
+_hb_face_builder_data_create (void)
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) calloc (1, sizeof (hb_face_builder_data_t));
+  if (unlikely (!data))
+    return nullptr;
+
+  data->tables.init ();
+
+  return data;
+}
+
+static void
+_hb_face_builder_data_destroy (void *user_data)
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
+
+  for (unsigned int i = 0; i < data->tables.len; i++)
+    hb_blob_destroy (data->tables[i].blob);
+
+  data->tables.fini ();
+
+  free (data);
+}
+
+static hb_blob_t *
+_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
+{
+
+  unsigned int table_count = data->tables.len;
+  unsigned int face_length = table_count * 16 + 12;
+
+  for (unsigned int i = 0; i < table_count; i++)
+    face_length += hb_ceil_to_4 (hb_blob_get_length (data->tables[i].blob));
+
+  char *buf = (char *) malloc (face_length);
+  if (unlikely (!buf))
+    return nullptr;
+
+  hb_serialize_context_t c (buf, face_length);
+  OT::OpenTypeFontFile *f = c.start_serialize<OT::OpenTypeFontFile> ();
+
+  bool is_cff = data->tables.lsearch (HB_TAG ('C','F','F',' ')) || data->tables.lsearch (HB_TAG ('C','F','F','2'));
+  hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
+
+  Supplier<hb_tag_t>    tags_supplier  (&data->tables[0].tag, table_count, sizeof (data->tables[0]));
+  Supplier<hb_blob_t *> blobs_supplier (&data->tables[0].blob, table_count, sizeof (data->tables[0]));
+  bool ret = f->serialize_single (&c,
+				  sfnt_tag,
+				  tags_supplier,
+				  blobs_supplier,
+				  table_count);
+
+  c.end_serialize ();
+
+  if (unlikely (!ret))
+  {
+    free (buf);
+    return nullptr;
+  }
+
+  return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, free);
+}
+
+static hb_blob_t *
+_hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
+
+  if (!tag)
+    return _hb_face_builder_data_reference_blob (data);
+
+  hb_face_builder_data_t::table_entry_t *entry = data->tables.lsearch (tag);
+  if (entry)
+    return hb_blob_reference (entry->blob);
+
+  return nullptr;
+}
+
+
+/**
+ * hb_face_builder_create:
+ *
+ * Creates a #hb_face_t that can be used with hb_face_builder_add_table().
+ * After tables are added to the face, it can be compiled to a binary
+ * font file by calling hb_face_reference_blob().
+ *
+ * Return value: (transfer full): New face.
+ *
+ * Since: 1.9.0
+ **/
+hb_face_t *
+hb_face_builder_create (void)
+{
+  hb_face_builder_data_t *data = _hb_face_builder_data_create ();
+  if (unlikely (!data)) return hb_face_get_empty ();
+
+  return hb_face_create_for_tables (_hb_face_builder_reference_table,
+				    data,
+				    _hb_face_builder_data_destroy);
+}
+
+/**
+ * hb_face_builder_add_table:
+ *
+ * Add table for @tag with data provided by @blob to the face.  @face must
+ * be created using hb_face_builder_create().
+ *
+ * Since: 1.9.0
+ **/
+hb_bool_t
+hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
+{
+  if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy))
+    return false;
+
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
+  hb_face_builder_data_t::table_entry_t *entry = data->tables.push ();
+
+  entry->tag = tag;
+  entry->blob = hb_blob_reference (blob);
+
+  return true;
+}
diff --git a/src/hb-face.h b/src/hb-face.h
index 983ee56..e8ff090 100644
--- a/src/hb-face.h
+++ b/src/hb-face.h
@@ -33,6 +33,7 @@
 
 #include "hb-common.h"
 #include "hb-blob.h"
+#include "hb-set.h"
 
 HB_BEGIN_DECLS
 
@@ -76,19 +77,19 @@
 		       hb_bool_t           replace);
 
 HB_EXTERN void *
-hb_face_get_user_data (hb_face_t          *face,
+hb_face_get_user_data (const hb_face_t    *face,
 		       hb_user_data_key_t *key);
 
 HB_EXTERN void
 hb_face_make_immutable (hb_face_t *face);
 
 HB_EXTERN hb_bool_t
-hb_face_is_immutable (hb_face_t *face);
+hb_face_is_immutable (const hb_face_t *face);
 
 
 HB_EXTERN hb_blob_t *
-hb_face_reference_table (hb_face_t *face,
-			 hb_tag_t   tag);
+hb_face_reference_table (const hb_face_t *face,
+			 hb_tag_t tag);
 
 HB_EXTERN hb_blob_t *
 hb_face_reference_blob (hb_face_t *face);
@@ -98,28 +99,60 @@
 		   unsigned int  index);
 
 HB_EXTERN unsigned int
-hb_face_get_index (hb_face_t    *face);
+hb_face_get_index (const hb_face_t *face);
 
 HB_EXTERN void
 hb_face_set_upem (hb_face_t    *face,
 		  unsigned int  upem);
 
 HB_EXTERN unsigned int
-hb_face_get_upem (hb_face_t *face);
+hb_face_get_upem (const hb_face_t *face);
 
 HB_EXTERN void
 hb_face_set_glyph_count (hb_face_t    *face,
 			 unsigned int  glyph_count);
 
 HB_EXTERN unsigned int
-hb_face_get_glyph_count (hb_face_t *face);
+hb_face_get_glyph_count (const hb_face_t *face);
 
 HB_EXTERN unsigned int
-hb_face_get_table_tags (hb_face_t    *face,
+hb_face_get_table_tags (const hb_face_t *face,
 			unsigned int  start_offset,
 			unsigned int *table_count, /* IN/OUT */
 			hb_tag_t     *table_tags /* OUT */);
 
+
+/*
+ * Character set.
+ */
+
+HB_EXTERN void
+hb_face_collect_unicodes (hb_face_t *face,
+			  hb_set_t  *out);
+
+HB_EXTERN void
+hb_face_collect_variation_selectors (hb_face_t *face,
+				     hb_set_t  *out);
+
+HB_EXTERN void
+hb_face_collect_variation_unicodes (hb_face_t *face,
+				    hb_codepoint_t variation_selector,
+				    hb_set_t  *out);
+
+
+/*
+ * Builder face.
+ */
+
+HB_EXTERN hb_face_t *
+hb_face_builder_create (void);
+
+HB_EXTERN hb_bool_t
+hb_face_builder_add_table (hb_face_t *face,
+			   hb_tag_t   tag,
+			   hb_blob_t *blob);
+
+
 HB_END_DECLS
 
 #endif /* HB_FACE_H */
diff --git a/src/hb-face.hh b/src/hb-face.hh
new file mode 100644
index 0000000..89673ff
--- /dev/null
+++ b/src/hb-face.hh
@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2009  Red Hat, Inc.
+ * Copyright © 2011  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_FACE_HH
+#define HB_FACE_HH
+
+#include "hb.hh"
+
+#include "hb-shaper.hh"
+#include "hb-shape-plan.hh"
+
+
+/*
+ * hb_face_t
+ */
+
+struct hb_face_t
+{
+  hb_object_header_t header;
+
+  hb_bool_t immutable;
+
+  hb_reference_table_func_t  reference_table_func;
+  void                      *user_data;
+  hb_destroy_func_t          destroy;
+
+  unsigned int index;			/* Face index in a collection, zero-based. */
+  mutable unsigned int upem;		/* Units-per-EM. */
+  mutable unsigned int num_glyphs;	/* Number of glyphs. */
+
+  struct hb_shaper_data_t shaper_data;	/* Various shaper data. */
+
+  /* Cache */
+  struct plan_node_t
+  {
+    hb_shape_plan_t *shape_plan;
+    plan_node_t *next;
+  };
+  hb_atomic_ptr_t<plan_node_t> shape_plans;
+
+  inline hb_blob_t *reference_table (hb_tag_t tag) const
+  {
+    hb_blob_t *blob;
+
+    if (unlikely (!reference_table_func))
+      return hb_blob_get_empty ();
+
+    blob = reference_table_func (/*XXX*/const_cast<hb_face_t *> (this), tag, user_data);
+    if (unlikely (!blob))
+      return hb_blob_get_empty ();
+
+    return blob;
+  }
+
+  inline HB_PURE_FUNC unsigned int get_upem (void) const
+  {
+    if (unlikely (!upem))
+      load_upem ();
+    return upem;
+  }
+
+  inline unsigned int get_num_glyphs (void) const
+  {
+    if (unlikely (num_glyphs == (unsigned int) -1))
+      load_num_glyphs ();
+    return num_glyphs;
+  }
+
+  private:
+  HB_INTERNAL void load_upem (void) const;
+  HB_INTERNAL void load_num_glyphs (void) const;
+};
+DECLARE_NULL_INSTANCE (hb_face_t);
+
+#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
+#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, face);
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
+
+
+#endif /* HB_FACE_HH */
diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc
index 3f09c3f..dc8536c 100644
--- a/src/hb-fallback-shape.cc
+++ b/src/hb-fallback-shape.cc
@@ -25,7 +25,7 @@
  */
 
 #define HB_SHAPER fallback
-#include "hb-shaper-impl-private.hh"
+#include "hb-shaper-impl.hh"
 
 
 HB_SHAPER_DATA_ENSURE_DEFINE(fallback, face)
@@ -36,16 +36,16 @@
  * shaper face data
  */
 
-struct hb_fallback_shaper_face_data_t {};
+struct hb_fallback_face_data_t {};
 
-hb_fallback_shaper_face_data_t *
+hb_fallback_face_data_t *
 _hb_fallback_shaper_face_data_create (hb_face_t *face HB_UNUSED)
 {
-  return (hb_fallback_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_fallback_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_face_data_destroy (hb_fallback_shaper_face_data_t *data HB_UNUSED)
+_hb_fallback_shaper_face_data_destroy (hb_fallback_face_data_t *data HB_UNUSED)
 {
 }
 
@@ -54,16 +54,16 @@
  * shaper font data
  */
 
-struct hb_fallback_shaper_font_data_t {};
+struct hb_fallback_font_data_t {};
 
-hb_fallback_shaper_font_data_t *
+hb_fallback_font_data_t *
 _hb_fallback_shaper_font_data_create (hb_font_t *font HB_UNUSED)
 {
-  return (hb_fallback_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_fallback_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_font_data_destroy (hb_fallback_shaper_font_data_t *data HB_UNUSED)
+_hb_fallback_shaper_font_data_destroy (hb_fallback_font_data_t *data HB_UNUSED)
 {
 }
 
@@ -72,20 +72,20 @@
  * shaper shape_plan data
  */
 
-struct hb_fallback_shaper_shape_plan_data_t {};
+struct hb_fallback_shape_plan_data_t {};
 
-hb_fallback_shaper_shape_plan_data_t *
+hb_fallback_shape_plan_data_t *
 _hb_fallback_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
 					    const hb_feature_t *user_features HB_UNUSED,
 					    unsigned int        num_user_features HB_UNUSED,
 					    const int          *coords HB_UNUSED,
 					    unsigned int        num_coords HB_UNUSED)
 {
-  return (hb_fallback_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_fallback_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
-_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shaper_shape_plan_data_t *data HB_UNUSED)
+_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shape_plan_data_t *data HB_UNUSED)
 {
 }
 
diff --git a/src/hb-font.cc b/src/hb-font.cc
index 4d62b9e..135d310 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -26,9 +26,25 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-font-private.hh"
+#include "hb-font.hh"
+#include "hb-machinery.hh"
+
+#include "hb-ot.h"
+
+
+/**
+ * SECTION:hb-font
+ * @title: hb-font
+ * @short_description: Font objects
+ * @include: hb.h
+ *
+ * Font objects represent a font face at a certain size and other
+ * parameters (pixels per EM, points per EM, variation settings.)
+ * Fonts are created from font faces, and are used as input to
+ * hb_shape() among other things.
+ **/
 
 
 /*
@@ -45,10 +61,10 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_font_h_extents_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_font_extents_t *metrics,
-				   void *user_data HB_UNUSED)
+hb_font_get_font_h_extents_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_font_extents_t *metrics,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_font_h_extents (metrics);
   if (ret) {
@@ -69,10 +85,10 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_font_v_extents_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_font_extents_t *metrics,
-				   void *user_data HB_UNUSED)
+hb_font_get_font_v_extents_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_font_extents_t *metrics,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_font_v_extents (metrics);
   if (ret) {
@@ -86,7 +102,7 @@
 static hb_bool_t
 hb_font_get_nominal_glyph_nil (hb_font_t *font HB_UNUSED,
 			       void *font_data HB_UNUSED,
-			       hb_codepoint_t unicode,
+			       hb_codepoint_t unicode HB_UNUSED,
 			       hb_codepoint_t *glyph,
 			       void *user_data HB_UNUSED)
 {
@@ -94,20 +110,53 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_nominal_glyph_parent (hb_font_t *font,
-				  void *font_data HB_UNUSED,
-				  hb_codepoint_t unicode,
-				  hb_codepoint_t *glyph,
-				  void *user_data HB_UNUSED)
+hb_font_get_nominal_glyph_default (hb_font_t *font,
+				   void *font_data HB_UNUSED,
+				   hb_codepoint_t unicode,
+				   hb_codepoint_t *glyph,
+				   void *user_data HB_UNUSED)
 {
+  if (font->has_nominal_glyphs_func_set ())
+  {
+    return font->get_nominal_glyphs (1, &unicode, 0, glyph, 0);
+  }
   return font->parent->get_nominal_glyph (unicode, glyph);
 }
 
+#define hb_font_get_nominal_glyphs_nil hb_font_get_nominal_glyphs_default
+static unsigned int
+hb_font_get_nominal_glyphs_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    unsigned int count,
+				    const hb_codepoint_t *first_unicode,
+				    unsigned int unicode_stride,
+				    hb_codepoint_t *first_glyph,
+				    unsigned int glyph_stride,
+				    void *user_data HB_UNUSED)
+{
+  if (font->has_nominal_glyph_func_set ())
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (!font->get_nominal_glyph (*first_unicode, first_glyph))
+        return i;
+
+      first_unicode = &StructAtOffset<hb_codepoint_t> (first_unicode, unicode_stride);
+      first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+    }
+    return count;
+  }
+
+  return font->parent->get_nominal_glyphs (count,
+					   first_unicode, unicode_stride,
+					   first_glyph, glyph_stride);
+}
+
 static hb_bool_t
 hb_font_get_variation_glyph_nil (hb_font_t *font HB_UNUSED,
 				 void *font_data HB_UNUSED,
-				 hb_codepoint_t unicode,
-				 hb_codepoint_t variation_selector,
+				 hb_codepoint_t unicode HB_UNUSED,
+				 hb_codepoint_t variation_selector HB_UNUSED,
 				 hb_codepoint_t *glyph,
 				 void *user_data HB_UNUSED)
 {
@@ -115,12 +164,12 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_variation_glyph_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t unicode,
-				    hb_codepoint_t variation_selector,
-				    hb_codepoint_t *glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_variation_glyph_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t unicode,
+				     hb_codepoint_t variation_selector,
+				     hb_codepoint_t *glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent->get_variation_glyph (unicode, variation_selector, glyph);
 }
@@ -129,42 +178,118 @@
 static hb_position_t
 hb_font_get_glyph_h_advance_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
-				 hb_codepoint_t glyph,
+				 hb_codepoint_t glyph HB_UNUSED,
 				 void *user_data HB_UNUSED)
 {
   return font->x_scale;
 }
 static hb_position_t
-hb_font_get_glyph_h_advance_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_h_advance_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t glyph,
+				     void *user_data HB_UNUSED)
 {
+  if (font->has_glyph_h_advances_func_set ())
+  {
+    hb_position_t ret;
+    font->get_glyph_h_advances (1, &glyph, 0, &ret, 0);
+    return ret;
+  }
   return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph));
 }
 
 static hb_position_t
 hb_font_get_glyph_v_advance_nil (hb_font_t *font,
 				 void *font_data HB_UNUSED,
-				 hb_codepoint_t glyph,
+				 hb_codepoint_t glyph HB_UNUSED,
 				 void *user_data HB_UNUSED)
 {
   /* TODO use font_extents.ascender+descender */
   return font->y_scale;
 }
 static hb_position_t
-hb_font_get_glyph_v_advance_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_v_advance_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t glyph,
+				     void *user_data HB_UNUSED)
 {
+  if (font->has_glyph_v_advances_func_set ())
+  {
+    hb_position_t ret;
+    font->get_glyph_v_advances (1, &glyph, 0, &ret, 0);
+    return ret;
+  }
   return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph));
 }
 
+#define hb_font_get_glyph_h_advances_nil hb_font_get_glyph_h_advances_default
+static void
+hb_font_get_glyph_h_advances_default (hb_font_t* font,
+				      void* font_data HB_UNUSED,
+				      unsigned int count,
+				      const hb_codepoint_t *first_glyph,
+				      unsigned int glyph_stride,
+				      hb_position_t *first_advance,
+				      unsigned int advance_stride,
+				      void *user_data HB_UNUSED)
+{
+  if (font->has_glyph_h_advance_func_set ())
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance = font->get_glyph_h_advance (*first_glyph);
+      first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+    }
+    return;
+  }
+
+  font->parent->get_glyph_h_advances (count,
+				      first_glyph, glyph_stride,
+				      first_advance, advance_stride);
+  for (unsigned int i = 0; i < count; i++)
+  {
+    *first_advance = font->parent_scale_x_distance (*first_advance);
+    first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+  }
+}
+
+#define hb_font_get_glyph_v_advances_nil hb_font_get_glyph_v_advances_default
+static void
+hb_font_get_glyph_v_advances_default (hb_font_t* font,
+				      void* font_data HB_UNUSED,
+				      unsigned int count,
+				      const hb_codepoint_t *first_glyph,
+				      unsigned int glyph_stride,
+				      hb_position_t *first_advance,
+				      unsigned int advance_stride,
+				      void *user_data HB_UNUSED)
+{
+  if (font->has_glyph_v_advance_func_set ())
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance = font->get_glyph_v_advance (*first_glyph);
+      first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+    }
+    return;
+  }
+
+  font->parent->get_glyph_v_advances (count,
+				      first_glyph, glyph_stride,
+				      first_advance, advance_stride);
+  for (unsigned int i = 0; i < count; i++)
+  {
+    *first_advance = font->parent_scale_y_distance (*first_advance);
+    first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+  }
+}
+
 static hb_bool_t
 hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED,
 				void *font_data HB_UNUSED,
-				hb_codepoint_t glyph,
+				hb_codepoint_t glyph HB_UNUSED,
 				hb_position_t *x,
 				hb_position_t *y,
 				void *user_data HB_UNUSED)
@@ -173,12 +298,12 @@
   return true;
 }
 static hb_bool_t
-hb_font_get_glyph_h_origin_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_codepoint_t glyph,
-				   hb_position_t *x,
-				   hb_position_t *y,
-				   void *user_data HB_UNUSED)
+hb_font_get_glyph_h_origin_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_codepoint_t glyph,
+				    hb_position_t *x,
+				    hb_position_t *y,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y);
   if (ret)
@@ -189,7 +314,7 @@
 static hb_bool_t
 hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED,
 				void *font_data HB_UNUSED,
-				hb_codepoint_t glyph,
+				hb_codepoint_t glyph HB_UNUSED,
 				hb_position_t *x,
 				hb_position_t *y,
 				void *user_data HB_UNUSED)
@@ -198,12 +323,12 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_v_origin_parent (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_codepoint_t glyph,
-				   hb_position_t *x,
-				   hb_position_t *y,
-				   void *user_data HB_UNUSED)
+hb_font_get_glyph_v_origin_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    hb_codepoint_t glyph,
+				    hb_position_t *x,
+				    hb_position_t *y,
+				    void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y);
   if (ret)
@@ -214,18 +339,18 @@
 static hb_position_t
 hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED,
 				 void *font_data HB_UNUSED,
-				 hb_codepoint_t left_glyph,
-				 hb_codepoint_t right_glyph,
+				 hb_codepoint_t left_glyph HB_UNUSED,
+				 hb_codepoint_t right_glyph HB_UNUSED,
 				 void *user_data HB_UNUSED)
 {
   return 0;
 }
 static hb_position_t
-hb_font_get_glyph_h_kerning_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t left_glyph,
-				    hb_codepoint_t right_glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_h_kerning_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t left_glyph,
+				     hb_codepoint_t right_glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph));
 }
@@ -233,18 +358,18 @@
 static hb_position_t
 hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED,
 				 void *font_data HB_UNUSED,
-				 hb_codepoint_t top_glyph,
-				 hb_codepoint_t bottom_glyph,
+				 hb_codepoint_t top_glyph HB_UNUSED,
+				 hb_codepoint_t bottom_glyph HB_UNUSED,
 				 void *user_data HB_UNUSED)
 {
   return 0;
 }
 static hb_position_t
-hb_font_get_glyph_v_kerning_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t top_glyph,
-				    hb_codepoint_t bottom_glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_v_kerning_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     hb_codepoint_t top_glyph,
+				     hb_codepoint_t bottom_glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph));
 }
@@ -252,7 +377,7 @@
 static hb_bool_t
 hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED,
 			       void *font_data HB_UNUSED,
-			       hb_codepoint_t glyph,
+			       hb_codepoint_t glyph HB_UNUSED,
 			       hb_glyph_extents_t *extents,
 			       void *user_data HB_UNUSED)
 {
@@ -260,11 +385,11 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_extents_parent (hb_font_t *font,
-				  void *font_data HB_UNUSED,
-				  hb_codepoint_t glyph,
-				  hb_glyph_extents_t *extents,
-				  void *user_data HB_UNUSED)
+hb_font_get_glyph_extents_default (hb_font_t *font,
+				   void *font_data HB_UNUSED,
+				   hb_codepoint_t glyph,
+				   hb_glyph_extents_t *extents,
+				   void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents);
   if (ret) {
@@ -277,8 +402,8 @@
 static hb_bool_t
 hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED,
 				     void *font_data HB_UNUSED,
-				     hb_codepoint_t glyph,
-				     unsigned int point_index,
+				     hb_codepoint_t glyph HB_UNUSED,
+				     unsigned int point_index HB_UNUSED,
 				     hb_position_t *x,
 				     hb_position_t *y,
 				     void *user_data HB_UNUSED)
@@ -287,13 +412,13 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_contour_point_parent (hb_font_t *font,
-					void *font_data HB_UNUSED,
-					hb_codepoint_t glyph,
-					unsigned int point_index,
-					hb_position_t *x,
-					hb_position_t *y,
-					void *user_data HB_UNUSED)
+hb_font_get_glyph_contour_point_default (hb_font_t *font,
+					 void *font_data HB_UNUSED,
+					 hb_codepoint_t glyph,
+					 unsigned int point_index,
+					 hb_position_t *x,
+					 hb_position_t *y,
+					 void *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y);
   if (ret)
@@ -304,7 +429,7 @@
 static hb_bool_t
 hb_font_get_glyph_name_nil (hb_font_t *font HB_UNUSED,
 			    void *font_data HB_UNUSED,
-			    hb_codepoint_t glyph,
+			    hb_codepoint_t glyph HB_UNUSED,
 			    char *name, unsigned int size,
 			    void *user_data HB_UNUSED)
 {
@@ -312,11 +437,11 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_name_parent (hb_font_t *font,
-			       void *font_data HB_UNUSED,
-			       hb_codepoint_t glyph,
-			       char *name, unsigned int size,
-			       void *user_data HB_UNUSED)
+hb_font_get_glyph_name_default (hb_font_t *font,
+				void *font_data HB_UNUSED,
+				hb_codepoint_t glyph,
+				char *name, unsigned int size,
+				void *user_data HB_UNUSED)
 {
   return font->parent->get_glyph_name (glyph, name, size);
 }
@@ -324,7 +449,8 @@
 static hb_bool_t
 hb_font_get_glyph_from_name_nil (hb_font_t *font HB_UNUSED,
 				 void *font_data HB_UNUSED,
-				 const char *name, int len, /* -1 means nul-terminated */
+				 const char *name HB_UNUSED,
+				 int len HB_UNUSED, /* -1 means nul-terminated */
 				 hb_codepoint_t *glyph,
 				 void *user_data HB_UNUSED)
 {
@@ -332,16 +458,17 @@
   return false;
 }
 static hb_bool_t
-hb_font_get_glyph_from_name_parent (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    const char *name, int len, /* -1 means nul-terminated */
-				    hb_codepoint_t *glyph,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_from_name_default (hb_font_t *font,
+				     void *font_data HB_UNUSED,
+				     const char *name, int len, /* -1 means nul-terminated */
+				     hb_codepoint_t *glyph,
+				     void *user_data HB_UNUSED)
 {
   return font->parent->get_glyph_from_name (name, len, glyph);
 }
 
-static const hb_font_funcs_t _hb_font_funcs_nil = {
+DEFINE_NULL_INSTANCE (hb_font_funcs_t) =
+{
   HB_OBJECT_HEADER_STATIC,
 
   true, /* immutable */
@@ -364,7 +491,8 @@
     }
   }
 };
-static const hb_font_funcs_t _hb_font_funcs_parent = {
+
+static const hb_font_funcs_t _hb_font_funcs_default = {
   HB_OBJECT_HEADER_STATIC,
 
   true, /* immutable */
@@ -381,7 +509,7 @@
   },
   {
     {
-#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_parent,
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_default,
       HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
     }
@@ -406,7 +534,7 @@
   if (!(ffuncs = hb_object_create<hb_font_funcs_t> ()))
     return hb_font_funcs_get_empty ();
 
-  ffuncs->get = _hb_font_funcs_parent.get;
+  ffuncs->get = _hb_font_funcs_default.get;
 
   return ffuncs;
 }
@@ -423,7 +551,7 @@
 hb_font_funcs_t *
 hb_font_funcs_get_empty (void)
 {
-  return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_parent);
+  return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_default);
 }
 
 /**
@@ -519,6 +647,8 @@
 {
   if (unlikely (hb_object_is_inert (ffuncs)))
     return;
+  if (ffuncs->immutable)
+    return;
 
   ffuncs->immutable = true;
 }
@@ -562,9 +692,9 @@
     ffuncs->user_data.name = user_data;                                  \
     ffuncs->destroy.name = destroy;                                      \
   } else {                                                               \
-    ffuncs->get.f.name = hb_font_get_##name##_parent;                    \
-    ffuncs->user_data.name = nullptr;                                       \
-    ffuncs->destroy.name = nullptr;                                         \
+    ffuncs->get.f.name = hb_font_get_##name##_default;                   \
+    ffuncs->user_data.name = nullptr;                                    \
+    ffuncs->destroy.name = nullptr;                                      \
   }                                                                      \
 }
 
@@ -572,11 +702,16 @@
 #undef HB_FONT_FUNC_IMPLEMENT
 
 bool
+hb_font_t::has_func_set (unsigned int i)
+{
+  return this->klass->get.array[i] != _hb_font_funcs_default.get.array[i];
+}
+
+bool
 hb_font_t::has_func (unsigned int i)
 {
-  if (parent && parent != hb_font_get_empty () && parent->has_func (i))
-    return true;
-  return this->klass->get.array[i] != _hb_font_funcs_parent.get.array[i];
+  return has_func_set (i) ||
+	 (parent && parent != &_hb_Null_hb_font_t && parent->has_func (i));
 }
 
 /* Public getters */
@@ -718,6 +853,43 @@
 }
 
 /**
+ * hb_font_get_glyph_h_advances:
+ * @font: a font.
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+void
+hb_font_get_glyph_h_advances (hb_font_t* font,
+			      unsigned int count,
+			      const hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride)
+{
+  font->get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
+}
+/**
+ * hb_font_get_glyph_v_advances:
+ * @font: a font.
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+void
+hb_font_get_glyph_v_advances (hb_font_t* font,
+			      unsigned int count,
+			      const hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride)
+{
+  font->get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
+}
+
+/**
  * hb_font_get_glyph_h_origin:
  * @font: a font.
  * @glyph: 
@@ -770,6 +942,7 @@
  * Return value: 
  *
  * Since: 0.9.2
+ * Deprecated: 2.0.0
  **/
 hb_position_t
 hb_font_get_glyph_h_kerning (hb_font_t *font,
@@ -789,6 +962,7 @@
  * Return value: 
  *
  * Since: 0.9.2
+ * Deprecated: 2.0.0
  **/
 hb_position_t
 hb_font_get_glyph_v_kerning (hb_font_t *font,
@@ -888,7 +1062,7 @@
  * hb_font_get_extents_for_direction:
  * @font: a font.
  * @direction:
- * @extents:
+ * @extents: (out):
  *
  *
  *
@@ -921,6 +1095,26 @@
 {
   return font->get_glyph_advance_for_direction (glyph, direction, x, y);
 }
+/**
+ * hb_font_get_glyph_advances_for_direction:
+ * @font: a font.
+ * @direction: 
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+HB_EXTERN void
+hb_font_get_glyph_advances_for_direction (hb_font_t* font,
+					  hb_direction_t direction,
+					  unsigned int count,
+					  const hb_codepoint_t *first_glyph,
+					  unsigned glyph_stride,
+					  hb_position_t *first_advance,
+					  unsigned advance_stride)
+{
+  font->get_glyph_advances_for_direction (direction, count, first_glyph, glyph_stride, first_advance, advance_stride);
+}
 
 /**
  * hb_font_get_glyph_origin_for_direction:
@@ -997,6 +1191,7 @@
  * 
  *
  * Since: 0.9.2
+ * Deprecated: 2.0.0
  **/
 void
 hb_font_get_glyph_kerning_for_direction (hb_font_t *font,
@@ -1100,18 +1295,39 @@
  * hb_font_t
  */
 
-/**
- * hb_font_create: (Xconstructor)
- * @face: a face.
- *
- * 
- *
- * Return value: (transfer full): 
- *
- * Since: 0.9.2
- **/
-hb_font_t *
-hb_font_create (hb_face_t *face)
+DEFINE_NULL_INSTANCE (hb_font_t) =
+{
+  HB_OBJECT_HEADER_STATIC,
+
+  true, /* immutable */
+
+  nullptr, /* parent */
+  const_cast<hb_face_t *> (&_hb_Null_hb_face_t),
+
+  1000, /* x_scale */
+  1000, /* y_scale */
+
+  0, /* x_ppem */
+  0, /* y_ppem */
+  0, /* ptem */
+
+  0, /* num_coords */
+  nullptr, /* coords */
+
+  const_cast<hb_font_funcs_t *> (&_hb_Null_hb_font_funcs_t), /* klass */
+  nullptr, /* user_data */
+  nullptr, /* destroy */
+
+  {
+#define HB_SHAPER_IMPLEMENT(shaper) HB_ATOMIC_PTR_INIT (HB_SHAPER_DATA_INVALID),
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+  }
+};
+
+
+static hb_font_t *
+_hb_font_create (hb_face_t *face)
 {
   hb_font_t *font;
 
@@ -1131,6 +1347,27 @@
 }
 
 /**
+ * hb_font_create: (Xconstructor)
+ * @face: a face.
+ *
+ * 
+ *
+ * Return value: (transfer full): 
+ *
+ * Since: 0.9.2
+ **/
+hb_font_t *
+hb_font_create (hb_face_t *face)
+{
+  hb_font_t *font = _hb_font_create (face);
+
+  /* Install our in-house, very lightweight, funcs. */
+  hb_ot_font_set_funcs (font);
+
+  return font;
+}
+
+/**
  * hb_font_create_sub_font:
  * @parent: parent font.
  *
@@ -1146,7 +1383,7 @@
   if (unlikely (!parent))
     parent = hb_font_get_empty ();
 
-  hb_font_t *font = hb_font_create (parent->face);
+  hb_font_t *font = _hb_font_create (parent->face);
 
   if (unlikely (hb_object_is_inert (font)))
     return font;
@@ -1187,36 +1424,7 @@
 hb_font_t *
 hb_font_get_empty (void)
 {
-  static const hb_font_t _hb_font_nil = {
-    HB_OBJECT_HEADER_STATIC,
-
-    true, /* immutable */
-
-    nullptr, /* parent */
-    const_cast<hb_face_t *> (&_hb_face_nil),
-
-    1000, /* x_scale */
-    1000, /* y_scale */
-
-    0, /* x_ppem */
-    0, /* y_ppem */
-    0, /* ptem */
-
-    0, /* num_coords */
-    nullptr, /* coords */
-
-    const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */
-    nullptr, /* user_data */
-    nullptr, /* destroy */
-
-    {
-#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-    }
-  };
-
-  return const_cast<hb_font_t *> (&_hb_font_nil);
+  return const_cast<hb_font_t *> (&Null(hb_font_t));
 }
 
 /**
@@ -1319,6 +1527,8 @@
 {
   if (unlikely (hb_object_is_inert (font)))
     return;
+  if (font->immutable)
+    return;
 
   if (font->parent)
     hb_font_make_immutable (font->parent);
@@ -1578,9 +1788,11 @@
 /**
  * hb_font_set_ptem:
  * @font: a font.
- * @ptem: 
+ * @ptem: font size in points.
  *
- * Sets "point size" of the font.
+ * Sets "point size" of the font.  Set to 0 to unset.
+ *
+ * There are 72 points in an inch.
  *
  * Since: 1.6.0
  **/
@@ -1718,8 +1930,6 @@
 }
 
 
-#ifndef HB_DISABLE_DEPRECATED
-
 /*
  * Deprecated get_glyph_func():
  */
@@ -1806,9 +2016,9 @@
 /**
  * hb_font_funcs_set_glyph_func:
  * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @func: (closure user_data) (destroy destroy) (scope notified): callback function.
+ * @user_data: data to pass to @func.
+ * @destroy: function to call when @user_data is not needed anymore.
  *
  * Deprecated.  Use hb_font_funcs_set_nominal_glyph_func() and
  * hb_font_funcs_set_variation_glyph_func() instead.
@@ -1842,5 +2052,3 @@
 					  trampoline,
 					  trampoline_destroy);
 }
-
-#endif /* HB_DISABLE_DEPRECATED */
diff --git a/src/hb-font.h b/src/hb-font.h
index c95b61d..74c61ab 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -125,6 +125,14 @@
 							 hb_codepoint_t *glyph,
 							 void *user_data);
 
+typedef unsigned int (*hb_font_get_nominal_glyphs_func_t) (hb_font_t *font, void *font_data,
+							   unsigned int count,
+							   const hb_codepoint_t *first_unicode,
+							   unsigned int unicode_stride,
+							   hb_codepoint_t *first_glyph,
+							   unsigned int glyph_stride,
+							   void *user_data);
+
 
 typedef hb_position_t (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, void *font_data,
 							   hb_codepoint_t glyph,
@@ -132,6 +140,16 @@
 typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t;
 typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t;
 
+typedef void (*hb_font_get_glyph_advances_func_t) (hb_font_t* font, void* font_data,
+						   unsigned int count,
+						   const hb_codepoint_t *first_glyph,
+						   unsigned glyph_stride,
+						   hb_position_t *first_advance,
+						   unsigned advance_stride,
+						   void *user_data);
+typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_h_advances_func_t;
+typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t;
+
 typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data,
 						      hb_codepoint_t glyph,
 						      hb_position_t *x, hb_position_t *y,
@@ -139,12 +157,6 @@
 typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t;
 typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t;
 
-typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data,
-							   hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
-							   void *user_data);
-typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t;
-typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t;
-
 
 typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data,
 						       hb_codepoint_t glyph,
@@ -217,6 +229,22 @@
 				      void *user_data, hb_destroy_func_t destroy);
 
 /**
+ * hb_font_funcs_set_nominal_glyphs_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Since: 2.0.0
+ **/
+HB_EXTERN void
+hb_font_funcs_set_nominal_glyphs_func (hb_font_funcs_t *ffuncs,
+				       hb_font_get_nominal_glyphs_func_t func,
+				       void *user_data, hb_destroy_func_t destroy);
+
+/**
  * hb_font_funcs_set_variation_glyph_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
@@ -265,6 +293,38 @@
 					void *user_data, hb_destroy_func_t destroy);
 
 /**
+ * hb_font_funcs_set_glyph_h_advances_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_h_advances_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_h_advances_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
+/**
+ * hb_font_funcs_set_glyph_v_advances_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 1.8.6
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_v_advances_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_v_advances_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
+/**
  * hb_font_funcs_set_glyph_h_origin_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
@@ -297,38 +357,6 @@
 				       void *user_data, hb_destroy_func_t destroy);
 
 /**
- * hb_font_funcs_set_glyph_h_kerning_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
- *
- * 
- *
- * Since: 0.9.2
- **/
-HB_EXTERN void
-hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs,
-					hb_font_get_glyph_h_kerning_func_t func,
-					void *user_data, hb_destroy_func_t destroy);
-
-/**
- * hb_font_funcs_set_glyph_v_kerning_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
- *
- * 
- *
- * Since: 0.9.2
- **/
-HB_EXTERN void
-hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs,
-					hb_font_get_glyph_v_kerning_func_t func,
-					void *user_data, hb_destroy_func_t destroy);
-
-/**
  * hb_font_funcs_set_glyph_extents_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
@@ -417,6 +445,21 @@
 hb_font_get_glyph_v_advance (hb_font_t *font,
 			     hb_codepoint_t glyph);
 
+HB_EXTERN void
+hb_font_get_glyph_h_advances (hb_font_t* font,
+			      unsigned int count,
+			      const hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride);
+HB_EXTERN void
+hb_font_get_glyph_v_advances (hb_font_t* font,
+			      unsigned int count,
+			      const hb_codepoint_t *first_glyph,
+			      unsigned glyph_stride,
+			      hb_position_t *first_advance,
+			      unsigned advance_stride);
+
 HB_EXTERN hb_bool_t
 hb_font_get_glyph_h_origin (hb_font_t *font,
 			    hb_codepoint_t glyph,
@@ -426,13 +469,6 @@
 			    hb_codepoint_t glyph,
 			    hb_position_t *x, hb_position_t *y);
 
-HB_EXTERN hb_position_t
-hb_font_get_glyph_h_kerning (hb_font_t *font,
-			     hb_codepoint_t left_glyph, hb_codepoint_t right_glyph);
-HB_EXTERN hb_position_t
-hb_font_get_glyph_v_kerning (hb_font_t *font,
-			     hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph);
-
 HB_EXTERN hb_bool_t
 hb_font_get_glyph_extents (hb_font_t *font,
 			   hb_codepoint_t glyph,
@@ -472,6 +508,14 @@
 					 hb_direction_t direction,
 					 hb_position_t *x, hb_position_t *y);
 HB_EXTERN void
+hb_font_get_glyph_advances_for_direction (hb_font_t* font,
+					  hb_direction_t direction,
+					  unsigned int count,
+					  const hb_codepoint_t *first_glyph,
+					  unsigned glyph_stride,
+					  hb_position_t *first_advance,
+					  unsigned advance_stride);
+HB_EXTERN void
 hb_font_get_glyph_origin_for_direction (hb_font_t *font,
 					hb_codepoint_t glyph,
 					hb_direction_t direction,
@@ -487,12 +531,6 @@
 					     hb_direction_t direction,
 					     hb_position_t *x, hb_position_t *y);
 
-HB_EXTERN void
-hb_font_get_glyph_kerning_for_direction (hb_font_t *font,
-					 hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
-					 hb_direction_t direction,
-					 hb_position_t *x, hb_position_t *y);
-
 HB_EXTERN hb_bool_t
 hb_font_get_glyph_extents_for_origin (hb_font_t *font,
 				      hb_codepoint_t glyph,
diff --git a/src/hb-font.hh b/src/hb-font.hh
new file mode 100644
index 0000000..3dce233
--- /dev/null
+++ b/src/hb-font.hh
@@ -0,0 +1,623 @@
+/*
+ * Copyright © 2009  Red Hat, Inc.
+ * Copyright © 2011  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_FONT_HH
+#define HB_FONT_HH
+
+#include "hb.hh"
+
+#include "hb-face.hh"
+#include "hb-shaper.hh"
+
+
+/*
+ * hb_font_funcs_t
+ */
+
+#define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \
+  HB_FONT_FUNC_IMPLEMENT (font_h_extents) \
+  HB_FONT_FUNC_IMPLEMENT (font_v_extents) \
+  HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \
+  HB_FONT_FUNC_IMPLEMENT (nominal_glyphs) \
+  HB_FONT_FUNC_IMPLEMENT (variation_glyph) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_h_advances) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_v_advances) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_extents) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_name) \
+  HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \
+  /* ^--- Add new callbacks here */
+
+struct hb_font_funcs_t
+{
+  hb_object_header_t header;
+
+  hb_bool_t immutable;
+
+  struct {
+#define HB_FONT_FUNC_IMPLEMENT(name) void *name;
+    HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+  } user_data;
+
+  struct {
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name;
+    HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+  } destroy;
+
+  /* Don't access these directly.  Call font->get_*() instead. */
+  union get_t {
+    struct get_funcs_t {
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name;
+      HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+    } f;
+    void (*array[0
+#define HB_FONT_FUNC_IMPLEMENT(name) +1
+      HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+		]) (void);
+  } get;
+};
+DECLARE_NULL_INSTANCE (hb_font_funcs_t);
+
+
+/*
+ * hb_font_t
+ */
+
+struct hb_font_t
+{
+  hb_object_header_t header;
+
+  hb_bool_t immutable;
+
+  hb_font_t *parent;
+  hb_face_t *face;
+
+  int x_scale;
+  int y_scale;
+
+  unsigned int x_ppem;
+  unsigned int y_ppem;
+
+  float ptem;
+
+  /* Font variation coordinates. */
+  unsigned int num_coords;
+  int *coords;
+
+  hb_font_funcs_t   *klass;
+  void              *user_data;
+  hb_destroy_func_t  destroy;
+
+  struct hb_shaper_data_t shaper_data;
+
+
+  /* Convert from font-space to user-space */
+  inline int dir_scale (hb_direction_t direction)
+  { return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; }
+  inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, x_scale); }
+  inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); }
+  inline hb_position_t em_scalef_x (float v) { return em_scalef (v, this->x_scale); }
+  inline hb_position_t em_scalef_y (float v) { return em_scalef (v, this->y_scale); }
+  inline float em_fscale_x (int16_t v) { return em_fscale (v, x_scale); }
+  inline float em_fscale_y (int16_t v) { return em_fscale (v, y_scale); }
+  inline hb_position_t em_scale_dir (int16_t v, hb_direction_t direction)
+  { return em_scale (v, dir_scale (direction)); }
+
+  /* Convert from parent-font user-space to our user-space */
+  inline hb_position_t parent_scale_x_distance (hb_position_t v) {
+    if (unlikely (parent && parent->x_scale != x_scale))
+      return (hb_position_t) (v * (int64_t) this->x_scale / this->parent->x_scale);
+    return v;
+  }
+  inline hb_position_t parent_scale_y_distance (hb_position_t v) {
+    if (unlikely (parent && parent->y_scale != y_scale))
+      return (hb_position_t) (v * (int64_t) this->y_scale / this->parent->y_scale);
+    return v;
+  }
+  inline hb_position_t parent_scale_x_position (hb_position_t v) {
+    return parent_scale_x_distance (v);
+  }
+  inline hb_position_t parent_scale_y_position (hb_position_t v) {
+    return parent_scale_y_distance (v);
+  }
+
+  inline void parent_scale_distance (hb_position_t *x, hb_position_t *y) {
+    *x = parent_scale_x_distance (*x);
+    *y = parent_scale_y_distance (*y);
+  }
+  inline void parent_scale_position (hb_position_t *x, hb_position_t *y) {
+    *x = parent_scale_x_position (*x);
+    *y = parent_scale_y_position (*y);
+  }
+
+
+  /* Public getters */
+
+  HB_INTERNAL bool has_func (unsigned int i);
+  HB_INTERNAL bool has_func_set (unsigned int i);
+
+  /* has_* ... */
+#define HB_FONT_FUNC_IMPLEMENT(name) \
+  bool \
+  has_##name##_func (void) \
+  { \
+    hb_font_funcs_t *funcs = this->klass; \
+    unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \
+    return has_func (i); \
+  } \
+  bool \
+  has_##name##_func_set (void) \
+  { \
+    hb_font_funcs_t *funcs = this->klass; \
+    unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \
+    return has_func_set (i); \
+  }
+  HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+
+  inline hb_bool_t get_font_h_extents (hb_font_extents_t *extents)
+  {
+    memset (extents, 0, sizeof (*extents));
+    return klass->get.f.font_h_extents (this, user_data,
+					extents,
+					klass->user_data.font_h_extents);
+  }
+  inline hb_bool_t get_font_v_extents (hb_font_extents_t *extents)
+  {
+    memset (extents, 0, sizeof (*extents));
+    return klass->get.f.font_v_extents (this, user_data,
+					extents,
+					klass->user_data.font_v_extents);
+  }
+
+  inline bool has_glyph (hb_codepoint_t unicode)
+  {
+    hb_codepoint_t glyph;
+    return get_nominal_glyph (unicode, &glyph);
+  }
+
+  inline hb_bool_t get_nominal_glyph (hb_codepoint_t unicode,
+				      hb_codepoint_t *glyph)
+  {
+    *glyph = 0;
+    return klass->get.f.nominal_glyph (this, user_data,
+				       unicode, glyph,
+				       klass->user_data.nominal_glyph);
+  }
+  inline unsigned int get_nominal_glyphs (unsigned int count,
+					  const hb_codepoint_t *first_unicode,
+					  unsigned int unicode_stride,
+					  hb_codepoint_t *first_glyph,
+					  unsigned int glyph_stride)
+  {
+    return klass->get.f.nominal_glyphs (this, user_data,
+					count,
+					first_unicode, unicode_stride,
+					first_glyph, glyph_stride,
+					klass->user_data.nominal_glyphs);
+  }
+
+  inline hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector,
+					hb_codepoint_t *glyph)
+  {
+    *glyph = 0;
+    return klass->get.f.variation_glyph (this, user_data,
+					 unicode, variation_selector, glyph,
+					 klass->user_data.variation_glyph);
+  }
+
+  inline hb_position_t get_glyph_h_advance (hb_codepoint_t glyph)
+  {
+    return klass->get.f.glyph_h_advance (this, user_data,
+					 glyph,
+					 klass->user_data.glyph_h_advance);
+  }
+
+  inline hb_position_t get_glyph_v_advance (hb_codepoint_t glyph)
+  {
+    return klass->get.f.glyph_v_advance (this, user_data,
+					 glyph,
+					 klass->user_data.glyph_v_advance);
+  }
+
+  inline void get_glyph_h_advances (unsigned int count,
+				    const hb_codepoint_t *first_glyph,
+				    unsigned int glyph_stride,
+				    hb_position_t *first_advance,
+				    unsigned int advance_stride)
+  {
+    return klass->get.f.glyph_h_advances (this, user_data,
+					  count,
+					  first_glyph, glyph_stride,
+					  first_advance, advance_stride,
+					  klass->user_data.glyph_h_advances);
+  }
+
+  inline void get_glyph_v_advances (unsigned int count,
+				    const hb_codepoint_t *first_glyph,
+				    unsigned int glyph_stride,
+				    hb_position_t *first_advance,
+				    unsigned int advance_stride)
+  {
+    return klass->get.f.glyph_v_advances (this, user_data,
+					  count,
+					  first_glyph, glyph_stride,
+					  first_advance, advance_stride,
+					  klass->user_data.glyph_v_advances);
+  }
+
+  inline hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph,
+				       hb_position_t *x, hb_position_t *y)
+  {
+    *x = *y = 0;
+    return klass->get.f.glyph_h_origin (this, user_data,
+					glyph, x, y,
+					klass->user_data.glyph_h_origin);
+  }
+
+  inline hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph,
+				       hb_position_t *x, hb_position_t *y)
+  {
+    *x = *y = 0;
+    return klass->get.f.glyph_v_origin (this, user_data,
+					glyph, x, y,
+					klass->user_data.glyph_v_origin);
+  }
+
+  inline hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, hb_codepoint_t right_glyph)
+  {
+    return klass->get.f.glyph_h_kerning (this, user_data,
+					 left_glyph, right_glyph,
+					 klass->user_data.glyph_h_kerning);
+  }
+
+  inline hb_position_t get_glyph_v_kerning (hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph)
+  {
+    return klass->get.f.glyph_v_kerning (this, user_data,
+					 top_glyph, bottom_glyph,
+					 klass->user_data.glyph_v_kerning);
+  }
+
+  inline hb_bool_t get_glyph_extents (hb_codepoint_t glyph,
+				      hb_glyph_extents_t *extents)
+  {
+    memset (extents, 0, sizeof (*extents));
+    return klass->get.f.glyph_extents (this, user_data,
+				       glyph,
+				       extents,
+				       klass->user_data.glyph_extents);
+  }
+
+  inline hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index,
+					    hb_position_t *x, hb_position_t *y)
+  {
+    *x = *y = 0;
+    return klass->get.f.glyph_contour_point (this, user_data,
+					     glyph, point_index,
+					     x, y,
+					     klass->user_data.glyph_contour_point);
+  }
+
+  inline hb_bool_t get_glyph_name (hb_codepoint_t glyph,
+				   char *name, unsigned int size)
+  {
+    if (size) *name = '\0';
+    return klass->get.f.glyph_name (this, user_data,
+				    glyph,
+				    name, size,
+				    klass->user_data.glyph_name);
+  }
+
+  inline hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */
+					hb_codepoint_t *glyph)
+  {
+    *glyph = 0;
+    if (len == -1) len = strlen (name);
+    return klass->get.f.glyph_from_name (this, user_data,
+					 name, len,
+					 glyph,
+					 klass->user_data.glyph_from_name);
+  }
+
+
+  /* A bit higher-level, and with fallback */
+
+  inline void get_h_extents_with_fallback (hb_font_extents_t *extents)
+  {
+    if (!get_font_h_extents (extents))
+    {
+      extents->ascender = y_scale * .8;
+      extents->descender = extents->ascender - y_scale;
+      extents->line_gap = 0;
+    }
+  }
+  inline void get_v_extents_with_fallback (hb_font_extents_t *extents)
+  {
+    if (!get_font_v_extents (extents))
+    {
+      extents->ascender = x_scale / 2;
+      extents->descender = extents->ascender - x_scale;
+      extents->line_gap = 0;
+    }
+  }
+
+  inline void get_extents_for_direction (hb_direction_t direction,
+					 hb_font_extents_t *extents)
+  {
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+      get_h_extents_with_fallback (extents);
+    else
+      get_v_extents_with_fallback (extents);
+  }
+
+  inline void get_glyph_advance_for_direction (hb_codepoint_t glyph,
+					       hb_direction_t direction,
+					       hb_position_t *x, hb_position_t *y)
+  {
+    *x = *y = 0;
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+      *x = get_glyph_h_advance (glyph);
+    else
+      *y = get_glyph_v_advance (glyph);
+  }
+  inline void get_glyph_advances_for_direction (hb_direction_t direction,
+						unsigned int count,
+						const hb_codepoint_t *first_glyph,
+						unsigned glyph_stride,
+						hb_position_t *first_advance,
+						unsigned advance_stride)
+  {
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+      get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
+    else
+      get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
+  }
+
+  inline void guess_v_origin_minus_h_origin (hb_codepoint_t glyph,
+					     hb_position_t *x, hb_position_t *y)
+  {
+    *x = get_glyph_h_advance (glyph) / 2;
+
+    /* TODO cache this somehow?! */
+    hb_font_extents_t extents;
+    get_h_extents_with_fallback (&extents);
+    *y = extents.ascender;
+  }
+
+  inline void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph,
+						hb_position_t *x, hb_position_t *y)
+  {
+    if (!get_glyph_h_origin (glyph, x, y) &&
+	 get_glyph_v_origin (glyph, x, y))
+    {
+      hb_position_t dx, dy;
+      guess_v_origin_minus_h_origin (glyph, &dx, &dy);
+      *x -= dx; *y -= dy;
+    }
+  }
+  inline void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph,
+						hb_position_t *x, hb_position_t *y)
+  {
+    if (!get_glyph_v_origin (glyph, x, y) &&
+	 get_glyph_h_origin (glyph, x, y))
+    {
+      hb_position_t dx, dy;
+      guess_v_origin_minus_h_origin (glyph, &dx, &dy);
+      *x += dx; *y += dy;
+    }
+  }
+
+  inline void get_glyph_origin_for_direction (hb_codepoint_t glyph,
+					      hb_direction_t direction,
+					      hb_position_t *x, hb_position_t *y)
+  {
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
+      get_glyph_h_origin_with_fallback (glyph, x, y);
+    else
+      get_glyph_v_origin_with_fallback (glyph, x, y);
+  }
+
+  inline void add_glyph_h_origin (hb_codepoint_t glyph,
+				  hb_position_t *x, hb_position_t *y)
+  {
+    hb_position_t origin_x, origin_y;
+
+    get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y);
+
+    *x += origin_x;
+    *y += origin_y;
+  }
+  inline void add_glyph_v_origin (hb_codepoint_t glyph,
+				  hb_position_t *x, hb_position_t *y)
+  {
+    hb_position_t origin_x, origin_y;
+
+    get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y);
+
+    *x += origin_x;
+    *y += origin_y;
+  }
+  inline void add_glyph_origin_for_direction (hb_codepoint_t glyph,
+					      hb_direction_t direction,
+					      hb_position_t *x, hb_position_t *y)
+  {
+    hb_position_t origin_x, origin_y;
+
+    get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y);
+
+    *x += origin_x;
+    *y += origin_y;
+  }
+
+  inline void subtract_glyph_h_origin (hb_codepoint_t glyph,
+				       hb_position_t *x, hb_position_t *y)
+  {
+    hb_position_t origin_x, origin_y;
+
+    get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y);
+
+    *x -= origin_x;
+    *y -= origin_y;
+  }
+  inline void subtract_glyph_v_origin (hb_codepoint_t glyph,
+				       hb_position_t *x, hb_position_t *y)
+  {
+    hb_position_t origin_x, origin_y;
+
+    get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y);
+
+    *x -= origin_x;
+    *y -= origin_y;
+  }
+  inline void subtract_glyph_origin_for_direction (hb_codepoint_t glyph,
+						   hb_direction_t direction,
+						   hb_position_t *x, hb_position_t *y)
+  {
+    hb_position_t origin_x, origin_y;
+
+    get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y);
+
+    *x -= origin_x;
+    *y -= origin_y;
+  }
+
+  inline void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
+					       hb_direction_t direction,
+					       hb_position_t *x, hb_position_t *y)
+  {
+    if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) {
+      *y = 0;
+      *x = get_glyph_h_kerning (first_glyph, second_glyph);
+    } else {
+      *x = 0;
+      *y = get_glyph_v_kerning (first_glyph, second_glyph);
+    }
+  }
+
+  inline hb_bool_t get_glyph_extents_for_origin (hb_codepoint_t glyph,
+						 hb_direction_t direction,
+						 hb_glyph_extents_t *extents)
+  {
+    hb_bool_t ret = get_glyph_extents (glyph, extents);
+
+    if (ret)
+      subtract_glyph_origin_for_direction (glyph, direction, &extents->x_bearing, &extents->y_bearing);
+
+    return ret;
+  }
+
+  inline hb_bool_t get_glyph_contour_point_for_origin (hb_codepoint_t glyph, unsigned int point_index,
+						       hb_direction_t direction,
+						       hb_position_t *x, hb_position_t *y)
+  {
+    hb_bool_t ret = get_glyph_contour_point (glyph, point_index, x, y);
+
+    if (ret)
+      subtract_glyph_origin_for_direction (glyph, direction, x, y);
+
+    return ret;
+  }
+
+  /* Generates gidDDD if glyph has no name. */
+  inline void
+  glyph_to_string (hb_codepoint_t glyph,
+		   char *s, unsigned int size)
+  {
+    if (get_glyph_name (glyph, s, size)) return;
+
+    if (size && snprintf (s, size, "gid%u", glyph) < 0)
+      *s = '\0';
+  }
+
+  /* Parses gidDDD and uniUUUU strings automatically. */
+  inline hb_bool_t
+  glyph_from_string (const char *s, int len, /* -1 means nul-terminated */
+		     hb_codepoint_t *glyph)
+  {
+    if (get_glyph_from_name (s, len, glyph)) return true;
+
+    if (len == -1) len = strlen (s);
+
+    /* Straight glyph index. */
+    if (hb_codepoint_parse (s, len, 10, glyph))
+      return true;
+
+    if (len > 3)
+    {
+      /* gidDDD syntax for glyph indices. */
+      if (0 == strncmp (s, "gid", 3) &&
+	  hb_codepoint_parse (s + 3, len - 3, 10, glyph))
+	return true;
+
+      /* uniUUUU and other Unicode character indices. */
+      hb_codepoint_t unichar;
+      if (0 == strncmp (s, "uni", 3) &&
+	  hb_codepoint_parse (s + 3, len - 3, 16, &unichar) &&
+	  get_nominal_glyph (unichar, glyph))
+	return true;
+    }
+
+    return false;
+  }
+
+  inline hb_position_t em_scale (int16_t v, int scale)
+  {
+    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);
+  }
+  inline hb_position_t em_scalef (float v, int scale)
+  {
+    return (hb_position_t) round (v * scale / face->get_upem ());
+  }
+  inline float em_fscale (int16_t v, int scale)
+  {
+    return (float) v * scale / face->get_upem ();
+  }
+};
+DECLARE_NULL_INSTANCE (hb_font_t);
+
+#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
+#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font);
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
+
+
+#endif /* HB_FONT_HH */
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index f8a6b0c..5e05110 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -27,46 +27,58 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb-ft.h"
 
-#include "hb-font-private.hh"
+#include "hb-font.hh"
+#include "hb-machinery.hh"
+#include "hb-cache.hh"
 
 #include FT_ADVANCES_H
 #include FT_MULTIPLE_MASTERS_H
 #include FT_TRUETYPE_TABLES_H
 
 
+/**
+ * SECTION:hb-ft
+ * @title: hb-ft
+ * @short_description: FreeType integration
+ * @include: hb-ft.h
+ *
+ * Functions for using HarfBuzz with the FreeType library to provide face and
+ * font data.
+ **/
+
+
 /* TODO:
  *
  * In general, this file does a fine job of what it's supposed to do.
  * There are, however, things that need more work:
  *
- *   - I remember seeing FT_Get_Advance() without the NO_HINTING flag to be buggy.
- *     Have not investigated.
- *
  *   - FreeType works in 26.6 mode.  Clients can decide to use that mode, and everything
  *     would work fine.  However, we also abuse this API for performing in font-space,
  *     but don't pass the correct flags to FreeType.  We just abuse the no-hinting mode
  *     for that, such that no rounding etc happens.  As such, we don't set ppem, and
  *     pass NO_HINTING as load_flags.  Would be much better to use NO_SCALE, and scale
- *     ourselves, like we do in uniscribe, etc.
+ *     ourselves.
  *
  *   - We don't handle / allow for emboldening / obliqueing.
  *
  *   - In the future, we should add constructors to create fonts in font space?
- *
- *   - FT_Load_Glyph() is extremely costly.  Do something about it?
  */
 
 
 struct hb_ft_font_t
 {
+  mutable hb_mutex_t lock;
   FT_Face ft_face;
   int load_flags;
   bool symbol; /* Whether selected cmap is symbol cmap. */
   bool unref; /* Whether to destroy ft_face when done. */
+
+  mutable hb_atomic_int_t cached_x_scale;
+  mutable hb_advance_cache_t advance_cache;
 };
 
 static hb_ft_font_t *
@@ -77,12 +89,16 @@
   if (unlikely (!ft_font))
     return nullptr;
 
+  ft_font->lock.init ();
   ft_font->ft_face = ft_face;
   ft_font->symbol = symbol;
   ft_font->unref = unref;
 
   ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
 
+  ft_font->cached_x_scale.set (0);
+  ft_font->advance_cache.init ();
+
   return ft_font;
 }
 
@@ -97,9 +113,13 @@
 {
   hb_ft_font_t *ft_font = (hb_ft_font_t *) data;
 
+  ft_font->advance_cache.fini ();
+
   if (ft_font->unref)
     _hb_ft_face_destroy (ft_font->ft_face);
 
+  ft_font->lock.fini ();
+
   free (ft_font);
 }
 
@@ -167,6 +187,7 @@
 			 void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode);
 
   if (unlikely (!g))
@@ -190,6 +211,32 @@
   return true;
 }
 
+static unsigned int
+hb_ft_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
+			  void *font_data,
+			  unsigned int count,
+			  const hb_codepoint_t *first_unicode,
+			  unsigned int unicode_stride,
+			  hb_codepoint_t *first_glyph,
+			  unsigned int glyph_stride,
+			  void *user_data HB_UNUSED)
+{
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
+  unsigned int done;
+  for (done = 0;
+       done < count && (*first_glyph = FT_Get_Char_Index (ft_font->ft_face, *first_unicode));
+       done++)
+  {
+    first_unicode = &StructAtOffset<hb_codepoint_t> (first_unicode, unicode_stride);
+    first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+  }
+  /* We don't need to do ft_font->symbol dance here, since HB calls the singular
+   * nominal_glyph() for what we don't handle here. */
+  return done;
+}
+
+
 static hb_bool_t
 hb_ft_get_variation_glyph (hb_font_t *font HB_UNUSED,
 			   void *font_data,
@@ -199,6 +246,7 @@
 			   void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   unsigned int g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector);
 
   if (unlikely (!g))
@@ -208,22 +256,45 @@
   return true;
 }
 
-static hb_position_t
-hb_ft_get_glyph_h_advance (hb_font_t *font,
-			   void *font_data,
-			   hb_codepoint_t glyph,
-			   void *user_data HB_UNUSED)
+static void
+hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data,
+			    unsigned count,
+			    const hb_codepoint_t *first_glyph,
+			    unsigned glyph_stride,
+			    hb_position_t *first_advance,
+			    unsigned advance_stride,
+			    void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
-  FT_Fixed v;
+  hb_lock_t lock (ft_font->lock);
+  FT_Face ft_face = ft_font->ft_face;
+  int load_flags = ft_font->load_flags;
+  int mult = font->x_scale < 0 ? -1 : +1;
 
-  if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags, &v)))
-    return 0;
+  if (font->x_scale != ft_font->cached_x_scale.get ())
+  {
+    ft_font->advance_cache.clear ();
+    ft_font->cached_x_scale.set (font->x_scale);
+  }
 
-  if (font->x_scale < 0)
-    v = -v;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    FT_Fixed v = 0;
+    hb_codepoint_t glyph = *first_glyph;
 
-  return (v + (1<<9)) >> 10;
+    unsigned int cv;
+    if (ft_font->advance_cache.get (glyph, &cv))
+      v = cv;
+    else
+    {
+      FT_Get_Advance (ft_face, glyph, load_flags, &v);
+      ft_font->advance_cache.set (glyph, v);
+    }
+
+    *first_advance = (v * mult + (1<<9)) >> 10;
+    first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+    first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+  }
 }
 
 static hb_position_t
@@ -233,6 +304,7 @@
 			   void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Fixed v;
 
   if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v)))
@@ -255,6 +327,7 @@
 			  void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
@@ -273,23 +346,6 @@
   return true;
 }
 
-static hb_position_t
-hb_ft_get_glyph_h_kerning (hb_font_t *font,
-			   void *font_data,
-			   hb_codepoint_t left_glyph,
-			   hb_codepoint_t right_glyph,
-			   void *user_data HB_UNUSED)
-{
-  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
-  FT_Vector kerningv;
-
-  FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED;
-  if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv))
-    return 0;
-
-  return kerningv.x;
-}
-
 static hb_bool_t
 hb_ft_get_glyph_extents (hb_font_t *font,
 			 void *font_data,
@@ -298,6 +354,7 @@
 			 void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
@@ -330,6 +387,7 @@
 			       void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
@@ -355,8 +413,10 @@
 		      void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
+  FT_Face ft_face = ft_font->ft_face;
 
-  hb_bool_t ret = !FT_Get_Glyph_Name (ft_font->ft_face, glyph, name, size);
+  hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size);
   if (ret && (size && !*name))
     ret = false;
 
@@ -371,6 +431,7 @@
 			   void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
 
   if (len < 0)
@@ -403,6 +464,7 @@
 			  void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
   metrics->ascender = ft_face->size->metrics.ascender;
   metrics->descender = ft_face->size->metrics.descender;
@@ -416,41 +478,25 @@
   return true;
 }
 
-static hb_font_funcs_t *static_ft_funcs = nullptr;
-
 #ifdef HB_USE_ATEXIT
-static
-void free_static_ft_funcs (void)
-{
-retry:
-  hb_font_funcs_t *ft_funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ft_funcs);
-  if (!hb_atomic_ptr_cmpexch (&static_ft_funcs, ft_funcs, nullptr))
-    goto retry;
-
-  hb_font_funcs_destroy (ft_funcs);
-}
+static void free_static_ft_funcs (void);
 #endif
 
-static void
-_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref)
+static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ft_font_funcs_lazy_loader_t>
 {
-retry:
-  hb_font_funcs_t *funcs = (hb_font_funcs_t *) hb_atomic_ptr_get (&static_ft_funcs);
-
-  if (unlikely (!funcs))
+  static inline hb_font_funcs_t *create (void)
   {
-    funcs = hb_font_funcs_create ();
+    hb_font_funcs_t *funcs = hb_font_funcs_create ();
 
     hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr);
     //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, nullptr, nullptr);
     hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, nullptr, nullptr);
+    hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ft_get_nominal_glyphs, nullptr, nullptr);
     hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, nullptr, nullptr);
-    hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ft_get_glyph_h_advance, nullptr, nullptr);
+    hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ft_get_glyph_h_advances, nullptr, nullptr);
     hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, nullptr, nullptr);
     //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, nullptr, nullptr);
     hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, nullptr, nullptr);
-    hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, nullptr, nullptr);
-    //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ft_get_glyph_v_kerning, nullptr, nullptr);
     hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, nullptr, nullptr);
     hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, nullptr, nullptr);
     hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, nullptr, nullptr);
@@ -458,20 +504,35 @@
 
     hb_font_funcs_make_immutable (funcs);
 
-    if (!hb_atomic_ptr_cmpexch (&static_ft_funcs, nullptr, funcs)) {
-      hb_font_funcs_destroy (funcs);
-      goto retry;
-    }
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_ft_funcs);
+#endif
+
+    return funcs;
+  }
+} static_ft_funcs;
 
 #ifdef HB_USE_ATEXIT
-    atexit (free_static_ft_funcs); /* First person registers atexit() callback. */
+static
+void free_static_ft_funcs (void)
+{
+  static_ft_funcs.free_instance ();
+}
 #endif
-  };
 
+static hb_font_funcs_t *
+_hb_ft_get_font_funcs (void)
+{
+  return static_ft_funcs.get_unconst ();
+}
+
+static void
+_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref)
+{
   bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL;
 
   hb_font_set_funcs (font,
-		     funcs,
+		     _hb_ft_get_font_funcs (),
 		     _hb_ft_font_create (ft_face, symbol, unref),
 		     _hb_ft_font_destroy);
 }
@@ -683,47 +744,47 @@
   return hb_ft_font_create (ft_face, _hb_ft_face_destroy);
 }
 
+#ifdef HB_USE_ATEXIT
+static void free_static_ft_library (void);
+#endif
 
-/* Thread-safe, lock-free, FT_Library */
+static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<FT_Library>::value,
+							     hb_ft_library_lazy_loader_t>
+{
+  static inline FT_Library create (void)
+  {
+    FT_Library l;
+    if (FT_Init_FreeType (&l))
+      return nullptr;
 
-static FT_Library ft_library;
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_ft_library);
+#endif
+
+    return l;
+  }
+  static inline void destroy (FT_Library l)
+  {
+    FT_Done_FreeType (l);
+  }
+  static inline FT_Library get_null (void)
+  {
+    return nullptr;
+  }
+} static_ft_library;
 
 #ifdef HB_USE_ATEXIT
 static
-void free_ft_library (void)
+void free_static_ft_library (void)
 {
-retry:
-  FT_Library library = (FT_Library) hb_atomic_ptr_get (&ft_library);
-  if (!hb_atomic_ptr_cmpexch (&ft_library, library, nullptr))
-    goto retry;
-
-  FT_Done_FreeType (library);
+  static_ft_library.free_instance ();
 }
 #endif
 
 static FT_Library
 get_ft_library (void)
 {
-retry:
-  FT_Library library = (FT_Library) hb_atomic_ptr_get (&ft_library);
-
-  if (unlikely (!library))
-  {
-    /* Not found; allocate one. */
-    if (FT_Init_FreeType (&library))
-      return nullptr;
-
-    if (!hb_atomic_ptr_cmpexch (&ft_library, nullptr, library)) {
-      FT_Done_FreeType (library);
-      goto retry;
-    }
-
-#ifdef HB_USE_ATEXIT
-    atexit (free_ft_library); /* First person registers atexit() callback. */
-#endif
-  }
-
-  return library;
+  return static_ft_library.get_unconst ();
 }
 
 static void
diff --git a/src/hb-glib.cc b/src/hb-glib.cc
index 246380a..23a0d89 100644
--- a/src/hb-glib.cc
+++ b/src/hb-glib.cc
@@ -26,11 +26,21 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb-glib.h"
 
-#include "hb-unicode-private.hh"
+#include "hb-machinery.hh"
+
+
+/**
+ * SECTION:hb-glib
+ * @title: hb-glib
+ * @short_description: GLib integration
+ * @include: hb-glib.h
+ *
+ * Functions for using HarfBuzz with the GLib library to provide Unicode data.
+ **/
 
 
 #if !GLIB_CHECK_VERSION(2,29,14)
@@ -201,14 +211,6 @@
   return (hb_unicode_combining_class_t) g_unichar_combining_class (unicode);
 }
 
-static unsigned int
-hb_glib_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs HB_UNUSED,
-				 hb_codepoint_t      unicode,
-				 void               *user_data HB_UNUSED)
-{
-  return g_unichar_iswide (unicode) ? 2 : 1;
-}
-
 static hb_unicode_general_category_t
 hb_glib_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 				  hb_codepoint_t      unicode,
@@ -333,82 +335,50 @@
   return ret;
 }
 
-static unsigned int
-hb_glib_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED,
-					 hb_codepoint_t      u,
-					 hb_codepoint_t     *decomposed,
-					 void               *user_data HB_UNUSED)
-{
-#if GLIB_CHECK_VERSION(2,29,12)
-  return g_unichar_fully_decompose (u, true, decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN);
+
+#ifdef HB_USE_ATEXIT
+static void free_static_glib_funcs (void);
 #endif
 
-  /* If the user doesn't have GLib >= 2.29.12 we have to perform
-   * a round trip to UTF-8 and the associated memory management dance. */
-  gchar utf8[6];
-  gchar *utf8_decomposed, *c;
-  gsize utf8_len, utf8_decomposed_len, i;
+static struct hb_glib_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_glib_unicode_funcs_lazy_loader_t>
+{
+  static inline hb_unicode_funcs_t *create (void)
+  {
+    hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr);
 
-  /* Convert @u to UTF-8 and nor