Merge "Use ICU in libandroidicu"
diff --git a/.circleci/config.yml b/.circleci/config.yml
index e73f53c..e6a5a1d 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -2,13 +2,32 @@
 
 jobs:
 
+  macos-10.12.6-aat-fonts:
+    macos:
+      xcode: "9.2.0"
+    steps:
+      - checkout
+      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel freetype glib cairo
+      - run: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
+      - run: make -j4
+      - run: make check || .ci/fail.sh
+
+  macos-10.13.6-aat-fonts:
+    macos:
+      xcode: "10.1.0"
+    steps:
+      - checkout
+      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel freetype glib cairo
+      - run: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
+      - run: make -j4
+      - run: make check || .ci/fail.sh
+
   macos-llvm-gcc-4.2:
     macos:
       xcode: "8.3.3"
     steps:
       - checkout
-      - run: brew update-reset
-      - run: brew install wget pkg-config libtool ragel freetype glib cairo
+      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel freetype glib cairo
       - run: wget https://packages.macports.org/llvm-gcc42/llvm-gcc42-2336.11_3+universal.darwin_15.i386-x86_64.tbz2 && tar zxvf llvm-gcc42-2336.11_3+universal.darwin_15.i386-x86_64.tbz2
       - run: CC=$PWD/opt/local/bin/llvm-gcc-4.2 CXX=$PWD/opt/local/bin/llvm-g++-4.2 ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
       # Ignoring assembler complains, https://stackoverflow.com/a/39867021
@@ -20,8 +39,7 @@
       xcode: "8.3.3"
     steps:
       - checkout
-      - run: brew update-reset
-      - run: brew install wget pkg-config libtool ragel
+      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel
       - run: wget https://packages.macports.org/apple-gcc42/apple-gcc42-5666.3_15+universal.darwin_15.i386-x86_64.tbz2 && tar zxvf apple-gcc42-5666.3_15+universal.darwin_15.i386-x86_64.tbz2
       - run: CPP=$PWD/opt/local/bin/i686-apple-darwin15-cpp-apple-4.2.1 CC=$PWD/opt/local/bin/i686-apple-darwin15-gcc-apple-4.2.1 CXX=$PWD/opt/local/bin/i686-apple-darwin15-g++-apple-4.2.1 ./autogen.sh
       # Ignoring assembler complains, https://stackoverflow.com/a/39867021
@@ -32,10 +50,11 @@
       xcode: "10.0.0"
     steps:
       - checkout
-      - run: brew update-reset
-      - run: brew install cmake
+      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
       # not needed to be a framework but we like to test that also
-      - run: cmake -DBUILD_FRAMEWORK=ON -H. -Bbuild -GXcode -DHB_IOS=ON
+      # TODO: wrong way of targeting iOS as it doesn't point to iOS headers thus building
+      # CoreText support is not possible, after the fix feel free HB_IOS from CMake altogether
+      - run: cmake -DBUILD_FRAMEWORK=ON -H. -Bbuild -GXcode -DHB_HAVE_CORETEXT=OFF -DHB_BUILD_SUBSET=OFF -DHB_BUILD_TESTS=OFF
       - run: cd build && xcodebuild -sdk iphoneos12.0 -configuration Release build -arch arm64
 
   distcheck:
@@ -118,7 +137,7 @@
       - run: apt update || true
       - run: apt install -y clang lld binutils libtool autoconf automake make pkg-config gtk-doc-tools ragel libfreetype6-dev libfontconfig1-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
       - run: pip install fonttools
-      - run: CFLAGS="-Weverything -Wno-reserved-id-macro -Wno-conversion -Wno-padded -Wno-sign-conversion -Wno-cast-qual -Wno-documentation -Wno-documentation-unknown-command" CXXFLAGS="-Weverything -Wno-old-style-cast -Wno-documentation -Wno-documentation-unknown-command -Wno-c++98-compat -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-sign-conversion -Wno-padded -Wno-shorten-64-to-32 -Wno-extra-semi -Wno-reserved-id-macro -Wno-float-conversion -Wno-format-pedantic -Wno-shadow -Wno-conversion -Wno-zero-as-null-pointer-constant -Wno-missing-field-initializers -Wno-used-but-marked-unused -Wno-unused-macros -Wno-comma -Wno-float-equal -Wno-disabled-macro-expansion -Wno-weak-vtables -Wno-unused-parameter -Wno-covered-switch-default -Wno-unreachable-code" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 --with-fontconfig
+      - run: CFLAGS="-Weverything -Wno-reserved-id-macro -Wno-conversion -Wno-padded -Wno-sign-conversion -Wno-cast-qual -Wno-documentation -Wno-documentation-unknown-command" CXXFLAGS="-Weverything -Wno-old-style-cast -Wno-documentation -Wno-documentation-unknown-command -Wno-c++98-compat -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-sign-conversion -Wno-padded -Wno-shorten-64-to-32 -Wno-reserved-id-macro -Wno-float-conversion -Wno-format-pedantic -Wno-shadow -Wno-conversion -Wno-zero-as-null-pointer-constant -Wno-missing-field-initializers -Wno-used-but-marked-unused -Wno-unused-macros -Wno-comma -Wno-float-equal -Wno-disabled-macro-expansion -Wno-weak-vtables -Wno-unused-parameter -Wno-covered-switch-default -Wno-unreachable-code" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 --with-fontconfig
       - run: make -j32 CPPFLAGS="-Werror"
       - run: make check CPPFLAGS="-Werror" || .ci/fail.sh
 
@@ -223,6 +242,7 @@
 
   crosscompile-notest-djgpp:
     docker:
+      # https://gist.github.com/ebraminio/8551fc74f27951e668102baa2f6b1175
       - image: quay.io/ebraminio/djgpp
     steps:
       - checkout
@@ -285,20 +305,22 @@
       - run: cmake -Bbuild -H. -GNinja
       - run: ninja -Cbuild
 
-  crosscompile-cmake-notest-windows-x64:
-    docker:
-      - image: dockcross/windows-x64
-    steps:
-      - checkout
-      - run: apt update && apt install ragel
-      - run: cmake -Bbuild -H. -GNinja
-      - run: ninja -Cbuild
+  #crosscompile-cmake-notest-windows-x64:
+  #  docker:
+  #    - image: dockcross/windows-x64
+  #  steps:
+  #    - checkout
+  #    - run: apt update && apt install ragel
+  #    - run: cmake -Bbuild -H. -GNinja
+  #    - run: ninja -Cbuild
 
 workflows:
   version: 2
   build:
     jobs:
       # macOS
+      - macos-10.12.6-aat-fonts
+      - macos-10.13.6-aat-fonts
       - macos-llvm-gcc-4.2
       - macos-notest-apple-gcc-i686-4.2
       - macos-notest-ios
@@ -334,4 +356,4 @@
       - crosscompile-cmake-notest-browser-asmjs
       - crosscompile-cmake-notest-linux-arm64
       - crosscompile-cmake-notest-linux-mips
-      - crosscompile-cmake-notest-windows-x64
+      #- crosscompile-cmake-notest-windows-x64
diff --git a/.codecov.yml b/.codecov.yml
new file mode 100644
index 0000000..e9b8ab4
--- /dev/null
+++ b/.codecov.yml
@@ -0,0 +1,7 @@
+comment: off
+
+coverage:
+  status:
+    project:
+      default:
+        threshold: 1%
diff --git a/.travis.yml b/.travis.yml
index eadfa76..3e77b26 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,7 +11,7 @@
     - CONFIGURE_OPTS="--with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2"
     - NOCONFIGURE=1
     # COVERITY_SCAN_TOKEN
-    - secure: "MRJtVu/fQoWNwMAamvIJBCX/1SMvEuEUk/ljAif/y2/3syyWgxFGp17UGnDILdoZYyCqTM+jQciY2P0nVqbjjOAUlML4QOAalqw8kPp8iTsnHUe+KOMVrOVP6p6qAQxk1im1O41cCMkmVKvk+NXe/on5euz6LGF2laHZaOAMoes="
+    - secure: "k6l/18dpsoPAf0E5RQWCr+rgjbHns0H3k0WzSYovCoVg0B7RVlV8x8OjyEOBzEvXI4aaHRdH6MHCPDFnX4fa7ysImlT6LxxIG8YhDdLkJWyS0hHbcJiGxko9AhAGzOZcDl8fZi13d697wagMqqXpjN5v2T/AQm8t4X9z2otJosY="
 
 matrix:
   include:
@@ -28,7 +28,8 @@
         - make check || .ci/fail.sh
         - rm -rf freetype-2.9
       after_success:
-        - bash .ci/run-coveralls.sh # for coveralls.io code coverage tracking
+        - bash .ci/run-coveralls.sh # coveralls.io code coverage
+        - bash <(curl -s https://codecov.io/bash) # codecov.io code coverage
         - bash .ci/deploy-docs.sh
         - bash .ci/trigger-coverity.sh
 
@@ -50,8 +51,10 @@
         - brew update;
           # Workaround Travis/brew bug
         - brew uninstall libtool && brew install libtool
-        - brew install ragel freetype glib gobject-introspection cairo icu4c graphite2 || true
-        - brew link --force icu4c # icu4c is keg-only
+        - brew install ragel freetype glib gobject-introspection cairo graphite2 || true
+        - brew upgrade icu4c || true
+        - export PATH="/usr/local/opt/icu4c/sbin:/usr/local/opt/icu4c/bin:$PATH"
+        - export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig"
       script:
         - ./autogen.sh
         - ./configure $CONFIGURE_OPTS --with-coretext
diff --git a/Android.bp b/Android.bp
index d318f33..9b2581b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -61,42 +61,45 @@
     },
     srcs: [
         "src/hb-aat-layout.cc",
+        "src/hb-aat-map.cc",
         "src/hb-blob.cc",
         "src/hb-buffer-serialize.cc",
         "src/hb-buffer.cc",
         "src/hb-common.cc",
         "src/hb-face.cc",
         "src/hb-font.cc",
-        "src/hb-ot-tag.cc",
-        "src/hb-set.cc",
-        "src/hb-shape.cc",
-        "src/hb-shape-plan.cc",
-        "src/hb-shaper.cc",
-        "src/hb-static.cc",
-        "src/hb-unicode.cc",
-        "src/hb-warning.cc",
+        "src/hb-icu.cc",
+        "src/hb-ot-cff1-table.cc",
+        "src/hb-ot-cff2-table.cc",
         "src/hb-ot-face.cc",
         "src/hb-ot-font.cc",
         "src/hb-ot-layout.cc",
         "src/hb-ot-map.cc",
         "src/hb-ot-math.cc",
-        "src/hb-ot-shape.cc",
         "src/hb-ot-shape-complex-arabic.cc",
         "src/hb-ot-shape-complex-default.cc",
         "src/hb-ot-shape-complex-hangul.cc",
         "src/hb-ot-shape-complex-hebrew.cc",
-        "src/hb-ot-shape-complex-indic.cc",
         "src/hb-ot-shape-complex-indic-table.cc",
+        "src/hb-ot-shape-complex-indic.cc",
         "src/hb-ot-shape-complex-khmer.cc",
         "src/hb-ot-shape-complex-myanmar.cc",
         "src/hb-ot-shape-complex-thai.cc",
-        "src/hb-ot-shape-complex-use.cc",
         "src/hb-ot-shape-complex-use-table.cc",
-        "src/hb-ot-shape-normalize.cc",
+        "src/hb-ot-shape-complex-use.cc",
+        "src/hb-ot-shape-complex-vowel-constraints.cc",
         "src/hb-ot-shape-fallback.cc",
+        "src/hb-ot-shape-normalize.cc",
+        "src/hb-ot-shape.cc",
+        "src/hb-ot-tag.cc",
         "src/hb-ot-var.cc",
-
-        "src/hb-icu.cc",
+        "src/hb-set.cc",
+        "src/hb-shape-plan.cc",
+        "src/hb-shape.cc",
+        "src/hb-shaper.cc",
+        "src/hb-static.cc",
+        "src/hb-unicode.cc",
+        "src/hb-warning.cc",
     ],
 
     target: {
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4eb23af..b6241e9 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)
@@ -82,16 +85,6 @@
   endif ()
 endif ()
 
-set (HB_DISABLE_SUBSET OFF)
-set (HB_DISABLE_TESTS OFF)
-option(HB_IOS "Apply iOS specific build flags" OFF)
-if (HB_IOS)
-  # We should fix their issue and enable them
-  set (HB_DISABLE_SUBSET ON)
-  set (HB_DISABLE_TESTS ON)
-  set (HB_HAVE_CORETEXT OFF)
-endif ()
-
 include_directories(AFTER
   ${PROJECT_SOURCE_DIR}/src
   ${PROJECT_BINARY_DIR}/src
@@ -175,10 +168,6 @@
 add_prefix_to_list(HB_BASE_headers "${PROJECT_SOURCE_DIR}/src/")
 extract_make_variable(HB_FALLBACK_sources ${SRCSOURCES})
 add_prefix_to_list(HB_FALLBACK_sources "${PROJECT_SOURCE_DIR}/src/")
-extract_make_variable(HB_OT_sources ${SRCSOURCES})
-add_prefix_to_list(HB_OT_sources "${PROJECT_SOURCE_DIR}/src/")
-extract_make_variable(HB_OT_headers ${SRCSOURCES})
-add_prefix_to_list(HB_OT_headers "${PROJECT_SOURCE_DIR}/src/")
 
 extract_make_variable(HB_SUBSET_sources ${SRCSOURCES})
 add_prefix_to_list(HB_SUBSET_sources "${PROJECT_SOURCE_DIR}/src/")
@@ -187,13 +176,10 @@
 add_prefix_to_list(HB_SUBSET_headers "${PROJECT_SOURCE_DIR}/src/")
 
 extract_make_variable(HB_BASE_RAGEL_GENERATED_sources ${SRCSOURCES})
-extract_make_variable(HB_OT_RAGEL_GENERATED_sources ${SRCSOURCES})
 #if (IN_HB_DIST)
   add_prefix_to_list(HB_BASE_RAGEL_GENERATED_sources "${PROJECT_SOURCE_DIR}/src/")
-  add_prefix_to_list(HB_OT_RAGEL_GENERATED_sources "${PROJECT_SOURCE_DIR}/src/")
 #else ()
 #  add_prefix_to_list(HB_BASE_RAGEL_GENERATED_sources "${PROJECT_BINARY_DIR}/src/")
-#  add_prefix_to_list(HB_OT_RAGEL_GENERATED_sources "${PROJECT_BINARY_DIR}/src/")
 #endif ()
 
 extract_make_variable(HB_VIEW_sources ${UTILSOURCES})
@@ -219,7 +205,7 @@
 
 ## Define ragel tasks
 # if (NOT IN_HB_DIST)
-#  foreach (ragel_output IN ITEMS ${HB_BASE_RAGEL_GENERATED_sources} ${HB_OT_RAGEL_GENERATED_sources})
+#  foreach (ragel_output IN ITEMS ${HB_BASE_RAGEL_GENERATED_sources})
 #    string(REGEX MATCH "([^/]+)\\.hh" temp ${ragel_output})
 #    set (target_name ${CMAKE_MATCH_1})
 #    add_custom_command(OUTPUT ${ragel_output}
@@ -253,8 +239,6 @@
   ${HB_BASE_RAGEL_GENERATED_sources}
 
   ${HB_FALLBACK_sources}
-  ${HB_OT_sources}
-  ${HB_OT_RAGEL_GENERATED_sources}
 )
 
 set (subset_project_sources
@@ -267,7 +251,6 @@
   #${HB_VERSION_H}
 
   ${HB_BASE_headers}
-  ${HB_OT_headers}
 )
 
 set (subset_project_headers
@@ -556,7 +539,7 @@
 target_link_libraries(harfbuzz ${THIRD_PARTY_LIBS})
 
 ## Define harfbuzz-subset library
-if (NOT HB_DISABLE_SUBSET)
+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})
@@ -580,7 +563,7 @@
     set (CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "m") # libm
     set (CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "")
     set_target_properties(harfbuzz PROPERTIES LINKER_LANGUAGE C)
-    if (NOT HB_DISABLE_SUBSET)
+    if (HB_BUILD_SUBSET)
       set_target_properties(harfbuzz-subset PROPERTIES LINKER_LANGUAGE C)
     endif ()
 
@@ -713,6 +696,8 @@
       -DHB_H_IN
       -DHB_OT_H
       -DHB_OT_H_IN
+      -DHB_AAT_H
+      -DHB_AAT_H_IN
       -DHB_GOBJECT_H
       -DHB_GOBJECT_H_IN
       -DHB_EXTERN=
@@ -861,7 +846,7 @@
 endif ()
 
 
-if (NOT HB_DISABLE_TESTS)
+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})
diff --git a/NEWS b/NEWS
index b8d3640..890c258 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,159 @@
+Overview of changes leading to 2.3.0
+Thursday, December 20, 2018
+====================================
+- Fix regression on big-endian architectures.  Ouch!
+- Misc bug and build fixes.
+- Fix subsetting of simple GSUB/GDEF.
+- Merge CFF / CFF2 support contributed by Adobe.  This mostly involves
+  the subsetter, but also get_glyph_extents on CFF fonts.
+
+New API in hb-aat.h:
++hb_aat_layout_has_substitution()
++hb_aat_layout_has_positioning()
++hb_aat_layout_has_tracking()
+
+
+Overview of changes leading to 2.2.0
+Thursday, November 29, 2018
+====================================
+- Misc shaping bug fixes.
+- Add font variations named-instance API.
+- Deprecate font variations axis enumeration API and add replacement.
+- AAT shaping improvements:
+  o Fixed 'kern' table Format 2 implementation.
+  o Implement 'feat' table API for feature detection.
+  o Blacklist 'GSUB' table of fonts from 'MUTF' foundry that also have 'morx'.
+
+New API:
++hb_aat_layout_feature_type_t
++hb_aat_layout_feature_selector_t
++hb_aat_layout_get_feature_types()
++hb_aat_layout_feature_type_get_name_id
++hb_aat_layout_feature_selector_info_t
++HB_AAT_LAYOUT_NO_SELECTOR_INDEX
++hb_aat_layout_feature_type_get_selector_infos()
++hb_ot_var_axis_flags_t
++hb_ot_var_axis_info_t
++hb_ot_var_get_axis_infos()
++hb_ot_var_find_axis_info()
++hb_ot_var_get_named_instance_count()
++hb_ot_var_named_instance_get_subfamily_name_id()
++hb_ot_var_named_instance_get_postscript_name_id()
++hb_ot_var_named_instance_get_design_coords()
+
+Deprecated API:
++HB_OT_VAR_NO_AXIS_INDEX
++hb_ot_var_axis_t
++hb_ot_var_get_axes()
++hb_ot_var_find_axis()
+
+
+Overview of changes leading to 2.1.3
+Friday, November 16, 2018
+====================================
+- Fix AAT 'mort' shaping, which was broken in 2.1.2
+
+
+Overview of changes leading to 2.1.2
+Friday, November 16, 2018
+====================================
+- Various internal changes.
+- AAT shaping improvements:
+  o Implement kern table Format 1 state-machine-based kerning.
+  o Implement cross-stream kerning (cursive positioning, etc).
+  o Ignore emptyish GSUB tables (zero scripts) if morx present.
+  o Don't apply GPOS if morx is being applied.  Matches Apple.
+
+
+-Overview of changes leading to 2.1.1
+Monday, November 5, 2018
+====================================
+- AAT improvements:
+  o Implement 'mort' table.
+  o Implement 'kern' subtables Format 1 and Format 3.
+
+
+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
 ====================================
diff --git a/README b/README
index 55775c8..e50f8f0 100644
--- a/README
+++ b/README
@@ -1,9 +1,10 @@
-[![Build Status](https://travis-ci.org/harfbuzz/harfbuzz.svg)](https://travis-ci.org/harfbuzz/harfbuzz)
-[![Build status](https://ci.appveyor.com/api/projects/status/0t0flrxpstj9lb9w?svg=true)](https://ci.appveyor.com/project/harfbuzz/harfbuzz)
-[![CircleCI](https://circleci.com/gh/harfbuzz/harfbuzz.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz)
-[![Coverity](https://img.shields.io/coverity/scan/5450.svg)](https://scan.coverity.com/projects/behdad-harfbuzz)
-[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f17f1708783c447488bc8dd317150eaa)](https://app.codacy.com/app/behdad/harfbuzz)
-[![Coverage Status](https://img.shields.io/coveralls/harfbuzz/harfbuzz.svg)](https://coveralls.io/r/harfbuzz/harfbuzz)
+[![Travis Build Status](https://travis-ci.org/harfbuzz/harfbuzz.svg)](https://travis-ci.org/harfbuzz/harfbuzz)
+[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/0t0flrxpstj9lb9w?svg=true)](https://ci.appveyor.com/project/harfbuzz/harfbuzz)
+[![CircleCI Build Status](https://circleci.com/gh/harfbuzz/harfbuzz.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz)
+[![Coverity Code Health](https://img.shields.io/coverity/scan/5450.svg)](https://scan.coverity.com/projects/behdad-harfbuzz)
+[![Codacy Code Health](https://api.codacy.com/project/badge/Grade/f17f1708783c447488bc8dd317150eaa)](https://app.codacy.com/app/behdad/harfbuzz)
+[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/master/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz)
+[![Coverals Code Coverage](https://img.shields.io/coveralls/harfbuzz/harfbuzz.svg)](https://coveralls.io/r/harfbuzz/harfbuzz)
 [ABI Tracker](http://abi-laboratory.pro/tracker/timeline/harfbuzz/)
 
 This is HarfBuzz, a text shaping library.
diff --git a/README.version b/README.version
index 1781d25..787f6dd 100644
--- a/README.version
+++ b/README.version
@@ -1,3 +1,3 @@
-URL: https://github.com/harfbuzz/harfbuzz/commit/0a3b7a0fb0734a66926dfda5d95d3cacea8890ce
-Version: 2.0.2
+URL: https://github.com/harfbuzz/harfbuzz/commit/4941e95f10fe0fe658752134a42b58896fb19c42
+Version: 2.3.0
 BugComponent: 25699
diff --git a/RELEASING.md b/RELEASING.md
index 4f5705e..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.
diff --git a/appveyor.yml b/appveyor.yml
index bf98219..21d4ea7 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -28,25 +28,19 @@
       MINGW_CHOST: i686-w64-mingw32
       MSYS2_ARCH: i686
 
-    - compiler: cygwin
-      CYGWIN_PREFIX: C:\Cygwin64
-      CYGWIN_ARCH: x86_64
-   # Lots of test failures here!
-   #- compiler: cygwin
-   #  CYGWIN_PREFIX: C:\Cygwin
-   #  CYGWIN_ARCH: x86
-
 
 install:
-  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --force --noconfirm -Sy && pacman --noconfirm --force -S pacman-mirrors && pacman --force -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 ../'
@@ -54,14 +48,8 @@
   - '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 --force -Syyu mingw-w64-$MSYS2_ARCH-gcc"'
-  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm --force -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%"=="cygwin" set PATH=%PATH%;c:\msys64\mingw64\bin' # msys2 is added just for having "ragel" on PATH
-  - 'if "%compiler%"=="cygwin" curl https://raw.githubusercontent.com/mirror/mingw-w64/023eb04c396d4e8d8fcf604cfababc53dae13398/mingw-w64-headers/include/dwrite_1.h -o %CYGWIN_PREFIX%\usr\include\dwrite_1.h'
-  - 'if "%compiler%"=="cygwin" %CYGWIN_PREFIX%\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER; ./autogen.sh --with-uniscribe --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 --with-directwrite; 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\
@@ -75,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 a2d0992..0315537 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [2.0.2],
+        [2.3.0],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -12,7 +12,6 @@
 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])
@@ -508,6 +507,7 @@
 test/fuzzing/Makefile
 test/shaping/Makefile
 test/shaping/data/Makefile
+test/shaping/data/aots/Makefile
 test/shaping/data/in-house/Makefile
 test/shaping/data/text-rendering-tests/Makefile
 test/subset/Makefile
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..2735338 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,124 @@
         <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>Apple Advanced Typography API</title>
+        <xi:include href="xml/hb-aat-layout.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 fccfcb0..fd7682e 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -1,8 +1,20 @@
-<SECTION>
-<FILE>hb</FILE>
 <SUBSECTION Private>
 HB_H_IN
-HB_EXTERN
+HB_OT_H_IN
+</SECTION>
+
+<SECTION>
+<FILE>hb-aat-layout</FILE>
+HB_AAT_LAYOUT_NO_SELECTOR_INDEX
+hb_aat_layout_feature_type_t
+hb_aat_layout_feature_selector_t
+hb_aat_layout_feature_selector_info_t
+hb_aat_layout_feature_type_get_name_id
+hb_aat_layout_feature_type_get_selector_infos
+hb_aat_layout_get_feature_types
+hb_aat_layout_has_positioning
+hb_aat_layout_has_substitution
+hb_aat_layout_has_tracking
 </SECTION>
 
 <SECTION>
@@ -146,6 +158,10 @@
 uint32_t
 uint64_t
 uint8_t
+<SUBSECTION Private>
+HB_EXTERN
+HB_DEPRECATED
+HB_DEPRECATED_FOR
 </SECTION>
 
 <SECTION>
@@ -159,6 +175,10 @@
 hb_ot_layout_table_find_script
 hb_ot_tag_from_language
 hb_ot_tags_from_script
+HB_OT_VAR_NO_AXIS_INDEX
+hb_ot_var_axis_t
+hb_ot_var_find_axis
+hb_ot_var_get_axes
 hb_set_invert
 hb_unicode_eastasian_width_func_t
 hb_unicode_eastasian_width
@@ -358,6 +378,7 @@
 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
@@ -390,6 +411,7 @@
 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
@@ -413,11 +435,6 @@
 </SECTION>
 
 <SECTION>
-<FILE>hb-gobject</FILE>
-
-</SECTION>
-
-<SECTION>
 <FILE>hb-graphite2</FILE>
 HB_GRAPHITE2_TAG_SILF
 hb_graphite2_face_get_gr_face
@@ -452,9 +469,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>
@@ -463,16 +498,31 @@
 </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
@@ -518,23 +568,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
@@ -555,15 +588,29 @@
 </SECTION>
 
 <SECTION>
-<FILE>hb-ot-tag</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
+<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_has_data
+hb_ot_var_axis_flags_t
+hb_ot_var_axis_info_t
+hb_ot_var_find_axis_info
+hb_ot_var_get_axis_count
+hb_ot_var_get_axis_infos
+hb_ot_var_get_named_instance_count
+hb_ot_var_named_instance_get_subfamily_name_id
+hb_ot_var_named_instance_get_postscript_name_id
+hb_ot_var_named_instance_get_design_coords
+hb_ot_var_normalize_variations
+hb_ot_var_normalize_coords
 </SECTION>
 
 <SECTION>
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..f48e89c 100644
--- a/docs/usermanual-clusters.xml
+++ b/docs/usermanual-clusters.xml
@@ -1,304 +1,542 @@
+<?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>
-  <para>
-    In shaping text, a <emphasis>cluster</emphasis> is a sequence of
-    code points that needs to be treated as a single, indivisible unit.
-  </para>
-  <para>
-    When you add text to a HB buffer, each character is associated with
-    a <emphasis>cluster value</emphasis>. This is an arbitrary number as
-    far as HB is concerned.
-  </para>
-  <para>
-    Most clients will use UTF-8, UTF-16, or UTF-32 indices, but the
-    actual number does not matter. Moreover, it is not required for the
-    cluster values to be monotonically increasing, but pretty much all
-    of HB's tests are performed on monotonically increasing cluster
-    numbers. Nevertheless, there is no such assumption in the code
-    itself. With that in mind, let's examine what happens with cluster
-    values during shaping under each cluster-level.
-  </para>
-  <para>
-    HarfBuzz provides three <emphasis>levels</emphasis> of clustering
-    support. Level 0 is the default behavior and reproduces the behavior
-    of the old HarfBuzz library. Level 1 tweaks this behavior slightly
-    to produce better results, so level 1 clustering is recommended for
-    code that is not required to implement backward compatibility with
-    the old HarfBuzz.
-  </para>
-  <para>
-    Level 2 differs significantly in how it treats cluster values.
-    Levels 0 and 1 both process ligatures and glyph decomposition by
-    merging clusters; level 2 does not.
-  </para>
-  <para>
-    The conceptual model for what the cluster values mean, in levels 0
-    and 1, is this:
-  </para>
-  <itemizedlist spacing="compact">
-    <listitem>
-      <para>
-        the sequence of cluster values will always remain monotone
-      </para>
-    </listitem>
-    <listitem>
-      <para>
-        each value represents a single cluster
-      </para>
-    </listitem>
-    <listitem>
-      <para>
-        each cluster contains one or more glyphs and one or more
-        characters
-      </para>
-    </listitem>
-  </itemizedlist>
-  <para>
-    Assuming that initial cluster numbers were monotonically increasing
-    and distinct, then all adjacent glyphs having the same cluster
-    number belong to the same cluster, and all characters belong to the
-    cluster that has the highest number not larger than their initial
-    cluster number. This will become clearer with an example.
-  </para>
-</sect1>
-<sect1 id="a-clustering-example-for-levels-0-and-1">
-  <title>A clustering example for levels 0 and 1</title>
-  <para>
-    Let's say we start with the following character sequence and cluster
-    values:
-  </para>
-  <programlisting>
-   A,B,C,D,E
-   0,1,2,3,4
-</programlisting>
-  <para>
-    We then map the characters to glyphs. For simplicity, let's assume
-    that each character maps to the corresponding, identical-looking
-    glyph:
-  </para>
-  <programlisting>
-   A,B,C,D,E
-   0,1,2,3,4
-</programlisting>
-  <para>
-    Now if, for example, <literal>B</literal> and <literal>C</literal>
-    ligate, then the clusters to which they belong &quot;merge&quot;.
-    This merged cluster takes for its cluster number the minimum of all
-    the cluster numbers of the clusters that went in. In this case, we
-    get:
-  </para>
-  <programlisting>
-   A,BC,D,E
-   0,1 ,3,4
-</programlisting>
-  <para>
-    Now let's assume that the <literal>BC</literal> glyph decomposes
-    into three components, and <literal>D</literal> also decomposes into
-    two. The components each inherit the cluster value of their parent:
-  </para>
-  <programlisting>
-   A,BC0,BC1,BC2,D0,D1,E
-   0,1  ,1  ,1  ,3 ,3 ,4
-</programlisting>
-  <para>
-    Now if <literal>BC2</literal> and <literal>D0</literal> ligate, then
-    their clusters (numbers 1 and 3) merge into
-    <literal>min(1,3) = 1</literal>:
-  </para>
-  <programlisting>
-   A,BC0,BC1,BC2D0,D1,E
-   0,1  ,1  ,1    ,1 ,4
-</programlisting>
-  <para>
-    At this point, cluster 1 means: the character sequence
-    <literal>BCD</literal> is represented by glyphs
-    <literal>BC0,BC1,BC2D0,D1</literal> and cannot be broken down any
-    further.
-  </para>
-</sect1>
-<sect1 id="reordering-in-levels-0-and-1">
-  <title>Reordering in levels 0 and 1</title>
-  <para>
-    Another common operation in the more complex shapers is when things
-    reorder. In those cases, to maintain monotone clusters, HB merges
-    the clusters of everything in the reordering sequence. For example,
-    let's again start with the character sequence:
-  </para>
-  <programlisting>
-   A,B,C,D,E
-   0,1,2,3,4
-</programlisting>
-  <para>
-    If <literal>D</literal> is reordered before <literal>B</literal>,
-    then the <literal>B</literal>, <literal>C</literal>, and
-    <literal>D</literal> clusters merge, and we get:
-  </para>
-  <programlisting>
-   A,D,B,C,E
-   0,1,1,1,4
-</programlisting>
-  <para>
-    This is clearly not ideal, but it is the only sensible way to
-    maintain monotone indices and retain the true relationship between
-    glyphs and characters.
-  </para>
-</sect1>
-<sect1 id="the-distinction-between-levels-0-and-1">
-  <title>The distinction between levels 0 and 1</title>
-  <para>
-    So, the above is pretty much what cluster levels 0 and 1 do. The
-    only difference between the two is this: in level 0, at the very
-    beginning of the shaping process, we also merge clusters between
-    base characters and all Unicode marks (combining or not) following
-    them. E.g.:
-  </para>
-  <programlisting>
-  A,acute,B
-  0,1    ,2
-</programlisting>
-  <para>
-    will become:
-  </para>
-  <programlisting>
-  A,acute,B
-  0,0    ,2
-</programlisting>
-  <para>
-    This is the default behavior. We do it because Windows did it and
-    old HarfBuzz did it, so this remained the default. But this behavior
-    makes it impossible to color diacritic marks differently from their
-    base characters. That's why in level 1 we do not perform this
-    initial merging step.
-  </para>
-  <para>
-    For clients, level 0 is more convenient if they rely on HarfBuzz
-    clusters for cursor positioning. But that's wrong anyway: cursor
-    positions should be determined based on Unicode grapheme boundaries,
-    NOT shaping clusters. As such, level 1 clusters are preferred.
-  </para>
-  <para>
-    One last note about levels 0 and 1. We currently don't allow a
-    <literal>MultipleSubst</literal> lookup to replace a glyph with zero
-    glyphs (i.e., to delete a glyph). But in some other situations,
-    glyphs can be deleted. In those cases, if the glyph being deleted is
-    the last glyph of its cluster, we make sure to merge the cluster
-    with a neighboring cluster.
-  </para>
-  <para>
-    This is, primarily, to make sure that the starting cluster of the
-    text always has the cluster index pointing to the start of the text
-    for the run; more than one client currently relies on this
-    guarantee.
-  </para>
-  <para>
-    Incidentally, Apple's CoreText does something else to maintain the
-    same promise: it inserts a glyph with id 65535 at the beginning of
-    the glyph string if the glyph corresponding to the first character
-    in the run was deleted. HarfBuzz might do something similar in the
-    future.
-  </para>
-</sect1>
-<sect1 id="level-2">
-  <title>Level 2</title>
-  <para>
-    Level 2 is a different beast from levels 0 and 1. It is simple to
-    describe, but hard to make sense of. It simply doesn't do any
-    cluster merging whatsoever. When things ligate or otherwise multiple
-    glyphs turn into one, the cluster value of the first glyph is
-    retained.
-  </para>
-  <para>
-    Here are a few examples of why processing cluster values produced at
-    this level might be tricky:
-  </para>
-  <sect2 id="ligatures-with-combining-marks">
-    <title>Ligatures with combining marks</title>
+  <section id="clusters">
+    <title>Clusters</title>
     <para>
-      Imagine capital letters are bases and lower case letters are
-      combining marks. With an input sequence like this:
+      In text shaping, a <emphasis>cluster</emphasis> is a sequence of
+      characters that needs to be treated as a single, indivisible
+      unit.
+    </para>
+    <para>
+      A cluster is distinct from a <emphasis>grapheme</emphasis>,
+      which is the smallest unit of a writing system or script,
+      because clusters are only relevant for script shaping and the
+      layout of glyphs.
+    </para>
+    <para>
+      For example, a grapheme may be a letter, a number, a logogram,
+      or a symbol. When two letters form a ligature, however, they
+      combine into a single glyph. They are therefore part of the same
+      cluster and are treated as a unit &mdash; even though the two
+      original, underlying letters are separate graphemes.
+    </para>
+    <para>
+      During the shaping process, there are several shaping operations
+      that may merge adjacent characters (for example, when two code
+      points form a ligature or a conjunct form and are replaced by a
+      single glyph) or split one character into several (for example,
+      when decomposing a code point through the
+      <literal>ccmp</literal> feature).
+    </para>
+    <para>
+      HarfBuzz tracks clusters independently from how these
+      shaping operations affect the individual glyphs that comprise the
+      output HarfBuzz returns in a buffer. Consequently,
+      a client program using HarfBuzz can utilize the cluster
+      information to implement features such as:
+    </para>
+    <itemizedlist>
+      <listitem>
+	<para>
+	  Correctly positioning the cursor within a shaped text run,
+	  even when characters have formed ligatures, composed or
+	  decomposed, reordered, or undergone other shaping operations.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  Correctly highlighting a text selection that includes some,
+	  but not all, of the characters in a word. 
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  Applying text attributes (such as color or underlining) to
+	  part, but not all, of a word.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  Generating output document formats (such as PDF) with
+	  embedded text that can be fully extracted.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  Determining the mapping between input characters and output
+	  glyphs, such as which glyphs are ligatures.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  Performing line-breaking, justification, and other
+	  line-level or paragraph-level operations that must be done
+	  after shaping is complete, but which require character-level
+	  properties.
+	</para>
+      </listitem>
+    </itemizedlist>
+    <para>
+      When you add text to a HarfBuzz buffer, each code point must be
+      assigned a <emphasis>cluster value</emphasis>.
+    </para>
+    <para>
+      This cluster value is an arbitrary number; HarfBuzz uses it only
+      to distinguish between clusters. Many client programs will use
+      the index of each code point in the input text stream as the
+      cluster value. This is for the sake of convenience; the actual
+      value does not matter.
+    </para>
+    <para>
+      Client programs can choose how HarfBuzz handles clusters during
+      shaping by setting the
+      <literal>cluster_level</literal> of the
+      buffer. HarfBuzz offers three <emphasis>levels</emphasis> of
+      clustering support for this property:
+    </para>
+    <itemizedlist>
+      <listitem>
+	<para><emphasis>Level 0</emphasis> is the default and
+	reproduces the behavior of the old HarfBuzz library.
+	</para>
+	<para>
+	  The distinguishing feature of level 0 behavior is that, at
+	  the beginning of processing the buffer, all code points that
+	  are categorized as <emphasis>marks</emphasis>,
+	  <emphasis>modifier symbols</emphasis>, or
+	  <emphasis>Emoji extended pictographic</emphasis> modifiers,
+	  as well as the <emphasis>Zero Width Joiner</emphasis> and
+	  <emphasis>Zero Width Non-Joiner</emphasis> code points, are
+	  assigned the cluster value of the closest preceding code
+	  point from <emphasis>different</emphasis> category. 
+	</para>
+	<para>
+	  In essence, whenever a base character is followed by a mark
+	  character or a sequence of mark characters, those marks are
+	  reassigned to the same initial cluster value as the base
+	  character. This reassignment is referred to as
+	  "merging" the affected clusters. This behavior is based on
+	  the Grapheme Cluster Boundary specification in <ulink
+	  url="https://www.unicode.org/reports/tr29/#Regex_Definitions">Unicode
+	  Technical Report 29</ulink>.
+	</para>
+	<para>
+	  Client programs can specify level 0 behavior for a buffer by
+	  setting its <literal>cluster_level</literal> to
+	  <literal>HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES</literal>. 
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  <emphasis>Level 1</emphasis> tweaks the old behavior
+	  slightly to produce better results. Therefore, level 1
+	  clustering is recommended for code that is not required to
+	  implement backward compatibility with the old HarfBuzz.
+	</para>
+	<para>
+	  Level 1 differs from level 0 by not merging the 
+	  clusters of marks and other modifier code points with the
+	  preceding "base" code point's cluster. By preserving the
+	  separate cluster values of these marks and modifier code
+	  points, script shapers can perform additional operations
+	  that might lead to improved results (for example, reordering
+	  a sequence of marks).
+	</para>
+	<para>
+	  Client programs can specify level 1 behavior for a buffer by
+	  setting its <literal>cluster_level</literal> to
+	  <literal>HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS</literal>. 
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  <emphasis>Level 2</emphasis> differs significantly in how it
+	  treats cluster values. In level 2, HarfBuzz never merges
+	  clusters.
+	</para>
+	<para>
+	  This difference can be seen most clearly when HarfBuzz processes
+	  ligature substitutions and glyph decompositions. In level 0 
+	  and level 1, ligatures and glyph decomposition both involve
+	  merging clusters; in level 2, neither of these operations
+	  triggers a merge.
+	</para>
+	<para>
+	  Client programs can specify level 2 behavior for a buffer by
+	  setting its <literal>cluster_level</literal> to
+	  <literal>HB_BUFFER_CLUSTER_LEVEL_CHARACTERS</literal>. 
+	</para>
+      </listitem>
+    </itemizedlist>
+    <para>
+      As mentioned earlier, client programs using HarfBuzz often
+      assign initial cluster values in a buffer by reusing the indices
+      of the code points in the input text. This gives a sequence of
+      cluster values that is monotonically increasing (for example,
+      0,1,2,3,4,5). 
+    </para>
+    <para>
+      It is not <emphasis>required</emphasis> that the cluster values
+      in a buffer be monotonically increasing. However, if the initial
+      cluster values in a buffer are monotonic and the buffer is
+      configured to use cluster level 0 or 1, then HarfBuzz
+      guarantees that the final cluster values in the shaped buffer
+      will also be monotonic. No such guarantee is made for cluster
+      level 2.
+    </para>
+    <para>
+      In levels 0 and 1, HarfBuzz implements the following conceptual
+      model for cluster values:
+    </para>
+    <itemizedlist spacing="compact">
+      <listitem>
+	<para>
+          If the sequence of input cluster values is monotonic, the
+	  sequence of cluster values will remain monotonic.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+          Each cluster value represents a single cluster.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+          Each cluster contains one or more glyphs and one or more
+          characters.
+	</para>
+      </listitem>
+    </itemizedlist>
+    <para>
+      In practice, this model offers several benefits. Assuming that
+      the initial cluster values were monotonically increasing
+      and distinct before shaping began, then, in the final output:
+    </para>
+    <itemizedlist spacing="compact">
+      <listitem>
+	<para>
+	  All adjacent glyphs having the same final cluster
+	  value belong to the same cluster.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+          Each character belongs to the cluster that has the highest
+	  cluster value <emphasis>not larger than</emphasis> its
+	  initial cluster value.
+	</para>
+      </listitem>
+    </itemizedlist>
+	
+  </section>
+  <section id="a-clustering-example-for-levels-0-and-1">
+    <title>A clustering example for levels 0 and 1</title>
+    <para>
+      The guarantees and benefits of level 0 and level 1 can be seen
+      with some examples. First, let us examine what happens with cluster
+      values when shaping involves cluster merging with ligatures and
+      decomposition.
+    </para>
+    <para>
+      Let's say we start with the following character sequence (top row) and
+      initial cluster values (bottom row):
     </para>
     <programlisting>
-  A,a,B,b,C,c
-  0,1,2,3,4,5
-</programlisting>
+      A,B,C,D,E
+      0,1,2,3,4
+    </programlisting>
     <para>
-      if <literal>A,B,C</literal> ligate, then here are the cluster
-      values one would get under the various levels:
-    </para>
-    <para>
-      level 0:
+      During shaping, HarfBuzz maps these characters to glyphs from
+      the font. For simplicity, let us assume that each character maps
+      to the corresponding, identical-looking glyph:
     </para>
     <programlisting>
-  ABC,a,b,c
-  0  ,0,0,0
-</programlisting>
+      A,B,C,D,E
+      0,1,2,3,4
+    </programlisting>
     <para>
-      level 1:
+      Now if, for example, <literal>B</literal> and <literal>C</literal>
+      form a ligature, then the clusters to which they belong
+      &quot;merge&quot;. This merged cluster takes for its cluster
+      value the minimum of all the cluster values of the clusters that
+      went in to the ligature. In this case, we get:
     </para>
     <programlisting>
-  ABC,a,b,c
-  0  ,0,0,5
-</programlisting>
+      A,BC,D,E
+      0,1 ,3,4
+    </programlisting>
     <para>
-      level 2:
+      because 1 is the minimum of the set {1,2}, which were the
+      cluster values of <literal>B</literal> and
+      <literal>C</literal>. 
+    </para>
+    <para>
+      Next, let us say that the <literal>BC</literal> ligature glyph
+      decomposes into three components, and <literal>D</literal> also
+      decomposes into two components. These components each inherit the
+      cluster value of their parent: 
     </para>
     <programlisting>
-  ABC,a,b,c
-  0  ,1,3,5
-</programlisting>
+      A,BC0,BC1,BC2,D0,D1,E
+      0,1  ,1  ,1  ,3 ,3 ,4
+    </programlisting>
     <para>
-      Making sense of the last example is the hardest for a client,
-      because there is nothing in the cluster values to suggest that
-      <literal>B</literal> and <literal>C</literal> ligated with
-      <literal>A</literal>.
-    </para>
-  </sect2>
-  <sect2 id="reordering">
-    <title>Reordering</title>
-    <para>
-      Another tricky case is when things reorder. Under level 2:
+      Next, if <literal>BC2</literal> and <literal>D0</literal> form a
+      ligature, then their clusters (cluster values 1 and 3) merge into
+      <literal>min(1,3) = 1</literal>:
     </para>
     <programlisting>
-  A,B,C,D,E
-  0,1,2,3,4
-</programlisting>
+      A,BC0,BC1,BC2D0,D1,E
+      0,1  ,1  ,1    ,1 ,4
+    </programlisting>
     <para>
-      Now imagine <literal>D</literal> moves before
-      <literal>B</literal>:
+      At this point, cluster 1 means: the character sequence
+      <literal>BCD</literal> is represented by glyphs
+      <literal>BC0,BC1,BC2D0,D1</literal> and cannot be broken down any
+      further.
+    </para>
+  </section>
+  <section id="reordering-in-levels-0-and-1">
+    <title>Reordering in levels 0 and 1</title>
+    <para>
+      Another common operation in the more complex shapers is glyph
+      reordering. In order to maintain a monotonic cluster sequence
+      when glyph reordering takes place, HarfBuzz merges the clusters
+      of everything in the reordering sequence.
+    </para>
+    <para>
+      For example, let us again start with the character sequence (top
+      row) and initial cluster values (bottom row):
     </para>
     <programlisting>
-  A,D,B,C,E
-  0,3,1,2,4
-</programlisting>
+      A,B,C,D,E
+      0,1,2,3,4
+    </programlisting>
     <para>
-      Now, if <literal>D</literal> ligates with <literal>B</literal>, we
+      If <literal>D</literal> is reordered to before <literal>B</literal>,
+      then HarfBuzz merges the <literal>B</literal>,
+      <literal>C</literal>, and <literal>D</literal> clusters, and we
       get:
     </para>
     <programlisting>
-  A,DB,C,E
-  0,3 ,2,4
-</programlisting>
+      A,D,B,C,E
+      0,1,1,1,4
+    </programlisting>
     <para>
-      In a different scenario, <literal>A</literal> and
-      <literal>B</literal> could have ligated
-      <emphasis>before</emphasis> <literal>D</literal> reordered; that
-      would have resulted in:
+      This is clearly not ideal, but it is the only sensible way to
+      maintain a monotonic sequence of cluster values and retain the
+      true relationship between glyphs and characters.
+    </para>
+  </section>
+  <section id="the-distinction-between-levels-0-and-1">
+    <title>The distinction between levels 0 and 1</title>
+    <para>
+      The preceding examples demonstrate the main effects of using
+      cluster levels 0 and 1. The only difference between the two
+      levels is this: in level 0, at the very beginning of the shaping
+      process, HarfBuzz also merges clusters between any base character
+      and all Unicode marks (combining or not) that follow it.
+    </para>
+    <para>
+      For example, let us start with the following character sequence
+      (top row) and accompanying initial cluster values (bottom row):
     </para>
     <programlisting>
-  AB,D,C,E
-  0 ,3,2,4   
-</programlisting>
+      A,acute,B
+      0,1    ,2
+    </programlisting>
     <para>
-      There's no way to differentiate between these two scenarios based
-      on the cluster numbers alone.
+      The <literal>acute</literal> is a Unicode mark. If HarfBuzz is
+      using cluster level 0 on this sequence, then the
+      <literal>A</literal> and <literal>acute</literal> clusters will
+      merge, and the result will become:
+    </para>
+    <programlisting>
+      A,acute,B
+      0,0    ,2
+    </programlisting>
+    <para>
+      This initial cluster merging is the default behavior of the
+      Windows shaping engine, and the old HarfBuzz codebase copied
+      that behavior to maintain compatibility. Consequently, it has
+      remained the default behavior in the new HarfBuzz codebase.
     </para>
     <para>
-      Another problem happens with ligatures under level 2 if the
-      direction of the text is forced to opposite of its natural
-      direction (e.g. left-to-right Arabic). But that's too much of a
-      corner case to worry about.
+      But this initial cluster-merging behavior makes it impossible to
+      color diacritic marks differently from their base
+      characters. That is why, in level 1, HarfBuzz does not perform
+      the initial merging step.
     </para>
-  </sect2>
-</sect1>
+    <para>
+      For client programs that rely on HarfBuzz cluster values to
+      perform cursor positioning, level 0 is more convenient. But
+      relying on cluster boundaries for cursor positioning is wrong: cursor
+      positions should be determined based on Unicode grapheme
+      boundaries, not on shaping-cluster boundaries. As such, level 1
+      clusters are preferred. 
+    </para>
+    <para>
+      One last note about levels 0 and 1. HarfBuzz currently does not allow a
+      <literal>MultipleSubst</literal> lookup to replace a glyph with zero
+      glyphs (in other words, to delete a glyph). But, in some other situations,
+      glyphs can be deleted. In those cases, if the glyph being deleted is
+      the last glyph of its cluster, HarfBuzz makes sure to merge the cluster
+      with a neighboring cluster.
+    </para>
+    <para>
+      This is done primarily to make sure that the starting cluster of the
+      text always has the cluster index pointing to the start of the text
+      for the run; more than one client currently relies on this
+      guarantee.
+    </para>
+    <para>
+      Incidentally, Apple's CoreText does something else to maintain the
+      same promise: it inserts a glyph with id 65535 at the beginning of
+      the glyph string if the glyph corresponding to the first character
+      in the run was deleted. HarfBuzz might do something similar in the
+      future.
+    </para>
+  </section>
+  <section id="level-2">
+    <title>Level 2</title>
+    <para>
+      HarfBuzz's level 2 cluster behavior uses a significantly
+      different model than that of level 0 and level 1.
+    </para>
+    <para>
+      The level 2 behavior is easy to describe, but it may be
+      difficult to understand in practical terms. In brief, level 2 
+      performs no merging of clusters whatsoever.
+    </para>
+    <para>
+      When glyphs form a ligature (or when some other feature
+      substitutes multiple glyphs with one glyph), the cluster value
+      of the first glyph is retained as the cluster value for the
+      ligature. However, no subsequent clusters &mdash; including
+      marks and modifiers &mdash; are affected.
+    </para>
+    <para>
+      Level 2 cluster behavior is less complex than level 0 or level
+      1, but there are a few cases in which processing cluster values
+      produced at level 2 may be tricky. 
+    </para>
+    <section id="ligatures-with-combining-marks-in-level-2">
+      <title>Ligatures with combining marks in level 2</title>
+      <para>
+	The first example of how HarfBuzz's level 2 cluster behavior
+	can be tricky is when the text to be shaped includes combining
+	marks attached to ligatures.
+      </para>
+      <para>
+	Let us start with an input sequence with the following
+	characters (top row) and initial cluster values (bottom row):
+      </para>
+      <programlisting>
+	A,acute,B,breve,C,circumflex
+	0,1    ,2,3    ,4,5
+      </programlisting>
+      <para>
+	If the sequence <literal>A,B,C</literal> forms a ligature,
+	then these are the cluster values HarfBuzz will return under
+	the various cluster levels:
+      </para>
+      <para>
+	Level 0:
+      </para>
+      <programlisting>
+	ABC,acute,breve,circumflex
+	0  ,0    ,0    ,0
+      </programlisting>
+      <para>
+	Level 1:
+      </para>
+      <programlisting>
+	ABC,acute,breve,circumflex
+	0  ,0    ,0    ,5
+      </programlisting>
+      <para>
+	Level 2:
+      </para>
+      <programlisting>
+	ABC,acute,breve,circumflex
+	0  ,1    ,3    ,5
+      </programlisting>
+      <para>
+	Making sense of the level 2 result is the hardest for a client
+	program, because there is nothing in the cluster values that
+	indicates that <literal>B</literal> and <literal>C</literal>
+	formed a ligature with <literal>A</literal>.
+      </para>
+      <para>
+	In contrast, the "merged" cluster values of the mark glyphs
+	that are seen in the level 0 and level 1 output are evidence
+	that a ligature substitution took place. 
+      </para>
+    </section>
+    <section id="reordering-in-level-2">
+      <title>Reordering in level 2</title>
+      <para>
+	Another example of how HarfBuzz's level 2 cluster behavior
+	can be tricky is when glyphs reorder. Consider an input sequence
+	with the following characters (top row) and initial cluster
+	values (bottom row):
+      </para>
+      <programlisting>
+	A,B,C,D,E
+	0,1,2,3,4
+      </programlisting>
+      <para>
+	Now imagine <literal>D</literal> moves before
+	<literal>B</literal> in a reordering operation. The cluster
+	values will then be:
+      </para>
+      <programlisting>
+	A,D,B,C,E
+	0,3,1,2,4
+      </programlisting>
+      <para>
+	Next, if <literal>D</literal> forms a ligature with
+	<literal>B</literal>, the output is:
+      </para>
+      <programlisting>
+	A,DB,C,E
+	0,3 ,2,4
+      </programlisting>
+      <para>
+	However, in a different scenario, in which the shaping rules
+	of the script instead caused <literal>A</literal> and
+	<literal>B</literal> to form a ligature
+	<emphasis>before</emphasis> the <literal>D</literal> reordered, the
+	result would be:
+      </para>
+      <programlisting>
+	AB,D,C,E
+	0 ,3,2,4   
+      </programlisting>
+      <para>
+	There is no way for a client program to differentiate between
+	these two scenarios based on the cluster values
+	alone. Consequently, client programs that use level 2 might
+	need to undertake additional work in order to manage cursor
+	positioning, text attributes, or other desired features.
+      </para>
+    </section>
+    <section id="other-considerations-in-level-2">
+      <title>Other considerations in level 2</title>
+      <para>
+	There may be other problems encountered with ligatures under
+	level 2, such as if the direction of the text is forced to
+	opposite of its natural direction (for example, left-to-right
+	Arabic). But, generally speaking, these other scenarios are
+	minor corner cases that are too obscure for most client
+	programs to need to worry about.
+      </para>
+    </section>
+  </section>
 </chapter>
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..932bd94
--- /dev/null
+++ b/docs/usermanual-getting-started.xml
@@ -0,0 +1,223 @@
+<?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);
+      FT_Set_Char_Size(face, 0, 1000, 0, 0);
+      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-hello-harfbuzz.xml b/docs/usermanual-hello-harfbuzz.xml
deleted file mode 100644
index 716b2f2..0000000
--- a/docs/usermanual-hello-harfbuzz.xml
+++ /dev/null
@@ -1,183 +0,0 @@
-<chapter id="hello-harfbuzz">
-  <title>Hello, HarfBuzz</title>
-  <para>
-    Here's the simplest HarfBuzz that can possibly work. We will improve
-    it later.
-  </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, strlen(text), 0, strlen(text));
-</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_guess_segment_properties(buf);
-</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>
-  <section id="what-harfbuzz-doesnt-do">
-    <title>What HarfBuzz doesn't do</title>
-    <para>
-      The code above will take a UTF8 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>
-      If you are implementing a text layout engine you may have other
-      responsibilities, that HarfBuzz will not help you with:
-    </para>
-    <itemizedlist>
-      <listitem>
-        <para>
-          HarfBuzz won't help you with bidirectionality. If you want to
-          lay out text with mixed Hebrew and English, you will need to
-          ensure that the buffer provided to HarfBuzz has those
-          characters in the correct layout order. This will be different
-          from the logical order in which the Unicode text is stored. 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 reorder a string from logical order into presentation order.
-          Before sending your string to HarfBuzz, you may need to apply the
-          bidi algorithm to it. Libraries such as ICU and fribidi 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, 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.
-          Similarly if you change font, font size, script, language or
-          direction within your string, you will need to shape each run
-          independently and then output them independently. HarfBuzz
-          expects to shape a run of characters sharing the same
-          properties.
-        </para>
-      </listitem>
-      <listitem>
-        <para>
-          HarfBuzz won't help you with line breaking, hyphenation or
-          justification. As mentioned above, it 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. If you
-          want to layout text in paragraphs, you will probably want to send
-          each word of your text to HarfBuzz to determine its shaped width
-          after glyph substitutions, then work out how many words will fit
-          on a line, and then finally output each word of the line separated
-          by a space of the correct size to fully justify the paragraph.
-        </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 - what you then do with the glyphs that your font
-      returns is up to you. The example we saw above enough to get us
-      started using HarfBuzz. Now we are going to use the remainder of
-      HarfBuzz's API to refine that example and improve our text shaping
-      capabilities.
-    </para>
-  </section>
-</chapter>
\ No newline at end of file
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 e0ea1c5..3618d03 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,7 +15,6 @@
 # Convenience targets:
 lib: $(BUILT_SOURCES) libharfbuzz.la
 libs: $(BUILT_SOURCES) $(lib_LTLIBRARIES)
-fuzzing: $(BUILT_SOURCES) libharfbuzz-fuzzing.la libharfbuzz-subset-fuzzing.la
 
 lib_LTLIBRARIES = libharfbuzz.la
 
@@ -29,10 +28,6 @@
 HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources)
 HBHEADERS = $(HB_BASE_headers)
 
-HBSOURCES += $(HB_OT_sources)
-HBSOURCES += $(HB_OT_RAGEL_GENERATED_sources)
-HBHEADERS += $(HB_OT_headers)
-
 if HAVE_FALLBACK
 HBSOURCES += $(HB_FALLBACK_sources)
 endif
@@ -169,37 +164,6 @@
 pkgconfig_DATA += harfbuzz-subset.pc
 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 \
-	-DHB_SANITIZE_MAX_OPS_MIN=128 \
-	-DHB_BUFFER_MAX_LEN_FACTOR=3 \
-	-DHB_BUFFER_MAX_LEN_MIN=8 \
-	-DHB_BUFFER_MAX_LEN_DEFAULT=128 \
-	-DHB_BUFFER_MAX_OPS_FACTOR=8 \
-	-DHB_BUFFER_MAX_OPS_MIN=64 \
-	-DHB_BUFFER_MAX_OPS_DEFAULT=1024 \
-	$(NULL)
-EXTRA_LTLIBRARIES = libharfbuzz-fuzzing.la libharfbuzz-subset-fuzzing.la
-
-libharfbuzz_fuzzing_la_LINK = $(chosen_linker) $(libharfbuzz_fuzzing_la_LDFLAGS)
-libharfbuzz_fuzzing_la_SOURCES = $(libharfbuzz_la_SOURCES)
-libharfbuzz_fuzzing_la_CPPFLAGS = $(HBCFLAGS) $(FUZZING_CPPFLAGS)
-libharfbuzz_fuzzing_la_LDFLAGS = $(AM_LDFLAGS)
-libharfbuzz_fuzzing_la_LIBADD = $(libharfbuzz_la_LIBADD)
-EXTRA_libharfbuzz_fuzzing_la_DEPENDENCIES = $(EXTRA_libharfbuzz_la_DEPENDENCIES)
-CLEANFILES += libharfbuzz-fuzzing.la
-
-libharfbuzz_subset_fuzzing_la_LINK = $(chosen_linker) $(libharfbuzz_subset_fuzzing_la_LDFLAGS)
-libharfbuzz_subset_fuzzing_la_SOURCES = $(libharfbuzz_subset_la_SOURCES)
-libharfbuzz_subset_fuzzing_la_CPPFLAGS = $(HBCFLAGS) $(FUZZING_CPPFLAGS)
-libharfbuzz_subset_fuzzing_la_LDFLAGS = $(AM_LDFLAGS)
-libharfbuzz_subset_fuzzing_la_LIBADD = $(libharfbuzz_subset_la_LIBADD)
-EXTRA_libharfbuzz_subset_fuzzing_la_DEPENDENCIES = $(EXTRA_libharfbuzz_subset_la_DEPENDENCIES)
-CLEANFILES += libharfbuzz-subset-fuzzing.la
-
 if HAVE_ICU
 if HAVE_ICU_BUILTIN
 HBCFLAGS += $(ICU_CFLAGS)
@@ -269,7 +233,7 @@
 CLEANFILES += $(pkgconfig_DATA)
 
 
-DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def harfbuzz-deprecated.def
+DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def harfbuzz-deprecated-symbols.txt
 if HAVE_GOBJECT
 DEF_FILES += harfbuzz-gobject.def
 endif
@@ -283,8 +247,8 @@
 	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
 harfbuzz-gobject.def: $(HB_GOBJECT_headers)
 	$(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^
-harfbuzz-deprecated.def: $(srcdir)/hb-deprecated.h
-	$(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 = \
@@ -295,6 +259,7 @@
 	gen-os2-unicode-ranges.py \
 	gen-tag-table.py \
 	gen-use-table.py \
+	gen-vowel-constraints.py \
 	$(NULL)
 EXTRA_DIST += $(GENERATORS)
 
@@ -316,22 +281,24 @@
 	$(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 tag-table use-table emoji-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)) \
-	$(patsubst %,$(srcdir)/%,$(HB_OT_RAGEL_GENERATED_sources)) \
 	$(NULL)
 BUILT_SOURCES += $(RAGEL_GENERATED)
 EXTRA_DIST += \
 	$(HB_BASE_RAGEL_sources) \
-	$(HB_OT_RAGEL_sources) \
 	$(NULL)
 # We decided to add ragel-generated files to git...
 #MAINTAINERCLEANFILES += $(RAGEL_GENERATED)
@@ -343,6 +310,7 @@
 	main \
 	test \
 	test-buffer-serialize \
+	test-name-table \
 	test-size-params \
 	test-would-substitute \
 	$(NULL)
@@ -356,17 +324,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 \
@@ -403,15 +384,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
 
@@ -446,6 +418,8 @@
 	-DHB_H_IN \
 	-DHB_OT_H \
 	-DHB_OT_H_IN \
+	-DHB_AAT_H \
+	-DHB_AAT_H_IN \
 	-DHB_GOBJECT_H \
 	-DHB_GOBJECT_H_IN \
 	-DHB_EXTERN= \
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 59cde6b..98b6228 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -1,77 +1,172 @@
 # Base and default-included sources and headers
 
 HB_BASE_sources = \
+	hb-aat-fdsc-table.hh \
+	hb-aat-layout-ankr-table.hh \
+	hb-aat-layout-bsln-table.hh \
+	hb-aat-layout-common.hh \
+	hb-aat-layout-feat-table.hh \
+	hb-aat-layout-just-table.hh \
+	hb-aat-layout-kerx-table.hh \
+	hb-aat-layout-lcar-table.hh \
+	hb-aat-layout-morx-table.hh \
+	hb-aat-layout-trak-table.hh \
+	hb-aat-layout.cc \
+	hb-aat-layout.hh \
+	hb-aat-ltag-table.hh \
+	hb-aat-map.cc \
+	hb-aat-map.hh \
+	hb-array.hh \
 	hb-atomic.hh \
-	hb-blob.hh \
 	hb-blob.cc \
-	hb-buffer.hh \
+	hb-blob.hh \
 	hb-buffer-serialize.cc \
 	hb-buffer.cc \
+	hb-buffer.hh \
 	hb-cache.hh \
+	hb-cff-interp-common.hh \
+	hb-cff-interp-cs-common.hh \
+	hb-cff-interp-dict-common.hh \
+	hb-cff1-interp-cs.hh \
+	hb-cff2-interp-cs.hh \
 	hb-common.cc \
 	hb-debug.hh \
 	hb-dsalgs.hh \
-	hb-face.hh \
 	hb-face.cc \
-	hb-font.hh \
+	hb-face.hh \
 	hb-font.cc \
-	hb-iter.hh \
-	hb-map.hh \
-	hb-map.cc \
+	hb-font.hh \
+	hb-kern.hh \
 	hb-machinery.hh \
+	hb-map.cc \
+	hb-map.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-cff-common.hh \
+	hb-ot-cff1-table.cc \
+	hb-ot-cff1-table.hh \
+	hb-ot-cff2-table.cc \
+	hb-ot-cff2-table.hh \
 	hb-ot-cmap-table.hh \
+	hb-ot-color-cbdt-table.hh \
+	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-color.cc \
+	hb-ot-face.cc \
+	hb-ot-face.hh \
+	hb-ot-font.cc \
+	hb-ot-gasp-table.hh \
 	hb-ot-glyf-table.hh \
 	hb-ot-hdmx-table.hh \
 	hb-ot-head-table.hh \
 	hb-ot-hhea-table.hh \
 	hb-ot-hmtx-table.hh \
 	hb-ot-kern-table.hh \
+	hb-ot-layout-base-table.hh \
+	hb-ot-layout-common.hh \
+	hb-ot-layout-gdef-table.hh \
+	hb-ot-layout-gpos-table.hh \
+	hb-ot-layout-gsub-table.hh \
+	hb-ot-layout-gsubgpos.hh \
+	hb-ot-layout-jstf-table.hh \
+	hb-ot-layout.cc \
+	hb-ot-layout.hh \
+	hb-ot-map.cc \
+	hb-ot-map.hh \
+	hb-ot-math-table.hh \
+	hb-ot-math.cc \
 	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-shape-complex-arabic-fallback.hh \
+	hb-ot-shape-complex-arabic-table.hh \
+	hb-ot-shape-complex-arabic-win1256.hh \
+	hb-ot-shape-complex-arabic.cc \
+	hb-ot-shape-complex-arabic.hh \
+	hb-ot-shape-complex-default.cc \
+	hb-ot-shape-complex-hangul.cc \
+	hb-ot-shape-complex-hebrew.cc \
+	hb-ot-shape-complex-indic-table.cc \
+	hb-ot-shape-complex-indic.cc \
+	hb-ot-shape-complex-indic.hh \
+	hb-ot-shape-complex-khmer.cc \
+	hb-ot-shape-complex-khmer.hh \
+	hb-ot-shape-complex-myanmar.cc \
+	hb-ot-shape-complex-myanmar.hh \
+	hb-ot-shape-complex-thai.cc \
+	hb-ot-shape-complex-use-table.cc \
+	hb-ot-shape-complex-use.cc \
+	hb-ot-shape-complex-use.hh \
+	hb-ot-shape-complex-vowel-constraints.cc \
+	hb-ot-shape-complex-vowel-constraints.hh \
+	hb-ot-shape-complex.hh \
+	hb-ot-shape-fallback.cc \
+	hb-ot-shape-fallback.hh \
+	hb-ot-shape-normalize.cc \
+	hb-ot-shape-normalize.hh \
+	hb-ot-shape.cc \
+	hb-ot-shape.hh \
+	hb-ot-stat-table.hh \
 	hb-ot-tag-table.hh \
 	hb-ot-tag.cc \
-	hb.hh \
+	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-var.cc \
+	hb-ot-vorg-table.hh \
 	hb-set-digest.hh \
-	hb-set.hh \
 	hb-set.cc \
-	hb-shape.cc \
-	hb-shape-plan.hh \
+	hb-set.hh \
 	hb-shape-plan.cc \
-	hb-shaper-list.hh \
+	hb-shape-plan.hh \
+	hb-shape.cc \
 	hb-shaper-impl.hh \
-	hb-shaper.hh \
+	hb-shaper-list.hh \
 	hb-shaper.cc \
+	hb-shaper.hh \
 	hb-static.cc \
 	hb-string-array.hh \
-	hb-unicode.hh \
 	hb-unicode-emoji-table.hh \
 	hb-unicode.cc \
-	hb-vector.hh \
+	hb-unicode.hh \
 	hb-utf.hh \
+	hb-vector.hh \
 	hb-warning.cc \
+	hb.hh \
 	$(NULL)
 
 HB_BASE_RAGEL_GENERATED_sources = \
 	hb-buffer-deserialize-json.hh \
 	hb-buffer-deserialize-text.hh \
+	hb-ot-shape-complex-indic-machine.hh \
+	hb-ot-shape-complex-khmer-machine.hh \
+	hb-ot-shape-complex-myanmar-machine.hh \
+	hb-ot-shape-complex-use-machine.hh \
 	$(NULL)
 HB_BASE_RAGEL_sources = \
 	hb-buffer-deserialize-json.rl \
 	hb-buffer-deserialize-text.rl \
+	hb-ot-shape-complex-indic-machine.rl \
+	hb-ot-shape-complex-khmer-machine.rl \
+	hb-ot-shape-complex-myanmar-machine.rl \
+	hb-ot-shape-complex-use-machine.rl \
 	$(NULL)
 
 HB_BASE_headers = \
-	hb.h \
+	hb-aat-layout.h \
+	hb-aat.h \
 	hb-blob.h \
 	hb-buffer.h \
 	hb-common.h \
@@ -79,104 +174,25 @@
 	hb-face.h \
 	hb-font.h \
 	hb-map.h \
-	hb-set.h \
-	hb-shape.h \
-	hb-shape-plan.h \
-	hb-unicode.h \
-	hb-version.h \
-	$(NULL)
-
-HB_FALLBACK_sources = \
-	hb-fallback-shape.cc	\
-	$(NULL)
-
-HB_OT_sources = \
-	hb-aat-layout.cc \
-	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.hh \
-	hb-aat-ltag-table.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.hh \
-	hb-ot-layout-gdef-table.hh \
-	hb-ot-layout-gpos-table.hh \
-	hb-ot-layout-gsubgpos.hh \
-	hb-ot-layout-gsub-table.hh \
-	hb-ot-layout-jstf-table.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.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.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.hh \
-	hb-ot-shape-complex-indic-table.cc \
-	hb-ot-shape-complex-khmer.hh \
-	hb-ot-shape-complex-khmer.cc \
-	hb-ot-shape-complex-myanmar.hh \
-	hb-ot-shape-complex-myanmar.cc \
-	hb-ot-shape-complex-thai.cc \
-	hb-ot-shape-complex-use.cc \
-	hb-ot-shape-complex-use.hh \
-	hb-ot-shape-complex-use-table.cc \
-	hb-ot-shape-complex.hh \
-	hb-ot-shape-normalize.hh \
-	hb-ot-shape-normalize.cc \
-	hb-ot-shape-fallback.hh \
-	hb-ot-shape-fallback.cc \
-	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 \
-	$(NULL)
-
-HB_OT_RAGEL_GENERATED_sources = \
-	hb-ot-shape-complex-indic-machine.hh \
-	hb-ot-shape-complex-khmer-machine.hh \
-	hb-ot-shape-complex-myanmar-machine.hh \
-	hb-ot-shape-complex-use-machine.hh \
-	$(NULL)
-HB_OT_RAGEL_sources = \
-	hb-ot-shape-complex-indic-machine.rl \
-	hb-ot-shape-complex-khmer-machine.rl \
-	hb-ot-shape-complex-myanmar-machine.rl \
-	hb-ot-shape-complex-use-machine.rl \
-	$(NULL)
-
-HB_OT_headers = \
-	hb-ot.h \
+	hb-ot-color.h \
+	hb-ot-deprecated.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 \
+	hb-ot.h \
+	hb-set.h \
+	hb-shape-plan.h \
+	hb-shape.h \
+	hb-unicode.h \
+	hb-version.h \
+	hb.h \
+	$(NULL)
+
+HB_FALLBACK_sources = \
+	hb-fallback-shape.cc	\
 	$(NULL)
 
 # Optional Sources and Headers with external deps
@@ -210,15 +226,26 @@
 
 # Sources for libharfbuzz-subset
 HB_SUBSET_sources = \
+	hb-ot-cff1-table.cc \
+	hb-ot-cff2-table.cc \
 	hb-static.cc \
-	hb-subset.cc \
-	hb-subset.hh \
+	hb-subset-cff-common.cc \
+	hb-subset-cff-common.hh \
+	hb-subset-cff1.cc \
+	hb-subset-cff1.hh \
+	hb-subset-cff2.cc \
+	hb-subset-cff2.hh \
 	hb-subset-glyf.cc \
 	hb-subset-glyf.hh \
+	hb-subset-glyf.hh \
 	hb-subset-input.cc \
 	hb-subset-input.hh \
 	hb-subset-plan.cc \
 	hb-subset-plan.hh \
+	hb-subset-plan.hh \
+	hb-subset.cc \
+	hb-subset.hh \
+	hb-subset.hh \
 	$(NULL)
 
 HB_SUBSET_headers = \
diff --git a/src/dump-emoji.cc b/src/dump-emoji.cc
deleted file mode 100644
index f45bc31..0000000
--- a/src/dump-emoji.cc
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright © 2018  Ebrahim Byagowi
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- */
-
-#include "hb-static.cc"
-#include "hb-ot-color-cbdt-table.hh"
-#include "hb-ot-color-colr-table.hh"
-#include "hb-ot-color-cpal-table.hh"
-#include "hb-ot-color-sbix-table.hh"
-#include "hb-ot-color-svg-table.hh"
-
-#include "hb-ft.h"
-
-#include <ft2build.h>
-#include FT_FREETYPE_H
-#include FT_GLYPH_H
-
-#include <cairo.h>
-#include <cairo-ft.h>
-#include <cairo-svg.h>
-
-#ifdef HAVE_GLIB
-#include <glib.h>
-#endif
-#include <stdlib.h>
-#include <stdio.h>
-
-static void cbdt_callback (const uint8_t* data, unsigned int length,
-			   unsigned int group, unsigned int gid)
-{
-  char output_path[255];
-  sprintf (output_path, "out/cbdt-%d-%d.png", group, gid);
-  FILE *f = fopen (output_path, "wb");
-  fwrite (data, 1, length, f);
-  fclose (f);
-}
-
-static void sbix_callback (const uint8_t* data, unsigned int length,
-			   unsigned int group, unsigned int gid)
-{
-  char output_path[255];
-  sprintf (output_path, "out/sbix-%d-%d.png", group, gid);
-  FILE *f = fopen (output_path, "wb");
-  fwrite (data, 1, length, f);
-  fclose (f);
-}
-
-static void svg_callback (const uint8_t* data, unsigned int length,
-			  unsigned int start_glyph, unsigned int end_glyph)
-{
-  char output_path[255];
-  if (start_glyph == end_glyph)
-    sprintf (output_path, "out/svg-%d.svg", start_glyph);
-  else
-    sprintf (output_path, "out/svg-%d-%d.svg", start_glyph, end_glyph);
-
-  // append "z" if the content is gzipped
-  if ((data[0] == 0x1F) && (data[1] == 0x8B))
-    strcat (output_path, "z");
-
-  FILE *f = fopen (output_path, "wb");
-  fwrite (data, 1, length, f);
-  fclose (f);
-}
-
-static void colr_cpal_rendering (cairo_font_face_t *cairo_face, unsigned int upem, unsigned int num_glyphs,
-				 const OT::COLR *colr, const OT::CPAL *cpal)
-{
-  for (unsigned int i = 0; i < num_glyphs; ++i)
-  {
-    unsigned int first_layer_index, num_layers;
-    if (colr->get_base_glyph_record (i, &first_layer_index, &num_layers))
-    {
-      // Measure
-      cairo_text_extents_t extents;
-      {
-	cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
-	cairo_t *cr = cairo_create (surface);
-	cairo_set_font_face (cr, cairo_face);
-	cairo_set_font_size (cr, upem);
-
-	cairo_glyph_t *glyphs = (cairo_glyph_t *) calloc (num_layers, sizeof (cairo_glyph_t));
-	for (unsigned int j = 0; j < num_layers; ++j)
-	{
-	  hb_codepoint_t glyph_id;
-	  unsigned int color_index;
-	  colr->get_layer_record (first_layer_index + j, &glyph_id, &color_index);
-	  glyphs[j].index = glyph_id;
-	}
-	cairo_glyph_extents (cr, glyphs, num_layers, &extents);
-	free (glyphs);
-	cairo_surface_destroy (surface);
-	cairo_destroy (cr);
-      }
-
-      // Add a slight margin
-      extents.width += extents.width / 10;
-      extents.height += extents.height / 10;
-      extents.x_bearing -= extents.width / 20;
-      extents.y_bearing -= extents.height / 20;
-
-      // Render
-      unsigned int pallet_count = cpal->get_palette_count ();
-      for (unsigned int pallet = 0; pallet < pallet_count; ++pallet) {
-	char output_path[255];
-
-	// If we have more than one pallet, use a better namin
-	if (pallet_count == 1)
-	  sprintf (output_path, "out/colr-%d.svg", i);
-	else
-	  sprintf (output_path, "out/colr-%d-%d.svg", i, pallet);
-
-	cairo_surface_t *surface = cairo_svg_surface_create (output_path, extents.width, extents.height);
-	cairo_t *cr = cairo_create (surface);
-	cairo_set_font_face (cr, cairo_face);
-	cairo_set_font_size (cr, upem);
-
-	for (unsigned int j = 0; j < num_layers; ++j)
-	{
-	  hb_codepoint_t glyph_id;
-	  unsigned int color_index;
-	  colr->get_layer_record (first_layer_index + j, &glyph_id, &color_index);
-
-	  uint32_t color = cpal->get_color_record_argb (color_index, pallet);
-	  int alpha = color & 0xFF;
-	  int r = (color >> 8) & 0xFF;
-	  int g = (color >> 16) & 0xFF;
-	  int b = (color >> 24) & 0xFF;
-	  cairo_set_source_rgba (cr, r / 255., g / 255., b / 255., alpha);
-
-	  cairo_glyph_t glyph;
-	  glyph.index = glyph_id;
-	  glyph.x = -extents.x_bearing;
-	  glyph.y = -extents.y_bearing;
-	  cairo_show_glyphs (cr, &glyph, 1);
-	}
-
-	cairo_surface_destroy (surface);
-	cairo_destroy (cr);
-      }
-    }
-  }
-}
-
-static void dump_glyphs (cairo_font_face_t *cairo_face, unsigned int upem,
-			 unsigned int num_glyphs)
-{
-  // Dump every glyph available on the font
-  return; // disabled for now
-  for (unsigned int i = 0; i < num_glyphs; ++i)
-  {
-    cairo_text_extents_t extents;
-    cairo_glyph_t glyph = {0};
-    glyph.index = i;
-
-    // Measure
-    {
-      cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
-      cairo_t *cr = cairo_create (surface);
-      cairo_set_font_face (cr, cairo_face);
-      cairo_set_font_size (cr, upem);
-
-      cairo_glyph_extents (cr, &glyph, 1, &extents);
-      cairo_surface_destroy (surface);
-      cairo_destroy (cr);
-    }
-
-    // Add a slight margin
-    extents.width += extents.width / 10;
-    extents.height += extents.height / 10;
-    extents.x_bearing -= extents.width / 20;
-    extents.y_bearing -= extents.height / 20;
-
-    // Render
-    {
-      char output_path[255];
-      sprintf (output_path, "out/%d.svg", i);
-      cairo_surface_t *surface = cairo_svg_surface_create (output_path, extents.width, extents.height);
-      cairo_t *cr = cairo_create (surface);
-      cairo_set_font_face (cr, cairo_face);
-      cairo_set_font_size (cr, upem);
-      glyph.x = -extents.x_bearing;
-      glyph.y = -extents.y_bearing;
-      cairo_show_glyphs (cr, &glyph, 1);
-      cairo_surface_destroy (surface);
-      cairo_destroy (cr);
-    }
-  }
-}
-
-int main (int argc, char **argv)
-{
-  if (argc != 2) {
-    fprintf (stderr, "usage: %s font-file.ttf\n"
-		     "run it like `rm -rf out && mkdir out && %s font-file.ttf`\n",
-		     argv[0], argv[0]);
-    exit (1);
-  }
-
-
-  FILE *font_name_file = fopen ("out/_font_name_file.txt", "r");
-  if (font_name_file != nullptr)
-  {
-    fprintf (stderr, "Purge or move ./out folder in order to run a new dump\n");
-    exit (1);
-  }
-
-  font_name_file = fopen ("out/_font_name_file.txt", "w");
-  if (font_name_file == nullptr)
-  {
-    fprintf (stderr, "./out is not accessible, create it please\n");
-    exit (1);
-  }
-  fwrite (argv[1], 1, strlen (argv[1]), font_name_file);
-  fclose (font_name_file);
-
-  hb_blob_t *blob = hb_blob_create_from_file (argv[1]);
-  hb_face_t *face = hb_face_create (blob, 0);
-  hb_font_t *font = hb_font_create (face);
-
-  OT::CBDT::accelerator_t cbdt;
-  cbdt.init (face);
-  cbdt.dump (cbdt_callback);
-  cbdt.fini ();
-
-  OT::sbix::accelerator_t sbix;
-  sbix.init (face);
-  sbix.dump (sbix_callback);
-  sbix.fini ();
-
-  OT::SVG::accelerator_t svg;
-  svg.init (face);
-  svg.dump (svg_callback);
-  svg.fini ();
-
-  hb_blob_t* colr_blob = hb_sanitize_context_t ().reference_table<OT::COLR> (face);
-  const OT::COLR *colr = colr_blob->as<OT::COLR> ();
-
-  hb_blob_t* cpal_blob = hb_sanitize_context_t ().reference_table<OT::CPAL> (face);
-  const OT::CPAL *cpal = cpal_blob->as<OT::CPAL> ();
-
-  cairo_font_face_t *cairo_face;
-  {
-    FT_Library library;
-    FT_Init_FreeType (&library);
-    FT_Face ftface;
-    FT_New_Face (library, argv[1], 0, &ftface);
-    cairo_face = cairo_ft_font_face_create_for_ft_face (ftface, 0);
-  }
-  unsigned int num_glyphs = hb_face_get_glyph_count (face);
-  unsigned int upem = hb_face_get_upem (face);
-  colr_cpal_rendering (cairo_face, upem, num_glyphs, colr, cpal);
-  dump_glyphs (cairo_face, upem, num_glyphs);
-
-
-  hb_font_destroy (font);
-  hb_face_destroy (face);
-  hb_blob_destroy (blob);
-
-  return 0;
-}
diff --git a/src/dump-indic-data.cc b/src/dump-indic-data.cc
index a506889..8ddc9d5 100644
--- a/src/dump-indic-data.cc
+++ b/src/dump-indic-data.cc
@@ -27,7 +27,7 @@
 #include "hb-ot-shape-complex-indic.hh"
 
 int
-main (void)
+main ()
 {
   for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
   {
diff --git a/src/dump-khmer-data.cc b/src/dump-khmer-data.cc
index 1e79a97..cffbb92 100644
--- a/src/dump-khmer-data.cc
+++ b/src/dump-khmer-data.cc
@@ -27,7 +27,7 @@
 #include "hb-ot-shape-complex-khmer.hh"
 
 int
-main (void)
+main ()
 {
   for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
   {
diff --git a/src/dump-myanmar-data.cc b/src/dump-myanmar-data.cc
index 9f8b12e..c1a303f 100644
--- a/src/dump-myanmar-data.cc
+++ b/src/dump-myanmar-data.cc
@@ -27,7 +27,7 @@
 #include "hb-ot-shape-complex-myanmar.hh"
 
 int
-main (void)
+main ()
 {
   for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
   {
diff --git a/src/dump-use-data.cc b/src/dump-use-data.cc
index 4a8b258..d639426 100644
--- a/src/dump-use-data.cc
+++ b/src/dump-use-data.cc
@@ -27,7 +27,7 @@
 #include "hb-ot-shape-complex-use.hh"
 
 int
-main (void)
+main ()
 {
   for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
   {
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
index 278e0b2..9afe747 100755
--- a/src/gen-emoji-table.py
+++ b/src/gen-emoji-table.py
@@ -12,7 +12,7 @@
 f = open(sys.argv[1])
 header = [f.readline () for _ in range(10)]
 
-sets = OrderedDict()
+ranges = OrderedDict()
 for line in f.readlines():
 	line = line.strip()
 	if not line or line[0] == '#':
@@ -25,9 +25,12 @@
 	else:
 		start = end = rang[0]
 
-	if typ not in sets:
-		sets[typ] = set()
-	sets[typ].add((start, end))
+	if typ not in ranges:
+		ranges[typ] = []
+	if ranges[typ] and ranges[typ][-1][1] == start - 1:
+		ranges[typ][-1] = (ranges[typ][-1][0], end)
+	else:
+		ranges[typ].append((start, end))
 
 
 
@@ -49,7 +52,7 @@
 print ('#include "hb-unicode.hh"')
 print ()
 
-for typ,s in sets.items():
+for typ,s in ranges.items():
 	if typ != "Extended_Pictographic": continue
 	print()
 	print("static const struct hb_unicode_range_t _hb_unicode_emoji_%s_table[] =" % typ)
diff --git a/src/gen-use-table.py b/src/gen-use-table.py
index ebfae6f..099f6a1 100755
--- a/src/gen-use-table.py
+++ b/src/gen-use-table.py
@@ -50,6 +50,8 @@
 # TODO https://github.com/roozbehp/unicode-data/issues/9
 data[0][0x11C44] = 'Consonant_Placeholder'
 data[0][0x11C45] = 'Consonant_Placeholder'
+# TODO https://github.com/harfbuzz/harfbuzz/pull/1399
+data[0][0x111C8] = 'Consonant_Placeholder'
 for u in range (0xFE00, 0xFE0F + 1):
 	data[0][u] = defaults[0]
 
@@ -168,7 +170,7 @@
 def is_BASE_IND(U, UISC, UGC):
 	#SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
 	return (UISC in [Consonant_Dead, Modifying_Letter] or
-		(UGC == Po and not U in [0x104B, 0x104E, 0x2022, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
+		(UGC == Po and not U in [0x104B, 0x104E, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
 		False # SPEC-DRAFT-OUTDATED! U == 0x002D
 		)
 def is_BASE_NUM(U, UISC, UGC):
@@ -200,7 +202,8 @@
 	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
+	# https://github.com/harfbuzz/harfbuzz/issues/1379
+	return U in [0x11046, 0x1134D]
 def is_HALANT_NUM(U, UISC, UGC):
 	return UISC == Number_Joiner
 def is_ZWNJ(U, UISC, UGC):
@@ -353,6 +356,9 @@
 		# TODO: https://github.com/harfbuzz/harfbuzz/issues/1105
 		if U == 0x11134: UISC = Gemination_Mark
 
+		# TODO: https://github.com/harfbuzz/harfbuzz/pull/1399
+		if U == 0x111C9: UISC = Consonant_Final
+
 		values = [k for k,v in items if v(U,UISC,UGC)]
 		assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values)
 		USE = values[0]
diff --git a/src/gen-vowel-constraints.py b/src/gen-vowel-constraints.py
new file mode 100755
index 0000000..b7f6be2
--- /dev/null
+++ b/src/gen-vowel-constraints.py
@@ -0,0 +1,219 @@
+#!/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 ('  }')
+print ('}')
+
+print ()
+print ('/* == End of generated functions == */')
diff --git a/src/hb-aat-fdsc-table.hh b/src/hb-aat-fdsc-table.hh
new file mode 100644
index 0000000..a1af595
--- /dev/null
+++ b/src/hb-aat-fdsc-table.hh
@@ -0,0 +1,126 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_AAT_FDSC_TABLE_HH
+#define HB_AAT_FDSC_TABLE_HH
+
+#include "hb-aat-layout-common.hh"
+#include "hb-open-type.hh"
+
+/*
+ * fdsc -- Font descriptors
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fdsc.html
+ */
+#define HB_AAT_TAG_fdsc HB_TAG('f','d','s','c')
+
+
+namespace AAT {
+
+
+struct FontDescriptor
+{
+  bool has_data () const { return tag; }
+
+  int cmp (hb_tag_t a) const { return tag.cmp (a); }
+
+  float get_value () const { return u.value.to_float (); }
+
+  enum non_alphabetic_value_t {
+    Alphabetic		= 0,
+    Dingbats		= 1,
+    PiCharacters	= 2,
+    Fleurons		= 3,
+    DecorativeBorders	= 4,
+    InternationalSymbols= 5,
+    MathSymbols		= 6
+  };
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  Tag		tag;		/* The 4-byte table tag name. */
+  union {
+  Fixed		value;		/* The value for the descriptor tag. */
+  HBUINT32	nalfType;	/* If the tag is `nalf`, see non_alphabetic_value_t */
+  } u;
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct fdsc
+{
+  enum { tableTag = HB_AAT_TAG_fdsc };
+
+  enum {
+    Weight	 = HB_TAG ('w','g','h','t'),
+				/* Percent weight relative to regular weight.
+				 * (defaul value: 1.0) */
+    Width 	 = HB_TAG ('w','d','t','h'),
+				/* Percent width relative to regular width.
+				 * (default value: 1.0) */
+    Slant 	 = HB_TAG ('s','l','n','t'),
+				/* Angle of slant in degrees, where positive
+				 * is clockwise from straight up.
+				 * (default value: 0.0) */
+    OpticalSize  = HB_TAG ('o','p','s','z'),
+				/* Point size the font was designed for.
+				 * (default value: 12.0) */
+    NonAlphabetic= HB_TAG ('n','a','l','f')
+				/* These values are treated as integers,
+				 * not fixed32s. 0 means alphabetic, and greater
+				 * integers mean the font is non-alphabetic (e.g. symbols).
+				 * (default value: 0) */
+  };
+
+  const FontDescriptor &get_descriptor (hb_tag_t style) const
+  { return descriptors.lsearch (style); }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  descriptors.sanitize (c));
+  }
+
+  protected:
+  Fixed		version;	/* Version number of the font descriptors
+				 * table (0x00010000 for the current version). */
+  LArrayOf<FontDescriptor>
+		descriptors;	/* List of tagged-coordinate pairs style descriptors
+				 * that will be included to characterize this font.
+				 * Each descriptor consists of a <tag, value> pair.
+				 * These pairs are located in the gxFontDescriptor
+				 * array that follows. */
+  public:
+  DEFINE_SIZE_ARRAY (8, descriptors);
+};
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_FDSC_TABLE_HH */
diff --git a/src/hb-aat-layout-ankr-table.hh b/src/hb-aat-layout-ankr-table.hh
index 5f7656d..4235b36 100644
--- a/src/hb-aat-layout-ankr-table.hh
+++ b/src/hb-aat-layout-ankr-table.hh
@@ -36,10 +36,12 @@
 
 namespace AAT {
 
+using namespace OT;
+
 
 struct Anchor
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -56,12 +58,12 @@
 
 struct ankr
 {
-  static const hb_tag_t tableTag = HB_AAT_TAG_ankr;
+  enum { 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 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)
@@ -73,7 +75,7 @@
     return anchors[i];
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
diff --git a/src/hb-aat-layout-bsln-table.hh b/src/hb-aat-layout-bsln-table.hh
index b864086..5bc59cb 100644
--- a/src/hb-aat-layout-bsln-table.hh
+++ b/src/hb-aat-layout-bsln-table.hh
@@ -39,7 +39,7 @@
 
 struct BaselineTableFormat0Part
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this)));
@@ -57,7 +57,7 @@
 
 struct BaselineTableFormat1Part
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
@@ -75,7 +75,7 @@
 
 struct BaselineTableFormat2Part
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this)));
@@ -98,7 +98,7 @@
 
 struct BaselineTableFormat3Part
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && lookupTable.sanitize (c));
@@ -116,15 +116,16 @@
 
 struct bsln
 {
-  static const hb_tag_t tableTag = HB_AAT_TAG_bsln;
+  enum { tableTag = HB_AAT_TAG_bsln };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!(c->check_struct (this) && defaultBaseline < 32)))
       return_trace (false);
 
-    switch (format) {
+    switch (format)
+    {
     case 0: return_trace (parts.format0.sanitize (c));
     case 1: return_trace (parts.format1.sanitize (c));
     case 2: return_trace (parts.format2.sanitize (c));
diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh
index a99ccaf..95d6b31 100644
--- a/src/hb-aat-layout-common.hh
+++ b/src/hb-aat-layout-common.hh
@@ -28,6 +28,7 @@
 #define HB_AAT_LAYOUT_COMMON_HH
 
 #include "hb-aat-layout.hh"
+#include "hb-open-type.hh"
 
 
 namespace AAT {
@@ -47,39 +48,50 @@
   friend struct Lookup<T>;
 
   private:
-  inline const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  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
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (arrayZ.sanitize (c, c->get_num_glyphs ()));
   }
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (arrayZ.sanitize (c, c->get_num_glyphs (), base));
+  }
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 0 */
   UnsizedArrayOf<T>
 		arrayZ;		/* Array of lookup values, indexed by glyph index. */
   public:
-  DEFINE_SIZE_ARRAY (2, arrayZ);
+  DEFINE_SIZE_UNBOUNDED (2);
 };
 
 
 template <typename T>
 struct LookupSegmentSingle
 {
-  inline int cmp (hb_codepoint_t g) const {
-    return g < first ? -1 : g <= last ? 0 : +1 ;
-  }
+  enum { TerminationWordCount = 2 };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  int cmp (hb_codepoint_t g) const
+  { return g < first ? -1 : g <= last ? 0 : +1 ; }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && value.sanitize (c));
   }
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && value.sanitize (c, base));
+  }
 
   GlyphID	last;		/* Last GlyphID in this segment */
   GlyphID	first;		/* First GlyphID in this segment */
@@ -94,17 +106,22 @@
   friend struct Lookup<T>;
 
   private:
-  inline const T* get_value (hb_codepoint_t glyph_id) const
+  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
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (segments.sanitize (c));
   }
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (segments.sanitize (c, base));
+  }
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 2 */
@@ -119,22 +136,31 @@
 template <typename T>
 struct LookupSegmentArray
 {
-  inline const T* get_value (hb_codepoint_t glyph_id, const void *base) const
+  enum { TerminationWordCount = 2 };
+
+  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 ;
-  }
+  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
+  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));
   }
+  template <typename T2>
+  bool sanitize (hb_sanitize_context_t *c, const void *base, T2 user_data) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  first <= last &&
+		  valuesZ.sanitize (c, base, last - first + 1, user_data));
+  }
 
   GlyphID	last;		/* Last GlyphID in this segment */
   GlyphID	first;		/* First GlyphID in this segment */
@@ -151,17 +177,22 @@
   friend struct Lookup<T>;
 
   private:
-  inline const T* get_value (hb_codepoint_t glyph_id) const
+  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
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (segments.sanitize (c, this));
   }
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (segments.sanitize (c, this, base));
+  }
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 4 */
@@ -176,13 +207,20 @@
 template <typename T>
 struct LookupSingle
 {
-  inline int cmp (hb_codepoint_t g) const { return glyph.cmp (g); }
+  enum { TerminationWordCount = 1 };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  int cmp (hb_codepoint_t g) const { return glyph.cmp (g); }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && value.sanitize (c));
   }
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && value.sanitize (c, base));
+  }
 
   GlyphID	glyph;		/* Last GlyphID */
   T		value;		/* The lookup value (only one) */
@@ -196,17 +234,22 @@
   friend struct Lookup<T>;
 
   private:
-  inline const T* get_value (hb_codepoint_t glyph_id) const
+  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
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (entries.sanitize (c));
   }
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (entries.sanitize (c, base));
+  }
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 6 */
@@ -222,17 +265,22 @@
   friend struct Lookup<T>;
 
   private:
-  inline const T* get_value (hb_codepoint_t glyph_id) const
+  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
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount));
   }
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount, base));
+  }
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 8 */
@@ -252,7 +300,7 @@
   friend struct Lookup<T>;
 
   private:
-  inline const typename T::type get_value_or_null (hb_codepoint_t glyph_id) const
+  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);
@@ -267,7 +315,7 @@
     return v;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -291,7 +339,7 @@
 template <typename T>
 struct Lookup
 {
-  inline const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  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);
@@ -303,7 +351,7 @@
     }
   }
 
-  inline const typename T::type get_value_or_null (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  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. */
@@ -314,7 +362,15 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  typename T::type get_class (hb_codepoint_t glyph_id,
+			      unsigned int num_glyphs,
+			      unsigned int outOfRange) const
+  {
+    const T *v = get_value (glyph_id, num_glyphs);
+    return v ? *v : outOfRange;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
@@ -328,6 +384,20 @@
     default:return_trace (true);
     }
   }
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format) {
+    case 0: return_trace (u.format0.sanitize (c, base));
+    case 2: return_trace (u.format2.sanitize (c, base));
+    case 4: return_trace (u.format4.sanitize (c, base));
+    case 6: return_trace (u.format6.sanitize (c, base));
+    case 8: return_trace (u.format8.sanitize (c, base));
+    case 10: return_trace (false); /* No need to support format10 apparently */
+    default:return_trace (true);
+    }
+  }
 
   protected:
   union {
@@ -349,34 +419,37 @@
 /* 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);
-}
+/*static*/ inline const AAT::Lookup<OT::HBUINT16>& Null<AAT::Lookup<OT::HBUINT16> > ()
+{ 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);
-}
+/*static*/ inline const AAT::Lookup<OT::HBUINT32>& Null<AAT::Lookup<OT::HBUINT32> > ()
+{ 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);
-}
+/*static*/ inline const AAT::Lookup<OT::Offset<OT::HBUINT16, false> >& Null<AAT::Lookup<OT::Offset<OT::HBUINT16, false> > > ()
+{ return *reinterpret_cast<const AAT::Lookup<OT::Offset<OT::HBUINT16, false> > *> (_hb_Null_AAT_Lookup); }
 namespace AAT {
 
+enum { DELETED_GLYPH = 0xFFFF };
 
 /*
- * Extended State Table
+ * (Extended) State Table
  */
 
 template <typename T>
 struct Entry
 {
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  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. */
+     * if I need to remove that as well.
+     *
+     * HOWEVER! Because we are a template, our DEFINE_SIZE_STATIC
+     * assertion wouldn't be checked, hence the line below. */
+    static_assert (T::static_size, "");
+
     return_trace (c->check_struct (this));
   }
 
@@ -393,7 +466,7 @@
 template <>
 struct Entry<void>
 {
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  bool sanitize (hb_sanitize_context_t *c, unsigned int count /*XXX Unused?*/) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -406,9 +479,13 @@
   DEFINE_SIZE_STATIC (4);
 };
 
-template <typename Extra>
+template <typename Types, typename Extra>
 struct StateTable
 {
+  typedef typename Types::HBUINT HBUINT;
+  typedef typename Types::HBUSHORT HBUSHORT;
+  typedef typename Types::ClassTypeNarrow ClassType;
+
   enum State
   {
     STATE_START_OF_TEXT = 0,
@@ -422,62 +499,109 @@
     CLASS_END_OF_LINE = 3,
   };
 
-  inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  int new_state (unsigned int newState) const
+  { return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; }
+
+  unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
   {
-    const HBUINT16 *v = (this+classTable).get_value (glyph_id, num_glyphs);
-    return v ? (unsigned) *v : (unsigned) CLASS_OUT_OF_BOUNDS;
+    if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH;
+    return (this+classTable).get_class (glyph_id, num_glyphs, 1);
   }
 
-  inline const Entry<Extra> *get_entries () const
-  {
-    return (this+entryTable).arrayZ;
-  }
+  const Entry<Extra> *get_entries () const
+  { return (this+entryTable).arrayZ; }
 
-  inline const Entry<Extra> *get_entryZ (unsigned int state, unsigned int klass) const
+  const Entry<Extra> *get_entryZ (int state, unsigned int klass) const
   {
     if (unlikely (klass >= nClasses)) return nullptr;
 
-    const HBUINT16 *states = (this+stateArrayTable).arrayZ;
+    const HBUSHORT *states = (this+stateArrayTable).arrayZ;
     const Entry<Extra> *entries = (this+entryTable).arrayZ;
 
     unsigned int entry = states[state * nClasses + klass];
+    DEBUG_MSG (APPLY, nullptr, "e%u", entry);
 
     return &entries[entry];
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c,
-			unsigned int *num_entries_out = nullptr) const
+  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 HBUSHORT *states = (this+stateArrayTable).arrayZ;
     const Entry<Extra> *entries = (this+entryTable).arrayZ;
 
     unsigned int num_classes = nClasses;
+    if (unlikely (hb_unsigned_mul_overflows (num_classes, states[0].static_size)))
+      return_trace (false);
+    unsigned int row_stride = num_classes * states[0].static_size;
 
-    unsigned int num_states = 1;
+    /* Apple 'kern' table has this peculiarity:
+     *
+     * "Because the stateTableOffset in the state table header is (strictly
+     * speaking) redundant, some 'kern' tables use it to record an initial
+     * state where that should not be StartOfText. To determine if this is
+     * done, calculate what the stateTableOffset should be. If it's different
+     * from the actual stateTableOffset, use it as the initial state."
+     *
+     * We implement this by calling the initial state zero, but allow *negative*
+     * states if the start state indeed was not the first state.  Since the code
+     * is shared, this will also apply to 'mort' table.  The 'kerx' / 'morx'
+     * tables are not affected since those address states by index, not offset.
+     */
+
+    int min_state = 0;
+    int max_state = 0;
     unsigned int num_entries = 0;
 
-    unsigned int state = 0;
+    int state_pos = 0;
+    int state_neg = 0;
     unsigned int entry = 0;
-    while (state < num_states)
+    while (min_state < state_neg || state_pos <= max_state)
     {
-      if (unlikely (hb_unsigned_mul_overflows (num_classes, states[0].static_size)))
-	return_trace (false);
+      if (min_state < state_neg)
+      {
+	/* Negative states. */
+	if (unlikely (hb_unsigned_mul_overflows (min_state, num_classes)))
+	  return_trace (false);
+	if (unlikely (!c->check_range (&states[min_state * num_classes],
+				       -min_state,
+				       row_stride)))
+	  return_trace (false);
+	if ((c->max_ops -= state_neg - min_state) < 0)
+	  return_trace (false);
+	{ /* Sweep new states. */
+	  const HBUSHORT *stop = &states[min_state * num_classes];
+	  if (unlikely (stop > states))
+	    return_trace (false);
+	  for (const HBUSHORT *p = states; stop < p; p--)
+	    num_entries = MAX<unsigned int> (num_entries, *(p - 1) + 1);
+	  state_neg = min_state;
+	}
+      }
 
-      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 (state_pos <= max_state)
+      {
+	/* Positive states. */
+	if (unlikely (!c->check_range (states,
+				       max_state + 1,
+				       row_stride)))
+	  return_trace (false);
+	if ((c->max_ops -= max_state - state_pos + 1) < 0)
+	  return_trace (false);
+	{ /* Sweep new states. */
+	  if (unlikely (hb_unsigned_mul_overflows ((max_state + 1), num_classes)))
+	    return_trace (false);
+	  const HBUSHORT *stop = &states[(max_state + 1) * num_classes];
+	  if (unlikely (stop < states))
+	    return_trace (false);
+	  for (const HBUSHORT *p = &states[state_pos * num_classes]; p < stop; p++)
+	    num_entries = MAX<unsigned int> (num_entries, *p + 1);
+	  state_pos = max_state + 1;
+	}
       }
 
       if (unlikely (!c->check_array (entries, num_entries)))
@@ -487,7 +611,11 @@
       { /* 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);
+	{
+	  int newState = new_state (p->newState);
+	  min_state = MIN (min_state, newState);
+	  max_state = MAX (max_state, newState);
+	}
 	entry = num_entries;
       }
     }
@@ -499,42 +627,131 @@
   }
 
   protected:
-  HBUINT32	nClasses;	/* Number of classes, which is the number of indices
+  HBUINT	nClasses;	/* Number of classes, which is the number of indices
 				 * in a single line in the state array. */
-  LOffsetTo<Lookup<HBUINT16>, false>
+  OffsetTo<ClassType, HBUINT, false>
 		classTable;	/* Offset to the class table. */
-  LOffsetTo<UnsizedArrayOf<HBUINT16>, false>
+  OffsetTo<UnsizedArrayOf<HBUSHORT>, HBUINT, false>
 		stateArrayTable;/* Offset to the state array. */
-  LOffsetTo<UnsizedArrayOf<Entry<Extra> >, false>
+  OffsetTo<UnsizedArrayOf<Entry<Extra> >, HBUINT, false>
 		entryTable;	/* Offset to the entry array. */
 
   public:
-  DEFINE_SIZE_STATIC (16);
+  DEFINE_SIZE_STATIC (4 * sizeof (HBUINT));
 };
 
-template <typename EntryData>
+template <typename HBUCHAR>
+struct ClassTable
+{
+  unsigned int get_class (hb_codepoint_t glyph_id, unsigned int outOfRange) const
+  {
+    unsigned int i = glyph_id - firstGlyph;
+    return i >= classArray.len ? outOfRange : classArray.arrayZ[i];
+  }
+  unsigned int get_class (hb_codepoint_t glyph_id,
+			  unsigned int num_glyphs HB_UNUSED,
+			  unsigned int outOfRange) const
+  {
+    return get_class (glyph_id, outOfRange);
+  }
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && classArray.sanitize (c));
+  }
+  protected:
+  GlyphID		firstGlyph;	/* First glyph index included in the trimmed array. */
+  ArrayOf<HBUCHAR>	classArray;	/* The class codes (indexed by glyph index minus
+					 * firstGlyph). */
+  public:
+  DEFINE_SIZE_ARRAY (4, classArray);
+};
+
+struct ObsoleteTypes
+{
+  enum { extended = false };
+  typedef HBUINT16 HBUINT;
+  typedef HBUINT8 HBUSHORT;
+  typedef ClassTable<HBUINT8> ClassTypeNarrow;
+  typedef ClassTable<HBUINT16> ClassTypeWide;
+
+  template <typename T>
+  static unsigned int offsetToIndex (unsigned int offset,
+				     const void *base,
+				     const T *array)
+  {
+    return (offset - ((const char *) array - (const char *) base)) / sizeof (T);
+  }
+  template <typename T>
+  static unsigned int byteOffsetToIndex (unsigned int offset,
+					 const void *base,
+					 const T *array)
+  {
+    return offsetToIndex (offset, base, array);
+  }
+  template <typename T>
+  static unsigned int wordOffsetToIndex (unsigned int offset,
+					 const void *base,
+					 const T *array)
+  {
+    return offsetToIndex (2 * offset, base, array);
+  }
+};
+struct ExtendedTypes
+{
+  enum { extended = true };
+  typedef HBUINT32 HBUINT;
+  typedef HBUINT16 HBUSHORT;
+  typedef Lookup<HBUINT16> ClassTypeNarrow;
+  typedef Lookup<HBUINT16> ClassTypeWide;
+
+  template <typename T>
+  static unsigned int offsetToIndex (unsigned int offset,
+				     const void *base,
+				     const T *array)
+  {
+    return offset;
+  }
+  template <typename T>
+  static unsigned int byteOffsetToIndex (unsigned int offset,
+					 const void *base,
+					 const T *array)
+  {
+    return offset / 2;
+  }
+  template <typename T>
+  static unsigned int wordOffsetToIndex (unsigned int offset,
+					 const void *base,
+					 const T *array)
+  {
+    return offset;
+  }
+};
+
+template <typename Types, typename EntryData>
 struct StateTableDriver
 {
-  inline StateTableDriver (const StateTable<EntryData> &machine_,
-			   hb_buffer_t *buffer_,
-			   hb_face_t *face_) :
+  StateTableDriver (const StateTable<Types, 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)
+  void drive (context_t *c)
   {
     if (!c->in_place)
       buffer->clear_output ();
 
-    unsigned int state = StateTable<EntryData>::STATE_START_OF_TEXT;
+    int state = StateTable<Types, EntryData>::STATE_START_OF_TEXT;
     bool last_was_dont_advance = false;
-    for (buffer->idx = 0;;)
+    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;
+			   (unsigned) StateTable<Types, EntryData>::CLASS_END_OF_TEXT;
+      DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
       const Entry<EntryData> *entry = machine.get_entryZ (state, klass);
       if (unlikely (!entry))
 	break;
@@ -548,7 +765,7 @@
 	/* 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->newState == StateTable<Types, EntryData>::STATE_START_OF_TEXT &&
 	      entry->flags == context_t::DontAdvance))
 	  buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
       }
@@ -562,32 +779,30 @@
       }
 
       if (unlikely (!c->transition (this, entry)))
-        break;
-
-      if (unlikely (!buffer->successful)) return;
+	break;
 
       last_was_dont_advance = (entry->flags & context_t::DontAdvance) && buffer->max_ops-- > 0;
 
-      state = entry->newState;
+      state = machine.new_state (entry->newState);
+      DEBUG_MSG (APPLY, nullptr, "s%d", state);
 
       if (buffer->idx == buffer->len)
-        break;
+	break;
 
       if (!last_was_dont_advance)
-        buffer->next_glyph ();
+	buffer->next_glyph ();
     }
 
     if (!c->in_place)
     {
       for (; buffer->successful && buffer->idx < buffer->len;)
 	buffer->next_glyph ();
-      if (likely (buffer->successful))
-	buffer->swap_buffers ();
+      buffer->swap_buffers ();
     }
   }
 
   public:
-  const StateTable<EntryData> &machine;
+  const StateTable<Types, EntryData> &machine;
   hb_buffer_t *buffer;
   unsigned int num_glyphs;
 };
@@ -598,47 +813,34 @@
 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"; }
+  const char *get_name () { 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; }
+  return_t dispatch (const T &obj) { return obj.apply (this); }
+  static return_t default_return_value () { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
 
-  hb_ot_shape_plan_t *plan;
+  const 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 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);
-  }
+  HB_INTERNAL hb_aat_apply_context_t (const 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)));
 
-  inline void set_lookup_index (unsigned int i) { lookup_index = i; }
+  HB_INTERNAL ~hb_aat_apply_context_t ();
 
-  inline ~hb_aat_apply_context_t (void)
-  {
-    sanitizer.end_processing ();
-  }
+  HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_, const char *ankr_end_);
+
+  void set_lookup_index (unsigned int i) { lookup_index = i; }
 };
 
 
diff --git a/src/hb-aat-layout-feat-table.hh b/src/hb-aat-layout-feat-table.hh
index b670caa..63c69a2 100644
--- a/src/hb-aat-layout-feat-table.hh
+++ b/src/hb-aat-layout-feat-table.hh
@@ -39,7 +39,28 @@
 
 struct SettingName
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  friend struct FeatureName;
+
+  int cmp (hb_aat_layout_feature_selector_t key) const
+  { return (int) key - (int) setting; }
+
+  hb_aat_layout_feature_selector_t get_selector () const
+  { return (hb_aat_layout_feature_selector_t) (unsigned) setting; }
+
+  void get_info (hb_aat_layout_feature_selector_info_t *s,
+			hb_aat_layout_feature_selector_t default_selector) const
+  {
+    s->name_id = nameIndex;
+
+    s->enable = (hb_aat_layout_feature_selector_t) (unsigned int) setting;
+    s->disable = default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID ?
+		 (hb_aat_layout_feature_selector_t) (s->enable + 1) :
+		 default_selector;
+
+    s->reserved = 0;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this)));
@@ -51,35 +72,76 @@
   public:
   DEFINE_SIZE_STATIC (4);
 };
+DECLARE_NULL_NAMESPACE_BYTES (AAT, SettingName);
+
+struct feat;
 
 struct FeatureName
 {
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) &&
-			  (base+settingTable).sanitize (c, nSettings)));
-  }
+  int cmp (hb_aat_layout_feature_type_t key) const
+  { return (int) key - (int) feature; }
 
   enum {
-    Exclusive = 0x8000,		/* If set, the feature settings are mutually exclusive. */
-    NotDefault = 0x4000,	/* If clear, then the setting with an index of 0 in
+    Exclusive	= 0x8000,	/* If set, the feature settings are mutually exclusive. */
+    NotDefault	= 0x4000,	/* If clear, then the setting with an index of 0 in
 				 * the setting name array for this feature should
 				 * be taken as the default for the feature
 				 * (if one is required). If set, then bits 0-15 of this
 				 * featureFlags field contain the index of the setting
 				 * which is to be taken as the default. */
-    IndexMask = 0x00FF		/* If bits 30 and 31 are set, then these sixteen bits
+    IndexMask	= 0x00FF	/* If bits 30 and 31 are set, then these sixteen bits
 				 * indicate the index of the setting in the setting name
 				 * array for this feature which should be taken
 				 * as the default. */
   };
 
+  unsigned int get_selector_infos (unsigned int                           start_offset,
+				   unsigned int                          *selectors_count, /* IN/OUT.  May be NULL. */
+				   hb_aat_layout_feature_selector_info_t *selectors,       /* OUT.     May be NULL. */
+				   unsigned int                          *pdefault_index,  /* OUT.     May be NULL. */
+				   const void *base) const
+  {
+    hb_array_t< const SettingName> settings_table = (base+settingTableZ).as_array (nSettings);
+
+    static_assert (Index::NOT_FOUND_INDEX == HB_AAT_LAYOUT_NO_SELECTOR_INDEX, "");
+
+    hb_aat_layout_feature_selector_t default_selector = HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID;
+    unsigned int default_index = Index::NOT_FOUND_INDEX;
+    if (featureFlags & Exclusive)
+    {
+      default_index = (featureFlags & NotDefault) ? featureFlags & IndexMask : 0;
+      default_selector = settings_table[default_index].get_selector ();
+    }
+    if (pdefault_index)
+      *pdefault_index = default_index;
+
+    if (selectors_count)
+    {
+      hb_array_t<const SettingName> arr = settings_table.sub_array (start_offset, selectors_count);
+      unsigned int count = arr.len;
+      for (unsigned int i = 0; i < count; i++)
+        settings_table[start_offset + i].get_info (&selectors[i], default_selector);
+    }
+    return settings_table.len;
+  }
+
+  hb_aat_layout_feature_type_t get_feature_type () const
+  { return (hb_aat_layout_feature_type_t) (unsigned int) feature; }
+
+  hb_ot_name_id_t get_feature_name_id () const { return nameIndex; }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  (base+settingTableZ).sanitize (c, nSettings)));
+  }
+
   protected:
   HBUINT16	feature;	/* Feature type. */
   HBUINT16	nSettings;	/* The number of records in the setting name array. */
   LOffsetTo<UnsizedArrayOf<SettingName>, false>
-		settingTable;	/* Offset in bytes from the beginning of this table to
+		settingTableZ;	/* 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
 				 * exclusivity value, as described below. */
@@ -93,13 +155,49 @@
 
 struct feat
 {
-  static const hb_tag_t tableTag = HB_AAT_TAG_feat;
+  enum { tableTag = HB_AAT_TAG_feat };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool has_data () const { return version.to_int (); }
+
+  unsigned int get_feature_types (unsigned int                  start_offset,
+				  unsigned int                 *count,
+				  hb_aat_layout_feature_type_t *features) const
+  {
+    unsigned int feature_count = featureNameCount;
+    if (count && *count)
+    {
+      unsigned int len = MIN (feature_count - start_offset, *count);
+      for (unsigned int i = 0; i < len; i++)
+	features[i] = namesZ[i + start_offset].get_feature_type ();
+      *count = len;
+    }
+    return featureNameCount;
+  }
+
+  const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const
+  {
+    return namesZ.bsearch (featureNameCount, feature_type);
+  }
+
+  hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const
+  { return get_feature (feature).get_feature_name_id (); }
+
+  unsigned int get_selector_infos (hb_aat_layout_feature_type_t           feature_type,
+				   unsigned int                           start_offset,
+				   unsigned int                          *selectors_count, /* IN/OUT.  May be NULL. */
+				   hb_aat_layout_feature_selector_info_t *selectors,       /* OUT.     May be NULL. */
+				   unsigned int                          *default_index    /* OUT.     May be NULL. */) const
+  {
+    return get_feature (feature_type).get_selector_infos (start_offset, selectors_count, selectors,
+							  default_index, this);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
-			  names.sanitize (c, featureNameCount, this)));
+			  version.major == 1 &&
+			  namesZ.sanitize (c, featureNameCount, this)));
   }
 
   protected:
@@ -109,8 +207,8 @@
 				/* The number of entries in the feature name array. */
   HBUINT16	reserved1;	/* Reserved (set to zero). */
   HBUINT32	reserved2;	/* Reserved (set to zero). */
-  UnsizedArrayOf<FeatureName>
-		names;		/* The feature name array. */
+  SortedUnsizedArrayOf<FeatureName>
+		namesZ;		/* The feature name array. */
   public:
   DEFINE_SIZE_STATIC (24);
 };
diff --git a/src/hb-aat-layout-just-table.hh b/src/hb-aat-layout-just-table.hh
new file mode 100644
index 0000000..d39945a
--- /dev/null
+++ b/src/hb-aat-layout-just-table.hh
@@ -0,0 +1,417 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_AAT_LAYOUT_JUST_TABLE_HH
+#define HB_AAT_LAYOUT_JUST_TABLE_HH
+
+#include "hb-aat-layout-common.hh"
+#include "hb-ot-layout.hh"
+#include "hb-open-type.hh"
+
+#include "hb-aat-layout-morx-table.hh"
+
+/*
+ * just -- Justification
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6just.html
+ */
+#define HB_AAT_TAG_just HB_TAG('j','u','s','t')
+
+
+namespace AAT {
+
+using namespace OT;
+
+
+struct ActionSubrecordHeader
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  HBUINT16 	actionClass; 	/* The JustClass value associated with this
+				 * ActionSubrecord. */
+  HBUINT16 	actionType; 	/* The type of postcompensation action. */
+  HBUINT16 	actionLength;	/* Length of this ActionSubrecord record, which
+				 * must be a multiple of 4. */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct DecompositionAction
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  ActionSubrecordHeader
+		header;
+  Fixed		lowerLimit; 	/* If the distance factor is less than this value,
+				 * then the ligature is decomposed. */
+  Fixed		upperLimit; 	/* If the distance factor is greater than this value,
+				 * then the ligature is decomposed. */
+  HBUINT16 	order;		/* Numerical order in which this ligature will
+				 * be decomposed; you may want infrequent ligatures
+				 * to decompose before more frequent ones. The ligatures
+				 * on the line of text will decompose in increasing
+				 * value of this field. */
+  ArrayOf<HBUINT16>
+		decomposedglyphs;
+				/* Number of 16-bit glyph indexes that follow;
+				 * the ligature will be decomposed into these glyphs.
+				 *
+				 * Array of decomposed glyphs. */
+  public:
+  DEFINE_SIZE_ARRAY (18, decomposedglyphs);
+};
+
+struct UnconditionalAddGlyphAction
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  ActionSubrecordHeader
+		header;
+  GlyphID	addGlyph;	/* Glyph that should be added if the distance factor
+				 * is growing. */
+
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct ConditionalAddGlyphAction
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  protected:
+  ActionSubrecordHeader
+		header;
+  Fixed 	substThreshold; /* Distance growth factor (in ems) at which
+				 * this glyph is replaced and the growth factor
+				 * recalculated. */
+  GlyphID 	addGlyph; 	/* Glyph to be added as kashida. If this value is
+				 * 0xFFFF, no extra glyph will be added. Note that
+				 * generally when a glyph is added, justification
+				 * will need to be redone. */
+  GlyphID 	substGlyph; 	/* Glyph to be substituted for this glyph if the
+				 * growth factor equals or exceeds the value of
+				 * substThreshold. */
+  public:
+  DEFINE_SIZE_STATIC (14);
+};
+
+struct DuctileGlyphAction
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  protected:
+  ActionSubrecordHeader
+		header;
+  HBUINT32 	variationAxis;	/* The 4-byte tag identifying the ductile axis.
+				 * This would normally be 0x64756374 ('duct'),
+				 * but you may use any axis the font contains. */
+  Fixed 	minimumLimit; 	/* The lowest value for the ductility axis tha
+				 * still yields an acceptable appearance. Normally
+				 * this will be 1.0. */
+  Fixed 	noStretchValue; /* This is the default value that corresponds to
+				 * no change in appearance. Normally, this will
+				 * be 1.0. */
+  Fixed 	maximumLimit; 	/* The highest value for the ductility axis that
+				 * still yields an acceptable appearance. */
+  public:
+  DEFINE_SIZE_STATIC (22);
+};
+
+struct RepeatedAddGlyphAction
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  protected:
+  ActionSubrecordHeader
+		header;
+  HBUINT16 	flags;		/* Currently unused; set to 0. */
+  GlyphID 	glyph;		/* Glyph that should be added if the distance factor
+				 * is growing. */
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+struct ActionSubrecord
+{
+  unsigned int get_length () const { return u.header.actionLength; }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+
+    switch (u.header.actionType)
+    {
+    case 0:  return_trace (u.decompositionAction.sanitize (c));
+    case 1:  return_trace (u.unconditionalAddGlyphAction.sanitize (c));
+    case 2:  return_trace (u.conditionalAddGlyphAction.sanitize (c));
+    // case 3: return_trace (u.stretchGlyphAction.sanitize (c));
+    case 4:  return_trace (u.decompositionAction.sanitize (c));
+    case 5:  return_trace (u.decompositionAction.sanitize (c));
+    default: return_trace (true);
+    }
+  }
+
+  protected:
+  union	{
+  ActionSubrecordHeader		header;
+  DecompositionAction		decompositionAction;
+  UnconditionalAddGlyphAction	unconditionalAddGlyphAction;
+  ConditionalAddGlyphAction	conditionalAddGlyphAction;
+  /* StretchGlyphAction stretchGlyphAction; -- Not supported by CoreText */
+  DuctileGlyphAction		ductileGlyphAction;
+  RepeatedAddGlyphAction	repeatedAddGlyphAction;
+  } u;				/* Data. The format of this data depends on
+				 * the value of the actionType field. */
+  public:
+  DEFINE_SIZE_UNION (6, header);
+};
+
+struct PostcompensationActionChain
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+
+    unsigned int offset = min_size;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      const ActionSubrecord& subrecord = StructAtOffset<ActionSubrecord> (this, offset);
+      if (unlikely (!subrecord.sanitize (c))) return_trace (false);
+      offset += subrecord.get_length ();
+    }
+
+    return_trace (true);
+  }
+
+  protected:
+  HBUINT32	count;
+
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct JustWidthDeltaEntry
+{
+  enum Flags
+  {
+    Reserved1		=0xE000,/* Reserved. You should set these bits to zero. */
+    UnlimiteGap		=0x1000,/* The glyph can take unlimited gap. When this
+				 * glyph participates in the justification process,
+				 * it and any other glyphs on the line having this
+				 * bit set absorb all the remaining gap. */
+    Reserved2		=0x0FF0,/* Reserved. You should set these bits to zero. */
+    Priority		=0x000F /* The justification priority of the glyph. */
+  };
+
+  enum Priority
+  {
+    Kashida		= 0,	/* Kashida priority. This is the highest priority
+				 * during justification. */
+    Whitespace		= 1,	/* Whitespace priority. Any whitespace glyphs (as
+				 * identified in the glyph properties table) will
+				 * get this priority. */
+    InterCharacter	= 2,	/* Inter-character priority. Give this to any
+				 * remaining glyphs. */
+    NullPriority	= 3	/* Null priority. You should set this priority for
+				 * glyphs that only participate in justification
+				 * after the above priorities. Normally all glyphs
+				 * have one of the previous three values. If you
+				 * don't want a glyph to participate in justification,
+				 * and you don't want to set its factors to zero,
+				 * you may instead assign it to the null priority. */
+  };
+
+  protected:
+  Fixed		beforeGrowLimit;/* The ratio by which the advance width of the
+				 * glyph is permitted to grow on the left or top side. */
+  Fixed		beforeShrinkLimit;
+				/* The ratio by which the advance width of the
+				 * glyph is permitted to shrink on the left or top side. */
+  Fixed		afterGrowLimit;	/* The ratio by which the advance width of the glyph
+				 * is permitted to shrink on the left or top side. */
+  Fixed		afterShrinkLimit;
+				/* The ratio by which the advance width of the glyph
+				 * is at most permitted to shrink on the right or
+				 * bottom side. */
+  HBUINT16	growFlags;	/* Flags controlling the grow case. */
+  HBUINT16	shrinkFlags;	/* Flags controlling the shrink case. */
+
+  public:
+  DEFINE_SIZE_STATIC (20);
+};
+
+struct WidthDeltaPair
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  protected:
+  HBUINT32	justClass;	/* The justification category associated
+				 * with the wdRecord field. Only 7 bits of
+				 * this field are used. (The other bits are
+				 * used as padding to guarantee longword
+				 * alignment of the following record). */
+  JustWidthDeltaEntry
+		wdRecord;	/* The actual width delta record. */
+
+  public:
+  DEFINE_SIZE_STATIC (24);
+};
+  
+typedef OT::LArrayOf<WidthDeltaPair> WidthDeltaCluster;
+
+struct JustificationCategory
+{
+  typedef void EntryData;
+
+  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. */
+    MarkCategory	=0x3F80,/* The justification category for the marked
+				 * glyph if nonzero. */
+    CurrentCategory	=0x007F /* The justification category for the current
+				 * glyph if nonzero. */
+  };
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  morphHeader.sanitize (c) &&
+			  stHeader.sanitize (c)));
+  }
+
+  protected:
+  ChainSubtable<ObsoleteTypes>
+		morphHeader;	/* Metamorphosis-style subtable header. */
+  StateTable<ObsoleteTypes, EntryData>
+		stHeader;	/* The justification insertion state table header */
+  public:
+  DEFINE_SIZE_STATIC (30);
+};
+
+struct JustificationHeader
+{
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  justClassTable.sanitize (c, base, base) &&
+			  wdcTable.sanitize (c, base) &&
+			  pcTable.sanitize (c, base) &&
+			  lookupTable.sanitize (c, base)));
+  }
+
+  protected:
+  OffsetTo<JustificationCategory>
+		justClassTable;	/* Offset to the justification category state table. */
+  OffsetTo<WidthDeltaCluster>
+  		wdcTable;	/* Offset from start of justification table to start
+				 * of the subtable containing the width delta factors
+				 * for the glyphs in your font.
+				 *
+				 * The width delta clusters table. */
+  OffsetTo<PostcompensationActionChain>
+		pcTable;	/* Offset from start of justification table to start
+				 * of postcompensation subtable (set to zero if none).
+				 *
+				 * The postcompensation subtable, if present in the font. */
+  Lookup<OffsetTo<WidthDeltaCluster> >
+  		lookupTable;	/* Lookup table associating glyphs with width delta
+				 * clusters. See the description of Width Delta Clusters
+				 * table for details on how to interpret the lookup values. */
+
+  public:
+  DEFINE_SIZE_MIN (8);
+};
+
+struct just
+{
+  enum { tableTag = HB_AAT_TAG_just };
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+
+    return_trace (likely (c->check_struct (this) &&
+			  version.major == 1 &&
+			  horizData.sanitize (c, this, this) &&
+			  vertData.sanitize (c, this, this)));
+  }
+
+  protected:
+  FixedVersion<>version;	/* Version of the justification table
+				 * (0x00010000u for version 1.0). */
+  HBUINT16	format; 	/* Format of the justification table (set to 0). */
+  OffsetTo<JustificationHeader>
+		horizData;	/* Byte offset from the start of the justification table
+				 * to the header for tables that contain justification
+				 * information for horizontal text.
+				 * If you are not including this information,
+				 * store 0. */
+  OffsetTo<JustificationHeader>
+		vertData;	/* ditto, vertical */
+
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_JUST_TABLE_HH */
diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index e8eb43b..291f3e3 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -28,10 +28,8 @@
 #ifndef HB_AAT_LAYOUT_KERX_TABLE_HH
 #define HB_AAT_LAYOUT_KERX_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"
+#include "hb-kern.hh"
+#include "hb-aat-layout-ankr-table.hh"
 
 /*
  * kerx -- Extended Kerning
@@ -51,72 +49,118 @@
 	       const void *base,
 	       hb_aat_apply_context_t *c)
 {
-  if (likely (!tupleCount)) return value;
+  if (likely (!tupleCount || !c)) return value;
 
   unsigned int offset = value;
   const FWORD *pv = &StructAtOffset<FWORD> (base, offset);
-  if (unlikely (!pv->sanitize (&c->sanitizer))) return 0;
+  if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0;
   return *pv;
 }
 
 
-struct KerxSubTableHeader
+struct hb_glyph_pair_t
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
-  }
-
-  public:
-  HBUINT32	length;
-  HBUINT32	coverage;
-  HBUINT32	tupleCount;
-  public:
-  DEFINE_SIZE_STATIC (12);
+  hb_codepoint_t left;
+  hb_codepoint_t right;
 };
 
-struct KerxSubTableFormat0
+struct KernPair
 {
-  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+  int get_kerning () const { return value; }
+
+  int cmp (const hb_glyph_pair_t &o) const
   {
-    if (header.tupleCount) return 0; /* TODO kerxTupleKern */
-    hb_glyph_pair_t pair = {left, right};
-    int i = pairs.bsearch (pair);
-    return i == -1 ? 0 : pairs[i].get_kerning ();
+    int ret = left.cmp (o.left);
+    if (ret) return ret;
+    return right.cmp (o.right);
   }
 
-  inline bool apply (hb_aat_apply_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  GlyphID	left;
+  GlyphID	right;
+  FWORD		value;
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+template <typename KernSubTableHeader>
+struct KerxSubTableFormat0
+{
+  int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
+		   hb_aat_apply_context_t *c = nullptr) const
+  {
+    hb_glyph_pair_t pair = {left, right};
+    int v = pairs.bsearch (pair).get_kerning ();
+    return kerxTupleKern (v, header.tuple_count (), this, c);
+  }
+
+  bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
     if (!c->plan->requested_kerning)
       return false;
 
-    hb_kern_machine_t<KerxSubTableFormat0> machine (*this);
+    if (header.coverage & header.Backwards)
+      return false;
 
+    accelerator_t accel (*this, c);
+    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
 
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  struct accelerator_t
+  {
+    const KerxSubTableFormat0 &table;
+    hb_aat_apply_context_t *c;
+
+    accelerator_t (const KerxSubTableFormat0 &table_,
+		   hb_aat_apply_context_t *c_) :
+		     table (table_), c (c_) {}
+
+    int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+    { return table.get_kerning (left, right, c); }
+  };
+
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) &&
-			  pairs.sanitize (c)));
+    return_trace (likely (pairs.sanitize (c)));
   }
 
   protected:
-  KerxSubTableHeader	header;
-  BinSearchArrayOf<KernPair, HBUINT32>
+  KernSubTableHeader	header;
+  BinSearchArrayOf<KernPair, typename KernSubTableHeader::Types::HBUINT>
 			pairs;	/* Sorted kern records. */
   public:
-  DEFINE_SIZE_ARRAY (28, pairs);
+  DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs);
 };
 
-struct KerxSubTableFormat1
+
+template <bool extended>
+struct Format1Entry;
+
+template <>
+struct Format1Entry<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. */
+  };
+
   struct EntryData
   {
     HBUINT16	kernActionIndex;/* Index into the kerning value array. If
@@ -126,44 +170,78 @@
     DEFINE_SIZE_STATIC (2);
   };
 
+  static bool performAction (const Entry<EntryData> *entry)
+  { return entry->data.kernActionIndex != 0xFFFF; }
+
+  static unsigned int kernActionIndex (const Entry<EntryData> *entry)
+  { return entry->data.kernActionIndex; }
+};
+template <>
+struct Format1Entry<false>
+{
+  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. */
+    Offset		= 0x3FFF,	/* Byte offset from beginning of subtable to the
+					 * value table for the glyphs on the kerning stack. */
+
+    Reset		= 0x0000,	/* Not supported? */
+  };
+
+  typedef void EntryData;
+
+  static bool performAction (const Entry<EntryData> *entry)
+  { return entry->flags & Offset; }
+
+  static unsigned int kernActionIndex (const Entry<EntryData> *entry)
+  { return entry->flags & Offset; }
+};
+
+template <typename KernSubTableHeader>
+struct KerxSubTableFormat1
+{
+  typedef typename KernSubTableHeader::Types Types;
+  typedef typename Types::HBUINT HBUINT;
+
+  typedef Format1Entry<Types::extended> Format1EntryT;
+  typedef typename Format1EntryT::EntryData EntryData;
+
   struct driver_context_t
   {
-    static const bool in_place = true;
-    enum Flags
+    enum { in_place = true };
+    enum
     {
-      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. */
+      DontAdvance	= Format1EntryT::DontAdvance,
     };
 
-    inline driver_context_t (const KerxSubTableFormat1 *table,
-			     hb_aat_apply_context_t *c_) :
+    driver_context_t (const KerxSubTableFormat1 *table_,
+		      hb_aat_apply_context_t *c_) :
 	c (c_),
+	table (table_),
 	/* 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) {}
+	depth (0),
+	crossStream (table->header.coverage & table->header.CrossStream) {}
 
-    inline bool is_actionable (StateTableDriver<EntryData> *driver,
-			       const Entry<EntryData> *entry)
+    bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
+			const Entry<EntryData> *entry)
     {
-      return entry->data.kernActionIndex != 0xFFFF;
+      return Format1EntryT::performAction (entry);
     }
-    inline bool transition (StateTableDriver<EntryData> *driver,
-			    const Entry<EntryData> *entry)
+    bool transition (StateTableDriver<Types, EntryData> *driver,
+		     const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
       unsigned int flags = entry->flags;
 
-      if (flags & Reset)
-      {
+      if (flags & Format1EntryT::Reset)
 	depth = 0;
-      }
 
-      if (flags & Push)
+      if (flags & Format1EntryT::Push)
       {
 	if (likely (depth < ARRAY_LENGTH (stack)))
 	  stack[depth++] = buffer->idx;
@@ -171,34 +249,90 @@
 	  depth = 0; /* Probably not what CoreText does, but better? */
       }
 
-      if (entry->data.kernActionIndex != 0xFFFF)
+      if (Format1EntryT::performAction (entry) && depth)
       {
-	const FWORD *actions = &kernAction[entry->data.kernActionIndex];
-	if (!c->sanitizer.check_array (actions, depth))
+	unsigned int tuple_count = MAX (1u, table->header.tuple_count ());
+
+	unsigned int kern_idx = Format1EntryT::kernActionIndex (entry);
+	kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ);
+	const FWORD *actions = &kernAction[kern_idx];
+	if (!c->sanitizer.check_array (actions, depth, tuple_count))
 	{
 	  depth = 0;
 	  return false;
 	}
 
 	hb_mask_t kern_mask = c->plan->kern_mask;
-	for (unsigned int i = 0; i < depth; i++)
+
+	/* From Apple 'kern' spec:
+	 * "Each pops one glyph from the kerning stack and applies the kerning value to it.
+	 * The end of the list is marked by an odd value... */
+	bool last = false;
+	while (!last && depth)
 	{
-	  /* 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)
+	  unsigned int idx = stack[--depth];
+	  int v = *actions;
+	  actions += tuple_count;
+	  if (idx >= buffer->len) continue;
+
+	  /* "The end of the list is marked by an odd value..." */
+	  last = v & 1;
+	  v &= ~1;
+
+	  hb_glyph_position_t &o = buffer->pos[idx];
+
+	  /* Testing shows that CoreText only applies kern (cross-stream or not)
+	   * if none has been applied by previous subtables.  That is, it does
+	   * NOT seem to accumulate as otherwise implied by specs. */
+
+	  /* The following flag is undocumented in the spec, but described
+	   * in the 'kern' table example. */
+	  if (v == -0x8000)
 	  {
-	    /* 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);
+	    o.attach_type() = ATTACH_TYPE_NONE;
+	    o.attach_chain() = 0;
+	    o.x_offset = o.y_offset = 0;
+	  }
+	  else if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
+	  {
+	    if (crossStream)
+	    {
+	      if (buffer->pos[idx].attach_type() && !buffer->pos[idx].y_offset)
+	      {
+		o.y_offset = c->font->em_scale_y (v);
+		buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+	      }
+	    }
+	    else if (buffer->info[idx].mask & kern_mask)
+	    {
+	      if (!buffer->pos[idx].x_offset)
+	      {
+		buffer->pos[idx].x_advance += c->font->em_scale_x (v);
+		buffer->pos[idx].x_offset += c->font->em_scale_x (v);
+	      }
+	    }
+	  }
+	  else
+	  {
+	    if (crossStream)
+	    {
+	      /* CoreText doesn't do crossStream kerning in vertical.  We do. */
+	      if (buffer->pos[idx].attach_type() && !buffer->pos[idx].x_offset)
+	      {
+		o.x_offset = c->font->em_scale_x (v);
+		buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+	      }
+	    }
+	    else if (buffer->info[idx].mask & kern_mask)
+	    {
+	      if (!buffer->pos[idx].y_offset)
+	      {
+		buffer->pos[idx].y_advance += c->font->em_scale_y (v);
+		buffer->pos[idx].y_offset += c->font->em_scale_y (v);
+	      }
+	    }
 	  }
 	}
-	depth = 0;
       }
 
       return true;
@@ -206,30 +340,30 @@
 
     private:
     hb_aat_apply_context_t *c;
+    const KerxSubTableFormat1 *table;
     const UnsizedArrayOf<FWORD> &kernAction;
     unsigned int stack[8];
     unsigned int depth;
+    bool crossStream;
   };
 
-  inline bool apply (hb_aat_apply_context_t *c) const
+  bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
-    if (!c->plan->requested_kerning)
+    if (!c->plan->requested_kerning &&
+	!(header.coverage & header.CrossStream))
       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);
+    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
     driver.drive (&dc);
 
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* The rest of array sanitizations are done at run-time. */
@@ -238,42 +372,66 @@
   }
 
   protected:
-  KerxSubTableHeader				header;
-  StateTable<EntryData>				machine;
-  LOffsetTo<UnsizedArrayOf<FWORD>, false>	kernAction;
+  KernSubTableHeader				header;
+  StateTable<Types, EntryData>			machine;
+  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT, false>kernAction;
   public:
-  DEFINE_SIZE_STATIC (32);
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT));
 };
 
+template <typename KernSubTableHeader>
 struct KerxSubTableFormat2
 {
-  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
-			  hb_aat_apply_context_t *c) const
+  typedef typename KernSubTableHeader::Types Types;
+  typedef typename Types::HBUINT HBUINT;
+
+  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 ();
-    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);
+    unsigned int l = (this+leftClassTable).get_class (left, num_glyphs, 0);
+    unsigned int r = (this+rightClassTable).get_class (right, num_glyphs, 0);
+
+    const UnsizedArrayOf<FWORD> &arrayZ = this+array;
+    unsigned int kern_idx = l + r;
+    kern_idx = Types::offsetToIndex (kern_idx, this, &arrayZ);
+    const FWORD *v = &arrayZ[kern_idx];
     if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
-    return kerxTupleKern (*v, header.tupleCount, this, c);
+
+    return kerxTupleKern (*v, header.tuple_count (), this, c);
   }
 
-  inline bool apply (hb_aat_apply_context_t *c) const
+  bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
     if (!c->plan->requested_kerning)
       return false;
 
+    if (header.coverage & header.Backwards)
+      return false;
+
     accelerator_t accel (*this, c);
-    hb_kern_machine_t<accelerator_t> machine (accel);
+    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
 
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  struct accelerator_t
+  {
+    const KerxSubTableFormat2 &table;
+    hb_aat_apply_context_t *c;
+
+    accelerator_t (const KerxSubTableFormat2 &table_,
+		   hb_aat_apply_context_t *c_) :
+		     table (table_), c (c_) {}
+
+    int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+    { return table.get_kerning (left, right, c); }
+  };
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
@@ -282,37 +440,27 @@
 			  c->check_range (this, array)));
   }
 
-  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); }
-  };
-
   protected:
-  KerxSubTableHeader	header;
-  HBUINT32		rowWidth;	/* The width, in bytes, of a row in the table. */
-  LOffsetTo<Lookup<HBUINT16>, false>
+  KernSubTableHeader	header;
+  HBUINT		rowWidth;	/* The width, in bytes, of a row in the table. */
+  OffsetTo<typename Types::ClassTypeWide, HBUINT, false>
 			leftClassTable;	/* Offset from beginning of this subtable to
 					 * left-hand class table. */
-  LOffsetTo<Lookup<HBUINT16>, false>
+  OffsetTo<typename Types::ClassTypeWide, HBUINT, false>
 			rightClassTable;/* Offset from beginning of this subtable to
 					 * right-hand class table. */
-  LOffsetTo<UnsizedArrayOf<FWORD>, false>
+  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT, false>
 			 array;		/* Offset from beginning of this subtable to
 					 * the start of the kerning array. */
   public:
-  DEFINE_SIZE_STATIC (28);
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 4 * sizeof (HBUINT));
 };
 
+template <typename KernSubTableHeader>
 struct KerxSubTableFormat4
 {
+  typedef ExtendedTypes Types;
+
   struct EntryData
   {
     HBUINT16	ankrActionIndex;/* Either 0xFFFF (for no action) or the index of
@@ -323,7 +471,7 @@
 
   struct driver_context_t
   {
-    static const bool in_place = true;
+    enum { in_place = true };
     enum Flags
     {
       Mark		= 0x8000,	/* If set, remember this glyph as the marked glyph. */
@@ -341,7 +489,7 @@
 					 * point table. */
     };
 
-    inline driver_context_t (const KerxSubTableFormat4 *table,
+    driver_context_t (const KerxSubTableFormat4 *table,
 			     hb_aat_apply_context_t *c_) :
 	c (c_),
 	action_type ((table->flags & ActionType) >> 30),
@@ -349,16 +497,15 @@
 	mark_set (false),
 	mark (0) {}
 
-    inline bool is_actionable (StateTableDriver<EntryData> *driver,
-			       const Entry<EntryData> *entry)
+    bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
+			const Entry<EntryData> *entry)
     {
       return entry->data.ankrActionIndex != 0xFFFF;
     }
-    inline bool transition (StateTableDriver<EntryData> *driver,
-			    const Entry<EntryData> *entry)
+    bool transition (StateTableDriver<Types, 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)
       {
@@ -400,14 +547,14 @@
 	      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);
+	    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);
@@ -434,7 +581,7 @@
 	buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
       }
 
-      if (flags & Mark)
+      if (entry->flags & Mark)
       {
 	mark_set = true;
 	mark = buffer->idx;
@@ -451,19 +598,19 @@
     unsigned int mark;
   };
 
-  inline bool apply (hb_aat_apply_context_t *c) const
+  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);
+    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
     driver.drive (&dc);
 
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* The rest of array sanitizations are done at run-time. */
@@ -472,13 +619,14 @@
   }
 
   protected:
-  KerxSubTableHeader	header;
-  StateTable<EntryData>	machine;
-  HBUINT32		flags;
+  KernSubTableHeader		header;
+  StateTable<Types, EntryData>	machine;
+  HBUINT32			flags;
   public:
-  DEFINE_SIZE_STATIC (32);
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20);
 };
 
+template <typename KernSubTableHeader>
 struct KerxSubTableFormat6
 {
   enum Flags
@@ -486,15 +634,15 @@
     ValuesAreLong	= 0x00000001,
   };
 
-  inline bool is_long (void) const { return flags & ValuesAreLong; }
+  bool is_long () const { return flags & ValuesAreLong; }
 
-  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
+  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;
+      const typename 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;
@@ -502,35 +650,38 @@
       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);
+      return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
     }
     else
     {
-      const U::Short &t = u.s;
+      const typename 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);
+      return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
     }
   }
 
-  inline bool apply (hb_aat_apply_context_t *c) const
+  bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
     if (!c->plan->requested_kerning)
       return false;
 
+    if (header.coverage & header.Backwards)
+      return false;
+
     accelerator_t accel (*this, c);
-    hb_kern_machine_t<accelerator_t> machine (accel);
+    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
 
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
@@ -544,7 +695,7 @@
 			     u.s.columnIndexTable.sanitize (c, this) &&
 			     c->check_range (this, u.s.array)
 			   )) &&
-			  (header.tupleCount == 0 ||
+			  (header.tuple_count () == 0 ||
 			   c->check_range (this, vector))));
   }
 
@@ -553,16 +704,16 @@
     const KerxSubTableFormat6 &table;
     hb_aat_apply_context_t *c;
 
-    inline accelerator_t (const KerxSubTableFormat6 &table_,
-			  hb_aat_apply_context_t *c_) :
-			    table (table_), c (c_) {}
+    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
+    int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
     { return table.get_kerning (left, right, c); }
   };
 
   protected:
-  KerxSubTableHeader		header;
+  KernSubTableHeader		header;
   HBUINT32			flags;
   HBUINT16			rowCount;
   HBUINT16			columnCount;
@@ -583,65 +734,88 @@
   } u;
   LOffsetTo<UnsizedArrayOf<FWORD>, false>	vector;
   public:
-  DEFINE_SIZE_STATIC (36);
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24);
 };
 
-struct KerxTable
-{
-  friend struct kerx;
 
-  inline unsigned int get_size (void) const { return u.header.length; }
-  inline unsigned int get_type (void) const { return u.header.coverage & SubtableType; }
+struct KerxSubTableHeader
+{
+  typedef ExtendedTypes Types;
+
+  unsigned int tuple_count () const { return tupleCount; }
+  bool is_horizontal () const       { return !(coverage & Vertical); }
 
   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. */
+    Vertical	= 0x80000000u,	/* Set if table has vertical kerning values. */
+    CrossStream	= 0x40000000u,	/* Set if table has cross-stream kerning values. */
+    Variation	= 0x20000000u,	/* Set if table has variation kerning values. */
+    Backwards	= 0x10000000u,	/* 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	= 0x0FFFFF00u,	/* Reserved, set to zero. */
+    SubtableType= 0x000000FFu,	/* Subtable type. */
   };
 
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  public:
+  HBUINT32	length;
+  HBUINT32	coverage;
+  HBUINT32	tupleCount;
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+struct KerxSubTable
+{
+  friend struct kerx;
+
+  unsigned int get_size () const { return u.header.length; }
+  unsigned int get_type () const { return u.header.coverage & u.header.SubtableType; }
+
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  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 ());
+    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
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!u.header.sanitize (c) ||
+	u.header.length <= u.header.static_size ||
 	!c->check_range (this, u.header.length))
       return_trace (false);
 
     return_trace (dispatch (c));
   }
 
-protected:
+  public:
   union {
-  KerxSubTableHeader	header;
-  KerxSubTableFormat0	format0;
-  KerxSubTableFormat1	format1;
-  KerxSubTableFormat2	format2;
-  KerxSubTableFormat4	format4;
-  KerxSubTableFormat6	format6;
+  KerxSubTableHeader				header;
+  KerxSubTableFormat0<KerxSubTableHeader>	format0;
+  KerxSubTableFormat1<KerxSubTableHeader>	format1;
+  KerxSubTableFormat2<KerxSubTableHeader>	format2;
+  KerxSubTableFormat4<KerxSubTableHeader>	format4;
+  KerxSubTableFormat6<KerxSubTableHeader>	format6;
   } u;
-public:
+  public:
   DEFINE_SIZE_MIN (12);
 };
 
@@ -650,73 +824,171 @@
  * The 'kerx' Table
  */
 
-struct kerx
+template <typename T>
+struct KerxTable
 {
-  static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
+  /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
+  const T* thiz () const { return static_cast<const T *> (this); }
 
-  inline bool has_data (void) const { return version != 0; }
-
-  inline void apply (hb_aat_apply_context_t *c) const
+  bool has_state_machine () const
   {
+    typedef typename T::SubTable SubTable;
+
+    const SubTable *st = &thiz()->firstSubTable;
+    unsigned int count = thiz()->tableCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (st->get_type () == 1)
+        return true;
+      st = &StructAfter<SubTable> (*st);
+    }
+    return false;
+  }
+
+  bool has_cross_stream () const
+  {
+    typedef typename T::SubTable SubTable;
+
+    const SubTable *st = &thiz()->firstSubTable;
+    unsigned int count = thiz()->tableCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (st->u.header.coverage & st->u.header.CrossStream)
+        return true;
+      st = &StructAfter<SubTable> (*st);
+    }
+    return false;
+  }
+
+  int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+  {
+    typedef typename T::SubTable SubTable;
+
+    int v = 0;
+    const SubTable *st = &thiz()->firstSubTable;
+    unsigned int count = thiz()->tableCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) ||
+	  !st->u.header.is_horizontal ())
+        continue;
+      v += st->get_kerning (left, right);
+      st = &StructAfter<SubTable> (*st);
+    }
+    return v;
+  }
+
+  bool apply (AAT::hb_aat_apply_context_t *c) const
+  {
+    typedef typename T::SubTable SubTable;
+
+    bool ret = false;
+    bool seenCrossStream = false;
     c->set_lookup_index (0);
-    const KerxTable *table = &firstTable;
-    unsigned int count = tableCount;
+    const SubTable *st = &thiz()->firstSubTable;
+    unsigned int count = thiz()->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 (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation))
+        goto skip;
 
-      if (HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
-	  bool (table->u.header.coverage & KerxTable::Vertical))
+      if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
 	goto skip;
 
-      reverse = bool (table->u.header.coverage & KerxTable::Backwards) !=
+      reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
-      if (!c->buffer->message (c->font, "start kerx subtable %d", c->lookup_index))
+      if (!c->buffer->message (c->font, "start %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), 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 (!seenCrossStream &&
+	  (st->u.header.coverage & st->u.header.CrossStream))
+      {
+        /* Attach all glyphs into a chain. */
+        seenCrossStream = true;
+	hb_glyph_position_t *pos = c->buffer->pos;
+	unsigned int count = c->buffer->len;
+	for (unsigned int i = 0; i < count; i++)
+	{
+	  pos[i].attach_type() = ATTACH_TYPE_CURSIVE;
+	  pos[i].attach_chain() = HB_DIRECTION_IS_FORWARD (c->buffer->props.direction) ? -1 : +1;
+	  /* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT,
+	   * since there needs to be a non-zero attachment for post-positioning to
+	   * be needed. */
+	}
+      }
 
       if (reverse)
 	c->buffer->reverse ();
 
-      (void) c->buffer->message (c->font, "end kerx subtable %d", c->lookup_index);
+      {
+	/* See comment in sanitize() for conditional here. */
+	hb_sanitize_with_object_t with (&c->sanitizer, i < count - 1 ? st : (const SubTable *) nullptr);
+	ret |= st->dispatch (c);
+      }
+
+      if (reverse)
+	c->buffer->reverse ();
+
+      (void) c->buffer->message (c->font, "end %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index);
 
     skip:
-      table = &StructAfter<KerxTable> (*table);
+      st = &StructAfter<SubTable> (*st);
+      c->set_lookup_index (c->lookup_index + 1);
     }
+
+    return ret;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!version.sanitize (c) || version < 2 ||
-	!tableCount.sanitize (c))
+    if (unlikely (!thiz()->version.sanitize (c) ||
+		  (unsigned) thiz()->version < (unsigned) T::minVersion ||
+		  !thiz()->tableCount.sanitize (c)))
       return_trace (false);
 
-    const KerxTable *table = &firstTable;
-    unsigned int count = tableCount;
+    typedef typename T::SubTable SubTable;
+
+    const SubTable *st = &thiz()->firstSubTable;
+    unsigned int count = thiz()->tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (!table->sanitize (c))
+      if (unlikely (!st->u.header.sanitize (c)))
 	return_trace (false);
-      table = &StructAfter<KerxTable> (*table);
+      /* OpenType kern table has 2-byte subtable lengths.  That's limiting.
+       * MS implementation also only supports one subtable, of format 0,
+       * anyway.  Certain versions of some fonts, like Calibry, contain
+       * kern subtable that exceeds 64kb.  Looks like, the subtable length
+       * is simply ignored.  Which makes sense.  It's only needed if you
+       * have multiple subtables.  To handle such fonts, we just ignore
+       * the length for the last subtable. */
+      hb_sanitize_with_object_t with (c, i < count - 1 ? st : (const SubTable *) nullptr);
+
+      if (unlikely (!st->sanitize (c)))
+	return_trace (false);
+
+      st = &StructAfter<SubTable> (*st);
     }
 
     return_trace (true);
   }
+};
+
+struct kerx : KerxTable<kerx>
+{
+  friend struct KerxTable<kerx>;
+
+  enum { tableTag = HB_AAT_TAG_kerx };
+  enum { minVersion = 2u };
+
+  typedef KerxSubTableHeader SubTableHeader;
+  typedef SubTableHeader::Types Types;
+  typedef KerxSubTable SubTable;
+
+  bool has_data () const { return version; }
 
   protected:
   HBUINT16	version;	/* The version number of the extended kerning table
@@ -724,13 +996,14 @@
   HBUINT16	unused;		/* Set to 0. */
   HBUINT32	tableCount;	/* The number of subtables included in the extended kerning
 				 * table. */
-  KerxTable	firstTable;	/* Subtables. */
+  SubTable	firstSubTable;	/* Subtables. */
 /*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */
 
   public:
   DEFINE_SIZE_MIN (8);
 };
 
+
 } /* namespace AAT */
 
 
diff --git a/src/hb-aat-layout-lcar-table.hh b/src/hb-aat-layout-lcar-table.hh
new file mode 100644
index 0000000..cac9697
--- /dev/null
+++ b/src/hb-aat-layout-lcar-table.hh
@@ -0,0 +1,93 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+#ifndef HB_AAT_LAYOUT_LCAR_TABLE_HH
+#define HB_AAT_LAYOUT_LCAR_TABLE_HH
+
+#include "hb-open-type.hh"
+#include "hb-aat-layout-common.hh"
+
+/*
+ * lcar -- Ligature caret
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6lcar.html
+ */
+#define HB_AAT_TAG_lcar HB_TAG('l','c','a','r')
+
+
+namespace AAT {
+
+typedef ArrayOf<HBINT16> LigCaretClassEntry;
+
+struct lcar
+{
+  enum { tableTag = HB_AAT_TAG_lcar };
+
+  unsigned int get_lig_carets (hb_font_t      *font,
+			       hb_direction_t  direction,
+			       hb_codepoint_t  glyph,
+			       unsigned int    start_offset,
+			       unsigned int   *caret_count /* IN/OUT */,
+			       hb_position_t  *caret_array /* OUT */) const
+  {
+    const OffsetTo<LigCaretClassEntry>* entry_offset = lookup.get_value (glyph,
+									 font->face->get_num_glyphs ());
+    const LigCaretClassEntry& array = entry_offset ? this+*entry_offset : Null (LigCaretClassEntry);
+    if (caret_count)
+    {
+      hb_array_t<const HBINT16> arr = array.sub_array (start_offset, caret_count);
+      unsigned int count = arr.len;
+      for (unsigned int i = 0; i < count; ++i)
+	switch (format)
+	{
+	case 0: caret_array[i] = font->em_scale_dir (arr[i], direction); break;
+	case 1:
+	  hb_position_t x, y;
+	  font->get_glyph_contour_point_for_origin (glyph, arr[i], direction, &x, &y);
+	  caret_array[i] = HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
+	  break;
+	}
+    }
+    return array.len;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  version.major == 1 &&
+			  lookup.sanitize (c, this)));
+  }
+
+  protected:
+  FixedVersion<>version;	/* Version number of the ligature caret table */
+  HBUINT16	format;		/* Format of the ligature caret table. */
+  Lookup<OffsetTo<LigCaretClassEntry> >
+		lookup;		/* data Lookup table associating glyphs */
+
+  public:
+  DEFINE_SIZE_MIN (8);
+};
+
+} /* namespace AAT */
+
+#endif /* HB_AAT_LAYOUT_LCAR_TABLE_HH */
diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
index 651af21..c7600aa 100644
--- a/src/hb-aat-layout-morx-table.hh
+++ b/src/hb-aat-layout-morx-table.hh
@@ -30,26 +30,31 @@
 #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
  * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html
  */
 #define HB_AAT_TAG_morx HB_TAG('m','o','r','x')
+#define HB_AAT_TAG_mort HB_TAG('m','o','r','t')
 
 
 namespace AAT {
 
 using namespace OT;
 
-
+template <typename Types>
 struct RearrangementSubtable
 {
+  typedef typename Types::HBUINT HBUINT;
+
   typedef void EntryData;
 
   struct driver_context_t
   {
-    static const bool in_place = true;
+    enum { in_place = true };
     enum Flags
     {
       MarkFirst		= 0x8000,	/* If set, make the current glyph the first
@@ -64,17 +69,17 @@
       Verb		= 0x000F,	/* The type of rearrangement specified. */
     };
 
-    inline driver_context_t (const RearrangementSubtable *table) :
+    driver_context_t (const RearrangementSubtable *table HB_UNUSED) :
 	ret (false),
 	start (0), end (0) {}
 
-    inline bool is_actionable (StateTableDriver<EntryData> *driver,
-			       const Entry<EntryData> *entry)
+    bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
+			const Entry<EntryData> *entry)
     {
       return (entry->flags & Verb) && start < end;
     }
-    inline bool transition (StateTableDriver<EntryData> *driver,
-			    const Entry<EntryData> *entry)
+    bool transition (StateTableDriver<Types, EntryData> *driver,
+		     const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
       unsigned int flags = entry->flags;
@@ -158,32 +163,35 @@
     unsigned int end;
   };
 
-  inline bool apply (hb_aat_apply_context_t *c) const
+  bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
     driver_context_t dc (this);
 
-    StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
+    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
     driver.drive (&dc);
 
     return_trace (dc.ret);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (machine.sanitize (c));
   }
 
   protected:
-  StateTable<EntryData>	machine;
+  StateTable<Types, EntryData>	machine;
   public:
   DEFINE_SIZE_STATIC (16);
 };
 
+template <typename Types>
 struct ContextualSubtable
 {
+  typedef typename Types::HBUINT HBUINT;
+
   struct EntryData
   {
     HBUINT16	markIndex;	/* Index of the substitution table for the
@@ -196,7 +204,7 @@
 
   struct driver_context_t
   {
-    static const bool in_place = true;
+    enum { in_place = true };
     enum Flags
     {
       SetMark		= 0x8000,	/* If set, make the current glyph the marked glyph. */
@@ -205,14 +213,17 @@
       Reserved		= 0x3FFF,	/* These bits are reserved and should be set to 0. */
     };
 
-    inline driver_context_t (const ContextualSubtable *table) :
+    driver_context_t (const ContextualSubtable *table_,
+			     hb_aat_apply_context_t *c_) :
 	ret (false),
+	c (c_),
 	mark_set (false),
 	mark (0),
+	table (table_),
 	subs (table+table->substitutionTables) {}
 
-    inline bool is_actionable (StateTableDriver<EntryData> *driver,
-			       const Entry<EntryData> *entry)
+    bool is_actionable (StateTableDriver<Types, EntryData> *driver,
+			const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
 
@@ -221,8 +232,8 @@
 
       return entry->data.markIndex != 0xFFFF || entry->data.currentIndex != 0xFFFF;
     }
-    inline bool transition (StateTableDriver<EntryData> *driver,
-			    const Entry<EntryData> *entry)
+    bool transition (StateTableDriver<Types, EntryData> *driver,
+		     const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
 
@@ -231,30 +242,55 @@
       if (buffer->idx == buffer->len && !mark_set)
         return true;
 
-      if (entry->data.markIndex != 0xFFFF)
+      const GlyphID *replacement;
+
+      replacement = nullptr;
+      if (Types::extended)
       {
-	const Lookup<GlyphID> &lookup = subs[entry->data.markIndex];
-	hb_glyph_info_t *info = buffer->info;
-	const GlyphID *replacement = lookup.get_value (info[mark].codepoint, driver->num_glyphs);
-	if (replacement)
+	if (entry->data.markIndex != 0xFFFF)
 	{
-	  buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
-	  info[mark].codepoint = *replacement;
-	  ret = true;
+	  const Lookup<GlyphID> &lookup = subs[entry->data.markIndex];
+	  replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs);
 	}
       }
-      if (entry->data.currentIndex != 0xFFFF)
+      else
       {
-        unsigned int idx = MIN (buffer->idx, buffer->len - 1);
-	const Lookup<GlyphID> &lookup = subs[entry->data.currentIndex];
-	hb_glyph_info_t *info = buffer->info;
-	const GlyphID *replacement = lookup.get_value (info[idx].codepoint, driver->num_glyphs);
-	if (replacement)
+	unsigned int offset = entry->data.markIndex + buffer->info[mark].codepoint;
+	const UnsizedArrayOf<GlyphID> &subs_old = (const UnsizedArrayOf<GlyphID> &) subs;
+	replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
+	if (!replacement->sanitize (&c->sanitizer) || !*replacement)
+	  replacement = nullptr;
+      }
+      if (replacement)
+      {
+	buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
+	buffer->info[mark].codepoint = *replacement;
+	ret = true;
+      }
+
+      replacement = nullptr;
+      unsigned int idx = MIN (buffer->idx, buffer->len - 1);
+      if (Types::extended)
+      {
+	if (entry->data.currentIndex != 0xFFFF)
 	{
-	  info[idx].codepoint = *replacement;
-	  ret = true;
+	  const Lookup<GlyphID> &lookup = subs[entry->data.currentIndex];
+	  replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs);
 	}
       }
+      else
+      {
+	unsigned int offset = entry->data.currentIndex + buffer->info[idx].codepoint;
+	const UnsizedArrayOf<GlyphID> &subs_old = (const UnsizedArrayOf<GlyphID> &) subs;
+	replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
+	if (!replacement->sanitize (&c->sanitizer) || !*replacement)
+	  replacement = nullptr;
+      }
+      if (replacement)
+      {
+	buffer->info[idx].codepoint = *replacement;
+	ret = true;
+      }
 
       if (entry->flags & SetMark)
       {
@@ -268,30 +304,34 @@
     public:
     bool ret;
     private:
+    hb_aat_apply_context_t *c;
     bool mark_set;
     unsigned int mark;
-    const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32, false> &subs;
+    const ContextualSubtable *table;
+    const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT, false> &subs;
   };
 
-  inline bool apply (hb_aat_apply_context_t *c) const
+  bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
-    driver_context_t dc (this);
+    driver_context_t dc (this, c);
 
-    StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
+    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
     driver.drive (&dc);
 
     return_trace (dc.ret);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
 
     unsigned int num_entries = 0;
     if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false);
 
+    if (!Types::extended) return_trace (true);
+
     unsigned int num_lookups = 0;
 
     const Entry<EntryData> *entries = machine.get_entries ();
@@ -309,16 +349,32 @@
   }
 
   protected:
-  StateTable<EntryData>
+  StateTable<Types, EntryData>
 		machine;
-  LOffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32, false>, false>
+  OffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT, false>, HBUINT, false>
 		substitutionTables;
   public:
   DEFINE_SIZE_STATIC (20);
 };
 
-struct LigatureSubtable
+
+template <bool extended>
+struct LigatureEntry;
+
+template <>
+struct LigatureEntry<true>
 {
+  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
+					   next iteration. */
+    PerformAction	= 0x2000,	/* Use the ligActionIndex to process a ligature
+					 * group. */
+    Reserved		= 0x1FFF,	/* These bits are reserved and should be set to 0. */
+  };
+
   struct EntryData
   {
     HBUINT16	ligActionIndex;	/* Index to the first ligActionTable entry
@@ -328,18 +384,50 @@
     DEFINE_SIZE_STATIC (2);
   };
 
+  static bool performAction (const Entry<EntryData> *entry)
+  { return entry->flags & PerformAction; }
+
+  static unsigned int ligActionIndex (const Entry<EntryData> *entry)
+  { return entry->data.ligActionIndex; }
+};
+template <>
+struct LigatureEntry<false>
+{
+  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
+					   next iteration. */
+    Offset		= 0x3FFF,	/* Byte offset from beginning of subtable to the
+					 * ligature action list. This value must be a
+					 * multiple of 4. */
+  };
+
+  typedef void EntryData;
+
+  static bool performAction (const Entry<EntryData> *entry)
+  { return entry->flags & Offset; }
+
+  static unsigned int ligActionIndex (const Entry<EntryData> *entry)
+  { return entry->flags & Offset; }
+};
+
+
+template <typename Types>
+struct LigatureSubtable
+{
+  typedef typename Types::HBUINT HBUINT;
+
+  typedef LigatureEntry<Types::extended> LigatureEntryT;
+  typedef typename LigatureEntryT::EntryData EntryData;
+
   struct driver_context_t
   {
-    static const bool in_place = false;
-    enum Flags
+    enum { in_place = false };
+    enum
     {
-      SetComponent	= 0x8000,	/* Push this glyph onto the component stack for
-					 * eventual processing. */
-      DontAdvance	= 0x4000,	/* Leave the glyph pointer at this glyph for the
-					   next iteration. */
-      PerformAction	= 0x2000,	/* Use the ligActionIndex to process a ligature
-					 * group. */
-      Reserved		= 0x1FFF,	/* These bits are reserved and should be set to 0. */
+      DontAdvance	= LigatureEntryT::DontAdvance,
     };
     enum LigActionFlags
     {
@@ -353,27 +441,28 @@
 					 * into the component table. */
     };
 
-    inline driver_context_t (const LigatureSubtable *table,
-			     hb_aat_apply_context_t *c_) :
+    driver_context_t (const LigatureSubtable *table_,
+		      hb_aat_apply_context_t *c_) :
 	ret (false),
 	c (c_),
+	table (table_),
 	ligAction (table+table->ligAction),
 	component (table+table->component),
 	ligature (table+table->ligature),
 	match_length (0) {}
 
-    inline bool is_actionable (StateTableDriver<EntryData> *driver,
-			       const Entry<EntryData> *entry)
+    bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
+			const Entry<EntryData> *entry)
     {
-      return entry->flags & PerformAction;
+      return LigatureEntryT::performAction (entry);
     }
-    inline bool transition (StateTableDriver<EntryData> *driver,
-			    const Entry<EntryData> *entry)
+    bool transition (StateTableDriver<Types, EntryData> *driver,
+		     const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
-      unsigned int flags = entry->flags;
 
-      if (flags & SetComponent)
+      DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx);
+      if (entry->flags & LigatureEntryT::SetComponent)
       {
         if (unlikely (match_length >= ARRAY_LENGTH (match_positions)))
 	  return false;
@@ -383,66 +472,83 @@
 	  match_length--;
 
 	match_positions[match_length++] = buffer->out_len;
+	DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len);
       }
 
-      if (flags & PerformAction)
+      if (LigatureEntryT::performAction (entry))
       {
+	DEBUG_MSG (APPLY, nullptr, "Perform action with %u", 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;
 
-	/* TODO Only when ligation happens? */
-	buffer->merge_out_clusters (match_positions[0], buffer->out_len);
+	if (buffer->idx >= buffer->len)
+	  return false; // TODO Work on previous instead?
 
 	unsigned int cursor = match_length;
+
+	unsigned int action_idx = LigatureEntryT::ligActionIndex (entry);
+	action_idx = Types::offsetToIndex (action_idx, table, ligAction.arrayZ);
+	const HBUINT32 *actionData = &ligAction[action_idx];
+
+	unsigned int ligature_idx = 0;
+	unsigned int action;
         do
 	{
 	  if (unlikely (!cursor))
+	  {
+	    /* Stack underflow.  Clear the stack. */
+	    DEBUG_MSG (APPLY, nullptr, "Stack underflow");
+	    match_length = 0;
 	    break;
+	  }
 
+	  DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1);
 	  buffer->move_to (match_positions[--cursor]);
 
-	  const HBUINT32 &actionData = ligAction[action_idx];
-	  if (unlikely (!actionData.sanitize (&c->sanitizer))) return false;
-	  action = actionData;
+	  if (unlikely (!actionData->sanitize (&c->sanitizer))) return false;
+	  action = *actionData;
 
 	  uint32_t uoffset = action & LigActionOffset;
 	  if (uoffset & 0x20000000)
 	    uoffset |= 0xC0000000; /* Sign-extend. */
 	  int32_t offset = (int32_t) uoffset;
-	  if (buffer->idx >= buffer->len)
-	    return false; // TODO Work on previous instead?
 	  unsigned int component_idx = buffer->cur().codepoint + offset;
-
+	  component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ);
 	  const HBUINT16 &componentData = component[component_idx];
 	  if (unlikely (!componentData.sanitize (&c->sanitizer))) return false;
 	  ligature_idx += componentData;
 
+	  DEBUG_MSG (APPLY, nullptr, "Action store %u last %u",
+		     bool (action & LigActionStore),
+		     bool (action & LigActionLast));
 	  if (action & (LigActionStore | LigActionLast))
 	  {
+	    ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ);
 	    const GlyphID &ligatureData = ligature[ligature_idx];
 	    if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false;
 	    hb_codepoint_t lig = ligatureData;
 
+	    DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig);
 	    buffer->replace_glyph (lig);
 
+	    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->skip_glyph ();
-	      end--;
+	      buffer->replace_glyph (DELETED_GLYPH);
 	    }
+
+	    buffer->move_to (lig_end);
+	    buffer->merge_out_clusters (match_positions[cursor], buffer->out_len);
 	  }
 
-	  action_idx++;
+	  actionData++;
 	}
 	while (!(action & LigActionLast));
-	match_length = 0;
 	buffer->move_to (end);
       }
 
@@ -453,6 +559,7 @@
     bool ret;
     private:
     hb_aat_apply_context_t *c;
+    const LigatureSubtable *table;
     const UnsizedArrayOf<HBUINT32> &ligAction;
     const UnsizedArrayOf<HBUINT16> &component;
     const UnsizedArrayOf<GlyphID> &ligature;
@@ -460,19 +567,19 @@
     unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
   };
 
-  inline bool apply (hb_aat_apply_context_t *c) const
+  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->face);
+    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
     driver.drive (&dc);
 
     return_trace (dc.ret);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* The rest of array sanitizations are done at run-time. */
@@ -481,21 +588,22 @@
   }
 
   protected:
-  StateTable<EntryData>
+  StateTable<Types, EntryData>
 		machine;
-  LOffsetTo<UnsizedArrayOf<HBUINT32>, false>
+  OffsetTo<UnsizedArrayOf<HBUINT32>, HBUINT, false>
 		ligAction;	/* Offset to the ligature action table. */
-  LOffsetTo<UnsizedArrayOf<HBUINT16>, false>
+  OffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT, false>
 		component;	/* Offset to the component table. */
-  LOffsetTo<UnsizedArrayOf<GlyphID>, false>
+  OffsetTo<UnsizedArrayOf<GlyphID>, HBUINT, false>
 		ligature;	/* Offset to the actual ligature lists. */
   public:
   DEFINE_SIZE_STATIC (28);
 };
 
+template <typename Types>
 struct NoncontextualSubtable
 {
-  inline bool apply (hb_aat_apply_context_t *c) const
+  bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
@@ -517,7 +625,7 @@
     return_trace (ret);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (substitute.sanitize (c));
@@ -529,8 +637,11 @@
   DEFINE_SIZE_MIN (2);
 };
 
+template <typename Types>
 struct InsertionSubtable
 {
+  typedef typename Types::HBUINT HBUINT;
+
   struct EntryData
   {
     HBUINT16	currentInsertIndex;	/* Zero-based index into the insertion glyph table.
@@ -549,7 +660,7 @@
 
   struct driver_context_t
   {
-    static const bool in_place = false;
+    enum { in_place = false };
     enum Flags
     {
       SetMark		= 0x8000,	/* If set, mark the current glyph. */
@@ -598,22 +709,22 @@
 					 * marked location is 31 glyphs. */
     };
 
-    inline driver_context_t (const InsertionSubtable *table,
-			     hb_aat_apply_context_t *c_) :
+    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,
-			       const Entry<EntryData> *entry)
+    bool is_actionable (StateTableDriver<Types, 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)
+    bool transition (StateTableDriver<Types, EntryData> *driver,
+		     const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
       unsigned int flags = entry->flags;
@@ -698,19 +809,19 @@
     const UnsizedArrayOf<GlyphID> &insertionAction;
   };
 
-  inline bool apply (hb_aat_apply_context_t *c) const
+  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->face);
+    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
     driver.drive (&dc);
 
     return_trace (dc.ret);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* The rest of array sanitizations are done at run-time. */
@@ -719,9 +830,9 @@
   }
 
   protected:
-  StateTable<EntryData>
+  StateTable<Types, EntryData>
 		machine;
-  LOffsetTo<UnsizedArrayOf<GlyphID>, false>
+  OffsetTo<UnsizedArrayOf<GlyphID>, HBUINT, false>
 		insertionAction;	/* Byte offset from stateHeader to the start of
 					 * the insertion glyph table. */
   public:
@@ -731,7 +842,7 @@
 
 struct Feature
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -749,30 +860,32 @@
   DEFINE_SIZE_STATIC (12);
 };
 
-
+template <typename Types>
 struct ChainSubtable
 {
+  typedef typename Types::HBUINT HBUINT;
+
+  template <typename T>
   friend struct Chain;
 
-  inline unsigned int get_size (void) const { return length; }
-  inline unsigned int get_type (void) const { return coverage & SubtableType; }
+  unsigned int get_size () const     { return length; }
+  unsigned int get_type () const     { return coverage & 0xFF; }
+  unsigned int get_coverage () const { return coverage >> (sizeof (HBUINT) * 8 - 8); }
 
   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. */
+    Vertical		= 0x80,	/* If set, this subtable will only be applied
+				 * to vertical text. If clear, this subtable
+				 * will only be applied to horizontal text. */
+    Backwards		= 0x40,	/* If set, this subtable will process glyphs
+				 * in descending order. If clear, it will
+				 * process the glyphs in ascending order. */
+    AllDirections	= 0x20,	/* If set, this subtable will be applied to
+				 * both horizontal and vertical text (i.e.
+				 * the state of bit 0x80000000 is ignored). */
+    Logical		= 0x10,	/* If set, this subtable will process glyphs
+				 * in logical order (or reverse logical order,
+				 * depending on the value of bit 0x80000000). */
   };
   enum Type
   {
@@ -784,7 +897,7 @@
   };
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     unsigned int subtable_type = get_type ();
     TRACE_DISPATCH (this, subtable_type);
@@ -798,53 +911,78 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    hb_sanitize_with_object_t with (&c->sanitizer, this);
+    return_trace (dispatch (c));
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!length.sanitize (c) ||
-	length < min_size ||
+	length <= min_size ||
 	!c->check_range (this, length))
       return_trace (false);
 
+    hb_sanitize_with_object_t with (c, this);
     return_trace (dispatch (c));
   }
 
   protected:
-  HBUINT32	length;		/* Total subtable length, including this header. */
-  HBUINT32	coverage;	/* Coverage flags and subtable type. */
+  HBUINT	length;		/* Total subtable length, including this header. */
+  HBUINT	coverage;	/* Coverage flags and subtable type. */
   HBUINT32	subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */
   union {
-  RearrangementSubtable		rearrangement;
-  ContextualSubtable		contextual;
-  LigatureSubtable		ligature;
-  NoncontextualSubtable		noncontextual;
-  InsertionSubtable		insertion;
+  RearrangementSubtable<Types>	rearrangement;
+  ContextualSubtable<Types>	contextual;
+  LigatureSubtable<Types>	ligature;
+  NoncontextualSubtable<Types>	noncontextual;
+  InsertionSubtable<Types>	insertion;
   } u;
   public:
-  DEFINE_SIZE_MIN (12);
+  DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4);
 };
 
+template <typename Types>
 struct Chain
 {
-  inline void apply (hb_aat_apply_context_t *c) const
+  typedef typename Types::HBUINT HBUINT;
+
+  hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const
   {
-    uint32_t flags = defaultFlags;
+    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];
-	if (false) /* XXX Check if feature enabled... */
+	const Feature &feature = featureZ[i];
+	hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType;
+	hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting;
+      retry:
+	const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch ((uint16_t) type);
+	if (info && info->setting == setting)
 	{
 	  flags &= feature.disableFlags;
 	  flags |= feature.enableFlags;
 	}
+	else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS)
+	{
+	  /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */
+	  type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE;
+	  setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS;
+	  goto retry;
+	}
       }
     }
+    return flags;
+  }
 
-    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (&featureZ, featureZ[0].static_size * featureCount);
+  void apply (hb_aat_apply_context_t *c,
+		     hb_mask_t flags) const
+  {
+    const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types> > (featureZ.as_array (featureCount));
     unsigned int count = subtableCount;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -853,9 +991,9 @@
       if (!(subtable->subFeatureFlags & flags))
         goto skip;
 
-      if (!(subtable->coverage & ChainSubtable::AllDirections) &&
+      if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) &&
 	  HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
-	  bool (subtable->coverage & ChainSubtable::Vertical))
+	  bool (subtable->get_coverage() & ChainSubtable<Types>::Vertical))
         goto skip;
 
       /* Buffer contents is always in logical direction.  Determine if
@@ -885,9 +1023,9 @@
 				(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) !=
+      reverse = subtable->get_coverage () & ChainSubtable<Types>::Logical ?
+		bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) :
+		bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) !=
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
       if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
@@ -896,9 +1034,7 @@
       if (reverse)
         c->buffer->reverse ();
 
-      c->sanitizer.set_object (*subtable);
-
-      subtable->dispatch (c);
+      subtable->apply (c);
 
       if (reverse)
         c->buffer->reverse ();
@@ -908,14 +1044,14 @@
       if (unlikely (!c->buffer->successful)) return;
 
     skip:
-      subtable = &StructAfter<ChainSubtable> (*subtable);
+      subtable = &StructAfter<ChainSubtable<Types> > (*subtable);
       c->set_lookup_index (c->lookup_index + 1);
     }
   }
 
-  inline unsigned int get_size (void) const { return length; }
+  unsigned int get_size () const { return length; }
 
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int version) const
+  bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const
   {
     TRACE_SANITIZE (this);
     if (!length.sanitize (c) ||
@@ -926,13 +1062,13 @@
     if (!c->check_array (featureZ.arrayZ, featureCount))
       return_trace (false);
 
-    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (&featureZ, featureZ[0].static_size * featureCount);
+    const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types> > (featureZ.as_array (featureCount));
     unsigned int count = subtableCount;
     for (unsigned int i = 0; i < count; i++)
     {
       if (!subtable->sanitize (c))
 	return_trace (false);
-      subtable = &StructAfter<ChainSubtable> (*subtable);
+      subtable = &StructAfter<ChainSubtable<Types> > (*subtable);
     }
 
     return_trace (true);
@@ -941,56 +1077,68 @@
   protected:
   HBUINT32	defaultFlags;	/* The default specification for subtables. */
   HBUINT32	length;		/* Total byte count, including this header. */
-  HBUINT32	featureCount;	/* Number of feature subtable entries. */
-  HBUINT32	subtableCount;	/* The number of subtables in the chain. */
+  HBUINT	featureCount;	/* Number of feature subtable entries. */
+  HBUINT	subtableCount;	/* The number of subtables in the chain. */
 
   UnsizedArrayOf<Feature>	featureZ;	/* Features. */
 /*ChainSubtable	firstSubtable;*//* Subtables. */
 /*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */
 
   public:
-  DEFINE_SIZE_MIN (16);
+  DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT));
 };
 
 
 /*
- * The 'morx' Table
+ * The 'mort'/'morx' Table
  */
 
-struct morx
+template <typename Types>
+struct mortmorx
 {
-  static const hb_tag_t tableTag = HB_AAT_TAG_morx;
+  enum { tableTag = HB_AAT_TAG_morx };
 
-  inline bool has_data (void) const { return version != 0; }
+  bool has_data () const { return version != 0; }
 
-  inline void apply (hb_aat_apply_context_t *c) const
+  void compile_flags (const hb_aat_map_builder_t *mapper,
+		      hb_aat_map_t *map) const
   {
-    if (unlikely (!c->buffer->successful)) return;
-    c->set_lookup_index (0);
-    const Chain *chain = &firstChain;
+    const Chain<Types> *chain = &firstChain;
     unsigned int count = chainCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      chain->apply (c);
-      if (unlikely (!c->buffer->successful)) return;
-      chain = &StructAfter<Chain> (*chain);
+      map->chain_flags.push (chain->compile_flags (mapper));
+      chain = &StructAfter<Chain<Types> > (*chain);
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  void apply (hb_aat_apply_context_t *c) const
+  {
+    if (unlikely (!c->buffer->successful)) return;
+    c->set_lookup_index (0);
+    const Chain<Types> *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<Types> > (*chain);
+    }
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!version.sanitize (c) || version < 2 ||
-	!chainCount.sanitize (c))
+    if (!version.sanitize (c) || !version || !chainCount.sanitize (c))
       return_trace (false);
 
-    const Chain *chain = &firstChain;
+    const Chain<Types> *chain = &firstChain;
     unsigned int count = chainCount;
     for (unsigned int i = 0; i < count; i++)
     {
       if (!chain->sanitize (c, version))
 	return_trace (false);
-      chain = &StructAfter<Chain> (*chain);
+      chain = &StructAfter<Chain<Types> > (*chain);
     }
 
     return_trace (true);
@@ -998,16 +1146,26 @@
 
   protected:
   HBUINT16	version;	/* Version number of the glyph metamorphosis table.
-				 * 2 or 3. */
+				 * 1, 2, or 3. */
   HBUINT16	unused;		/* Set to 0. */
   HBUINT32	chainCount;	/* Number of metamorphosis chains contained in this
 				 * table. */
-  Chain		firstChain;	/* Chains. */
+  Chain<Types>	firstChain;	/* Chains. */
 
   public:
   DEFINE_SIZE_MIN (8);
 };
 
+struct morx : mortmorx<ExtendedTypes>
+{
+  enum { tableTag = HB_AAT_TAG_morx };
+};
+struct mort : mortmorx<ObsoleteTypes>
+{
+  enum { tableTag = HB_AAT_TAG_mort };
+};
+
+
 } /* namespace AAT */
 
 
diff --git a/src/hb-aat-layout-trak-table.hh b/src/hb-aat-layout-trak-table.hh
index 16729d1..0648f61 100644
--- a/src/hb-aat-layout-trak-table.hh
+++ b/src/hb-aat-layout-trak-table.hh
@@ -46,25 +46,19 @@
 {
   friend struct TrackData;
 
-  inline float get_track_value () const
-  {
-    return track.to_float ();
-  }
+  float get_track_value () const { return track.to_float (); }
 
-  inline int get_value (const void *base,
-			unsigned int index,
-			unsigned int nSizes) const
-  {
-    return hb_array_t<FWORD> ((base+valuesZ).arrayZ, nSizes)[index];
-  }
+  int get_value (const void *base, unsigned int index,
+		 unsigned int table_size) const
+  { return (base+valuesZ).as_array (table_size)[index]; }
 
   public:
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base,
-			unsigned int nSizes) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base,
+		 unsigned int table_size) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
-			  (valuesZ.sanitize (c, base, nSizes))));
+			  (valuesZ.sanitize (c, base, table_size))));
   }
 
   protected:
@@ -82,13 +76,13 @@
 
 struct TrackData
 {
-  inline float interpolate_at (unsigned int idx,
-			       float target_size,
-			       const TrackTableEntry &trackTableEntry,
-			       const void *base) const
+  float interpolate_at (unsigned int idx,
+			float target_size,
+			const TrackTableEntry &trackTableEntry,
+			const void *base) const
   {
     unsigned int sizes = nSizes;
-    hb_array_t<Fixed> size_table ((base+sizeTable).arrayZ, sizes);
+    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 ();
@@ -97,7 +91,7 @@
 	   (1.f - t) * trackTableEntry.get_value (base, idx, sizes);
   }
 
-  inline int get_tracking (const void *base, float ptem) const
+  int get_tracking (const void *base, float ptem) const
   {
     /* CoreText points are CSS pixels (96 per inch),
      * NOT typographic points (72 per inch).
@@ -133,8 +127,7 @@
     if (!sizes) return 0.;
     if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
 
-    /* TODO bfind() */
-    hb_array_t<Fixed> size_table ((base+sizeTable).arrayZ, sizes);
+    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)
@@ -144,12 +137,12 @@
 				  *trackTableEntry, base));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  sizeTable.sanitize (c, base, nSizes) &&
-		  trackTable.sanitize (c, nTracks, base, nSizes));
+    return_trace (likely (c->check_struct (this) &&
+			  sizeTable.sanitize (c, base, nSizes) &&
+			  trackTable.sanitize (c, nTracks, base, nSizes)));
   }
 
   protected:
@@ -167,23 +160,16 @@
 
 struct trak
 {
-  static const hb_tag_t tableTag = HB_AAT_TAG_trak;
+  enum { tableTag = HB_AAT_TAG_trak };
 
-  inline bool has_data (void) const { return version.to_int () != 0; }
+  bool has_data () const { return version.to_int (); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-
-    return_trace (unlikely (c->check_struct (this) &&
-			    horizData.sanitize (c, this, this) &&
-			    vertData.sanitize (c, this, this)));
-  }
-
-  inline bool apply (hb_aat_apply_context_t *c) const
+  bool apply (hb_aat_apply_context_t *c) const
   {
     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);
@@ -197,6 +183,7 @@
       hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
       foreach_grapheme (buffer, start, end)
       {
+        if (!(buffer->info[start].mask & trak_mask)) continue;
 	buffer->pos[start].x_advance += advance_to_add;
 	buffer->pos[start].x_offset += offset_to_add;
       }
@@ -209,6 +196,7 @@
       hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
       foreach_grapheme (buffer, start, end)
       {
+        if (!(buffer->info[start].mask & trak_mask)) continue;
 	buffer->pos[start].y_advance += advance_to_add;
 	buffer->pos[start].y_offset += offset_to_add;
       }
@@ -217,15 +205,27 @@
     return_trace (true);
   }
 
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+
+    return_trace (likely (c->check_struct (this) &&
+			  version.major == 1 &&
+			  horizData.sanitize (c, this, this) &&
+			  vertData.sanitize (c, this, this)));
+  }
+
   protected:
-  FixedVersion<>	version;	/* Version of the tracking table
+  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. */
+  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_STATIC (12);
diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc
index e9da850..8de8205 100644
--- a/src/hb-aat-layout.cc
+++ b/src/hb-aat-layout.cc
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2017  Google, Inc.
+ * Copyright © 2018  Ebrahim Byagowi
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -28,94 +29,107 @@
 
 #include "hb-ot-face.hh"
 #include "hb-aat-layout.hh"
+#include "hb-aat-fdsc-table.hh" // Just so we compile it; unused otherwise.
 #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-feat-table.hh"
+#include "hb-aat-layout-just-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"
+
+
+/**
+ * SECTION:hb-aat-layout
+ * @title: hb-aat-layout
+ * @short_description: Apple Advanced Typography Layout
+ * @include: hb-aat.h
+ *
+ * Functions for querying OpenType Layout features in the font face.
+ **/
 
 
 /* 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*/},
+  {HB_TAG ('a','f','r','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS,               HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS,             HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS},
+  {HB_TAG ('c','2','p','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE,              HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS,         HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE},
+  {HB_TAG ('c','2','s','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE,              HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS,          HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE},
+  {HB_TAG ('c','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON,       HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF},
+  {HB_TAG ('c','a','s','e'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT,   HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON,       HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF},
+  {HB_TAG ('c','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES,               HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON,        HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF},
+  {HB_TAG ('c','p','s','p'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT,   HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON,      HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF},
+  {HB_TAG ('c','s','w','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF},
+  {HB_TAG ('d','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES,               HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON,              HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF},
+  {HB_TAG ('e','x','p','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE,         HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS,              (hb_aat_layout_feature_selector_t) 16},
+  {HB_TAG ('f','r','a','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS,               HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS,             HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS},
+  {HB_TAG ('f','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT,                (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('h','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT,            (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('h','i','s','t'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES,               HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON,        HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF},
+  {HB_TAG ('h','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA,          HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON,        HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF},
+  {HB_TAG ('h','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES,               HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON,        HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF},
+  {HB_TAG ('h','n','g','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION,         HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL,                HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION},
+  {HB_TAG ('h','o','j','o'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE,         HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS,                (hb_aat_layout_feature_selector_t) 16},
+  {HB_TAG ('h','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT,                (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('i','t','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN,        HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON,            HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF},
+  {HB_TAG ('j','p','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE,         HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS,             (hb_aat_layout_feature_selector_t) 16},
+  {HB_TAG ('j','p','7','8'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE,         HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS,             (hb_aat_layout_feature_selector_t) 16},
+  {HB_TAG ('j','p','8','3'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE,         HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS,             (hb_aat_layout_feature_selector_t) 16},
+  {HB_TAG ('j','p','9','0'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE,         HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS,             (hb_aat_layout_feature_selector_t) 16},
+  {HB_TAG ('l','i','g','a'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES,               HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON,            HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF},
+  {HB_TAG ('l','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE,             HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS,             (hb_aat_layout_feature_selector_t) 2},
+  {HB_TAG ('m','g','r','k'), HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS,     HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON,          HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF},
+  {HB_TAG ('n','l','c','k'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE,         HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS,                  (hb_aat_layout_feature_selector_t) 16},
+  {HB_TAG ('o','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE,             HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS,             (hb_aat_layout_feature_selector_t) 2},
+  {HB_TAG ('o','r','d','n'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION,       HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS,                       HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION},
+  {HB_TAG ('p','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT,          (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('p','c','a','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE,              HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS,         HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE},
+  {HB_TAG ('p','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT,              (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('p','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING,          HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS,           (hb_aat_layout_feature_selector_t) 4},
+  {HB_TAG ('p','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT,              (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('q','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT,             (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('r','u','b','y'), HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA,               HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON,                   HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF},
+  {HB_TAG ('s','i','n','f'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION,       HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS,           HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION},
+  {HB_TAG ('s','m','c','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE,              HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS,          HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE},
+  {HB_TAG ('s','m','p','l'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE,         HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS,          (hb_aat_layout_feature_selector_t) 16},
+  {HB_TAG ('s','s','0','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON,           HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF},
+  {HB_TAG ('s','s','0','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON,           HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF},
+  {HB_TAG ('s','s','0','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON,         HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF},
+  {HB_TAG ('s','s','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON,          HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF},
+  {HB_TAG ('s','s','0','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON,          HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF},
+  {HB_TAG ('s','s','0','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON,           HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF},
+  {HB_TAG ('s','s','0','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON,         HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF},
+  {HB_TAG ('s','s','0','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON,         HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF},
+  {HB_TAG ('s','s','0','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON,          HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF},
+  {HB_TAG ('s','s','1','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON,           HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF},
+  {HB_TAG ('s','s','1','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON,        HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF},
+  {HB_TAG ('s','s','1','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON,        HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF},
+  {HB_TAG ('s','s','1','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON,      HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF},
+  {HB_TAG ('s','s','1','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON,      HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF},
+  {HB_TAG ('s','s','1','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON,       HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF},
+  {HB_TAG ('s','s','1','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON,       HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF},
+  {HB_TAG ('s','s','1','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON,     HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF},
+  {HB_TAG ('s','s','1','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON,      HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF},
+  {HB_TAG ('s','s','1','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON,      HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF},
+  {HB_TAG ('s','s','2','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES,  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON,        HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF},
+  {HB_TAG ('s','u','b','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION,       HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS,                      HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION},
+  {HB_TAG ('s','u','p','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION,       HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS,                      HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION},
+  {HB_TAG ('s','w','s','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON,            HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF},
+  {HB_TAG ('t','i','t','l'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS,           HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS,                   HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS},
+  {HB_TAG ('t','n','a','m'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE,         HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS,   (hb_aat_layout_feature_selector_t) 16},
+  {HB_TAG ('t','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING,          HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS,             (hb_aat_layout_feature_selector_t) 4},
+  {HB_TAG ('t','r','a','d'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE,         HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS,         (hb_aat_layout_feature_selector_t) 16},
+  {HB_TAG ('t','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT,               (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('u','n','i','c'), HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE,             (hb_aat_layout_feature_selector_t) 14,                 (hb_aat_layout_feature_selector_t) 15},
+  {HB_TAG ('v','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT,          (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('v','e','r','t'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION,   HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON,   HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF},
+  {HB_TAG ('v','h','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT,            (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('v','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA,          HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON,         HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF},
+  {HB_TAG ('v','p','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING,            HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT,          (hb_aat_layout_feature_selector_t) 7},
+  {HB_TAG ('v','r','t','2'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION,   HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON,   HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF},
+  {HB_TAG ('z','e','r','o'), HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS,      HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON,                HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF},
 };
 
 const hb_aat_feature_mapping_t *
@@ -130,113 +144,249 @@
 
 
 /*
- * morx/kerx/trak
+ * hb_aat_apply_context_t
  */
 
-static inline const AAT::morx&
-_get_morx (hb_face_t *face, hb_blob_t **blob = nullptr)
+AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
+						     hb_font_t *font_,
+						     hb_buffer_t *buffer_,
+						     hb_blob_t *blob) :
+						       plan (plan_),
+						       font (font_),
+						       face (font->face),
+						       buffer (buffer_),
+						       sanitizer (),
+						       ankr_table (&Null(AAT::ankr)),
+						       ankr_end (nullptr),
+						       lookup_index (0),
+						       debug_depth (0)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
-  {
-    if (blob)
-      *blob = hb_blob_get_empty ();
-    return Null(AAT::morx);
-  }
-  const AAT::morx& morx = *(hb_ot_face_data (face)->morx.get ());
-  if (blob)
-    *blob = hb_ot_face_data (face)->morx.get_blob ();
-  return morx;
+  sanitizer.init (blob);
+  sanitizer.set_num_glyphs (face->get_num_glyphs ());
+  sanitizer.start_processing ();
+  sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
 }
-static inline const AAT::kerx&
-_get_kerx (hb_face_t *face, hb_blob_t **blob = nullptr)
+
+AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t ()
+{ sanitizer.end_processing (); }
+
+void
+AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_,
+					     const char      *ankr_end_)
 {
-  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 ());
+  ankr_table = ankr_table_;
+  ankr_end = ankr_end_;
 }
 
 
+/*
+ * mort/morx/kerx/trak
+ */
+
+
+void
+hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
+			   hb_aat_map_t *map)
+{
+  const AAT::morx& morx = *mapper->face->table.morx;
+  if (morx.has_data ())
+  {
+    morx.compile_flags (mapper, map);
+    return;
+  }
+
+  const AAT::mort& mort = *mapper->face->table.mort;
+  if (mort.has_data ())
+  {
+    mort.compile_flags (mapper, map);
+    return;
+  }
+}
+
+
+/*
+ * hb_aat_layout_has_substitution:
+ * @face:
+ *
+ * Returns:
+ * Since: 2.3.0
+ */
 hb_bool_t
 hb_aat_layout_has_substitution (hb_face_t *face)
 {
-  return _get_morx (face).has_data ();
+  return face->table.morx->has_data () ||
+	 face->table.mort->has_data ();
 }
 
 void
-hb_aat_layout_substitute (hb_ot_shape_plan_t *plan,
+hb_aat_layout_substitute (const 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);
+  hb_blob_t *morx_blob = font->face->table.morx.get_blob ();
+  const AAT::morx& morx = *morx_blob->as<AAT::morx> ();
+  if (morx.has_data ())
+  {
+    AAT::hb_aat_apply_context_t c (plan, font, buffer, morx_blob);
+    morx.apply (&c);
+    return;
+  }
 
-  AAT::hb_aat_apply_context_t c (plan, font, buffer, blob);
-  morx.apply (&c);
+  hb_blob_t *mort_blob = font->face->table.mort.get_blob ();
+  const AAT::mort& mort = *mort_blob->as<AAT::mort> ();
+  if (mort.has_data ())
+  {
+    AAT::hb_aat_apply_context_t c (plan, font, buffer, mort_blob);
+    mort.apply (&c);
+    return;
+  }
 }
 
+void
+hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer)
+{
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  hb_glyph_position_t *pos = buffer->pos;
+  for (unsigned int i = 0; i < count; i++)
+    if (unlikely (info[i].codepoint == AAT::DELETED_GLYPH))
+      pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0;
+}
 
+static bool
+is_deleted_glyph (const hb_glyph_info_t *info)
+{
+  return info->codepoint == AAT::DELETED_GLYPH;
+}
+
+void
+hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer)
+{
+  hb_ot_layout_delete_glyphs_inplace (buffer, is_deleted_glyph);
+}
+
+/*
+ * hb_aat_layout_has_positioning:
+ * @face:
+ *
+ * Returns:
+ * Since: 2.3.0
+ */
 hb_bool_t
 hb_aat_layout_has_positioning (hb_face_t *face)
 {
-  return _get_kerx (face).has_data ();
+  return face->table.kerx->has_data ();
 }
 
 void
-hb_aat_layout_position (hb_ot_shape_plan_t *plan,
+hb_aat_layout_position (const 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 *kerx_blob = font->face->table.kerx.get_blob ();
+  const AAT::kerx& kerx = *kerx_blob->as<AAT::kerx> ();
 
-  hb_blob_t *ankr_blob;
-  const AAT::ankr& ankr = _get_ankr (font->face, &ankr_blob);
+  hb_blob_t *ankr_blob = font->face->table.ankr.get_blob ();;
+  const AAT::ankr& ankr = *font->face->table.ankr;
 
-  AAT::hb_aat_apply_context_t c (plan, font, buffer, blob,
-				 ankr, ankr_blob->data + ankr_blob->length);
+  AAT::hb_aat_apply_context_t c (plan, font, buffer, kerx_blob);
+  c.set_ankr_table (&ankr, ankr_blob->data + ankr_blob->length);
   kerx.apply (&c);
 }
 
+
+/*
+ * hb_aat_layout_has_tracking:
+ * @face:
+ *
+ * Returns:
+ * Since: 2.3.0
+ */
 hb_bool_t
 hb_aat_layout_has_tracking (hb_face_t *face)
 {
-  return _get_trak (face).has_data ();
+  return face->table.trak->has_data ();
 }
 
 void
-hb_aat_layout_track (hb_ot_shape_plan_t *plan,
+hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
 		     hb_font_t *font,
 		     hb_buffer_t *buffer)
 {
-  const AAT::trak& trak = _get_trak (font->face);
+  const AAT::trak& trak = *font->face->table.trak;
 
   AAT::hb_aat_apply_context_t c (plan, font, buffer);
   trak.apply (&c);
 }
+
+
+hb_language_t
+_hb_aat_language_get (hb_face_t *face,
+		      unsigned int i)
+{
+  return face->table.ltag->get_language (i);
+}
+
+/**
+ * hb_aat_layout_get_feature_types:
+ * @face: a face object
+ * @start_offset: iteration's start offset
+ * @feature_count:(inout) (allow-none): buffer size as input, filled size as output
+ * @features: (out caller-allocates) (array length=feature_count): features buffer
+ *
+ * Return value: Number of all available feature types.
+ *
+ * Since: 2.2.0
+ */
+unsigned int
+hb_aat_layout_get_feature_types (hb_face_t                    *face,
+				 unsigned int                  start_offset,
+				 unsigned int                 *feature_count, /* IN/OUT.  May be NULL. */
+				 hb_aat_layout_feature_type_t *features       /* OUT.     May be NULL. */)
+{
+  return face->table.feat->get_feature_types (start_offset, feature_count, features);
+}
+
+/**
+ * hb_aat_layout_feature_type_get_name_id:
+ * @face: a face object
+ * @feature_type: feature id
+ *
+ * Return value: Name ID index
+ *
+ * Since: 2.2.0
+ */
+hb_ot_name_id_t
+hb_aat_layout_feature_type_get_name_id (hb_face_t                    *face,
+					hb_aat_layout_feature_type_t  feature_type)
+{
+  return face->table.feat->get_feature_name_id (feature_type);
+}
+
+/**
+ * hb_aat_layout_feature_type_get_selectors:
+ * @face:    a face object
+ * @feature_type: feature id
+ * @start_offset:    iteration's start offset
+ * @selector_count: (inout) (allow-none): buffer size as input, filled size as output
+ * @selectors: (out caller-allocates) (array length=selector_count): settings buffer
+ * @default_index: (out) (allow-none): index of default selector if any
+ *
+ * If upon return, @default_index is set to #HB_AAT_LAYOUT_NO_SELECTOR_INDEX, then
+ * the feature type is non-exclusive.  Otherwise, @default_index is the index of
+ * the selector that is selected by default.
+ *
+ * Return value: Number of all available feature selectors.
+ *
+ * Since: 2.2.0
+ */
+unsigned int
+hb_aat_layout_feature_type_get_selector_infos (hb_face_t                             *face,
+					       hb_aat_layout_feature_type_t           feature_type,
+					       unsigned int                           start_offset,
+					       unsigned int                          *selector_count, /* IN/OUT.  May be NULL. */
+					       hb_aat_layout_feature_selector_info_t *selectors,      /* OUT.     May be NULL. */
+					       unsigned int                          *default_index   /* OUT.     May be NULL. */)
+{
+  return face->table.feat->get_selector_infos (feature_type, start_offset, selector_count, selectors, default_index);
+}
diff --git a/src/hb-aat-layout.h b/src/hb-aat-layout.h
new file mode 100644
index 0000000..760aaae
--- /dev/null
+++ b/src/hb-aat-layout.h
@@ -0,0 +1,486 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_AAT_H_IN
+#error "Include <hb-aat.h> instead."
+#endif
+
+#ifndef HB_AAT_LAYOUT_H
+#define HB_AAT_LAYOUT_H
+
+#include "hb.h"
+
+#include "hb-ot.h"
+
+HB_BEGIN_DECLS
+
+/**
+ * hb_aat_layout_feature_type_t:
+ *
+ *
+ * Since: 2.2.0
+ */
+typedef enum
+{
+  HB_AAT_LAYOUT_FEATURE_TYPE_INVALID				= 0xFFFF,
+
+  HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC			= 0,
+  HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES				= 1,
+  HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION			= 2,
+  HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE			= 3,
+  HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION		= 4,
+  HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT		= 5,
+  HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING			= 6,
+  HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE			= 8,
+  HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE			= 9,
+  HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION			= 10,
+  HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS				= 11,
+  HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE	= 13,
+  HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS			= 14,
+  HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS		= 15,
+  HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE			= 16,
+  HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES		= 17,
+  HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE		= 18,
+  HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS			= 19,
+  HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE			= 20,
+  HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE			= 21,
+  HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING			= 22,
+  HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION			= 23,
+  HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE			= 24,
+  HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE			= 25,
+  HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE		= 26,
+  HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE		= 27,
+  HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA				= 28,
+  HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE	= 29,
+  HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE	= 30,
+  HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE	= 31,
+  HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN			= 32,
+  HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT		= 33,
+  HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA			= 34,
+  HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES		= 35,
+  HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES		= 36,
+  HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE				= 37,
+  HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE				= 38,
+  HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE			= 39,
+  HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE		= 103,
+
+  _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
+} hb_aat_layout_feature_type_t;
+
+/**
+ * hb_aat_layout_feature_selector_t:
+ *
+ *
+ * Since: 2.2.0
+ */
+typedef enum
+{
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID			= 0xFFFF,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_ON		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_OFF		= 1,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON		= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF		= 5,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_ON			= 6,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_OFF			= 7,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_ON		= 8,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_OFF		= 9,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_ON		= 10,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_OFF	= 11,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_ON		= 12,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_OFF		= 13,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_ON	= 14,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_OFF	= 15,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_ON		= 16,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_OFF		= 17,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON	= 18,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF	= 19,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON	= 20,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF	= 21,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_UNCONNECTED			= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PARTIALLY_CONNECTED		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CURSIVE			= 2,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_AND_LOWER_CASE		= 0, /* deprecated */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_CAPS			= 1, /* deprecated */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_LOWER_CASE			= 2, /* deprecated */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS			= 3, /* deprecated */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS			= 4, /* deprecated */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS_AND_SMALL_CAPS	= 5, /* deprecated */
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON	= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF	= 1,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_ON	= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_OFF	= 1,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_NUMBERS		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_NUMBERS		= 3,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_ON	= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_OFF	= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_ON		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_OFF		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_ON	= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_OFF	= 5,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_ON		= 6,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_OFF		= 7,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_ON		= 8,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_OFF		= 9,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SHOW_DIACRITICS		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HIDE_DIACRITICS		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DECOMPOSE_DIACRITICS		= 2,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS			= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS			= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS			= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS		= 4,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS			= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS		= 2,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_ON		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_OFF		= 1,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_ON		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_OFF		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_ON		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_OFF		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON		= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF		= 5,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_ON		= 6,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_OFF		= 7,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_ON		= 8,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_OFF		= 9,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_ON		= 10,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_OFF	= 11,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_ON		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_OFF		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_ON	= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_OFF	= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_ON		= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_OFF		= 5,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_ON	= 6,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_OFF	= 7,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_ON			= 8,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_OFF			= 9,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON		= 10,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF		= 11,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ORNAMENTS			= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DINGBATS			= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PI_CHARACTERS			= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_FLEURONS			= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DECORATIVE_BORDERS		= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_INTERNATIONAL_SYMBOLS		= 5,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_MATH_SYMBOLS			= 6,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ALTERNATES			= 0,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL1			= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL2			= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL3			= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL4			= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL5			= 4,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DISPLAY_TEXT			= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ENGRAVED_TEXT			= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ILLUMINATED_CAPS		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS			= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_TALL_CAPS			= 5,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS		= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_ONE		= 5,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_TWO		= 6,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_THREE		= 7,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FOUR		= 8,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FIVE		= 9,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS		= 10,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS		= 11,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS		= 12,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS			= 13,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS	= 14,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS		= 1,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT		= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT		= 5,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT		= 6,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HIRAGANA_TO_KATAKANA		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_KATAKANA_TO_HIRAGANA		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_KANA_TO_ROMANIZATION		= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_HIRAGANA	= 5,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_KATAKANA	= 6,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_ONE	= 7,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_TWO	= 8,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_THREE	= 9,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ANNOTATION			= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_BOX_ANNOTATION			= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ROUNDED_BOX_ANNOTATION		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CIRCLE_ANNOTATION		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_CIRCLE_ANNOTATION	= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PARENTHESIS_ANNOTATION		= 5,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIOD_ANNOTATION		= 6,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMAN_NUMERAL_ANNOTATION	= 7,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAMOND_ANNOTATION		= 8,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_BOX_ANNOTATION	= 9,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_ROUNDED_BOX_ANNOTATION= 10,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_KANA		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_KANA		= 1,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_IDEOGRAPHS		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_IDEOGRAPHS	= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_IDEOGRAPHS		= 2,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_ON	= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_OFF	= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_ON	= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_OFF	= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_ON	= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_OFF	= 5,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_RUBY_KANA			= 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF instead */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA			= 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON instead */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON			= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF			= 3,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_SYMBOL_ALTERNATIVES	= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_ONE		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_TWO		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_THREE		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FOUR		= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FIVE		= 5,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_IDEOGRAPHIC_ALTERNATIVES	= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_ONE		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_TWO		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_THREE		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FOUR		= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FIVE		= 5,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED	= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE	= 1,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN		= 0,    /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN		= 1,    /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF		= 3,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON	= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF	= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON	= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF	= 3,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON	= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF	= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF	= 3,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLISTIC_ALTERNATES	= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON		= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF		= 5,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON		= 6,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF	= 7,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON		= 8,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF		= 9,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON		= 10,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF		= 11,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON		= 12,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF		= 13,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON		= 14,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF	= 15,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON		= 16,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF	= 17,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON		= 18,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF		= 19,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON		= 20,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF		= 21,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON	= 22,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF	= 23,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON	= 24,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF	= 25,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON	= 26,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF	= 27,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON	= 28,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF	= 29,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON	= 30,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF	= 31,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON	= 32,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF	= 33,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON	= 34,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF	= 35,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON	= 36,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF	= 37,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON	= 38,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF	= 39,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON	= 40,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF	= 41,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON	= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF	= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF		= 3,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON	= 4,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF= 5,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS		= 2,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS		= 2,
+
+  /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE */
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_CJK_ROMAN		= 0,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_CJK_ROMAN		= 1,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN		= 2,
+  HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN		= 3,
+
+  _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
+} hb_aat_layout_feature_selector_t;
+
+HB_EXTERN unsigned int
+hb_aat_layout_get_feature_types (hb_face_t                    *face,
+				 unsigned int                  start_offset,
+				 unsigned int                 *feature_count, /* IN/OUT.  May be NULL. */
+				 hb_aat_layout_feature_type_t *features       /* OUT.     May be NULL. */);
+
+HB_EXTERN hb_ot_name_id_t
+hb_aat_layout_feature_type_get_name_id (hb_face_t                    *face,
+					hb_aat_layout_feature_type_t  feature_type);
+
+typedef struct hb_aat_layout_feature_selector_info_t
+{
+  hb_ot_name_id_t			name_id;
+  hb_aat_layout_feature_selector_t	enable;
+  hb_aat_layout_feature_selector_t	disable;
+  /*< private >*/
+  unsigned int				reserved;
+} hb_aat_layout_feature_selector_info_t;
+
+#define HB_AAT_LAYOUT_NO_SELECTOR_INDEX		0xFFFFu
+
+HB_EXTERN unsigned int
+hb_aat_layout_feature_type_get_selector_infos (hb_face_t                             *face,
+					       hb_aat_layout_feature_type_t           feature_type,
+					       unsigned int                           start_offset,
+					       unsigned int                          *selector_count, /* IN/OUT.  May be NULL. */
+					       hb_aat_layout_feature_selector_info_t *selectors,      /* OUT.     May be NULL. */
+					       unsigned int                          *default_index   /* OUT.     May be NULL. */);
+
+
+/*
+ * morx/mort
+ */
+
+HB_EXTERN hb_bool_t
+hb_aat_layout_has_substitution (hb_face_t *face);
+
+
+/*
+ * kerx
+ */
+
+HB_EXTERN hb_bool_t
+hb_aat_layout_has_positioning (hb_face_t *face);
+
+
+/*
+ * trak
+ */
+
+HB_EXTERN hb_bool_t
+hb_aat_layout_has_tracking (hb_face_t *face);
+
+
+HB_END_DECLS
+
+#endif /* HB_AAT_LAYOUT_H */
diff --git a/src/hb-aat-layout.hh b/src/hb-aat-layout.hh
index d0eb019..8346d9f 100644
--- a/src/hb-aat-layout.hh
+++ b/src/hb-aat-layout.hh
@@ -35,11 +35,11 @@
 struct hb_aat_feature_mapping_t
 {
   hb_tag_t otFeatureTag;
-  uint16_t aatFeatureType;
-  uint16_t selectorToEnable;
-  uint16_t selectorToDisable;
+  hb_aat_layout_feature_type_t aatFeatureType;
+  hb_aat_layout_feature_selector_t selectorToEnable;
+  hb_aat_layout_feature_selector_t selectorToDisable;
 
-  static inline int cmp (const void *key_, const void *entry_)
+  static 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_;
@@ -52,29 +52,34 @@
 HB_INTERNAL const hb_aat_feature_mapping_t *
 hb_aat_layout_find_feature_mapping (hb_tag_t tag);
 
-
-HB_INTERNAL hb_bool_t
-hb_aat_layout_has_substitution (hb_face_t *face);
+HB_INTERNAL void
+hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
+			   hb_aat_map_t *map);
 
 HB_INTERNAL void
-hb_aat_layout_substitute (hb_ot_shape_plan_t *plan,
+hb_aat_layout_substitute (const 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_zero_width_deleted_glyphs (hb_buffer_t *buffer);
 
 HB_INTERNAL void
-hb_aat_layout_position (hb_ot_shape_plan_t *plan,
+hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer);
+
+HB_INTERNAL void
+hb_aat_layout_position (const 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_aat_layout_track (const 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 08a1b51..3b00b55 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.hh"
+#include "hb-open-type.hh"
 
 /*
  * ltag -- Language Tag
@@ -36,10 +36,14 @@
 
 namespace AAT {
 
+using namespace OT;
+
 
 struct FTStringRange
 {
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  friend struct ltag;
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && (base+tag).sanitize (c, length));
@@ -56,12 +60,21 @@
 
 struct ltag
 {
-  static const hb_tag_t tableTag = HB_AAT_TAG_ltag;
+  enum { tableTag = HB_AAT_TAG_ltag };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  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);
+  }
+
+  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..1c65a67
--- /dev/null
+++ b/src/hb-aat-map.cc
@@ -0,0 +1,68 @@
+/*
+ * 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)
+{
+  if (tag == HB_TAG ('a','a','l','t'))
+  {
+    feature_info_t *info = features.push();
+    info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
+    info->setting = (hb_aat_layout_feature_selector_t) value;
+    return;
+  }
+
+  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)
+{
+  /* 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..fa312db
--- /dev/null
+++ b/src/hb-aat-map.hh
@@ -0,0 +1,91 @@
+/*
+ * 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_aat_map_t
+{
+  friend struct hb_aat_map_builder_t;
+
+  public:
+
+  void init ()
+  {
+    memset (this, 0, sizeof (*this));
+    chain_flags.init ();
+  }
+  void fini () { 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_) {}
+
+  HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value=1);
+
+  HB_INTERNAL void compile (hb_aat_map_t  &m);
+
+  public:
+  struct feature_info_t
+  {
+    hb_aat_layout_feature_type_t  type;
+    hb_aat_layout_feature_selector_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 (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-aat.h b/src/hb-aat.h
new file mode 100644
index 0000000..c14313d
--- /dev/null
+++ b/src/hb-aat.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_AAT_H
+#define HB_AAT_H
+#define HB_AAT_H_IN
+
+#include "hb.h"
+
+#include "hb-aat-layout.h"
+
+HB_BEGIN_DECLS
+
+HB_END_DECLS
+
+#undef HB_AAT_H_IN
+#endif /* HB_AAT_H */
diff --git a/src/hb-array.hh b/src/hb-array.hh
new file mode 100644
index 0000000..d4df165
--- /dev/null
+++ b/src/hb-array.hh
@@ -0,0 +1,274 @@
+/*
+ * 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_ARRAY_HH
+#define HB_ARRAY_HH
+
+#include "hb.hh"
+
+
+template <typename Type>
+struct hb_sorted_array_t;
+
+template <typename Type>
+struct hb_array_t
+{
+  typedef Type ItemType;
+  enum { item_size = hb_static_size (Type) };
+
+  /*
+   * Constructors.
+   */
+  hb_array_t () : arrayZ (nullptr), len (0) {}
+  hb_array_t (const hb_array_t &o) : arrayZ (o.arrayZ), len (o.len) {}
+  hb_array_t (Type *array_, unsigned int len_) : arrayZ (array_), len (len_) {}
+  template <unsigned int len_> hb_array_t (Type (&array_)[len_]) : arrayZ (array_), len (len_) {}
+
+  /*
+   * Operators.
+   */
+
+  Type& operator [] (int i_) const
+  {
+    unsigned int i = (unsigned int) i_;
+    if (unlikely (i >= len)) return CrapOrNull(Type);
+    return arrayZ[i];
+  }
+
+  explicit_operator bool () const { return len; }
+  Type * operator & () const { return arrayZ; }
+  Type & operator * () { return (this->operator [])[0]; }
+  operator hb_array_t<const Type> () { return hb_array_t<const Type> (arrayZ, len); }
+  template <typename T> operator T * () const { return arrayZ; }
+
+  hb_array_t<Type> & operator += (unsigned int count)
+  {
+    if (unlikely (count > len))
+      count = len;
+    len -= count;
+    arrayZ += count;
+    return *this;
+  }
+  hb_array_t<Type> & operator -= (unsigned int count)
+  {
+    if (unlikely (count > len))
+      count = len;
+    len -= count;
+    return *this;
+  }
+  hb_array_t<Type> & operator ++ () { *this += 1; }
+  hb_array_t<Type> & operator -- () { *this -= 1; }
+  hb_array_t<Type> operator + (unsigned int count)
+  { hb_array_t<Type> copy (*this); *this += count; return copy; }
+  hb_array_t<Type> operator - (unsigned int count)
+  { hb_array_t<Type> copy (*this); *this -= count; return copy; }
+  hb_array_t<Type>  operator ++ (int)
+  { hb_array_t<Type> copy (*this); ++*this; return copy; }
+  hb_array_t<Type>  operator -- (int)
+  { hb_array_t<Type> copy (*this); --*this; return copy; }
+
+  /*
+   * Compare, Sort, and Search.
+   */
+
+  /* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */
+  int cmp (const hb_array_t<Type> &a) const
+  {
+    if (len != a.len)
+      return (int) a.len - (int) len;
+    return hb_memcmp (a.arrayZ, arrayZ, get_size ());
+  }
+  static int cmp (const void *pa, const void *pb)
+  {
+    hb_array_t<Type> *a = (hb_array_t<Type> *) pa;
+    hb_array_t<Type> *b = (hb_array_t<Type> *) pb;
+    return b->cmp (*a);
+  }
+
+  template <typename T>
+  Type *lsearch (const T &x, Type *not_found = nullptr)
+  {
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!this->arrayZ[i].cmp (x))
+	return &this->arrayZ[i];
+    return not_found;
+  }
+  template <typename T>
+  const Type *lsearch (const T &x, const Type *not_found = nullptr) const
+  {
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!this->arrayZ[i].cmp (x))
+	return &this->arrayZ[i];
+    return not_found;
+  }
+
+  hb_sorted_array_t<Type> qsort (int (*cmp_)(const void*, const void*))
+  {
+    ::qsort (arrayZ, len, item_size, cmp_);
+    return hb_sorted_array_t<Type> (*this);
+  }
+  hb_sorted_array_t<Type> qsort ()
+  {
+    ::qsort (arrayZ, len, item_size, Type::cmp);
+    return hb_sorted_array_t<Type> (*this);
+  }
+  void qsort (unsigned int start, unsigned int end)
+  {
+    end = MIN (end, len);
+    assert (start <= end);
+    ::qsort (arrayZ + start, end - start, item_size, Type::cmp);
+  }
+
+  /*
+   * Other methods.
+   */
+
+  unsigned int get_size () const { return len * item_size; }
+
+  hb_array_t<Type> sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
+  {
+    if (!start_offset && !seg_count)
+      return *this;
+
+    unsigned int count = len;
+    if (unlikely (start_offset > count))
+      count = 0;
+    else
+      count -= start_offset;
+    if (seg_count)
+      count = *seg_count = MIN (count, *seg_count);
+    return hb_array_t<Type> (arrayZ + start_offset, count);
+  }
+  hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const
+  { return sub_array (start_offset, &seg_count); }
+
+  /* Only call if you allocated the underlying array using malloc() or similar. */
+  void free ()
+  { ::free ((void *) arrayZ); arrayZ = nullptr; len = 0; }
+
+  template <typename hb_sanitize_context_t>
+  bool sanitize (hb_sanitize_context_t *c) const
+  { return c->check_array (arrayZ, len); }
+
+  /*
+   * Members
+   */
+
+  public:
+  Type *arrayZ;
+  unsigned int len;
+};
+template <typename T>
+inline hb_array_t<T> hb_array (T *array, unsigned int len)
+{ return hb_array_t<T> (array, len); }
+
+
+enum hb_bfind_not_found_t
+{
+  HB_BFIND_NOT_FOUND_DONT_STORE,
+  HB_BFIND_NOT_FOUND_STORE,
+  HB_BFIND_NOT_FOUND_STORE_CLOSEST,
+};
+
+template <typename Type>
+struct hb_sorted_array_t : hb_array_t<Type>
+{
+  hb_sorted_array_t () : hb_array_t<Type> () {}
+  hb_sorted_array_t (const hb_array_t<Type> &o) : hb_array_t<Type> (o) {}
+  hb_sorted_array_t (Type *array_, unsigned int len_) : hb_array_t<Type> (array_, len_) {}
+
+  hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
+  { return hb_sorted_array_t<Type> (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); }
+  hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const
+  { return sub_array (start_offset, &seg_count); }
+
+  template <typename T>
+  Type *bsearch (const T &x, Type *not_found = nullptr)
+  {
+    unsigned int i;
+    return bfind (x, &i) ? &this->arrayZ[i] : not_found;
+  }
+  template <typename T>
+  const Type *bsearch (const T &x, const Type *not_found = nullptr) const
+  {
+    unsigned int i;
+    return bfind (x, &i) ? &this->arrayZ[i] : not_found;
+  }
+  template <typename T>
+  bool bfind (const T &x, unsigned int *i = nullptr,
+		     hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
+		     unsigned int to_store = (unsigned int) -1) const
+  {
+    int min = 0, max = (int) this->len - 1;
+    const Type *array = this->arrayZ;
+    while (min <= max)
+    {
+      int mid = ((unsigned int) min + (unsigned int) max) / 2;
+      int c = array[mid].cmp (x);
+      if (c < 0)
+        max = mid - 1;
+      else if (c > 0)
+        min = mid + 1;
+      else
+      {
+	if (i)
+	  *i = mid;
+	return true;
+      }
+    }
+    if (i)
+    {
+      switch (not_found)
+      {
+	case HB_BFIND_NOT_FOUND_DONT_STORE:
+	  break;
+
+	case HB_BFIND_NOT_FOUND_STORE:
+	  *i = to_store;
+	  break;
+
+	case HB_BFIND_NOT_FOUND_STORE_CLOSEST:
+	  if (max < 0 || (max < (int) this->len && array[max].cmp (x) > 0))
+	    max++;
+	  *i = max;
+	  break;
+      }
+    }
+    return false;
+  }
+};
+template <typename T>
+inline hb_sorted_array_t<T> hb_sorted_array (T *array, unsigned int len)
+{ return hb_sorted_array_t<T> (array, len); }
+
+
+typedef hb_array_t<const char> hb_bytes_t;
+typedef hb_array_t<const unsigned char> hb_ubytes_t;
+
+
+#endif /* HB_ARRAY_HH */
diff --git a/src/hb-atomic.hh b/src/hb-atomic.hh
index 5cb7ca5..9321932 100644
--- a/src/hb-atomic.hh
+++ b/src/hb-atomic.hh
@@ -100,11 +100,11 @@
 #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__))
+#elif !defined(HB_NO_MT) && defined(_WIN32)
 
 #include <windows.h>
 
-static inline void _hb_memory_barrier (void)
+static inline void _hb_memory_barrier ()
 {
 #if !defined(MemoryBarrier)
   /* MinGW has a convoluted history of supporting MemoryBarrier. */
@@ -143,7 +143,7 @@
 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);
+  int result = atomic_add_int_nv ((uint_t *) AI, V) - V;
   _hb_memory_r_barrier ();
   return result;
 }
@@ -257,43 +257,43 @@
 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; }
+inline int hb_atomic_int_impl_get (const 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; }
+inline void *hb_atomic_ptr_impl_get (void ** const 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); }
+  void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
+  void set (int v_) { hb_atomic_int_impl_set (&v, v_); }
+  int get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); }
+  int get () const { return hb_atomic_int_impl_get (&v); }
+  int inc () { return hb_atomic_int_impl_add (&v,  1); }
+  int dec () { return hb_atomic_int_impl_add (&v, -1); }
 
-  mutable int v;
+  int v;
 };
 
 
-template <typename T> struct hb_remove_ptr_t { typedef T value; };
-template <typename T> struct hb_remove_ptr_t<T *> { typedef T value; };
-
 #define HB_ATOMIC_PTR_INIT(V)          {V}
 template <typename P>
 struct hb_atomic_ptr_t
 {
-  typedef typename hb_remove_ptr_t<P>::value T;
+  typedef typename hb_remove_pointer (P) 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_); }
+  void init (T* v_ = nullptr) { set_relaxed (v_); }
+  void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); }
+  T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); }
+  T *get () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); }
+  bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }
 
-  mutable T *v;
+  T * operator -> () const                    { return get (); }
+  template <typename C> operator C * () const { return get (); }
+
+  T *v;
 };
 
 
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index 368491c..b53b20b 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -40,19 +40,18 @@
 #include <stdlib.h>
 
 
-DEFINE_NULL_INSTANCE (hb_blob_t) =
-{
-  HB_OBJECT_HEADER_STATIC,
+/**
+ * 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.
+ **/
 
-  true, /* immutable */
-
-  nullptr, /* data */
-  0, /* length */
-  HB_MEMORY_MODE_READONLY, /* mode */
-
-  nullptr, /* user_data */
-  nullptr  /* destroy */
-};
 
 /**
  * hb_blob_create: (skip)
@@ -138,7 +137,7 @@
 {
   hb_blob_t *blob;
 
-  if (!length || offset >= parent->length)
+  if (!length || !parent || offset >= parent->length)
     return hb_blob_get_empty ();
 
   hb_blob_make_immutable (parent);
@@ -189,7 +188,7 @@
  * Since: 0.9.2
  **/
 hb_blob_t *
-hb_blob_get_empty (void)
+hb_blob_get_empty ()
 {
   return const_cast<hb_blob_t *> (&Null(hb_blob_t));
 }
@@ -286,12 +285,10 @@
 void
 hb_blob_make_immutable (hb_blob_t *blob)
 {
-  if (hb_object_is_inert (blob))
-    return;
-  if (blob->immutable)
+  if (hb_object_is_immutable (blob))
     return;
 
-  blob->immutable = true;
+  hb_object_make_immutable (blob);
 }
 
 /**
@@ -307,7 +304,7 @@
 hb_bool_t
 hb_blob_is_immutable (hb_blob_t *blob)
 {
-  return blob->immutable;
+  return hb_object_is_immutable (blob);
 }
 
 
@@ -381,7 +378,7 @@
 
 
 bool
-hb_blob_t::try_make_writable_inplace_unix (void)
+hb_blob_t::try_make_writable_inplace_unix ()
 {
 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
   uintptr_t pagesize = -1, mask, length;
@@ -424,7 +421,7 @@
 }
 
 bool
-hb_blob_t::try_make_writable_inplace (void)
+hb_blob_t::try_make_writable_inplace ()
 {
   DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n");
 
@@ -439,9 +436,9 @@
 }
 
 bool
-hb_blob_t::try_make_writable (void)
+hb_blob_t::try_make_writable ()
 {
-  if (this->immutable)
+  if (hb_object_is_immutable (this))
     return false;
 
   if (this->mode == HB_MEMORY_MODE_WRITABLE)
@@ -484,7 +481,7 @@
 # include <fcntl.h>
 #endif
 
-#if defined(_WIN32) || defined(__CYGWIN__)
+#ifdef _WIN32
 # include <windows.h>
 #else
 # ifndef O_BINARY
@@ -500,19 +497,19 @@
 {
   char *contents;
   unsigned long length;
-#if defined(_WIN32) || defined(__CYGWIN__)
+#ifdef _WIN32
   HANDLE mapping;
 #endif
 };
 
-#if (defined(HAVE_MMAP) || defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_MMAP)
+#if (defined(HAVE_MMAP) || defined(_WIN32)) && !defined(HB_NO_MMAP)
 static void
 _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__)
+#elif defined(_WIN32)
   UnmapViewOfFile (file->contents);
   CloseHandle (file->mapping);
 #else
@@ -563,7 +560,7 @@
 fail_without_close:
   free (file);
 
-#elif (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_MMAP)
+#elif defined(_WIN32) && !defined(HB_NO_MMAP)
   hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t));
   if (unlikely (!file)) return hb_blob_get_empty ();
 
diff --git a/src/hb-blob.hh b/src/hb-blob.hh
index bee8c97..4ea13f8 100644
--- a/src/hb-blob.hh
+++ b/src/hb-blob.hh
@@ -38,12 +38,9 @@
 
 struct hb_blob_t
 {
-  inline void fini_shallow (void)
-  {
-    destroy_user_data ();
-  }
+  void fini_shallow () { destroy_user_data (); }
 
-  inline void destroy_user_data (void)
+  void destroy_user_data ()
   {
     if (destroy)
     {
@@ -53,25 +50,20 @@
     }
   }
 
-  HB_INTERNAL bool try_make_writable (void);
-  HB_INTERNAL bool try_make_writable_inplace (void);
-  HB_INTERNAL bool try_make_writable_inplace_unix (void);
+  HB_INTERNAL bool try_make_writable ();
+  HB_INTERNAL bool try_make_writable_inplace ();
+  HB_INTERNAL bool try_make_writable_inplace_unix ();
 
   template <typename Type>
-  inline const Type* as (void) const
+  const Type* as () const
   {
-    return unlikely (!data) ? &Null(Type) : reinterpret_cast<const Type *> (data);
+    return length < hb_null_size (Type) ? &Null(Type) : reinterpret_cast<const Type *> (data);
   }
-  inline hb_bytes_t as_bytes (void) const
-  {
-    return hb_bytes_t (data, length);
-  }
+  hb_bytes_t as_bytes () const
+  { return hb_bytes_t (data, length); }
 
   public:
   hb_object_header_t header;
-  ASSERT_POD ();
-
-  bool immutable;
 
   const char *data;
   unsigned int length;
@@ -80,7 +72,30 @@
   void *user_data;
   hb_destroy_func_t destroy;
 };
-DECLARE_NULL_INSTANCE (hb_blob_t);
+
+
+/*
+ * hb_blob_ptr_t
+ */
+
+template <typename P>
+struct hb_blob_ptr_t
+{
+  typedef typename hb_remove_pointer (P) T;
+
+  hb_blob_ptr_t (hb_blob_t *b_ = nullptr) : b (b_) {}
+  hb_blob_t * operator = (hb_blob_t *b_) { return b = b_; }
+  const T * operator -> () const { return get (); }
+  const T & operator * () const  { return *get (); }
+  template <typename C> operator const C * () const { return get (); }
+  operator const char * () const { return (const char *) get (); }
+  const T * get () const { return b->as<T> (); }
+  hb_blob_t * get_blob () const { return b.get_raw (); }
+  unsigned int get_length () const { return b.get ()->length; }
+  void destroy () { hb_blob_destroy (b.get ()); b = nullptr; }
+
+  hb_nonnull_ptr_t<hb_blob_t> b;
+};
 
 
 #endif /* HB_BLOB_HH */
diff --git a/src/hb-buffer-serialize.cc b/src/hb-buffer-serialize.cc
index c1d82ab..6e265e8 100644
--- a/src/hb-buffer-serialize.cc
+++ b/src/hb-buffer-serialize.cc
@@ -44,7 +44,7 @@
  * Since: 0.9.7
  **/
 const char **
-hb_buffer_serialize_list_formats (void)
+hb_buffer_serialize_list_formats ()
 {
   return 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
@@ -246,7 +246,7 @@
 
     if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS)
     {
-      if (info[i].mask &HB_GLYPH_FLAG_DEFINED)
+      if (info[i].mask & HB_GLYPH_FLAG_DEFINED)
 	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED));
     }
 
@@ -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
  **/
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index ce9b053..2dc02e9 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -33,14 +33,15 @@
 
 /**
  * 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.
@@ -214,9 +215,9 @@
 /* HarfBuzz-Internal API */
 
 void
-hb_buffer_t::reset (void)
+hb_buffer_t::reset ()
 {
-  if (unlikely (hb_object_is_inert (this)))
+  if (unlikely (hb_object_is_immutable (this)))
     return;
 
   hb_unicode_funcs_destroy (unicode);
@@ -229,9 +230,9 @@
 }
 
 void
-hb_buffer_t::clear (void)
+hb_buffer_t::clear ()
 {
-  if (unlikely (hb_object_is_inert (this)))
+  if (unlikely (hb_object_is_immutable (this)))
     return;
 
   hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT;
@@ -286,9 +287,9 @@
 
 
 void
-hb_buffer_t::remove_output (void)
+hb_buffer_t::remove_output ()
 {
-  if (unlikely (hb_object_is_inert (this)))
+  if (unlikely (hb_object_is_immutable (this)))
     return;
 
   have_output = false;
@@ -299,9 +300,9 @@
 }
 
 void
-hb_buffer_t::clear_output (void)
+hb_buffer_t::clear_output ()
 {
-  if (unlikely (hb_object_is_inert (this)))
+  if (unlikely (hb_object_is_immutable (this)))
     return;
 
   have_output = true;
@@ -312,9 +313,9 @@
 }
 
 void
-hb_buffer_t::clear_positions (void)
+hb_buffer_t::clear_positions ()
 {
-  if (unlikely (hb_object_is_inert (this)))
+  if (unlikely (hb_object_is_immutable (this)))
     return;
 
   have_output = false;
@@ -327,7 +328,7 @@
 }
 
 void
-hb_buffer_t::swap_buffers (void)
+hb_buffer_t::swap_buffers ()
 {
   if (unlikely (!successful)) return;
 
@@ -479,7 +480,7 @@
 }
 
 void
-hb_buffer_t::reverse (void)
+hb_buffer_t::reverse ()
 {
   if (unlikely (!len))
     return;
@@ -488,7 +489,7 @@
 }
 
 void
-hb_buffer_t::reverse_clusters (void)
+hb_buffer_t::reverse_clusters ()
 {
   unsigned int i, start, count, last_cluster;
 
@@ -635,7 +636,7 @@
 }
 
 void
-hb_buffer_t::guess_segment_properties (void)
+hb_buffer_t::guess_segment_properties ()
 {
   assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
 	  (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
@@ -708,7 +709,7 @@
  * Since: 0.9.2
  **/
 hb_buffer_t *
-hb_buffer_create (void)
+hb_buffer_create ()
 {
   hb_buffer_t *buffer;
 
@@ -726,14 +727,14 @@
 /**
  * hb_buffer_get_empty:
  *
- * 
+ *
  *
  * Return value: (transfer full):
  *
  * Since: 0.9.2
  **/
 hb_buffer_t *
-hb_buffer_get_empty (void)
+hb_buffer_get_empty ()
 {
   return const_cast<hb_buffer_t *> (&Null(hb_buffer_t));
 }
@@ -784,14 +785,14 @@
 /**
  * hb_buffer_set_user_data: (skip)
  * @buffer: an #hb_buffer_t.
- * @key: 
- * @data: 
- * @destroy: 
- * @replace: 
+ * @key:
+ * @data:
+ * @destroy:
+ * @replace:
  *
- * 
  *
- * Return value: 
+ *
+ * Return value:
  *
  * Since: 0.9.2
  **/
@@ -808,11 +809,11 @@
 /**
  * hb_buffer_get_user_data: (skip)
  * @buffer: an #hb_buffer_t.
- * @key: 
+ * @key:
  *
- * 
  *
- * Return value: 
+ *
+ * Return value:
  *
  * Since: 0.9.2
  **/
@@ -862,9 +863,9 @@
 /**
  * hb_buffer_set_unicode_funcs:
  * @buffer: an #hb_buffer_t.
- * @unicode_funcs: 
+ * @unicode_funcs:
  *
- * 
+ *
  *
  * Since: 0.9.2
  **/
@@ -872,7 +873,7 @@
 hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
 			     hb_unicode_funcs_t *unicode_funcs)
 {
-  if (unlikely (hb_object_is_inert (buffer)))
+  if (unlikely (hb_object_is_immutable (buffer)))
     return;
 
   if (!unicode_funcs)
@@ -887,9 +888,9 @@
  * hb_buffer_get_unicode_funcs:
  * @buffer: an #hb_buffer_t.
  *
- * 
  *
- * Return value: 
+ *
+ * Return value:
  *
  * Since: 0.9.2
  **/
@@ -919,7 +920,7 @@
 			 hb_direction_t  direction)
 
 {
-  if (unlikely (hb_object_is_inert (buffer)))
+  if (unlikely (hb_object_is_immutable (buffer)))
     return;
 
   buffer->props.direction = direction;
@@ -963,7 +964,7 @@
 hb_buffer_set_script (hb_buffer_t *buffer,
 		      hb_script_t  script)
 {
-  if (unlikely (hb_object_is_inert (buffer)))
+  if (unlikely (hb_object_is_immutable (buffer)))
     return;
 
   buffer->props.script = script;
@@ -1007,7 +1008,7 @@
 hb_buffer_set_language (hb_buffer_t   *buffer,
 			hb_language_t  language)
 {
-  if (unlikely (hb_object_is_inert (buffer)))
+  if (unlikely (hb_object_is_immutable (buffer)))
     return;
 
   buffer->props.language = language;
@@ -1045,7 +1046,7 @@
 hb_buffer_set_segment_properties (hb_buffer_t *buffer,
 				  const hb_segment_properties_t *props)
 {
-  if (unlikely (hb_object_is_inert (buffer)))
+  if (unlikely (hb_object_is_immutable (buffer)))
     return;
 
   buffer->props = *props;
@@ -1081,7 +1082,7 @@
 hb_buffer_set_flags (hb_buffer_t       *buffer,
 		     hb_buffer_flags_t  flags)
 {
-  if (unlikely (hb_object_is_inert (buffer)))
+  if (unlikely (hb_object_is_immutable (buffer)))
     return;
 
   buffer->flags = flags;
@@ -1093,7 +1094,7 @@
  *
  * See hb_buffer_set_flags().
  *
- * Return value: 
+ * Return value:
  * The @buffer flags.
  *
  * Since: 0.9.7
@@ -1107,9 +1108,9 @@
 /**
  * hb_buffer_set_cluster_level:
  * @buffer: an #hb_buffer_t.
- * @cluster_level: 
+ * @cluster_level:
  *
- * 
+ *
  *
  * Since: 0.9.42
  **/
@@ -1117,7 +1118,7 @@
 hb_buffer_set_cluster_level (hb_buffer_t       *buffer,
 		     hb_buffer_cluster_level_t  cluster_level)
 {
-  if (unlikely (hb_object_is_inert (buffer)))
+  if (unlikely (hb_object_is_immutable (buffer)))
     return;
 
   buffer->cluster_level = cluster_level;
@@ -1127,9 +1128,9 @@
  * hb_buffer_get_cluster_level:
  * @buffer: an #hb_buffer_t.
  *
- * 
  *
- * Return value: 
+ *
+ * Return value:
  *
  * Since: 0.9.42
  **/
@@ -1156,7 +1157,7 @@
 hb_buffer_set_replacement_codepoint (hb_buffer_t    *buffer,
 				     hb_codepoint_t  replacement)
 {
-  if (unlikely (hb_object_is_inert (buffer)))
+  if (unlikely (hb_object_is_immutable (buffer)))
     return;
 
   buffer->replacement = replacement;
@@ -1168,7 +1169,7 @@
  *
  * See hb_buffer_set_replacement_codepoint().
  *
- * Return value: 
+ * Return value:
  * The @buffer replacement #hb_codepoint_t.
  *
  * Since: 0.9.31
@@ -1196,7 +1197,7 @@
 hb_buffer_set_invisible_glyph (hb_buffer_t    *buffer,
 			       hb_codepoint_t  invisible)
 {
-  if (unlikely (hb_object_is_inert (buffer)))
+  if (unlikely (hb_object_is_immutable (buffer)))
     return;
 
   buffer->invisible = invisible;
@@ -1208,7 +1209,7 @@
  *
  * See hb_buffer_set_invisible_glyph().
  *
- * Return value: 
+ * Return value:
  * The @buffer invisible #hb_codepoint_t.
  *
  * Since: 2.0.0
@@ -1319,7 +1320,7 @@
  * Similar to hb_buffer_pre_allocate(), but clears any new items added at the
  * end.
  *
- * Return value: 
+ * Return value:
  * %true if @buffer memory allocation succeeded, %false otherwise.
  *
  * Since: 0.9.2
@@ -1328,7 +1329,7 @@
 hb_buffer_set_length (hb_buffer_t  *buffer,
 		      unsigned int  length)
 {
-  if (unlikely (hb_object_is_inert (buffer)))
+  if (unlikely (hb_object_is_immutable (buffer)))
     return length == 0;
 
   if (!buffer->ensure (length))
@@ -1534,7 +1535,7 @@
   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
 	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
 
-  if (unlikely (hb_object_is_inert (buffer)))
+  if (unlikely (hb_object_is_immutable (buffer)))
     return;
 
   if (text_length == -1)
@@ -1665,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);
 }
 
 /**
@@ -1726,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);
 }
 
 
@@ -1999,7 +2000,7 @@
  * @user_data:
  * @destroy:
  *
- * 
+ *
  *
  * Since: 1.1.3
  **/
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index d0aed02..f989d25 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -89,11 +89,14 @@
  * 				   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.
+ *
+ * Since: 1.5.0
  */
 typedef enum { /*< flags >*/
   HB_GLYPH_FLAG_UNSAFE_TO_BREAK		= 0x00000001,
 
-  HB_GLYPH_FLAG_DEFINED			= 0x00000001 /*< skip >*/ /* OR of all defined flags */
+  HB_GLYPH_FLAG_DEFINED			= 0x00000001 /* OR of all defined flags */
 } hb_glyph_flags_t;
 
 HB_EXTERN hb_glyph_flags_t
diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh
index 0d888e1..8a3170c 100644
--- a/src/hb-buffer.hh
+++ b/src/hb-buffer.hh
@@ -86,7 +86,6 @@
 struct hb_buffer_t
 {
   hb_object_header_t header;
-  ASSERT_POD ();
 
   /* Information about how the text in the buffer should be treated */
   hb_unicode_funcs_t *unicode; /* Unicode functions */
@@ -138,7 +137,9 @@
 
   /* Methods */
 
-  inline void allocate_var (unsigned int start, unsigned int count)
+  bool in_error () const { return !successful; }
+
+  void allocate_var (unsigned int start, unsigned int count)
   {
 #ifndef HB_NDEBUG
     unsigned int end = start + count;
@@ -148,7 +149,7 @@
     allocated_var_bits |= bits;
 #endif
   }
-  inline void deallocate_var (unsigned int start, unsigned int count)
+  void deallocate_var (unsigned int start, unsigned int count)
   {
 #ifndef HB_NDEBUG
     unsigned int end = start + count;
@@ -158,7 +159,7 @@
     allocated_var_bits &= ~bits;
 #endif
   }
-  inline void assert_var (unsigned int start, unsigned int count)
+  void assert_var (unsigned int start, unsigned int count)
   {
 #ifndef HB_NDEBUG
     unsigned int end = start + count;
@@ -167,53 +168,51 @@
     assert (bits == (allocated_var_bits & bits));
 #endif
   }
-  inline void deallocate_var_all (void)
+  void deallocate_var_all ()
   {
 #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]; }
+  hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
+  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]; }
+  hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
+  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]; }
+  hb_glyph_info_t &prev ()      { return out_info[out_len ? out_len - 1 : 0]; }
+  hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; }
 
-  inline bool has_separate_output (void) const { return info != out_info; }
+  bool has_separate_output () const { return info != out_info; }
 
 
-  HB_INTERNAL void reset (void);
-  HB_INTERNAL void clear (void);
+  HB_INTERNAL void reset ();
+  HB_INTERNAL void clear ();
 
-  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++; }
+  unsigned int backtrack_len () const { return have_output? out_len : idx; }
+  unsigned int lookahead_len () const { return len - idx; }
+  unsigned int next_serial () { 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 reverse ();
+  HB_INTERNAL void reverse_clusters ();
+  HB_INTERNAL void guess_segment_properties ();
 
-  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 swap_buffers ();
+  HB_INTERNAL void remove_output ();
+  HB_INTERNAL void clear_output ();
+  HB_INTERNAL void clear_positions ();
 
   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)
+  void replace_glyph (hb_codepoint_t glyph_index)
   {
     if (unlikely (out_info != info || out_len != idx)) {
       if (unlikely (!make_room_for (1, 1))) return;
@@ -225,7 +224,7 @@
     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)
+  hb_glyph_info_t & output_glyph (hb_codepoint_t glyph_index)
   {
     if (unlikely (!make_room_for (0, 1))) return Crap(hb_glyph_info_t);
 
@@ -239,7 +238,7 @@
 
     return out_info[out_len - 1];
   }
-  inline void output_info (const hb_glyph_info_t &glyph_info)
+  void output_info (const hb_glyph_info_t &glyph_info)
   {
     if (unlikely (!make_room_for (0, 1))) return;
 
@@ -248,7 +247,7 @@
     out_len++;
   }
   /* Copies glyph at idx to output but doesn't advance idx */
-  inline void copy_glyph (void)
+  void copy_glyph ()
   {
     if (unlikely (!make_room_for (0, 1))) return;
 
@@ -258,8 +257,8 @@
   }
   /* Copies glyph at idx to output and advance idx.
    * If there's no output, just advance idx. */
-  inline void
-  next_glyph (void)
+  void
+  next_glyph ()
   {
     if (have_output)
     {
@@ -275,7 +274,7 @@
   }
   /* Copies n glyphs at idx to output and advance idx.
    * If there's no output, just advance idx. */
-  inline void
+  void
   next_glyphs (unsigned int n)
   {
     if (have_output)
@@ -291,16 +290,13 @@
     idx += n;
   }
   /* Advance idx without copying to output. */
-  inline void skip_glyph (void)
-  {
-    idx++;
-  }
-  inline void reset_masks (hb_mask_t mask)
+  void skip_glyph () { idx++; }
+  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)
+  void add_masks (hb_mask_t mask)
   {
     for (unsigned int j = 0; j < len; j++)
       info[j].mask |= mask;
@@ -308,7 +304,7 @@
   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)
+  void merge_clusters (unsigned int start, unsigned int end)
   {
     if (end - start < 2)
       return;
@@ -317,9 +313,9 @@
   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);
+  HB_INTERNAL void delete_glyph ();
 
-  inline void unsafe_to_break (unsigned int start,
+  void unsafe_to_break (unsigned int start,
 			       unsigned int end)
   {
     if (end - start < 2)
@@ -335,10 +331,10 @@
 
   HB_INTERNAL bool enlarge (unsigned int size);
 
-  inline bool ensure (unsigned int size)
+  bool ensure (unsigned int size)
   { return likely (!size || size < allocated) ? true : enlarge (size); }
 
-  inline bool ensure_inplace (unsigned int size)
+  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);
@@ -347,12 +343,12 @@
   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; }
+  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)
+  bool messaging () { return unlikely (message_func); }
+  bool message (hb_font_t *font, const char *fmt, ...) HB_PRINTF_FUNC(3, 4)
   {
     if (!messaging ())
       return true;
@@ -364,7 +360,7 @@
   }
   HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0);
 
-  static inline void
+  static void
   set_cluster (hb_glyph_info_t &inf, unsigned int cluster, unsigned int mask = 0)
   {
     if (inf.cluster != cluster)
@@ -377,7 +373,7 @@
     inf.cluster = cluster;
   }
 
-  inline int
+  int
   _unsafe_to_break_find_min_cluster (const hb_glyph_info_t *infos,
 				     unsigned int start, unsigned int end,
 				     unsigned int cluster) const
@@ -386,7 +382,7 @@
       cluster = MIN<unsigned int> (cluster, infos[i].cluster);
     return cluster;
   }
-  inline void
+  void
   _unsafe_to_break_set_mask (hb_glyph_info_t *infos,
 			     unsigned int start, unsigned int end,
 			     unsigned int cluster)
@@ -399,13 +395,9 @@
       }
   }
 
-  inline void
-  unsafe_to_break_all (void)
-  {
-    unsafe_to_break_impl (0, len);
-  }
-  inline void
-  safe_to_break_all (void)
+  void unsafe_to_break_all ()
+  { unsafe_to_break_impl (0, len); }
+  void safe_to_break_all ()
   {
     for (unsigned int i = 0; i < len; i++)
       info[i].mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
diff --git a/src/hb-cache.hh b/src/hb-cache.hh
index eb48f18..bf26d96 100644
--- a/src/hb-cache.hh
+++ b/src/hb-cache.hh
@@ -39,16 +39,16 @@
   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) {}
+  void init () { clear (); }
+  void fini () {}
 
-  inline void clear (void)
+  void clear ()
   {
     for (unsigned i = 0; i < ARRAY_LENGTH (values); i++)
       values[i].set_relaxed (-1);
   }
 
-  inline bool get (unsigned int key, unsigned int *value) const
+  bool get (unsigned int key, unsigned int *value) const
   {
     unsigned int k = key & ((1u<<cache_bits)-1);
     unsigned int v = values[k].get_relaxed ();
@@ -59,7 +59,7 @@
     return true;
   }
 
-  inline bool set (unsigned int key, unsigned int value)
+  bool set (unsigned int key, unsigned int value)
   {
     if (unlikely ((key >> key_bits) || (value >> value_bits)))
       return false; /* Overflows */
diff --git a/src/hb-cff-interp-common.hh b/src/hb-cff-interp-common.hh
new file mode 100644
index 0000000..c2be034
--- /dev/null
+++ b/src/hb-cff-interp-common.hh
@@ -0,0 +1,744 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_CFF_INTERP_COMMON_HH
+#define HB_CFF_INTERP_COMMON_HH
+
+namespace CFF {
+
+using namespace OT;
+
+typedef unsigned int OpCode;
+
+
+/* === Dict operators === */
+
+/* One byte operators (0-31) */
+#define OpCode_version		  0 /* CFF Top */
+#define OpCode_Notice		  1 /* CFF Top */
+#define OpCode_FullName		  2 /* CFF Top */
+#define OpCode_FamilyName	  3 /* CFF Top */
+#define OpCode_Weight		  4 /* CFF Top */
+#define OpCode_FontBBox		  5 /* CFF Top */
+#define OpCode_BlueValues	  6 /* CFF Private, CFF2 Private */
+#define OpCode_OtherBlues	  7 /* CFF Private, CFF2 Private */
+#define OpCode_FamilyBlues	  8 /* CFF Private, CFF2 Private */
+#define OpCode_FamilyOtherBlues	  9 /* CFF Private, CFF2 Private */
+#define OpCode_StdHW		 10 /* CFF Private, CFF2 Private */
+#define OpCode_StdVW		 11 /* CFF Private, CFF2 Private */
+#define OpCode_escape		 12 /* All. Shared with CS */
+#define OpCode_UniqueID		 13 /* CFF Top */
+#define OpCode_XUID		 14 /* CFF Top */
+#define OpCode_charset		 15 /* CFF Top (0) */
+#define OpCode_Encoding		 16 /* CFF Top (0) */
+#define OpCode_CharStrings	 17 /* CFF Top, CFF2 Top */
+#define OpCode_Private		 18 /* CFF Top, CFF2 FD */
+#define OpCode_Subrs		 19 /* CFF Private, CFF2 Private */
+#define OpCode_defaultWidthX	 20 /* CFF Private (0) */
+#define OpCode_nominalWidthX	 21 /* CFF Private (0) */
+#define OpCode_vsindexdict	 22 /* CFF2 Private/CS */
+#define OpCode_blenddict	 23 /* CFF2 Private/CS */
+#define OpCode_vstore		 24 /* CFF2 Top */
+#define OpCode_reserved25	 25
+#define OpCode_reserved26	 26
+#define OpCode_reserved27	 27
+
+/* Numbers */
+#define OpCode_shortint		 28 /* 16-bit integer, All */
+#define OpCode_longintdict	 29 /* 32-bit integer, All */
+#define OpCode_BCD		 30 /* Real number, CFF2 Top/FD */
+#define OpCode_reserved31	 31
+
+/* 1-byte integers */
+#define OpCode_OneByteIntFirst	 32 /* All. beginning of the range of first byte ints */
+#define OpCode_OneByteIntLast	246 /* All. ending of the range of first byte int */
+
+/* 2-byte integers */
+#define OpCode_TwoBytePosInt0	247 /* All. first byte of two byte positive int (+108 to +1131) */
+#define OpCode_TwoBytePosInt1	248
+#define OpCode_TwoBytePosInt2	249
+#define OpCode_TwoBytePosInt3	250
+
+#define OpCode_TwoByteNegInt0	251 /* All. first byte of two byte negative int (-1131 to -108) */
+#define OpCode_TwoByteNegInt1	252
+#define OpCode_TwoByteNegInt2	253
+#define OpCode_TwoByteNegInt3	254
+
+/* Two byte escape operators 12, (0-41) */
+#define OpCode_ESC_Base		256
+#define Make_OpCode_ESC(byte2)	((OpCode)(OpCode_ESC_Base + (byte2)))
+
+inline OpCode Unmake_OpCode_ESC (OpCode op)  { return (OpCode)(op - OpCode_ESC_Base); }
+inline bool Is_OpCode_ESC (OpCode op) { return op >= OpCode_ESC_Base; }
+inline unsigned int OpCode_Size (OpCode op) { return Is_OpCode_ESC (op) ? 2: 1; }
+
+#define OpCode_Copyright	Make_OpCode_ESC(0) /* CFF Top */
+#define OpCode_isFixedPitch	Make_OpCode_ESC(1) /* CFF Top (false) */
+#define OpCode_ItalicAngle	Make_OpCode_ESC(2) /* CFF Top (0) */
+#define OpCode_UnderlinePosition Make_OpCode_ESC(3) /* CFF Top (-100) */
+#define OpCode_UnderlineThickness Make_OpCode_ESC(4) /* CFF Top (50) */
+#define OpCode_PaintType	Make_OpCode_ESC(5) /* CFF Top (0) */
+#define OpCode_CharstringType	Make_OpCode_ESC(6) /* CFF Top (2) */
+#define OpCode_FontMatrix	Make_OpCode_ESC(7) /* CFF Top, CFF2 Top (.001 0 0 .001 0 0)*/
+#define OpCode_StrokeWidth	Make_OpCode_ESC(8) /* CFF Top (0) */
+#define OpCode_BlueScale	Make_OpCode_ESC(9) /* CFF Private, CFF2 Private (0.039625) */
+#define OpCode_BlueShift	Make_OpCode_ESC(10) /* CFF Private, CFF2 Private (7) */
+#define OpCode_BlueFuzz		Make_OpCode_ESC(11) /* CFF Private, CFF2 Private (1) */
+#define OpCode_StemSnapH	Make_OpCode_ESC(12) /* CFF Private, CFF2 Private */
+#define OpCode_StemSnapV	Make_OpCode_ESC(13) /* CFF Private, CFF2 Private */
+#define OpCode_ForceBold	Make_OpCode_ESC(14) /* CFF Private (false) */
+#define OpCode_reservedESC15	Make_OpCode_ESC(15)
+#define OpCode_reservedESC16	Make_OpCode_ESC(16)
+#define OpCode_LanguageGroup	Make_OpCode_ESC(17) /* CFF Private, CFF2 Private (0) */
+#define OpCode_ExpansionFactor	Make_OpCode_ESC(18) /* CFF Private, CFF2 Private (0.06) */
+#define OpCode_initialRandomSeed Make_OpCode_ESC(19) /* CFF Private (0) */
+#define OpCode_SyntheticBase	Make_OpCode_ESC(20) /* CFF Top */
+#define OpCode_PostScript	Make_OpCode_ESC(21) /* CFF Top */
+#define OpCode_BaseFontName	Make_OpCode_ESC(22) /* CFF Top */
+#define OpCode_BaseFontBlend	Make_OpCode_ESC(23) /* CFF Top */
+#define OpCode_reservedESC24	Make_OpCode_ESC(24)
+#define OpCode_reservedESC25	Make_OpCode_ESC(25)
+#define OpCode_reservedESC26	Make_OpCode_ESC(26)
+#define OpCode_reservedESC27	Make_OpCode_ESC(27)
+#define OpCode_reservedESC28	Make_OpCode_ESC(28)
+#define OpCode_reservedESC29	Make_OpCode_ESC(29)
+#define OpCode_ROS		Make_OpCode_ESC(30) /* CFF Top_CID */
+#define OpCode_CIDFontVersion	Make_OpCode_ESC(31) /* CFF Top_CID (0) */
+#define OpCode_CIDFontRevision	Make_OpCode_ESC(32) /* CFF Top_CID (0) */
+#define OpCode_CIDFontType	Make_OpCode_ESC(33) /* CFF Top_CID (0) */
+#define OpCode_CIDCount		Make_OpCode_ESC(34) /* CFF Top_CID (8720) */
+#define OpCode_UIDBase		Make_OpCode_ESC(35) /* CFF Top_CID */
+#define OpCode_FDArray		Make_OpCode_ESC(36) /* CFF Top_CID, CFF2 Top */
+#define OpCode_FDSelect		Make_OpCode_ESC(37) /* CFF Top_CID, CFF2 Top */
+#define OpCode_FontName		Make_OpCode_ESC(38) /* CFF Top_CID */
+
+
+/* === CharString operators === */
+
+#define OpCode_hstem		  1 /* CFF, CFF2 */
+#define OpCode_Reserved2	  2
+#define OpCode_vstem		  3 /* CFF, CFF2 */
+#define OpCode_vmoveto		  4 /* CFF, CFF2 */
+#define OpCode_rlineto		  5 /* CFF, CFF2 */
+#define OpCode_hlineto		  6 /* CFF, CFF2 */
+#define OpCode_vlineto		  7 /* CFF, CFF2 */
+#define OpCode_rrcurveto	  8 /* CFF, CFF2 */
+#define OpCode_Reserved9	  9
+#define OpCode_callsubr		 10 /* CFF, CFF2 */
+#define OpCode_return		 11 /* CFF */
+//#define OpCode_escape		 12 /* CFF, CFF2 */
+#define OpCode_Reserved13	 13
+#define OpCode_endchar		 14 /* CFF */
+#define OpCode_vsindexcs	 15 /* CFF2 */
+#define OpCode_blendcs		 16 /* CFF2 */
+#define OpCode_Reserved17	 17
+#define OpCode_hstemhm		 18 /* CFF, CFF2 */
+#define OpCode_hintmask		 19 /* CFF, CFF2 */
+#define OpCode_cntrmask		 20 /* CFF, CFF2 */
+#define OpCode_rmoveto		 21 /* CFF, CFF2 */
+#define OpCode_hmoveto		 22 /* CFF, CFF2 */
+#define OpCode_vstemhm		 23 /* CFF, CFF2 */
+#define OpCode_rcurveline	 24 /* CFF, CFF2 */
+#define OpCode_rlinecurve	 25 /* CFF, CFF2 */
+#define OpCode_vvcurveto	 26 /* CFF, CFF2 */
+#define OpCode_hhcurveto	 27 /* CFF, CFF2 */
+//#define OpCode_shortint	 28 /* CFF, CFF2 */
+#define OpCode_callgsubr	 29 /* CFF, CFF2 */
+#define OpCode_vhcurveto	 30 /* CFF, CFF2 */
+#define OpCode_hvcurveto	 31 /* CFF, CFF2 */
+
+#define OpCode_fixedcs		255 /* 32-bit fixed */
+
+/* Two byte escape operators 12, (0-41) */
+#define OpCode_dotsection	Make_OpCode_ESC(0) /* CFF (obsoleted) */
+#define OpCode_ReservedESC1	Make_OpCode_ESC(1)
+#define OpCode_ReservedESC2	Make_OpCode_ESC(2)
+#define OpCode_and		Make_OpCode_ESC(3) /* CFF */
+#define OpCode_or		Make_OpCode_ESC(4) /* CFF */
+#define OpCode_not		Make_OpCode_ESC(5) /* CFF */
+#define OpCode_ReservedESC6	Make_OpCode_ESC(6)
+#define OpCode_ReservedESC7	Make_OpCode_ESC(7)
+#define OpCode_ReservedESC8	Make_OpCode_ESC(8)
+#define OpCode_abs		Make_OpCode_ESC(9) /* CFF */
+#define OpCode_add		Make_OpCode_ESC(10) /* CFF */
+#define OpCode_sub		Make_OpCode_ESC(11) /* CFF */
+#define OpCode_div		Make_OpCode_ESC(12) /* CFF */
+#define OpCode_ReservedESC13	Make_OpCode_ESC(13)
+#define OpCode_neg		Make_OpCode_ESC(14) /* CFF */
+#define OpCode_eq		Make_OpCode_ESC(15) /* CFF */
+#define OpCode_ReservedESC16	Make_OpCode_ESC(16)
+#define OpCode_ReservedESC17	Make_OpCode_ESC(17)
+#define OpCode_drop		Make_OpCode_ESC(18) /* CFF */
+#define OpCode_ReservedESC19	Make_OpCode_ESC(19)
+#define OpCode_put		Make_OpCode_ESC(20) /* CFF */
+#define OpCode_get		Make_OpCode_ESC(21) /* CFF */
+#define OpCode_ifelse		Make_OpCode_ESC(22) /* CFF */
+#define OpCode_random		Make_OpCode_ESC(23) /* CFF */
+#define OpCode_mul		Make_OpCode_ESC(24) /* CFF */
+//#define OpCode_reservedESC25	Make_OpCode_ESC(25)
+#define OpCode_sqrt		Make_OpCode_ESC(26) /* CFF */
+#define OpCode_dup		Make_OpCode_ESC(27) /* CFF */
+#define OpCode_exch		Make_OpCode_ESC(28) /* CFF */
+#define OpCode_index		Make_OpCode_ESC(29) /* CFF */
+#define OpCode_roll		Make_OpCode_ESC(30) /* CFF */
+#define OpCode_reservedESC31	Make_OpCode_ESC(31)
+#define OpCode_reservedESC32	Make_OpCode_ESC(32)
+#define OpCode_reservedESC33	Make_OpCode_ESC(33)
+#define OpCode_hflex		Make_OpCode_ESC(34) /* CFF, CFF2 */
+#define OpCode_flex		Make_OpCode_ESC(35) /* CFF, CFF2 */
+#define OpCode_hflex1		Make_OpCode_ESC(36) /* CFF, CFF2 */
+#define OpCode_flex1		Make_OpCode_ESC(37) /* CFF, CFF2 */
+
+
+#define OpCode_Invalid		0xFFFFu
+
+
+struct Number
+{
+  void init () { set_real (0.0); }
+  void fini () {}
+
+  void set_int (int v)       { value = (double) v; }
+  int to_int () const        { return (int) value; }
+
+  void set_fixed (int32_t v) { value = v / 65536.0; }
+  int32_t to_fixed () const  { return (int32_t) (value * 65536.0); }
+
+  void set_real (double v)	 { value = v; }
+  double to_real () const    { return value; }
+
+  int ceil () const          { return (int) ::ceil (value); }
+  int floor () const         { return (int) ::floor (value); }
+
+  bool in_int_range () const
+  { return ((double) (int16_t) to_int () == value); }
+
+  bool operator > (const Number &n) const
+  { return value > n.to_real (); }
+
+  bool operator < (const Number &n) const
+  { return n > *this; }
+
+  bool operator >= (const Number &n) const
+  { return !(*this < n); }
+
+  bool operator <= (const Number &n) const
+  { return !(*this > n); }
+
+  const Number &operator += (const Number &n)
+  {
+    set_real (to_real () + n.to_real ());
+
+    return *this;
+  }
+
+  protected:
+  double  value;
+};
+
+/* byte string */
+struct UnsizedByteStr : UnsizedArrayOf <HBUINT8>
+{
+  // encode 2-byte int (Dict/CharString) or 4-byte int (Dict)
+  template <typename INTTYPE, int minVal, int maxVal>
+  static bool serialize_int (hb_serialize_context_t *c, OpCode intOp, int value)
+  {
+    TRACE_SERIALIZE (this);
+
+    if (unlikely ((value < minVal || value > maxVal)))
+      return_trace (false);
+
+    HBUINT8 *p = c->allocate_size<HBUINT8> (1);
+    if (unlikely (p == nullptr)) return_trace (false);
+    p->set (intOp);
+
+    INTTYPE *ip = c->allocate_size<INTTYPE> (INTTYPE::static_size);
+    if (unlikely (ip == nullptr)) return_trace (false);
+    ip->set ((unsigned int)value);
+
+    return_trace (true);
+  }
+
+  static bool serialize_int4 (hb_serialize_context_t *c, int value)
+  { return serialize_int<HBUINT32, 0, 0x7FFFFFFF> (c, OpCode_longintdict, value); }
+
+  static bool serialize_int2 (hb_serialize_context_t *c, int value)
+  { return serialize_int<HBUINT16, 0, 0x7FFF> (c, OpCode_shortint, value); }
+
+  /* Defining null_size allows a Null object may be created. Should be safe because:
+   * A descendent struct Dict uses a Null pointer to indicate a missing table,
+   * checked before access.
+   * ByteStr, a wrapper struct pairing a byte pointer along with its length, always
+   * checks the length before access. A Null pointer is used as the initial pointer
+   * along with zero length by the default ctor.
+   */
+  DEFINE_SIZE_MIN(0);
+};
+
+struct ByteStr
+{
+  ByteStr ()
+    : str (&Null(UnsizedByteStr)), len (0) {}
+  ByteStr (const UnsizedByteStr& s, unsigned int l)
+    : str (&s), len (l) {}
+  ByteStr (const char *s, unsigned int l=0)
+    : str ((const UnsizedByteStr *)s), len (l) {}
+  /* sub-string */
+  ByteStr (const ByteStr &bs, unsigned int offset, unsigned int len_)
+  {
+    str = (const UnsizedByteStr *)&bs.str[offset];
+    len = len_;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const { return str->sanitize (c, len); }
+
+  const HBUINT8& operator [] (unsigned int i) const
+  {
+    if (likely (str && (i < len)))
+      return (*str)[i];
+    else
+      return Null(HBUINT8);
+  }
+
+  bool serialize (hb_serialize_context_t *c, const ByteStr &src)
+  {
+    TRACE_SERIALIZE (this);
+    HBUINT8 *dest = c->allocate_size<HBUINT8> (src.len);
+    if (unlikely (dest == nullptr))
+      return_trace (false);
+    memcpy (dest, src.str, src.len);
+    return_trace (true);
+  }
+
+  unsigned int get_size () const { return len; }
+
+  bool check_limit (unsigned int offset, unsigned int count) const
+  { return (offset + count <= len); }
+
+  const UnsizedByteStr *str;
+  unsigned int len;
+};
+
+struct SubByteStr
+{
+  SubByteStr ()
+  { init (); }
+
+  void init ()
+  {
+    str = ByteStr (0);
+    offset = 0;
+    error = false;
+  }
+
+  void fini () {}
+
+  SubByteStr (const ByteStr &str_, unsigned int offset_ = 0)
+    : str (str_), offset (offset_), error (false) {}
+
+  void reset (const ByteStr &str_, unsigned int offset_ = 0)
+  {
+    str = str_;
+    offset = offset_;
+    error = false;
+  }
+
+  const HBUINT8& operator [] (int i) {
+    if (unlikely ((unsigned int)(offset + i) >= str.len))
+    {
+      set_error ();
+      return Null(HBUINT8);
+    }
+    else
+      return str[offset + i];
+  }
+
+  operator ByteStr () const { return ByteStr (str, offset, str.len - offset); }
+
+  bool avail (unsigned int count=1) const
+  {
+    return (!in_error () && str.check_limit (offset, count));
+  }
+  void inc (unsigned int count=1)
+  {
+    if (likely (!in_error () && (offset <= str.len) && (offset + count <= str.len)))
+    {
+      offset += count;
+    }
+    else
+    {
+      offset = str.len;
+      set_error ();
+    }
+  }
+
+  void set_error ()      { error = true; }
+  bool in_error () const { return error; }
+
+  ByteStr       str;
+  unsigned int  offset; /* beginning of the sub-string within str */
+
+  protected:
+  bool	  error;
+};
+
+typedef hb_vector_t<ByteStr> ByteStrArray;
+
+/* stack */
+template <typename ELEM, int LIMIT>
+struct Stack
+{
+  void init ()
+  {
+    error = false;
+    count = 0;
+    elements.init ();
+    elements.resize (kSizeLimit);
+    for (unsigned int i = 0; i < elements.len; i++)
+      elements[i].init ();
+  }
+
+  void fini ()
+  {
+    elements.fini_deep ();
+  }
+
+  ELEM& operator [] (unsigned int i)
+  {
+    if (unlikely (i >= count)) set_error ();
+    return elements[i];
+  }
+
+  void push (const ELEM &v)
+  {
+    if (likely (count < elements.len))
+      elements[count++] = v;
+    else
+      set_error ();
+  }
+
+  ELEM &push ()
+  {
+    if (likely (count < elements.len))
+      return elements[count++];
+    else
+    {
+      set_error ();
+      return Crap(ELEM);
+    }
+  }
+
+  ELEM& pop ()
+  {
+    if (likely (count > 0))
+      return elements[--count];
+    else
+    {
+      set_error ();
+      return Crap(ELEM);
+    }
+  }
+
+  void pop (unsigned int n)
+  {
+    if (likely (count >= n))
+      count -= n;
+    else
+      set_error ();
+  }
+
+  const ELEM& peek ()
+  {
+    if (likely (count > 0))
+      return elements[count-1];
+    else
+    {
+      set_error ();
+      return Null(ELEM);
+    }
+  }
+
+  void unpop ()
+  {
+    if (likely (count < elements.len))
+      count++;
+    else
+      set_error ();
+  }
+
+  void clear () { count = 0; }
+
+  bool in_error () const { return (error || elements.in_error ()); }
+  void set_error ()      { error = true; }
+
+  unsigned int get_count () const { return count; }
+  bool is_empty () const { return count == 0; }
+
+  static const unsigned int kSizeLimit = LIMIT;
+
+  protected:
+  bool error;
+  unsigned int count;
+  hb_vector_t<ELEM, kSizeLimit> elements;
+};
+
+/* argument stack */
+template <typename ARG=Number>
+struct ArgStack : Stack<ARG, 513>
+{
+  void push_int (int v)
+  {
+    ARG &n = S::push ();
+    n.set_int (v);
+  }
+
+  void push_fixed (int32_t v)
+  {
+    ARG &n = S::push ();
+    n.set_fixed (v);
+  }
+
+  void push_real (double v)
+  {
+    ARG &n = S::push ();
+    n.set_real (v);
+  }
+
+  ARG& pop_num () { return this->pop (); }
+
+  int pop_int ()  { return this->pop ().to_int (); }
+
+  unsigned int pop_uint ()
+  {
+    int i = pop_int ();
+    if (unlikely (i < 0))
+    {
+      i = 0;
+      S::set_error ();
+    }
+    return (unsigned)i;
+  }
+
+  void push_longint_from_substr (SubByteStr& substr)
+  {
+    push_int ((substr[0] << 24) | (substr[1] << 16) | (substr[2] << 8) | (substr[3]));
+    substr.inc (4);
+  }
+
+  bool push_fixed_from_substr (SubByteStr& substr)
+  {
+    if (unlikely (!substr.avail (4)))
+      return false;
+    push_fixed ((int32_t)*(const HBUINT32*)&substr[0]);
+    substr.inc (4);
+    return true;
+  }
+
+  hb_array_t<const ARG> get_subarray (unsigned int start) const
+  {
+    return S::elements.sub_array (start);
+  }
+
+  private:
+  typedef Stack<ARG, 513> S;
+};
+
+/* an operator prefixed by its operands in a byte string */
+struct OpStr
+{
+  void init () {}
+  void fini () {}
+
+  OpCode  op;
+  ByteStr str;
+};
+
+/* base of OP_SERIALIZER */
+struct OpSerializer
+{
+  protected:
+  bool copy_opstr (hb_serialize_context_t *c, const OpStr& opstr) const
+  {
+    TRACE_SERIALIZE (this);
+
+    HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
+    if (unlikely (d == nullptr)) return_trace (false);
+    memcpy (d, &opstr.str.str[0], opstr.str.len);
+    return_trace (true);
+  }
+};
+
+template <typename VAL>
+struct ParsedValues
+{
+  void init ()
+  {
+    opStart = 0;
+    values.init ();
+  }
+  void fini () { values.fini_deep (); }
+
+  void add_op (OpCode op, const SubByteStr& substr = SubByteStr ())
+  {
+    VAL *val = values.push ();
+    val->op = op;
+    val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
+    opStart = substr.offset;
+  }
+
+  void add_op (OpCode op, const SubByteStr& substr, const VAL &v)
+  {
+    VAL *val = values.push (v);
+    val->op = op;
+    val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
+    opStart = substr.offset;
+  }
+
+  bool has_op (OpCode op) const
+  {
+    for (unsigned int i = 0; i < get_count (); i++)
+      if (get_value (i).op == op) return true;
+    return false;
+  }
+
+  unsigned get_count () const { return values.len; }
+  const VAL &get_value (unsigned int i) const { return values[i]; }
+  const VAL &operator [] (unsigned int i) const { return get_value (i); }
+
+  unsigned int       opStart;
+  hb_vector_t<VAL>   values;
+};
+
+template <typename ARG=Number>
+struct InterpEnv
+{
+  void init (const ByteStr &str_)
+  {
+    substr.reset (str_);
+    argStack.init ();
+    error = false;
+  }
+  void fini () { argStack.fini (); }
+
+  bool in_error () const
+  { return error || substr.in_error () || argStack.in_error (); }
+
+  void set_error () { error = true; }
+
+  OpCode fetch_op ()
+  {
+    OpCode  op = OpCode_Invalid;
+    if (unlikely (!substr.avail ()))
+      return OpCode_Invalid;
+    op = (OpCode)(unsigned char)substr[0];
+    if (op == OpCode_escape) {
+      if (unlikely (!substr.avail ()))
+	return OpCode_Invalid;
+      op = Make_OpCode_ESC(substr[1]);
+      substr.inc ();
+    }
+    substr.inc ();
+    return op;
+  }
+
+  const ARG& eval_arg (unsigned int i)
+  {
+    return argStack[i];
+  }
+
+  ARG& pop_arg ()
+  {
+    return argStack.pop ();
+  }
+
+  void pop_n_args (unsigned int n)
+  {
+    argStack.pop (n);
+  }
+
+  void clear_args ()
+  {
+    pop_n_args (argStack.get_count ());
+  }
+
+  SubByteStr    substr;
+  ArgStack<ARG> argStack;
+  protected:
+  bool	  error;
+};
+
+typedef InterpEnv<> NumInterpEnv;
+
+template <typename ARG=Number>
+struct OpSet
+{
+  static void process_op (OpCode op, InterpEnv<ARG>& env)
+  {
+    switch (op) {
+      case OpCode_shortint:
+	env.argStack.push_int ((int16_t)((env.substr[0] << 8) | env.substr[1]));
+	env.substr.inc (2);
+	break;
+
+      case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
+      case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
+	env.argStack.push_int ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + env.substr[0] + 108));
+	env.substr.inc ();
+	break;
+
+      case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
+      case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
+	env.argStack.push_int ((int16_t)(-(op - OpCode_TwoByteNegInt0) * 256 - env.substr[0] - 108));
+	env.substr.inc ();
+	break;
+
+      default:
+	/* 1-byte integer */
+	if (likely ((OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast)))
+	{
+	  env.argStack.push_int ((int)op - 139);
+	} else {
+	  /* invalid unknown operator */
+	  env.clear_args ();
+	  env.set_error ();
+	}
+	break;
+    }
+  }
+};
+
+template <typename ENV>
+struct Interpreter {
+
+  ~Interpreter() { fini (); }
+
+  void fini () { env.fini (); }
+
+  ENV env;
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_INTERP_COMMON_HH */
diff --git a/src/hb-cff-interp-cs-common.hh b/src/hb-cff-interp-cs-common.hh
new file mode 100644
index 0000000..e78d557
--- /dev/null
+++ b/src/hb-cff-interp-cs-common.hh
@@ -0,0 +1,904 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_CFF_INTERP_CS_COMMON_HH
+#define HB_CFF_INTERP_CS_COMMON_HH
+
+#include "hb.hh"
+#include "hb-cff-interp-common.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+enum CSType {
+  CSType_CharString,
+  CSType_GlobalSubr,
+  CSType_LocalSubr
+};
+
+struct CallContext
+{
+  void init (const SubByteStr substr_=SubByteStr (), CSType type_=CSType_CharString, unsigned int subr_num_=0)
+  {
+    substr = substr_;
+    type = type_;
+    subr_num = subr_num_;
+  }
+
+  void fini () {}
+
+  SubByteStr      substr;
+  CSType	  type;
+  unsigned int    subr_num;
+};
+
+/* call stack */
+const unsigned int kMaxCallLimit = 10;
+struct CallStack : Stack<CallContext, kMaxCallLimit> {};
+
+template <typename SUBRS>
+struct BiasedSubrs
+{
+  void init (const SUBRS &subrs_)
+  {
+    subrs = &subrs_;
+    unsigned int  nSubrs = get_count ();
+    if (nSubrs < 1240)
+      bias = 107;
+    else if (nSubrs < 33900)
+      bias = 1131;
+    else
+      bias = 32768;
+  }
+
+  void fini () {}
+
+  unsigned int get_count () const { return (subrs == nullptr)? 0: subrs->count; }
+  unsigned int get_bias () const { return bias; }
+
+  ByteStr operator [] (unsigned int index) const
+  {
+    if (unlikely ((subrs == nullptr) || index >= subrs->count))
+      return Null(ByteStr);
+    else
+      return (*subrs)[index];
+  }
+
+  protected:
+  unsigned int  bias;
+  const SUBRS   *subrs;
+};
+
+struct Point
+{
+  void init ()
+  {
+    x.init ();
+    y.init ();
+  }
+
+  void set_int (int _x, int _y)
+  {
+    x.set_int (_x);
+    y.set_int (_y);
+  }
+
+  void move_x (const Number &dx) { x += dx; }
+  void move_y (const Number &dy) { y += dy; }
+  void move (const Number &dx, const Number &dy) { move_x (dx); move_y (dy); }
+  void move (const Point &d) { move_x (d.x); move_y (d.y); }
+
+  Number  x;
+  Number  y;
+};
+
+template <typename ARG, typename SUBRS>
+struct CSInterpEnv : InterpEnv<ARG>
+{
+  void init (const ByteStr &str, const SUBRS &globalSubrs_, const SUBRS &localSubrs_)
+  {
+    InterpEnv<ARG>::init (str);
+
+    context.init (str, CSType_CharString);
+    seen_moveto = true;
+    seen_hintmask = false;
+    hstem_count = 0;
+    vstem_count = 0;
+    pt.init ();
+    callStack.init ();
+    globalSubrs.init (globalSubrs_);
+    localSubrs.init (localSubrs_);
+  }
+  void fini ()
+  {
+    InterpEnv<ARG>::fini ();
+
+    callStack.fini ();
+    globalSubrs.fini ();
+    localSubrs.fini ();
+  }
+
+  bool in_error () const
+  {
+    return callStack.in_error () || SUPER::in_error ();
+  }
+
+  bool popSubrNum (const BiasedSubrs<SUBRS>& biasedSubrs, unsigned int &subr_num)
+  {
+    int n = SUPER::argStack.pop_int ();
+    n += biasedSubrs.get_bias ();
+    if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.get_count ())))
+      return false;
+
+    subr_num = (unsigned int)n;
+    return true;
+  }
+
+  void callSubr (const BiasedSubrs<SUBRS>& biasedSubrs, CSType type)
+  {
+    unsigned int subr_num;
+
+    if (unlikely (!popSubrNum (biasedSubrs, subr_num)
+		 || callStack.get_count () >= kMaxCallLimit))
+    {
+      SUPER::set_error ();
+      return;
+    }
+    context.substr = SUPER::substr;
+    callStack.push (context);
+
+    context.init ( biasedSubrs[subr_num], type, subr_num);
+    SUPER::substr = context.substr;
+  }
+
+  void returnFromSubr ()
+  {
+    if (unlikely (SUPER::substr.in_error ()))
+      SUPER::set_error ();
+    context = callStack.pop ();
+    SUPER::substr = context.substr;
+  }
+
+  void determine_hintmask_size ()
+  {
+    if (!seen_hintmask)
+    {
+      vstem_count += SUPER::argStack.get_count() / 2;
+      hintmask_size = (hstem_count + vstem_count + 7) >> 3;
+      seen_hintmask = true;
+    }
+  }
+
+  void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; }
+  bool is_endchar () const { return endchar_flag; }
+
+  const Number &get_x () const { return pt.x; }
+  const Number &get_y () const { return pt.y; }
+  const Point &get_pt () const { return pt; }
+
+  void moveto (const Point &pt_ ) { pt = pt_; }
+
+  public:
+  CallContext   context;
+  bool	  endchar_flag;
+  bool	  seen_moveto;
+  bool	  seen_hintmask;
+
+  unsigned int  hstem_count;
+  unsigned int  vstem_count;
+  unsigned int  hintmask_size;
+  CallStack	    callStack;
+  BiasedSubrs<SUBRS>   globalSubrs;
+  BiasedSubrs<SUBRS>   localSubrs;
+
+  private:
+  Point	 pt;
+
+  typedef InterpEnv<ARG> SUPER;
+};
+
+template <typename ENV, typename PARAM>
+struct PathProcsNull
+{
+  static void rmoveto (ENV &env, PARAM& param) {}
+  static void hmoveto (ENV &env, PARAM& param) {}
+  static void vmoveto (ENV &env, PARAM& param) {}
+  static void rlineto (ENV &env, PARAM& param) {}
+  static void hlineto (ENV &env, PARAM& param) {}
+  static void vlineto (ENV &env, PARAM& param) {}
+  static void rrcurveto (ENV &env, PARAM& param) {}
+  static void rcurveline (ENV &env, PARAM& param) {}
+  static void rlinecurve (ENV &env, PARAM& param) {}
+  static void vvcurveto (ENV &env, PARAM& param) {}
+  static void hhcurveto (ENV &env, PARAM& param) {}
+  static void vhcurveto (ENV &env, PARAM& param) {}
+  static void hvcurveto (ENV &env, PARAM& param) {}
+  static void moveto (ENV &env, PARAM& param, const Point &pt) {}
+  static void line (ENV &env, PARAM& param, const Point &pt1) {}
+  static void curve (ENV &env, PARAM& param, const Point &pt1, const Point &pt2, const Point &pt3) {}
+  static void hflex (ENV &env, PARAM& param) {}
+  static void flex (ENV &env, PARAM& param) {}
+  static void hflex1 (ENV &env, PARAM& param) {}
+  static void flex1 (ENV &env, PARAM& param) {}
+};
+
+template <typename ARG, typename OPSET, typename ENV, typename PARAM, typename PATH=PathProcsNull<ENV, PARAM> >
+struct CSOpSet : OpSet<ARG>
+{
+  static void process_op (OpCode op, ENV &env, PARAM& param)
+  {
+    switch (op) {
+
+      case OpCode_return:
+	env.returnFromSubr ();
+	break;
+      case OpCode_endchar:
+	OPSET::check_width (op, env, param);
+	env.set_endchar (true);
+	OPSET::flush_args_and_op (op, env, param);
+	break;
+
+      case OpCode_fixedcs:
+	env.argStack.push_fixed_from_substr (env.substr);
+	break;
+
+      case OpCode_callsubr:
+	env.callSubr (env.localSubrs, CSType_LocalSubr);
+	break;
+
+      case OpCode_callgsubr:
+	env.callSubr (env.globalSubrs, CSType_GlobalSubr);
+	break;
+
+      case OpCode_hstem:
+      case OpCode_hstemhm:
+	OPSET::check_width (op, env, param);
+	OPSET::process_hstem (op, env, param);
+	break;
+      case OpCode_vstem:
+      case OpCode_vstemhm:
+	OPSET::check_width (op, env, param);
+	OPSET::process_vstem (op, env, param);
+	break;
+      case OpCode_hintmask:
+      case OpCode_cntrmask:
+	OPSET::check_width (op, env, param);
+	OPSET::process_hintmask (op, env, param);
+	break;
+      case OpCode_rmoveto:
+	OPSET::check_width (op, env, param);
+	PATH::rmoveto (env, param);
+	OPSET::process_post_move (op, env, param);
+	break;
+      case OpCode_hmoveto:
+	OPSET::check_width (op, env, param);
+	PATH::hmoveto (env, param);
+	OPSET::process_post_move (op, env, param);
+	break;
+      case OpCode_vmoveto:
+	OPSET::check_width (op, env, param);
+	PATH::vmoveto (env, param);
+	OPSET::process_post_move (op, env, param);
+	break;
+      case OpCode_rlineto:
+	PATH::rlineto (env, param);
+	process_post_path (op, env, param);
+	break;
+      case OpCode_hlineto:
+	PATH::hlineto (env, param);
+	process_post_path (op, env, param);
+	break;
+      case OpCode_vlineto:
+	PATH::vlineto (env, param);
+	process_post_path (op, env, param);
+	break;
+      case OpCode_rrcurveto:
+	PATH::rrcurveto (env, param);
+	process_post_path (op, env, param);
+	break;
+      case OpCode_rcurveline:
+	PATH::rcurveline (env, param);
+	process_post_path (op, env, param);
+	break;
+      case OpCode_rlinecurve:
+	PATH::rlinecurve (env, param);
+	process_post_path (op, env, param);
+	break;
+      case OpCode_vvcurveto:
+	PATH::vvcurveto (env, param);
+	process_post_path (op, env, param);
+	break;
+      case OpCode_hhcurveto:
+	PATH::hhcurveto (env, param);
+	process_post_path (op, env, param);
+	break;
+      case OpCode_vhcurveto:
+	PATH::vhcurveto (env, param);
+	process_post_path (op, env, param);
+	break;
+      case OpCode_hvcurveto:
+	PATH::hvcurveto (env, param);
+	process_post_path (op, env, param);
+	break;
+
+      case OpCode_hflex:
+	PATH::hflex (env, param);
+	OPSET::process_post_flex (op, env, param);
+	break;
+
+      case OpCode_flex:
+	PATH::flex (env, param);
+	OPSET::process_post_flex (op, env, param);
+	break;
+
+      case OpCode_hflex1:
+	PATH::hflex1 (env, param);
+	OPSET::process_post_flex (op, env, param);
+	break;
+
+      case OpCode_flex1:
+	PATH::flex1 (env, param);
+	OPSET::process_post_flex (op, env, param);
+	break;
+
+      default:
+	SUPER::process_op (op, env);
+	break;
+    }
+  }
+
+  static void process_hstem (OpCode op, ENV &env, PARAM& param)
+  {
+    env.hstem_count += env.argStack.get_count () / 2;
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static void process_vstem (OpCode op, ENV &env, PARAM& param)
+  {
+    env.vstem_count += env.argStack.get_count () / 2;
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static void process_hintmask (OpCode op, ENV &env, PARAM& param)
+  {
+    env.determine_hintmask_size ();
+    if (likely (env.substr.avail (env.hintmask_size)))
+    {
+      OPSET::flush_hintmask (op, env, param);
+      env.substr.inc (env.hintmask_size);
+    }
+  }
+
+  static void process_post_flex (OpCode op, ENV &env, PARAM& param)
+  {
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static void check_width (OpCode op, ENV &env, PARAM& param)
+  {}
+
+  static void process_post_move (OpCode op, ENV &env, PARAM& param)
+  {
+    if (!env.seen_moveto)
+    {
+      env.determine_hintmask_size ();
+      env.seen_moveto = true;
+    }
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static void process_post_path (OpCode op, ENV &env, PARAM& param)
+  {
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static void flush_args_and_op (OpCode op, ENV &env, PARAM& param)
+  {
+    OPSET::flush_args (env, param);
+    OPSET::flush_op (op, env, param);
+  }
+
+  static void flush_args (ENV &env, PARAM& param)
+  {
+    env.pop_n_args (env.argStack.get_count ());
+  }
+
+  static void flush_op (OpCode op, ENV &env, PARAM& param)
+  {
+  }
+
+  static void flush_hintmask (OpCode op, ENV &env, PARAM& param)
+  {
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static bool is_number_op (OpCode op)
+  {
+    switch (op)
+    {
+      case OpCode_shortint:
+      case OpCode_fixedcs:
+      case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
+      case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
+      case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
+      case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
+	return true;
+
+      default:
+	/* 1-byte integer */
+	return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast);
+    }
+  }
+
+  protected:
+  typedef OpSet<ARG>  SUPER;
+};
+
+template <typename PATH, typename ENV, typename PARAM>
+struct PathProcs
+{
+  static void rmoveto (ENV &env, PARAM& param)
+  {
+    Point pt1 = env.get_pt ();
+    const Number &dy = env.pop_arg ();
+    const Number &dx = env.pop_arg ();
+    pt1.move (dx, dy);
+    PATH::moveto (env, param, pt1);
+  }
+
+  static void hmoveto (ENV &env, PARAM& param)
+  {
+    Point pt1 = env.get_pt ();
+    pt1.move_x (env.pop_arg ());
+    PATH::moveto (env, param, pt1);
+  }
+
+  static void vmoveto (ENV &env, PARAM& param)
+  {
+    Point pt1 = env.get_pt ();
+    pt1.move_y (env.pop_arg ());
+    PATH::moveto (env, param, pt1);
+  }
+
+  static void rlineto (ENV &env, PARAM& param)
+  {
+    for (unsigned int i = 0; i + 2 <= env.argStack.get_count (); i += 2)
+    {
+      Point pt1 = env.get_pt ();
+      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
+      PATH::line (env, param, pt1);
+    }
+  }
+
+  static void hlineto (ENV &env, PARAM& param)
+  {
+    Point pt1;
+    unsigned int i = 0;
+    for (; i + 2 <= env.argStack.get_count (); i += 2)
+    {
+      pt1 = env.get_pt ();
+      pt1.move_x (env.eval_arg (i));
+      PATH::line (env, param, pt1);
+      pt1.move_y (env.eval_arg (i+1));
+      PATH::line (env, param, pt1);
+    }
+    if (i < env.argStack.get_count ())
+    {
+      pt1 = env.get_pt ();
+      pt1.move_x (env.eval_arg (i));
+      PATH::line (env, param, pt1);
+    }
+  }
+
+  static void vlineto (ENV &env, PARAM& param)
+  {
+    Point pt1;
+    unsigned int i = 0;
+    for (; i + 2 <= env.argStack.get_count (); i += 2)
+    {
+      pt1 = env.get_pt ();
+      pt1.move_y (env.eval_arg (i));
+      PATH::line (env, param, pt1);
+      pt1.move_x (env.eval_arg (i+1));
+      PATH::line (env, param, pt1);
+    }
+    if (i < env.argStack.get_count ())
+    {
+      pt1 = env.get_pt ();
+      pt1.move_y (env.eval_arg (i));
+      PATH::line (env, param, pt1);
+    }
+  }
+
+  static void rrcurveto (ENV &env, PARAM& param)
+  {
+    for (unsigned int i = 0; i + 6 <= env.argStack.get_count (); i += 6)
+    {
+      Point pt1 = env.get_pt ();
+      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
+      Point pt2 = pt1;
+      pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
+      Point pt3 = pt2;
+      pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
+      PATH::curve (env, param, pt1, pt2, pt3);
+    }
+  }
+
+  static void rcurveline (ENV &env, PARAM& param)
+  {
+    unsigned int i = 0;
+    for (; i + 6 <= env.argStack.get_count (); i += 6)
+    {
+      Point pt1 = env.get_pt ();
+      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
+      Point pt2 = pt1;
+      pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
+      Point pt3 = pt2;
+      pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
+      PATH::curve (env, param, pt1, pt2, pt3);
+    }
+    for (; i + 2 <= env.argStack.get_count (); i += 2)
+    {
+      Point pt1 = env.get_pt ();
+      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
+      PATH::line (env, param, pt1);
+    }
+  }
+
+  static void rlinecurve (ENV &env, PARAM& param)
+  {
+    unsigned int i = 0;
+    unsigned int line_limit = (env.argStack.get_count () % 6);
+    for (; i + 2 <= line_limit; i += 2)
+    {
+      Point pt1 = env.get_pt ();
+      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
+      PATH::line (env, param, pt1);
+    }
+    for (; i + 6 <= env.argStack.get_count (); i += 6)
+    {
+      Point pt1 = env.get_pt ();
+      pt1.move (env.eval_arg (i), env.eval_arg (i+1));
+      Point pt2 = pt1;
+      pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
+      Point pt3 = pt2;
+      pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
+      PATH::curve (env, param, pt1, pt2, pt3);
+    }
+  }
+
+  static void vvcurveto (ENV &env, PARAM& param)
+  {
+    unsigned int i = 0;
+    Point pt1 = env.get_pt ();
+    if ((env.argStack.get_count () & 1) != 0)
+      pt1.move_x (env.eval_arg (i++));
+    for (; i + 4 <= env.argStack.get_count (); i += 4)
+    {
+      pt1.move_y (env.eval_arg (i));
+      Point pt2 = pt1;
+      pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
+      Point pt3 = pt2;
+      pt3.move_y (env.eval_arg (i+3));
+      PATH::curve (env, param, pt1, pt2, pt3);
+      pt1 = env.get_pt ();
+    }
+  }
+
+  static void hhcurveto (ENV &env, PARAM& param)
+  {
+    unsigned int i = 0;
+    Point pt1 = env.get_pt ();
+    if ((env.argStack.get_count () & 1) != 0)
+      pt1.move_y (env.eval_arg (i++));
+    for (; i + 4 <= env.argStack.get_count (); i += 4)
+    {
+      pt1.move_x (env.eval_arg (i));
+      Point pt2 = pt1;
+      pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
+      Point pt3 = pt2;
+      pt3.move_x (env.eval_arg (i+3));
+      PATH::curve (env, param, pt1, pt2, pt3);
+      pt1 = env.get_pt ();
+    }
+  }
+
+  static void vhcurveto (ENV &env, PARAM& param)
+  {
+    Point pt1, pt2, pt3;
+    unsigned int i = 0;
+    if ((env.argStack.get_count () % 8) >= 4)
+    {
+      Point pt1 = env.get_pt ();
+      pt1.move_y (env.eval_arg (i));
+      Point pt2 = pt1;
+      pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
+      Point pt3 = pt2;
+      pt3.move_x (env.eval_arg (i+3));
+      i += 4;
+
+      for (; i + 8 <= env.argStack.get_count (); i += 8)
+      {
+	PATH::curve (env, param, pt1, pt2, pt3);
+	pt1 = env.get_pt ();
+	pt1.move_x (env.eval_arg (i));
+	pt2 = pt1;
+	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
+	pt3 = pt2;
+	pt3.move_y (env.eval_arg (i+3));
+	PATH::curve (env, param, pt1, pt2, pt3);
+
+	pt1 = pt3;
+	pt1.move_y (env.eval_arg (i+4));
+	pt2 = pt1;
+	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
+	pt3 = pt2;
+	pt3.move_x (env.eval_arg (i+7));
+      }
+      if (i < env.argStack.get_count ())
+	pt3.move_y (env.eval_arg (i));
+      PATH::curve (env, param, pt1, pt2, pt3);
+    }
+    else
+    {
+      for (; i + 8 <= env.argStack.get_count (); i += 8)
+      {
+	pt1 = env.get_pt ();
+	pt1.move_y (env.eval_arg (i));
+	pt2 = pt1;
+	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
+	pt3 = pt2;
+	pt3.move_x (env.eval_arg (i+3));
+	PATH::curve (env, param, pt1, pt2, pt3);
+
+	pt1 = pt3;
+	pt1.move_x (env.eval_arg (i+4));
+	pt2 = pt1;
+	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
+	pt3 = pt2;
+	pt3.move_y (env.eval_arg (i+7));
+	if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0))
+	  pt3.move_x (env.eval_arg (i+8));
+	PATH::curve (env, param, pt1, pt2, pt3);
+      }
+    }
+  }
+
+  static void hvcurveto (ENV &env, PARAM& param)
+  {
+    Point pt1, pt2, pt3;
+    unsigned int i = 0;
+    if ((env.argStack.get_count () % 8) >= 4)
+    {
+      Point pt1 = env.get_pt ();
+      pt1.move_x (env.eval_arg (i));
+      Point pt2 = pt1;
+      pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
+      Point pt3 = pt2;
+      pt3.move_y (env.eval_arg (i+3));
+      i += 4;
+
+      for (; i + 8 <= env.argStack.get_count (); i += 8)
+      {
+	PATH::curve (env, param, pt1, pt2, pt3);
+	pt1 = env.get_pt ();
+	pt1.move_y (env.eval_arg (i));
+	pt2 = pt1;
+	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
+	pt3 = pt2;
+	pt3.move_x (env.eval_arg (i+3));
+	PATH::curve (env, param, pt1, pt2, pt3);
+
+	pt1 = pt3;
+	pt1.move_x (env.eval_arg (i+4));
+	pt2 = pt1;
+	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
+	pt3 = pt2;
+	pt3.move_y (env.eval_arg (i+7));
+      }
+      if (i < env.argStack.get_count ())
+	pt3.move_x (env.eval_arg (i));
+      PATH::curve (env, param, pt1, pt2, pt3);
+    }
+    else
+    {
+      for (; i + 8 <= env.argStack.get_count (); i += 8)
+      {
+	pt1 = env.get_pt ();
+	pt1.move_x (env.eval_arg (i));
+	pt2 = pt1;
+	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
+	pt3 = pt2;
+	pt3.move_y (env.eval_arg (i+3));
+	PATH::curve (env, param, pt1, pt2, pt3);
+
+	pt1 = pt3;
+	pt1.move_y (env.eval_arg (i+4));
+	pt2 = pt1;
+	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
+	pt3 = pt2;
+	pt3.move_x (env.eval_arg (i+7));
+	if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0))
+	  pt3.move_y (env.eval_arg (i+8));
+	PATH::curve (env, param, pt1, pt2, pt3);
+      }
+    }
+  }
+
+  /* default actions to be overridden */
+  static void moveto (ENV &env, PARAM& param, const Point &pt)
+  { env.moveto (pt); }
+
+  static void line (ENV &env, PARAM& param, const Point &pt1)
+  { PATH::moveto (env, param, pt1); }
+
+  static void curve (ENV &env, PARAM& param, const Point &pt1, const Point &pt2, const Point &pt3)
+  { PATH::moveto (env, param, pt3); }
+
+  static void hflex (ENV &env, PARAM& param)
+  {
+    if (likely (env.argStack.get_count () == 7))
+    {
+      Point pt1 = env.get_pt ();
+      pt1.move_x (env.eval_arg (0));
+      Point pt2 = pt1;
+      pt2.move (env.eval_arg (1), env.eval_arg (2));
+      Point pt3 = pt2;
+      pt3.move_x (env.eval_arg (3));
+      Point pt4 = pt3;
+      pt4.move_x (env.eval_arg (4));
+      Point pt5 = pt4;
+      pt5.move_x (env.eval_arg (5));
+      pt5.y = pt1.y;
+      Point pt6 = pt5;
+      pt6.move_x (env.eval_arg (6));
+
+      curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
+    }
+    else
+      env.set_error ();
+  }
+
+  static void flex (ENV &env, PARAM& param)
+  {
+    if (likely (env.argStack.get_count () == 13))
+    {
+      Point pt1 = env.get_pt ();
+      pt1.move (env.eval_arg (0), env.eval_arg (1));
+      Point pt2 = pt1;
+      pt2.move (env.eval_arg (2), env.eval_arg (3));
+      Point pt3 = pt2;
+      pt3.move (env.eval_arg (4), env.eval_arg (5));
+      Point pt4 = pt3;
+      pt4.move (env.eval_arg (6), env.eval_arg (7));
+      Point pt5 = pt4;
+      pt5.move (env.eval_arg (8), env.eval_arg (9));
+      Point pt6 = pt5;
+      pt6.move (env.eval_arg (10), env.eval_arg (11));
+
+      curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
+    }
+    else
+      env.set_error ();
+  }
+
+  static void hflex1 (ENV &env, PARAM& param)
+  {
+    if (likely (env.argStack.get_count () == 9))
+    {
+      Point pt1 = env.get_pt ();
+      pt1.move (env.eval_arg (0), env.eval_arg (1));
+      Point pt2 = pt1;
+      pt2.move (env.eval_arg (2), env.eval_arg (3));
+      Point pt3 = pt2;
+      pt3.move_x (env.eval_arg (4));
+      Point pt4 = pt3;
+      pt4.move_x (env.eval_arg (5));
+      Point pt5 = pt4;
+      pt5.move (env.eval_arg (6), env.eval_arg (7));
+      Point pt6 = pt5;
+      pt6.move_x (env.eval_arg (8));
+      pt6.y = env.get_pt ().y;
+
+      curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
+    }
+    else
+      env.set_error ();
+  }
+
+  static void flex1 (ENV &env, PARAM& param)
+  {
+    if (likely (env.argStack.get_count () == 11))
+    {
+      Point d;
+      d.init ();
+      for (unsigned int i = 0; i < 10; i += 2)
+	d.move (env.eval_arg (i), env.eval_arg (i+1));
+
+      Point pt1 = env.get_pt ();
+      pt1.move (env.eval_arg (0), env.eval_arg (1));
+      Point pt2 = pt1;
+      pt2.move (env.eval_arg (2), env.eval_arg (3));
+      Point pt3 = pt2;
+      pt3.move (env.eval_arg (4), env.eval_arg (5));
+      Point pt4 = pt3;
+      pt4.move (env.eval_arg (6), env.eval_arg (7));
+      Point pt5 = pt4;
+      pt5.move (env.eval_arg (8), env.eval_arg (9));
+      Point pt6 = pt5;
+
+      if (fabs (d.x.to_real ()) > fabs (d.y.to_real ()))
+      {
+	pt6.move_x (env.eval_arg (10));
+	pt6.y = env.get_pt ().y;
+      }
+      else
+      {
+	pt6.x = env.get_pt ().x;
+	pt6.move_y (env.eval_arg (10));
+      }
+
+      curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
+    }
+    else
+      env.set_error ();
+  }
+
+  protected:
+  static void curve2 (ENV &env, PARAM& param,
+		      const Point &pt1, const Point &pt2, const Point &pt3,
+		      const Point &pt4, const Point &pt5, const Point &pt6)
+  {
+    PATH::curve (env, param, pt1, pt2, pt3);
+    PATH::curve (env, param, pt4, pt5, pt6);
+  }
+};
+
+template <typename ENV, typename OPSET, typename PARAM>
+struct CSInterpreter : Interpreter<ENV>
+{
+  bool interpret (PARAM& param)
+  {
+    SUPER::env.set_endchar (false);
+
+    for (;;) {
+      OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
+      if (unlikely (SUPER::env.in_error ()))
+	return false;
+      if (SUPER::env.is_endchar ())
+	break;
+    }
+
+    return true;
+  }
+
+  private:
+  typedef Interpreter<ENV> SUPER;
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_INTERP_CS_COMMON_HH */
diff --git a/src/hb-cff-interp-dict-common.hh b/src/hb-cff-interp-dict-common.hh
new file mode 100644
index 0000000..8000d76
--- /dev/null
+++ b/src/hb-cff-interp-dict-common.hh
@@ -0,0 +1,294 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_CFF_INTERP_DICT_COMMON_HH
+#define HB_CFF_INTERP_DICT_COMMON_HH
+
+#include "hb-cff-interp-common.hh"
+#include <math.h>
+#include <float.h>
+
+namespace CFF {
+
+using namespace OT;
+
+/* an opstr and the parsed out dict value(s) */
+struct DictVal : OpStr
+{
+  void init () { single_val.set_int (0); }
+  void fini () {}
+
+  Number	      single_val;
+};
+
+typedef DictVal NumDictVal;
+
+template <typename VAL> struct DictValues : ParsedValues<VAL> {};
+
+template <typename OPSTR=OpStr>
+struct TopDictValues : DictValues<OPSTR>
+{
+  void init ()
+  {
+    DictValues<OPSTR>::init ();
+    charStringsOffset = 0;
+    FDArrayOffset = 0;
+  }
+  void fini () { DictValues<OPSTR>::fini (); }
+
+  unsigned int calculate_serialized_op_size (const OPSTR& opstr) const
+  {
+    switch (opstr.op)
+    {
+      case OpCode_CharStrings:
+      case OpCode_FDArray:
+	return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
+
+      default:
+	return opstr.str.len;
+    }
+  }
+
+  unsigned int  charStringsOffset;
+  unsigned int  FDArrayOffset;
+};
+
+struct DictOpSet : OpSet<Number>
+{
+  static void process_op (OpCode op, InterpEnv<Number>& env)
+  {
+    switch (op) {
+      case OpCode_longintdict:  /* 5-byte integer */
+	env.argStack.push_longint_from_substr (env.substr);
+	break;
+
+      case OpCode_BCD:  /* real number */
+	env.argStack.push_real (parse_bcd (env.substr));
+	break;
+
+      default:
+	OpSet<Number>::process_op (op, env);
+	break;
+    }
+  }
+
+  static double parse_bcd (SubByteStr& substr)
+  {
+    bool    neg = false;
+    double  int_part = 0;
+    uint64_t frac_part = 0;
+    uint32_t  frac_count = 0;
+    bool    exp_neg = false;
+    uint32_t  exp_part = 0;
+    bool    exp_overflow = false;
+    enum Part { INT_PART=0, FRAC_PART, EXP_PART } part = INT_PART;
+    enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END };
+    const uint64_t MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 1^52-1 */
+    const uint32_t MAX_EXP = 0x7FFu; /* 1^11-1 */
+
+    double  value = 0.0;
+    unsigned char byte = 0;
+    for (uint32_t i = 0;; i++)
+    {
+      char d;
+      if ((i & 1) == 0)
+      {
+	if (!substr.avail ())
+	{
+	  substr.set_error ();
+	  return 0.0;
+	}
+	byte = substr[0];
+	substr.inc ();
+	d = byte >> 4;
+      }
+      else
+	d = byte & 0x0F;
+
+      switch (d)
+      {
+	case RESERVED:
+	  substr.set_error ();
+	  return value;
+
+	case END:
+	  value = (double)(neg? -int_part: int_part);
+	  if (frac_count > 0)
+	  {
+	    double frac = (frac_part / pow (10.0, (double)frac_count));
+	    if (neg) frac = -frac;
+	    value += frac;
+	  }
+	  if (unlikely (exp_overflow))
+	  {
+	    if (value == 0.0)
+	      return value;
+	    if (exp_neg)
+	      return neg? -DBL_MIN: DBL_MIN;
+	    else
+	      return neg? -DBL_MAX: DBL_MAX;
+	  }
+	  if (exp_part != 0)
+	  {
+	    if (exp_neg)
+	      value /= pow (10.0, (double)exp_part);
+	    else
+	      value *= pow (10.0, (double)exp_part);
+	  }
+	  return value;
+
+	case NEG:
+	  if (i != 0)
+	  {
+	    substr.set_error ();
+	    return 0.0;
+	  }
+	  neg = true;
+	  break;
+
+	case DECIMAL:
+	  if (part != INT_PART)
+	  {
+	    substr.set_error ();
+	    return value;
+	  }
+	  part = FRAC_PART;
+	  break;
+
+	case EXP_NEG:
+	  exp_neg = true;
+	  HB_FALLTHROUGH;
+
+	case EXP_POS:
+	  if (part == EXP_PART)
+	  {
+	    substr.set_error ();
+	    return value;
+	  }
+	  part = EXP_PART;
+	  break;
+
+	default:
+	  switch (part) {
+	    default:
+	    case INT_PART:
+	      int_part = (int_part * 10) + d;
+	      break;
+
+	    case FRAC_PART:
+	      if (likely (frac_part <= MAX_FRACT / 10))
+	      {
+		frac_part = (frac_part * 10) + (unsigned)d;
+		frac_count++;
+	      }
+	      break;
+
+	    case EXP_PART:
+	      if (likely (exp_part * 10 + d <= MAX_EXP))
+	      {
+	      	exp_part = (exp_part * 10) + d;
+	      }
+	      else
+	      	exp_overflow = true;
+	      break;
+	  }
+      }
+    }
+
+    return value;
+  }
+
+  static bool is_hint_op (OpCode op)
+  {
+    switch (op)
+    {
+      case OpCode_BlueValues:
+      case OpCode_OtherBlues:
+      case OpCode_FamilyBlues:
+      case OpCode_FamilyOtherBlues:
+      case OpCode_StemSnapH:
+      case OpCode_StemSnapV:
+      case OpCode_StdHW:
+      case OpCode_StdVW:
+      case OpCode_BlueScale:
+      case OpCode_BlueShift:
+      case OpCode_BlueFuzz:
+      case OpCode_ForceBold:
+      case OpCode_LanguageGroup:
+      case OpCode_ExpansionFactor:
+	return true;
+      default:
+	return false;
+    }
+  }
+};
+
+template <typename VAL=OpStr>
+struct TopDictOpSet : DictOpSet
+{
+  static void process_op (OpCode op, InterpEnv<Number>& env, TopDictValues<VAL> & dictval)
+  {
+    switch (op) {
+      case OpCode_CharStrings:
+	dictval.charStringsOffset = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+      case OpCode_FDArray:
+	dictval.FDArrayOffset = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+      case OpCode_FontMatrix:
+	env.clear_args ();
+	break;
+      default:
+	DictOpSet::process_op (op, env);
+	break;
+    }
+  }
+};
+
+template <typename OPSET, typename PARAM, typename ENV=NumInterpEnv>
+struct DictInterpreter : Interpreter<ENV>
+{
+  bool interpret (PARAM& param)
+  {
+    param.init ();
+    while (SUPER::env.substr.avail ())
+    {
+      OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
+      if (unlikely (SUPER::env.in_error ()))
+	return false;
+    }
+
+    return true;
+  }
+
+  private:
+  typedef Interpreter<ENV> SUPER;
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_INTERP_DICT_COMMON_HH */
diff --git a/src/hb-cff1-interp-cs.hh b/src/hb-cff1-interp-cs.hh
new file mode 100644
index 0000000..68e1d81
--- /dev/null
+++ b/src/hb-cff1-interp-cs.hh
@@ -0,0 +1,161 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_CFF1_INTERP_CS_HH
+#define HB_CFF1_INTERP_CS_HH
+
+#include "hb.hh"
+#include "hb-cff-interp-cs-common.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+typedef BiasedSubrs<CFF1Subrs>   CFF1BiasedSubrs;
+
+struct CFF1CSInterpEnv : CSInterpEnv<Number, CFF1Subrs>
+{
+  template <typename ACC>
+  void init (const ByteStr &str, ACC &acc, unsigned int fd)
+  {
+    SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
+    processed_width = false;
+    has_width = false;
+    arg_start = 0;
+    in_seac = false;
+  }
+
+  void fini () { SUPER::fini (); }
+
+  void set_width (bool has_width_)
+  {
+    if (likely (!processed_width && (SUPER::argStack.get_count () > 0)))
+    {
+      if (has_width_)
+      {
+	width = SUPER::argStack[0];
+	has_width = true;
+	arg_start = 1;
+      }
+    }
+    processed_width = true;
+  }
+
+  void clear_args ()
+  {
+    arg_start = 0;
+    SUPER::clear_args ();
+  }
+
+  void set_in_seac (bool _in_seac) { in_seac = _in_seac; }
+
+  bool	  processed_width;
+  bool	  has_width;
+  unsigned int  arg_start;
+  Number	width;
+  bool	  in_seac;
+
+  private:
+  typedef CSInterpEnv<Number, CFF1Subrs> SUPER;
+};
+
+template <typename OPSET, typename PARAM, typename PATH=PathProcsNull<CFF1CSInterpEnv, PARAM> >
+struct CFF1CSOpSet : CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH>
+{
+  /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */
+  /* Type 1-originated deprecated opcodes, seac behavior of endchar and dotsection are supported */
+
+  static void process_op (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
+  {
+    switch (op) {
+      case OpCode_dotsection:
+	SUPER::flush_args_and_op (op, env, param);
+	break;
+
+      case OpCode_endchar:
+	OPSET::check_width (op, env, param);
+	if (env.argStack.get_count () >= 4)
+	{
+	  OPSET::process_seac (env, param);
+	}
+	OPSET::flush_args_and_op (op, env, param);
+	env.set_endchar (true);
+	break;
+
+      default:
+	SUPER::process_op (op, env, param);
+    }
+  }
+
+  static void check_width (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
+  {
+    if (!env.processed_width)
+    {
+      bool  has_width = false;
+      switch (op)
+      {
+	case OpCode_endchar:
+	case OpCode_hstem:
+	case OpCode_hstemhm:
+	case OpCode_vstem:
+	case OpCode_vstemhm:
+	case OpCode_hintmask:
+	case OpCode_cntrmask:
+	  has_width = ((env.argStack.get_count () & 1) != 0);
+	  break;
+	case OpCode_hmoveto:
+	case OpCode_vmoveto:
+	  has_width = (env.argStack.get_count () > 1);
+	  break;
+	case OpCode_rmoveto:
+	  has_width = (env.argStack.get_count () > 2);
+	  break;
+	default:
+	  return;
+      }
+      env.set_width (has_width);
+    }
+  }
+
+  static void process_seac (CFF1CSInterpEnv &env, PARAM& param)
+  {
+  }
+
+  static void flush_args (CFF1CSInterpEnv &env, PARAM& param)
+  {
+    SUPER::flush_args (env, param);
+    env.clear_args ();  /* pop off width */
+  }
+
+  private:
+  typedef CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH>  SUPER;
+};
+
+template <typename OPSET, typename PARAM>
+struct CFF1CSInterpreter : CSInterpreter<CFF1CSInterpEnv, OPSET, PARAM> {};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF1_INTERP_CS_HH */
diff --git a/src/hb-cff2-interp-cs.hh b/src/hb-cff2-interp-cs.hh
new file mode 100644
index 0000000..709bdef
--- /dev/null
+++ b/src/hb-cff2-interp-cs.hh
@@ -0,0 +1,271 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_CFF2_INTERP_CS_HH
+#define HB_CFF2_INTERP_CS_HH
+
+#include "hb.hh"
+#include "hb-cff-interp-cs-common.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+struct BlendArg : Number
+{
+  void init ()
+  {
+    Number::init ();
+    deltas.init ();
+  }
+
+  void fini ()
+  {
+    Number::fini ();
+    deltas.fini_deep ();
+  }
+
+  void set_int (int v) { reset_blends (); Number::set_int (v); }
+  void set_fixed (int32_t v) { reset_blends (); Number::set_fixed (v); }
+  void set_real (double v) { reset_blends (); Number::set_real (v); }
+
+  void set_blends (unsigned int numValues_, unsigned int valueIndex_,
+			  unsigned int numBlends, hb_array_t<const BlendArg> blends_)
+  {
+    numValues = numValues_;
+    valueIndex = valueIndex_;
+    deltas.resize (numBlends);
+    for (unsigned int i = 0; i < numBlends; i++)
+      deltas[i] = blends_[i];
+  }
+
+  bool blending () const { return deltas.len > 0; }
+  void reset_blends ()
+  {
+    numValues = valueIndex = 0;
+    deltas.resize (0);
+  }
+
+  unsigned int numValues;
+  unsigned int valueIndex;
+  hb_vector_t<Number> deltas;
+};
+
+typedef InterpEnv<BlendArg> BlendInterpEnv;
+typedef BiasedSubrs<CFF2Subrs>   CFF2BiasedSubrs;
+
+struct CFF2CSInterpEnv : CSInterpEnv<BlendArg, CFF2Subrs>
+{
+  template <typename ACC>
+  void init (const ByteStr &str, ACC &acc, unsigned int fd,
+		    const int *coords_=nullptr, unsigned int num_coords_=0)
+  {
+    SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
+
+    coords = coords_;
+    num_coords = num_coords_;
+    varStore = acc.varStore;
+    seen_blend = false;
+    seen_vsindex_ = false;
+    scalars.init ();
+    do_blend = (coords != nullptr) && num_coords && (varStore != &Null(CFF2VariationStore));
+    set_ivs (acc.privateDicts[fd].ivs);
+  }
+
+  void fini ()
+  {
+    scalars.fini ();
+    SUPER::fini ();
+  }
+
+  OpCode fetch_op ()
+  {
+    if (this->substr.avail ())
+      return SUPER::fetch_op ();
+
+    /* make up return or endchar op */
+    if (this->callStack.is_empty ())
+      return OpCode_endchar;
+    else
+      return OpCode_return;
+  }
+
+  const BlendArg& eval_arg (unsigned int i)
+  {
+    BlendArg  &arg = argStack[i];
+    blend_arg (arg);
+    return arg;
+  }
+
+  const BlendArg& pop_arg ()
+  {
+    BlendArg  &arg = argStack.pop ();
+    blend_arg (arg);
+    return arg;
+  }
+
+  void process_blend ()
+  {
+    if (!seen_blend)
+    {
+      region_count = varStore->varStore.get_region_index_count (get_ivs ());
+      if (do_blend)
+      {
+	scalars.resize (region_count);
+	varStore->varStore.get_scalars (get_ivs (),
+					(int *)coords, num_coords,
+					&scalars[0], region_count);
+      }
+      seen_blend = true;
+    }
+  }
+
+  void process_vsindex ()
+  {
+    unsigned int  index = argStack.pop_uint ();
+    if (unlikely (seen_vsindex () || seen_blend))
+    {
+      set_error ();
+    }
+    else
+    {
+      set_ivs (index);
+    }
+    seen_vsindex_ = true;
+  }
+
+  unsigned int get_region_count () const { return region_count; }
+  void	 set_region_count (unsigned int region_count_) { region_count = region_count_; }
+  unsigned int get_ivs () const { return ivs; }
+  void	 set_ivs (unsigned int ivs_) { ivs = ivs_; }
+  bool	 seen_vsindex () const { return seen_vsindex_; }
+
+  protected:
+  void blend_arg (BlendArg &arg)
+  {
+    if (do_blend && arg.blending ())
+    {
+      if (likely (scalars.len == arg.deltas.len))
+      {
+	double v = arg.to_real ();
+	for (unsigned int i = 0; i < scalars.len; i++)
+	{
+	  v += (double)scalars[i] * arg.deltas[i].to_real ();
+	}
+	arg.set_real (v);
+	arg.deltas.resize (0);
+      }
+    }
+  }
+
+  protected:
+  const int     *coords;
+  unsigned int  num_coords;
+  const	 CFF2VariationStore *varStore;
+  unsigned int  region_count;
+  unsigned int  ivs;
+  hb_vector_t<float>  scalars;
+  bool	  do_blend;
+  bool	  seen_vsindex_;
+  bool	  seen_blend;
+
+  typedef CSInterpEnv<BlendArg, CFF2Subrs> SUPER;
+};
+template <typename OPSET, typename PARAM, typename PATH=PathProcsNull<CFF2CSInterpEnv, PARAM> >
+struct CFF2CSOpSet : CSOpSet<BlendArg, OPSET, CFF2CSInterpEnv, PARAM, PATH>
+{
+  static void process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param)
+  {
+    switch (op) {
+      case OpCode_callsubr:
+      case OpCode_callgsubr:
+	/* a subroutine number shoudln't be a blended value */
+	if (unlikely (env.argStack.peek ().blending ()))
+	{
+	  env.set_error ();
+	  break;
+	}
+	SUPER::process_op (op, env, param);
+	break;
+
+      case OpCode_blendcs:
+	OPSET::process_blend (env, param);
+	break;
+
+      case OpCode_vsindexcs:
+	if (unlikely (env.argStack.peek ().blending ()))
+	{
+	  env.set_error ();
+	  break;
+	}
+	OPSET::process_vsindex (env, param);
+	break;
+
+      default:
+	SUPER::process_op (op, env, param);
+    }
+  }
+
+  static void process_blend (CFF2CSInterpEnv &env, PARAM& param)
+  {
+    unsigned int n, k;
+
+    env.process_blend ();
+    k = env.get_region_count ();
+    n = env.argStack.pop_uint ();
+    /* copy the blend values into blend array of the default values */
+    unsigned int start = env.argStack.get_count () - ((k+1) * n);
+    /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */
+    if (unlikely (start > env.argStack.get_count ()))
+    {
+      env.set_error ();
+      return;
+    }
+    for (unsigned int i = 0; i < n; i++)
+    {
+      const hb_array_t<const BlendArg>	blends = env.argStack.get_subarray (start + n + (i * k));
+      env.argStack[start + i].set_blends (n, i, k, blends);
+    }
+
+    /* pop off blend values leaving default values now adorned with blend values */
+    env.argStack.pop (k * n);
+  }
+
+  static void process_vsindex (CFF2CSInterpEnv &env, PARAM& param)
+  {
+    env.process_vsindex ();
+    env.clear_args ();
+  }
+
+  private:
+  typedef CSOpSet<BlendArg, OPSET, CFF2CSInterpEnv, PARAM, PATH>  SUPER;
+};
+
+template <typename OPSET, typename PARAM>
+struct CFF2CSInterpreter : CSInterpreter<CFF2CSInterpEnv, OPSET, PARAM> {};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF2_INTERP_CS_HH */
diff --git a/src/hb-common.cc b/src/hb-common.cc
index ba48dd5..93f5b79 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -36,12 +36,22 @@
 #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_atomic_int_t _hb_options;
 
 void
-_hb_options_init (void)
+_hb_options_init ()
 {
   hb_options_union_t u;
   u.i = 0;
@@ -194,7 +204,7 @@
    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
   '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
-  '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+   0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
    0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
@@ -237,11 +247,10 @@
   struct hb_language_item_t *next;
   hb_language_t lang;
 
-  inline bool operator == (const char *s) const {
-    return lang_equal (lang, s);
-  }
+  bool operator == (const char *s) const
+  { return lang_equal (lang, s); }
 
-  inline hb_language_item_t & operator = (const char *s) {
+  hb_language_item_t & operator = (const char *s) {
     /* If a custom allocated is used calling strdup() pairs
     badly with a call to the custom free() in fini() below.
     Therefore don't call strdup(), implement its behavior.
@@ -258,7 +267,7 @@
     return *this;
   }
 
-  void fini (void) { free ((void *) lang); }
+  void fini () { free ((void *) lang); }
 };
 
 
@@ -266,12 +275,12 @@
 
 static hb_atomic_ptr_t <hb_language_item_t> langs;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
 static void
-free_langs (void)
+free_langs ()
 {
 retry:
-  hb_language_item_t *first_lang = langs.get ();
+  hb_language_item_t *first_lang = langs;
   if (unlikely (!langs.cmpexch (first_lang, nullptr)))
     goto retry;
 
@@ -288,7 +297,7 @@
 lang_find_or_insert (const char *key)
 {
 retry:
-  hb_language_item_t *first_lang = langs.get ();
+  hb_language_item_t *first_lang = langs;
 
   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
     if (*lang == key)
@@ -313,7 +322,7 @@
     goto retry;
   }
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
   if (!first_lang)
     atexit (free_langs); /* First person registers atexit() callback. */
 #endif
@@ -394,11 +403,11 @@
  * Since: 0.9.2
  **/
 hb_language_t
-hb_language_get_default (void)
+hb_language_get_default ()
 {
   static hb_atomic_ptr_t <hb_language_t> default_language;
 
-  hb_language_t language = default_language.get ();
+  hb_language_t language = default_language;
   if (unlikely (language == HB_LANGUAGE_INVALID))
   {
     language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
@@ -615,6 +624,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.
@@ -645,7 +667,7 @@
  * Since: 0.9.2
  **/
 const char *
-hb_version_string (void)
+hb_version_string ()
 {
   return HB_VERSION_STRING;
 }
@@ -757,43 +779,43 @@
 
 #ifdef USE_XLOCALE
 
-#ifdef HB_USE_ATEXIT
-static void free_static_C_locale (void);
+#if HB_USE_ATEXIT
+static void free_static_C_locale ();
 #endif
 
-static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_ptr_t<HB_LOCALE_T>::value,
+static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer (HB_LOCALE_T),
 							  hb_C_locale_lazy_loader_t>
 {
-  static inline HB_LOCALE_T create (void)
+  static HB_LOCALE_T create ()
   {
     HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C");
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
     atexit (free_static_C_locale);
 #endif
 
     return C_locale;
   }
-  static inline void destroy (HB_LOCALE_T p)
+  static void destroy (HB_LOCALE_T p)
   {
     HB_FREE_LOCALE (p);
   }
-  static inline HB_LOCALE_T get_null (void)
+  static HB_LOCALE_T get_null ()
   {
     return nullptr;
   }
 } static_C_locale;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
 static
-void free_static_C_locale (void)
+void free_static_C_locale ()
 {
   static_C_locale.free_instance ();
 }
 #endif
 
 static HB_LOCALE_T
-get_C_locale (void)
+get_C_locale ()
 {
   return static_C_locale.get_unconst ();
 }
@@ -911,7 +933,7 @@
 
   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)
diff --git a/src/hb-common.h b/src/hb-common.h
index 2f09f43..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" {
@@ -444,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 9f7745d..c181950 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -26,8 +26,6 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#define HB_SHAPER coretext
-
 #include "hb.hh"
 #include "hb-shaper-impl.hh"
 
@@ -35,6 +33,16 @@
 #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
 
@@ -91,13 +99,8 @@
 }
 
 
-HB_SHAPER_DATA_ENSURE_DEFINE(coretext, face)
-HB_SHAPER_DATA_ENSURE_DEFINE_WITH_CONDITION(coretext, font,
-	fabs (CTFontGetSize((CTFontRef) data) - coretext_font_size_from_ptem (font->ptem)) <= .5
-)
-
 static CTFontDescriptorRef
-get_last_resort_font_desc (void)
+get_last_resort_font_desc ()
 {
   // TODO Handle allocation failures?
   CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0);
@@ -302,8 +305,7 @@
 CGFontRef
 hb_coretext_face_get_cg_font (hb_face_t *face)
 {
-  if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr;
-  return (CGFontRef) HB_SHAPER_DATA_GET (face);
+  return (CGFontRef) (const void *) face->data.coretext;
 }
 
 
@@ -311,8 +313,9 @@
 _hb_coretext_shaper_font_data_create (hb_font_t *font)
 {
   hb_face_t *face = font->face;
-  if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr;
-  CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face);
+  const hb_coretext_face_data_t *face_data = face->data.coretext;
+  if (unlikely (!face_data)) return nullptr;
+  CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext;
 
   CTFontRef ct_font = create_ct_font (cg_font, coretext_font_size_from_ptem (font->ptem));
 
@@ -331,6 +334,38 @@
   CFRelease ((CTFontRef) data);
 }
 
+static const hb_coretext_font_data_t *
+hb_coretext_font_data_sync (hb_font_t *font)
+{
+retry:
+  const hb_coretext_font_data_t *data = font->data.coretext;
+  if (unlikely (!data)) return nullptr;
+
+  if (fabs (CTFontGetSize((CTFontRef) data) - coretext_font_size_from_ptem (font->ptem)) > .5)
+  {
+    /* XXX-MT-bug
+     * Note that evaluating condition above can be dangerous if another thread
+     * got here first and destructed data.  That's, as always, bad use pattern.
+     * If you modify the font (change font size), other threads must not be
+     * using it at the same time.  However, since this check is delayed to
+     * when one actually tries to shape something, this is a XXX race condition
+     * (and the only one we have that I know of) right now.  Ie. you modify the
+     * font size in one thread, then (supposedly safely) try to use it from two
+     * or more threads and BOOM!  I'm not sure how to fix this.  We want RCU.
+     */
+
+    /* Drop and recreate. */
+    /* If someone dropped it in the mean time, throw it away and don't touch it.
+     * Otherwise, destruct it. */
+    if (likely (font->data.coretext.cmpexch (const_cast<hb_coretext_font_data_t *> (data), nullptr)))
+      _hb_coretext_shaper_font_data_destroy (const_cast<hb_coretext_font_data_t *> (data));
+    else
+      goto retry;
+  }
+  return font->data.coretext;
+}
+
+
 /*
  * Since: 1.7.2
  */
@@ -343,13 +378,13 @@
   hb_font_t *font = hb_font_create (face);
   hb_face_destroy (face);
 
-  if (unlikely (hb_object_is_inert (font)))
+  if (unlikely (hb_object_is_immutable (font)))
     return font;
 
   hb_font_set_ptem (font, coretext_font_size_to_ptem (CTFontGetSize(ct_font)));
 
   /* Let there be dragons here... */
-  HB_SHAPER_DATA (HB_SHAPER, font).set_relaxed ((hb_coretext_font_data_t *) CFRetain (ct_font));
+  font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font));
 
   return font;
 }
@@ -357,31 +392,8 @@
 CTFontRef
 hb_coretext_font_get_ct_font (hb_font_t *font)
 {
-  if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return nullptr;
-  return (CTFontRef) HB_SHAPER_DATA_GET (font);
-}
-
-
-
-/*
- * shaper shape_plan data
- */
-
-struct hb_coretext_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_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
-}
-
-void
-_hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shape_plan_data_t *data HB_UNUSED)
-{
+  const hb_coretext_font_data_t *data = hb_coretext_font_data_sync (font);
+  return data ? (CTFontRef) data : nullptr;
 }
 
 
@@ -440,8 +452,8 @@
                     unsigned int        num_features)
 {
   hb_face_t *face = font->face;
-  CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face);
-  CTFontRef ct_font = (CTFontRef) HB_SHAPER_DATA_GET (font);
+  CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext;
+  CTFontRef ct_font = (CTFontRef) hb_coretext_font_data_sync (font);
 
   CGFloat ct_font_size = CTFontGetSize (ct_font);
   CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size;
@@ -464,8 +476,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.
@@ -474,7 +486,7 @@
   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 hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag);
@@ -513,7 +525,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++)
     {
@@ -586,7 +598,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.arrayZ ());
       }
     }
   }
@@ -1142,9 +1154,6 @@
  * AAT shaper
  */
 
-HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, face)
-HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, font)
-
 /*
  * shaper face data
  */
@@ -1154,20 +1163,8 @@
 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};
-
-  for (unsigned int i = 0; i < ARRAY_LENGTH (tags); i++)
-  {
-    hb_blob_t *blob = face->reference_table (tags[i]);
-    if (hb_blob_get_length (blob))
-    {
-      hb_blob_destroy (blob);
-      return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
-    }
-    hb_blob_destroy (blob);
-  }
-
-  return nullptr;
+  return hb_aat_layout_has_substitution (face) || hb_aat_layout_has_positioning (face) ?
+	 (hb_coretext_aat_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
 }
 
 void
@@ -1185,7 +1182,7 @@
 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_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
+  return font->data.coretext ? (hb_coretext_aat_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
 }
 
 void
@@ -1195,28 +1192,6 @@
 
 
 /*
- * shaper shape_plan data
- */
-
-struct hb_coretext_aat_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_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
-}
-
-void
-_hb_coretext_aat_shaper_shape_plan_data_destroy (hb_coretext_aat_shape_plan_data_t *data HB_UNUSED)
-{
-}
-
-
-/*
  * shaper
  */
 
diff --git a/src/hb-debug.hh b/src/hb-debug.hh
index 58c190d..41a85b5 100644
--- a/src/hb-debug.hh
+++ b/src/hb-debug.hh
@@ -56,12 +56,12 @@
 static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), "");
 
 HB_INTERNAL void
-_hb_options_init (void);
+_hb_options_init ();
 
 extern HB_INTERNAL hb_atomic_int_t _hb_options;
 
 static inline hb_options_t
-hb_options (void)
+hb_options ()
 {
   /* Make a local copy, so we can access bitfield threadsafely. */
   hb_options_union_t u;
@@ -173,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,
@@ -192,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,
@@ -216,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,
@@ -284,7 +284,7 @@
     _hb_debug_msg_va<max_level> (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap);
     va_end (ap);
   }
-  inline ~hb_auto_trace_t (void)
+  ~hb_auto_trace_t ()
   {
     _hb_warn_no_return<ret_t> (returned);
     if (!returned) {
@@ -293,14 +293,16 @@
     if (plevel) --*plevel;
   }
 
-  inline ret_t ret (ret_t v, unsigned int line = 0)
+  ret_t ret (ret_t v,
+	     const char *func = "",
+	     unsigned int line = 0)
   {
     if (unlikely (returned)) {
       fprintf (stderr, "OUCH, double calls to return_trace().  This is a bug, please report.\n");
       return v;
     }
 
-    _hb_debug_msg<max_level> (what, obj, nullptr, true, plevel ? *plevel : 1, -1,
+    _hb_debug_msg<max_level> (what, obj, func, true, plevel ? *plevel : 1, -1,
 			      "return %s (line %d)",
 			      hb_printer_t<ret_t>().print (v), line);
     if (plevel) --*plevel;
@@ -325,17 +327,21 @@
 				   const char *message,
 				   ...) HB_PRINTF_FUNC(6, 7) {}
 
-  inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; }
+  ret_t ret (ret_t v,
+	     const char *func HB_UNUSED = nullptr,
+	     unsigned int line HB_UNUSED = 0) { return v; }
 };
 
 /* For disabled tracing; optimize out everything.
  * https://github.com/harfbuzz/harfbuzz/pull/605 */
 template <typename ret_t>
 struct hb_no_trace_t {
-  inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; }
+  ret_t ret (ret_t v,
+	     const char *func HB_UNUSED = "",
+	     unsigned int line HB_UNUSED = 0) { return v; }
 };
 
-#define return_trace(RET) return trace.ret (RET, __LINE__)
+#define return_trace(RET) return trace.ret (RET, HB_FUNC, __LINE__)
 
 
 /*
diff --git a/src/hb-deprecated.h b/src/hb-deprecated.h
index 5af9bdb..4a5e702 100644
--- a/src/hb-deprecated.h
+++ b/src/hb-deprecated.h
@@ -36,10 +36,23 @@
 #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
 
+
 #define HB_SCRIPT_CANADIAN_ABORIGINAL		HB_SCRIPT_CANADIAN_SYLLABICS
 
 #define HB_BUFFER_FLAGS_DEFAULT			HB_BUFFER_FLAG_DEFAULT
@@ -146,12 +159,6 @@
 						   hb_unicode_decompose_compatibility_func_t func,
 						   void *user_data, hb_destroy_func_t destroy);
 
-/**
- * hb_unicode_decompose_compatibility:
- * 
- *
- * Deprecated: 2.0.0
- **/
 HB_EXTERN HB_DEPRECATED unsigned int
 hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
 				    hb_codepoint_t      u,
@@ -211,29 +218,6 @@
 					 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
 
diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc
index 35197c2..e93cf90 100644
--- a/src/hb-directwrite.cc
+++ b/src/hb-directwrite.cc
@@ -23,7 +23,6 @@
  */
 
 #include "hb.hh"
-#define HB_SHAPER directwrite
 #include "hb-shaper-impl.hh"
 
 #include <DWrite_1.h>
@@ -31,18 +30,14 @@
 #include "hb-directwrite.h"
 
 
-HB_SHAPER_DATA_ENSURE_DEFINE (directwrite, face)
-HB_SHAPER_DATA_ENSURE_DEFINE (directwrite, font)
-
-
 /*
  * hb-directwrite uses new/delete syntatically but as we let users
  * to override malloc/free, we will redefine new/delete so users
  * won't need to do that by their own.
  */
-void* operator new (size_t size) { return malloc (size); }
-void* operator new [] (size_t size) { return malloc (size); }
-void operator delete (void* pointer) { free (pointer); }
+void* operator new (size_t size)        { return malloc (size); }
+void* operator new [] (size_t size)     { return malloc (size); }
+void operator delete (void* pointer)    { free (pointer); }
 void operator delete [] (void* pointer) { free (pointer); }
 
 
@@ -59,19 +54,19 @@
   IDWriteFontFileStream *mFontFileStream;
 public:
   DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream)
-  {
-    mFontFileStream = fontFileStream;
-  }
+  { mFontFileStream = fontFileStream; }
 
   // IUnknown interface
-  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) { return S_OK; }
-  IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
+  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
+  { return S_OK; }
+  IFACEMETHOD_ (ULONG, AddRef) ()  { return 1; }
   IFACEMETHOD_ (ULONG, Release) () { return 1; }
 
   // IDWriteFontFileLoader methods
-  virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey (void const* fontFileReferenceKey,
-    uint32_t fontFileReferenceKeySize,
-    OUT IDWriteFontFileStream** fontFileStream)
+  virtual HRESULT STDMETHODCALLTYPE
+  CreateStreamFromKey (void const* fontFileReferenceKey,
+		       uint32_t fontFileReferenceKeySize,
+		       OUT IDWriteFontFileStream** fontFileStream)
   {
     *fontFileStream = mFontFileStream;
     return S_OK;
@@ -91,19 +86,20 @@
   }
 
   // IUnknown interface
-  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) { return S_OK; }
-  IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
+  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
+  { return S_OK; }
+  IFACEMETHOD_ (ULONG, AddRef) ()  { return 1; }
   IFACEMETHOD_ (ULONG, Release) () { return 1; }
 
   // IDWriteFontFileStream methods
-  virtual HRESULT STDMETHODCALLTYPE ReadFileFragment (void const** fragmentStart,
-    UINT64 fileOffset,
-    UINT64 fragmentSize,
-    OUT void** fragmentContext)
+  virtual HRESULT STDMETHODCALLTYPE
+  ReadFileFragment (void const** fragmentStart,
+		    UINT64 fileOffset,
+		    UINT64 fragmentSize,
+		    OUT void** fragmentContext)
   {
     // We are required to do bounds checking.
-    if (fileOffset + fragmentSize > mSize)
-      return E_FAIL;
+    if (fileOffset + fragmentSize > mSize) return E_FAIL;
 
     // truncate the 64 bit fileOffset to size_t sized index into mData
     size_t index = static_cast<size_t> (fileOffset);
@@ -114,18 +110,18 @@
     return S_OK;
   }
 
-  virtual void STDMETHODCALLTYPE ReleaseFileFragment (void* fragmentContext) { }
+  virtual void STDMETHODCALLTYPE
+  ReleaseFileFragment (void* fragmentContext) {}
 
-  virtual HRESULT STDMETHODCALLTYPE GetFileSize (OUT UINT64* fileSize)
+  virtual HRESULT STDMETHODCALLTYPE
+  GetFileSize (OUT UINT64* fileSize)
   {
     *fileSize = mSize;
     return S_OK;
   }
 
-  virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime (OUT UINT64* lastWriteTime)
-  {
-    return E_NOTIMPL;
-  }
+  virtual HRESULT STDMETHODCALLTYPE
+  GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; }
 };
 
 
@@ -152,17 +148,14 @@
 
   // TODO: factory and fontFileLoader should be cached separately
   IDWriteFactory* dwriteFactory;
-  DWriteCreateFactory (
-    DWRITE_FACTORY_TYPE_SHARED,
-    __uuidof (IDWriteFactory),
-    (IUnknown**) &dwriteFactory
-  );
+  DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory),
+		       (IUnknown**) &dwriteFactory);
 
   HRESULT hr;
   hb_blob_t *blob = hb_face_reference_blob (face);
-  DWriteFontFileStream *fontFileStream = new DWriteFontFileStream (
-    (uint8_t *) hb_blob_get_data (blob, nullptr),
-    hb_blob_get_length (blob));
+  DWriteFontFileStream *fontFileStream;
+  fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr),
+					     hb_blob_get_length (blob));
 
   DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream);
   dwriteFactory->RegisterFontFileLoader (fontFileLoader);
@@ -170,7 +163,7 @@
   IDWriteFontFile *fontFile;
   uint64_t fontFileKey = 0;
   hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey),
-      fontFileLoader, &fontFile);
+						     fontFileLoader, &fontFile);
 
 #define FAIL(...) \
   HB_STMT_START { \
@@ -193,7 +186,7 @@
 
   IDWriteFontFace *fontFace;
   dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0,
-    DWRITE_FONT_SIMULATIONS_NONE, &fontFace);
+				 DWRITE_FONT_SIMULATIONS_NONE, &fontFace);
 
   data->dwriteFactory = dwriteFactory;
   data->fontFile = fontFile;
@@ -233,15 +226,11 @@
  * shaper font data
  */
 
-struct hb_directwrite_font_data_t
-{
-};
+struct hb_directwrite_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_font_data_t *data = new hb_directwrite_font_data_t;
   if (unlikely (!data))
     return nullptr;
@@ -256,35 +245,14 @@
 }
 
 
-/*
- * shaper shape_plan data
- */
-
-struct hb_directwrite_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_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
-}
-
-void
-_hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shape_plan_data_t *data HB_UNUSED)
-{
-}
-
 // Most of TextAnalysis is originally written by Bas Schouten for Mozilla project
 // but now is relicensed to MIT for HarfBuzz use
-class TextAnalysis
-  : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
+class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
 {
 public:
 
-  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) { return S_OK; }
+  IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject)
+  { return S_OK; }
   IFACEMETHOD_ (ULONG, AddRef) () { return 1; }
   IFACEMETHOD_ (ULONG, Release) () { return 1; }
 
@@ -301,7 +269,7 @@
     uint8_t mBidiLevel;
     bool mIsSideways;
 
-    inline bool ContainsTextPosition (uint32_t aTextPosition) const
+    bool ContainsTextPosition (uint32_t aTextPosition) const
     {
       return aTextPosition >= mTextStart &&
 	     aTextPosition <  mTextStart + mTextLength;
@@ -311,16 +279,10 @@
   };
 
 public:
-  TextAnalysis (const wchar_t* text,
-    uint32_t textLength,
-    const wchar_t* localeName,
-    DWRITE_READING_DIRECTION readingDirection)
-    : mText (text)
-    , mTextLength (textLength)
-    , mLocaleName (localeName)
-    , mReadingDirection (readingDirection)
-    , mCurrentRun (nullptr) { };
-
+  TextAnalysis (const wchar_t* text, uint32_t textLength,
+		const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection)
+	       : mText (text), mTextLength (textLength), mLocaleName (localeName),
+		 mReadingDirection (readingDirection), mCurrentRun (nullptr) {}
   ~TextAnalysis ()
   {
     // delete runs, except mRunHead which is part of the TextAnalysis object
@@ -332,8 +294,8 @@
     }
   }
 
-  STDMETHODIMP GenerateResults (IDWriteTextAnalyzer* textAnalyzer,
-    Run **runHead)
+  STDMETHODIMP
+  GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead)
   {
     // Analyzes the text using the script analyzer and returns
     // the result as a series of runs.
@@ -358,9 +320,10 @@
 
   // IDWriteTextAnalysisSource implementation
 
-  IFACEMETHODIMP GetTextAtPosition (uint32_t textPosition,
-    OUT wchar_t const** textString,
-    OUT uint32_t* textLength)
+  IFACEMETHODIMP
+  GetTextAtPosition (uint32_t textPosition,
+		     OUT wchar_t const** textString,
+		     OUT uint32_t* textLength)
   {
     if (textPosition >= mTextLength)
     {
@@ -376,9 +339,10 @@
     return S_OK;
   }
 
-  IFACEMETHODIMP GetTextBeforePosition (uint32_t textPosition,
-    OUT wchar_t const** textString,
-    OUT uint32_t* textLength)
+  IFACEMETHODIMP
+  GetTextBeforePosition (uint32_t textPosition,
+			 OUT wchar_t const** textString,
+			 OUT uint32_t* textLength)
   {
     if (textPosition == 0 || textPosition > mTextLength)
     {
@@ -396,19 +360,16 @@
   }
 
   IFACEMETHODIMP_ (DWRITE_READING_DIRECTION)
-    GetParagraphReadingDirection () { return mReadingDirection; }
+  GetParagraphReadingDirection () { return mReadingDirection; }
 
-  IFACEMETHODIMP GetLocaleName (uint32_t textPosition,
-    uint32_t* textLength,
-    wchar_t const** localeName)
-  {
-    return S_OK;
-  }
+  IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength,
+				wchar_t const** localeName)
+  { return S_OK; }
 
   IFACEMETHODIMP
-    GetNumberSubstitution (uint32_t textPosition,
-    OUT uint32_t* textLength,
-    OUT IDWriteNumberSubstitution** numberSubstitution)
+  GetNumberSubstitution (uint32_t textPosition,
+			 OUT uint32_t* textLength,
+			 OUT IDWriteNumberSubstitution** numberSubstitution)
   {
     // We do not support number substitution.
     *numberSubstitution = nullptr;
@@ -420,9 +381,8 @@
   // IDWriteTextAnalysisSink implementation
 
   IFACEMETHODIMP
-    SetScriptAnalysis (uint32_t textPosition,
-    uint32_t textLength,
-    DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
+  SetScriptAnalysis (uint32_t textPosition, uint32_t textLength,
+		     DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
   {
     SetCurrentRun (textPosition);
     SplitCurrentRun (textPosition);
@@ -436,19 +396,19 @@
   }
 
   IFACEMETHODIMP
-    SetLineBreakpoints (uint32_t textPosition,
-    uint32_t textLength,
-    const DWRITE_LINE_BREAKPOINT* lineBreakpoints) { return S_OK; }
+  SetLineBreakpoints (uint32_t textPosition,
+		      uint32_t textLength,
+		      const DWRITE_LINE_BREAKPOINT* lineBreakpoints)
+  { return S_OK; }
 
-  IFACEMETHODIMP SetBidiLevel (uint32_t textPosition,
-    uint32_t textLength,
-    uint8_t explicitLevel,
-    uint8_t resolvedLevel) { return S_OK; }
+  IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength,
+			       uint8_t explicitLevel, uint8_t resolvedLevel)
+  { return S_OK; }
 
   IFACEMETHODIMP
-    SetNumberSubstitution (uint32_t textPosition,
-    uint32_t textLength,
-    IDWriteNumberSubstitution* numberSubstitution) { return S_OK; }
+  SetNumberSubstitution (uint32_t textPosition, uint32_t textLength,
+			 IDWriteNumberSubstitution* numberSubstitution)
+  { return S_OK; }
 
 protected:
   Run *FetchNextRun (IN OUT uint32_t* textLength)
@@ -548,15 +508,15 @@
 
 static hb_bool_t
 _hb_directwrite_shape_full (hb_shape_plan_t    *shape_plan,
-  hb_font_t          *font,
-  hb_buffer_t        *buffer,
-  const hb_feature_t *features,
-  unsigned int        num_features,
-  float               lineWidth)
+			    hb_font_t          *font,
+			    hb_buffer_t        *buffer,
+			    const hb_feature_t *features,
+			    unsigned int        num_features,
+			    float               lineWidth)
 {
   hb_face_t *face = font->face;
-  hb_directwrite_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-  hb_directwrite_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
+  const hb_directwrite_face_data_t *face_data = face->data.directwrite;
+  const hb_directwrite_font_data_t *font_data = font->data.directwrite;
   IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
   IDWriteFontFace *fontFace = face_data->fontFace;
 
@@ -608,9 +568,10 @@
 
   // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
 
-  DWRITE_READING_DIRECTION readingDirection = buffer->props.direction ?
-    DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
-    DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
+  DWRITE_READING_DIRECTION readingDirection;
+  readingDirection = buffer->props.direction ?
+		     DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
+		     DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
 
   /*
   * There's an internal 16-bit limit on some things inside the analyzer,
@@ -639,10 +600,8 @@
 
   const wchar_t localeName[20] = {0};
   if (buffer->props.language != nullptr)
-  {
     mbstowcs ((wchar_t*) localeName,
-      hb_language_to_string (buffer->props.language), 20);
-  }
+	      hb_language_to_string (buffer->props.language), 20);
 
   // TODO: it does work but doesn't care about ranges
   DWRITE_TYPOGRAPHIC_FEATURES typographic_features;
@@ -653,27 +612,29 @@
     for (unsigned int i = 0; i < num_features; ++i)
     {
       typographic_features.features[i].nameTag = (DWRITE_FONT_FEATURE_TAG)
-	hb_uint32_swap (features[i].tag);
+						 hb_uint32_swap (features[i].tag);
       typographic_features.features[i].parameter = features[i].value;
     }
   }
-  const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures =
-    (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features;
+  const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures;
+  dwFeatures = (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features;
   const uint32_t featureRangeLengths[] = { textLength };
   //
 
-  uint16_t* clusterMap = new uint16_t[textLength];
-  DWRITE_SHAPING_TEXT_PROPERTIES* textProperties =
-    new DWRITE_SHAPING_TEXT_PROPERTIES[textLength];
+  uint16_t* clusterMap;
+  clusterMap = new uint16_t[textLength];
+  DWRITE_SHAPING_TEXT_PROPERTIES* textProperties;
+  textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength];
 retry_getglyphs:
   uint16_t* glyphIndices = new uint16_t[maxGlyphCount];
-  DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties =
-    new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount];
+  DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties;
+  glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount];
 
   hr = analyzer->GetGlyphs (textString, textLength, fontFace, false,
-    isRightToLeft, &runHead->mScript, localeName, nullptr, &dwFeatures,
-    featureRangeLengths, 1, maxGlyphCount, clusterMap, textProperties, glyphIndices,
-    glyphProperties, &glyphCount);
+			    isRightToLeft, &runHead->mScript, localeName,
+			    nullptr, &dwFeatures, featureRangeLengths, 1,
+			    maxGlyphCount, clusterMap, textProperties,
+			    glyphIndices, glyphProperties, &glyphCount);
 
   if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
   {
@@ -693,30 +654,28 @@
   /* The -2 in the following is to compensate for possible
    * alignment needed after the WORD array.  sizeof (WORD) == 2. */
   unsigned int glyphs_size = (scratch_size * sizeof (int) - 2)
-         / (sizeof (WORD) +
-            sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) +
-            sizeof (int) +
-            sizeof (DWRITE_GLYPH_OFFSET) +
-            sizeof (uint32_t));
+			     / (sizeof (WORD) +
+			        sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) +
+			        sizeof (int) +
+			        sizeof (DWRITE_GLYPH_OFFSET) +
+			        sizeof (uint32_t));
   ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
 
 #undef ALLOCATE_ARRAY
 
   int fontEmSize = font->face->get_upem ();
-  if (fontEmSize < 0)
-    fontEmSize = -fontEmSize;
+  if (fontEmSize < 0) fontEmSize = -fontEmSize;
 
-  if (fontEmSize < 0)
-    fontEmSize = -fontEmSize;
+  if (fontEmSize < 0) fontEmSize = -fontEmSize;
   double x_mult = (double) font->x_scale / fontEmSize;
   double y_mult = (double) font->y_scale / fontEmSize;
 
-  hr = analyzer->GetGlyphPlacements (textString,
-    clusterMap, textProperties, textLength, glyphIndices,
-    glyphProperties, glyphCount, fontFace, fontEmSize,
-    false, isRightToLeft, &runHead->mScript, localeName,
-    &dwFeatures, featureRangeLengths, 1,
-    glyphAdvances, glyphOffsets);
+  hr = analyzer->GetGlyphPlacements (textString, clusterMap, textProperties,
+				     textLength, glyphIndices, glyphProperties,
+				     glyphCount, fontFace, fontEmSize,
+				     false, isRightToLeft, &runHead->mScript, localeName,
+				     &dwFeatures, featureRangeLengths, 1,
+				     glyphAdvances, glyphOffsets);
 
   if (FAILED (hr))
     FAIL ("Analyzer failed to get glyph placements.");
@@ -726,12 +685,12 @@
 
   if (analyzer1 && lineWidth)
   {
-
     DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities =
       new DWRITE_JUSTIFICATION_OPPORTUNITY[maxGlyphCount];
-    hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize,
-      runHead->mScript, textLength, glyphCount, textString, clusterMap,
-      glyphProperties, justificationOpportunities);
+    hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize, runHead->mScript,
+						   textLength, glyphCount, textString,
+						   clusterMap, glyphProperties,
+						   justificationOpportunities);
 
     if (FAILED (hr))
       FAIL ("Analyzer failed to get justification opportunities.");
@@ -739,15 +698,14 @@
     float* justifiedGlyphAdvances = new float[maxGlyphCount];
     DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[glyphCount];
     hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities,
-      glyphAdvances, glyphOffsets, justifiedGlyphAdvances, justifiedGlyphOffsets);
+					  glyphAdvances, glyphOffsets, justifiedGlyphAdvances,
+					  justifiedGlyphOffsets);
 
-    if (FAILED (hr))
-      FAIL ("Analyzer failed to get justified glyph advances.");
+    if (FAILED (hr)) FAIL ("Analyzer failed to get justify glyph advances.");
 
     DWRITE_SCRIPT_PROPERTIES scriptProperties;
     hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties);
-    if (FAILED (hr))
-      FAIL ("Analyzer failed to get script properties.");
+    if (FAILED (hr)) FAIL ("Analyzer failed to get script properties.");
     uint32_t justificationCharacter = scriptProperties.justificationCharacter;
 
     // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
@@ -757,14 +715,15 @@
     retry_getjustifiedglyphs:
       uint16_t* modifiedGlyphIndices = new uint16_t[maxGlyphCount];
       float* modifiedGlyphAdvances = new float[maxGlyphCount];
-      DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets =
-	new DWRITE_GLYPH_OFFSET[maxGlyphCount];
+      DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
       uint32_t actualGlyphsCount;
       hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript,
-	textLength, glyphCount, maxGlyphCount, clusterMap, glyphIndices,
-	glyphAdvances, justifiedGlyphAdvances, justifiedGlyphOffsets,
-	glyphProperties, &actualGlyphsCount, modifiedClusterMap, modifiedGlyphIndices,
-	modifiedGlyphAdvances, modifiedGlyphOffsets);
+					  textLength, glyphCount, maxGlyphCount,
+					  clusterMap, glyphIndices, glyphAdvances,
+					  justifiedGlyphAdvances, justifiedGlyphOffsets,
+					  glyphProperties, &actualGlyphsCount,
+					  modifiedClusterMap, modifiedGlyphIndices,
+					  modifiedGlyphAdvances, modifiedGlyphOffsets);
 
       if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))
       {
@@ -804,7 +763,6 @@
     }
 
     delete [] justificationOpportunities;
-
   }
 
   /* Ok, we've got everything we need, now compose output buffer,
@@ -854,13 +812,11 @@
 
     /* TODO vertical */
     pos->x_advance = x_mult * (int32_t) info->mask;
-    pos->x_offset =
-      x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
+    pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32);
     pos->y_offset = y_mult * info->var2.i32;
   }
 
-  if (isRightToLeft)
-    hb_buffer_reverse (buffer);
+  if (isRightToLeft) hb_buffer_reverse (buffer);
 
   delete [] clusterMap;
   delete [] glyphIndices;
@@ -878,13 +834,13 @@
 
 hb_bool_t
 _hb_directwrite_shape (hb_shape_plan_t    *shape_plan,
-  hb_font_t          *font,
-  hb_buffer_t        *buffer,
-  const hb_feature_t *features,
-  unsigned int        num_features)
+		       hb_font_t          *font,
+		       hb_buffer_t        *buffer,
+		       const hb_feature_t *features,
+		       unsigned int        num_features)
 {
   return _hb_directwrite_shape_full (shape_plan, font, buffer,
-    features, num_features, 0);
+				     features, num_features, 0);
 }
 
 /*
@@ -893,16 +849,17 @@
 
 hb_bool_t
 hb_directwrite_shape_experimental_width (hb_font_t          *font,
-  hb_buffer_t        *buffer,
-  const hb_feature_t *features,
-  unsigned int        num_features,
-  float               width)
+					 hb_buffer_t        *buffer,
+					 const hb_feature_t *features,
+					 unsigned int        num_features,
+					 float               width)
 {
   static const char *shapers = "directwrite";
-  hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face,
-    &buffer->props, features, num_features, &shapers);
+  hb_shape_plan_t *shape_plan;
+  shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
+					    features, num_features, &shapers);
   hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
-    features, num_features, width);
+					      features, num_features, width);
 
   buffer->unsafe_to_break_all ();
 
diff --git a/src/hb-dsalgs.hh b/src/hb-dsalgs.hh
index eb15c08..48ce989 100644
--- a/src/hb-dsalgs.hh
+++ b/src/hb-dsalgs.hh
@@ -46,7 +46,7 @@
 static inline HB_CONST_FUNC unsigned int
 hb_popcount (T v)
 {
-#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && defined(__OPTIMIZE__)
+#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
   if (sizeof (T) <= sizeof (unsigned int))
     return __builtin_popcount (v);
 
@@ -89,7 +89,7 @@
 {
   if (unlikely (!v)) return 0;
 
-#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
+#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
   if (sizeof (T) <= sizeof (unsigned int))
     return sizeof (unsigned int) * 8 - __builtin_clz (v);
 
@@ -163,7 +163,7 @@
 {
   if (unlikely (!v)) return 0;
 
-#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
+#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
   if (sizeof (T) <= sizeof (unsigned int))
     return __builtin_ctz (v);
 
@@ -234,6 +234,15 @@
  * Tiny stuff.
  */
 
+template <typename T>
+static inline T* hb_addressof (T& arg)
+{
+  /* https://en.cppreference.com/w/cpp/memory/addressof */
+  return reinterpret_cast<T*>(
+	   &const_cast<char&>(
+	      reinterpret_cast<const volatile char&>(arg)));
+}
+
 /* ASCII tag/character handling */
 static inline bool ISALPHA (unsigned char c)
 { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
@@ -264,6 +273,17 @@
 /* A const version, but does not detect erratically being called on pointers. */
 #define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
 
+
+static inline int
+hb_memcmp (const void *a, const void *b, unsigned int len)
+{
+  /* It's illegal to pass NULL to memcmp(), even if len is zero.
+   * So, wrap it.
+   * https://sourceware.org/bugzilla/show_bug.cgi?id=23878 */
+  if (!len) return 0;
+  return memcmp (a, b, len);
+}
+
 static inline bool
 hb_unsigned_mul_overflows (unsigned int count, unsigned int size)
 {
@@ -276,11 +296,25 @@
   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> struct hb_is_signed;
+template <> struct hb_is_signed<signed char> { enum { value = true }; };
+template <> struct hb_is_signed<signed short> { enum { value = true }; };
+template <> struct hb_is_signed<signed int> { enum { value = true }; };
+template <> struct hb_is_signed<signed long> { enum { value = true }; };
+template <> struct hb_is_signed<unsigned char> { enum { value = false }; };
+template <> struct hb_is_signed<unsigned short> { enum { value = false }; };
+template <> struct hb_is_signed<unsigned int> { enum { value = false }; };
+template <> struct hb_is_signed<unsigned long> { enum { value = false }; };
+/* We need to define hb_is_signed for the typedefs we use on pre-Visual
+ * Studio 2010 for the int8_t type, since __int8/__int64 is not considered
+ * the same as char/long.  The previous lines will suffice for the other
+ * types, though.  Note that somehow, unsigned __int8 is considered same
+ * as unsigned char.
+ * https://github.com/harfbuzz/harfbuzz/pull/1499
+ */
+#if defined(_MSC_VER) && (_MSC_VER < 1600)
+template <> struct hb_is_signed<__int8> { enum { value = true }; };
+#endif
 
 template <typename T> static inline bool
 hb_in_range (T u, T lo, T hi)
@@ -290,7 +324,7 @@
    * 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), "");
+  static_assert (!hb_is_signed<T>::value, "");
 
   /* The casts below are important as if T is smaller than int,
    * the subtract results will become a signed int! */
@@ -313,6 +347,27 @@
  */
 
 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),
@@ -321,7 +376,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)
@@ -335,7 +390,12 @@
 }
 
 
-/* From https://github.com/noporpoise/sort_r */
+/* From https://github.com/noporpoise/sort_r
+ * With following modifications:
+ *
+ * 10 November 2018:
+ * https://github.com/noporpoise/sort_r/issues/7
+ */
 
 /* Isaac Turner 29 April 2014 Public Domain */
 
@@ -391,7 +451,7 @@
 
     /* Use median of first, middle and last items as pivot */
     char *x, *y, *xend, ch;
-    char *pl, *pr;
+    char *pl, *pm, *pr;
     char *last = b+w*(nel-1), *tmp;
     char *l[3];
     l[0] = b;
@@ -413,13 +473,15 @@
     pr = last;
 
     while(pl < pr) {
-      for(; pl < pr; pl += w) {
+      pm = pl+((pr-pl+1)>>1);
+      for(; pl < pm; pl += w) {
         if(sort_r_cmpswap(pl, pr, w, compar, arg)) {
           pr -= w; /* pivot now at pl */
           break;
         }
       }
-      for(; pl < pr; pr -= w) {
+      pm = pl+((pr-pl)>>1);
+      for(; pm < pr; pr -= w) {
         if(sort_r_cmpswap(pl, pr, w, compar, arg)) {
           pl += w; /* pivot now at pr */
           break;
@@ -490,94 +552,28 @@
 }
 
 
-template <typename Type>
-struct hb_auto_t : Type
-{
-  hb_auto_t (void) { Type::init (); }
-  /* Explicitly allow the following only for pointer and references,
-   * to avoid any accidental copies.
-   *
-   * Apparently if we template for all types, then gcc seems to
-   * capture a reference argument in the type, but clang doesn't,
-   * causing unwanted copies and bugs that come with it.  Ideally
-   * we should use C++11-style rvalue reference &&t1. */
-  template <typename T1> explicit hb_auto_t (T1 *t1) { Type::init (t1); }
-  template <typename T1> explicit hb_auto_t (T1 &t1) { Type::init (t1); }
-  ~hb_auto_t (void) { Type::fini (); }
-  private: /* Hide */
-  void init (void) {}
-  void fini (void) {}
-};
-
-template <typename T>
-struct hb_array_t
-{
-  inline hb_array_t (void) : arrayZ (nullptr), len (0) {}
-  inline hb_array_t (const T *array_, unsigned int len_) : arrayZ (array_), len (len_) {}
-
-  inline const T& operator [] (unsigned int i) const
-  {
-    if (unlikely (i >= len)) return Null(T);
-    return arrayZ[i];
-  }
-
-  inline void free (void) { ::free ((void *) arrayZ); arrayZ = nullptr; len = 0; }
-
-  const T *arrayZ;
-  unsigned int len;
-};
-
-struct hb_bytes_t
-{
-  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.arrayZ, arrayZ, len);
-  }
-  static inline int cmp (const void *pa, const void *pb)
-  {
-    hb_bytes_t *a = (hb_bytes_t *) pa;
-    hb_bytes_t *b = (hb_bytes_t *) pb;
-    return b->cmp (*a);
-  }
-
-  const char *arrayZ;
-  unsigned int len;
-};
-
-
 struct HbOpOr
 {
-  static const bool passthru_left = true;
-  static const bool passthru_right = true;
+  enum { passthru_left = true };
+  enum { passthru_right = true };
   template <typename T> static void process (T &o, const T &a, const T &b) { o = a | b; }
 };
 struct HbOpAnd
 {
-  static const bool passthru_left = false;
-  static const bool passthru_right = false;
+  enum { passthru_left = false };
+  enum { passthru_right = false };
   template <typename T> static void process (T &o, const T &a, const T &b) { o = a & b; }
 };
 struct HbOpMinus
 {
-  static const bool passthru_left = true;
-  static const bool passthru_right = false;
+  enum { passthru_left = true };
+  enum { passthru_right = false };
   template <typename T> static void process (T &o, const T &a, const T &b) { o = a & ~b; }
 };
 struct HbOpXor
 {
-  static const bool passthru_left = true;
-  static const bool passthru_right = true;
+  enum { passthru_left = true };
+  enum { passthru_right = true };
   template <typename T> static void process (T &o, const T &a, const T &b) { o = a ^ b; }
 };
 
@@ -593,8 +589,10 @@
   elt_t& operator [] (unsigned int i) { return u.v[i]; }
   const elt_t& operator [] (unsigned int i) const { return u.v[i]; }
 
+  void clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); }
+
   template <class Op>
-  inline hb_vector_size_t process (const hb_vector_size_t &o) const
+  hb_vector_size_t process (const hb_vector_size_t &o) const
   {
     hb_vector_size_t r;
 #if HB_VECTOR_SIZE
@@ -607,13 +605,13 @@
 	Op::process (r.u.v[i], u.v[i], o.u.v[i]);
     return r;
   }
-  inline hb_vector_size_t operator | (const hb_vector_size_t &o) const
+  hb_vector_size_t operator | (const hb_vector_size_t &o) const
   { return process<HbOpOr> (o); }
-  inline hb_vector_size_t operator & (const hb_vector_size_t &o) const
+  hb_vector_size_t operator & (const hb_vector_size_t &o) const
   { return process<HbOpAnd> (o); }
-  inline hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
+  hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
   { return process<HbOpXor> (o); }
-  inline hb_vector_size_t operator ~ () const
+  hb_vector_size_t operator ~ () const
   {
     hb_vector_size_t r;
 #if HB_VECTOR_SIZE && 0
diff --git a/src/hb-face.cc b/src/hb-face.cc
index bba1ee3..8bb8c51 100644
--- a/src/hb-face.cc
+++ b/src/hb-face.cc
@@ -36,6 +36,19 @@
 
 
 /**
+ * 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.
  *
@@ -69,23 +82,15 @@
 {
   HB_OBJECT_HEADER_STATIC,
 
-  true, /* immutable */
-
   nullptr, /* reference_table_func */
   nullptr, /* user_data */
   nullptr, /* destroy */
 
   0,    /* index */
-  1000, /* upem */
-  0,    /* num_glyphs */
+  HB_ATOMIC_INT_INIT (1000), /* upem */
+  HB_ATOMIC_INT_INIT (0),    /* num_glyphs */
 
-  {
-#define HB_SHAPER_IMPLEMENT(shaper) HB_ATOMIC_PTR_INIT (HB_SHAPER_DATA_INVALID),
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-  },
-
-  HB_ATOMIC_PTR_INIT (nullptr), /* shape_plans */
+  /* Zero for the rest is fine. */
 };
 
 
@@ -118,8 +123,10 @@
   face->user_data = user_data;
   face->destroy = destroy;
 
-  face->upem = 0;
-  face->num_glyphs = (unsigned int) -1;
+  face->num_glyphs.set_relaxed (-1);
+
+  face->data.init0 (face);
+  face->table.init0 (face);
 
   return face;
 }
@@ -217,7 +224,7 @@
  * Since: 0.9.2
  **/
 hb_face_t *
-hb_face_get_empty (void)
+hb_face_get_empty ()
 {
   return const_cast<hb_face_t *> (&Null(hb_face_t));
 }
@@ -252,7 +259,7 @@
 {
   if (!hb_object_destroy (face)) return;
 
-  for (hb_face_t::plan_node_t *node = face->shape_plans.get (); node; )
+  for (hb_face_t::plan_node_t *node = face->shape_plans; node; )
   {
     hb_face_t::plan_node_t *next = node->next;
     hb_shape_plan_destroy (node->shape_plan);
@@ -260,9 +267,8 @@
     node = next;
   }
 
-#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, face);
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
+  face->data.fini ();
+  face->table.fini ();
 
   if (face->destroy)
     face->destroy (face->user_data);
@@ -323,12 +329,10 @@
 void
 hb_face_make_immutable (hb_face_t *face)
 {
-  if (unlikely (hb_object_is_inert (face)))
-    return;
-  if (face->immutable)
+  if (hb_object_is_immutable (face))
     return;
 
-  face->immutable = true;
+  hb_object_make_immutable (face);
 }
 
 /**
@@ -344,7 +348,7 @@
 hb_bool_t
 hb_face_is_immutable (const hb_face_t *face)
 {
-  return face->immutable;
+  return hb_object_is_immutable (face);
 }
 
 
@@ -395,7 +399,7 @@
 hb_face_set_index (hb_face_t    *face,
 		   unsigned int  index)
 {
-  if (face->immutable)
+  if (hb_object_is_immutable (face))
     return;
 
   face->index = index;
@@ -430,10 +434,10 @@
 hb_face_set_upem (hb_face_t    *face,
 		  unsigned int  upem)
 {
-  if (face->immutable)
+  if (hb_object_is_immutable (face))
     return;
 
-  face->upem = upem;
+  face->upem.set_relaxed (upem);
 }
 
 /**
@@ -465,10 +469,10 @@
 hb_face_set_glyph_count (hb_face_t    *face,
 			 unsigned int  glyph_count)
 {
-  if (face->immutable)
+  if (hb_object_is_immutable (face))
     return;
 
-  face->num_glyphs = glyph_count;
+  face->num_glyphs.set_relaxed (glyph_count);
 }
 
 /**
@@ -538,8 +542,7 @@
 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);
+  face->table.cmap->collect_unicodes (out);
 }
 
 /**
@@ -555,8 +558,7 @@
 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);
+  face->table.cmap->collect_variation_selectors (out);
 }
 
 /**
@@ -573,8 +575,7 @@
 				    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->table.cmap->collect_variation_unicodes (variation_selector, out);
 }
 
 
@@ -587,10 +588,10 @@
 {
   struct table_entry_t
   {
-    inline int cmp (const hb_tag_t *t) const
+    int cmp (hb_tag_t t) const
     {
-      if (*t < tag) return -1;
-      if (*t > tag) return -1;
+      if (t < tag) return -1;
+      if (t > tag) return -1;
       return 0;
     }
 
@@ -602,7 +603,7 @@
 };
 
 static hb_face_builder_data_t *
-_hb_face_builder_data_create (void)
+_hb_face_builder_data_create ()
 {
   hb_face_builder_data_t *data = (hb_face_builder_data_t *) calloc (1, sizeof (hb_face_builder_data_t));
   if (unlikely (!data))
@@ -641,18 +642,13 @@
     return nullptr;
 
   hb_serialize_context_t c (buf, face_length);
+  c.propagate_error (data->tables);
   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);
+  bool ret = f->serialize_single (&c, sfnt_tag, data->tables.as_array ());
 
   c.end_serialize ();
 
@@ -666,7 +662,7 @@
 }
 
 static hb_blob_t *
-_hb_face_builder_reference_table (hb_face_t *face, hb_tag_t tag, void *user_data)
+_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;
 
@@ -693,7 +689,7 @@
  * Since: 1.9.0
  **/
 hb_face_t *
-hb_face_builder_create (void)
+hb_face_builder_create ()
 {
   hb_face_builder_data_t *data = _hb_face_builder_data_create ();
   if (unlikely (!data)) return hb_face_get_empty ();
diff --git a/src/hb-face.hh b/src/hb-face.hh
index f90453d..68834ba 100644
--- a/src/hb-face.hh
+++ b/src/hb-face.hh
@@ -33,28 +33,31 @@
 
 #include "hb-shaper.hh"
 #include "hb-shape-plan.hh"
+#include "hb-ot-face.hh"
 
 
 /*
  * hb_face_t
  */
 
+#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, face);
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+
 struct hb_face_t
 {
   hb_object_header_t header;
-  ASSERT_POD ();
-
-  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. */
+  mutable hb_atomic_int_t upem;		/* Units-per-EM. */
+  mutable hb_atomic_int_t num_glyphs;	/* Number of glyphs. */
 
-  struct hb_shaper_data_t shaper_data;	/* Various shaper data. */
+  hb_shaper_object_dataset_t<hb_face_t> data;/* Various shaper data. */
+  hb_ot_face_t table;			/* All the face's tables. */
 
   /* Cache */
   struct plan_node_t
@@ -64,7 +67,7 @@
   };
   hb_atomic_ptr_t<plan_node_t> shape_plans;
 
-  inline hb_blob_t *reference_table (hb_tag_t tag) const
+  hb_blob_t *reference_table (hb_tag_t tag) const
   {
     hb_blob_t *blob;
 
@@ -78,31 +81,29 @@
     return blob;
   }
 
-  inline HB_PURE_FUNC unsigned int get_upem (void) const
+  HB_PURE_FUNC unsigned int get_upem () const
   {
-    if (unlikely (!upem))
-      load_upem ();
-    return upem;
+    unsigned int ret = upem.get_relaxed ();
+    if (unlikely (!ret))
+    {
+      return load_upem ();
+    }
+    return ret;
   }
 
-  inline unsigned int get_num_glyphs (void) const
+  unsigned int get_num_glyphs () const
   {
-    if (unlikely (num_glyphs == (unsigned int) -1))
-      load_num_glyphs ();
-    return num_glyphs;
+    unsigned int ret = num_glyphs.get_relaxed ();
+    if (unlikely (ret == (unsigned int) -1))
+      return load_num_glyphs ();
+    return ret;
   }
 
   private:
-  HB_INTERNAL void load_upem (void) const;
-  HB_INTERNAL void load_num_glyphs (void) const;
+  HB_INTERNAL unsigned int load_upem () const;
+  HB_INTERNAL unsigned int load_num_glyphs () 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 dc8536c..09f0290 100644
--- a/src/hb-fallback-shape.cc
+++ b/src/hb-fallback-shape.cc
@@ -24,14 +24,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#define HB_SHAPER fallback
 #include "hb-shaper-impl.hh"
 
 
-HB_SHAPER_DATA_ENSURE_DEFINE(fallback, face)
-HB_SHAPER_DATA_ENSURE_DEFINE(fallback, font)
-
-
 /*
  * shaper face data
  */
@@ -69,28 +64,6 @@
 
 
 /*
- * shaper shape_plan data
- */
-
-struct hb_fallback_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_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
-}
-
-void
-_hb_fallback_shaper_shape_plan_data_destroy (hb_fallback_shape_plan_data_t *data HB_UNUSED)
-{
-}
-
-
-/*
  * shaper
  */
 
diff --git a/src/hb-font.cc b/src/hb-font.cc
index b6b668d..0f0bcad 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -34,6 +34,19 @@
 #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.
+ **/
+
+
 /*
  * hb_font_funcs_t
  */
@@ -41,23 +54,23 @@
 static hb_bool_t
 hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED,
 				void *font_data HB_UNUSED,
-				hb_font_extents_t *metrics,
+				hb_font_extents_t *extents,
 				void *user_data HB_UNUSED)
 {
-  memset (metrics, 0, sizeof (*metrics));
+  memset (extents, 0, sizeof (*extents));
   return false;
 }
 static hb_bool_t
 hb_font_get_font_h_extents_default (hb_font_t *font,
 				    void *font_data HB_UNUSED,
-				    hb_font_extents_t *metrics,
+				    hb_font_extents_t *extents,
 				    void *user_data HB_UNUSED)
 {
-  hb_bool_t ret = font->parent->get_font_h_extents (metrics);
+  hb_bool_t ret = font->parent->get_font_h_extents (extents);
   if (ret) {
-    metrics->ascender = font->parent_scale_y_distance (metrics->ascender);
-    metrics->descender = font->parent_scale_y_distance (metrics->descender);
-    metrics->line_gap = font->parent_scale_y_distance (metrics->line_gap);
+    extents->ascender = font->parent_scale_y_distance (extents->ascender);
+    extents->descender = font->parent_scale_y_distance (extents->descender);
+    extents->line_gap = font->parent_scale_y_distance (extents->line_gap);
   }
   return ret;
 }
@@ -65,23 +78,23 @@
 static hb_bool_t
 hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED,
 				void *font_data HB_UNUSED,
-				hb_font_extents_t *metrics,
+				hb_font_extents_t *extents,
 				void *user_data HB_UNUSED)
 {
-  memset (metrics, 0, sizeof (*metrics));
+  memset (extents, 0, sizeof (*extents));
   return false;
 }
 static hb_bool_t
 hb_font_get_font_v_extents_default (hb_font_t *font,
 				    void *font_data HB_UNUSED,
-				    hb_font_extents_t *metrics,
+				    hb_font_extents_t *extents,
 				    void *user_data HB_UNUSED)
 {
-  hb_bool_t ret = font->parent->get_font_v_extents (metrics);
+  hb_bool_t ret = font->parent->get_font_v_extents (extents);
   if (ret) {
-    metrics->ascender = font->parent_scale_x_distance (metrics->ascender);
-    metrics->descender = font->parent_scale_x_distance (metrics->descender);
-    metrics->line_gap = font->parent_scale_x_distance (metrics->line_gap);
+    extents->ascender = font->parent_scale_x_distance (extents->ascender);
+    extents->descender = font->parent_scale_x_distance (extents->descender);
+    extents->line_gap = font->parent_scale_x_distance (extents->line_gap);
   }
   return ret;
 }
@@ -89,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)
 {
@@ -142,8 +155,8 @@
 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)
 {
@@ -276,7 +289,7 @@
 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)
@@ -301,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)
@@ -326,8 +339,8 @@
 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;
@@ -345,8 +358,8 @@
 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;
@@ -364,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)
 {
@@ -389,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)
@@ -416,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)
 {
@@ -436,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)
 {
@@ -457,8 +471,6 @@
 {
   HB_OBJECT_HEADER_STATIC,
 
-  true, /* immutable */
-
   {
 #define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
@@ -481,8 +493,6 @@
 static const hb_font_funcs_t _hb_font_funcs_default = {
   HB_OBJECT_HEADER_STATIC,
 
-  true, /* immutable */
-
   {
 #define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
@@ -513,7 +523,7 @@
  * Since: 0.9.2
  **/
 hb_font_funcs_t *
-hb_font_funcs_create (void)
+hb_font_funcs_create ()
 {
   hb_font_funcs_t *ffuncs;
 
@@ -535,7 +545,7 @@
  * Since: 0.9.2
  **/
 hb_font_funcs_t *
-hb_font_funcs_get_empty (void)
+hb_font_funcs_get_empty ()
 {
   return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_default);
 }
@@ -631,12 +641,10 @@
 void
 hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs)
 {
-  if (unlikely (hb_object_is_inert (ffuncs)))
-    return;
-  if (ffuncs->immutable)
+  if (hb_object_is_immutable (ffuncs))
     return;
 
-  ffuncs->immutable = true;
+  hb_object_make_immutable (ffuncs);
 }
 
 /**
@@ -652,7 +660,7 @@
 hb_bool_t
 hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs)
 {
-  return ffuncs->immutable;
+  return hb_object_is_immutable (ffuncs);
 }
 
 
@@ -664,7 +672,7 @@
                                  void                        *user_data, \
                                  hb_destroy_func_t            destroy)   \
 {                                                                        \
-  if (ffuncs->immutable) {                                               \
+  if (hb_object_is_immutable (ffuncs)) {                                 \
     if (destroy)                                                         \
       destroy (user_data);                                               \
     return;                                                              \
@@ -1285,8 +1293,6 @@
 {
   HB_OBJECT_HEADER_STATIC,
 
-  true, /* immutable */
-
   nullptr, /* parent */
   const_cast<hb_face_t *> (&_hb_Null_hb_face_t),
 
@@ -1300,15 +1306,9 @@
   0, /* num_coords */
   nullptr, /* coords */
 
-  const_cast<hb_font_funcs_t *> (&_hb_Null_hb_font_funcs_t), /* klass */
-  nullptr, /* user_data */
-  nullptr, /* destroy */
+  const_cast<hb_font_funcs_t *> (&_hb_Null_hb_font_funcs_t),
 
-  {
-#define HB_SHAPER_IMPLEMENT(shaper) HB_ATOMIC_PTR_INIT (HB_SHAPER_DATA_INVALID),
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-  }
+  /* Zero for the rest is fine. */
 };
 
 
@@ -1326,7 +1326,7 @@
   font->parent = hb_font_get_empty ();
   font->face = hb_face_reference (face);
   font->klass = hb_font_funcs_get_empty ();
-
+  font->data.init0 (font);
   font->x_scale = font->y_scale = hb_face_get_upem (face);
 
   return font;
@@ -1371,7 +1371,7 @@
 
   hb_font_t *font = _hb_font_create (parent->face);
 
-  if (unlikely (hb_object_is_inert (font)))
+  if (unlikely (hb_object_is_immutable (font)))
     return font;
 
   font->parent = hb_font_reference (parent);
@@ -1408,7 +1408,7 @@
  * Since: 0.9.2
  **/
 hb_font_t *
-hb_font_get_empty (void)
+hb_font_get_empty ()
 {
   return const_cast<hb_font_t *> (&Null(hb_font_t));
 }
@@ -1442,9 +1442,7 @@
 {
   if (!hb_object_destroy (font)) return;
 
-#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, font);
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
+  font->data.fini ();
 
   if (font->destroy)
     font->destroy (font->user_data);
@@ -1511,15 +1509,13 @@
 void
 hb_font_make_immutable (hb_font_t *font)
 {
-  if (unlikely (hb_object_is_inert (font)))
-    return;
-  if (font->immutable)
+  if (hb_object_is_immutable (font))
     return;
 
   if (font->parent)
     hb_font_make_immutable (font->parent);
 
-  font->immutable = true;
+  hb_object_make_immutable (font);
 }
 
 /**
@@ -1535,7 +1531,7 @@
 hb_bool_t
 hb_font_is_immutable (hb_font_t *font)
 {
-  return font->immutable;
+  return hb_object_is_immutable (font);
 }
 
 /**
@@ -1551,7 +1547,7 @@
 hb_font_set_parent (hb_font_t *font,
 		    hb_font_t *parent)
 {
-  if (font->immutable)
+  if (hb_object_is_immutable (font))
     return;
 
   if (!parent)
@@ -1593,7 +1589,7 @@
 hb_font_set_face (hb_font_t *font,
 		  hb_face_t *face)
 {
-  if (font->immutable)
+  if (hb_object_is_immutable (font))
     return;
 
   if (unlikely (!face))
@@ -1640,7 +1636,8 @@
 		   void              *font_data,
 		   hb_destroy_func_t  destroy)
 {
-  if (font->immutable) {
+  if (hb_object_is_immutable (font))
+  {
     if (destroy)
       destroy (font_data);
     return;
@@ -1675,7 +1672,8 @@
 		        hb_destroy_func_t  destroy)
 {
   /* Destroy user_data? */
-  if (font->immutable) {
+  if (hb_object_is_immutable (font))
+  {
     if (destroy)
       destroy (font_data);
     return;
@@ -1704,7 +1702,7 @@
 		   int x_scale,
 		   int y_scale)
 {
-  if (font->immutable)
+  if (hb_object_is_immutable (font))
     return;
 
   font->x_scale = x_scale;
@@ -1745,7 +1743,7 @@
 		  unsigned int x_ppem,
 		  unsigned int y_ppem)
 {
-  if (font->immutable)
+  if (hb_object_is_immutable (font))
     return;
 
   font->x_ppem = x_ppem;
@@ -1785,7 +1783,7 @@
 void
 hb_font_set_ptem (hb_font_t *font, float ptem)
 {
-  if (font->immutable)
+  if (hb_object_is_immutable (font))
     return;
 
   font->ptem = ptem;
@@ -1832,7 +1830,7 @@
 			const hb_variation_t *variations,
 			unsigned int variations_length)
 {
-  if (font->immutable)
+  if (hb_object_is_immutable (font))
     return;
 
   if (!variations_length)
@@ -1863,7 +1861,7 @@
 			       const float *coords,
 			       unsigned int coords_length)
 {
-  if (font->immutable)
+  if (hb_object_is_immutable (font))
     return;
 
   int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr;
@@ -1884,7 +1882,7 @@
 				   const int *coords, /* 2.14 normalized */
 				   unsigned int coords_length)
 {
-  if (font->immutable)
+  if (hb_object_is_immutable (font))
     return;
 
   int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr;
diff --git a/src/hb-font.h b/src/hb-font.h
index 74c61ab..e2086d8 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -110,7 +110,7 @@
 /* func types */
 
 typedef hb_bool_t (*hb_font_get_font_extents_func_t) (hb_font_t *font, void *font_data,
-						       hb_font_extents_t *metrics,
+						       hb_font_extents_t *extents,
 						       void *user_data);
 typedef hb_font_get_font_extents_func_t hb_font_get_font_h_extents_func_t;
 typedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t;
diff --git a/src/hb-font.hh b/src/hb-font.hh
index 2df5e42..aaa0fd9 100644
--- a/src/hb-font.hh
+++ b/src/hb-font.hh
@@ -62,9 +62,6 @@
 struct hb_font_funcs_t
 {
   hb_object_header_t header;
-  ASSERT_POD ();
-
-  hb_bool_t immutable;
 
   struct {
 #define HB_FONT_FUNC_IMPLEMENT(name) void *name;
@@ -89,7 +86,7 @@
 #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);
@@ -99,12 +96,13 @@
  * hb_font_t
  */
 
+#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, font);
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+
 struct hb_font_t
 {
   hb_object_header_t header;
-  ASSERT_POD ();
-
-  hb_bool_t immutable;
 
   hb_font_t *parent;
   hb_face_t *face;
@@ -125,44 +123,46 @@
   void              *user_data;
   hb_destroy_func_t  destroy;
 
-  struct hb_shaper_data_t shaper_data;
+  hb_shaper_object_dataset_t<hb_font_t> data; /* Various shaper data. */
 
 
   /* Convert from font-space to user-space */
-  inline int dir_scale (hb_direction_t direction)
+  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)
+  hb_position_t em_scale_x (int16_t v) { return em_scale (v, x_scale); }
+  hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); }
+  hb_position_t em_scalef_x (float v) { return em_scalef (v, this->x_scale); }
+  hb_position_t em_scalef_y (float v) { return em_scalef (v, this->y_scale); }
+  float em_fscale_x (int16_t v) { return em_fscale (v, x_scale); }
+  float em_fscale_y (int16_t v) { return em_fscale (v, y_scale); }
+  hb_position_t em_scale_dir (int16_t v, hb_direction_t direction)
   { return em_scale (v, dir_scale (direction)); }
 
   /* Convert from parent-font user-space to our user-space */
-  inline hb_position_t parent_scale_x_distance (hb_position_t v) {
+  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) {
+  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);
-  }
+  hb_position_t parent_scale_x_position (hb_position_t v)
+  { return parent_scale_x_distance (v); }
+  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) {
+  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) {
+  void parent_scale_position (hb_position_t *x, hb_position_t *y)
+  {
     *x = parent_scale_x_position (*x);
     *y = parent_scale_y_position (*y);
   }
@@ -176,14 +176,14 @@
   /* has_* ... */
 #define HB_FONT_FUNC_IMPLEMENT(name) \
   bool \
-  has_##name##_func (void) \
+  has_##name##_func () \
   { \
     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) \
+  has_##name##_func_set () \
   { \
     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]); \
@@ -192,14 +192,14 @@
   HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
 
-  inline hb_bool_t get_font_h_extents (hb_font_extents_t *extents)
+  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)
+  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,
@@ -207,13 +207,13 @@
 					klass->user_data.font_v_extents);
   }
 
-  inline bool has_glyph (hb_codepoint_t unicode)
+  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_bool_t get_nominal_glyph (hb_codepoint_t unicode,
 				      hb_codepoint_t *glyph)
   {
     *glyph = 0;
@@ -221,11 +221,11 @@
 				       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)
+  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,
@@ -234,8 +234,8 @@
 					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)
+  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,
@@ -243,25 +243,25 @@
 					 klass->user_data.variation_glyph);
   }
 
-  inline hb_position_t get_glyph_h_advance (hb_codepoint_t glyph)
+  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)
+  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)
+  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,
@@ -270,11 +270,11 @@
 					  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)
+  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,
@@ -283,7 +283,7 @@
 					  klass->user_data.glyph_v_advances);
   }
 
-  inline hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph,
+  hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph,
 				       hb_position_t *x, hb_position_t *y)
   {
     *x = *y = 0;
@@ -292,8 +292,8 @@
 					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)
+  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,
@@ -301,21 +301,23 @@
 					klass->user_data.glyph_v_origin);
   }
 
-  inline hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, hb_codepoint_t right_glyph)
+  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)
+  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_bool_t get_glyph_extents (hb_codepoint_t glyph,
 				      hb_glyph_extents_t *extents)
   {
     memset (extents, 0, sizeof (*extents));
@@ -325,7 +327,7 @@
 				       klass->user_data.glyph_extents);
   }
 
-  inline hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index,
+  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;
@@ -335,8 +337,8 @@
 					     klass->user_data.glyph_contour_point);
   }
 
-  inline hb_bool_t get_glyph_name (hb_codepoint_t glyph,
-				   char *name, unsigned int size)
+  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,
@@ -345,8 +347,8 @@
 				    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)
+  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);
@@ -359,7 +361,7 @@
 
   /* A bit higher-level, and with fallback */
 
-  inline void get_h_extents_with_fallback (hb_font_extents_t *extents)
+  void get_h_extents_with_fallback (hb_font_extents_t *extents)
   {
     if (!get_font_h_extents (extents))
     {
@@ -368,7 +370,7 @@
       extents->line_gap = 0;
     }
   }
-  inline void get_v_extents_with_fallback (hb_font_extents_t *extents)
+  void get_v_extents_with_fallback (hb_font_extents_t *extents)
   {
     if (!get_font_v_extents (extents))
     {
@@ -378,8 +380,8 @@
     }
   }
 
-  inline void get_extents_for_direction (hb_direction_t direction,
-					 hb_font_extents_t *extents)
+  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);
@@ -387,9 +389,9 @@
       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)
+  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)))
@@ -397,12 +399,12 @@
     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)
+  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);
@@ -410,8 +412,8 @@
       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)
+  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;
 
@@ -421,8 +423,8 @@
     *y = extents.ascender;
   }
 
-  inline void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph,
-						hb_position_t *x, hb_position_t *y)
+  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))
@@ -432,8 +434,8 @@
       *x -= dx; *y -= dy;
     }
   }
-  inline void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph,
-						hb_position_t *x, hb_position_t *y)
+  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))
@@ -444,9 +446,9 @@
     }
   }
 
-  inline void get_glyph_origin_for_direction (hb_codepoint_t glyph,
-					      hb_direction_t direction,
-					      hb_position_t *x, hb_position_t *y)
+  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);
@@ -454,8 +456,8 @@
       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)
+  void add_glyph_h_origin (hb_codepoint_t glyph,
+			   hb_position_t *x, hb_position_t *y)
   {
     hb_position_t origin_x, origin_y;
 
@@ -464,8 +466,8 @@
     *x += origin_x;
     *y += origin_y;
   }
-  inline void add_glyph_v_origin (hb_codepoint_t glyph,
-				  hb_position_t *x, hb_position_t *y)
+  void add_glyph_v_origin (hb_codepoint_t glyph,
+			   hb_position_t *x, hb_position_t *y)
   {
     hb_position_t origin_x, origin_y;
 
@@ -474,9 +476,9 @@
     *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)
+  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;
 
@@ -486,8 +488,8 @@
     *y += origin_y;
   }
 
-  inline void subtract_glyph_h_origin (hb_codepoint_t glyph,
-				       hb_position_t *x, hb_position_t *y)
+  void subtract_glyph_h_origin (hb_codepoint_t glyph,
+			        hb_position_t *x, hb_position_t *y)
   {
     hb_position_t origin_x, origin_y;
 
@@ -496,8 +498,8 @@
     *x -= origin_x;
     *y -= origin_y;
   }
-  inline void subtract_glyph_v_origin (hb_codepoint_t glyph,
-				       hb_position_t *x, hb_position_t *y)
+  void subtract_glyph_v_origin (hb_codepoint_t glyph,
+				hb_position_t *x, hb_position_t *y)
   {
     hb_position_t origin_x, origin_y;
 
@@ -506,9 +508,9 @@
     *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)
+  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;
 
@@ -518,9 +520,9 @@
     *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)
+  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;
@@ -531,9 +533,9 @@
     }
   }
 
-  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 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);
 
@@ -543,9 +545,9 @@
     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 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);
 
@@ -556,7 +558,7 @@
   }
 
   /* Generates gidDDD if glyph has no name. */
-  inline void
+  void
   glyph_to_string (hb_codepoint_t glyph,
 		   char *s, unsigned int size)
   {
@@ -567,7 +569,7 @@
   }
 
   /* Parses gidDDD and uniUUUU strings automatically. */
-  inline hb_bool_t
+  hb_bool_t
   glyph_from_string (const char *s, int len, /* -1 means nul-terminated */
 		     hb_codepoint_t *glyph)
   {
@@ -597,29 +599,19 @@
     return false;
   }
 
-  inline hb_position_t em_scale (int16_t v, int scale)
+  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 ();
-  }
+  hb_position_t em_scalef (float v, int scale)
+  { return (hb_position_t) round (v * scale / face->get_upem ()); }
+  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 18fb72a..5f6e380 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -40,6 +40,17 @@
 #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.
@@ -124,7 +135,7 @@
 void
 hb_ft_font_set_load_flags (hb_font_t *font, int load_flags)
 {
-  if (font->immutable)
+  if (hb_object_is_immutable (font))
     return;
 
   if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
@@ -455,9 +466,9 @@
   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;
-  metrics->line_gap = ft_face->size->metrics.height - (ft_face->size->metrics.ascender - ft_face->size->metrics.descender);
+  metrics->ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale);
+  metrics->descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale);
+  metrics->line_gap = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale ) - (metrics->ascender - metrics->descender);
   if (font->y_scale < 0)
   {
     metrics->ascender = -metrics->ascender;
@@ -467,13 +478,13 @@
   return true;
 }
 
-#ifdef HB_USE_ATEXIT
-static void free_static_ft_funcs (void);
+#if HB_USE_ATEXIT
+static void free_static_ft_funcs ();
 #endif
 
 static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ft_font_funcs_lazy_loader_t>
 {
-  static inline hb_font_funcs_t *create (void)
+  static hb_font_funcs_t *create ()
   {
     hb_font_funcs_t *funcs = hb_font_funcs_create ();
 
@@ -493,7 +504,7 @@
 
     hb_font_funcs_make_immutable (funcs);
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
     atexit (free_static_ft_funcs);
 #endif
 
@@ -501,16 +512,16 @@
   }
 } static_ft_funcs;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
 static
-void free_static_ft_funcs (void)
+void free_static_ft_funcs ()
 {
   static_ft_funcs.free_instance ();
 }
 #endif
 
 static hb_font_funcs_t *
-_hb_ft_get_font_funcs (void)
+_hb_ft_get_font_funcs ()
 {
   return static_ft_funcs.get_unconst ();
 }
@@ -733,45 +744,45 @@
   return hb_ft_font_create (ft_face, _hb_ft_face_destroy);
 }
 
-#ifdef HB_USE_ATEXIT
-static void free_static_ft_library (void);
+#if HB_USE_ATEXIT
+static void free_static_ft_library ();
 #endif
 
-static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_ptr_t<FT_Library>::value,
+static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer (FT_Library),
 							     hb_ft_library_lazy_loader_t>
 {
-  static inline FT_Library create (void)
+  static FT_Library create ()
   {
     FT_Library l;
     if (FT_Init_FreeType (&l))
       return nullptr;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
     atexit (free_static_ft_library);
 #endif
 
     return l;
   }
-  static inline void destroy (FT_Library l)
+  static void destroy (FT_Library l)
   {
     FT_Done_FreeType (l);
   }
-  static inline FT_Library get_null (void)
+  static FT_Library get_null ()
   {
     return nullptr;
   }
 } static_ft_library;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
 static
-void free_static_ft_library (void)
+void free_static_ft_library ()
 {
   static_ft_library.free_instance ();
 }
 #endif
 
 static FT_Library
-get_ft_library (void)
+get_ft_library ()
 {
   return static_ft_library.get_unconst ();
 }
diff --git a/src/hb-glib.cc b/src/hb-glib.cc
index a34acbb..5763754 100644
--- a/src/hb-glib.cc
+++ b/src/hb-glib.cc
@@ -33,6 +33,16 @@
 #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)
 static const hb_script_t
 glib_script_to_script[] =
@@ -326,13 +336,13 @@
 }
 
 
-#ifdef HB_USE_ATEXIT
-static void free_static_glib_funcs (void);
+#if HB_USE_ATEXIT
+static void free_static_glib_funcs ();
 #endif
 
 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)
+  static hb_unicode_funcs_t *create ()
   {
     hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr);
 
@@ -345,7 +355,7 @@
 
     hb_unicode_funcs_make_immutable (funcs);
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
     atexit (free_static_glib_funcs);
 #endif
 
@@ -353,16 +363,16 @@
   }
 } static_glib_funcs;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
 static
-void free_static_glib_funcs (void)
+void free_static_glib_funcs ()
 {
   static_glib_funcs.free_instance ();
 }
 #endif
 
 hb_unicode_funcs_t *
-hb_glib_get_unicode_funcs (void)
+hb_glib_get_unicode_funcs ()
 {
   return static_glib_funcs.get_unconst ();
 }
diff --git a/src/hb-gobject-enums.cc.tmpl b/src/hb-gobject-enums.cc.tmpl
index e056df4..e3a9a6b 100644
--- a/src/hb-gobject-enums.cc.tmpl
+++ b/src/hb-gobject-enums.cc.tmpl
@@ -46,7 +46,7 @@
 
 /*** BEGIN value-header ***/
 GType
-@enum_name@_get_type (void)
+@enum_name@_get_type ()
 {
   static gsize type_id = 0;
 
diff --git a/src/hb-gobject-enums.h.tmpl b/src/hb-gobject-enums.h.tmpl
index 606727c..7ef9dfc 100644
--- a/src/hb-gobject-enums.h.tmpl
+++ b/src/hb-gobject-enums.h.tmpl
@@ -43,7 +43,7 @@
 
 /*** BEGIN value-header ***/
 HB_EXTERN GType
-@enum_name@_get_type (void) G_GNUC_CONST;
+@enum_name@_get_type () G_GNUC_CONST;
 #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
 
 /*** END value-header ***/
diff --git a/src/hb-gobject-structs.cc b/src/hb-gobject-structs.cc
index 1b87585..846240e 100644
--- a/src/hb-gobject-structs.cc
+++ b/src/hb-gobject-structs.cc
@@ -26,6 +26,18 @@
 
 #include "hb.hh"
 
+
+/**
+ * SECTION:hb-gobject
+ * @title: hb-gobject
+ * @short_description: GObject integration
+ * @include: hb-gobject.h
+ *
+ * Functions for using HarfBuzz with the GObject library to provide
+ * type data.
+ **/
+
+
 /* g++ didn't like older gtype.h gcc-only code path. */
 #include <glib.h>
 #if !GLIB_CHECK_VERSION(2,29,16)
@@ -39,7 +51,7 @@
 
 #define HB_DEFINE_BOXED_TYPE(name,copy_func,free_func) \
 GType \
-hb_gobject_##name##_get_type (void) \
+hb_gobject_##name##_get_type () \
 { \
    static gsize type_id = 0; \
    if (g_once_init_enter (&type_id)) { \
diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index 6b2b6f1..a1c602c 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -26,18 +26,23 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#define HB_SHAPER graphite2
 #include "hb-shaper-impl.hh"
 
 #include "hb-graphite2.h"
 
 #include <graphite2/Segment.h>
 
-#include "hb-ot-tag.h"
+#include "hb-ot-layout.h"
 
 
-HB_SHAPER_DATA_ENSURE_DEFINE(graphite2, face)
-HB_SHAPER_DATA_ENSURE_DEFINE(graphite2, font)
+/**
+ * SECTION:hb-graphite2
+ * @title: hb-graphite2
+ * @short_description: Graphite2 integration
+ * @include: hb-graphite2.h
+ *
+ * Functions for using HarfBuzz with the Graphite2 fonts.
+ **/
 
 
 /*
@@ -61,7 +66,7 @@
 static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len)
 {
   hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data;
-  hb_graphite2_tablelist_t *tlist = face_data->tlist.get ();
+  hb_graphite2_tablelist_t *tlist = face_data->tlist;
 
   hb_blob_t *blob = nullptr;
 
@@ -84,7 +89,7 @@
     p->tag = tag;
 
 retry:
-    hb_graphite2_tablelist_t *tlist = face_data->tlist.get ();
+    hb_graphite2_tablelist_t *tlist = face_data->tlist;
     p->next = tlist;
 
     if (unlikely (!face_data->tlist.cmpexch (tlist, p)))
@@ -100,7 +105,7 @@
 static void hb_graphite2_release_table(const void *data, const void *table_buffer)
 {
   hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data;
-  hb_graphite2_tablelist_t *tlist = face_data->tlist.get();
+  hb_graphite2_tablelist_t *tlist = face_data->tlist;
 
   hb_graphite2_tablelist_t *prev = nullptr;
   hb_graphite2_tablelist_t *curr = tlist;
@@ -154,7 +159,7 @@
 void
 _hb_graphite2_shaper_face_data_destroy (hb_graphite2_face_data_t *data)
 {
-  hb_graphite2_tablelist_t *tlist = data->tlist.get ();
+  hb_graphite2_tablelist_t *tlist = data->tlist;
 
   while (tlist)
   {
@@ -175,8 +180,8 @@
 gr_face *
 hb_graphite2_face_get_gr_face (hb_face_t *face)
 {
-  if (unlikely (!hb_graphite2_shaper_face_data_ensure (face))) return nullptr;
-  return HB_SHAPER_DATA_GET (face)->grface;
+  const hb_graphite2_face_data_t *data = face->data.graphite2;
+  return data ? data->grface : nullptr;
 }
 
 
@@ -197,39 +202,20 @@
 {
 }
 
-/*
+/**
+ * hb_graphite2_font_get_gr_font:
+ *
  * Since: 0.9.10
+ * Deprecated: 1.4.2
  */
 gr_font *
-hb_graphite2_font_get_gr_font (hb_font_t *font)
+hb_graphite2_font_get_gr_font (hb_font_t *font HB_UNUSED)
 {
   return nullptr;
 }
 
 
 /*
- * shaper shape_plan data
- */
-
-struct hb_graphite2_shape_plan_data_t {};
-
-hb_graphite2_shape_plan_data_t *
-_hb_graphite2_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
-					     const hb_feature_t *user_features HB_UNUSED,
-					     unsigned int        num_user_features HB_UNUSED,
-					     const int          *coords HB_UNUSED,
-					     unsigned int        num_coords HB_UNUSED)
-{
-  return (hb_graphite2_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
-}
-
-void
-_hb_graphite2_shaper_shape_plan_data_destroy (hb_graphite2_shape_plan_data_t *data HB_UNUSED)
-{
-}
-
-
-/*
  * shaper
  */
 
@@ -243,14 +229,14 @@
 };
 
 hb_bool_t
-_hb_graphite2_shape (hb_shape_plan_t    *shape_plan,
+_hb_graphite2_shape (hb_shape_plan_t    *shape_plan HB_UNUSED,
 		     hb_font_t          *font,
 		     hb_buffer_t        *buffer,
 		     const hb_feature_t *features,
 		     unsigned int        num_features)
 {
   hb_face_t *face = font->face;
-  gr_face *grface = HB_SHAPER_DATA_GET (face)->grface;
+  gr_face *grface = face->data.graphite2->grface;
 
   const char *lang = hb_language_to_string (hb_buffer_get_language (buffer));
   const char *lang_end = lang ? strchr (lang, '-') : nullptr;
diff --git a/src/hb-icu.cc b/src/hb-icu.cc
index 68220bc..c26c91d 100644
--- a/src/hb-icu.cc
+++ b/src/hb-icu.cc
@@ -40,6 +40,16 @@
 #include <unicode/uversion.h>
 
 
+/**
+ * SECTION:hb-icu
+ * @title: hb-icu
+ * @short_description: ICU integration
+ * @include: hb-icu.h
+ *
+ * Functions for using HarfBuzz with the ICU library to provide Unicode data.
+ **/
+
+
 hb_script_t
 hb_icu_script_to_script (UScriptCode script)
 {
@@ -55,8 +65,8 @@
   if (unlikely (script == HB_SCRIPT_INVALID))
     return USCRIPT_INVALID_CODE;
 
-  unsigned int maxScriptCode = u_getIntPropertyMaxValue(UCHAR_SCRIPT);
-  for (unsigned int i = 0; i <= maxScriptCode; i++)
+  unsigned int numScriptCode = 1 + u_getIntPropertyMaxValue (UCHAR_SCRIPT);
+  for (unsigned int i = 0; i < numScriptCode; i++)
     if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script))
       return (UScriptCode) i;
 
@@ -291,13 +301,13 @@
 }
 
 
-#ifdef HB_USE_ATEXIT
-static void free_static_icu_funcs (void);
+#if HB_USE_ATEXIT
+static void free_static_icu_funcs ();
 #endif
 
 static struct hb_icu_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_icu_unicode_funcs_lazy_loader_t>
 {
-  static inline hb_unicode_funcs_t *create (void)
+  static hb_unicode_funcs_t *create ()
   {
     void *user_data = nullptr;
 #if U_ICU_VERSION_MAJOR_NUM >= 49
@@ -317,7 +327,7 @@
 
     hb_unicode_funcs_make_immutable (funcs);
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
     atexit (free_static_icu_funcs);
 #endif
 
@@ -325,16 +335,16 @@
   }
 } static_icu_funcs;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
 static
-void free_static_icu_funcs (void)
+void free_static_icu_funcs ()
 {
   static_icu_funcs.free_instance ();
 }
 #endif
 
 hb_unicode_funcs_t *
-hb_icu_get_unicode_funcs (void)
+hb_icu_get_unicode_funcs ()
 {
   return static_icu_funcs.get_unconst ();
 }
diff --git a/src/hb-iter.hh b/src/hb-iter.hh
deleted file mode 100644
index 6fd6aed..0000000
--- a/src/hb-iter.hh
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * 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_ITER_HH
-#define HB_ITER_HH
-
-#include "hb.hh"
-
-
-/* Unified iterator object.
- *
- * The goal of this template is to make the same iterator interface
- * available to all types, and make it very easy and compact to use.
- * Iterator objects are small, light-weight, objects that can be
- * copied by value.  If the collection / object being iterated on
- * is writable, then the iterator points to lvalues, otherwise it
- * returns rvalues.
- *
- * The way to declare, initialize, and use iterators, eg.:
- *
- *   Iter<const int *> s (src);
- *   Iter<int *> t (dst);
- *   for (; s && t; s++, t++)
- *     *s = *t;
- */
-
-template <typename T>
-struct Iter;
-
-#if 0
-template <typename T>
-struct Iter
-{
-  explicit inline Iter (const T &c);
-};
-#endif
-
-template <typename T>
-struct Iter<T *>
-{
-  /* Type of items. */
-  typedef T Value;
-
-  /* Constructors. */
-  inline Iter (T *array_, int length_) :
-    array (array_), length (MAX (length_, 0)) {}
-  template <unsigned int length_>
-  explicit inline Iter (T (&array_)[length_]) :
-    array (array_), length (length_) {}
-
-  /* Emptiness. */
-  explicit_operator inline operator bool (void) const { return bool (length); }
-
-  /* Current item. */
-  inline T &operator * (void)
-  {
-    if (unlikely (!length)) return CrapOrNull(T);
-    return *array;
-  }
-  inline T &operator -> (void)
-  {
-    return (operator *);
-  }
-
-  /* Next. */
-  inline Iter<T *> & operator ++ (void)
-  {
-    if (unlikely (!length)) return *this;
-    array++;
-    length--;
-    return *this;
-  }
-  /* Might return void, or a copy of pre-increment iterator. */
-  inline void operator ++ (int)
-  {
-    if (unlikely (!length)) return;
-    array++;
-    length--;
-  }
-
-  /* Some iterators might implement len(). */
-  inline unsigned int len (void) const { return length; }
-
-  /* Some iterators might implement fast-forward.
-   * Only implement it if it's constant-time. */
-  inline void operator += (unsigned int n)
-  {
-    n = MIN (n, length);
-    array += n;
-    length -= n;
-  }
-
-  /* Some iterators might implement random-access.
-   * Only implement it if it's constant-time. */
-  inline Iter<T *> & operator [] (unsigned int i)
-  {
-    if (unlikely (i >= length)) return CrapOrNull(T);
-    return array[i];
-  }
-
-  private:
-  T *array;
-  unsigned int length;
-};
-
-/* XXX Remove
- * Just to test these compile. */
-static inline void
-m (void)
-{
-  const int src[10] = {};
-  int dst[20];
-
-  Iter<const int *> s (src);
-  Iter<const int *> s2 (src, 5);
-  Iter<int *> t (dst);
-
-  s2 = s;
-
-  for (; s && t; ++s, ++t)
-   {
-    *t = *s;
-   }
-}
-
-#endif /* HB_ITER_HH */
diff --git a/src/hb-kern.hh b/src/hb-kern.hh
new file mode 100644
index 0000000..fd5bb9e
--- /dev/null
+++ b/src/hb-kern.hh
@@ -0,0 +1,139 @@
+/*
+ * 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_KERN_HH
+#define HB_KERN_HH
+
+#include "hb-open-type.hh"
+#include "hb-aat-layout-common.hh"
+#include "hb-ot-layout-gpos-table.hh"
+
+
+namespace OT {
+
+
+template <typename Driver>
+struct hb_kern_machine_t
+{
+  hb_kern_machine_t (const Driver &driver_,
+		     bool crossStream_ = false) :
+		       driver (driver_),
+		       crossStream (crossStream_) {}
+
+  HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW
+  void kern (hb_font_t   *font,
+	     hb_buffer_t *buffer,
+	     hb_mask_t    kern_mask,
+	     bool         scale = true) const
+  {
+    OT::hb_ot_apply_context_t c (1, font, buffer);
+    c.set_lookup_mask (kern_mask);
+    c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
+    OT::hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c.iter_input;
+    skippy_iter.init (&c);
+
+    bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction);
+    unsigned int count = buffer->len;
+    hb_glyph_info_t *info = buffer->info;
+    hb_glyph_position_t *pos = buffer->pos;
+    for (unsigned int idx = 0; idx < count;)
+    {
+      if (!(info[idx].mask & kern_mask))
+      {
+	idx++;
+	continue;
+      }
+
+      skippy_iter.reset (idx, 1);
+      if (!skippy_iter.next ())
+      {
+	idx++;
+	continue;
+      }
+
+      unsigned int i = idx;
+      unsigned int j = skippy_iter.idx;
+
+      hb_position_t kern = driver.get_kerning (info[i].codepoint,
+					       info[j].codepoint);
+
+
+      if (likely (!kern))
+        goto skip;
+
+      if (horizontal)
+      {
+        if (scale)
+	  kern = font->em_scale_x (kern);
+	if (crossStream)
+	{
+	  pos[j].y_offset = kern;
+	  buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+	}
+	else
+	{
+	  hb_position_t kern1 = kern >> 1;
+	  hb_position_t kern2 = kern - kern1;
+	  pos[i].x_advance += kern1;
+	  pos[j].x_advance += kern2;
+	  pos[j].x_offset += kern2;
+	}
+      }
+      else
+      {
+        if (scale)
+	  kern = font->em_scale_y (kern);
+	if (crossStream)
+	{
+	  pos[j].x_offset = kern;
+	  buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+	}
+	else
+	{
+	  hb_position_t kern1 = kern >> 1;
+	  hb_position_t kern2 = kern - kern1;
+	  pos[i].y_advance += kern1;
+	  pos[j].y_advance += kern2;
+	  pos[j].y_offset += kern2;
+	}
+      }
+
+      buffer->unsafe_to_break (i, j + 1);
+
+    skip:
+      idx = skippy_iter.idx;
+    }
+  }
+
+  const Driver &driver;
+  bool crossStream;
+};
+
+
+} /* namespace OT */
+
+
+#endif /* HB_KERN_HH */
diff --git a/src/hb-machinery.hh b/src/hb-machinery.hh
index ae34c92..9d2ae95 100644
--- a/src/hb-machinery.hh
+++ b/src/hb-machinery.hh
@@ -32,7 +32,7 @@
 #include "hb.hh"
 #include "hb-blob.hh"
 
-#include "hb-iter.hh"
+#include "hb-array.hh"
 #include "hb-vector.hh"
 
 
@@ -81,49 +81,50 @@
 
 /* Check _assertion in a method environment */
 #define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \
-  inline void _instance_assertion_on_line_##_line (void) const \
-  { \
-    static_assert ((_assertion), ""); \
-    ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \
-  }
+  void _instance_assertion_on_line_##_line () const \
+  { static_assert ((_assertion), ""); }
 # define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion)
 # define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion)
 
 /* Check that _code compiles in a method environment */
 #define _DEFINE_COMPILES_ASSERTION1(_line, _code) \
-  inline void _compiles_assertion_on_line_##_line (void) const \
+  void _compiles_assertion_on_line_##_line () const \
   { _code; }
 # define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code)
 # define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code)
 
 
 #define DEFINE_SIZE_STATIC(size) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \
-  enum { static_size = (size) }; \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)) \
+  unsigned int get_size () const { return (size); } \
+  enum { null_size = (size) }; \
   enum { min_size = (size) }; \
-  inline unsigned int get_size (void) const { return (size); }
+  enum { static_size = (size) }
 
 #define DEFINE_SIZE_UNION(size, _member) \
-  DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \
-  static const unsigned int min_size = (size)
+  DEFINE_COMPILES_ASSERTION ((void) this->u._member.static_size) \
+  DEFINE_INSTANCE_ASSERTION (sizeof(this->u._member) == (size)) \
+  enum { null_size = (size) }; \
+  enum { min_size = (size) }
 
 #define DEFINE_SIZE_MIN(size) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \
-  static const unsigned int min_size = (size)
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \
+  enum { null_size = (size) }; \
+  enum { min_size = (size) }
+
+#define DEFINE_SIZE_UNBOUNDED(size) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \
+  enum { min_size = (size) }
 
 #define DEFINE_SIZE_ARRAY(size, array) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + VAR * sizeof (array[0])); \
-  DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \
-  enum { min_size = (size) }; \
+  DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + VAR * sizeof ((array)[0])) \
+  enum { null_size = (size) }; \
+  enum { min_size = (size) }
 
 #define DEFINE_SIZE_ARRAY_SIZED(size, array) \
-	DEFINE_SIZE_ARRAY(size, array); \
-	inline unsigned int get_size (void) const { return (size - array[0].min_size + array.get_size ()); }
-
-#define DEFINE_SIZE_ARRAY2(size, array1, array2) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \
-  DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \
-  static const unsigned int min_size = (size)
+  unsigned int get_size () const { return (size - (array).min_size + (array).get_size ()); } \
+  DEFINE_SIZE_ARRAY(size, array)
 
 
 /*
@@ -136,8 +137,9 @@
   enum { max_debug_depth = MaxDebugDepth };
   typedef Return return_t;
   template <typename T, typename F>
-  inline bool may_dispatch (const T *obj, const F *format) { return true; }
-  static return_t no_dispatch_return_value (void) { return Context::default_return_value (); }
+  bool may_dispatch (const T *obj HB_UNUSED, const F *format HB_UNUSED) { return true; }
+  static return_t no_dispatch_return_value () { return Context::default_return_value (); }
+  static bool stop_sublookup_iteration (const return_t r HB_UNUSED) { return false; }
 };
 
 
@@ -224,7 +226,7 @@
 struct hb_sanitize_context_t :
        hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
 {
-  inline hb_sanitize_context_t (void) :
+  hb_sanitize_context_t () :
 	debug_depth (0),
 	start (nullptr), end (nullptr),
 	max_ops (0),
@@ -233,51 +235,61 @@
 	num_glyphs (65536),
 	num_glyphs_set (false) {}
 
-  inline const char *get_name (void) { return "SANITIZE"; }
+  const char *get_name () { return "SANITIZE"; }
   template <typename T, typename F>
-  inline bool may_dispatch (const T *obj, const F *format)
+  bool may_dispatch (const T *obj HB_UNUSED, const F *format)
   { return format->sanitize (this); }
   template <typename T>
-  inline return_t dispatch (const T &obj) { return obj.sanitize (this); }
-  static return_t default_return_value (void) { return true; }
-  static return_t no_dispatch_return_value (void) { return false; }
+  return_t dispatch (const T &obj) { return obj.sanitize (this); }
+  static return_t default_return_value () { return true; }
+  static return_t no_dispatch_return_value () { return false; }
   bool stop_sublookup_iteration (const return_t r) const { return !r; }
 
-  inline void init (hb_blob_t *b)
+  void init (hb_blob_t *b)
   {
     this->blob = hb_blob_reference (b);
     this->writable = false;
   }
 
-  inline void set_num_glyphs (unsigned int num_glyphs_)
+  void set_num_glyphs (unsigned int num_glyphs_)
   {
     num_glyphs = num_glyphs_;
     num_glyphs_set = true;
   }
-  inline unsigned int get_num_glyphs (void) { return num_glyphs; }
+  unsigned int get_num_glyphs () { return num_glyphs; }
 
-  inline void set_max_ops (int max_ops_) { max_ops = max_ops_; }
+  void set_max_ops (int max_ops_) { max_ops = max_ops_; }
 
-  /* TODO
-   * This set_object() thing is to use sanitize at runtime lookup
-   * application time.  This is very distinct from the regular
-   * sanitizer operation, so, eventually, separate into another
-   * type and make hb_aat_apply_context_t use that one instead
-   * of abusing this one.
-   */
   template <typename T>
-  inline void set_object (const T& obj)
+  void set_object (const T *obj)
   {
-    this->start = (const char *) &obj;
-    this->end = (const char *) &obj + obj.get_size ();
-    assert (this->start <= this->end); /* Must not overflow. */
+    reset_object ();
+
+    if (!obj) return;
+
+    const char *obj_start = (const char *) obj;
+    const char *obj_end = (const char *) obj + obj->get_size ();
+    assert (obj_start <= obj_end); /* Must not overflow. */
+
+    if (unlikely (obj_end < this->start || this->end < obj_start))
+      this->start = this->end = nullptr;
+    else
+    {
+      this->start = MAX (this->start, obj_start);
+      this->end   = MIN (this->end  , obj_end  );
+    }
   }
 
-  inline void start_processing (void)
+  void reset_object ()
   {
     this->start = this->blob->data;
     this->end = this->start + this->blob->length;
     assert (this->start <= this->end); /* Must not overflow. */
+  }
+
+  void start_processing ()
+  {
+    reset_object ();
     this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR,
 			 (unsigned) HB_SANITIZE_MAX_OPS_MIN);
     this->edit_count = 0;
@@ -289,7 +301,7 @@
 		     (unsigned long) (this->end - this->start));
   }
 
-  inline void end_processing (void)
+  void end_processing ()
   {
     DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1,
 		     "end [%p..%p] %u edit requests",
@@ -300,7 +312,8 @@
     this->start = this->end = nullptr;
   }
 
-  inline bool check_range (const void *base, unsigned int len) const
+  bool check_range (const void *base,
+			   unsigned int len) const
   {
     const char *p = (const char *) base;
     bool ok = this->start <= p &&
@@ -318,29 +331,43 @@
   }
 
   template <typename T>
-  inline bool check_array (const T *base, unsigned int len, unsigned int record_size = T::static_size) const
+  bool check_range (const T *base,
+			   unsigned int a,
+			   unsigned int b) const
   {
-    const char *p = (const char *) base;
-    bool overflows = hb_unsigned_mul_overflows (len, record_size);
-    unsigned int array_size = record_size * len;
-    bool ok = !overflows && this->check_range (base, array_size);
+    return !hb_unsigned_mul_overflows (a, b) &&
+	   this->check_range (base, a * b);
+  }
 
-    DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
-       "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s",
-       p, p + (record_size * len), record_size, len, (unsigned int) array_size,
-       this->start, this->end,
-       overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE");
+  template <typename T>
+  bool check_range (const T *base,
+			   unsigned int a,
+			   unsigned int b,
+			   unsigned int c) const
+  {
+    return !hb_unsigned_mul_overflows (a, b) &&
+	   this->check_range (base, a * b, c);
+  }
 
-    return likely (ok);
+  template <typename T>
+  bool check_array (const T *base, unsigned int len) const
+  {
+    return this->check_range (base, len, hb_static_size (T));
+  }
+
+  template <typename T>
+  bool check_array (const T *base,
+		    unsigned int a,
+		    unsigned int b) const
+  {
+    return this->check_range (base, a, b, hb_static_size (T));
   }
 
   template <typename Type>
-  inline bool check_struct (const Type *obj) const
-  {
-    return likely (this->check_range (obj, obj->min_size));
-  }
+  bool check_struct (const Type *obj) const
+  { return likely (this->check_range (obj, obj->min_size)); }
 
-  inline bool may_edit (const void *base, unsigned int len)
+  bool may_edit (const void *base, unsigned int len)
   {
     if (this->edit_count >= HB_SANITIZE_MAX_EDITS)
       return false;
@@ -359,16 +386,18 @@
   }
 
   template <typename Type, typename ValueType>
-  inline bool try_set (const Type *obj, const ValueType &v) {
-    if (this->may_edit (obj, obj->static_size)) {
-      const_cast<Type *> (obj)->set (v);
+  bool try_set (const Type *obj, const ValueType &v)
+  {
+    if (this->may_edit (obj, hb_static_size (Type)))
+    {
+      hb_assign (* const_cast<Type *> (obj), v);
       return true;
     }
     return false;
   }
 
   template <typename Type>
-  inline hb_blob_t *sanitize_blob (hb_blob_t *blob)
+  hb_blob_t *sanitize_blob (hb_blob_t *blob)
   {
     bool sane;
 
@@ -435,7 +464,7 @@
   }
 
   template <typename Type>
-  inline hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag)
+  hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag)
   {
     if (!num_glyphs_set)
       set_num_glyphs (hb_face_get_glyph_count (face));
@@ -453,6 +482,19 @@
   bool  num_glyphs_set;
 };
 
+struct hb_sanitize_with_object_t
+{
+  template <typename T>
+  hb_sanitize_with_object_t (hb_sanitize_context_t *c,
+				    const T& obj) : c (c)
+  { c->set_object (obj); }
+  ~hb_sanitize_with_object_t ()
+  { c->reset_object (); }
+
+  private:
+  hb_sanitize_context_t *c;
+};
+
 
 /*
  * Serialize
@@ -460,25 +502,42 @@
 
 struct hb_serialize_context_t
 {
-  inline hb_serialize_context_t (void *start_, unsigned int size)
+  hb_serialize_context_t (void *start_, unsigned int size)
   {
     this->start = (char *) start_;
     this->end = this->start + size;
     reset ();
   }
 
-  inline void reset (void)
+  bool in_error () const { return !this->successful; }
+
+  void reset ()
   {
-    this->ran_out_of_room = false;
+    this->successful = true;
     this->head = this->start;
     this->debug_depth = 0;
   }
 
-  inline bool err (bool e) { return this->ran_out_of_room = this->ran_out_of_room || e; }
+  bool propagate_error (bool e)
+  { return this->successful = this->successful && e; }
+  template <typename T> bool propagate_error (const T &obj)
+  { return this->successful = this->successful && !obj.in_error (); }
+  template <typename T> bool propagate_error (const T *obj)
+  { return this->successful = this->successful && !obj->in_error (); }
+  template <typename T1, typename T2> bool propagate_error (T1 &o1, T2 &o2)
+  { return propagate_error (o1) && propagate_error (o2); }
+  template <typename T1, typename T2> bool propagate_error (T1 *o1, T2 *o2)
+  { return propagate_error (o1) && propagate_error (o2); }
+  template <typename T1, typename T2, typename T3>
+  bool propagate_error (T1 &o1, T2 &o2, T3 &o3)
+  { return propagate_error (o1) && propagate_error (o2, o3); }
+  template <typename T1, typename T2, typename T3>
+  bool propagate_error (T1 *o1, T2 *o2, T3 *o3)
+  { return propagate_error (o1) && propagate_error (o2, o3); }
 
   /* To be called around main operation. */
   template <typename Type>
-  inline Type *start_serialize (void)
+  Type *start_serialize ()
   {
     DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
 		     "start [%p..%p] (%lu bytes)",
@@ -487,18 +546,18 @@
 
     return start_embed<Type> ();
   }
-  inline void end_serialize (void)
+  void end_serialize ()
   {
     DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1,
 		     "end [%p..%p] serialized %d bytes; %s",
 		     this->start, this->end,
 		     (int) (this->head - this->start),
-		     this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room");
+		     this->successful ? "successful" : "UNSUCCESSFUL");
   }
 
-  inline unsigned int length (void) const { return this->head - this->start; }
+  unsigned int length () const { return this->head - this->start; }
 
-  inline void align (unsigned int alignment)
+  void align (unsigned int alignment)
   {
     unsigned int l = length () % alignment;
     if (l)
@@ -506,17 +565,17 @@
   }
 
   template <typename Type>
-  inline Type *start_embed (void) const
+  Type *start_embed (const Type *_ HB_UNUSED = nullptr) const
   {
     Type *ret = reinterpret_cast<Type *> (this->head);
     return ret;
   }
 
   template <typename Type>
-  inline Type *allocate_size (unsigned int size)
+  Type *allocate_size (unsigned int size)
   {
-    if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) {
-      this->ran_out_of_room = true;
+    if (unlikely (!this->successful || this->end - this->head < ptrdiff_t (size))) {
+      this->successful = false;
       return nullptr;
     }
     memset (this->head, 0, size);
@@ -526,13 +585,13 @@
   }
 
   template <typename Type>
-  inline Type *allocate_min (void)
+  Type *allocate_min ()
   {
     return this->allocate_size<Type> (Type::min_size);
   }
 
   template <typename Type>
-  inline Type *embed (const Type &obj)
+  Type *embed (const Type &obj)
   {
     unsigned int size = obj.get_size ();
     Type *ret = this->allocate_size<Type> (size);
@@ -540,50 +599,50 @@
     memcpy (ret, &obj, size);
     return ret;
   }
+  template <typename Type>
+  hb_serialize_context_t &operator << (const Type &obj) { embed (obj); return *this; }
 
   template <typename Type>
-  inline Type *extend_min (Type &obj)
+  Type *extend_size (Type &obj, unsigned int size)
   {
-    unsigned int size = obj.min_size;
-    assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
+    assert (this->start <= (char *) &obj);
+    assert ((char *) &obj <= this->head);
+    assert ((char *) &obj + size >= this->head);
     if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
     return reinterpret_cast<Type *> (&obj);
   }
 
   template <typename Type>
-  inline Type *extend (Type &obj)
-  {
-    unsigned int size = obj.get_size ();
-    assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
-    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
-    return reinterpret_cast<Type *> (&obj);
-  }
+  Type *extend_min (Type &obj) { return extend_size (obj, obj.min_size); }
+
+  template <typename Type>
+  Type *extend (Type &obj) { return extend_size (obj, obj.get_size ()); }
 
   /* Output routines. */
   template <typename Type>
-  inline Type *copy (void) const
+  Type *copy () const
   {
-    assert (!this->ran_out_of_room);
+    assert (this->successful);
     unsigned int len = this->head - this->start;
     void *p = malloc (len);
     if (p)
       memcpy (p, this->start, len);
     return reinterpret_cast<Type *> (p);
   }
-  inline hb_bytes_t copy_bytes (void) const
+  hb_bytes_t copy_bytes () const
   {
-    assert (!this->ran_out_of_room);
+    assert (this->successful);
     unsigned int len = this->head - this->start;
     void *p = malloc (len);
     if (p)
       memcpy (p, this->start, len);
     else
       return hb_bytes_t ();
-    return hb_bytes_t (p, len);
+    return hb_bytes_t ((char *) p, len);
   }
-  inline hb_blob_t *copy_blob (void) const
+  hb_blob_t *copy_blob () const
   {
-    assert (!this->ran_out_of_room);
+    assert (this->successful);
     return hb_blob_create (this->start,
 			   this->head - this->start,
 			   HB_MEMORY_MODE_DUPLICATE,
@@ -593,54 +652,10 @@
   public:
   unsigned int debug_depth;
   char *start, *end, *head;
-  bool ran_out_of_room;
+  bool successful;
 };
 
 
-/*
- * Supplier
- */
-
-template <typename Type>
-struct Supplier
-{
-  inline Supplier (const Type *array, unsigned int len_, unsigned int stride_=sizeof (Type))
-  {
-    head = array;
-    len = len_;
-    stride = stride_;
-  }
-  inline Supplier (const hb_vector_t<Type> *v)
-  {
-    head = v->arrayZ();
-    len = v->len;
-    stride = sizeof (Type);
-  }
-
-  inline const Type operator [] (unsigned int i) const
-  {
-    if (unlikely (i >= len)) return Type ();
-    return * (const Type *) (const void *) ((const char *) head + stride * i);
-  }
-
-  inline Supplier<Type> & operator += (unsigned int count)
-  {
-    if (unlikely (count > len))
-      count = len;
-    len -= count;
-    head = (const Type *) (const void *) ((const char *) head + stride * count);
-    return *this;
-  }
-
-  private:
-  inline Supplier (const Supplier<Type> &); /* Disallow copy */
-  inline Supplier<Type>& operator= (const Supplier<Type> &); /* Disallow copy */
-
-  unsigned int len;
-  unsigned int stride;
-  const Type *head;
-};
-
 
 /*
  * Big-endian integers.
@@ -653,14 +668,8 @@
 {
   public:
   typedef Type type;
-  inline void set (Type V)
-  {
-    v = V;
-  }
-  inline operator Type (void) const
-  {
-    return v;
-  }
+  void set (Type V)      { v = V; }
+  operator Type () const { return v; }
   private: uint8_t v;
 };
 template <typename Type>
@@ -668,13 +677,25 @@
 {
   public:
   typedef Type type;
-  inline void set (Type V)
+  void set (Type V)
   {
     v[0] = (V >>  8) & 0xFF;
     v[1] = (V      ) & 0xFF;
   }
-  inline operator Type (void) const
+  operator Type () const
   {
+#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \
+    defined(__BYTE_ORDER) && \
+    (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN)
+    /* Spoon-feed the compiler a big-endian integer with alignment 1.
+     * https://github.com/harfbuzz/harfbuzz/pull/1398 */
+    struct __attribute__((packed)) packed_uint16_t { uint16_t v; };
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+    return __builtin_bswap16 (((packed_uint16_t *) this)->v);
+#else /* __BYTE_ORDER == __BIG_ENDIAN */
+    return ((packed_uint16_t *) this)->v;
+#endif
+#endif
     return (v[0] <<  8)
          + (v[1]      );
   }
@@ -685,13 +706,13 @@
 {
   public:
   typedef Type type;
-  inline void set (Type V)
+  void set (Type V)
   {
     v[0] = (V >> 16) & 0xFF;
     v[1] = (V >>  8) & 0xFF;
     v[2] = (V      ) & 0xFF;
   }
-  inline operator Type (void) const
+  operator Type () const
   {
     return (v[0] << 16)
          + (v[1] <<  8)
@@ -704,14 +725,14 @@
 {
   public:
   typedef Type type;
-  inline void set (Type V)
+  void set (Type V)
   {
     v[0] = (V >> 24) & 0xFF;
     v[1] = (V >> 16) & 0xFF;
     v[2] = (V >>  8) & 0xFF;
     v[3] = (V      ) & 0xFF;
   }
-  inline operator Type (void) const
+  operator Type () const
   {
     return (v[0] << 24)
          + (v[1] << 16)
@@ -731,26 +752,21 @@
 {
   static_assert (WheresData > 0, "");
 
-  inline Data * get_data (void) const
-  {
-    return *(((Data **) (void *) this) - WheresData);
-  }
+  Data * get_data () const
+  { return *(((Data **) (void *) this) - WheresData); }
+
+  bool is_inert () const { return !get_data (); }
 
   template <typename Stored, typename Subclass>
-  inline Stored * call_create (void) const
-  {
-    Data *data = this->get_data ();
-    return likely (data) ? Subclass::create (data) : nullptr;
-  }
+  Stored * call_create () const { return Subclass::create (get_data ()); }
 };
 template <>
 struct hb_data_wrapper_t<void, 0>
 {
+  bool is_inert () const { return false; }
+
   template <typename Stored, typename Funcs>
-  inline Stored * call_create (void) const
-  {
-    return Funcs::create ();
-  }
+  Stored * call_create () const { return Funcs::create (); }
 };
 
 template <typename T1, typename T2> struct hb_non_void_t { typedef T1 value; };
@@ -767,50 +783,45 @@
 				 hb_lazy_loader_t<Returned,Subclass,Data,WheresData,Stored>
 				>::value Funcs;
 
-  inline void init0 (void) {} /* Init, when memory is already set to 0. No-op for us. */
-  inline void init (void) { instance.set_relaxed (nullptr); }
-  inline void fini (void)
-  {
-    do_destroy (instance.get ());
-  }
-  inline void free_instance (void)
+  void init0 () {} /* Init, when memory is already set to 0. No-op for us. */
+  void init ()  { instance.set_relaxed (nullptr); }
+  void fini ()  { do_destroy (instance.get ()); }
+
+  void free_instance ()
   {
   retry:
     Stored *p = instance.get ();
-    if (unlikely (p && !this->instance.cmpexch (p, nullptr)))
+    if (unlikely (p && !cmpexch (p, nullptr)))
       goto retry;
     do_destroy (p);
   }
 
-  inline Stored * do_create (void) const
+  static void do_destroy (Stored *p)
   {
-    Stored *p = this->template call_create<Stored, Funcs> ();
-    if (unlikely (!p))
-      p = const_cast<Stored *> (Funcs::get_null ());
-    return p;
-  }
-  static inline void do_destroy (Stored *p)
-  {
-    if (p && p != Funcs::get_null ())
+    if (p && p != const_cast<Stored *> (Funcs::get_null ()))
       Funcs::destroy (p);
   }
 
-  inline const Returned * operator -> (void) const { return get (); }
-  inline const Returned & operator * (void) const { return *get (); }
+  const Returned * operator -> () const { return get (); }
+  const Returned & operator * () const  { return *get (); }
+  explicit_operator bool () const
+  { return get_stored () != Funcs::get_null (); }
+  template <typename C> operator const C * () const { return get (); }
 
-  inline Data * get_data (void) const
-  {
-    return *(((Data **) this) - WheresData);
-  }
-
-  inline Stored * get_stored (void) const
+  Stored * get_stored () const
   {
   retry:
     Stored *p = this->instance.get ();
     if (unlikely (!p))
     {
-      p = do_create ();
-      if (unlikely (!this->instance.cmpexch (nullptr, p)))
+      if (unlikely (this->is_inert ()))
+	return const_cast<Stored *> (Funcs::get_null ());
+
+      p = this->template call_create<Stored, Funcs> ();
+      if (unlikely (!p))
+	p = const_cast<Stored *> (Funcs::get_null ());
+
+      if (unlikely (!cmpexch (nullptr, p)))
       {
         do_destroy (p);
 	goto retry;
@@ -818,52 +829,47 @@
     }
     return p;
   }
-  inline Stored * get_stored_relaxed (void) const
+  Stored * get_stored_relaxed () const
   {
     return this->instance.get_relaxed ();
   }
 
-  inline void set_stored (Stored *instance_)
+  bool cmpexch (Stored *current, Stored *value) const
   {
-    /* This *must* be called when there are no other threads accessing.
-     * However, to make TSan, etc, happy, we using cmpexch. */
-  retry:
-    Stored *p = this->instance.get ();
-    if (unlikely (!this->instance.cmpexch (p, instance_)))
-      goto retry;
-    do_destroy (p);
+    /* This *must* be called when there are no other threads accessing. */
+    return this->instance.cmpexch (current, value);
   }
 
-  inline const Returned * get (void) const { return Funcs::convert (get_stored ()); }
-  inline const Returned * get_relaxed (void) const { return Funcs::convert (get_stored_relaxed ()); }
-  inline Returned * get_unconst (void) const { return const_cast<Returned *> (Funcs::convert (get_stored ())); }
+  const Returned * get () const { return Funcs::convert (get_stored ()); }
+  const Returned * get_relaxed () const { return Funcs::convert (get_stored_relaxed ()); }
+  Returned * get_unconst () const { return const_cast<Returned *> (Funcs::convert (get_stored ())); }
 
   /* To be possibly overloaded by subclasses. */
-  static inline Returned* convert (Stored *p) { return p; }
+  static Returned* convert (Stored *p) { return p; }
 
   /* By default null/init/fini the object. */
-  static inline const Stored* get_null (void) { return &Null(Stored); }
-  static inline Stored *create (Data *data)
+  static const Stored* get_null () { return &Null(Stored); }
+  static Stored *create (Data *data)
   {
     Stored *p = (Stored *) calloc (1, sizeof (Stored));
     if (likely (p))
       p->init (data);
     return p;
   }
-  static inline Stored *create (void)
+  static Stored *create ()
   {
     Stored *p = (Stored *) calloc (1, sizeof (Stored));
     if (likely (p))
       p->init ();
     return p;
   }
-  static inline void destroy (Stored *p)
+  static void destroy (Stored *p)
   {
     p->fini ();
     free (p);
   }
 
-  private:
+//  private:
   /* Must only have one pointer. */
   hb_atomic_ptr_t<Stored *> instance;
 };
@@ -881,52 +887,34 @@
 						 hb_face_t, WheresFace,
 						 hb_blob_t>
 {
-  static inline hb_blob_t *create (hb_face_t *face)
-  {
-    return hb_sanitize_context_t ().reference_table<T> (face);
-  }
-  static inline void destroy (hb_blob_t *p)
-  {
-    hb_blob_destroy (p);
-  }
-  static inline const hb_blob_t *get_null (void)
-  {
-      return hb_blob_get_empty ();
-  }
-  static inline const T* convert (const hb_blob_t *blob)
-  {
-    return blob->as<T> ();
-  }
+  static hb_blob_t *create (hb_face_t *face)
+  { return hb_sanitize_context_t ().reference_table<T> (face); }
+  static void destroy (hb_blob_t *p) { hb_blob_destroy (p); }
 
-  inline hb_blob_t* get_blob (void) const
-  {
-    return this->get_stored ();
-  }
+  static const hb_blob_t *get_null ()
+  { return hb_blob_get_empty (); }
+
+  static const T* convert (const hb_blob_t *blob)
+  { return blob->as<T> (); }
+
+  hb_blob_t* get_blob () const { return this->get_stored (); }
 };
 
 template <typename Subclass>
 struct hb_font_funcs_lazy_loader_t : hb_lazy_loader_t<hb_font_funcs_t, Subclass>
 {
-  static inline void destroy (hb_font_funcs_t *p)
-  {
-    hb_font_funcs_destroy (p);
-  }
-  static inline const hb_font_funcs_t *get_null (void)
-  {
-      return hb_font_funcs_get_empty ();
-  }
+  static void destroy (hb_font_funcs_t *p)
+  { hb_font_funcs_destroy (p); }
+  static const hb_font_funcs_t *get_null ()
+  { return hb_font_funcs_get_empty (); }
 };
 template <typename Subclass>
 struct hb_unicode_funcs_lazy_loader_t : hb_lazy_loader_t<hb_unicode_funcs_t, Subclass>
 {
-  static inline void destroy (hb_unicode_funcs_t *p)
-  {
-    hb_unicode_funcs_destroy (p);
-  }
-  static inline const hb_unicode_funcs_t *get_null (void)
-  {
-      return hb_unicode_funcs_get_empty ();
-  }
+  static void destroy (hb_unicode_funcs_t *p)
+  { hb_unicode_funcs_destroy (p); }
+  static const hb_unicode_funcs_t *get_null ()
+  { return hb_unicode_funcs_get_empty (); }
 };
 
 
diff --git a/src/hb-map.cc b/src/hb-map.cc
index 225f37b..a2c770c 100644
--- a/src/hb-map.cc
+++ b/src/hb-map.cc
@@ -27,7 +27,16 @@
 #include "hb-map.hh"
 
 
-/* Public API */
+/**
+ * SECTION:hb-map
+ * @title: hb-map
+ * @short_description: Object representing integer to integer mapping
+ * @include: hb.h
+ *
+ * Map objects are integer-to-integer hash-maps.  Currently they are
+ * not used in the HarfBuzz public API, but are provided for client's
+ * use if desired.
+ **/
 
 
 /**
@@ -38,7 +47,7 @@
  * Since: 1.7.7
  **/
 hb_map_t *
-hb_map_create (void)
+hb_map_create ()
 {
   hb_map_t *map;
 
@@ -58,7 +67,7 @@
  * Since: 1.7.7
  **/
 hb_map_t *
-hb_map_get_empty (void)
+hb_map_get_empty ()
 {
   return const_cast<hb_map_t *> (&Null(hb_map_t));
 }
diff --git a/src/hb-map.hh b/src/hb-map.hh
index b55e3a9..3b119da 100644
--- a/src/hb-map.hh
+++ b/src/hb-map.hh
@@ -44,13 +44,17 @@
 
 struct hb_map_t
 {
+  HB_NO_COPY_ASSIGN (hb_map_t);
+  hb_map_t ()  { init (); }
+  ~hb_map_t () { fini (); }
+
   struct item_t
   {
     hb_codepoint_t key;
     hb_codepoint_t value;
 
-    inline bool is_unused (void) const { return key == INVALID; }
-    inline bool is_tombstone (void) const { return key != INVALID && value == INVALID; }
+    bool is_unused () const    { return key == INVALID; }
+    bool is_tombstone () const { return key != INVALID && value == INVALID; }
   };
 
   hb_object_header_t header;
@@ -61,7 +65,7 @@
   unsigned int prime;
   item_t *items;
 
-  inline void init_shallow (void)
+  void init_shallow ()
   {
     successful = true;
     population = occupancy = 0;
@@ -69,22 +73,26 @@
     prime = 0;
     items = nullptr;
   }
-  inline void init (void)
+  void init ()
   {
     hb_object_init (this);
     init_shallow ();
   }
-  inline void fini_shallow (void)
+  void fini_shallow ()
   {
     free (items);
+    items = nullptr;
   }
-  inline void fini (void)
+  void fini ()
   {
+    population = occupancy = 0;
     hb_object_fini (this);
     fini_shallow ();
   }
 
-  inline bool resize (void)
+  bool in_error () const { return !successful; }
+
+  bool resize ()
   {
     if (unlikely (!successful)) return false;
 
@@ -118,7 +126,7 @@
     return true;
   }
 
-  inline void set (hb_codepoint_t key, hb_codepoint_t value)
+  void set (hb_codepoint_t key, hb_codepoint_t value)
   {
     if (unlikely (!successful)) return;
     if (unlikely (key == INVALID)) return;
@@ -143,46 +151,36 @@
       population++;
 
   }
-  inline hb_codepoint_t get (hb_codepoint_t key) const
+  hb_codepoint_t get (hb_codepoint_t key) const
   {
     if (unlikely (!items)) return INVALID;
     unsigned int i = bucket_for (key);
     return items[i].key == key ? items[i].value : INVALID;
   }
 
-  inline void del (hb_codepoint_t key)
-  {
-    set (key, INVALID);
-  }
-  inline bool has (hb_codepoint_t key) const
-  {
-    return get (key) != INVALID;
-  }
+  void del (hb_codepoint_t key) { set (key, INVALID); }
 
-  inline hb_codepoint_t operator [] (unsigned int key) const
+  bool has (hb_codepoint_t key) const
+  { return get (key) != INVALID; }
+
+  hb_codepoint_t operator [] (unsigned int key) const
   { return get (key); }
 
-  static const hb_codepoint_t INVALID = HB_MAP_VALUE_INVALID;
+  enum { INVALID = HB_MAP_VALUE_INVALID };
 
-  inline void clear (void)
+  void clear ()
   {
     memset (items, 0xFF, ((size_t) mask + 1) * sizeof (item_t));
     population = occupancy = 0;
   }
 
-  inline bool is_empty (void) const
-  {
-    return population == 0;
-  }
+  bool is_empty () const { return population == 0; }
 
-  inline unsigned int get_population () const
-  {
-    return population;
-  }
+  unsigned int get_population () const { return population; }
 
   protected:
 
-  inline unsigned int bucket_for (hb_codepoint_t key) const
+  unsigned int bucket_for (hb_codepoint_t key) const
   {
     unsigned int i = Hash (key) % prime;
     unsigned int step = 0;
@@ -198,7 +196,7 @@
     return tombstone == INVALID ? i : tombstone;
   }
 
-  static inline unsigned int prime_for (unsigned int shift)
+  static unsigned int prime_for (unsigned int shift)
   {
     /* Following comment and table copied from glib. */
     /* Each table size has an associated prime modulo (the first prime
diff --git a/src/hb-mutex.hh b/src/hb-mutex.hh
index 75b89ad..35f1fde 100644
--- a/src/hb-mutex.hh
+++ b/src/hb-mutex.hh
@@ -48,7 +48,7 @@
 /* Defined externally, i.e. in config.h; must have typedef'ed hb_mutex_impl_t as well. */
 
 
-#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__))
+#elif !defined(HB_NO_MT) && defined(_WIN32)
 
 #include <windows.h>
 typedef CRITICAL_SECTION hb_mutex_impl_t;
@@ -131,16 +131,16 @@
 
   hb_mutex_impl_t m;
 
-  inline void init   (void) { hb_mutex_impl_init   (&m); }
-  inline void lock   (void) { hb_mutex_impl_lock   (&m); }
-  inline void unlock (void) { hb_mutex_impl_unlock (&m); }
-  inline void fini (void) { hb_mutex_impl_finish (&m); }
+  void init   () { hb_mutex_impl_init   (&m); }
+  void lock   () { hb_mutex_impl_lock   (&m); }
+  void unlock () { hb_mutex_impl_unlock (&m); }
+  void fini ()   { hb_mutex_impl_finish (&m); }
 };
 
 struct hb_lock_t
 {
-  inline hb_lock_t (hb_mutex_t &mutex_) : mutex (mutex_) { mutex.lock (); }
-  inline ~hb_lock_t (void) { mutex.unlock (); }
+  hb_lock_t (hb_mutex_t &mutex_) : mutex (mutex_) { mutex.lock (); }
+  ~hb_lock_t () { mutex.unlock (); }
   private:
   hb_mutex_t &mutex;
 };
diff --git a/src/hb-null.hh b/src/hb-null.hh
index 8766226..8a3e0d4 100644
--- a/src/hb-null.hh
+++ b/src/hb-null.hh
@@ -36,37 +36,105 @@
 
 /* Global nul-content Null pool.  Enlarge as necessary. */
 
-#define HB_NULL_POOL_SIZE 1024
+#define HB_NULL_POOL_SIZE 9880
+
+/* Use SFINAE to sniff whether T has min_size; in which case return T::null_size,
+ * otherwise return sizeof(T). */
+
+/* The hard way...
+ * https://stackoverflow.com/questions/7776448/sfinae-tried-with-bool-gives-compiler-error-template-argument-tvalue-invol
+ */
+
+template<bool> struct _hb_bool_type {};
+
+template <typename T, typename B>
+struct _hb_null_size
+{ enum { value = sizeof (T) }; };
+template <typename T>
+struct _hb_null_size<T, _hb_bool_type<(bool) (1 + (unsigned int) T::min_size)> >
+{ enum { value = T::null_size }; };
+
+template <typename T>
+struct hb_null_size
+{ enum { value = _hb_null_size<T, _hb_bool_type<true> >::value }; };
+#define hb_null_size(T) hb_null_size<T>::value
+
+/* These doesn't belong here, but since is copy/paste from above, put it here. */
+
+/* hb_static_size (T)
+ * Returns T::static_size if T::min_size is defined, or sizeof (T) otherwise. */
+
+template <typename T, typename B>
+struct _hb_static_size
+{ enum { value = sizeof (T) }; };
+template <typename T>
+struct _hb_static_size<T, _hb_bool_type<(bool) (1 + (unsigned int) T::min_size)> >
+{ enum { value = T::static_size }; };
+
+template <typename T>
+struct hb_static_size
+{ enum { value = _hb_static_size<T, _hb_bool_type<true> >::value }; };
+#define hb_static_size(T) hb_static_size<T>::value
+
+
+/* hb_assign (obj, value)
+ * Calls obj.set (value) if obj.min_size is defined and value has different type
+ * from obj, or obj = v otherwise. */
+
+template <typename T, typename V, typename B>
+struct _hb_assign
+{ static inline void value (T &o, const V v) { o = v; } };
+template <typename T, typename V>
+struct _hb_assign<T, V, _hb_bool_type<(bool) (1 + (unsigned int) T::min_size)> >
+{ static inline void value (T &o, const V v) { o.set (v); } };
+template <typename T>
+struct _hb_assign<T, T, _hb_bool_type<(bool) (1 + (unsigned int) T::min_size)> >
+{ static inline void value (T &o, const T v) { o = v; } };
+
+template <typename T, typename V>
+static inline void hb_assign (T &o, const V v)
+{ _hb_assign<T, V, _hb_bool_type<true> >::value (o, v); };
+
+
+/*
+ * Null()
+ */
 
 extern HB_INTERNAL
 hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)];
 
 /* Generic nul-content Null objects. */
 template <typename Type>
-static inline Type const & Null (void) {
-  static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
+static inline Type const & Null () {
+  static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
   return *reinterpret_cast<Type const *> (_hb_NullPool);
 }
-#define Null(Type) Null<Type>()
+template <typename QType>
+struct NullHelper
+{
+  typedef typename hb_remove_const (typename hb_remove_reference (QType)) Type;
+  static const Type & get_null () { return Null<Type> (); }
+};
+#define Null(Type) NullHelper<Type>::get_null ()
 
 /* Specializations for arbitrary-content Null objects expressed in bytes. */
 #define DECLARE_NULL_NAMESPACE_BYTES(Namespace, Type) \
 	} /* Close namespace. */ \
-	extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::min_size]; \
+	extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size]; \
 	template <> \
-	/*static*/ inline const Namespace::Type& Null<Namespace::Type> (void) { \
+	/*static*/ inline const Namespace::Type& Null<Namespace::Type> () { \
 	  return *reinterpret_cast<const Namespace::Type *> (_hb_Null_##Namespace##_##Type); \
 	} \
 	namespace Namespace { \
 	static_assert (true, "Just so we take semicolon after.")
 #define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \
-	const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::min_size]
+	const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size]
 
 /* Specializations for arbitrary-content Null objects expressed as struct initializer. */
 #define DECLARE_NULL_INSTANCE(Type) \
 	extern HB_INTERNAL const Type _hb_Null_##Type; \
 	template <> \
-	/*static*/ inline const Type& Null<Type> (void) { \
+	/*static*/ inline const Type& Null<Type> () { \
 	  return _hb_Null_##Type; \
 	} \
 static_assert (true, "Just so we take semicolon after.")
@@ -84,23 +152,53 @@
 
 /* CRAP pool: Common Region for Access Protection. */
 template <typename Type>
-static inline Type& Crap (void) {
-  static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
+static inline Type& Crap () {
+  static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
   Type *obj = reinterpret_cast<Type *> (_hb_CrapPool);
-  *obj = Null(Type);
+  memcpy (obj, &Null(Type), sizeof (*obj));
   return *obj;
 }
-#define Crap(Type) Crap<Type>()
+template <typename QType>
+struct CrapHelper
+{
+  typedef typename hb_remove_const (typename hb_remove_reference (QType)) Type;
+  static Type & get_crap () { return Crap<Type> (); }
+};
+#define Crap(Type) CrapHelper<Type>::get_crap ()
 
 template <typename Type>
-struct CrapOrNull {
-  static inline Type & get (void) { return Crap(Type); }
+struct CrapOrNullHelper {
+  static Type & get () { return Crap(Type); }
 };
 template <typename Type>
-struct CrapOrNull<const Type> {
-  static inline Type const & get (void) { return Null(Type); }
+struct CrapOrNullHelper<const Type> {
+  static const Type & get () { return Null(Type); }
 };
-#define CrapOrNull(Type) CrapOrNull<Type>::get ()
+#define CrapOrNull(Type) CrapOrNullHelper<Type>::get ()
+
+
+/*
+ * hb_nonnull_ptr_t
+ */
+
+template <typename P>
+struct hb_nonnull_ptr_t
+{
+  typedef typename hb_remove_pointer (P) T;
+
+  hb_nonnull_ptr_t (T *v_ = nullptr) : v (v_) {}
+  T * operator = (T *v_)   { return v = v_; }
+  T * operator -> () const { return get (); }
+  T & operator * () const  { return *get (); }
+  T ** operator & () const { return &v; }
+  /* Only auto-cast to const types. */
+  template <typename C> operator const C * () const { return get (); }
+  operator const char * () const { return (const char *) get (); }
+  T * get () const { return v ? v : const_cast<T *> (&Null(T)); }
+  T * get_raw () const { return v; }
+
+  T *v;
+};
 
 
 #endif /* HB_NULL_HH */
diff --git a/src/hb-object.hh b/src/hb-object.hh
index ca85af6..6eb1173 100644
--- a/src/hb-object.hh
+++ b/src/hb-object.hh
@@ -47,10 +47,10 @@
 {
   hb_vector_t <item_t, 1> items;
 
-  inline void init (void) { items.init (); }
+  void init () { items.init (); }
 
   template <typename T>
-  inline item_t *replace_or_insert (T v, lock_t &l, bool replace)
+  item_t *replace_or_insert (T v, lock_t &l, bool replace)
   {
     l.lock ();
     item_t *item = items.find (v);
@@ -73,7 +73,7 @@
   }
 
   template <typename T>
-  inline void remove (T v, lock_t &l)
+  void remove (T v, lock_t &l)
   {
     l.lock ();
     item_t *item = items.find (v);
@@ -89,7 +89,7 @@
   }
 
   template <typename T>
-  inline bool find (T v, item_t *i, lock_t &l)
+  bool find (T v, item_t *i, lock_t &l)
   {
     l.lock ();
     item_t *item = items.find (v);
@@ -100,7 +100,7 @@
   }
 
   template <typename T>
-  inline item_t *find_or_insert (T v, lock_t &l)
+  item_t *find_or_insert (T v, lock_t &l)
   {
     l.lock ();
     item_t *item = items.find (v);
@@ -111,7 +111,7 @@
     return item;
   }
 
-  inline void fini (lock_t &l)
+  void fini (lock_t &l)
   {
     if (!items.len) {
       /* No need for locking. */
@@ -145,14 +145,14 @@
 {
   mutable hb_atomic_int_t ref_count;
 
-  inline void init (int v = 1) { ref_count.set_relaxed (v); }
-  inline int get_relaxed (void) const { return ref_count.get_relaxed (); }
-  inline int inc (void) const { return ref_count.inc (); }
-  inline int dec (void) const { return ref_count.dec (); }
-  inline void fini (void) { ref_count.set_relaxed (HB_REFERENCE_COUNT_POISON_VALUE); }
+  void init (int v = 1) { ref_count.set_relaxed (v); }
+  int get_relaxed () const { return ref_count.get_relaxed (); }
+  int inc () const { return ref_count.inc (); }
+  int dec () const { return ref_count.dec (); }
+  void fini () { ref_count.set_relaxed (HB_REFERENCE_COUNT_POISON_VALUE); }
 
-  inline bool is_inert (void) const { return ref_count.get_relaxed () == HB_REFERENCE_COUNT_INERT_VALUE; }
-  inline bool is_valid (void) const { return ref_count.get_relaxed () > 0; }
+  bool is_inert () const { return ref_count.get_relaxed () == HB_REFERENCE_COUNT_INERT_VALUE; }
+  bool is_valid () const { return ref_count.get_relaxed () > 0; }
 };
 
 
@@ -165,16 +165,16 @@
     void *data;
     hb_destroy_func_t destroy;
 
-    inline bool operator == (hb_user_data_key_t *other_key) const { return key == other_key; }
-    inline bool operator == (hb_user_data_item_t &other) const { return key == other.key; }
+    bool operator == (hb_user_data_key_t *other_key) const { return key == other_key; }
+    bool operator == (hb_user_data_item_t &other) const { return key == other.key; }
 
-    void fini (void) { if (destroy) destroy (data); }
+    void fini () { if (destroy) destroy (data); }
   };
 
   hb_mutex_t lock;
   hb_lockable_set_t<hb_user_data_item_t, hb_mutex_t> items;
 
-  inline void init (void) { lock.init (); items.init (); }
+  void init () { lock.init (); items.init (); }
 
   HB_INTERNAL bool set (hb_user_data_key_t *key,
 			void *              data,
@@ -183,7 +183,7 @@
 
   HB_INTERNAL void *get (hb_user_data_key_t *key);
 
-  inline void fini (void) { items.fini (lock); lock.fini (); }
+  void fini () { items.fini (lock); lock.fini (); }
 };
 
 
@@ -194,13 +194,15 @@
 struct hb_object_header_t
 {
   hb_reference_count_t ref_count;
+  mutable hb_atomic_int_t writable;
   hb_atomic_ptr_t<hb_user_data_array_t> user_data;
-
-#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INIT, HB_ATOMIC_PTR_INIT (nullptr)}
-
-  private:
-  ASSERT_POD ();
 };
+#define HB_OBJECT_HEADER_STATIC \
+	{ \
+	  HB_REFERENCE_COUNT_INIT, \
+	  HB_ATOMIC_INT_INIT (false), \
+	  HB_ATOMIC_PTR_INIT (nullptr) \
+	}
 
 
 /*
@@ -217,7 +219,7 @@
 }
 
 template <typename Type>
-static inline Type *hb_object_create (void)
+static inline Type *hb_object_create ()
 {
   Type *obj = (Type *) calloc (1, sizeof (Type));
 
@@ -232,6 +234,7 @@
 static inline void hb_object_init (Type *obj)
 {
   obj->header.ref_count.init ();
+  obj->header.writable.set_relaxed (true);
   obj->header.user_data.init ();
 }
 template <typename Type>
@@ -245,6 +248,16 @@
   return likely (obj->header.ref_count.is_valid ());
 }
 template <typename Type>
+static inline bool hb_object_is_immutable (const Type *obj)
+{
+  return !obj->header.writable.get_relaxed ();
+}
+template <typename Type>
+static inline void hb_object_make_immutable (const Type *obj)
+{
+  obj->header.writable.set_relaxed (false);
+}
+template <typename Type>
 static inline Type *hb_object_reference (Type *obj)
 {
   hb_object_trace (obj, HB_FUNC);
@@ -276,6 +289,7 @@
   {
     user_data->fini ();
     free (user_data);
+    user_data = nullptr;
   }
 }
 template <typename Type>
diff --git a/src/hb-open-file.hh b/src/hb-open-file.hh
index 817791a..822a92d 100644
--- a/src/hb-open-file.hh
+++ b/src/hb-open-file.hh
@@ -54,8 +54,7 @@
 
 typedef struct TableRecord
 {
-  int cmp (Tag t) const
-  { return -t.cmp (tag); }
+  int cmp (Tag t) const { return -t.cmp (tag); }
 
   static int cmp (const void *pa, const void *pb)
   {
@@ -64,7 +63,7 @@
     return b->cmp (a->tag);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -83,13 +82,10 @@
 {
   friend struct OpenTypeFontFile;
 
-  inline unsigned int get_table_count (void) const
-  { return tables.len; }
-  inline const TableRecord& get_table (unsigned int i) const
-  {
-    return tables[i];
-  }
-  inline unsigned int get_table_tags (unsigned int  start_offset,
+  unsigned int get_table_count () const { return tables.len; }
+  const TableRecord& get_table (unsigned int i) const
+  { return tables[i]; }
+  unsigned int get_table_tags (unsigned int  start_offset,
 				      unsigned int *table_count, /* IN/OUT */
 				      hb_tag_t     *table_tags /* OUT */) const
   {
@@ -107,18 +103,13 @@
     }
     return tables.len;
   }
-  inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const
+  bool find_table_index (hb_tag_t tag, unsigned int *table_index) const
   {
     Tag t;
     t.set (tag);
-    /* Linear-search for small tables to work around fonts with unsorted
-     * table list. */
-    int i = tables.len < 64 ? tables.lsearch (t) : tables.bsearch (t);
-    if (table_index)
-      *table_index = i == -1 ? (unsigned) Index::NOT_FOUND_INDEX : (unsigned) i;
-    return i != -1;
+    return tables.bfind (t, table_index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
   }
-  inline const TableRecord& get_table_by_tag (hb_tag_t tag) const
+  const TableRecord& get_table_by_tag (hb_tag_t tag) const
   {
     unsigned int table_index;
     find_table_index (tag, &table_index);
@@ -127,11 +118,10 @@
 
   public:
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 hb_tag_t sfnt_tag,
-			 Supplier<hb_tag_t> &tags,
-			 Supplier<hb_blob_t *> &blobs,
-			 unsigned int table_count)
+  template <typename item_t>
+  bool serialize (hb_serialize_context_t *c,
+		  hb_tag_t sfnt_tag,
+		  hb_array_t<item_t> items)
   {
     TRACE_SERIALIZE (this);
     /* Alloc 12 for the OTHeader. */
@@ -140,17 +130,17 @@
     sfnt_version.set (sfnt_tag);
     /* Take space for numTables, searchRange, entrySelector, RangeShift
      * and the TableRecords themselves.  */
-    if (unlikely (!tables.serialize (c, table_count))) return_trace (false);
+    if (unlikely (!tables.serialize (c, items.len))) return_trace (false);
 
     const char *dir_end = (const char *) c->head;
     HBUINT32 *checksum_adjustment = nullptr;
 
     /* Write OffsetTables, alloc for and write actual table blobs. */
-    for (unsigned int i = 0; i < table_count; i++)
+    for (unsigned int i = 0; i < tables.len; i++)
     {
       TableRecord &rec = tables.arrayZ[i];
-      hb_blob_t *blob = blobs[i];
-      rec.tag.set (tags[i]);
+      hb_blob_t *blob = items[i].blob;
+      rec.tag.set (items[i].tag);
       rec.length.set (hb_blob_get_length (blob));
       rec.offset.serialize (c, this);
 
@@ -164,7 +154,7 @@
       c->align (4);
       const char *end = (const char *) c->head;
 
-      if (tags[i] == HB_OT_TAG_head && end - start >= head::static_size)
+      if (items[i].tag == HB_OT_TAG_head && end - start >= head::static_size)
       {
 	head *h = (head *) start;
 	checksum_adjustment = &h->checkSumAdjustment;
@@ -173,8 +163,6 @@
 
       rec.checkSum.set_for_data (start, end - start);
     }
-    tags += table_count;
-    blobs += table_count;
 
     tables.qsort ();
 
@@ -185,7 +173,7 @@
       /* The following line is a slower version of the following block. */
       //checksum.set_for_data (this, (const char *) c->head - (const char *) this);
       checksum.set_for_data (this, dir_end - (const char *) this);
-      for (unsigned int i = 0; i < table_count; i++)
+      for (unsigned int i = 0; i < items.len; i++)
       {
 	TableRecord &rec = tables.arrayZ[i];
 	checksum.set (checksum + rec.checkSum);
@@ -197,7 +185,7 @@
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && tables.sanitize (c));
@@ -220,10 +208,10 @@
 {
   friend struct TTCHeader;
 
-  inline unsigned int get_face_count (void) const { return table.len; }
-  inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
+  unsigned int get_face_count () const { return table.len; }
+  const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (table.sanitize (c, this));
@@ -246,7 +234,7 @@
 
   private:
 
-  inline unsigned int get_face_count (void) const
+  unsigned int get_face_count () const
   {
     switch (u.header.version.major) {
     case 2: /* version 2 is compatible with version 1 */
@@ -254,7 +242,7 @@
     default:return 0;
     }
   }
-  inline const OpenTypeFontFace& get_face (unsigned int i) const
+  const OpenTypeFontFace& get_face (unsigned int i) const
   {
     switch (u.header.version.major) {
     case 2: /* version 2 is compatible with version 1 */
@@ -263,7 +251,7 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!u.header.version.sanitize (c))) return_trace (false);
@@ -293,10 +281,10 @@
 
 struct ResourceRecord
 {
-  inline const OpenTypeFontFace & get_face (const void *data_base) const
+  const OpenTypeFontFace & get_face (const void *data_base) const
   { return CastR<OpenTypeFontFace> ((data_base+offset).arrayZ); }
 
-  inline bool sanitize (hb_sanitize_context_t *c,
+  bool sanitize (hb_sanitize_context_t *c,
 			const void *data_base) const
   {
     TRACE_SANITIZE (this);
@@ -322,21 +310,18 @@
 
 struct ResourceTypeRecord
 {
-  inline unsigned int get_resource_count (void) const
+  unsigned int get_resource_count () const
   { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; }
 
-  inline bool is_sfnt (void) const { return tag == HB_TAG_sfnt; }
+  bool is_sfnt () const { return tag == HB_TAG_sfnt; }
 
-  inline const ResourceRecord& get_resource_record (unsigned int i,
-						    const void *type_base) const
-  {
-    return hb_array_t<ResourceRecord> ((type_base+resourcesZ).arrayZ,
-				       get_resource_count ()) [i];
-  }
+  const ResourceRecord& get_resource_record (unsigned int i,
+					     const void *type_base) const
+  { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; }
 
-  inline bool sanitize (hb_sanitize_context_t *c,
-			const void *type_base,
-			const void *data_base) const
+  bool sanitize (hb_sanitize_context_t *c,
+		 const void *type_base,
+		 const void *data_base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -357,7 +342,7 @@
 
 struct ResourceMap
 {
-  inline unsigned int get_face_count (void) const
+  unsigned int get_face_count () const
   {
     unsigned int count = get_type_count ();
     for (unsigned int i = 0; i < count; i++)
@@ -369,8 +354,8 @@
     return 0;
   }
 
-  inline const OpenTypeFontFace& get_face (unsigned int idx,
-					   const void *data_base) const
+  const OpenTypeFontFace& get_face (unsigned int idx,
+				    const void *data_base) const
   {
     unsigned int count = get_type_count ();
     for (unsigned int i = 0; i < count; i++)
@@ -384,7 +369,7 @@
     return Null (OpenTypeFontFace);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *data_base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *data_base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -394,9 +379,9 @@
   }
 
   private:
-  inline unsigned int get_type_count (void) const { return (this+typeList).lenM1 + 1; }
+  unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; }
 
-  inline const ResourceTypeRecord& get_type_record (unsigned int i) const
+  const ResourceTypeRecord& get_type_record (unsigned int i) const
   { return (this+typeList)[i]; }
 
   protected:
@@ -415,11 +400,11 @@
 
 struct ResourceForkHeader
 {
-  inline unsigned int get_face_count (void) const
+  unsigned int get_face_count () const
   { return (this+map).get_face_count (); }
 
-  inline const OpenTypeFontFace& get_face (unsigned int idx,
-					   unsigned int *base_offset = nullptr) const
+  const OpenTypeFontFace& get_face (unsigned int idx,
+				    unsigned int *base_offset = nullptr) const
   {
     const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data));
     if (base_offset)
@@ -427,7 +412,7 @@
     return face;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -463,9 +448,9 @@
     Typ1Tag		= HB_TAG ('t','y','p','1')  /* Obsolete Apple Type1 font in SFNT container */
   };
 
-  inline hb_tag_t get_tag (void) const { return u.tag; }
+  hb_tag_t get_tag () const { return u.tag; }
 
-  inline unsigned int get_face_count (void) const
+  unsigned int get_face_count () const
   {
     switch (u.tag) {
     case CFFTag:	/* All the non-collection tags */
@@ -477,7 +462,7 @@
     default:		return 0;
     }
   }
-  inline const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const
+  const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const
   {
     if (base_offset)
       *base_offset = 0;
@@ -495,19 +480,18 @@
     }
   }
 
-  inline bool serialize_single (hb_serialize_context_t *c,
-				hb_tag_t sfnt_tag,
-			        Supplier<hb_tag_t> &tags,
-			        Supplier<hb_blob_t *> &blobs,
-			        unsigned int table_count)
+  template <typename item_t>
+  bool serialize_single (hb_serialize_context_t *c,
+			 hb_tag_t sfnt_tag,
+			 hb_array_t<item_t> items)
   {
     TRACE_SERIALIZE (this);
     assert (sfnt_tag != TTCTag);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    return_trace (u.fontFace.serialize (c, sfnt_tag, tags, blobs, table_count));
+    return_trace (u.fontFace.serialize (c, sfnt_tag, items));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!u.tag.sanitize (c))) return_trace (false);
diff --git a/src/hb-open-type.hh b/src/hb-open-type.hh
index 08e7206..7f88279 100644
--- a/src/hb-open-type.hh
+++ b/src/hb-open-type.hh
@@ -52,18 +52,24 @@
  * Int types
  */
 
+template <bool is_signed> struct hb_signedness_int;
+template <> struct hb_signedness_int<false> { typedef unsigned int value; };
+template <> struct hb_signedness_int<true>  { typedef   signed int value; };
+
 /* Integer types in big-endian order and no alignment requirement */
 template <typename Type, unsigned int Size>
 struct IntType
 {
   typedef Type type;
-  inline void set (Type i) { v.set (i); }
-  inline operator Type(void) const { return v; }
-  inline bool operator == (const IntType<Type,Size> &o) const { return (Type) v == (Type) o.v; }
-  inline bool operator != (const IntType<Type,Size> &o) const { return !(*this == o); }
-  static inline int cmp (const IntType<Type,Size> *a, const IntType<Type,Size> *b) { return b->cmp (*a); }
+  typedef typename hb_signedness_int<hb_is_signed<Type>::value>::value wide_type;
+
+  void set (wide_type i) { v.set (i); }
+  operator wide_type () const { return v; }
+  bool operator == (const IntType<Type,Size> &o) const { return (Type) v == (Type) o.v; }
+  bool operator != (const IntType<Type,Size> &o) const { return !(*this == o); }
+  static int cmp (const IntType<Type,Size> *a, const IntType<Type,Size> *b) { return b->cmp (*a); }
   template <typename Type2>
-  inline int cmp (Type2 a) const
+  int cmp (Type2 a) const
   {
     Type b = v;
     if (sizeof (Type) < sizeof (int) && sizeof (Type2) < sizeof (int))
@@ -71,7 +77,7 @@
     else
       return a < b ? -1 : a == b ? 0 : +1;
   }
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this)));
@@ -88,6 +94,8 @@
 typedef IntType<int16_t,  2> HBINT16;	/* 16-bit signed integer. */
 typedef IntType<uint32_t, 4> HBUINT32;	/* 32-bit unsigned integer. */
 typedef IntType<int32_t,  4> HBINT32;	/* 32-bit signed integer. */
+/* Note: we cannot defined a signed HBINT24 because there's no corresponding C type.
+ * Works for unsigned, but not signed, since we rely on compiler for sign-extension. */
 typedef IntType<uint32_t, 3> HBUINT24;	/* 24-bit unsigned integer. */
 
 /* 16-bit signed integer (HBINT16) that describes a quantity in FUnits. */
@@ -103,8 +111,8 @@
 struct F2DOT14 : HBINT16
 {
   // 16384 means 1<<14
-  inline float to_float (void) const { return ((int32_t) v) / 16384.f; }
-  inline void set_float (float f) { v.set (round (f * 16384.f)); }
+  float to_float () const  { return ((int32_t) v) / 16384.f; }
+  void set_float (float f) { v.set (round (f * 16384.f)); }
   public:
   DEFINE_SIZE_STATIC (2);
 };
@@ -113,8 +121,8 @@
 struct Fixed : HBINT32
 {
   // 65536 means 1<<16
-  inline float to_float (void) const { return ((int32_t) v) / 65536.f; }
-  inline void set_float (float f) { v.set (round (f * 65536.f)); }
+  float to_float () const  { return ((int32_t) v) / 65536.f; }
+  void set_float (float f) { v.set (round (f * 65536.f)); }
   public:
   DEFINE_SIZE_STATIC (4);
 };
@@ -123,7 +131,7 @@
  * 1904. The value is represented as a signed 64-bit integer. */
 struct LONGDATETIME
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this)));
@@ -140,8 +148,8 @@
 struct Tag : HBUINT32
 {
   /* What the char* converters return is NOT nul-terminated.  Print using "%.4s" */
-  inline operator const char* (void) const { return reinterpret_cast<const char *> (&this->v); }
-  inline operator char* (void) { return reinterpret_cast<char *> (&this->v); }
+  operator const char* () const { return reinterpret_cast<const char *> (&this->v); }
+  operator char* ()             { return reinterpret_cast<char *> (&this->v); }
   public:
   DEFINE_SIZE_STATIC (4);
 };
@@ -149,24 +157,23 @@
 /* Glyph index number, same as uint16 (length = 16 bits) */
 typedef HBUINT16 GlyphID;
 
-/* Name-table index, same as uint16 (length = 16 bits) */
-typedef HBUINT16 NameID;
-
 /* Script/language-system/feature index */
 struct Index : HBUINT16 {
   enum { NOT_FOUND_INDEX = 0xFFFFu };
 };
 DECLARE_NULL_NAMESPACE_BYTES (OT, Index);
 
+typedef Index NameID;
+
 /* Offset, Null offset = 0 */
 template <typename Type, bool has_null=true>
 struct Offset : Type
 {
   typedef Type type;
 
-  inline bool is_null (void) const { return has_null && 0 == *this; }
+  bool is_null () const { return has_null && 0 == *this; }
 
-  inline void *serialize (hb_serialize_context_t *c, const void *base)
+  void *serialize (hb_serialize_context_t *c, const void *base)
   {
     void *t = c->start_embed<void> ();
     this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */
@@ -174,7 +181,7 @@
   }
 
   public:
-  DEFINE_SIZE_STATIC (sizeof(Type));
+  DEFINE_SIZE_STATIC (sizeof (Type));
 };
 
 typedef Offset<HBUINT16> Offset16;
@@ -185,7 +192,7 @@
 struct CheckSum : HBUINT32
 {
   /* This is reference implementation from the spec. */
-  static inline uint32_t CalcTableChecksum (const HBUINT32 *Table, uint32_t Length)
+  static uint32_t CalcTableChecksum (const HBUINT32 *Table, uint32_t Length)
   {
     uint32_t Sum = 0L;
     assert (0 == (Length & 3));
@@ -197,7 +204,7 @@
   }
 
   /* Note: data should be 4byte aligned and have 4byte padding at the end. */
-  inline void set_for_data (const void *data, unsigned int length)
+  void set_for_data (const void *data, unsigned int length)
   { set (CalcTableChecksum ((const HBUINT32 *) data, length)); }
 
   public:
@@ -212,9 +219,9 @@
 template <typename FixedType=HBUINT16>
 struct FixedVersion
 {
-  inline uint32_t to_int (void) const { return (major << (sizeof(FixedType) * 8)) + minor; }
+  uint32_t to_int () const { return (major << (sizeof (FixedType) * 8)) + minor; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -223,7 +230,7 @@
   FixedType major;
   FixedType minor;
   public:
-  DEFINE_SIZE_STATIC (2 * sizeof(FixedType));
+  DEFINE_SIZE_STATIC (2 * sizeof (FixedType));
 };
 
 
@@ -232,34 +239,42 @@
  * Use: (base+offset)
  */
 
-template <typename Type, bool has_null_> struct assert_has_min_size { static_assert (Type::min_size > 0, ""); };
-template <typename Type> struct assert_has_min_size<Type, false> {};
+template <typename Type, bool has_null>
+struct _hb_has_null
+{
+  static const Type *get_null () { return nullptr; }
+  static Type *get_crap ()       { return nullptr; }
+};
+template <typename Type>
+struct _hb_has_null<Type, true>
+{
+  static const Type *get_null () { return &Null(Type); }
+  static Type *get_crap ()       { return &Crap(Type); }
+};
 
 template <typename Type, typename OffsetType=HBUINT16, bool has_null=true>
 struct OffsetTo : Offset<OffsetType, has_null>
 {
-  static_assert (sizeof (assert_has_min_size<Type, has_null>) || true, "");
-
-  inline const Type& operator () (const void *base) const
+  const Type& operator () (const void *base) const
   {
-    if (unlikely (this->is_null ())) return Null(Type);
+    if (unlikely (this->is_null ())) return *_hb_has_null<Type, has_null>::get_null ();
     return StructAtOffset<const Type> (base, *this);
   }
-  inline Type& operator () (void *base) const
+  Type& operator () (void *base) const
   {
-    if (unlikely (this->is_null ())) return Crap(Type);
+    if (unlikely (this->is_null ())) return *_hb_has_null<Type, has_null>::get_crap ();
     return StructAtOffset<Type> (base, *this);
   }
 
-  inline Type& serialize (hb_serialize_context_t *c, const void *base)
+  Type& serialize (hb_serialize_context_t *c, const void *base)
   {
     return * (Type *) Offset<OffsetType>::serialize (c, base);
   }
 
   template <typename T>
-  inline void serialize_subset (hb_subset_context_t *c, const T &src, const void *base)
+  void serialize_subset (hb_subset_context_t *c, const T &src, const void *base)
   {
-    if (&src == &Null(T))
+    if (&src == &Null (T))
     {
       this->set (0);
       return;
@@ -269,7 +284,7 @@
       this->set (0);
   }
 
-  inline bool sanitize_shallow (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize_shallow (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this))) return_trace (false);
@@ -278,7 +293,7 @@
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (sanitize_shallow (c, base) &&
@@ -287,7 +302,7 @@
 		   neuter (c)));
   }
   template <typename T1>
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1) const
   {
     TRACE_SANITIZE (this);
     return_trace (sanitize_shallow (c, base) &&
@@ -296,7 +311,7 @@
 		   neuter (c)));
   }
   template <typename T1, typename T2>
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2) const
   {
     TRACE_SANITIZE (this);
     return_trace (sanitize_shallow (c, base) &&
@@ -305,7 +320,7 @@
 		   neuter (c)));
   }
   template <typename T1, typename T2, typename T3>
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2, T3 d3) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2, T3 d3) const
   {
     TRACE_SANITIZE (this);
     return_trace (sanitize_shallow (c, base) &&
@@ -315,14 +330,15 @@
   }
 
   /* Set the offset to Null */
-  inline bool neuter (hb_sanitize_context_t *c) const
+  bool neuter (hb_sanitize_context_t *c) const
   {
     if (!has_null) return false;
     return c->try_set (this, 0);
   }
-  DEFINE_SIZE_STATIC (sizeof(OffsetType));
+  DEFINE_SIZE_STATIC (sizeof (OffsetType));
 };
 template <typename Type, bool has_null=true> struct LOffsetTo : OffsetTo<Type, HBUINT32, has_null> {};
+
 template <typename Base, typename OffsetType, bool has_null, typename Type>
 static inline const Type& operator + (const Base &base, const OffsetTo<Type, OffsetType, has_null> &offset) { return offset (base); }
 template <typename Base, typename OffsetType, bool has_null, typename Type>
@@ -336,10 +352,49 @@
 template <typename Type>
 struct UnsizedArrayOf
 {
-  inline const Type& operator [] (unsigned int i) const { return arrayZ[i]; }
-  inline Type& operator [] (unsigned int i) { return arrayZ[i]; }
+  typedef Type ItemType;
+  enum { item_size = hb_static_size (Type) };
 
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  HB_NO_CREATE_COPY_ASSIGN_TEMPLATE (UnsizedArrayOf, Type);
+
+  const Type& operator [] (int i_) const
+  {
+    unsigned int i = (unsigned int) i_;
+    const Type *p = &arrayZ[i];
+    if (unlikely (p < arrayZ)) return Null (Type); /* Overflowed. */
+    return *p;
+  }
+  Type& operator [] (int i_)
+  {
+    unsigned int i = (unsigned int) i_;
+    Type *p = &arrayZ[i];
+    if (unlikely (p < arrayZ)) return Crap (Type); /* Overflowed. */
+    return *p;
+  }
+
+  unsigned int get_size (unsigned int len) const
+  { return len * Type::static_size; }
+
+  template <typename T> operator T * () { return arrayZ; }
+  template <typename T> operator const T * () const { return arrayZ; }
+  hb_array_t<Type> as_array (unsigned int len)
+  { return hb_array (arrayZ, len); }
+  hb_array_t<const Type> as_array (unsigned int len) const
+  { return hb_array (arrayZ, len); }
+  operator hb_array_t<Type> ()             { return as_array (); }
+  operator hb_array_t<const Type> () const { return as_array (); }
+
+  template <typename T>
+  Type &lsearch (unsigned int len, const T &x, Type &not_found = Crap (Type))
+  { return *as_array (len).lsearch (x, &not_found); }
+  template <typename T>
+  const Type &lsearch (unsigned int len, const T &x, const Type &not_found = Null (Type)) const
+  { return *as_array (len).lsearch (x, &not_found); }
+
+  void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1)
+  { as_array (len).qsort (start, end); }
+
+  bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
@@ -355,27 +410,27 @@
 
     return_trace (true);
   }
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!arrayZ[i].sanitize (c, base)))
-        return_trace (false);
+	return_trace (false);
     return_trace (true);
   }
   template <typename T>
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base, T user_data) const
+  bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base, T user_data) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!arrayZ[i].sanitize (c, base, user_data)))
-        return_trace (false);
+	return_trace (false);
     return_trace (true);
   }
 
-  inline bool sanitize_shallow (hb_sanitize_context_t *c, unsigned int count) const
+  bool sanitize_shallow (hb_sanitize_context_t *c, unsigned int count) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_array (arrayZ, count));
@@ -384,7 +439,7 @@
   public:
   Type		arrayZ[VAR];
   public:
-  DEFINE_SIZE_ARRAY (0, arrayZ);
+  DEFINE_SIZE_UNBOUNDED (0);
 };
 
 /* Unsized array of offset's */
@@ -395,55 +450,102 @@
 template <typename Type, typename OffsetType, bool has_null=true>
 struct UnsizedOffsetListOf : UnsizedOffsetArrayOf<Type, OffsetType, has_null>
 {
-  inline const Type& operator [] (unsigned int i) const
+  const Type& operator [] (int i_) const
   {
-    return this+this->arrayZ[i];
+    unsigned int i = (unsigned int) i_;
+    const OffsetTo<Type, OffsetType, has_null> *p = &this->arrayZ[i];
+    if (unlikely (p < this->arrayZ)) return Null (Type); /* Overflowed. */
+    return this+*p;
+  }
+  Type& operator [] (int i_)
+  {
+    unsigned int i = (unsigned int) i_;
+    const OffsetTo<Type, OffsetType, has_null> *p = &this->arrayZ[i];
+    if (unlikely (p < this->arrayZ)) return Crap (Type); /* Overflowed. */
+    return this+*p;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+
+  bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
   {
     TRACE_SANITIZE (this);
     return_trace ((UnsizedOffsetArrayOf<Type, OffsetType, has_null>::sanitize (c, count, this)));
   }
   template <typename T>
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, T user_data) const
+  bool sanitize (hb_sanitize_context_t *c, unsigned int count, T user_data) const
   {
     TRACE_SANITIZE (this);
     return_trace ((UnsizedOffsetArrayOf<Type, OffsetType, has_null>::sanitize (c, count, this, user_data)));
   }
 };
 
+/* An array with sorted elements.  Supports binary searching. */
+template <typename Type>
+struct SortedUnsizedArrayOf : UnsizedArrayOf<Type>
+{
+  hb_sorted_array_t<Type> as_array (unsigned int len)
+  { return hb_sorted_array (this->arrayZ, len); }
+  hb_sorted_array_t<const Type> as_array (unsigned int len) const
+  { return hb_sorted_array (this->arrayZ, len); }
+  operator hb_sorted_array_t<Type> ()             { return as_array (); }
+  operator hb_sorted_array_t<const Type> () const { return as_array (); }
+
+  template <typename T>
+  Type &bsearch (unsigned int len, const T &x, Type &not_found = Crap (Type))
+  { return *as_array (len).bsearch (x, &not_found); }
+  template <typename T>
+  const Type &bsearch (unsigned int len, const T &x, const Type &not_found = Null (Type)) const
+  { return *as_array (len).bsearch (x, &not_found); }
+  template <typename T>
+  bool bfind (unsigned int len, const T &x, unsigned int *i = nullptr,
+		     hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
+		     unsigned int to_store = (unsigned int) -1) const
+  { return as_array (len).bfind (x, i, not_found, to_store); }
+};
+
+
 /* An array with a number of elements. */
 template <typename Type, typename LenType=HBUINT16>
 struct ArrayOf
 {
-  const Type *sub_array (unsigned int start_offset, unsigned int *pcount /* IN/OUT */) const
+  typedef Type ItemType;
+  enum { item_size = hb_static_size (Type) };
+
+  HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2 (ArrayOf, Type, LenType);
+
+  const Type& operator [] (int i_) const
   {
-    unsigned int count = len;
-    if (unlikely (start_offset > count))
-      count = 0;
-    else
-      count -= start_offset;
-    count = MIN (count, *pcount);
-    *pcount = count;
-    return arrayZ + start_offset;
+    unsigned int i = (unsigned int) i_;
+    if (unlikely (i >= len)) return Null (Type);
+    return arrayZ[i];
+  }
+  Type& operator [] (int i_)
+  {
+    unsigned int i = (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& operator [] (unsigned int i)
-  {
-    if (unlikely (i >= len)) return Crap(Type);
-    return arrayZ[i];
-  }
-  inline unsigned int get_size (void) const
+  unsigned int get_size () const
   { return len.static_size + len * Type::static_size; }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 unsigned int items_len)
+  hb_array_t<Type> as_array ()
+  { return hb_array (arrayZ, len); }
+  hb_array_t<const Type> as_array () const
+  { return hb_array (arrayZ, len); }
+  operator hb_array_t<Type> (void)             { return as_array (); }
+  operator hb_array_t<const Type> (void) const { return as_array (); }
+
+  hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
+  { return as_array ().sub_array (start_offset, count);}
+  hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
+  { return as_array ().sub_array (start_offset, count);}
+  hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
+  { return as_array ().sub_array (start_offset, count);}
+  hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
+  { return as_array ().sub_array (start_offset, count);}
+
+  bool serialize (hb_serialize_context_t *c, unsigned int items_len)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
@@ -451,19 +553,17 @@
     if (unlikely (!c->extend (*this))) return_trace (false);
     return_trace (true);
   }
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<Type> &items,
-			 unsigned int items_len)
+  template <typename T>
+  bool serialize (hb_serialize_context_t *c, hb_array_t<const T> items)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!serialize (c, items_len))) return_trace (false);
-    for (unsigned int i = 0; i < items_len; i++)
-      arrayZ[i] = items[i];
-    items += items_len;
+    if (unlikely (!serialize (c, items.len))) return_trace (false);
+    for (unsigned int i = 0; i < items.len; i++)
+      hb_assign (arrayZ[i], items[i]);
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
@@ -479,45 +579,39 @@
 
     return_trace (true);
   }
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!arrayZ[i].sanitize (c, base)))
-        return_trace (false);
+	return_trace (false);
     return_trace (true);
   }
   template <typename T>
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!arrayZ[i].sanitize (c, base, user_data)))
-        return_trace (false);
+	return_trace (false);
     return_trace (true);
   }
 
-  template <typename SearchType>
-  inline int lsearch (const SearchType &x) const
-  {
-    unsigned int count = len;
-    for (unsigned int i = 0; i < count; i++)
-      if (!this->arrayZ[i].cmp (x))
-        return i;
-    return -1;
-  }
+  template <typename T>
+  Type &lsearch (const T &x, Type &not_found = Crap (Type))
+  { return *as_array ().lsearch (x, &not_found); }
+  template <typename T>
+  const Type &lsearch (const T &x, const Type &not_found = Null (Type)) const
+  { return *as_array ().lsearch (x, &not_found); }
 
-  inline void qsort (void)
-  {
-    ::qsort (arrayZ, len, sizeof (Type), Type::cmp);
-  }
+  void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1)
+  { as_array ().qsort (start, end); }
 
-  private:
-  inline bool sanitize_shallow (hb_sanitize_context_t *c) const
+  bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (len.sanitize (c) && c->check_array (arrayZ, len));
@@ -533,25 +627,31 @@
 typedef ArrayOf<HBUINT8, HBUINT8> PString;
 
 /* Array of Offset's */
-template <typename Type, typename OffsetType=HBUINT16>
-struct OffsetArrayOf : ArrayOf<OffsetTo<Type, OffsetType> > {};
+template <typename Type>
+struct OffsetArrayOf : ArrayOf<OffsetTo<Type, HBUINT16> > {};
+template <typename Type>
+struct LOffsetArrayOf : ArrayOf<OffsetTo<Type, HBUINT32> > {};
+template <typename Type>
+struct LOffsetLArrayOf : ArrayOf<OffsetTo<Type, HBUINT32>, HBUINT32> {};
 
 /* Array of offsets relative to the beginning of the array itself. */
 template <typename Type>
 struct OffsetListOf : OffsetArrayOf<Type>
 {
-  inline const Type& operator [] (unsigned int i) const
+  const Type& operator [] (int i_) const
   {
-    if (unlikely (i >= this->len)) return Null(Type);
+    unsigned int i = (unsigned int) i_;
+    if (unlikely (i >= this->len)) return Null (Type);
     return this+this->arrayZ[i];
   }
-  inline const Type& operator [] (unsigned int i)
+  const Type& operator [] (int i_)
   {
-    if (unlikely (i >= this->len)) return Crap(Type);
+    unsigned int i = (unsigned int) i_;
+    if (unlikely (i >= this->len)) return Crap (Type);
     return this+this->arrayZ[i];
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     struct OffsetListOf<Type> *out = c->serializer->embed (*this);
@@ -562,13 +662,13 @@
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (OffsetArrayOf<Type>::sanitize (c, this));
   }
   template <typename T>
-  inline bool sanitize (hb_sanitize_context_t *c, T user_data) const
+  bool sanitize (hb_sanitize_context_t *c, T user_data) const
   {
     TRACE_SANITIZE (this);
     return_trace (OffsetArrayOf<Type>::sanitize (c, this, user_data));
@@ -579,35 +679,38 @@
 template <typename Type, typename LenType=HBUINT16>
 struct HeadlessArrayOf
 {
-  inline const Type& operator [] (unsigned int i) const
+  enum { item_size = Type::static_size };
+
+  HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2 (HeadlessArrayOf, Type, LenType);
+
+  const Type& operator [] (int i_) const
   {
-    if (unlikely (i >= lenP1 || !i)) return Null(Type);
+    unsigned int i = (unsigned int) i_;
+    if (unlikely (i >= lenP1 || !i)) return Null (Type);
     return arrayZ[i-1];
   }
-  inline Type& operator [] (unsigned int i)
+  Type& operator [] (int i_)
   {
-    if (unlikely (i >= lenP1 || !i)) return Crap(Type);
+    unsigned int i = (unsigned int) i_;
+    if (unlikely (i >= lenP1 || !i)) return Crap (Type);
     return arrayZ[i-1];
   }
-  inline unsigned int get_size (void) const
+  unsigned int get_size () const
   { return lenP1.static_size + (lenP1 ? lenP1 - 1 : 0) * Type::static_size; }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<Type> &items,
-			 unsigned int items_len)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const Type> items)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    lenP1.set (items_len); /* TODO(serialize) Overflow? */
-    if (unlikely (!items_len)) return_trace (true);
+    lenP1.set (items.len + 1); /* TODO(serialize) Overflow? */
     if (unlikely (!c->extend (*this))) return_trace (false);
-    for (unsigned int i = 0; i < items_len - 1; i++)
+    for (unsigned int i = 0; i < items.len; i++)
       arrayZ[i] = items[i];
-    items += items_len - 1;
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
@@ -625,7 +728,7 @@
   }
 
   private:
-  inline bool sanitize_shallow (hb_sanitize_context_t *c) const
+  bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (lenP1.sanitize (c) &&
@@ -643,33 +746,37 @@
 template <typename Type, typename LenType=HBUINT16>
 struct ArrayOfM1
 {
-  inline const Type& operator [] (unsigned int i) const
+  HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2 (ArrayOfM1, Type, LenType);
+
+  const Type& operator [] (int i_) const
   {
-    if (unlikely (i > lenM1)) return Null(Type);
+    unsigned int i = (unsigned int) i_;
+    if (unlikely (i > lenM1)) return Null (Type);
     return arrayZ[i];
   }
-  inline Type& operator [] (unsigned int i)
+  Type& operator [] (int i_)
   {
-    if (unlikely (i > lenM1)) return Crap(Type);
+    unsigned int i = (unsigned int) i_;
+    if (unlikely (i > lenM1)) return Crap (Type);
     return arrayZ[i];
   }
-  inline unsigned int get_size (void) const
+  unsigned int get_size () const
   { return lenM1.static_size + (lenM1 + 1) * Type::static_size; }
 
   template <typename T>
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
     unsigned int count = lenM1 + 1;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!arrayZ[i].sanitize (c, base, user_data)))
-        return_trace (false);
+	return_trace (false);
     return_trace (true);
   }
 
   private:
-  inline bool sanitize_shallow (hb_sanitize_context_t *c) const
+  bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (lenM1.sanitize (c) &&
@@ -687,25 +794,33 @@
 template <typename Type, typename LenType=HBUINT16>
 struct SortedArrayOf : ArrayOf<Type, LenType>
 {
-  template <typename SearchType>
-  inline int bsearch (const SearchType &x) const
-  {
-    /* Hand-coded bsearch here since this is in the hot inner loop. */
-    const Type *arr = this->arrayZ;
-    int min = 0, max = (int) this->len - 1;
-    while (min <= max)
-    {
-      int mid = (min + max) / 2;
-      int c = arr[mid].cmp (x);
-      if (c < 0)
-        max = mid - 1;
-      else if (c > 0)
-        min = mid + 1;
-      else
-        return mid;
-    }
-    return -1;
-  }
+  hb_sorted_array_t<Type> as_array ()
+  { return hb_sorted_array (this->arrayZ, this->len); }
+  hb_sorted_array_t<const Type> as_array () const
+  { return hb_sorted_array (this->arrayZ, this->len); }
+  operator hb_sorted_array_t<Type> ()             { return as_array (); }
+  operator hb_sorted_array_t<const Type> () const { return as_array (); }
+
+  hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
+  { return as_array ().sub_array (start_offset, count);}
+  hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
+  { return as_array ().sub_array (start_offset, count);}
+  hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
+  { return as_array ().sub_array (start_offset, count);}
+  hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
+  { return as_array ().sub_array (start_offset, count);}
+
+  template <typename T>
+  Type &bsearch (const T &x, Type &not_found = Crap (Type))
+  { return *as_array ().bsearch (x, &not_found); }
+  template <typename T>
+  const Type &bsearch (const T &x, const Type &not_found = Null (Type)) const
+  { return *as_array ().bsearch (x, &not_found); }
+  template <typename T>
+  bool bfind (const T &x, unsigned int *i = nullptr,
+		     hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
+		     unsigned int to_store = (unsigned int) -1) const
+  { return as_array ().bfind (x, i, not_found, to_store); }
 };
 
 /*
@@ -715,15 +830,15 @@
 template <typename LenType=HBUINT16>
 struct BinSearchHeader
 {
-  inline operator uint32_t (void) const { return len; }
+  operator uint32_t () const { return len; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
-  inline void set (unsigned int v)
+  void set (unsigned int v)
   {
     len.set (v);
     assert (len == v);
@@ -747,10 +862,11 @@
 template <typename Type, typename LenType=HBUINT16>
 struct BinSearchArrayOf : SortedArrayOf<Type, BinSearchHeader<LenType> > {};
 
+
 struct VarSizedBinSearchHeader
 {
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -772,19 +888,44 @@
 template <typename Type>
 struct VarSizedBinSearchArrayOf
 {
-  inline const Type& operator [] (unsigned int i) const
+  enum { item_size = Type::static_size };
+
+  HB_NO_CREATE_COPY_ASSIGN_TEMPLATE (VarSizedBinSearchArrayOf, Type);
+
+  bool last_is_terminator () const
   {
-    if (unlikely (i >= header.nUnits)) return Null(Type);
+    if (unlikely (!header.nUnits)) return false;
+
+    /* Gah.
+     *
+     * "The number of termination values that need to be included is table-specific.
+     * The value that indicates binary search termination is 0xFFFF." */
+    const HBUINT16 *words = &StructAtOffset<HBUINT16> (&bytesZ, (header.nUnits - 1) * header.unitSize);
+    unsigned int count = Type::TerminationWordCount;
+    for (unsigned int i = 0; i < count; i++)
+      if (words[i] != 0xFFFFu)
+        return false;
+    return true;
+  }
+
+  const Type& operator [] (int i_) const
+  {
+    unsigned int i = (unsigned int) i_;
+    if (unlikely (i >= get_length ())) return Null (Type);
     return StructAtOffset<Type> (&bytesZ, i * header.unitSize);
   }
-  inline Type& operator [] (unsigned int i)
+  Type& operator [] (int i_)
   {
+    unsigned int i = (unsigned int) i_;
+    if (unlikely (i >= get_length ())) return Crap (Type);
     return StructAtOffset<Type> (&bytesZ, i * header.unitSize);
   }
-  inline unsigned int get_size (void) const
+  unsigned int get_length () const
+  { return header.nUnits - last_is_terminator (); }
+  unsigned int get_size () const
   { return header.static_size + header.nUnits * header.unitSize; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
@@ -800,44 +941,54 @@
 
     return_trace (true);
   }
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return_trace (false);
-    unsigned int count = header.nUnits;
+    unsigned int count = get_length ();
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!(*this)[i].sanitize (c, base)))
-        return_trace (false);
+	return_trace (false);
+    return_trace (true);
+  }
+  template <typename T>
+  bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!sanitize_shallow (c))) return_trace (false);
+    unsigned int count = get_length ();
+    for (unsigned int i = 0; i < count; i++)
+      if (unlikely (!(*this)[i].sanitize (c, base, user_data)))
+	return_trace (false);
     return_trace (true);
   }
 
   template <typename T>
-  inline const Type *bsearch (const T &key) const
+  const Type *bsearch (const T &key) const
   {
     unsigned int size = header.unitSize;
-    int min = 0, max = (int) header.nUnits - 1;
+    int min = 0, max = (int) get_length () - 1;
     while (min <= max)
     {
-      int mid = (min + max) / 2;
+      int mid = ((unsigned int) min + (unsigned int) max) / 2;
       const Type *p = (const Type *) (((const char *) &bytesZ) + (mid * size));
       int c = p->cmp (key);
-      if (c < 0)
-	max = mid - 1;
-      else if (c > 0)
-	min = mid + 1;
-      else
-	return p;
+      if (c < 0) max = mid - 1;
+      else if (c > 0) min = mid + 1;
+      else return p;
     }
     return nullptr;
   }
 
   private:
-  inline bool sanitize_shallow (hb_sanitize_context_t *c) const
+  bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (header.sanitize (c) &&
 		  Type::static_size <= header.unitSize &&
-		  c->check_array (bytesZ.arrayZ, header.nUnits, header.unitSize));
+		  c->check_range (bytesZ.arrayZ,
+				  header.nUnits,
+				  header.unitSize));
   }
 
   protected:
diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh
new file mode 100644
index 0000000..db04b3e
--- /dev/null
+++ b/src/hb-ot-cff-common.hh
@@ -0,0 +1,711 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_OT_CFF_COMMON_HH
+#define HB_OT_CFF_COMMON_HH
+
+#include "hb-open-type.hh"
+#include "hb-ot-layout-common.hh"
+#include "hb-cff-interp-dict-common.hh"
+#include "hb-subset-plan.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+#define CFF_UNDEF_CODE  0xFFFFFFFF
+
+/* utility macro */
+template<typename Type>
+static inline const Type& StructAtOffsetOrNull(const void *P, unsigned int offset)
+{ return offset? (* reinterpret_cast<const Type*> ((const char *) P + offset)): Null(Type); }
+
+inline unsigned int calcOffSize(unsigned int dataSize)
+{
+  unsigned int size = 1;
+  unsigned int offset = dataSize + 1;
+  while ((offset & ~0xFF) != 0)
+  {
+    size++;
+    offset >>= 8;
+  }
+  /* format does not support size > 4; caller should handle it as an error */
+  return size;
+}
+
+struct code_pair
+{
+  hb_codepoint_t  code;
+  hb_codepoint_t  glyph;
+};
+
+typedef hb_vector_t<char, 1> StrBuff;
+struct StrBuffArray : hb_vector_t<StrBuff>
+{
+  void fini () { SUPER::fini_deep (); }
+
+  unsigned int total_size () const
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < len; i++)
+      size += (*this)[i].len;
+    return size;
+  }
+
+  private:
+  typedef hb_vector_t<StrBuff> SUPER;
+};
+
+/* CFF INDEX */
+template <typename COUNT>
+struct CFFIndex
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely ((count.sanitize (c) && count == 0) || /* empty INDEX */
+			  (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
+			   c->check_array (offsets, offSize, count + 1) &&
+			   c->check_array ((const HBUINT8*)data_base (), 1, max_offset () - 1))));
+  }
+
+  static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count)
+  { return offSize * (count + 1); }
+
+  unsigned int offset_array_size () const
+  { return calculate_offset_array_size (offSize, count); }
+
+  static unsigned int calculate_serialized_size (unsigned int offSize, unsigned int count, unsigned int dataSize)
+  {
+    if (count == 0)
+      return COUNT::static_size;
+    else
+      return min_size + calculate_offset_array_size (offSize, count) + dataSize;
+  }
+
+  bool serialize (hb_serialize_context_t *c, const CFFIndex &src)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned int size = src.get_size ();
+    CFFIndex *dest = c->allocate_size<CFFIndex> (size);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, &src, size);
+    return_trace (true);
+  }
+
+  bool serialize (hb_serialize_context_t *c,
+		  unsigned int offSize_,
+		  const ByteStrArray &byteArray)
+  {
+    TRACE_SERIALIZE (this);
+    if (byteArray.len == 0)
+    {
+      COUNT *dest = c->allocate_min<COUNT> ();
+      if (unlikely (dest == nullptr)) return_trace (false);
+      dest->set (0);
+    }
+    else
+    {
+      /* serialize CFFIndex header */
+      if (unlikely (!c->extend_min (*this))) return_trace (false);
+      this->count.set (byteArray.len);
+      this->offSize.set (offSize_);
+      if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (byteArray.len + 1))))
+	return_trace (false);
+
+      /* serialize indices */
+      unsigned int  offset = 1;
+      unsigned int  i = 0;
+      for (; i < byteArray.len; i++)
+      {
+	set_offset_at (i, offset);
+	offset += byteArray[i].get_size ();
+      }
+      set_offset_at (i, offset);
+
+      /* serialize data */
+      for (unsigned int i = 0; i < byteArray.len; i++)
+      {
+	ByteStr  *dest = c->start_embed<ByteStr> ();
+	if (unlikely (dest == nullptr ||
+		      !dest->serialize (c, byteArray[i])))
+	  return_trace (false);
+      }
+    }
+    return_trace (true);
+  }
+
+  bool serialize (hb_serialize_context_t *c,
+		  unsigned int offSize_,
+		  const StrBuffArray &buffArray)
+  {
+    ByteStrArray  byteArray;
+    byteArray.init ();
+    byteArray.resize (buffArray.len);
+    for (unsigned int i = 0; i < byteArray.len; i++)
+    {
+      byteArray[i] = ByteStr (buffArray[i].arrayZ (), buffArray[i].len);
+    }
+    bool result = this->serialize (c, offSize_, byteArray);
+    byteArray.fini ();
+    return result;
+  }
+
+  void set_offset_at (unsigned int index, unsigned int offset)
+  {
+    HBUINT8 *p = offsets + offSize * index + offSize;
+    unsigned int size = offSize;
+    for (; size; size--)
+    {
+      --p;
+      p->set (offset & 0xFF);
+      offset >>= 8;
+    }
+  }
+
+  unsigned int offset_at (unsigned int index) const
+  {
+    assert (index <= count);
+    const HBUINT8 *p = offsets + offSize * index;
+    unsigned int size = offSize;
+    unsigned int offset = 0;
+    for (; size; size--)
+      offset = (offset << 8) + *p++;
+    return offset;
+  }
+
+  unsigned int length_at (unsigned int index) const
+  {
+	if (likely ((offset_at (index + 1) >= offset_at (index)) &&
+		    (offset_at (index + 1) <= offset_at (count))))
+	  return offset_at (index + 1) - offset_at (index);
+	else
+	  return 0;
+  }
+
+  const char *data_base () const
+  { return (const char *)this + min_size + offset_array_size (); }
+
+  unsigned int data_size () const { return HBINT8::static_size; }
+
+  ByteStr operator [] (unsigned int index) const
+  {
+    if (likely (index < count))
+      return ByteStr (data_base () + offset_at (index) - 1, length_at (index));
+    else
+      return Null(ByteStr);
+  }
+
+  unsigned int get_size () const
+  {
+    if (this != &Null(CFFIndex))
+    {
+      if (count > 0)
+	return min_size + offset_array_size () + (offset_at (count) - 1);
+      else
+	return count.static_size;  /* empty CFFIndex contains count only */
+    }
+    else
+      return 0;
+  }
+
+  protected:
+  unsigned int max_offset () const
+  {
+    unsigned int max = 0;
+    for (unsigned int i = 0; i < count + 1u; i++)
+    {
+      unsigned int off = offset_at (i);
+      if (off > max) max = off;
+    }
+    return max;
+  }
+
+  public:
+  COUNT     count;	/* Number of object data. Note there are (count+1) offsets */
+  HBUINT8   offSize;      /* The byte size of each offset in the offsets array. */
+  HBUINT8   offsets[VAR]; /* The array of (count + 1) offsets into objects array (1-base). */
+  /* HBUINT8 data[VAR];      Object data */
+  public:
+  DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
+};
+
+template <typename COUNT, typename TYPE>
+struct CFFIndexOf : CFFIndex<COUNT>
+{
+  const ByteStr operator [] (unsigned int index) const
+  {
+    if (likely (index < CFFIndex<COUNT>::count))
+      return ByteStr (CFFIndex<COUNT>::data_base () + CFFIndex<COUNT>::offset_at (index) - 1, CFFIndex<COUNT>::length_at (index));
+    return Null(ByteStr);
+  }
+
+  template <typename DATA, typename PARAM1, typename PARAM2>
+  bool serialize (hb_serialize_context_t *c,
+		  unsigned int offSize_,
+		  const DATA *dataArray,
+		  unsigned int dataArrayLen,
+		  const hb_vector_t<unsigned int> &dataSizeArray,
+		  const PARAM1 &param1,
+		  const PARAM2 &param2)
+  {
+    TRACE_SERIALIZE (this);
+    /* serialize CFFIndex header */
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    this->count.set (dataArrayLen);
+    this->offSize.set (offSize_);
+    if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1))))
+      return_trace (false);
+
+    /* serialize indices */
+    unsigned int  offset = 1;
+    unsigned int  i = 0;
+    for (; i < dataArrayLen; i++)
+    {
+      CFFIndex<COUNT>::set_offset_at (i, offset);
+      offset += dataSizeArray[i];
+    }
+    CFFIndex<COUNT>::set_offset_at (i, offset);
+
+    /* serialize data */
+    for (unsigned int i = 0; i < dataArrayLen; i++)
+    {
+      TYPE  *dest = c->start_embed<TYPE> ();
+      if (unlikely (dest == nullptr ||
+		    !dest->serialize (c, dataArray[i], param1, param2)))
+	return_trace (false);
+    }
+    return_trace (true);
+  }
+
+  /* in parallel to above */
+  template <typename DATA, typename PARAM>
+  static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
+						 const DATA *dataArray,
+						 unsigned int dataArrayLen,
+						 hb_vector_t<unsigned int> &dataSizeArray, /* OUT */
+						 const PARAM &param)
+  {
+    /* determine offset size */
+    unsigned int  totalDataSize = 0;
+    for (unsigned int i = 0; i < dataArrayLen; i++)
+    {
+      unsigned int dataSize = TYPE::calculate_serialized_size (dataArray[i], param);
+      dataSizeArray[i] = dataSize;
+      totalDataSize += dataSize;
+    }
+    offSize_ = calcOffSize (totalDataSize);
+
+    return CFFIndex<COUNT>::calculate_serialized_size (offSize_, dataArrayLen, totalDataSize);
+  }
+};
+
+/* Top Dict, Font Dict, Private Dict */
+struct Dict : UnsizedByteStr
+{
+  template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
+  bool serialize (hb_serialize_context_t *c,
+		  const DICTVAL &dictval,
+		  OP_SERIALIZER& opszr,
+		  PARAM& param)
+  {
+    TRACE_SERIALIZE (this);
+    for (unsigned int i = 0; i < dictval.get_count (); i++)
+    {
+      if (unlikely (!opszr.serialize (c, dictval[i], param)))
+	return_trace (false);
+    }
+    return_trace (true);
+  }
+
+  /* in parallel to above */
+  template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
+  static unsigned int calculate_serialized_size (const DICTVAL &dictval,
+						 OP_SERIALIZER& opszr,
+						 PARAM& param)
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < dictval.get_count (); i++)
+      size += opszr.calculate_serialized_size (dictval[i], param);
+    return size;
+  }
+
+  template <typename DICTVAL, typename OP_SERIALIZER>
+  static unsigned int calculate_serialized_size (const DICTVAL &dictval,
+						 OP_SERIALIZER& opszr)
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < dictval.get_count (); i++)
+      size += opszr.calculate_serialized_size (dictval[i]);
+    return size;
+  }
+
+  template <typename INTTYPE, int minVal, int maxVal>
+  static bool serialize_int_op (hb_serialize_context_t *c, OpCode op, int value, OpCode intOp)
+  {
+    // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation
+    if (/*unlikely*/ (!serialize_int<INTTYPE, minVal, maxVal> (c, intOp, value)))
+      return false;
+
+    TRACE_SERIALIZE (this);
+    /* serialize the opcode */
+    HBUINT8 *p = c->allocate_size<HBUINT8> (OpCode_Size (op));
+    if (unlikely (p == nullptr)) return_trace (false);
+    if (Is_OpCode_ESC (op))
+    {
+      p->set (OpCode_escape);
+      op = Unmake_OpCode_ESC (op);
+      p++;
+    }
+    p->set (op);
+    return_trace (true);
+  }
+
+  static bool serialize_uint4_op (hb_serialize_context_t *c, OpCode op, int value)
+  { return serialize_int_op<HBUINT32, 0, 0x7FFFFFFF> (c, op, value, OpCode_longintdict); }
+
+  static bool serialize_uint2_op (hb_serialize_context_t *c, OpCode op, int value)
+  { return serialize_int_op<HBUINT16, 0, 0x7FFF> (c, op, value, OpCode_shortint); }
+
+  static bool serialize_offset4_op (hb_serialize_context_t *c, OpCode op, int value)
+  {
+    return serialize_uint4_op (c, op, value);
+  }
+
+  static bool serialize_offset2_op (hb_serialize_context_t *c, OpCode op, int value)
+  {
+    return serialize_uint2_op (c, op, value);
+  }
+};
+
+struct TopDict : Dict {};
+struct FontDict : Dict {};
+struct PrivateDict : Dict {};
+
+struct TableInfo
+{
+  void init () { offSize = offset = size = 0; }
+
+  unsigned int    offset;
+  unsigned int    size;
+  unsigned int    offSize;
+};
+
+/* used to remap font index or SID from fullset to subset.
+ * set to CFF_UNDEF_CODE if excluded from subset */
+struct Remap : hb_vector_t<hb_codepoint_t>
+{
+  void init () { SUPER::init (); }
+
+  void fini () { SUPER::fini (); }
+
+  bool reset (unsigned int size)
+  {
+    if (unlikely (!SUPER::resize (size)))
+      return false;
+    for (unsigned int i = 0; i < len; i++)
+      (*this)[i] = CFF_UNDEF_CODE;
+    count = 0;
+    return true;
+  }
+
+  bool identity (unsigned int size)
+  {
+    if (unlikely (!SUPER::resize (size)))
+      return false;
+    unsigned int i;
+    for (i = 0; i < len; i++)
+      (*this)[i] = i;
+    count = i;
+    return true;
+  }
+
+  bool excludes (hb_codepoint_t id) const
+  { return (id < len) && ((*this)[id] == CFF_UNDEF_CODE); }
+
+  bool includes (hb_codepoint_t id) const
+  { return !excludes (id); }
+
+  unsigned int add (unsigned int i)
+  {
+    if ((*this)[i] == CFF_UNDEF_CODE)
+      (*this)[i] = count++;
+    return (*this)[i];
+  }
+
+  hb_codepoint_t get_count () const { return count; }
+
+  protected:
+  hb_codepoint_t  count;
+
+  private:
+  typedef hb_vector_t<hb_codepoint_t> SUPER;
+};
+
+template <typename COUNT>
+struct FDArray : CFFIndexOf<COUNT, FontDict>
+{
+  /* used by CFF1 */
+  template <typename DICTVAL, typename OP_SERIALIZER>
+  bool serialize (hb_serialize_context_t *c,
+		  unsigned int offSize_,
+		  const hb_vector_t<DICTVAL> &fontDicts,
+		  OP_SERIALIZER& opszr)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    this->count.set (fontDicts.len);
+    this->offSize.set (offSize_);
+    if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (fontDicts.len + 1))))
+      return_trace (false);
+
+    /* serialize font dict offsets */
+    unsigned int  offset = 1;
+    unsigned int fid = 0;
+    for (; fid < fontDicts.len; fid++)
+    {
+      CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
+      offset += FontDict::calculate_serialized_size (fontDicts[fid], opszr);
+    }
+    CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
+
+    /* serialize font dicts */
+    for (unsigned int i = 0; i < fontDicts.len; i++)
+    {
+      FontDict *dict = c->start_embed<FontDict> ();
+      if (unlikely (!dict->serialize (c, fontDicts[i], opszr, fontDicts[i])))
+	return_trace (false);
+    }
+    return_trace (true);
+  }
+
+  /* used by CFF2 */
+  template <typename DICTVAL, typename OP_SERIALIZER>
+  bool serialize (hb_serialize_context_t *c,
+		  unsigned int offSize_,
+		  const hb_vector_t<DICTVAL> &fontDicts,
+		  unsigned int fdCount,
+		  const Remap &fdmap,
+		  OP_SERIALIZER& opszr,
+		  const hb_vector_t<TableInfo> &privateInfos)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    this->count.set (fdCount);
+    this->offSize.set (offSize_);
+    if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (fdCount + 1))))
+      return_trace (false);
+
+    /* serialize font dict offsets */
+    unsigned int  offset = 1;
+    unsigned int  fid = 0;
+    for (unsigned i = 0; i < fontDicts.len; i++)
+      if (fdmap.includes (i))
+      {
+	CFFIndexOf<COUNT, FontDict>::set_offset_at (fid++, offset);
+	offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
+      }
+    CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
+
+    /* serialize font dicts */
+    for (unsigned int i = 0; i < fontDicts.len; i++)
+      if (fdmap.includes (i))
+      {
+	FontDict *dict = c->start_embed<FontDict> ();
+	if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[i]])))
+	  return_trace (false);
+      }
+    return_trace (true);
+  }
+
+  /* in parallel to above */
+  template <typename OP_SERIALIZER, typename DICTVAL>
+  static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
+						 const hb_vector_t<DICTVAL> &fontDicts,
+						 unsigned int fdCount,
+						 const Remap &fdmap,
+						 OP_SERIALIZER& opszr)
+  {
+    unsigned int dictsSize = 0;
+    for (unsigned int i = 0; i < fontDicts.len; i++)
+      if (fdmap.includes (i))
+	dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
+
+    offSize_ = calcOffSize (dictsSize);
+    return CFFIndex<COUNT>::calculate_serialized_size (offSize_, fdCount, dictsSize);
+  }
+};
+
+/* FDSelect */
+struct FDSelect0 {
+  bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!(c->check_struct (this))))
+      return_trace (false);
+    for (unsigned int i = 0; i < c->get_num_glyphs (); i++)
+      if (unlikely (!fds[i].sanitize (c)))
+	return_trace (false);
+
+    return_trace (true);
+  }
+
+  hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    return (hb_codepoint_t)fds[glyph];
+  }
+
+  unsigned int get_size (unsigned int num_glyphs) const
+  { return HBUINT8::static_size * num_glyphs; }
+
+  HBUINT8     fds[VAR];
+
+  DEFINE_SIZE_MIN (1);
+};
+
+template <typename GID_TYPE, typename FD_TYPE>
+struct FDSelect3_4_Range {
+  bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) && (first < c->get_num_glyphs ()) && (fd < fdcount)));
+  }
+
+  GID_TYPE    first;
+  FD_TYPE     fd;
+
+  DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size);
+};
+
+template <typename GID_TYPE, typename FD_TYPE>
+struct FDSelect3_4 {
+  unsigned int get_size () const
+  { return GID_TYPE::static_size * 2 + FDSelect3_4_Range<GID_TYPE, FD_TYPE>::static_size * nRanges; }
+
+  bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!(c->check_struct (this) && (nRanges > 0) && (ranges[0].first == 0))))
+      return_trace (false);
+
+    for (unsigned int i = 0; i < nRanges; i++)
+    {
+      if (unlikely (!ranges[i].sanitize (c, fdcount)))
+	return_trace (false);
+      if ((0 < i) && unlikely (ranges[i - 1].first >= ranges[i].first))
+	return_trace (false);
+    }
+    if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ())))
+      return_trace (false);
+
+    return_trace (true);
+  }
+
+  hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    unsigned int i;
+    for (i = 1; i < nRanges; i++)
+      if (glyph < ranges[i].first)
+	break;
+
+    return (hb_codepoint_t)ranges[i - 1].fd;
+  }
+
+  GID_TYPE &sentinel ()  { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
+  const GID_TYPE &sentinel () const  { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
+
+  GID_TYPE	 nRanges;
+  FDSelect3_4_Range<GID_TYPE, FD_TYPE>  ranges[VAR];
+  /* GID_TYPE sentinel */
+
+  DEFINE_SIZE_ARRAY (GID_TYPE::static_size, ranges);
+};
+
+typedef FDSelect3_4<HBUINT16, HBUINT8> FDSelect3;
+typedef FDSelect3_4_Range<HBUINT16, HBUINT8> FDSelect3_Range;
+
+struct FDSelect {
+  bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+  {
+    TRACE_SANITIZE (this);
+
+    return_trace (likely (c->check_struct (this) && (format == 0 || format == 3) &&
+			  (format == 0)?
+			  u.format0.sanitize (c, fdcount):
+			  u.format3.sanitize (c, fdcount)));
+  }
+
+  bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned int size = src.get_size (num_glyphs);
+    FDSelect *dest = c->allocate_size<FDSelect> (size);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, &src, size);
+    return_trace (true);
+  }
+
+  unsigned int calculate_serialized_size (unsigned int num_glyphs) const
+  { return get_size (num_glyphs); }
+
+  unsigned int get_size (unsigned int num_glyphs) const
+  {
+    unsigned int size = format.static_size;
+    if (format == 0)
+      size += u.format0.get_size (num_glyphs);
+    else
+      size += u.format3.get_size ();
+    return size;
+  }
+
+  hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    if (this == &Null(FDSelect))
+      return 0;
+    if (format == 0)
+      return u.format0.get_fd (glyph);
+    else
+      return u.format3.get_fd (glyph);
+  }
+
+  HBUINT8       format;
+  union {
+    FDSelect0   format0;
+    FDSelect3   format3;
+  } u;
+
+  DEFINE_SIZE_MIN (1);
+};
+
+template <typename COUNT>
+struct Subrs : CFFIndex<COUNT>
+{
+  typedef COUNT count_type;
+  typedef CFFIndex<COUNT> SUPER;
+};
+
+} /* namespace CFF */
+
+#endif /* HB_OT_CFF_COMMON_HH */
diff --git a/src/hb-ot-cff1-table.cc b/src/hb-ot-cff1-table.cc
new file mode 100644
index 0000000..51cecb6
--- /dev/null
+++ b/src/hb-ot-cff1-table.cc
@@ -0,0 +1,385 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#include "hb-ot-cff1-table.hh"
+#include "hb-cff1-interp-cs.hh"
+
+using namespace CFF;
+
+/* SID to code */
+static const uint8_t standard_encoding_to_code [] =
+{
+    0,   32,   33,   34,   35,   36,   37,   38,  39,   40,   41,   42,   43,   44,   45,   46,
+   47,   48,   49,   50,   51,   52,   53,   54,  55,   56,   57,   58,   59,   60,   61,   62,
+   63,   64,   65,   66,   67,   68,   69,   70,  71,   72,   73,   74,   75,   76,   77,   78,
+   79,   80,   81,   82,   83,   84,   85,   86,  87,   88,   89,   90,   91,   92,   93,   94,
+   95,   96,   97,   98,   99,  100,  101,  102, 103,  104,  105,  106,  107,  108,  109,  110,
+  111,  112,  113,  114,  115,  116,  117,  118, 119,  120,  121,  122,  123,  124,  125,  126,
+  161,  162,  163,  164,  165,  166,  167,  168, 169,  170,  171,  172,  173,  174,  175,  177,
+  178,  179,  180,  182,  183,  184,  185,  186, 187,  188,  189,  191,  193,  194,  195,  196,
+  197,  198,  199,  200,  202,  203,  205,  206, 207,  208,  225,  227,  232,  233,  234,  235,
+  241,  245,  248,  249,  250,  251
+};
+
+/* SID to code */
+static const uint8_t expert_encoding_to_code [] =
+{
+    0,   32,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,   44,   45,   46,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,   58,   59,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,   47,    0,    0,    0,    0,    0,    0,    0,    0,    0,   87,   88,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,  201,    0,    0,    0,    0,  189,    0,    0,  188,    0,
+    0,    0,    0,  190,  202,    0,    0,    0,    0,  203,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,   33,   34,   36,   37,   38,   39,   40,   41,   42,   43,   48,
+   49,   50,   51,   52,   53,   54,   55,   56,   57,   60,   61,   62,   63,   65,   66,   67,
+   68,   69,   73,   76,   77,   78,   79,   82,   83,   84,   86,   89,   90,   91,   93,   94,
+   95,   96,   97,   98,   99,  100,  101,  102,  103,  104,  105,  106,  107,  108,  109,  110,
+  111,  112,  113,  114,  115,  116,  117,  118,  119,  120,  121,  122,  123,  124,  125,  126,
+  161,  162,  163,  166,  167,  168,  169,  170,  172,  175,  178,  179,  182,  183,  184,  191,
+  192,  193,  194,  195,  196,  197,  200,  204,  205,  206,  207,  208,  209,  210,  211,  212,
+  213,  214,  215,  216,  217,  218,  219,  220,  221,  222,  223,  224,  225,  226,  227,  228,
+  229,  230,  231,  232,  233,  234,  235,  236,  237,  238,  239,  240,  241,  242,  243,  244,
+  245,  246,  247,  248,  249,  250,  251,  252,  253,  254,  255
+};
+
+/* glyph ID to SID */
+static const uint16_t expert_charset_to_sid [] =
+{
+    0,    1,  229,  230,  231,  232,  233,  234,  235,  236,  237,  238,   13,   14,   15,   99,
+  239,  240,  241,  242,  243,  244,  245,  246,  247,  248,   27,   28,  249,  250,  251,  252,
+  253,  254,  255,  256,  257,  258,  259,  260,  261,  262,  263,  264,  265,  266,  109,  110,
+  267,  268,  269,  270,  271,  272,  273,  274,  275,  276,  277,  278,  279,  280,  281,  282,
+  283,  284,  285,  286,  287,  288,  289,  290,  291,  292,  293,  294,  295,  296,  297,  298,
+  299,  300,  301,  302,  303,  304,  305,  306,  307,  308,  309,  310,  311,  312,  313,  314,
+  315,  316,  317,  318,  158,  155,  163,  319,  320,  321,  322,  323,  324,  325,  326,  150,
+  164,  169,  327,  328,  329,  330,  331,  332,  333,  334,  335,  336,  337,  338,  339,  340,
+  341,  342,  343,  344,  345,  346,  347,  348,  349,  350,  351,  352,  353,  354,  355,  356,
+  357,  358,  359,  360,  361,  362,  363,  364,  365,  366,  367,  368,  369,  370,  371,  372,
+  373,  374,  375,  376,  377,  378
+};
+
+/* glyph ID to SID */
+static const uint16_t expert_subset_charset_to_sid [] =
+{
+    0,    1,  231,  232,  235,  236,  237,  238,   13,   14,   15,   99,  239,  240,  241,  242,
+  243,  244,  245,  246,  247,  248,   27,   28,  249,  250,  251,  253,  254,  255,  256,  257,
+  258,  259,  260,  261,  262,  263,  264,  265,  266,  109,  110,  267,  268,  269,  270,  272,
+  300,  301,  302,  305,  314,  315,  158,  155,  163,  320,  321,  322,  323,  324,  325,  326,
+  150,  164,  169,  327,  328,  329,  330,  331,  332,  333,  334,  335,  336,  337,  338,  339,
+  340,  341,  342,  343,  344,  345,  346
+};
+
+/* code to SID */
+static const uint8_t standard_encoding_to_sid [] =
+{
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13,   14,   15,   16,
+    17,  18,   19,   20,   21,   22,   23,   24,   25,   26,   27,   28,   29,   30,   31,   32,
+    33,  34,   35,   36,   37,   38,   39,   40,   41,   42,   43,   44,   45,   46,   47,   48,
+    49,  50,   51,   52,   53,   54,   55,   56,   57,   58,   59,   60,   61,   62,   63,   64,
+    65,  66,   67,   68,   69,   70,   71,   72,   73,   74,   75,   76,   77,   78,   79,   80,
+    81,  82,   83,   84,   85,   86,   87,   88,   89,   90,   91,   92,   93,   94,   95,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,   96,   97,   98,   99,  100,  101,  102,  103,  104,  105,  106,  107,  108,  109,  110,
+    0,  111,  112,  113,  114,    0,  115,  116,  117,  118,  119,  120,  121,  122,    0,  123,
+    0,  124,  125,  126,  127,  128,  129,  130,  131,    0,  132,  133,    0,  134,  135,  136,
+  137,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,   138,   0,  139,    0,    0,    0,    0,  140,  141,  142,  143,    0,    0,    0,    0,
+    0,   144,   0,    0,    0,  145,    0,    0,  146,  147,  148,  149,    0,    0,    0,    0
+};
+
+hb_codepoint_t OT::cff1::lookup_standard_encoding_for_code (hb_codepoint_t sid)
+{
+  if (sid < ARRAY_LENGTH (standard_encoding_to_code))
+    return (hb_codepoint_t)standard_encoding_to_code[sid];
+  else
+    return 0;
+}
+
+hb_codepoint_t OT::cff1::lookup_expert_encoding_for_code (hb_codepoint_t sid)
+{
+  if (sid < ARRAY_LENGTH (expert_encoding_to_code))
+    return (hb_codepoint_t)expert_encoding_to_code[sid];
+  else
+    return 0;
+}
+
+hb_codepoint_t OT::cff1::lookup_expert_charset_for_sid (hb_codepoint_t glyph)
+{
+  if (glyph < ARRAY_LENGTH (expert_charset_to_sid))
+    return (hb_codepoint_t)expert_charset_to_sid[glyph];
+  else
+    return 0;
+}
+
+hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph)
+{
+  if (glyph < ARRAY_LENGTH (expert_subset_charset_to_sid))
+    return (hb_codepoint_t)expert_subset_charset_to_sid[glyph];
+  else
+    return 0;
+}
+
+hb_codepoint_t OT::cff1::lookup_standard_encoding_for_sid (hb_codepoint_t code)
+{
+  if (code < ARRAY_LENGTH (standard_encoding_to_sid))
+    return (hb_codepoint_t)standard_encoding_to_sid[code];
+  else
+    return CFF_UNDEF_SID;
+}
+
+struct Bounds
+{
+  void init ()
+  {
+    min.set_int (0x7FFFFFFF, 0x7FFFFFFF);
+    max.set_int (-0x80000000, -0x80000000);
+  }
+
+  void update (const Point &pt)
+  {
+    if (pt.x < min.x) min.x = pt.x;
+    if (pt.x > max.x) max.x = pt.x;
+    if (pt.y < min.y) min.y = pt.y;
+    if (pt.y > max.y) max.y = pt.y;
+  }
+
+  void merge (const Bounds &b)
+  {
+    if (empty ())
+      *this = b;
+    else if (!b.empty ())
+    {
+      if (b.min.x < min.x) min.x = b.min.x;
+      if (b.max.x > max.x) max.x = b.max.x;
+      if (b.min.y < min.y) min.y = b.min.y;
+      if (b.max.y > max.y) max.y = b.max.y;
+    }
+  }
+
+  void offset (const Point &delta)
+  {
+    if (!empty ())
+    {
+      min.move (delta);
+      max.move (delta);
+    }
+  }
+
+  bool empty () const
+  { return (min.x >= max.x) || (min.y >= max.y); }
+
+  Point min;
+  Point max;
+};
+
+struct ExtentsParam
+{
+  void init (const OT::cff1::accelerator_t *_cff)
+  {
+    path_open = false;
+    cff = _cff;
+    bounds.init ();
+  }
+
+  void start_path ()         { path_open = true; }
+  void end_path ()           { path_open = false; }
+  bool is_path_open () const { return path_open; }
+
+  bool    path_open;
+  Bounds  bounds;
+
+  const OT::cff1::accelerator_t *cff;
+};
+
+struct CFF1PathProcs_Extents : PathProcs<CFF1PathProcs_Extents, CFF1CSInterpEnv, ExtentsParam>
+{
+  static void moveto (CFF1CSInterpEnv &env, ExtentsParam& param, const Point &pt)
+  {
+    param.end_path ();
+    env.moveto (pt);
+  }
+
+  static void line (CFF1CSInterpEnv &env, ExtentsParam& param, const Point &pt1)
+  {
+    if (!param.is_path_open ())
+    {
+      param.start_path ();
+      param.bounds.update (env.get_pt ());
+    }
+    env.moveto (pt1);
+    param.bounds.update (env.get_pt ());
+  }
+
+  static void curve (CFF1CSInterpEnv &env, ExtentsParam& param, const Point &pt1, const Point &pt2, const Point &pt3)
+  {
+    if (!param.is_path_open ())
+    {
+      param.start_path ();
+      param.bounds.update (env.get_pt ());
+    }
+    /* include control points */
+    param.bounds.update (pt1);
+    param.bounds.update (pt2);
+    env.moveto (pt3);
+    param.bounds.update (env.get_pt ());
+  }
+};
+
+static bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, Bounds &bounds, bool in_seac=false);
+
+struct CFF1CSOpSet_Extents : CFF1CSOpSet<CFF1CSOpSet_Extents, ExtentsParam, CFF1PathProcs_Extents>
+{
+  static void process_seac (CFF1CSInterpEnv &env, ExtentsParam& param)
+  {
+    unsigned int  n = env.argStack.get_count ();
+    Point delta;
+    delta.x = env.argStack[n-4];
+    delta.y = env.argStack[n-3];
+    hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ());
+    hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ());
+
+    Bounds  base_bounds, accent_bounds;
+    if (likely (!env.in_seac && base && accent
+	       && _get_bounds (param.cff, base, base_bounds, true)
+	       && _get_bounds (param.cff, accent, accent_bounds, true)))
+    {
+      param.bounds.merge (base_bounds);
+      accent_bounds.offset (delta);
+      param.bounds.merge (accent_bounds);
+    }
+    else
+      env.set_error ();
+  }
+};
+
+bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, Bounds &bounds, bool in_seac)
+{
+  bounds.init ();
+  if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false;
+
+  unsigned int fd = cff->fdSelect->get_fd (glyph);
+  CFF1CSInterpreter<CFF1CSOpSet_Extents, ExtentsParam> interp;
+  const ByteStr str = (*cff->charStrings)[glyph];
+  interp.env.init (str, *cff, fd);
+  interp.env.set_in_seac (in_seac);
+  ExtentsParam  param;
+  param.init (cff);
+  if (unlikely (!interp.interpret (param))) return false;
+  bounds = param.bounds;
+  return true;
+}
+
+bool OT::cff1::accelerator_t::get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
+{
+  Bounds  bounds;
+
+  if (!_get_bounds (this, glyph, bounds))
+    return false;
+
+  if (bounds.min.x >= bounds.max.x)
+  {
+    extents->width = 0;
+    extents->x_bearing = 0;
+  }
+  else
+  {
+    extents->x_bearing = (int32_t)bounds.min.x.floor ();
+    extents->width = (int32_t)bounds.max.x.ceil () - extents->x_bearing;
+  }
+  if (bounds.min.y >= bounds.max.y)
+  {
+    extents->height = 0;
+    extents->y_bearing = 0;
+  }
+  else
+  {
+    extents->y_bearing = (int32_t)bounds.max.y.ceil ();
+    extents->height = (int32_t)bounds.min.y.floor () - extents->y_bearing;
+  }
+
+  return true;
+}
+
+struct GetSeacParam
+{
+  void init (const OT::cff1::accelerator_t *_cff)
+  {
+    cff = _cff;
+    base = 0;
+    accent = 0;
+  }
+
+  bool has_seac () const { return base && accent; }
+
+  const OT::cff1::accelerator_t *cff;
+  hb_codepoint_t  base;
+  hb_codepoint_t  accent;
+};
+
+struct CFF1CSOpSet_Seac : CFF1CSOpSet<CFF1CSOpSet_Seac, GetSeacParam>
+{
+  static void process_seac (CFF1CSInterpEnv &env, GetSeacParam& param)
+  {
+    unsigned int  n = env.argStack.get_count ();
+    hb_codepoint_t  base_char = (hb_codepoint_t)env.argStack[n-2].to_int ();
+    hb_codepoint_t  accent_char = (hb_codepoint_t)env.argStack[n-1].to_int ();
+
+    param.base = param.cff->std_code_to_glyph (base_char);
+    param.accent = param.cff->std_code_to_glyph (accent_char);
+  }
+};
+
+bool OT::cff1::accelerator_t::get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const
+{
+  if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
+
+  unsigned int fd = fdSelect->get_fd (glyph);
+  CFF1CSInterpreter<CFF1CSOpSet_Seac, GetSeacParam> interp;
+  const ByteStr str = (*charStrings)[glyph];
+  interp.env.init (str, *this, fd);
+  GetSeacParam  param;
+  param.init (this);
+  if (unlikely (!interp.interpret (param))) return false;
+
+  if (param.has_seac ())
+  {
+    *base = param.base;
+    *accent = param.accent;
+    return true;
+  }
+  return false;
+}
diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh
new file mode 100644
index 0000000..72dc0ef
--- /dev/null
+++ b/src/hb-ot-cff1-table.hh
@@ -0,0 +1,1300 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_OT_CFF1_TABLE_HH
+#define HB_OT_CFF1_TABLE_HH
+
+#include "hb-ot-head-table.hh"
+#include "hb-ot-cff-common.hh"
+#include "hb-subset-cff1.hh"
+
+namespace CFF {
+
+/*
+ * CFF -- Compact Font Format (CFF)
+ * http://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf
+ */
+#define HB_OT_TAG_cff1 HB_TAG('C','F','F',' ')
+
+#define CFF_UNDEF_SID   CFF_UNDEF_CODE
+
+enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 };
+enum CharsetID { ISOAdobeCharset = 0, ExpertCharset = 1, ExpertSubsetCharset = 2 };
+
+typedef CFFIndex<HBUINT16>  CFF1Index;
+template <typename Type> struct CFF1IndexOf : CFFIndexOf<HBUINT16, Type> {};
+
+typedef CFFIndex<HBUINT16> CFF1Index;
+typedef CFF1Index          CFF1CharStrings;
+typedef FDArray<HBUINT16>  CFF1FDArray;
+typedef Subrs<HBUINT16>    CFF1Subrs;
+
+struct CFF1FDSelect : FDSelect {};
+
+/* Encoding */
+struct Encoding0 {
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && codes[nCodes - 1].sanitize (c));
+  }
+
+  hb_codepoint_t get_code (hb_codepoint_t glyph) const
+  {
+    assert (glyph > 0);
+    glyph--;
+    if (glyph < nCodes)
+    {
+      return (hb_codepoint_t)codes[glyph];
+    }
+    else
+      return CFF_UNDEF_CODE;
+  }
+
+  unsigned int get_size () const
+  { return HBUINT8::static_size * (nCodes + 1); }
+
+  HBUINT8     nCodes;
+  HBUINT8     codes[VAR];
+
+  DEFINE_SIZE_ARRAY(1, codes);
+};
+
+struct Encoding1_Range {
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  HBUINT8   first;
+  HBUINT8   nLeft;
+
+  DEFINE_SIZE_STATIC (2);
+};
+
+struct Encoding1 {
+  unsigned int get_size () const
+  { return HBUINT8::static_size + Encoding1_Range::static_size * nRanges; }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && ((nRanges == 0) || (ranges[nRanges - 1]).sanitize (c)));
+  }
+
+  hb_codepoint_t get_code (hb_codepoint_t glyph) const
+  {
+    assert (glyph > 0);
+    glyph--;
+    for (unsigned int i = 0; i < nRanges; i++)
+    {
+      if (glyph <= ranges[i].nLeft)
+      {
+	return (hb_codepoint_t)ranges[i].first + glyph;
+      }
+      glyph -= (ranges[i].nLeft + 1);
+    }
+    return CFF_UNDEF_CODE;
+  }
+
+  HBUINT8	   nRanges;
+  Encoding1_Range   ranges[VAR];
+
+  DEFINE_SIZE_ARRAY (1, ranges);
+};
+
+struct SuppEncoding {
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  HBUINT8   code;
+  HBUINT16  glyph;
+
+  DEFINE_SIZE_STATIC (3);
+};
+
+struct CFF1SuppEncData {
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && ((nSups == 0) || (supps[nSups - 1]).sanitize (c)));
+  }
+
+  void get_codes (hb_codepoint_t sid, hb_vector_t<hb_codepoint_t> &codes) const
+  {
+    for (unsigned int i = 0; i < nSups; i++)
+      if (sid == supps[i].glyph)
+	codes.push (supps[i].code);
+  }
+
+  unsigned int get_size () const
+  { return HBUINT8::static_size + SuppEncoding::static_size * nSups; }
+
+  HBUINT8	 nSups;
+  SuppEncoding   supps[VAR];
+
+  DEFINE_SIZE_ARRAY (1, supps);
+};
+
+struct Encoding {
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+    unsigned int fmt = format & 0x7F;
+    if (unlikely (fmt > 1))
+      return_trace (false);
+    if (unlikely (!((fmt == 0)? u.format0.sanitize (c): u.format1.sanitize (c))))
+      return_trace (false);
+    return_trace (((format & 0x80) == 0) || suppEncData ().sanitize (c));
+  }
+
+  /* serialize a fullset Encoding */
+  bool serialize (hb_serialize_context_t *c, const Encoding &src)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned int size = src.get_size ();
+    Encoding *dest = c->allocate_size<Encoding> (size);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, &src, size);
+    return_trace (true);
+  }
+
+  /* serialize a subset Encoding */
+  bool serialize (hb_serialize_context_t *c,
+		  uint8_t format,
+		  unsigned int enc_count,
+		  const hb_vector_t<code_pair>& code_ranges,
+		  const hb_vector_t<code_pair>& supp_codes)
+  {
+    TRACE_SERIALIZE (this);
+    Encoding *dest = c->extend_min (*this);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    dest->format.set (format | ((supp_codes.len > 0)? 0x80: 0));
+    if (format == 0)
+    {
+      Encoding0 *fmt0 = c->allocate_size<Encoding0> (Encoding0::min_size + HBUINT8::static_size * enc_count);
+    if (unlikely (fmt0 == nullptr)) return_trace (false);
+      fmt0->nCodes.set (enc_count);
+      unsigned int glyph = 0;
+      for (unsigned int i = 0; i < code_ranges.len; i++)
+      {
+	hb_codepoint_t code = code_ranges[i].code;
+	for (int left = (int)code_ranges[i].glyph; left >= 0; left--)
+	  fmt0->codes[glyph++].set (code++);
+	if (unlikely (!((glyph <= 0x100) && (code <= 0x100))))
+	  return_trace (false);
+      }
+    }
+    else
+    {
+      Encoding1 *fmt1 = c->allocate_size<Encoding1> (Encoding1::min_size + Encoding1_Range::static_size * code_ranges.len);
+      if (unlikely (fmt1 == nullptr)) return_trace (false);
+      fmt1->nRanges.set (code_ranges.len);
+      for (unsigned int i = 0; i < code_ranges.len; i++)
+      {
+	if (unlikely (!((code_ranges[i].code <= 0xFF) && (code_ranges[i].glyph <= 0xFF))))
+	  return_trace (false);
+	fmt1->ranges[i].first.set (code_ranges[i].code);
+	fmt1->ranges[i].nLeft.set (code_ranges[i].glyph);
+      }
+    }
+    if (supp_codes.len > 0)
+    {
+      CFF1SuppEncData *suppData = c->allocate_size<CFF1SuppEncData> (CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_codes.len);
+      if (unlikely (suppData == nullptr)) return_trace (false);
+      suppData->nSups.set (supp_codes.len);
+      for (unsigned int i = 0; i < supp_codes.len; i++)
+      {
+	suppData->supps[i].code.set (supp_codes[i].code);
+	suppData->supps[i].glyph.set (supp_codes[i].glyph); /* actually SID */
+      }
+    }
+    return_trace (true);
+  }
+
+  /* parallel to above: calculate the size of a subset Encoding */
+  static unsigned int calculate_serialized_size (uint8_t format,
+						 unsigned int enc_count,
+						 unsigned int supp_count)
+  {
+    unsigned int  size = min_size;
+    if (format == 0)
+      size += Encoding0::min_size + HBUINT8::static_size * enc_count;
+    else
+      size += Encoding1::min_size + Encoding1_Range::static_size * enc_count;
+    if (supp_count > 0)
+      size += CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_count;
+    return size;
+  }
+
+  unsigned int get_size () const
+  {
+    unsigned int size = min_size;
+    if (table_format () == 0)
+      size += u.format0.get_size ();
+    else
+      size += u.format1.get_size ();
+    if (has_supplement ())
+      size += suppEncData ().get_size ();
+    return size;
+  }
+
+  hb_codepoint_t get_code (hb_codepoint_t glyph) const
+  {
+    if (table_format () == 0)
+      return u.format0.get_code (glyph);
+    else
+      return u.format1.get_code (glyph);
+  }
+
+  uint8_t table_format () const { return (format & 0x7F); }
+  bool  has_supplement () const { return (format & 0x80) != 0; }
+
+  void get_supplement_codes (hb_codepoint_t sid, hb_vector_t<hb_codepoint_t> &codes) const
+  {
+    codes.resize (0);
+    if (has_supplement ())
+      suppEncData().get_codes (sid, codes);
+  }
+
+  protected:
+  const CFF1SuppEncData &suppEncData () const
+  {
+    if ((format & 0x7F) == 0)
+      return StructAfter<CFF1SuppEncData> (u.format0.codes[u.format0.nCodes-1]);
+    else
+      return StructAfter<CFF1SuppEncData> (u.format1.ranges[u.format1.nRanges-1]);
+  }
+
+  public:
+  HBUINT8       format;
+
+  union {
+    Encoding0   format0;
+    Encoding1   format1;
+  } u;
+  /* CFF1SuppEncData  suppEncData; */
+
+  DEFINE_SIZE_MIN (1);
+};
+
+/* Charset */
+struct Charset0 {
+  bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c));
+  }
+
+  hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+  {
+    if (glyph == 0)
+      return 0;
+    else
+      return sids[glyph - 1];
+  }
+
+  hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
+  {
+    if (sid == 0)
+      return 0;
+
+    for (unsigned int glyph = 1; glyph < num_glyphs; glyph++)
+    {
+      if (sids[glyph-1] == sid)
+	return glyph;
+    }
+    return 0;
+  }
+
+  unsigned int get_size (unsigned int num_glyphs) const
+  {
+    assert (num_glyphs > 0);
+    return HBUINT16::static_size * (num_glyphs - 1);
+  }
+
+  HBUINT16  sids[VAR];
+
+  DEFINE_SIZE_ARRAY(0, sids);
+};
+
+template <typename TYPE>
+struct Charset_Range {
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  HBUINT16  first;
+  TYPE      nLeft;
+
+  DEFINE_SIZE_STATIC (HBUINT16::static_size + TYPE::static_size);
+};
+
+template <typename TYPE>
+struct Charset1_2 {
+  bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+    num_glyphs--;
+    for (unsigned int i = 0; num_glyphs > 0; i++)
+    {
+      if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1)))
+	return_trace (false);
+      num_glyphs -= (ranges[i].nLeft + 1);
+    }
+    return_trace (true);
+  }
+
+  hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+  {
+    if (glyph == 0) return 0;
+    glyph--;
+    for (unsigned int i = 0;; i++)
+    {
+      if (glyph <= ranges[i].nLeft)
+	return (hb_codepoint_t)ranges[i].first + glyph;
+      glyph -= (ranges[i].nLeft + 1);
+    }
+
+    return 0;
+  }
+
+  hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
+  {
+    if (sid == 0) return 0;
+    hb_codepoint_t  glyph = 1;
+    for (unsigned int i = 0;; i++)
+    {
+      if (glyph >= num_glyphs)
+      	return 0;
+      if ((ranges[i].first <= sid) && (sid <= ranges[i].first + ranges[i].nLeft))
+	return glyph + (sid - ranges[i].first);
+      glyph += (ranges[i].nLeft + 1);
+    }
+
+    return 0;
+  }
+
+  unsigned int get_size (unsigned int num_glyphs) const
+  {
+    unsigned int size = HBUINT8::static_size;
+    int glyph = (int)num_glyphs;
+
+    assert (glyph > 0);
+    glyph--;
+    for (unsigned int i = 0; glyph > 0; i++)
+    {
+      glyph -= (ranges[i].nLeft + 1);
+      size += Charset_Range<TYPE>::static_size;
+    }
+
+    return size;
+  }
+
+  Charset_Range<TYPE>   ranges[VAR];
+
+  DEFINE_SIZE_ARRAY (0, ranges);
+};
+
+typedef Charset1_2<HBUINT8>     Charset1;
+typedef Charset1_2<HBUINT16>    Charset2;
+typedef Charset_Range<HBUINT8>  Charset1_Range;
+typedef Charset_Range<HBUINT16> Charset2_Range;
+
+struct Charset {
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+    if (format == 0)
+      return_trace (u.format0.sanitize (c, c->get_num_glyphs ()));
+    else if (format == 1)
+      return_trace (u.format1.sanitize (c, c->get_num_glyphs ()));
+    else if (likely (format == 2))
+      return_trace (u.format2.sanitize (c, c->get_num_glyphs ()));
+    else
+      return_trace (false);
+  }
+
+  /* serialize a fullset Charset */
+  bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned int size = src.get_size (num_glyphs);
+    Charset *dest = c->allocate_size<Charset> (size);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, &src, size);
+    return_trace (true);
+  }
+
+  /* serialize a subset Charset */
+  bool serialize (hb_serialize_context_t *c,
+		  uint8_t format,
+		  unsigned int num_glyphs,
+		  const hb_vector_t<code_pair>& sid_ranges)
+  {
+    TRACE_SERIALIZE (this);
+    Charset *dest = c->extend_min (*this);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    dest->format.set (format);
+    if (format == 0)
+    {
+      Charset0 *fmt0 = c->allocate_size<Charset0> (Charset0::min_size + HBUINT16::static_size * (num_glyphs - 1));
+    if (unlikely (fmt0 == nullptr)) return_trace (false);
+      unsigned int glyph = 0;
+      for (unsigned int i = 0; i < sid_ranges.len; i++)
+      {
+	hb_codepoint_t sid = sid_ranges[i].code;
+	for (int left = (int)sid_ranges[i].glyph; left >= 0; left--)
+	  fmt0->sids[glyph++].set (sid++);
+      }
+    }
+    else if (format == 1)
+    {
+      Charset1 *fmt1 = c->allocate_size<Charset1> (Charset1::min_size + Charset1_Range::static_size * sid_ranges.len);
+      if (unlikely (fmt1 == nullptr)) return_trace (false);
+      for (unsigned int i = 0; i < sid_ranges.len; i++)
+      {
+      	if (unlikely (!(sid_ranges[i].glyph <= 0xFF)))
+	  return_trace (false);
+	fmt1->ranges[i].first.set (sid_ranges[i].code);
+	fmt1->ranges[i].nLeft.set (sid_ranges[i].glyph);
+      }
+    }
+    else /* format 2 */
+    {
+      Charset2 *fmt2 = c->allocate_size<Charset2> (Charset2::min_size + Charset2_Range::static_size * sid_ranges.len);
+      if (unlikely (fmt2 == nullptr)) return_trace (false);
+      for (unsigned int i = 0; i < sid_ranges.len; i++)
+      {
+      	if (unlikely (!(sid_ranges[i].glyph <= 0xFFFF)))
+	  return_trace (false);
+	fmt2->ranges[i].first.set (sid_ranges[i].code);
+	fmt2->ranges[i].nLeft.set (sid_ranges[i].glyph);
+      }
+    }
+    return_trace (true);
+  }
+
+  /* parallel to above: calculate the size of a subset Charset */
+  static unsigned int calculate_serialized_size (
+			uint8_t format,
+			unsigned int count)
+  {
+    unsigned int  size = min_size;
+    if (format == 0)
+      size += Charset0::min_size + HBUINT16::static_size * (count - 1);
+    else if (format == 1)
+      size += Charset1::min_size + Charset1_Range::static_size * count;
+    else
+      size += Charset2::min_size + Charset2_Range::static_size * count;
+
+    return size;
+  }
+
+  unsigned int get_size (unsigned int num_glyphs) const
+  {
+    unsigned int size = min_size;
+    if (format == 0)
+      size += u.format0.get_size (num_glyphs);
+    else if (format == 1)
+      size += u.format1.get_size (num_glyphs);
+    else
+      size += u.format2.get_size (num_glyphs);
+    return size;
+  }
+
+  hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+  {
+    if (format == 0)
+      return u.format0.get_sid (glyph);
+    else if (format == 1)
+      return u.format1.get_sid (glyph);
+    else
+      return u.format2.get_sid (glyph);
+  }
+
+  hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
+  {
+    if (format == 0)
+      return u.format0.get_glyph (sid, num_glyphs);
+    else if (format == 1)
+      return u.format1.get_glyph (sid, num_glyphs);
+    else
+      return u.format2.get_glyph (sid, num_glyphs);
+  }
+
+  HBUINT8       format;
+  union {
+    Charset0    format0;
+    Charset1    format1;
+    Charset2    format2;
+  } u;
+
+  DEFINE_SIZE_MIN (1);
+};
+
+struct CFF1StringIndex : CFF1Index
+{
+  bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings,
+		  unsigned int offSize_, const Remap &sidmap)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely ((strings.count == 0) || (sidmap.get_count () == 0)))
+    {
+      if (!unlikely (c->extend_min (this->count)))
+	return_trace (false);
+      count.set (0);
+      return_trace (true);
+    }
+
+    ByteStrArray bytesArray;
+    bytesArray.init ();
+    if (!bytesArray.resize (sidmap.get_count ()))
+      return_trace (false);
+    for (unsigned int i = 0; i < strings.count; i++)
+    {
+      hb_codepoint_t  j = sidmap[i];
+      if (j != CFF_UNDEF_CODE)
+	bytesArray[j] = strings[i];
+    }
+
+    bool result = CFF1Index::serialize (c, offSize_, bytesArray);
+    bytesArray.fini ();
+    return_trace (result);
+  }
+
+  /* in parallel to above */
+  unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const Remap &sidmap) const
+  {
+    offSize = 0;
+    if ((count == 0) || (sidmap.get_count () == 0))
+      return count.static_size;
+
+    unsigned int dataSize = 0;
+    for (unsigned int i = 0; i < count; i++)
+      if (sidmap[i] != CFF_UNDEF_CODE)
+	dataSize += length_at (i);
+
+    offSize = calcOffSize(dataSize);
+    return CFF1Index::calculate_serialized_size (offSize, sidmap.get_count (), dataSize);
+  }
+};
+
+struct CFF1TopDictInterpEnv : NumInterpEnv
+{
+  CFF1TopDictInterpEnv ()
+    : NumInterpEnv(), prev_offset(0), last_offset(0) {}
+
+  unsigned int prev_offset;
+  unsigned int last_offset;
+};
+
+struct NameDictValues
+{
+  enum NameDictValIndex
+  {
+      version,
+      notice,
+      copyright,
+      fullName,
+      familyName,
+      weight,
+      postscript,
+      fontName,
+      baseFontName,
+      registry,
+      ordering,
+
+      ValCount
+  };
+
+  void init ()
+  {
+    for (unsigned int i = 0; i < ValCount; i++)
+      values[i] = CFF_UNDEF_SID;
+  }
+
+  unsigned int& operator[] (unsigned int i)
+  { assert (i < ValCount); return values[i]; }
+
+  unsigned int operator[] (unsigned int i) const
+  { assert (i < ValCount); return values[i]; }
+
+  static enum NameDictValIndex name_op_to_index (OpCode op)
+  {
+    switch (op) {
+      default: // can't happen - just make some compiler happy
+      case OpCode_version:
+	return version;
+      case OpCode_Notice:
+	return notice;
+      case OpCode_Copyright:
+	return copyright;
+      case OpCode_FullName:
+	return fullName;
+      case OpCode_FamilyName:
+	return familyName;
+      case OpCode_Weight:
+	return weight;
+      case OpCode_PostScript:
+	return postscript;
+      case OpCode_FontName:
+	return fontName;
+      case OpCode_BaseFontName:
+	return baseFontName;
+    }
+  }
+
+  unsigned int  values[ValCount];
+};
+
+struct CFF1TopDictVal : OpStr
+{
+  unsigned int  last_arg_offset;
+};
+
+struct CFF1TopDictValues : TopDictValues<CFF1TopDictVal>
+{
+  void init ()
+  {
+    TopDictValues<CFF1TopDictVal>::init ();
+
+    nameSIDs.init ();
+    ros_supplement = 0;
+    cidCount = 8720;
+    EncodingOffset = 0;
+    CharsetOffset = 0;
+    FDSelectOffset = 0;
+    privateDictInfo.init ();
+  }
+  void fini () { TopDictValues<CFF1TopDictVal>::fini (); }
+
+  bool is_CID () const
+  { return nameSIDs[NameDictValues::registry] != CFF_UNDEF_SID; }
+
+  NameDictValues  nameSIDs;
+  unsigned int    ros_supplement_offset;
+  unsigned int    ros_supplement;
+  unsigned int    cidCount;
+
+  unsigned int    EncodingOffset;
+  unsigned int    CharsetOffset;
+  unsigned int    FDSelectOffset;
+  TableInfo       privateDictInfo;
+};
+
+struct CFF1TopDictOpSet : TopDictOpSet<CFF1TopDictVal>
+{
+  static void process_op (OpCode op, CFF1TopDictInterpEnv& env, CFF1TopDictValues& dictval)
+  {
+    CFF1TopDictVal  val;
+    val.last_arg_offset = (env.last_offset-1) - dictval.opStart;  /* offset to the last argument */
+
+    switch (op) {
+      case OpCode_version:
+      case OpCode_Notice:
+      case OpCode_Copyright:
+      case OpCode_FullName:
+      case OpCode_FamilyName:
+      case OpCode_Weight:
+      case OpCode_PostScript:
+      case OpCode_BaseFontName:
+	dictval.nameSIDs[NameDictValues::name_op_to_index (op)] = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+      case OpCode_isFixedPitch:
+      case OpCode_ItalicAngle:
+      case OpCode_UnderlinePosition:
+      case OpCode_UnderlineThickness:
+      case OpCode_PaintType:
+      case OpCode_CharstringType:
+      case OpCode_UniqueID:
+      case OpCode_StrokeWidth:
+      case OpCode_SyntheticBase:
+      case OpCode_CIDFontVersion:
+      case OpCode_CIDFontRevision:
+      case OpCode_CIDFontType:
+      case OpCode_UIDBase:
+      case OpCode_FontBBox:
+      case OpCode_XUID:
+      case OpCode_BaseFontBlend:
+	env.clear_args ();
+	break;
+
+      case OpCode_CIDCount:
+	dictval.cidCount = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+
+      case OpCode_ROS:
+	dictval.ros_supplement = env.argStack.pop_uint ();
+	dictval.nameSIDs[NameDictValues::ordering] = env.argStack.pop_uint ();
+	dictval.nameSIDs[NameDictValues::registry] = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+
+      case OpCode_Encoding:
+	dictval.EncodingOffset = env.argStack.pop_uint ();
+	env.clear_args ();
+	if (unlikely (dictval.EncodingOffset == 0)) return;
+	break;
+
+      case OpCode_charset:
+	dictval.CharsetOffset = env.argStack.pop_uint ();
+	env.clear_args ();
+	if (unlikely (dictval.CharsetOffset == 0)) return;
+	break;
+
+      case OpCode_FDSelect:
+	dictval.FDSelectOffset = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+
+      case OpCode_Private:
+	dictval.privateDictInfo.offset = env.argStack.pop_uint ();
+	dictval.privateDictInfo.size = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+
+      default:
+	env.last_offset = env.substr.offset;
+	TopDictOpSet<CFF1TopDictVal>::process_op (op, env, dictval);
+	/* Record this operand below if stack is empty, otherwise done */
+	if (!env.argStack.is_empty ()) return;
+	break;
+    }
+
+    if (unlikely (env.in_error ())) return;
+
+    dictval.add_op (op, env.substr, val);
+  }
+};
+
+struct CFF1FontDictValues : DictValues<OpStr>
+{
+  void init ()
+  {
+    DictValues<OpStr>::init ();
+    privateDictInfo.init ();
+    fontName = CFF_UNDEF_SID;
+  }
+  void fini () { DictValues<OpStr>::fini (); }
+
+  TableInfo       privateDictInfo;
+  unsigned int    fontName;
+};
+
+struct CFF1FontDictOpSet : DictOpSet
+{
+  static void process_op (OpCode op, NumInterpEnv& env, CFF1FontDictValues& dictval)
+  {
+    switch (op) {
+      case OpCode_FontName:
+	dictval.fontName = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+      case OpCode_FontMatrix:
+      case OpCode_PaintType:
+	env.clear_args ();
+	break;
+      case OpCode_Private:
+	dictval.privateDictInfo.offset = env.argStack.pop_uint ();
+	dictval.privateDictInfo.size = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+
+      default:
+	DictOpSet::process_op (op, env);
+	if (!env.argStack.is_empty ()) return;
+	break;
+    }
+
+    if (unlikely (env.in_error ())) return;
+
+    dictval.add_op (op, env.substr);
+  }
+};
+
+template <typename VAL>
+struct CFF1PrivateDictValues_Base : DictValues<VAL>
+{
+  void init ()
+  {
+    DictValues<VAL>::init ();
+    subrsOffset = 0;
+    localSubrs = &Null(CFF1Subrs);
+  }
+  void fini () { DictValues<VAL>::fini (); }
+
+  unsigned int calculate_serialized_size () const
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < DictValues<VAL>::get_count; i++)
+      if (DictValues<VAL>::get_value (i).op == OpCode_Subrs)
+	size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
+      else
+	size += DictValues<VAL>::get_value (i).str.len;
+    return size;
+  }
+
+  unsigned int      subrsOffset;
+  const CFF1Subrs    *localSubrs;
+};
+
+typedef CFF1PrivateDictValues_Base<OpStr> CFF1PrivateDictValues_Subset;
+typedef CFF1PrivateDictValues_Base<NumDictVal> CFF1PrivateDictValues;
+
+struct CFF1PrivateDictOpSet : DictOpSet
+{
+  static void process_op (OpCode op, NumInterpEnv& env, CFF1PrivateDictValues& dictval)
+  {
+    NumDictVal val;
+    val.init ();
+
+    switch (op) {
+      case OpCode_BlueValues:
+      case OpCode_OtherBlues:
+      case OpCode_FamilyBlues:
+      case OpCode_FamilyOtherBlues:
+      case OpCode_StemSnapH:
+      case OpCode_StemSnapV:
+	env.clear_args ();
+	break;
+      case OpCode_StdHW:
+      case OpCode_StdVW:
+      case OpCode_BlueScale:
+      case OpCode_BlueShift:
+      case OpCode_BlueFuzz:
+      case OpCode_ForceBold:
+      case OpCode_LanguageGroup:
+      case OpCode_ExpansionFactor:
+      case OpCode_initialRandomSeed:
+      case OpCode_defaultWidthX:
+      case OpCode_nominalWidthX:
+	val.single_val = env.argStack.pop_num ();
+	env.clear_args ();
+	break;
+      case OpCode_Subrs:
+	dictval.subrsOffset = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+
+      default:
+	DictOpSet::process_op (op, env);
+	if (!env.argStack.is_empty ()) return;
+	break;
+    }
+
+    if (unlikely (env.in_error ())) return;
+
+    dictval.add_op (op, env.substr, val);
+  }
+};
+
+struct CFF1PrivateDictOpSet_Subset : DictOpSet
+{
+  static void process_op (OpCode op, NumInterpEnv& env, CFF1PrivateDictValues_Subset& dictval)
+  {
+    switch (op) {
+      case OpCode_BlueValues:
+      case OpCode_OtherBlues:
+      case OpCode_FamilyBlues:
+      case OpCode_FamilyOtherBlues:
+      case OpCode_StemSnapH:
+      case OpCode_StemSnapV:
+      case OpCode_StdHW:
+      case OpCode_StdVW:
+      case OpCode_BlueScale:
+      case OpCode_BlueShift:
+      case OpCode_BlueFuzz:
+      case OpCode_ForceBold:
+      case OpCode_LanguageGroup:
+      case OpCode_ExpansionFactor:
+      case OpCode_initialRandomSeed:
+      case OpCode_defaultWidthX:
+      case OpCode_nominalWidthX:
+	env.clear_args ();
+	break;
+
+      case OpCode_Subrs:
+	dictval.subrsOffset = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+
+      default:
+	DictOpSet::process_op (op, env);
+	if (!env.argStack.is_empty ()) return;
+	break;
+    }
+
+    if (unlikely (env.in_error ())) return;
+
+    dictval.add_op (op, env.substr);
+  }
+};
+
+typedef DictInterpreter<CFF1TopDictOpSet, CFF1TopDictValues, CFF1TopDictInterpEnv> CFF1TopDict_Interpreter;
+typedef DictInterpreter<CFF1FontDictOpSet, CFF1FontDictValues> CFF1FontDict_Interpreter;
+typedef DictInterpreter<CFF1PrivateDictOpSet, CFF1PrivateDictValues> CFF1PrivateDict_Interpreter;
+
+typedef CFF1Index CFF1NameIndex;
+typedef CFF1IndexOf<TopDict> CFF1TopDictIndex;
+
+}; /* namespace CFF */
+
+namespace OT {
+
+using namespace CFF;
+
+struct cff1
+{
+  static const hb_tag_t tableTag	= HB_OT_TAG_cff1;
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  likely (version.major == 1));
+  }
+
+  template <typename PRIVOPSET, typename PRIVDICTVAL>
+  struct accelerator_templ_t
+  {
+    void init (hb_face_t *face)
+    {
+      topDict.init ();
+      fontDicts.init ();
+      privateDicts.init ();
+
+      this->blob = sc.reference_table<cff1> (face);
+
+      /* setup for run-time santization */
+      sc.init (this->blob);
+      sc.start_processing ();
+
+      const OT::cff1 *cff = this->blob->template as<OT::cff1> ();
+
+      if (cff == &Null(OT::cff1))
+      { fini (); return; }
+
+      nameIndex = &cff->nameIndex (cff);
+      if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc))
+      { fini (); return; }
+
+      topDictIndex = &StructAtOffset<CFF1TopDictIndex> (nameIndex, nameIndex->get_size ());
+      if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0))
+      { fini (); return; }
+
+      { /* parse top dict */
+	const ByteStr topDictStr = (*topDictIndex)[0];
+	if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
+	CFF1TopDict_Interpreter top_interp;
+	top_interp.env.init (topDictStr);
+	topDict.init ();
+	if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
+      }
+
+      if (is_predef_charset ())
+	charset = &Null(Charset);
+      else
+      {
+	charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset);
+	if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc))) { fini (); return; }
+      }
+
+      fdCount = 1;
+      if (is_CID ())
+      {
+	fdArray = &StructAtOffsetOrNull<CFF1FDArray> (cff, topDict.FDArrayOffset);
+	fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDict.FDSelectOffset);
+	if (unlikely ((fdArray == &Null(CFF1FDArray)) || !fdArray->sanitize (&sc) ||
+	    (fdSelect == &Null(CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count)))
+	{ fini (); return; }
+
+	fdCount = fdArray->count;
+      }
+      else
+      {
+	fdArray = &Null(CFF1FDArray);
+	fdSelect = &Null(CFF1FDSelect);
+      }
+
+      stringIndex = &StructAtOffset<CFF1StringIndex> (topDictIndex, topDictIndex->get_size ());
+      if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc))
+      { fini (); return; }
+
+      globalSubrs = &StructAtOffset<CFF1Subrs> (stringIndex, stringIndex->get_size ());
+      if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc))
+      { fini (); return; }
+
+      charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDict.charStringsOffset);
+
+      if ((charStrings == &Null(CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc)))
+      { fini (); return; }
+
+      num_glyphs = charStrings->count;
+      if (num_glyphs != sc.get_num_glyphs ())
+      { fini (); return; }
+
+      privateDicts.resize (fdCount);
+      for (unsigned int i = 0; i < fdCount; i++)
+	privateDicts[i].init ();
+
+      // parse CID font dicts and gather private dicts
+      if (is_CID ())
+      {
+	for (unsigned int i = 0; i < fdCount; i++)
+	{
+	  ByteStr fontDictStr = (*fdArray)[i];
+	  if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
+	  CFF1FontDictValues  *font;
+	  CFF1FontDict_Interpreter font_interp;
+	  font_interp.env.init (fontDictStr);
+	  font = fontDicts.push ();
+	  if (unlikely (font == &Crap(CFF1FontDictValues))) { fini (); return; }
+	  font->init ();
+	  if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
+	  PRIVDICTVAL  *priv = &privateDicts[i];
+	  const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
+	  if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+	  DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
+	  priv_interp.env.init (privDictStr);
+	  priv->init ();
+	  if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+
+	  priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
+	  if (priv->localSubrs != &Null(CFF1Subrs) &&
+	      unlikely (!priv->localSubrs->sanitize (&sc)))
+	  { fini (); return; }
+	}
+      }
+      else  /* non-CID */
+      {
+	CFF1TopDictValues  *font = &topDict;
+	PRIVDICTVAL  *priv = &privateDicts[0];
+
+	const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
+	if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+	DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
+	priv_interp.env.init (privDictStr);
+	priv->init ();
+	if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+
+	priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
+	if (priv->localSubrs != &Null(CFF1Subrs) &&
+	    unlikely (!priv->localSubrs->sanitize (&sc)))
+	{ fini (); return; }
+      }
+    }
+
+    void fini ()
+    {
+      sc.end_processing ();
+      topDict.fini ();
+      fontDicts.fini_deep ();
+      privateDicts.fini_deep ();
+      hb_blob_destroy (blob);
+      blob = nullptr;
+    }
+
+    bool is_valid () const { return blob != nullptr; }
+    bool is_CID () const { return topDict.is_CID (); }
+
+    bool is_predef_charset () const { return topDict.CharsetOffset <= ExpertSubsetCharset; }
+
+    unsigned int std_code_to_glyph (hb_codepoint_t code) const
+    {
+      hb_codepoint_t sid = lookup_standard_encoding_for_sid (code);
+      if (unlikely (sid == CFF_UNDEF_SID))
+	return 0;
+
+      if (charset != &Null(Charset))
+	return charset->get_glyph (sid, num_glyphs);
+      else if ((topDict.CharsetOffset == ISOAdobeCharset)
+	      && (code <= 228 /*zcaron*/)) return sid;
+      return 0;
+    }
+
+    protected:
+    hb_blob_t	       *blob;
+    hb_sanitize_context_t   sc;
+
+    public:
+    const Charset	   *charset;
+    const CFF1NameIndex     *nameIndex;
+    const CFF1TopDictIndex  *topDictIndex;
+    const CFF1StringIndex   *stringIndex;
+    const CFF1Subrs	 *globalSubrs;
+    const CFF1CharStrings   *charStrings;
+    const CFF1FDArray       *fdArray;
+    const CFF1FDSelect      *fdSelect;
+    unsigned int	    fdCount;
+
+    CFF1TopDictValues       topDict;
+    hb_vector_t<CFF1FontDictValues>   fontDicts;
+    hb_vector_t<PRIVDICTVAL>	  privateDicts;
+
+    unsigned int	    num_glyphs;
+  };
+
+  struct accelerator_t : accelerator_templ_t<CFF1PrivateDictOpSet, CFF1PrivateDictValues>
+  {
+    HB_INTERNAL bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const;
+    HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const;
+  };
+
+  struct accelerator_subset_t : accelerator_templ_t<CFF1PrivateDictOpSet_Subset, CFF1PrivateDictValues_Subset>
+  {
+    void init (hb_face_t *face)
+    {
+      SUPER::init (face);
+      if (blob == nullptr) return;
+
+      const OT::cff1 *cff = this->blob->as<OT::cff1> ();
+      encoding = &Null(Encoding);
+      if (is_CID ())
+      {
+	if (unlikely (charset == &Null(Charset))) { fini (); return; }
+      }
+      else
+      {
+	if (!is_predef_encoding ())
+	{
+	  encoding = &StructAtOffsetOrNull<Encoding> (cff, topDict.EncodingOffset);
+	  if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; }
+	}
+      }
+    }
+
+    bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; }
+
+    hb_codepoint_t  glyph_to_code (hb_codepoint_t glyph) const
+    {
+      if (encoding != &Null(Encoding))
+	return encoding->get_code (glyph);
+      else
+      {
+	hb_codepoint_t  sid = glyph_to_sid (glyph);
+	if (sid == 0) return 0;
+	hb_codepoint_t  code = 0;
+	switch (topDict.EncodingOffset)
+	{
+	  case  StandardEncoding:
+	    code = lookup_standard_encoding_for_code (sid);
+	    break;
+	  case  ExpertEncoding:
+	    code = lookup_expert_encoding_for_code (sid);
+	    break;
+	  default:
+	    break;
+	}
+	return code;
+      }
+    }
+
+    hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const
+    {
+      if (charset != &Null(Charset))
+	return charset->get_sid (glyph);
+      else
+      {
+	hb_codepoint_t sid = 0;
+	switch (topDict.CharsetOffset)
+	{
+	  case  ISOAdobeCharset:
+	    if (glyph <= 228 /*zcaron*/) sid = glyph;
+	    break;
+	  case  ExpertCharset:
+	    sid = lookup_expert_charset_for_sid (glyph);
+	    break;
+	  case  ExpertSubsetCharset:
+	      sid = lookup_expert_subset_charset_for_sid (glyph);
+	    break;
+	  default:
+	    break;
+	}
+	return sid;
+      }
+    }
+
+    const Encoding	  *encoding;
+
+    private:
+    typedef accelerator_templ_t<CFF1PrivateDictOpSet_Subset, CFF1PrivateDictValues_Subset> SUPER;
+  };
+
+  bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_blob_t *cff_prime = nullptr;
+
+    bool success = true;
+    if (hb_subset_cff1 (plan, &cff_prime)) {
+      success = success && plan->add_table (HB_OT_TAG_cff1, cff_prime);
+      hb_blob_t *head_blob = hb_sanitize_context_t().reference_table<head> (plan->source);
+      success = success && head_blob && plan->add_table (HB_OT_TAG_head, head_blob);
+      hb_blob_destroy (head_blob);
+    } else {
+      success = false;
+    }
+    hb_blob_destroy (cff_prime);
+
+    return success;
+  }
+
+  protected:
+  HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_code (hb_codepoint_t sid);
+  HB_INTERNAL static hb_codepoint_t lookup_expert_encoding_for_code (hb_codepoint_t sid);
+  HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_sid (hb_codepoint_t glyph);
+  HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph);
+  HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_sid (hb_codepoint_t code);
+
+  public:
+  FixedVersion<HBUINT8> version;	  /* Version of CFF table. set to 0x0100u */
+  OffsetTo<CFF1NameIndex, HBUINT8> nameIndex; /* headerSize = Offset to Name INDEX. */
+  HBUINT8	       offSize;	  /* offset size (unused?) */
+
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct cff1_accelerator_t : cff1::accelerator_t {};
+} /* namespace OT */
+
+#endif /* HB_OT_CFF1_TABLE_HH */
diff --git a/src/hb-ot-cff2-table.cc b/src/hb-ot-cff2-table.cc
new file mode 100644
index 0000000..7188a3b
--- /dev/null
+++ b/src/hb-ot-cff2-table.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#include "hb-ot-cff2-table.hh"
+#include "hb-cff2-interp-cs.hh"
+
+using namespace CFF;
+
+struct ExtentsParam
+{
+  void init ()
+  {
+    path_open = false;
+    min_x.set_int (0x7FFFFFFF);
+    min_y.set_int (0x7FFFFFFF);
+    max_x.set_int (-0x80000000);
+    max_y.set_int (-0x80000000);
+  }
+
+  void start_path ()         { path_open = true; }
+  void end_path ()           { path_open = false; }
+  bool is_path_open () const { return path_open; }
+
+  void update_bounds (const Point &pt)
+  {
+    if (pt.x < min_x) min_x = pt.x;
+    if (pt.x > max_x) max_x = pt.x;
+    if (pt.y < min_y) min_y = pt.y;
+    if (pt.y > max_y) max_y = pt.y;
+  }
+
+  bool  path_open;
+  Number min_x;
+  Number min_y;
+  Number max_x;
+  Number max_y;
+};
+
+struct CFF2PathProcs_Extents : PathProcs<CFF2PathProcs_Extents, CFF2CSInterpEnv, ExtentsParam>
+{
+  static void moveto (CFF2CSInterpEnv &env, ExtentsParam& param, const Point &pt)
+  {
+    param.end_path ();
+    env.moveto (pt);
+  }
+
+  static void line (CFF2CSInterpEnv &env, ExtentsParam& param, const Point &pt1)
+  {
+    if (!param.is_path_open ())
+    {
+      param.start_path ();
+      param.update_bounds (env.get_pt ());
+    }
+    env.moveto (pt1);
+    param.update_bounds (env.get_pt ());
+  }
+
+  static void curve (CFF2CSInterpEnv &env, ExtentsParam& param, const Point &pt1, const Point &pt2, const Point &pt3)
+  {
+    if (!param.is_path_open ())
+    {
+      param.start_path ();
+      param.update_bounds (env.get_pt ());
+    }
+    /* include control points */
+    param.update_bounds (pt1);
+    param.update_bounds (pt2);
+    env.moveto (pt3);
+    param.update_bounds (env.get_pt ());
+  }
+};
+
+struct CFF2CSOpSet_Extents : CFF2CSOpSet<CFF2CSOpSet_Extents, ExtentsParam, CFF2PathProcs_Extents> {};
+
+bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
+					   hb_codepoint_t glyph,
+					   hb_glyph_extents_t *extents) const
+{
+  if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
+
+  unsigned int num_coords;
+  const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
+  unsigned int fd = fdSelect->get_fd (glyph);
+  CFF2CSInterpreter<CFF2CSOpSet_Extents, ExtentsParam> interp;
+  const ByteStr str = (*charStrings)[glyph];
+  interp.env.init (str, *this, fd, coords, num_coords);
+  ExtentsParam  param;
+  param.init ();
+  if (unlikely (!interp.interpret (param))) return false;
+
+  if (param.min_x >= param.max_x)
+  {
+    extents->width = 0;
+    extents->x_bearing = 0;
+  }
+  else
+  {
+    extents->x_bearing = (int32_t)param.min_x.floor ();
+    extents->width = (int32_t)param.max_x.ceil () - extents->x_bearing;
+  }
+  if (param.min_y >= param.max_y)
+  {
+    extents->height = 0;
+    extents->y_bearing = 0;
+  }
+  else
+  {
+    extents->y_bearing = (int32_t)param.max_y.ceil ();
+    extents->height = (int32_t)param.min_y.floor () - extents->y_bearing;
+  }
+
+  return true;
+}
diff --git a/src/hb-ot-cff2-table.hh b/src/hb-ot-cff2-table.hh
new file mode 100644
index 0000000..25c41ef
--- /dev/null
+++ b/src/hb-ot-cff2-table.hh
@@ -0,0 +1,565 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_OT_CFF2_TABLE_HH
+#define HB_OT_CFF2_TABLE_HH
+
+#include "hb-ot-head-table.hh"
+#include "hb-ot-cff-common.hh"
+#include "hb-subset-cff2.hh"
+
+namespace CFF {
+
+/*
+ * CFF2 -- Compact Font Format (CFF) Version 2
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/cff2
+ */
+#define HB_OT_TAG_cff2 HB_TAG('C','F','F','2')
+
+typedef CFFIndex<HBUINT32>  CFF2Index;
+template <typename Type> struct CFF2IndexOf : CFFIndexOf<HBUINT32, Type> {};
+
+typedef CFF2Index         CFF2CharStrings;
+typedef FDArray<HBUINT32> CFF2FDArray;
+typedef Subrs<HBUINT32>   CFF2Subrs;
+
+typedef FDSelect3_4<HBUINT32, HBUINT16> FDSelect4;
+typedef FDSelect3_4_Range<HBUINT32, HBUINT16> FDSelect4_Range;
+
+struct CFF2FDSelect
+{
+  bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+  {
+    TRACE_SANITIZE (this);
+
+    return_trace (likely (c->check_struct (this) && (format == 0 || format == 3 || format == 4) &&
+			  (format == 0)?
+			  u.format0.sanitize (c, fdcount):
+			    ((format == 3)?
+			    u.format3.sanitize (c, fdcount):
+			    u.format4.sanitize (c, fdcount))));
+  }
+
+  bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned int size = src.get_size (num_glyphs);
+    CFF2FDSelect *dest = c->allocate_size<CFF2FDSelect> (size);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, &src, size);
+    return_trace (true);
+  }
+
+  unsigned int calculate_serialized_size (unsigned int num_glyphs) const
+  { return get_size (num_glyphs); }
+
+  unsigned int get_size (unsigned int num_glyphs) const
+  {
+    unsigned int size = format.static_size;
+    if (format == 0)
+      size += u.format0.get_size (num_glyphs);
+    else if (format == 3)
+      size += u.format3.get_size ();
+    else
+      size += u.format4.get_size ();
+    return size;
+  }
+
+  hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    if (this == &Null(CFF2FDSelect))
+      return 0;
+    if (format == 0)
+      return u.format0.get_fd (glyph);
+    else if (format == 3)
+      return u.format3.get_fd (glyph);
+    else
+      return u.format4.get_fd (glyph);
+  }
+
+  HBUINT8       format;
+  union {
+    FDSelect0   format0;
+    FDSelect3   format3;
+    FDSelect4   format4;
+  } u;
+
+  DEFINE_SIZE_MIN (2);
+};
+
+struct CFF2VariationStore
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)) && c->check_range (&varStore, size) && varStore.sanitize (c));
+  }
+
+  bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned int size_ = varStore->get_size ();
+    CFF2VariationStore *dest = c->allocate_size<CFF2VariationStore> (size_);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, varStore, size_);
+    return_trace (true);
+  }
+
+  unsigned int get_size () const { return HBUINT16::static_size + size; }
+
+  HBUINT16	size;
+  VariationStore  varStore;
+
+  DEFINE_SIZE_MIN (2 + VariationStore::min_size);
+};
+
+struct CFF2TopDictValues : TopDictValues<>
+{
+  void init ()
+  {
+    TopDictValues<>::init ();
+    vstoreOffset = 0;
+    FDSelectOffset = 0;
+  }
+  void fini () { TopDictValues<>::fini (); }
+
+  unsigned int calculate_serialized_size () const
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < get_count (); i++)
+    {
+      OpCode op = get_value (i).op;
+      switch (op)
+      {
+	case OpCode_vstore:
+	case OpCode_FDSelect:
+	  size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
+	  break;
+	default:
+	  size += TopDictValues<>::calculate_serialized_op_size (get_value (i));
+	  break;
+      }
+    }
+    return size;
+  }
+
+  unsigned int  vstoreOffset;
+  unsigned int  FDSelectOffset;
+};
+
+struct CFF2TopDictOpSet : TopDictOpSet<>
+{
+  static void process_op (OpCode op, NumInterpEnv& env, CFF2TopDictValues& dictval)
+  {
+    switch (op) {
+      case OpCode_FontMatrix:
+	{
+	  DictVal val;
+	  val.init ();
+	  dictval.add_op (op, env.substr);
+	  env.clear_args ();
+	}
+	break;
+
+      case OpCode_vstore:
+	dictval.vstoreOffset = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+      case OpCode_FDSelect:
+	dictval.FDSelectOffset = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+
+      default:
+	SUPER::process_op (op, env, dictval);
+	/* Record this operand below if stack is empty, otherwise done */
+	if (!env.argStack.is_empty ()) return;
+    }
+
+    if (unlikely (env.in_error ())) return;
+
+    dictval.add_op (op, env.substr);
+  }
+
+  typedef TopDictOpSet<> SUPER;
+};
+
+struct CFF2FontDictValues : DictValues<OpStr>
+{
+  void init ()
+  {
+    DictValues<OpStr>::init ();
+    privateDictInfo.init ();
+  }
+  void fini () { DictValues<OpStr>::fini (); }
+
+  TableInfo    privateDictInfo;
+};
+
+struct CFF2FontDictOpSet : DictOpSet
+{
+  static void process_op (OpCode op, NumInterpEnv& env, CFF2FontDictValues& dictval)
+  {
+    switch (op) {
+      case OpCode_Private:
+	dictval.privateDictInfo.offset = env.argStack.pop_uint ();
+	dictval.privateDictInfo.size = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+
+      default:
+	SUPER::process_op (op, env);
+	if (!env.argStack.is_empty ())
+	  return;
+    }
+
+    if (unlikely (env.in_error ())) return;
+
+    dictval.add_op (op, env.substr);
+  }
+
+  private:
+  typedef DictOpSet SUPER;
+};
+
+template <typename VAL>
+struct CFF2PrivateDictValues_Base : DictValues<VAL>
+{
+  void init ()
+  {
+    DictValues<VAL>::init ();
+    subrsOffset = 0;
+    localSubrs = &Null(CFF2Subrs);
+    ivs = 0;
+  }
+  void fini () { DictValues<VAL>::fini (); }
+
+  unsigned int calculate_serialized_size () const
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < DictValues<VAL>::get_count; i++)
+      if (DictValues<VAL>::get_value (i).op == OpCode_Subrs)
+	size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
+      else
+	size += DictValues<VAL>::get_value (i).str.len;
+    return size;
+  }
+
+  unsigned int      subrsOffset;
+  const CFF2Subrs   *localSubrs;
+  unsigned int      ivs;
+};
+
+typedef CFF2PrivateDictValues_Base<OpStr> CFF2PrivateDictValues_Subset;
+typedef CFF2PrivateDictValues_Base<NumDictVal> CFF2PrivateDictValues;
+
+struct CFF2PrivDictInterpEnv : NumInterpEnv
+{
+  void init (const ByteStr &str)
+  {
+    NumInterpEnv::init (str);
+    ivs = 0;
+    seen_vsindex = false;
+  }
+
+  void process_vsindex ()
+  {
+    if (likely (!seen_vsindex))
+    {
+      set_ivs (argStack.pop_uint ());
+    }
+    seen_vsindex = true;
+  }
+
+  unsigned int get_ivs () const { return ivs; }
+  void	 set_ivs (unsigned int ivs_) { ivs = ivs_; }
+
+  protected:
+  unsigned int  ivs;
+  bool	  seen_vsindex;
+};
+
+struct CFF2PrivateDictOpSet : DictOpSet
+{
+  static void process_op (OpCode op, CFF2PrivDictInterpEnv& env, CFF2PrivateDictValues& dictval)
+  {
+    NumDictVal val;
+    val.init ();
+
+    switch (op) {
+      case OpCode_StdHW:
+      case OpCode_StdVW:
+      case OpCode_BlueScale:
+      case OpCode_BlueShift:
+      case OpCode_BlueFuzz:
+      case OpCode_ExpansionFactor:
+      case OpCode_LanguageGroup:
+	val.single_val = env.argStack.pop_num ();
+	env.clear_args ();
+	break;
+      case OpCode_BlueValues:
+      case OpCode_OtherBlues:
+      case OpCode_FamilyBlues:
+      case OpCode_FamilyOtherBlues:
+      case OpCode_StemSnapH:
+      case OpCode_StemSnapV:
+	env.clear_args ();
+	break;
+      case OpCode_Subrs:
+	dictval.subrsOffset = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+      case OpCode_vsindexdict:
+	env.process_vsindex ();
+	dictval.ivs = env.get_ivs ();
+	env.clear_args ();
+	break;
+      case OpCode_blenddict:
+	break;
+
+      default:
+	DictOpSet::process_op (op, env);
+	if (!env.argStack.is_empty ()) return;
+	break;
+    }
+
+    if (unlikely (env.in_error ())) return;
+
+    dictval.add_op (op, env.substr, val);
+  }
+};
+
+struct CFF2PrivateDictOpSet_Subset : DictOpSet
+{
+  static void process_op (OpCode op, CFF2PrivDictInterpEnv& env, CFF2PrivateDictValues_Subset& dictval)
+  {
+    switch (op) {
+      case OpCode_BlueValues:
+      case OpCode_OtherBlues:
+      case OpCode_FamilyBlues:
+      case OpCode_FamilyOtherBlues:
+      case OpCode_StdHW:
+      case OpCode_StdVW:
+      case OpCode_BlueScale:
+      case OpCode_BlueShift:
+      case OpCode_BlueFuzz:
+      case OpCode_StemSnapH:
+      case OpCode_StemSnapV:
+      case OpCode_LanguageGroup:
+      case OpCode_ExpansionFactor:
+	env.clear_args ();
+	break;
+
+      case OpCode_blenddict:
+	env.clear_args ();
+	return;
+
+      case OpCode_Subrs:
+	dictval.subrsOffset = env.argStack.pop_uint ();
+	env.clear_args ();
+	break;
+
+      default:
+	SUPER::process_op (op, env);
+	if (!env.argStack.is_empty ()) return;
+	break;
+    }
+
+    if (unlikely (env.in_error ())) return;
+
+    dictval.add_op (op, env.substr);
+  }
+
+  private:
+  typedef DictOpSet SUPER;
+};
+
+typedef DictInterpreter<CFF2TopDictOpSet, CFF2TopDictValues> CFF2TopDict_Interpreter;
+typedef DictInterpreter<CFF2FontDictOpSet, CFF2FontDictValues> CFF2FontDict_Interpreter;
+
+}; /* namespace CFF */
+
+namespace OT {
+
+using namespace CFF;
+
+struct cff2
+{
+  static const hb_tag_t tableTag	= HB_OT_TAG_cff2;
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  likely (version.major == 2));
+  }
+
+  template <typename PRIVOPSET, typename PRIVDICTVAL>
+  struct accelerator_templ_t
+  {
+    void init (hb_face_t *face)
+    {
+      topDict.init ();
+      fontDicts.init ();
+      privateDicts.init ();
+
+      this->blob = sc.reference_table<cff2> (face);
+
+      /* setup for run-time santization */
+      sc.init (this->blob);
+      sc.start_processing ();
+
+      const OT::cff2 *cff2 = this->blob->template as<OT::cff2> ();
+
+      if (cff2 == &Null(OT::cff2))
+      { fini (); return; }
+
+      { /* parse top dict */
+	ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
+	if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
+	CFF2TopDict_Interpreter top_interp;
+	top_interp.env.init (topDictStr);
+	topDict.init ();
+	if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
+      }
+
+      globalSubrs = &StructAtOffset<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize);
+      varStore = &StructAtOffsetOrNull<CFF2VariationStore> (cff2, topDict.vstoreOffset);
+      charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset);
+      fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset);
+      fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (cff2, topDict.FDSelectOffset);
+
+      if (((varStore != &Null(CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) ||
+	  (charStrings == &Null(CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) ||
+	  (globalSubrs == &Null(CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) ||
+	  (fdArray == &Null(CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) ||
+	  (((fdSelect != &Null(CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count)))))
+      { fini (); return; }
+
+      num_glyphs = charStrings->count;
+      if (num_glyphs != sc.get_num_glyphs ())
+      { fini (); return; }
+
+      fdCount = fdArray->count;
+      privateDicts.resize (fdCount);
+
+      /* parse font dicts and gather private dicts */
+      for (unsigned int i = 0; i < fdCount; i++)
+      {
+	const ByteStr fontDictStr = (*fdArray)[i];
+	if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
+	CFF2FontDictValues  *font;
+	CFF2FontDict_Interpreter font_interp;
+	font_interp.env.init (fontDictStr);
+	font = fontDicts.push ();
+	if (unlikely (font == &Crap(CFF2FontDictValues))) { fini (); return; }
+	font->init ();
+	if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
+
+	const ByteStr privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
+	if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+	DictInterpreter<PRIVOPSET, PRIVDICTVAL, CFF2PrivDictInterpEnv>  priv_interp;
+	priv_interp.env.init(privDictStr);
+	privateDicts[i].init ();
+	if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; }
+
+	privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (privDictStr.str, privateDicts[i].subrsOffset);
+	if (privateDicts[i].localSubrs != &Null(CFF2Subrs) &&
+	  unlikely (!privateDicts[i].localSubrs->sanitize (&sc)))
+	{ fini (); return; }
+      }
+    }
+
+    void fini ()
+    {
+      sc.end_processing ();
+      fontDicts.fini_deep ();
+      privateDicts.fini_deep ();
+      hb_blob_destroy (blob);
+      blob = nullptr;
+    }
+
+    bool is_valid () const { return blob != nullptr; }
+
+    protected:
+    hb_blob_t	       *blob;
+    hb_sanitize_context_t   sc;
+
+    public:
+    CFF2TopDictValues	 topDict;
+    const CFF2Subrs	   *globalSubrs;
+    const CFF2VariationStore  *varStore;
+    const CFF2CharStrings     *charStrings;
+    const CFF2FDArray	 *fdArray;
+    const CFF2FDSelect	*fdSelect;
+    unsigned int	      fdCount;
+
+    hb_vector_t<CFF2FontDictValues>     fontDicts;
+    hb_vector_t<PRIVDICTVAL>  privateDicts;
+
+    unsigned int	    num_glyphs;
+  };
+
+  struct accelerator_t : accelerator_templ_t<CFF2PrivateDictOpSet, CFF2PrivateDictValues>
+  {
+    HB_INTERNAL bool get_extents (hb_font_t *font,
+				  hb_codepoint_t glyph,
+				  hb_glyph_extents_t *extents) const;
+  };
+
+  typedef accelerator_templ_t<CFF2PrivateDictOpSet_Subset, CFF2PrivateDictValues_Subset> accelerator_subset_t;
+
+  bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_blob_t *cff2_prime = nullptr;
+
+    bool success = true;
+    if (hb_subset_cff2 (plan, &cff2_prime)) {
+      success = success && plan->add_table (HB_OT_TAG_cff2, cff2_prime);
+      hb_blob_t *head_blob = hb_sanitize_context_t().reference_table<head> (plan->source);
+      success = success && head_blob && plan->add_table (HB_OT_TAG_head, head_blob);
+      hb_blob_destroy (head_blob);
+    } else {
+      success = false;
+    }
+    hb_blob_destroy (cff2_prime);
+
+    return success;
+  }
+
+  public:
+  FixedVersion<HBUINT8> version;	/* Version of CFF2 table. set to 0x0200u */
+  OffsetTo<TopDict, HBUINT8, false> topDict;   /* headerSize = Offset to Top DICT. */
+  HBUINT16       topDictSize;	   /* Top DICT size */
+
+  public:
+  DEFINE_SIZE_STATIC (5);
+};
+
+struct cff2_accelerator_t : cff2::accelerator_t {};
+} /* namespace OT */
+
+#endif /* HB_OT_CFF2_TABLE_HH */
diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index e5793c3..7567e52 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -41,7 +41,7 @@
 
 struct CmapSubtableFormat0
 {
-  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
+  bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
     hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0;
     if (!gid)
@@ -49,14 +49,14 @@
     *glyph = gid;
     return true;
   }
-  inline void collect_unicodes (hb_set_t *out) const
+  void collect_unicodes (hb_set_t *out) const
   {
     for (unsigned int i = 0; i < 256; i++)
       if (glyphIdArray[i])
-        out->add (i);
+	out->add (i);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -82,8 +82,8 @@
   };
 
   bool serialize (hb_serialize_context_t *c,
-                  const hb_subset_plan_t *plan,
-                  const hb_vector_t<segment_plan> &segments)
+		  const hb_subset_plan_t *plan,
+		  const hb_vector_t<segment_plan> &segments)
   {
     TRACE_SERIALIZE (this);
 
@@ -96,8 +96,8 @@
     this->entrySelector.set (MAX (1u, hb_bit_storage (segments.len)) - 1);
     this->searchRange.set (2 * (1u << this->entrySelector));
     this->rangeShift.set (segments.len * 2 > this->searchRange
-                          ? 2 * segments.len - this->searchRange
-                          : 0);
+			  ? 2 * segments.len - this->searchRange
+			  : 0);
 
     HBUINT16 *end_count = c->allocate_size<HBUINT16> (HBUINT16::static_size * segments.len);
     c->allocate_size<HBUINT16> (HBUINT16::static_size); // 2 bytes of padding.
@@ -114,70 +114,70 @@
       start_count[i].set (segments[i].start_code);
       if (segments[i].use_delta)
       {
-        hb_codepoint_t cp = segments[i].start_code;
-        hb_codepoint_t start_gid = 0;
-        if (unlikely (!plan->new_gid_for_codepoint (cp, &start_gid) && cp != 0xFFFF))
-          return_trace (false);
-        id_delta[i].set (start_gid - segments[i].start_code);
+	hb_codepoint_t cp = segments[i].start_code;
+	hb_codepoint_t start_gid = 0;
+	if (unlikely (!plan->new_gid_for_codepoint (cp, &start_gid) && cp != 0xFFFF))
+	  return_trace (false);
+	id_delta[i].set (start_gid - segments[i].start_code);
       } else {
-        id_delta[i].set (0);
-        unsigned int num_codepoints = segments[i].end_code - segments[i].start_code + 1;
-        HBUINT16 *glyph_id_array = c->allocate_size<HBUINT16> (HBUINT16::static_size * num_codepoints);
-        if (glyph_id_array == nullptr)
-          return_trace (false);
-        // From the cmap spec:
-        //
-        // id_range_offset[i]/2
-        // + (cp - segments[i].start_code)
-        // + (id_range_offset + i)
-        // =
-        // glyph_id_array + (cp - segments[i].start_code)
-        //
-        // So, solve for id_range_offset[i]:
-        //
-        // id_range_offset[i]
-        // =
-        // 2 * (glyph_id_array - id_range_offset - i)
-        id_range_offset[i].set (2 * (
-            glyph_id_array - id_range_offset - i));
-        for (unsigned int j = 0; j < num_codepoints; j++)
-        {
-          hb_codepoint_t cp = segments[i].start_code + j;
-          hb_codepoint_t new_gid;
-          if (unlikely (!plan->new_gid_for_codepoint (cp, &new_gid)))
-            return_trace (false);
-          glyph_id_array[j].set (new_gid);
-        }
+	id_delta[i].set (0);
+	unsigned int num_codepoints = segments[i].end_code - segments[i].start_code + 1;
+	HBUINT16 *glyph_id_array = c->allocate_size<HBUINT16> (HBUINT16::static_size * num_codepoints);
+	if (glyph_id_array == nullptr)
+	  return_trace (false);
+	// From the cmap spec:
+	//
+	// id_range_offset[i]/2
+	// + (cp - segments[i].start_code)
+	// + (id_range_offset + i)
+	// =
+	// glyph_id_array + (cp - segments[i].start_code)
+	//
+	// So, solve for id_range_offset[i]:
+	//
+	// id_range_offset[i]
+	// =
+	// 2 * (glyph_id_array - id_range_offset - i)
+	id_range_offset[i].set (2 * (
+	    glyph_id_array - id_range_offset - i));
+	for (unsigned int j = 0; j < num_codepoints; j++)
+	{
+	  hb_codepoint_t cp = segments[i].start_code + j;
+	  hb_codepoint_t new_gid;
+	  if (unlikely (!plan->new_gid_for_codepoint (cp, &new_gid)))
+	    return_trace (false);
+	  glyph_id_array[j].set (new_gid);
+	}
       }
     }
 
     return_trace (true);
   }
 
-  static inline size_t get_sub_table_size (const hb_vector_t<segment_plan> &segments)
+  static size_t get_sub_table_size (const hb_vector_t<segment_plan> &segments)
   {
     size_t segment_size = 0;
     for (unsigned int i = 0; i < segments.len; i++)
     {
       // Parallel array entries
       segment_size +=
-            2  // end count
-          + 2  // start count
-          + 2  // delta
-          + 2; // range offset
+	    2  // end count
+	  + 2  // start count
+	  + 2  // delta
+	  + 2; // range offset
 
       if (!segments[i].use_delta)
-        // Add bytes for the glyph index array entries for this segment.
-        segment_size += (segments[i].end_code - segments[i].start_code + 1) * 2;
+	// Add bytes for the glyph index array entries for this segment.
+	segment_size += (segments[i].end_code - segments[i].start_code + 1) * 2;
     }
 
     return min_size
-        + 2 // Padding
-        + segment_size;
+	+ 2 // Padding
+	+ segment_size;
   }
 
-  static inline bool create_sub_table_plan (const hb_subset_plan_t *plan,
-                                            hb_vector_t<segment_plan> *segments)
+  static bool create_sub_table_plan (const hb_subset_plan_t *plan,
+				     hb_vector_t<segment_plan> *segments)
   {
     segment_plan *segment = nullptr;
     hb_codepoint_t last_gid = 0;
@@ -191,24 +191,22 @@
 	return false;
       }
 
-      if (cp > 0xFFFF) {
-        // We are now outside of unicode BMP, stop adding to this cmap.
-        break;
-      }
+      /* Stop adding to cmap if we are now outside of unicode BMP. */
+      if (cp > 0xFFFF) break;
 
-      if (!segment
-          || cp != segment->end_code + 1u)
+      if (!segment ||
+	  cp != segment->end_code + 1u)
       {
-        segment = segments->push ();
-        segment->start_code.set (cp);
-        segment->end_code.set (cp);
-        segment->use_delta = true;
+	segment = segments->push ();
+	segment->start_code.set (cp);
+	segment->end_code.set (cp);
+	segment->use_delta = true;
       } else {
-        segment->end_code.set (cp);
-        if (last_gid + 1u != new_gid)
-          // gid's are not consecutive in this segment so delta
-          // cannot be used.
-          segment->use_delta = false;
+	segment->end_code.set (cp);
+	if (last_gid + 1u != new_gid)
+	  // gid's are not consecutive in this segment so delta
+	  // cannot be used.
+	  segment->use_delta = false;
       }
 
       last_gid = new_gid;
@@ -228,7 +226,11 @@
 
   struct accelerator_t
   {
-    inline void init (const CmapSubtableFormat4 *subtable)
+    accelerator_t () {}
+    accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); }
+    ~accelerator_t () { fini (); }
+
+    void init (const CmapSubtableFormat4 *subtable)
     {
       segCount = subtable->segCountX2 / 2;
       endCount = subtable->values.arrayZ;
@@ -238,9 +240,9 @@
       glyphIdArray = idRangeOffset + segCount;
       glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2;
     }
-    inline void fini (void) {}
+    void fini () {}
 
-    inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
+    bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
     {
       /* Custom two-array bsearch. */
       int min = 0, max = (int) this->segCount - 1;
@@ -249,7 +251,7 @@
       unsigned int i;
       while (min <= max)
       {
-	int mid = (min + max) / 2;
+	int mid = ((unsigned int) min + (unsigned int) max) / 2;
 	if (codepoint < startCount[mid])
 	  max = mid - 1;
 	else if (codepoint > endCount[mid])
@@ -284,15 +286,15 @@
       *glyph = gid;
       return true;
     }
-    static inline bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph)
+    static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph)
     {
       return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph);
     }
-    inline void collect_unicodes (hb_set_t *out) const
+    void collect_unicodes (hb_set_t *out) const
     {
       unsigned int count = this->segCount;
       if (count && this->startCount[count - 1] == 0xFFFFu)
-        count--; /* Skip sentinel segment. */
+	count--; /* Skip sentinel segment. */
       for (unsigned int i = 0; i < count; i++)
       {
 	unsigned int rangeOffset = this->idRangeOffset[i];
@@ -325,18 +327,18 @@
     unsigned int glyphIdArrayLength;
   };
 
-  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
+  bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
-    hb_auto_t<accelerator_t> accel (this);
+    accelerator_t accel (this);
     return accel.get_glyph_func (&accel, codepoint, glyph);
   }
-  inline void collect_unicodes (hb_set_t *out) const
+  void collect_unicodes (hb_set_t *out) const
   {
-    hb_auto_t<accelerator_t> accel (this);
+    accelerator_t accel (this);
     accel.collect_unicodes (out);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this)))
@@ -401,7 +403,7 @@
     return 0;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -415,11 +417,12 @@
   public:
   DEFINE_SIZE_STATIC (12);
 };
+DECLARE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup);
 
 template <typename UINT>
 struct CmapSubtableTrimmed
 {
-  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
+  bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
     /* Rely on our implicit array bound-checking. */
     hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode];
@@ -428,16 +431,16 @@
     *glyph = gid;
     return true;
   }
-  inline void collect_unicodes (hb_set_t *out) const
+  void collect_unicodes (hb_set_t *out) const
   {
     hb_codepoint_t start = startCharCode;
     unsigned int count = glyphIdArray.len;
     for (unsigned int i = 0; i < count; i++)
       if (glyphIdArray[i])
-        out->add (start + i);
+	out->add (start + i);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && glyphIdArray.sanitize (c));
@@ -463,19 +466,16 @@
 {
   friend struct cmap;
 
-  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
+  bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
-    int i = groups.bsearch (codepoint);
-    if (i == -1)
-      return false;
-    hb_codepoint_t gid = T::group_get_glyph (groups[i], codepoint);
+    hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint);
     if (!gid)
       return false;
     *glyph = gid;
     return true;
   }
 
-  inline void collect_unicodes (hb_set_t *out) const
+  void collect_unicodes (hb_set_t *out) const
   {
     for (unsigned int i = 0; i < this->groups.len; i++) {
       out->add_range (this->groups[i].startCharCode,
@@ -484,19 +484,18 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && groups.sanitize (c));
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-                         const hb_vector_t<CmapSubtableLongGroup> &group_data)
+  bool serialize (hb_serialize_context_t *c,
+		  const hb_vector_t<CmapSubtableLongGroup> &group_data)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    Supplier<CmapSubtableLongGroup> supplier (group_data.arrayZ(), group_data.len);
-    if (unlikely (!groups.serialize (c, supplier, group_data.len))) return_trace (false);
+    if (unlikely (!groups.serialize (c, group_data.as_array ()))) return_trace (false);
     return true;
   }
 
@@ -513,13 +512,14 @@
 
 struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
 {
-  static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
-						hb_codepoint_t u)
-  { return group.glyphID + (u - group.startCharCode); }
+  static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
+					 hb_codepoint_t u)
+  { return likely (group.startCharCode <= group.endCharCode) ?
+	   group.glyphID + (u - group.startCharCode) : 0; }
 
 
   bool serialize (hb_serialize_context_t *c,
-                  const hb_vector_t<CmapSubtableLongGroup> &groups)
+		  const hb_vector_t<CmapSubtableLongGroup> &groups)
   {
     if (unlikely (!c->extend_min (*this))) return false;
 
@@ -530,13 +530,13 @@
     return CmapSubtableLongSegmented<CmapSubtableFormat12>::serialize (c, groups);
   }
 
-  static inline size_t get_sub_table_size (const hb_vector_t<CmapSubtableLongGroup> &groups)
+  static size_t get_sub_table_size (const hb_vector_t<CmapSubtableLongGroup> &groups)
   {
     return 16 + 12 * groups.len;
   }
 
-  static inline bool create_sub_table_plan (const hb_subset_plan_t *plan,
-                                            hb_vector_t<CmapSubtableLongGroup> *groups)
+  static bool create_sub_table_plan (const hb_subset_plan_t *plan,
+				     hb_vector_t<CmapSubtableLongGroup> *groups)
   {
     CmapSubtableLongGroup *group = nullptr;
 
@@ -551,14 +551,12 @@
 
       if (!group || !_is_gid_consecutive (group, cp, new_gid))
       {
-        group = groups->push ();
-        group->startCharCode.set (cp);
-        group->endCharCode.set (cp);
-        group->glyphID.set (new_gid);
-      } else
-      {
-        group->endCharCode.set (cp);
+	group = groups->push ();
+	group->startCharCode.set (cp);
+	group->endCharCode.set (cp);
+	group->glyphID.set (new_gid);
       }
+      else group->endCharCode.set (cp);
     }
 
     DEBUG_MSG(SUBSET, nullptr, "cmap");
@@ -571,9 +569,9 @@
   }
 
  private:
-  static inline bool _is_gid_consecutive (CmapSubtableLongGroup *group,
-					  hb_codepoint_t cp,
-					  hb_codepoint_t new_gid)
+  static bool _is_gid_consecutive (CmapSubtableLongGroup *group,
+				   hb_codepoint_t cp,
+				   hb_codepoint_t new_gid)
   {
     return (cp - 1 == group->endCharCode) &&
 	new_gid == group->glyphID + (cp - group->startCharCode);
@@ -583,8 +581,8 @@
 
 struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13>
 {
-  static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
-						hb_codepoint_t u HB_UNUSED)
+  static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
+					 hb_codepoint_t u HB_UNUSED)
   { return group.glyphID; }
 };
 
@@ -597,14 +595,14 @@
 
 struct UnicodeValueRange
 {
-  inline int cmp (const hb_codepoint_t &codepoint) const
+  int cmp (const hb_codepoint_t &codepoint) const
   {
     if (codepoint < startUnicodeValue) return -1;
     if (codepoint > startUnicodeValue + additionalCount) return +1;
     return 0;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -619,7 +617,7 @@
 
 struct DefaultUVS : SortedArrayOf<UnicodeValueRange, HBUINT32>
 {
-  inline void collect_unicodes (hb_set_t *out) const
+  void collect_unicodes (hb_set_t *out) const
   {
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
@@ -632,17 +630,17 @@
   }
 
   public:
-  DEFINE_SIZE_ARRAY (4, arrayZ);
+  DEFINE_SIZE_ARRAY (4, *this);
 };
 
 struct UVSMapping
 {
-  inline int cmp (const hb_codepoint_t &codepoint) const
+  int cmp (const hb_codepoint_t &codepoint) const
   {
     return unicodeValue.cmp (codepoint);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -656,7 +654,7 @@
 
 struct NonDefaultUVS : SortedArrayOf<UVSMapping, HBUINT32>
 {
-  inline void collect_unicodes (hb_set_t *out) const
+  void collect_unicodes (hb_set_t *out) const
   {
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
@@ -664,42 +662,38 @@
   }
 
   public:
-  DEFINE_SIZE_ARRAY (4, arrayZ);
+  DEFINE_SIZE_ARRAY (4, *this);
 };
 
 struct VariationSelectorRecord
 {
-  inline glyph_variant_t get_glyph (hb_codepoint_t codepoint,
-				    hb_codepoint_t *glyph,
-				    const void *base) const
+  glyph_variant_t get_glyph (hb_codepoint_t codepoint,
+			     hb_codepoint_t *glyph,
+			     const void *base) const
   {
-    int i;
-    const DefaultUVS &defaults = base+defaultUVS;
-    i = defaults.bsearch (codepoint);
-    if (i != -1)
+    if ((base+defaultUVS).bfind (codepoint))
       return GLYPH_VARIANT_USE_DEFAULT;
-    const NonDefaultUVS &nonDefaults = base+nonDefaultUVS;
-    i = nonDefaults.bsearch (codepoint);
-    if (i != -1 && nonDefaults[i].glyphID)
+    const UVSMapping &nonDefault = (base+nonDefaultUVS).bsearch (codepoint);
+    if (nonDefault.glyphID)
     {
-      *glyph = nonDefaults[i].glyphID;
+      *glyph = nonDefault.glyphID;
        return GLYPH_VARIANT_FOUND;
     }
     return GLYPH_VARIANT_NOT_FOUND;
   }
 
-  inline void collect_unicodes (hb_set_t *out, const void *base) const
+  void collect_unicodes (hb_set_t *out, const void *base) const
   {
     (base+defaultUVS).collect_unicodes (out);
     (base+nonDefaultUVS).collect_unicodes (out);
   }
 
-  inline int cmp (const hb_codepoint_t &variation_selector) const
+  int cmp (const hb_codepoint_t &variation_selector) const
   {
     return varSelector.cmp (variation_selector);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -718,26 +712,26 @@
 
 struct CmapSubtableFormat14
 {
-  inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint,
-					    hb_codepoint_t variation_selector,
-					    hb_codepoint_t *glyph) const
+  glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint,
+				     hb_codepoint_t variation_selector,
+				     hb_codepoint_t *glyph) const
   {
-    return record[record.bsearch (variation_selector)].get_glyph (codepoint, glyph, this);
+    return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this);
   }
 
-  inline void collect_variation_selectors (hb_set_t *out) const
+  void collect_variation_selectors (hb_set_t *out) const
   {
     unsigned int count = record.len;
     for (unsigned int i = 0; i < count; i++)
       out->add (record.arrayZ[i].varSelector);
   }
-  inline void collect_variation_unicodes (hb_codepoint_t variation_selector,
-					  hb_set_t *out) const
+  void collect_variation_unicodes (hb_codepoint_t variation_selector,
+				   hb_set_t *out) const
   {
-    record[record.bsearch (variation_selector)].collect_unicodes (out, this);
+    record.bsearch (variation_selector).collect_unicodes (out, this);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -758,8 +752,8 @@
 {
   /* Note: We intentionally do NOT implement subtable formats 2 and 8. */
 
-  inline bool get_glyph (hb_codepoint_t codepoint,
-			 hb_codepoint_t *glyph) const
+  bool get_glyph (hb_codepoint_t codepoint,
+		  hb_codepoint_t *glyph) const
   {
     switch (u.format) {
     case  0: return u.format0 .get_glyph (codepoint, glyph);
@@ -772,7 +766,7 @@
     default: return false;
     }
   }
-  inline void collect_unicodes (hb_set_t *out) const
+  void collect_unicodes (hb_set_t *out) const
   {
     switch (u.format) {
     case  0: u.format0 .collect_unicodes (out); return;
@@ -786,7 +780,7 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
@@ -820,7 +814,7 @@
 
 struct EncodingRecord
 {
-  inline int cmp (const EncodingRecord &other) const
+  int cmp (const EncodingRecord &other) const
   {
     int ret;
     ret = platformID.cmp (other.platformID);
@@ -830,7 +824,7 @@
     return 0;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -847,57 +841,35 @@
 
 struct cmap
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_cmap;
+  enum { tableTag = HB_OT_TAG_cmap };
 
   struct subset_plan
   {
-    subset_plan(void)
-    {
-      format4_segments.init();
-      format12_groups.init();
-    }
-
-    ~subset_plan(void)
-    {
-      format4_segments.fini();
-      format12_groups.fini();
-    }
-
-    inline size_t final_size() const
+    size_t final_size () const
     {
       return 4 // header
-          +  8 * 3 // 3 EncodingRecord
-          +  CmapSubtableFormat4::get_sub_table_size (this->format4_segments)
-          +  CmapSubtableFormat12::get_sub_table_size (this->format12_groups);
+	  +  8 * 3 // 3 EncodingRecord
+	  +  CmapSubtableFormat4::get_sub_table_size (this->format4_segments)
+	  +  CmapSubtableFormat12::get_sub_table_size (this->format12_groups);
     }
 
-    // Format 4
     hb_vector_t<CmapSubtableFormat4::segment_plan> format4_segments;
-    // Format 12
     hb_vector_t<CmapSubtableLongGroup> format12_groups;
   };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool _create_plan (const hb_subset_plan_t *plan,
+		     subset_plan *cmap_plan) const
   {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  likely (version == 0) &&
-		  encodingRecord.sanitize (c, this));
-  }
-
-  inline bool _create_plan (const hb_subset_plan_t *plan,
-                            subset_plan *cmap_plan) const
-  {
-    if (unlikely( !CmapSubtableFormat4::create_sub_table_plan (plan, &cmap_plan->format4_segments)))
+    if (unlikely (!CmapSubtableFormat4::create_sub_table_plan (plan, &cmap_plan->format4_segments)))
       return false;
 
     return CmapSubtableFormat12::create_sub_table_plan (plan, &cmap_plan->format12_groups);
   }
 
-  inline bool _subset (const hb_subset_plan_t *plan,
-                       const subset_plan &cmap_subset_plan,
-		       size_t dest_sz,
-		       void *dest) const
+  bool _subset (const hb_subset_plan_t *plan,
+		const subset_plan &cmap_subset_plan,
+		size_t dest_sz,
+		void *dest) const
   {
     hb_serialize_context_t c (dest, dest_sz);
 
@@ -937,7 +909,7 @@
 
       CmapSubtableFormat4 &format4 = subtable.u.format4;
       if (unlikely (!format4.serialize (&c, plan, cmap_subset_plan.format4_segments)))
-        return false;
+	return false;
     }
 
     // Write out format 12 sub table.
@@ -947,7 +919,7 @@
 
       CmapSubtableFormat12 &format12 = subtable.u.format12;
       if (unlikely (!format12.serialize (&c, cmap_subset_plan.format12_groups)))
-        return false;
+	return false;
     }
 
     c.end_serialize ();
@@ -955,7 +927,7 @@
     return true;
   }
 
-  inline bool subset (hb_subset_plan_t *plan) const
+  bool subset (hb_subset_plan_t *plan) const
   {
     subset_plan cmap_subset_plan;
 
@@ -966,7 +938,7 @@
     }
 
     // We now know how big our blob needs to be
-    size_t dest_sz = cmap_subset_plan.final_size();
+    size_t dest_sz = cmap_subset_plan.final_size ();
     void *dest = malloc (dest_sz);
     if (unlikely (!dest)) {
       DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for cmap subset output", (unsigned long) dest_sz);
@@ -981,11 +953,11 @@
     }
 
     // all done, write the blob into dest
-    hb_blob_t *cmap_prime = hb_blob_create ((const char *)dest,
-                                            dest_sz,
-                                            HB_MEMORY_MODE_READONLY,
-                                            dest,
-                                            free);
+    hb_blob_t *cmap_prime = hb_blob_create ((const char *) dest,
+					    dest_sz,
+					    HB_MEMORY_MODE_READONLY,
+					    dest,
+					    free);
     bool result =  plan->add_table (HB_OT_TAG_cmap, cmap_prime);
     hb_blob_destroy (cmap_prime);
     return result;
@@ -1017,69 +989,80 @@
     }
 
     /* Meh. */
-    return &Null(CmapSubtable);
+    return &Null (CmapSubtable);
   }
 
   struct accelerator_t
   {
-    inline void init (hb_face_t *face)
+    void init (hb_face_t *face)
     {
-      this->blob = hb_sanitize_context_t().reference_table<cmap> (face);
-      const cmap *table = this->blob->as<cmap> ();
-      const CmapSubtableFormat14 *subtable_uvs = nullptr;
+      this->table = hb_sanitize_context_t ().reference_table<cmap> (face);
       bool symbol;
-      subtable = table->find_best_subtable (&symbol);
-
-      /* UVS subtable. */
-      if (!subtable_uvs)
+      this->subtable = table->find_best_subtable (&symbol);
+      this->subtable_uvs = &Null (CmapSubtableFormat14);
       {
 	const CmapSubtable *st = table->find_subtable (0, 5);
 	if (st && st->u.format == 14)
 	  subtable_uvs = &st->u.format14;
       }
-      /* Meh. */
-      if (!subtable_uvs) subtable_uvs = &Null(CmapSubtableFormat14);
-
-      this->subtable_uvs = subtable_uvs;
 
       this->get_glyph_data = subtable;
       if (unlikely (symbol))
       {
-	this->get_glyph_func = get_glyph_from_symbol<CmapSubtable>;
+	this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable>;
       } else {
 	switch (subtable->u.format) {
 	/* Accelerate format 4 and format 12. */
 	default:
-	  this->get_glyph_func = get_glyph_from<CmapSubtable>;
+	  this->get_glyph_funcZ = get_glyph_from<CmapSubtable>;
 	  break;
 	case 12:
-	  this->get_glyph_func = get_glyph_from<CmapSubtableFormat12>;
+	  this->get_glyph_funcZ = get_glyph_from<CmapSubtableFormat12>;
 	  break;
 	case  4:
 	  {
 	    this->format4_accel.init (&subtable->u.format4);
 	    this->get_glyph_data = &this->format4_accel;
-	    this->get_glyph_func = this->format4_accel.get_glyph_func;
+	    this->get_glyph_funcZ = this->format4_accel.get_glyph_func;
 	  }
 	  break;
 	}
       }
     }
 
-    inline void fini (void)
-    {
-      hb_blob_destroy (this->blob);
-    }
+    void fini () { this->table.destroy (); }
 
-    inline bool get_nominal_glyph (hb_codepoint_t  unicode,
+    bool get_nominal_glyph (hb_codepoint_t  unicode,
 				   hb_codepoint_t *glyph) const
     {
-      return this->get_glyph_func (this->get_glyph_data, unicode, glyph);
+      if (unlikely (!this->get_glyph_funcZ)) return false;
+      return this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph);
+    }
+    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) const
+    {
+      if (unlikely (!this->get_glyph_funcZ)) return 0;
+
+      hb_cmap_get_glyph_func_t get_glyph_funcZ = this->get_glyph_funcZ;
+      const void *get_glyph_data = this->get_glyph_data;
+
+      unsigned int done;
+      for (done = 0;
+	   done < count && get_glyph_funcZ (get_glyph_data, *first_unicode, first_glyph);
+	   done++)
+      {
+	first_unicode = &StructAtOffset<hb_codepoint_t> (first_unicode, unicode_stride);
+	first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+      }
+      return done;
     }
 
-    inline bool get_variation_glyph (hb_codepoint_t  unicode,
-				     hb_codepoint_t  variation_selector,
-				     hb_codepoint_t *glyph) const
+    bool get_variation_glyph (hb_codepoint_t  unicode,
+			      hb_codepoint_t  variation_selector,
+			      hb_codepoint_t *glyph) const
     {
       switch (this->subtable_uvs->get_glyph_variant (unicode,
 						     variation_selector,
@@ -1093,16 +1076,16 @@
       return get_nominal_glyph (unicode, glyph);
     }
 
-    inline void collect_unicodes (hb_set_t *out) const
+    void collect_unicodes (hb_set_t *out) const
     {
       subtable->collect_unicodes (out);
     }
-    inline void collect_variation_selectors (hb_set_t *out) const
+    void collect_variation_selectors (hb_set_t *out) const
     {
       subtable_uvs->collect_variation_selectors (out);
     }
-    inline void collect_variation_unicodes (hb_codepoint_t variation_selector,
-					    hb_set_t *out) const
+    void collect_variation_unicodes (hb_codepoint_t variation_selector,
+				     hb_set_t *out) const
     {
       subtable_uvs->collect_variation_unicodes (variation_selector, out);
     }
@@ -1113,16 +1096,16 @@
 					      hb_codepoint_t *glyph);
 
     template <typename Type>
-    static inline bool get_glyph_from (const void *obj,
-				       hb_codepoint_t codepoint,
-				       hb_codepoint_t *glyph)
+    static bool get_glyph_from (const void *obj,
+				hb_codepoint_t codepoint,
+				hb_codepoint_t *glyph)
     {
       const Type *typed_obj = (const Type *) obj;
       return typed_obj->get_glyph (codepoint, glyph);
     }
 
     template <typename Type>
-    static inline bool get_glyph_from_symbol (const void *obj,
+    static bool get_glyph_from_symbol (const void *obj,
 					      hb_codepoint_t codepoint,
 					      hb_codepoint_t *glyph)
     {
@@ -1144,31 +1127,41 @@
     }
 
     private:
-    const CmapSubtable *subtable;
-    const CmapSubtableFormat14 *subtable_uvs;
+    hb_nonnull_ptr_t<const CmapSubtable> subtable;
+    hb_nonnull_ptr_t<const CmapSubtableFormat14> subtable_uvs;
 
-    hb_cmap_get_glyph_func_t get_glyph_func;
+    hb_cmap_get_glyph_func_t get_glyph_funcZ;
     const void *get_glyph_data;
 
     CmapSubtableFormat4::accelerator_t format4_accel;
 
-    hb_blob_t *blob;
+    hb_blob_ptr_t<cmap> table;
   };
 
   protected:
 
-  inline const CmapSubtable *find_subtable (unsigned int platform_id,
-					    unsigned int encoding_id) const
+  const CmapSubtable *find_subtable (unsigned int platform_id,
+				     unsigned int encoding_id) const
   {
     EncodingRecord key;
     key.platformID.set (platform_id);
     key.encodingID.set (encoding_id);
 
-    int result = encodingRecord.bsearch (key);
-    if (result == -1 || !encodingRecord[result].subtable)
+    const EncodingRecord &result = encodingRecord.bsearch (key);
+    if (!result.subtable)
       return nullptr;
 
-    return &(this+encodingRecord[result].subtable);
+    return &(this+result.subtable);
+  }
+
+  public:
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  likely (version == 0) &&
+		  encodingRecord.sanitize (c, this));
   }
 
   protected:
diff --git a/src/hb-ot-color-cbdt-table.hh b/src/hb-ot-color-cbdt-table.hh
index 1e1fe09..b1971a7 100644
--- a/src/hb-ot-color-cbdt-table.hh
+++ b/src/hb-ot-color-cbdt-table.hh
@@ -45,13 +45,13 @@
 
 struct SmallGlyphMetrics
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
-  inline void get_extents (hb_glyph_extents_t *extents) const
+  void get_extents (hb_glyph_extents_t *extents) const
   {
     extents->x_bearing = bearingX;
     extents->y_bearing = bearingY;
@@ -79,7 +79,7 @@
 
 struct SBitLineMetrics
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -108,7 +108,7 @@
 
 struct IndexSubtableHeader
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -124,7 +124,7 @@
 template <typename OffsetType>
 struct IndexSubtableFormat1Or3
 {
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
+  bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -155,7 +155,7 @@
 
 struct IndexSubtable
 {
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
+  bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
   {
     TRACE_SANITIZE (this);
     if (!u.header.sanitize (c)) return_trace (false);
@@ -166,7 +166,7 @@
     }
   }
 
-  inline bool get_extents (hb_glyph_extents_t *extents) const
+  bool get_extents (hb_glyph_extents_t *extents HB_UNUSED) const
   {
     switch (u.header.indexFormat) {
     case 2: case 5: /* TODO */
@@ -201,7 +201,7 @@
 
 struct IndexSubtableRecord
 {
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -209,8 +209,8 @@
 		  offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1));
   }
 
-  inline bool get_extents (hb_glyph_extents_t *extents,
-			   const void *base) const
+  bool get_extents (hb_glyph_extents_t *extents,
+		    const void *base) const
   {
     return (base+offsetToSubtable).get_extents (extents);
   }
@@ -237,7 +237,7 @@
 {
   friend struct CBDT;
 
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
   {
     TRACE_SANITIZE (this);
     return_trace (indexSubtablesZ.sanitize (c, count, this));
@@ -265,7 +265,7 @@
   friend struct CBLC;
   friend struct CBDT;
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -332,9 +332,9 @@
 {
   friend struct CBDT;
 
-  static const hb_tag_t tableTag = HB_OT_TAG_CBLC;
+  enum { tableTag = HB_OT_TAG_CBLC };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -343,26 +343,30 @@
   }
 
   protected:
-  const IndexSubtableRecord *find_table (hb_codepoint_t glyph,
-					 unsigned int *x_ppem, unsigned int *y_ppem,
-					 const void **base) const
+  const BitmapSizeTable &choose_strike (hb_font_t *font) const
   {
-    /* TODO: Make it possible to select strike. */
+    unsigned count = sizeTables.len;
+    if (unlikely (!count))
+      return Null(BitmapSizeTable);
 
-    unsigned int count = sizeTables.len;
-    for (uint32_t i = 0; i < count; ++i)
+    unsigned int requested_ppem = MAX (font->x_ppem, font->y_ppem);
+    if (!requested_ppem)
+      requested_ppem = 1<<30; /* Choose largest strike. */
+    unsigned int best_i = 0;
+    unsigned int best_ppem = MAX (sizeTables[0].ppemX, sizeTables[0].ppemY);
+
+    for (unsigned int i = 1; i < count; i++)
     {
-      unsigned int startGlyphIndex = sizeTables.arrayZ[i].startGlyphIndex;
-      unsigned int endGlyphIndex = sizeTables.arrayZ[i].endGlyphIndex;
-      if (startGlyphIndex <= glyph && glyph <= endGlyphIndex)
+      unsigned int ppem = MAX (sizeTables[i].ppemX, sizeTables[i].ppemY);
+      if ((requested_ppem <= ppem && ppem < best_ppem) ||
+	  (requested_ppem > best_ppem && ppem > best_ppem))
       {
-	*x_ppem = sizeTables[i].ppemX;
-	*y_ppem = sizeTables[i].ppemY;
-	return sizeTables[i].find_table (glyph, this, base);
+	best_i = i;
+	best_ppem = ppem;
       }
     }
 
-    return nullptr;
+    return sizeTables[best_i];
   }
 
   protected:
@@ -374,51 +378,31 @@
 
 struct CBDT
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_CBDT;
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  likely (version.major == 2 || version.major == 3));
-  }
+  enum { tableTag = HB_OT_TAG_CBDT };
 
   struct accelerator_t
   {
-    inline void init (hb_face_t *face)
+    void init (hb_face_t *face)
     {
+      cblc = hb_sanitize_context_t().reference_table<CBLC> (face);
+      cbdt = hb_sanitize_context_t().reference_table<CBDT> (face);
+
       upem = hb_face_get_upem (face);
-
-      cblc_blob = hb_sanitize_context_t().reference_table<CBLC> (face);
-      cbdt_blob = hb_sanitize_context_t().reference_table<CBDT> (face);
-      cbdt_len = hb_blob_get_length (cbdt_blob);
-
-      if (hb_blob_get_length (cblc_blob) == 0) {
-	cblc = nullptr;
-	cbdt = nullptr;
-	return;  /* Not a bitmap font. */
-      }
-      cblc = cblc_blob->as<CBLC> ();
-      cbdt = cbdt_blob->as<CBDT> ();
-
     }
 
-    inline void fini (void)
+    void fini ()
     {
-      hb_blob_destroy (this->cblc_blob);
-      hb_blob_destroy (this->cbdt_blob);
+      this->cblc.destroy ();
+      this->cbdt.destroy ();
     }
 
-    inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
+    bool get_extents (hb_font_t *font, hb_codepoint_t glyph,
+		      hb_glyph_extents_t *extents) const
     {
-      unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */
-
-      if (!cblc)
-	return false;  // Not a color bitmap font.
-
       const void *base;
-      const IndexSubtableRecord *subtable_record = this->cblc->find_table (glyph, &x_ppem, &y_ppem, &base);
-      if (!subtable_record || !x_ppem || !y_ppem)
+      const BitmapSizeTable &strike = this->cblc->choose_strike (font);
+      const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base);
+      if (!subtable_record || !strike.ppemX || !strike.ppemY)
 	return false;
 
       if (subtable_record->get_extents (extents, base))
@@ -429,6 +413,7 @@
 	return false;
 
       {
+	unsigned int cbdt_len = cbdt.get_length ();
 	if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length))
 	  return false;
 
@@ -437,90 +422,104 @@
 	  case 17: {
 	    if (unlikely (image_length < GlyphBitmapDataFormat17::min_size))
 	      return false;
-
 	    const GlyphBitmapDataFormat17& glyphFormat17 =
 		StructAtOffset<GlyphBitmapDataFormat17> (this->cbdt, image_offset);
 	    glyphFormat17.glyphMetrics.get_extents (extents);
+	    break;
 	  }
-	  break;
+	  case 18: {
+	    if (unlikely (image_length < GlyphBitmapDataFormat18::min_size))
+	      return false;
+	    const GlyphBitmapDataFormat18& glyphFormat18 =
+		StructAtOffset<GlyphBitmapDataFormat18> (this->cbdt, image_offset);
+	    glyphFormat18.glyphMetrics.get_extents (extents);
+	    break;
+	  }
 	  default:
 	    // TODO: Support other image formats.
 	    return false;
 	}
       }
 
-      /* Convert to the font units. */
-      extents->x_bearing *= upem / (float) x_ppem;
-      extents->y_bearing *= upem / (float) y_ppem;
-      extents->width *= upem / (float) x_ppem;
-      extents->height *= upem / (float) y_ppem;
+      /* Convert to font units. */
+      double x_scale = upem / (double) strike.ppemX;
+      double y_scale = upem / (double) strike.ppemY;
+      extents->x_bearing = round (extents->x_bearing * x_scale);
+      extents->y_bearing = round (extents->y_bearing * y_scale);
+      extents->width = round (extents->width * x_scale);
+      extents->height = round (extents->height * y_scale);
 
       return true;
     }
 
-    inline void dump (void (*callback) (const uint8_t* data, unsigned int length,
-        unsigned int group, unsigned int gid)) const
+    hb_blob_t* reference_png (hb_font_t      *font,
+				     hb_codepoint_t  glyph) const
     {
-      if (!cblc)
-	return;  // Not a color bitmap font.
+      const void *base;
+      const BitmapSizeTable &strike = this->cblc->choose_strike (font);
+      const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base);
+      if (!subtable_record || !strike.ppemX || !strike.ppemY)
+	return hb_blob_get_empty ();
 
-      for (unsigned int i = 0; i < cblc->sizeTables.len; ++i)
+      unsigned int image_offset = 0, image_length = 0, image_format = 0;
+      if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format))
+	return hb_blob_get_empty ();
+
       {
-        const BitmapSizeTable &sizeTable = cblc->sizeTables[i];
-        const IndexSubtableArray &subtable_array = cblc+sizeTable.indexSubtableArrayOffset;
-        for (unsigned int j = 0; j < sizeTable.numberOfIndexSubtables; ++j)
-        {
-          const IndexSubtableRecord &subtable_record = subtable_array.indexSubtablesZ[j];
-          for (unsigned int gid = subtable_record.firstGlyphIndex;
-                gid <= subtable_record.lastGlyphIndex; ++gid)
-          {
-            unsigned int image_offset = 0, image_length = 0, image_format = 0;
+	unsigned int cbdt_len = cbdt.get_length ();
+	if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length))
+	  return hb_blob_get_empty ();
 
-            if (!subtable_record.get_image_data (gid, &subtable_array,
-                  &image_offset, &image_length, &image_format))
-              continue;
-
-            switch (image_format)
-            {
-            case 17: {
-              const GlyphBitmapDataFormat17& glyphFormat17 =
-                StructAtOffset<GlyphBitmapDataFormat17> (this->cbdt, image_offset);
-              callback ((const uint8_t *) &glyphFormat17.data.arrayZ,
-                glyphFormat17.data.len, i, gid);
-            }
-            break;
-            case 18: {
-              const GlyphBitmapDataFormat18& glyphFormat18 =
-                StructAtOffset<GlyphBitmapDataFormat18> (this->cbdt, image_offset);
-              callback ((const uint8_t *) &glyphFormat18.data.arrayZ,
-                glyphFormat18.data.len, i, gid);
-            }
-            break;
-            case 19: {
-              const GlyphBitmapDataFormat19& glyphFormat19 =
-                StructAtOffset<GlyphBitmapDataFormat19> (this->cbdt, image_offset);
-              callback ((const uint8_t *) &glyphFormat19.data.arrayZ,
-                glyphFormat19.data.len, i, gid);
-            }
-            break;
-            default:
-              continue;
-            }
-          }
-        }
+	switch (image_format)
+	{
+	  case 17: {
+	    if (unlikely (image_length < GlyphBitmapDataFormat17::min_size))
+	      return hb_blob_get_empty ();
+	    const GlyphBitmapDataFormat17& glyphFormat17 =
+	      StructAtOffset<GlyphBitmapDataFormat17> (this->cbdt, image_offset);
+	    return hb_blob_create_sub_blob (cbdt.get_blob (),
+					    image_offset + GlyphBitmapDataFormat17::min_size,
+					    glyphFormat17.data.len);
+	  }
+	  case 18: {
+	    if (unlikely (image_length < GlyphBitmapDataFormat18::min_size))
+	      return hb_blob_get_empty ();
+	    const GlyphBitmapDataFormat18& glyphFormat18 =
+	      StructAtOffset<GlyphBitmapDataFormat18> (this->cbdt, image_offset);
+	    return hb_blob_create_sub_blob (cbdt.get_blob (),
+					    image_offset + GlyphBitmapDataFormat18::min_size,
+					    glyphFormat18.data.len);
+	  }
+	  case 19: {
+	    if (unlikely (image_length < GlyphBitmapDataFormat19::min_size))
+	      return hb_blob_get_empty ();
+	    const GlyphBitmapDataFormat19& glyphFormat19 =
+	      StructAtOffset<GlyphBitmapDataFormat19> (this->cbdt, image_offset);
+	    return hb_blob_create_sub_blob (cbdt.get_blob (),
+					    image_offset + GlyphBitmapDataFormat19::min_size,
+					    glyphFormat19.data.len);
+	  }
+	}
       }
+
+      return hb_blob_get_empty ();
     }
 
-    private:
-    hb_blob_t *cblc_blob;
-    hb_blob_t *cbdt_blob;
-    const CBLC *cblc;
-    const CBDT *cbdt;
+    bool has_data () const { return cbdt.get_length (); }
 
-    unsigned int cbdt_len;
+    private:
+    hb_blob_ptr_t<CBLC> cblc;
+    hb_blob_ptr_t<CBDT> cbdt;
+
     unsigned int upem;
   };
 
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  likely (version.major == 2 || version.major == 3));
+  }
 
   protected:
   FixedVersion<>		version;
diff --git a/src/hb-ot-color-colr-table.hh b/src/hb-ot-color-colr-table.hh
index a59d2bf..3883604 100644
--- a/src/hb-ot-color-colr-table.hh
+++ b/src/hb-ot-color-colr-table.hh
@@ -39,55 +39,82 @@
 
 struct LayerRecord
 {
-  friend struct COLR;
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
-  protected:
-  GlyphID	glyphid;	/* Glyph ID of layer glyph */
-  HBUINT16	colorIdx;	/* Index value to use with a selected color palette */
+  public:
+  GlyphID	glyphId;	/* Glyph ID of layer glyph */
+  Index		colorIdx;	/* Index value to use with a
+				 * selected color palette.
+				 * An index value of 0xFFFF
+				 * is a special case indicating
+				 * that the text foreground
+				 * color (defined by a
+				 * higher-level client) should
+				 * be used and shall not be
+				 * treated as actual index
+				 * into CPAL ColorRecord array. */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
 struct BaseGlyphRecord
 {
-  friend struct COLR;
+  int cmp (hb_codepoint_t g) const
+  { return g < glyphId ? -1 : g > glyphId ? 1 : 0; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this)));
   }
 
-  inline int cmp (hb_codepoint_t g) const {
-    return g < glyphid ? -1 : g > glyphid ? 1 : 0;
-  }
-
-  protected:
-  GlyphID	glyphid;	/* Glyph ID of reference glyph */
-  HBUINT16	firstLayerIdx;	/* Index to the layer record */
-  HBUINT16	numLayers;	/* Number of color layers associated with this glyph */
+  public:
+  GlyphID	glyphId;	/* Glyph ID of reference glyph */
+  HBUINT16	firstLayerIdx;	/* Index (from beginning of
+				 * the Layer Records) to the
+				 * layer record. There will be
+				 * numLayers consecutive entries
+				 * for this base glyph. */
+  HBUINT16	numLayers;	/* Number of color layers
+				 * associated with this glyph */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
-static int compare_bgr (const void *pa, const void *pb)
-{
-  const hb_codepoint_t *a = (const hb_codepoint_t *) pa;
-  const BaseGlyphRecord *b = (const BaseGlyphRecord *) pb;
-  return b->cmp (*a);
-}
-
 struct COLR
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_COLR;
+  enum { tableTag = HB_OT_TAG_COLR };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool has_data () const { return numBaseGlyphs; }
+
+  unsigned int get_glyph_layers (hb_codepoint_t       glyph,
+				 unsigned int         start_offset,
+				 unsigned int        *count, /* IN/OUT.  May be NULL. */
+				 hb_ot_color_layer_t *layers /* OUT.     May be NULL. */) const
+  {
+    const BaseGlyphRecord &record = (this+baseGlyphsZ).bsearch (numBaseGlyphs, glyph);
+
+    hb_array_t<const LayerRecord> all_layers ((this+layersZ).arrayZ, numLayers);
+    hb_array_t<const LayerRecord> glyph_layers = all_layers.sub_array (record.firstLayerIdx,
+								       record.numLayers);
+    if (count)
+    {
+      hb_array_t<const LayerRecord> segment_layers = glyph_layers.sub_array (start_offset, *count);
+      *count = segment_layers.len;
+      for (unsigned int i = 0; i < segment_layers.len; i++)
+      {
+        layers[i].glyph = segment_layers.arrayZ[i].glyphId;
+        layers[i].color_index = segment_layers.arrayZ[i].colorIdx;
+      }
+    }
+    return glyph_layers.len;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
@@ -95,45 +122,14 @@
 			  (this+layersZ).sanitize (c, numLayers)));
   }
 
-  inline bool get_base_glyph_record (hb_codepoint_t glyph_id,
-				     unsigned int *first_layer /* OUT */,
-				     unsigned int *num_layers /* OUT */) const
-  {
-    const BaseGlyphRecord* record;
-    record = (BaseGlyphRecord *) bsearch (&glyph_id, &(this+baseGlyphsZ), numBaseGlyphs,
-					  sizeof (BaseGlyphRecord), compare_bgr);
-    if (unlikely (!record))
-      return false;
-
-    *first_layer = record->firstLayerIdx;
-    *num_layers = record->numLayers;
-    return true;
-  }
-
-  inline bool get_layer_record (unsigned int record,
-				hb_codepoint_t *glyph_id /* OUT */,
-				unsigned int *palette_index /* OUT */) const
-  {
-    if (unlikely (record >= numLayers))
-    {
-      *glyph_id = 0;
-      *palette_index = 0xFFFF;
-      return false;
-    }
-    const LayerRecord &layer = (this+layersZ)[record];
-    *glyph_id = layer.glyphid;
-    *palette_index = layer.colorIdx;
-    return true;
-  }
-
   protected:
-  HBUINT16	version;	/* Table version number */
-  HBUINT16	numBaseGlyphs;	/* Number of Base Glyph Records */
-  LOffsetTo<UnsizedArrayOf<BaseGlyphRecord>, false>
+  HBUINT16	version;	/* Table version number (starts at 0). */
+  HBUINT16	numBaseGlyphs;	/* Number of Base Glyph Records. */
+  LOffsetTo<SortedUnsizedArrayOf<BaseGlyphRecord>, false>
 		baseGlyphsZ;	/* Offset to Base Glyph records. */
   LOffsetTo<UnsizedArrayOf<LayerRecord>, false>
-		layersZ;	/* Offset to Layer Records */
-  HBUINT16	numLayers;	/* Number of Layer Records */
+		layersZ;	/* Offset to Layer Records. */
+  HBUINT16	numLayers;	/* Number of Layer Records. */
   public:
   DEFINE_SIZE_STATIC (14);
 };
diff --git a/src/hb-ot-color-cpal-table.hh b/src/hb-ot-color-cpal-table.hh
index e354ced..bd57d8a 100644
--- a/src/hb-ot-color-cpal-table.hh
+++ b/src/hb-ot-color-cpal-table.hh
@@ -29,53 +29,8 @@
 #define HB_OT_COLOR_CPAL_TABLE_HH
 
 #include "hb-open-type.hh"
-
-
-/*
- * Following parts to be moved to a public header.
- */
-
-/**
- * hb_ot_color_t:
- * ARGB data type for holding color values.
- *
- * Since: REPLACEME
- */
-typedef uint32_t hb_ot_color_t;
-
-
-/**
- * hb_ot_color_palette_flags_t:
- * @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: default indicating that there is nothing special to note about a color palette.
- * @HB_OT_COLOR_PALETTE_FLAG_FOR_LIGHT_BACKGROUND: flag indicating that the color palette is suitable for rendering text on light background.
- * @HB_OT_COLOR_PALETTE_FLAG_FOR_DARK_BACKGROUND: flag indicating that the color palette is suitable for rendering text on dark background.
- *
- * Since: REPLACEME
- */
-typedef enum { /*< flags >*/
-  HB_OT_COLOR_PALETTE_FLAG_DEFAULT = 0x00000000u,
-  HB_OT_COLOR_PALETTE_FLAG_FOR_LIGHT_BACKGROUND = 0x00000001u,
-  HB_OT_COLOR_PALETTE_FLAG_FOR_DARK_BACKGROUND = 0x00000002u,
-} hb_ot_color_palette_flags_t;
-
-// HB_EXTERN unsigned int
-// hb_ot_color_get_palette_count (hb_face_t *face);
-
-// HB_EXTERN unsigned int
-// hb_ot_color_get_palette_name_id (hb_face_t *face, unsigned int palette);
-
-// HB_EXTERN hb_ot_color_palette_flags_t
-// hb_ot_color_get_palette_flags (hb_face_t *face, unsigned int palette);
-
-// HB_EXTERN unsigned int
-// hb_ot_color_get_palette_colors (hb_face_t       *face,
-// 				unsigned int     palette, /* default=0 */
-// 				unsigned int     start_offset,
-// 				unsigned int    *color_count /* IN/OUT */,
-// 				hb_ot_color_t   *colors /* OUT */);
-
-
-
+#include "hb-ot-color.h"
+#include "hb-ot-name.h"
 
 
 /*
@@ -92,29 +47,43 @@
 {
   friend struct CPAL;
 
-  inline bool
-  sanitize (hb_sanitize_context_t *c, const void *base, unsigned int palettes) const
+  private:
+  hb_ot_color_palette_flags_t get_palette_flags (const void *base,
+						 unsigned int palette_index,
+						 unsigned int palette_count) const
+  {
+    if (!paletteFlagsZ) return HB_OT_COLOR_PALETTE_FLAG_DEFAULT;
+    return (hb_ot_color_palette_flags_t) (uint32_t)
+	   (base+paletteFlagsZ).as_array (palette_count)[palette_index];
+  }
+
+  hb_ot_name_id_t get_palette_name_id (const void *base,
+				       unsigned int palette_index,
+				       unsigned int palette_count) const
+  {
+    if (!paletteLabelsZ) return HB_OT_NAME_ID_INVALID;
+    return (base+paletteLabelsZ).as_array (palette_count)[palette_index];
+  }
+
+  hb_ot_name_id_t get_color_name_id (const void *base,
+				     unsigned int color_index,
+				     unsigned int color_count) const
+  {
+    if (!colorLabelsZ) return HB_OT_NAME_ID_INVALID;
+    return (base+colorLabelsZ).as_array (color_count)[color_index];
+  }
+
+  public:
+  bool sanitize (hb_sanitize_context_t *c,
+		 const void *base,
+		 unsigned int palette_count,
+		 unsigned int color_count) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  (base+paletteFlagsZ).sanitize (c, palettes) &&
-		  (base+paletteLabelZ).sanitize (c, palettes) &&
-		  (base+paletteEntryLabelZ).sanitize (c, palettes));
-  }
-
-  private:
-  inline hb_ot_color_palette_flags_t
-  get_palette_flags (const void *base, unsigned int palette) const
-  {
-    // range checked at the CPAL caller
-    return (hb_ot_color_palette_flags_t) (uint32_t) (base+paletteFlagsZ)[palette];
-  }
-
-  inline unsigned int
-  get_palette_name_id (const void *base, unsigned int palette) const
-  {
-    // range checked at the CPAL caller
-    return (base+paletteLabelZ)[palette];
+		  (!paletteFlagsZ  || (base+paletteFlagsZ).sanitize (c, palette_count)) &&
+		  (!paletteLabelsZ || (base+paletteLabelsZ).sanitize (c, palette_count)) &&
+		  (!colorLabelsZ   || (base+colorLabelsZ).sanitize (c, color_count)));
   }
 
   protected:
@@ -122,13 +91,13 @@
 		paletteFlagsZ;		/* Offset from the beginning of CPAL table to
 					 * the Palette Type Array. Set to 0 if no array
 					 * is provided. */
-  LOffsetTo<UnsizedArrayOf<HBUINT16>, false>
-		paletteLabelZ;		/* Offset from the beginning of CPAL table to
-					 * the Palette Labels Array. Set to 0 if no
+  LOffsetTo<UnsizedArrayOf<NameID>, false>
+		paletteLabelsZ;		/* Offset from the beginning of CPAL table to
+					 * the palette labels array. Set to 0 if no
 					 * array is provided. */
-  LOffsetTo<UnsizedArrayOf<HBUINT16>, false>
-		paletteEntryLabelZ;	/* Offset from the beginning of CPAL table to
-					 * the Palette Entry Label Array. Set to 0
+  LOffsetTo<UnsizedArrayOf<NameID>, false>
+		colorLabelsZ;		/* Offset from the beginning of CPAL table to
+					 * the color labels array. Set to 0
 					 * if no array is provided. */
   public:
   DEFINE_SIZE_STATIC (12);
@@ -138,72 +107,72 @@
 
 struct CPAL
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_CPAL;
+  enum { tableTag = HB_OT_TAG_CPAL };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool has_data () const { return numPalettes; }
+
+  unsigned int get_size () const
+  { return min_size + numPalettes * sizeof (colorRecordIndicesZ[0]); }
+
+  unsigned int get_palette_count () const { return numPalettes; }
+  unsigned int get_color_count () const   { return numColors; }
+
+  hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette_index) const
+  { return v1 ().get_palette_flags (this, palette_index, numPalettes); }
+
+  hb_ot_name_id_t get_palette_name_id (unsigned int palette_index) const
+  { return v1 ().get_palette_name_id (this, palette_index, numPalettes); }
+
+  hb_ot_name_id_t get_color_name_id (unsigned int color_index) const
+  { return v1 ().get_color_name_id (this, color_index, numColors); }
+
+  unsigned int get_palette_colors (unsigned int  palette_index,
+				   unsigned int  start_offset,
+				   unsigned int *color_count, /* IN/OUT.  May be NULL. */
+				   hb_color_t   *colors       /* OUT.     May be NULL. */) const
+  {
+    if (unlikely (palette_index >= numPalettes))
+    {
+      if (color_count) *color_count = 0;
+      return 0;
+    }
+    unsigned int start_index = colorRecordIndicesZ[palette_index];
+    hb_array_t<const BGRAColor> all_colors ((this+colorRecordsZ).arrayZ, numColorRecords);
+    hb_array_t<const BGRAColor> palette_colors = all_colors.sub_array (start_index,
+								       numColors);
+    if (color_count)
+    {
+      hb_array_t<const BGRAColor> segment_colors = palette_colors.sub_array (start_offset, *color_count);
+      /* Always return numColors colors per palette even if it has out-of-bounds start index. */
+      unsigned int count = MIN<unsigned int> (MAX<int> (numColors - start_offset, 0), *color_count);
+      *color_count = count;
+      for (unsigned int i = 0; i < count; i++)
+        colors[i] = segment_colors[i]; /* Bound-checked read. */
+    }
+    return numColors;
+  }
+
+  private:
+  const CPALV1Tail& v1 () const
+  {
+    if (version == 0) return Null(CPALV1Tail);
+    return StructAfter<CPALV1Tail> (*this);
+  }
+
+  public:
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!(c->check_struct (this) &&	// it checks colorRecordIndices also
-						// see #get_size
-		    (this+colorRecordsZ).sanitize (c, numColorRecords))))
-      return_trace (false);
-
-    // Check for indices sanity so no need for doing it runtime
-    for (unsigned int i = 0; i < numPalettes; ++i)
-      if (unlikely (colorRecordIndicesZ[i] + numPaletteEntries > numColorRecords))
-	return_trace (false);
-
-    // If version is zero, we are done here; otherwise we need to check tail also
-    if (version == 0)
-      return_trace (true);
-
-    const CPALV1Tail &v1 = StructAfter<CPALV1Tail> (*this);
-    return_trace (likely (v1.sanitize (c, this, numPalettes)));
-  }
-
-  inline unsigned int get_size (void) const
-  {
-    return min_size + numPalettes * sizeof (HBUINT16);
-  }
-
-  inline hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette) const
-  {
-    if (unlikely (version == 0 || palette >= numPalettes))
-      return HB_OT_COLOR_PALETTE_FLAG_DEFAULT;
-
-    const CPALV1Tail& cpal1 = StructAfter<CPALV1Tail> (*this);
-    return cpal1.get_palette_flags (this, palette);
-  }
-
-  inline unsigned int get_palette_name_id (unsigned int palette) const
-  {
-    if (unlikely (version == 0 || palette >= numPalettes))
-      return 0xFFFF;
-
-    const CPALV1Tail& cpal1 = StructAfter<CPALV1Tail> (*this);
-    return cpal1.get_palette_name_id (this, palette);
-  }
-
-  inline unsigned int get_palette_count () const
-  {
-    return numPalettes;
-  }
-
-  inline hb_ot_color_t
-  get_color_record_argb (unsigned int color_index, unsigned int palette) const
-  {
-    if (unlikely (color_index >= numPaletteEntries || palette >= numPalettes))
-      return 0;
-
-    // No need for more range check as it is already done on #sanitize
-    const UnsizedArrayOf<BGRAColor>& color_records = this+colorRecordsZ;
-    return color_records[colorRecordIndicesZ[palette] + color_index];
+    return_trace (c->check_struct (this) &&
+		  (this+colorRecordsZ).sanitize (c, numColorRecords) &&
+		  colorRecordIndicesZ.sanitize (c, numPalettes) &&
+		  (version == 0 || v1 ().sanitize (c, this, numPalettes, numColors)));
   }
 
   protected:
   HBUINT16	version;		/* Table version number */
   /* Version 0 */
-  HBUINT16	numPaletteEntries;	/* Number of palette entries in each palette. */
+  HBUINT16	numColors;		/* Number of colors in each palette. */
   HBUINT16	numPalettes;		/* Number of palettes in the table. */
   HBUINT16	numColorRecords;	/* Total number of color records, combined for
 					 * all palettes. */
diff --git a/src/hb-ot-color-sbix-table.hh b/src/hb-ot-color-sbix-table.hh
index 1b643c7..ed93d65 100644
--- a/src/hb-ot-color-sbix-table.hh
+++ b/src/hb-ot-color-sbix-table.hh
@@ -62,19 +62,65 @@
 
 struct SBIXStrike
 {
-  friend struct sbix;
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  imageOffsetsZ.sanitize_shallow (c, c->get_num_glyphs () + 1));
   }
 
-  protected:
+  hb_blob_t *get_glyph_blob (unsigned int  glyph_id,
+			     hb_blob_t    *sbix_blob,
+			     hb_tag_t      file_type,
+			     int          *x_offset,
+			     int          *y_offset,
+			     unsigned int  num_glyphs,
+			     unsigned int *strike_ppem) const
+  {
+    if (unlikely (!ppem)) return hb_blob_get_empty (); /* To get Null() object out of the way. */
+
+    unsigned int retry_count = 8;
+    unsigned int sbix_len = sbix_blob->length;
+    unsigned int strike_offset = (const char *) this - (const char *) sbix_blob->data;
+    assert (strike_offset < sbix_len);
+
+  retry:
+    if (unlikely (glyph_id >= num_glyphs ||
+		  imageOffsetsZ[glyph_id + 1] <= imageOffsetsZ[glyph_id] ||
+		  imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] <= SBIXGlyph::min_size ||
+		  (unsigned int) imageOffsetsZ[glyph_id + 1] > sbix_len - strike_offset))
+      return hb_blob_get_empty ();
+
+    unsigned int glyph_offset = strike_offset + (unsigned int) imageOffsetsZ[glyph_id] + SBIXGlyph::min_size;
+    unsigned int glyph_length = imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] - SBIXGlyph::min_size;
+
+    const SBIXGlyph *glyph = &(this+imageOffsetsZ[glyph_id]);
+
+    if (glyph->graphicType == HB_TAG ('d','u','p','e'))
+    {
+      if (glyph_length >= 2)
+      {
+	glyph_id = *((HBUINT16 *) &glyph->data);
+	if (retry_count--)
+	  goto retry;
+      }
+      return hb_blob_get_empty ();
+    }
+
+    if (unlikely (file_type != glyph->graphicType))
+      return hb_blob_get_empty ();
+
+    if (strike_ppem) *strike_ppem = ppem;
+    if (x_offset) *x_offset = glyph->xOffset;
+    if (y_offset) *y_offset = glyph->yOffset;
+    return hb_blob_create_sub_blob (sbix_blob, glyph_offset, glyph_length);
+  }
+
+  public:
   HBUINT16	ppem;		/* The PPEM size for which this strike was designed. */
   HBUINT16	resolution;	/* The device pixel density (in PPI) for which this
 				 * strike was designed. (E.g., 96 PPI, 192 PPI.) */
+  protected:
   UnsizedArrayOf<LOffsetTo<SBIXGlyph> >
 		imageOffsetsZ;	/* Offset from the beginning of the strike data header
 				 * to bitmap data for an individual glyph ID. */
@@ -84,64 +130,157 @@
 
 struct sbix
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_sbix;
+  enum { tableTag = HB_OT_TAG_sbix };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) && strikes.sanitize (c, this)));
-  }
+  bool has_data () const { return version; }
+
+  const SBIXStrike &get_strike (unsigned int i) const { return this+strikes[i]; }
 
   struct accelerator_t
   {
-    inline void init (hb_face_t *face)
+    void init (hb_face_t *face)
     {
-      sbix_blob = hb_sanitize_context_t().reference_table<sbix> (face);
-      sbix_len = hb_blob_get_length (sbix_blob);
-      sbix_table = sbix_blob->as<sbix> ();
+      table = hb_sanitize_context_t().reference_table<sbix> (face);
+      num_glyphs = face->get_num_glyphs ();
+    }
+    void fini () { table.destroy (); }
+
+    bool has_data () const { return table->has_data (); }
+
+    bool get_extents (hb_font_t          *font,
+		      hb_codepoint_t      glyph,
+		      hb_glyph_extents_t *extents) const
+    {
+      /* We only support PNG right now, and following function checks type. */
+      return get_png_extents (font, glyph, extents);
     }
 
-    inline void fini (void)
+    hb_blob_t *reference_png (hb_font_t      *font,
+			      hb_codepoint_t  glyph_id,
+			      int            *x_offset,
+			      int            *y_offset,
+			      unsigned int   *available_ppem) const
     {
-      hb_blob_destroy (sbix_blob);
-    }
-
-    inline void dump (void (*callback) (const uint8_t* data, unsigned int length,
-					unsigned int group, unsigned int gid)) const
-    {
-      for (unsigned group = 0; group < sbix_table->strikes.len; ++group)
-      {
-	const SBIXStrike &strike = sbix_table->strikes[group](sbix_table);
-	for (unsigned int glyph = 0; glyph < num_glyphs; ++glyph)
-	  if (strike.imageOffsetsZ[glyph + 1] - strike.imageOffsetsZ[glyph] > 0)
-	  {
-	    const SBIXGlyph &sbixGlyph = strike.imageOffsetsZ[glyph]((const void *) &strike);
-	    callback ((const uint8_t*) &sbixGlyph.data,
-		      strike.imageOffsetsZ[glyph + 1] - strike.imageOffsetsZ[glyph] - 8,
-		      group, glyph);
-	  }
-      }
+      return choose_strike (font).get_glyph_blob (glyph_id, table.get_blob (),
+						  HB_TAG ('p','n','g',' '),
+						  x_offset, y_offset,
+						  num_glyphs, available_ppem);
     }
 
     private:
-    hb_blob_t *sbix_blob;
-    const sbix *sbix_table;
 
-    unsigned int sbix_len;
+    const SBIXStrike &choose_strike (hb_font_t *font) const
+    {
+      unsigned count = table->strikes.len;
+      if (unlikely (!count))
+        return Null(SBIXStrike);
+
+      unsigned int requested_ppem = MAX (font->x_ppem, font->y_ppem);
+      if (!requested_ppem)
+        requested_ppem = 1<<30; /* Choose largest strike. */
+      /* TODO Add DPI sensitivity as well? */
+      unsigned int best_i = 0;
+      unsigned int best_ppem = table->get_strike (0).ppem;
+
+      for (unsigned int i = 1; i < count; i++)
+      {
+	unsigned int ppem = (table->get_strike (i)).ppem;
+	if ((requested_ppem <= ppem && ppem < best_ppem) ||
+	    (requested_ppem > best_ppem && ppem > best_ppem))
+	{
+	  best_i = i;
+	  best_ppem = ppem;
+	}
+      }
+
+      return table->get_strike (best_i);
+    }
+
+    struct PNGHeader
+    {
+      HBUINT8	signature[8];
+      struct
+      {
+        struct
+	{
+	  HBUINT32	length;
+	  Tag		type;
+	}		header;
+	HBUINT32	width;
+	HBUINT32	height;
+	HBUINT8		bitDepth;
+	HBUINT8		colorType;
+	HBUINT8		compressionMethod;
+	HBUINT8		filterMethod;
+	HBUINT8		interlaceMethod;
+      } IHDR;
+
+      public:
+      DEFINE_SIZE_STATIC (29);
+    };
+
+    bool get_png_extents (hb_font_t          *font,
+			  hb_codepoint_t      glyph,
+			  hb_glyph_extents_t *extents) const
+    {
+      /* Following code is safe to call even without data.
+       * But faster to short-circuit. */
+      if (!has_data ())
+        return false;
+
+      int x_offset = 0, y_offset = 0;
+      unsigned int strike_ppem = 0;
+      hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
+
+      const PNGHeader &png = *blob->as<PNGHeader>();
+
+      extents->x_bearing = x_offset;
+      extents->y_bearing = y_offset;
+      extents->width     = png.IHDR.width;
+      extents->height    = png.IHDR.height;
+
+      /* Convert to font units. */
+      if (strike_ppem)
+      {
+	double scale = font->face->get_upem () / (double) strike_ppem;
+	extents->x_bearing = round (extents->x_bearing * scale);
+	extents->y_bearing = round (extents->y_bearing * scale);
+	extents->width = round (extents->width * scale);
+	extents->height = round (extents->height * scale);
+      }
+
+      hb_blob_destroy (blob);
+
+      return strike_ppem;
+    }
+
+    private:
+    hb_blob_ptr_t<sbix> table;
+
     unsigned int num_glyphs;
   };
 
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  version >= 1 &&
+			  strikes.sanitize (c, this)));
+  }
+
   protected:
   HBUINT16	version;	/* Table version number — set to 1 */
   HBUINT16	flags;		/* Bit 0: Set to 1. Bit 1: Draw outlines.
 				 * Bits 2 to 15: reserved (set to 0). */
-  LArrayOf<LOffsetTo<SBIXStrike> >
+  LOffsetLArrayOf<SBIXStrike>
 		strikes;	/* Offsets from the beginning of the 'sbix'
 				 * table to data for each individual bitmap strike. */
   public:
   DEFINE_SIZE_ARRAY (8, strikes);
 };
 
+struct sbix_accelerator_t : sbix::accelerator_t {};
+
 } /* namespace OT */
 
 #endif /* HB_OT_COLOR_SBIX_TABLE_HH */
diff --git a/src/hb-ot-color-svg-table.hh b/src/hb-ot-color-svg-table.hh
index 53d4668..554453f 100644
--- a/src/hb-ot-color-svg-table.hh
+++ b/src/hb-ot-color-svg-table.hh
@@ -40,13 +40,21 @@
 
 struct SVGDocumentIndexEntry
 {
-  friend struct SVG;
+  int cmp (hb_codepoint_t g) const
+  { return g < startGlyphID ? -1 : g > endGlyphID ? 1 : 0; }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void* base) const
+  hb_blob_t *reference_blob (hb_blob_t *svg_blob, unsigned int index_offset) const
+  {
+    return hb_blob_create_sub_blob (svg_blob,
+				    index_offset + (unsigned int) svgDoc,
+				    svgDocLength);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  (base+svgDoc).sanitize (c, svgDocLength));
+		  svgDoc.sanitize (c, base, svgDocLength));
   }
 
   protected:
@@ -57,86 +65,59 @@
   LOffsetTo<UnsizedArrayOf<HBUINT8>, false>
 		svgDoc;		/* Offset from the beginning of the SVG Document Index
 				 * to an SVG document. Must be non-zero. */
-  HBUINT32 svgDocLength;	/* Length of the SVG document.
+  HBUINT32	svgDocLength;	/* Length of the SVG document.
 				 * Must be non-zero. */
   public:
   DEFINE_SIZE_STATIC (12);
 };
 
-struct SVGDocumentIndex
-{
-  friend struct SVG;
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  entries.sanitize (c, this));
-  }
-
-  protected:
-  ArrayOf<SVGDocumentIndexEntry>
-		entries;	/* Array of SVG Document Index Entries. */
-  public:
-  DEFINE_SIZE_ARRAY (2, entries);
-};
-
 struct SVG
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_SVG;
+  enum { tableTag = HB_OT_TAG_SVG };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) &&
-			  (this+svgDocIndex).sanitize (c)));
-  }
+  bool has_data () const { return svgDocEntries; }
 
   struct accelerator_t
   {
-    inline void init (hb_face_t *face)
+    void init (hb_face_t *face)
+    { table = hb_sanitize_context_t().reference_table<SVG> (face); }
+    void fini () { table.destroy (); }
+
+    hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const
     {
-      svg_blob = hb_sanitize_context_t().reference_table<SVG> (face);
-      svg_len = hb_blob_get_length (svg_blob);
-      svg = svg_blob->as<SVG> ();
+      return table->get_glyph_entry (glyph_id).reference_blob (table.get_blob (),
+							       table->svgDocEntries);
     }
 
-    inline void fini (void)
-    {
-      hb_blob_destroy (svg_blob);
-    }
-
-    inline void
-    dump (void (*callback) (const uint8_t* data, unsigned int length,
-			    unsigned int start_glyph, unsigned int end_glyph)) const
-    {
-      const SVGDocumentIndex &index = svg+svg->svgDocIndex;
-      const ArrayOf<SVGDocumentIndexEntry> &entries = index.entries;
-      for (unsigned int i = 0; i < entries.len; ++i)
-      {
-	const SVGDocumentIndexEntry &entry = entries[i];
-	callback ((const uint8_t*) &entry.svgDoc (&index), entry.svgDocLength,
-						  entry.startGlyphID, entry.endGlyphID);
-      }
-    }
+    bool has_data () const { return table->has_data (); }
 
     private:
-    hb_blob_t *svg_blob;
-    const SVG *svg;
-
-    unsigned int svg_len;
+    hb_blob_ptr_t<SVG> table;
   };
 
+  const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const
+  { return (this+svgDocEntries).bsearch (glyph_id); }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  (this+svgDocEntries).sanitize_shallow (c)));
+  }
+
   protected:
   HBUINT16	version;	/* Table version (starting at 0). */
-  LOffsetTo<SVGDocumentIndex>
-		svgDocIndex;	/* Offset (relative to the start of the SVG table) to the
+  LOffsetTo<SortedArrayOf<SVGDocumentIndexEntry> >
+		svgDocEntries;	/* Offset (relative to the start of the SVG table) to the
 				 * SVG Documents Index. Must be non-zero. */
+				/* Array of SVG Document Index Entries. */
   HBUINT32	reserved;	/* Set to 0. */
   public:
   DEFINE_SIZE_STATIC (10);
 };
 
+struct SVG_accelerator_t : SVG::accelerator_t {};
+
 } /* namespace OT */
 
 
diff --git a/src/hb-ot-color.cc b/src/hb-ot-color.cc
index 7cdff38..791135b 100644
--- a/src/hb-ot-color.cc
+++ b/src/hb-ot-color.cc
@@ -22,160 +22,278 @@
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
- * Google Author(s): Sascha Brawer
+ * Google Author(s): Sascha Brawer, Behdad Esfahbod
  */
 
 #include "hb-open-type.hh"
+#include "hb-ot-color-cbdt-table.hh"
 #include "hb-ot-color-colr-table.hh"
 #include "hb-ot-color-cpal-table.hh"
+#include "hb-ot-color-sbix-table.hh"
+#include "hb-ot-color-svg-table.hh"
+#include "hb-ot-face.hh"
 #include "hb-ot.h"
 
 #include <stdlib.h>
 #include <string.h>
 
 #include "hb-ot-layout.hh"
-#include "hb-shaper.hh"
-
-#if 0
-HB_MARK_AS_FLAG_T (hb_ot_color_palette_flags_t)
-//HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) Hmm?
-
-
-static inline const OT::COLR&
-_get_colr (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::COLR);
-  return *(hb_ot_face_data (face)->colr.get ());
-}
-
-static inline const OT::CPAL&
-_get_cpal (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::CPAL);
-  return *(hb_ot_face_data (face)->cpal.get ());
-}
 
 
 /**
- * hb_ot_color_get_palette_count:
+ * SECTION:hb-ot-color
+ * @title: hb-ot-color
+ * @short_description: OpenType Color Fonts
+ * @include: hb-ot.h
+ *
+ * Functions for fetching color-font information from OpenType font faces.
+ **/
+
+
+/*
+ * CPAL
+ */
+
+
+/**
+ * hb_ot_color_has_palettes:
+ * @face: a font face.
+ *
+ * Returns: whether CPAL table is available.
+ *
+ * Since: 2.1.0
+ */
+hb_bool_t
+hb_ot_color_has_palettes (hb_face_t *face)
+{
+  return face->table.CPAL->has_data ();
+}
+
+/**
+ * hb_ot_color_palette_get_count:
  * @face: a font face.
  *
  * Returns: the number of color palettes in @face, or zero if @face has
  * no colors.
  *
- * Since: REPLACEME
+ * Since: 2.1.0
  */
 unsigned int
-hb_ot_color_get_palette_count (hb_face_t *face)
+hb_ot_color_palette_get_count (hb_face_t *face)
 {
-  const OT::CPAL& cpal = _get_cpal (face);
-  return cpal.get_palette_count ();
+  return face->table.CPAL->get_palette_count ();
 }
 
-
 /**
- * hb_ot_color_get_palette_name_id:
- * @face: a font face.
- * @palette: the index of the color palette whose name is being requested.
+ * hb_ot_color_palette_get_name_id:
+ * @face:    a font face.
+ * @palette_index: the index of the color palette whose name is being requested.
  *
  * Retrieves the name id of a color palette. For example, a color font can
  * have themed palettes like "Spring", "Summer", "Fall", and "Winter".
  *
  * Returns: an identifier within @face's `name` table.
- * If the requested palette has no name, or if @face has no colors,
- * or if @palette is not between 0 and hb_ot_color_get_palette_count(),
- * the result is 0xFFFF. The implementation does not check whether
- * the returned palette name id is actually in @face's `name` table.
+ * If the requested palette has no name the result is #HB_OT_NAME_ID_INVALID.
  *
- * Since: REPLACEME
+ * Since: 2.1.0
  */
-unsigned int
-hb_ot_color_get_palette_name_id (hb_face_t *face, unsigned int palette)
+hb_ot_name_id_t
+hb_ot_color_palette_get_name_id (hb_face_t *face,
+				 unsigned int palette_index)
 {
-  const OT::CPAL& cpal = _get_cpal (face);
-  return cpal.get_palette_name_id (palette);
+  return face->table.CPAL->get_palette_name_id (palette_index);
 }
 
+/**
+ * hb_ot_color_palette_color_get_name_id:
+ * @face:        a font face.
+ * @color_index: palette entry index.
+ *
+ * Returns: Name ID associated with a palette entry, e.g. eye color
+ *
+ * Since: 2.1.0
+ */
+hb_ot_name_id_t
+hb_ot_color_palette_color_get_name_id (hb_face_t *face,
+				       unsigned int color_index)
+{
+  return face->table.CPAL->get_color_name_id (color_index);
+}
 
 /**
- * hb_ot_color_get_palette_flags:
- * @face: a font face
- * @palette: the index of the color palette whose flags are being requested
+ * hb_ot_color_palette_get_flags:
+ * @face:          a font face
+ * @palette_index: the index of the color palette whose flags are being requested
  *
- * Returns: the flags for the requested color palette.  If @face has no colors,
- * or if @palette is not between 0 and hb_ot_color_get_palette_count(),
- * the result is #HB_OT_COLOR_PALETTE_FLAG_DEFAULT.
+ * Returns: the flags for the requested color palette.
  *
- * Since: REPLACEME
+ * Since: 2.1.0
  */
 hb_ot_color_palette_flags_t
-hb_ot_color_get_palette_flags (hb_face_t *face, unsigned int palette)
+hb_ot_color_palette_get_flags (hb_face_t *face,
+			       unsigned int palette_index)
 {
-  const OT::CPAL& cpal = _get_cpal(face);
-  return cpal.get_palette_flags (palette);
+  return face->table.CPAL->get_palette_flags (palette_index);
 }
 
-
 /**
- * hb_ot_color_get_palette_colors:
+ * hb_ot_color_palette_get_colors:
  * @face:         a font face.
- * @palette:      the index of the color palette whose colors
+ * @palette_index:the index of the color palette whose colors
  *                are being requested.
  * @start_offset: the index of the first color being requested.
  * @color_count:  (inout) (optional): on input, how many colors
  *                can be maximally stored into the @colors array;
  *                on output, how many colors were actually stored.
- * @colors: (array length=color_count) (optional):
- *                an array of #hb_ot_color_t records. After calling
+ * @colors: (array length=color_count) (out) (optional):
+ *                an array of #hb_color_t records. After calling
  *                this function, @colors will be filled with
  *                the palette colors. If @colors is NULL, the function
  *                will just return the number of total colors
  *                without storing any actual colors; this can be used
  *                for allocating a buffer of suitable size before calling
- *                hb_ot_color_get_palette_colors() a second time.
+ *                hb_ot_color_palette_get_colors() a second time.
  *
  * Retrieves the colors in a color palette.
  *
- * Returns: the total number of colors in the palette. All palettes in
- * a font have the same number of colors. If @face has no colors, or if
- * @palette is not between 0 and hb_ot_color_get_palette_count(),
- * the result is zero.
+ * Returns: the total number of colors in the palette.
  *
- * Since: REPLACEME
+ * Since: 2.1.0
  */
 unsigned int
-hb_ot_color_get_palette_colors (hb_face_t       *face,
-				unsigned int     palette, /* default=0 */
-				unsigned int     start_offset,
-				unsigned int    *color_count /* IN/OUT */,
-				hb_ot_color_t   *colors /* OUT */)
+hb_ot_color_palette_get_colors (hb_face_t     *face,
+				unsigned int   palette_index,
+				unsigned int   start_offset,
+				unsigned int  *colors_count  /* IN/OUT.  May be NULL. */,
+				hb_color_t    *colors        /* OUT.     May be NULL. */)
 {
-  const OT::CPAL& cpal = _get_cpal(face);
-  if (unlikely (palette >= cpal.numPalettes))
-  {
-    if (color_count) *color_count = 0;
-    return 0;
-  }
-
-  const OT::ColorRecord* crec = &cpal.offsetFirstColorRecord (&cpal);
-  crec += cpal.colorRecordIndices[palette];
-
-  unsigned int num_results = 0;
-  if (likely (color_count && colors))
-  {
-    for (unsigned int i = start_offset;
-	 i < cpal.numPaletteEntries && num_results < *color_count; ++i)
-    {
-      hb_ot_color_t* result = &colors[num_results];
-      result->red = crec[i].red;
-      result->green = crec[i].green;
-      result->blue = crec[i].blue;
-      result->alpha = crec[i].alpha;
-      ++num_results;
-    }
-  }
-
-  if (likely (color_count)) *color_count = num_results;
-  return cpal.numPaletteEntries;
+  return face->table.CPAL->get_palette_colors (palette_index, start_offset, colors_count, colors);
 }
-#endif
+
+
+/*
+ * COLR
+ */
+
+/**
+ * hb_ot_color_has_layers:
+ * @face: a font face.
+ *
+ * Returns: whether COLR table is available.
+ *
+ * Since: 2.1.0
+ */
+hb_bool_t
+hb_ot_color_has_layers (hb_face_t *face)
+{
+  return face->table.COLR->has_data ();
+}
+
+/**
+ * hb_ot_color_glyph_get_layers:
+ * @face:         a font face.
+ * @glyph:        a layered color glyph id.
+ * @start_offset: starting offset of layers.
+ * @count:  (inout) (optional): gets number of layers available to be written on buffer
+ * 				and returns number of written layers.
+ * @layers: (array length=count) (out) (optional): layers buffer to buffer.
+ *
+ * Returns: Total number of layers a layered color glyph have.
+ *
+ * Since: 2.1.0
+ */
+unsigned int
+hb_ot_color_glyph_get_layers (hb_face_t           *face,
+			      hb_codepoint_t       glyph,
+			      unsigned int         start_offset,
+			      unsigned int        *count, /* IN/OUT.  May be NULL. */
+			      hb_ot_color_layer_t *layers /* OUT.     May be NULL. */)
+{
+  return face->table.COLR->get_glyph_layers (glyph, start_offset, count, layers);
+}
+
+
+/*
+ * SVG
+ */
+
+/**
+ * hb_ot_color_has_svg:
+ * @face: a font face.
+ *
+ * Check whether @face has SVG glyph images.
+ *
+ * Returns true if available, false otherwise.
+ *
+ * Since: 2.1.0
+ */
+hb_bool_t
+hb_ot_color_has_svg (hb_face_t *face)
+{
+  return face->table.SVG->has_data ();
+}
+
+/**
+ * hb_ot_color_glyph_reference_svg:
+ * @face:  a font face.
+ * @glyph: a svg glyph index.
+ *
+ * Get SVG document for a glyph. The blob may be either plain text or gzip-encoded.
+ *
+ * Returns: (transfer full): respective svg blob of the glyph, if available.
+ *
+ * Since: 2.1.0
+ */
+hb_blob_t *
+hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph)
+{
+  return face->table.SVG->reference_blob_for_glyph (glyph);
+}
+
+
+/*
+ * PNG: CBDT or sbix
+ */
+
+/**
+ * hb_ot_color_has_png:
+ * @face: a font face.
+ *
+ * Check whether @face has PNG glyph images (either CBDT or sbix tables).
+ *
+ * Returns true if available, false otherwise.
+ *
+ * Since: 2.1.0
+ */
+hb_bool_t
+hb_ot_color_has_png (hb_face_t *face)
+{
+  return face->table.CBDT->has_data () || face->table.sbix->has_data ();
+}
+
+/**
+ * hb_ot_color_glyph_reference_png:
+ * @font:  a font object, not face. upem should be set on
+ * 	   that font object if one wants to get optimal png blob, otherwise
+ * 	   return the biggest one
+ * @glyph: a glyph index.
+ *
+ * Get PNG image for a glyph.
+ *
+ * Returns: (transfer full): respective PNG blob of the glyph, if available.
+ *
+ * Since: 2.1.0
+ */
+hb_blob_t *
+hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t  glyph)
+{
+  hb_blob_t *blob = hb_blob_get_empty ();
+
+  if (font->face->table.sbix->has_data ())
+    blob = font->face->table.sbix->reference_png (font, glyph, nullptr, nullptr, nullptr);
+
+  if (!blob->length && font->face->table.CBDT->has_data ())
+    blob = font->face->table.CBDT->reference_png (font, glyph);
+
+  return blob;
+}
diff --git a/src/hb-ot-color.h b/src/hb-ot-color.h
new file mode 100644
index 0000000..49646bf
--- /dev/null
+++ b/src/hb-ot-color.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright © 2016  Google, Inc.
+ * Copyright © 2018  Khaled Hosny
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Sascha Brawer, Behdad Esfahbod
+ */
+
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
+#ifndef HB_OT_COLOR_H
+#define HB_OT_COLOR_H
+
+#include "hb.h"
+#include "hb-ot-name.h"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * Color palettes.
+ */
+
+HB_EXTERN hb_bool_t
+hb_ot_color_has_palettes (hb_face_t *face);
+
+HB_EXTERN unsigned int
+hb_ot_color_palette_get_count (hb_face_t *face);
+
+HB_EXTERN hb_ot_name_id_t
+hb_ot_color_palette_get_name_id (hb_face_t *face,
+				 unsigned int palette_index);
+
+HB_EXTERN hb_ot_name_id_t
+hb_ot_color_palette_color_get_name_id (hb_face_t *face,
+				       unsigned int color_index);
+
+/**
+ * hb_ot_color_palette_flags_t:
+ * @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: default indicating that there is nothing special
+ *   to note about a color palette.
+ * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND: flag indicating that the color
+ *   palette is appropriate to use when displaying the font on a light background such as white.
+ * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND: flag indicating that the color
+ *   palette is appropriate to use when displaying the font on a dark background such as black.
+ *
+ * Since: 2.1.0
+ */
+typedef enum { /*< flags >*/
+  HB_OT_COLOR_PALETTE_FLAG_DEFAULT			= 0x00000000u,
+  HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND	= 0x00000001u,
+  HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND	= 0x00000002u
+} hb_ot_color_palette_flags_t;
+
+HB_EXTERN hb_ot_color_palette_flags_t
+hb_ot_color_palette_get_flags (hb_face_t *face,
+			       unsigned int palette_index);
+
+HB_EXTERN unsigned int
+hb_ot_color_palette_get_colors (hb_face_t    *face,
+				unsigned int  palette_index,
+				unsigned int  start_offset,
+				unsigned int *color_count,  /* IN/OUT.  May be NULL. */
+				hb_color_t   *colors        /* OUT.     May be NULL. */);
+
+
+/*
+ * Color layers.
+ */
+
+HB_EXTERN hb_bool_t
+hb_ot_color_has_layers (hb_face_t *face);
+
+/**
+ * hb_ot_color_layer_t:
+ *
+ * Pairs of glyph and color index.
+ *
+ * Since: 2.1.0
+ **/
+typedef struct hb_ot_color_layer_t
+{
+  hb_codepoint_t glyph;
+  unsigned int   color_index;
+} hb_ot_color_layer_t;
+
+HB_EXTERN unsigned int
+hb_ot_color_glyph_get_layers (hb_face_t           *face,
+			      hb_codepoint_t       glyph,
+			      unsigned int         start_offset,
+			      unsigned int        *count, /* IN/OUT.  May be NULL. */
+			      hb_ot_color_layer_t *layers /* OUT.     May be NULL. */);
+
+/*
+ * SVG
+ */
+
+HB_EXTERN hb_bool_t
+hb_ot_color_has_svg (hb_face_t *face);
+
+HB_EXTERN hb_blob_t *
+hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph);
+
+/*
+ * PNG: CBDT or sbix
+ */
+
+HB_EXTERN hb_bool_t
+hb_ot_color_has_png (hb_face_t *face);
+
+HB_EXTERN hb_blob_t *
+hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t glyph);
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_COLOR_H */
diff --git a/src/hb-ot-deprecated.h b/src/hb-ot-deprecated.h
new file mode 100644
index 0000000..bce51b7
--- /dev/null
+++ b/src/hb-ot-deprecated.h
@@ -0,0 +1,107 @@
+/*
+ * 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_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
+#ifndef HB_OT_DEPRECATED_H
+#define HB_OT_DEPRECATED_H
+
+#include "hb.h"
+#include "hb-ot-name.h"
+
+
+HB_BEGIN_DECLS
+
+#ifndef HB_DISABLE_DEPRECATED
+
+
+/* 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);
+
+
+/**
+ * HB_OT_VAR_NO_AXIS_INDEX:
+ *
+ * Since: 1.4.2
+ * Deprecated: 2.2.0
+ */
+#define HB_OT_VAR_NO_AXIS_INDEX		0xFFFFFFFFu
+
+/**
+ * hb_ot_var_axis_t:
+ *
+ * Since: 1.4.2
+ * Deprecated: 2.2.0
+ */
+typedef struct hb_ot_var_axis_t
+{
+  hb_tag_t tag;
+  hb_ot_name_id_t name_id;
+  float min_value;
+  float default_value;
+  float max_value;
+} hb_ot_var_axis_t;
+
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_get_axis_infos) unsigned int
+hb_ot_var_get_axes (hb_face_t        *face,
+		    unsigned int      start_offset,
+		    unsigned int     *axes_count /* IN/OUT */,
+		    hb_ot_var_axis_t *axes_array /* OUT */);
+
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_find_axis_info) hb_bool_t
+hb_ot_var_find_axis (hb_face_t        *face,
+		     hb_tag_t          axis_tag,
+		     unsigned int     *axis_index,
+		     hb_ot_var_axis_t *axis_info);
+
+
+#endif
+
+HB_END_DECLS
+
+#endif /* HB_OT_DEPRECATED_H */
diff --git a/src/hb-ot-face.cc b/src/hb-ot-face.cc
index 1bc68d3..9b17526 100644
--- a/src/hb-ot-face.cc
+++ b/src/hb-ot-face.cc
@@ -28,16 +28,21 @@
 
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-glyf-table.hh"
+#include "hb-ot-cff1-table.hh"
+#include "hb-ot-cff2-table.hh"
 #include "hb-ot-hmtx-table.hh"
 #include "hb-ot-kern-table.hh"
+#include "hb-ot-name-table.hh"
 #include "hb-ot-post-table.hh"
 #include "hb-ot-color-cbdt-table.hh"
+#include "hb-ot-color-sbix-table.hh"
+#include "hb-ot-color-svg-table.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
 
 
-void hb_ot_face_data_t::init0 (hb_face_t *face)
+void hb_ot_face_t::init0 (hb_face_t *face)
 {
   this->face = face;
 #define HB_OT_TABLE(Namespace, Type) Type.init0 ();
@@ -46,7 +51,7 @@
 #undef HB_OT_ACCELERATOR
 #undef HB_OT_TABLE
 }
-void hb_ot_face_data_t::fini (void)
+void hb_ot_face_t::fini ()
 {
 #define HB_OT_TABLE(Namespace, Type) Type.fini ();
 #define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type)
@@ -54,23 +59,3 @@
 #undef HB_OT_ACCELERATOR
 #undef HB_OT_TABLE
 }
-
-hb_ot_face_data_t *
-_hb_ot_face_data_create (hb_face_t *face)
-{
-  hb_ot_face_data_t *data = (hb_ot_face_data_t *) calloc (1, sizeof (hb_ot_face_data_t));
-  if (unlikely (!data))
-    return nullptr;
-
-  data->init0 (face);
-
-  return data;
-}
-
-void
-_hb_ot_face_data_destroy (hb_ot_face_data_t *data)
-{
-  data->fini ();
-  free (data);
-}
-
diff --git a/src/hb-ot-face.hh b/src/hb-ot-face.hh
index e305922..7f47ba6 100644
--- a/src/hb-ot-face.hh
+++ b/src/hb-ot-face.hh
@@ -34,41 +34,52 @@
 #include "hb-machinery.hh"
 
 
-#define hb_ot_face_data(face) ((hb_ot_face_data_t *) face->shaper_data.ot.get_relaxed ())
-
-
 /*
- * hb_ot_face_data_t
+ * hb_ot_face_t
  */
 
-/* Most of these tables are NOT needed for shaping.  But we need to hook them *somewhere*.
- * This is as good as any place. */
 #define HB_OT_TABLES \
+    /* OpenType fundamentals. */ \
+    HB_OT_TABLE(OT, head) \
+    HB_OT_ACCELERATOR(OT, cmap) \
+    HB_OT_ACCELERATOR(OT, hmtx) \
+    HB_OT_ACCELERATOR(OT, vmtx) \
+    HB_OT_ACCELERATOR(OT, post) \
+    HB_OT_TABLE(OT, kern) \
+    HB_OT_ACCELERATOR(OT, glyf) \
+    HB_OT_ACCELERATOR(OT, cff1) \
+    HB_OT_ACCELERATOR(OT, cff2) \
+    HB_OT_TABLE(OT, VORG) \
+    HB_OT_ACCELERATOR(OT, name) \
+    HB_OT_TABLE(OT, OS2) \
+    HB_OT_TABLE(OT, STAT) \
     /* OpenType shaping. */ \
-    HB_OT_TABLE(OT, JSTF) \
+    HB_OT_ACCELERATOR(OT, GDEF) \
+    HB_OT_ACCELERATOR(OT, GSUB) \
+    HB_OT_ACCELERATOR(OT, GPOS) \
     HB_OT_TABLE(OT, BASE) \
+    HB_OT_TABLE(OT, JSTF) \
     /* AAT shaping. */ \
+    HB_OT_TABLE(AAT, mort) \
     HB_OT_TABLE(AAT, morx) \
     HB_OT_TABLE(AAT, kerx) \
     HB_OT_TABLE(AAT, ankr) \
     HB_OT_TABLE(AAT, trak) \
+    HB_OT_TABLE(AAT, lcar) \
+    HB_OT_TABLE(AAT, ltag) \
+    HB_OT_TABLE(AAT, feat) \
     /* OpenType variations. */ \
     HB_OT_TABLE(OT, fvar) \
     HB_OT_TABLE(OT, avar) \
     HB_OT_TABLE(OT, MVAR) \
     /* OpenType math. */ \
     HB_OT_TABLE(OT, MATH) \
-    /* OpenType fundamentals. */ \
-    HB_OT_ACCELERATOR(OT, GDEF) \
-    HB_OT_ACCELERATOR(OT, GSUB) \
-    HB_OT_ACCELERATOR(OT, GPOS) \
-    HB_OT_ACCELERATOR(OT, cmap) \
-    HB_OT_ACCELERATOR(OT, hmtx) \
-    HB_OT_ACCELERATOR(OT, vmtx) \
-    HB_OT_ACCELERATOR(OT, post) \
-    HB_OT_ACCELERATOR(OT, kern) \
-    HB_OT_ACCELERATOR(OT, glyf) \
+    /* OpenType color fonts. */ \
+    HB_OT_TABLE(OT, COLR) \
+    HB_OT_TABLE(OT, CPAL) \
     HB_OT_ACCELERATOR(OT, CBDT) \
+    HB_OT_ACCELERATOR(OT, sbix) \
+    HB_OT_ACCELERATOR(OT, SVG) \
     /* */
 
 /* Declare tables. */
@@ -78,10 +89,10 @@
 #undef HB_OT_ACCELERATOR
 #undef HB_OT_TABLE
 
-struct hb_ot_face_data_t
+struct hb_ot_face_t
 {
   HB_INTERNAL void init0 (hb_face_t *face);
-  HB_INTERNAL void fini (void);
+  HB_INTERNAL void fini ();
 
 #define HB_OT_TABLE_ORDER(Namespace, Type) \
     HB_PASTE (ORDER_, HB_PASTE (Namespace, HB_PASTE (_, Type)))
@@ -106,11 +117,4 @@
 };
 
 
-HB_INTERNAL hb_ot_face_data_t *
-_hb_ot_face_data_create (hb_face_t *face);
-
-HB_INTERNAL void
-_hb_ot_face_data_destroy (hb_ot_face_data_t *data);
-
-
 #endif /* HB_OT_FACE_HH */
diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc
index e6df038..c080f0f 100644
--- a/src/hb-ot-font.cc
+++ b/src/hb-ot-font.cc
@@ -33,11 +33,29 @@
 #include "hb-ot-face.hh"
 
 #include "hb-ot-cmap-table.hh"
+#include "hb-ot-glyf-table.hh"
+#include "hb-ot-cff1-table.hh"
+#include "hb-ot-cff2-table.hh"
 #include "hb-ot-hmtx-table.hh"
 #include "hb-ot-kern-table.hh"
+#include "hb-ot-os2-table.hh"
 #include "hb-ot-post-table.hh"
-#include "hb-ot-glyf-table.hh"
+#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise.
+#include "hb-ot-vorg-table.hh"
 #include "hb-ot-color-cbdt-table.hh"
+#include "hb-ot-color-sbix-table.hh"
+
+
+/**
+ * SECTION:hb-ot-font
+ * @title: hb-ot-font
+ * @short_description: OpenType font implementation
+ * @include: hb-ot.h
+ *
+ * Functions for using OpenType fonts with hb_shape().  Not that fonts returned
+ * by hb_font_create() default to using these functions, so most clients would
+ * never need to call these functions directly.
+ **/
 
 
 static hb_bool_t
@@ -47,8 +65,8 @@
 			 hb_codepoint_t *glyph,
 			 void *user_data HB_UNUSED)
 {
-  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
-  return ot_face->cmap.get ()->get_nominal_glyph (unicode, glyph);
+  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  return ot_face->cmap->get_nominal_glyph (unicode, glyph);
 }
 
 static unsigned int
@@ -61,17 +79,10 @@
 			  unsigned int glyph_stride,
 			  void *user_data HB_UNUSED)
 {
-  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
-  const OT::cmap_accelerator_t &cmap = *ot_face->cmap.get ();
-  unsigned int done;
-  for (done = 0;
-       done < count && cmap.get_nominal_glyph (*first_unicode, first_glyph);
-       done++)
-  {
-    first_unicode = &StructAtOffset<hb_codepoint_t> (first_unicode, unicode_stride);
-    first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
-  }
-  return done;
+  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  return ot_face->cmap->get_nominal_glyphs (count,
+					    first_unicode, unicode_stride,
+					    first_glyph, glyph_stride);
 }
 
 static hb_bool_t
@@ -82,8 +93,8 @@
 			   hb_codepoint_t *glyph,
 			   void *user_data HB_UNUSED)
 {
-  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
-  return ot_face->cmap.get ()->get_variation_glyph (unicode, variation_selector, glyph);
+  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  return ot_face->cmap->get_variation_glyph (unicode, variation_selector, glyph);
 }
 
 static void
@@ -95,8 +106,8 @@
 			    unsigned advance_stride,
 			    void *user_data HB_UNUSED)
 {
-  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
-  const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx.get ();
+  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx;
 
   for (unsigned int i = 0; i < count; i++)
   {
@@ -115,8 +126,8 @@
 			    unsigned advance_stride,
 			    void *user_data HB_UNUSED)
 {
-  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
-  const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx.get ();
+  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
 
   for (unsigned int i = 0; i < count; i++)
   {
@@ -134,15 +145,21 @@
 			  hb_position_t *y,
 			  void *user_data HB_UNUSED)
 {
-  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
+  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
 
   *x = font->get_glyph_h_advance (glyph) / 2;
 
-  hb_glyph_extents_t extents = {0};
-  bool ret = ot_face->glyf->get_extents (glyph, &extents);
-  if (ret)
+  const OT::VORG &VORG = *ot_face->VORG;
+  if (VORG.has_data ())
   {
-    const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx.get ();
+    *y = font->em_scale_y (VORG.get_y_origin (glyph));
+    return true;
+  }
+
+  hb_glyph_extents_t extents = {0};
+  if (ot_face->glyf->get_extents (glyph, &extents))
+  {
+    const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
     hb_position_t tsb = vmtx.get_side_bearing (glyph);
     *y = font->em_scale_y (extents.y_bearing + tsb);
     return true;
@@ -162,10 +179,16 @@
 			 hb_glyph_extents_t *extents,
 			 void *user_data HB_UNUSED)
 {
-  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
-  bool ret = ot_face->glyf->get_extents (glyph, extents);
+  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  bool ret = ot_face->sbix->get_extents (font, glyph, extents);
   if (!ret)
-    ret = ot_face->CBDT->get_extents (glyph, extents);
+    ret = ot_face->glyf->get_extents (glyph, extents);
+  if (!ret)
+    ret = ot_face->cff1->get_extents (glyph, extents);
+  if (!ret)
+    ret = ot_face->cff2->get_extents (font, glyph, extents);
+  if (!ret)
+    ret = ot_face->CBDT->get_extents (font, glyph, extents);
   // TODO Hook up side-bearings variations.
   extents->x_bearing = font->em_scale_x (extents->x_bearing);
   extents->y_bearing = font->em_scale_y (extents->y_bearing);
@@ -181,7 +204,7 @@
                       char *name, unsigned int size,
                       void *user_data HB_UNUSED)
 {
-  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
+  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
   return ot_face->post->get_glyph_name (glyph, name, size);
 }
 
@@ -192,7 +215,7 @@
                            hb_codepoint_t *glyph,
                            void *user_data HB_UNUSED)
 {
-  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
+  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
   return ot_face->post->get_glyph_from_name (name, len, glyph);
 }
 
@@ -202,8 +225,8 @@
 			  hb_font_extents_t *metrics,
 			  void *user_data HB_UNUSED)
 {
-  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
-  const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx.get ();
+  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx;
   metrics->ascender = font->em_scale_y (hmtx.ascender);
   metrics->descender = font->em_scale_y (hmtx.descender);
   metrics->line_gap = font->em_scale_y (hmtx.line_gap);
@@ -217,8 +240,8 @@
 			  hb_font_extents_t *metrics,
 			  void *user_data HB_UNUSED)
 {
-  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
-  const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx.get ();
+  const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
+  const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
   metrics->ascender = font->em_scale_x (vmtx.ascender);
   metrics->descender = font->em_scale_x (vmtx.descender);
   metrics->line_gap = font->em_scale_x (vmtx.line_gap);
@@ -226,13 +249,13 @@
   return vmtx.has_font_extents;
 }
 
-#ifdef HB_USE_ATEXIT
-static void free_static_ot_funcs (void);
+#if HB_USE_ATEXIT
+static void free_static_ot_funcs ();
 #endif
 
 static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ot_font_funcs_lazy_loader_t>
 {
-  static inline hb_font_funcs_t *create (void)
+  static hb_font_funcs_t *create ()
   {
     hb_font_funcs_t *funcs = hb_font_funcs_create ();
 
@@ -252,7 +275,7 @@
 
     hb_font_funcs_make_immutable (funcs);
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
     atexit (free_static_ot_funcs);
 #endif
 
@@ -260,16 +283,16 @@
   }
 } static_ot_funcs;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
 static
-void free_static_ot_funcs (void)
+void free_static_ot_funcs ()
 {
   static_ot_funcs.free_instance ();
 }
 #endif
 
 static hb_font_funcs_t *
-_hb_ot_get_font_funcs (void)
+_hb_ot_get_font_funcs ()
 {
   return static_ot_funcs.get_unconst ();
 }
@@ -283,11 +306,8 @@
 void
 hb_ot_font_set_funcs (hb_font_t *font)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (font->face))) return;
-  hb_ot_face_data_t *ot_face = hb_ot_face_data (font->face);
-
   hb_font_set_funcs (font,
 		     _hb_ot_get_font_funcs (),
-		     ot_face,
+		     &font->face->table,
 		     nullptr);
 }
diff --git a/src/hb-ot-gasp-table.hh b/src/hb-ot-gasp-table.hh
new file mode 100644
index 0000000..d328fd2
--- /dev/null
+++ b/src/hb-ot-gasp-table.hh
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_OT_GASP_TABLE_HH
+#define HB_OT_GASP_TABLE_HH
+
+#include "hb-open-type.hh"
+#include "hb-ot-hhea-table.hh"
+#include "hb-ot-os2-table.hh"
+#include "hb-ot-var-hvar-table.hh"
+
+/*
+ * gasp -- Grid-fitting and Scan-conversion Procedure
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/gasp
+ */
+#define HB_OT_TAG_gasp HB_TAG('g','a','s','p')
+
+
+namespace OT {
+
+struct GaspRange
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  HBUINT16 	rangeMaxPPEM;	/* Upper limit of range, in PPEM */
+  HBUINT16 	rangeGaspBehavior;
+				/* Flags describing desired rasterizer behavior. */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct gasp
+{
+  enum { tableTag = HB_OT_TAG_gasp };
+
+  const GaspRange &get_gasp_range (unsigned int i) const
+  { return gaspRanges[i]; }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  gaspRanges.sanitize (c));
+  }
+
+  protected:
+  HBUINT16	version;	/* Version number (set to 1) */
+  ArrayOf<GaspRange>
+		gaspRanges;	/* Number of records to follow
+				 * Sorted by ppem */
+  public:
+  DEFINE_SIZE_ARRAY (4, gaspRanges);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_GASP_TABLE_HH */
diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh
index 2145ac0..a6dec40 100644
--- a/src/hb-ot-glyf-table.hh
+++ b/src/hb-ot-glyf-table.hh
@@ -45,9 +45,9 @@
 {
   friend struct glyf;
 
-  static const hb_tag_t tableTag = HB_OT_TAG_loca;
+  enum { tableTag = HB_OT_TAG_loca };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
   {
     TRACE_SANITIZE (this);
     return_trace (true);
@@ -55,7 +55,10 @@
 
   protected:
   UnsizedArrayOf<HBUINT8>	dataZ;		/* Location data. */
-  DEFINE_SIZE_ARRAY (0, dataZ);
+  public:
+  DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always
+			* check the size externally, allow Null() object of it by
+			* defining it MIN() instead. */
 };
 
 
@@ -68,9 +71,9 @@
 
 struct glyf
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_glyf;
+  enum { tableTag = HB_OT_TAG_glyf };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
   {
     TRACE_SANITIZE (this);
     /* We don't check for anything specific here.  The users of the
@@ -78,7 +81,7 @@
     return_trace (true);
   }
 
-  inline bool subset (hb_subset_plan_t *plan) const
+  bool subset (hb_subset_plan_t *plan) const
   {
     hb_blob_t *glyf_prime = nullptr;
     hb_blob_t *loca_prime = nullptr;
@@ -101,7 +104,7 @@
   static bool
   _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca)
   {
-    hb_blob_t *head_blob = hb_sanitize_context_t().reference_table<head> (plan->source);
+    hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<head> (plan->source);
     hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob);
     hb_blob_destroy (head_blob);
 
@@ -119,9 +122,9 @@
   struct GlyphHeader
   {
     HBINT16		numberOfContours;	/* If the number of contours is
-                                                 * greater than or equal to zero,
-                                                 * this is a simple glyph; if negative,
-                                                 * this is a composite glyph. */
+						 * greater than or equal to zero,
+						 * this is a simple glyph; if negative,
+						 * this is a composite glyph. */
     FWORD		xMin;			/* Minimum x for coordinate data. */
     FWORD		yMin;			/* Minimum y for coordinate data. */
     FWORD		xMax;			/* Maximum x for coordinate data. */
@@ -148,28 +151,23 @@
     };
 
     HBUINT16 flags;
-    HBUINT16 glyphIndex;
+    GlyphID  glyphIndex;
 
-    inline unsigned int get_size (void) const
+    unsigned int get_size () const
     {
       unsigned int size = min_size;
-      if (flags & ARG_1_AND_2_ARE_WORDS) {
-        // arg1 and 2 are int16
-        size += 4;
-      } else {
-        // arg1 and 2 are int8
-        size += 2;
-      }
-      if (flags & WE_HAVE_A_SCALE) {
-        // One x 16 bit (scale)
-        size += 2;
-      } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
-        // Two x 16 bit (xscale, yscale)
-        size += 4;
-      } else if (flags & WE_HAVE_A_TWO_BY_TWO) {
-        // Four x 16 bit (xscale, scale01, scale10, yscale)
-        size += 8;
-      }
+      // arg1 and 2 are int16
+      if (flags & ARG_1_AND_2_ARE_WORDS) size += 4;
+      // arg1 and 2 are int8
+      else size += 2;
+
+      // One x 16 bit (scale)
+      if (flags & WE_HAVE_A_SCALE) size += 2;
+      // Two x 16 bit (xscale, yscale)
+      else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4;
+      // Four x 16 bit (xscale, scale01, scale10, yscale)
+      else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8;
+
       return size;
     }
 
@@ -179,7 +177,7 @@
       const char *glyph_end;
       const CompositeGlyphHeader *current;
 
-      inline bool move_to_next ()
+      bool move_to_next ()
       {
 	if (current->flags & CompositeGlyphHeader::MORE_COMPONENTS)
 	{
@@ -193,15 +191,15 @@
 	return false;
       }
 
-      inline bool in_range (const CompositeGlyphHeader *composite) const
+      bool in_range (const CompositeGlyphHeader *composite) const
       {
 	return (const char *) composite >= glyph_start
 	  && ((const char *) composite + CompositeGlyphHeader::min_size) <= glyph_end
-	  && ((const char *) composite + composite->get_size()) <= glyph_end;
+	  && ((const char *) composite + composite->get_size ()) <= glyph_end;
       }
     };
 
-    static inline bool get_iterator (const char * glyph_data,
+    static bool get_iterator (const char * glyph_data,
 				     unsigned int length,
 				     CompositeGlyphHeader::Iterator *iterator /* OUT */)
     {
@@ -211,15 +209,15 @@
       const GlyphHeader &glyph_header = StructAtOffset<GlyphHeader> (glyph_data, 0);
       if (glyph_header.numberOfContours < 0)
       {
-        const CompositeGlyphHeader *possible =
+	const CompositeGlyphHeader *possible =
 	  &StructAfter<CompositeGlyphHeader, GlyphHeader> (glyph_header);
 
 	iterator->glyph_start = glyph_data;
 	iterator->glyph_end = (const char *) glyph_data + length;
 	if (!iterator->in_range (possible))
-          return false;
-        iterator->current = possible;
-        return true;
+	  return false;
+	iterator->current = possible;
+	return true;
       }
 
       return false;
@@ -230,34 +228,26 @@
 
   struct accelerator_t
   {
-    inline void init (hb_face_t *face)
+    void init (hb_face_t *face)
     {
       memset (this, 0, sizeof (accelerator_t));
 
-      hb_blob_t *head_blob = hb_sanitize_context_t().reference_table<head> (face);
-      const head *head_table = head_blob->as<head> ();
-      if (head_table->indexToLocFormat > 1 || head_table->glyphDataFormat != 0)
-      {
+      const OT::head &head = *face->table.head;
+      if (head.indexToLocFormat > 1 || head.glyphDataFormat != 0)
 	/* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
-	hb_blob_destroy (head_blob);
 	return;
-      }
-      short_offset = 0 == head_table->indexToLocFormat;
-      hb_blob_destroy (head_blob);
+      short_offset = 0 == head.indexToLocFormat;
 
-      loca_blob = hb_sanitize_context_t().reference_table<loca> (face);
-      loca_table = loca_blob->as<loca> ();
-      glyf_blob = hb_sanitize_context_t().reference_table<glyf> (face);
-      glyf_table = glyf_blob->as<glyf> ();
+      loca_table = hb_sanitize_context_t ().reference_table<loca> (face);
+      glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
 
-      num_glyphs = MAX (1u, hb_blob_get_length (loca_blob) / (short_offset ? 2 : 4)) - 1;
-      glyf_len = hb_blob_get_length (glyf_blob);
+      num_glyphs = MAX (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1;
     }
 
-    inline void fini (void)
+    void fini ()
     {
-      hb_blob_destroy (loca_blob);
-      hb_blob_destroy (glyf_blob);
+      loca_table.destroy ();
+      glyf_table.destroy ();
     }
 
     /*
@@ -265,35 +255,37 @@
      * If true is returned a pointer to the composite glyph will be written into
      * composite.
      */
-    inline bool get_composite (hb_codepoint_t glyph,
-			       CompositeGlyphHeader::Iterator *composite /* OUT */) const
+    bool get_composite (hb_codepoint_t glyph,
+			CompositeGlyphHeader::Iterator *composite /* OUT */) const
     {
       if (unlikely (!num_glyphs))
 	return false;
 
       unsigned int start_offset, end_offset;
       if (!get_offsets (glyph, &start_offset, &end_offset))
-        return false; /* glyph not found */
+	return false; /* glyph not found */
 
-      return CompositeGlyphHeader::get_iterator ((const char*) this->glyf_table + start_offset,
+      return CompositeGlyphHeader::get_iterator ((const char *) this->glyf_table + start_offset,
 						 end_offset - start_offset,
 						 composite);
     }
 
     enum simple_glyph_flag_t {
+      FLAG_ON_CURVE = 0x01,
       FLAG_X_SHORT = 0x02,
       FLAG_Y_SHORT = 0x04,
       FLAG_REPEAT = 0x08,
       FLAG_X_SAME = 0x10,
-      FLAG_Y_SAME = 0x20
+      FLAG_Y_SAME = 0x20,
+      FLAG_RESERVED1 = 0x40,
+      FLAG_RESERVED2 = 0x80
     };
 
     /* based on FontTools _g_l_y_f.py::trim */
-    inline bool remove_padding(unsigned int start_offset,
-                               unsigned int *end_offset) const
+    bool remove_padding (unsigned int start_offset,
+				unsigned int *end_offset) const
     {
-      if (*end_offset - start_offset < GlyphHeader::static_size)
-        return true;
+      if (*end_offset - start_offset < GlyphHeader::static_size) return true;
 
       const char *glyph = ((const char *) glyf_table) + start_offset;
       const char * const glyph_end = glyph + (*end_offset - start_offset);
@@ -301,143 +293,139 @@
       int16_t num_contours = (int16_t) glyph_header.numberOfContours;
 
       if (num_contours < 0)
-        /* Trimming for composites not implemented.
-         * If removing hints it falls out of that. */
-        return true;
+	/* Trimming for composites not implemented.
+	 * If removing hints it falls out of that. */
+	return true;
       else if (num_contours > 0)
       {
-        /* simple glyph w/contours, possibly trimmable */
-        glyph += GlyphHeader::static_size + 2 * num_contours;
+	/* simple glyph w/contours, possibly trimmable */
+	glyph += GlyphHeader::static_size + 2 * num_contours;
 
-        if (unlikely (glyph + 2 >= glyph_end)) return false;
-        uint16_t nCoordinates = (uint16_t) StructAtOffset<HBUINT16>(glyph - 2, 0) + 1;
-        uint16_t nInstructions = (uint16_t) StructAtOffset<HBUINT16>(glyph, 0);
+	if (unlikely (glyph + 2 >= glyph_end)) return false;
+	uint16_t nCoordinates = (uint16_t) StructAtOffset<HBUINT16> (glyph - 2, 0) + 1;
+	uint16_t nInstructions = (uint16_t) StructAtOffset<HBUINT16> (glyph, 0);
 
-        glyph += 2 + nInstructions;
-        if (unlikely (glyph + 2 >= glyph_end)) return false;
+	glyph += 2 + nInstructions;
+	if (unlikely (glyph + 2 >= glyph_end)) return false;
 
-        unsigned int coordBytes = 0;
-        unsigned int coordsWithFlags = 0;
-        while (glyph < glyph_end)
-        {
-          uint8_t flag = (uint8_t) *glyph;
-          glyph++;
+	unsigned int coordBytes = 0;
+	unsigned int coordsWithFlags = 0;
+	while (glyph < glyph_end)
+	{
+	  uint8_t flag = (uint8_t) *glyph;
+	  glyph++;
 
-          unsigned int repeat = 1;
-          if (flag & FLAG_REPEAT)
-          {
-            if (glyph >= glyph_end)
-            {
-              DEBUG_MSG(SUBSET, nullptr, "Bad flag");
-              return false;
-            }
-            repeat = ((uint8_t) *glyph) + 1;
-            glyph++;
-          }
+	  unsigned int repeat = 1;
+	  if (flag & FLAG_REPEAT)
+	  {
+	    if (glyph >= glyph_end)
+	    {
+	      DEBUG_MSG(SUBSET, nullptr, "Bad flag");
+	      return false;
+	    }
+	    repeat = ((uint8_t) *glyph) + 1;
+	    glyph++;
+	  }
 
-          unsigned int xBytes, yBytes;
-          xBytes = yBytes = 0;
-          if (flag & FLAG_X_SHORT)
-            xBytes = 1;
-          else if ((flag & FLAG_X_SAME) == 0)
-            xBytes = 2;
+	  unsigned int xBytes, yBytes;
+	  xBytes = yBytes = 0;
+	  if (flag & FLAG_X_SHORT) xBytes = 1;
+	  else if ((flag & FLAG_X_SAME) == 0) xBytes = 2;
 
-          if (flag & FLAG_Y_SHORT)
-            yBytes = 1;
-          else if ((flag & FLAG_Y_SAME) == 0)
-            yBytes = 2;
+	  if (flag & FLAG_Y_SHORT) yBytes = 1;
+	  else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2;
 
-          coordBytes += (xBytes + yBytes) * repeat;
-          coordsWithFlags += repeat;
-          if (coordsWithFlags >= nCoordinates)
-            break;
-        }
+	  coordBytes += (xBytes + yBytes) * repeat;
+	  coordsWithFlags += repeat;
+	  if (coordsWithFlags >= nCoordinates)
+	    break;
+	}
 
-        if (coordsWithFlags != nCoordinates)
-        {
-          DEBUG_MSG(SUBSET, nullptr, "Expect %d coords to have flags, got flags for %d", nCoordinates, coordsWithFlags);
-          return false;
-        }
-        glyph += coordBytes;
+	if (coordsWithFlags != nCoordinates)
+	{
+	  DEBUG_MSG(SUBSET, nullptr, "Expect %d coords to have flags, got flags for %d", nCoordinates, coordsWithFlags);
+	  return false;
+	}
+	glyph += coordBytes;
 
-        if (glyph < glyph_end)
-          *end_offset -= glyph_end - glyph;
+	if (glyph < glyph_end)
+	  *end_offset -= glyph_end - glyph;
       }
       return true;
     }
 
-    inline bool get_offsets (hb_codepoint_t  glyph,
-                             unsigned int   *start_offset /* OUT */,
-                             unsigned int   *end_offset   /* OUT */) const
+    bool get_offsets (hb_codepoint_t  glyph,
+			     unsigned int   *start_offset /* OUT */,
+			     unsigned int   *end_offset   /* OUT */) const
     {
       if (unlikely (glyph >= num_glyphs))
 	return false;
 
       if (short_offset)
       {
-        const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ;
+	const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ;
 	*start_offset = 2 * offsets[glyph];
 	*end_offset   = 2 * offsets[glyph + 1];
       }
       else
       {
-        const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ;
+	const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ;
 
 	*start_offset = offsets[glyph];
 	*end_offset   = offsets[glyph + 1];
       }
 
-      if (*start_offset > *end_offset || *end_offset > glyf_len)
+      if (*start_offset > *end_offset || *end_offset > glyf_table.get_length ())
 	return false;
 
       return true;
     }
 
-    inline bool get_instruction_offsets(unsigned int start_offset,
-                                        unsigned int end_offset,
-                                        unsigned int *instruction_start /* OUT */,
-                                        unsigned int *instruction_end /* OUT */) const
+    bool get_instruction_offsets (unsigned int start_offset,
+				  unsigned int end_offset,
+				  unsigned int *instruction_start /* OUT */,
+				  unsigned int *instruction_end /* OUT */) const
     {
       if (end_offset - start_offset < GlyphHeader::static_size)
       {
-        *instruction_start = 0;
-        *instruction_end = 0;
-        return true; /* Empty glyph; no instructions. */
+	*instruction_start = 0;
+	*instruction_end = 0;
+	return true; /* Empty glyph; no instructions. */
       }
       const GlyphHeader &glyph_header = StructAtOffset<GlyphHeader> (glyf_table, start_offset);
       int16_t num_contours = (int16_t) glyph_header.numberOfContours;
       if (num_contours < 0)
       {
-        CompositeGlyphHeader::Iterator composite_it;
-        if (unlikely (!CompositeGlyphHeader::get_iterator (
-            (const char*) this->glyf_table + start_offset,
-             end_offset - start_offset, &composite_it))) return false;
-        const CompositeGlyphHeader *last;
-        do {
-          last = composite_it.current;
-        } while (composite_it.move_to_next());
+	CompositeGlyphHeader::Iterator composite_it;
+	if (unlikely (!CompositeGlyphHeader::get_iterator (
+	    (const char*) this->glyf_table + start_offset,
+	     end_offset - start_offset, &composite_it))) return false;
+	const CompositeGlyphHeader *last;
+	do {
+	  last = composite_it.current;
+	} while (composite_it.move_to_next ());
 
-        if ( (uint16_t) last->flags & CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS)
-          *instruction_start = ((char *) last - (char *) glyf_table->dataZ.arrayZ) + last->get_size();
-        else
-          *instruction_start = end_offset;
-        *instruction_end = end_offset;
-        if (unlikely (*instruction_start > *instruction_end))
-        {
-          DEBUG_MSG(SUBSET, nullptr, "Invalid instruction offset, %d is outside [%d, %d]", *instruction_start, start_offset, end_offset);
-          return false;
-        }
+	if ((uint16_t) last->flags & CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS)
+	  *instruction_start = ((char *) last - (char *) glyf_table->dataZ.arrayZ) + last->get_size ();
+	else
+	  *instruction_start = end_offset;
+	*instruction_end = end_offset;
+	if (unlikely (*instruction_start > *instruction_end))
+	{
+	  DEBUG_MSG(SUBSET, nullptr, "Invalid instruction offset, %d is outside [%d, %d]", *instruction_start, start_offset, end_offset);
+	  return false;
+	}
       }
       else
       {
-        unsigned int instruction_length_offset = start_offset + GlyphHeader::static_size + 2 * num_contours;
+	unsigned int instruction_length_offset = start_offset + GlyphHeader::static_size + 2 * num_contours;
 	if (unlikely (instruction_length_offset + 2 > end_offset))
 	{
 	  DEBUG_MSG(SUBSET, nullptr, "Glyph size is too short, missing field instructionLength.");
 	  return false;
 	}
 
-        const HBUINT16 &instruction_length = StructAtOffset<HBUINT16> (glyf_table, instruction_length_offset);
+	const HBUINT16 &instruction_length = StructAtOffset<HBUINT16> (glyf_table, instruction_length_offset);
 	unsigned int start = instruction_length_offset + 2;
 	unsigned int end = start + (uint16_t) instruction_length;
 	if (unlikely (end > end_offset)) // Out of bounds of the current glyph
@@ -447,17 +435,16 @@
 	}
 
 	*instruction_start = start;
-        *instruction_end = end;
+	*instruction_end = end;
       }
       return true;
     }
 
-    inline bool get_extents (hb_codepoint_t glyph,
-			     hb_glyph_extents_t *extents) const
+    bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
     {
       unsigned int start_offset, end_offset;
       if (!get_offsets (glyph, &start_offset, &end_offset))
-        return false;
+	return false;
 
       if (end_offset - start_offset < GlyphHeader::static_size)
 	return true; /* Empty glyph; zero extents. */
@@ -475,17 +462,16 @@
     private:
     bool short_offset;
     unsigned int num_glyphs;
-    const loca *loca_table;
-    const glyf *glyf_table;
-    hb_blob_t *loca_blob;
-    hb_blob_t *glyf_blob;
-    unsigned int glyf_len;
+    hb_blob_ptr_t<loca> loca_table;
+    hb_blob_ptr_t<glyf> glyf_table;
   };
 
   protected:
   UnsizedArrayOf<HBUINT8>	dataZ;		/* Glyphs data. */
-
-  DEFINE_SIZE_ARRAY (0, dataZ);
+  public:
+  DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always
+			* check the size externally, allow Null() object of it by
+			* defining it MIN() instead. */
 };
 
 struct glyf_accelerator_t : glyf::accelerator_t {};
diff --git a/src/hb-ot-hdmx-table.hh b/src/hb-ot-hdmx-table.hh
index 04511b5..48421ee 100644
--- a/src/hb-ot-hdmx-table.hh
+++ b/src/hb-ot-hdmx-table.hh
@@ -44,58 +44,51 @@
   struct SubsetView
   {
     const DeviceRecord *source_device_record;
-    unsigned int size_device_record;
+    unsigned int sizeDeviceRecord;
     hb_subset_plan_t *subset_plan;
 
-    inline void init(const DeviceRecord *source_device_record,
-		     unsigned int size_device_record,
-		     hb_subset_plan_t   *subset_plan)
+    void init (const DeviceRecord *source_device_record,
+	       unsigned int sizeDeviceRecord,
+	       hb_subset_plan_t   *subset_plan)
     {
       this->source_device_record = source_device_record;
-      this->size_device_record = size_device_record;
+      this->sizeDeviceRecord = sizeDeviceRecord;
       this->subset_plan = subset_plan;
     }
 
-    inline unsigned int len () const
-    {
-      return this->subset_plan->glyphs.len;
-    }
+    unsigned int len () const
+    { return this->subset_plan->glyphs.len; }
 
-    inline const HBUINT8* operator [] (unsigned int i) const
+    const HBUINT8* operator [] (unsigned int i) const
     {
-      if (unlikely (i >= len())) return nullptr;
+      if (unlikely (i >= len ())) return nullptr;
       hb_codepoint_t gid = this->subset_plan->glyphs [i];
 
-      const HBUINT8* width = &(this->source_device_record->widthsZ[gid]);
-
-      if (width < ((const HBUINT8 *) this->source_device_record) + size_device_record)
-	return width;
-      else
-	return nullptr;
+      if (gid >= sizeDeviceRecord - DeviceRecord::min_size)
+        return nullptr;
+      return &(this->source_device_record->widthsZ[gid]);
     }
   };
 
-  static inline unsigned int get_size (unsigned int count)
-  {
-    return hb_ceil_to_4 (min_size + count * HBUINT8::static_size);
-  }
+  static unsigned int get_size (unsigned int count)
+  { return hb_ceil_to_4 (min_size + count * HBUINT8::static_size); }
 
-  inline bool serialize (hb_serialize_context_t *c, const SubsetView &subset_view)
+  bool serialize (hb_serialize_context_t *c, const SubsetView &subset_view)
   {
     TRACE_SERIALIZE (this);
 
-    unsigned int size = get_size (subset_view.len());
+    unsigned int size = get_size (subset_view.len ());
     if (unlikely (!c->allocate_size<DeviceRecord> (size)))
     {
-      DEBUG_MSG (SUBSET, nullptr, "Couldn't allocate enough space for DeviceRecord: %d.",
-                 size);
+      DEBUG_MSG(SUBSET, nullptr, "Couldn't allocate enough space for DeviceRecord: %d.",
+		 size);
       return_trace (false);
     }
 
-    this->pixel_size.set (subset_view.source_device_record->pixel_size);
-    this->max_width.set (subset_view.source_device_record->max_width);
+    this->pixelSize.set (subset_view.source_device_record->pixelSize);
+    this->maxWidth.set (subset_view.source_device_record->maxWidth);
 
-    for (unsigned int i = 0; i < subset_view.len(); i++)
+    for (unsigned int i = 0; i < subset_view.len (); i++)
     {
       const HBUINT8 *width = subset_view[i];
       if (!width)
@@ -109,16 +102,16 @@
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int size_device_record) const
+  bool sanitize (hb_sanitize_context_t *c, unsigned int sizeDeviceRecord) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
-			  c->check_range (this, size_device_record)));
+			  c->check_range (this, sizeDeviceRecord)));
   }
 
-  HBUINT8			pixel_size;   /* Pixel size for following widths (as ppem). */
-  HBUINT8			max_width;    /* Maximum width. */
-  UnsizedArrayOf<HBUINT8>	widthsZ;  /* Array of widths (numGlyphs is from the 'maxp' table). */
+  HBUINT8			pixelSize;	/* Pixel size for following widths (as ppem). */
+  HBUINT8			maxWidth;	/* Maximum width. */
+  UnsizedArrayOf<HBUINT8>	widthsZ;	/* Array of widths (numGlyphs is from the 'maxp' table). */
   public:
   DEFINE_SIZE_ARRAY (2, widthsZ);
 };
@@ -126,33 +119,33 @@
 
 struct hdmx
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_hdmx;
+  enum { tableTag = HB_OT_TAG_hdmx };
 
-  inline unsigned int get_size (void) const
+  unsigned int get_size () const
+  { return min_size + numRecords * sizeDeviceRecord; }
+
+  const DeviceRecord& operator [] (unsigned int i) const
   {
-    return min_size + num_records * size_device_record;
+    /* XXX Null(DeviceRecord) is NOT safe as it's num-glyphs lengthed.
+     * https://github.com/harfbuzz/harfbuzz/issues/1300 */
+    if (unlikely (i >= numRecords)) return Null (DeviceRecord);
+    return StructAtOffset<DeviceRecord> (&this->firstDeviceRecord, i * sizeDeviceRecord);
   }
 
-  inline const DeviceRecord& operator [] (unsigned int i) const
-  {
-    if (unlikely (i >= num_records)) return Null(DeviceRecord);
-    return StructAtOffset<DeviceRecord> (&this->dataZ, i * size_device_record);
-  }
-
-  inline bool serialize (hb_serialize_context_t *c, const hdmx *source_hdmx, hb_subset_plan_t *plan)
+  bool serialize (hb_serialize_context_t *c, const hdmx *source_hdmx, hb_subset_plan_t *plan)
   {
     TRACE_SERIALIZE (this);
 
     if (unlikely (!c->extend_min ((*this))))  return_trace (false);
 
     this->version.set (source_hdmx->version);
-    this->num_records.set (source_hdmx->num_records);
-    this->size_device_record.set (DeviceRecord::get_size (plan->glyphs.len));
+    this->numRecords.set (source_hdmx->numRecords);
+    this->sizeDeviceRecord.set (DeviceRecord::get_size (plan->glyphs.len));
 
-    for (unsigned int i = 0; i < source_hdmx->num_records; i++)
+    for (unsigned int i = 0; i < source_hdmx->numRecords; i++)
     {
       DeviceRecord::SubsetView subset_view;
-      subset_view.init (&(*source_hdmx)[i], source_hdmx->size_device_record, plan);
+      subset_view.init (&(*source_hdmx)[i], source_hdmx->sizeDeviceRecord, plan);
 
       if (!c->start_embed<DeviceRecord> ()->serialize (c, subset_view))
 	return_trace (false);
@@ -161,12 +154,12 @@
     return_trace (true);
   }
 
-  static inline size_t get_subsetted_size (const hdmx *source_hdmx, hb_subset_plan_t *plan)
+  static size_t get_subsetted_size (const hdmx *source_hdmx, hb_subset_plan_t *plan)
   {
-    return min_size + source_hdmx->num_records * DeviceRecord::get_size (plan->glyphs.len);
+    return min_size + source_hdmx->numRecords * DeviceRecord::get_size (plan->glyphs.len);
   }
 
-  inline bool subset (hb_subset_plan_t *plan) const
+  bool subset (hb_subset_plan_t *plan) const
   {
     size_t dest_size = get_subsetted_size (this, plan);
     hdmx *dest = (hdmx *) malloc (dest_size);
@@ -197,22 +190,22 @@
     return result;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && version == 0 &&
-		  !hb_unsigned_mul_overflows (num_records, size_device_record) &&
-		  size_device_record >= DeviceRecord::min_size &&
-		  c->check_range (this, get_size()));
+    return_trace (c->check_struct (this) &&
+		  !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) &&
+		  sizeDeviceRecord >= DeviceRecord::min_size &&
+		  c->check_range (this, get_size ()));
   }
 
   protected:
-  HBUINT16			version;		/* Table version number (0) */
-  HBUINT16			num_records;		/* Number of device records. */
-  HBUINT32			size_device_record;	/* Size of a device record, 32-bit aligned. */
-  UnsizedArrayOf<HBUINT8>	dataZ;			/* Array of device records. */
+  HBUINT16		version;		/* Table version number (0) */
+  HBUINT16		numRecords;		/* Number of device records. */
+  HBUINT32		sizeDeviceRecord;	/* Size of a device record, 32-bit aligned. */
+  DeviceRecord		firstDeviceRecord;	/* Array of device records. */
   public:
-  DEFINE_SIZE_ARRAY (8, dataZ);
+  DEFINE_SIZE_MIN (8);
 };
 
 } /* namespace OT */
diff --git a/src/hb-ot-head-table.hh b/src/hb-ot-head-table.hh
index 602e365..41062ca 100644
--- a/src/hb-ot-head-table.hh
+++ b/src/hb-ot-head-table.hh
@@ -45,16 +45,29 @@
 {
   friend struct OffsetTable;
 
-  static const hb_tag_t tableTag	= HB_OT_TAG_head;
+  enum { tableTag = HB_OT_TAG_head };
 
-  inline unsigned int get_upem (void) const
+  unsigned int get_upem () const
   {
     unsigned int upem = unitsPerEm;
     /* If no valid head table found, assume 1000, which matches typical Type1 usage. */
     return 16 <= upem && upem <= 16384 ? upem : 1000;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  enum mac_style_flag_t {
+    BOLD	= 1u<<0,
+    ITALIC	= 1u<<1,
+    UNDERLINE	= 1u<<2,
+    OUTLINE	= 1u<<3,
+    SHADOW	= 1u<<4,
+    CONDENSED	= 1u<<5
+  };
+
+  bool is_bold () const      { return macStyle & BOLD; }
+  bool is_italic () const    { return macStyle & ITALIC; }
+  bool is_condensed () const { return macStyle & CONDENSED; }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh
index 3336cad..7bc1817 100644
--- a/src/hb-ot-hhea-table.hh
+++ b/src/hb-ot-hhea-table.hh
@@ -45,7 +45,7 @@
 template <typename T>
 struct _hea
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && likely (version.major == 1));
@@ -86,10 +86,10 @@
 };
 
 struct hhea : _hea<hhea> {
-  static const hb_tag_t tableTag	= HB_OT_TAG_hhea;
+  enum { tableTag = HB_OT_TAG_hhea };
 };
 struct vhea : _hea<vhea> {
-  static const hb_tag_t tableTag	= HB_OT_TAG_vhea;
+  enum { tableTag = HB_OT_TAG_vhea };
 };
 
 
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index 5293fda..fa74a0e 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -56,7 +56,7 @@
 template <typename T, typename H>
 struct hmtxvmtx
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
   {
     TRACE_SANITIZE (this);
     /* We don't check for anything specific here.  The users of the
@@ -65,11 +65,11 @@
   }
 
 
-  inline bool subset_update_header (hb_subset_plan_t *plan,
-                                    unsigned int num_hmetrics) const
+  bool subset_update_header (hb_subset_plan_t *plan,
+				    unsigned int num_hmetrics) const
   {
-    hb_blob_t *src_blob = hb_sanitize_context_t().reference_table<H> (plan->source, H::tableTag);
-    hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail(src_blob);
+    hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table<H> (plan->source, H::tableTag);
+    hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob);
     hb_blob_destroy (src_blob);
 
     if (unlikely (!dest_blob)) {
@@ -86,7 +86,7 @@
     return result;
   }
 
-  inline bool subset (hb_subset_plan_t *plan) const
+  bool subset (hb_subset_plan_t *plan) const
   {
     typename T::accelerator_t _mtx;
     _mtx.init (plan->source);
@@ -96,15 +96,15 @@
     hb_vector_t<hb_codepoint_t> &gids = plan->glyphs;
     unsigned int num_advances = gids.len;
     unsigned int last_advance = _mtx.get_advance (gids[num_advances - 1]);
-    while (num_advances > 1
-        && last_advance == _mtx.get_advance (gids[num_advances - 2]))
+    while (num_advances > 1 &&
+	   last_advance == _mtx.get_advance (gids[num_advances - 2]))
     {
       num_advances--;
     }
 
     /* alloc the new table */
     size_t dest_sz = num_advances * 4
-                  + (gids.len - num_advances) * 2;
+		  + (gids.len - num_advances) * 2;
     void *dest = (void *) malloc (dest_sz);
     if (unlikely (!dest))
     {
@@ -113,7 +113,7 @@
     DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in src has %d advances, %d lsbs", HB_UNTAG(T::tableTag), _mtx.num_advances, _mtx.num_metrics - _mtx.num_advances);
     DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %u bytes", HB_UNTAG(T::tableTag), num_advances, gids.len - num_advances, (unsigned int) dest_sz);
 
-    const char *source_table = hb_blob_get_data (_mtx.blob, nullptr);
+    const char *source_table = hb_blob_get_data (_mtx.table.get_blob (), nullptr);
     // Copy everything over
     LongMetric * old_metrics = (LongMetric *) source_table;
     FWORD *lsbs = (FWORD *) (old_metrics + _mtx.num_advances);
@@ -186,25 +186,18 @@
   {
     friend struct hmtxvmtx;
 
-    inline void init (hb_face_t *face,
+    void init (hb_face_t *face,
 		      unsigned int default_advance_ = 0)
     {
       default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);
 
       bool got_font_extents = false;
-      if (T::os2Tag)
+      if (T::os2Tag != HB_TAG_NONE && face->table.OS2->is_typo_metrics ())
       {
-	hb_blob_t *os2_blob = hb_sanitize_context_t().reference_table<os2> (face);
-	const os2 *os2_table = os2_blob->as<os2> ();
-#define USE_TYPO_METRICS (1u<<7)
-	if (0 != (os2_table->fsSelection & USE_TYPO_METRICS))
-	{
-	  ascender = os2_table->sTypoAscender;
-	  descender = os2_table->sTypoDescender;
-	  line_gap = os2_table->sTypoLineGap;
-	  got_font_extents = (ascender | descender) != 0;
-	}
-	hb_blob_destroy (os2_blob);
+	ascender = abs (face->table.OS2->sTypoAscender);
+	descender = -abs (face->table.OS2->sTypoDescender);
+	line_gap = face->table.OS2->sTypoLineGap;
+	got_font_extents = (ascender | descender) != 0;
       }
 
       hb_blob_t *_hea_blob = hb_sanitize_context_t().reference_table<H> (face);
@@ -212,8 +205,8 @@
       num_advances = _hea_table->numberOfLongMetrics;
       if (!got_font_extents)
       {
-	ascender = _hea_table->ascender;
-	descender = _hea_table->descender;
+	ascender = abs (_hea_table->ascender);
+	descender = -abs (_hea_table->descender);
 	line_gap = _hea_table->lineGap;
 	got_font_extents = (ascender | descender) != 0;
       }
@@ -221,10 +214,10 @@
 
       has_font_extents = got_font_extents;
 
-      blob = hb_sanitize_context_t().reference_table<hmtxvmtx> (face, T::tableTag);
+      table = hb_sanitize_context_t().reference_table<hmtxvmtx> (face, T::tableTag);
 
       /* Cap num_metrics() and num_advances() based on table length. */
-      unsigned int len = hb_blob_get_length (blob);
+      unsigned int len = table.get_length ();
       if (unlikely (num_advances * 4 > len))
 	num_advances = len / 4;
       num_metrics = num_advances + (len - 4 * num_advances) / 2;
@@ -234,35 +227,33 @@
       if (unlikely (!num_advances))
       {
 	num_metrics = num_advances = 0;
-	hb_blob_destroy (blob);
-	blob = hb_blob_get_empty ();
+	table.destroy ();
+	table = hb_blob_get_empty ();
       }
-      table = blob->as<hmtxvmtx> ();
 
-      var_blob = hb_sanitize_context_t().reference_table<HVARVVAR> (face, T::variationsTag);
-      var_table = var_blob->as<HVARVVAR> ();
+      var_table = hb_sanitize_context_t().reference_table<HVARVVAR> (face, T::variationsTag);
     }
 
-    inline void fini (void)
+    void fini ()
     {
-      hb_blob_destroy (blob);
-      hb_blob_destroy (var_blob);
+      table.destroy ();
+      var_table.destroy ();
     }
 
     /* TODO Add variations version. */
-    inline unsigned int get_side_bearing (hb_codepoint_t glyph) const
+    unsigned int get_side_bearing (hb_codepoint_t glyph) const
     {
       if (glyph < num_advances)
         return table->longMetricZ[glyph].sb;
 
-      if (unlikely (glyph > num_metrics))
+      if (unlikely (glyph >= num_metrics))
         return 0;
 
       const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_advances];
       return bearings[glyph - num_advances];
     }
 
-    inline unsigned int get_advance (hb_codepoint_t glyph) const
+    unsigned int get_advance (hb_codepoint_t glyph) const
     {
       if (unlikely (glyph >= num_metrics))
       {
@@ -278,11 +269,11 @@
       return table->longMetricZ[MIN (glyph, (uint32_t) num_advances - 1)].advance;
     }
 
-    inline unsigned int get_advance (hb_codepoint_t  glyph,
-                                     hb_font_t      *font) const
+    unsigned int get_advance (hb_codepoint_t  glyph,
+			      hb_font_t      *font) const
     {
       unsigned int advance = get_advance (glyph);
-      if (likely(glyph < num_metrics))
+      if (likely (glyph < num_metrics))
       {
 	advance += (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?!
       }
@@ -291,9 +282,9 @@
 
     public:
     bool has_font_extents;
-    unsigned short ascender;
-    unsigned short descender;
-    unsigned short line_gap;
+    int ascender;
+    int descender;
+    int line_gap;
 
     protected:
     unsigned int num_metrics;
@@ -301,10 +292,8 @@
     unsigned int default_advance;
 
     private:
-    const hmtxvmtx *table;
-    hb_blob_t *blob;
-    const HVARVVAR *var_table;
-    hb_blob_t *var_blob;
+    hb_blob_ptr_t<hmtxvmtx> table;
+    hb_blob_ptr_t<HVARVVAR> var_table;
   };
 
   protected:
@@ -334,14 +323,14 @@
 };
 
 struct hmtx : hmtxvmtx<hmtx, hhea> {
-  static const hb_tag_t tableTag	= HB_OT_TAG_hmtx;
-  static const hb_tag_t variationsTag	= HB_OT_TAG_HVAR;
-  static const hb_tag_t os2Tag		= HB_OT_TAG_os2;
+  enum { tableTag = HB_OT_TAG_hmtx };
+  enum { variationsTag = HB_OT_TAG_HVAR };
+  enum { os2Tag = HB_OT_TAG_OS2 };
 };
 struct vmtx : hmtxvmtx<vmtx, vhea> {
-  static const hb_tag_t tableTag	= HB_OT_TAG_vmtx;
-  static const hb_tag_t variationsTag	= HB_OT_TAG_VVAR;
-  static const hb_tag_t os2Tag		= HB_TAG_NONE;
+  enum { tableTag = HB_OT_TAG_vmtx };
+  enum { variationsTag = HB_OT_TAG_VVAR };
+  enum { os2Tag = HB_TAG_NONE };
 };
 
 struct hmtx_accelerator_t : hmtx::accelerator_t {};
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 63551d3..5e75d08 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -27,88 +27,7 @@
 #ifndef HB_OT_KERN_TABLE_HH
 #define HB_OT_KERN_TABLE_HH
 
-#include "hb-open-type.hh"
-#include "hb-ot-shape.hh"
-#include "hb-ot-layout-gsubgpos.hh"
-
-
-template <typename Driver>
-struct hb_kern_machine_t
-{
-  hb_kern_machine_t (const Driver &driver_) : driver (driver_) {}
-
-  HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW
-  inline void kern (hb_font_t   *font,
-		    hb_buffer_t *buffer,
-		    hb_mask_t    kern_mask,
-		    bool         scale = true) const
-  {
-    OT::hb_ot_apply_context_t c (1, font, buffer);
-    c.set_lookup_mask (kern_mask);
-    c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
-    OT::hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c.iter_input;
-    skippy_iter.init (&c);
-
-    bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction);
-    unsigned int count = buffer->len;
-    hb_glyph_info_t *info = buffer->info;
-    hb_glyph_position_t *pos = buffer->pos;
-    for (unsigned int idx = 0; idx < count;)
-    {
-      if (!(info[idx].mask & kern_mask))
-      {
-	idx++;
-	continue;
-      }
-
-      skippy_iter.reset (idx, 1);
-      if (!skippy_iter.next ())
-      {
-	idx++;
-	continue;
-      }
-
-      unsigned int i = idx;
-      unsigned int j = skippy_iter.idx;
-
-      hb_position_t kern = driver.get_kerning (info[i].codepoint,
-					       info[j].codepoint);
-
-
-      if (likely (!kern))
-        goto skip;
-
-
-      if (horizontal)
-      {
-        if (scale)
-	  kern = font->em_scale_x (kern);
-	hb_position_t kern1 = kern >> 1;
-	hb_position_t kern2 = kern - kern1;
-	pos[i].x_advance += kern1;
-	pos[j].x_advance += kern2;
-	pos[j].x_offset += kern2;
-      }
-      else
-      {
-        if (scale)
-	  kern = font->em_scale_y (kern);
-	hb_position_t kern1 = kern >> 1;
-	hb_position_t kern2 = kern - kern1;
-	pos[i].y_advance += kern1;
-	pos[j].y_advance += kern2;
-	pos[j].y_offset += kern2;
-      }
-
-      buffer->unsafe_to_break (i, j + 1);
-
-    skip:
-      idx = skippy_iter.idx;
-    }
-  }
-
-  const Driver &driver;
-};
+#include "hb-aat-layout-kerx-table.hh"
 
 
 /*
@@ -122,374 +41,287 @@
 namespace OT {
 
 
-struct hb_glyph_pair_t
+template <typename KernSubTableHeader>
+struct KernSubTableFormat3
 {
-  hb_codepoint_t left;
-  hb_codepoint_t right;
-};
-
-struct KernPair
-{
-  inline int get_kerning (void) const
-  { return value; }
-
-  inline int cmp (const hb_glyph_pair_t &o) const
+  int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
   {
-    int ret = left.cmp (o.left);
-    if (ret) return ret;
-    return right.cmp (o.right);
+    hb_array_t<const FWORD> kernValue = kernValueZ.as_array (kernValueCount);
+    hb_array_t<const HBUINT8> leftClass = StructAfter<const UnsizedArrayOf<HBUINT8> > (kernValue).as_array (glyphCount);
+    hb_array_t<const HBUINT8> rightClass = StructAfter<const UnsizedArrayOf<HBUINT8> > (leftClass).as_array (glyphCount);
+    hb_array_t<const HBUINT8> kernIndex = StructAfter<const UnsizedArrayOf<HBUINT8> > (rightClass).as_array (leftClassCount * rightClassCount);
+
+    unsigned int leftC = leftClass[left];
+    unsigned int rightC = rightClass[right];
+    if (unlikely (leftC >= leftClassCount || rightC >= rightClassCount))
+      return 0;
+    unsigned int i = leftC * rightClassCount + rightC;
+    return kernValue[kernIndex[i]];
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool apply (AAT::hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    if (!c->plan->requested_kerning)
+      return false;
+
+    if (header.coverage & header.Backwards)
+      return false;
+
+    hb_kern_machine_t<KernSubTableFormat3> machine (*this, header.coverage & header.CrossStream);
+    machine.kern (c->font, c->buffer, c->plan->kern_mask);
+
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  c->check_range (kernValueZ,
+				  kernValueCount * sizeof (FWORD) +
+				  glyphCount * 2 +
+				  leftClassCount * rightClassCount));
+  }
+
+  protected:
+  KernSubTableHeader	header;
+  HBUINT16		glyphCount;	/* The number of glyphs in this font. */
+  HBUINT8		kernValueCount;	/* The number of kerning values. */
+  HBUINT8		leftClassCount;	/* The number of left-hand classes. */
+  HBUINT8		rightClassCount;/* The number of right-hand classes. */
+  HBUINT8		flags;		/* Set to zero (reserved for future use). */
+  UnsizedArrayOf<FWORD>	kernValueZ;	/* The kerning values.
+					 * Length kernValueCount. */
+#if 0
+  UnsizedArrayOf<HBUINT8>leftClass;	/* The left-hand classes.
+					 * Length glyphCount. */
+  UnsizedArrayOf<HBUINT8>rightClass;	/* The right-hand classes.
+					 * Length glyphCount. */
+  UnsizedArrayOf<HBUINT8>kernIndex;	/* The indices into the kernValue array.
+					 * Length leftClassCount * rightClassCount */
+#endif
+  public:
+  DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 6, kernValueZ);
+};
+
+template <typename KernSubTableHeader>
+struct KernSubTable
+{
+  unsigned int get_size () const { return u.header.length; }
+  unsigned int get_type () const { return u.header.format; }
+
+  int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+  {
+    switch (get_type ()) {
+    /* This method hooks up to hb_font_t's get_h_kerning.  Only support Format0. */
+    case 0: return u.format0.get_kerning (left, right);
+    default:return 0;
+    }
+  }
+
+  template <typename context_t>
+  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 (u.header.apple ? c->dispatch (u.format1) : c->default_return_value ());
+    case 2:	return_trace (c->dispatch (u.format2));
+    case 3:	return_trace (u.header.apple ? c->dispatch (u.format3) : c->default_return_value ());
+    default:	return_trace (c->default_return_value ());
+    }
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!u.header.sanitize (c) ||
+		  u.header.length < u.header.min_size ||
+		  !c->check_range (this, u.header.length))) return_trace (false);
+
+    return_trace (dispatch (c));
+  }
+
+  public:
+  union {
+  KernSubTableHeader				header;
+  AAT::KerxSubTableFormat0<KernSubTableHeader>	format0;
+  AAT::KerxSubTableFormat1<KernSubTableHeader>	format1;
+  AAT::KerxSubTableFormat2<KernSubTableHeader>	format2;
+  KernSubTableFormat3<KernSubTableHeader>	format3;
+  } u;
+  public:
+  DEFINE_SIZE_MIN (KernSubTableHeader::static_size);
+};
+
+
+struct KernOTSubTableHeader
+{
+  enum { apple = false };
+  typedef AAT::ObsoleteTypes Types;
+
+  unsigned int tuple_count () const { return 0; }
+  bool is_horizontal () const { return (coverage & Horizontal); }
+
+  enum Coverage
+  {
+    Horizontal	= 0x01u,
+    Minimum	= 0x02u,
+    CrossStream	= 0x04u,
+    Override	= 0x08u,
+
+    /* Not supported: */
+    Backwards	= 0x00u,
+    Variation	= 0x00u,
+  };
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
-  protected:
-  GlyphID	left;
-  GlyphID	right;
-  FWORD		value;
+  public:
+  HBUINT16	versionZ;	/* Unused. */
+  HBUINT16	length;		/* Length of the subtable (including this header). */
+  HBUINT8	format;		/* Subtable format. */
+  HBUINT8	coverage;	/* Coverage bits. */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
-struct KernSubTableFormat0
+struct KernOT : AAT::KerxTable<KernOT>
 {
-  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 ();
-  }
+  friend struct AAT::KerxTable<KernOT>;
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (pairs.sanitize (c));
-  }
+  enum { tableTag = HB_OT_TAG_kern };
+  enum { minVersion = 0u };
+
+  typedef KernOTSubTableHeader SubTableHeader;
+  typedef SubTableHeader::Types Types;
+  typedef KernSubTable<SubTableHeader> SubTable;
 
   protected:
-  BinSearchArrayOf<KernPair> pairs;	/* Array of kerning pairs. */
+  HBUINT16	version;	/* Version--0x0000u */
+  HBUINT16	tableCount;	/* Number of subtables in the kerning table. */
+  SubTable	firstSubTable;	/* Subtables. */
   public:
-  DEFINE_SIZE_ARRAY (8, pairs);
+  DEFINE_SIZE_MIN (4);
 };
 
-struct KernClassTable
-{
-  inline unsigned int get_class (hb_codepoint_t g) const { return classes[g - firstGlyph]; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+struct KernAATSubTableHeader
+{
+  enum { apple = true };
+  typedef AAT::ObsoleteTypes Types;
+
+  unsigned int tuple_count () const { return 0; }
+  bool is_horizontal () const       { return !(coverage & Vertical); }
+
+  enum Coverage
+  {
+    Vertical	= 0x80u,
+    CrossStream	= 0x40u,
+    Variation	= 0x20u,
+
+    /* Not supported: */
+    Backwards	= 0x00u,
+  };
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (firstGlyph.sanitize (c) && classes.sanitize (c));
+    return_trace (c->check_struct (this));
   }
 
-  protected:
-  HBUINT16		firstGlyph;	/* First glyph in class range. */
-  ArrayOf<HBUINT16>	classes;	/* Glyph classes. */
   public:
-  DEFINE_SIZE_ARRAY (4, classes);
+  HBUINT32	length;		/* Length of the subtable (including this header). */
+  HBUINT8	coverage;	/* Coverage bits. */
+  HBUINT8	format;		/* Subtable format. */
+  HBUINT16	tupleIndex;	/* The tuple index (used for variations fonts).
+			       * This value specifies which tuple this subtable covers.
+			       * Note: We don't implement. */
+  public:
+  DEFINE_SIZE_STATIC (8);
 };
 
-struct KernSubTableFormat2
+struct KernAAT : AAT::KerxTable<KernAAT>
 {
-  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
-  {
-    /* This subtable is disabled.  It's not cleaer to me *exactly* where the offests are
-     * based from.  I *think* they should be based from beginning of kern subtable wrapper,
-     * *NOT* "this".  Since we know of no fonts that use this subtable, we are disabling
-     * it.  Someday fix it and re-enable.  Better yet, find fonts that use it... Meh,
-     * Windows doesn't implement it.  Maybe just remove... */
-    return 0;
-    unsigned int l = (this+leftClassTable).get_class (left);
-    unsigned int r = (this+rightClassTable).get_class (right);
-    unsigned int offset = l + r;
-    const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
-    if (unlikely ((const char *) v < (const char *) &array ||
-		  (const char *) v > (const char *) end - 2))
-      return 0;
-    return *v;
-  }
+  friend struct AAT::KerxTable<KernAAT>;
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (true); /* Disabled.  See above. */
-    return_trace (rowWidth.sanitize (c) &&
-		  leftClassTable.sanitize (c, this) &&
-		  rightClassTable.sanitize (c, this) &&
-		  array.sanitize (c, this));
-  }
+  enum { tableTag = HB_OT_TAG_kern };
+  enum { minVersion = 0x00010000u };
+
+  typedef KernAATSubTableHeader SubTableHeader;
+  typedef SubTableHeader::Types Types;
+  typedef KernSubTable<SubTableHeader> SubTable;
 
   protected:
-  HBUINT16	rowWidth;	/* The width, in bytes, of a row in the table. */
-  OffsetTo<KernClassTable>
-		leftClassTable;	/* Offset from beginning of this subtable to
-				 * left-hand class table. */
-  OffsetTo<KernClassTable>
-		rightClassTable;/* Offset from beginning of this subtable to
-				 * right-hand class table. */
-  OffsetTo<FWORD>
-		array;		/* Offset from beginning of this subtable to
-				 * the start of the kerning array. */
+  HBUINT32	version;	/* Version--0x00010000u */
+  HBUINT32	tableCount;	/* Number of subtables in the kerning table. */
+  SubTable	firstSubTable;	/* Subtables. */
   public:
   DEFINE_SIZE_MIN (8);
 };
 
-struct KernSubTable
-{
-  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end, unsigned int format) const
-  {
-    switch (format) {
-    case 0: return u.format0.get_kerning (left, right);
-    case 2: return u.format2.get_kerning (left, right, end);
-    default:return 0;
-    }
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int format) const
-  {
-    TRACE_SANITIZE (this);
-    switch (format) {
-    case 0: return_trace (u.format0.sanitize (c));
-    case 2: return_trace (u.format2.sanitize (c));
-    default:return_trace (true);
-    }
-  }
-
-  protected:
-  union {
-  KernSubTableFormat0	format0;
-  KernSubTableFormat2	format2;
-  } u;
-  public:
-  DEFINE_SIZE_MIN (0);
-};
-
-
-template <typename T>
-struct KernSubTableWrapper
-{
-  /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
-  inline const T* thiz (void) const { return static_cast<const T *> (this); }
-
-  inline bool is_horizontal (void) const
-  { return (thiz()->coverage & T::CheckFlags) == T::CheckHorizontal; }
-
-  inline bool is_override (void) const
-  { return bool (thiz()->coverage & T::Override); }
-
-  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
-  { return thiz()->subtable.get_kerning (left, right, end, thiz()->format); }
-
-  inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
-  { return is_horizontal () ? get_kerning (left, right, end) : 0; }
-
-  inline unsigned int get_size (void) const { return thiz()->length; }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (thiz()) &&
-		  thiz()->length >= T::min_size &&
-		  c->check_range (thiz(), thiz()->length) &&
-		  thiz()->subtable.sanitize (c, thiz()->format));
-  }
-};
-
-template <typename T>
-struct KernTable
-{
-  /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
-  inline const T* thiz (void) const { return static_cast<const T *> (this); }
-
-  inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
-  {
-    int v = 0;
-    const typename T::SubTableWrapper *st = CastP<typename T::SubTableWrapper> (&thiz()->dataZ);
-    unsigned int count = thiz()->nTables;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      if (st->is_override ())
-        v = 0;
-      v += st->get_h_kerning (left, right, st->length + (const char *) st);
-      st = &StructAfter<typename T::SubTableWrapper> (*st);
-    }
-    return v;
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (thiz()) ||
-		  thiz()->version != T::VERSION))
-      return_trace (false);
-
-    const typename T::SubTableWrapper *st = CastP<typename T::SubTableWrapper> (&thiz()->dataZ);
-    unsigned int count = thiz()->nTables;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      if (unlikely (!st->sanitize (c)))
-	return_trace (false);
-      st = &StructAfter<typename T::SubTableWrapper> (*st);
-    }
-
-    return_trace (true);
-  }
-};
-
-struct KernOT : KernTable<KernOT>
-{
-  friend struct KernTable<KernOT>;
-
-  static const uint16_t VERSION = 0x0000u;
-
-  struct SubTableWrapper : KernSubTableWrapper<SubTableWrapper>
-  {
-    friend struct KernTable<KernOT>;
-    friend struct KernSubTableWrapper<SubTableWrapper>;
-
-    enum Coverage
-    {
-      Direction		= 0x01u,
-      Minimum		= 0x02u,
-      CrossStream	= 0x04u,
-      Override		= 0x08u,
-
-      Variation		= 0x00u, /* Not supported. */
-
-      CheckFlags	= 0x07u,
-      CheckHorizontal	= 0x01u
-    };
-
-    protected:
-    HBUINT16	versionZ;	/* Unused. */
-    HBUINT16	length;		/* Length of the subtable (including this header). */
-    HBUINT8	format;		/* Subtable format. */
-    HBUINT8	coverage;	/* Coverage bits. */
-    KernSubTable subtable;	/* Subtable data. */
-    public:
-    DEFINE_SIZE_MIN (6);
-  };
-
-  protected:
-  HBUINT16			version;	/* Version--0x0000u */
-  HBUINT16			nTables;	/* Number of subtables in the kerning table. */
-  UnsizedArrayOf<HBUINT8>	dataZ;
-  public:
-  DEFINE_SIZE_ARRAY (4, dataZ);
-};
-
-struct KernAAT : KernTable<KernAAT>
-{
-  friend struct KernTable<KernAAT>;
-
-  static const uint32_t VERSION = 0x00010000u;
-
-  struct SubTableWrapper : KernSubTableWrapper<SubTableWrapper>
-  {
-    friend struct KernTable<KernAAT>;
-    friend struct KernSubTableWrapper<SubTableWrapper>;
-
-    enum Coverage
-    {
-      Direction		= 0x80u,
-      CrossStream	= 0x40u,
-      Variation		= 0x20u,
-
-      Override		= 0x00u, /* Not supported. */
-
-      CheckFlags	= 0xE0u,
-      CheckHorizontal	= 0x00u
-    };
-
-    protected:
-    HBUINT32	length;		/* Length of the subtable (including this header). */
-    HBUINT8	coverage;	/* Coverage bits. */
-    HBUINT8	format;		/* Subtable format. */
-    HBUINT16	tupleIndex;	/* The tuple index (used for variations fonts).
-				 * This value specifies which tuple this subtable covers. */
-    KernSubTable subtable;	/* Subtable data. */
-    public:
-    DEFINE_SIZE_MIN (8);
-  };
-
-  protected:
-  HBUINT32			version;	/* Version--0x00010000u */
-  HBUINT32			nTables;	/* Number of subtables in the kerning table. */
-  UnsizedArrayOf<HBUINT8>	dataZ;
-  public:
-  DEFINE_SIZE_ARRAY (8, dataZ);
-};
-
 struct kern
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_kern;
+  enum { tableTag = HB_OT_TAG_kern };
 
-  inline bool has_data (void) const
-  { return u.version32 != 0; }
+  bool has_data () const { return u.version32; }
+  unsigned int get_type () const { return u.major; }
 
-  inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+  bool has_state_machine () const
   {
-    switch (u.major) {
+    switch (get_type ()) {
+    case 0: return u.ot.has_state_machine ();
+    case 1: return u.aat.has_state_machine ();
+    default:return false;
+    }
+  }
+
+  bool has_cross_stream () const
+  {
+    switch (get_type ()) {
+    case 0: return u.ot.has_cross_stream ();
+    case 1: return u.aat.has_cross_stream ();
+    default:return false;
+    }
+  }
+
+  int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+  {
+    switch (get_type ()) {
     case 0: return u.ot.get_h_kerning (left, right);
     case 1: return u.aat.get_h_kerning (left, right);
     default:return 0;
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool apply (AAT::hb_aat_apply_context_t *c) const
+  { return dispatch (c); }
+
+  template <typename context_t>
+  typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_SANITIZE (this);
-    if (!u.version32.sanitize (c)) return_trace (false);
-    switch (u.major) {
-    case 0: return_trace (u.ot.sanitize (c));
-    case 1: return_trace (u.aat.sanitize (c));
-    default:return_trace (true);
+    unsigned int subtable_type = get_type ();
+    TRACE_DISPATCH (this, subtable_type);
+    switch (subtable_type) {
+    case 0:	return_trace (c->dispatch (u.ot));
+    case 1:	return_trace (c->dispatch (u.aat));
+    default:	return_trace (c->default_return_value ());
     }
   }
 
-  struct accelerator_t
+  bool sanitize (hb_sanitize_context_t *c) const
   {
-    inline void init (hb_face_t *face)
-    {
-      blob = hb_sanitize_context_t().reference_table<kern> (face);
-      table = blob->as<kern> ();
-    }
-    inline void fini (void)
-    {
-      hb_blob_destroy (blob);
-    }
-
-    inline bool has_data (void) const
-    { return table->has_data (); }
-
-    inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
-    { return table->get_h_kerning (left, right); }
-
-    inline int get_kerning (hb_codepoint_t first, hb_codepoint_t second) const
-    { return get_h_kerning (first, second); }
-
-    inline void apply (hb_font_t *font,
-		       hb_buffer_t  *buffer,
-		       hb_mask_t kern_mask) const
-    {
-      /* We only apply horizontal kerning in this table. */
-      if (!HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
-        return;
-
-      hb_kern_machine_t<accelerator_t> machine (*this);
-
-      machine.kern (font, buffer, kern_mask);
-    }
-
-    private:
-    hb_blob_t *blob;
-    const kern *table;
-  };
+    TRACE_SANITIZE (this);
+    if (!u.version32.sanitize (c)) return_trace (false);
+    return_trace (dispatch (c));
+  }
 
   protected:
   union {
@@ -502,8 +334,6 @@
   DEFINE_SIZE_UNION (4, version32);
 };
 
-struct kern_accelerator_t : kern::accelerator_t {};
-
 } /* namespace OT */
 
 
diff --git a/src/hb-ot-layout-base-table.hh b/src/hb-ot-layout-base-table.hh
index 449e745..6c39932 100644
--- a/src/hb-ot-layout-base-table.hh
+++ b/src/hb-ot-layout-base-table.hh
@@ -1,6 +1,7 @@
 /*
  * Copyright © 2016 Elie Roux <elie.roux@telecom-bretagne.eu>
  * Copyright © 2018  Google, Inc.
+ * Copyright © 2018  Ebrahim Byagowi
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -31,6 +32,9 @@
 #include "hb-open-type.hh"
 #include "hb-ot-layout-common.hh"
 
+/* To be removed */
+typedef hb_tag_t hb_ot_layout_baseline_t;
+
 namespace OT {
 
 /*
@@ -38,19 +42,14 @@
  * https://docs.microsoft.com/en-us/typography/opentype/spec/base
  */
 
-
-/* XXX Review this. */
-#define NOT_INDEXED		((unsigned int) -1)
-
-
 struct BaseCoordFormat1
 {
-  inline int get_coord (void) const { return coordinate; }
+  hb_position_t get_coord () const { return coordinate; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
+    return_trace (likely (c->check_struct (this)));
   }
 
   protected:
@@ -62,13 +61,13 @@
 
 struct BaseCoordFormat2
 {
-  inline int get_coord (void) const
+  hb_position_t get_coord () const
   {
     /* TODO */
     return coordinate;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -86,45 +85,53 @@
 
 struct BaseCoordFormat3
 {
-  inline int get_coord (void) const
+  hb_position_t get_coord (hb_font_t *font,
+			   const VariationStore &var_store,
+			   hb_direction_t direction) const
   {
-    /* TODO */
-    return coordinate;
+    const Device &device = this+deviceTable;
+    return coordinate + (HB_DIRECTION_IS_VERTICAL (direction) ?
+			 device.get_y_delta (font, var_store) :
+			 device.get_x_delta (font, var_store));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && deviceTable.sanitize (c, this));
+    return_trace (likely (c->check_struct (this) &&
+			  deviceTable.sanitize (c, this)));
   }
 
   protected:
-  HBUINT16		format;		/* Format identifier--format = 3 */
-  FWORD			coordinate;	/* X or Y value, in design units */
-  OffsetTo<Device>	deviceTable;	/* Offset to Device table for X or
-					 * Y value, from beginning of
-					 * BaseCoord table (may be NULL). */
+  HBUINT16	format;		/* Format identifier--format = 3 */
+  FWORD		coordinate;	/* X or Y value, in design units */
+  OffsetTo<Device>
+		deviceTable;	/* Offset to Device table for X or
+				 * Y value, from beginning of
+				 * BaseCoord table (may be NULL). */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 struct BaseCoord
 {
-  inline int get_coord (void) const
+  hb_position_t get_coord (hb_font_t *font,
+			   const VariationStore &var_store,
+			   hb_direction_t direction) const
   {
-    /* XXX wire up direction and font. */
     switch (u.format) {
     case 1: return u.format1.get_coord ();
     case 2: return u.format2.get_coord ();
-    case 3: return u.format3.get_coord ();
+    case 3: return u.format3.get_coord (font, var_store, direction);
     default:return 0;
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return_trace (false);
+    if (unlikely (!u.format.sanitize (c))) return_trace (false);
     switch (u.format) {
     case 1: return_trace (u.format1.sanitize (c));
     case 2: return_trace (u.format2.sanitize (c));
@@ -146,28 +153,40 @@
 
 struct FeatMinMaxRecord
 {
-  inline int get_min_value (void) const { return (this+minCoord).get_coord(); }
-  inline int get_max_value (void) const { return (this+maxCoord).get_coord(); }
+  static int cmp (const void *key_, const void *entry_)
+  {
+    hb_tag_t key = * (hb_tag_t *) key_;
+    const FeatMinMaxRecord &entry = * (const FeatMinMaxRecord *) entry_;
+    return key < (unsigned int) entry.tag ? -1 :
+	   key > (unsigned int) entry.tag ? 1 :
+	   0;
+  }
 
-  inline const Tag& get_tag () const { return tag; }
+  void get_min_max (const BaseCoord **min, const BaseCoord **max) const
+  {
+    if (likely (min)) *min = &(this+minCoord);
+    if (likely (max)) *max = &(this+maxCoord);
+  }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  minCoord.sanitize (c, base) &&
-		  maxCoord.sanitize (c, base));
+    return_trace (likely (c->check_struct (this) &&
+			  minCoord.sanitize (c, this) &&
+			  maxCoord.sanitize (c, this)));
   }
 
   protected:
-  Tag                   tag;		/* 4-byte feature identification tag--must
-					 * match feature tag in FeatureList */
-  OffsetTo<BaseCoord>   minCoord;	/* Offset to BaseCoord table that defines
-					 * the minimum extent value, from beginning
-					 * of MinMax table (may be NULL) */
-  OffsetTo<BaseCoord>   maxCoord;	/* Offset to BaseCoord table that defines
-					 * the maximum extent value, from beginning
-					 * of MinMax table (may be NULL) */
+  Tag		tag;		/* 4-byte feature identification tag--must
+				 * match feature tag in FeatureList */
+  OffsetTo<BaseCoord>
+		minCoord;	/* Offset to BaseCoord table that defines
+				 * the minimum extent value, from beginning
+				 * of MinMax table (may be NULL) */
+  OffsetTo<BaseCoord>
+		maxCoord;	/* Offset to BaseCoord table that defines
+				 * the maximum extent value, from beginning
+				 * of MinMax table (may be NULL) */
   public:
   DEFINE_SIZE_STATIC (8);
 
@@ -175,260 +194,207 @@
 
 struct MinMax
 {
-  inline unsigned int get_feature_tag_index (Tag featureTableTag) const
+  void get_min_max (hb_tag_t          feature_tag,
+			   const BaseCoord **min,
+			   const BaseCoord **max) const
   {
-    /* TODO bsearch */
-    unsigned int count = featMinMaxRecords.len;
-    for (unsigned int i = 0; i < count; i++)
+    /* TODO Replace hb_bsearch() with .bsearch(). */
+    const FeatMinMaxRecord *minMaxCoord = (const FeatMinMaxRecord *)
+					  hb_bsearch (&feature_tag, featMinMaxRecords.arrayZ,
+						      featMinMaxRecords.len,
+						      FeatMinMaxRecord::static_size,
+						      FeatMinMaxRecord::cmp);
+    if (minMaxCoord)
+      minMaxCoord->get_min_max (min, max);
+    else
     {
-      Tag tag = featMinMaxRecords[i].get_tag ();
-      int cmp = tag.cmp(featureTableTag);
-      if (cmp == 0) return i;
-      if (cmp > 0)  return NOT_INDEXED;
+      if (likely (min)) *min = &(this+minCoord);
+      if (likely (max)) *max = &(this+maxCoord);
     }
-    return NOT_INDEXED;
   }
 
-  inline int get_min_value (unsigned int featureTableTagIndex) const
-  {
-    if (featureTableTagIndex == NOT_INDEXED)
-      return (this+minCoord).get_coord();
-    return featMinMaxRecords[featureTableTagIndex].get_min_value();
-  }
-
-  inline int get_max_value (unsigned int featureTableTagIndex) const
-  {
-    if (featureTableTagIndex == NOT_INDEXED)
-      return (this+maxCoord).get_coord();
-    return featMinMaxRecords[featureTableTagIndex].get_max_value();
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  minCoord.sanitize (c, this) &&
-		  maxCoord.sanitize (c, this) &&
-		  featMinMaxRecords.sanitize (c, this));
+    return_trace (likely (c->check_struct (this) &&
+			  minCoord.sanitize (c, this) &&
+			  maxCoord.sanitize (c, this) &&
+			  featMinMaxRecords.sanitize (c, this)));
   }
 
   protected:
-  OffsetTo<BaseCoord>	minCoord;	/* Offset to BaseCoord table that defines
-					 * minimum extent value, from the beginning
-					 * of MinMax table (may be NULL) */
-  OffsetTo<BaseCoord>	maxCoord;	/* Offset to BaseCoord table that defines
-					 * maximum extent value, from the beginning
-					 * of MinMax table (may be NULL) */
-  ArrayOf<FeatMinMaxRecord>
-		featMinMaxRecords;	/* Array of FeatMinMaxRecords, in alphabetical
-					 * order by featureTableTag */
+  OffsetTo<BaseCoord>
+		minCoord;	/* Offset to BaseCoord table that defines
+				 * minimum extent value, from the beginning
+				 * of MinMax table (may be NULL) */
+  OffsetTo<BaseCoord>
+		maxCoord;	/* Offset to BaseCoord table that defines
+				 * maximum extent value, from the beginning
+				 * of MinMax table (may be NULL) */
+  SortedArrayOf<FeatMinMaxRecord>
+		featMinMaxRecords;
+				/* Array of FeatMinMaxRecords, in alphabetical
+				 * order by featureTableTag */
   public:
   DEFINE_SIZE_ARRAY (6, featMinMaxRecords);
 };
 
-/* TODO... */
-struct BaseLangSysRecord
-{
-  inline const Tag& get_tag(void) const
-  { return baseLangSysTag; }
-
-  inline unsigned int get_feature_tag_index (Tag featureTableTag) const
-  { return (this+minMax).get_feature_tag_index( featureTableTag); }
-
-  inline int get_min_value (unsigned int featureTableTagIndex) const
-  { return (this+minMax).get_min_value( featureTableTagIndex); }
-
-  inline int get_max_value (unsigned int featureTableTagIndex) const
-  { return (this+minMax).get_max_value (featureTableTagIndex); }
-
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  minMax.sanitize (c, base));
-  }
-
-  protected:
-  Tag			baseLangSysTag;
-  OffsetTo<MinMax>	minMax;
-  public:
-  DEFINE_SIZE_STATIC (6);
-
-};
-
 struct BaseValues
 {
-  inline unsigned int get_default_base_tag_index (void) const
-  { return defaultIndex; }
-
-  inline int get_base_coord (unsigned int baselineTagIndex) const
+  const BaseCoord &get_base_coord (int baseline_tag_index) const
   {
-    return (this+baseCoords[baselineTagIndex]).get_coord ();
+    if (baseline_tag_index == -1) baseline_tag_index = defaultIndex;
+    return this+baseCoords[baseline_tag_index];
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  baseCoords.sanitize (c, this));
+    return_trace (likely (c->check_struct (this) &&
+			  baseCoords.sanitize (c, this)));
   }
 
   protected:
-  Index				defaultIndex;
-  OffsetArrayOf<BaseCoord>	baseCoords;
+  Index		defaultIndex;	/* Index number of default baseline for this
+				 * script — equals index position of baseline tag
+				 * in baselineTags array of the BaseTagList */
+  OffsetArrayOf<BaseCoord>
+		baseCoords;	/* Number of BaseCoord tables defined — should equal
+				 * baseTagCount in the BaseTagList
+				 *
+				 * Array of offsets to BaseCoord tables, from beginning of
+				 * BaseValues table — order matches baselineTags array in
+				 * the BaseTagList */
   public:
   DEFINE_SIZE_ARRAY (4, baseCoords);
 };
 
-struct BaseScript {
-
-  inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const
+struct BaseLangSysRecord
+{
+  static int cmp (const void *key_, const void *entry_)
   {
-    /* XXX bsearch */
-    Tag tag;
-    int cmp;
-    unsigned int count = baseLangSysRecords.len;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      tag = baseLangSysRecords[i].get_tag ();
-      // taking advantage of alphabetical order
-      cmp = tag.cmp(baseLangSysTag);
-      if (cmp == 0) return i;
-      if (cmp > 0)  return NOT_INDEXED;
-    }
-    return NOT_INDEXED;
+    hb_tag_t key = * (hb_tag_t *) key_;
+    const BaseLangSysRecord &entry = * (const BaseLangSysRecord *) entry_;
+    return key < (unsigned int) entry.baseLangSysTag ? -1 :
+	   key > (unsigned int) entry.baseLangSysTag ? 1 :
+	   0;
   }
 
-  inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const
-  {
-    if (baseLangSysIndex == NOT_INDEXED)
-    {
-      if (unlikely(defaultMinMax)) return NOT_INDEXED;
-      return (this+defaultMinMax).get_feature_tag_index (featureTableTag);
-    }
-    return baseLangSysRecords[baseLangSysIndex].get_feature_tag_index (featureTableTag);
-  }
+  const MinMax &get_min_max () const
+  { return this+minMax; }
 
-  inline int get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    if (baseLangSysIndex == NOT_INDEXED)
-      return (this+defaultMinMax).get_min_value (featureTableTagIndex);
-    return baseLangSysRecords[baseLangSysIndex].get_max_value (featureTableTagIndex);
-  }
-
-  inline int get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    if (baseLangSysIndex == NOT_INDEXED)
-      return (this+defaultMinMax).get_min_value (featureTableTagIndex);
-    return baseLangSysRecords[baseLangSysIndex].get_max_value (featureTableTagIndex);
-  }
-
-  inline unsigned int get_default_base_tag_index (void) const
-  { return (this+baseValues).get_default_base_tag_index (); }
-
-  inline int get_base_coord (unsigned int baselineTagIndex) const
-  { return (this+baseValues).get_base_coord (baselineTagIndex); }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  baseValues.sanitize (c, this) &&
-		  defaultMinMax.sanitize (c, this) &&
-		  baseLangSysRecords.sanitize (c, this));
+    return_trace (likely (c->check_struct (this) &&
+			  minMax.sanitize (c, this)));
   }
 
   protected:
-  OffsetTo<BaseValues>		baseValues;
-  OffsetTo<MinMax>		defaultMinMax;
-  ArrayOf<BaseLangSysRecord>	baseLangSysRecords;
-
+  Tag		baseLangSysTag;	/* 4-byte language system identification tag */
+  OffsetTo<MinMax>
+		minMax;		/* Offset to MinMax table, from beginning
+				 * of BaseScript table */
   public:
-    DEFINE_SIZE_ARRAY (6, baseLangSysRecords);
+  DEFINE_SIZE_STATIC (6);
 };
 
+struct BaseScript
+{
+  const MinMax &get_min_max (hb_tag_t language_tag) const
+  {
+    /* TODO Replace hb_bsearch() with .bsearch(). */
+    const BaseLangSysRecord* record = (const BaseLangSysRecord *)
+				      hb_bsearch (&language_tag, baseLangSysRecords.arrayZ,
+						  baseLangSysRecords.len,
+						  BaseLangSysRecord::static_size,
+						  BaseLangSysRecord::cmp);
+    return record ? record->get_min_max () : this+defaultMinMax;
+  }
 
-struct BaseScriptRecord {
+  const BaseCoord &get_base_coord (int baseline_tag_index) const
+  { return (this+baseValues).get_base_coord (baseline_tag_index); }
 
-  inline const Tag& get_tag (void) const
-  { return baseScriptTag; }
+  bool is_empty () const { return !baseValues; }
 
-  inline unsigned int get_default_base_tag_index(void) const
-  { return (this+baseScript).get_default_base_tag_index (); }
-
-  inline int get_base_coord(unsigned int baselineTagIndex) const
-  { return (this+baseScript).get_base_coord (baselineTagIndex); }
-
-  inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const
-  { return (this+baseScript).get_lang_tag_index (baseLangSysTag); }
-
-  inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const
-  { return (this+baseScript).get_feature_tag_index (baseLangSysIndex, featureTableTag); }
-
-  inline int get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  { return (this+baseScript).get_max_value (baseLangSysIndex, featureTableTagIndex); }
-
-  inline int get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  { return (this+baseScript).get_min_value (baseLangSysIndex, featureTableTagIndex); }
-
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  baseScript.sanitize (c, base));
+    return_trace (likely (c->check_struct (this) &&
+			  baseValues.sanitize (c, this) &&
+			  defaultMinMax.sanitize (c, this) &&
+			  baseLangSysRecords.sanitize (c, this)));
   }
 
   protected:
-  Tag                   baseScriptTag;
-  OffsetTo<BaseScript>  baseScript;
+  OffsetTo<BaseValues>
+		baseValues;	/* Offset to BaseValues table, from beginning
+				 * of BaseScript table (may be NULL) */
+  OffsetTo<MinMax>
+		defaultMinMax;	/* Offset to MinMax table, from beginning of
+				 * BaseScript table (may be NULL) */
+  SortedArrayOf<BaseLangSysRecord>
+		baseLangSysRecords;
+				/* Number of BaseLangSysRecords
+				 * defined — may be zero (0) */
 
   public:
-    DEFINE_SIZE_STATIC (6);
+  DEFINE_SIZE_ARRAY (6, baseLangSysRecords);
 };
 
-struct BaseScriptList {
-
-  inline unsigned int get_base_script_index (Tag baseScriptTag) const
+struct BaseScriptList;
+struct BaseScriptRecord
+{
+  static int cmp (const void *key_, const void *entry_)
   {
-    /* XXX bsearch? */
-    unsigned int count = baseScriptRecords.len;
-    for (unsigned int i = 0; i < count; i++)
-      if (baseScriptRecords[i].get_tag() == baseScriptTag)
-        return i;
-    return NOT_INDEXED;
+    hb_tag_t key = * (hb_tag_t *) key_;
+    const BaseScriptRecord &entry = * (const BaseScriptRecord *) entry_;
+    return key < (unsigned int) entry.baseScriptTag ? -1 :
+	   key > (unsigned int) entry.baseScriptTag ? 1 :
+	   0;
   }
 
-  inline unsigned int get_default_base_tag_index (unsigned int baseScriptIndex) const
+  const BaseScript &get_base_script (const BaseScriptList *list) const
+  { return list+baseScript; }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
-    return baseScriptRecords[baseScriptIndex].get_default_base_tag_index();
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  baseScript.sanitize (c, base)));
   }
 
-  inline int get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  protected:
+  Tag		baseScriptTag;	/* 4-byte script identification tag */
+  OffsetTo<BaseScript>
+		baseScript;	/* Offset to BaseScript table, from beginning
+				 * of BaseScriptList */
+
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct BaseScriptList
+{
+  const BaseScriptRecord *find_record (hb_tag_t script) const
   {
-    return baseScriptRecords[baseScriptIndex].get_base_coord(baselineTagIndex);
+    /* TODO Replace hb_bsearch() with .bsearch(). */
+    return (const BaseScriptRecord *) hb_bsearch (&script, baseScriptRecords.arrayZ,
+						  baseScriptRecords.len,
+						  BaseScriptRecord::static_size,
+						  BaseScriptRecord::cmp);
   }
 
-  inline unsigned int get_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
+  /* TODO: Or client should handle fallback? */
+  const BaseScript &get_base_script (hb_tag_t script) const
   {
-    return baseScriptRecords[baseScriptIndex].get_lang_tag_index(baseLangSysTag);
+    const BaseScriptRecord *record = find_record (script);
+    if (!record) record = find_record ((hb_script_t) HB_TAG ('D','F','L','T'));
+
+    return record ? record->get_base_script (this) : Null (BaseScript);
   }
 
-  inline unsigned int get_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
-  {
-    return baseScriptRecords[baseScriptIndex].get_feature_tag_index(baseLangSysIndex, featureTableTag);
-  }
-
-  inline int get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return baseScriptRecords[baseScriptIndex].get_max_value(baseLangSysIndex, featureTableTagIndex);
-  }
-
-  inline int get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return baseScriptRecords[baseScriptIndex].get_min_value(baseLangSysIndex, featureTableTagIndex);
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -436,86 +402,61 @@
   }
 
   protected:
-  ArrayOf<BaseScriptRecord>	baseScriptRecords;
+  SortedArrayOf<BaseScriptRecord>
+			baseScriptRecords;
 
   public:
   DEFINE_SIZE_ARRAY (2, baseScriptRecords);
 };
 
-struct BaseTagList
-{
-  inline unsigned int get_tag_index (Tag baselineTag) const
-  {
-    /* TODO bsearch? */
-    unsigned int count = baselineTags.len;
-    for (unsigned int i = 0; i < count; i++)
-      if (baselineTags[i] == baselineTag)
-        return i;
-    return NOT_INDEXED;
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
-  }
-
-  protected:
-  SortedArrayOf<Tag>  baselineTags;
-
-  public:
-  DEFINE_SIZE_ARRAY (2, baselineTags);
-};
-
 struct Axis
 {
-
-  inline unsigned int get_base_tag_index (Tag baselineTag) const
+  bool get_baseline (hb_ot_layout_baseline_t   baseline,
+			    hb_tag_t                  script_tag,
+			    hb_tag_t                  language_tag,
+			    const BaseCoord         **coord) const
   {
-    return (this+baseTagList).get_tag_index(baselineTag);
+    const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
+    if (base_script.is_empty ()) return false;
+
+    if (likely (coord)) *coord = &base_script.get_base_coord ((this+baseTagList).bsearch (baseline));
+
+    return true;
   }
 
-  inline unsigned int get_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
+  bool get_min_max (hb_tag_t          script_tag,
+		    hb_tag_t          language_tag,
+		    hb_tag_t          feature_tag,
+		    const BaseCoord **min_coord,
+		    const BaseCoord **max_coord) const
   {
-    return (this+baseScriptList).get_default_base_tag_index(baseScriptIndex);
+    const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
+    if (base_script.is_empty ()) return false;
+
+    base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord);
+
+    return true;
   }
 
-  inline int get_base_coord (unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
-  {
-    return (this+baseScriptList).get_base_coord(baseScriptIndex, baselineTagIndex);
-  }
-
-  inline unsigned int get_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
-  {
-    return (this+baseScriptList).get_lang_tag_index(baseScriptIndex, baseLangSysTag);
-  }
-
-  inline unsigned int get_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
-  {
-    return (this+baseScriptList).get_feature_tag_index(baseScriptIndex, baseLangSysIndex, featureTableTag);
-  }
-
-  inline int get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+baseScriptList).get_max_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
-  }
-
-  inline int get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+baseScriptList).get_min_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  baseTagList.sanitize (c, this) &&
-		  baseScriptList.sanitize (c, this));
+    return_trace (likely (c->check_struct (this) &&
+			  (this+baseTagList).sanitize (c) &&
+			  (this+baseScriptList).sanitize (c)));
   }
 
   protected:
-  OffsetTo<BaseTagList>		baseTagList;
-  OffsetTo<BaseScriptList>	baseScriptList;
+  OffsetTo<SortedArrayOf<Tag> >
+		baseTagList;	/* Offset to BaseTagList table, from beginning
+				 * of Axis table (may be NULL)
+				 * Array of 4-byte baseline identification tags — must
+				 * be in alphabetical order */
+  OffsetTo<BaseScriptList>
+		baseScriptList;	/* Offset to BaseScriptList table, from beginning
+				 * of Axis table
+				 * Array of BaseScriptRecords, in alphabetical order
+				 * by baseScriptTag */
 
   public:
   DEFINE_SIZE_STATIC (4);
@@ -523,101 +464,72 @@
 
 struct BASE
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_BASE;
+  enum { tableTag = HB_OT_TAG_BASE };
 
-  inline bool has_v_axis(void) { return vAxis != 0; }
+  const Axis &get_axis (hb_direction_t direction) const
+  { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; }
 
-  inline bool has_h_axis(void) { return hAxis != 0; }
+  const VariationStore &get_var_store () const
+  { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; }
 
-  inline unsigned int get_h_base_tag_index (Tag baselineTag) const
+  bool get_baseline (hb_font_t               *font,
+		     hb_ot_layout_baseline_t  baseline,
+		     hb_direction_t           direction,
+		     hb_tag_t                 script_tag,
+		     hb_tag_t                 language_tag,
+		     hb_position_t           *base) const
   {
-    return (this+hAxis).get_base_tag_index(baselineTag);
+    const BaseCoord *base_coord;
+    if (!get_axis (direction).get_baseline (baseline, script_tag, language_tag, &base_coord))
+      return false;
+
+    if (likely (base && base_coord)) *base = base_coord->get_coord (font,
+								    get_var_store (),
+								    direction);
+    return true;
   }
 
-  inline unsigned int get_h_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
+  /* TODO: Expose this separately sometime? */
+  bool get_min_max (hb_font_t      *font,
+		    hb_direction_t  direction,
+		    hb_tag_t        script_tag,
+		    hb_tag_t        language_tag,
+		    hb_tag_t        feature_tag,
+		    hb_position_t  *min,
+		    hb_position_t  *max)
   {
-    return (this+hAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
+    const BaseCoord *min_coord, *max_coord;
+    if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,
+					   &min_coord, &max_coord))
+      return false;
+
+    const VariationStore &var_store = get_var_store ();
+    if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction);
+    if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction);
+    return true;
   }
 
-  inline int get_h_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
-  {
-    return (this+hAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
-  }
-
-  inline unsigned int get_v_base_tag_index(Tag baselineTag) const
-  {
-    return (this+vAxis).get_base_tag_index(baselineTag);
-  }
-
-  inline unsigned int get_v_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
-  {
-    return (this+vAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
-  }
-
-  inline int get_v_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
-  {
-    return (this+vAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
-  }
-
-  inline unsigned int get_h_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
-  {
-    return (this+hAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
-  }
-
-  inline unsigned int get_h_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
-  {
-    return (this+hAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
-  }
-
-  inline int get_h_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+hAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
-  }
-
-  inline int get_h_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+hAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
-  }
-
-  inline unsigned int get_v_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
-  {
-    return (this+vAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag);
-  }
-
-  inline unsigned int get_v_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
-  {
-    return (this+vAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
-  }
-
-  inline int get_v_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+vAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
-  }
-
-  inline int get_v_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
-  {
-    return (this+vAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  likely (version.major == 1) &&
-		  hAxis.sanitize (c, this) &&
-		  vAxis.sanitize (c, this) &&
-		  (version.to_int () < 0x00010001u || varStore.sanitize (c, this)));
+    return_trace (likely (c->check_struct (this) &&
+			  likely (version.major == 1) &&
+			  hAxis.sanitize (c, this) &&
+			  vAxis.sanitize (c, this) &&
+			  (version.to_int () < 0x00010001u || varStore.sanitize (c, this))));
   }
 
   protected:
-  FixedVersion<>  version;
-  OffsetTo<Axis>  hAxis;
-  OffsetTo<Axis>  vAxis;
+  FixedVersion<>version;	/* Version of the BASE table */
+  OffsetTo<Axis>hAxis;		/* Offset to horizontal Axis table, from beginning
+				 * of BASE table (may be NULL) */
+  OffsetTo<Axis>vAxis;		/* Offset to vertical Axis table, from beginning
+				 * of BASE table (may be NULL) */
   LOffsetTo<VariationStore>
-		varStore;		/* Offset to the table of Item Variation
-					 * Store--from beginning of BASE
-					 * header (may be NULL).  Introduced
-					 * in version 0x00010001. */
+		varStore;	/* Offset to the table of Item Variation
+				 * Store--from beginning of BASE
+				 * header (may be NULL).  Introduced
+				 * in version 0x00010001. */
   public:
   DEFINE_SIZE_MIN (8);
 };
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh
index 98f6a07..d9f5728 100644
--- a/src/hb-ot-layout-common.hh
+++ b/src/hb-ot-layout-common.hh
@@ -51,6 +51,14 @@
 #define HB_CLOSURE_MAX_STAGES	32
 #endif
 
+#ifndef HB_MAX_SCRIPTS
+#define HB_MAX_SCRIPTS	500
+#endif
+
+#ifndef HB_MAX_LANGSYS
+#define HB_MAX_LANGSYS	2000
+#endif
+
 
 namespace OT {
 
@@ -78,11 +86,9 @@
 template <typename Type>
 struct Record
 {
-  inline int cmp (hb_tag_t a) const {
-    return tag.cmp (a);
-  }
+  int cmp (hb_tag_t a) const { return tag.cmp (a); }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     const Record_sanitize_closure_t closure = {tag, base};
@@ -100,15 +106,15 @@
 template <typename Type>
 struct RecordArrayOf : SortedArrayOf<Record<Type> >
 {
-  inline const OffsetTo<Type>& get_offset (unsigned int i) const
+  const OffsetTo<Type>& get_offset (unsigned int i) const
   { return (*this)[i].offset; }
-  inline OffsetTo<Type>& get_offset (unsigned int i)
+  OffsetTo<Type>& get_offset (unsigned int i)
   { return (*this)[i].offset; }
-  inline const Tag& get_tag (unsigned int i) const
+  const Tag& get_tag (unsigned int i) const
   { return (*this)[i].tag; }
-  inline unsigned int get_tags (unsigned int start_offset,
-				unsigned int *record_count /* IN/OUT */,
-				hb_tag_t     *record_tags /* OUT */) const
+  unsigned int get_tags (unsigned int start_offset,
+			 unsigned int *record_count /* IN/OUT */,
+			 hb_tag_t     *record_tags /* OUT */) const
   {
     if (record_count) {
       const Record<Type> *arr = this->sub_array (start_offset, record_count);
@@ -118,27 +124,19 @@
     }
     return this->len;
   }
-  inline bool find_index (hb_tag_t tag, unsigned int *index) const
+  bool find_index (hb_tag_t tag, unsigned int *index) const
   {
-    /* If we want to allow non-sorted data, we can lsearch(). */
-    int i = this->/*lsearch*/bsearch (tag);
-    if (i != -1) {
-        if (index) *index = i;
-        return true;
-    } else {
-      if (index) *index = Index::NOT_FOUND_INDEX;
-      return false;
-    }
+    return this->bfind (tag, index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
   }
 };
 
 template <typename Type>
 struct RecordListOf : RecordArrayOf<Type>
 {
-  inline const Type& operator [] (unsigned int i) const
+  const Type& operator [] (unsigned int i) const
   { return this+this->get_offset (i); }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     struct RecordListOf<Type> *out = c->serializer->embed (*this);
@@ -149,7 +147,7 @@
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (RecordArrayOf<Type>::sanitize (c, this));
@@ -159,23 +157,21 @@
 
 struct RangeRecord
 {
-  inline int cmp (hb_codepoint_t g) const {
-    return g < start ? -1 : g <= end ? 0 : +1 ;
-  }
+  int cmp (hb_codepoint_t g) const
+  { return g < start ? -1 : g <= end ? 0 : +1; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   { return glyphs->intersects (start, end); }
 
   template <typename set_t>
-  inline bool add_coverage (set_t *glyphs) const {
-    return glyphs->add_range (start, end);
-  }
+  bool add_coverage (set_t *glyphs) const
+  { return glyphs->add_range (start, end); }
 
   GlyphID	start;		/* First GlyphID in the range */
   GlyphID	end;		/* Last GlyphID in the range */
@@ -188,9 +184,9 @@
 
 struct IndexArray : ArrayOf<Index>
 {
-  inline unsigned int get_indexes (unsigned int start_offset,
-				   unsigned int *_count /* IN/OUT */,
-				   unsigned int *_indexes /* OUT */) const
+  unsigned int get_indexes (unsigned int start_offset,
+			    unsigned int *_count /* IN/OUT */,
+			    unsigned int *_indexes /* OUT */) const
   {
     if (_count) {
       const HBUINT16 *arr = this->sub_array (start_offset, _count);
@@ -201,7 +197,7 @@
     return this->len;
   }
 
-  inline void add_indexes_to (hb_set_t* output /* OUT */) const
+  void add_indexes_to (hb_set_t* output /* OUT */) const
   {
     output->add_array (arrayZ, len);
   }
@@ -215,33 +211,33 @@
 
 struct LangSys
 {
-  inline unsigned int get_feature_count (void) const
+  unsigned int get_feature_count () const
   { return featureIndex.len; }
-  inline hb_tag_t get_feature_index (unsigned int i) const
+  hb_tag_t get_feature_index (unsigned int i) const
   { return featureIndex[i]; }
-  inline unsigned int get_feature_indexes (unsigned int start_offset,
-					   unsigned int *feature_count /* IN/OUT */,
-					   unsigned int *feature_indexes /* OUT */) const
+  unsigned int get_feature_indexes (unsigned int start_offset,
+				    unsigned int *feature_count /* IN/OUT */,
+				    unsigned int *feature_indexes /* OUT */) const
   { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); }
-  inline void add_feature_indexes_to (hb_set_t *feature_indexes) const
+  void add_feature_indexes_to (hb_set_t *feature_indexes) const
   { featureIndex.add_indexes_to (feature_indexes); }
 
-  inline bool has_required_feature (void) const { return reqFeatureIndex != 0xFFFFu; }
-  inline unsigned int get_required_feature_index (void) const
+  bool has_required_feature () const { return reqFeatureIndex != 0xFFFFu; }
+  unsigned int get_required_feature_index () const
   {
     if (reqFeatureIndex == 0xFFFFu)
       return Index::NOT_FOUND_INDEX;
    return reqFeatureIndex;;
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     return_trace (c->serializer->embed (*this));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c,
-			const Record_sanitize_closure_t * = nullptr) const
+  bool sanitize (hb_sanitize_context_t *c,
+		 const Record_sanitize_closure_t * = nullptr) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && featureIndex.sanitize (c));
@@ -260,26 +256,26 @@
 
 struct Script
 {
-  inline unsigned int get_lang_sys_count (void) const
+  unsigned int get_lang_sys_count () const
   { return langSys.len; }
-  inline const Tag& get_lang_sys_tag (unsigned int i) const
+  const Tag& get_lang_sys_tag (unsigned int i) const
   { return langSys.get_tag (i); }
-  inline unsigned int get_lang_sys_tags (unsigned int start_offset,
-					 unsigned int *lang_sys_count /* IN/OUT */,
-					 hb_tag_t     *lang_sys_tags /* OUT */) const
+  unsigned int get_lang_sys_tags (unsigned int start_offset,
+				  unsigned int *lang_sys_count /* IN/OUT */,
+				  hb_tag_t     *lang_sys_tags /* OUT */) const
   { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); }
-  inline const LangSys& get_lang_sys (unsigned int i) const
+  const LangSys& get_lang_sys (unsigned int i) const
   {
     if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys ();
     return this+langSys[i].offset;
   }
-  inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const
+  bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const
   { return langSys.find_index (tag, index); }
 
-  inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; }
-  inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
+  bool has_default_lang_sys () const           { return defaultLangSys != 0; }
+  const LangSys& get_default_lang_sys () const { return this+defaultLangSys; }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     struct Script *out = c->serializer->embed (*this);
@@ -291,8 +287,8 @@
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c,
-			const Record_sanitize_closure_t * = nullptr) const
+  bool sanitize (hb_sanitize_context_t *c,
+		 const Record_sanitize_closure_t * = nullptr) const
   {
     TRACE_SANITIZE (this);
     return_trace (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
@@ -315,7 +311,7 @@
 /* https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#size */
 struct FeatureParamsSize
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this))) return_trace (false);
@@ -427,7 +423,7 @@
 /* https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#ssxx */
 struct FeatureParamsStylisticSet
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* Right now minorVersion is at zero.  Which means, any table supports
@@ -461,7 +457,7 @@
 /* https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99 */
 struct FeatureParamsCharacterVariants
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -502,7 +498,7 @@
 
 struct FeatureParams
 {
-  inline bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) const
+  bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) const
   {
     TRACE_SANITIZE (this);
     if (tag == HB_TAG ('s','i','z','e'))
@@ -514,25 +510,25 @@
     return_trace (true);
   }
 
-  inline const FeatureParamsSize& get_size_params (hb_tag_t tag) const
+  const FeatureParamsSize& get_size_params (hb_tag_t tag) const
   {
     if (tag == HB_TAG ('s','i','z','e'))
       return u.size;
-    return Null(FeatureParamsSize);
+    return Null (FeatureParamsSize);
   }
 
-  inline const FeatureParamsStylisticSet& get_stylistic_set_params (hb_tag_t tag) const
+  const FeatureParamsStylisticSet& get_stylistic_set_params (hb_tag_t tag) const
   {
     if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */
       return u.stylisticSet;
-    return Null(FeatureParamsStylisticSet);
+    return Null (FeatureParamsStylisticSet);
   }
 
-  inline const FeatureParamsCharacterVariants& get_character_variants_params (hb_tag_t tag) const
+  const FeatureParamsCharacterVariants& get_character_variants_params (hb_tag_t tag) const
   {
     if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */
       return u.characterVariants;
-    return Null(FeatureParamsCharacterVariants);
+    return Null (FeatureParamsCharacterVariants);
   }
 
   private:
@@ -547,19 +543,21 @@
 
 struct Feature
 {
-  inline unsigned int get_lookup_count (void) const
+  unsigned int get_lookup_count () const
   { return lookupIndex.len; }
-  inline hb_tag_t get_lookup_index (unsigned int i) const
+  hb_tag_t get_lookup_index (unsigned int i) const
   { return lookupIndex[i]; }
-  inline unsigned int get_lookup_indexes (unsigned int start_index,
-					  unsigned int *lookup_count /* IN/OUT */,
-					  unsigned int *lookup_tags /* OUT */) const
+  unsigned int get_lookup_indexes (unsigned int start_index,
+				   unsigned int *lookup_count /* IN/OUT */,
+				   unsigned int *lookup_tags /* OUT */) const
   { return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); }
+  void add_lookup_indexes_to (hb_set_t *lookup_indexes) const
+  { lookupIndex.add_indexes_to (lookup_indexes); }
 
-  inline const FeatureParams &get_feature_params (void) const
+  const FeatureParams &get_feature_params () const
   { return this+featureParams; }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     struct Feature *out = c->serializer->embed (*this);
@@ -568,8 +566,8 @@
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c,
-			const Record_sanitize_closure_t *closure = nullptr) const
+  bool sanitize (hb_sanitize_context_t *c,
+		 const Record_sanitize_closure_t *closure = nullptr) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c))))
@@ -648,20 +646,20 @@
 
 struct Lookup
 {
-  inline unsigned int get_subtable_count (void) const { return subTable.len; }
+  unsigned int get_subtable_count () const { return subTable.len; }
 
   template <typename TSubTable>
-  inline const TSubTable& get_subtable (unsigned int i) const
+  const TSubTable& get_subtable (unsigned int i) const
   { return this+CastR<OffsetArrayOf<TSubTable> > (subTable)[i]; }
 
   template <typename TSubTable>
-  inline const OffsetArrayOf<TSubTable>& get_subtables (void) const
+  const OffsetArrayOf<TSubTable>& get_subtables () const
   { return CastR<OffsetArrayOf<TSubTable> > (subTable); }
   template <typename TSubTable>
-  inline OffsetArrayOf<TSubTable>& get_subtables (void)
+  OffsetArrayOf<TSubTable>& get_subtables ()
   { return CastR<OffsetArrayOf<TSubTable> > (subTable); }
 
-  inline unsigned int get_size (void) const
+  unsigned int get_size () const
   {
     const HBUINT16 &markFilteringSet = StructAfter<const HBUINT16> (subTable);
     if (lookupFlag & LookupFlag::UseMarkFilteringSet)
@@ -669,12 +667,12 @@
     return (const char *) &markFilteringSet - (const char *) this;
   }
 
-  inline unsigned int get_type (void) const { return lookupType; }
+  unsigned int get_type () const { return lookupType; }
 
   /* lookup_props is a 32-bit integer where the lower 16-bit is LookupFlag and
    * higher 16-bit is mark-filtering-set if the lookup uses one.
    * Not to be confused with glyph_props which is very similar. */
-  inline uint32_t get_props (void) const
+  uint32_t get_props () const
   {
     unsigned int flag = lookupFlag;
     if (unlikely (flag & LookupFlag::UseMarkFilteringSet))
@@ -686,7 +684,7 @@
   }
 
   template <typename TSubTable, typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     unsigned int lookup_type = get_type ();
     TRACE_DISPATCH (this, lookup_type);
@@ -694,15 +692,15 @@
     for (unsigned int i = 0; i < count; i++) {
       typename context_t::return_t r = get_subtable<TSubTable> (i).dispatch (c, lookup_type);
       if (c->stop_sublookup_iteration (r))
-        return_trace (r);
+	return_trace (r);
     }
     return_trace (c->default_return_value ());
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 unsigned int lookup_type,
-			 uint32_t lookup_props,
-			 unsigned int num_subtables)
+  bool serialize (hb_serialize_context_t *c,
+		  unsigned int lookup_type,
+		  uint32_t lookup_props,
+		  unsigned int num_subtables)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
@@ -722,12 +720,12 @@
   template <typename TSubTable>
   struct SubTableSubsetWrapper
   {
-    inline SubTableSubsetWrapper (const TSubTable &subtable_,
-				  unsigned int lookup_type_) :
-				    subtable (subtable_),
-				    lookup_type (lookup_type_) {}
+    SubTableSubsetWrapper (const TSubTable &subtable_,
+			   unsigned int lookup_type_) :
+			     subtable (subtable_),
+			     lookup_type (lookup_type_) {}
 
-    inline bool subset (hb_subset_context_t *c) const
+    bool subset (hb_subset_context_t *c) const
     { return subtable.dispatch (c, lookup_type); }
 
     private:
@@ -736,7 +734,7 @@
   };
 
   template <typename TSubTable>
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     struct Lookup *out = c->serializer->embed (*this);
@@ -758,8 +756,16 @@
     return_trace (true);
   }
 
+  /* Older compilers need this to NOT be locally defined in a function. */
   template <typename TSubTable>
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  struct SubTableSanitizeWrapper : TSubTable
+  {
+    bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) const
+    { return this->dispatch (c, lookup_type); }
+  };
+
+  template <typename TSubTable>
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false);
@@ -769,7 +775,9 @@
       if (!markFilteringSet.sanitize (c)) return_trace (false);
     }
 
-    if (unlikely (!dispatch<TSubTable> (c))) return_trace (false);
+    if (unlikely (!CastR<OffsetArrayOf<SubTableSanitizeWrapper<TSubTable> > > (subTable)
+		   .sanitize (c, this, get_type ())))
+      return_trace (false);
 
     if (unlikely (get_type () == TSubTable::Extension))
     {
@@ -780,7 +788,7 @@
       unsigned int type = get_subtable<TSubTable> (0).u.extension.get_type ();
       unsigned int count = get_subtable_count ();
       for (unsigned int i = 1; i < count; i++)
-        if (get_subtable<TSubTable> (i).u.extension.get_type () != type)
+	if (get_subtable<TSubTable> (i).u.extension.get_type () != type)
 	  return_trace (false);
     }
     return_trace (true);
@@ -792,11 +800,11 @@
   HBUINT16	lookupFlag;		/* Lookup qualifiers */
   ArrayOf<Offset16>
 		subTable;		/* Array of SubTables */
-  HBUINT16	markFilteringSetX[VAR];	/* Index (base 0) into GDEF mark glyph sets
+/*HBUINT16	markFilteringSetX[VAR];*//* Index (base 0) into GDEF mark glyph sets
 					 * structure. This field is only present if bit
 					 * UseMarkFilteringSet of lookup flags is set. */
   public:
-  DEFINE_SIZE_ARRAY2 (6, subTable, markFilteringSetX);
+  DEFINE_SIZE_ARRAY (6, subTable);
 };
 
 typedef OffsetListOf<Lookup> LookupList;
@@ -811,59 +819,53 @@
   friend struct Coverage;
 
   private:
-  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  unsigned int get_coverage (hb_codepoint_t glyph_id) const
   {
-    int i = glyphArray.bsearch (glyph_id);
-    static_assert ((((unsigned int) -1) == NOT_COVERED), "");
+    unsigned int i;
+    glyphArray.bfind (glyph_id, &i, HB_BFIND_NOT_FOUND_STORE, NOT_COVERED);
     return i;
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 unsigned int num_glyphs)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return_trace (false);
-    glyphArray.len.set (num_glyphs);
-    if (unlikely (!c->extend (glyphArray))) return_trace (false);
-    for (unsigned int i = 0; i < num_glyphs; i++)
-      glyphArray[i] = glyphs[i];
-    glyphs += num_glyphs;
-    return_trace (true);
+    return_trace (glyphArray.serialize (c, glyphs));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (glyphArray.sanitize (c));
   }
 
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     /* TODO Speed up, using hb_set_next() and bsearch()? */
     unsigned int count = glyphArray.len;
     for (unsigned int i = 0; i < count; i++)
       if (glyphs->has (glyphArray[i]))
-        return true;
+	return true;
     return false;
   }
-  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+  bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
   { return glyphs->has (glyphArray[index]); }
 
   template <typename set_t>
-  inline bool add_coverage (set_t *glyphs) const {
+  bool add_coverage (set_t *glyphs) const
+  {
     return glyphs->add_sorted_array (glyphArray.arrayZ, glyphArray.len);
   }
 
   public:
   /* Older compilers need this to be public. */
   struct Iter {
-    inline void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; };
-    inline void fini (void) {};
-    inline bool more (void) { return i < c->glyphArray.len; }
-    inline void next (void) { i++; }
-    inline hb_codepoint_t get_glyph (void) { return c->glyphArray[i]; }
-    inline unsigned int get_coverage (void) { return i; }
+    void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; }
+    void fini () {}
+    bool more () { return i < c->glyphArray.len; }
+    void next () { i++; }
+    hb_codepoint_t get_glyph () { return c->glyphArray[i]; }
+    unsigned int get_coverage () { return i; }
 
     private:
     const struct CoverageFormat1 *c;
@@ -884,68 +886,65 @@
   friend struct Coverage;
 
   private:
-  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  unsigned int get_coverage (hb_codepoint_t glyph_id) const
   {
-    int i = rangeRecord.bsearch (glyph_id);
-    if (i != -1) {
-      const RangeRecord &range = rangeRecord[i];
-      return (unsigned int) range.value + (glyph_id - range.start);
-    }
-    return NOT_COVERED;
+    const RangeRecord &range = rangeRecord.bsearch (glyph_id);
+    return likely (range.start <= range.end) ?
+	   (unsigned int) range.value + (glyph_id - range.start) :
+	   NOT_COVERED;
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 unsigned int num_glyphs)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
 
-    if (unlikely (!num_glyphs))
+    if (unlikely (!glyphs.len))
     {
       rangeRecord.len.set (0);
       return_trace (true);
     }
 
     unsigned int num_ranges = 1;
-    for (unsigned int i = 1; i < num_glyphs; i++)
+    for (unsigned int i = 1; i < glyphs.len; i++)
       if (glyphs[i - 1] + 1 != glyphs[i])
-        num_ranges++;
+	num_ranges++;
     rangeRecord.len.set (num_ranges);
     if (unlikely (!c->extend (rangeRecord))) return_trace (false);
 
     unsigned int range = 0;
     rangeRecord[range].start = glyphs[0];
     rangeRecord[range].value.set (0);
-    for (unsigned int i = 1; i < num_glyphs; i++)
-      if (glyphs[i - 1] + 1 != glyphs[i]) {
+    for (unsigned int i = 1; i < glyphs.len; i++)
+    {
+      if (glyphs[i - 1] + 1 != glyphs[i])
+      {
 	range++;
 	rangeRecord[range].start = glyphs[i];
 	rangeRecord[range].value.set (i);
-        rangeRecord[range].end = glyphs[i];
-      } else {
-        rangeRecord[range].end = glyphs[i];
       }
-    glyphs += num_glyphs;
+      rangeRecord[range].end = glyphs[i];
+    }
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (rangeRecord.sanitize (c));
   }
 
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     /* TODO Speed up, using hb_set_next() and bsearch()? */
     unsigned int count = rangeRecord.len;
     for (unsigned int i = 0; i < count; i++)
       if (rangeRecord[i].intersects (glyphs))
-        return true;
+	return true;
     return false;
   }
-  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+  bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
   {
     unsigned int i;
     unsigned int count = rangeRecord.len;
@@ -954,19 +953,20 @@
       if (range.value <= index &&
 	  index < (unsigned int) range.value + (range.end - range.start) &&
 	  range.intersects (glyphs))
-        return true;
+	return true;
       else if (index < range.value)
-        return false;
+	return false;
     }
     return false;
   }
 
   template <typename set_t>
-  inline bool add_coverage (set_t *glyphs) const {
+  bool add_coverage (set_t *glyphs) const
+  {
     unsigned int count = rangeRecord.len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!rangeRecord[i].add_coverage (glyphs)))
-        return false;
+	return false;
     return true;
   }
 
@@ -974,7 +974,7 @@
   /* Older compilers need this to be public. */
   struct Iter
   {
-    inline void init (const CoverageFormat2 &c_)
+    void init (const CoverageFormat2 &c_)
     {
       c = &c_;
       coverage = 0;
@@ -982,17 +982,17 @@
       j = c->rangeRecord.len ? c->rangeRecord[0].start : 0;
       if (unlikely (c->rangeRecord[0].start > c->rangeRecord[0].end))
       {
-        /* Broken table. Skip. */
-        i = c->rangeRecord.len;
+	/* Broken table. Skip. */
+	i = c->rangeRecord.len;
       }
     }
-    inline void fini (void) {};
-    inline bool more (void) { return i < c->rangeRecord.len; }
-    inline void next (void)
+    void fini () {}
+    bool more () { return i < c->rangeRecord.len; }
+    void next ()
     {
       if (j >= c->rangeRecord[i].end)
       {
-        i++;
+	i++;
 	if (more ())
 	{
 	  hb_codepoint_t old = j;
@@ -1010,8 +1010,8 @@
       coverage++;
       j++;
     }
-    inline hb_codepoint_t get_glyph (void) { return j; }
-    inline unsigned int get_coverage (void) { return coverage; }
+    hb_codepoint_t get_glyph () { return j; }
+    unsigned int get_coverage () { return coverage; }
 
     private:
     const struct CoverageFormat2 *c;
@@ -1032,7 +1032,7 @@
 
 struct Coverage
 {
-  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  unsigned int get_coverage (hb_codepoint_t glyph_id) const
   {
     switch (u.format) {
     case 1: return u.format1.get_coverage (glyph_id);
@@ -1041,26 +1041,27 @@
     }
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 unsigned int num_glyphs)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
+
     unsigned int num_ranges = 1;
-    for (unsigned int i = 1; i < num_glyphs; i++)
+    for (unsigned int i = 1; i < glyphs.len; i++)
       if (glyphs[i - 1] + 1 != glyphs[i])
-        num_ranges++;
-    u.format.set (num_glyphs * 2 < num_ranges * 3 ? 1 : 2);
+	num_ranges++;
+    u.format.set (glyphs.len * 2 < num_ranges * 3 ? 1 : 2);
+
     switch (u.format)
     {
-    case 1: return_trace (u.format1.serialize (c, glyphs, num_glyphs));
-    case 2: return_trace (u.format2.serialize (c, glyphs, num_glyphs));
+    case 1: return_trace (u.format1.serialize (c, glyphs));
+    case 2: return_trace (u.format2.serialize (c, glyphs));
     default:return_trace (false);
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
@@ -1072,7 +1073,7 @@
     }
   }
 
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     switch (u.format)
     {
@@ -1081,7 +1082,7 @@
     default:return false;
     }
   }
-  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+  bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
   {
     switch (u.format)
     {
@@ -1094,7 +1095,7 @@
   /* Might return false if array looks unsorted.
    * Used for faster rejection of corrupt data. */
   template <typename set_t>
-  inline bool add_coverage (set_t *glyphs) const
+  bool add_coverage (set_t *glyphs) const
   {
     switch (u.format)
     {
@@ -1106,9 +1107,9 @@
 
   struct Iter
   {
-    Iter (void) : format (0), u () {};
-    inline void init (const Coverage &c_)
+    Iter (const Coverage &c_)
     {
+      memset (this, 0, sizeof (*this));
       format = c_.u.format;
       switch (format)
       {
@@ -1117,8 +1118,7 @@
       default:				     return;
       }
     }
-    inline void fini (void) {}
-    inline bool more (void)
+    bool more ()
     {
       switch (format)
       {
@@ -1127,7 +1127,7 @@
       default:return false;
       }
     }
-    inline void next (void)
+    void next ()
     {
       switch (format)
       {
@@ -1136,7 +1136,7 @@
       default:			 break;
       }
     }
-    inline hb_codepoint_t get_glyph (void)
+    hb_codepoint_t get_glyph ()
     {
       switch (format)
       {
@@ -1145,7 +1145,7 @@
       default:return 0;
       }
     }
-    inline unsigned int get_coverage (void)
+    unsigned int get_coverage ()
     {
       switch (format)
       {
@@ -1178,33 +1178,85 @@
  * Class Definition Table
  */
 
+static inline void ClassDef_serialize (hb_serialize_context_t *c,
+				       hb_array_t<const GlyphID> glyphs,
+				       hb_array_t<const HBUINT16> klasses);
+
 struct ClassDefFormat1
 {
   friend struct ClassDef;
 
   private:
-  inline unsigned int get_class (hb_codepoint_t glyph_id) const
+  unsigned int get_class (hb_codepoint_t glyph_id) const
   {
-    unsigned int i = (unsigned int) (glyph_id - startGlyph);
-    if (unlikely (i < classValue.len))
-      return classValue[i];
-    return 0;
+    return classValue[(unsigned int) (glyph_id - startGlyph)];
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const HBUINT16> glyphs,
+		  hb_array_t<const HBUINT16> klasses)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+
+    if (unlikely (!glyphs.len))
+    {
+      startGlyph.set (0);
+      classValue.len.set (0);
+      return_trace (true);
+    }
+
+    hb_codepoint_t glyph_min = glyphs[0];
+    hb_codepoint_t glyph_max = glyphs[glyphs.len - 1];
+
+    startGlyph.set (glyph_min);
+    classValue.len.set (glyph_max - glyph_min + 1);
+    if (unlikely (!c->extend (classValue))) return_trace (false);
+
+    for (unsigned int i = 0; i < glyphs.len; i++)
+      classValue[glyphs[i] - glyph_min] = klasses[i];
+
+    return_trace (true);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset;
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+    hb_vector_t<GlyphID> glyphs;
+    hb_vector_t<HBUINT16> klasses;
+
+    hb_codepoint_t start = startGlyph;
+    hb_codepoint_t end   = start + classValue.len;
+    for (hb_codepoint_t g = start; g < end; g++)
+    {
+      unsigned int value = classValue[g - start];
+      if (!value) continue;
+      if (!glyphset.has (g)) continue;
+      glyphs.push()->set (glyph_map[g]);
+      klasses.push()->set (value);
+    }
+    c->serializer->propagate_error (glyphs, klasses);
+    ClassDef_serialize (c->serializer, glyphs, klasses);
+    return_trace (glyphs.len);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && classValue.sanitize (c));
   }
 
   template <typename set_t>
-  inline bool add_coverage (set_t *glyphs) const {
+  bool add_coverage (set_t *glyphs) const
+  {
     unsigned int start = 0;
     unsigned int count = classValue.len;
     for (unsigned int i = 0; i < count; i++)
     {
       if (classValue[i])
-        continue;
+	continue;
 
       if (start != i)
 	if (unlikely (!glyphs->add_range (startGlyph + start, startGlyph + i)))
@@ -1220,53 +1272,48 @@
   }
 
   template <typename set_t>
-  inline bool add_class (set_t *glyphs, unsigned int klass) const {
+  bool add_class (set_t *glyphs, unsigned int klass) const
+  {
     unsigned int count = classValue.len;
     for (unsigned int i = 0; i < count; i++)
-    {
-      if (classValue[i] == klass)
-        glyphs->add (startGlyph + i);
-    }
+      if (classValue[i] == klass) glyphs->add (startGlyph + i);
     return true;
   }
 
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     /* TODO Speed up, using hb_set_next()? */
     hb_codepoint_t start = startGlyph;
     hb_codepoint_t end = startGlyph + classValue.len;
     for (hb_codepoint_t iter = startGlyph - 1;
 	 hb_set_next (glyphs, &iter) && iter < end;)
-      if (classValue[iter - start])
-        return true;
+      if (classValue[iter - start]) return true;
     return false;
   }
-  inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
+  bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const
+  {
     unsigned int count = classValue.len;
     if (klass == 0)
     {
       /* Match if there's any glyph that is not listed! */
       hb_codepoint_t g = HB_SET_VALUE_INVALID;
-      if (!hb_set_next (glyphs, &g))
-        return false;
-      if (g < startGlyph)
-        return true;
+      if (!hb_set_next (glyphs, &g)) return false;
+      if (g < startGlyph) return true;
       g = startGlyph + count - 1;
-      if (hb_set_next (glyphs, &g))
-        return true;
+      if (hb_set_next (glyphs, &g)) return true;
       /* Fall through. */
     }
     for (unsigned int i = 0; i < count; i++)
       if (classValue[i] == klass && glyphs->has (startGlyph + i))
-        return true;
+	return true;
     return false;
   }
 
   protected:
-  HBUINT16	classFormat;		/* Format identifier--format = 1 */
-  GlyphID	startGlyph;		/* First GlyphID of the classValueArray */
+  HBUINT16	classFormat;	/* Format identifier--format = 1 */
+  GlyphID	startGlyph;	/* First GlyphID of the classValueArray */
   ArrayOf<HBUINT16>
-		classValue;		/* Array of Class Values--one per GlyphID */
+		classValue;	/* Array of Class Values--one per GlyphID */
   public:
   DEFINE_SIZE_ARRAY (6, classValue);
 };
@@ -1276,22 +1323,84 @@
   friend struct ClassDef;
 
   private:
-  inline unsigned int get_class (hb_codepoint_t glyph_id) const
+  unsigned int get_class (hb_codepoint_t glyph_id) const
   {
-    int i = rangeRecord.bsearch (glyph_id);
-    if (unlikely (i != -1))
-      return rangeRecord[i].value;
-    return 0;
+    return rangeRecord.bsearch (glyph_id).value;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const HBUINT16> glyphs,
+		  hb_array_t<const HBUINT16> klasses)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+
+    if (unlikely (!glyphs.len))
+    {
+      rangeRecord.len.set (0);
+      return_trace (true);
+    }
+
+    unsigned int num_ranges = 1;
+    for (unsigned int i = 1; i < glyphs.len; i++)
+      if (glyphs[i - 1] + 1 != glyphs[i] ||
+	  klasses[i - 1] != klasses[i])
+	num_ranges++;
+    rangeRecord.len.set (num_ranges);
+    if (unlikely (!c->extend (rangeRecord))) return_trace (false);
+
+    unsigned int range = 0;
+    rangeRecord[range].start = glyphs[0];
+    rangeRecord[range].value.set (klasses[0]);
+    for (unsigned int i = 1; i < glyphs.len; i++)
+    {
+      if (glyphs[i - 1] + 1 != glyphs[i] ||
+	  klasses[i - 1] != klasses[i])
+      {
+	range++;
+	rangeRecord[range].start = glyphs[i];
+	rangeRecord[range].value = klasses[i];
+      }
+      rangeRecord[range].end = glyphs[i];
+    }
+    return_trace (true);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset;
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+    hb_vector_t<GlyphID> glyphs;
+    hb_vector_t<HBUINT16> klasses;
+
+    unsigned int count = rangeRecord.len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      unsigned int value = rangeRecord[i].value;
+      if (!value) continue;
+      hb_codepoint_t start = rangeRecord[i].start;
+      hb_codepoint_t end   = rangeRecord[i].end + 1;
+      for (hb_codepoint_t g = start; g < end; g++)
+      {
+	if (!glyphset.has (g)) continue;
+	glyphs.push ()->set (glyph_map[g]);
+	klasses.push ()->set (value);
+      }
+    }
+    c->serializer->propagate_error (glyphs, klasses);
+    ClassDef_serialize (c->serializer, glyphs, klasses);
+    return_trace (glyphs.len);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (rangeRecord.sanitize (c));
   }
 
   template <typename set_t>
-  inline bool add_coverage (set_t *glyphs) const
+  bool add_coverage (set_t *glyphs) const
   {
     unsigned int count = rangeRecord.len;
     for (unsigned int i = 0; i < count; i++)
@@ -1302,28 +1411,28 @@
   }
 
   template <typename set_t>
-  inline bool add_class (set_t *glyphs, unsigned int klass) const
+  bool add_class (set_t *glyphs, unsigned int klass) const
   {
     unsigned int count = rangeRecord.len;
     for (unsigned int i = 0; i < count; i++)
     {
       if (rangeRecord[i].value == klass)
-        if (unlikely (!rangeRecord[i].add_coverage (glyphs)))
+	if (unlikely (!rangeRecord[i].add_coverage (glyphs)))
 	  return false;
     }
     return true;
   }
 
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     /* TODO Speed up, using hb_set_next() and bsearch()? */
     unsigned int count = rangeRecord.len;
     for (unsigned int i = 0; i < count; i++)
       if (rangeRecord[i].intersects (glyphs))
-        return true;
+	return true;
     return false;
   }
-  inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const
+  bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const
   {
     unsigned int count = rangeRecord.len;
     if (klass == 0)
@@ -1339,12 +1448,12 @@
 	g = rangeRecord[i].end;
       }
       if (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g))
-        return true;
+	return true;
       /* Fall through. */
     }
     for (unsigned int i = 0; i < count; i++)
       if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs))
-        return true;
+	return true;
     return false;
   }
 
@@ -1359,7 +1468,7 @@
 
 struct ClassDef
 {
-  inline unsigned int get_class (hb_codepoint_t glyph_id) const
+  unsigned int get_class (hb_codepoint_t glyph_id) const
   {
     switch (u.format) {
     case 1: return u.format1.get_class (glyph_id);
@@ -1368,7 +1477,49 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs,
+		  hb_array_t<const HBUINT16> klasses)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+
+    unsigned int format = 2;
+    if (glyphs.len)
+    {
+      hb_codepoint_t glyph_min = glyphs[0];
+      hb_codepoint_t glyph_max = glyphs[glyphs.len - 1];
+
+      unsigned int num_ranges = 1;
+      for (unsigned int i = 1; i < glyphs.len; i++)
+	if (glyphs[i - 1] + 1 != glyphs[i] ||
+	    klasses[i - 1] != klasses[i])
+	  num_ranges++;
+
+      if (1 + (glyph_max - glyph_min + 1) < num_ranges * 3)
+        format = 1;
+    }
+    u.format.set (format);
+
+    switch (u.format)
+    {
+    case 1: return_trace (u.format1.serialize (c, glyphs, klasses));
+    case 2: return_trace (u.format2.serialize (c, glyphs, klasses));
+    default:return_trace (false);
+    }
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    switch (u.format) {
+    case 1: return_trace (u.format1.subset (c));
+    case 2: return_trace (u.format2.subset (c));
+    default:return_trace (false);
+    }
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
@@ -1382,7 +1533,8 @@
   /* Might return false if array looks unsorted.
    * Used for faster rejection of corrupt data. */
   template <typename set_t>
-  inline bool add_coverage (set_t *glyphs) const {
+  bool add_coverage (set_t *glyphs) const
+  {
     switch (u.format) {
     case 1: return u.format1.add_coverage (glyphs);
     case 2: return u.format2.add_coverage (glyphs);
@@ -1393,7 +1545,8 @@
   /* Might return false if array looks unsorted.
    * Used for faster rejection of corrupt data. */
   template <typename set_t>
-  inline bool add_class (set_t *glyphs, unsigned int klass) const {
+  bool add_class (set_t *glyphs, unsigned int klass) const
+  {
     switch (u.format) {
     case 1: return u.format1.add_class (glyphs, klass);
     case 2: return u.format2.add_class (glyphs, klass);
@@ -1401,14 +1554,16 @@
     }
   }
 
-  inline bool intersects (const hb_set_t *glyphs) const {
+  bool intersects (const hb_set_t *glyphs) const
+  {
     switch (u.format) {
     case 1: return u.format1.intersects (glyphs);
     case 2: return u.format2.intersects (glyphs);
     default:return false;
     }
   }
-  inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
+  bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const
+  {
     switch (u.format) {
     case 1: return u.format1.intersects_class (glyphs, klass);
     case 2: return u.format2.intersects_class (glyphs, klass);
@@ -1426,6 +1581,11 @@
   DEFINE_SIZE_UNION (2, format);
 };
 
+static inline void ClassDef_serialize (hb_serialize_context_t *c,
+				       hb_array_t<const GlyphID> glyphs,
+				       hb_array_t<const HBUINT16> klasses)
+{ c->start_embed<ClassDef> ()->serialize (c, glyphs, klasses); }
+
 
 /*
  * Item Variation Store
@@ -1433,7 +1593,7 @@
 
 struct VarRegionAxis
 {
-  inline float evaluate (int coord) const
+  float evaluate (int coord) const
   {
     int start = startCoord, peak = peakCoord, end = endCoord;
 
@@ -1456,7 +1616,7 @@
       return float (end - coord) / (end - peak);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -1474,7 +1634,7 @@
 
 struct VarRegionList
 {
-  inline float evaluate (unsigned int region_index,
+  float evaluate (unsigned int region_index,
 			 const int *coords, unsigned int coord_len) const
   {
     if (unlikely (region_index >= regionCount))
@@ -1489,19 +1649,21 @@
       int coord = i < coord_len ? coords[i] : 0;
       float factor = axes[i].evaluate (coord);
       if (factor == 0.f)
-        return 0.;
+	return 0.;
       v *= factor;
     }
     return v;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  axesZ.sanitize (c, (unsigned int) axisCount * (unsigned int) regionCount));
   }
 
+  unsigned int get_region_count () const { return regionCount; }
+
   protected:
   HBUINT16	axisCount;
   HBUINT16	regionCount;
@@ -1513,13 +1675,16 @@
 
 struct VarData
 {
-  inline unsigned int get_row_size (void) const
+  unsigned int get_region_index_count () const
+  { return regionIndices.len; }
+
+  unsigned int get_row_size () const
   { return shortCount + regionIndices.len; }
 
-  inline unsigned int get_size (void) const
+  unsigned int get_size () const
   { return itemCount * get_row_size (); }
 
-  inline float get_delta (unsigned int inner,
+  float get_delta (unsigned int inner,
 			  const int *coords, unsigned int coord_count,
 			  const VarRegionList &regions) const
   {
@@ -1551,29 +1716,42 @@
    return delta;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  void get_scalars (int *coords, unsigned int coord_count,
+                    const VarRegionList &regions,
+                    float *scalars /*OUT */,
+                    unsigned int num_scalars) const
+  {
+    assert (num_scalars == regionIndices.len);
+   for (unsigned int i = 0; i < num_scalars; i++)
+   {
+     scalars[i] = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count);
+   }
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  regionIndices.sanitize(c) &&
+		  regionIndices.sanitize (c) &&
 		  shortCount <= regionIndices.len &&
-		  c->check_array (&StructAfter<HBUINT8> (regionIndices),
-				  itemCount, get_row_size ()));
+		  c->check_range (&StructAfter<HBUINT8> (regionIndices),
+				  itemCount,
+				  get_row_size ()));
   }
 
   protected:
   HBUINT16		itemCount;
   HBUINT16		shortCount;
   ArrayOf<HBUINT16>	regionIndices;
-  UnsizedArrayOf<HBUINT8>bytesX;
+/*UnsizedArrayOf<HBUINT8>bytesX;*/
   public:
-  DEFINE_SIZE_ARRAY2 (6, regionIndices, bytesX);
+  DEFINE_SIZE_ARRAY (6, regionIndices);
 };
 
 struct VariationStore
 {
-  inline float get_delta (unsigned int outer, unsigned int inner,
-			  const int *coords, unsigned int coord_count) const
+  float get_delta (unsigned int outer, unsigned int inner,
+		   const int *coords, unsigned int coord_count) const
   {
     if (unlikely (outer >= dataSets.len))
       return 0.;
@@ -1583,15 +1761,15 @@
 					     this+regions);
   }
 
-  inline float get_delta (unsigned int index,
-			  const int *coords, unsigned int coord_count) const
+  float get_delta (unsigned int index,
+		   const int *coords, unsigned int coord_count) const
   {
     unsigned int outer = index >> 16;
     unsigned int inner = index & 0xFFFF;
     return get_delta (outer, inner, coords, coord_count);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -1600,10 +1778,22 @@
 		  dataSets.sanitize (c, this));
   }
 
+  unsigned int get_region_index_count (unsigned int ivs) const
+  { return (this+dataSets[ivs]).get_region_index_count (); }
+
+  void get_scalars (unsigned int ivs,
+		    int *coords, unsigned int coord_count,
+		    float *scalars /*OUT*/,
+		    unsigned int num_scalars) const
+  {
+    (this+dataSets[ivs]).get_scalars (coords, coord_count, this+regions,
+                                      &scalars[0], num_scalars);
+  }
+
   protected:
   HBUINT16				format;
   LOffsetTo<VarRegionList>		regions;
-  OffsetArrayOf<VarData, HBUINT32>	dataSets;
+  LOffsetArrayOf<VarData>		dataSets;
   public:
   DEFINE_SIZE_ARRAY (8, dataSets);
 };
@@ -1617,13 +1807,13 @@
   friend struct Condition;
 
   private:
-  inline bool evaluate (const int *coords, unsigned int coord_len) const
+  bool evaluate (const int *coords, unsigned int coord_len) const
   {
     int coord = axisIndex < coord_len ? coords[axisIndex] : 0;
     return filterRangeMinValue <= coord && coord <= filterRangeMaxValue;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -1640,7 +1830,7 @@
 
 struct Condition
 {
-  inline bool evaluate (const int *coords, unsigned int coord_len) const
+  bool evaluate (const int *coords, unsigned int coord_len) const
   {
     switch (u.format) {
     case 1: return u.format1.evaluate (coords, coord_len);
@@ -1648,7 +1838,7 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
@@ -1669,23 +1859,23 @@
 
 struct ConditionSet
 {
-  inline bool evaluate (const int *coords, unsigned int coord_len) const
+  bool evaluate (const int *coords, unsigned int coord_len) const
   {
     unsigned int count = conditions.len;
     for (unsigned int i = 0; i < count; i++)
       if (!(this+conditions.arrayZ[i]).evaluate (coords, coord_len))
-        return false;
+	return false;
     return true;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (conditions.sanitize (c, this));
   }
 
   protected:
-  OffsetArrayOf<Condition, HBUINT32> conditions;
+  LOffsetArrayOf<Condition>	conditions;
   public:
   DEFINE_SIZE_ARRAY (2, conditions);
 };
@@ -1694,7 +1884,7 @@
 {
   friend struct FeatureTableSubstitution;
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && feature.sanitize (c, base));
@@ -1709,7 +1899,7 @@
 
 struct FeatureTableSubstitution
 {
-  inline const Feature *find_substitute (unsigned int feature_index) const
+  const Feature *find_substitute (unsigned int feature_index) const
   {
     unsigned int count = substitutions.len;
     for (unsigned int i = 0; i < count; i++)
@@ -1721,7 +1911,7 @@
     return nullptr;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
@@ -1741,7 +1931,7 @@
 {
   friend struct FeatureVariations;
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (conditions.sanitize (c, base) &&
@@ -1761,7 +1951,7 @@
 {
   enum { NOT_FOUND_INDEX = 0xFFFFFFFFu };
 
-  inline bool find_index (const int *coords, unsigned int coord_len,
+  bool find_index (const int *coords, unsigned int coord_len,
 			  unsigned int *index) const
   {
     unsigned int count = varRecords.len;
@@ -1778,20 +1968,20 @@
     return false;
   }
 
-  inline const Feature *find_substitute (unsigned int variations_index,
-					 unsigned int feature_index) const
+  const Feature *find_substitute (unsigned int variations_index,
+				  unsigned int feature_index) const
   {
     const FeatureVariationRecord &record = varRecords[variations_index];
     return (this+record.substitutions).find_substitute (feature_index);
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     return_trace (c->serializer->embed (*this));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
@@ -1818,20 +2008,20 @@
 
   private:
 
-  inline hb_position_t get_x_delta (hb_font_t *font) const
+  hb_position_t get_x_delta (hb_font_t *font) const
   { return get_delta (font->x_ppem, font->x_scale); }
 
-  inline hb_position_t get_y_delta (hb_font_t *font) const
+  hb_position_t get_y_delta (hb_font_t *font) const
   { return get_delta (font->y_ppem, font->y_scale); }
 
-  inline unsigned int get_size (void) const
+  unsigned int get_size () const
   {
     unsigned int f = deltaFormat;
     if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * HBUINT16::static_size;
     return HBUINT16::static_size * (4 + ((endSize - startSize) >> (4 - f)));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && c->check_range (this, this->get_size ()));
@@ -1839,7 +2029,7 @@
 
   private:
 
-  inline int get_delta (unsigned int ppem, int scale) const
+  int get_delta (unsigned int ppem, int scale) const
   {
     if (!ppem) return 0;
 
@@ -1849,7 +2039,7 @@
 
     return (int) (pixels * (int64_t) scale / ppem);
   }
-  inline int get_delta_pixels (unsigned int ppem_size) const
+  int get_delta_pixels (unsigned int ppem_size) const
   {
     unsigned int f = deltaFormat;
     if (unlikely (f < 1 || f > 3))
@@ -1892,13 +2082,13 @@
 
   private:
 
-  inline hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store) const
+  hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store) const
   { return font->em_scalef_x (get_delta (font, store)); }
 
-  inline hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store) const
+  hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store) const
   { return font->em_scalef_y (get_delta (font, store)); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -1906,7 +2096,7 @@
 
   private:
 
-  inline float get_delta (hb_font_t *font, const VariationStore &store) const
+  float get_delta (hb_font_t *font, const VariationStore &store) const
   {
     return store.get_delta (outerIndex, innerIndex, font->coords, font->num_coords);
   }
@@ -1932,7 +2122,7 @@
 
 struct Device
 {
-  inline hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store=Null(VariationStore)) const
+  hb_position_t get_x_delta (hb_font_t *font, const VariationStore &store=Null (VariationStore)) const
   {
     switch (u.b.format)
     {
@@ -1944,7 +2134,7 @@
       return 0;
     }
   }
-  inline hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store=Null(VariationStore)) const
+  hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store=Null (VariationStore)) const
   {
     switch (u.b.format)
     {
@@ -1957,7 +2147,7 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!u.b.format.sanitize (c)) return_trace (false);
diff --git a/src/hb-ot-layout-gdef-table.hh b/src/hb-ot-layout-gdef-table.hh
index 7570908..3099fb9 100644
--- a/src/hb-ot-layout-gdef-table.hh
+++ b/src/hb-ot-layout-gdef-table.hh
@@ -46,10 +46,10 @@
 
 struct AttachList
 {
-  inline unsigned int get_attach_points (hb_codepoint_t glyph_id,
-					 unsigned int start_offset,
-					 unsigned int *point_count /* IN/OUT */,
-					 unsigned int *point_array /* OUT */) const
+  unsigned int get_attach_points (hb_codepoint_t glyph_id,
+				  unsigned int start_offset,
+				  unsigned int *point_count /* IN/OUT */,
+				  unsigned int *point_array /* OUT */) const
   {
     unsigned int index = (this+coverage).get_coverage (glyph_id);
     if (index == NOT_COVERED)
@@ -61,9 +61,10 @@
 
     const AttachPoint &points = this+attachPoint[index];
 
-    if (point_count) {
-      const HBUINT16 *array = points.sub_array (start_offset, point_count);
-      unsigned int count = *point_count;
+    if (point_count)
+    {
+      hb_array_t<const HBUINT16> array = points.sub_array (start_offset, point_count);
+      unsigned int count = array.len;
       for (unsigned int i = 0; i < count; i++)
 	point_array[i] = array[i];
     }
@@ -71,7 +72,7 @@
     return points.len;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && attachPoint.sanitize (c, this));
@@ -97,12 +98,12 @@
   friend struct CaretValue;
 
   private:
-  inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction) const
+  hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction) const
   {
     return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -120,16 +121,14 @@
   friend struct CaretValue;
 
   private:
-  inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const
+  hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const
   {
     hb_position_t x, y;
-    if (font->get_glyph_contour_point_for_origin (glyph_id, caretValuePoint, direction, &x, &y))
-      return HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
-    else
-      return 0;
+    font->get_glyph_contour_point_for_origin (glyph_id, caretValuePoint, direction, &x, &y);
+    return HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -146,14 +145,15 @@
 {
   friend struct CaretValue;
 
-  inline hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, const VariationStore &var_store) const
+  hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction,
+				 const VariationStore &var_store) const
   {
     return HB_DIRECTION_IS_HORIZONTAL (direction) ?
            font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) :
            font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font, var_store);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && deviceTable.sanitize (c, this));
@@ -172,7 +172,7 @@
 
 struct CaretValue
 {
-  inline hb_position_t get_caret_value (hb_font_t *font,
+  hb_position_t get_caret_value (hb_font_t *font,
 					hb_direction_t direction,
 					hb_codepoint_t glyph_id,
 					const VariationStore &var_store) const
@@ -185,7 +185,7 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
@@ -210,17 +210,18 @@
 
 struct LigGlyph
 {
-  inline unsigned int get_lig_carets (hb_font_t *font,
-				      hb_direction_t direction,
-				      hb_codepoint_t glyph_id,
-				      const VariationStore &var_store,
-				      unsigned int start_offset,
-				      unsigned int *caret_count /* IN/OUT */,
-				      hb_position_t *caret_array /* OUT */) const
+  unsigned int get_lig_carets (hb_font_t *font,
+			       hb_direction_t direction,
+			       hb_codepoint_t glyph_id,
+			       const VariationStore &var_store,
+			       unsigned int start_offset,
+			       unsigned int *caret_count /* IN/OUT */,
+			       hb_position_t *caret_array /* OUT */) const
   {
-    if (caret_count) {
-      const OffsetTo<CaretValue> *array = carets.sub_array (start_offset, caret_count);
-      unsigned int count = *caret_count;
+    if (caret_count)
+    {
+      hb_array_t <const OffsetTo<CaretValue> > array = carets.sub_array (start_offset, caret_count);
+      unsigned int count = array.len;
       for (unsigned int i = 0; i < count; i++)
 	caret_array[i] = (this+array[i]).get_caret_value (font, direction, glyph_id, var_store);
     }
@@ -228,7 +229,7 @@
     return carets.len;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (carets.sanitize (c, this));
@@ -245,13 +246,13 @@
 
 struct LigCaretList
 {
-  inline unsigned int get_lig_carets (hb_font_t *font,
-				      hb_direction_t direction,
-				      hb_codepoint_t glyph_id,
-				      const VariationStore &var_store,
-				      unsigned int start_offset,
-				      unsigned int *caret_count /* IN/OUT */,
-				      hb_position_t *caret_array /* OUT */) const
+  unsigned int get_lig_carets (hb_font_t *font,
+			       hb_direction_t direction,
+			       hb_codepoint_t glyph_id,
+			       const VariationStore &var_store,
+			       unsigned int start_offset,
+			       unsigned int *caret_count /* IN/OUT */,
+			       hb_position_t *caret_array /* OUT */) const
   {
     unsigned int index = (this+coverage).get_coverage (glyph_id);
     if (index == NOT_COVERED)
@@ -264,7 +265,7 @@
     return lig_glyph.get_lig_carets (font, direction, glyph_id, var_store, start_offset, caret_count, caret_array);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this));
@@ -284,10 +285,10 @@
 
 struct MarkGlyphSetsFormat1
 {
-  inline bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
   { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this));
@@ -304,7 +305,7 @@
 
 struct MarkGlyphSets
 {
-  inline bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
   {
     switch (u.format) {
     case 1: return u.format1.covers (set_index, glyph_id);
@@ -312,7 +313,7 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
@@ -340,7 +341,7 @@
 
 struct GDEF
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_GDEF;
+  enum { tableTag = HB_OT_TAG_GDEF };
 
   enum GlyphClasses {
     UnclassifiedGlyph	= 0,
@@ -350,47 +351,47 @@
     ComponentGlyph	= 4
   };
 
-  inline bool has_data (void) const { return version.to_int () != 0; }
-  inline bool has_glyph_classes (void) const { return glyphClassDef != 0; }
-  inline unsigned int get_glyph_class (hb_codepoint_t glyph) const
+  bool has_data () const { return version.to_int (); }
+  bool has_glyph_classes () const { return glyphClassDef != 0; }
+  unsigned int get_glyph_class (hb_codepoint_t glyph) const
   { return (this+glyphClassDef).get_class (glyph); }
-  inline void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const
+  void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const
   { (this+glyphClassDef).add_class (glyphs, klass); }
 
-  inline bool has_mark_attachment_types (void) const { return markAttachClassDef != 0; }
-  inline unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const
+  bool has_mark_attachment_types () const { return markAttachClassDef != 0; }
+  unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const
   { return (this+markAttachClassDef).get_class (glyph); }
 
-  inline bool has_attach_points (void) const { return attachList != 0; }
-  inline unsigned int get_attach_points (hb_codepoint_t glyph_id,
-					 unsigned int start_offset,
-					 unsigned int *point_count /* IN/OUT */,
-					 unsigned int *point_array /* OUT */) const
+  bool has_attach_points () const { return attachList != 0; }
+  unsigned int get_attach_points (hb_codepoint_t glyph_id,
+				  unsigned int start_offset,
+				  unsigned int *point_count /* IN/OUT */,
+				  unsigned int *point_array /* OUT */) const
   { return (this+attachList).get_attach_points (glyph_id, start_offset, point_count, point_array); }
 
-  inline bool has_lig_carets (void) const { return ligCaretList != 0; }
-  inline unsigned int get_lig_carets (hb_font_t *font,
-				      hb_direction_t direction,
-				      hb_codepoint_t glyph_id,
-				      unsigned int start_offset,
-				      unsigned int *caret_count /* IN/OUT */,
-				      hb_position_t *caret_array /* OUT */) const
+  bool has_lig_carets () const { return ligCaretList != 0; }
+  unsigned int get_lig_carets (hb_font_t *font,
+			       hb_direction_t direction,
+			       hb_codepoint_t glyph_id,
+			       unsigned int start_offset,
+			       unsigned int *caret_count /* IN/OUT */,
+			       hb_position_t *caret_array /* OUT */) const
   { return (this+ligCaretList).get_lig_carets (font,
 					       direction, glyph_id, get_var_store(),
 					       start_offset, caret_count, caret_array); }
 
-  inline bool has_mark_sets (void) const { return version.to_int () >= 0x00010002u && markGlyphSetsDef != 0; }
-  inline bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  bool has_mark_sets () const { return version.to_int () >= 0x00010002u && markGlyphSetsDef != 0; }
+  bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const
   { return version.to_int () >= 0x00010002u && (this+markGlyphSetsDef).covers (set_index, glyph_id); }
 
-  inline bool has_var_store (void) const { return version.to_int () >= 0x00010003u && varStore != 0; }
-  inline const VariationStore &get_var_store (void) const
+  bool has_var_store () const { return version.to_int () >= 0x00010003u && varStore != 0; }
+  const VariationStore &get_var_store () const
   { return version.to_int () >= 0x00010003u ? this+varStore : Null(VariationStore); }
 
   /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing
    * glyph class and other bits, and high 8-bit the mark attachment type (if any).
    * Not to be confused with lookup_props which is very similar. */
-  inline unsigned int get_glyph_props (hb_codepoint_t glyph) const
+  unsigned int get_glyph_props (hb_codepoint_t glyph) const
   {
     unsigned int klass = get_glyph_class (glyph);
 
@@ -408,27 +409,54 @@
     }
   }
 
+  HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
+				   hb_face_t *face) const;
+
   struct accelerator_t
   {
-    HB_INTERNAL inline void init (hb_face_t *face);
-
-    inline void fini (void)
+    void init (hb_face_t *face)
     {
-      hb_blob_destroy (this->blob);
+      this->table = hb_sanitize_context_t().reference_table<GDEF> (face);
+      if (unlikely (this->table->is_blacklisted (this->table.get_blob (), face)))
+      {
+	hb_blob_destroy (this->table.get_blob ());
+	this->table = hb_blob_get_empty ();
+      }
     }
 
-    hb_blob_t *blob;
-    const GDEF *table;
+    void fini () { this->table.destroy (); }
+
+    hb_blob_ptr_t<GDEF> table;
   };
 
-  inline unsigned int get_size (void) const
+  unsigned int get_size () const
   {
     return min_size +
 	   (version.to_int () >= 0x00010002u ? markGlyphSetsDef.static_size : 0) +
 	   (version.to_int () >= 0x00010003u ? varStore.static_size : 0);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    struct GDEF *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    out->glyphClassDef.serialize_subset (c, this+glyphClassDef, out);
+    out->attachList.set (0);//TODO(subset) serialize_subset (c, this+attachList, out);
+    out->ligCaretList.set (0);//TODO(subset) serialize_subset (c, this+ligCaretList, out);
+    out->markAttachClassDef.serialize_subset (c, this+markAttachClassDef, out);
+
+    if (version.to_int () >= 0x00010002u)
+      out->markGlyphSetsDef.set (0);// TODO(subset) serialize_subset (c, this+markGlyphSetsDef, out);
+
+    if (version.to_int () >= 0x00010003u)
+      out->varStore.set (0);// TODO(subset) serialize_subset (c, this+varStore, out);
+
+    return_trace (true);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index dad6c4e..b123336 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -53,7 +53,7 @@
 
 typedef HBUINT16 Value;
 
-typedef Value ValueRecord[VAR];
+typedef UnsizedArrayOf<Value> ValueRecord;
 
 struct ValueFormat : HBUINT16
 {
@@ -98,65 +98,65 @@
 					 * PosTable (may be NULL) */
 #endif
 
-  inline unsigned int get_len (void) const
-  { return hb_popcount ((unsigned int) *this); }
-  inline unsigned int get_size (void) const
-  { return get_len () * Value::static_size; }
+  unsigned int get_len () const  { return hb_popcount ((unsigned int) *this); }
+  unsigned int get_size () const { return get_len () * Value::static_size; }
 
-  void apply_value (hb_ot_apply_context_t   *c,
+  bool apply_value (hb_ot_apply_context_t   *c,
 		    const void           *base,
 		    const Value          *values,
 		    hb_glyph_position_t  &glyph_pos) const
   {
+    bool ret = false;
     unsigned int format = *this;
-    if (!format) return;
+    if (!format) return ret;
 
     hb_font_t *font = c->font;
-    hb_bool_t horizontal = HB_DIRECTION_IS_HORIZONTAL (c->direction);
+    bool horizontal = HB_DIRECTION_IS_HORIZONTAL (c->direction);
 
-    if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++));
-    if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++));
+    if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++, &ret));
+    if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++, &ret));
     if (format & xAdvance) {
-      if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values));
+      if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret));
       values++;
     }
     /* y_advance values grow downward but font-space grows upward, hence negation */
     if (format & yAdvance) {
-      if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values));
+      if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret));
       values++;
     }
 
-    if (!has_device ()) return;
+    if (!has_device ()) return ret;
 
     bool use_x_device = font->x_ppem || font->num_coords;
     bool use_y_device = font->y_ppem || font->num_coords;
 
-    if (!use_x_device && !use_y_device) return;
+    if (!use_x_device && !use_y_device) return ret;
 
     const VariationStore &store = c->var_store;
 
     /* pixel -> fractional pixel */
     if (format & xPlaDevice) {
-      if (use_x_device) glyph_pos.x_offset  += (base + get_device (values)).get_x_delta (font, store);
+      if (use_x_device) glyph_pos.x_offset  += (base + get_device (values, &ret)).get_x_delta (font, store);
       values++;
     }
     if (format & yPlaDevice) {
-      if (use_y_device) glyph_pos.y_offset  += (base + get_device (values)).get_y_delta (font, store);
+      if (use_y_device) glyph_pos.y_offset  += (base + get_device (values, &ret)).get_y_delta (font, store);
       values++;
     }
     if (format & xAdvDevice) {
-      if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values)).get_x_delta (font, store);
+      if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store);
       values++;
     }
     if (format & yAdvDevice) {
       /* y_advance values grow downward but font-space grows upward, hence negation */
-      if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values)).get_y_delta (font, store);
+      if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values, &ret)).get_y_delta (font, store);
       values++;
     }
+    return ret;
   }
 
   private:
-  inline bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
+  bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
   {
     unsigned int format = *this;
 
@@ -173,39 +173,46 @@
     return true;
   }
 
-  static inline OffsetTo<Device>& get_device (Value* value)
+  static OffsetTo<Device>& get_device (Value* value)
   { return *CastP<OffsetTo<Device> > (value); }
-  static inline const OffsetTo<Device>& get_device (const Value* value)
-  { return *CastP<OffsetTo<Device> > (value); }
+  static const OffsetTo<Device>& get_device (const Value* value, bool *worked=nullptr)
+  {
+    if (worked) *worked |= *value;
+    return *CastP<OffsetTo<Device> > (value);
+  }
 
-  static inline const HBINT16& get_short (const Value* value)
-  { return *CastP<HBINT16> (value); }
+  static const HBINT16& get_short (const Value* value, bool *worked=nullptr)
+  {
+    if (worked) *worked |= *value;
+    return *CastP<HBINT16> (value);
+  }
 
   public:
 
-  inline bool has_device (void) const {
+  bool has_device () const
+  {
     unsigned int format = *this;
     return (format & devices) != 0;
   }
 
-  inline bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const
+  bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
   }
 
-  inline bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
+  bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
   {
     TRACE_SANITIZE (this);
     unsigned int len = get_len ();
 
-    if (!c->check_array (values, count, get_size ())) return_trace (false);
+    if (!c->check_range (values, count, get_size ())) return_trace (false);
 
     if (!has_device ()) return_trace (true);
 
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
-        return_trace (false);
+	return_trace (false);
       values += len;
     }
 
@@ -213,7 +220,7 @@
   }
 
   /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
-  inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
+  bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
   {
     TRACE_SANITIZE (this);
 
@@ -221,7 +228,7 @@
 
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
-        return_trace (false);
+	return_trace (false);
       values += stride;
     }
 
@@ -232,15 +239,15 @@
 
 struct AnchorFormat1
 {
-  inline void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
-			  float *x, float *y) const
+  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
+		   float *x, float *y) const
   {
     hb_font_t *font = c->font;
     *x = font->em_fscale_x (xCoordinate);
     *y = font->em_fscale_y (yCoordinate);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -256,22 +263,22 @@
 
 struct AnchorFormat2
 {
-  inline void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
-			  float *x, float *y) const
+  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
+		   float *x, float *y) const
   {
     hb_font_t *font = c->font;
     unsigned int x_ppem = font->x_ppem;
     unsigned int y_ppem = font->y_ppem;
     hb_position_t cx = 0, cy = 0;
-    hb_bool_t ret;
+    bool ret;
 
     ret = (x_ppem || y_ppem) &&
-	   font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
+	  font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
     *x = ret && x_ppem ? cx : font->em_fscale_x (xCoordinate);
     *y = ret && y_ppem ? cy : font->em_fscale_y (yCoordinate);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -288,8 +295,8 @@
 
 struct AnchorFormat3
 {
-  inline void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
-			  float *x, float *y) const
+  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
+		   float *x, float *y) const
   {
     hb_font_t *font = c->font;
     *x = font->em_fscale_x (xCoordinate);
@@ -301,7 +308,7 @@
       *y += (this+yDeviceTable).get_y_delta (font, c->var_store);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
@@ -325,8 +332,8 @@
 
 struct Anchor
 {
-  inline void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
-			  float *x, float *y) const
+  void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
+		   float *x, float *y) const
   {
     *x = *y = 0;
     switch (u.format) {
@@ -337,7 +344,7 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
@@ -363,14 +370,16 @@
 
 struct AnchorMatrix
 {
-  inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols, bool *found) const {
+  const Anchor& get_anchor (unsigned int row, unsigned int col,
+			    unsigned int cols, bool *found) const
+  {
     *found = false;
     if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
     *found = !matrixZ[row * cols + col].is_null ();
     return this+matrixZ[row * cols + col];
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
+  bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
   {
     TRACE_SANITIZE (this);
     if (!c->check_struct (this)) return_trace (false);
@@ -396,7 +405,7 @@
 {
   friend struct MarkArray;
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && markAnchor.sanitize (c, base));
@@ -413,10 +422,10 @@
 
 struct MarkArray : ArrayOf<MarkRecord>	/* Array of MarkRecords--in Coverage order */
 {
-  inline bool apply (hb_ot_apply_context_t *c,
-		     unsigned int mark_index, unsigned int glyph_index,
-		     const AnchorMatrix &anchors, unsigned int class_count,
-		     unsigned int glyph_pos) const
+  bool apply (hb_ot_apply_context_t *c,
+	      unsigned int mark_index, unsigned int glyph_index,
+	      const AnchorMatrix &anchors, unsigned int class_count,
+	      unsigned int glyph_pos) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -447,7 +456,7 @@
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (ArrayOf<MarkRecord>::sanitize (c, this));
@@ -459,19 +468,18 @@
 
 struct SinglePosFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -484,14 +492,14 @@
     return_trace (true);
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -515,19 +523,18 @@
 
 struct SinglePosFormat2
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -544,14 +551,14 @@
     return_trace (true);
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -576,7 +583,7 @@
 struct SinglePos
 {
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -614,7 +621,7 @@
 {
   friend struct PairPosFormat1;
 
-  inline bool intersects (const hb_set_t *glyphs,
+  bool intersects (const hb_set_t *glyphs,
 			  const ValueFormat *valueFormats) const
   {
     unsigned int len1 = valueFormats[0].get_len ();
@@ -626,13 +633,13 @@
     for (unsigned int i = 0; i < count; i++)
     {
       if (glyphs->has (record->secondGlyph))
-        return true;
+	return true;
       record = &StructAtOffset<const PairValueRecord> (record, record_size);
     }
     return false;
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c,
+  void collect_glyphs (hb_collect_glyphs_context_t *c,
 			      const ValueFormat *valueFormats) const
   {
     TRACE_COLLECT_GLYPHS (this);
@@ -644,7 +651,7 @@
     c->input->add_array (&record->secondGlyph, len, record_size);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c,
+  bool apply (hb_ot_apply_context_t *c,
 		     const ValueFormat *valueFormats,
 		     unsigned int pos) const
   {
@@ -663,18 +670,19 @@
     int min = 0, max = (int) count - 1;
     while (min <= max)
     {
-      int mid = (min + max) / 2;
+      int mid = ((unsigned int) min + (unsigned int) max) / 2;
       const PairValueRecord *record = &StructAtOffset<PairValueRecord> (&firstPairValueRecord, record_size * mid);
       hb_codepoint_t mid_x = record->secondGlyph;
       if (x < mid_x)
-        max = mid - 1;
+	max = mid - 1;
       else if (x > mid_x)
-        min = mid + 1;
+	min = mid + 1;
       else
       {
-        buffer->unsafe_to_break (buffer->idx, pos + 1);
-	valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
-	valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
+	/* Note the intentional use of "|" instead of short-circuit "||". */
+	if (valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()) |
+	    valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]))
+	  buffer->unsafe_to_break (buffer->idx, pos + 1);
 	if (len2)
 	  pos++;
 	buffer->idx = pos;
@@ -693,11 +701,14 @@
     unsigned int stride; /* 1 + len1 + len2 */
   };
 
-  inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
+  bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
   {
     TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
-       && c->check_array (&firstPairValueRecord, len, HBUINT16::static_size * closure->stride))) return_trace (false);
+       && c->check_range (&firstPairValueRecord,
+			  len,
+			  HBUINT16::static_size,
+			  closure->stride))) return_trace (false);
 
     unsigned int count = len;
     const PairValueRecord *record = &firstPairValueRecord;
@@ -716,21 +727,21 @@
 
 struct PairPosFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     unsigned int count = pairSet.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
+	break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (glyphs->has (iter.get_glyph ()) &&
 	  (this+pairSet[iter.get_coverage ()]).intersects (glyphs, valueFormat))
-        return true;
+	return true;
     }
     return false;
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -739,10 +750,9 @@
       (this+pairSet[i]).collect_glyphs (c, valueFormat);
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -756,14 +766,14 @@
     return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
 
@@ -802,23 +812,22 @@
 
 struct PairPosFormat2
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     return (this+coverage).intersects (glyphs) &&
 	   (this+classDef2).intersects (glyphs);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
     if (unlikely (!(this+classDef2).add_coverage (c->input))) return;
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -837,10 +846,11 @@
     unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
 
-    buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
-    valueFormat1.apply_value (c, this, v, buffer->cur_pos());
-    valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
+    /* Note the intentional use of "|" instead of short-circuit "||". */
+    if (valueFormat1.apply_value (c, this, v, buffer->cur_pos()) |
+	valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]))
+      buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
 
     buffer->idx = skippy_iter.idx;
     if (len2)
@@ -849,14 +859,14 @@
     return_trace (true);
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
@@ -869,7 +879,9 @@
     unsigned int stride = len1 + len2;
     unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
     unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
-    return_trace (c->check_array (values, count, record_size) &&
+    return_trace (c->check_range ((const void *) values,
+				  count,
+				  record_size) &&
 		  valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
 		  valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
   }
@@ -907,7 +919,7 @@
 struct PairPos
 {
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -931,7 +943,7 @@
 {
   friend struct CursivePosFormat1;
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
@@ -955,19 +967,18 @@
 
 struct CursivePosFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -1068,14 +1079,14 @@
     return_trace (true);
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
@@ -1096,7 +1107,7 @@
 struct CursivePos
 {
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -1121,21 +1132,20 @@
 
 struct MarkBasePosFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   { return (this+markCoverage).intersects (glyphs) &&
 	   (this+baseCoverage).intersects (glyphs); }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+markCoverage).add_coverage (c->input))) return;
     if (unlikely (!(this+baseCoverage).add_coverage (c->input))) return;
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+markCoverage; }
+  const Coverage &get_coverage () const { return this+markCoverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -1175,14 +1185,14 @@
     return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -1214,7 +1224,7 @@
 struct MarkBasePos
 {
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -1244,21 +1254,20 @@
 
 struct MarkLigPosFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   { return (this+markCoverage).intersects (glyphs) &&
 	   (this+ligatureCoverage).intersects (glyphs); }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+markCoverage).add_coverage (c->input))) return;
     if (unlikely (!(this+ligatureCoverage).add_coverage (c->input))) return;
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+markCoverage; }
+  const Coverage &get_coverage () const { return this+markCoverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -1301,14 +1310,14 @@
     return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -1341,7 +1350,7 @@
 struct MarkLigPos
 {
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -1366,21 +1375,20 @@
 
 struct MarkMarkPosFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   { return (this+mark1Coverage).intersects (glyphs) &&
 	   (this+mark2Coverage).intersects (glyphs); }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+mark1Coverage).add_coverage (c->input))) return;
     if (unlikely (!(this+mark2Coverage).add_coverage (c->input))) return;
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+mark1Coverage; }
+  const Coverage &get_coverage () const { return this+mark1Coverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -1406,7 +1414,7 @@
       if (id1 == 0) /* Marks belonging to the same base. */
 	goto good;
       else if (comp1 == comp2) /* Marks belonging to the same ligature component. */
-        goto good;
+	goto good;
     } else {
       /* If ligature ids don't match, it may be the case that one of the marks
        * itself is a ligature.  In which case match. */
@@ -1424,14 +1432,14 @@
     return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -1465,7 +1473,7 @@
 struct MarkMarkPos
 {
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -1517,10 +1525,9 @@
   };
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
+  typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   {
     TRACE_DISPATCH (this, lookup_type);
-    if (unlikely (!c->may_dispatch (this, &u.sub_format))) return_trace (c->no_dispatch_return_value ());
     switch (lookup_type) {
     case Single:		return_trace (u.single.dispatch (c));
     case Pair:			return_trace (u.pair.dispatch (c));
@@ -1537,7 +1544,6 @@
 
   protected:
   union {
-  HBUINT16		sub_format;
   SinglePos		single;
   PairPos		pair;
   CursivePos		cursive;
@@ -1549,7 +1555,7 @@
   ExtensionPos		extension;
   } u;
   public:
-  DEFINE_SIZE_UNION (2, sub_format);
+  DEFINE_SIZE_MIN (0);
 };
 
 
@@ -1557,34 +1563,34 @@
 {
   typedef struct PosLookupSubTable SubTable;
 
-  inline const SubTable& get_subtable (unsigned int i) const
+  const SubTable& get_subtable (unsigned int i) const
   { return Lookup::get_subtable<SubTable> (i); }
 
-  inline bool is_reverse (void) const
+  bool is_reverse () const
   {
     return false;
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     return_trace (dispatch (c));
   }
 
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     hb_intersects_context_t c (glyphs);
     return dispatch (&c);
   }
 
-  inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
+  hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     return_trace (dispatch (c));
   }
 
   template <typename set_t>
-  inline void add_coverage (set_t *glyphs) const
+  void add_coverage (set_t *glyphs) const
   {
     hb_add_coverage_context_t<set_t> c (glyphs);
     dispatch (&c);
@@ -1593,16 +1599,16 @@
   static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
 
   template <typename context_t>
-  static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
+  static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   { return Lookup::dispatch<SubTable> (c); }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   { return Lookup::subset<SubTable> (c); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   { return Lookup::sanitize<SubTable> (c); }
 };
 
@@ -1613,21 +1619,24 @@
 
 struct GPOS : GSUBGPOS
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_GPOS;
+  enum { tableTag = HB_OT_TAG_GPOS };
 
-  inline const PosLookup& get_lookup (unsigned int i) const
+  const PosLookup& get_lookup (unsigned int i) const
   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
 
   static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
   static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer);
   static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer);
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   { return GSUBGPOS::subset<PosLookup> (c); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   { return GSUBGPOS::sanitize<PosLookup> (c); }
 
+  HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
+				   hb_face_t *face) const;
+
   typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t;
 };
 
@@ -1715,7 +1724,7 @@
 }
 
 void
-GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
+GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED)
 {
   //_hb_buffer_assert_gsubgpos_vars (buffer);
 }
@@ -1736,18 +1745,21 @@
 }
 
 
+struct GPOS_accelerator_t : GPOS::accelerator_t {};
+
+
 /* Out-of-class implementation for methods recursing */
 
 template <typename context_t>
 /*static*/ inline typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
 {
-  const PosLookup &l = _get_gpos_relaxed (c->face)->get_lookup (lookup_index);
+  const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index);
   return l.dispatch (c);
 }
 
 /*static*/ inline bool PosLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
 {
-  const PosLookup &l = _get_gpos_relaxed (c->face).get_lookup (lookup_index);
+  const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
   unsigned int saved_lookup_index = c->lookup_index;
   c->set_lookup_index (lookup_index);
@@ -1758,8 +1770,6 @@
   return ret;
 }
 
-struct GPOS_accelerator_t : GPOS::accelerator_t {};
-
 
 } /* namespace OT */
 
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 2ce52a1..180e7f2 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -36,19 +36,18 @@
 
 
 static inline void SingleSubst_serialize (hb_serialize_context_t *c,
-					  Supplier<GlyphID> &glyphs,
-					  Supplier<GlyphID> &substitutes,
-					  unsigned int num_glyphs);
+					  hb_array_t<const GlyphID> glyphs,
+					  hb_array_t<const GlyphID> substitutes);
 
 struct SingleSubstFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       /* TODO Switch to range-based API to work around malicious fonts.
        * https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -58,11 +57,11 @@
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       /* TODO Switch to range-based API to work around malicious fonts.
        * https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -71,16 +70,15 @@
     }
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
@@ -95,43 +93,37 @@
     return_trace (true);
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 unsigned int num_glyphs,
-			 int delta)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs,
+		  int delta)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
     deltaGlyphID.set (delta); /* TODO(serialize) overflow? */
     return_trace (true);
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    hb_auto_t<hb_vector_t<GlyphID> > from;
-    hb_auto_t<hb_vector_t<GlyphID> > to;
+    const hb_set_t &glyphset = *c->plan->glyphset;
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+    hb_vector_t<GlyphID> from;
+    hb_vector_t<GlyphID> to;
     hb_codepoint_t delta = deltaGlyphID;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
-      if (!c->plan->glyphset->has (iter.get_glyph ()))
-        continue;
-      from.push ()->set (iter.get_glyph ());
-      to.push ()->set ((iter.get_glyph () + delta) & 0xFFFF);
+      if (!glyphset.has (iter.get_glyph ())) continue;
+      from.push ()->set (glyph_map[iter.get_glyph ()]);
+      to.push ()->set (glyph_map[(iter.get_glyph () + delta) & 0xFFFF]);
     }
-    c->serializer->err (from.in_error () || to.in_error ());
-
-    Supplier<GlyphID> from_supplier (&from);
-    Supplier<GlyphID> to_supplier (&to);
-    SingleSubst_serialize (c->serializer,
-			   from_supplier,
-			   to_supplier,
-			   from.len);
+    c->serializer->propagate_error (from, to);
+    SingleSubst_serialize (c->serializer, from, to);
     return_trace (from.len);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
@@ -150,14 +142,14 @@
 
 struct SingleSubstFormat2
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     unsigned int count = substitute.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -166,12 +158,12 @@
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
     unsigned int count = substitute.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -179,16 +171,15 @@
     }
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
@@ -201,42 +192,36 @@
     return_trace (true);
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 Supplier<GlyphID> &substitutes,
-			 unsigned int num_glyphs)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs,
+		  hb_array_t<const GlyphID> substitutes)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return_trace (false);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
+    if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
     return_trace (true);
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    hb_auto_t<hb_vector_t<GlyphID> > from;
-    hb_auto_t<hb_vector_t<GlyphID> > to;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    const hb_set_t &glyphset = *c->plan->glyphset;
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+    hb_vector_t<GlyphID> from;
+    hb_vector_t<GlyphID> to;
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
-      if (!c->plan->glyphset->has (iter.get_glyph ()))
-        continue;
-      from.push ()->set (iter.get_glyph ());
-      to.push ()->set (substitute[iter.get_coverage ()]);
+      if (!glyphset.has (iter.get_glyph ())) continue;
+      from.push ()->set (glyph_map[iter.get_glyph ()]);
+      to.push ()->set (glyph_map[substitute[iter.get_coverage ()]]);
     }
-    c->serializer->err (from.in_error () || to.in_error ());
-
-    Supplier<GlyphID> from_supplier (&from);
-    Supplier<GlyphID> to_supplier (&to);
-    SingleSubst_serialize (c->serializer,
-			   from_supplier,
-			   to_supplier,
-			   from.len);
+    c->serializer->propagate_error (from, to);
+    SingleSubst_serialize (c->serializer, from, to);
     return_trace (from.len);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
@@ -256,35 +241,35 @@
 
 struct SingleSubst
 {
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 Supplier<GlyphID> &substitutes,
-			 unsigned int num_glyphs)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs,
+		  hb_array_t<const GlyphID> substitutes)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned int format = 2;
     int delta = 0;
-    if (num_glyphs) {
+    if (glyphs.len)
+    {
       format = 1;
       /* TODO(serialize) check for wrap-around */
       delta = substitutes[0] - glyphs[0];
-      for (unsigned int i = 1; i < num_glyphs; i++)
-	if (delta != substitutes[i] - glyphs[i]) {
+      for (unsigned int i = 1; i < glyphs.len; i++)
+	if (delta != (int) (substitutes[i] - glyphs[i])) {
 	  format = 2;
 	  break;
 	}
     }
     u.format.set (format);
     switch (u.format) {
-    case 1: return_trace (u.format1.serialize (c, glyphs, num_glyphs, delta));
-    case 2: return_trace (u.format2.serialize (c, glyphs, substitutes, num_glyphs));
+    case 1: return_trace (u.format1.serialize (c, glyphs, delta));
+    case 2: return_trace (u.format2.serialize (c, glyphs, substitutes));
     default:return_trace (false);
     }
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -305,19 +290,13 @@
 
 static inline void
 SingleSubst_serialize (hb_serialize_context_t *c,
-		       Supplier<GlyphID> &glyphs,
-		       Supplier<GlyphID> &substitutes,
-		       unsigned int num_glyphs)
-{
-  c->start_embed<SingleSubst> ()->serialize (c,
-					     glyphs,
-					     substitutes,
-					     num_glyphs);
-}
+		       hb_array_t<const GlyphID> glyphs,
+		       hb_array_t<const GlyphID> substitutes)
+{ c->start_embed<SingleSubst> ()->serialize (c, glyphs, substitutes); }
 
 struct Sequence
 {
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     unsigned int count = substitute.len;
@@ -325,13 +304,13 @@
       c->out->add (substitute[i]);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     c->output->add_array (substitute.arrayZ, substitute.len);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int count = substitute.len;
@@ -363,17 +342,14 @@
     return_trace (true);
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 unsigned int num_glyphs)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!substitute.serialize (c, glyphs, num_glyphs))) return_trace (false);
-    return_trace (true);
+    return_trace (substitute.serialize (c, glyphs));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (substitute.sanitize (c));
@@ -388,14 +364,14 @@
 
 struct MultipleSubstFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     unsigned int count = sequence.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -404,7 +380,7 @@
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -413,16 +389,15 @@
       (this+sequence[i]).collect_glyphs (c);
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
@@ -432,32 +407,33 @@
     return_trace ((this+sequence[index]).apply (c));
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 Supplier<unsigned int> &substitute_len_list,
-			 unsigned int num_glyphs,
-			 Supplier<GlyphID> &substitute_glyphs_list)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs,
+		  hb_array_t<const unsigned int> substitute_len_list,
+		  hb_array_t<const GlyphID> substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!sequence.serialize (c, num_glyphs))) return_trace (false);
-    for (unsigned int i = 0; i < num_glyphs; i++)
-      if (unlikely (!sequence[i].serialize (c, this).serialize (c,
-								substitute_glyphs_list,
-								substitute_len_list[i]))) return_trace (false);
-    substitute_len_list += num_glyphs;
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
-    return_trace (true);
+    if (unlikely (!sequence.serialize (c, glyphs.len))) return_trace (false);
+    for (unsigned int i = 0; i < glyphs.len; i++)
+    {
+      unsigned int substitute_len = substitute_len_list[i];
+      if (unlikely (!sequence[i].serialize (c, this)
+				.serialize (c, substitute_glyphs_list.sub_array (0, substitute_len))))
+	return_trace (false);
+      substitute_glyphs_list += substitute_len;
+    }
+    return_trace (coverage.serialize (c, this).serialize (c, glyphs));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
@@ -477,24 +453,23 @@
 
 struct MultipleSubst
 {
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 Supplier<unsigned int> &substitute_len_list,
-			 unsigned int num_glyphs,
-			 Supplier<GlyphID> &substitute_glyphs_list)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs,
+		  hb_array_t<const unsigned int> substitute_len_list,
+		  hb_array_t<const GlyphID> substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned int format = 1;
     u.format.set (format);
     switch (u.format) {
-    case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, num_glyphs, substitute_glyphs_list));
+    case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list));
     default:return_trace (false);
     }
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -513,7 +488,7 @@
 
 struct AlternateSet
 {
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     unsigned int count = alternates.len;
@@ -521,13 +496,13 @@
       c->out->add (alternates[i]);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     c->output->add_array (alternates.arrayZ, alternates.len);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int count = alternates.len;
@@ -552,17 +527,14 @@
     return_trace (true);
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 unsigned int num_glyphs)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!alternates.serialize (c, glyphs, num_glyphs))) return_trace (false);
-    return_trace (true);
+    return_trace (alternates.serialize (c, glyphs));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (alternates.sanitize (c));
@@ -578,45 +550,44 @@
 
 struct AlternateSubstFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     unsigned int count = alternateSet.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
+	break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
 	(this+alternateSet[iter.get_coverage ()]).closure (c);
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
     unsigned int count = alternateSet.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
+	break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       (this+alternateSet[iter.get_coverage ()]).collect_glyphs (c);
     }
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
@@ -626,32 +597,33 @@
     return_trace ((this+alternateSet[index]).apply (c));
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 Supplier<unsigned int> &alternate_len_list,
-			 unsigned int num_glyphs,
-			 Supplier<GlyphID> &alternate_glyphs_list)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs,
+		  hb_array_t<const unsigned int> alternate_len_list,
+		  hb_array_t<const GlyphID> alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!alternateSet.serialize (c, num_glyphs))) return_trace (false);
-    for (unsigned int i = 0; i < num_glyphs; i++)
-      if (unlikely (!alternateSet[i].serialize (c, this).serialize (c,
-								    alternate_glyphs_list,
-								    alternate_len_list[i]))) return_trace (false);
-    alternate_len_list += num_glyphs;
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
-    return_trace (true);
+    if (unlikely (!alternateSet.serialize (c, glyphs.len))) return_trace (false);
+    for (unsigned int i = 0; i < glyphs.len; i++)
+    {
+      unsigned int alternate_len = alternate_len_list[i];
+      if (unlikely (!alternateSet[i].serialize (c, this)
+				    .serialize (c, alternate_glyphs_list.sub_array (0, alternate_len))))
+	return_trace (false);
+      alternate_glyphs_list += alternate_len;
+    }
+    return_trace (coverage.serialize (c, this).serialize (c, glyphs));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
@@ -671,24 +643,23 @@
 
 struct AlternateSubst
 {
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &glyphs,
-			 Supplier<unsigned int> &alternate_len_list,
-			 unsigned int num_glyphs,
-			 Supplier<GlyphID> &alternate_glyphs_list)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> glyphs,
+		  hb_array_t<const unsigned int> alternate_len_list,
+		  hb_array_t<const GlyphID> alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned int format = 1;
     u.format.set (format);
     switch (u.format) {
-    case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, num_glyphs, alternate_glyphs_list));
+    case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list));
     default:return_trace (false);
     }
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -708,7 +679,7 @@
 
 struct Ligature
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     unsigned int count = component.lenP1;
     for (unsigned int i = 1; i < count; i++)
@@ -717,7 +688,7 @@
     return true;
   }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     unsigned int count = component.lenP1;
@@ -727,14 +698,14 @@
     c->out->add (ligGlyph);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     c->input->add_array (component.arrayZ, component.lenP1 ? component.lenP1 - 1 : 0);
     c->output->add (ligGlyph);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     if (c->len != component.lenP1)
@@ -747,7 +718,7 @@
     return_trace (true);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int count = component.lenP1;
@@ -786,20 +757,19 @@
     return_trace (true);
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 GlyphID ligature,
-			 Supplier<GlyphID> &components, /* Starting from second */
-			 unsigned int num_components /* Including first component */)
+  bool serialize (hb_serialize_context_t *c,
+		  GlyphID ligature,
+		  hb_array_t<const GlyphID> components /* Starting from second */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
     ligGlyph = ligature;
-    if (unlikely (!component.serialize (c, components, num_components))) return_trace (false);
+    if (unlikely (!component.serialize (c, components))) return_trace (false);
     return_trace (true);
   }
 
   public:
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
@@ -817,7 +787,7 @@
 
 struct LigatureSet
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     unsigned int num_ligs = ligature.len;
     for (unsigned int i = 0; i < num_ligs; i++)
@@ -826,7 +796,7 @@
     return false;
   }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     unsigned int num_ligs = ligature.len;
@@ -834,7 +804,7 @@
       (this+ligature[i]).closure (c);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     unsigned int num_ligs = ligature.len;
@@ -842,7 +812,7 @@
       (this+ligature[i]).collect_glyphs (c);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     unsigned int num_ligs = ligature.len;
@@ -855,7 +825,7 @@
     return_trace (false);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int num_ligs = ligature.len;
@@ -868,26 +838,28 @@
     return_trace (false);
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &ligatures,
-			 Supplier<unsigned int> &component_count_list,
-			 unsigned int num_ligatures,
-			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> ligatures,
+		  hb_array_t<const unsigned int> component_count_list,
+		  hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!ligature.serialize (c, num_ligatures))) return_trace (false);
-    for (unsigned int i = 0; i < num_ligatures; i++)
-      if (unlikely (!ligature[i].serialize (c, this).serialize (c,
-								ligatures[i],
-								component_list,
-								component_count_list[i]))) return_trace (false);
-    ligatures += num_ligatures;
-    component_count_list += num_ligatures;
+    if (unlikely (!ligature.serialize (c, ligatures.len))) return_trace (false);
+    for (unsigned int i = 0; i < ligatures.len; i++)
+    {
+      unsigned int component_count = MAX<int> (component_count_list[i] - 1, 0);
+      if (unlikely (!ligature[i].serialize (c, this)
+				.serialize (c,
+					    ligatures[i],
+					    component_list.sub_array (0, component_count))))
+	return_trace (false);
+      component_list += component_count;
+    }
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (ligature.sanitize (c, this));
@@ -903,10 +875,10 @@
 
 struct LigatureSubstFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     unsigned int count = ligatureSet.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -917,11 +889,11 @@
     return false;
   }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     unsigned int count = ligatureSet.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -930,12 +902,12 @@
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
     unsigned int count = ligatureSet.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -943,10 +915,9 @@
     }
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
@@ -956,7 +927,7 @@
     return_trace (lig_set.would_apply (c));
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
@@ -967,36 +938,40 @@
     return_trace (lig_set.apply (c));
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &first_glyphs,
-			 Supplier<unsigned int> &ligature_per_first_glyph_count_list,
-			 unsigned int num_first_glyphs,
-			 Supplier<GlyphID> &ligatures_list,
-			 Supplier<unsigned int> &component_count_list,
-			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> first_glyphs,
+		  hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+		  hb_array_t<const GlyphID> ligatures_list,
+		  hb_array_t<const unsigned int> component_count_list,
+		  hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return_trace (false);
-    for (unsigned int i = 0; i < num_first_glyphs; i++)
-      if (unlikely (!ligatureSet[i].serialize (c, this).serialize (c,
-								   ligatures_list,
-								   component_count_list,
-								   ligature_per_first_glyph_count_list[i],
-								   component_list))) return_trace (false);
-    ligature_per_first_glyph_count_list += num_first_glyphs;
-    if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return_trace (false);
-    return_trace (true);
+    if (unlikely (!ligatureSet.serialize (c, first_glyphs.len))) return_trace (false);
+    for (unsigned int i = 0; i < first_glyphs.len; i++)
+    {
+      unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
+      if (unlikely (!ligatureSet[i].serialize (c, this)
+				   .serialize (c,
+					       ligatures_list.sub_array (0, ligature_count),
+					       component_count_list.sub_array (0, ligature_count),
+					       component_list))) return_trace (false);
+      ligatures_list += ligature_count;
+      component_count_list += ligature_count;
+      for (unsigned int i = 0; i < ligature_count; i++)
+	component_list += MAX<int> (component_count_list[i] - 1, 0);
+    }
+    return_trace (coverage.serialize (c, this).serialize (c, first_glyphs));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
@@ -1016,13 +991,12 @@
 
 struct LigatureSubst
 {
-  inline bool serialize (hb_serialize_context_t *c,
-			 Supplier<GlyphID> &first_glyphs,
-			 Supplier<unsigned int> &ligature_per_first_glyph_count_list,
-			 unsigned int num_first_glyphs,
-			 Supplier<GlyphID> &ligatures_list,
-			 Supplier<unsigned int> &component_count_list,
-			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  bool serialize (hb_serialize_context_t *c,
+		  hb_array_t<const GlyphID> first_glyphs,
+		  hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+		  hb_array_t<const GlyphID> ligatures_list,
+		  hb_array_t<const unsigned int> component_count_list,
+		  hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
@@ -1032,7 +1006,6 @@
     case 1: return_trace (u.format1.serialize (c,
 					       first_glyphs,
 					       ligature_per_first_glyph_count_list,
-					       num_first_glyphs,
 					       ligatures_list,
 					       component_count_list,
 					       component_list));
@@ -1041,7 +1014,7 @@
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -1067,13 +1040,13 @@
 {
   typedef struct SubstLookupSubTable SubTable;
 
-  inline bool is_reverse (void) const;
+  bool is_reverse () const;
 };
 
 
 struct ReverseChainSingleSubstFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     if (!(this+coverage).intersects (glyphs))
       return false;
@@ -1095,7 +1068,7 @@
     return true;
   }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
@@ -1114,7 +1087,7 @@
 
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
     count = substitute.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -1123,7 +1096,7 @@
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
@@ -1144,16 +1117,15 @@
     c->output->add_array (substitute.arrayZ, substitute.len);
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
@@ -1186,14 +1158,14 @@
     return_trace (false);
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
@@ -1228,7 +1200,7 @@
 struct ReverseChainSingleSubst
 {
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -1268,10 +1240,9 @@
   };
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
+  typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   {
     TRACE_DISPATCH (this, lookup_type);
-    if (unlikely (!c->may_dispatch (this, &u.sub_format))) return_trace (c->no_dispatch_return_value ());
     switch (lookup_type) {
     case Single:		return_trace (u.single.dispatch (c));
     case Multiple:		return_trace (u.multiple.dispatch (c));
@@ -1287,7 +1258,6 @@
 
   protected:
   union {
-  HBUINT16			sub_format;
   SingleSubst			single;
   MultipleSubst			multiple;
   AlternateSubst		alternate;
@@ -1298,7 +1268,7 @@
   ReverseChainSingleSubst	reverseChainContextSingle;
   } u;
   public:
-  DEFINE_SIZE_UNION (2, sub_format);
+  DEFINE_SIZE_MIN (0);
 };
 
 
@@ -1306,13 +1276,13 @@
 {
   typedef SubstLookupSubTable SubTable;
 
-  inline const SubTable& get_subtable (unsigned int i) const
+  const SubTable& get_subtable (unsigned int i) const
   { return Lookup::get_subtable<SubTable> (i); }
 
-  inline static bool lookup_type_is_reverse (unsigned int lookup_type)
+  static bool lookup_type_is_reverse (unsigned int lookup_type)
   { return lookup_type == SubTable::ReverseChainSingle; }
 
-  inline bool is_reverse (void) const
+  bool is_reverse () const
   {
     unsigned int type = get_type ();
     if (unlikely (type == SubTable::Extension))
@@ -1320,19 +1290,19 @@
     return lookup_type_is_reverse (type);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     return_trace (dispatch (c));
   }
 
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     hb_intersects_context_t c (glyphs);
     return dispatch (&c);
   }
 
-  inline hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
+  hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
   {
     TRACE_CLOSURE (this);
     if (!c->should_visit_lookup (this_index))
@@ -1347,7 +1317,7 @@
     return_trace (ret);
   }
 
-  inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
+  hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
@@ -1355,14 +1325,14 @@
   }
 
   template <typename set_t>
-  inline void add_coverage (set_t *glyphs) const
+  void add_coverage (set_t *glyphs) const
   {
     hb_add_coverage_context_t<set_t> c (glyphs);
     dispatch (&c);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c,
-			   const hb_ot_layout_lookup_accelerator_t *accel) const
+  bool would_apply (hb_would_apply_context_t *c,
+		    const hb_ot_layout_lookup_accelerator_t *accel) const
   {
     TRACE_WOULD_APPLY (this);
     if (unlikely (!c->len))  return_trace (false);
@@ -1372,96 +1342,92 @@
 
   static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
 
-  inline SubTable& serialize_subtable (hb_serialize_context_t *c,
+  SubTable& serialize_subtable (hb_serialize_context_t *c,
 				       unsigned int i)
   { return get_subtables<SubTable> ()[i].serialize (c, this); }
 
-  inline bool serialize_single (hb_serialize_context_t *c,
-				uint32_t lookup_props,
-			        Supplier<GlyphID> &glyphs,
-			        Supplier<GlyphID> &substitutes,
-			        unsigned int num_glyphs)
+  bool serialize_single (hb_serialize_context_t *c,
+			 uint32_t lookup_props,
+		         hb_array_t<const GlyphID> glyphs,
+		         hb_array_t<const GlyphID> substitutes)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
-    return_trace (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes, num_glyphs));
+    return_trace (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes));
   }
 
-  inline bool serialize_multiple (hb_serialize_context_t *c,
-				  uint32_t lookup_props,
-				  Supplier<GlyphID> &glyphs,
-				  Supplier<unsigned int> &substitute_len_list,
-				  unsigned int num_glyphs,
-				  Supplier<GlyphID> &substitute_glyphs_list)
+  bool serialize_multiple (hb_serialize_context_t *c,
+			   uint32_t lookup_props,
+			   hb_array_t<const GlyphID> glyphs,
+			   hb_array_t<const unsigned int> substitute_len_list,
+			   hb_array_t<const GlyphID> substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
     return_trace (serialize_subtable (c, 0).u.multiple.serialize (c,
 								  glyphs,
 								  substitute_len_list,
-								  num_glyphs,
 								  substitute_glyphs_list));
   }
 
-  inline bool serialize_alternate (hb_serialize_context_t *c,
-				   uint32_t lookup_props,
-				   Supplier<GlyphID> &glyphs,
-				   Supplier<unsigned int> &alternate_len_list,
-				   unsigned int num_glyphs,
-				   Supplier<GlyphID> &alternate_glyphs_list)
+  bool serialize_alternate (hb_serialize_context_t *c,
+			    uint32_t lookup_props,
+			    hb_array_t<const GlyphID> glyphs,
+			    hb_array_t<const unsigned int> alternate_len_list,
+			    hb_array_t<const GlyphID> alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
     return_trace (serialize_subtable (c, 0).u.alternate.serialize (c,
 								   glyphs,
 								   alternate_len_list,
-								   num_glyphs,
 								   alternate_glyphs_list));
   }
 
-  inline bool serialize_ligature (hb_serialize_context_t *c,
-				  uint32_t lookup_props,
-				  Supplier<GlyphID> &first_glyphs,
-				  Supplier<unsigned int> &ligature_per_first_glyph_count_list,
-				  unsigned int num_first_glyphs,
-				  Supplier<GlyphID> &ligatures_list,
-				  Supplier<unsigned int> &component_count_list,
-				  Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  bool serialize_ligature (hb_serialize_context_t *c,
+			   uint32_t lookup_props,
+			   hb_array_t<const GlyphID> first_glyphs,
+			   hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+			   hb_array_t<const GlyphID> ligatures_list,
+			   hb_array_t<const unsigned int> component_count_list,
+			   hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
     return_trace (serialize_subtable (c, 0).u.ligature.serialize (c,
 								  first_glyphs,
 								  ligature_per_first_glyph_count_list,
-								  num_first_glyphs,
 								  ligatures_list,
 								  component_count_list,
 								  component_list));
   }
 
   template <typename context_t>
-  static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
+  static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
 
-  static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index)
+  static hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index)
   {
     if (!c->should_visit_lookup (lookup_index))
       return HB_VOID;
 
     hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index);
 
-    c->flush ();
+    /* While in theory we should flush here, it will cause timeouts because a recursive
+     * lookup can keep growing the glyph set.  Skip, and outer loop will retry up to
+     * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
+    //c->flush ();
 
     return ret;
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   { return Lookup::dispatch<SubTable> (c); }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   { return Lookup::subset<SubTable> (c); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   { return Lookup::sanitize<SubTable> (c); }
 };
 
@@ -1472,24 +1438,30 @@
 
 struct GSUB : GSUBGPOS
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_GSUB;
+  enum { tableTag = HB_OT_TAG_GSUB };
 
-  inline const SubstLookup& get_lookup (unsigned int i) const
+  const SubstLookup& get_lookup (unsigned int i) const
   { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   { return GSUBGPOS::subset<SubstLookup> (c); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   { return GSUBGPOS::sanitize<SubstLookup> (c); }
 
+  HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
+				   hb_face_t *face) const;
+
   typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
 };
 
 
+struct GSUB_accelerator_t : GSUB::accelerator_t {};
+
+
 /* Out-of-class implementation for methods recursing */
 
-/*static*/ inline bool ExtensionSubst::is_reverse (void) const
+/*static*/ inline bool ExtensionSubst::is_reverse () const
 {
   unsigned int type = get_type ();
   if (unlikely (type == SubTable::Extension))
@@ -1500,13 +1472,13 @@
 template <typename context_t>
 /*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
 {
-  const SubstLookup &l = _get_gsub_relaxed (c->face).get_lookup (lookup_index);
+  const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
   return l.dispatch (c);
 }
 
 /*static*/ inline bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
 {
-  const SubstLookup &l = _get_gsub_relaxed (c->face).get_lookup (lookup_index);
+  const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
   unsigned int saved_lookup_index = c->lookup_index;
   c->set_lookup_index (lookup_index);
@@ -1517,8 +1489,6 @@
   return ret;
 }
 
-struct GSUB_accelerator_t : GSUB::accelerator_t {};
-
 } /* namespace OT */
 
 
diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh
index a406626..ac6e646 100644
--- a/src/hb-ot-layout-gsubgpos.hh
+++ b/src/hb-ot-layout-gsubgpos.hh
@@ -44,10 +44,10 @@
 struct hb_intersects_context_t :
        hb_dispatch_context_t<hb_intersects_context_t, bool, 0>
 {
-  inline const char *get_name (void) { return "INTERSECTS"; }
+  const char *get_name () { return "INTERSECTS"; }
   template <typename T>
-  inline return_t dispatch (const T &obj) { return obj.intersects (this->glyphs); }
-  static return_t default_return_value (void) { return false; }
+  return_t dispatch (const T &obj) { return obj.intersects (this->glyphs); }
+  static return_t default_return_value () { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
 
   const hb_set_t *glyphs;
@@ -61,12 +61,11 @@
 struct hb_closure_context_t :
        hb_dispatch_context_t<hb_closure_context_t, hb_void_t, HB_DEBUG_CLOSURE>
 {
-  inline const char *get_name (void) { return "CLOSURE"; }
+  const char *get_name () { return "CLOSURE"; }
   typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
   template <typename T>
-  inline return_t dispatch (const T &obj) { obj.closure (this); return HB_VOID; }
-  static return_t default_return_value (void) { return HB_VOID; }
-  bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; }
+  return_t dispatch (const T &obj) { obj.closure (this); return HB_VOID; }
+  static return_t default_return_value () { return HB_VOID; }
   void recurse (unsigned int lookup_index)
   {
     if (unlikely (nesting_level_left == 0 || !recurse_func))
@@ -93,30 +92,27 @@
 
   hb_face_t *face;
   hb_set_t *glyphs;
-  hb_auto_t<hb_set_t> out[1];
+  hb_set_t out[1];
   recurse_func_t recurse_func;
   unsigned int nesting_level_left;
   unsigned int debug_depth;
 
   hb_closure_context_t (hb_face_t *face_,
 			hb_set_t *glyphs_,
-                        hb_map_t *done_lookups_,
-		        unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
+			hb_map_t *done_lookups_,
+			unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
 			  face (face_),
 			  glyphs (glyphs_),
 			  recurse_func (nullptr),
 			  nesting_level_left (nesting_level_left_),
 			  debug_depth (0),
-                          done_lookups (done_lookups_) {}
+			  done_lookups (done_lookups_) {}
 
-  ~hb_closure_context_t (void)
-  {
-    flush ();
-  }
+  ~hb_closure_context_t () { flush (); }
 
   void set_recurse_func (recurse_func_t func) { recurse_func = func; }
 
-  void flush (void)
+  void flush ()
   {
     hb_set_union (glyphs, out);
     hb_set_clear (out);
@@ -130,10 +126,10 @@
 struct hb_would_apply_context_t :
        hb_dispatch_context_t<hb_would_apply_context_t, bool, HB_DEBUG_WOULD_APPLY>
 {
-  inline const char *get_name (void) { return "WOULD_APPLY"; }
+  const char *get_name () { return "WOULD_APPLY"; }
   template <typename T>
-  inline return_t dispatch (const T &obj) { return obj.would_apply (this); }
-  static return_t default_return_value (void) { return false; }
+  return_t dispatch (const T &obj) { return obj.would_apply (this); }
+  static return_t default_return_value () { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
 
   hb_face_t *face;
@@ -157,12 +153,11 @@
 struct hb_collect_glyphs_context_t :
        hb_dispatch_context_t<hb_collect_glyphs_context_t, hb_void_t, HB_DEBUG_COLLECT_GLYPHS>
 {
-  inline const char *get_name (void) { return "COLLECT_GLYPHS"; }
+  const char *get_name () { return "COLLECT_GLYPHS"; }
   typedef return_t (*recurse_func_t) (hb_collect_glyphs_context_t *c, unsigned int lookup_index);
   template <typename T>
-  inline return_t dispatch (const T &obj) { obj.collect_glyphs (this); return HB_VOID; }
-  static return_t default_return_value (void) { return HB_VOID; }
-  bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; }
+  return_t dispatch (const T &obj) { obj.collect_glyphs (this); return HB_VOID; }
+  static return_t default_return_value () { return HB_VOID; }
   void recurse (unsigned int lookup_index)
   {
     if (unlikely (nesting_level_left == 0 || !recurse_func))
@@ -226,7 +221,7 @@
 			      recursed_lookups (hb_set_create ()),
 			      nesting_level_left (nesting_level_left_),
 			      debug_depth (0) {}
-  ~hb_collect_glyphs_context_t (void) { hb_set_destroy (recursed_lookups); }
+  ~hb_collect_glyphs_context_t () { hb_set_destroy (recursed_lookups); }
 
   void set_recurse_func (recurse_func_t func) { recurse_func = func; }
 };
@@ -237,11 +232,11 @@
 struct hb_add_coverage_context_t :
        hb_dispatch_context_t<hb_add_coverage_context_t<set_t>, const Coverage &, HB_DEBUG_GET_COVERAGE>
 {
-  inline const char *get_name (void) { return "GET_COVERAGE"; }
+  const char *get_name () { return "GET_COVERAGE"; }
   typedef const Coverage &return_t;
   template <typename T>
-  inline return_t dispatch (const T &obj) { return obj.get_coverage (); }
-  static return_t default_return_value (void) { return Null(Coverage); }
+  return_t dispatch (const T &obj) { return obj.get_coverage (); }
+  static return_t default_return_value () { return Null(Coverage); }
   bool stop_sublookup_iteration (return_t r) const
   {
     r.add_coverage (set);
@@ -262,7 +257,7 @@
 {
   struct matcher_t
   {
-    inline matcher_t (void) :
+    matcher_t () :
 	     lookup_props (0),
 	     ignore_zwnj (false),
 	     ignore_zwj (false),
@@ -271,16 +266,16 @@
 	     syllable arg1(0),
 #undef arg1
 	     match_func (nullptr),
-	     match_data (nullptr) {};
+	     match_data (nullptr) {}
 
     typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data);
 
-    inline void set_ignore_zwnj (bool ignore_zwnj_) { ignore_zwnj = ignore_zwnj_; }
-    inline void set_ignore_zwj (bool ignore_zwj_) { ignore_zwj = ignore_zwj_; }
-    inline void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; }
-    inline void set_mask (hb_mask_t mask_) { mask = mask_; }
-    inline void set_syllable (uint8_t syllable_)  { syllable = syllable_; }
-    inline void set_match_func (match_func_t match_func_,
+    void set_ignore_zwnj (bool ignore_zwnj_) { ignore_zwnj = ignore_zwnj_; }
+    void set_ignore_zwj (bool ignore_zwj_) { ignore_zwj = ignore_zwj_; }
+    void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; }
+    void set_mask (hb_mask_t mask_) { mask = mask_; }
+    void set_syllable (uint8_t syllable_)  { syllable = syllable_; }
+    void set_match_func (match_func_t match_func_,
 				const void *match_data_)
     { match_func = match_func_; match_data = match_data_; }
 
@@ -290,15 +285,15 @@
       MATCH_MAYBE
     };
 
-    inline may_match_t may_match (const hb_glyph_info_t &info,
-				  const HBUINT16          *glyph_data) const
+    may_match_t may_match (const hb_glyph_info_t &info,
+				  const HBUINT16        *glyph_data) const
     {
       if (!(info.mask & mask) ||
 	  (syllable && syllable != info.syllable ()))
 	return MATCH_NO;
 
       if (match_func)
-        return match_func (info.codepoint, *glyph_data, match_data) ? MATCH_YES : MATCH_NO;
+	return match_func (info.codepoint, *glyph_data, match_data) ? MATCH_YES : MATCH_NO;
 
       return MATCH_MAYBE;
     }
@@ -309,9 +304,8 @@
       SKIP_MAYBE
     };
 
-    inline may_skip_t
-    may_skip (const hb_ot_apply_context_t *c,
-	      const hb_glyph_info_t    &info) const
+    may_skip_t may_skip (const hb_ot_apply_context_t *c,
+			 const hb_glyph_info_t       &info) const
     {
       if (!c->check_glyph_property (&info, lookup_props))
 	return SKIP_YES;
@@ -336,7 +330,7 @@
 
   struct skipping_iterator_t
   {
-    inline void init (hb_ot_apply_context_t *c_, bool context_match = false)
+    void init (hb_ot_apply_context_t *c_, bool context_match = false)
     {
       c = c_;
       match_glyph_data = nullptr;
@@ -348,19 +342,19 @@
       matcher.set_ignore_zwj  (context_match || c->auto_zwj);
       matcher.set_mask (context_match ? -1 : c->lookup_mask);
     }
-    inline void set_lookup_props (unsigned int lookup_props)
+    void set_lookup_props (unsigned int lookup_props)
     {
       matcher.set_lookup_props (lookup_props);
     }
-    inline void set_match_func (matcher_t::match_func_t match_func_,
-				const void *match_data_,
-				const HBUINT16 glyph_data[])
+    void set_match_func (matcher_t::match_func_t match_func_,
+			 const void *match_data_,
+			 const HBUINT16 glyph_data[])
     {
       matcher.set_match_func (match_func_, match_data_);
       match_glyph_data = glyph_data;
     }
 
-    inline void reset (unsigned int start_index_,
+    void reset (unsigned int start_index_,
 		       unsigned int num_items_)
     {
       idx = start_index_;
@@ -369,15 +363,13 @@
       matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0);
     }
 
-    inline void reject (void) { num_items++; match_glyph_data--; }
+    void reject () { num_items++; match_glyph_data--; }
 
-    inline matcher_t::may_skip_t
-    may_skip (const hb_glyph_info_t    &info) const
-    {
-      return matcher.may_skip (c, info);
-    }
+    matcher_t::may_skip_t
+    may_skip (const hb_glyph_info_t &info) const
+    { return matcher.may_skip (c, info); }
 
-    inline bool next (void)
+    bool next ()
     {
       assert (num_items > 0);
       while (idx + num_items < end)
@@ -404,7 +396,7 @@
       }
       return false;
     }
-    inline bool prev (void)
+    bool prev ()
     {
       assert (num_items > 0);
       while (idx > num_items - 1)
@@ -443,11 +435,11 @@
   };
 
 
-  inline const char *get_name (void) { return "APPLY"; }
+  const char *get_name () { return "APPLY"; }
   typedef return_t (*recurse_func_t) (hb_ot_apply_context_t *c, unsigned int lookup_index);
   template <typename T>
-  inline return_t dispatch (const T &obj) { return obj.apply (this); }
-  static return_t default_return_value (void) { return false; }
+  return_t dispatch (const T &obj) { return obj.apply (this); }
+  static return_t default_return_value () { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
   return_t recurse (unsigned int sub_lookup_index)
   {
@@ -491,7 +483,7 @@
 			iter_input (), iter_context (),
 			font (font_), face (font->face), buffer (buffer_),
 			recurse_func (nullptr),
-			gdef (_get_gdef (face)),
+			gdef (*face->table.GDEF->table),
 			var_store (gdef.get_var_store ()),
 			direction (buffer_->props.direction),
 			lookup_mask (1),
@@ -506,31 +498,30 @@
 			random (false),
 			random_state (1) { init_iters (); }
 
-  inline void init_iters (void)
+  void init_iters ()
   {
     iter_input.init (this, false);
     iter_context.init (this, true);
   }
 
-  inline void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; init_iters (); }
-  inline void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; init_iters (); }
-  inline void set_auto_zwnj (bool auto_zwnj_) { auto_zwnj = auto_zwnj_; init_iters (); }
-  inline void set_random (bool random_) { random = random_; }
-  inline void set_recurse_func (recurse_func_t func) { recurse_func = func; }
-  inline void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; }
-  inline void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; init_iters (); }
+  void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; init_iters (); }
+  void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; init_iters (); }
+  void set_auto_zwnj (bool auto_zwnj_) { auto_zwnj = auto_zwnj_; init_iters (); }
+  void set_random (bool random_) { random = random_; }
+  void set_recurse_func (recurse_func_t func) { recurse_func = func; }
+  void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; }
+  void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; init_iters (); }
 
-  inline uint32_t random_number (void)
+  uint32_t random_number ()
   {
     /* http://www.cplusplus.com/reference/random/minstd_rand/ */
     random_state = random_state * 48271 % 2147483647;
     return random_state;
   }
 
-  inline bool
-  match_properties_mark (hb_codepoint_t  glyph,
-			 unsigned int    glyph_props,
-			 unsigned int    match_props) const
+  bool match_properties_mark (hb_codepoint_t  glyph,
+			      unsigned int    glyph_props,
+			      unsigned int    match_props) const
   {
     /* If using mark filtering sets, the high short of
      * match_props has the set index.
@@ -548,9 +539,8 @@
     return true;
   }
 
-  inline bool
-  check_glyph_property (const hb_glyph_info_t *info,
-			unsigned int  match_props) const
+  bool check_glyph_property (const hb_glyph_info_t *info,
+			     unsigned int  match_props) const
   {
     hb_codepoint_t glyph = info->codepoint;
     unsigned int glyph_props = _hb_glyph_info_get_glyph_props (info);
@@ -567,7 +557,7 @@
     return true;
   }
 
-  inline void _set_glyph_props (hb_codepoint_t glyph_index,
+  void _set_glyph_props (hb_codepoint_t glyph_index,
 			  unsigned int class_guess = 0,
 			  bool ligature = false,
 			  bool component = false) const
@@ -594,23 +584,23 @@
       _hb_glyph_info_set_glyph_props (&buffer->cur(), add_in | class_guess);
   }
 
-  inline void replace_glyph (hb_codepoint_t glyph_index) const
+  void replace_glyph (hb_codepoint_t glyph_index) const
   {
     _set_glyph_props (glyph_index);
     buffer->replace_glyph (glyph_index);
   }
-  inline void replace_glyph_inplace (hb_codepoint_t glyph_index) const
+  void replace_glyph_inplace (hb_codepoint_t glyph_index) const
   {
     _set_glyph_props (glyph_index);
     buffer->cur().codepoint = glyph_index;
   }
-  inline void replace_glyph_with_ligature (hb_codepoint_t glyph_index,
+  void replace_glyph_with_ligature (hb_codepoint_t glyph_index,
 					   unsigned int class_guess) const
   {
     _set_glyph_props (glyph_index, class_guess, true);
     buffer->replace_glyph (glyph_index);
   }
-  inline void output_glyph_for_component (hb_codepoint_t glyph_index,
+  void output_glyph_for_component (hb_codepoint_t glyph_index,
 					  unsigned int class_guess) const
   {
     _set_glyph_props (glyph_index, class_guess, false, true);
@@ -623,7 +613,7 @@
        hb_dispatch_context_t<hb_get_subtables_context_t, hb_void_t, HB_DEBUG_APPLY>
 {
   template <typename Type>
-  static inline bool apply_to (const void *obj, OT::hb_ot_apply_context_t *c)
+  static bool apply_to (const void *obj, OT::hb_ot_apply_context_t *c)
   {
     const Type *typed_obj = (const Type *) obj;
     return typed_obj->apply (c);
@@ -634,7 +624,7 @@
   struct hb_applicable_t
   {
     template <typename T>
-    inline void init (const T &obj_, hb_apply_func_t apply_func_)
+    void init (const T &obj_, hb_apply_func_t apply_func_)
     {
       obj = &obj_;
       apply_func = apply_func_;
@@ -642,7 +632,7 @@
       obj_.get_coverage ().add_coverage (&digest);
     }
 
-    inline bool apply (OT::hb_ot_apply_context_t *c) const
+    bool apply (OT::hb_ot_apply_context_t *c) const
     {
       return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c);
     }
@@ -656,16 +646,15 @@
   typedef hb_vector_t<hb_applicable_t, 2> array_t;
 
   /* Dispatch interface. */
-  inline const char *get_name (void) { return "GET_SUBTABLES"; }
+  const char *get_name () { return "GET_SUBTABLES"; }
   template <typename T>
-  inline return_t dispatch (const T &obj)
+  return_t dispatch (const T &obj)
   {
     hb_applicable_t *entry = array.push();
     entry->init (obj, apply_to<T>);
     return HB_VOID;
   }
-  static return_t default_return_value (void) { return HB_VOID; }
-  bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; }
+  static return_t default_return_value () { return HB_VOID; }
 
   hb_get_subtables_context_t (array_t &array_) :
 			      array (array_),
@@ -852,9 +841,9 @@
        * component, otherwise we shouldn't ligate them... */
       if (first_lig_id != this_lig_id || first_lig_comp != this_lig_comp)
       {
-        /* ...unless, we are attached to a base ligature and that base
+	/* ...unless, we are attached to a base ligature and that base
 	 * ligature is ignorable. */
-        if (ligbase == LIGBASE_NOT_CHECKED)
+	if (ligbase == LIGBASE_NOT_CHECKED)
 	{
 	  bool found = false;
 	  const hb_glyph_info_t *out = buffer->out_info;
@@ -876,7 +865,7 @@
 	    ligbase = LIGBASE_MAY_NOT_SKIP;
 	}
 
-        if (ligbase == LIGBASE_MAY_NOT_SKIP)
+	if (ligbase == LIGBASE_MAY_NOT_SKIP)
 	  return_trace (false);
       }
     }
@@ -977,7 +966,7 @@
     {
       if (is_ligature)
       {
-        unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
+	unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
 	if (this_comp == 0)
 	  this_comp = last_num_components;
 	unsigned int new_lig_comp = components_so_far - last_num_components +
@@ -999,7 +988,7 @@
     /* Re-adjust components for any marks following. */
     for (unsigned int i = buffer->idx; i < buffer->len; i++) {
       if (last_lig_id == _hb_glyph_info_get_lig_id (&buffer->info[i])) {
-        unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]);
+	unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]);
 	if (!this_comp)
 	  break;
 	unsigned int new_lig_comp = components_so_far - last_num_components +
@@ -1061,7 +1050,7 @@
 
 struct LookupRecord
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -1133,7 +1122,7 @@
     int delta = new_len - orig_len;
 
     if (!delta)
-        continue;
+      continue;
 
     /* Recursed lookup changed buffer len.  Adjust.
      *
@@ -1298,55 +1287,62 @@
 
 struct Rule
 {
-  inline bool intersects (const hb_set_t *glyphs, ContextClosureLookupContext &lookup_context) const
+  bool intersects (const hb_set_t *glyphs, ContextClosureLookupContext &lookup_context) const
   {
     return context_intersects (glyphs,
 			       inputCount, inputZ.arrayZ,
 			       lookup_context);
   }
 
-  inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
+  void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
-    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAtOffset<UnsizedArrayOf<LookupRecord> > (inputZ.arrayZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
+    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord> >
+						       (inputZ.as_array ((inputCount ? inputCount - 1 : 0)));
     context_closure_lookup (c,
 			    inputCount, inputZ.arrayZ,
 			    lookupCount, lookupRecord.arrayZ,
 			    lookup_context);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c,
+		       ContextCollectGlyphsLookupContext &lookup_context) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAtOffset<UnsizedArrayOf<LookupRecord> > (inputZ.arrayZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
+    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord> >
+						       (inputZ.as_array (inputCount ? inputCount - 1 : 0));
     context_collect_glyphs_lookup (c,
 				   inputCount, inputZ.arrayZ,
 				   lookupCount, lookupRecord.arrayZ,
 				   lookup_context);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
+  bool would_apply (hb_would_apply_context_t *c,
+		    ContextApplyLookupContext &lookup_context) const
   {
     TRACE_WOULD_APPLY (this);
-    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAtOffset<UnsizedArrayOf<LookupRecord> > (inputZ.arrayZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
+    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord> >
+						       (inputZ.as_array (inputCount ? inputCount - 1 : 0));
     return_trace (context_would_apply_lookup (c, inputCount, inputZ.arrayZ, lookupCount, lookupRecord.arrayZ, lookup_context));
   }
 
-  inline bool apply (hb_ot_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
+  bool apply (hb_ot_apply_context_t *c,
+	      ContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
-    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAtOffset<UnsizedArrayOf<LookupRecord> > (inputZ.arrayZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
+    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord> >
+						       (inputZ.as_array (inputCount ? inputCount - 1 : 0));
     return_trace (context_apply_lookup (c, inputCount, inputZ.arrayZ, lookupCount, lookupRecord.arrayZ, lookup_context));
   }
 
   public:
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (inputCount.sanitize (c) &&
 		  lookupCount.sanitize (c) &&
 		  c->check_range (inputZ.arrayZ,
-				  inputZ[0].static_size * (inputCount ? inputCount - 1 : 0) +
+				  inputZ.item_size * (inputCount ? inputCount - 1 : 0) +
 				  LookupRecord::static_size * lookupCount));
   }
 
@@ -1367,16 +1363,18 @@
 
 struct RuleSet
 {
-  inline bool intersects (const hb_set_t *glyphs, ContextClosureLookupContext &lookup_context) const
+  bool intersects (const hb_set_t *glyphs,
+		   ContextClosureLookupContext &lookup_context) const
   {
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       if ((this+rule[i]).intersects (glyphs, lookup_context))
-        return true;
+	return true;
     return false;
   }
 
-  inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
+  void closure (hb_closure_context_t *c,
+		ContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
     unsigned int num_rules = rule.len;
@@ -1384,7 +1382,8 @@
       (this+rule[i]).closure (c, lookup_context);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c,
+		       ContextCollectGlyphsLookupContext &lookup_context) const
   {
     TRACE_COLLECT_GLYPHS (this);
     unsigned int num_rules = rule.len;
@@ -1392,31 +1391,33 @@
       (this+rule[i]).collect_glyphs (c, lookup_context);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
+  bool would_apply (hb_would_apply_context_t *c,
+		    ContextApplyLookupContext &lookup_context) const
   {
     TRACE_WOULD_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
     {
       if ((this+rule[i]).would_apply (c, lookup_context))
-        return_trace (true);
+	return_trace (true);
     }
     return_trace (false);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
+  bool apply (hb_ot_apply_context_t *c,
+	      ContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
     {
       if ((this+rule[i]).apply (c, lookup_context))
-        return_trace (true);
+	return_trace (true);
     }
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (rule.sanitize (c, this));
@@ -1433,7 +1434,7 @@
 
 struct ContextFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     struct ContextClosureLookupContext lookup_context = {
       {intersects_glyph},
@@ -1441,18 +1442,18 @@
     };
 
     unsigned int count = ruleSet.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
+	break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (glyphs->has (iter.get_glyph ()) &&
 	  (this+ruleSet[iter.get_coverage ()]).intersects (glyphs, lookup_context))
-        return true;
+	return true;
     }
     return false;
   }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
 
@@ -1462,16 +1463,16 @@
     };
 
     unsigned int count = ruleSet.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
+	break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
 	(this+ruleSet[iter.get_coverage ()]).closure (c, lookup_context);
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     (this+coverage).add_coverage (c->input);
@@ -1486,7 +1487,7 @@
       (this+ruleSet[i]).collect_glyphs (c, lookup_context);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
 
@@ -1498,10 +1499,9 @@
     return_trace (rule_set.would_apply (c, lookup_context));
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
@@ -1516,14 +1516,14 @@
     return_trace (rule_set.apply (c, lookup_context));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
@@ -1544,7 +1544,7 @@
 
 struct ContextFormat2
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     if (!(this+coverage).intersects (glyphs))
       return false;
@@ -1560,12 +1560,12 @@
     for (unsigned int i = 0; i < count; i++)
       if (class_def.intersects_class (glyphs, i) &&
 	  (this+ruleSet[i]).intersects (glyphs, lookup_context))
-        return true;
+	return true;
 
     return false;
   }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     if (!(this+coverage).intersects (c->glyphs))
@@ -1586,7 +1586,7 @@
       }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     (this+coverage).add_coverage (c->input);
@@ -1602,7 +1602,7 @@
       (this+ruleSet[i]).collect_glyphs (c, lookup_context);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
 
@@ -1616,10 +1616,9 @@
     return_trace (rule_set.would_apply (c, lookup_context));
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
@@ -1635,14 +1634,14 @@
     return_trace (rule_set.apply (c, lookup_context));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this));
@@ -1666,7 +1665,7 @@
 
 struct ContextFormat3
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     if (!(this+coverageZ[0]).intersects (glyphs))
       return false;
@@ -1680,13 +1679,13 @@
 			       lookup_context);
   }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     if (!(this+coverageZ[0]).intersects (c->glyphs))
       return;
 
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ.arrayZ, coverageZ[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
     struct ContextClosureLookupContext lookup_context = {
       {intersects_coverage},
       this
@@ -1697,12 +1696,12 @@
 			    lookup_context);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     (this+coverageZ[0]).add_coverage (c->input);
 
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ.arrayZ, coverageZ[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
     struct ContextCollectGlyphsLookupContext lookup_context = {
       {collect_coverage},
       this
@@ -1714,11 +1713,11 @@
 				   lookup_context);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
 
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ.arrayZ, coverageZ[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
     struct ContextApplyLookupContext lookup_context = {
       {match_coverage},
       this
@@ -1726,16 +1725,15 @@
     return_trace (context_would_apply_lookup (c, glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), lookupCount, lookupRecord, lookup_context));
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverageZ[0]; }
+  const Coverage &get_coverage () const { return this+coverageZ[0]; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ.arrayZ, coverageZ[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
     struct ContextApplyLookupContext lookup_context = {
       {match_coverage},
       this
@@ -1743,14 +1741,14 @@
     return_trace (context_apply_lookup (c, glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), lookupCount, lookupRecord, lookup_context));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!c->check_struct (this)) return_trace (false);
@@ -1759,7 +1757,7 @@
     if (!c->check_array (coverageZ.arrayZ, count)) return_trace (false);
     for (unsigned int i = 0; i < count; i++)
       if (!coverageZ[i].sanitize (c, this)) return_trace (false);
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ.arrayZ, coverageZ[0].static_size * count);
+    const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
     return_trace (c->check_array (lookupRecord, lookupCount));
   }
 
@@ -1781,7 +1779,7 @@
 struct Context
 {
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -1864,15 +1862,15 @@
 }
 
 static inline void chain_context_collect_glyphs_lookup (hb_collect_glyphs_context_t *c,
-						        unsigned int backtrackCount,
-						        const HBUINT16 backtrack[],
-						        unsigned int inputCount, /* Including the first glyph (not matched) */
-						        const HBUINT16 input[], /* Array of input values--start with second glyph */
-						        unsigned int lookaheadCount,
-						        const HBUINT16 lookahead[],
-						        unsigned int lookupCount,
-						        const LookupRecord lookupRecord[],
-						        ChainContextCollectGlyphsLookupContext &lookup_context)
+							unsigned int backtrackCount,
+							const HBUINT16 backtrack[],
+							unsigned int inputCount, /* Including the first glyph (not matched) */
+							const HBUINT16 input[], /* Array of input values--start with second glyph */
+							unsigned int lookaheadCount,
+							const HBUINT16 lookahead[],
+							unsigned int lookupCount,
+							const LookupRecord lookupRecord[],
+							ChainContextCollectGlyphsLookupContext &lookup_context)
 {
   collect_array (c, c->before,
 		 backtrackCount, backtrack,
@@ -1930,15 +1928,15 @@
 			  lookup_context.funcs.match, lookup_context.match_data[2],
 			  match_length, &end_index)
       && (c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index),
-          apply_lookup (c,
-		       inputCount, match_positions,
-		       lookupCount, lookupRecord,
-		       match_length));
+	  apply_lookup (c,
+			inputCount, match_positions,
+			lookupCount, lookupRecord,
+			match_length));
 }
 
 struct ChainRule
 {
-  inline bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const
+  bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const
   {
     const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16> > (backtrack);
     const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16> > (input);
@@ -1949,7 +1947,8 @@
 				     lookup_context);
   }
 
-  inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
+  void closure (hb_closure_context_t *c,
+		ChainContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
     const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16> > (backtrack);
@@ -1963,7 +1962,8 @@
 				  lookup_context);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c,
+		       ChainContextCollectGlyphsLookupContext &lookup_context) const
   {
     TRACE_COLLECT_GLYPHS (this);
     const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16> > (backtrack);
@@ -1977,7 +1977,8 @@
 					 lookup_context);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
+  bool would_apply (hb_would_apply_context_t *c,
+		    ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_WOULD_APPLY (this);
     const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16> > (backtrack);
@@ -1990,7 +1991,7 @@
 						    lookup.arrayZ, lookup_context));
   }
 
-  inline bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
+  bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
     const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16> > (backtrack);
@@ -2003,7 +2004,7 @@
 					      lookup.arrayZ, lookup_context));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!backtrack.sanitize (c)) return_trace (false);
@@ -2035,15 +2036,15 @@
 
 struct ChainRuleSet
 {
-  inline bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const
+  bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const
   {
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       if ((this+rule[i]).intersects (glyphs, lookup_context))
-        return true;
+	return true;
     return false;
   }
-  inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
+  void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
     unsigned int num_rules = rule.len;
@@ -2051,7 +2052,7 @@
       (this+rule[i]).closure (c, lookup_context);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const
   {
     TRACE_COLLECT_GLYPHS (this);
     unsigned int num_rules = rule.len;
@@ -2059,29 +2060,29 @@
       (this+rule[i]).collect_glyphs (c, lookup_context);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
+  bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_WOULD_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       if ((this+rule[i]).would_apply (c, lookup_context))
-        return_trace (true);
+	return_trace (true);
 
     return_trace (false);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
+  bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
       if ((this+rule[i]).apply (c, lookup_context))
-        return_trace (true);
+	return_trace (true);
 
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (rule.sanitize (c, this));
@@ -2097,7 +2098,7 @@
 
 struct ChainContextFormat1
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     struct ChainContextClosureLookupContext lookup_context = {
       {intersects_glyph},
@@ -2105,18 +2106,18 @@
     };
 
     unsigned int count = ruleSet.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
+	break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (glyphs->has (iter.get_glyph ()) &&
 	  (this+ruleSet[iter.get_coverage ()]).intersects (glyphs, lookup_context))
-        return true;
+	return true;
     }
     return false;
   }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
 
@@ -2126,16 +2127,16 @@
     };
 
     unsigned int count = ruleSet.len;
-    for (hb_auto_t<Coverage::Iter> iter (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
+	break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
 	(this+ruleSet[iter.get_coverage ()]).closure (c, lookup_context);
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     (this+coverage).add_coverage (c->input);
@@ -2150,7 +2151,7 @@
       (this+ruleSet[i]).collect_glyphs (c, lookup_context);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
 
@@ -2162,10 +2163,9 @@
     return_trace (rule_set.would_apply (c, lookup_context));
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
@@ -2179,14 +2179,14 @@
     return_trace (rule_set.apply (c, lookup_context));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
@@ -2206,7 +2206,7 @@
 
 struct ChainContextFormat2
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     if (!(this+coverage).intersects (glyphs))
       return false;
@@ -2226,11 +2226,11 @@
     for (unsigned int i = 0; i < count; i++)
       if (input_class_def.intersects_class (glyphs, i) &&
 	  (this+ruleSet[i]).intersects (glyphs, lookup_context))
-        return true;
+	return true;
 
     return false;
   }
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     if (!(this+coverage).intersects (c->glyphs))
@@ -2255,7 +2255,7 @@
       }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     (this+coverage).add_coverage (c->input);
@@ -2276,7 +2276,7 @@
       (this+ruleSet[i]).collect_glyphs (c, lookup_context);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
 
@@ -2295,10 +2295,9 @@
     return_trace (rule_set.would_apply (c, lookup_context));
   }
 
-  inline const Coverage &get_coverage (void) const
-  { return this+coverage; }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
@@ -2319,14 +2318,14 @@
     return_trace (rule_set.apply (c, lookup_context));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) &&
@@ -2362,7 +2361,7 @@
 
 struct ChainContextFormat3
 {
-  inline bool intersects (const hb_set_t *glyphs) const
+  bool intersects (const hb_set_t *glyphs) const
   {
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
 
@@ -2381,7 +2380,7 @@
 				     lookup_context);
   }
 
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
@@ -2403,7 +2402,7 @@
 				  lookup_context);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
@@ -2424,7 +2423,7 @@
 					 lookup_context);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
 
@@ -2442,13 +2441,13 @@
 						    lookup.len, lookup.arrayZ, lookup_context));
   }
 
-  inline const Coverage &get_coverage (void) const
+  const Coverage &get_coverage () const
   {
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     return this+input[0];
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
@@ -2469,14 +2468,14 @@
 					      lookup.len, lookup.arrayZ, lookup_context));
   }
 
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     // TODO(subset)
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!backtrack.sanitize (c, this)) return_trace (false);
@@ -2513,7 +2512,7 @@
 struct ChainContext
 {
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -2538,10 +2537,10 @@
 template <typename T>
 struct ExtensionFormat1
 {
-  inline unsigned int get_type (void) const { return extensionLookupType; }
+  unsigned int get_type () const { return extensionLookupType; }
 
   template <typename X>
-  inline const X& get_subtable (void) const
+  const X& get_subtable () const
   {
     unsigned int offset = extensionOffset;
     if (unlikely (!offset)) return Null(typename T::SubTable);
@@ -2549,7 +2548,7 @@
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, format);
     if (unlikely (!c->may_dispatch (this, this))) return_trace (c->no_dispatch_return_value ());
@@ -2557,7 +2556,7 @@
   }
 
   /* This is called from may_dispatch() above with hb_sanitize_context_t. */
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -2579,7 +2578,7 @@
 template <typename T>
 struct Extension
 {
-  inline unsigned int get_type (void) const
+  unsigned int get_type () const
   {
     switch (u.format) {
     case 1: return u.format1.get_type ();
@@ -2587,7 +2586,7 @@
     }
   }
   template <typename X>
-  inline const X& get_subtable (void) const
+  const X& get_subtable () const
   {
     switch (u.format) {
     case 1: return u.format1.template get_subtable<typename T::SubTable> ();
@@ -2596,7 +2595,7 @@
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -2621,7 +2620,7 @@
 struct hb_ot_layout_lookup_accelerator_t
 {
   template <typename TLookup>
-  inline void init (const TLookup &lookup)
+  void init (const TLookup &lookup)
   {
     digest.init ();
     lookup.add_coverage (&digest);
@@ -2630,20 +2629,17 @@
     OT::hb_get_subtables_context_t c_get_subtables (subtables);
     lookup.dispatch (&c_get_subtables);
   }
-  inline void fini (void)
-  {
-    subtables.fini ();
-  }
+  void fini () { subtables.fini (); }
 
-  inline bool may_have (hb_codepoint_t g) const
+  bool may_have (hb_codepoint_t g) const
   { return digest.may_have (g); }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
-     for (unsigned int i = 0; i < subtables.len; i++)
-       if (subtables[i].apply (c))
-         return true;
-     return false;
+    for (unsigned int i = 0; i < subtables.len; i++)
+      if (subtables[i].apply (c))
+	return true;
+    return false;
   }
 
   private:
@@ -2653,43 +2649,43 @@
 
 struct GSUBGPOS
 {
-  inline bool has_data (void) const { return version.to_int () != 0; }
-  inline unsigned int get_script_count (void) const
+  bool has_data () const { return version.to_int (); }
+  unsigned int get_script_count () const
   { return (this+scriptList).len; }
-  inline const Tag& get_script_tag (unsigned int i) const
+  const Tag& get_script_tag (unsigned int i) const
   { return (this+scriptList).get_tag (i); }
-  inline unsigned int get_script_tags (unsigned int start_offset,
-				       unsigned int *script_count /* IN/OUT */,
-				       hb_tag_t     *script_tags /* OUT */) const
+  unsigned int get_script_tags (unsigned int start_offset,
+				unsigned int *script_count /* IN/OUT */,
+				hb_tag_t     *script_tags /* OUT */) const
   { return (this+scriptList).get_tags (start_offset, script_count, script_tags); }
-  inline const Script& get_script (unsigned int i) const
+  const Script& get_script (unsigned int i) const
   { return (this+scriptList)[i]; }
-  inline bool find_script_index (hb_tag_t tag, unsigned int *index) const
+  bool find_script_index (hb_tag_t tag, unsigned int *index) const
   { return (this+scriptList).find_index (tag, index); }
 
-  inline unsigned int get_feature_count (void) const
+  unsigned int get_feature_count () const
   { return (this+featureList).len; }
-  inline hb_tag_t get_feature_tag (unsigned int i) const
+  hb_tag_t get_feature_tag (unsigned int i) const
   { return i == Index::NOT_FOUND_INDEX ? HB_TAG_NONE : (this+featureList).get_tag (i); }
-  inline unsigned int get_feature_tags (unsigned int start_offset,
-					unsigned int *feature_count /* IN/OUT */,
-					hb_tag_t     *feature_tags /* OUT */) const
+  unsigned int get_feature_tags (unsigned int start_offset,
+				 unsigned int *feature_count /* IN/OUT */,
+				 hb_tag_t     *feature_tags /* OUT */) const
   { return (this+featureList).get_tags (start_offset, feature_count, feature_tags); }
-  inline const Feature& get_feature (unsigned int i) const
+  const Feature& get_feature (unsigned int i) const
   { return (this+featureList)[i]; }
-  inline bool find_feature_index (hb_tag_t tag, unsigned int *index) const
+  bool find_feature_index (hb_tag_t tag, unsigned int *index) const
   { return (this+featureList).find_index (tag, index); }
 
-  inline unsigned int get_lookup_count (void) const
+  unsigned int get_lookup_count () const
   { return (this+lookupList).len; }
-  inline const Lookup& get_lookup (unsigned int i) const
+  const Lookup& get_lookup (unsigned int i) const
   { return (this+lookupList)[i]; }
 
-  inline bool find_variations_index (const int *coords, unsigned int num_coords,
-				     unsigned int *index) const
+  bool find_variations_index (const int *coords, unsigned int num_coords,
+			      unsigned int *index) const
   { return (version.to_int () >= 0x00010001u ? this+featureVars : Null(FeatureVariations))
 	   .find_index (coords, num_coords, index); }
-  inline const Feature& get_feature_variation (unsigned int feature_index,
+  const Feature& get_feature_variation (unsigned int feature_index,
 					       unsigned int variations_index) const
   {
     if (FeatureVariations::NOT_FOUND_INDEX != variations_index &&
@@ -2698,17 +2694,18 @@
       const Feature *feature = (this+featureVars).find_substitute (variations_index,
 								   feature_index);
       if (feature)
-        return *feature;
+	return *feature;
     }
     return get_feature (feature_index);
   }
 
   template <typename TLookup>
-  inline bool subset (hb_subset_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
     struct GSUBGPOS *out = c->serializer->embed (*this);
     if (unlikely (!out)) return_trace (false);
+
     out->scriptList.serialize_subset (c, this+scriptList, out);
     out->featureList.serialize_subset (c, this+featureList, out);
 
@@ -2721,17 +2718,18 @@
 
     if (version.to_int () >= 0x00010001u)
      out->featureVars.serialize_subset (c, this+featureVars, out);
+
     return_trace (true);
   }
 
-  inline unsigned int get_size (void) const
+  unsigned int get_size () const
   {
     return min_size +
 	   (version.to_int () >= 0x00010001u ? featureVars.static_size : 0);
   }
 
   template <typename TLookup>
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     typedef OffsetListOf<TLookup> TLookupList;
@@ -2746,31 +2744,34 @@
   template <typename T>
   struct accelerator_t
   {
-    inline void init (hb_face_t *face)
+    void init (hb_face_t *face)
     {
-      this->blob = hb_sanitize_context_t().reference_table<T> (face);
-      table = this->blob->template as<T> ();
+      this->table = hb_sanitize_context_t().reference_table<T> (face);
+      if (unlikely (this->table->is_blacklisted (this->table.get_blob (), face)))
+      {
+	hb_blob_destroy (this->table.get_blob ());
+	this->table = hb_blob_get_empty ();
+      }
 
       this->lookup_count = table->get_lookup_count ();
 
       this->accels = (hb_ot_layout_lookup_accelerator_t *) calloc (this->lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t));
       if (unlikely (!this->accels))
-        this->lookup_count = 0;
+	this->lookup_count = 0;
 
       for (unsigned int i = 0; i < this->lookup_count; i++)
 	this->accels[i].init (table->get_lookup (i));
     }
 
-    inline void fini (void)
+    void fini ()
     {
       for (unsigned int i = 0; i < this->lookup_count; i++)
 	this->accels[i].fini ();
       free (this->accels);
-      hb_blob_destroy (this->blob);
+      this->table.destroy ();
     }
 
-    hb_blob_t *blob;
-    const T *table;
+    hb_blob_ptr_t<T> table;
     unsigned int lookup_count;
     hb_ot_layout_lookup_accelerator_t *accels;
   };
diff --git a/src/hb-ot-layout-jstf-table.hh b/src/hb-ot-layout-jstf-table.hh
index 2fb23cb..3beedc4 100644
--- a/src/hb-ot-layout-jstf-table.hh
+++ b/src/hb-ot-layout-jstf-table.hh
@@ -54,7 +54,7 @@
 
 struct JstfPriority
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -123,8 +123,8 @@
 
 struct JstfLangSys : OffsetListOf<JstfPriority>
 {
-  inline bool sanitize (hb_sanitize_context_t *c,
-			const Record_sanitize_closure_t * = nullptr) const
+  bool sanitize (hb_sanitize_context_t *c,
+		 const Record_sanitize_closure_t * = nullptr) const
   {
     TRACE_SANITIZE (this);
     return_trace (OffsetListOf<JstfPriority>::sanitize (c));
@@ -145,27 +145,27 @@
 
 struct JstfScript
 {
-  inline unsigned int get_lang_sys_count (void) const
+  unsigned int get_lang_sys_count () const
   { return langSys.len; }
-  inline const Tag& get_lang_sys_tag (unsigned int i) const
+  const Tag& get_lang_sys_tag (unsigned int i) const
   { return langSys.get_tag (i); }
-  inline unsigned int get_lang_sys_tags (unsigned int start_offset,
-					 unsigned int *lang_sys_count /* IN/OUT */,
-					 hb_tag_t     *lang_sys_tags /* OUT */) const
+  unsigned int get_lang_sys_tags (unsigned int start_offset,
+				  unsigned int *lang_sys_count /* IN/OUT */,
+				  hb_tag_t     *lang_sys_tags /* OUT */) const
   { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); }
-  inline const JstfLangSys& get_lang_sys (unsigned int i) const
+  const JstfLangSys& get_lang_sys (unsigned int i) const
   {
     if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys ();
     return this+langSys[i].offset;
   }
-  inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const
+  bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const
   { return langSys.find_index (tag, index); }
 
-  inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; }
-  inline const JstfLangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
+  bool has_default_lang_sys () const               { return defaultLangSys != 0; }
+  const JstfLangSys& get_default_lang_sys () const { return this+defaultLangSys; }
 
-  inline bool sanitize (hb_sanitize_context_t *c,
-			const Record_sanitize_closure_t * = nullptr) const
+  bool sanitize (hb_sanitize_context_t *c,
+		 const Record_sanitize_closure_t * = nullptr) const
   {
     TRACE_SANITIZE (this);
     return_trace (extenderGlyphs.sanitize (c, this) &&
@@ -195,22 +195,22 @@
 
 struct JSTF
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_JSTF;
+  enum { tableTag = HB_OT_TAG_JSTF };
 
-  inline unsigned int get_script_count (void) const
+  unsigned int get_script_count () const
   { return scriptList.len; }
-  inline const Tag& get_script_tag (unsigned int i) const
+  const Tag& get_script_tag (unsigned int i) const
   { return scriptList.get_tag (i); }
-  inline unsigned int get_script_tags (unsigned int start_offset,
-				       unsigned int *script_count /* IN/OUT */,
-				       hb_tag_t     *script_tags /* OUT */) const
+  unsigned int get_script_tags (unsigned int start_offset,
+				unsigned int *script_count /* IN/OUT */,
+				hb_tag_t     *script_tags /* OUT */) const
   { return scriptList.get_tags (start_offset, script_count, script_tags); }
-  inline const JstfScript& get_script (unsigned int i) const
+  const JstfScript& get_script (unsigned int i) const
   { return this+scriptList[i].offset; }
-  inline bool find_script_index (hb_tag_t tag, unsigned int *index) const
+  bool find_script_index (hb_tag_t tag, unsigned int *index) const
   { return scriptList.find_index (tag, index); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index fb1d9b1..67ce923 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -34,77 +34,63 @@
 #include "hb-ot-map.hh"
 #include "hb-map.hh"
 
+#include "hb-ot-kern-table.hh"
+#include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
-
-// Just so we compile them; unused otherwise:
-#include "hb-ot-layout-base-table.hh"
-#include "hb-ot-layout-jstf-table.hh"
-#include "hb-ot-color-colr-table.hh"
-#include "hb-ot-color-cpal-table.hh"
-#include "hb-ot-color-sbix-table.hh"
-#include "hb-ot-color-svg-table.hh"
-#include "hb-ot-kern-table.hh"
+#include "hb-ot-layout-base-table.hh" // Just so we compile it; unused otherwise.
+#include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-ot-name-table.hh"
+#include "hb-ot-os2-table.hh"
+
+#include "hb-aat-layout-lcar-table.hh"
+#include "hb-aat-layout-morx-table.hh"
 
 
-static const OT::kern::accelerator_t& _get_kern (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::kern::accelerator_t);
-  return *hb_ot_face_data (face)->kern;
-}
-const OT::GDEF& _get_gdef (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GDEF);
-  return *hb_ot_face_data (face)->GDEF->table;
-}
-static hb_blob_t * _get_gsub_blob (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return hb_blob_get_empty ();
-  return hb_ot_face_data (face)->GSUB->blob;
-}
-static inline const OT::GSUB& _get_gsub (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GSUB);
-  return *hb_ot_face_data (face)->GSUB->table;
-}
-const OT::GSUB& _get_gsub_relaxed (hb_face_t *face)
-{
-  return *hb_ot_face_data (face)->GSUB.get_relaxed ()->table;
-}
-static hb_blob_t * _get_gpos_blob (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return hb_blob_get_empty ();
-  return hb_ot_face_data (face)->GPOS->blob;
-}
-static inline const OT::GPOS& _get_gpos (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::GPOS);
-  return *hb_ot_face_data (face)->GPOS->table;
-}
-const OT::GPOS& _get_gpos_relaxed (hb_face_t *face)
-{
-  return *hb_ot_face_data (face)->GPOS.get_relaxed ()->table;
-}
+/**
+ * SECTION:hb-ot-layout
+ * @title: hb-ot-layout
+ * @short_description: OpenType Layout
+ * @include: hb-ot.h
+ *
+ * Functions for querying OpenType Layout features in the font face.
+ **/
 
 
 /*
  * kern
  */
 
-hb_bool_t
+bool
 hb_ot_layout_has_kerning (hb_face_t *face)
 {
-  return _get_kern (face).has_data ();
+  return face->table.kern->has_data ();
+}
+
+bool
+hb_ot_layout_has_machine_kerning (hb_face_t *face)
+{
+  return face->table.kern->has_state_machine ();
+}
+
+bool
+hb_ot_layout_has_cross_kerning (hb_face_t *face)
+{
+  return face->table.kern->has_cross_stream ();
 }
 
 void
-hb_ot_layout_kern (hb_font_t *font,
-		   hb_buffer_t  *buffer,
-		   hb_mask_t kern_mask)
+hb_ot_layout_kern (const hb_ot_shape_plan_t *plan,
+		   hb_font_t *font,
+		   hb_buffer_t  *buffer)
 {
-  _get_kern (font->face).apply (font, buffer, kern_mask);
+  hb_blob_t *blob = font->face->table.kern.get_blob ();
+  const AAT::kern& kern = *blob->as<AAT::kern> ();
+
+  AAT::hb_aat_apply_context_t c (plan, font, buffer, blob);
+
+  kern.apply (&c);
 }
 
 
@@ -112,10 +98,9 @@
  * GDEF
  */
 
-static bool
-_hb_ot_blacklist_gdef (unsigned int gdef_len,
-		       unsigned int gsub_len,
-		       unsigned int gpos_len)
+bool
+OT::GDEF::is_blacklisted (hb_blob_t *blob,
+			  hb_face_t *face) const
 {
   /* The ugly business of blacklisting individual fonts' tables happen here!
    * See this thread for why we finally had to bend in and do this:
@@ -134,8 +119,10 @@
    *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279693
    *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279875
    */
-#define ENCODE(x,y,z) ((int64_t) (x) << 32 | (int64_t) (y) << 16 | (z))
-  switch ENCODE(gdef_len, gsub_len, gpos_len)
+#define ENCODE(x,y,z) (((uint64_t) (x) << 48) | ((uint64_t) (y) << 24) | (uint64_t) (z))
+  switch ENCODE(blob->length,
+		face->table.GSUB->table.get_length (),
+		face->table.GPOS->table.get_length ())
   {
     /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */
     case ENCODE (442, 2874, 42038):
@@ -214,29 +201,13 @@
   return false;
 }
 
-inline void
-OT::GDEF::accelerator_t::init (hb_face_t *face)
-{
-  this->blob = hb_sanitize_context_t().reference_table<GDEF> (face);
-
-  if (unlikely (_hb_ot_blacklist_gdef (this->blob->length,
-				       _get_gsub_blob (face)->length,
-				       _get_gpos_blob (face)->length)))
-  {
-    hb_blob_destroy (this->blob);
-    this->blob = hb_blob_get_empty ();
-  }
-
-  table = this->blob->as<GDEF> ();
-}
-
 static void
 _hb_ot_layout_set_glyph_props (hb_font_t *font,
 			       hb_buffer_t *buffer)
 {
   _hb_buffer_assert_gsubgpos_vars (buffer);
 
-  const OT::GDEF &gdef = _get_gdef (font->face);
+  const OT::GDEF &gdef = *font->face->table.GDEF->table;
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
   {
@@ -251,7 +222,7 @@
 hb_bool_t
 hb_ot_layout_has_glyph_classes (hb_face_t *face)
 {
-  return _get_gdef (face).has_glyph_classes ();
+  return face->table.GDEF->table->has_glyph_classes ();
 }
 
 /**
@@ -263,7 +234,7 @@
 hb_ot_layout_get_glyph_class (hb_face_t      *face,
 			      hb_codepoint_t  glyph)
 {
-  return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
+  return (hb_ot_layout_glyph_class_t) face->table.GDEF->table->get_glyph_class (glyph);
 }
 
 /**
@@ -276,7 +247,7 @@
 				  hb_ot_layout_glyph_class_t  klass,
 				  hb_set_t                   *glyphs /* OUT */)
 {
-  return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
+  return face->table.GDEF->table->get_glyphs_in_class (klass, glyphs);
 }
 
 unsigned int
@@ -286,7 +257,10 @@
 				unsigned int   *point_count /* IN/OUT */,
 				unsigned int   *point_array /* OUT */)
 {
-  return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
+  return face->table.GDEF->table->get_attach_points (glyph,
+						     start_offset,
+						     point_count,
+						     point_array);
 }
 
 unsigned int
@@ -297,7 +271,15 @@
 				  unsigned int   *caret_count /* IN/OUT */,
 				  hb_position_t  *caret_array /* OUT */)
 {
-  return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
+  unsigned int result_caret_count = 0;
+  unsigned int result = font->face->table.GDEF->table->get_lig_carets (font, direction, glyph, start_offset, &result_caret_count, caret_array);
+  if (result)
+  {
+    if (caret_count) *caret_count = result_caret_count;
+  }
+  else
+    result = font->face->table.lcar->get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
+  return result;
 }
 
 
@@ -305,13 +287,45 @@
  * GSUB/GPOS
  */
 
+bool
+OT::GSUB::is_blacklisted (hb_blob_t *blob HB_UNUSED,
+			  hb_face_t *face) const
+{
+  /* Mac OS X prefers morx over GSUB.  It also ships with various Indic fonts,
+   * all by 'MUTF' foundry (Tamil MN, Tamil Sangam MN, etc.), that have broken
+   * GSUB/GPOS tables.  Some have GSUB with zero scripts, those are ignored by
+   * our morx/GSUB preference code.  But if GSUB has non-zero scripts, we tend
+   * to prefer it over morx because we want to be consistent with other OpenType
+   * shapers.
+   *
+   * To work around broken Indic Mac system fonts, we ignore GSUB table if
+   * OS/2 VendorId is 'MUTF' and font has morx table as well.
+   *
+   * https://github.com/harfbuzz/harfbuzz/issues/1410
+   * https://github.com/harfbuzz/harfbuzz/issues/1348
+   * https://github.com/harfbuzz/harfbuzz/issues/1391
+   */
+  if (unlikely (face->table.OS2->achVendID == HB_TAG ('M','U','T','F') &&
+		face->table.morx->has_data ()))
+    return true;
+
+  return false;
+}
+
+bool
+OT::GPOS::is_blacklisted (hb_blob_t *blob HB_UNUSED,
+			  hb_face_t *face HB_UNUSED) const
+{
+  return false;
+}
+
 static const OT::GSUBGPOS&
 get_gsubgpos_table (hb_face_t *face,
 		    hb_tag_t   table_tag)
 {
   switch (table_tag) {
-    case HB_OT_TAG_GSUB: return _get_gsub (face);
-    case HB_OT_TAG_GPOS: return _get_gpos (face);
+    case HB_OT_TAG_GSUB: return *face->table.GSUB->table;
+    case HB_OT_TAG_GPOS: return *face->table.GPOS->table;
     default:             return Null(OT::GSUBGPOS);
   }
 }
@@ -440,7 +454,7 @@
   return g.get_feature_tags (start_offset, feature_count, feature_tags);
 }
 
-hb_bool_t
+bool
 hb_ot_layout_table_find_feature (hb_face_t    *face,
 				 hb_tag_t      table_tag,
 				 hb_tag_t      feature_tag,
@@ -483,7 +497,12 @@
 				   hb_tag_t      language_tag,
 				   unsigned int *language_index)
 {
-  return hb_ot_layout_script_select_language (face, table_tag, script_index, 1, &language_tag, language_index);
+  return hb_ot_layout_script_select_language (face,
+					      table_tag,
+					      script_index,
+					      1,
+					      &language_tag,
+					      language_index);
 }
 
 /**
@@ -555,19 +574,6 @@
   return l.has_required_feature ();
 }
 
-static void
-_hb_ot_layout_language_add_feature_indexes_to (hb_face_t    *face,
-                                               hb_tag_t      table_tag,
-                                               unsigned int  script_index,
-                                               unsigned int  language_index,
-                                               hb_set_t     *feature_indexes /* OUT */)
-{
-  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
-  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
-  l.add_feature_indexes_to (feature_indexes);
-}
-
-
 unsigned int
 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
 					   hb_tag_t      table_tag,
@@ -665,135 +671,136 @@
 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
 				     hb_tag_t      table_tag)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return 0;
-  switch (table_tag)
+  return get_gsubgpos_table (face, table_tag).get_lookup_count ();
+}
+
+
+struct hb_collect_features_context_t
+{
+  hb_collect_features_context_t (hb_face_t       *face,
+				 hb_tag_t         table_tag,
+				 hb_set_t        *feature_indexes_)
+    : g (get_gsubgpos_table (face, table_tag)),
+      feature_indexes (feature_indexes_),
+      script_count(0),langsys_count(0) {}
+
+  bool inline visited (const OT::Script &s)
   {
-    case HB_OT_TAG_GSUB:
-    {
-      return hb_ot_face_data (face)->GSUB->lookup_count;
-    }
-    case HB_OT_TAG_GPOS:
-    {
-      return hb_ot_face_data (face)->GPOS->lookup_count;
-    }
+    /* We might have Null() object here.  Don't want to involve
+     * that in the memoize.  So, detect empty objects and return. */
+    if (unlikely (!s.has_default_lang_sys () &&
+		  !s.get_lang_sys_count ()))
+      return true;
+
+    if (script_count++ > HB_MAX_SCRIPTS)
+      return true;
+
+    return visited (s, visited_script);
   }
-  return 0;
-}
+  bool inline visited (const OT::LangSys &l)
+  {
+    /* We might have Null() object here.  Don't want to involve
+     * that in the memoize.  So, detect empty objects and return. */
+    if (unlikely (!l.has_required_feature () &&
+		  !l.get_feature_count ()))
+      return true;
+
+    if (langsys_count++ > HB_MAX_LANGSYS)
+      return true;
+
+    return visited (l, visited_langsys);
+  }
+
+  private:
+  template <typename T>
+  bool inline visited (const T &p, hb_set_t &visited_set)
+  {
+    hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) &p - (uintptr_t) &g);
+     if (visited_set.has (delta))
+      return true;
+
+    visited_set.add (delta);
+    return false;
+  }
+
+  public:
+  const OT::GSUBGPOS &g;
+  hb_set_t           *feature_indexes;
+
+  private:
+  hb_set_t visited_script;
+  hb_set_t visited_langsys;
+  unsigned int script_count;
+  unsigned int langsys_count;
+};
 
 static void
-_hb_ot_layout_collect_lookups_lookups (hb_face_t      *face,
-				       hb_tag_t        table_tag,
-				       unsigned int    feature_index,
-				       hb_set_t       *lookup_indexes /* OUT */)
+langsys_collect_features (hb_collect_features_context_t *c,
+			  const OT::LangSys  &l,
+			  const hb_tag_t     *features)
 {
-  unsigned int lookup_indices[32];
-  unsigned int offset, len;
+  if (c->visited (l)) return;
 
-  offset = 0;
-  do {
-    len = ARRAY_LENGTH (lookup_indices);
-    hb_ot_layout_feature_get_lookups (face,
-				      table_tag,
-				      feature_index,
-				      offset, &len,
-				      lookup_indices);
-
-    for (unsigned int i = 0; i < len; i++)
-      lookup_indexes->add (lookup_indices[i]);
-
-    offset += len;
-  } while (len == ARRAY_LENGTH (lookup_indices));
-}
-
-static void
-_hb_ot_layout_collect_features_features (hb_face_t      *face,
-                                         hb_tag_t        table_tag,
-                                         unsigned int    script_index,
-                                         unsigned int    language_index,
-                                         const hb_tag_t *features,
-                                         hb_set_t       *feature_indexes /* OUT */)
-{
   if (!features)
   {
-    unsigned int required_feature_index;
-    if (hb_ot_layout_language_get_required_feature (face,
-						    table_tag,
-						    script_index,
-						    language_index,
-						    &required_feature_index,
-						    nullptr))
-      feature_indexes->add (required_feature_index);
+    /* All features. */
+    if (l.has_required_feature ())
+      c->feature_indexes->add (l.get_required_feature_index ());
 
-    /* All features */
-    _hb_ot_layout_language_add_feature_indexes_to (face,
-                                                   table_tag,
-                                                   script_index,
-                                                   language_index,
-                                                   feature_indexes);
+    l.add_feature_indexes_to (c->feature_indexes);
   }
   else
   {
+    /* Ugh. Any faster way? */
     for (; *features; features++)
     {
-      unsigned int feature_index;
-      if (hb_ot_layout_language_find_feature (face,
-					      table_tag,
-					      script_index,
-					      language_index,
-					      *features,
-					      &feature_index))
-        feature_indexes->add (feature_index);
+      hb_tag_t feature_tag = *features;
+      unsigned int num_features = l.get_feature_count ();
+      for (unsigned int i = 0; i < num_features; i++)
+      {
+	unsigned int feature_index = l.get_feature_index (i);
+
+	if (feature_tag == c->g.get_feature_tag (feature_index))
+	{
+	  c->feature_indexes->add (feature_index);
+	  break;
+	}
+      }
     }
   }
 }
 
 static void
-_hb_ot_layout_collect_features_languages (hb_face_t      *face,
-                                          hb_tag_t        table_tag,
-                                          unsigned int    script_index,
-                                          const hb_tag_t *languages,
-                                          const hb_tag_t *features,
-                                          hb_set_t       *feature_indexes /* OUT */)
+script_collect_features (hb_collect_features_context_t *c,
+			 const OT::Script   &s,
+			 const hb_tag_t *languages,
+			 const hb_tag_t *features)
 {
-  _hb_ot_layout_collect_features_features (face,
-                                           table_tag,
-                                           script_index,
-                                           HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
-                                           features,
-                                           feature_indexes);
+  if (c->visited (s)) return;
 
   if (!languages)
   {
-    /* All languages */
-    unsigned int count = hb_ot_layout_script_get_language_tags (face,
-								table_tag,
-								script_index,
-								0, nullptr, nullptr);
+    /* All languages. */
+    if (s.has_default_lang_sys ())
+      langsys_collect_features (c,
+				s.get_default_lang_sys (),
+				features);
+
+    unsigned int count = s.get_lang_sys_count ();
     for (unsigned int language_index = 0; language_index < count; language_index++)
-      _hb_ot_layout_collect_features_features (face,
-                                               table_tag,
-                                               script_index,
-                                               language_index,
-                                               features,
-                                               feature_indexes);
+      langsys_collect_features (c,
+				s.get_lang_sys (language_index),
+				features);
   }
   else
   {
     for (; *languages; languages++)
     {
       unsigned int language_index;
-      if (hb_ot_layout_script_select_language (face,
-					       table_tag,
-					       script_index,
-					       1,
-					       languages,
-					       &language_index))
-        _hb_ot_layout_collect_features_features (face,
-                                                 table_tag,
-                                                 script_index,
-                                                 language_index,
-                                                 features,
-                                                 feature_indexes);
+      if (s.find_lang_sys_index (*languages, &language_index))
+	langsys_collect_features (c,
+				  s.get_lang_sys (language_index),
+				  features);
     }
   }
 }
@@ -811,35 +818,27 @@
                                const hb_tag_t *features,
                                hb_set_t       *feature_indexes /* OUT */)
 {
+  hb_collect_features_context_t c (face, table_tag, feature_indexes);
   if (!scripts)
   {
-    /* All scripts */
-    unsigned int count = hb_ot_layout_table_get_script_tags (face,
-							     table_tag,
-							     0, nullptr, nullptr);
+    /* All scripts. */
+    unsigned int count = c.g.get_script_count ();
     for (unsigned int script_index = 0; script_index < count; script_index++)
-      _hb_ot_layout_collect_features_languages (face,
-                                                table_tag,
-                                                script_index,
-                                                languages,
-                                                features,
-                                                feature_indexes);
+      script_collect_features (&c,
+			       c.g.get_script (script_index),
+			       languages,
+			       features);
   }
   else
   {
     for (; *scripts; scripts++)
     {
       unsigned int script_index;
-      if (hb_ot_layout_table_find_script (face,
-					  table_tag,
-					  *scripts,
-					  &script_index))
-        _hb_ot_layout_collect_features_languages (face,
-                                                  table_tag,
-                                                  script_index,
-                                                  languages,
-                                                  features,
-                                                  feature_indexes);
+      if (c.g.find_script_index (*scripts, &script_index))
+	script_collect_features (&c,
+				 c.g.get_script (script_index),
+				 languages,
+				 features);
     }
   }
 }
@@ -857,10 +856,14 @@
 			      const hb_tag_t *features,
 			      hb_set_t       *lookup_indexes /* OUT */)
 {
-  hb_auto_t<hb_set_t> feature_indexes;
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+
+  hb_set_t feature_indexes;
   hb_ot_layout_collect_features (face, table_tag, scripts, languages, features, &feature_indexes);
-  for (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID; hb_set_next (&feature_indexes, &feature_index);)
-    _hb_ot_layout_collect_lookups_lookups (face, table_tag, feature_index, lookup_indexes);
+
+  for (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID;
+       hb_set_next (&feature_indexes, &feature_index);)
+    g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes);
 }
 
 /**
@@ -877,8 +880,6 @@
 				    hb_set_t     *glyphs_after,  /* OUT.  May be NULL */
 				    hb_set_t     *glyphs_output  /* OUT.  May be NULL */)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
-
   OT::hb_collect_glyphs_context_t c (face,
 				     glyphs_before,
 				     glyphs_input,
@@ -889,13 +890,13 @@
   {
     case HB_OT_TAG_GSUB:
     {
-      const OT::SubstLookup& l = hb_ot_face_data (face)->GSUB->table->get_lookup (lookup_index);
+      const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
       l.collect_glyphs (&c);
       return;
     }
     case HB_OT_TAG_GPOS:
     {
-      const OT::PosLookup& l = hb_ot_face_data (face)->GPOS->table->get_lookup (lookup_index);
+      const OT::PosLookup& l = face->table.GPOS->table->get_lookup (lookup_index);
       l.collect_glyphs (&c);
       return;
     }
@@ -942,7 +943,7 @@
 hb_bool_t
 hb_ot_layout_has_substitution (hb_face_t *face)
 {
-  return _get_gsub (face).has_data ();
+  return face->table.GSUB->table->has_data ();
 }
 
 /**
@@ -957,23 +958,25 @@
 				      unsigned int          glyphs_length,
 				      hb_bool_t             zero_context)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
-  return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
+  return hb_ot_layout_lookup_would_substitute_fast (face,
+						    lookup_index,
+						    glyphs, glyphs_length,
+						    zero_context);
 }
 
-hb_bool_t
+bool
 hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
 					   unsigned int          lookup_index,
 					   const hb_codepoint_t *glyphs,
 					   unsigned int          glyphs_length,
-					   hb_bool_t             zero_context)
+					   bool                  zero_context)
 {
-  if (unlikely (lookup_index >= hb_ot_face_data (face)->GSUB->lookup_count)) return false;
+  if (unlikely (lookup_index >= face->table.GSUB->lookup_count)) return false;
   OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
 
-  const OT::SubstLookup& l = hb_ot_face_data (face)->GSUB->table->get_lookup (lookup_index);
+  const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
 
-  return l.would_apply (&c, &hb_ot_face_data (face)->GSUB->accels[lookup_index]);
+  return l.would_apply (&c, &face->table.GSUB->accels[lookup_index]);
 }
 
 void
@@ -983,6 +986,56 @@
 _hb_ot_layout_set_glyph_props (font, buffer);
 }
 
+void
+hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer,
+				    bool (*filter) (const hb_glyph_info_t *info))
+{
+  /* Merge clusters and delete filtered glyphs.
+   * NOTE! We can't use out-buffer as we have positioning data. */
+  unsigned int j = 0;
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  hb_glyph_position_t *pos = buffer->pos;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    if (filter (&info[i]))
+    {
+      /* Merge clusters.
+       * Same logic as buffer->delete_glyph(), but for in-place removal. */
+
+      unsigned int cluster = info[i].cluster;
+      if (i + 1 < count && cluster == info[i + 1].cluster)
+	continue; /* Cluster survives; do nothing. */
+
+      if (j)
+      {
+	/* Merge cluster backward. */
+	if (cluster < info[j - 1].cluster)
+	{
+	  unsigned int mask = info[i].mask;
+	  unsigned int old_cluster = info[j - 1].cluster;
+	  for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--)
+	    buffer->set_cluster (info[k - 1], cluster, mask);
+	}
+	continue;
+      }
+
+      if (i + 1 < count)
+	buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */
+
+      continue;
+    }
+
+    if (j != i)
+    {
+      info[j] = info[i];
+      pos[j] = pos[i];
+    }
+    j++;
+  }
+  buffer->len = j;
+}
+
 /**
  * hb_ot_layout_lookup_substitute_closure:
  *
@@ -993,10 +1046,10 @@
 				        unsigned int  lookup_index,
 				        hb_set_t     *glyphs)
 {
-  hb_auto_t<hb_map_t> done_lookups;
+  hb_map_t done_lookups;
   OT::hb_closure_context_t c (face, glyphs, &done_lookups);
 
-  const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
+  const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
 
   l.closure (&c, lookup_index);
 }
@@ -1014,9 +1067,9 @@
                                          const hb_set_t *lookups,
                                          hb_set_t       *glyphs)
 {
-  hb_auto_t<hb_map_t> done_lookups;
+  hb_map_t done_lookups;
   OT::hb_closure_context_t c (face, glyphs, &done_lookups);
-  const OT::GSUB& gsub = _get_gsub (face);
+  const OT::GSUB& gsub = *face->table.GSUB->table;
 
   unsigned int iteration_count = 0;
   unsigned int glyphs_length;
@@ -1033,9 +1086,8 @@
       for (unsigned int i = 0; i < gsub.get_lookup_count (); i++)
         gsub.get_lookup (i).closure (&c, i);
     }
-    iteration_count++;
-  } while (iteration_count <= HB_CLOSURE_MAX_STAGES
-           && glyphs_length != glyphs->get_population ());
+  } while (iteration_count++ <= HB_CLOSURE_MAX_STAGES &&
+	   glyphs_length != glyphs->get_population ());
 }
 
 /*
@@ -1045,7 +1097,7 @@
 hb_bool_t
 hb_ot_layout_has_positioning (hb_face_t *face)
 {
-  return _get_gpos (face).has_data ();
+  return face->table.GPOS->table->has_data ();
 }
 
 void
@@ -1072,14 +1124,14 @@
  * Since: 0.9.10
  **/
 hb_bool_t
-hb_ot_layout_get_size_params (hb_face_t    *face,
-			      unsigned int *design_size,       /* OUT.  May be NULL */
-			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
-			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
-			      unsigned int *range_start,       /* OUT.  May be NULL */
-			      unsigned int *range_end          /* OUT.  May be NULL */)
+hb_ot_layout_get_size_params (hb_face_t       *face,
+			      unsigned int    *design_size,       /* OUT.  May be NULL */
+			      unsigned int    *subfamily_id,      /* OUT.  May be NULL */
+			      hb_ot_name_id_t *subfamily_name_id, /* OUT.  May be NULL */
+			      unsigned int    *range_start,       /* OUT.  May be NULL */
+			      unsigned int    *range_end          /* OUT.  May be NULL */)
 {
-  const OT::GPOS &gpos = _get_gpos (face);
+  const OT::GPOS &gpos = *face->table.GPOS->table;
   const hb_tag_t tag = HB_TAG ('s','i','z','e');
 
   unsigned int num_features = gpos.get_feature_count ();
@@ -1105,7 +1157,7 @@
 
   if (design_size) *design_size = 0;
   if (subfamily_id) *subfamily_id = 0;
-  if (subfamily_name_id) *subfamily_name_id = 0;
+  if (subfamily_name_id) *subfamily_name_id = HB_OT_NAME_ID_INVALID;
   if (range_start) *range_start = 0;
   if (range_end) *range_end = 0;
 
@@ -1115,8 +1167,8 @@
 /**
  * hb_ot_layout_feature_get_name_ids:
  * @face: #hb_face_t to work upon
- * @table_tag:
- * @feature_index:
+ * @table_tag: table tag to query, "GSUB" or "GPOS".
+ * @feature_index: index of feature to query.
  * @label_id: (out) (allow-none): The ‘name’ table name ID that specifies a string
  *            for a user-interface label for this feature. (May be NULL.)
  * @tooltip_id: (out) (allow-none): The ‘name’ table name ID that specifies a string
@@ -1137,14 +1189,14 @@
  * Since: 2.0.0
  **/
 hb_bool_t
-hb_ot_layout_feature_get_name_ids (hb_face_t    *face,
-				   hb_tag_t      table_tag,
-				   unsigned int  feature_index,
-				   hb_name_id_t *label_id,             /* OUT.  May be NULL */
-				   hb_name_id_t *tooltip_id,           /* OUT.  May be NULL */
-				   hb_name_id_t *sample_id,            /* OUT.  May be NULL */
-				   unsigned int *num_named_parameters, /* OUT.  May be NULL */
-				   hb_name_id_t *first_param_id        /* OUT.  May be NULL */)
+hb_ot_layout_feature_get_name_ids (hb_face_t       *face,
+				   hb_tag_t         table_tag,
+				   unsigned int     feature_index,
+				   hb_ot_name_id_t *label_id,             /* OUT.  May be NULL */
+				   hb_ot_name_id_t *tooltip_id,           /* OUT.  May be NULL */
+				   hb_ot_name_id_t *sample_id,            /* OUT.  May be NULL */
+				   unsigned int    *num_named_parameters, /* OUT.  May be NULL */
+				   hb_ot_name_id_t *first_param_id        /* OUT.  May be NULL */)
 {
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
@@ -1160,10 +1212,10 @@
     {
       if (label_id) *label_id = ss_params.uiNameID;
       // ssXX features don't have the rest
-      if (tooltip_id) *tooltip_id = HB_NAME_ID_INVALID;
-      if (sample_id) *sample_id = HB_NAME_ID_INVALID;
+      if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID;
+      if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID;
       if (num_named_parameters) *num_named_parameters = 0;
-      if (first_param_id) *first_param_id = HB_NAME_ID_INVALID;
+      if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID;
       return true;
     }
     const OT::FeatureParamsCharacterVariants& cv_params =
@@ -1179,19 +1231,19 @@
     }
   }
 
-  if (label_id) *label_id = HB_NAME_ID_INVALID;
-  if (tooltip_id) *tooltip_id = HB_NAME_ID_INVALID;
-  if (sample_id) *sample_id = HB_NAME_ID_INVALID;
+  if (label_id) *label_id = HB_OT_NAME_ID_INVALID;
+  if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID;
+  if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID;
   if (num_named_parameters) *num_named_parameters = 0;
-  if (first_param_id) *first_param_id = HB_NAME_ID_INVALID;
+  if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID;
   return false;
 }
 
 /**
- * hb_ot_layout_feature_get_characters::
+ * hb_ot_layout_feature_get_characters:
  * @face: #hb_face_t to work upon
- * @table_tag:
- * @feature_index:
+ * @table_tag: table tag to query, "GSUB" or "GPOS".
+ * @feature_index: index of feature to query.
  * @start_offset: In case the resulting char_count was equal to its input value, there
  *                is a chance there were more characters on the tag so this API can be
  *                called with an offset till resulting char_count gets to a number
@@ -1199,7 +1251,7 @@
  *                one shot copying).
  * @char_count: (inout) (allow-none): The count of characters for which this feature
  *              provides glyph variants. (May be zero.)
- * @characters: (out) (allow-none): A buffer pointer. The Unicode Scalar Value
+ * @characters: (out caller-allocates) (array length=char_count): A buffer pointer. The Unicode codepoints
  *              of the characters for which this feature provides glyph variants.
  *
  * Fetches characters listed by designer under feature parameters for "Character
@@ -1248,12 +1300,12 @@
 struct GSUBProxy
 {
   enum { table_index = 0 };
-  static const bool inplace = false;
+  enum { inplace = false };
   typedef OT::SubstLookup Lookup;
 
   GSUBProxy (hb_face_t *face) :
-    table (*hb_ot_face_data (face)->GSUB->table),
-    accels (hb_ot_face_data (face)->GSUB->accels) {}
+    table (*face->table.GSUB->table),
+    accels (face->table.GSUB->accels) {}
 
   const OT::GSUB &table;
   const OT::hb_ot_layout_lookup_accelerator_t *accels;
@@ -1262,12 +1314,12 @@
 struct GPOSProxy
 {
   enum { table_index = 1 };
-  static const bool inplace = true;
+  enum { inplace = true };
   typedef OT::PosLookup Lookup;
 
   GPOSProxy (hb_face_t *face) :
-    table (*hb_ot_face_data (face)->GPOS->table),
-    accels (hb_ot_face_data (face)->GPOS->accels) {}
+    table (*face->table.GPOS->table),
+    accels (face->table.GPOS->accels) {}
 
   const OT::GPOS &table;
   const OT::hb_ot_layout_lookup_accelerator_t *accels;
@@ -1309,10 +1361,8 @@
     if (accel.may_have (buffer->cur().codepoint) &&
 	(buffer->cur().mask & c->lookup_mask) &&
 	c->check_glyph_property (&buffer->cur(), c->lookup_props))
-    {
-     if (accel.apply (c))
-       ret = true;
-    }
+     ret |= accel.apply (c);
+
     /* The reverse lookup doesn't "advance" cursor (for good reason). */
     buffer->idx--;
 
@@ -1421,3 +1471,61 @@
 {
   apply_string<GSUBProxy> (c, lookup, accel);
 }
+
+#if 0
+static const OT::BASE& _get_base (hb_face_t *face)
+{
+  return *face->table.BASE;
+}
+
+hb_bool_t
+hb_ot_layout_get_baseline (hb_font_t               *font,
+			   hb_ot_layout_baseline_t  baseline,
+			   hb_direction_t           direction,
+			   hb_tag_t                 script_tag,
+			   hb_tag_t                 language_tag,
+			   hb_position_t           *coord        /* OUT.  May be NULL. */)
+{
+  const OT::BASE &base = _get_base (font->face);
+  bool result = base.get_baseline (font, baseline, direction, script_tag,
+				   language_tag, coord);
+
+  /* TODO: Simulate https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags#ideographic-em-box */
+  if (!result && coord) *coord = 0;
+
+  if (coord) *coord = font->em_scale_dir (*coord, direction);
+
+  return result;
+}
+
+/* To be moved to public header */
+/*
+ * BASE
+ */
+
+/**
+ * hb_ot_layout_baseline_t:
+ *
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags
+ *
+ * Since: DONTREPLACEME
+ */
+typedef enum {
+  HB_OT_LAYOUT_BASELINE_HANG = HB_TAG('h','a','n','g'),
+  HB_OT_LAYOUT_BASELINE_ICFB = HB_TAG('i','c','f','b'),
+  HB_OT_LAYOUT_BASELINE_ICFT = HB_TAG('i','c','f','t'),
+  HB_OT_LAYOUT_BASELINE_IDEO = HB_TAG('i','d','e','o'),
+  HB_OT_LAYOUT_BASELINE_IDTB = HB_TAG('i','d','t','b'),
+  HB_OT_LAYOUT_BASELINE_MATH = HB_TAG('m','a','t','h'),
+  HB_OT_LAYOUT_BASELINE_ROMN = HB_TAG('r','o','m','n')
+} hb_ot_layout_baseline_t;
+
+HB_EXTERN hb_bool_t
+hb_ot_layout_get_baseline (hb_font_t               *font,
+			   hb_ot_layout_baseline_t  baseline,
+			   hb_direction_t           direction,
+			   hb_tag_t                 script_tag,
+			   hb_tag_t                 language_tag,
+			   hb_position_t           *coord        /* OUT.  May be NULL. */);
+
+#endif
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index 9bd18c8..e473954 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -33,7 +33,6 @@
 
 #include "hb.h"
 
-#include "hb-ot-tag.h"
 #include "hb-ot-name.h"
 
 HB_BEGIN_DECLS
@@ -47,6 +46,47 @@
 
 
 /*
+ * Script & Language tags.
+ */
+
+#define HB_OT_TAG_DEFAULT_SCRIPT	HB_TAG ('D', 'F', 'L', 'T')
+#define HB_OT_TAG_DEFAULT_LANGUAGE	HB_TAG ('d', 'f', 'l', 't')
+
+/**
+ * HB_OT_MAX_TAGS_PER_SCRIPT:
+ *
+ * Since: 2.0.0
+ **/
+#define HB_OT_MAX_TAGS_PER_SCRIPT	3u
+/**
+ * HB_OT_MAX_TAGS_PER_LANGUAGE:
+ *
+ * Since: 2.0.0
+ **/
+#define HB_OT_MAX_TAGS_PER_LANGUAGE	3u
+
+HB_EXTERN void
+hb_ot_tags_from_script_and_language (hb_script_t   script,
+				     hb_language_t language,
+				     unsigned int *script_count /* IN/OUT */,
+				     hb_tag_t     *script_tags /* OUT */,
+				     unsigned int *language_count /* IN/OUT */,
+				     hb_tag_t     *language_tags /* OUT */);
+
+HB_EXTERN hb_script_t
+hb_ot_tag_to_script (hb_tag_t tag);
+
+HB_EXTERN hb_language_t
+hb_ot_tag_to_language (hb_tag_t tag);
+
+HB_EXTERN void
+hb_ot_tags_to_script_and_language (hb_tag_t       script_tag,
+				   hb_tag_t       language_tag,
+				   hb_script_t   *script /* OUT */,
+				   hb_language_t *language /* OUT */);
+
+
+/*
  * GDEF
  */
 
@@ -324,23 +364,23 @@
 /* Optical 'size' feature info.  Returns true if found.
  * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#size */
 HB_EXTERN hb_bool_t
-hb_ot_layout_get_size_params (hb_face_t    *face,
-			      unsigned int *design_size,       /* OUT.  May be NULL */
-			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
-			      hb_name_id_t *subfamily_name_id, /* OUT.  May be NULL */
-			      unsigned int *range_start,       /* OUT.  May be NULL */
-			      unsigned int *range_end          /* OUT.  May be NULL */);
+hb_ot_layout_get_size_params (hb_face_t       *face,
+			      unsigned int    *design_size,       /* OUT.  May be NULL */
+			      unsigned int    *subfamily_id,      /* OUT.  May be NULL */
+			      hb_ot_name_id_t *subfamily_name_id, /* OUT.  May be NULL */
+			      unsigned int    *range_start,       /* OUT.  May be NULL */
+			      unsigned int    *range_end          /* OUT.  May be NULL */);
 
 
 HB_EXTERN hb_bool_t
-hb_ot_layout_feature_get_name_ids (hb_face_t    *face,
-				   hb_tag_t      table_tag,
-				   unsigned int  feature_index,
-				   hb_name_id_t *label_id             /* OUT.  May be NULL */,
-				   hb_name_id_t *tooltip_id           /* OUT.  May be NULL */,
-				   hb_name_id_t *sample_id            /* OUT.  May be NULL */,
-				   unsigned int *num_named_parameters /* OUT.  May be NULL */,
-				   hb_name_id_t *first_param_id       /* OUT.  May be NULL */);
+hb_ot_layout_feature_get_name_ids (hb_face_t       *face,
+				   hb_tag_t         table_tag,
+				   unsigned int     feature_index,
+				   hb_ot_name_id_t *label_id             /* OUT.  May be NULL */,
+				   hb_ot_name_id_t *tooltip_id           /* OUT.  May be NULL */,
+				   hb_ot_name_id_t *sample_id            /* OUT.  May be NULL */,
+				   unsigned int    *num_named_parameters /* OUT.  May be NULL */,
+				   hb_ot_name_id_t *first_param_id       /* OUT.  May be NULL */);
 
 
 HB_EXTERN unsigned int
@@ -351,22 +391,6 @@
 				     unsigned int   *char_count    /* IN/OUT.  May be NULL */,
 				     hb_codepoint_t *characters    /* OUT.     May be NULL */);
 
-/*
- * BASE
- */
-#if 0
-
-#define HB_OT_TAG_BASE_HANG HB_TAG('h','a','n','g')
-#define HB_OT_TAG_BASE_ICFB HB_TAG('i','c','f','b')
-#define HB_OT_TAG_BASE_ICFT HB_TAG('i','c','f','t')
-#define HB_OT_TAG_BASE_IDEO HB_TAG('i','d','e','o')
-#define HB_OT_TAG_BASE_IDTB HB_TAG('i','d','t','b')
-#define HB_OT_TAG_BASE_MATH HB_TAG('m','a','t','h')
-#define HB_OT_TAG_BASE_ROMN HB_TAG('r','o','m','n')
-
-#endif
-
-
 HB_END_DECLS
 
 #endif /* HB_OT_LAYOUT_H */
diff --git a/src/hb-ot-layout.hh b/src/hb-ot-layout.hh
index 64b3d74..a00b940 100644
--- a/src/hb-ot-layout.hh
+++ b/src/hb-ot-layout.hh
@@ -34,37 +34,35 @@
 #include "hb-font.hh"
 #include "hb-buffer.hh"
 #include "hb-open-type.hh"
+#include "hb-ot-shape.hh"
 #include "hb-set-digest.hh"
 
 
-namespace OT
-{
-  struct GDEF;
-  struct GSUB;
-  struct GPOS;
-}
-
-HB_INTERNAL const OT::GDEF& _get_gdef (hb_face_t *face);
-HB_INTERNAL const OT::GSUB& _get_gsub_relaxed (hb_face_t *face);
-HB_INTERNAL const OT::GPOS& _get_gpos_relaxed (hb_face_t *face);
+struct hb_ot_shape_plan_t;
 
 
 /*
  * kern
  */
 
-HB_INTERNAL hb_bool_t
+HB_INTERNAL bool
 hb_ot_layout_has_kerning (hb_face_t *face);
 
+HB_INTERNAL bool
+hb_ot_layout_has_machine_kerning (hb_face_t *face);
+
+HB_INTERNAL bool
+hb_ot_layout_has_cross_kerning (hb_face_t *face);
+
 HB_INTERNAL void
-hb_ot_layout_kern (hb_font_t *font,
-		   hb_buffer_t  *buffer,
-		   hb_mask_t kern_mask);
+hb_ot_layout_kern (const hb_ot_shape_plan_t *plan,
+		   hb_font_t *font,
+		   hb_buffer_t  *buffer);
 
 
 /* Private API corresponding to hb-ot-layout.h: */
 
-HB_INTERNAL hb_bool_t
+HB_INTERNAL bool
 hb_ot_layout_table_find_feature (hb_face_t    *face,
 				 hb_tag_t      table_tag,
 				 hb_tag_t      feature_tag,
@@ -98,12 +96,12 @@
  * GSUB/GPOS
  */
 
-HB_INTERNAL hb_bool_t
+HB_INTERNAL bool
 hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
 					   unsigned int          lookup_index,
 					   const hb_codepoint_t *glyphs,
 					   unsigned int          glyphs_length,
-					   hb_bool_t             zero_context);
+					   bool                  zero_context);
 
 
 /* Should be called before all the substitute_lookup's are done. */
@@ -111,6 +109,9 @@
 hb_ot_layout_substitute_start (hb_font_t    *font,
 			       hb_buffer_t  *buffer);
 
+HB_INTERNAL void
+hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer,
+				    bool (*filter) (const hb_glyph_info_t *info));
 
 namespace OT {
   struct hb_ot_apply_context_t;
@@ -311,13 +312,13 @@
 
 static inline bool _hb_glyph_info_ligated (const hb_glyph_info_t *info);
 
-static inline hb_bool_t
+static inline bool
 _hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info)
 {
   return (info->unicode_props() & UPROPS_MASK_IGNORABLE) &&
 	 !_hb_glyph_info_ligated (info);
 }
-static inline hb_bool_t
+static inline bool
 _hb_glyph_info_is_default_ignorable_and_not_hidden (const hb_glyph_info_t *info)
 {
   return ((info->unicode_props() & (UPROPS_MASK_IGNORABLE|UPROPS_MASK_HIDDEN))
@@ -371,17 +372,17 @@
   return _hb_glyph_info_get_general_category (info) ==
 	 HB_UNICODE_GENERAL_CATEGORY_FORMAT;
 }
-static inline hb_bool_t
+static inline bool
 _hb_glyph_info_is_zwnj (const hb_glyph_info_t *info)
 {
   return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_ZWNJ);
 }
-static inline hb_bool_t
+static inline bool
 _hb_glyph_info_is_zwj (const hb_glyph_info_t *info)
 {
   return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_ZWJ);
 }
-static inline hb_bool_t
+static inline bool
 _hb_glyph_info_is_joiner (const hb_glyph_info_t *info)
 {
   return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & (UPROPS_MASK_Cf_ZWNJ|UPROPS_MASK_Cf_ZWJ));
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index 45d7dbd..884202c 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -27,7 +27,7 @@
  */
 
 #include "hb-ot-map.hh"
-
+#include "hb-ot-shape.hh"
 #include "hb-ot-layout.hh"
 
 
@@ -68,7 +68,7 @@
   }
 }
 
-hb_ot_map_builder_t::~hb_ot_map_builder_t (void)
+hb_ot_map_builder_t::~hb_ot_map_builder_t ()
 {
   feature_infos.fini ();
   for (unsigned int table_index = 0; table_index < 2; table_index++)
@@ -79,8 +79,8 @@
 				       hb_ot_map_feature_flags_t flags,
 				       unsigned int value)
 {
-  feature_info_t *info = feature_infos.push();
   if (unlikely (!tag)) return;
+  feature_info_t *info = feature_infos.push();
   info->tag = tag;
   info->seq = feature_infos.len;
   info->max_value = value;
@@ -143,9 +143,8 @@
 }
 
 void
-hb_ot_map_builder_t::compile (hb_ot_map_t  &m,
-			      const int    *coords,
-			      unsigned int  num_coords)
+hb_ot_map_builder_t::compile (hb_ot_map_t                  &m,
+			      const hb_ot_shape_plan_key_t &key)
 {
   static_assert ((!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1))), "");
   unsigned int global_bit_mask = HB_GLYPH_FLAG_DEFINED + 1;
@@ -175,6 +174,7 @@
   }
 
   /* Sort features and merge duplicates */
+  if (feature_infos.len)
   {
     feature_infos.qsort ();
     unsigned int j = 0;
@@ -281,13 +281,6 @@
   {
     /* Collect lookup indices for features */
 
-    unsigned int variations_index;
-    hb_ot_layout_table_find_feature_variations (face,
-						table_tags[table_index],
-						coords,
-						num_coords,
-						&variations_index);
-
     unsigned int stage_index = 0;
     unsigned int last_num_lookups = 0;
     for (unsigned stage = 0; stage < current_stage[table_index]; stage++)
@@ -296,14 +289,14 @@
 	  required_feature_stage[table_index] == stage)
 	add_lookups (m, table_index,
 		     required_feature_index[table_index],
-		     variations_index,
+		     key.variations_index[table_index],
 		     global_bit_mask);
 
       for (unsigned i = 0; i < m.features.len; i++)
         if (m.features[i].stage[table_index] == stage)
 	  add_lookups (m, table_index,
 		       m.features[i].index[table_index],
-		       variations_index,
+		       key.variations_index[table_index],
 		       m.features[i].mask,
 		       m.features[i].auto_zwnj,
 		       m.features[i].auto_zwj,
diff --git a/src/hb-ot-map.hh b/src/hb-ot-map.hh
index 40b9921..35e9493 100644
--- a/src/hb-ot-map.hh
+++ b/src/hb-ot-map.hh
@@ -57,8 +57,8 @@
     unsigned int auto_zwj : 1;
     unsigned int random : 1;
 
-    inline int cmp (const hb_tag_t *tag_) const
-    { return *tag_ < tag ? -1 : *tag_ > tag ? 1 : 0; }
+    int cmp (const hb_tag_t tag_) const
+    { return tag_ < tag ? -1 : tag_ > tag ? 1 : 0; }
   };
 
   struct lookup_map_t {
@@ -83,7 +83,7 @@
     pause_func_t pause_func;
   };
 
-  inline void init (void)
+  void init ()
   {
     memset (this, 0, sizeof (*this));
 
@@ -94,7 +94,7 @@
       stages[table_index].init ();
     }
   }
-  inline void fini (void)
+  void fini ()
   {
     features.fini ();
     for (unsigned int table_index = 0; table_index < 2; table_index++)
@@ -104,36 +104,42 @@
     }
   }
 
-  inline hb_mask_t get_global_mask (void) const { return global_mask; }
+  hb_mask_t get_global_mask () const { return global_mask; }
 
-  inline hb_mask_t get_mask (hb_tag_t feature_tag, unsigned int *shift = nullptr) const {
+  hb_mask_t get_mask (hb_tag_t feature_tag, unsigned int *shift = nullptr) const
+  {
     const feature_map_t *map = features.bsearch (feature_tag);
     if (shift) *shift = map ? map->shift : 0;
     return map ? map->mask : 0;
   }
 
-  inline bool needs_fallback (hb_tag_t feature_tag) const {
+  bool needs_fallback (hb_tag_t feature_tag) const
+  {
     const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->needs_fallback : false;
   }
 
-  inline hb_mask_t get_1_mask (hb_tag_t feature_tag) const {
+  hb_mask_t get_1_mask (hb_tag_t feature_tag) const
+  {
     const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->_1_mask : 0;
   }
 
-  inline unsigned int get_feature_index (unsigned int table_index, hb_tag_t feature_tag) const {
+  unsigned int get_feature_index (unsigned int table_index, hb_tag_t feature_tag) const
+  {
     const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->index[table_index] : HB_OT_LAYOUT_NO_FEATURE_INDEX;
   }
 
-  inline unsigned int get_feature_stage (unsigned int table_index, hb_tag_t feature_tag) const {
+  unsigned int get_feature_stage (unsigned int table_index, hb_tag_t feature_tag) const
+  {
     const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->stage[table_index] : (unsigned int) -1;
   }
 
-  inline void get_stage_lookups (unsigned int table_index, unsigned int stage,
-				 const struct lookup_map_t **plookups, unsigned int *lookup_count) const {
+  void get_stage_lookups (unsigned int table_index, unsigned int stage,
+			  const struct lookup_map_t **plookups, unsigned int *lookup_count) const
+  {
     if (unlikely (stage == (unsigned int) -1)) {
       *plookups = nullptr;
       *lookup_count = 0;
@@ -162,7 +168,7 @@
   hb_mask_t global_mask;
 
   hb_vector_t<feature_map_t, 8> features;
-  hb_vector_t<lookup_map_t, 32> lookups[2]; /* GSUB/GPOS */
+  hb_vector_t<lookup_map_t, 16> lookups[2]; /* GSUB/GPOS */
   hb_vector_t<stage_map_t, 4> stages[2]; /* GSUB/GPOS */
 };
 
@@ -188,6 +194,7 @@
   hb_ot_map_feature_flags_t flags;
 };
 
+struct hb_ot_shape_plan_key_t;
 
 struct hb_ot_map_builder_t
 {
@@ -196,31 +203,30 @@
   HB_INTERNAL hb_ot_map_builder_t (hb_face_t *face_,
 				   const hb_segment_properties_t *props_);
 
-  HB_INTERNAL ~hb_ot_map_builder_t (void);
+  HB_INTERNAL ~hb_ot_map_builder_t ();
 
   HB_INTERNAL void add_feature (hb_tag_t tag,
 				hb_ot_map_feature_flags_t flags=F_NONE,
 				unsigned int value=1);
 
-  inline void add_feature (const hb_ot_map_feature_t &feat)
+  void add_feature (const hb_ot_map_feature_t &feat)
   { add_feature (feat.tag, feat.flags); }
 
-  inline void enable_feature (hb_tag_t tag,
+  void enable_feature (hb_tag_t tag,
 			      hb_ot_map_feature_flags_t flags=F_NONE,
 			      unsigned int value=1)
   { add_feature (tag, F_GLOBAL | flags, value); }
 
-  inline void disable_feature (hb_tag_t tag)
+  void disable_feature (hb_tag_t tag)
   { add_feature (tag, F_GLOBAL, 0); }
 
-  inline void add_gsub_pause (hb_ot_map_t::pause_func_t pause_func)
+  void add_gsub_pause (hb_ot_map_t::pause_func_t pause_func)
   { add_pause (0, pause_func); }
-  inline void add_gpos_pause (hb_ot_map_t::pause_func_t pause_func)
+  void add_gpos_pause (hb_ot_map_t::pause_func_t pause_func)
   { add_pause (1, pause_func); }
 
-  HB_INTERNAL void compile (hb_ot_map_t  &m,
-			    const int    *coords,
-			    unsigned int  num_coords);
+  HB_INTERNAL void compile (hb_ot_map_t                  &m,
+			    const hb_ot_shape_plan_key_t &key);
 
   private:
 
diff --git a/src/hb-ot-math-table.hh b/src/hb-ot-math-table.hh
index 2f87112..120ab06 100644
--- a/src/hb-ot-math-table.hh
+++ b/src/hb-ot-math-table.hh
@@ -36,12 +36,12 @@
 
 struct MathValueRecord
 {
-  inline hb_position_t get_x_value (hb_font_t *font, const void *base) const
+  hb_position_t get_x_value (hb_font_t *font, const void *base) const
   { return font->em_scale_x (value) + (base+deviceTable).get_x_delta (font); }
-  inline hb_position_t get_y_value (hb_font_t *font, const void *base) const
+  hb_position_t get_y_value (hb_font_t *font, const void *base) const
   { return font->em_scale_y (value) + (base+deviceTable).get_y_delta (font); }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && deviceTable.sanitize (c, base));
@@ -59,7 +59,7 @@
 
 struct MathConstants
 {
-  inline bool sanitize_math_value_records (hb_sanitize_context_t *c) const
+  bool sanitize_math_value_records (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
 
@@ -71,13 +71,13 @@
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && sanitize_math_value_records(c));
+    return_trace (c->check_struct (this) && sanitize_math_value_records (c));
   }
 
-  inline hb_position_t get_value (hb_ot_math_constant_t constant,
+  hb_position_t get_value (hb_ot_math_constant_t constant,
 				  hb_font_t *font) const
   {
     switch (constant) {
@@ -94,7 +94,7 @@
     case HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE:
     case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP:
     case HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT:
-      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_x_value(font, this);
+      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_x_value (font, this);
 
     case HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT:
     case HB_OT_MATH_CONSTANT_AXIS_HEIGHT:
@@ -143,7 +143,7 @@
     case HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP:
     case HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN:
     case HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN:
-      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_y_value(font, this);
+      return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_y_value (font, this);
 
     case HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT:
       return radicalDegreeBottomRaisePercent;
@@ -165,7 +165,7 @@
 
 struct MathItalicsCorrectionInfo
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -173,8 +173,8 @@
 		  italicsCorrection.sanitize (c, this));
   }
 
-  inline hb_position_t get_value (hb_codepoint_t glyph,
-				  hb_font_t *font) const
+  hb_position_t get_value (hb_codepoint_t glyph,
+			   hb_font_t *font) const
   {
     unsigned int index = (this+coverage).get_coverage (glyph);
     return italicsCorrection[index].get_x_value (font, this);
@@ -196,7 +196,7 @@
 
 struct MathTopAccentAttachment
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -204,13 +204,13 @@
 		  topAccentAttachment.sanitize (c, this));
   }
 
-  inline hb_position_t get_value (hb_codepoint_t glyph,
-				  hb_font_t *font) const
+  hb_position_t get_value (hb_codepoint_t glyph,
+			   hb_font_t *font) const
   {
     unsigned int index = (this+topAccentCoverage).get_coverage (glyph);
     if (index == NOT_COVERED)
       return font->get_glyph_h_advance (glyph) / 2;
-    return topAccentAttachment[index].get_x_value(font, this);
+    return topAccentAttachment[index].get_x_value (font, this);
   }
 
   protected:
@@ -229,7 +229,7 @@
 
 struct MathKern
 {
-  inline bool sanitize_math_value_records (hb_sanitize_context_t *c) const
+  bool sanitize_math_value_records (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     unsigned int count = 2 * heightCount + 1;
@@ -238,7 +238,7 @@
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -246,7 +246,7 @@
 		  sanitize_math_value_records (c));
   }
 
-  inline hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
+  hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
   {
     const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
     const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
@@ -265,7 +265,7 @@
     while (count > 0)
     {
       unsigned int half = count / 2;
-      hb_position_t height = correctionHeight[i + half].get_y_value(font, this);
+      hb_position_t height = correctionHeight[i + half].get_y_value (font, this);
       if (sign * height < sign * correction_height)
       {
 	i += half + 1;
@@ -273,7 +273,7 @@
       } else
 	count = half;
     }
-    return kernValue[i].get_x_value(font, this);
+    return kernValue[i].get_x_value (font, this);
   }
 
   protected:
@@ -294,7 +294,7 @@
 
 struct MathKernInfoRecord
 {
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
 
@@ -306,10 +306,10 @@
     return_trace (true);
   }
 
-  inline hb_position_t get_kerning (hb_ot_math_kern_t kern,
-				    hb_position_t correction_height,
-				    hb_font_t *font,
-				    const void *base) const
+  hb_position_t get_kerning (hb_ot_math_kern_t kern,
+			     hb_position_t correction_height,
+			     hb_font_t *font,
+			     const void *base) const
   {
     unsigned int idx = kern;
     if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0;
@@ -327,7 +327,7 @@
 
 struct MathKernInfo
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -335,10 +335,10 @@
 		  mathKernInfoRecords.sanitize (c, this));
   }
 
-  inline hb_position_t get_kerning (hb_codepoint_t glyph,
-				    hb_ot_math_kern_t kern,
-				    hb_position_t correction_height,
-				    hb_font_t *font) const
+  hb_position_t get_kerning (hb_codepoint_t glyph,
+			     hb_ot_math_kern_t kern,
+			     hb_position_t correction_height,
+			     hb_font_t *font) const
   {
     unsigned int index = (this+mathKernCoverage).get_coverage (glyph);
     return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this);
@@ -361,31 +361,31 @@
 
 struct MathGlyphInfo
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  mathItalicsCorrectionInfo.sanitize (c, this) &&
 		  mathTopAccentAttachment.sanitize (c, this) &&
 		  extendedShapeCoverage.sanitize (c, this) &&
-		  mathKernInfo.sanitize(c, this));
+		  mathKernInfo.sanitize (c, this));
   }
 
-  inline hb_position_t
+  hb_position_t
   get_italics_correction (hb_codepoint_t  glyph, hb_font_t *font) const
   { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); }
 
-  inline hb_position_t
+  hb_position_t
   get_top_accent_attachment (hb_codepoint_t  glyph, hb_font_t *font) const
   { return (this+mathTopAccentAttachment).get_value (glyph, font); }
 
-  inline bool is_extended_shape (hb_codepoint_t glyph) const
+  bool is_extended_shape (hb_codepoint_t glyph) const
   { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; }
 
-  inline hb_position_t get_kerning (hb_codepoint_t glyph,
-				    hb_ot_math_kern_t kern,
-				    hb_position_t correction_height,
-				    hb_font_t *font) const
+  hb_position_t get_kerning (hb_codepoint_t glyph,
+			     hb_ot_math_kern_t kern,
+			     hb_position_t correction_height,
+			     hb_font_t *font) const
   { return (this+mathKernInfo).get_kerning (glyph, kern, correction_height, font); }
 
   protected:
@@ -416,7 +416,7 @@
 {
   friend struct MathGlyphConstruction;
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -425,8 +425,8 @@
   protected:
   GlyphID variantGlyph;       /* Glyph ID for the variant. */
   HBUINT16  advanceMeasurement; /* Advance width/height, in design units, of the
-			       * variant, in the direction of requested
-			       * glyph extension. */
+				 * variant, in the direction of requested
+				 * glyph extension. */
 
   public:
   DEFINE_SIZE_STATIC (4);
@@ -446,15 +446,15 @@
 
 struct MathGlyphPartRecord
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
-  inline void extract (hb_ot_math_glyph_part_t &out,
-		       int scale,
-		       hb_font_t *font) const
+  void extract (hb_ot_math_glyph_part_t &out,
+		int scale,
+		hb_font_t *font) const
   {
     out.glyph			= glyph;
 
@@ -491,27 +491,26 @@
 
 struct MathGlyphAssembly
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  italicsCorrection.sanitize(c, this) &&
-		  partRecords.sanitize(c));
+		  italicsCorrection.sanitize (c, this) &&
+		  partRecords.sanitize (c));
   }
 
-  inline unsigned int get_parts (hb_direction_t direction,
-				 hb_font_t *font,
-				 unsigned int start_offset,
-				 unsigned int *parts_count, /* IN/OUT */
-				 hb_ot_math_glyph_part_t *parts /* OUT */,
-				 hb_position_t *italics_correction /* OUT */) const
+  unsigned int get_parts (hb_direction_t direction,
+			  hb_font_t *font,
+			  unsigned int start_offset,
+			  unsigned int *parts_count, /* IN/OUT */
+			  hb_ot_math_glyph_part_t *parts /* OUT */,
+			  hb_position_t *italics_correction /* OUT */) const
   {
     if (parts_count)
     {
       int scale = font->dir_scale (direction);
-      const MathGlyphPartRecord *arr =
-	    partRecords.sub_array (start_offset, parts_count);
-      unsigned int count = *parts_count;
+      hb_array_t<const MathGlyphPartRecord> arr = partRecords.sub_array (start_offset, parts_count);
+      unsigned int count = arr.len;
       for (unsigned int i = 0; i < count; i++)
 	arr[i].extract (parts[i], scale, font);
     }
@@ -536,29 +535,27 @@
 
 struct MathGlyphConstruction
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  glyphAssembly.sanitize(c, this) &&
-		  mathGlyphVariantRecord.sanitize(c));
+		  glyphAssembly.sanitize (c, this) &&
+		  mathGlyphVariantRecord.sanitize (c));
   }
 
-  inline const MathGlyphAssembly &get_assembly (void) const
-  { return this+glyphAssembly; }
+  const MathGlyphAssembly &get_assembly () const { return this+glyphAssembly; }
 
-  inline unsigned int get_variants (hb_direction_t direction,
-				    hb_font_t *font,
-				    unsigned int start_offset,
-				    unsigned int *variants_count, /* IN/OUT */
-				    hb_ot_math_glyph_variant_t *variants /* OUT */) const
+  unsigned int get_variants (hb_direction_t direction,
+			     hb_font_t *font,
+			     unsigned int start_offset,
+			     unsigned int *variants_count, /* IN/OUT */
+			     hb_ot_math_glyph_variant_t *variants /* OUT */) const
   {
     if (variants_count)
     {
       int scale = font->dir_scale (direction);
-      const MathGlyphVariantRecord *arr =
-	    mathGlyphVariantRecord.sub_array (start_offset, variants_count);
-      unsigned int count = *variants_count;
+      hb_array_t<const MathGlyphVariantRecord> arr = mathGlyphVariantRecord.sub_array (start_offset, variants_count);
+      unsigned int count = arr.len;
       for (unsigned int i = 0; i < count; i++)
       {
 	variants[i].glyph = arr[i].variantGlyph;
@@ -582,7 +579,7 @@
 
 struct MathVariants
 {
-  inline bool sanitize_offsets (hb_sanitize_context_t *c) const
+  bool sanitize_offsets (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     unsigned int count = vertGlyphCount + horizGlyphCount;
@@ -591,7 +588,7 @@
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -601,20 +598,20 @@
 		  sanitize_offsets (c));
   }
 
-  inline hb_position_t get_min_connector_overlap (hb_direction_t direction,
+  hb_position_t get_min_connector_overlap (hb_direction_t direction,
 						  hb_font_t *font) const
   { return font->em_scale_dir (minConnectorOverlap, direction); }
 
-  inline unsigned int get_glyph_variants (hb_codepoint_t glyph,
-					  hb_direction_t direction,
-					  hb_font_t *font,
-					  unsigned int start_offset,
-					  unsigned int *variants_count, /* IN/OUT */
-					  hb_ot_math_glyph_variant_t *variants /* OUT */) const
+  unsigned int get_glyph_variants (hb_codepoint_t glyph,
+				   hb_direction_t direction,
+				   hb_font_t *font,
+				   unsigned int start_offset,
+				   unsigned int *variants_count, /* IN/OUT */
+				   hb_ot_math_glyph_variant_t *variants /* OUT */) const
   { return get_glyph_construction (glyph, direction, font)
 	   .get_variants (direction, font, start_offset, variants_count, variants); }
 
-  inline unsigned int get_glyph_parts (hb_codepoint_t glyph,
+  unsigned int get_glyph_parts (hb_codepoint_t glyph,
 				       hb_direction_t direction,
 				       hb_font_t *font,
 				       unsigned int start_offset,
@@ -628,10 +625,10 @@
 		       italics_correction); }
 
   private:
-  inline const MathGlyphConstruction &
-		get_glyph_construction (hb_codepoint_t glyph,
-					hb_direction_t direction,
-					hb_font_t *font) const
+  const MathGlyphConstruction &
+  get_glyph_construction (hb_codepoint_t glyph,
+			  hb_direction_t direction,
+			  hb_font_t *font HB_UNUSED) const
   {
     bool vertical = HB_DIRECTION_IS_VERTICAL (direction);
     unsigned int count = vertical ? vertGlyphCount : horizGlyphCount;
@@ -639,7 +636,7 @@
 						  : horizGlyphCoverage;
 
     unsigned int index = (this+coverage).get_coverage (glyph);
-    if (unlikely (index >= count)) return Null(MathGlyphConstruction);
+    if (unlikely (index >= count)) return Null (MathGlyphConstruction);
 
     if (!vertical)
       index += vertGlyphCount;
@@ -682,11 +679,11 @@
 
 struct MATH
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_MATH;
+  enum { tableTag = HB_OT_TAG_MATH };
 
-  inline bool has_data (void) const { return version.to_int () != 0; }
+  bool has_data () const { return version.to_int (); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
@@ -696,15 +693,13 @@
 		  mathVariants.sanitize (c, this));
   }
 
-  inline hb_position_t get_constant (hb_ot_math_constant_t  constant,
+  hb_position_t get_constant (hb_ot_math_constant_t  constant,
 				     hb_font_t		   *font) const
   { return (this+mathConstants).get_value (constant, font); }
 
-  inline const MathGlyphInfo &get_math_glyph_info (void) const
-  { return this+mathGlyphInfo; }
+  const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; }
 
-  inline const MathVariants &get_math_variants (void) const
-  { return this+mathVariants; }
+  const MathVariants &get_variants () const    { return this+mathVariants; }
 
   protected:
   FixedVersion<>version;		/* Version of the MATH table
diff --git a/src/hb-ot-math.cc b/src/hb-ot-math.cc
index c693f48..bd31bf5 100644
--- a/src/hb-ot-math.cc
+++ b/src/hb-ot-math.cc
@@ -29,13 +29,16 @@
 #include "hb-ot-face.hh"
 #include "hb-ot-math-table.hh"
 
-static inline const OT::MATH&
-_get_math (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::MATH);
-  hb_ot_face_data_t * data = hb_ot_face_data (face);
-  return *(data->MATH.get ());
-}
+
+/**
+ * SECTION:hb-ot-math
+ * @title: hb-ot-math
+ * @short_description: OpenType Math information
+ * @include: hb-ot.h
+ *
+ * Functions for fetching mathematics layout data from OpenType fonts.
+ **/
+
 
 /*
  * OT::MATH
@@ -55,7 +58,7 @@
 hb_bool_t
 hb_ot_math_has_data (hb_face_t *face)
 {
-  return _get_math (face).has_data ();
+  return face->table.MATH->has_data ();
 }
 
 /**
@@ -77,8 +80,7 @@
 hb_ot_math_get_constant (hb_font_t *font,
 			 hb_ot_math_constant_t constant)
 {
-  const OT::MATH &math = _get_math (font->face);
-  return math.get_constant(constant, font);
+  return font->face->table.MATH->get_constant(constant, font);
 }
 
 /**
@@ -94,8 +96,7 @@
 hb_ot_math_get_glyph_italics_correction (hb_font_t *font,
 					 hb_codepoint_t glyph)
 {
-  const OT::MATH &math = _get_math (font->face);
-  return math.get_math_glyph_info().get_italics_correction (glyph, font);
+  return font->face->table.MATH->get_glyph_info().get_italics_correction (glyph, font);
 }
 
 /**
@@ -111,8 +112,7 @@
 hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font,
 					    hb_codepoint_t glyph)
 {
-  const OT::MATH &math = _get_math (font->face);
-  return math.get_math_glyph_info().get_top_accent_attachment (glyph, font);
+  return font->face->table.MATH->get_glyph_info().get_top_accent_attachment (glyph, font);
 }
 
 /**
@@ -128,8 +128,7 @@
 hb_ot_math_is_glyph_extended_shape (hb_face_t *face,
 				    hb_codepoint_t glyph)
 {
-  const OT::MATH &math = _get_math (face);
-  return math.get_math_glyph_info().is_extended_shape (glyph);
+  return face->table.MATH->get_glyph_info().is_extended_shape (glyph);
 }
 
 /**
@@ -155,8 +154,10 @@
 			      hb_ot_math_kern_t kern,
 			      hb_position_t correction_height)
 {
-  const OT::MATH &math = _get_math (font->face);
-  return math.get_math_glyph_info().get_kerning (glyph, kern, correction_height, font);
+  return font->face->table.MATH->get_glyph_info().get_kerning (glyph,
+							       kern,
+							       correction_height,
+							       font);
 }
 
 /**
@@ -186,11 +187,10 @@
 			       unsigned int *variants_count, /* IN/OUT */
 			       hb_ot_math_glyph_variant_t *variants /* OUT */)
 {
-  const OT::MATH &math = _get_math (font->face);
-  return math.get_math_variants().get_glyph_variants (glyph, direction, font,
-						      start_offset,
-						      variants_count,
-						      variants);
+  return font->face->table.MATH->get_variants().get_glyph_variants (glyph, direction, font,
+								    start_offset,
+								    variants_count,
+								    variants);
 }
 
 /**
@@ -211,8 +211,7 @@
 hb_ot_math_get_min_connector_overlap (hb_font_t *font,
 				      hb_direction_t direction)
 {
-  const OT::MATH &math = _get_math (font->face);
-  return math.get_math_variants().get_min_connector_overlap (direction, font);
+  return font->face->table.MATH->get_variants().get_min_connector_overlap (direction, font);
 }
 
 /**
@@ -244,10 +243,11 @@
 			       hb_ot_math_glyph_part_t *parts, /* OUT */
 			       hb_position_t *italics_correction /* OUT */)
 {
-  const OT::MATH &math = _get_math (font->face);
-  return math.get_math_variants().get_glyph_parts (glyph, direction, font,
-						   start_offset,
-						   parts_count,
-						   parts,
-						   italics_correction);
+  return font->face->table.MATH->get_variants().get_glyph_parts (glyph,
+								 direction,
+								 font,
+								 start_offset,
+								 parts_count,
+								 parts,
+								 italics_correction);
 }
diff --git a/src/hb-ot-maxp-table.hh b/src/hb-ot-maxp-table.hh
index 2572ad2..ba474fd 100644
--- a/src/hb-ot-maxp-table.hh
+++ b/src/hb-ot-maxp-table.hh
@@ -41,7 +41,7 @@
 
 struct maxpV1Tail
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -71,19 +71,16 @@
 
 struct maxp
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_maxp;
+  enum { tableTag = HB_OT_TAG_maxp };
 
-  inline unsigned int get_num_glyphs (void) const
-  {
-    return numGlyphs;
-  }
+  unsigned int get_num_glyphs () const { return numGlyphs; }
 
-  inline void set_num_glyphs (unsigned int count)
+  void set_num_glyphs (unsigned int count)
   {
     numGlyphs.set (count);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this)))
@@ -92,12 +89,12 @@
     if (version.major == 1)
     {
       const maxpV1Tail &v1 = StructAfter<maxpV1Tail> (*this);
-      return v1.sanitize (c);
+      return_trace (v1.sanitize (c));
     }
     return_trace (likely (version.major == 0 && version.minor == 0x5000u));
   }
 
-  inline bool subset (hb_subset_plan_t *plan) const
+  bool subset (hb_subset_plan_t *plan) const
   {
     hb_blob_t *maxp_blob = hb_sanitize_context_t().reference_table<maxp> (plan->source);
     hb_blob_t *maxp_prime_blob = hb_blob_copy_writable_or_fail (maxp_blob);
@@ -117,7 +114,7 @@
     return result;
   }
 
-  static inline void drop_hint_fields (hb_subset_plan_t *plan, maxp *maxp_prime)
+  static void drop_hint_fields (hb_subset_plan_t *plan HB_UNUSED, maxp *maxp_prime)
   {
     if (maxp_prime->version.major == 1)
     {
diff --git a/src/hb-ot-name-language.cc b/src/hb-ot-name-language.cc
new file mode 100644
index 0000000..0e37e0a
--- /dev/null
+++ b/src/hb-ot-name-language.cc
@@ -0,0 +1,457 @@
+/*
+ * 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
+ */
+
+#include "hb-ot-name-language.hh"
+
+/* Following two tables were generated by joining FreeType, FontConfig,
+ * and OpenType specification language lists, then filled in missing
+ * entries using:
+ * https://docs.microsoft.com/en-us/windows/desktop/intl/language-identifier-constants-and-strings
+ */
+
+struct hb_ot_language_map_t
+{
+  static int cmp (const void *key, const void *item)
+  {
+    unsigned int a = * (unsigned int *) key;
+    unsigned int b = ((const hb_ot_language_map_t *) item)->code;
+    return a < b ? -1 : a > b ? +1 : 0;
+  }
+
+  uint16_t	code;
+  char		lang[6];
+};
+
+static const hb_ot_language_map_t
+hb_ms_language_map[] =
+{
+  {0x0001,	"ar"},	/* ??? */
+  {0x0004,	"zh"},	/* ??? */
+  {0x0009,	"en"},	/* ??? */
+  {0x0401,	"ar"},	/* Arabic (Saudi Arabia) */
+  {0x0402,	"bg"},	/* Bulgarian (Bulgaria) */
+  {0x0403,	"ca"},	/* Catalan (Catalan) */
+  {0x0404,	"zh-tw"},	/* Chinese (Taiwan) */
+  {0x0405,	"cs"},	/* Czech (Czech Republic) */
+  {0x0406,	"da"},	/* Danish (Denmark) */
+  {0x0407,	"de"},	/* German (Germany) */
+  {0x0408,	"el"},	/* Greek (Greece) */
+  {0x0409,	"en"},	/* English (United States) */
+  {0x040A,	"es"},	/* Spanish (Traditional Sort) (Spain) */
+  {0x040B,	"fi"},	/* Finnish (Finland) */
+  {0x040C,	"fr"},	/* French (France) */
+  {0x040D,	"he"},	/* Hebrew (Israel) */
+  {0x040E,	"hu"},	/* Hungarian (Hungary) */
+  {0x040F,	"is"},	/* Icelandic (Iceland) */
+  {0x0410,	"it"},	/* Italian (Italy) */
+  {0x0411,	"ja"},	/* Japanese (Japan) */
+  {0x0412,	"ko"},	/* Korean (Korea) */
+  {0x0413,	"nl"},	/* Dutch (Netherlands) */
+  {0x0414,	"no"},	/* Norwegian (Bokmal) (Norway) */
+  {0x0415,	"pl"},	/* Polish (Poland) */
+  {0x0416,	"pt"},	/* Portuguese (Brazil) */
+  {0x0417,	"rm"},	/* Romansh (Switzerland) */
+  {0x0418,	"ro"},	/* Romanian (Romania) */
+  {0x0419,	"ru"},	/* Russian (Russia) */
+  {0x041A,	"hr"},	/* Croatian (Croatia) */
+  {0x041B,	"sk"},	/* Slovak (Slovakia) */
+  {0x041C,	"sq"},	/* Albanian (Albania) */
+  {0x041D,	"sv"},	/* Swedish (Sweden) */
+  {0x041E,	"th"},	/* Thai (Thailand) */
+  {0x041F,	"tr"},	/* Turkish (Turkey) */
+  {0x0420,	"ur"},	/* Urdu (Islamic Republic of Pakistan) */
+  {0x0421,	"id"},	/* Indonesian (Indonesia) */
+  {0x0422,	"uk"},	/* Ukrainian (Ukraine) */
+  {0x0423,	"be"},	/* Belarusian (Belarus) */
+  {0x0424,	"sl"},	/* Slovenian (Slovenia) */
+  {0x0425,	"et"},	/* Estonian (Estonia) */
+  {0x0426,	"lv"},	/* Latvian (Latvia) */
+  {0x0427,	"lt"},	/* Lithuanian (Lithuania) */
+  {0x0428,	"tg"},	/* Tajik (Cyrillic) (Tajikistan) */
+  {0x0429,	"fa"},	/* Persian (Iran) */
+  {0x042A,	"vi"},	/* Vietnamese (Vietnam) */
+  {0x042B,	"hy"},	/* Armenian (Armenia) */
+  {0x042C,	"az"},	/* Azeri (Latin) (Azerbaijan) */
+  {0x042D,	"eu"},	/* Basque (Basque) */
+  {0x042E,	"hsb"},	/* Upper Sorbian (Germany) */
+  {0x042F,	"mk"},	/* Macedonian (FYROM) (Former Yugoslav Republic of Macedonia) */
+  {0x0430,	"st"},	/* ??? */
+  {0x0431,	"ts"},	/* ??? */
+  {0x0432,	"tn"},	/* Setswana (South Africa) */
+  {0x0433,	"ven"},	/* ??? */
+  {0x0434,	"xh"},	/* isiXhosa (South Africa) */
+  {0x0435,	"zu"},	/* isiZulu (South Africa) */
+  {0x0436,	"af"},	/* Afrikaans (South Africa) */
+  {0x0437,	"ka"},	/* Georgian (Georgia) */
+  {0x0438,	"fo"},	/* Faroese (Faroe Islands) */
+  {0x0439,	"hi"},	/* Hindi (India) */
+  {0x043A,	"mt"},	/* Maltese (Malta) */
+  {0x043B,	"se"},	/* Sami (Northern) (Norway) */
+  {0x043C,	"ga"},	/* ??? */
+  {0x043D,	"yi"},	/* ??? */
+  {0x043E,	"ms"},	/* Malay (Malaysia) */
+  {0x043F,	"kk"},	/* Kazakh (Kazakhstan) */
+  {0x0440,	"ky"},	/* Kyrgyz (Kyrgyzstan) */
+  {0x0441,	"sw"},	/* Kiswahili (Kenya) */
+  {0x0442,	"tk"},	/* Turkmen (Turkmenistan) */
+  {0x0443,	"uz"},	/* Uzbek (Latin) (Uzbekistan) */
+  {0x0444,	"tt"},	/* Tatar (Russia) */
+  {0x0445,	"bn"},	/* Bengali (India) */
+  {0x0446,	"pa"},	/* Punjabi (India) */
+  {0x0447,	"gu"},	/* Gujarati (India) */
+  {0x0448,	"or"},	/* Odia (formerly Oriya) (India) */
+  {0x0449,	"ta"},	/* Tamil (India) */
+  {0x044A,	"te"},	/* Telugu (India) */
+  {0x044B,	"kn"},	/* Kannada (India) */
+  {0x044C,	"ml"},	/* Malayalam (India) */
+  {0x044D,	"as"},	/* Assamese (India) */
+  {0x044E,	"mr"},	/* Marathi (India) */
+  {0x044F,	"sa"},	/* Sanskrit (India) */
+  {0x0450,	"mn"},	/* Mongolian (Cyrillic) (Mongolia) */
+  {0x0451,	"bo"},	/* Tibetan (PRC) */
+  {0x0452,	"cy"},	/* Welsh (United Kingdom) */
+  {0x0453,	"km"},	/* Khmer (Cambodia) */
+  {0x0454,	"lo"},	/* Lao (Lao P.D.R.) */
+  {0x0455,	"my"},	/* ??? */
+  {0x0456,	"gl"},	/* Galician (Galician) */
+  {0x0457,	"kok"},	/* Konkani (India) */
+  {0x0458,	"mni"},	/* ??? */
+  {0x0459,	"sd"},	/* ??? */
+  {0x045A,	"syr"},	/* Syriac (Syria) */
+  {0x045B,	"si"},	/* Sinhala (Sri Lanka) */
+  {0x045C,	"chr"},	/* ??? */
+  {0x045D,	"iu"},	/* Inuktitut (Canada) */
+  {0x045E,	"am"},	/* Amharic (Ethiopia) */
+  {0x0460,	"ks"},	/* ??? */
+  {0x0461,	"ne"},	/* Nepali (Nepal) */
+  {0x0462,	"fy"},	/* Frisian (Netherlands) */
+  {0x0463,	"ps"},	/* Pashto (Afghanistan) */
+  {0x0464,	"phi"},	/* Filipino (Philippines) */
+  {0x0465,	"div"},	/* Divehi (Maldives) */
+  {0x0468,	"ha"},	/* Hausa (Latin) (Nigeria) */
+  {0x046A,	"yo"},	/* Yoruba (Nigeria) */
+  {0x046B,	"quz"},	/* Quechua (Bolivia) */
+  {0x046C,	"nso"},	/* Sesotho sa Leboa (South Africa) */
+  {0x046D,	"ba"},	/* Bashkir (Russia) */
+  {0x046E,	"lb"},	/* Luxembourgish (Luxembourg) */
+  {0x046F,	"kl"},	/* Greenlandic (Greenland) */
+  {0x0470,	"ibo"},	/* Igbo (Nigeria) */
+  {0x0471,	"kau"},	/* ??? */
+  {0x0472,	"om"},	/* ??? */
+  {0x0473,	"ti"},	/* ??? */
+  {0x0474,	"gn"},	/* ??? */
+  {0x0475,	"haw"},	/* ??? */
+  {0x0476,	"la"},	/* ??? */
+  {0x0477,	"so"},	/* ??? */
+  {0x0478,	"ii"},	/* Yi (PRC) */
+  {0x0479,	"pap"},	/* ??? */
+  {0x047A,	"arn"},	/* Mapudungun (Chile) */
+  {0x047C,	"moh"},	/* Mohawk (Mohawk) */
+  {0x047E,	"br"},	/* Breton (France) */
+  {0x0480,	"ug"},	/* Uighur (PRC) */
+  {0x0481,	"mi"},	/* Maori (New Zealand) */
+  {0x0482,	"oc"},	/* Occitan (France) */
+  {0x0483,	"co"},	/* Corsican (France) */
+  {0x0484,	"gsw"},	/* Alsatian (France) */
+  {0x0485,	"sah"},	/* Yakut (Russia) */
+  {0x0486,	"qut"},	/* K'iche (Guatemala) */
+  {0x0487,	"rw"},	/* Kinyarwanda (Rwanda) */
+  {0x0488,	"wo"},	/* Wolof (Senegal) */
+  {0x048C,	"fa"},	/* Dari (Afghanistan) */
+  {0x0801,	"ar"},	/* Arabic (Iraq) */
+  {0x0804,	"zh-cn"},	/* Chinese (People’s Republic of China) */
+  {0x0807,	"de"},	/* German (Switzerland) */
+  {0x0809,	"en"},	/* English (United Kingdom) */
+  {0x080A,	"es"},	/* Spanish (Mexico) */
+  {0x080C,	"fr"},	/* French (Belgium) */
+  {0x0810,	"it"},	/* Italian (Switzerland) */
+  {0x0812,	"ko"},	/* ??? */
+  {0x0813,	"nl"},	/* Dutch (Belgium) */
+  {0x0814,	"nn"},	/* Norwegian (Nynorsk) (Norway) */
+  {0x0816,	"pt"},	/* Portuguese (Portugal) */
+  {0x0818,	"mo"},	/* ??? */
+  {0x0819,	"ru"},	/* ??? */
+  {0x081A,	"sr"},	/* Serbian (Latin) (Serbia) */
+  {0x081D,	"sv"},	/* Sweden (Finland) */
+  {0x0820,	"ur"},	/* ??? */
+  {0x0827,	"lt"},	/* ??? */
+  {0x082C,	"az"},	/* Azeri (Cyrillic) (Azerbaijan) */
+  {0x082E,	"dsb"},	/* Lower Sorbian (Germany) */
+//{0x083B,	""},	/* Sami (Northern) (Sweden) */
+  {0x083C,	"gd"},	/* Irish (Ireland) */
+  {0x083E,	"ms"},	/* Malay (Brunei Darussalam) */
+  {0x0843,	"uz"},	/* Uzbek (Cyrillic) (Uzbekistan) */
+  {0x0845,	"bn"},	/* Bengali (Bangladesh) */
+  {0x0846,	"ar"},	/* ??? */
+  {0x0850,	"mn"},	/* Mongolian (Traditional) (People’s Republic of China) */
+  {0x0851,	"dz"},	/* ??? */
+  {0x085D,	"iu"},	/* Inuktitut (Latin) (Canada) */
+  {0x085F,	"tzm"},	/* Tamazight (Latin) (Algeria) */
+  {0x0861,	"ne"},	/* ??? */
+//{0x086B,	""},	/* Quechua (Ecuador) */
+  {0x0873,	"ti"},	/* ??? */
+  {0x0C01,	"ar"},	/* Arabic (Egypt) */
+  {0x0C04,	"zh-hk"},	/* Chinese (Hong Kong S.A.R.) */
+  {0x0C07,	"de"},	/* German (Austria) */
+  {0x0C09,	"en"},	/* English (Australia) */
+  {0x0C0A,	"es"},	/* Spanish (Modern Sort) (Spain) */
+  {0x0C0C,	"fr"},	/* French (Canada) */
+  {0x0C1A,	"sr"},	/* Serbian (Cyrillic) (Serbia) */
+  {0x0C3B,	"se"},	/* Sami (Northern) (Finland) */
+//{0x0C6B,	""},	/* Quechua (Peru) */
+  {0x1001,	"ar"},	/* Arabic (Libya) */
+  {0x1004,	"zh-sg"},	/* Chinese (Singapore) */
+  {0x1007,	"de"},	/* German (Luxembourg) */
+  {0x1009,	"en"},	/* English (Canada) */
+  {0x100A,	"es"},	/* Spanish (Guatemala) */
+  {0x100C,	"fr"},	/* French (Switzerland) */
+  {0x101A,	"hr"},	/* Croatian (Latin) (Bosnia and Herzegovina) */
+  {0x103B,	"smj"},	/* Sami (Lule) (Norway) */
+  {0x1401,	"ar"},	/* Arabic (Algeria) */
+//{0x1404,	""},	/* Chinese (Macao S.A.R.) */
+  {0x1407,	"de"},	/* German (Liechtenstein) */
+  {0x1409,	"en"},	/* English (New Zealand) */
+  {0x140A,	"es"},	/* Spanish (Costa Rica) */
+  {0x140C,	"fr"},	/* French (Luxembourg) */
+  {0x141A,	"bs"},	/* Bosnian (Latin) (Bosnia and Herzegovina) */
+//{0x143B,	""},	/* Sami (Lule) (Sweden) */
+  {0x1801,	"ar"},	/* Arabic (Morocco) */
+  {0x1809,	"en"},	/* English (Ireland) */
+  {0x180A,	"es"},	/* Spanish (Panama) */
+  {0x180C,	"fr"},	/* French (Principality of Monaco) */
+//{0x181A,	""},	/* Serbian (Latin) (Bosnia and Herzegovina) */
+  {0x183B,	"sma"},	/* Sami (Southern) (Norway) */
+  {0x1C01,	"ar"},	/* Arabic (Tunisia) */
+  {0x1C09,	"en"},	/* English (South Africa) */
+  {0x1C0A,	"es"},	/* Spanish (Dominican Republic) */
+  {0x1C0C,	"fr"},	/* ??? */
+//{0x1C1A,	""},	/* Serbian (Cyrillic) (Bosnia and Herzegovina) */
+//{0x1C3B,	""},	/* Sami (Southern) (Sweden) */
+  {0x2001,	"ar"},	/* Arabic (Oman) */
+  {0x2009,	"en"},	/* English (Jamaica) */
+  {0x200A,	"es"},	/* Spanish (Venezuela) */
+  {0x200C,	"fr"},	/* ??? */
+  {0x201A,	"bs"},	/* Bosnian (Cyrillic) (Bosnia and Herzegovina) */
+  {0x203B,	"sms"},	/* Sami (Skolt) (Finland) */
+  {0x2401,	"ar"},	/* Arabic (Yemen) */
+  {0x2409,	"en"},	/* English (Caribbean) */
+  {0x240A,	"es"},	/* Spanish (Colombia) */
+  {0x240C,	"fr"},	/* ??? */
+  {0x243B,	"smn"},	/* Sami (Inari) (Finland) */
+  {0x2801,	"ar"},	/* Arabic (Syria) */
+  {0x2809,	"en"},	/* English (Belize) */
+  {0x280A,	"es"},	/* Spanish (Peru) */
+  {0x280C,	"fr"},	/* ??? */
+  {0x2C01,	"ar"},	/* Arabic (Jordan) */
+  {0x2C09,	"en"},	/* English (Trinidad and Tobago) */
+  {0x2C0A,	"es"},	/* Spanish (Argentina) */
+  {0x2C0C,	"fr"},	/* ??? */
+  {0x3001,	"ar"},	/* Arabic (Lebanon) */
+  {0x3009,	"en"},	/* English (Zimbabwe) */
+  {0x300A,	"es"},	/* Spanish (Ecuador) */
+  {0x300C,	"fr"},	/* ??? */
+  {0x3401,	"ar"},	/* Arabic (Kuwait) */
+  {0x3409,	"en"},	/* English (Republic of the Philippines) */
+  {0x340A,	"es"},	/* Spanish (Chile) */
+  {0x340C,	"fr"},	/* ??? */
+  {0x3801,	"ar"},	/* Arabic (U.A.E.) */
+  {0x380A,	"es"},	/* Spanish (Uruguay) */
+  {0x380C,	"fr"},	/* ??? */
+  {0x3C01,	"ar"},	/* Arabic (Bahrain) */
+  {0x3C09,	"en"},	/* ??? */
+  {0x3C0A,	"es"},	/* Spanish (Paraguay) */
+  {0x3C0C,	"fr"},	/* ??? */
+  {0x4001,	"ar"},	/* Arabic (Qatar) */
+  {0x4009,	"en"},	/* English (India) */
+  {0x400A,	"es"},	/* Spanish (Bolivia) */
+  {0x4409,	"en"},	/* English (Malaysia) */
+  {0x440A,	"es"},	/* Spanish (El Salvador) */
+  {0x4809,	"en"},	/* English (Singapore) */
+  {0x480A,	"es"},	/* Spanish (Honduras) */
+  {0x4C0A,	"es"},	/* Spanish (Nicaragua) */
+  {0x500A,	"es"},	/* Spanish (Puerto Rico) */
+  {0x540A,	"es"},	/* Spanish (United States) */
+  {0xE40A,	"es"},	/* ??? */
+  {0xE40C,	"fr"},	/* ??? */
+};
+
+static const hb_ot_language_map_t
+hb_mac_language_map[] =
+{
+  {  0,	"en"},	/* English */
+  {  1,	"fr"},	/* French */
+  {  2,	"de"},	/* German */
+  {  3,	"it"},	/* Italian */
+  {  4,	"nl"},	/* Dutch */
+  {  5,	"sv"},	/* Swedish */
+  {  6,	"es"},	/* Spanish */
+  {  7,	"da"},	/* Danish */
+  {  8,	"pt"},	/* Portuguese */
+  {  9,	"no"},	/* Norwegian */
+  { 10,	"he"},	/* Hebrew */
+  { 11,	"ja"},	/* Japanese */
+  { 12,	"ar"},	/* Arabic */
+  { 13,	"fi"},	/* Finnish */
+  { 14,	"el"},	/* Greek */
+  { 15,	"is"},	/* Icelandic */
+  { 16,	"mt"},	/* Maltese */
+  { 17,	"tr"},	/* Turkish */
+  { 18,	"hr"},	/* Croatian */
+  { 19,	"zh-tw"},	/* Chinese (Traditional) */
+  { 20,	"ur"},	/* Urdu */
+  { 21,	"hi"},	/* Hindi */
+  { 22,	"th"},	/* Thai */
+  { 23,	"ko"},	/* Korean */
+  { 24,	"lt"},	/* Lithuanian */
+  { 25,	"pl"},	/* Polish */
+  { 26,	"hu"},	/* Hungarian */
+  { 27,	"et"},	/* Estonian */
+  { 28,	"lv"},	/* Latvian */
+//{ 29,	""},	/* Sami */
+  { 30,	"fo"},	/* Faroese */
+  { 31,	"fa"},	/* Farsi/Persian */
+  { 32,	"ru"},	/* Russian */
+  { 33,	"zh-cn"},	/* Chinese (Simplified) */
+  { 34,	"nl"},	/* Flemish */
+  { 35,	"ga"},	/* Irish Gaelic */
+  { 36,	"sq"},	/* Albanian */
+  { 37,	"ro"},	/* Romanian */
+  { 38,	"cs"},	/* Czech */
+  { 39,	"sk"},	/* Slovak */
+  { 40,	"sl"},	/* Slovenian */
+  { 41,	"yi"},	/* Yiddish */
+  { 42,	"sr"},	/* Serbian */
+  { 43,	"mk"},	/* Macedonian */
+  { 44,	"bg"},	/* Bulgarian */
+  { 45,	"uk"},	/* Ukrainian */
+  { 46,	"be"},	/* Byelorussian */
+  { 47,	"uz"},	/* Uzbek */
+  { 48,	"kk"},	/* Kazakh */
+  { 49,	"az"},	/* Azerbaijani (Cyrillic script) */
+  { 50,	"az"},	/* Azerbaijani (Arabic script) */
+  { 51,	"hy"},	/* Armenian */
+  { 52,	"ka"},	/* Georgian */
+  { 53,	"mo"},	/* Moldavian */
+  { 54,	"ky"},	/* Kirghiz */
+  { 55,	"tg"},	/* Tajiki */
+  { 56,	"tk"},	/* Turkmen */
+  { 57,	"mn"},	/* Mongolian (Mongolian script) */
+  { 58,	"mn"},	/* Mongolian (Cyrillic script) */
+  { 59,	"ps"},	/* Pashto */
+  { 60,	"ku"},	/* Kurdish */
+  { 61,	"ks"},	/* Kashmiri */
+  { 62,	"sd"},	/* Sindhi */
+  { 63,	"bo"},	/* Tibetan */
+  { 64,	"ne"},	/* Nepali */
+  { 65,	"sa"},	/* Sanskrit */
+  { 66,	"mr"},	/* Marathi */
+  { 67,	"bn"},	/* Bengali */
+  { 68,	"as"},	/* Assamese */
+  { 69,	"gu"},	/* Gujarati */
+  { 70,	"pa"},	/* Punjabi */
+  { 71,	"or"},	/* Oriya */
+  { 72,	"ml"},	/* Malayalam */
+  { 73,	"kn"},	/* Kannada */
+  { 74,	"ta"},	/* Tamil */
+  { 75,	"te"},	/* Telugu */
+  { 76,	"si"},	/* Sinhalese */
+  { 77,	"my"},	/* Burmese */
+  { 78,	"km"},	/* Khmer */
+  { 79,	"lo"},	/* Lao */
+  { 80,	"vi"},	/* Vietnamese */
+  { 81,	"id"},	/* Indonesian */
+  { 82,	"tl"},	/* Tagalog */
+  { 83,	"ms"},	/* Malay (Roman script) */
+  { 84,	"ms"},	/* Malay (Arabic script) */
+  { 85,	"am"},	/* Amharic */
+  { 86,	"ti"},	/* Tigrinya */
+  { 87,	"om"},	/* Galla */
+  { 88,	"so"},	/* Somali */
+  { 89,	"sw"},	/* Swahili */
+  { 90,	"rw"},	/* Kinyarwanda/Ruanda */
+  { 91,	"rn"},	/* Rundi */
+  { 92,	"ny"},	/* Nyanja/Chewa */
+  { 93,	"mg"},	/* Malagasy */
+  { 94,	"eo"},	/* Esperanto */
+  {128,	"cy"},	/* Welsh */
+  {129,	"eu"},	/* Basque */
+  {130,	"ca"},	/* Catalan */
+  {131,	"la"},	/* Latin */
+  {132,	"qu"},	/* Quechua */
+  {133,	"gn"},	/* Guarani */
+  {134,	"ay"},	/* Aymara */
+  {135,	"tt"},	/* Tatar */
+  {136,	"ug"},	/* Uighur */
+  {137,	"dz"},	/* Dzongkha */
+  {138,	"jw"},	/* Javanese (Roman script) */
+  {139,	"su"},	/* Sundanese (Roman script) */
+  {140,	"gl"},	/* Galician */
+  {141,	"af"},	/* Afrikaans */
+  {142,	"br"},	/* Breton */
+  {143,	"iu"},	/* Inuktitut */
+  {144,	"gd"},	/* Scottish Gaelic */
+  {145,	"gv"},	/* Manx Gaelic */
+  {146,	"ga"},	/* Irish Gaelic (with dot above) */
+  {147,	"to"},	/* Tongan */
+  {148,	"el"},	/* Greek (polytonic) */
+  {149,	"ik"},	/* Greenlandic */
+  {150,	"az"},	/* Azerbaijani (Roman script) */
+};
+
+
+static hb_language_t
+_hb_ot_name_language_for (unsigned int code,
+			  const hb_ot_language_map_t *array,
+			  unsigned int len)
+{
+  const hb_ot_language_map_t *entry = (const hb_ot_language_map_t *)
+				      hb_bsearch (&code,
+						  array,
+						  len,
+						  sizeof (array[0]),
+						  hb_ot_language_map_t::cmp);
+
+  if (entry)
+    return hb_language_from_string (entry->lang, -1);
+
+  return HB_LANGUAGE_INVALID;
+}
+
+hb_language_t
+_hb_ot_name_language_for_ms_code (unsigned int code)
+{
+  return _hb_ot_name_language_for (code,
+				   hb_ms_language_map,
+				   ARRAY_LENGTH (hb_ms_language_map));
+}
+
+hb_language_t
+_hb_ot_name_language_for_mac_code (unsigned int code)
+{
+  return _hb_ot_name_language_for (code,
+				   hb_mac_language_map,
+				   ARRAY_LENGTH (hb_mac_language_map));
+}
diff --git a/src/hb-ot-name-language.hh b/src/hb-ot-name-language.hh
new file mode 100644
index 0000000..903076c
--- /dev/null
+++ b/src/hb-ot-name-language.hh
@@ -0,0 +1,40 @@
+/*
+ * 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_OT_NAME_LANGUAGE_HH
+#define HB_OT_NAME_LANGUAGE_HH
+
+#include "hb.hh"
+
+
+HB_INTERNAL hb_language_t
+_hb_ot_name_language_for_ms_code (unsigned int code);
+
+HB_INTERNAL hb_language_t
+_hb_ot_name_language_for_mac_code (unsigned int code);
+
+
+#endif /* HB_OT_NAME_LANGUAGE_HH */
diff --git a/src/hb-ot-name-table.hh b/src/hb-ot-name-table.hh
index bb49c2c..705ae0e 100644
--- a/src/hb-ot-name-table.hh
+++ b/src/hb-ot-name-table.hh
@@ -28,37 +28,72 @@
 #define HB_OT_NAME_TABLE_HH
 
 #include "hb-open-type.hh"
+#include "hb-ot-name-language.hh"
+#include "hb-aat-layout.hh"
 
 
 namespace OT {
 
 
+#define entry_score var.u16[0]
+#define entry_index var.u16[1]
+
+
 /*
  * name -- Naming
  * https://docs.microsoft.com/en-us/typography/opentype/spec/name
  */
 #define HB_OT_TAG_name HB_TAG('n','a','m','e')
 
+#define UNSUPPORTED	42
 
 struct NameRecord
 {
-  static int cmp (const void *pa, const void *pb)
+  hb_language_t language (hb_face_t *face) const
   {
-    const NameRecord *a = (const NameRecord *) pa;
-    const NameRecord *b = (const NameRecord *) pb;
-    int ret;
-    ret = b->platformID.cmp (a->platformID);
-    if (ret) return ret;
-    ret = b->encodingID.cmp (a->encodingID);
-    if (ret) return ret;
-    ret = b->languageID.cmp (a->languageID);
-    if (ret) return ret;
-    ret = b->nameID.cmp (a->nameID);
-    if (ret) return ret;
-    return 0;
+    unsigned int p = platformID;
+    unsigned int l = languageID;
+
+    if (p == 3)
+      return _hb_ot_name_language_for_ms_code (l);
+
+    if (p == 1)
+      return _hb_ot_name_language_for_mac_code (l);
+
+    if (p == 0)
+      return _hb_aat_language_get (face, l);
+
+    return HB_LANGUAGE_INVALID;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  uint16_t score () const
+  {
+    /* Same order as in cmap::find_best_subtable(). */
+    unsigned int p = platformID;
+    unsigned int e = encodingID;
+
+    /* 32-bit. */
+    if (p == 3 && e == 10) return 0;
+    if (p == 0 && e ==  6) return 1;
+    if (p == 0 && e ==  4) return 2;
+
+    /* 16-bit. */
+    if (p == 3 && e ==  1) return 3;
+    if (p == 0 && e ==  3) return 4;
+    if (p == 0 && e ==  2) return 5;
+    if (p == 0 && e ==  1) return 6;
+    if (p == 0 && e ==  0) return 7;
+
+    /* Symbol. */
+    if (p == 3 && e ==  0) return 8;
+
+    /* We treat all Mac Latin names as ASCII only. */
+    if (p == 1 && e ==  0) return 10; /* 10 is magic number :| */
+
+    return UNSUPPORTED;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     /* We can check from base all the way up to the end of string... */
@@ -75,63 +110,168 @@
   DEFINE_SIZE_STATIC (12);
 };
 
+static int
+_hb_ot_name_entry_cmp_key (const void *pa, const void *pb)
+{
+  const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
+  const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
+
+  /* Compare by name_id, then language. */
+
+  if (a->name_id != b->name_id)
+    return a->name_id < b->name_id ? -1 : +1;
+
+  if (a->language == b->language) return 0;
+  if (!a->language) return -1;
+  if (!b->language) return +1;
+  return strcmp (hb_language_to_string (a->language),
+		 hb_language_to_string (b->language));
+}
+
+static int
+_hb_ot_name_entry_cmp (const void *pa, const void *pb)
+{
+  /* Compare by name_id, then language, then score, then index. */
+
+  int v = _hb_ot_name_entry_cmp_key (pa, pb);
+  if (v)
+    return v;
+
+  const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
+  const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
+
+  if (a->entry_score != b->entry_score)
+    return a->entry_score < b->entry_score ? -1 : +1;
+
+  if (a->entry_index != b->entry_index)
+    return a->entry_index < b->entry_index ? -1 : +1;
+
+  return 0;
+}
+
 struct name
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_name;
+  enum { tableTag = HB_OT_TAG_name };
 
-  inline unsigned int get_name (unsigned int platform_id,
-				unsigned int encoding_id,
-				unsigned int language_id,
-				unsigned int name_id,
-				void *buffer,
-				unsigned int buffer_length) const
+  unsigned int get_size () const
+  { return min_size + count * nameRecordZ.item_size; }
+
+  bool sanitize_records (hb_sanitize_context_t *c) const
   {
-    NameRecord key;
-    key.platformID.set (platform_id);
-    key.encodingID.set (encoding_id);
-    key.languageID.set (language_id);
-    key.nameID.set (name_id);
-    NameRecord *match = (NameRecord *) bsearch (&key, nameRecordZ.arrayZ, count, sizeof (nameRecordZ[0]), NameRecord::cmp);
-
-    if (!match)
-      return 0;
-
-    unsigned int length = MIN (buffer_length, (unsigned int) match->length);
-    memcpy (buffer, (char *) this + stringOffset + match->offset, length);
-    return length;
-  }
-
-  inline unsigned int get_size (void) const
-  { return min_size + count * nameRecordZ[0].min_size; }
-
-  inline bool sanitize_records (hb_sanitize_context_t *c) const {
     TRACE_SANITIZE (this);
-    char *string_pool = (char *) this + stringOffset;
+    const void *string_pool = (this+stringOffset).arrayZ;
     unsigned int _count = count;
+    /* Move to run-time?! */
     for (unsigned int i = 0; i < _count; i++)
       if (!nameRecordZ[i].sanitize (c, string_pool)) return_trace (false);
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  likely (format == 0 || format == 1) &&
 		  c->check_array (nameRecordZ.arrayZ, count) &&
-		  sanitize_records (c));
+		  c->check_range (this, stringOffset));
   }
 
+  struct accelerator_t
+  {
+    void init (hb_face_t *face)
+    {
+      this->table = hb_sanitize_context_t().reference_table<name> (face);
+      assert (this->table.get_length () >= this->table->stringOffset);
+      this->pool = (const char *) (const void *) (this->table+this->table->stringOffset);
+      this->pool_len = this->table.get_length () - this->table->stringOffset;
+      const hb_array_t<const NameRecord> all_names (this->table->nameRecordZ.arrayZ,
+						    this->table->count);
+
+      this->names.init ();
+      this->names.alloc (all_names.len);
+
+      for (unsigned int i = 0; i < all_names.len; i++)
+      {
+	hb_ot_name_entry_t *entry = this->names.push ();
+
+	entry->name_id = all_names[i].nameID;
+	entry->language = all_names[i].language (face);
+	entry->entry_score =  all_names[i].score ();
+	entry->entry_index = i;
+      }
+
+      this->names.qsort (_hb_ot_name_entry_cmp);
+      /* Walk and pick best only for each name_id,language pair,
+       * while dropping unsupported encodings. */
+      unsigned int j = 0;
+      for (unsigned int i = 0; i < this->names.len; i++)
+      {
+        if (this->names[i].entry_score == UNSUPPORTED ||
+	    this->names[i].language == HB_LANGUAGE_INVALID)
+	  continue;
+        if (i &&
+	    this->names[i - 1].name_id  == this->names[i].name_id &&
+	    this->names[i - 1].language == this->names[i].language)
+	  continue;
+	this->names[j++] = this->names[i];
+      }
+      this->names.resize (j);
+    }
+
+    void fini ()
+    {
+      this->names.fini ();
+      this->table.destroy ();
+    }
+
+    int get_index (hb_ot_name_id_t   name_id,
+			  hb_language_t     language,
+			  unsigned int     *width=nullptr) const
+    {
+      const hb_ot_name_entry_t key = {name_id, {0}, language};
+      const hb_ot_name_entry_t *entry = (const hb_ot_name_entry_t *)
+					hb_bsearch (&key,
+						    this->names.arrayZ(),
+						    this->names.len,
+						    sizeof (key),
+						    _hb_ot_name_entry_cmp_key);
+      if (!entry)
+        return -1;
+
+      if (width)
+        *width = entry->entry_score < 10 ? 2 : 1;
+
+      return entry->entry_index;
+    }
+
+    hb_bytes_t get_name (unsigned int idx) const
+    {
+      const hb_array_t<const NameRecord> all_names (table->nameRecordZ.arrayZ, table->count);
+      const NameRecord &record = all_names[idx];
+      const hb_bytes_t string_pool (pool, pool_len);
+      return string_pool.sub_array (record.offset, record.length);
+    }
+
+    private:
+    const char *pool;
+    unsigned int pool_len;
+    public:
+    hb_blob_ptr_t<name> table;
+    hb_vector_t<hb_ot_name_entry_t> names;
+  };
+
   /* We only implement format 0 for now. */
   HBUINT16	format;			/* Format selector (=0/1). */
   HBUINT16	count;			/* Number of name records. */
-  Offset16	stringOffset;		/* Offset to start of string storage (from start of table). */
+  OffsetTo<UnsizedArrayOf<HBUINT8>, HBUINT16, false>
+		stringOffset;		/* Offset to start of string storage (from start of table). */
   UnsizedArrayOf<NameRecord>
 		nameRecordZ;		/* The name records where count is the number of records. */
   public:
   DEFINE_SIZE_ARRAY (6, nameRecordZ);
 };
 
+struct name_accelerator_t : name::accelerator_t {};
 
 } /* namespace OT */
 
diff --git a/src/hb-ot-name.cc b/src/hb-ot-name.cc
new file mode 100644
index 0000000..b2eda29
--- /dev/null
+++ b/src/hb-ot-name.cc
@@ -0,0 +1,224 @@
+/*
+ * 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
+ */
+
+#include "hb.hh"
+
+#include "hb-ot-name-table.hh"
+
+#include "hb-ot-face.hh"
+#include "hb-utf.hh"
+
+
+/**
+ * SECTION:hb-ot-name
+ * @title: hb-ot-name
+ * @short_description: OpenType font name information
+ * @include: hb-ot.h
+ *
+ * Functions for fetching name strings from OpenType fonts.
+ **/
+
+
+/**
+ * hb_ot_name_list_names:
+ * @face: font face.
+ * @num_entries: (out) (allow-none): number of returned entries.
+ *
+ * Enumerates all available name IDs and language combinations. Returned
+ * array is owned by the @face and should not be modified.  It can be
+ * used as long as @face is alive.
+ *
+ * Returns: (out) (transfer none) (array length=num_entries): Array of available name entries.
+ * Since: 2.1.0
+ **/
+const hb_ot_name_entry_t *
+hb_ot_name_list_names (hb_face_t    *face,
+		       unsigned int *num_entries /* OUT */)
+{
+  const OT::name_accelerator_t &name = *face->table.name;
+  if (num_entries) *num_entries = name.names.len;
+  return name.names.arrayZ();
+}
+
+
+template <typename in_utf_t, typename out_utf_t>
+static inline unsigned int
+hb_ot_name_convert_utf (hb_bytes_t                       bytes,
+			unsigned int                    *text_size /* IN/OUT */,
+			typename out_utf_t::codepoint_t *text /* OUT */)
+{
+  unsigned int src_len = bytes.len / sizeof (typename in_utf_t::codepoint_t);
+  const typename in_utf_t::codepoint_t *src = (const typename in_utf_t::codepoint_t *) bytes.arrayZ;
+  const typename in_utf_t::codepoint_t *src_end = src + src_len;
+
+  typename out_utf_t::codepoint_t *dst = text;
+
+  hb_codepoint_t unicode;
+  const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
+
+  if (text_size && *text_size)
+  {
+    (*text_size)--; /* Same room for NUL-termination. */
+    const typename out_utf_t::codepoint_t *dst_end = text + *text_size;
+
+    while (src < src_end && dst < dst_end)
+    {
+      const typename in_utf_t::codepoint_t *src_next = in_utf_t::next (src, src_end, &unicode, replacement);
+      typename out_utf_t::codepoint_t *dst_next = out_utf_t::encode (dst, dst_end, unicode);
+      if (dst_next == dst)
+        break; /* Out-of-room. */
+
+      dst = dst_next;
+      src = src_next;
+    };
+
+    *text_size = dst - text;
+    *dst = 0; /* NUL-terminate. */
+  }
+
+  /* Accumulate length of rest. */
+  unsigned int dst_len = dst - text;
+  while (src < src_end)
+  {
+    src = in_utf_t::next (src, src_end, &unicode, replacement);
+    dst_len += out_utf_t::encode_len (unicode);
+  };
+  return dst_len;
+}
+
+template <typename utf_t>
+static inline unsigned int
+hb_ot_name_get_utf (hb_face_t       *face,
+		    hb_ot_name_id_t  name_id,
+		    hb_language_t    language,
+		    unsigned int    *text_size /* IN/OUT */,
+		    typename utf_t::codepoint_t *text /* OUT */)
+{
+  const OT::name_accelerator_t &name = *face->table.name;
+
+  if (!language)
+    language = hb_language_from_string ("en", 2);
+
+  unsigned int width;
+  int idx = name.get_index (name_id, language, &width);
+  if (idx != -1)
+  {
+    hb_bytes_t bytes = name.get_name (idx);
+
+    if (width == 2) /* UTF16-BE */
+      return hb_ot_name_convert_utf<hb_utf16_be_t, utf_t> (bytes, text_size, text);
+
+    if (width == 1) /* ASCII */
+      return hb_ot_name_convert_utf<hb_ascii_t, utf_t> (bytes, text_size, text);
+  }
+
+  if (text_size)
+  {
+    if (*text_size)
+      *text = 0;
+    *text_size = 0;
+  }
+  return 0;
+}
+
+/**
+ * hb_ot_name_get_utf8:
+ * @face: font face.
+ * @name_id: OpenType name identifier to fetch.
+ * @language: language to fetch the name for.
+ * @text_size: (inout) (allow-none): input size of @text buffer, and output size of
+ *                                   text written to buffer.
+ * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into.
+ *
+ * Fetches a font name from the OpenType 'name' table.
+ * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed.
+ * Returns string in UTF-8 encoding.
+ *
+ * Returns: full length of the requested string, or 0 if not found.
+ * Since: 2.1.0
+ **/
+unsigned int
+hb_ot_name_get_utf8 (hb_face_t       *face,
+		     hb_ot_name_id_t  name_id,
+		     hb_language_t    language,
+		     unsigned int    *text_size /* IN/OUT */,
+		     char            *text      /* OUT */)
+{
+  return hb_ot_name_get_utf<hb_utf8_t> (face, name_id, language, text_size,
+					(hb_utf8_t::codepoint_t *) text);
+}
+
+/**
+ * hb_ot_name_get_utf16:
+ * @face: font face.
+ * @name_id: OpenType name identifier to fetch.
+ * @language: language to fetch the name for.
+ * @text_size: (inout) (allow-none): input size of @text buffer, and output size of
+ *                                   text written to buffer.
+ * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into.
+ *
+ * Fetches a font name from the OpenType 'name' table.
+ * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed.
+ * Returns string in UTF-16 encoding.
+ *
+ * Returns: full length of the requested string, or 0 if not found.
+ * Since: 2.1.0
+ **/
+unsigned int
+hb_ot_name_get_utf16 (hb_face_t       *face,
+		      hb_ot_name_id_t  name_id,
+		      hb_language_t    language,
+		      unsigned int    *text_size /* IN/OUT */,
+		      uint16_t        *text      /* OUT */)
+{
+  return hb_ot_name_get_utf<hb_utf16_t> (face, name_id, language, text_size, text);
+}
+
+/**
+ * hb_ot_name_get_utf32:
+ * @face: font face.
+ * @name_id: OpenType name identifier to fetch.
+ * @language: language to fetch the name for.
+ * @text_size: (inout) (allow-none): input size of @text buffer, and output size of
+ *                                   text written to buffer.
+ * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into.
+ *
+ * Fetches a font name from the OpenType 'name' table.
+ * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed.
+ * Returns string in UTF-32 encoding.
+ *
+ * Returns: full length of the requested string, or 0 if not found.
+ * Since: 2.1.0
+ **/
+unsigned int
+hb_ot_name_get_utf32 (hb_face_t       *face,
+		      hb_ot_name_id_t  name_id,
+		      hb_language_t    language,
+		      unsigned int    *text_size /* IN/OUT */,
+		      uint32_t        *text      /* OUT */)
+{
+  return hb_ot_name_get_utf<hb_utf32_t> (face, name_id, language, text_size, text);
+}
diff --git a/src/hb-ot-name.h b/src/hb-ot-name.h
index 49423e8..3b4ad58 100644
--- a/src/hb-ot-name.h
+++ b/src/hb-ot-name.h
@@ -35,18 +35,94 @@
 
 
 /**
- * hb_name_id_t:
+ * hb_ot_name_id_t:
+ * @HB_OT_NAME_ID_INVALID: Value to represent a nonexistent name ID.
  *
- * Since: 2.0.0
- */
-typedef unsigned int hb_name_id_t;
-
-/**
- * HB_NAME_ID_INVALID
+ * An integral type representing an OpenType 'name' table name identifier.
+ * There are predefined name IDs, as well as name IDs return from other
+ * API.  These can be used to fetch name strings from a font face.
  *
  * Since: 2.0.0
  **/
-#define HB_NAME_ID_INVALID 0xFFFF
+enum
+{
+  HB_OT_NAME_ID_COPYRIGHT		= 0,
+  HB_OT_NAME_ID_FONT_FAMILY		= 1,
+  HB_OT_NAME_ID_FONT_SUBFAMILY		= 2,
+  HB_OT_NAME_ID_UNIQUE_ID		= 3,
+  HB_OT_NAME_ID_FULL_NAME		= 4,
+  HB_OT_NAME_ID_VERSION_STRING		= 5,
+  HB_OT_NAME_ID_POSTSCRIPT_NAME		= 6,
+  HB_OT_NAME_ID_TRADEMARK		= 7,
+  HB_OT_NAME_ID_MANUFACTURER		= 8,
+  HB_OT_NAME_ID_DESIGNER		= 9,
+  HB_OT_NAME_ID_DESCRIPTION		= 10,
+  HB_OT_NAME_ID_VENDOR_URL		= 11,
+  HB_OT_NAME_ID_DESIGNER_URL		= 12,
+  HB_OT_NAME_ID_LICENSE			= 13,
+  HB_OT_NAME_ID_LICENSE_URL		= 14,
+/*HB_OT_NAME_ID_RESERVED		= 15,*/
+  HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY	= 16,
+  HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY	= 17,
+  HB_OT_NAME_ID_MAC_FULL_NAME		= 18,
+  HB_OT_NAME_ID_SAMPLE_TEXT		= 19,
+  HB_OT_NAME_ID_CID_FINDFONT_NAME	= 20,
+  HB_OT_NAME_ID_WWS_FAMILY		= 21,
+  HB_OT_NAME_ID_WWS_SUBFAMILY		= 22,
+  HB_OT_NAME_ID_LIGHT_BACKGROUND	= 23,
+  HB_OT_NAME_ID_DARK_BACKGROUND		= 24,
+  HB_OT_NAME_ID_VARIATIONS_PS_PREFIX	= 25,
+
+  HB_OT_NAME_ID_INVALID			= 0xFFFF
+};
+
+typedef unsigned int hb_ot_name_id_t;
+
+
+/**
+ * hb_ot_name_entry_t:
+ * @name_id: name ID
+ * @language: language
+ *
+ * Structure representing a name ID in a particular language.
+ *
+ * Since: 2.1.0
+ **/
+typedef struct hb_ot_name_entry_t
+{
+  hb_ot_name_id_t name_id;
+  /*< private >*/
+  hb_var_int_t    var;
+  /*< public >*/
+  hb_language_t   language;
+} hb_ot_name_entry_t;
+
+HB_EXTERN const hb_ot_name_entry_t *
+hb_ot_name_list_names (hb_face_t    *face,
+		       unsigned int *num_entries /* OUT */);
+
+
+HB_EXTERN unsigned int
+hb_ot_name_get_utf8 (hb_face_t       *face,
+		     hb_ot_name_id_t  name_id,
+		     hb_language_t    language,
+		     unsigned int    *text_size /* IN/OUT */,
+		     char            *text      /* OUT */);
+
+HB_EXTERN unsigned int
+hb_ot_name_get_utf16 (hb_face_t       *face,
+		      hb_ot_name_id_t  name_id,
+		      hb_language_t    language,
+		      unsigned int    *text_size /* IN/OUT */,
+		      uint16_t        *text      /* OUT */);
+
+HB_EXTERN unsigned int
+hb_ot_name_get_utf32 (hb_face_t       *face,
+		      hb_ot_name_id_t  name_id,
+		      hb_language_t    language,
+		      unsigned int    *text_size /* IN/OUT */,
+		      uint32_t        *text      /* OUT */);
+
 
 HB_END_DECLS
 
diff --git a/src/hb-ot-os2-table.hh b/src/hb-ot-os2-table.hh
index 71d2bf5..4c1812c 100644
--- a/src/hb-ot-os2-table.hh
+++ b/src/hb-ot-os2-table.hh
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2011,2012  Google, Inc.
+ * Copyright © 2018  Ebrahim Byagowi
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -30,32 +31,128 @@
 #include "hb-open-type.hh"
 #include "hb-ot-os2-unicode-ranges.hh"
 
-namespace OT {
+#include "hb-set.hh"
 
 /*
  * OS/2 and Windows Metrics
  * https://docs.microsoft.com/en-us/typography/opentype/spec/os2
  */
-#define HB_OT_TAG_os2 HB_TAG('O','S','/','2')
+#define HB_OT_TAG_OS2 HB_TAG('O','S','/','2')
 
-struct os2
+
+namespace OT {
+
+struct OS2V1Tail
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_os2;
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
 
-  inline bool subset (hb_subset_plan_t *plan) const
+  public:
+  HBUINT32	ulCodePageRange1;
+  HBUINT32	ulCodePageRange2;
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct OS2V2Tail
+{
+  bool sanitize (hb_sanitize_context_t *c) const
   {
-    hb_blob_t *os2_blob = hb_sanitize_context_t().reference_table<os2> (plan->source);
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  HBINT16	sxHeight;
+  HBINT16	sCapHeight;
+  HBUINT16	usDefaultChar;
+  HBUINT16	usBreakChar;
+  HBUINT16	usMaxContext;
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
+struct OS2V5Tail
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  HBUINT16	usLowerOpticalPointSize;
+  HBUINT16	usUpperOpticalPointSize;
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct OS2
+{
+  enum { tableTag = HB_OT_TAG_OS2 };
+
+  bool has_data () const { return this != &Null (OS2); }
+
+  const OS2V1Tail &v1 () const { return version >= 1 ? v1X : Null (OS2V1Tail); }
+  const OS2V2Tail &v2 () const { return version >= 2 ? v2X : Null (OS2V2Tail); }
+  const OS2V5Tail &v5 () const { return version >= 5 ? v5X : Null (OS2V5Tail); }
+
+  enum selection_flag_t {
+    ITALIC		= 1u<<0,
+    UNDERSCORE		= 1u<<1,
+    NEGATIVE		= 1u<<2,
+    OUTLINED		= 1u<<3,
+    STRIKEOUT		= 1u<<4,
+    BOLD		= 1u<<5,
+    REGULAR		= 1u<<6,
+    USE_TYPO_METRICS	= 1u<<7,
+    WWS			= 1u<<8,
+    OBLIQUE		= 1u<<9
+  };
+
+  bool is_italic () const       { return fsSelection & ITALIC; }
+  bool is_oblique () const      { return fsSelection & OBLIQUE; }
+  bool is_typo_metrics () const { return fsSelection & USE_TYPO_METRICS; }
+
+  enum width_class_t {
+    FWIDTH_ULTRA_CONDENSED	= 1, /* 50% */
+    FWIDTH_EXTRA_CONDENSED	= 2, /* 62.5% */
+    FWIDTH_CONDENSED		= 3, /* 75% */
+    FWIDTH_SEMI_CONDENSED	= 4, /* 87.5% */
+    FWIDTH_NORMAL		= 5, /* 100% */
+    FWIDTH_SEMI_EXPANDED	= 6, /* 112.5% */
+    FWIDTH_EXPANDED		= 7, /* 125% */
+    FWIDTH_EXTRA_EXPANDED	= 8, /* 150% */
+    FWIDTH_ULTRA_EXPANDED	= 9  /* 200% */
+  };
+
+  float get_width () const
+  {
+    switch (usWidthClass) {
+    case FWIDTH_ULTRA_CONDENSED:return 50.f;
+    case FWIDTH_EXTRA_CONDENSED:return 62.5f;
+    case FWIDTH_CONDENSED:	return 75.f;
+    case FWIDTH_SEMI_CONDENSED:	return 87.5f;
+    default:
+    case FWIDTH_NORMAL:		return 100.f;
+    case FWIDTH_SEMI_EXPANDED:	return 112.5f;
+    case FWIDTH_EXPANDED:	return 125.f;
+    case FWIDTH_EXTRA_EXPANDED:	return 150.f;
+    case FWIDTH_ULTRA_EXPANDED:	return 200.f;
+    }
+  }
+
+  bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_blob_t *os2_blob = hb_sanitize_context_t ().reference_table<OS2> (plan->source);
     hb_blob_t *os2_prime_blob = hb_blob_create_sub_blob (os2_blob, 0, -1);
     // TODO(grieger): move to hb_blob_copy_writable_or_fail
     hb_blob_destroy (os2_blob);
 
-    os2 *os2_prime = (os2 *) hb_blob_get_data_writable (os2_prime_blob, nullptr);
+    OS2 *os2_prime = (OS2 *) hb_blob_get_data_writable (os2_prime_blob, nullptr);
     if (unlikely (!os2_prime)) {
       hb_blob_destroy (os2_prime_blob);
       return false;
@@ -67,14 +164,14 @@
     os2_prime->usLastCharIndex.set (max_cp);
 
     _update_unicode_ranges (plan->unicodes, os2_prime->ulUnicodeRange);
-    bool result = plan->add_table (HB_OT_TAG_os2, os2_prime_blob);
+    bool result = plan->add_table (HB_OT_TAG_OS2, os2_prime_blob);
 
     hb_blob_destroy (os2_prime_blob);
     return result;
   }
 
-  inline void _update_unicode_ranges (const hb_set_t *codepoints,
-                                      HBUINT32 ulUnicodeRange[4]) const
+  void _update_unicode_ranges (const hb_set_t *codepoints,
+			       HBUINT32 ulUnicodeRange[4]) const
   {
     for (unsigned int i = 0; i < 4; i++)
       ulUnicodeRange[i].set (0);
@@ -84,24 +181,24 @@
       unsigned int bit = _hb_ot_os2_get_unicode_range_bit (cp);
       if (bit < 128)
       {
-        unsigned int block = bit / 32;
-        unsigned int bit_in_block = bit % 32;
-        unsigned int mask = 1 << bit_in_block;
-        ulUnicodeRange[block].set (ulUnicodeRange[block] | mask);
+	unsigned int block = bit / 32;
+	unsigned int bit_in_block = bit % 32;
+	unsigned int mask = 1 << bit_in_block;
+	ulUnicodeRange[block].set (ulUnicodeRange[block] | mask);
       }
       if (cp >= 0x10000 && cp <= 0x110000)
       {
-        /* the spec says that bit 57 ("Non Plane 0") implies that there's
-           at least one codepoint beyond the BMP; so I also include all
-           the non-BMP codepoints here */
-        ulUnicodeRange[1].set (ulUnicodeRange[1] | (1 << 25));
+	/* the spec says that bit 57 ("Non Plane 0") implies that there's
+	   at least one codepoint beyond the BMP; so I also include all
+	   the non-BMP codepoints here */
+	ulUnicodeRange[1].set (ulUnicodeRange[1] | (1 << 25));
       }
     }
   }
 
-  static inline void find_min_and_max_codepoint (const hb_set_t *codepoints,
-                                                 uint16_t *min_cp, /* OUT */
-                                                 uint16_t *max_cp  /* OUT */)
+  static void find_min_and_max_codepoint (const hb_set_t *codepoints,
+						 uint16_t *min_cp, /* OUT */
+						 uint16_t *max_cp  /* OUT */)
   {
     *min_cp = codepoints->get_min ();
     *max_cp = codepoints->get_max ();
@@ -118,17 +215,21 @@
   };
 
   // https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681
-  inline font_page_t get_font_page () const
+  font_page_t get_font_page () const
+  { return (font_page_t) (version == 0 ? fsSelection & 0xFF00 : 0); }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
-    if (version != 0)
-      return (font_page_t) 0;
-    return (font_page_t) (fsSelection & 0xFF00);
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this))) return_trace (false);
+    if (unlikely (version >= 1 && !v1X.sanitize (c))) return_trace (false);
+    if (unlikely (version >= 2 && !v2X.sanitize (c))) return_trace (false);
+    if (unlikely (version >= 5 && !v5X.sanitize (c))) return_trace (false);
+    return_trace (true);
   }
 
   public:
   HBUINT16	version;
-
-  /* Version 0 */
   HBINT16	xAvgCharWidth;
   HBUINT16	usWeightClass;
   HBUINT16	usWidthClass;
@@ -155,24 +256,11 @@
   HBINT16	sTypoLineGap;
   HBUINT16	usWinAscent;
   HBUINT16	usWinDescent;
-
-  /* Version 1 */
-  //HBUINT32	ulCodePageRange1;
-  //HBUINT32	ulCodePageRange2;
-
-  /* Version 2 */
-  //HBINT16	sxHeight;
-  //HBINT16	sCapHeight;
-  //HBUINT16	usDefaultChar;
-  //HBUINT16	usBreakChar;
-  //HBUINT16	usMaxContext;
-
-  /* Version 5 */
-  //HBUINT16	usLowerOpticalPointSize;
-  //HBUINT16	usUpperOpticalPointSize;
-
+  OS2V1Tail	v1X;
+  OS2V2Tail	v2X;
+  OS2V5Tail	v5X;
   public:
-  DEFINE_SIZE_STATIC (78);
+  DEFINE_SIZE_MIN (78);
 };
 
 } /* namespace OT */
diff --git a/src/hb-ot-os2-unicode-ranges.hh b/src/hb-ot-os2-unicode-ranges.hh
index 1978008..b0ccd00 100644
--- a/src/hb-ot-os2-unicode-ranges.hh
+++ b/src/hb-ot-os2-unicode-ranges.hh
@@ -34,7 +34,7 @@
 struct OS2Range
 {
   static int
-  cmp (const void *_key, const void *_item, void *_arg)
+  cmp (const void *_key, const void *_item)
   {
     hb_codepoint_t cp = *((hb_codepoint_t *) _key);
     const OS2Range *range = (OS2Range *) _item;
@@ -233,10 +233,10 @@
 static unsigned int
 _hb_ot_os2_get_unicode_range_bit (hb_codepoint_t cp)
 {
-  OS2Range *range = (OS2Range*) hb_bsearch_r (&cp, _hb_os2_unicode_ranges,
-					      ARRAY_LENGTH (_hb_os2_unicode_ranges),
-					      sizeof (OS2Range),
-					      OS2Range::cmp, nullptr);
+  OS2Range *range = (OS2Range*) hb_bsearch (&cp, _hb_os2_unicode_ranges,
+					    ARRAY_LENGTH (_hb_os2_unicode_ranges),
+					    sizeof (OS2Range),
+					    OS2Range::cmp);
   if (range != nullptr)
     return range->bit;
   return -1;
diff --git a/src/hb-ot-post-table.hh b/src/hb-ot-post-table.hh
index bd049f9..33b7070 100644
--- a/src/hb-ot-post-table.hh
+++ b/src/hb-ot-post-table.hh
@@ -49,48 +49,39 @@
 
 struct postV2Tail
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  friend struct post;
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (glyphNameIndex.sanitize (c));
   }
 
+  protected:
   ArrayOf<HBUINT16>	glyphNameIndex;	/* This is not an offset, but is the
 					 * ordinal number of the glyph in 'post'
 					 * string tables. */
-  UnsizedArrayOf<HBUINT8>
-			namesX;		/* Glyph names with length bytes [variable]
+/*UnsizedArrayOf<HBUINT8>
+			namesX;*/	/* Glyph names with length bytes [variable]
 					 * (a Pascal string). */
 
-  DEFINE_SIZE_ARRAY2 (2, glyphNameIndex, namesX);
+  public:
+  DEFINE_SIZE_ARRAY (2, glyphNameIndex);
 };
 
 struct post
 {
-  static const hb_tag_t tableTag = HB_OT_TAG_post;
+  enum { tableTag = HB_OT_TAG_post };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (this)))
-      return_trace (false);
-    if (version.to_int () == 0x00020000)
-    {
-      const postV2Tail &v2 = StructAfter<postV2Tail> (*this);
-      return_trace (v2.sanitize (c));
-    }
-    return_trace (true);
-  }
-
-  inline bool subset (hb_subset_plan_t *plan) const
+  bool subset (hb_subset_plan_t *plan) const
   {
     unsigned int post_prime_length;
-    hb_blob_t *post_blob = hb_sanitize_context_t().reference_table<post>(plan->source);
-    hb_blob_t *post_prime_blob = hb_blob_create_sub_blob (post_blob, 0, post::static_size);
+    hb_blob_t *post_blob = hb_sanitize_context_t ().reference_table<post>(plan->source);
+    hb_blob_t *post_prime_blob = hb_blob_create_sub_blob (post_blob, 0, post::min_size);
     post *post_prime = (post *) hb_blob_get_data_writable (post_prime_blob, &post_prime_length);
     hb_blob_destroy (post_blob);
 
-    if (unlikely (!post_prime || post_prime_length != post::static_size))
+    if (unlikely (!post_prime || post_prime_length != post::min_size))
     {
       hb_blob_destroy (post_prime_blob);
       DEBUG_MSG(SUBSET, nullptr, "Invalid source post table with length %d.", post_prime_length);
@@ -106,61 +97,55 @@
 
   struct accelerator_t
   {
-    inline void init (hb_face_t *face)
+    void init (hb_face_t *face)
     {
       index_to_offset.init ();
 
-      blob = hb_sanitize_context_t().reference_table<post> (face);
-      const post *table = blob->as<post> ();
-      unsigned int table_length = blob->length;
+      table = hb_sanitize_context_t ().reference_table<post> (face);
+      unsigned int table_length = table.get_length ();
 
       version = table->version.to_int ();
-      if (version != 0x00020000)
-        return;
+      if (version != 0x00020000) return;
 
-      const postV2Tail &v2 = StructAfter<postV2Tail> (*table);
+      const postV2Tail &v2 = table->v2X;
 
       glyphNameIndex = &v2.glyphNameIndex;
       pool = &StructAfter<uint8_t> (v2.glyphNameIndex);
 
-      const uint8_t *end = (uint8_t *) table + table_length;
-      for (const uint8_t *data = pool; data < end && data + *data <= end; data += 1 + *data)
+      const uint8_t *end = (const uint8_t *) (const void *) table + table_length;
+      for (const uint8_t *data = pool;
+	   index_to_offset.len < 65535 && data < end && data + *data < end;
+	   data += 1 + *data)
 	index_to_offset.push (data - pool);
     }
-    inline void fini (void)
+    void fini ()
     {
       index_to_offset.fini ();
       free (gids_sorted_by_name.get ());
-      hb_blob_destroy (blob);
+      table.destroy ();
     }
 
-    inline bool get_glyph_name (hb_codepoint_t glyph,
-				char *buf, unsigned int buf_len) const
+    bool get_glyph_name (hb_codepoint_t glyph,
+			 char *buf, unsigned int buf_len) const
     {
       hb_bytes_t s = find_glyph_name (glyph);
-      if (!s.len)
-        return false;
-      if (!buf_len)
-	return true;
-      if (buf_len <= s.len) /* What to do with truncation? Returning false for now. */
-        return false;
-      strncpy (buf, s.arrayZ, s.len);
-      buf[s.len] = '\0';
+      if (!s.len) return false;
+      if (!buf_len) return true;
+      unsigned int len = MIN (buf_len - 1, s.len);
+      strncpy (buf, s.arrayZ, len);
+      buf[len] = '\0';
       return true;
     }
 
-    inline bool get_glyph_from_name (const char *name, int len,
-				     hb_codepoint_t *glyph) const
+    bool get_glyph_from_name (const char *name, int len,
+			      hb_codepoint_t *glyph) const
     {
       unsigned int count = get_glyph_count ();
-      if (unlikely (!count))
-        return false;
+      if (unlikely (!count)) return false;
 
-      if (len < 0)
-	len = strlen (name);
+      if (len < 0) len = strlen (name);
 
-      if (unlikely (!len))
-	return false;
+      if (unlikely (!len)) return false;
 
     retry:
       uint16_t *gids = gids_sorted_by_name.get ();
@@ -183,7 +168,8 @@
       }
 
       hb_bytes_t st (name, len);
-      const uint16_t *gid = (const uint16_t *) hb_bsearch_r (&st, gids, count, sizeof (gids[0]), cmp_key, (void *) this);
+      const uint16_t *gid = (const uint16_t *) hb_bsearch_r (hb_addressof (st), gids, count,
+							     sizeof (gids[0]), cmp_key, (void *) this);
       if (gid)
       {
 	*glyph = *gid;
@@ -195,18 +181,18 @@
 
     protected:
 
-    inline unsigned int get_glyph_count (void) const
+    unsigned int get_glyph_count () const
     {
       if (version == 0x00010000)
-        return NUM_FORMAT1_NAMES;
+	return NUM_FORMAT1_NAMES;
 
       if (version == 0x00020000)
-        return glyphNameIndex->len;
+	return glyphNameIndex->len;
 
       return 0;
     }
 
-    static inline int cmp_gids (const void *pa, const void *pb, void *arg)
+    static int cmp_gids (const void *pa, const void *pb, void *arg)
     {
       const accelerator_t *thiz = (const accelerator_t *) arg;
       uint16_t a = * (const uint16_t *) pa;
@@ -214,7 +200,7 @@
       return thiz->find_glyph_name (b).cmp (thiz->find_glyph_name (a));
     }
 
-    static inline int cmp_key (const void *pk, const void *po, void *arg)
+    static int cmp_key (const void *pk, const void *po, void *arg)
     {
       const accelerator_t *thiz = (const accelerator_t *) arg;
       const hb_bytes_t *key = (const hb_bytes_t *) pk;
@@ -222,7 +208,7 @@
       return thiz->find_glyph_name (o).cmp (*key);
     }
 
-    inline hb_bytes_t find_glyph_name (hb_codepoint_t glyph) const
+    hb_bytes_t find_glyph_name (hb_codepoint_t glyph) const
     {
       if (version == 0x00010000)
       {
@@ -252,7 +238,7 @@
     }
 
     private:
-    hb_blob_t *blob;
+    hb_blob_ptr_t<post> table;
     uint32_t version;
     const ArrayOf<HBUINT16> *glyphNameIndex;
     hb_vector_t<uint32_t, 1> index_to_offset;
@@ -260,6 +246,15 @@
     hb_atomic_ptr_t<uint16_t *> gids_sorted_by_name;
   };
 
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  (version.to_int () == 0x00010000 ||
+			   (version.to_int () == 0x00020000 && v2X.sanitize (c)) ||
+			   version.to_int () == 0x00030000)));
+  }
+
   public:
   FixedVersion<>version;		/* 0x00010000 for version 1.0
 					 * 0x00020000 for version 2.0
@@ -292,8 +287,8 @@
 					 * is downloaded as a Type 1 font. */
   HBUINT32	maxMemType1;		/* Maximum memory usage when an OpenType font
 					 * is downloaded as a Type 1 font. */
-/*postV2Tail	v2[VAR];*/
-  DEFINE_SIZE_STATIC (32);
+  postV2Tail	v2X;
+  DEFINE_SIZE_MIN (32);
 };
 
 struct post_accelerator_t : post::accelerator_t {};
diff --git a/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh
index 2aa0367..2a1f2f8 100644
--- a/src/hb-ot-shape-complex-arabic-fallback.hh
+++ b/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -79,8 +79,6 @@
    * May not be good-enough for presidential candidate interviews, but good-enough for us... */
   hb_stable_sort (&glyphs[0], num_glyphs, (int(*)(const OT::GlyphID*, const OT::GlyphID *)) OT::GlyphID::cmp, &substitutes[0]);
 
-  Supplier<OT::GlyphID> glyphs_supplier      (glyphs, num_glyphs);
-  Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs);
 
   /* Each glyph takes four bytes max, and there's some overhead. */
   char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128];
@@ -88,9 +86,8 @@
   OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
   bool ret = lookup->serialize_single (&c,
 				       OT::LookupFlag::IgnoreMarks,
-				       glyphs_supplier,
-				       substitutes_supplier,
-				       num_glyphs);
+				       hb_array (glyphs, num_glyphs),
+				       hb_array (substitutes, num_glyphs));
   c.end_serialize ();
   /* TODO sanitize the results? */
 
@@ -155,11 +152,6 @@
   if (!num_ligatures)
     return nullptr;
 
-  Supplier<OT::GlyphID>   first_glyphs_supplier                      (first_glyphs, num_first_glyphs);
-  Supplier<unsigned int > ligature_per_first_glyph_count_supplier    (ligature_per_first_glyph_count_list, num_first_glyphs);
-  Supplier<OT::GlyphID>   ligatures_supplier                         (ligature_list, num_ligatures);
-  Supplier<unsigned int > component_count_supplier                   (component_count_list, num_ligatures);
-  Supplier<OT::GlyphID>   component_supplier                         (component_list, num_ligatures);
 
   /* 16 bytes per ligature ought to be enough... */
   char buf[ARRAY_LENGTH_CONST (ligature_list) * 16 + 128];
@@ -167,12 +159,11 @@
   OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
   bool ret = lookup->serialize_ligature (&c,
 					 OT::LookupFlag::IgnoreMarks,
-					 first_glyphs_supplier,
-					 ligature_per_first_glyph_count_supplier,
-					 num_first_glyphs,
-					 ligatures_supplier,
-					 component_count_supplier,
-					 component_supplier);
+					 hb_array (first_glyphs, num_first_glyphs),
+					 hb_array (ligature_per_first_glyph_count_list, num_first_glyphs),
+					 hb_array (ligature_list, num_ligatures),
+					 hb_array (component_count_list, num_ligatures),
+					 hb_array (component_list, num_ligatures));
   c.end_serialize ();
   /* TODO sanitize the results? */
 
@@ -194,8 +185,6 @@
 
 struct arabic_fallback_plan_t
 {
-  ASSERT_POD ();
-
   unsigned int num_lookups;
   bool free_lookups;
 
@@ -204,7 +193,7 @@
   OT::hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS];
 };
 
-#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_WIN1256)
+#if defined(_WIN32) && !defined(HB_NO_WIN1256)
 #define HB_WITH_WIN1256
 #endif
 
@@ -214,15 +203,18 @@
 
 struct ManifestLookup
 {
+  public:
   OT::Tag tag;
   OT::OffsetTo<OT::SubstLookup> lookupOffset;
+  public:
+  DEFINE_SIZE_STATIC (6);
 };
 typedef OT::ArrayOf<ManifestLookup> Manifest;
 
 static bool
-arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan,
-				   const hb_ot_shape_plan_t *plan,
-				   hb_font_t *font)
+arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan HB_UNUSED,
+				   const hb_ot_shape_plan_t *plan HB_UNUSED,
+				   hb_font_t *font HB_UNUSED)
 {
 #ifdef HB_WITH_WIN1256
   /* Does this font look like it's Windows-1256-encoded? */
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index b564439..50a5213 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -243,8 +243,6 @@
 
 struct arabic_shape_plan_t
 {
-  ASSERT_POD ();
-
   /* The "+ 1" in the next array is to accommodate for the "NONE" command,
    * which is not an OpenType feature, but this simplifies the code by not
    * having to do a "if (... < NONE) ..." and just rely on the fact that
@@ -281,7 +279,7 @@
 {
   arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data;
 
-  arabic_fallback_plan_destroy (arabic_plan->fallback_plan.get ());
+  arabic_fallback_plan_destroy (arabic_plan->fallback_plan);
 
   free (data);
 }
@@ -391,7 +389,7 @@
     return;
 
 retry:
-  arabic_fallback_plan_t *fallback_plan = arabic_plan->fallback_plan.get ();
+  arabic_fallback_plan_t *fallback_plan = arabic_plan->fallback_plan;
   if (unlikely (!fallback_plan))
   {
     /* This sucks.  We need a font to build the fallback plan... */
@@ -416,7 +414,7 @@
 
 static void
 record_stch (const hb_ot_shape_plan_t *plan,
-	     hb_font_t *font,
+	     hb_font_t *font HB_UNUSED,
 	     hb_buffer_t *buffer)
 {
   const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
@@ -440,7 +438,7 @@
 }
 
 static void
-apply_stch (const hb_ot_shape_plan_t *plan,
+apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
 	    hb_buffer_t              *buffer,
 	    hb_font_t                *font)
 {
@@ -626,7 +624,7 @@
 }
 
 static void
-reorder_marks_arabic (const hb_ot_shape_plan_t *plan,
+reorder_marks_arabic (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		      hb_buffer_t              *buffer,
 		      unsigned int              start,
 		      unsigned int              end)
diff --git a/src/hb-ot-shape-complex-hangul.cc b/src/hb-ot-shape-complex-hangul.cc
index 9595402..e143867 100644
--- a/src/hb-ot-shape-complex-hangul.cc
+++ b/src/hb-ot-shape-complex-hangul.cc
@@ -70,8 +70,6 @@
 
 struct hangul_shape_plan_t
 {
-  ASSERT_POD ();
-
   hb_mask_t mask_array[HANGUL_FEATURE_COUNT];
 };
 
@@ -128,7 +126,7 @@
 }
 
 static void
-preprocess_text_hangul (const hb_ot_shape_plan_t *plan,
+preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED,
 			hb_buffer_t              *buffer,
 			hb_font_t                *font)
 {
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index f1ae303..d2d0a5a 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -25,6 +25,7 @@
  */
 
 #include "hb-ot-shape-complex-indic.hh"
+#include "hb-ot-shape-complex-vowel-constraints.hh"
 #include "hb-ot-layout.hh"
 
 
@@ -115,7 +116,8 @@
   {HB_TAG('c','j','c','t'), F_GLOBAL_MANUAL_JOINERS},
   /*
    * Other features.
-   * These features are applied all at once, after final_reordering.
+   * These features are applied all at once, after final_reordering
+   * but before clearing syllables.
    * Default Bengali font in Windows for example has intermixed
    * lookups for init,pres,abvs,blws features.
    */
@@ -224,7 +226,7 @@
 
 struct would_substitute_feature_t
 {
-  inline void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
+  void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
   {
     zero_context = zero_context_;
     map->get_stage_lookups (0/*GSUB*/,
@@ -232,9 +234,9 @@
 			    &lookups, &count);
   }
 
-  inline bool would_substitute (const hb_codepoint_t *glyphs,
-				unsigned int          glyphs_count,
-				hb_face_t            *face) const
+  bool would_substitute (const hb_codepoint_t *glyphs,
+			 unsigned int          glyphs_count,
+			 hb_face_t            *face) const
   {
     for (unsigned int i = 0; i < count; i++)
       if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context))
@@ -250,9 +252,7 @@
 
 struct indic_shape_plan_t
 {
-  ASSERT_POD ();
-
-  inline bool load_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
+  bool load_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
   {
     hb_codepoint_t glyph = virama_glyph.get_relaxed ();
     if (unlikely (glyph == (hb_codepoint_t) -1))
@@ -331,275 +331,6 @@
   free (data);
 }
 
-static void
-_output_with_dotted_circle (hb_buffer_t *buffer)
-{
-  hb_glyph_info_t &dottedcircle = buffer->output_glyph (0x25CCu);
-  _hb_glyph_info_reset_continuation (&dottedcircle);
-
-  buffer->next_glyph ();
-}
-
-static void
-preprocess_text_indic (const hb_ot_shape_plan_t *plan,
-		       hb_buffer_t              *buffer,
-		       hb_font_t                *font)
-{
-  /* UGLY UGLY UGLY business of adding dotted-circle in the middle of
-   * vowel-sequences that look like another vowel.  Data for each script
-   * collected from Unicode 11 book, tables named "Vowel Letters" with
-   * "Use" and "Do Not Use" columns.
-   *
-   * https://github.com/harfbuzz/harfbuzz/issues/1019
-   */
-  bool processed = false;
-  buffer->clear_output ();
-  unsigned int count = buffer->len;
-  switch ((unsigned) buffer->props.script)
-  {
-    case HB_SCRIPT_DEVANAGARI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0905u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x093Au: case 0x093Bu: case 0x093Eu: case 0x0945u:
-	      case 0x0946u: case 0x0949u: case 0x094Au: case 0x094Bu:
-	      case 0x094Cu: case 0x094Fu: case 0x0956u: case 0x0957u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0906u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x093Au: case 0x0945u: case 0x0946u: case 0x0947u:
-	      case 0x0948u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0909u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0941u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x090Fu:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0945u: case 0x0946u: case 0x0947u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0930u:
-	    if (0x094Du == buffer->cur(1).codepoint &&
-		buffer->idx + 2 < count &&
-	        0x0907u == buffer->cur(2).codepoint)
-	    {
-	      buffer->next_glyph ();
-	      buffer->next_glyph ();
-	      buffer->output_glyph (0x25CCu);
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_BENGALI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0985u:
-	    matched = 0x09BE == buffer->cur(1).codepoint;
-	    break;
-	  case 0x098Bu:
-	    matched = 0x09C3 == buffer->cur(1).codepoint;
-	    break;
-	  case 0x098Cu:
-	    matched = 0x09E2 == buffer->cur(1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_GURMUKHI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0A05u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0A3Eu: case 0x0A48u: case 0x0A4Cu:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0A72u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0A3Fu: case 0x0A40u: case 0x0A47u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0A73u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0A41u: case 0x0A42u: case 0x0A4Bu:
-		matched = true;
-		break;
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_GUJARATI:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0A85u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0ABEu: case 0x0AC5u: case 0x0AC7u: case 0x0AC8u:
-	      case 0x0AC9u: case 0x0ACBu: case 0x0ACCu:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0AC5u:
-	    matched = 0x0ABE == buffer->cur(1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_ORIYA:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0B05u:
-	    matched = 0x0B3E == buffer->cur(1).codepoint;
-	    break;
-	  case 0x0B0Fu: case 0x0B13u:
-	    matched = 0x0B57 == buffer->cur(1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_TELUGU:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0C12u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0C4Cu: case 0x0C55u:
-		matched = true;
-		break;
-	    }
-	    break;
-	  case 0x0C3Fu: case 0x0C46u: case 0xC4Au:
-	    matched = 0x0C55 == buffer->cur(1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_KANNADA:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0C89u: case 0x0C8Bu:
-	    matched = 0x0CBE == buffer->cur(1).codepoint;
-	    break;
-	  case 0x0C92u:
-	    matched = 0x0CCC == buffer->cur(1).codepoint;
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    case HB_SCRIPT_MALAYALAM:
-      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
-      {
-	bool matched = false;
-	switch (buffer->cur().codepoint)
-	{
-	  case 0x0D07u: case 0x0D09u:
-	    matched = 0x0D57 == buffer->cur(1).codepoint;
-	    break;
-	  case 0x0D0Eu:
-	    matched = 0x0D46 == buffer->cur(1).codepoint;
-	    break;
-	  case 0x0D12u:
-	    switch (buffer->cur(1).codepoint)
-	    {
-	      case 0x0D3Eu: case 0x0D57u:
-		matched = true;
-		break;
-	    }
-	    break;
-	}
-	buffer->next_glyph ();
-	if (matched) _output_with_dotted_circle (buffer);
-      }
-      processed = true;
-      break;
-
-    default:
-      break;
-  }
-  if (processed)
-  {
-    if (buffer->idx < count)
-      buffer->next_glyph ();
-    if (likely (buffer->successful))
-      buffer->swap_buffers ();
-  }
-}
-
 static indic_position_t
 consonant_position_from_face (const indic_shape_plan_t *indic_plan,
 			      const hb_codepoint_t consonant,
@@ -1055,8 +786,10 @@
      *
      * We could use buffer->sort() for this, if there was no special
      * reordering of pre-base stuff happening later...
+     * We don't want to merge_clusters all of that, which buffer->sort()
+     * would.
      */
-    if (indic_plan->is_old_spec || end - base > 127)
+    if (indic_plan->is_old_spec || end - start > 127)
       buffer->merge_clusters (base, end);
     else
     {
@@ -1285,7 +1018,6 @@
     else
       buffer->next_glyph ();
   }
-
   buffer->swap_buffers ();
 }
 
@@ -1785,6 +1517,14 @@
 }
 
 
+static void
+preprocess_text_indic (const hb_ot_shape_plan_t *plan,
+		       hb_buffer_t              *buffer,
+		       hb_font_t                *font)
+{
+  _hb_preprocess_text_vowel_constraints (plan, buffer, font);
+}
+
 static bool
 decompose_indic (const hb_ot_shape_normalize_context_t *c,
 		 hb_codepoint_t  ab,
diff --git a/src/hb-ot-shape-complex-khmer.cc b/src/hb-ot-shape-complex-khmer.cc
index 88d1626..4475ceb 100644
--- a/src/hb-ot-shape-complex-khmer.cc
+++ b/src/hb-ot-shape-complex-khmer.cc
@@ -46,7 +46,7 @@
   {HB_TAG('c','f','a','r'), F_MANUAL_JOINERS},
   /*
    * Other features.
-   * These features are applied all at once.
+   * These features are applied all at once after clearing syllables.
    */
   {HB_TAG('p','r','e','s'), F_GLOBAL_MANUAL_JOINERS},
   {HB_TAG('a','b','v','s'), F_GLOBAL_MANUAL_JOINERS},
@@ -127,28 +127,31 @@
 
   for (; i < KHMER_NUM_FEATURES; i++)
     map->add_feature (khmer_features[i]);
-
-  map->enable_feature (HB_TAG('c','a','l','t'));
-  map->enable_feature (HB_TAG('c','l','i','g'));
-
 }
 
 static void
 override_features_khmer (hb_ot_shape_planner_t *plan)
 {
+  hb_ot_map_builder_t *map = &plan->map;
+
+  /* Khmer spec has 'clig' as part of required shaping features:
+   * "Apply feature 'clig' to form ligatures that are desired for
+   * typographical correctness.", hence in overrides... */
+  map->enable_feature (HB_TAG('c','l','i','g'));
+
   /* Uniscribe does not apply 'kern' in Khmer. */
   if (hb_options ().uniscribe_bug_compatible)
   {
-    plan->map.disable_feature (HB_TAG('k','e','r','n'));
+    map->disable_feature (HB_TAG('k','e','r','n'));
   }
 
-  plan->map.disable_feature (HB_TAG('l','i','g','a'));
+  map->disable_feature (HB_TAG('l','i','g','a'));
 }
 
 
 struct would_substitute_feature_t
 {
-  inline void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
+  void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
   {
     zero_context = zero_context_;
     map->get_stage_lookups (0/*GSUB*/,
@@ -156,9 +159,9 @@
 			    &lookups, &count);
   }
 
-  inline bool would_substitute (const hb_codepoint_t *glyphs,
-				unsigned int          glyphs_count,
-				hb_face_t            *face) const
+  bool would_substitute (const hb_codepoint_t *glyphs,
+			 unsigned int          glyphs_count,
+			 hb_face_t            *face) const
   {
     for (unsigned int i = 0; i < count; i++)
       if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context))
@@ -174,9 +177,7 @@
 
 struct khmer_shape_plan_t
 {
-  ASSERT_POD ();
-
-  inline bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
+  bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
   {
     hb_codepoint_t glyph = virama_glyph;
     if (unlikely (virama_glyph == (hb_codepoint_t) -1))
@@ -267,7 +268,7 @@
 
 static void
 reorder_consonant_syllable (const hb_ot_shape_plan_t *plan,
-			    hb_face_t *face,
+			    hb_face_t *face HB_UNUSED,
 			    hb_buffer_t *buffer,
 			    unsigned int start, unsigned int end)
 {
@@ -416,7 +417,6 @@
     else
       buffer->next_glyph ();
   }
-
   buffer->swap_buffers ();
 }
 
@@ -438,8 +438,6 @@
 		 hb_font_t *font HB_UNUSED,
 		 hb_buffer_t *buffer)
 {
-  /* TODO: In USE, we clear syllables right after reorder.  Figure out
-   * what Uniscribe does. */
   hb_glyph_info_t *info = buffer->info;
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc
index e201a23..8fdf2f4 100644
--- a/src/hb-ot-shape-complex-myanmar.cc
+++ b/src/hb-ot-shape-complex-myanmar.cc
@@ -36,7 +36,7 @@
 {
   /*
    * Basic features.
-   * These features are applied in order, one at a time, after initial_reordering.
+   * These features are applied in order, one at a time, after reordering.
    */
   HB_TAG('r','p','h','f'),
   HB_TAG('p','r','e','f'),
@@ -48,7 +48,7 @@
 {
   /*
    * Other features.
-   * These features are applied all at once, after final_reordering.
+   * These features are applied all at once, after clearing syllables.
    */
   HB_TAG('p','r','e','s'),
   HB_TAG('a','b','v','s'),
@@ -80,13 +80,13 @@
 		 hb_font_t *font,
 		 hb_buffer_t *buffer);
 static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer);
+reorder (const hb_ot_shape_plan_t *plan,
+	 hb_font_t *font,
+	 hb_buffer_t *buffer);
 static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font,
-		  hb_buffer_t *buffer);
+clear_syllables (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font,
+		 hb_buffer_t *buffer);
 
 static void
 collect_features_myanmar (hb_ot_shape_planner_t *plan)
@@ -102,7 +102,7 @@
   map->enable_feature (HB_TAG('c','c','m','p'));
 
 
-  map->add_gsub_pause (initial_reordering);
+  map->add_gsub_pause (reorder);
 
   for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
   {
@@ -110,7 +110,7 @@
     map->add_gsub_pause (nullptr);
   }
 
-  map->add_gsub_pause (final_reordering);
+  map->add_gsub_pause (clear_syllables);
 
   for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
     map->enable_feature (other_features[i], F_MANUAL_ZWJ);
@@ -274,8 +274,8 @@
 }
 
 static void
-initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
-			     hb_face_t *face,
+initial_reordering_syllable (const hb_ot_shape_plan_t *plan HB_UNUSED,
+			     hb_face_t *face HB_UNUSED,
 			     hb_buffer_t *buffer,
 			     unsigned int start, unsigned int end)
 {
@@ -343,37 +343,34 @@
     else
       buffer->next_glyph ();
   }
-
   buffer->swap_buffers ();
 }
 
 static void
-initial_reordering (const hb_ot_shape_plan_t *plan,
-		    hb_font_t *font,
-		    hb_buffer_t *buffer)
+reorder (const hb_ot_shape_plan_t *plan,
+	 hb_font_t *font,
+	 hb_buffer_t *buffer)
 {
   insert_dotted_circles (plan, font, buffer);
 
   foreach_syllable (buffer, start, end)
     initial_reordering_syllable (plan, font->face, buffer, start, end);
-}
-
-static void
-final_reordering (const hb_ot_shape_plan_t *plan,
-		  hb_font_t *font HB_UNUSED,
-		  hb_buffer_t *buffer)
-{
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-
-  /* Zero syllables now... */
-  for (unsigned int i = 0; i < count; i++)
-    info[i].syllable() = 0;
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position);
 }
 
+static void
+clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		 hb_font_t *font HB_UNUSED,
+		 hb_buffer_t *buffer)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    info[i].syllable() = 0;
+}
+
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
 {
@@ -394,27 +391,6 @@
 };
 
 
-/* Uniscribe seems to have a shaper for 'mymr' that is like the
- * generic shaper, except that it zeros mark advances GDEF_LATE. */
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_old =
-{
-  nullptr, /* collect_features */
-  nullptr, /* override_features */
-  nullptr, /* data_create */
-  nullptr, /* data_destroy */
-  nullptr, /* preprocess_text */
-  nullptr, /* postprocess_glyphs */
-  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
-  nullptr, /* decompose */
-  nullptr, /* compose */
-  nullptr, /* setup_masks */
-  HB_TAG_NONE, /* gpos_tag */
-  nullptr, /* reorder_marks */
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
-  true, /* fallback_position */
-};
-
-
 /* Ugly Zawgyi encoding.
  * Disable all auto processing.
  * https://github.com/harfbuzz/harfbuzz/issues/1162 */
diff --git a/src/hb-ot-shape-complex-thai.cc b/src/hb-ot-shape-complex-thai.cc
index b687fe6..650c980 100644
--- a/src/hb-ot-shape-complex-thai.cc
+++ b/src/hb-ot-shape-complex-thai.cc
@@ -357,8 +357,7 @@
 	buffer->merge_out_clusters (start - 1, end);
     }
   }
-  if (likely (buffer->successful))
-    buffer->swap_buffers ();
+  buffer->swap_buffers ();
 
   /* If font has Thai GSUB, we are done. */
   if (plan->props.script == HB_SCRIPT_THAI && !plan->map.found_script[0])
diff --git a/src/hb-ot-shape-complex-use-table.cc b/src/hb-ot-shape-complex-use-table.cc
index e9c88ae..2f3eb70 100644
--- a/src/hb-ot-shape-complex-use-table.cc
+++ b/src/hb-ot-shape-complex-use-table.cc
@@ -544,7 +544,7 @@
   /* 11190 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 111A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 111B0 */     B,     B,     B,  VPst,  VPre,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,
-  /* 111C0 */     H,     B,     R,     R,     O,     O,     O,     O,     O,    FM, CMBlw,  VAbv,  VBlw,     O,     O,     O,
+  /* 111C0 */     H,     B,     R,     R,     O,     O,     O,     O,    GB,  FBlw, CMBlw,  VAbv,  VBlw,     O,     O,     O,
   /* 111D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
   /* Sinhala Archaic Numbers */
@@ -582,7 +582,7 @@
   /* 11310 */     B,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11320 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
   /* 11330 */     B,     O,     B,     B,     O,     B,     B,     B,     B,     B,     O, CMBlw, CMBlw,     B,  VPst,  VPst,
-  /* 11340 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,     O,  VPre,  VPre,     O,     O,  VPst,  VPst,     H,     O,     O,
+  /* 11340 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,     O,  VPre,  VPre,     O,     O,  VPst,  VPst,   HVM,     O,     O,
   /* 11350 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     B,     B,
   /* 11360 */     B,     B,  VPst,  VPst,     O,     O, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
   /* 11370 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc
index f9a580c..2e3f202 100644
--- a/src/hb-ot-shape-complex-use.cc
+++ b/src/hb-ot-shape-complex-use.cc
@@ -28,6 +28,7 @@
 
 #include "hb-ot-shape-complex-use.hh"
 #include "hb-ot-shape-complex-arabic.hh"
+#include "hb-ot-shape-complex-vowel-constraints.hh"
 
 /* buffer var allocations */
 #define use_category() complex_var_u8_0()
@@ -79,7 +80,8 @@
 {
   /*
    * Other features.
-   * These features are applied all at once, after reordering.
+   * These features are applied all at once, after reordering and
+   * clearing syllables.
    */
   HB_TAG('a','b','v','s'),
   HB_TAG('b','l','w','s'),
@@ -119,6 +121,10 @@
 reorder (const hb_ot_shape_plan_t *plan,
 	 hb_font_t *font,
 	 hb_buffer_t *buffer);
+static void
+clear_syllables (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font,
+		 hb_buffer_t *buffer);
 
 static void
 collect_features_use (hb_ot_shape_planner_t *plan)
@@ -147,6 +153,7 @@
     map->enable_feature (basic_features[i], F_MANUAL_ZWJ);
 
   map->add_gsub_pause (reorder);
+  map->add_gsub_pause (clear_syllables);
 
   /* "Topographical features" */
   for (unsigned int i = 0; i < ARRAY_LENGTH (arabic_features); i++)
@@ -164,8 +171,6 @@
 
 struct use_shape_plan_t
 {
-  ASSERT_POD ();
-
   hb_mask_t rphf_mask;
 
   arabic_shape_plan_t *arabic_plan;
@@ -372,7 +377,7 @@
 }
 
 static void
-clear_substitution_flags (const hb_ot_shape_plan_t *plan,
+clear_substitution_flags (const hb_ot_shape_plan_t *plan HB_UNUSED,
 			  hb_font_t *font HB_UNUSED,
 			  hb_buffer_t *buffer)
 {
@@ -384,7 +389,7 @@
 
 static void
 record_rphf (const hb_ot_shape_plan_t *plan,
-	     hb_font_t *font,
+	     hb_font_t *font HB_UNUSED,
 	     hb_buffer_t *buffer)
 {
   const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data;
@@ -406,8 +411,8 @@
 }
 
 static void
-record_pref (const hb_ot_shape_plan_t *plan,
-	     hb_font_t *font,
+record_pref (const hb_ot_shape_plan_t *plan HB_UNUSED,
+	     hb_font_t *font HB_UNUSED,
 	     hb_buffer_t *buffer)
 {
   hb_glyph_info_t *info = buffer->info;
@@ -427,7 +432,8 @@
 static inline bool
 is_halant (const hb_glyph_info_t &info)
 {
-  return info.use_category() == USE_H && !_hb_glyph_info_ligated (&info);
+  return (info.use_category() == USE_H || info.use_category() == USE_HVM) &&
+	 !_hb_glyph_info_ligated (&info);
 }
 
 static void
@@ -444,19 +450,38 @@
 
   hb_glyph_info_t *info = buffer->info;
 
-#define BASE_FLAGS (FLAG (USE_B) | FLAG (USE_GB))
+#define POST_BASE_FLAGS64 (FLAG64 (USE_FM) | \
+			   FLAG64 (USE_FAbv) | \
+			   FLAG64 (USE_FBlw) | \
+			   FLAG64 (USE_FPst) | \
+			   FLAG64 (USE_MAbv) | \
+			   FLAG64 (USE_MBlw) | \
+			   FLAG64 (USE_MPst) | \
+			   FLAG64 (USE_MPre) | \
+			   FLAG64 (USE_VAbv) | \
+			   FLAG64 (USE_VBlw) | \
+			   FLAG64 (USE_VPst) | \
+			   FLAG64 (USE_VPre) | \
+			   FLAG64 (USE_VMAbv) | \
+			   FLAG64 (USE_VMBlw) | \
+			   FLAG64 (USE_VMPst) | \
+			   FLAG64 (USE_VMPre))
 
   /* Move things forward. */
   if (info[start].use_category() == USE_R && end - start > 1)
   {
-    /* Got a repha.  Reorder it to after first base, before first halant. */
+    /* Got a repha.  Reorder it towards the end, but before the first post-base
+     * glyph. */
     for (unsigned int i = start + 1; i < end; i++)
-      if ((FLAG_UNSAFE (info[i].use_category()) & (BASE_FLAGS)) || is_halant (info[i]))
+    {
+      bool is_post_base_glyph = (FLAG64_UNSAFE (info[i].use_category()) & POST_BASE_FLAGS64) ||
+				is_halant (info[i]);
+      if (is_post_base_glyph || i == end - 1)
       {
-	/* If we hit a halant, move before it; otherwise it's a base: move to it's
-	 * place, and shift things in between backward. */
+	/* If we hit a post-base glyph, move before it; otherwise move to the
+	 * end. Shift things in between backward. */
 
-	if (is_halant (info[i]))
+	if (is_post_base_glyph)
 	  i--;
 
 	buffer->merge_clusters (start, i + 1);
@@ -466,21 +491,19 @@
 
 	break;
       }
+    }
   }
 
   /* Move things back. */
-  unsigned int j = end;
+  unsigned int j = start;
   for (unsigned int i = start; i < end; i++)
   {
     uint32_t flag = FLAG_UNSAFE (info[i].use_category());
-    if ((flag & (BASE_FLAGS)) || is_halant (info[i]))
+    if (is_halant (info[i]))
     {
-      /* If we hit a halant, move after it; otherwise it's a base: move to it's
-       * place, and shift things in between backward. */
-      if (is_halant (info[i]))
-	j = i + 1;
-      else
-	j = i;
+      /* If we hit a halant, move after it; otherwise move to the beginning, and
+       * shift things in between forward. */
+      j = i + 1;
     }
     else if (((flag) & (FLAG (USE_VPre) | FLAG (USE_VMPre))) &&
 	     /* Only move the first component of a MultipleSubst. */
@@ -547,7 +570,6 @@
     else
       buffer->next_glyph ();
   }
-
   buffer->swap_buffers ();
 }
 
@@ -558,17 +580,30 @@
 {
   insert_dotted_circles (plan, font, buffer);
 
-  hb_glyph_info_t *info = buffer->info;
-
   foreach_syllable (buffer, start, end)
     reorder_syllable (buffer, start, end);
 
-  /* Zero syllables now... */
+  HB_BUFFER_DEALLOCATE_VAR (buffer, use_category);
+}
+
+static void
+clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		 hb_font_t *font HB_UNUSED,
+		 hb_buffer_t *buffer)
+{
+  hb_glyph_info_t *info = buffer->info;
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
     info[i].syllable() = 0;
+}
 
-  HB_BUFFER_DEALLOCATE_VAR (buffer, use_category);
+
+static void
+preprocess_text_use (const hb_ot_shape_plan_t *plan,
+		     hb_buffer_t              *buffer,
+		     hb_font_t                *font)
+{
+  _hb_preprocess_text_vowel_constraints (plan, buffer, font);
 }
 
 static bool
@@ -591,7 +626,7 @@
   nullptr, /* override_features */
   data_create_use,
   data_destroy_use,
-  nullptr, /* preprocess_text */
+  preprocess_text_use,
   nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   nullptr, /* decompose */
diff --git a/src/hb-ot-shape-complex-vowel-constraints.cc b/src/hb-ot-shape-complex-vowel-constraints.cc
new file mode 100644
index 0000000..0e53258
--- /dev/null
+++ b/src/hb-ot-shape-complex-vowel-constraints.cc
@@ -0,0 +1,437 @@
+/* == Start of generated functions == */
+/*
+ * The following functions are generated by running:
+ *
+ *   ./gen-vowel-constraints.py use Scripts.txt
+ *
+ * on files with these headers:
+ *
+ * # Copied from https://docs.microsoft.com/en-us/typography/script-development/use
+ * # On October 23, 2018; with documentd dated 02/07/2018.
+ *
+ * # Scripts-11.0.0.txt
+ * # Date: 2018-02-21, 05:34:31 GMT
+ */
+
+#include "hb-ot-shape-complex-vowel-constraints.hh"
+
+static void
+_output_dotted_circle (hb_buffer_t *buffer)
+{
+  hb_glyph_info_t &dottedcircle = buffer->output_glyph (0x25CCu);
+  _hb_glyph_info_reset_continuation (&dottedcircle);
+}
+
+static void
+_output_with_dotted_circle (hb_buffer_t *buffer)
+{
+  _output_dotted_circle (buffer);
+  buffer->next_glyph ();
+}
+
+void
+_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED,
+				       hb_buffer_t              *buffer,
+				       hb_font_t                *font HB_UNUSED)
+{
+  /* UGLY UGLY UGLY business of adding dotted-circle in the middle of
+   * vowel-sequences that look like another vowel.  Data for each script
+   * collected from the USE script development spec.
+   *
+   * https://github.com/harfbuzz/harfbuzz/issues/1019
+   */
+  bool processed = false;
+  buffer->clear_output ();
+  unsigned int count = buffer->len;
+  switch ((unsigned) buffer->props.script)
+  {
+    case HB_SCRIPT_DEVANAGARI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0905u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x093Au: case 0x093Bu: case 0x093Eu: case 0x0945u:
+	      case 0x0946u: case 0x0949u: case 0x094Au: case 0x094Bu:
+	      case 0x094Cu: case 0x094Fu: case 0x0956u: case 0x0957u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0906u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x093Au: case 0x0945u: case 0x0946u: case 0x0947u:
+	      case 0x0948u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0909u:
+	    matched = 0x0941u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x090Fu:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0945u: case 0x0946u: case 0x0947u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0930u:
+	    if (0x094Du == buffer->cur (1).codepoint &&
+		buffer->idx + 2 < count &&
+		0x0907u == buffer->cur (2).codepoint)
+	    {
+	      buffer->next_glyph ();
+	      buffer->next_glyph ();
+	      _output_dotted_circle (buffer);
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_BENGALI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0985u:
+	    matched = 0x09BEu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x098Bu:
+	    matched = 0x09C3u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x098Cu:
+	    matched = 0x09E2u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_GURMUKHI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0A05u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0A3Eu: case 0x0A48u: case 0x0A4Cu:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0A72u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0A3Fu: case 0x0A40u: case 0x0A47u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0A73u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0A41u: case 0x0A42u: case 0x0A4Bu:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_GUJARATI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0A85u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0ABEu: case 0x0AC5u: case 0x0AC7u: case 0x0AC8u:
+	      case 0x0AC9u: case 0x0ACBu: case 0x0ACCu:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0AC5u:
+	    matched = 0x0ABEu == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_ORIYA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0B05u:
+	    matched = 0x0B3Eu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0B0Fu: case 0x0B13u:
+	    matched = 0x0B57u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_TELUGU:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0C12u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0C4Cu: case 0x0C55u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0C3Fu: case 0x0C46u: case 0x0C4Au:
+	    matched = 0x0C55u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_KANNADA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0C89u: case 0x0C8Bu:
+	    matched = 0x0CBEu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0C92u:
+	    matched = 0x0CCCu == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_MALAYALAM:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0D07u: case 0x0D09u:
+	    matched = 0x0D57u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D0Eu:
+	    matched = 0x0D46u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D12u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0D3Eu: case 0x0D57u:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_SINHALA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x0D85u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0DCFu: case 0x0DD0u: case 0x0DD1u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x0D8Bu: case 0x0D8Fu: case 0x0D94u:
+	    matched = 0x0DDFu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D8Du:
+	    matched = 0x0DD8u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x0D91u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x0DCAu: case 0x0DD9u: case 0x0DDAu: case 0x0DDCu:
+	      case 0x0DDDu:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_BRAHMI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11005u:
+	    matched = 0x11038u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x1100Bu:
+	    matched = 0x1103Eu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x1100Fu:
+	    matched = 0x11042u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_KHUDAWADI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x112B0u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x112E0u: case 0x112E5u: case 0x112E6u: case 0x112E7u:
+	      case 0x112E8u:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_TIRHUTA:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11481u:
+	    matched = 0x114B0u == buffer->cur (1).codepoint;
+	    break;
+	  case 0x1148Bu: case 0x1148Du:
+	    matched = 0x114BAu == buffer->cur (1).codepoint;
+	    break;
+	  case 0x114AAu:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x114B5u: case 0x114B6u:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_MODI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11600u: case 0x11601u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x11639u: case 0x1163Au:
+		matched = true;
+		break;
+	    }
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    case HB_SCRIPT_TAKRI:
+      for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
+      {
+	bool matched = false;
+	switch (buffer->cur ().codepoint)
+	{
+	  case 0x11680u:
+	    switch (buffer->cur (1).codepoint)
+	    {
+	      case 0x116ADu: case 0x116B4u: case 0x116B5u:
+		matched = true;
+		break;
+	    }
+	    break;
+	  case 0x11686u:
+	    matched = 0x116B2u == buffer->cur (1).codepoint;
+	    break;
+	}
+	buffer->next_glyph ();
+	if (matched) _output_with_dotted_circle (buffer);
+      }
+      processed = true;
+      break;
+
+    default:
+      break;
+  }
+  if (processed)
+  {
+    if (buffer->idx < count)
+      buffer->next_glyph ();
+    buffer->swap_buffers ();
+  }
+}
+
+/* == End of generated functions == */
diff --git a/src/hb-ot-shape-complex-vowel-constraints.hh b/src/hb-ot-shape-complex-vowel-constraints.hh
new file mode 100644
index 0000000..d9082d4
--- /dev/null
+++ b/src/hb-ot-shape-complex-vowel-constraints.hh
@@ -0,0 +1,39 @@
+/*
+ * 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_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH
+#define HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH
+
+#include "hb.hh"
+
+#include "hb-ot-shape-complex.hh"
+
+HB_INTERNAL void
+_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan,
+				       hb_buffer_t              *buffer,
+				       hb_font_t                *font);
+
+#endif /* HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH */
diff --git a/src/hb-ot-shape-complex.hh b/src/hb-ot-shape-complex.hh
index 2944d74..a2499de 100644
--- a/src/hb-ot-shape-complex.hh
+++ b/src/hb-ot-shape-complex.hh
@@ -57,7 +57,6 @@
   HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (khmer) \
   HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_old) \
   HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_zawgyi) \
   HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
   HB_COMPLEX_SHAPER_IMPLEMENT (use) \
@@ -269,12 +268,25 @@
 	return &_hb_ot_complex_shaper_khmer;
 
     case HB_SCRIPT_MYANMAR:
-      if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','2'))
-	return &_hb_ot_complex_shaper_myanmar;
-      else if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','r'))
-	return &_hb_ot_complex_shaper_myanmar_old;
-      else
+      /* If the designer designed the font for the 'DFLT' script,
+       * (or we ended up arbitrarily pick 'latn'), use the default shaper.
+       * Otherwise, use the specific shaper.
+       *
+       * If designer designed for 'mymr' tag, also send to default
+       * shaper.  That's tag used from before Myanmar shaping spec
+       * was developed.  The shaping spec uses 'mym2' tag. */
+      if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') ||
+	  planner->map.chosen_script[0] == HB_TAG ('l','a','t','n') ||
+	  planner->map.chosen_script[0] == HB_TAG ('m','y','m','r'))
 	return &_hb_ot_complex_shaper_default;
+      else
+	return &_hb_ot_complex_shaper_myanmar;
+
+
+    /* https://github.com/harfbuzz/harfbuzz/issues/1162 */
+    case HB_SCRIPT_MYANMAR_ZAWGYI:
+
+      return &_hb_ot_complex_shaper_myanmar_zawgyi;
 
 
     /* Unicode-2.0 additions */
@@ -375,10 +387,6 @@
 	return &_hb_ot_complex_shaper_default;
       else
 	return &_hb_ot_complex_shaper_use;
-
-    /* https://github.com/harfbuzz/harfbuzz/issues/1162 */
-    case HB_SCRIPT_MYANMAR_ZAWGYI:
-      return &_hb_ot_complex_shaper_myanmar_zawgyi;
   }
 }
 
diff --git a/src/hb-ot-shape-fallback.cc b/src/hb-ot-shape-fallback.cc
index 4c5ccc9..d3afdff 100644
--- a/src/hb-ot-shape-fallback.cc
+++ b/src/hb-ot-shape-fallback.cc
@@ -25,7 +25,7 @@
  */
 
 #include "hb-ot-shape-fallback.hh"
-#include "hb-ot-kern-table.hh"
+#include "hb-kern.hh"
 
 static unsigned int
 recategorize_combining_class (hb_codepoint_t u,
@@ -192,7 +192,7 @@
 }
 
 static inline void
-position_mark (const hb_ot_shape_plan_t *plan,
+position_mark (const hb_ot_shape_plan_t *plan HB_UNUSED,
 	       hb_font_t *font,
 	       hb_buffer_t  *buffer,
 	       hb_glyph_extents_t &base_extents,
@@ -464,23 +464,30 @@
       !font->has_glyph_h_kerning_func () :
       !font->has_glyph_v_kerning_func ())
     return;
+
+  bool reverse = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
+
+  if (reverse)
+    buffer->reverse ();
+
   hb_ot_shape_fallback_kern_driver_t driver (font, buffer);
-  hb_kern_machine_t<hb_ot_shape_fallback_kern_driver_t> machine (driver);
+  OT::hb_kern_machine_t<hb_ot_shape_fallback_kern_driver_t> machine (driver);
   machine.kern (font, buffer, plan->kern_mask, false);
+
+  if (reverse)
+    buffer->reverse ();
 }
 
 
 /* Adjusts width of various spaces. */
 void
-_hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan,
+_hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan HB_UNUSED,
 			      hb_font_t *font,
 			      hb_buffer_t  *buffer)
 {
-  if (!HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
-    return;
-
   hb_glyph_info_t *info = buffer->info;
   hb_glyph_position_t *pos = buffer->pos;
+  bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction);
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
     if (_hb_glyph_info_is_unicode_space (&info[i]) && !_hb_glyph_info_ligated (&info[i]))
@@ -501,27 +508,40 @@
 	case t::SPACE_EM_5:
 	case t::SPACE_EM_6:
 	case t::SPACE_EM_16:
-	  pos[i].x_advance = (font->x_scale + ((int) space_type)/2) / (int) space_type;
+	  if (horizontal)
+	    pos[i].x_advance = +(font->x_scale + ((int) space_type)/2) / (int) space_type;
+	  else
+	    pos[i].y_advance = -(font->y_scale + ((int) space_type)/2) / (int) space_type;
 	  break;
 
 	case t::SPACE_4_EM_18:
-	  pos[i].x_advance = (int64_t) font->x_scale * 4 / 18;
+	  if (horizontal)
+	    pos[i].x_advance = (int64_t) +font->x_scale * 4 / 18;
+	  else
+	    pos[i].y_advance = (int64_t) -font->y_scale * 4 / 18;
 	  break;
 
 	case t::SPACE_FIGURE:
 	  for (char u = '0'; u <= '9'; u++)
 	    if (font->get_nominal_glyph (u, &glyph))
 	    {
-	      pos[i].x_advance = font->get_glyph_h_advance (glyph);
+	      if (horizontal)
+		pos[i].x_advance = font->get_glyph_h_advance (glyph);
+	      else
+		pos[i].y_advance = font->get_glyph_v_advance (glyph);
 	      break;
 	    }
 	  break;
 
 	case t::SPACE_PUNCTUATION:
-	  if (font->get_nominal_glyph ('.', &glyph))
-	    pos[i].x_advance = font->get_glyph_h_advance (glyph);
-	  else if (font->get_nominal_glyph (',', &glyph))
-	    pos[i].x_advance = font->get_glyph_h_advance (glyph);
+	  if (font->get_nominal_glyph ('.', &glyph) ||
+	      font->get_nominal_glyph (',', &glyph))
+	  {
+	    if (horizontal)
+	      pos[i].x_advance = font->get_glyph_h_advance (glyph);
+	    else
+	      pos[i].y_advance = font->get_glyph_v_advance (glyph);
+	  }
 	  break;
 
 	case t::SPACE_NARROW:
@@ -530,7 +550,10 @@
 	   * However, in my testing, many fonts have their regular space being about that
 	   * size.  To me, a percentage of the space width makes more sense.  Half is as
 	   * good as any. */
-	  pos[i].x_advance /= 2;
+	  if (horizontal)
+	    pos[i].x_advance /= 2;
+	  else
+	    pos[i].y_advance /= 2;
 	  break;
       }
     }
diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index a8229a9..82bb24b 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -213,17 +213,19 @@
 }
 
 static inline void
-handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool short_circuit)
+handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c,
+				   unsigned int end,
+				   bool short_circuit HB_UNUSED)
 {
   /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
   hb_buffer_t * const buffer = c->buffer;
   hb_font_t * const font = c->font;
   for (; buffer->idx < end - 1 && buffer->successful;) {
     if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) {
-      /* The next two lines are some ugly lines... But work. */
       if (font->get_variation_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index()))
       {
-	buffer->replace_glyphs (2, 1, &buffer->cur().codepoint);
+	hb_codepoint_t unicode = buffer->cur().codepoint;
+	buffer->replace_glyphs (2, 1, &unicode);
       }
       else
       {
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index a553887..6e5ce15 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -26,8 +26,6 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#define HB_SHAPER ot
-#define hb_ot_shape_plan_data_t hb_ot_shape_plan_t
 #include "hb-shaper-impl.hh"
 
 #include "hb-ot-shape.hh"
@@ -42,6 +40,21 @@
 #include "hb-aat-layout.hh"
 
 
+/**
+ * SECTION:hb-ot-shape
+ * @title: hb-ot-shape
+ * @short_description: OpenType shaping support
+ * @include: hb-ot.h
+ *
+ * Support functions for OpenType shaping related queries.
+ **/
+
+
+static void
+hb_ot_shape_collect_features (hb_ot_shape_planner_t          *planner,
+			      const hb_feature_t             *user_features,
+			      unsigned int                    num_user_features);
+
 static bool
 _hb_apply_morx (hb_face_t *face)
 {
@@ -49,29 +62,53 @@
       hb_aat_layout_has_substitution (face))
     return true;
 
-  return !hb_ot_layout_has_substitution (face) &&
+  /* Ignore empty GSUB tables. */
+  return (!hb_ot_layout_has_substitution (face) ||
+	  !hb_ot_layout_table_get_script_tags (face,
+					       HB_OT_TAG_GSUB,
+					       0, nullptr, nullptr)) &&
 	 hb_aat_layout_has_substitution (face);
 }
 
+hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t                     *face,
+					      const hb_segment_properties_t *props) :
+						face (face),
+						props (*props),
+						map (face, props),
+						aat_map (face, props),
+						apply_morx (_hb_apply_morx (face))
+{
+  shaper = hb_ot_shape_complex_categorize (this);
+
+  script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE;
+  script_fallback_mark_positioning = shaper->fallback_position;
+
+  if (apply_morx)
+    shaper = &_hb_ot_complex_shaper_default;
+}
+
 void
-hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
-				const int          *coords,
-				unsigned int        num_coords)
+hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t           &plan,
+				const hb_ot_shape_plan_key_t &key)
 {
   plan.props = props;
   plan.shaper = shaper;
-  map.compile (plan.map, coords, num_coords);
+  map.compile (plan.map, key);
+  if (apply_morx)
+    aat_map.compile (plan.aat_map);
 
-  plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m'));
   plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c'));
   plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r'));
   plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m'));
   plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask);
-  hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (plan.props.direction) ?
+  plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m'));
+  hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (props.direction) ?
 		      HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n');
   plan.kern_mask = plan.map.get_mask (kern_tag);
+  plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k'));
 
   plan.requested_kerning = !!plan.kern_mask;
+  plan.requested_tracking = !!plan.trak_mask;
   bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX;
   bool disable_gpos = plan.shaper->gpos_tag &&
 		      plan.shaper->gpos_tag != plan.map.chosen_script[1];
@@ -87,7 +124,7 @@
    * Decide who does substitutions. GSUB, morx, or fallback.
    */
 
-  plan.apply_morx = _hb_apply_morx (face);
+  plan.apply_morx = apply_morx;
 
   /*
    * Decide who does positioning. GPOS, kerx, kern, or fallback.
@@ -95,28 +132,97 @@
 
   if (hb_options ().aat && hb_aat_layout_has_positioning (face))
     plan.apply_kerx = true;
-  else if (!disable_gpos && hb_ot_layout_has_positioning (face))
+  else if (!apply_morx && !disable_gpos && hb_ot_layout_has_positioning (face))
     plan.apply_gpos = true;
   else if (hb_aat_layout_has_positioning (face))
     plan.apply_kerx = true;
 
-  if (plan.requested_kerning && !plan.apply_kerx && !has_gpos_kern)
+  if (!plan.apply_kerx && !has_gpos_kern)
   {
     /* Apparently Apple applies kerx if GPOS kern was not applied. */
     if (hb_aat_layout_has_positioning (face))
       plan.apply_kerx = true;
-    if (hb_ot_layout_has_kerning (face))
+    else if (hb_ot_layout_has_kerning (face))
       plan.apply_kern = true;
-    else
-      plan.fallback_kerning = true;
   }
 
+  plan.zero_marks = script_zero_marks &&
+		    !plan.apply_kerx &&
+		    (!plan.apply_kern || !hb_ot_layout_has_machine_kerning (face));
   plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k'));
-  if (!plan.apply_gpos && !plan.apply_kerx)
-    plan.fallback_mark_positioning = true;
+
+  plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos &&
+					      !plan.apply_kerx &&
+					      (!plan.apply_kern || !hb_ot_layout_has_cross_kerning (face));
+
+  plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing &&
+				   script_fallback_mark_positioning;
 
   /* Currently we always apply trak. */
-  plan.apply_trak = hb_aat_layout_has_tracking (face);
+  plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face);
+}
+
+bool
+hb_ot_shape_plan_t::init0 (hb_face_t                     *face,
+			   const hb_shape_plan_key_t     *key)
+{
+  map.init ();
+  aat_map.init ();
+
+  hb_ot_shape_planner_t planner (face,
+				 &key->props);
+
+  hb_ot_shape_collect_features (&planner,
+				key->user_features,
+				key->num_user_features);
+
+  planner.compile (*this, key->ot);
+
+  if (shaper->data_create)
+  {
+    data = shaper->data_create (this);
+    if (unlikely (!data))
+      return false;
+  }
+
+  return true;
+}
+
+void
+hb_ot_shape_plan_t::fini ()
+{
+  if (shaper->data_destroy)
+    shaper->data_destroy (const_cast<void *> (data));
+
+  map.fini ();
+  aat_map.fini ();
+}
+
+void
+hb_ot_shape_plan_t::substitute (hb_font_t   *font,
+				hb_buffer_t *buffer) const
+{
+  if (unlikely (apply_morx))
+    hb_aat_layout_substitute (this, font, buffer);
+  else
+    map.substitute (this, font, buffer);
+}
+
+void
+hb_ot_shape_plan_t::position (hb_font_t   *font,
+			      hb_buffer_t *buffer) const
+{
+  if (this->apply_gpos)
+    map.position (this, font, buffer);
+  else if (this->apply_kerx)
+    hb_aat_layout_position (this, font, buffer);
+  else if (this->apply_kern)
+    hb_ot_layout_kern (this, font, buffer);
+  else
+    _hb_ot_shape_fallback_kern (this, font, buffer);
+
+  if (this->apply_trak)
+    hb_aat_layout_track (this, font, buffer);
 }
 
 
@@ -144,7 +250,6 @@
 
 static void
 hb_ot_shape_collect_features (hb_ot_shape_planner_t          *planner,
-			      const hb_segment_properties_t  *props,
 			      const hb_feature_t             *user_features,
 			      unsigned int                    num_user_features)
 {
@@ -153,7 +258,7 @@
   map->enable_feature (HB_TAG('r','v','r','n'));
   map->add_gsub_pause (nullptr);
 
-  switch (props->direction) {
+  switch (planner->props.direction) {
     case HB_DIRECTION_LTR:
       map->enable_feature (HB_TAG ('l','t','r','a'));
       map->enable_feature (HB_TAG ('l','t','r','m'));
@@ -177,17 +282,22 @@
   /* Random! */
   map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE);
 
-  map->enable_feature (HB_TAG('H','A','R','F'));
+  /* Tracking.  We enable dummy feature here just to allow disabling
+   * AAT 'trak' table using features.
+   * https://github.com/harfbuzz/harfbuzz/issues/1303 */
+  map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK);
+
+  map->enable_feature (HB_TAG ('H','A','R','F'));
 
   if (planner->shaper->collect_features)
     planner->shaper->collect_features (planner);
 
-  map->enable_feature (HB_TAG('B','U','Z','Z'));
+  map->enable_feature (HB_TAG ('B','U','Z','Z'));
 
   for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++)
     map->add_feature (common_features[i]);
 
-  if (HB_DIRECTION_IS_HORIZONTAL (props->direction))
+  if (HB_DIRECTION_IS_HORIZONTAL (planner->props.direction))
     for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++)
       map->add_feature (horizontal_features[i]);
   else
@@ -199,9 +309,6 @@
     map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH);
   }
 
-  if (planner->shaper->override_features)
-    planner->shaper->override_features (planner);
-
   for (unsigned int i = 0; i < num_user_features; i++)
   {
     const hb_feature_t *feature = &user_features[i];
@@ -210,6 +317,19 @@
 		       feature->end == HB_FEATURE_GLOBAL_END) ?  F_GLOBAL : F_NONE,
 		      feature->value);
   }
+
+  if (planner->apply_morx)
+  {
+    hb_aat_map_builder_t *aat_map = &planner->aat_map;
+    for (unsigned int i = 0; i < num_user_features; i++)
+    {
+      const hb_feature_t *feature = &user_features[i];
+      aat_map->add_feature (feature->tag, feature->value);
+    }
+  }
+
+  if (planner->shaper->override_features)
+    planner->shaper->override_features (planner);
 }
 
 
@@ -217,18 +337,17 @@
  * shaper face data
  */
 
-HB_SHAPER_DATA_ENSURE_DEFINE(ot, face)
+struct hb_ot_face_data_t {};
 
 hb_ot_face_data_t *
 _hb_ot_shaper_face_data_create (hb_face_t *face)
 {
-  return _hb_ot_face_data_create (face);
+  return (hb_ot_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
 
 void
 _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data)
 {
-  _hb_ot_face_data_destroy (data);
 }
 
 
@@ -236,8 +355,6 @@
  * shaper font data
  */
 
-HB_SHAPER_DATA_ENSURE_DEFINE(ot, font)
-
 struct hb_ot_font_data_t {};
 
 hb_ot_font_data_t *
@@ -247,67 +364,12 @@
 }
 
 void
-_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data)
+_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data HB_UNUSED)
 {
 }
 
 
 /*
- * shaper shape_plan data
- */
-
-hb_ot_shape_plan_data_t *
-_hb_ot_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
-				      const hb_feature_t *user_features,
-				      unsigned int        num_user_features,
-				      const int          *coords,
-				      unsigned int        num_coords)
-{
-  hb_ot_shape_plan_t *plan = (hb_ot_shape_plan_t *) calloc (1, sizeof (hb_ot_shape_plan_t));
-  if (unlikely (!plan))
-    return nullptr;
-
-  plan->init ();
-
-  hb_ot_shape_planner_t planner (shape_plan);
-
-  /* Ugly that we have to do this here...
-   * If we are going to apply morx, choose default shaper. */
-  if (_hb_apply_morx (planner.face))
-    planner.shaper = &_hb_ot_complex_shaper_default;
-  else
-    planner.shaper = hb_ot_shape_complex_categorize (&planner);
-
-  hb_ot_shape_collect_features (&planner, &shape_plan->props,
-				user_features, num_user_features);
-
-  planner.compile (*plan, coords, num_coords);
-
-  if (plan->shaper->data_create) {
-    plan->data = plan->shaper->data_create (plan);
-    if (unlikely (!plan->data))
-    {
-      free (plan);
-      return nullptr;
-    }
-  }
-
-  return plan;
-}
-
-void
-_hb_ot_shaper_shape_plan_data_destroy (hb_ot_shape_plan_data_t *plan)
-{
-  if (plan->shaper->data_destroy)
-    plan->shaper->data_destroy (const_cast<void *> (plan->data));
-
-  plan->fini ();
-
-  free (plan);
-}
-
-
-/*
  * shaper
  */
 
@@ -393,7 +455,6 @@
   buffer->output_info (info);
   while (buffer->idx < buffer->len && buffer->successful)
     buffer->next_glyph ();
-
   buffer->swap_buffers ();
 }
 
@@ -445,7 +506,9 @@
 }
 
 
-/* Substitute */
+/*
+ * Substitute
+ */
 
 static inline void
 hb_ot_mirror_chars (const hb_ot_shape_context_t *c)
@@ -551,10 +614,8 @@
 }
 
 static void
-hb_ot_zero_width_default_ignorables (const hb_ot_shape_context_t *c)
+hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer)
 {
-  hb_buffer_t *buffer = c->buffer;
-
   if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) ||
       (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) ||
       (buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES))
@@ -570,21 +631,19 @@
 }
 
 static void
-hb_ot_hide_default_ignorables (const hb_ot_shape_context_t *c)
+hb_ot_hide_default_ignorables (hb_buffer_t *buffer,
+			       hb_font_t   *font)
 {
-  hb_buffer_t *buffer = c->buffer;
-
   if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) ||
       (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES))
     return;
 
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
-  hb_glyph_position_t *pos = buffer->pos;
 
-  hb_codepoint_t invisible = c->buffer->invisible;
+  hb_codepoint_t invisible = buffer->invisible;
   if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) &&
-      (invisible || c->font->get_nominal_glyph (' ', &invisible)))
+      (invisible || font->get_nominal_glyph (' ', &invisible)))
   {
     /* Replace default-ignorables with a zero-advance invisible glyph. */
     for (unsigned int i = 0; i < count; i++)
@@ -594,49 +653,7 @@
     }
   }
   else
-  {
-    /* Merge clusters and delete default-ignorables.
-     * NOTE! We can't use out-buffer as we have positioning data. */
-    unsigned int j = 0;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      if (_hb_glyph_info_is_default_ignorable (&info[i]))
-      {
-	/* Merge clusters.
-	 * Same logic as buffer->delete_glyph(), but for in-place removal. */
-
-	unsigned int cluster = info[i].cluster;
-	if (i + 1 < count && cluster == info[i + 1].cluster)
-	  continue; /* Cluster survives; do nothing. */
-
-	if (j)
-	{
-	  /* Merge cluster backward. */
-	  if (cluster < info[j - 1].cluster)
-	  {
-	    unsigned int mask = info[i].mask;
-	    unsigned int old_cluster = info[j - 1].cluster;
-	    for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--)
-	      buffer->set_cluster (info[k - 1], cluster, mask);
-	  }
-	  continue;
-	}
-
-	if (i + 1 < count)
-	  buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */
-
-	continue;
-      }
-
-      if (j != i)
-      {
-	info[j] = info[i];
-	pos[j] = pos[i];
-      }
-      j++;
-    }
-    buffer->len = j;
-  }
+    hb_ot_layout_delete_glyphs_inplace (buffer, _hb_glyph_info_is_default_ignorable);
 }
 
 
@@ -653,10 +670,10 @@
 }
 
 static inline void
-hb_synthesize_glyph_classes (const hb_ot_shape_context_t *c)
+hb_synthesize_glyph_classes (hb_buffer_t *buffer)
 {
-  unsigned int count = c->buffer->len;
-  hb_glyph_info_t *info = c->buffer->info;
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
   {
     hb_ot_layout_glyph_props_flags_t klass;
@@ -708,16 +725,13 @@
   hb_ot_layout_substitute_start (c->font, buffer);
 
   if (c->plan->fallback_glyph_classes)
-    hb_synthesize_glyph_classes (c);
+    hb_synthesize_glyph_classes (c->buffer);
 
-  if (unlikely (c->plan->apply_morx))
-    hb_aat_layout_substitute (c->plan, c->font, c->buffer);
-  else
-    c->plan->substitute (c->font, buffer);
+  c->plan->substitute (c->font, buffer);
 }
 
 static inline void
-hb_ot_substitute (const hb_ot_shape_context_t *c)
+hb_ot_substitute_pre (const hb_ot_shape_context_t *c)
 {
   hb_ot_substitute_default (c);
 
@@ -726,7 +740,21 @@
   hb_ot_substitute_complex (c);
 }
 
-/* Position */
+static inline void
+hb_ot_substitute_post (const hb_ot_shape_context_t *c)
+{
+  hb_ot_hide_default_ignorables (c->buffer, c->font);
+  if (c->plan->apply_morx)
+    hb_aat_layout_remove_deleted_glyphs (c->buffer);
+
+  if (c->plan->shaper->postprocess_glyphs)
+    c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
+}
+
+
+/*
+ * Position
+ */
 
 static inline void
 adjust_mark_offsets (hb_glyph_position_t *pos)
@@ -797,17 +825,16 @@
   hb_glyph_info_t *info = c->buffer->info;
   hb_glyph_position_t *pos = c->buffer->pos;
 
-  /* If the font has no GPOS, AND, no fallback positioning will
-   * happen, AND, direction is forward, then when zeroing mark
-   * widths, we shift the mark with it, such that the mark
-   * is positioned hanging over the previous glyph.  When
+  /* If the font has no GPOS and direction is forward, then when
+   * zeroing mark widths, we shift the mark with it, such that the
+   * mark is positioned hanging over the previous glyph.  When
    * direction is backward we don't shift and it will end up
    * hanging over the next glyph after the final reordering.
-   * If fallback positinoing happens or GPOS is present, we don't
-   * care.
+   *
+   * Note: If fallback positinoing happens, we don't care about
+   * this as it will be overriden.
    */
-  bool adjust_offsets_when_zeroing = c->plan->fallback_mark_positioning &&
-				     !c->plan->shaper->fallback_position &&
+  bool adjust_offsets_when_zeroing = c->plan->adjust_mark_positioning_when_zeroing &&
 				     HB_DIRECTION_IS_FORWARD (c->buffer->props.direction);
 
   /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */
@@ -821,7 +848,7 @@
 
   hb_ot_layout_position_start (c->font, c->buffer);
 
-  if (!c->plan->apply_kerx)
+  if (c->plan->zero_marks)
     switch (c->plan->shaper->zero_width_marks)
     {
       case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
@@ -834,15 +861,9 @@
 	break;
     }
 
-  if (c->plan->apply_gpos)
-    c->plan->position (c->font, c->buffer);
-  else if (c->plan->apply_kerx)
-    hb_aat_layout_position (c->plan, c->font, c->buffer);
+  c->plan->position (c->font, c->buffer);
 
-  if (c->plan->apply_trak)
-    hb_aat_layout_track (c->plan, c->font, c->buffer);
-
-  if (!c->plan->apply_kerx)
+  if (c->plan->zero_marks)
     switch (c->plan->shaper->zero_width_marks)
     {
       case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
@@ -855,9 +876,11 @@
 	break;
     }
 
-  /* Finishing off GPOS has to follow a certain order. */
+  /* Finish off.  Has to follow a certain order. */
   hb_ot_layout_position_finish_advances (c->font, c->buffer);
-  hb_ot_zero_width_default_ignorables (c);
+  hb_ot_zero_width_default_ignorables (c->buffer);
+  if (c->plan->apply_morx)
+    hb_aat_layout_zero_width_deleted_glyphs (c->buffer);
   hb_ot_layout_position_finish_offsets (c->font, c->buffer);
 
   /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
@@ -866,6 +889,9 @@
       c->font->subtract_glyph_h_origin (info[i].codepoint,
 					&pos[i].x_offset,
 					&pos[i].y_offset);
+
+  if (c->plan->fallback_mark_positioning)
+    _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer);
 }
 
 static inline void
@@ -877,19 +903,9 @@
 
   hb_ot_position_complex (c);
 
-  if (c->plan->fallback_mark_positioning && c->plan->shaper->fallback_position)
-    _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer);
-
   if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
     hb_buffer_reverse (c->buffer);
 
-  /* Visual fallback goes here. */
-
-  if (c->plan->apply_kern)
-    hb_ot_layout_kern (c->font, c->buffer, c->plan->kern_mask);
-  else if (c->plan->fallback_kerning)
-    _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
-
   _hb_buffer_deallocate_gsubgpos_vars (c->buffer);
 }
 
@@ -955,13 +971,9 @@
   if (c->plan->shaper->preprocess_text)
     c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
 
-  hb_ot_substitute (c);
+  hb_ot_substitute_pre (c);
   hb_ot_position (c);
-
-  hb_ot_hide_default_ignorables (c);
-
-  if (c->plan->shaper->postprocess_glyphs)
-    c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
+  hb_ot_substitute_post (c);
 
   hb_propagate_flags (c->buffer);
 
@@ -982,7 +994,7 @@
 	      const hb_feature_t *features,
 	      unsigned int        num_features)
 {
-  hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), font, font->face, buffer, features, num_features};
+  hb_ot_shape_context_t c = {&shape_plan->ot, font, font->face, buffer, features, num_features};
   hb_ot_shape_internal (&c);
 
   return true;
@@ -999,8 +1011,7 @@
 				  hb_tag_t         table_tag,
 				  hb_set_t        *lookup_indexes /* OUT */)
 {
-  /* XXX Does the first part always succeed? */
-  HB_SHAPER_DATA_GET (shape_plan)->collect_lookups (table_tag, lookup_indexes);
+  shape_plan->ot.collect_lookups (table_tag, lookup_indexes);
 }
 
 
diff --git a/src/hb-ot-shape.hh b/src/hb-ot-shape.hh
index c9c0d3e..73a11e1 100644
--- a/src/hb-ot-shape.hh
+++ b/src/hb-ot-shape.hh
@@ -30,25 +30,54 @@
 #include "hb.hh"
 
 #include "hb-ot-map.hh"
-#include "hb-shape-plan.hh"
+#include "hb-aat-map.hh"
 
 
+struct hb_ot_shape_plan_key_t
+{
+  unsigned int variations_index[2];
+
+  void init (hb_face_t   *face,
+		    const int   *coords,
+		    unsigned int num_coords)
+  {
+    for (unsigned int table_index = 0; table_index < 2; table_index++)
+      hb_ot_layout_table_find_feature_variations (face,
+						  table_tags[table_index],
+						  coords,
+						  num_coords,
+						  &variations_index[table_index]);
+  }
+
+  bool equal (const hb_ot_shape_plan_key_t *other)
+  {
+    return 0 == memcmp (this, other, sizeof (*this));
+  }
+};
+
+
+struct hb_shape_plan_key_t;
 
 struct hb_ot_shape_plan_t
 {
   hb_segment_properties_t props;
   const struct hb_ot_complex_shaper_t *shaper;
   hb_ot_map_t map;
+  hb_aat_map_t aat_map;
   const void *data;
-  hb_mask_t rtlm_mask, frac_mask, numr_mask, dnom_mask;
+  hb_mask_t frac_mask, numr_mask, dnom_mask;
+  hb_mask_t rtlm_mask;
   hb_mask_t kern_mask;
+  hb_mask_t trak_mask;
 
   bool requested_kerning : 1;
+  bool requested_tracking : 1;
   bool has_frac : 1;
   bool has_gpos_mark : 1;
+  bool zero_marks : 1;
   bool fallback_glyph_classes : 1;
-  bool fallback_kerning : 1;
   bool fallback_mark_positioning : 1;
+  bool adjust_mark_positioning_when_zeroing : 1;
 
   bool apply_gpos : 1;
   bool apply_kerx : 1;
@@ -56,8 +85,7 @@
   bool apply_morx : 1;
   bool apply_trak : 1;
 
-
-  inline void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const
+  void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const
   {
     unsigned int table_index;
     switch (table_tag) {
@@ -67,39 +95,34 @@
     }
     map.collect_lookups (table_index, lookups);
   }
-  inline void substitute (hb_font_t *font, hb_buffer_t *buffer) const { map.substitute (this, font, buffer); }
-  inline void position (hb_font_t *font, hb_buffer_t *buffer) const { map.position (this, font, buffer); }
 
-  void init (void)
-  {
-    memset (this, 0, sizeof (*this));
-    map.init ();
-  }
-  void fini (void) {
-    map.fini ();
-  }
+  HB_INTERNAL bool init0 (hb_face_t                     *face,
+			  const hb_shape_plan_key_t     *key);
+  HB_INTERNAL void fini ();
+
+  HB_INTERNAL void substitute (hb_font_t *font, hb_buffer_t *buffer) const;
+  HB_INTERNAL void position (hb_font_t *font, hb_buffer_t *buffer) const;
 };
 
+struct hb_shape_plan_t;
+
 struct hb_ot_shape_planner_t
 {
   /* In the order that they are filled in. */
   hb_face_t *face;
   hb_segment_properties_t props;
-  const struct hb_ot_complex_shaper_t *shaper;
   hb_ot_map_builder_t map;
+  hb_aat_map_builder_t aat_map;
+  bool apply_morx : 1;
+  bool script_zero_marks : 1;
+  bool script_fallback_mark_positioning : 1;
+  const struct hb_ot_complex_shaper_t *shaper;
 
-  hb_ot_shape_planner_t (const hb_shape_plan_t *master_plan) :
-			 face (master_plan->face_unsafe),
-			 props (master_plan->props),
-			 shaper (nullptr),
-			 map (face, &props) {}
+  HB_INTERNAL hb_ot_shape_planner_t (hb_face_t                     *face,
+				     const hb_segment_properties_t *props);
 
-  HB_INTERNAL void compile (hb_ot_shape_plan_t &plan,
-			    const int          *coords,
-			    unsigned int        num_coords);
-
-  private:
-  HB_DISALLOW_COPY_AND_ASSIGN (hb_ot_shape_planner_t);
+  HB_INTERNAL void compile (hb_ot_shape_plan_t           &plan,
+			    const hb_ot_shape_plan_key_t &key);
 };
 
 
diff --git a/src/hb-ot-stat-table.hh b/src/hb-ot-stat-table.hh
new file mode 100644
index 0000000..ffab28c
--- /dev/null
+++ b/src/hb-ot-stat-table.hh
@@ -0,0 +1,280 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_OT_STAT_TABLE_HH
+#define HB_OT_STAT_TABLE_HH
+
+#include "hb-open-type.hh"
+#include "hb-ot-layout-common.hh"
+
+/*
+ * STAT -- Style Attributes
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/stat
+ */
+#define HB_OT_TAG_STAT HB_TAG('S','T','A','T')
+
+
+namespace OT {
+
+enum
+{
+  OLDER_SIBLING_FONT_ATTRIBUTE = 0x0001,	/* If set, this axis value table
+						 * provides axis value information
+						 * that is applicable to other fonts
+						 * within the same font family. This
+						 * is used if the other fonts were
+						 * released earlier and did not include
+						 * information about values for some axis.
+						 * If newer versions of the other
+						 * fonts include the information
+						 * themselves and are present,
+						 * then this record is ignored. */
+  ELIDABLE_AXIS_VALUE_NAME = 0x0002		/* If set, it indicates that the axis
+						 * value represents the “normal” value
+						 * for the axis and may be omitted when
+						 * composing name strings. */
+  // Reserved = 0xFFFC				/* Reserved for future use — set to zero. */
+};
+
+struct AxisValueFormat1
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier — set to 1. */
+  HBUINT16	axisIndex;	/* Zero-base index into the axis record array
+				 * identifying the axis of design variation
+				 * to which the axis value record applies.
+				 * Must be less than designAxisCount. */
+  HBUINT16	flags;		/* Flags — see below for details. */
+  NameID	valueNameID;	/* The name ID for entries in the 'name' table
+				 * that provide a display string for this
+				 * attribute value. */
+  Fixed		value;		/* A numeric value for this attribute value. */
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+struct AxisValueFormat2
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier — set to 2. */
+  HBUINT16	axisIndex;	/* Zero-base index into the axis record array
+				 * identifying the axis of design variation
+				 * to which the axis value record applies.
+				 * Must be less than designAxisCount. */
+  HBUINT16	flags;		/* Flags — see below for details. */
+  NameID	valueNameID;	/* The name ID for entries in the 'name' table
+				 * that provide a display string for this
+				 * attribute value. */
+  Fixed		nominalValue;	/* A numeric value for this attribute value. */
+  Fixed		rangeMinValue;	/* The minimum value for a range associated
+				 * with the specified name ID. */
+  Fixed		rangeMaxValue;	/* The maximum value for a range associated
+				 * with the specified name ID. */
+  public:
+  DEFINE_SIZE_STATIC (20);
+};
+
+struct AxisValueFormat3
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier — set to 3. */
+  HBUINT16	axisIndex;	/* Zero-base index into the axis record array
+				 * identifying the axis of design variation
+				 * to which the axis value record applies.
+				 * Must be less than designAxisCount. */
+  HBUINT16	flags;		/* Flags — see below for details. */
+  NameID	valueNameID;	/* The name ID for entries in the 'name' table
+				 * that provide a display string for this
+				 * attribute value. */
+  Fixed		value;		/* A numeric value for this attribute value. */
+  Fixed		linkedValue;	/* The numeric value for a style-linked mapping
+				 * from this value. */
+  public:
+  DEFINE_SIZE_STATIC (16);
+};
+
+struct AxisValueRecord
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  protected:
+  HBUINT16	axisIndex;	/* Zero-base index into the axis record array
+				 * identifying the axis to which this value
+				 * applies. Must be less than designAxisCount. */
+  Fixed		value;		/* A numeric value for this attribute value. */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct AxisValueFormat4
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier — set to 4. */
+  HBUINT16	axisCount;	/* The total number of axes contributing to
+				 * this axis-values combination. */
+  HBUINT16	flags;		/* Flags — see below for details. */
+  NameID	valueNameID;	/* The name ID for entries in the 'name' table
+				 * that provide a display string for this
+				 * attribute value. */
+  UnsizedArrayOf<AxisValueRecord>
+		axisValues;	/* Array of AxisValue records that provide the
+				 * combination of axis values, one for each
+				 * contributing axis. */
+  public:
+  DEFINE_SIZE_ARRAY (8, axisValues);
+};
+
+struct AxisValue
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (c->check_struct (this)))
+      return_trace (false);
+
+    switch (u.format)
+    {
+    case 1:  return_trace (likely (u.format1.sanitize (c)));
+    case 2:  return_trace (likely (u.format2.sanitize (c)));
+    case 3:  return_trace (likely (u.format3.sanitize (c)));
+    case 4:  return_trace (likely (u.format4.sanitize (c)));
+    default: return_trace (true);
+    }
+  }
+
+  protected:
+  union
+  {
+  HBUINT16		format;
+  AxisValueFormat1	format1;
+  AxisValueFormat2	format2;
+  AxisValueFormat3	format3;
+  AxisValueFormat4	format4;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+struct StatAxisRecord
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  protected:
+  Tag		tag;		/* A tag identifying the axis of design variation. */
+  NameID	nameID;		/* The name ID for entries in the 'name' table that
+				 * provide a display string for this axis. */
+  HBUINT16	ordering;	/* A value that applications can use to determine
+				 * primary sorting of face names, or for ordering
+				 * of descriptors when composing family or face names. */
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct STAT
+{
+  enum { tableTag = HB_OT_TAG_STAT };
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  majorVersion == 1 &&
+			  minorVersion > 0 &&
+			  designAxesOffset.sanitize (c, this, designAxisCount) &&
+			  offsetToAxisValueOffsets.sanitize (c, this, axisValueCount, &(this+offsetToAxisValueOffsets))));
+  }
+
+  protected:
+  HBUINT16	majorVersion;	/* Major version number of the style attributes
+				 * table — set to 1. */
+  HBUINT16	minorVersion;	/* Minor version number of the style attributes
+				 * table — set to 2. */
+  HBUINT16	designAxisSize;	/* The size in bytes of each axis record. */
+  HBUINT16	designAxisCount;/* The number of design axis records. In a
+				 * font with an 'fvar' table, this value must be
+				 * greater than or equal to the axisCount value
+				 * in the 'fvar' table. In all fonts, must
+				 * be greater than zero if axisValueCount
+				 * is greater than zero. */
+  LOffsetTo<UnsizedArrayOf<StatAxisRecord>, false>
+		designAxesOffset;
+				/* Offset in bytes from the beginning of
+				 * the STAT table to the start of the design
+				 * axes array. If designAxisCount is zero,
+				 * set to zero; if designAxisCount is greater
+				 * than zero, must be greater than zero. */
+  HBUINT16	axisValueCount;	/* The number of axis value tables. */
+  LOffsetTo<UnsizedArrayOf<OffsetTo<AxisValue> >, false>
+		offsetToAxisValueOffsets;
+				/* Offset in bytes from the beginning of
+				 * the STAT table to the start of the design
+				 * axes value offsets array. If axisValueCount
+				 * is zero, set to zero; if axisValueCount is
+				 * greater than zero, must be greater than zero. */
+  NameID	elidedFallbackNameID;
+				/* Name ID used as fallback when projection of
+				 * names into a particular font model produces
+				 * a subfamily name containing only elidable
+				 * elements. */
+  public:
+  DEFINE_SIZE_STATIC (20);
+};
+
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_STAT_TABLE_HH */
diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc
index 4dba9c3..d04e532 100644
--- a/src/hb-ot-tag.cc
+++ b/src/hb-ot-tag.cc
@@ -242,7 +242,6 @@
 static void
 hb_ot_tags_from_language (const char   *lang_str,
 			  const char   *limit,
-			  const char   *private_use_subtag,
 			  unsigned int *count,
 			  hb_tag_t     *tags)
 {
@@ -389,7 +388,7 @@
     needs_language = parse_private_use_subtag (private_use_subtag, language_count, language_tags, "-hbot", TOUPPER);
 
     if (needs_language && language_count && language_tags && *language_count)
-      hb_ot_tags_from_language (lang_str, limit, private_use_subtag, language_count, language_tags);
+      hb_ot_tags_from_language (lang_str, limit, language_count, language_tags);
   }
 
   if (needs_script && script_count && script_tags && *script_count)
@@ -504,7 +503,7 @@
 
 #ifdef MAIN
 static inline void
-test_langs_sorted (void)
+test_langs_sorted ()
 {
   for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages); i++)
   {
@@ -519,7 +518,7 @@
 }
 
 int
-main (void)
+main ()
 {
   test_langs_sorted ();
   return 0;
diff --git a/src/hb-ot-tag.h b/src/hb-ot-tag.h
deleted file mode 100644
index 33a4dbd..0000000
--- a/src/hb-ot-tag.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright © 2009  Red Hat, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_H_IN
-#error "Include <hb-ot.h> instead."
-#endif
-
-#ifndef HB_OT_TAG_H
-#define HB_OT_TAG_H
-
-#include "hb.h"
-
-HB_BEGIN_DECLS
-
-
-#define HB_OT_TAG_DEFAULT_SCRIPT	HB_TAG ('D', 'F', 'L', 'T')
-#define HB_OT_TAG_DEFAULT_LANGUAGE	HB_TAG ('d', 'f', 'l', 't')
-
-/**
- * HB_OT_MAX_TAGS_PER_SCRIPT:
- *
- * Since: 2.0.0
- **/
-#define HB_OT_MAX_TAGS_PER_SCRIPT	3u
-/**
- * HB_OT_MAX_TAGS_PER_LANGUAGE:
- *
- * Since: 2.0.0
- **/
-#define HB_OT_MAX_TAGS_PER_LANGUAGE	3u
-
-HB_EXTERN void
-hb_ot_tags_from_script_and_language (hb_script_t   script,
-				     hb_language_t language,
-				     unsigned int *script_count /* IN/OUT */,
-				     hb_tag_t     *script_tags /* OUT */,
-				     unsigned int *language_count /* IN/OUT */,
-				     hb_tag_t     *language_tags /* OUT */);
-
-HB_EXTERN hb_script_t
-hb_ot_tag_to_script (hb_tag_t tag);
-
-HB_EXTERN hb_language_t
-hb_ot_tag_to_language (hb_tag_t tag);
-
-HB_EXTERN void
-hb_ot_tags_to_script_and_language (hb_tag_t       script_tag,
-				   hb_tag_t       language_tag,
-				   hb_script_t   *script /* OUT */,
-				   hb_language_t *language /* OUT */);
-
-
-HB_END_DECLS
-
-#endif /* HB_OT_TAG_H */
diff --git a/src/hb-ot-var-avar-table.hh b/src/hb-ot-var-avar-table.hh
index d100ca2..67a60f5e 100644
--- a/src/hb-ot-var-avar-table.hh
+++ b/src/hb-ot-var-avar-table.hh
@@ -42,7 +42,7 @@
 
 struct AxisValueMap
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -59,7 +59,7 @@
 
 struct SegmentMaps : ArrayOf<AxisValueMap>
 {
-  inline int map (int value) const
+  int map (int value) const
   {
     /* The following special-cases are not part of OpenType, which requires
      * that at least -1, 0, and +1 must be mapped. But we include these as
@@ -94,14 +94,14 @@
   }
 
   public:
-  DEFINE_SIZE_ARRAY (2, arrayZ);
+  DEFINE_SIZE_ARRAY (2, *this);
 };
 
 struct avar
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_avar;
+  enum { tableTag = HB_OT_TAG_avar };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!(version.sanitize (c) &&
@@ -109,7 +109,7 @@
 		    c->check_struct (this))))
       return_trace (false);
 
-    const SegmentMaps *map = axisSegmentMapsZ.arrayZ;
+    const SegmentMaps *map = &firstAxisSegmentMaps;
     unsigned int count = axisCount;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -121,11 +121,11 @@
     return_trace (true);
   }
 
-  inline void map_coords (int *coords, unsigned int coords_length) const
+  void map_coords (int *coords, unsigned int coords_length) const
   {
     unsigned int count = MIN<unsigned int> (coords_length, axisCount);
 
-    const SegmentMaps *map = axisSegmentMapsZ.arrayZ;
+    const SegmentMaps *map = &firstAxisSegmentMaps;
     for (unsigned int i = 0; i < count; i++)
     {
       coords[i] = map->map (coords[i]);
@@ -140,8 +140,7 @@
   HBUINT16	axisCount;	/* The number of variation axes in the font. This
 				 * must be the same number as axisCount in the
 				 * 'fvar' table. */
-  UnsizedArrayOf<SegmentMaps>
-		axisSegmentMapsZ;
+  SegmentMaps   firstAxisSegmentMaps;
 
   public:
   DEFINE_SIZE_MIN (8);
diff --git a/src/hb-ot-var-fvar-table.hh b/src/hb-ot-var-fvar-table.hh
index 96c39c1..76c9eb2 100644
--- a/src/hb-ot-var-fvar-table.hh
+++ b/src/hb-ot-var-fvar-table.hh
@@ -42,7 +42,12 @@
 
 struct InstanceRecord
 {
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const
+  friend struct fvar;
+
+  hb_array_t<const Fixed> get_coordinates (unsigned int axis_count) const
+  { return coordinatesZ.as_array (axis_count); }
+
+  bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
@@ -52,7 +57,7 @@
   protected:
   NameID	subfamilyNameID;/* The name ID for entries in the 'name' table
 				 * that provide subfamily names for this instance. */
-  HBUINT16	reserved;	/* Reserved for future use — set to 0. */
+  HBUINT16	flags;		/* Reserved for future use — set to 0. */
   UnsizedArrayOf<Fixed>
 		coordinatesZ;	/* The coordinates array for this instance. */
   //NameID	postScriptNameIDX;/*Optional. The name ID for entries in the 'name'
@@ -60,12 +65,17 @@
   //				  * instance. */
 
   public:
-  DEFINE_SIZE_ARRAY (4, coordinatesZ);
+  DEFINE_SIZE_UNBOUNDED (4);
 };
 
 struct AxisRecord
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  enum
+  {
+    AXIS_FLAG_HIDDEN	= 0x0001,
+  };
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -76,7 +86,7 @@
   Fixed		minValue;	/* The minimum coordinate value for the axis. */
   Fixed		defaultValue;	/* The default coordinate value for the axis. */
   Fixed		maxValue;	/* The maximum coordinate value for the axis. */
-  HBUINT16	reserved;	/* Reserved for future use — set to 0. */
+  HBUINT16	flags;		/* Axis flags. */
   NameID	axisNameID;	/* The name ID for entries in the 'name' table that
 				 * provide a display name for this axis. */
 
@@ -86,52 +96,58 @@
 
 struct fvar
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_fvar;
+  enum { tableTag = HB_OT_TAG_fvar };
 
-  inline bool has_data (void) const { return version.to_int () != 0; }
+  bool has_data () const { return version.to_int (); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
 		  likely (version.major == 1) &&
 		  c->check_struct (this) &&
+		  axisSize == 20 && /* Assumed in our code. */
 		  instanceSize >= axisCount * 4 + 4 &&
-		  axisSize <= 1024 && /* Arbitrary, just to simplify overflow checks. */
-		  instanceSize <= 1024 && /* Arbitrary, just to simplify overflow checks. */
-		  c->check_range (this, things) &&
-		  c->check_range (&StructAtOffset<char> (this, things),
-				  axisCount * axisSize + instanceCount * instanceSize));
+		  get_axes ().sanitize (c) &&
+		  c->check_range (get_instance (0), instanceCount, instanceSize));
   }
 
-  inline unsigned int get_axis_count (void) const
-  { return axisCount; }
+  unsigned int get_axis_count () const { return axisCount; }
 
-  inline bool get_axis (unsigned int index, hb_ot_var_axis_t *info) const
+  void get_axis_deprecated (unsigned int axis_index,
+				   hb_ot_var_axis_t *info) const
   {
-    if (unlikely (index >= axisCount))
-      return false;
-
-    if (info)
-    {
-      const AxisRecord &axis = get_axes ()[index];
-      info->tag = axis.axisTag;
-      info->name_id =  axis.axisNameID;
-      info->default_value = axis.defaultValue / 65536.;
-      /* Ensure order, to simplify client math. */
-      info->min_value = MIN<float> (info->default_value, axis.minValue / 65536.);
-      info->max_value = MAX<float> (info->default_value, axis.maxValue / 65536.);
-    }
-
-    return true;
+    const AxisRecord &axis = get_axes ()[axis_index];
+    info->tag = axis.axisTag;
+    info->name_id =  axis.axisNameID;
+    info->default_value = axis.defaultValue / 65536.;
+    /* Ensure order, to simplify client math. */
+    info->min_value = MIN<float> (info->default_value, axis.minValue / 65536.);
+    info->max_value = MAX<float> (info->default_value, axis.maxValue / 65536.);
   }
 
-  inline unsigned int get_axis_infos (unsigned int      start_offset,
-				      unsigned int     *axes_count /* IN/OUT */,
-				      hb_ot_var_axis_t *axes_array /* OUT */) const
+  void get_axis_info (unsigned int axis_index,
+		      hb_ot_var_axis_info_t *info) const
+  {
+    const AxisRecord &axis = get_axes ()[axis_index];
+    info->axis_index = axis_index;
+    info->tag = axis.axisTag;
+    info->name_id =  axis.axisNameID;
+    info->flags = (hb_ot_var_axis_flags_t) (unsigned int) axis.flags;
+    info->default_value = axis.defaultValue / 65536.;
+    /* Ensure order, to simplify client math. */
+    info->min_value = MIN<float> (info->default_value, axis.minValue / 65536.);
+    info->max_value = MAX<float> (info->default_value, axis.maxValue / 65536.);
+    info->reserved = 0;
+  }
+
+  unsigned int get_axes_deprecated (unsigned int      start_offset,
+				    unsigned int     *axes_count /* IN/OUT */,
+				    hb_ot_var_axis_t *axes_array /* OUT */) const
   {
     if (axes_count)
     {
+      /* TODO Rewrite as hb_array_t<>::sub-array() */
       unsigned int count = axisCount;
       start_offset = MIN (start_offset, count);
 
@@ -142,32 +158,70 @@
       *axes_count = count;
 
       for (unsigned int i = 0; i < count; i++)
-	get_axis (start_offset + i, axes_array + i);
+	get_axis_deprecated (start_offset + i, axes_array + i);
     }
     return axisCount;
   }
 
-  inline bool find_axis (hb_tag_t tag, unsigned int *index, hb_ot_var_axis_t *info) const
+  unsigned int get_axis_infos (unsigned int           start_offset,
+			       unsigned int          *axes_count /* IN/OUT */,
+			       hb_ot_var_axis_info_t *axes_array /* OUT */) const
+  {
+    if (axes_count)
+    {
+      /* TODO Rewrite as hb_array_t<>::sub-array() */
+      unsigned int count = axisCount;
+      start_offset = MIN (start_offset, count);
+
+      count -= start_offset;
+      axes_array += start_offset;
+
+      count = MIN (count, *axes_count);
+      *axes_count = count;
+
+      for (unsigned int i = 0; i < count; i++)
+	get_axis_info (start_offset + i, axes_array + i);
+    }
+    return axisCount;
+  }
+
+  bool find_axis_deprecated (hb_tag_t tag,
+			     unsigned int *axis_index,
+			     hb_ot_var_axis_t *info) const
   {
     const AxisRecord *axes = get_axes ();
     unsigned int count = get_axis_count ();
     for (unsigned int i = 0; i < count; i++)
       if (axes[i].axisTag == tag)
       {
-        if (index)
-	  *index = i;
-	return get_axis (i, info);
+        if (axis_index)
+	  *axis_index = i;
+	get_axis_deprecated (i, info);
+	return true;
       }
-    if (index)
-      *index = HB_OT_VAR_NO_AXIS_INDEX;
+    if (axis_index)
+      *axis_index = HB_OT_VAR_NO_AXIS_INDEX;
     return false;
   }
 
-  inline int normalize_axis_value (unsigned int axis_index, float v) const
+  bool find_axis_info (hb_tag_t tag,
+		       hb_ot_var_axis_info_t *info) const
   {
-    hb_ot_var_axis_t axis;
-    if (!get_axis (axis_index, &axis))
-      return 0;
+    const AxisRecord *axes = get_axes ();
+    unsigned int count = get_axis_count ();
+    for (unsigned int i = 0; i < count; i++)
+      if (axes[i].axisTag == tag)
+      {
+	get_axis_info (i, info);
+	return true;
+      }
+    return false;
+  }
+
+  int normalize_axis_value (unsigned int axis_index, float v) const
+  {
+    hb_ot_var_axis_info_t axis;
+    get_axis_info (axis_index, &axis);
 
     v = MAX (MIN (v, axis.max_value), axis.min_value); /* Clamp. */
 
@@ -180,17 +234,62 @@
     return (int) (v * 16384.f + (v >= 0.f ? .5f : -.5f));
   }
 
-  protected:
-  inline const AxisRecord * get_axes (void) const
-  { return &StructAtOffset<AxisRecord> (this, things); }
+  unsigned int get_instance_count () const { return instanceCount; }
 
-  inline const InstanceRecord * get_instances (void) const
-  { return &StructAtOffset<InstanceRecord> (get_axes () + axisCount, 0); }
+  hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const
+  {
+    const InstanceRecord *instance = get_instance (instance_index);
+    if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID;
+    return instance->subfamilyNameID;
+  }
+
+  hb_ot_name_id_t get_instance_postscript_name_id (unsigned int instance_index) const
+  {
+    const InstanceRecord *instance = get_instance (instance_index);
+    if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID;
+    if (instanceSize >= axisCount * 4 + 6)
+      return StructAfter<NameID> (instance->get_coordinates (axisCount));
+    return HB_OT_NAME_ID_INVALID;
+  }
+
+  unsigned int get_instance_coords (unsigned int  instance_index,
+					   unsigned int *coords_length, /* IN/OUT */
+					   float        *coords         /* OUT */) const
+  {
+    const InstanceRecord *instance = get_instance (instance_index);
+    if (unlikely (!instance))
+    {
+      if (coords_length)
+        *coords_length = 0;
+      return 0;
+    }
+
+    if (coords_length && *coords_length)
+    {
+      hb_array_t<const Fixed> instanceCoords = instance->get_coordinates (axisCount)
+							 .sub_array (0, *coords_length);
+      for (unsigned int i = 0; i < instanceCoords.len; i++)
+        coords[i] = instanceCoords.arrayZ[i].to_float ();
+    }
+    return axisCount;
+  }
+
+  protected:
+  hb_array_t<const AxisRecord> get_axes () const
+  { return hb_array (&(this+firstAxis), axisCount); }
+
+  const InstanceRecord *get_instance (unsigned int i) const
+  {
+    if (unlikely (i >= instanceCount)) return nullptr;
+   return &StructAtOffset<InstanceRecord> (&StructAfter<InstanceRecord> (get_axes ()),
+					   i * instanceSize);
+  }
 
   protected:
   FixedVersion<>version;	/* Version of the fvar table
 				 * initially set to 0x00010000u */
-  Offset16	things;		/* Offset in bytes from the beginning of the table
+  OffsetTo<AxisRecord>
+		firstAxis;	/* Offset in bytes from the beginning of the table
 				 * to the start of the AxisRecord array. */
   HBUINT16	reserved;	/* This field is permanently reserved. Set to 2. */
   HBUINT16	axisCount;	/* The number of variation axes in the font (the
diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh
index 66e086e..2640f07 100644
--- a/src/hb-ot-var-hvar-table.hh
+++ b/src/hb-ot-var-hvar-table.hh
@@ -35,11 +35,13 @@
 
 struct DeltaSetIndexMap
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  c->check_array (mapDataZ.arrayZ, mapCount, get_width ()));
+		  c->check_range (mapDataZ.arrayZ,
+				  mapCount,
+				  get_width ()));
   }
 
   unsigned int map (unsigned int v) const /* Returns 16.16 outer.inner. */
@@ -71,11 +73,9 @@
   }
 
   protected:
-  inline unsigned int get_width (void) const
-  { return ((format >> 4) & 3) + 1; }
+  unsigned int get_width () const          { return ((format >> 4) & 3) + 1; }
 
-  inline unsigned int get_inner_bitcount (void) const
-  { return (format & 0xF) + 1; }
+  unsigned int get_inner_bitcount () const { return (format & 0xF) + 1; }
 
   protected:
   HBUINT16	format;		/* A packed field that describes the compressed
@@ -100,10 +100,10 @@
 
 struct HVARVVAR
 {
-  static const hb_tag_t HVARTag	= HB_OT_TAG_HVAR;
-  static const hb_tag_t VVARTag	= HB_OT_TAG_VVAR;
+  enum { HVARTag = HB_OT_TAG_HVAR };
+  enum { VVARTag = HB_OT_TAG_VVAR };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
@@ -114,15 +114,14 @@
 		  rsbMap.sanitize (c, this));
   }
 
-  inline float get_advance_var (hb_codepoint_t glyph,
-				const int *coords, unsigned int coord_count) const
+  float get_advance_var (hb_codepoint_t glyph,
+			 const int *coords, unsigned int coord_count) const
   {
     unsigned int varidx = (this+advMap).map (glyph);
     return (this+varStore).get_delta (varidx, coords, coord_count);
   }
 
-  inline bool has_sidebearing_deltas (void) const
-  { return lsbMap && rsbMap; }
+  bool has_sidebearing_deltas () const { return lsbMap && rsbMap; }
 
   protected:
   FixedVersion<>version;	/* Version of the metrics variation table
@@ -141,12 +140,12 @@
 };
 
 struct HVAR : HVARVVAR {
-  static const hb_tag_t tableTag	= HB_OT_TAG_HVAR;
+  enum { tableTag = HB_OT_TAG_HVAR };
 };
 struct VVAR : HVARVVAR {
-  static const hb_tag_t tableTag	= HB_OT_TAG_VVAR;
+  enum { tableTag = HB_OT_TAG_VVAR };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (static_cast<const HVARVVAR *> (this)->sanitize (c) &&
diff --git a/src/hb-ot-var-mvar-table.hh b/src/hb-ot-var-mvar-table.hh
index 5d6b559..a5eeddc 100644
--- a/src/hb-ot-var-mvar-table.hh
+++ b/src/hb-ot-var-mvar-table.hh
@@ -35,7 +35,7 @@
 
 struct VariationValueRecord
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
@@ -58,9 +58,9 @@
 
 struct MVAR
 {
-  static const hb_tag_t tableTag	= HB_OT_TAG_MVAR;
+  enum { tableTag = HB_OT_TAG_MVAR };
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
@@ -68,11 +68,13 @@
 		  c->check_struct (this) &&
 		  valueRecordSize >= VariationValueRecord::static_size &&
 		  varStore.sanitize (c, this) &&
-		  c->check_array (valuesZ.arrayZ, valueRecordCount, valueRecordSize));
+		  c->check_range (valuesZ.arrayZ,
+				  valueRecordCount,
+				  valueRecordSize));
   }
 
-  inline float get_var (hb_tag_t tag,
-			const int *coords, unsigned int coord_count) const
+  float get_var (hb_tag_t tag,
+		 const int *coords, unsigned int coord_count) const
   {
     const VariationValueRecord *record;
     record = (VariationValueRecord *) bsearch (&tag, valuesZ.arrayZ,
@@ -85,7 +87,7 @@
   }
 
 protected:
-  static inline int tag_compare (const void *pa, const void *pb)
+  static int tag_compare (const void *pa, const void *pb)
   {
     const hb_tag_t *a = (const hb_tag_t *) pa;
     const Tag *b = (const Tag *) pb;
diff --git a/src/hb-ot-var.cc b/src/hb-ot-var.cc
index 472ee84..e327fb7 100644
--- a/src/hb-ot-var.cc
+++ b/src/hb-ot-var.cc
@@ -32,31 +32,27 @@
 #include "hb-ot-var-mvar-table.hh"
 #include "hb-ot-var.h"
 
+
+/**
+ * SECTION:hb-ot-var
+ * @title: hb-ot-var
+ * @short_description: OpenType Font Variations
+ * @include: hb-ot.h
+ *
+ * Functions for fetching information about OpenType Variable Fonts.
+ **/
+
+
 /*
  * fvar/avar
  */
 
-static inline const OT::fvar&
-_get_fvar (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::fvar);
-  hb_ot_face_data_t *layout = hb_ot_face_data (face);
-  return *(layout->fvar.get ());
-}
-static inline const OT::avar&
-_get_avar (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::avar);
-  hb_ot_face_data_t *layout = hb_ot_face_data (face);
-  return *(layout->avar.get ());
-}
 
 /**
  * hb_ot_var_has_data:
  * @face: #hb_face_t to test
  *
  * This function allows to verify the presence of OpenType variation data on the face.
- * Alternatively, use hb_ot_var_get_axis_count().
  *
  * Return value: true if face has a `fvar' table and false otherwise
  *
@@ -65,7 +61,7 @@
 hb_bool_t
 hb_ot_var_has_data (hb_face_t *face)
 {
-  return _get_fvar (face).has_data ();
+  return face->table.fvar->has_data ();
 }
 
 /**
@@ -76,14 +72,14 @@
 unsigned int
 hb_ot_var_get_axis_count (hb_face_t *face)
 {
-  const OT::fvar &fvar = _get_fvar (face);
-  return fvar.get_axis_count ();
+  return face->table.fvar->get_axis_count ();
 }
 
 /**
  * hb_ot_var_get_axes:
  *
  * Since: 1.4.2
+ * Deprecated: 2.2.0
  **/
 unsigned int
 hb_ot_var_get_axes (hb_face_t        *face,
@@ -91,14 +87,14 @@
 		    unsigned int     *axes_count /* IN/OUT */,
 		    hb_ot_var_axis_t *axes_array /* OUT */)
 {
-  const OT::fvar &fvar = _get_fvar (face);
-  return fvar.get_axis_infos (start_offset, axes_count, axes_array);
+  return face->table.fvar->get_axes_deprecated (start_offset, axes_count, axes_array);
 }
 
 /**
  * hb_ot_var_find_axis:
  *
  * Since: 1.4.2
+ * Deprecated: 2.2.0
  **/
 hb_bool_t
 hb_ot_var_find_axis (hb_face_t        *face,
@@ -106,8 +102,68 @@
 		     unsigned int     *axis_index,
 		     hb_ot_var_axis_t *axis_info)
 {
-  const OT::fvar &fvar = _get_fvar (face);
-  return fvar.find_axis (axis_tag, axis_index, axis_info);
+  return face->table.fvar->find_axis_deprecated (axis_tag, axis_index, axis_info);
+}
+
+/**
+ * hb_ot_var_get_axis_infos:
+ *
+ * Since: 2.2.0
+ **/
+HB_EXTERN unsigned int
+hb_ot_var_get_axis_infos (hb_face_t             *face,
+			  unsigned int           start_offset,
+			  unsigned int          *axes_count /* IN/OUT */,
+			  hb_ot_var_axis_info_t *axes_array /* OUT */)
+{
+  return face->table.fvar->get_axis_infos (start_offset, axes_count, axes_array);
+}
+
+/**
+ * hb_ot_var_find_axis_info:
+ *
+ * Since: 2.2.0
+ **/
+HB_EXTERN hb_bool_t
+hb_ot_var_find_axis_info (hb_face_t             *face,
+			  hb_tag_t               axis_tag,
+			  hb_ot_var_axis_info_t *axis_info)
+{
+  return face->table.fvar->find_axis_info (axis_tag, axis_info);
+}
+
+
+/*
+ * Named instances.
+ */
+
+unsigned int
+hb_ot_var_get_named_instance_count (hb_face_t *face)
+{
+  return face->table.fvar->get_instance_count ();
+}
+
+hb_ot_name_id_t
+hb_ot_var_named_instance_get_subfamily_name_id (hb_face_t   *face,
+						unsigned int instance_index)
+{
+  return face->table.fvar->get_instance_subfamily_name_id (instance_index);
+}
+
+hb_ot_name_id_t
+hb_ot_var_named_instance_get_postscript_name_id (hb_face_t  *face,
+						unsigned int instance_index)
+{
+  return face->table.fvar->get_instance_postscript_name_id (instance_index);
+}
+
+unsigned int
+hb_ot_var_named_instance_get_design_coords (hb_face_t    *face,
+					    unsigned int  instance_index,
+					    unsigned int *coords_length, /* IN/OUT */
+					    float        *coords         /* OUT */)
+{
+  return face->table.fvar->get_instance_coords (instance_index, coords_length, coords);
 }
 
 
@@ -126,17 +182,16 @@
   for (unsigned int i = 0; i < coords_length; i++)
     coords[i] = 0;
 
-  const OT::fvar &fvar = _get_fvar (face);
+  const OT::fvar &fvar = *face->table.fvar;
   for (unsigned int i = 0; i < variations_length; i++)
   {
-    unsigned int axis_index;
-    if (hb_ot_var_find_axis (face, variations[i].tag, &axis_index, nullptr) &&
-	axis_index < coords_length)
-      coords[axis_index] = fvar.normalize_axis_value (axis_index, variations[i].value);
+    hb_ot_var_axis_info_t info;
+    if (hb_ot_var_find_axis_info (face, variations[i].tag, &info) &&
+	info.axis_index < coords_length)
+      coords[info.axis_index] = fvar.normalize_axis_value (info.axis_index, variations[i].value);
   }
 
-  const OT::avar &avar = _get_avar (face);
-  avar.map_coords (coords, coords_length);
+  face->table.avar->map_coords (coords, coords_length);
 }
 
 /**
@@ -150,10 +205,9 @@
 			    const float *design_coords, /* IN */
 			    int *normalized_coords /* OUT */)
 {
-  const OT::fvar &fvar = _get_fvar (face);
+  const OT::fvar &fvar = *face->table.fvar;
   for (unsigned int i = 0; i < coords_length; i++)
     normalized_coords[i] = fvar.normalize_axis_value (i, design_coords[i]);
 
-  const OT::avar &avar = _get_avar (face);
-  avar.map_coords (normalized_coords, coords_length);
+  face->table.avar->map_coords (normalized_coords, coords_length);
 }
diff --git a/src/hb-ot-var.h b/src/hb-ot-var.h
index a2c0c5f..cf6f0c9 100644
--- a/src/hb-ot-var.h
+++ b/src/hb-ot-var.h
@@ -47,45 +47,86 @@
  * fvar / avar
  */
 
-/**
- * hb_ot_var_axis_t:
- *
- * Since: 1.4.2
- */
-typedef struct hb_ot_var_axis_t {
-  hb_tag_t tag;
-  unsigned int name_id;
-  float min_value;
-  float default_value;
-  float max_value;
-} hb_ot_var_axis_t;
-
 HB_EXTERN hb_bool_t
 hb_ot_var_has_data (hb_face_t *face);
 
-/**
- * HB_OT_VAR_NO_AXIS_INDEX:
- *
- * Since: 1.4.2
+
+/*
+ * Variation axes.
  */
-#define HB_OT_VAR_NO_AXIS_INDEX		0xFFFFFFFFu
+
 
 HB_EXTERN unsigned int
 hb_ot_var_get_axis_count (hb_face_t *face);
 
+/**
+ * hb_ot_var_axis_flags_t:
+ * @HB_OT_VAR_AXIS_FLAG_HIDDEN: The axis should not be exposed directly in user interfaces.
+ *
+ * Since: 2.2.0
+ */
+typedef enum { /*< flags >*/
+  HB_OT_VAR_AXIS_FLAG_HIDDEN	= 0x00000001u,
+
+  _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= 0x7FFFFFFFu /*< skip >*/
+} hb_ot_var_axis_flags_t;
+
+/**
+ * hb_ot_var_axis_info_t:
+ *
+ * Since: 2.2.0
+ */
+typedef struct hb_ot_var_axis_info_t
+{
+  unsigned int			axis_index;
+  hb_tag_t			tag;
+  hb_ot_name_id_t		name_id;
+  hb_ot_var_axis_flags_t	flags;
+  float				min_value;
+  float				default_value;
+  float				max_value;
+  /*< private >*/
+  unsigned int			reserved;
+} hb_ot_var_axis_info_t;
+
 HB_EXTERN unsigned int
-hb_ot_var_get_axes (hb_face_t        *face,
-		    unsigned int      start_offset,
-		    unsigned int     *axes_count /* IN/OUT */,
-		    hb_ot_var_axis_t *axes_array /* OUT */);
+hb_ot_var_get_axis_infos (hb_face_t             *face,
+			  unsigned int           start_offset,
+			  unsigned int          *axes_count /* IN/OUT */,
+			  hb_ot_var_axis_info_t *axes_array /* OUT */);
 
 HB_EXTERN hb_bool_t
-hb_ot_var_find_axis (hb_face_t        *face,
-		     hb_tag_t          axis_tag,
-		     unsigned int     *axis_index,
-		     hb_ot_var_axis_t *axis_info);
+hb_ot_var_find_axis_info (hb_face_t             *face,
+			  hb_tag_t               axis_tag,
+			  hb_ot_var_axis_info_t *axis_info);
 
 
+/*
+ * Named instances.
+ */
+
+HB_EXTERN unsigned int
+hb_ot_var_get_named_instance_count (hb_face_t *face);
+
+HB_EXTERN hb_ot_name_id_t
+hb_ot_var_named_instance_get_subfamily_name_id (hb_face_t   *face,
+						unsigned int instance_index);
+
+HB_EXTERN hb_ot_name_id_t
+hb_ot_var_named_instance_get_postscript_name_id (hb_face_t  *face,
+						unsigned int instance_index);
+
+HB_EXTERN unsigned int
+hb_ot_var_named_instance_get_design_coords (hb_face_t    *face,
+					    unsigned int  instance_index,
+					    unsigned int *coords_length, /* IN/OUT */
+					    float        *coords         /* OUT */);
+
+
+/*
+ * Conversions.
+ */
+
 HB_EXTERN void
 hb_ot_var_normalize_variations (hb_face_t            *face,
 				const hb_variation_t *variations, /* IN */
diff --git a/src/hb-ot-vorg-table.hh b/src/hb-ot-vorg-table.hh
new file mode 100644
index 0000000..884d043
--- /dev/null
+++ b/src/hb-ot-vorg-table.hh
@@ -0,0 +1,181 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_OT_VORG_TABLE_HH
+#define HB_OT_VORG_TABLE_HH
+
+#include "hb-open-type.hh"
+
+/*
+ * VORG -- Vertical Origin Table
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/vorg
+ */
+#define HB_OT_TAG_VORG HB_TAG('V','O','R','G')
+
+namespace OT {
+
+struct VertOriginMetric
+{
+  int cmp (hb_codepoint_t g) const { return glyph.cmp (g); }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  GlyphID	glyph;
+  FWORD		vertOriginY;
+
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct VORG
+{
+  enum { tableTag = HB_OT_TAG_VORG };
+
+  bool has_data () const { return version.to_int (); }
+
+  int get_y_origin (hb_codepoint_t glyph) const
+  {
+    unsigned int i;
+    if (!vertYOrigins.bfind (glyph, &i))
+      return defaultVertOriginY;
+    return vertYOrigins[i].vertOriginY;
+  }
+
+  bool _subset (const hb_subset_plan_t *plan HB_UNUSED,
+		const VORG *vorg_table,
+		const hb_vector_t<VertOriginMetric> &subset_metrics,
+		unsigned int dest_sz,
+		void *dest) const
+  {
+    hb_serialize_context_t c (dest, dest_sz);
+
+    VORG *subset_table = c.start_serialize<VORG> ();
+    if (unlikely (!c.extend_min (*subset_table)))
+      return false;
+
+    subset_table->version.major.set (1);
+    subset_table->version.minor.set (0);
+
+    subset_table->defaultVertOriginY.set (vorg_table->defaultVertOriginY);
+    subset_table->vertYOrigins.len.set (subset_metrics.len);
+
+    bool success = true;
+    if (subset_metrics.len > 0)
+    {
+      unsigned int  size = VertOriginMetric::static_size * subset_metrics.len;
+      VertOriginMetric  *metrics = c.allocate_size<VertOriginMetric> (size);
+      if (likely (metrics != nullptr))
+        memcpy (metrics, &subset_metrics[0], size);
+      else
+        success = false;
+    }
+    c.end_serialize ();
+
+    return success;
+  }
+
+  bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_blob_t *vorg_blob = hb_sanitize_context_t().reference_table<VORG> (plan->source);
+    const VORG *vorg_table = vorg_blob->as<VORG> ();
+
+    /* count the number of glyphs to be included in the subset table */
+    hb_vector_t<VertOriginMetric> subset_metrics;
+    subset_metrics.init ();
+    unsigned int glyph = 0;
+    unsigned int i = 0;
+    while ((glyph < plan->glyphs.len) && (i < vertYOrigins.len))
+    {
+      if (plan->glyphs[glyph] > vertYOrigins[i].glyph)
+        i++;
+      else if (plan->glyphs[glyph] < vertYOrigins[i].glyph)
+        glyph++;
+      else
+      {
+        VertOriginMetric *metrics = subset_metrics.push ();
+        metrics->glyph.set (glyph);
+        metrics->vertOriginY.set (vertYOrigins[i].vertOriginY);
+        glyph++;
+        i++;
+      }
+    }
+
+    /* alloc the new table */
+    unsigned int dest_sz = VORG::min_size + VertOriginMetric::static_size * subset_metrics.len;
+    void *dest = (void *) malloc (dest_sz);
+    if (unlikely (!dest))
+    {
+      subset_metrics.fini ();
+      hb_blob_destroy (vorg_blob);
+      return false;
+    }
+
+    /* serialize the new table */
+    if (!_subset (plan, vorg_table, subset_metrics, dest_sz, dest))
+    {
+      subset_metrics.fini ();
+      free (dest);
+      hb_blob_destroy (vorg_blob);
+      return false;
+    }
+
+    hb_blob_t *result = hb_blob_create ((const char *)dest,
+                                        dest_sz,
+                                        HB_MEMORY_MODE_READONLY,
+                                        dest,
+                                        free);
+    bool success = plan->add_table (HB_OT_TAG_VORG, result);
+    hb_blob_destroy (result);
+    subset_metrics.fini ();
+    hb_blob_destroy (vorg_blob);
+    return success;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  version.major == 1 &&
+                  vertYOrigins.sanitize (c));
+  }
+
+  protected:
+  FixedVersion<>	version;		/* Version of VORG table. Set to 0x00010000u. */
+  FWORD			defaultVertOriginY;	/* The default vertical origin. */
+  SortedArrayOf<VertOriginMetric>
+			vertYOrigins;		/* The array of vertical origins. */
+
+  public:
+  DEFINE_SIZE_ARRAY(8, vertYOrigins);
+};
+} /* namespace OT */
+
+#endif /* HB_OT_VORG_TABLE_HH */
diff --git a/src/hb-ot.h b/src/hb-ot.h
index 4b6e3cf..db78469 100644
--- a/src/hb-ot.h
+++ b/src/hb-ot.h
@@ -30,11 +30,12 @@
 
 #include "hb.h"
 
+#include "hb-ot-color.h"
+#include "hb-ot-deprecated.h"
 #include "hb-ot-font.h"
 #include "hb-ot-layout.h"
 #include "hb-ot-math.h"
 #include "hb-ot-name.h"
-#include "hb-ot-tag.h"
 #include "hb-ot-shape.h"
 #include "hb-ot-var.h"
 
diff --git a/src/hb-set-digest.hh b/src/hb-set-digest.hh
index 0f9329f..9f49af1 100644
--- a/src/hb-set-digest.hh
+++ b/src/hb-set-digest.hh
@@ -48,30 +48,25 @@
 template <typename mask_t, unsigned int shift>
 struct hb_set_digest_lowest_bits_t
 {
-  ASSERT_POD ();
-
   enum { mask_bytes = sizeof (mask_t) };
   enum { mask_bits = sizeof (mask_t) * 8 };
-  static const unsigned int num_bits = 0
-				     + (mask_bytes >= 1 ? 3 : 0)
-				     + (mask_bytes >= 2 ? 1 : 0)
-				     + (mask_bytes >= 4 ? 1 : 0)
-				     + (mask_bytes >= 8 ? 1 : 0)
-				     + (mask_bytes >= 16? 1 : 0)
-				     + 0;
+  enum { num_bits = 0
+		  + (mask_bytes >= 1 ? 3 : 0)
+		  + (mask_bytes >= 2 ? 1 : 0)
+		  + (mask_bytes >= 4 ? 1 : 0)
+		  + (mask_bytes >= 8 ? 1 : 0)
+		  + (mask_bytes >= 16? 1 : 0)
+		  + 0 };
 
   static_assert ((shift < sizeof (hb_codepoint_t) * 8), "");
   static_assert ((shift + num_bits <= sizeof (hb_codepoint_t) * 8), "");
 
-  inline void init (void) {
-    mask = 0;
-  }
+  void init () { mask = 0; }
 
-  inline void add (hb_codepoint_t g) {
-    mask |= mask_for (g);
-  }
+  void add (hb_codepoint_t g) { mask |= mask_for (g); }
 
-  inline bool add_range (hb_codepoint_t a, hb_codepoint_t b) {
+  bool add_range (hb_codepoint_t a, hb_codepoint_t b)
+  {
     if ((b >> shift) - (a >> shift) >= mask_bits - 1)
       mask = (mask_t) -1;
     else {
@@ -83,7 +78,7 @@
   }
 
   template <typename T>
-  inline void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+  void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
   {
     for (unsigned int i = 0; i < count; i++)
     {
@@ -92,7 +87,7 @@
     }
   }
   template <typename T>
-  inline bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+  bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
   {
     for (unsigned int i = 0; i < count; i++)
     {
@@ -102,53 +97,53 @@
     return true;
   }
 
-  inline bool may_have (hb_codepoint_t g) const {
-    return !!(mask & mask_for (g));
-  }
+  bool may_have (hb_codepoint_t g) const
+  { return !!(mask & mask_for (g)); }
 
   private:
 
-  static inline mask_t mask_for (hb_codepoint_t g) {
-    return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1));
-  }
+  static mask_t mask_for (hb_codepoint_t g)
+  { return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1)); }
   mask_t mask;
 };
 
 template <typename head_t, typename tail_t>
 struct hb_set_digest_combiner_t
 {
-  ASSERT_POD ();
-
-  inline void init (void) {
+  void init ()
+  {
     head.init ();
     tail.init ();
   }
 
-  inline void add (hb_codepoint_t g) {
+  void add (hb_codepoint_t g)
+  {
     head.add (g);
     tail.add (g);
   }
 
-  inline bool add_range (hb_codepoint_t a, hb_codepoint_t b) {
+  bool add_range (hb_codepoint_t a, hb_codepoint_t b)
+  {
     head.add_range (a, b);
     tail.add_range (a, b);
     return true;
   }
   template <typename T>
-  inline void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+  void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
   {
     head.add_array (array, count, stride);
     tail.add_array (array, count, stride);
   }
   template <typename T>
-  inline bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+  bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
   {
     head.add_sorted_array (array, count, stride);
     tail.add_sorted_array (array, count, stride);
     return true;
   }
 
-  inline bool may_have (hb_codepoint_t g) const {
+  bool may_have (hb_codepoint_t g) const
+  {
     return head.may_have (g) && tail.may_have (g);
   }
 
diff --git a/src/hb-set.cc b/src/hb-set.cc
index 09dc4b4..0682362 100644
--- a/src/hb-set.cc
+++ b/src/hb-set.cc
@@ -27,7 +27,16 @@
 #include "hb-set.hh"
 
 
-/* Public API */
+/**
+ * SECTION:hb-set
+ * @title: hb-set
+ * @short_description: Object representing a set of integers
+ * @include: hb.h
+ *
+ * Set objects represent a mathematical set of integer values.  They are
+ * used in non-shaping API to query certain set of characters or glyphs,
+ * or other integer values.
+ **/
 
 
 /**
@@ -38,7 +47,7 @@
  * Since: 0.9.2
  **/
 hb_set_t *
-hb_set_create (void)
+hb_set_create ()
 {
   hb_set_t *set;
 
@@ -58,7 +67,7 @@
  * Since: 0.9.2
  **/
 hb_set_t *
-hb_set_get_empty (void)
+hb_set_get_empty ()
 {
   return const_cast<hb_set_t *> (&Null(hb_set_t));
 }
@@ -391,7 +400,7 @@
  * Deprecated: 1.6.1
  **/
 void
-hb_set_invert (hb_set_t *set)
+hb_set_invert (hb_set_t *set HB_UNUSED)
 {
 }
 
diff --git a/src/hb-set.hh b/src/hb-set.hh
index 7ca3297..1959688 100644
--- a/src/hb-set.hh
+++ b/src/hb-set.hh
@@ -39,9 +39,13 @@
 
 struct hb_set_t
 {
+  HB_NO_COPY_ASSIGN (hb_set_t);
+  hb_set_t ()  { init (); }
+  ~hb_set_t () { fini (); }
+
   struct page_map_t
   {
-    inline int cmp (const page_map_t *o) const { return (int) o->major - (int) major; }
+    int cmp (const page_map_t &o) const { return (int) o.major - (int) major; }
 
     uint32_t major;
     uint32_t index;
@@ -49,13 +53,13 @@
 
   struct page_t
   {
-    inline void init0 (void) { memset (&v, 0, sizeof (v)); }
-    inline void init1 (void) { memset (&v, 0xff, sizeof (v)); }
+    void init0 () { v.clear (); }
+    void init1 () { v.clear (0xFF); }
 
-    inline unsigned int len (void) const
+    unsigned int len () const
     { return ARRAY_LENGTH_CONST (v); }
 
-    inline bool is_empty (void) const
+    bool is_empty () const
     {
       for (unsigned int i = 0; i < len (); i++)
         if (v[i])
@@ -63,11 +67,11 @@
       return true;
     }
 
-    inline void add (hb_codepoint_t g) { elt (g) |= mask (g); }
-    inline void del (hb_codepoint_t g) { elt (g) &= ~mask (g); }
-    inline bool has (hb_codepoint_t g) const { return !!(elt (g) & mask (g)); }
+    void add (hb_codepoint_t g) { elt (g) |= mask (g); }
+    void del (hb_codepoint_t g) { elt (g) &= ~mask (g); }
+    bool has (hb_codepoint_t g) const { return !!(elt (g) & mask (g)); }
 
-    inline void add_range (hb_codepoint_t a, hb_codepoint_t b)
+    void add_range (hb_codepoint_t a, hb_codepoint_t b)
     {
       elt_t *la = &elt (a);
       elt_t *lb = &elt (b);
@@ -84,12 +88,12 @@
       }
     }
 
-    inline bool is_equal (const page_t *other) const
+    bool is_equal (const page_t *other) const
     {
-      return 0 == memcmp (&v, &other->v, sizeof (v));
+      return 0 == hb_memcmp (&v, &other->v, sizeof (v));
     }
 
-    inline unsigned int get_population (void) const
+    unsigned int get_population () const
     {
       unsigned int pop = 0;
       for (unsigned int i = 0; i < len (); i++)
@@ -97,7 +101,7 @@
       return pop;
     }
 
-    inline bool next (hb_codepoint_t *codepoint) const
+    bool next (hb_codepoint_t *codepoint) const
     {
       unsigned int m = (*codepoint + 1) & MASK;
       if (!m)
@@ -119,7 +123,7 @@
       *codepoint = INVALID;
       return false;
     }
-    inline bool previous (hb_codepoint_t *codepoint) const
+    bool previous (hb_codepoint_t *codepoint) const
     {
       unsigned int m = (*codepoint - 1) & MASK;
       if (m == MASK)
@@ -141,14 +145,14 @@
       *codepoint = INVALID;
       return false;
     }
-    inline hb_codepoint_t get_min (void) const
+    hb_codepoint_t get_min () const
     {
       for (unsigned int i = 0; i < len (); i++)
         if (v[i])
 	  return i * ELT_BITS + elt_get_min (v[i]);
       return INVALID;
     }
-    inline hb_codepoint_t get_max (void) const
+    hb_codepoint_t get_max () const
     {
       for (int i = len () - 1; i >= 0; i--)
         if (v[i])
@@ -160,8 +164,8 @@
     enum { PAGE_BITS = 512 };
     static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, "");
 
-    static inline unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); }
-    static inline unsigned int elt_get_max (const elt_t &elt) { return hb_bit_storage (elt) - 1; }
+    static unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); }
+    static unsigned int elt_get_max (const elt_t &elt) { return hb_bit_storage (elt) - 1; }
 
     typedef hb_vector_size_t<elt_t, PAGE_BITS / 8> vector_t;
 
@@ -185,30 +189,33 @@
   hb_vector_t<page_map_t, 1> page_map;
   hb_vector_t<page_t, 1> pages;
 
-  inline void init_shallow (void)
+  void init_shallow ()
   {
     successful = true;
     population = 0;
     page_map.init ();
     pages.init ();
   }
-  inline void init (void)
+  void init ()
   {
     hb_object_init (this);
     init_shallow ();
   }
-  inline void fini_shallow (void)
+  void fini_shallow ()
   {
+    population = 0;
     page_map.fini ();
     pages.fini ();
   }
-  inline void fini (void)
+  void fini ()
   {
     hb_object_fini (this);
     fini_shallow ();
   }
 
-  inline bool resize (unsigned int count)
+  bool in_error () const { return !successful; }
+
+  bool resize (unsigned int count)
   {
     if (unlikely (!successful)) return false;
     if (!pages.resize (count) || !page_map.resize (count))
@@ -220,15 +227,17 @@
     return true;
   }
 
-  inline void clear (void) {
-    if (unlikely (hb_object_is_inert (this)))
+  void clear ()
+  {
+    if (unlikely (hb_object_is_immutable (this)))
       return;
     successful = true;
     population = 0;
     page_map.resize (0);
     pages.resize (0);
   }
-  inline bool is_empty (void) const {
+  bool is_empty () const
+  {
     unsigned int count = pages.len;
     for (unsigned int i = 0; i < count; i++)
       if (!pages[i].is_empty ())
@@ -236,9 +245,9 @@
     return true;
   }
 
-  inline void dirty (void) { population = (unsigned int) -1; }
+  void dirty () { population = (unsigned int) -1; }
 
-  inline void add (hb_codepoint_t g)
+  void add (hb_codepoint_t g)
   {
     if (unlikely (!successful)) return;
     if (unlikely (g == INVALID)) return;
@@ -246,7 +255,7 @@
     page_t *page = page_for_insert (g); if (unlikely (!page)) return;
     page->add (g);
   }
-  inline bool add_range (hb_codepoint_t a, hb_codepoint_t b)
+  bool add_range (hb_codepoint_t a, hb_codepoint_t b)
   {
     if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */
     if (unlikely (a > b || a == INVALID || b == INVALID)) return false;
@@ -276,7 +285,7 @@
   }
 
   template <typename T>
-  inline void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+  void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
   {
     if (unlikely (!successful)) return;
     if (!count) return;
@@ -302,7 +311,7 @@
   /* Might return false if array looks unsorted.
    * Used for faster rejection of corrupt data. */
   template <typename T>
-  inline bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+  bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
   {
     if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */
     if (!count) return true;
@@ -330,17 +339,17 @@
     return true;
   }
 
-  inline void del (hb_codepoint_t g)
+  void del (hb_codepoint_t g)
   {
     /* TODO perform op even if !successful. */
     if (unlikely (!successful)) return;
-    page_t *p = page_for (g);
-    if (!p)
+    page_t *page = page_for (g);
+    if (!page)
       return;
     dirty ();
-    p->del (g);
+    page->del (g);
   }
-  inline void del_range (hb_codepoint_t a, hb_codepoint_t b)
+  void del_range (hb_codepoint_t a, hb_codepoint_t b)
   {
     /* TODO perform op even if !successful. */
     /* TODO Optimize, like add_range(). */
@@ -348,31 +357,31 @@
     for (unsigned int i = a; i < b + 1; i++)
       del (i);
   }
-  inline bool has (hb_codepoint_t g) const
+  bool has (hb_codepoint_t g) const
   {
-    const page_t *p = page_for (g);
-    if (!p)
+    const page_t *page = page_for (g);
+    if (!page)
       return false;
-    return p->has (g);
+    return page->has (g);
   }
-  inline bool intersects (hb_codepoint_t first,
+  bool intersects (hb_codepoint_t first,
 			  hb_codepoint_t last) const
   {
     hb_codepoint_t c = first - 1;
     return next (&c) && c <= last;
   }
-  inline void set (const hb_set_t *other)
+  void set (const hb_set_t *other)
   {
     if (unlikely (!successful)) return;
     unsigned int count = other->pages.len;
     if (!resize (count))
       return;
     population = other->population;
-    memcpy (pages.arrayZ(), other->pages.arrayZ(), count * sizeof (pages.arrayZ()[0]));
-    memcpy (page_map.arrayZ(), other->page_map.arrayZ(), count * sizeof (page_map.arrayZ()[0]));
+    memcpy ((void *) pages, (const void *) other->pages, count * pages.item_size);
+    memcpy ((void *) page_map, (const void *) other->page_map, count * page_map.item_size);
   }
 
-  inline bool is_equal (const hb_set_t *other) const
+  bool is_equal (const hb_set_t *other) const
   {
     if (get_population () != other->get_population ())
       return false;
@@ -399,7 +408,7 @@
     return true;
   }
 
-  inline bool is_subset (const hb_set_t *larger_set) const
+  bool is_subset (const hb_set_t *larger_set) const
   {
     if (get_population () > larger_set->get_population ())
       return false;
@@ -414,7 +423,7 @@
   }
 
   template <class Op>
-  inline void process (const hb_set_t *other)
+  void process (const hb_set_t *other)
   {
     if (unlikely (!successful)) return;
 
@@ -512,23 +521,23 @@
       resize (newCount);
   }
 
-  inline void union_ (const hb_set_t *other)
+  void union_ (const hb_set_t *other)
   {
     process<HbOpOr> (other);
   }
-  inline void intersect (const hb_set_t *other)
+  void intersect (const hb_set_t *other)
   {
     process<HbOpAnd> (other);
   }
-  inline void subtract (const hb_set_t *other)
+  void subtract (const hb_set_t *other)
   {
     process<HbOpMinus> (other);
   }
-  inline void symmetric_difference (const hb_set_t *other)
+  void symmetric_difference (const hb_set_t *other)
   {
     process<HbOpXor> (other);
   }
-  inline bool next (hb_codepoint_t *codepoint) const
+  bool next (hb_codepoint_t *codepoint) const
   {
     if (unlikely (*codepoint == INVALID)) {
       *codepoint = get_min ();
@@ -537,7 +546,7 @@
 
     page_map_t map = {get_major (*codepoint), 0};
     unsigned int i;
-    page_map.bfind (map, &i);
+    page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST);
     if (i < page_map.len && page_map[i].major == map.major)
     {
       if (pages[page_map[i].index].next (codepoint))
@@ -559,7 +568,7 @@
     *codepoint = INVALID;
     return false;
   }
-  inline bool previous (hb_codepoint_t *codepoint) const
+  bool previous (hb_codepoint_t *codepoint) const
   {
     if (unlikely (*codepoint == INVALID)) {
       *codepoint = get_max ();
@@ -568,7 +577,7 @@
 
     page_map_t map = {get_major (*codepoint), 0};
     unsigned int i;
-    page_map.bfind (map, &i);
+    page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST);
     if (i < page_map.len && page_map[i].major == map.major)
     {
       if (pages[page_map[i].index].previous (codepoint))
@@ -590,7 +599,7 @@
     *codepoint = INVALID;
     return false;
   }
-  inline bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const
+  bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const
   {
     hb_codepoint_t i;
 
@@ -608,7 +617,7 @@
 
     return true;
   }
-  inline bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const
+  bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const
   {
     hb_codepoint_t i;
 
@@ -627,7 +636,7 @@
     return true;
   }
 
-  inline unsigned int get_population (void) const
+  unsigned int get_population () const
   {
     if (population != (unsigned int) -1)
       return population;
@@ -640,7 +649,7 @@
     population = pop;
     return pop;
   }
-  inline hb_codepoint_t get_min (void) const
+  hb_codepoint_t get_min () const
   {
     unsigned int count = pages.len;
     for (unsigned int i = 0; i < count; i++)
@@ -648,33 +657,35 @@
         return page_map[i].major * page_t::PAGE_BITS + page_at (i).get_min ();
     return INVALID;
   }
-  inline hb_codepoint_t get_max (void) const
+  hb_codepoint_t get_max () const
   {
     unsigned int count = pages.len;
     for (int i = count - 1; i >= 0; i++)
       if (!page_at (i).is_empty ())
-        return page_map[i].major * page_t::PAGE_BITS + page_at (i).get_max ();
+        return page_map[(unsigned) i].major * page_t::PAGE_BITS + page_at (i).get_max ();
     return INVALID;
   }
 
   static  const hb_codepoint_t INVALID = HB_SET_VALUE_INVALID;
 
-  inline page_t *page_for_insert (hb_codepoint_t g)
+  page_t *page_for_insert (hb_codepoint_t g)
   {
     page_map_t map = {get_major (g), pages.len};
     unsigned int i;
-    if (!page_map.bfind (map, &i))
+    if (!page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST))
     {
       if (!resize (pages.len + 1))
 	return nullptr;
 
       pages[map.index].init0 ();
-      memmove (&page_map[i + 1], &page_map[i], (page_map.len - 1 - i) * sizeof (page_map[0]));
+      memmove (page_map + i + 1,
+	       page_map + i,
+	       (page_map.len - 1 - i) * page_map.item_size);
       page_map[i] = map;
     }
     return &pages[page_map[i].index];
   }
-  inline page_t *page_for (hb_codepoint_t g)
+  page_t *page_for (hb_codepoint_t g)
   {
     page_map_t key = {get_major (g)};
     const page_map_t *found = page_map.bsearch (key);
@@ -682,7 +693,7 @@
       return &pages[found->index];
     return nullptr;
   }
-  inline const page_t *page_for (hb_codepoint_t g) const
+  const page_t *page_for (hb_codepoint_t g) const
   {
     page_map_t key = {get_major (g)};
     const page_map_t *found = page_map.bsearch (key);
@@ -690,10 +701,10 @@
       return &pages[found->index];
     return nullptr;
   }
-  inline page_t &page_at (unsigned int i) { return pages[page_map[i].index]; }
-  inline const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; }
-  inline unsigned int get_major (hb_codepoint_t g) const { return g / page_t::PAGE_BITS; }
-  inline hb_codepoint_t major_start (unsigned int major) const { return major * page_t::PAGE_BITS; }
+  page_t &page_at (unsigned int i) { return pages[page_map[i].index]; }
+  const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; }
+  unsigned int get_major (hb_codepoint_t g) const { return g / page_t::PAGE_BITS; }
+  hb_codepoint_t major_start (unsigned int major) const { return major * page_t::PAGE_BITS; }
 };
 
 
diff --git a/src/hb-shape-plan.cc b/src/hb-shape-plan.cc
index b2289f8..61ea8d0 100644
--- a/src/hb-shape-plan.cc
+++ b/src/hb-shape-plan.cc
@@ -31,47 +31,72 @@
 #include "hb-buffer.hh"
 
 
-static void
-hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
-		    const hb_feature_t *user_features,
-		    unsigned int        num_user_features,
-		    const int          *coords,
-		    unsigned int        num_coords,
-		    const char * const *shaper_list)
-{
-  DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
-		  "num_features=%d num_coords=%d shaper_list=%p",
-		  num_user_features,
-		  num_coords,
-		  shaper_list);
+/**
+ * SECTION:hb-shape-plan
+ * @title: hb-shape-plan
+ * @short_description: Object representing a shaping plan
+ * @include: hb.h
+ *
+ * Shape plans are not used for shaping directly, but can be access to query
+ * certain information about how shaping will perform given a set of input
+ * parameters (script, language, direction, features, etc.)
+ * Most client would not need to deal with shape plans directly.
+ **/
 
-  const hb_shaper_pair_t *shapers = _hb_shapers_get ();
+
+/*
+ * hb_shape_plan_key_t
+ */
+
+bool
+hb_shape_plan_key_t::init (bool                           copy,
+			   hb_face_t                     *face,
+			   const hb_segment_properties_t *props,
+			   const hb_feature_t            *user_features,
+			   unsigned int                   num_user_features,
+			   const int                     *coords,
+			   unsigned int                   num_coords,
+			   const char * const            *shaper_list)
+{
+  hb_feature_t *features = nullptr;
+  if (copy && num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t))))
+    goto bail;
+
+  this->props = *props;
+  this->num_user_features = num_user_features;
+  this->user_features = copy ? features : user_features;
+  if (copy && num_user_features)
+  {
+    memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
+    /* Make start/end uniform to easier catch bugs. */
+    for (unsigned int i = 0; i < num_user_features; i++)
+    {
+      if (features[0].start != HB_FEATURE_GLOBAL_START)
+	features[0].start = 1;
+      if (features[0].end   != HB_FEATURE_GLOBAL_END)
+	features[0].end   = 2;
+    }
+  }
+  this->shaper_func = nullptr;
+  this->shaper_name = nullptr;
+  this->ot.init (face, coords, num_coords);
+
+  /*
+   * Choose shaper.
+   */
 
 #define HB_SHAPER_PLAN(shaper) \
 	HB_STMT_START { \
-	  if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) \
+	  if (face->data.shaper) \
 	  { \
-	    /* XXX-MT-bug What happened to *ensure*ing this?!!!! */ \
-	    HB_SHAPER_DATA (shaper, shape_plan).set_relaxed ( \
-	      HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, \
-							       user_features, num_user_features, \
-							       coords, num_coords)); \
-	    shape_plan->shaper_func = _hb_##shaper##_shape; \
-	    shape_plan->shaper_name = #shaper; \
-	    return; \
+	    this->shaper_func = _hb_##shaper##_shape; \
+	    this->shaper_name = #shaper; \
+	    return true; \
 	  } \
 	} HB_STMT_END
 
-  if (likely (!shaper_list)) {
-    for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
-      if (false)
-	;
-#define HB_SHAPER_IMPLEMENT(shaper) \
-      else if (shapers[i].func == _hb_##shaper##_shape) \
-	HB_SHAPER_PLAN (shaper);
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-  } else {
+  if (unlikely (shaper_list))
+  {
     for (; *shaper_list; shaper_list++)
       if (false)
 	;
@@ -81,8 +106,50 @@
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
   }
-
+  else
+  {
+    const hb_shaper_entry_t *shapers = _hb_shapers_get ();
+    for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
+      if (false)
+	;
+#define HB_SHAPER_IMPLEMENT(shaper) \
+      else if (shapers[i].func == _hb_##shaper##_shape) \
+	HB_SHAPER_PLAN (shaper);
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+  }
 #undef HB_SHAPER_PLAN
+
+bail:
+  ::free (features);
+  return false;
+}
+
+bool
+hb_shape_plan_key_t::user_features_match (const hb_shape_plan_key_t *other)
+{
+  if (this->num_user_features != other->num_user_features)
+    return false;
+  for (unsigned int i = 0; i < num_user_features; i++)
+  {
+    if (this->user_features[i].tag   != other->user_features[i].tag   ||
+	this->user_features[i].value != other->user_features[i].value ||
+	(this->user_features[i].start == HB_FEATURE_GLOBAL_START &&
+	 this->user_features[i].end   == HB_FEATURE_GLOBAL_END) !=
+	(other->user_features[i].start == HB_FEATURE_GLOBAL_START &&
+	 other->user_features[i].end   == HB_FEATURE_GLOBAL_END))
+      return false;
+  }
+  return true;
+}
+
+bool
+hb_shape_plan_key_t::equal (const hb_shape_plan_key_t *other)
+{
+  return hb_segment_properties_equal (&this->props, &other->props) &&
+	 this->user_features_match (other) &&
+	 this->ot.equal (&other->ot) &&
+	 this->shaper_func == other->shaper_func;
 }
 
 
@@ -90,30 +157,6 @@
  * hb_shape_plan_t
  */
 
-DEFINE_NULL_INSTANCE (hb_shape_plan_t) =
-{
-  HB_OBJECT_HEADER_STATIC,
-
-  true, /* default_shaper_list */
-  nullptr, /* face */
-  HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
-
-  nullptr, /* shaper_func */
-  nullptr, /* shaper_name */
-
-  nullptr, /* user_features */
-  0,    /* num_user_featurs */
-
-  nullptr, /* coords */
-  0,    /* num_coords */
-
-  {
-#define HB_SHAPER_IMPLEMENT(shaper) HB_ATOMIC_PTR_INIT (HB_SHAPER_DATA_INVALID),
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-  },
-};
-
 
 /**
  * hb_shape_plan_create: (Xconstructor)
@@ -147,7 +190,7 @@
 		       const hb_segment_properties_t *props,
 		       const hb_feature_t            *user_features,
 		       unsigned int                   num_user_features,
-		       const int                     *orig_coords,
+		       const int                     *coords,
 		       unsigned int                   num_coords,
 		       const char * const            *shaper_list)
 {
@@ -158,49 +201,40 @@
 		  num_coords,
 		  shaper_list);
 
+  assert (props->direction != HB_DIRECTION_INVALID);
+
   hb_shape_plan_t *shape_plan;
-  hb_feature_t *features = nullptr;
-  int *coords = nullptr;
+
+  if (unlikely (!props))
+    goto bail;
+  if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
+    goto bail;
 
   if (unlikely (!face))
     face = hb_face_get_empty ();
-  if (unlikely (!props))
-    return hb_shape_plan_get_empty ();
-  if (num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t))))
-    return hb_shape_plan_get_empty ();
-  if (num_coords && !(coords = (int *) calloc (num_coords, sizeof (int))))
-  {
-    free (features);
-    return hb_shape_plan_get_empty ();
-  }
-  if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
-  {
-    free (coords);
-    free (features);
-    return hb_shape_plan_get_empty ();
-  }
-
-  assert (props->direction != HB_DIRECTION_INVALID);
-
   hb_face_make_immutable (face);
-  shape_plan->default_shaper_list = !shaper_list;
   shape_plan->face_unsafe = face;
-  shape_plan->props = *props;
-  shape_plan->num_user_features = num_user_features;
-  shape_plan->user_features = features;
-  if (num_user_features)
-    memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
-  shape_plan->num_coords = num_coords;
-  shape_plan->coords = coords;
-  if (num_coords)
-    memcpy (coords, orig_coords, num_coords * sizeof (int));
 
-  hb_shape_plan_plan (shape_plan,
-		      user_features, num_user_features,
-		      coords, num_coords,
-		      shaper_list);
+  if (unlikely (!shape_plan->key.init (true,
+				       face,
+				       props,
+				       user_features,
+				       num_user_features,
+				       coords,
+				       num_coords,
+				       shaper_list)))
+    goto bail2;
+  if (unlikely (!shape_plan->ot.init0 (face, &shape_plan->key)))
+    goto bail3;
 
   return shape_plan;
+
+bail3:
+  shape_plan->key.free ();
+bail2:
+  free (shape_plan);
+bail:
+  return hb_shape_plan_get_empty ();
 }
 
 /**
@@ -213,7 +247,7 @@
  * Since: 0.9.7
  **/
 hb_shape_plan_t *
-hb_shape_plan_get_empty (void)
+hb_shape_plan_get_empty ()
 {
   return const_cast<hb_shape_plan_t *> (&Null(hb_shape_plan_t));
 }
@@ -247,13 +281,8 @@
 {
   if (!hb_object_destroy (shape_plan)) return;
 
-#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-
-  free (shape_plan->user_features);
-  free (shape_plan->coords);
-
+  shape_plan->ot.fini ();
+  shape_plan->key.free ();
   free (shape_plan);
 }
 
@@ -299,6 +328,22 @@
   return hb_object_get_user_data (shape_plan, key);
 }
 
+/**
+ * hb_shape_plan_get_shaper:
+ * @shape_plan: a shape plan.
+ *
+ * 
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 0.9.7
+ **/
+const char *
+hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
+{
+  return shape_plan->key.shaper_name;
+}
+
 
 /**
  * hb_shape_plan_execute:
@@ -324,32 +369,31 @@
   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
 		  "num_features=%d shaper_func=%p, shaper_name=%s",
 		  num_features,
-		  shape_plan->shaper_func,
-		  shape_plan->shaper_name);
+		  shape_plan->key.shaper_func,
+		  shape_plan->key.shaper_name);
 
   if (unlikely (!buffer->len))
     return true;
 
-  assert (!hb_object_is_inert (buffer));
+  assert (!hb_object_is_immutable (buffer));
   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
 
   if (unlikely (hb_object_is_inert (shape_plan)))
     return false;
 
   assert (shape_plan->face_unsafe == font->face);
-  assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props));
+  assert (hb_segment_properties_equal (&shape_plan->key.props, &buffer->props));
 
 #define HB_SHAPER_EXECUTE(shaper) \
 	HB_STMT_START { \
-	  return HB_SHAPER_DATA (shaper, shape_plan).get () && \
-		 hb_##shaper##_shaper_font_data_ensure (font) && \
+	  return font->data.shaper && \
 		 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
 	} HB_STMT_END
 
   if (false)
     ;
 #define HB_SHAPER_IMPLEMENT(shaper) \
-  else if (shape_plan->shaper_func == _hb_##shaper##_shape) \
+  else if (shape_plan->key.shaper_func == _hb_##shaper##_shape) \
     HB_SHAPER_EXECUTE (shaper);
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
@@ -361,92 +405,9 @@
 
 
 /*
- * caching
+ * Caching
  */
 
-#if 0
-static unsigned int
-hb_shape_plan_hash (const hb_shape_plan_t *shape_plan)
-{
-  return hb_segment_properties_hash (&shape_plan->props) +
-	 shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func;
-}
-#endif
-
-/* User-feature caching is currently somewhat dumb:
- * it only finds matches where the feature array is identical,
- * not cases where the feature lists would be compatible for plan purposes
- * but have different ranges, for example.
- */
-struct hb_shape_plan_proposal_t
-{
-  const hb_segment_properties_t  props;
-  const char * const            *shaper_list;
-  const hb_feature_t            *user_features;
-  unsigned int                   num_user_features;
-  const int                     *coords;
-  unsigned int                   num_coords;
-  hb_shape_func_t               *shaper_func;
-};
-
-static inline hb_bool_t
-hb_shape_plan_user_features_match (const hb_shape_plan_t          *shape_plan,
-				   const hb_shape_plan_proposal_t *proposal)
-{
-  if (proposal->num_user_features != shape_plan->num_user_features)
-    return false;
-  for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++)
-    if (proposal->user_features[i].tag   != shape_plan->user_features[i].tag   ||
-        proposal->user_features[i].value != shape_plan->user_features[i].value ||
-        proposal->user_features[i].start != shape_plan->user_features[i].start ||
-        proposal->user_features[i].end   != shape_plan->user_features[i].end)
-      return false;
-  return true;
-}
-
-static inline hb_bool_t
-hb_shape_plan_coords_match (const hb_shape_plan_t          *shape_plan,
-			    const hb_shape_plan_proposal_t *proposal)
-{
-  if (proposal->num_coords != shape_plan->num_coords)
-    return false;
-  for (unsigned int i = 0, n = proposal->num_coords; i < n; i++)
-    if (proposal->coords[i] != shape_plan->coords[i])
-      return false;
-  return true;
-}
-
-static hb_bool_t
-hb_shape_plan_matches (const hb_shape_plan_t          *shape_plan,
-		       const hb_shape_plan_proposal_t *proposal)
-{
-  return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
-	 hb_shape_plan_user_features_match (shape_plan, proposal) &&
-	 hb_shape_plan_coords_match (shape_plan, proposal) &&
-	 ((shape_plan->default_shaper_list && !proposal->shaper_list) ||
-	  (shape_plan->shaper_func == proposal->shaper_func));
-}
-
-static inline hb_bool_t
-hb_non_global_user_features_present (const hb_feature_t *user_features,
-				     unsigned int        num_user_features)
-{
-  while (num_user_features) {
-    if (user_features->start != 0 || user_features->end != (unsigned int) -1)
-      return true;
-    num_user_features--;
-    user_features++;
-  }
-  return false;
-}
-
-static inline hb_bool_t
-hb_coords_present (const int *coords,
-		   unsigned int num_coords)
-{
-  return num_coords != 0;
-}
-
 /**
  * hb_shape_plan_create_cached:
  * @face: 
@@ -489,62 +450,38 @@
 		  num_user_features,
 		  shaper_list);
 
-  hb_shape_plan_proposal_t proposal = {
-    *props,
-    shaper_list,
-    user_features,
-    num_user_features,
-    nullptr
-  };
-
-  if (shaper_list) {
-    /* Choose shaper.  Adapted from hb_shape_plan_plan().
-     * Must choose shaper exactly the same way as that function. */
-    for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++)
-      if (false)
-	;
-#define HB_SHAPER_IMPLEMENT(shaper) \
-      else if (0 == strcmp (*shaper_item, #shaper) && \
-	       hb_##shaper##_shaper_face_data_ensure (face)) \
-      { \
-	proposal.shaper_func = _hb_##shaper##_shape; \
-	break; \
-      }
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-
-    if (unlikely (!proposal.shaper_func))
-      return hb_shape_plan_get_empty ();
-  }
-
-
 retry:
-  hb_face_t::plan_node_t *cached_plan_nodes = face->shape_plans.get ();
+  hb_face_t::plan_node_t *cached_plan_nodes = face->shape_plans;
 
-  /* Don't look for plan in the cache if there were variation coordinates XXX Fix me. */
-  if (!hb_coords_present (coords, num_coords))
+  bool dont_cache = hb_object_is_inert (face);
+
+  if (likely (!dont_cache))
+  {
+    hb_shape_plan_key_t key;
+    if (!key.init (false,
+		   face,
+		   props,
+		   user_features,
+		   num_user_features,
+		   coords,
+		   num_coords,
+		   shaper_list))
+      return hb_shape_plan_get_empty ();
+
     for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
-      if (hb_shape_plan_matches (node->shape_plan, &proposal))
+      if (node->shape_plan->key.equal (&key))
       {
         DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
         return hb_shape_plan_reference (node->shape_plan);
       }
+  }
 
-  /* Not found. */
   hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props,
 						       user_features, num_user_features,
 						       coords, num_coords,
 						       shaper_list);
 
-  /* Don't add to the cache if face is inert. */
-  if (unlikely (hb_object_is_inert (face)))
-    return shape_plan;
-
-  /* Don't add the plan to the cache if there were user features with non-global ranges */
-  if (hb_non_global_user_features_present (user_features, num_user_features))
-    return shape_plan;
-  /* Don't add the plan to the cache if there were variation coordinates XXX Fix me. */
-  if (hb_coords_present (coords, num_coords))
+  if (unlikely (dont_cache))
     return shape_plan;
 
   hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
@@ -564,19 +501,3 @@
 
   return hb_shape_plan_reference (shape_plan);
 }
-
-/**
- * hb_shape_plan_get_shaper:
- * @shape_plan: a shape plan.
- *
- * 
- *
- * Return value: (transfer none):
- *
- * Since: 0.9.7
- **/
-const char *
-hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
-{
-  return shape_plan->shaper_name;
-}
diff --git a/src/hb-shape-plan.hh b/src/hb-shape-plan.hh
index bf82b91..3a057fd 100644
--- a/src/hb-shape-plan.hh
+++ b/src/hb-shape-plan.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2012  Google, Inc.
+ * Copyright © 2012,2018  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -29,39 +29,44 @@
 
 #include "hb.hh"
 #include "hb-shaper.hh"
+#include "hb-ot-shape.hh"
 
 
+struct hb_shape_plan_key_t
+{
+  hb_segment_properties_t  props;
+
+  const hb_feature_t      *user_features;
+  unsigned int             num_user_features;
+
+  hb_ot_shape_plan_key_t   ot;
+
+  hb_shape_func_t         *shaper_func;
+  const char              *shaper_name;
+
+  HB_INTERNAL inline bool init (bool                           copy,
+				hb_face_t                     *face,
+				const hb_segment_properties_t *props,
+				const hb_feature_t            *user_features,
+				unsigned int                   num_user_features,
+				const int                     *coords,
+				unsigned int                   num_coords,
+				const char * const            *shaper_list);
+
+  HB_INTERNAL inline void free () { ::free ((void *) user_features); }
+
+  HB_INTERNAL bool user_features_match (const hb_shape_plan_key_t *other);
+
+  HB_INTERNAL bool equal (const hb_shape_plan_key_t *other);
+};
+
 struct hb_shape_plan_t
 {
   hb_object_header_t header;
-  ASSERT_POD ();
-
-  hb_bool_t default_shaper_list;
   hb_face_t *face_unsafe; /* We don't carry a reference to face. */
-  hb_segment_properties_t props;
-
-  hb_shape_func_t *shaper_func;
-  const char *shaper_name;
-
-  hb_feature_t *user_features;
-  unsigned int num_user_features;
-
-  int *coords;
-  unsigned int num_coords;
-
-  struct hb_shaper_data_t shaper_data;
+  hb_shape_plan_key_t key;
+  hb_ot_shape_plan_t ot;
 };
-DECLARE_NULL_INSTANCE (hb_shape_plan_t);
-
-#define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS \
-	, const hb_feature_t *user_features \
-	, unsigned int        num_user_features \
-	, const int          *coords \
-	, unsigned int        num_coords
-#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, shape_plan);
-#include "hb-shaper-list.hh"
-#undef HB_SHAPER_IMPLEMENT
-#undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 
 
 #endif /* HB_SHAPE_PLAN_HH */
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index e8eeff5..deff77b 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -34,20 +34,22 @@
 #include "hb-font.hh"
 #include "hb-machinery.hh"
 
+
 /**
  * SECTION:hb-shape
- * @title: Shaping
+ * @title: hb-shape
  * @short_description: Conversion of text strings into positioned glyphs
  * @include: hb.h
  *
  * Shaping is the central operation of HarfBuzz. Shaping operates on buffers,
  * which are sequences of Unicode characters that use the same font and have
- * the same text direction, script and language. After shaping the buffer
+ * the same text direction, script, and language. After shaping the buffer
  * contains the output glyphs and their positions.
  **/
 
-#ifdef HB_USE_ATEXIT
-static void free_static_shaper_list (void);
+
+#if HB_USE_ATEXIT
+static void free_static_shaper_list ();
 #endif
 
 static const char *nil_shaper_list[] = {nullptr};
@@ -55,37 +57,33 @@
 static struct hb_shaper_list_lazy_loader_t : hb_lazy_loader_t<const char *,
 							      hb_shaper_list_lazy_loader_t>
 {
-  static inline const char ** create (void)
+  static const char ** create ()
   {
     const char **shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *));
     if (unlikely (!shaper_list))
       return nullptr;
 
-    const hb_shaper_pair_t *shapers = _hb_shapers_get ();
+    const hb_shaper_entry_t *shapers = _hb_shapers_get ();
     unsigned int i;
     for (i = 0; i < HB_SHAPERS_COUNT; i++)
       shaper_list[i] = shapers[i].name;
     shaper_list[i] = nullptr;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
     atexit (free_static_shaper_list);
 #endif
 
     return shaper_list;
   }
-  static inline void destroy (const char **l)
-  {
-    free (l);
-  }
-  static inline const char ** get_null (void)
-  {
-    return nil_shaper_list;
-  }
+  static void destroy (const char **l)
+  { free (l); }
+  static const char ** get_null ()
+  { return nil_shaper_list; }
 } static_shaper_list;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
 static
-void free_static_shaper_list (void)
+void free_static_shaper_list ()
 {
   static_shaper_list.free_instance ();
 }
@@ -103,7 +101,7 @@
  * Since: 0.9.2
  **/
 const char **
-hb_shape_list_shapers (void)
+hb_shape_list_shapers ()
 {
   return static_shaper_list.get_unconst ();
 }
diff --git a/src/hb-shaper-impl.hh b/src/hb-shaper-impl.hh
index d40cb08..b674fce 100644
--- a/src/hb-shaper-impl.hh
+++ b/src/hb-shaper-impl.hh
@@ -30,14 +30,9 @@
 #include "hb.hh"
 
 #include "hb-shaper.hh"
-#include "hb-shape-plan.hh"
+#include "hb-face.hh"
 #include "hb-font.hh"
+#include "hb-shape-plan.hh"
 #include "hb-buffer.hh"
 
-
-#ifdef HB_SHAPER
-#define HB_SHAPER_DATA_GET(object) HB_SHAPER_DATA (HB_SHAPER, object).get ()
-#endif
-
-
 #endif /* HB_SHAPER_IMPL_HH */
diff --git a/src/hb-shaper-list.hh b/src/hb-shaper-list.hh
index 1fdb648..36d8fc7 100644
--- a/src/hb-shaper-list.hh
+++ b/src/hb-shaper-list.hh
@@ -34,10 +34,6 @@
 /* Only picks up fonts that have a "Silf" table. */
 HB_SHAPER_IMPLEMENT (graphite2)
 #endif
-#ifdef HAVE_CORETEXT
-/* Only picks up fonts that have a "mort" or "morx" table. */
-HB_SHAPER_IMPLEMENT (coretext_aat)
-#endif
 
 HB_SHAPER_IMPLEMENT (ot) /* <--- This is our main OpenType shaper. */
 
@@ -49,6 +45,10 @@
 #endif
 #ifdef HAVE_CORETEXT
 HB_SHAPER_IMPLEMENT (coretext)
+
+/* Only picks up fonts that have a "mort" or "morx" table.
+   Probably going to be removed https://github.com/harfbuzz/harfbuzz/issues/1478 */
+HB_SHAPER_IMPLEMENT (coretext_aat)
 #endif
 
 #ifdef HAVE_FALLBACK
diff --git a/src/hb-shaper.cc b/src/hb-shaper.cc
index 52418c0..575ab1f 100644
--- a/src/hb-shaper.cc
+++ b/src/hb-shaper.cc
@@ -29,26 +29,26 @@
 #include "hb-machinery.hh"
 
 
-static const hb_shaper_pair_t all_shapers[] = {
+static const hb_shaper_entry_t all_shapers[] = {
 #define HB_SHAPER_IMPLEMENT(name) {#name, _hb_##name##_shape},
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
 };
 
-#ifdef HB_USE_ATEXIT
-static void free_static_shapers (void);
+#if HB_USE_ATEXIT
+static void free_static_shapers ();
 #endif
 
-static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<const hb_shaper_pair_t,
+static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<const hb_shaper_entry_t,
 							  hb_shapers_lazy_loader_t>
 {
-  static inline hb_shaper_pair_t *create (void)
+  static hb_shaper_entry_t *create ()
   {
     char *env = getenv ("HB_SHAPER_LIST");
     if (!env || !*env)
       return nullptr;
 
-    hb_shaper_pair_t *shapers = (hb_shaper_pair_t *) calloc (1, sizeof (all_shapers));
+    hb_shaper_entry_t *shapers = (hb_shaper_entry_t *) calloc (1, sizeof (all_shapers));
     if (unlikely (!shapers))
       return nullptr;
 
@@ -68,7 +68,7 @@
 	    0 == strncmp (shapers[j].name, p, end - p))
 	{
 	  /* Reorder this shaper to position i */
-	 struct hb_shaper_pair_t t = shapers[j];
+	 struct hb_shaper_entry_t t = shapers[j];
 	 memmove (&shapers[i + 1], &shapers[i], sizeof (shapers[i]) * (j - i));
 	 shapers[i] = t;
 	 i++;
@@ -80,32 +80,26 @@
 	p = end + 1;
     }
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
     atexit (free_static_shapers);
 #endif
 
     return shapers;
   }
-  static inline void destroy (const hb_shaper_pair_t *p)
-  {
-    free ((void *) p);
-  }
-  static inline const hb_shaper_pair_t *get_null (void)
-  {
-    return all_shapers;
-  }
+  static void destroy (const hb_shaper_entry_t *p) { free ((void *) p); }
+  static const hb_shaper_entry_t *get_null ()      { return all_shapers; }
 } static_shapers;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
 static
-void free_static_shapers (void)
+void free_static_shapers ()
 {
   static_shapers.free_instance ();
 }
 #endif
 
-const hb_shaper_pair_t *
-_hb_shapers_get (void)
+const hb_shaper_entry_t *
+_hb_shapers_get ()
 {
   return static_shapers.get_unconst ();
 }
diff --git a/src/hb-shaper.hh b/src/hb-shaper.hh
index 361165e..79dc5d0 100644
--- a/src/hb-shaper.hh
+++ b/src/hb-shaper.hh
@@ -28,6 +28,7 @@
 #define HB_SHAPER_HH
 
 #include "hb.hh"
+#include "hb-machinery.hh"
 
 typedef hb_bool_t hb_shape_func_t (hb_shape_plan_t    *shape_plan,
 				   hb_font_t          *font,
@@ -40,95 +41,94 @@
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
 
-struct hb_shaper_pair_t {
+struct hb_shaper_entry_t {
   char name[16];
   hb_shape_func_t *func;
 };
 
-HB_INTERNAL const hb_shaper_pair_t *
-_hb_shapers_get (void);
+HB_INTERNAL const hb_shaper_entry_t *
+_hb_shapers_get ();
 
 
-/* Means: succeeded, but don't need to keep any data. */
+template <typename Data, unsigned int WheresData, typename T>
+struct hb_shaper_lazy_loader_t;
+
+#define HB_SHAPER_ORDER(Shaper) \
+  HB_PASTE (HB_SHAPER_ORDER_, Shaper)
+enum hb_shaper_order_t
+{
+  _HB_SHAPER_ORDER_ORDER_ZERO,
+#define HB_SHAPER_IMPLEMENT(Shaper) \
+      HB_SHAPER_ORDER (Shaper),
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+  _HB_SHAPERS_COUNT_PLUS_ONE,
+  HB_SHAPERS_COUNT = _HB_SHAPERS_COUNT_PLUS_ONE - 1,
+};
+
+template <enum hb_shaper_order_t order, typename Object> struct hb_shaper_object_data_type_t;
+
 #define HB_SHAPER_DATA_SUCCEEDED ((void *) +1)
-/* Means: tried but failed to create. */
-#define HB_SHAPER_DATA_INVALID ((void *) -1)
-
-#define HB_SHAPER_DATA_TYPE_NAME(shaper, object)	hb_##shaper##_##object##_data_t
-#define HB_SHAPER_DATA_TYPE(shaper, object)		struct HB_SHAPER_DATA_TYPE_NAME(shaper, object)
-#define HB_SHAPER_DATA_INSTANCE(shaper, object, instance)	(* reinterpret_cast<hb_atomic_ptr_t<HB_SHAPER_DATA_TYPE(shaper, object) *> *> (&(instance)->shaper_data.shaper))
-#define HB_SHAPER_DATA(shaper, object)			HB_SHAPER_DATA_INSTANCE(shaper, object, object)
+#define HB_SHAPER_DATA_TYPE(shaper, object)		hb_##shaper##_##object##_data_t
 #define HB_SHAPER_DATA_CREATE_FUNC(shaper, object)	_hb_##shaper##_shaper_##object##_data_create
 #define HB_SHAPER_DATA_DESTROY_FUNC(shaper, object)	_hb_##shaper##_shaper_##object##_data_destroy
-#define HB_SHAPER_DATA_ENSURE_FUNC(shaper, object)	hb_##shaper##_shaper_##object##_data_ensure
 
-#define HB_SHAPER_DATA_PROTOTYPE(shaper, object) \
-	HB_SHAPER_DATA_TYPE (shaper, object); /* Type forward declaration. */ \
+#define HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, object) \
+	\
+	struct HB_SHAPER_DATA_TYPE (shaper, object); /* Type forward declaration. */ \
 	extern "C" HB_INTERNAL HB_SHAPER_DATA_TYPE (shaper, object) * \
-	HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (hb_##object##_t *object HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS); \
+	HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (hb_##object##_t *object); \
 	extern "C" HB_INTERNAL void \
-	HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (HB_SHAPER_DATA_TYPE (shaper, object) *data); \
-	extern "C" HB_INTERNAL bool \
-	HB_SHAPER_DATA_ENSURE_FUNC (shaper, object) (hb_##object##_t *object)
-
-#define HB_SHAPER_DATA_DESTROY(shaper, object) \
-    if (HB_SHAPER_DATA_TYPE (shaper, object) *data = HB_SHAPER_DATA (shaper, object).get ()) \
-      if (data != HB_SHAPER_DATA_INVALID && data != HB_SHAPER_DATA_SUCCEEDED) \
-        HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data);
-
-#define HB_SHAPER_DATA_ENSURE_DEFINE(shaper, object) \
-	HB_SHAPER_DATA_ENSURE_DEFINE_WITH_CONDITION(shaper, object, true)
-
-#define HB_SHAPER_DATA_ENSURE_DEFINE_WITH_CONDITION(shaper, object, condition) \
-bool \
-HB_SHAPER_DATA_ENSURE_FUNC(shaper, object) (hb_##object##_t *object) \
-{\
-  retry: \
-  HB_SHAPER_DATA_TYPE (shaper, object) *data = HB_SHAPER_DATA (shaper, object).get (); \
-  if (likely (data) && !(condition)) { \
-    /* XXX-MT-bug \
-     * Note that evaluating condition above can be dangerous if another thread \
-     * got here first and destructed data.  That's, as always, bad use pattern. \
-     * If you modify the font (change font size), other threads must not be \
-     * using it at the same time.  However, since this check is delayed to \
-     * when one actually tries to shape something, this is a XXX race condition \
-     * (and the only know we have that I know of) right now.  Ie. you modify the \
-     * font size in one thread, then (supposedly safely) try to use it from two \
-     * or more threads and BOOM!  I'm not sure how to fix this.  We want RCU. \
-     * Maybe when it doesn't matter when we finally implement AAT shaping, as
-     * this (condition) is currently only used by hb-coretext. */ \
-    /* Drop and recreate. */ \
-    /* If someone dropped it in the mean time, throw it away and don't touch it. \
-     * Otherwise, destruct it. */ \
-    if (likely (HB_SHAPER_DATA (shaper, object).cmpexch (data, nullptr))) \
-    { \
-      HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data); \
-    } \
-    goto retry; \
-  } \
-  if (unlikely (!data)) { \
-    data = HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (object); \
-    if (unlikely (!data)) \
-      data = (HB_SHAPER_DATA_TYPE (shaper, object) *) HB_SHAPER_DATA_INVALID; \
-    if (unlikely (!HB_SHAPER_DATA (shaper, object).cmpexch (nullptr, data))) { \
-      if (data && \
-	  data != HB_SHAPER_DATA_INVALID && \
-	  data != HB_SHAPER_DATA_SUCCEEDED) \
-	HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data); \
-      goto retry; \
-    } \
-  } \
-  return data != nullptr && (void *) data != HB_SHAPER_DATA_INVALID; \
-}
+	HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (HB_SHAPER_DATA_TYPE (shaper, object) *shaper##_##object); \
+	\
+	template <> \
+	struct hb_shaper_object_data_type_t<HB_SHAPER_ORDER (shaper), hb_##object##_t> \
+	{ \
+	  typedef HB_SHAPER_DATA_TYPE(shaper, object) value; \
+	}; \
+	\
+	template <unsigned int WheresData> \
+	struct hb_shaper_lazy_loader_t<hb_##object##_t, WheresData, HB_SHAPER_DATA_TYPE(shaper, object)> \
+		: hb_lazy_loader_t<HB_SHAPER_DATA_TYPE(shaper, object), \
+				   hb_shaper_lazy_loader_t<hb_##object##_t, \
+							   WheresData, \
+							   HB_SHAPER_DATA_TYPE(shaper, object)>, \
+				   hb_##object##_t, WheresData> \
+	{ \
+	  typedef HB_SHAPER_DATA_TYPE(shaper, object) Type; \
+	  static Type* create (hb_##object##_t *data) \
+	  { return HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (data); } \
+	  static Type *get_null () { return nullptr; } \
+	  static void destroy (Type *p) { HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (p); } \
+	}; \
+	\
+	static_assert (true, "") /* Require semicolon. */
 
 
-/* For embedding in face / font / ... */
-struct hb_shaper_data_t {
-#define HB_SHAPER_IMPLEMENT(shaper) hb_atomic_ptr_t<void *> shaper;
+template <typename Object>
+struct hb_shaper_object_dataset_t
+{
+  void init0 (Object *parent_data)
+  {
+    this->parent_data = parent_data;
+#define HB_SHAPER_IMPLEMENT(shaper) shaper.init0 ();
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+  }
+  void fini ()
+  {
+#define HB_SHAPER_IMPLEMENT(shaper) shaper.fini ();
+#include "hb-shaper-list.hh"
+#undef HB_SHAPER_IMPLEMENT
+  }
+
+  Object *parent_data; /* MUST be JUST before the lazy loaders. */
+#define HB_SHAPER_IMPLEMENT(shaper) \
+	hb_shaper_lazy_loader_t<Object, HB_SHAPER_ORDER(shaper), \
+				typename hb_shaper_object_data_type_t<HB_SHAPER_ORDER(shaper), Object>::value \
+			       > shaper;
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
 };
-#define HB_SHAPERS_COUNT (sizeof (hb_shaper_data_t) / sizeof (void *))
-
 
 #endif /* HB_SHAPER_HH */
diff --git a/src/hb-static.cc b/src/hb-static.cc
index e550796..4c51588 100644
--- a/src/hb-static.cc
+++ b/src/hb-static.cc
@@ -27,11 +27,12 @@
 #include "hb.hh"
 
 #include "hb-open-type.hh"
-#include "hb-ot-layout-common.hh"
-#include "hb-aat-layout-ankr-table.hh" /* I don't even want to know why... */
-#include "hb-aat-layout-common.hh"
-
 #include "hb-face.hh"
+
+#include "hb-aat-layout-common.hh"
+#include "hb-aat-layout-feat-table.hh"
+#include "hb-ot-layout-common.hh"
+#include "hb-ot-cmap-table.hh"
 #include "hb-ot-head-table.hh"
 #include "hb-ot-maxp-table.hh"
 
@@ -43,28 +44,32 @@
 DEFINE_NULL_NAMESPACE_BYTES (OT, Index) =  {0xFF,0xFF};
 DEFINE_NULL_NAMESPACE_BYTES (OT, LangSys) = {0x00,0x00, 0xFF,0xFF, 0x00,0x00};
 DEFINE_NULL_NAMESPACE_BYTES (OT, RangeRecord) = {0x00,0x01, 0x00,0x00, 0x00, 0x00};
+DEFINE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup) = {0x00,0x00,0x00,0x01, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00};
+DEFINE_NULL_NAMESPACE_BYTES (AAT, SettingName) = {0xFF,0xFF, 0xFF,0xFF};
 /* Hand-coded because Lookup is a template.  Sad. */
 const unsigned char _hb_Null_AAT_Lookup[2] = {0xFF, 0xFF};
 
 
-void
-hb_face_t::load_num_glyphs (void) const
+unsigned int
+hb_face_t::load_num_glyphs () const
 {
   hb_sanitize_context_t c = hb_sanitize_context_t ();
   c.set_num_glyphs (0); /* So we don't recurse ad infinitum. */
   hb_blob_t *maxp_blob = c.reference_table<OT::maxp> (this);
   const OT::maxp *maxp_table = maxp_blob->as<OT::maxp> ();
-  num_glyphs = maxp_table->get_num_glyphs ();
+
+  unsigned int ret = maxp_table->get_num_glyphs ();
+  num_glyphs.set_relaxed (ret);
   hb_blob_destroy (maxp_blob);
+  return ret;
 }
 
-void
-hb_face_t::load_upem (void) const
+unsigned int
+hb_face_t::load_upem () const
 {
-  hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<OT::head> (this);
-  const OT::head *head_table = head_blob->as<OT::head> ();
-  upem = head_table->get_upem ();
-  hb_blob_destroy (head_blob);
+  unsigned int ret = table.head->get_upem ();
+  upem.set_relaxed (ret);
+  return ret;
 }
 
 #endif
diff --git a/src/hb-subset-cff-common.cc b/src/hb-subset-cff-common.cc
new file mode 100644
index 0000000..b6127a9
--- /dev/null
+++ b/src/hb-subset-cff-common.cc
@@ -0,0 +1,226 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#include "hb-ot-cff-common.hh"
+#include "hb-ot-cff2-table.hh"
+#include "hb-subset-cff-common.hh"
+
+/* Disable FDSelect format 0 for compatibility with fonttools which doesn't seem choose it.
+ * Rarely any/much smaller than format 3 anyway. */
+#define CFF_SERIALIZE_FDSELECT_0  0
+
+using namespace CFF;
+
+/**
+ * hb_plan_subset_cff_fdselect
+ * Determine an optimal FDSelect format according to a provided plan.
+ *
+ * Return value: FDSelect format, size, and ranges for the most compact subset FDSelect
+ * along with a font index remapping table
+ **/
+
+bool
+hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
+			    unsigned int fdCount,
+			    const FDSelect &src, /* IN */
+			    unsigned int &subset_fd_count /* OUT */,
+			    unsigned int &subset_fdselect_size /* OUT */,
+			    unsigned int &subset_fdselect_format /* OUT */,
+			    hb_vector_t<code_pair> &fdselect_ranges /* OUT */,
+			    Remap &fdmap /* OUT */)
+{
+  subset_fd_count = 0;
+  subset_fdselect_size = 0;
+  subset_fdselect_format = 0;
+  unsigned int  num_ranges = 0;
+
+  unsigned int subset_num_glyphs = glyphs.len;
+  if (subset_num_glyphs == 0)
+    return true;
+
+  {
+    /* use hb_set to determine the subset of font dicts */
+    hb_set_t  *set = hb_set_create ();
+    if (set == &Null (hb_set_t))
+      return false;
+    hb_codepoint_t  prev_fd = CFF_UNDEF_CODE;
+    for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++)
+    {
+      hb_codepoint_t  fd = src.get_fd (glyphs[i]);
+      set->add (fd);
+
+      if (fd != prev_fd)
+      {
+	num_ranges++;
+	prev_fd = fd;
+	code_pair pair = { fd, i };
+	fdselect_ranges.push (pair);
+      }
+    }
+
+    subset_fd_count = set->get_population ();
+    if (subset_fd_count == fdCount)
+    {
+      /* all font dicts belong to the subset. no need to subset FDSelect & FDArray */
+      fdmap.identity (fdCount);
+      hb_set_destroy (set);
+    }
+    else
+    {
+      /* create a fdmap */
+      if (!fdmap.reset (fdCount))
+      {
+	hb_set_destroy (set);
+	return false;
+      }
+
+      hb_codepoint_t  fd = CFF_UNDEF_CODE;
+      while (set->next (&fd))
+	fdmap.add (fd);
+      hb_set_destroy (set);
+      if (unlikely (fdmap.get_count () != subset_fd_count))
+      	return false;
+    }
+
+    /* update each font dict index stored as "code" in fdselect_ranges */
+    for (unsigned int i = 0; i < fdselect_ranges.len; i++)
+      fdselect_ranges[i].code = fdmap[fdselect_ranges[i].code];
+  }
+
+  /* determine which FDSelect format is most compact */
+  if (subset_fd_count > 0xFF)
+  {
+    if (unlikely (src.format != 4))
+      return false;
+    subset_fdselect_format = 4;
+    subset_fdselect_size = FDSelect::min_size + FDSelect4::min_size + FDSelect4_Range::static_size * num_ranges + HBUINT32::static_size;
+  }
+  else
+  {
+#if CFF_SERIALIZE_FDSELECT_0
+    unsigned int format0_size = FDSelect::min_size + FDSelect0::min_size + HBUINT8::static_size * subset_num_glyphs;
+#endif
+    unsigned int format3_size = FDSelect::min_size + FDSelect3::min_size + FDSelect3_Range::static_size * num_ranges + HBUINT16::static_size;
+
+#if CFF_SERIALIZE_FDSELECT_0
+    if (format0_size <= format3_size)
+    {
+      // subset_fdselect_format = 0;
+      subset_fdselect_size = format0_size;
+    }
+    else
+#endif
+    {
+      subset_fdselect_format = 3;
+      subset_fdselect_size = format3_size;
+    }
+  }
+
+  return true;
+}
+
+template <typename FDSELECT3_4>
+static inline bool
+serialize_fdselect_3_4 (hb_serialize_context_t *c,
+			  const unsigned int num_glyphs,
+			  const FDSelect &src,
+			  unsigned int size,
+			  const hb_vector_t<code_pair> &fdselect_ranges)
+{
+  TRACE_SERIALIZE (this);
+  FDSELECT3_4 *p = c->allocate_size<FDSELECT3_4> (size);
+  if (unlikely (p == nullptr)) return_trace (false);
+  p->nRanges.set (fdselect_ranges.len);
+  for (unsigned int i = 0; i < fdselect_ranges.len; i++)
+  {
+    p->ranges[i].first.set (fdselect_ranges[i].glyph);
+    p->ranges[i].fd.set (fdselect_ranges[i].code);
+  }
+  p->sentinel().set (num_glyphs);
+  return_trace (true);
+}
+
+/**
+ * hb_serialize_cff_fdselect
+ * Serialize a subset FDSelect format planned above.
+ **/
+bool
+hb_serialize_cff_fdselect (hb_serialize_context_t *c,
+			  const unsigned int num_glyphs,
+			  const FDSelect &src,
+			  unsigned int fd_count,
+			  unsigned int fdselect_format,
+			  unsigned int size,
+			  const hb_vector_t<code_pair> &fdselect_ranges)
+{
+  TRACE_SERIALIZE (this);
+  FDSelect  *p = c->allocate_min<FDSelect> ();
+  if (unlikely (p == nullptr)) return_trace (false);
+  p->format.set (fdselect_format);
+  size -= FDSelect::min_size;
+
+  switch (fdselect_format)
+  {
+#if CFF_SERIALIZE_FDSELECT_0
+    case 0:
+    {
+      FDSelect0 *p = c->allocate_size<FDSelect0> (size);
+      if (unlikely (p == nullptr)) return_trace (false);
+      unsigned int range_index = 0;
+      unsigned int  fd = fdselect_ranges[range_index++].code;
+      for (unsigned int i = 0; i < num_glyphs; i++)
+      {
+	if ((range_index < fdselect_ranges.len) &&
+	    (i >= fdselect_ranges[range_index].glyph))
+	{
+	  fd = fdselect_ranges[range_index++].code;
+	}
+	p->fds[i].set (fd);
+      }
+      break;
+    }
+#endif /* CFF_SERIALIZE_FDSELECT_0 */
+
+    case 3:
+      return serialize_fdselect_3_4<FDSelect3> (c,
+						num_glyphs,
+						src,
+						size,
+						fdselect_ranges);
+
+    case 4:
+      return serialize_fdselect_3_4<FDSelect4> (c,
+						num_glyphs,
+						src,
+						size,
+						fdselect_ranges);
+
+    default:
+      assert(false);
+  }
+
+  return_trace (true);
+}
diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh
new file mode 100644
index 0000000..820a090
--- /dev/null
+++ b/src/hb-subset-cff-common.hh
@@ -0,0 +1,992 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_SUBSET_CFF_COMMON_HH
+#define HB_SUBSET_CFF_COMMON_HH
+
+#include "hb.hh"
+
+#include "hb-subset-plan.hh"
+#include "hb-cff-interp-cs-common.hh"
+
+namespace CFF {
+
+/* Used for writing a temporary charstring */
+struct StrEncoder
+{
+  StrEncoder (StrBuff &buff_)
+    : buff (buff_), error (false) {}
+
+  void reset () { buff.resize (0); }
+
+  void encode_byte (unsigned char b)
+  {
+    if (unlikely (buff.push ((const char)b) == &Crap(char)))
+      set_error ();
+  }
+
+  void encode_int (int v)
+  {
+    if ((-1131 <= v) && (v <= 1131))
+    {
+      if ((-107 <= v) && (v <= 107))
+	encode_byte (v + 139);
+      else if (v > 0)
+      {
+	v -= 108;
+	encode_byte ((v >> 8) + OpCode_TwoBytePosInt0);
+	encode_byte (v & 0xFF);
+      }
+      else
+      {
+	v = -v - 108;
+	encode_byte ((v >> 8) + OpCode_TwoByteNegInt0);
+	encode_byte (v & 0xFF);
+      }
+    }
+    else
+    {
+      if (unlikely (v < -32768))
+	v = -32768;
+      else if (unlikely (v > 32767))
+	v = 32767;
+      encode_byte (OpCode_shortint);
+      encode_byte ((v >> 8) & 0xFF);
+      encode_byte (v & 0xFF);
+    }
+  }
+
+  void encode_num (const Number& n)
+  {
+    if (n.in_int_range ())
+    {
+      encode_int (n.to_int ());
+    }
+    else
+    {
+      int32_t v = n.to_fixed ();
+      encode_byte (OpCode_fixedcs);
+      encode_byte ((v >> 24) & 0xFF);
+      encode_byte ((v >> 16) & 0xFF);
+      encode_byte ((v >> 8) & 0xFF);
+      encode_byte (v & 0xFF);
+    }
+  }
+
+  void encode_op (OpCode op)
+  {
+    if (Is_OpCode_ESC (op))
+    {
+      encode_byte (OpCode_escape);
+      encode_byte (Unmake_OpCode_ESC (op));
+    }
+    else
+      encode_byte (op);
+  }
+
+  void copy_str (const ByteStr &str)
+  {
+    unsigned int  offset = buff.len;
+    buff.resize (offset + str.len);
+    if (unlikely (buff.len < offset + str.len))
+    {
+      set_error ();
+      return;
+    }
+    memcpy (&buff[offset], &str.str[0], str.len);
+  }
+
+  bool is_error () const { return error; }
+
+  protected:
+  void set_error () { error = true; }
+
+  StrBuff &buff;
+  bool    error;
+};
+
+struct CFFSubTableOffsets {
+  CFFSubTableOffsets () : privateDictsOffset (0)
+  {
+    topDictInfo.init ();
+    FDSelectInfo.init ();
+    FDArrayInfo.init ();
+    charStringsInfo.init ();
+    globalSubrsInfo.init ();
+    localSubrsInfos.init ();
+  }
+
+  ~CFFSubTableOffsets () { localSubrsInfos.fini (); }
+
+  TableInfo     topDictInfo;
+  TableInfo     FDSelectInfo;
+  TableInfo     FDArrayInfo;
+  TableInfo     charStringsInfo;
+  unsigned int  privateDictsOffset;
+  TableInfo     globalSubrsInfo;
+  hb_vector_t<TableInfo>  localSubrsInfos;
+};
+
+template <typename OPSTR=OpStr>
+struct CFFTopDict_OpSerializer : OpSerializer
+{
+  bool serialize (hb_serialize_context_t *c,
+		  const OPSTR &opstr,
+		  const CFFSubTableOffsets &offsets) const
+  {
+    TRACE_SERIALIZE (this);
+
+    switch (opstr.op)
+    {
+      case OpCode_CharStrings:
+	return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsInfo.offset));
+
+      case OpCode_FDArray:
+	return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayInfo.offset));
+
+      case OpCode_FDSelect:
+	return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset));
+
+      default:
+	return_trace (copy_opstr (c, opstr));
+    }
+    return_trace (true);
+  }
+
+  unsigned int calculate_serialized_size (const OPSTR &opstr) const
+  {
+    switch (opstr.op)
+    {
+      case OpCode_CharStrings:
+      case OpCode_FDArray:
+      case OpCode_FDSelect:
+	return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
+
+      default:
+	return opstr.str.len;
+    }
+  }
+};
+
+struct CFFFontDict_OpSerializer : OpSerializer
+{
+  bool serialize (hb_serialize_context_t *c,
+		  const OpStr &opstr,
+		  const TableInfo &privateDictInfo) const
+  {
+    TRACE_SERIALIZE (this);
+
+    if (opstr.op == OpCode_Private)
+    {
+      /* serialize the private dict size & offset as 2-byte & 4-byte integers */
+      if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) ||
+		    !UnsizedByteStr::serialize_int4 (c, privateDictInfo.offset)))
+	return_trace (false);
+
+      /* serialize the opcode */
+      HBUINT8 *p = c->allocate_size<HBUINT8> (1);
+      if (unlikely (p == nullptr)) return_trace (false);
+      p->set (OpCode_Private);
+
+      return_trace (true);
+    }
+    else
+    {
+      HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
+      if (unlikely (d == nullptr)) return_trace (false);
+      memcpy (d, &opstr.str.str[0], opstr.str.len);
+    }
+    return_trace (true);
+  }
+
+  unsigned int calculate_serialized_size (const OpStr &opstr) const
+  {
+    if (opstr.op == OpCode_Private)
+      return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
+    else
+      return opstr.str.len;
+  }
+};
+
+struct CFFPrivateDict_OpSerializer : OpSerializer
+{
+  CFFPrivateDict_OpSerializer (bool desubroutinize_, bool drop_hints_)
+    : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {}
+
+  bool serialize (hb_serialize_context_t *c,
+		  const OpStr &opstr,
+		  const unsigned int subrsOffset) const
+  {
+    TRACE_SERIALIZE (this);
+
+    if (drop_hints && DictOpSet::is_hint_op (opstr.op))
+      return true;
+    if (opstr.op == OpCode_Subrs)
+    {
+      if (desubroutinize || (subrsOffset == 0))
+	return_trace (true);
+      else
+	return_trace (FontDict::serialize_offset2_op (c, opstr.op, subrsOffset));
+    }
+    else
+      return_trace (copy_opstr (c, opstr));
+  }
+
+  unsigned int calculate_serialized_size (const OpStr &opstr,
+					  bool has_localsubr=true) const
+  {
+    if (drop_hints && DictOpSet::is_hint_op (opstr.op))
+      return 0;
+    if (opstr.op == OpCode_Subrs)
+    {
+      if (desubroutinize || !has_localsubr)
+	return 0;
+      else
+	return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (opstr.op);
+    }
+    else
+      return opstr.str.len;
+  }
+
+  protected:
+  const bool  desubroutinize;
+  const bool  drop_hints;
+};
+
+struct FlattenParam
+{
+  StrBuff     &flatStr;
+  bool	drop_hints;
+};
+
+template <typename ACC, typename ENV, typename OPSET>
+struct SubrFlattener
+{
+  SubrFlattener (const ACC &acc_,
+			const hb_vector_t<hb_codepoint_t> &glyphs_,
+			bool drop_hints_)
+    : acc (acc_),
+      glyphs (glyphs_),
+      drop_hints (drop_hints_)
+  {}
+
+  bool flatten (StrBuffArray &flat_charstrings)
+  {
+    if (!flat_charstrings.resize (glyphs.len))
+      return false;
+    for (unsigned int i = 0; i < glyphs.len; i++)
+      flat_charstrings[i].init ();
+    for (unsigned int i = 0; i < glyphs.len; i++)
+    {
+      hb_codepoint_t  glyph = glyphs[i];
+      const ByteStr str = (*acc.charStrings)[glyph];
+      unsigned int fd = acc.fdSelect->get_fd (glyph);
+      if (unlikely (fd >= acc.fdCount))
+      	return false;
+      CSInterpreter<ENV, OPSET, FlattenParam> interp;
+      interp.env.init (str, acc, fd);
+      FlattenParam  param = { flat_charstrings[i], drop_hints };
+      if (unlikely (!interp.interpret (param)))
+	return false;
+    }
+    return true;
+  }
+
+  const ACC &acc;
+  const hb_vector_t<hb_codepoint_t> &glyphs;
+  bool  drop_hints;
+};
+
+struct SubrClosures
+{
+  SubrClosures () : valid (false), global_closure (nullptr)
+  { local_closures.init (); }
+
+  void init (unsigned int fd_count)
+  {
+    valid = true;
+    global_closure = hb_set_create ();
+    if (global_closure == hb_set_get_empty ())
+      valid = false;
+    if (!local_closures.resize (fd_count))
+      valid = false;
+
+    for (unsigned int i = 0; i < local_closures.len; i++)
+    {
+      local_closures[i] = hb_set_create ();
+      if (local_closures[i] == hb_set_get_empty ())
+	valid = false;
+    }
+  }
+
+  void fini ()
+  {
+    hb_set_destroy (global_closure);
+    for (unsigned int i = 0; i < local_closures.len; i++)
+      hb_set_destroy (local_closures[i]);
+    local_closures.fini ();
+  }
+
+  void reset ()
+  {
+    hb_set_clear (global_closure);
+    for (unsigned int i = 0; i < local_closures.len; i++)
+      hb_set_clear (local_closures[i]);
+  }
+
+  bool is_valid () const { return valid; }
+  bool  valid;
+  hb_set_t  *global_closure;
+  hb_vector_t<hb_set_t *> local_closures;
+};
+
+struct ParsedCSOp : OpStr
+{
+  void init (unsigned int subr_num_ = 0)
+  {
+    OpStr::init ();
+    subr_num = subr_num_;
+    drop_flag = false;
+    keep_flag = false;
+    skip_flag = false;
+  }
+
+  void fini () { OpStr::fini (); }
+
+  bool for_drop () const { return drop_flag; }
+  void set_drop ()       { if (!for_keep ()) drop_flag = true; }
+
+  bool for_keep () const { return keep_flag; }
+  void set_keep ()       { keep_flag = true; }
+
+  bool for_skip () const { return skip_flag; }
+  void set_skip ()       { skip_flag = true; }
+
+  unsigned int  subr_num;
+
+  protected:
+  bool	  drop_flag : 1;
+  bool	  keep_flag : 1;
+  bool	  skip_flag : 1;
+};
+
+struct ParsedCStr : ParsedValues<ParsedCSOp>
+{
+  void init ()
+  {
+    SUPER::init ();
+    parsed = false;
+    hint_dropped = false;
+    has_prefix_ = false;
+  }
+
+  void add_op (OpCode op, const SubByteStr& substr)
+  {
+    if (!is_parsed ())
+      SUPER::add_op (op, substr);
+  }
+
+  void add_call_op (OpCode op, const SubByteStr& substr, unsigned int subr_num)
+  {
+    if (!is_parsed ())
+    {
+      unsigned int parsed_len = get_count ();
+      if (likely (parsed_len > 0))
+	values[parsed_len-1].set_skip ();
+
+      ParsedCSOp val;
+      val.init (subr_num);
+      SUPER::add_op (op, substr, val);
+    }
+  }
+
+  void set_prefix (const Number &num, OpCode op = OpCode_Invalid)
+  {
+    has_prefix_ = true;
+    prefix_op_ = op;
+    prefix_num_ = num;
+  }
+
+  bool at_end (unsigned int pos) const
+  {
+    return ((pos + 1 >= values.len) /* CFF2 */
+	|| (values[pos + 1].op == OpCode_return));
+  }
+
+  bool is_parsed () const { return parsed; }
+  void set_parsed ()      { parsed = true; }
+
+  bool is_hint_dropped () const { return hint_dropped; }
+  void set_hint_dropped ()      { hint_dropped = true; }
+
+  bool is_vsindex_dropped () const { return vsindex_dropped; }
+  void set_vsindex_dropped ()      { vsindex_dropped = true; }
+
+  bool has_prefix () const          { return has_prefix_; }
+  OpCode prefix_op () const         { return prefix_op_; }
+  const Number &prefix_num () const { return prefix_num_; }
+
+  protected:
+  bool    parsed;
+  bool    hint_dropped;
+  bool    vsindex_dropped;
+  bool    has_prefix_;
+  OpCode  prefix_op_;
+  Number  prefix_num_;
+
+  private:
+  typedef ParsedValues<ParsedCSOp> SUPER;
+};
+
+struct ParsedCStrs : hb_vector_t<ParsedCStr>
+{
+  void init (unsigned int len_ = 0)
+  {
+    SUPER::init ();
+    resize (len_);
+    for (unsigned int i = 0; i < len; i++)
+      (*this)[i].init ();
+  }
+  void fini () { SUPER::fini_deep (); }
+
+  private:
+  typedef hb_vector_t<ParsedCStr> SUPER;
+};
+
+struct SubrSubsetParam
+{
+  void init (ParsedCStr *parsed_charstring_,
+		    ParsedCStrs *parsed_global_subrs_, ParsedCStrs *parsed_local_subrs_,
+		    hb_set_t *global_closure_, hb_set_t *local_closure_,
+		    bool drop_hints_)
+  {
+    parsed_charstring = parsed_charstring_;
+    current_parsed_str = parsed_charstring;
+    parsed_global_subrs = parsed_global_subrs_;
+    parsed_local_subrs = parsed_local_subrs_;
+    global_closure = global_closure_;
+    local_closure = local_closure_;
+    drop_hints = drop_hints_;
+  }
+
+  ParsedCStr *get_parsed_str_for_context (CallContext &context)
+  {
+    switch (context.type)
+    {
+      case CSType_CharString:
+	return parsed_charstring;
+
+      case CSType_LocalSubr:
+	if (likely (context.subr_num < parsed_local_subrs->len))
+	  return &(*parsed_local_subrs)[context.subr_num];
+	break;
+
+      case CSType_GlobalSubr:
+	if (likely (context.subr_num < parsed_global_subrs->len))
+	  return &(*parsed_global_subrs)[context.subr_num];
+	break;
+    }
+    return nullptr;
+  }
+
+  template <typename ENV>
+  void set_current_str (ENV &env, bool calling)
+  {
+    ParsedCStr  *parsed_str = get_parsed_str_for_context (env.context);
+    if (likely (parsed_str != nullptr))
+    {
+      /* If the called subroutine is parsed partially but not completely yet,
+       * it must be because we are calling it recursively.
+       * Handle it as an error. */
+      if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.len > 0)))
+      	env.set_error ();
+      else
+      	current_parsed_str = parsed_str;
+    }
+    else
+      env.set_error ();
+  }
+
+  ParsedCStr    *current_parsed_str;
+
+  ParsedCStr    *parsed_charstring;
+  ParsedCStrs   *parsed_global_subrs;
+  ParsedCStrs   *parsed_local_subrs;
+  hb_set_t      *global_closure;
+  hb_set_t      *local_closure;
+  bool	  drop_hints;
+};
+
+struct SubrRemap : Remap
+{
+  void create (hb_set_t *closure)
+  {
+    /* create a remapping of subroutine numbers from old to new.
+     * no optimization based on usage counts. fonttools doesn't appear doing that either.
+     */
+    reset (closure->get_max () + 1);
+    for (hb_codepoint_t old_num = 0; old_num < len; old_num++)
+    {
+      if (hb_set_has (closure, old_num))
+	add (old_num);
+    }
+
+    if (get_count () < 1240)
+      bias = 107;
+    else if (get_count () < 33900)
+      bias = 1131;
+    else
+      bias = 32768;
+  }
+
+  hb_codepoint_t operator[] (unsigned int old_num) const
+  {
+    if (old_num >= len)
+      return CFF_UNDEF_CODE;
+    else
+      return Remap::operator[] (old_num);
+  }
+
+  int biased_num (unsigned int old_num) const
+  {
+    hb_codepoint_t new_num = (*this)[old_num];
+    return (int)new_num - bias;
+  }
+
+  protected:
+  int bias;
+};
+
+struct SubrRemaps
+{
+  SubrRemaps ()
+  {
+    global_remap.init ();
+    local_remaps.init ();
+  }
+
+  ~SubrRemaps () { fini (); }
+
+  void init (unsigned int fdCount)
+  {
+    local_remaps.resize (fdCount);
+    for (unsigned int i = 0; i < fdCount; i++)
+      local_remaps[i].init ();
+  }
+
+  void create (SubrClosures& closures)
+  {
+    global_remap.create (closures.global_closure);
+    for (unsigned int i = 0; i < local_remaps.len; i++)
+      local_remaps[i].create (closures.local_closures[i]);
+  }
+
+  void fini ()
+  {
+    global_remap.fini ();
+    local_remaps.fini_deep ();
+  }
+
+  SubrRemap	       global_remap;
+  hb_vector_t<SubrRemap>  local_remaps;
+};
+
+template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typename OPSET>
+struct SubrSubsetter
+{
+  SubrSubsetter ()
+  {
+    parsed_charstrings.init ();
+    parsed_global_subrs.init ();
+    parsed_local_subrs.init ();
+  }
+
+  ~SubrSubsetter ()
+  {
+    closures.fini ();
+    remaps.fini ();
+    parsed_charstrings.fini_deep ();
+    parsed_global_subrs.fini_deep ();
+    parsed_local_subrs.fini_deep ();
+  }
+
+  /* Subroutine subsetting with --no-desubroutinize runs in phases:
+   *
+   * 1. execute charstrings/subroutines to determine subroutine closures
+   * 2. parse out all operators and numbers
+   * 3. mark hint operators and operands for removal if --no-hinting
+   * 4. re-encode all charstrings and subroutines with new subroutine numbers
+   *
+   * Phases #1 and #2 are done at the same time in collect_subrs ().
+   * Phase #3 walks charstrings/subroutines forward then backward (hence parsing required),
+   * because we can't tell if a number belongs to a hint op until we see the first moveto.
+   *
+   * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number
+   * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine.
+   */
+  bool subset (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, bool drop_hints)
+  {
+    closures.init (acc.fdCount);
+    remaps.init (acc.fdCount);
+
+    parsed_charstrings.init (glyphs.len);
+    parsed_global_subrs.init (acc.globalSubrs->count);
+    parsed_local_subrs.resize (acc.fdCount);
+    for (unsigned int i = 0; i < acc.fdCount; i++)
+    {
+      parsed_local_subrs[i].init (acc.privateDicts[i].localSubrs->count);
+    }
+    if (unlikely (!closures.valid))
+      return false;
+
+    /* phase 1 & 2 */
+    for (unsigned int i = 0; i < glyphs.len; i++)
+    {
+      hb_codepoint_t  glyph = glyphs[i];
+      const ByteStr str = (*acc.charStrings)[glyph];
+      unsigned int fd = acc.fdSelect->get_fd (glyph);
+      if (unlikely (fd >= acc.fdCount))
+      	return false;
+
+      CSInterpreter<ENV, OPSET, SubrSubsetParam> interp;
+      interp.env.init (str, acc, fd);
+
+      SubrSubsetParam  param;
+      param.init (&parsed_charstrings[i],
+		  &parsed_global_subrs,  &parsed_local_subrs[fd],
+		  closures.global_closure, closures.local_closures[fd],
+		  drop_hints);
+
+      if (unlikely (!interp.interpret (param)))
+	return false;
+
+      /* finalize parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */
+      SUBSETTER::finalize_parsed_str (interp.env, param, parsed_charstrings[i]);
+    }
+
+    if (drop_hints)
+    {
+      /* mark hint ops and arguments for drop */
+      for (unsigned int i = 0; i < glyphs.len; i++)
+      {
+	unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
+	if (unlikely (fd >= acc.fdCount))
+	  return false;
+	SubrSubsetParam  param;
+	param.init (&parsed_charstrings[i],
+		    &parsed_global_subrs,  &parsed_local_subrs[fd],
+		    closures.global_closure, closures.local_closures[fd],
+		    drop_hints);
+
+	DropHintsParam  drop;
+	if (drop_hints_in_str (parsed_charstrings[i], param, drop))
+	{
+	  parsed_charstrings[i].set_hint_dropped ();
+	  if (drop.vsindex_dropped)
+	    parsed_charstrings[i].set_vsindex_dropped ();
+	}
+      }
+
+      /* after dropping hints recreate closures of actually used subrs */
+      closures.reset ();
+      for (unsigned int i = 0; i < glyphs.len; i++)
+      {
+	unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
+	if (unlikely (fd >= acc.fdCount))
+	  return false;
+	SubrSubsetParam  param;
+	param.init (&parsed_charstrings[i],
+		    &parsed_global_subrs,  &parsed_local_subrs[fd],
+		    closures.global_closure, closures.local_closures[fd],
+		    drop_hints);
+	collect_subr_refs_in_str (parsed_charstrings[i], param);
+      }
+    }
+
+    remaps.create (closures);
+
+    return true;
+  }
+
+  bool encode_charstrings (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, StrBuffArray &buffArray) const
+  {
+    if (unlikely (!buffArray.resize (glyphs.len)))
+      return false;
+    for (unsigned int i = 0; i < glyphs.len; i++)
+    {
+      unsigned int  fd = acc.fdSelect->get_fd (glyphs[i]);
+      if (unlikely (fd >= acc.fdCount))
+      	return false;
+      if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i])))
+	return false;
+    }
+    return true;
+  }
+
+  bool encode_subrs (const ParsedCStrs &subrs, const SubrRemap& remap, unsigned int fd, StrBuffArray &buffArray) const
+  {
+    unsigned int  count = remap.get_count ();
+
+    if (unlikely (!buffArray.resize (count)))
+      return false;
+    for (unsigned int old_num = 0; old_num < subrs.len; old_num++)
+    {
+      hb_codepoint_t new_num = remap[old_num];
+      if (new_num != CFF_UNDEF_CODE)
+      {
+	if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num])))
+	  return false;
+      }
+    }
+    return true;
+  }
+
+  bool encode_globalsubrs (StrBuffArray &buffArray)
+  {
+    return encode_subrs (parsed_global_subrs, remaps.global_remap, 0, buffArray);
+  }
+
+  bool encode_localsubrs (unsigned int fd, StrBuffArray &buffArray) const
+  {
+    return encode_subrs (parsed_local_subrs[fd], remaps.local_remaps[fd], fd, buffArray);
+  }
+
+  protected:
+  struct DropHintsParam
+  {
+    DropHintsParam ()
+      : seen_moveto (false),
+	ends_in_hint (false),
+	vsindex_dropped (false) {}
+
+    bool  seen_moveto;
+    bool  ends_in_hint;
+    bool  vsindex_dropped;
+  };
+
+  bool drop_hints_in_subr (ParsedCStr &str, unsigned int pos,
+			   ParsedCStrs &subrs, unsigned int subr_num,
+			   const SubrSubsetParam &param, DropHintsParam &drop)
+  {
+    drop.ends_in_hint = false;
+    bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop);
+
+    /* if this subr ends with a stem hint (i.e., not a number a potential argument for moveto),
+     * then this entire subroutine must be a hint. drop its call. */
+    if (drop.ends_in_hint)
+    {
+      str.values[pos].set_drop ();
+      /* if this subr call is at the end of the parent subr, propagate the flag
+       * otherwise reset the flag */
+      if (!str.at_end (pos))
+	drop.ends_in_hint = false;
+    }
+
+    return has_hint;
+  }
+
+  /* returns true if it sees a hint op before the first moveto */
+  bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam &param, DropHintsParam &drop)
+  {
+    bool  seen_hint = false;
+
+    for (unsigned int pos = 0; pos < str.values.len; pos++)
+    {
+      bool  has_hint = false;
+      switch (str.values[pos].op)
+      {
+	case OpCode_callsubr:
+	  has_hint = drop_hints_in_subr (str, pos,
+					*param.parsed_local_subrs, str.values[pos].subr_num,
+					param, drop);
+
+	  break;
+
+	case OpCode_callgsubr:
+	  has_hint = drop_hints_in_subr (str, pos,
+					*param.parsed_global_subrs, str.values[pos].subr_num,
+					param, drop);
+	  break;
+
+	case OpCode_rmoveto:
+	case OpCode_hmoveto:
+	case OpCode_vmoveto:
+	  drop.seen_moveto = true;
+	  break;
+
+	case OpCode_hintmask:
+	case OpCode_cntrmask:
+	  if (drop.seen_moveto)
+	  {
+	    str.values[pos].set_drop ();
+	    break;
+	  }
+	  HB_FALLTHROUGH;
+
+	case OpCode_hstemhm:
+	case OpCode_vstemhm:
+	case OpCode_hstem:
+	case OpCode_vstem:
+	  has_hint = true;
+	  str.values[pos].set_drop ();
+	  if (str.at_end (pos))
+	    drop.ends_in_hint = true;
+	  break;
+
+	case OpCode_dotsection:
+	  str.values[pos].set_drop ();
+	  break;
+
+	default:
+	  /* NONE */
+	  break;
+      }
+      if (has_hint)
+      {
+	for (int i = pos - 1; i >= 0; i--)
+	{
+	  ParsedCSOp  &csop = str.values[(unsigned)i];
+	  if (csop.for_drop ())
+	    break;
+	  csop.set_drop ();
+	  if (csop.op == OpCode_vsindexcs)
+	    drop.vsindex_dropped = true;
+	}
+	seen_hint |= has_hint;
+      }
+    }
+
+    return seen_hint;
+  }
+
+  void collect_subr_refs_in_subr (ParsedCStr &str, unsigned int pos,
+				  unsigned int subr_num, ParsedCStrs &subrs,
+				  hb_set_t *closure,
+				  const SubrSubsetParam &param)
+  {
+    hb_set_add (closure, subr_num);
+    collect_subr_refs_in_str (subrs[subr_num], param);
+  }
+
+  void collect_subr_refs_in_str (ParsedCStr &str, const SubrSubsetParam &param)
+  {
+    for (unsigned int pos = 0; pos < str.values.len; pos++)
+    {
+      if (!str.values[pos].for_drop ())
+      {
+	switch (str.values[pos].op)
+	{
+	  case OpCode_callsubr:
+	    collect_subr_refs_in_subr (str, pos,
+				       str.values[pos].subr_num, *param.parsed_local_subrs,
+				       param.local_closure, param);
+	    break;
+
+	  case OpCode_callgsubr:
+	    collect_subr_refs_in_subr (str, pos,
+				       str.values[pos].subr_num, *param.parsed_global_subrs,
+				       param.global_closure, param);
+	    break;
+
+	  default: break;
+	}
+      }
+    }
+  }
+
+  bool encode_str (const ParsedCStr &str, const unsigned int fd, StrBuff &buff) const
+  {
+    buff.init ();
+    StrEncoder  encoder (buff);
+    encoder.reset ();
+    /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints,
+     * re-insert it at the beginning of charstreing */
+    if (str.has_prefix () && str.is_hint_dropped ())
+    {
+      encoder.encode_num (str.prefix_num ());
+      if (str.prefix_op () != OpCode_Invalid)
+	encoder.encode_op (str.prefix_op ());
+    }
+    for (unsigned int i = 0; i < str.get_count(); i++)
+    {
+      const ParsedCSOp  &opstr = str.values[i];
+      if (!opstr.for_drop () && !opstr.for_skip ())
+      {
+	switch (opstr.op)
+	{
+	  case OpCode_callsubr:
+	    encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num));
+	    encoder.encode_op (OpCode_callsubr);
+	    break;
+
+	  case OpCode_callgsubr:
+	    encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num));
+	    encoder.encode_op (OpCode_callgsubr);
+	    break;
+
+	  default:
+	    encoder.copy_str (opstr.str);
+	    break;
+	}
+      }
+    }
+    return !encoder.is_error ();
+  }
+
+  protected:
+  SubrClosures	      closures;
+
+  ParsedCStrs	       parsed_charstrings;
+  ParsedCStrs	       parsed_global_subrs;
+  hb_vector_t<ParsedCStrs>  parsed_local_subrs;
+
+  SubrRemaps		remaps;
+
+  private:
+  typedef typename SUBRS::count_type subr_count_type;
+};
+};  /* namespace CFF */
+
+HB_INTERNAL bool
+hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
+			    unsigned int fdCount,
+			    const CFF::FDSelect &src, /* IN */
+			    unsigned int &subset_fd_count /* OUT */,
+			    unsigned int &subset_fdselect_size /* OUT */,
+			    unsigned int &subset_fdselect_format /* OUT */,
+			    hb_vector_t<CFF::code_pair> &fdselect_ranges /* OUT */,
+			    CFF::Remap &fdmap /* OUT */);
+
+HB_INTERNAL bool
+hb_serialize_cff_fdselect (hb_serialize_context_t *c,
+			  unsigned int num_glyphs,
+			  const CFF::FDSelect &src,
+			  unsigned int fd_count,
+			  unsigned int fdselect_format,
+			  unsigned int size,
+			  const hb_vector_t<CFF::code_pair> &fdselect_ranges);
+
+#endif /* HB_SUBSET_CFF_COMMON_HH */
diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc
new file mode 100644
index 0000000..09364a2
--- /dev/null
+++ b/src/hb-subset-cff1.cc
@@ -0,0 +1,1108 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#include "hb-open-type.hh"
+#include "hb-ot-cff1-table.hh"
+#include "hb-set.h"
+#include "hb-subset-cff1.hh"
+#include "hb-subset-plan.hh"
+#include "hb-subset-cff-common.hh"
+#include "hb-cff1-interp-cs.hh"
+
+using namespace CFF;
+
+struct RemapSID : Remap
+{
+  unsigned int add (unsigned int sid)
+  {
+    if ((sid != CFF_UNDEF_SID) && !is_std_std (sid))
+      return offset_sid (Remap::add (unoffset_sid (sid)));
+    else
+      return sid;
+  }
+
+  unsigned int operator[] (unsigned int sid) const
+  {
+    if (is_std_std (sid) || (sid == CFF_UNDEF_SID))
+      return sid;
+    else
+      return offset_sid (Remap::operator [] (unoffset_sid (sid)));
+  }
+
+  static const unsigned int num_std_strings = 391;
+
+  static bool is_std_std (unsigned int sid) { return sid < num_std_strings; }
+  static unsigned int offset_sid (unsigned int sid) { return sid + num_std_strings; }
+  static unsigned int unoffset_sid (unsigned int sid) { return sid - num_std_strings; }
+};
+
+struct CFF1SubTableOffsets : CFFSubTableOffsets
+{
+  CFF1SubTableOffsets ()
+    : CFFSubTableOffsets (),
+      nameIndexOffset (0),
+      encodingOffset (0)
+  {
+    stringIndexInfo.init ();
+    charsetInfo.init ();
+    privateDictInfo.init ();
+  }
+
+  unsigned int  nameIndexOffset;
+  TableInfo     stringIndexInfo;
+  unsigned int  encodingOffset;
+  TableInfo     charsetInfo;
+  TableInfo     privateDictInfo;
+};
+
+/* a copy of a parsed out CFF1TopDictValues augmented with additional operators */
+struct CFF1TopDictValuesMod : CFF1TopDictValues
+{
+  void init (const CFF1TopDictValues *base_= &Null(CFF1TopDictValues))
+  {
+    SUPER::init ();
+    base = base_;
+  }
+
+  void fini () { SUPER::fini (); }
+
+  unsigned get_count () const { return base->get_count () + SUPER::get_count (); }
+  const CFF1TopDictVal &get_value (unsigned int i) const
+  {
+    if (i < base->get_count ())
+      return (*base)[i];
+    else
+      return SUPER::values[i - base->get_count ()];
+  }
+  const CFF1TopDictVal &operator [] (unsigned int i) const { return get_value (i); }
+
+  void reassignSIDs (const RemapSID& sidmap)
+  {
+    for (unsigned int i = 0; i < NameDictValues::ValCount; i++)
+      nameSIDs[i] = sidmap[base->nameSIDs[i]];
+  }
+
+  protected:
+  typedef CFF1TopDictValues SUPER;
+  const CFF1TopDictValues *base;
+};
+
+struct TopDictModifiers
+{
+  TopDictModifiers (const CFF1SubTableOffsets &offsets_,
+			   const unsigned int (&nameSIDs_)[NameDictValues::ValCount])
+    : offsets (offsets_),
+      nameSIDs (nameSIDs_)
+  {}
+
+  const CFF1SubTableOffsets &offsets;
+  const unsigned int	(&nameSIDs)[NameDictValues::ValCount];
+};
+
+struct CFF1TopDict_OpSerializer : CFFTopDict_OpSerializer<CFF1TopDictVal>
+{
+  bool serialize (hb_serialize_context_t *c,
+		  const CFF1TopDictVal &opstr,
+		  const TopDictModifiers &mod) const
+  {
+    TRACE_SERIALIZE (this);
+
+    OpCode op = opstr.op;
+    switch (op)
+    {
+      case OpCode_charset:
+	return_trace (FontDict::serialize_offset4_op(c, op, mod.offsets.charsetInfo.offset));
+
+      case OpCode_Encoding:
+	return_trace (FontDict::serialize_offset4_op(c, op, mod.offsets.encodingOffset));
+
+      case OpCode_Private:
+	{
+	  if (unlikely (!UnsizedByteStr::serialize_int2 (c, mod.offsets.privateDictInfo.size)))
+	    return_trace (false);
+	  if (unlikely (!UnsizedByteStr::serialize_int4 (c, mod.offsets.privateDictInfo.offset)))
+	    return_trace (false);
+	  HBUINT8 *p = c->allocate_size<HBUINT8> (1);
+	  if (unlikely (p == nullptr)) return_trace (false);
+	  p->set (OpCode_Private);
+	}
+	break;
+
+      case OpCode_version:
+      case OpCode_Notice:
+      case OpCode_Copyright:
+      case OpCode_FullName:
+      case OpCode_FamilyName:
+      case OpCode_Weight:
+      case OpCode_PostScript:
+      case OpCode_BaseFontName:
+      case OpCode_FontName:
+	return_trace (FontDict::serialize_offset2_op(c, op, mod.nameSIDs[NameDictValues::name_op_to_index (op)]));
+
+      case OpCode_ROS:
+	{
+	  /* for registry & ordering, reassigned SIDs are serialized
+	   * for supplement, the original byte string is copied along with the op code */
+	  OpStr supp_op;
+	  supp_op.op = op;
+	  supp_op.str.str = opstr.str.str + opstr.last_arg_offset;
+	  if ( unlikely (!(opstr.str.len >= opstr.last_arg_offset + 3)))
+	    return_trace (false);
+	  supp_op.str.len = opstr.str.len - opstr.last_arg_offset;
+	  return_trace (UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[NameDictValues::registry]) &&
+			UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[NameDictValues::ordering]) &&
+			copy_opstr (c, supp_op));
+	}
+      default:
+	return_trace (CFFTopDict_OpSerializer<CFF1TopDictVal>::serialize (c, opstr, mod.offsets));
+    }
+    return_trace (true);
+  }
+
+  unsigned int calculate_serialized_size (const CFF1TopDictVal &opstr) const
+  {
+    OpCode op = opstr.op;
+    switch (op)
+    {
+      case OpCode_charset:
+      case OpCode_Encoding:
+	return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
+
+      case OpCode_Private:
+	return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
+
+      case OpCode_version:
+      case OpCode_Notice:
+      case OpCode_Copyright:
+      case OpCode_FullName:
+      case OpCode_FamilyName:
+      case OpCode_Weight:
+      case OpCode_PostScript:
+      case OpCode_BaseFontName:
+      case OpCode_FontName:
+	return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (op);
+
+      case OpCode_ROS:
+	return ((OpCode_Size (OpCode_shortint) + 2) * 2) + (opstr.str.len - opstr.last_arg_offset)/* supplement + op */;
+
+      default:
+	return CFFTopDict_OpSerializer<CFF1TopDictVal>::calculate_serialized_size (opstr);
+    }
+  }
+};
+
+struct FontDictValuesMod
+{
+  void init (const CFF1FontDictValues *base_,
+	     unsigned int fontName_,
+	     const TableInfo &privateDictInfo_)
+  {
+    base = base_;
+    fontName = fontName_;
+    privateDictInfo = privateDictInfo_;
+  }
+
+  unsigned get_count () const { return base->get_count (); }
+
+  const OpStr &operator [] (unsigned int i) const { return (*base)[i]; }
+
+  const CFF1FontDictValues    *base;
+  TableInfo		   privateDictInfo;
+  unsigned int		fontName;
+};
+
+struct CFF1FontDict_OpSerializer : CFFFontDict_OpSerializer
+{
+  bool serialize (hb_serialize_context_t *c,
+		  const OpStr &opstr,
+		  const FontDictValuesMod &mod) const
+  {
+    TRACE_SERIALIZE (this);
+
+    if (opstr.op == OpCode_FontName)
+      return_trace (FontDict::serialize_uint2_op (c, opstr.op, mod.fontName));
+    else
+      return_trace (SUPER::serialize (c, opstr, mod.privateDictInfo));
+  }
+
+  unsigned int calculate_serialized_size (const OpStr &opstr) const
+  {
+    if (opstr.op == OpCode_FontName)
+      return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_FontName);
+    else
+      return SUPER::calculate_serialized_size (opstr);
+  }
+
+  private:
+  typedef CFFFontDict_OpSerializer SUPER;
+};
+
+struct CFF1CSOpSet_Flatten : CFF1CSOpSet<CFF1CSOpSet_Flatten, FlattenParam>
+{
+  static void flush_args_and_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
+  {
+    if (env.arg_start > 0)
+      flush_width (env, param);
+
+    switch (op)
+    {
+      case OpCode_hstem:
+      case OpCode_hstemhm:
+      case OpCode_vstem:
+      case OpCode_vstemhm:
+      case OpCode_hintmask:
+      case OpCode_cntrmask:
+      case OpCode_dotsection:
+	if (param.drop_hints)
+	{
+	  env.clear_args ();
+	  return;
+	}
+	HB_FALLTHROUGH;
+
+      default:
+	SUPER::flush_args_and_op (op, env, param);
+	break;
+    }
+  }
+  static void flush_args (CFF1CSInterpEnv &env, FlattenParam& param)
+  {
+    StrEncoder  encoder (param.flatStr);
+    for (unsigned int i = env.arg_start; i < env.argStack.get_count (); i++)
+      encoder.encode_num (env.eval_arg (i));
+    SUPER::flush_args (env, param);
+  }
+
+  static void flush_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
+  {
+    StrEncoder  encoder (param.flatStr);
+    encoder.encode_op (op);
+  }
+
+  static void flush_width (CFF1CSInterpEnv &env, FlattenParam& param)
+  {
+    assert (env.has_width);
+    StrEncoder  encoder (param.flatStr);
+    encoder.encode_num (env.width);
+  }
+
+  static void flush_hintmask (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
+  {
+    SUPER::flush_hintmask (op, env, param);
+    if (!param.drop_hints)
+    {
+      StrEncoder  encoder (param.flatStr);
+      for (unsigned int i = 0; i < env.hintmask_size; i++)
+	encoder.encode_byte (env.substr[i]);
+    }
+  }
+
+  private:
+  typedef CFF1CSOpSet<CFF1CSOpSet_Flatten, FlattenParam> SUPER;
+};
+
+struct RangeList : hb_vector_t<code_pair>
+{
+  /* replace the first glyph ID in the "glyph" field each range with a nLeft value */
+  bool finalize (unsigned int last_glyph)
+  {
+    bool  two_byte = false;
+    for (unsigned int i = (*this).len; i > 0; i--)
+    {
+      code_pair &pair = (*this)[i - 1];
+      unsigned int  nLeft = last_glyph - pair.glyph - 1;
+      if (nLeft >= 0x100)
+	two_byte = true;
+      last_glyph = pair.glyph;
+      pair.glyph = nLeft;
+    }
+    return two_byte;
+  }
+};
+
+struct CFF1CSOpSet_SubrSubset : CFF1CSOpSet<CFF1CSOpSet_SubrSubset, SubrSubsetParam>
+{
+  static void process_op (OpCode op, CFF1CSInterpEnv &env, SubrSubsetParam& param)
+  {
+    switch (op) {
+
+      case OpCode_return:
+	param.current_parsed_str->add_op (op, env.substr);
+	param.current_parsed_str->set_parsed ();
+	env.returnFromSubr ();
+	param.set_current_str (env, false);
+	break;
+
+      case OpCode_endchar:
+	param.current_parsed_str->add_op (op, env.substr);
+	param.current_parsed_str->set_parsed ();
+	SUPER::process_op (op, env, param);
+	break;
+
+      case OpCode_callsubr:
+	process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
+	break;
+
+      case OpCode_callgsubr:
+	process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
+	break;
+
+      default:
+	SUPER::process_op (op, env, param);
+	param.current_parsed_str->add_op (op, env.substr);
+	break;
+    }
+  }
+
+  protected:
+  static void process_call_subr (OpCode op, CSType type,
+				 CFF1CSInterpEnv &env, SubrSubsetParam& param,
+				 CFF1BiasedSubrs& subrs, hb_set_t *closure)
+  {
+    SubByteStr    substr = env.substr;
+    env.callSubr (subrs, type);
+    param.current_parsed_str->add_call_op (op, substr, env.context.subr_num);
+    hb_set_add (closure, env.context.subr_num);
+    param.set_current_str (env, true);
+  }
+
+  private:
+  typedef CFF1CSOpSet<CFF1CSOpSet_SubrSubset, SubrSubsetParam> SUPER;
+};
+
+struct CFF1SubrSubsetter : SubrSubsetter<CFF1SubrSubsetter, CFF1Subrs, const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_SubrSubset>
+{
+  static void finalize_parsed_str (CFF1CSInterpEnv &env, SubrSubsetParam& param, ParsedCStr &charstring)
+  {
+    /* insert width at the beginning of the charstring as necessary */
+    if (env.has_width)
+      charstring.set_prefix (env.width);
+
+    /* subroutines/charstring left on the call stack are legally left unmarked
+     * unmarked when a subroutine terminates with endchar. mark them.
+     */
+    param.current_parsed_str->set_parsed ();
+    for (unsigned int i = 0; i < env.callStack.get_count (); i++)
+    {
+      ParsedCStr  *parsed_str = param.get_parsed_str_for_context (env.callStack[i]);
+      if (likely (parsed_str != nullptr))
+	parsed_str->set_parsed ();
+      else
+	env.set_error ();
+    }
+  }
+};
+
+struct cff_subset_plan {
+  cff_subset_plan ()
+    : final_size (0),
+      offsets (),
+      orig_fdcount (0),
+      subset_fdcount (1),
+      subset_fdselect_format (0),
+      drop_hints (false),
+      desubroutinize(false)
+  {
+    topdict_sizes.init ();
+    topdict_sizes.resize (1);
+    topdict_mod.init ();
+    subset_fdselect_ranges.init ();
+    fdmap.init ();
+    subset_charstrings.init ();
+    subset_globalsubrs.init ();
+    subset_localsubrs.init ();
+    fontdicts_mod.init ();
+    subset_enc_code_ranges.init ();
+    subset_enc_supp_codes.init ();
+    subset_charset_ranges.init ();
+    sidmap.init ();
+    for (unsigned int i = 0; i < NameDictValues::ValCount; i++)
+      topDictModSIDs[i] = CFF_UNDEF_SID;
+  }
+
+  ~cff_subset_plan ()
+  {
+    topdict_sizes.fini ();
+    topdict_mod.fini ();
+    subset_fdselect_ranges.fini ();
+    fdmap.fini ();
+    subset_charstrings.fini_deep ();
+    subset_globalsubrs.fini_deep ();
+    subset_localsubrs.fini_deep ();
+    fontdicts_mod.fini ();
+    subset_enc_code_ranges.fini ();
+    subset_enc_supp_codes.init ();
+    subset_charset_ranges.fini ();
+    sidmap.fini ();
+    fontdicts_mod.fini ();
+  }
+
+  unsigned int plan_subset_encoding (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
+  {
+    const Encoding *encoding = acc.encoding;
+    unsigned int  size0, size1, supp_size;
+    hb_codepoint_t  code, last_code = CFF_UNDEF_CODE;
+    hb_vector_t<hb_codepoint_t> supp_codes;
+
+    subset_enc_code_ranges.resize (0);
+    supp_size = 0;
+    supp_codes.init ();
+
+    subset_enc_num_codes = plan->glyphs.len - 1;
+    unsigned int glyph;
+    for (glyph = 1; glyph < plan->glyphs.len; glyph++)
+    {
+      hb_codepoint_t  orig_glyph = plan->glyphs[glyph];
+      code = acc.glyph_to_code (orig_glyph);
+      if (code == CFF_UNDEF_CODE)
+      {
+	subset_enc_num_codes = glyph - 1;
+	break;
+      }
+
+      if (code != last_code + 1)
+      {
+	code_pair pair = { code, glyph };
+	subset_enc_code_ranges.push (pair);
+      }
+      last_code = code;
+
+      if (encoding != &Null(Encoding))
+      {
+	hb_codepoint_t  sid = acc.glyph_to_sid (orig_glyph);
+	encoding->get_supplement_codes (sid, supp_codes);
+	for (unsigned int i = 0; i < supp_codes.len; i++)
+	{
+	  code_pair pair = { supp_codes[i], sid };
+	  subset_enc_supp_codes.push (pair);
+	}
+	supp_size += SuppEncoding::static_size * supp_codes.len;
+      }
+    }
+    supp_codes.fini ();
+
+    subset_enc_code_ranges.finalize (glyph);
+
+    assert (subset_enc_num_codes <= 0xFF);
+    size0 = Encoding0::min_size + HBUINT8::static_size * subset_enc_num_codes;
+    size1 = Encoding1::min_size + Encoding1_Range::static_size * subset_enc_code_ranges.len;
+
+    if (size0 < size1)
+      subset_enc_format = 0;
+    else
+      subset_enc_format = 1;
+
+    return Encoding::calculate_serialized_size (
+			subset_enc_format,
+			subset_enc_format? subset_enc_code_ranges.len: subset_enc_num_codes,
+			subset_enc_supp_codes.len);
+  }
+
+  unsigned int plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
+  {
+    unsigned int  size0, size_ranges;
+    hb_codepoint_t  sid, last_sid = CFF_UNDEF_CODE;
+
+    subset_charset_ranges.resize (0);
+    unsigned int glyph;
+    for (glyph = 1; glyph < plan->glyphs.len; glyph++)
+    {
+      hb_codepoint_t  orig_glyph = plan->glyphs[glyph];
+      sid = acc.glyph_to_sid (orig_glyph);
+
+      if (!acc.is_CID ())
+	sid = sidmap.add (sid);
+
+      if (sid != last_sid + 1)
+      {
+	code_pair pair = { sid, glyph };
+	subset_charset_ranges.push (pair);
+      }
+      last_sid = sid;
+    }
+
+    bool two_byte = subset_charset_ranges.finalize (glyph);
+
+    size0 = Charset0::min_size + HBUINT16::static_size * (plan->glyphs.len - 1);
+    if (!two_byte)
+      size_ranges = Charset1::min_size + Charset1_Range::static_size * subset_charset_ranges.len;
+    else
+      size_ranges = Charset2::min_size + Charset2_Range::static_size * subset_charset_ranges.len;
+
+    if (size0 < size_ranges)
+      subset_charset_format = 0;
+    else if (!two_byte)
+      subset_charset_format = 1;
+    else
+      subset_charset_format = 2;
+
+    return Charset::calculate_serialized_size (
+			subset_charset_format,
+			subset_charset_format? subset_charset_ranges.len: plan->glyphs.len);
+  }
+
+  bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc)
+  {
+    if (unlikely (!sidmap.reset (acc.stringIndex->count)))
+      return false;
+
+    for (unsigned int i = 0; i < NameDictValues::ValCount; i++)
+    {
+      unsigned int sid = acc.topDict.nameSIDs[i];
+      if (sid != CFF_UNDEF_SID)
+      {
+	(void)sidmap.add (sid);
+	topDictModSIDs[i] = sidmap[sid];
+      }
+    }
+
+    if (acc.fdArray != &Null(CFF1FDArray))
+      for (unsigned int i = 0; i < orig_fdcount; i++)
+	if (fdmap.includes (i))
+	  (void)sidmap.add (acc.fontDicts[i].fontName);
+
+    return true;
+  }
+
+  bool create (const OT::cff1::accelerator_subset_t &acc,
+		      hb_subset_plan_t *plan)
+  {
+     /* make sure notdef is first */
+    if ((plan->glyphs.len == 0) || (plan->glyphs[0] != 0)) return false;
+
+    final_size = 0;
+    num_glyphs = plan->glyphs.len;
+    orig_fdcount = acc.fdCount;
+    drop_hints = plan->drop_hints;
+    desubroutinize = plan->desubroutinize;
+
+    /* check whether the subset renumbers any glyph IDs */
+    gid_renum = false;
+    for (unsigned int glyph = 0; glyph < plan->glyphs.len; glyph++)
+    {
+      if (plan->glyphs[glyph] != glyph) {
+	gid_renum = true;
+	break;
+      }
+    }
+
+    subset_charset = gid_renum || !acc.is_predef_charset ();
+    subset_encoding = !acc.is_CID() && !acc.is_predef_encoding ();
+
+    /* CFF header */
+    final_size += OT::cff1::static_size;
+
+    /* Name INDEX */
+    offsets.nameIndexOffset = final_size;
+    final_size += acc.nameIndex->get_size ();
+
+    /* top dict INDEX */
+    {
+      /* Add encoding/charset to a (copy of) top dict as necessary */
+      topdict_mod.init (&acc.topDict);
+      bool need_to_add_enc = (subset_encoding && !acc.topDict.has_op (OpCode_Encoding));
+      bool need_to_add_set = (subset_charset && !acc.topDict.has_op (OpCode_charset));
+      if (need_to_add_enc || need_to_add_set)
+      {
+	if (need_to_add_enc)
+	  topdict_mod.add_op (OpCode_Encoding);
+	if (need_to_add_set)
+	  topdict_mod.add_op (OpCode_charset);
+      }
+      offsets.topDictInfo.offset = final_size;
+      CFF1TopDict_OpSerializer topSzr;
+      unsigned int topDictSize = TopDict::calculate_serialized_size (topdict_mod, topSzr);
+      offsets.topDictInfo.offSize = calcOffSize(topDictSize);
+      if (unlikely (offsets.topDictInfo.offSize > 4))
+      	return false;
+      final_size += CFF1IndexOf<TopDict>::calculate_serialized_size<CFF1TopDictValuesMod>
+						(offsets.topDictInfo.offSize,
+						 &topdict_mod, 1, topdict_sizes, topSzr);
+    }
+
+    /* Determine re-mapping of font index as fdmap among other info */
+    if (acc.fdSelect != &Null(CFF1FDSelect))
+    {
+	if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
+				  orig_fdcount,
+				  *acc.fdSelect,
+				  subset_fdcount,
+				  offsets.FDSelectInfo.size,
+				  subset_fdselect_format,
+				  subset_fdselect_ranges,
+				  fdmap)))
+	return false;
+    }
+    else
+      fdmap.identity (1);
+
+    /* remove unused SIDs & reassign SIDs */
+    {
+      /* SIDs for name strings in dicts are added before glyph names so they fit in 16-bit int range */
+      if (unlikely (!collect_sids_in_dicts (acc)))
+	return false;
+      if (unlikely (sidmap.get_count () > 0x8000))	/* assumption: a dict won't reference that many strings */
+      	return false;
+      if (subset_charset)
+	offsets.charsetInfo.size = plan_subset_charset (acc, plan);
+
+      topdict_mod.reassignSIDs (sidmap);
+    }
+
+    /* String INDEX */
+    {
+      offsets.stringIndexInfo.offset = final_size;
+      offsets.stringIndexInfo.size = acc.stringIndex->calculate_serialized_size (offsets.stringIndexInfo.offSize, sidmap);
+      final_size += offsets.stringIndexInfo.size;
+    }
+
+    if (desubroutinize)
+    {
+      /* Flatten global & local subrs */
+      SubrFlattener<const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_Flatten>
+		    flattener(acc, plan->glyphs, plan->drop_hints);
+      if (!flattener.flatten (subset_charstrings))
+	return false;
+
+      /* no global/local subroutines */
+      offsets.globalSubrsInfo.size = CFF1Subrs::calculate_serialized_size (1, 0, 0);
+    }
+    else
+    {
+      /* Subset subrs: collect used subroutines, leaving all unused ones behind */
+      if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints))
+	return false;
+
+      /* encode charstrings, global subrs, local subrs with new subroutine numbers */
+      if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings))
+	return false;
+
+      if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
+	return false;
+
+      /* global subrs */
+      unsigned int dataSize = subset_globalsubrs.total_size ();
+      offsets.globalSubrsInfo.offSize = calcOffSize (dataSize);
+      if (unlikely (offsets.globalSubrsInfo.offSize > 4))
+      	return false;
+      offsets.globalSubrsInfo.size = CFF1Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.len, dataSize);
+
+      /* local subrs */
+      if (!offsets.localSubrsInfos.resize (orig_fdcount))
+	return false;
+      if (!subset_localsubrs.resize (orig_fdcount))
+	return false;
+      for (unsigned int fd = 0; fd < orig_fdcount; fd++)
+      {
+	subset_localsubrs[fd].init ();
+	offsets.localSubrsInfos[fd].init ();
+	if (fdmap.includes (fd))
+	{
+	  if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
+	    return false;
+
+	  unsigned int dataSize = subset_localsubrs[fd].total_size ();
+	  if (dataSize > 0)
+	  {
+	    offsets.localSubrsInfos[fd].offset = final_size;
+	    offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize);
+	    if (unlikely (offsets.localSubrsInfos[fd].offSize > 4))
+	      return false;
+	    offsets.localSubrsInfos[fd].size = CFF1Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].len, dataSize);
+	  }
+	}
+      }
+    }
+
+    /* global subrs */
+    offsets.globalSubrsInfo.offset = final_size;
+    final_size += offsets.globalSubrsInfo.size;
+
+    /* Encoding */
+    if (!subset_encoding)
+      offsets.encodingOffset = acc.topDict.EncodingOffset;
+    else
+    {
+      offsets.encodingOffset = final_size;
+      final_size += plan_subset_encoding (acc, plan);
+    }
+
+    /* Charset */
+    if (!subset_charset && acc.is_predef_charset ())
+      offsets.charsetInfo.offset = acc.topDict.CharsetOffset;
+    else
+      offsets.charsetInfo.offset = final_size;
+    final_size += offsets.charsetInfo.size;
+
+    /* FDSelect */
+    if (acc.fdSelect != &Null(CFF1FDSelect))
+    {
+      offsets.FDSelectInfo.offset = final_size;
+      final_size += offsets.FDSelectInfo.size;
+    }
+
+    /* FDArray (FDIndex) */
+    if (acc.fdArray != &Null(CFF1FDArray)) {
+      offsets.FDArrayInfo.offset = final_size;
+      CFF1FontDict_OpSerializer fontSzr;
+      unsigned int dictsSize = 0;
+      for (unsigned int i = 0; i < acc.fontDicts.len; i++)
+	if (fdmap.includes (i))
+	  dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr);
+
+      offsets.FDArrayInfo.offSize = calcOffSize (dictsSize);
+      if (unlikely (offsets.FDArrayInfo.offSize > 4))
+      	return false;
+      final_size += CFF1Index::calculate_serialized_size (offsets.FDArrayInfo.offSize, subset_fdcount, dictsSize);
+    }
+
+    /* CharStrings */
+    {
+      offsets.charStringsInfo.offset = final_size;
+      unsigned int dataSize = subset_charstrings.total_size ();
+      offsets.charStringsInfo.offSize = calcOffSize (dataSize);
+      if (unlikely (offsets.charStringsInfo.offSize > 4))
+      	return false;
+      final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
+    }
+
+    /* private dicts & local subrs */
+    offsets.privateDictInfo.offset = final_size;
+    for (unsigned int i = 0; i < orig_fdcount; i++)
+    {
+      if (fdmap.includes (i))
+      {
+	bool  has_localsubrs = offsets.localSubrsInfos[i].size > 0;
+	CFFPrivateDict_OpSerializer privSzr (desubroutinize, plan->drop_hints);
+	unsigned int  priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr, has_localsubrs);
+	TableInfo  privInfo = { final_size, priv_size, 0 };
+	FontDictValuesMod fontdict_mod;
+	if (!acc.is_CID ())
+	  fontdict_mod.init ( &Null(CFF1FontDictValues), CFF_UNDEF_SID, privInfo );
+	else
+	  fontdict_mod.init ( &acc.fontDicts[i], sidmap[acc.fontDicts[i].fontName], privInfo );
+	fontdicts_mod.push (fontdict_mod);
+	final_size += privInfo.size;
+
+	if (!plan->desubroutinize && has_localsubrs)
+	{
+	  offsets.localSubrsInfos[i].offset = final_size;
+	  final_size += offsets.localSubrsInfos[i].size;
+	}
+      }
+    }
+
+    if (!acc.is_CID ())
+      offsets.privateDictInfo = fontdicts_mod[0].privateDictInfo;
+
+    return ((subset_charstrings.len == plan->glyphs.len)
+	   && (fontdicts_mod.len == subset_fdcount));
+  }
+
+  unsigned int get_final_size () const  { return final_size; }
+
+  unsigned int	      final_size;
+  hb_vector_t<unsigned int> topdict_sizes;
+  CFF1TopDictValuesMod      topdict_mod;
+  CFF1SubTableOffsets       offsets;
+
+  unsigned int    num_glyphs;
+  unsigned int    orig_fdcount;
+  unsigned int    subset_fdcount;
+  unsigned int    subset_fdselect_format;
+  hb_vector_t<code_pair>   subset_fdselect_ranges;
+
+  /* font dict index remap table from fullset FDArray to subset FDArray.
+   * set to CFF_UNDEF_CODE if excluded from subset */
+  Remap   fdmap;
+
+  StrBuffArray	    subset_charstrings;
+  StrBuffArray	    subset_globalsubrs;
+  hb_vector_t<StrBuffArray> subset_localsubrs;
+  hb_vector_t<FontDictValuesMod>  fontdicts_mod;
+
+  bool		    drop_hints;
+
+  bool		    gid_renum;
+  bool		    subset_encoding;
+  uint8_t		 subset_enc_format;
+  unsigned int	    subset_enc_num_codes;
+  RangeList	       subset_enc_code_ranges;
+  hb_vector_t<code_pair>  subset_enc_supp_codes;
+
+  uint8_t		 subset_charset_format;
+  RangeList	       subset_charset_ranges;
+  bool		    subset_charset;
+
+  RemapSID		sidmap;
+  unsigned int	    topDictModSIDs[NameDictValues::ValCount];
+
+  bool		    desubroutinize;
+  CFF1SubrSubsetter       subr_subsetter;
+};
+
+static inline bool _write_cff1 (const cff_subset_plan &plan,
+				const OT::cff1::accelerator_subset_t  &acc,
+				const hb_vector_t<hb_codepoint_t>& glyphs,
+				unsigned int dest_sz,
+				void *dest)
+{
+  hb_serialize_context_t c (dest, dest_sz);
+
+  char RETURN_OP[1] = { OpCode_return };
+  const ByteStr NULL_SUBR (RETURN_OP, 1);
+
+  OT::cff1 *cff = c.start_serialize<OT::cff1> ();
+  if (unlikely (!c.extend_min (*cff)))
+    return false;
+
+  /* header */
+  cff->version.major.set (0x01);
+  cff->version.minor.set (0x00);
+  cff->nameIndex.set (cff->min_size);
+  cff->offSize.set (4); /* unused? */
+
+  /* name INDEX */
+  {
+    assert (cff->nameIndex == c.head - c.start);
+    CFF1NameIndex *dest = c.start_embed<CFF1NameIndex> ();
+    if (unlikely (dest == nullptr)) return false;
+    if (unlikely (!dest->serialize (&c, *acc.nameIndex)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF name INDEX");
+      return false;
+    }
+  }
+
+  /* top dict INDEX */
+  {
+    assert (plan.offsets.topDictInfo.offset == c.head - c.start);
+    CFF1IndexOf<TopDict> *dest = c.start_embed< CFF1IndexOf<TopDict> > ();
+    if (dest == nullptr) return false;
+    CFF1TopDict_OpSerializer topSzr;
+    TopDictModifiers  modifier (plan.offsets, plan.topDictModSIDs);
+    if (unlikely (!dest->serialize (&c, plan.offsets.topDictInfo.offSize,
+				    &plan.topdict_mod, 1,
+				    plan.topdict_sizes, topSzr, modifier)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF top dict");
+      return false;
+    }
+  }
+
+  /* String INDEX */
+  {
+    assert (plan.offsets.stringIndexInfo.offset == c.head - c.start);
+    CFF1StringIndex *dest = c.start_embed<CFF1StringIndex> ();
+    if (unlikely (dest == nullptr)) return false;
+    if (unlikely (!dest->serialize (&c, *acc.stringIndex, plan.offsets.stringIndexInfo.offSize, plan.sidmap)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF string INDEX");
+      return false;
+    }
+  }
+
+  /* global subrs */
+  {
+    assert (plan.offsets.globalSubrsInfo.offset != 0);
+    assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start);
+
+    CFF1Subrs *dest = c.start_embed <CFF1Subrs> ();
+    if (unlikely (dest == nullptr)) return false;
+    if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines");
+      return false;
+    }
+  }
+
+  /* Encoding */
+  if (plan.subset_encoding)
+  {
+    assert (plan.offsets.encodingOffset == c.head - c.start);
+    Encoding *dest = c.start_embed<Encoding> ();
+    if (unlikely (dest == nullptr)) return false;
+    if (unlikely (!dest->serialize (&c,
+				    plan.subset_enc_format,
+				    plan.subset_enc_num_codes,
+				    plan.subset_enc_code_ranges,
+				    plan.subset_enc_supp_codes)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize Encoding");
+      return false;
+    }
+  }
+
+  /* Charset */
+  if (plan.subset_charset)
+  {
+    assert (plan.offsets.charsetInfo.offset == c.head - c.start);
+    Charset *dest = c.start_embed<Charset> ();
+    if (unlikely (dest == nullptr)) return false;
+    if (unlikely (!dest->serialize (&c,
+				    plan.subset_charset_format,
+				    plan.num_glyphs,
+				    plan.subset_charset_ranges)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize Charset");
+      return false;
+    }
+  }
+
+  /* FDSelect */
+  if (acc.fdSelect != &Null(CFF1FDSelect))
+  {
+    assert (plan.offsets.FDSelectInfo.offset == c.head - c.start);
+
+    if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs.len, *acc.fdSelect, acc.fdCount,
+					      plan.subset_fdselect_format, plan.offsets.FDSelectInfo.size,
+					      plan.subset_fdselect_ranges)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF subset FDSelect");
+      return false;
+    }
+  }
+
+  /* FDArray (FD Index) */
+  if (acc.fdArray != &Null(CFF1FDArray))
+  {
+    assert (plan.offsets.FDArrayInfo.offset == c.head - c.start);
+    CFF1FDArray  *fda = c.start_embed<CFF1FDArray> ();
+    if (unlikely (fda == nullptr)) return false;
+    CFF1FontDict_OpSerializer  fontSzr;
+    if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
+				   plan.fontdicts_mod,
+				   fontSzr)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF FDArray");
+      return false;
+    }
+  }
+
+  /* CharStrings */
+  {
+    assert (plan.offsets.charStringsInfo.offset == c.head - c.start);
+    CFF1CharStrings  *cs = c.start_embed<CFF1CharStrings> ();
+    if (unlikely (cs == nullptr)) return false;
+    if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF CharStrings");
+      return false;
+    }
+  }
+
+  /* private dicts & local subrs */
+  assert (plan.offsets.privateDictInfo.offset == c.head - c.start);
+  for (unsigned int i = 0; i < acc.privateDicts.len; i++)
+  {
+    if (plan.fdmap.includes (i))
+    {
+      PrivateDict  *pd = c.start_embed<PrivateDict> ();
+      if (unlikely (pd == nullptr)) return false;
+      unsigned int priv_size = plan.fontdicts_mod[plan.fdmap[i]].privateDictInfo.size;
+      bool result;
+      CFFPrivateDict_OpSerializer privSzr (plan.desubroutinize, plan.drop_hints);
+      /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
+      unsigned int  subroffset = (plan.offsets.localSubrsInfos[i].size > 0)? priv_size: 0;
+      result = pd->serialize (&c, acc.privateDicts[i], privSzr, subroffset);
+      if (unlikely (!result))
+      {
+	DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i);
+	return false;
+      }
+      if (plan.offsets.localSubrsInfos[i].size > 0)
+      {
+	CFF1Subrs *dest = c.start_embed <CFF1Subrs> ();
+	if (unlikely (dest == nullptr)) return false;
+	if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i])))
+	{
+	  DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines");
+	  return false;
+	}
+      }
+    }
+  }
+
+  assert (c.head == c.end);
+  c.end_serialize ();
+
+  return true;
+}
+
+static bool
+_hb_subset_cff1 (const OT::cff1::accelerator_subset_t  &acc,
+		const char		      *data,
+		hb_subset_plan_t		*plan,
+		hb_blob_t		       **prime /* OUT */)
+{
+  cff_subset_plan cff_plan;
+
+  if (unlikely (!cff_plan.create (acc, plan)))
+  {
+    DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan.");
+    return false;
+  }
+
+  unsigned int  cff_prime_size = cff_plan.get_final_size ();
+  char *cff_prime_data = (char *) calloc (1, cff_prime_size);
+
+  if (unlikely (!_write_cff1 (cff_plan, acc, plan->glyphs,
+			      cff_prime_size, cff_prime_data))) {
+    DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff.");
+    free (cff_prime_data);
+    return false;
+  }
+
+  *prime = hb_blob_create (cff_prime_data,
+			   cff_prime_size,
+			   HB_MEMORY_MODE_READONLY,
+			   cff_prime_data,
+			   free);
+  return true;
+}
+
+/**
+ * hb_subset_cff1:
+ * Subsets the CFF table according to a provided plan.
+ *
+ * Return value: subsetted cff table.
+ **/
+bool
+hb_subset_cff1 (hb_subset_plan_t *plan,
+		hb_blob_t       **prime /* OUT */)
+{
+  hb_blob_t *cff_blob = hb_sanitize_context_t().reference_table<CFF::cff1> (plan->source);
+  const char *data = hb_blob_get_data(cff_blob, nullptr);
+
+  OT::cff1::accelerator_subset_t acc;
+  acc.init(plan->source);
+  bool result = likely (acc.is_valid ()) &&
+			_hb_subset_cff1 (acc, data, plan, prime);
+  hb_blob_destroy (cff_blob);
+  acc.fini ();
+
+  return result;
+}
diff --git a/src/hb-subset-cff1.hh b/src/hb-subset-cff1.hh
new file mode 100644
index 0000000..1ec8678
--- /dev/null
+++ b/src/hb-subset-cff1.hh
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_SUBSET_CFF1_HH
+#define HB_SUBSET_CFF1_HH
+
+#include "hb.hh"
+
+#include "hb-subset-plan.hh"
+
+HB_INTERNAL bool
+hb_subset_cff1 (hb_subset_plan_t *plan,
+	       hb_blob_t	**cff_prime /* OUT */);
+
+#endif /* HB_SUBSET_CFF1_HH */
diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc
new file mode 100644
index 0000000..5a5f29d
--- /dev/null
+++ b/src/hb-subset-cff2.cc
@@ -0,0 +1,624 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#include "hb-open-type.hh"
+#include "hb-ot-cff2-table.hh"
+#include "hb-set.h"
+#include "hb-subset-cff2.hh"
+#include "hb-subset-plan.hh"
+#include "hb-subset-cff-common.hh"
+#include "hb-cff2-interp-cs.hh"
+
+using namespace CFF;
+
+struct CFF2SubTableOffsets : CFFSubTableOffsets
+{
+  CFF2SubTableOffsets ()
+    : CFFSubTableOffsets (),
+      varStoreOffset (0)
+  {}
+
+  unsigned int  varStoreOffset;
+};
+
+struct CFF2TopDict_OpSerializer : CFFTopDict_OpSerializer<>
+{
+  bool serialize (hb_serialize_context_t *c,
+		  const OpStr &opstr,
+		  const CFF2SubTableOffsets &offsets) const
+  {
+    TRACE_SERIALIZE (this);
+
+    switch (opstr.op)
+    {
+      case OpCode_vstore:
+	return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.varStoreOffset));
+
+      default:
+	return_trace (CFFTopDict_OpSerializer<>::serialize (c, opstr, offsets));
+    }
+  }
+
+  unsigned int calculate_serialized_size (const OpStr &opstr) const
+  {
+    switch (opstr.op)
+    {
+      case OpCode_vstore:
+	return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
+
+      default:
+	return CFFTopDict_OpSerializer<>::calculate_serialized_size (opstr);
+    }
+  }
+};
+
+struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
+{
+  static void flush_args_and_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param)
+  {
+    switch (op)
+    {
+      case OpCode_return:
+      case OpCode_endchar:
+	/* dummy opcodes in CFF2. ignore */
+	break;
+
+      case OpCode_hstem:
+      case OpCode_hstemhm:
+      case OpCode_vstem:
+      case OpCode_vstemhm:
+      case OpCode_hintmask:
+      case OpCode_cntrmask:
+	if (param.drop_hints)
+	{
+	  env.clear_args ();
+	  return;
+	}
+	HB_FALLTHROUGH;
+
+      default:
+	SUPER::flush_args_and_op (op, env, param);
+	break;
+    }
+  }
+
+  static void flush_args (CFF2CSInterpEnv &env, FlattenParam& param)
+  {
+    for (unsigned int i = 0; i < env.argStack.get_count ();)
+    {
+      const BlendArg &arg = env.argStack[i];
+      if (arg.blending ())
+      {
+      	if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues))))
+      	{
+	  env.set_error ();
+	  return;
+	}
+	flatten_blends (arg, i, env, param);
+	i += arg.numValues;
+      }
+      else
+      {
+	StrEncoder  encoder (param.flatStr);
+	encoder.encode_num (arg);
+	i++;
+      }
+    }
+    SUPER::flush_args (env, param);
+  }
+
+  static void flatten_blends (const BlendArg &arg, unsigned int i, CFF2CSInterpEnv &env, FlattenParam& param)
+  {
+    /* flatten the default values */
+    StrEncoder  encoder (param.flatStr);
+    for (unsigned int j = 0; j < arg.numValues; j++)
+    {
+      const BlendArg &arg1 = env.argStack[i + j];
+      if (unlikely (!((arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) &&
+	      (arg1.deltas.len == env.get_region_count ())))))
+      {
+      	env.set_error ();
+      	return;
+      }
+      encoder.encode_num (arg1);
+    }
+    /* flatten deltas for each value */
+    for (unsigned int j = 0; j < arg.numValues; j++)
+    {
+      const BlendArg &arg1 = env.argStack[i + j];
+      for (unsigned int k = 0; k < arg1.deltas.len; k++)
+	encoder.encode_num (arg1.deltas[k]);
+    }
+    /* flatten the number of values followed by blend operator */
+    encoder.encode_int (arg.numValues);
+    encoder.encode_op (OpCode_blendcs);
+  }
+
+  static void flush_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param)
+  {
+    switch (op)
+    {
+      case OpCode_return:
+      case OpCode_endchar:
+	return;
+      default:
+	StrEncoder  encoder (param.flatStr);
+	encoder.encode_op (op);
+    }
+  }
+
+  private:
+  typedef CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam> SUPER;
+  typedef CSOpSet<BlendArg, CFF2CSOpSet_Flatten, CFF2CSOpSet_Flatten, CFF2CSInterpEnv, FlattenParam> CSOPSET;
+};
+
+struct CFF2CSOpSet_SubrSubset : CFF2CSOpSet<CFF2CSOpSet_SubrSubset, SubrSubsetParam>
+{
+  static void process_op (OpCode op, CFF2CSInterpEnv &env, SubrSubsetParam& param)
+  {
+    switch (op) {
+
+      case OpCode_return:
+	param.current_parsed_str->set_parsed ();
+	env.returnFromSubr ();
+	param.set_current_str (env, false);
+	break;
+
+      case OpCode_endchar:
+	param.current_parsed_str->set_parsed ();
+	SUPER::process_op (op, env, param);
+	break;
+
+      case OpCode_callsubr:
+	process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
+	break;
+
+      case OpCode_callgsubr:
+	process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
+	break;
+
+      default:
+	SUPER::process_op (op, env, param);
+	param.current_parsed_str->add_op (op, env.substr);
+	break;
+    }
+  }
+
+  protected:
+  static void process_call_subr (OpCode op, CSType type,
+				 CFF2CSInterpEnv &env, SubrSubsetParam& param,
+				 CFF2BiasedSubrs& subrs, hb_set_t *closure)
+  {
+    SubByteStr    substr = env.substr;
+    env.callSubr (subrs, type);
+    param.current_parsed_str->add_call_op (op, substr, env.context.subr_num);
+    hb_set_add (closure, env.context.subr_num);
+    param.set_current_str (env, true);
+  }
+
+  private:
+  typedef CFF2CSOpSet<CFF2CSOpSet_SubrSubset, SubrSubsetParam> SUPER;
+};
+
+struct CFF2SubrSubsetter : SubrSubsetter<CFF2SubrSubsetter, CFF2Subrs, const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_SubrSubset>
+{
+  static void finalize_parsed_str (CFF2CSInterpEnv &env, SubrSubsetParam& param, ParsedCStr &charstring)
+  {
+    /* vsindex is inserted at the beginning of the charstring as necessary */
+    if (env.seen_vsindex ())
+    {
+      Number  ivs;
+      ivs.set_int ((int)env.get_ivs ());
+      charstring.set_prefix (ivs, OpCode_vsindexcs);
+    }
+  }
+};
+
+struct cff2_subset_plan {
+  cff2_subset_plan ()
+    : final_size (0),
+      orig_fdcount (0),
+      subset_fdcount(1),
+      subset_fdselect_format (0),
+      drop_hints (false),
+      desubroutinize (false)
+  {
+    subset_fdselect_ranges.init ();
+    fdmap.init ();
+    subset_charstrings.init ();
+    subset_globalsubrs.init ();
+    subset_localsubrs.init ();
+    privateDictInfos.init ();
+  }
+
+  ~cff2_subset_plan ()
+  {
+    subset_fdselect_ranges.fini ();
+    fdmap.fini ();
+    subset_charstrings.fini_deep ();
+    subset_globalsubrs.fini_deep ();
+    subset_localsubrs.fini_deep ();
+    privateDictInfos.fini ();
+  }
+
+  bool create (const OT::cff2::accelerator_subset_t &acc,
+	      hb_subset_plan_t *plan)
+  {
+    final_size = 0;
+    orig_fdcount = acc.fdArray->count;
+
+    drop_hints = plan->drop_hints;
+    desubroutinize = plan->desubroutinize;
+
+    /* CFF2 header */
+    final_size += OT::cff2::static_size;
+
+    /* top dict */
+    {
+      CFF2TopDict_OpSerializer topSzr;
+      offsets.topDictInfo.size = TopDict::calculate_serialized_size (acc.topDict, topSzr);
+      final_size += offsets.topDictInfo.size;
+    }
+
+    if (desubroutinize)
+    {
+      /* Flatten global & local subrs */
+      SubrFlattener<const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_Flatten>
+		    flattener(acc, plan->glyphs, plan->drop_hints);
+      if (!flattener.flatten (subset_charstrings))
+	return false;
+
+      /* no global/local subroutines */
+      offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (1, 0, 0);
+    }
+    else
+    {
+      /* Subset subrs: collect used subroutines, leaving all unused ones behind */
+      if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints))
+	return false;
+
+      /* encode charstrings, global subrs, local subrs with new subroutine numbers */
+      if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings))
+	return false;
+
+      if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
+	return false;
+
+      /* global subrs */
+      unsigned int dataSize = subset_globalsubrs.total_size ();
+      offsets.globalSubrsInfo.offSize = calcOffSize (dataSize);
+      offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.len, dataSize);
+
+      /* local subrs */
+      if (!offsets.localSubrsInfos.resize (orig_fdcount))
+	return false;
+      if (!subset_localsubrs.resize (orig_fdcount))
+	return false;
+      for (unsigned int fd = 0; fd < orig_fdcount; fd++)
+      {
+	subset_localsubrs[fd].init ();
+	offsets.localSubrsInfos[fd].init ();
+	if (fdmap.includes (fd))
+	{
+	  if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
+	    return false;
+
+	  unsigned int dataSize = subset_localsubrs[fd].total_size ();
+	  if (dataSize > 0)
+	  {
+	    offsets.localSubrsInfos[fd].offset = final_size;
+	    offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize);
+	    offsets.localSubrsInfos[fd].size = CFF2Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].len, dataSize);
+	  }
+	}
+      }
+    }
+
+    /* global subrs */
+    offsets.globalSubrsInfo.offset = final_size;
+    final_size += offsets.globalSubrsInfo.size;
+
+    /* variation store */
+    if (acc.varStore != &Null(CFF2VariationStore))
+    {
+      offsets.varStoreOffset = final_size;
+      final_size += acc.varStore->get_size ();
+    }
+
+    /* FDSelect */
+    if (acc.fdSelect != &Null(CFF2FDSelect))
+    {
+      offsets.FDSelectInfo.offset = final_size;
+      if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
+				  orig_fdcount,
+				  *(const FDSelect *)acc.fdSelect,
+				  subset_fdcount,
+				  offsets.FDSelectInfo.size,
+				  subset_fdselect_format,
+				  subset_fdselect_ranges,
+				  fdmap)))
+	return false;
+
+      final_size += offsets.FDSelectInfo.size;
+    }
+    else
+      fdmap.identity (1);
+
+    /* FDArray (FDIndex) */
+    {
+      offsets.FDArrayInfo.offset = final_size;
+      CFFFontDict_OpSerializer fontSzr;
+      unsigned int dictsSize = 0;
+      for (unsigned int i = 0; i < acc.fontDicts.len; i++)
+	if (fdmap.includes (i))
+	  dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr);
+
+      offsets.FDArrayInfo.offSize = calcOffSize (dictsSize);
+      final_size += CFF2Index::calculate_serialized_size (offsets.FDArrayInfo.offSize, subset_fdcount, dictsSize);
+    }
+
+    /* CharStrings */
+    {
+      offsets.charStringsInfo.offset = final_size;
+      unsigned int dataSize = subset_charstrings.total_size ();
+      offsets.charStringsInfo.offSize = calcOffSize (dataSize);
+      final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
+    }
+
+    /* private dicts & local subrs */
+    offsets.privateDictsOffset = final_size;
+    for (unsigned int i = 0; i < orig_fdcount; i++)
+    {
+      if (fdmap.includes (i))
+      {
+	bool  has_localsubrs = offsets.localSubrsInfos[i].size > 0;
+	CFFPrivateDict_OpSerializer privSzr (desubroutinize, drop_hints);
+	unsigned int  priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr, has_localsubrs);
+	TableInfo  privInfo = { final_size, priv_size, 0 };
+	privateDictInfos.push (privInfo);
+	final_size += privInfo.size;
+
+	if (!plan->desubroutinize && has_localsubrs)
+	{
+	  offsets.localSubrsInfos[i].offset = final_size;
+	  final_size += offsets.localSubrsInfos[i].size;
+	}
+      }
+    }
+
+    return true;
+  }
+
+  unsigned int get_final_size () const  { return final_size; }
+
+  unsigned int	final_size;
+  CFF2SubTableOffsets offsets;
+
+  unsigned int    orig_fdcount;
+  unsigned int    subset_fdcount;
+  unsigned int    subset_fdselect_format;
+  hb_vector_t<code_pair>   subset_fdselect_ranges;
+
+  Remap   fdmap;
+
+  StrBuffArray	    subset_charstrings;
+  StrBuffArray	    subset_globalsubrs;
+  hb_vector_t<StrBuffArray> subset_localsubrs;
+  hb_vector_t<TableInfo>  privateDictInfos;
+
+  bool	    drop_hints;
+  bool	    desubroutinize;
+  CFF2SubrSubsetter       subr_subsetter;
+};
+
+static inline bool _write_cff2 (const cff2_subset_plan &plan,
+				const OT::cff2::accelerator_subset_t  &acc,
+				const hb_vector_t<hb_codepoint_t>& glyphs,
+				unsigned int dest_sz,
+				void *dest)
+{
+  hb_serialize_context_t c (dest, dest_sz);
+
+  OT::cff2 *cff2 = c.start_serialize<OT::cff2> ();
+  if (unlikely (!c.extend_min (*cff2)))
+    return false;
+
+  /* header */
+  cff2->version.major.set (0x02);
+  cff2->version.minor.set (0x00);
+  cff2->topDict.set (OT::cff2::static_size);
+
+  /* top dict */
+  {
+    assert (cff2->topDict == c.head - c.start);
+    cff2->topDictSize.set (plan.offsets.topDictInfo.size);
+    TopDict &dict = cff2 + cff2->topDict;
+    CFF2TopDict_OpSerializer topSzr;
+    if (unlikely (!dict.serialize (&c, acc.topDict, topSzr, plan.offsets)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 top dict");
+      return false;
+    }
+  }
+
+  /* global subrs */
+  {
+    assert (cff2->topDict + plan.offsets.topDictInfo.size == c.head - c.start);
+    CFF2Subrs *dest = c.start_embed <CFF2Subrs> ();
+    if (unlikely (dest == nullptr)) return false;
+    if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines");
+      return false;
+    }
+  }
+
+  /* variation store */
+  if (acc.varStore != &Null(CFF2VariationStore))
+  {
+    assert (plan.offsets.varStoreOffset == c.head - c.start);
+    CFF2VariationStore *dest = c.start_embed<CFF2VariationStore> ();
+    if (unlikely (!dest->serialize (&c, acc.varStore)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Variation Store");
+      return false;
+    }
+  }
+
+  /* FDSelect */
+  if (acc.fdSelect != &Null(CFF2FDSelect))
+  {
+    assert (plan.offsets.FDSelectInfo.offset == c.head - c.start);
+
+    if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs.len, *(const FDSelect *)acc.fdSelect, acc.fdArray->count,
+					      plan.subset_fdselect_format, plan.offsets.FDSelectInfo.size,
+					      plan.subset_fdselect_ranges)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 subset FDSelect");
+      return false;
+    }
+  }
+
+  /* FDArray (FD Index) */
+  {
+    assert (plan.offsets.FDArrayInfo.offset == c.head - c.start);
+    CFF2FDArray  *fda = c.start_embed<CFF2FDArray> ();
+    if (unlikely (fda == nullptr)) return false;
+    CFFFontDict_OpSerializer  fontSzr;
+    if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
+				   acc.fontDicts, plan.subset_fdcount, plan.fdmap,
+				   fontSzr, plan.privateDictInfos)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDArray");
+      return false;
+    }
+  }
+
+  /* CharStrings */
+  {
+    assert (plan.offsets.charStringsInfo.offset == c.head - c.start);
+    CFF2CharStrings  *cs = c.start_embed<CFF2CharStrings> ();
+    if (unlikely (cs == nullptr)) return false;
+    if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 CharStrings");
+      return false;
+    }
+  }
+
+  /* private dicts & local subrs */
+  assert (plan.offsets.privateDictsOffset == c.head - c.start);
+  for (unsigned int i = 0; i < acc.privateDicts.len; i++)
+  {
+    if (plan.fdmap.includes (i))
+    {
+      PrivateDict  *pd = c.start_embed<PrivateDict> ();
+      if (unlikely (pd == nullptr)) return false;
+      unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size;
+      bool result;
+      CFFPrivateDict_OpSerializer privSzr (plan.desubroutinize, plan.drop_hints);
+      /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
+      unsigned int  subroffset = (plan.offsets.localSubrsInfos[i].size > 0)? priv_size: 0;
+      result = pd->serialize (&c, acc.privateDicts[i], privSzr, subroffset);
+      if (unlikely (!result))
+      {
+	DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i);
+	return false;
+      }
+      if (plan.offsets.localSubrsInfos[i].size > 0)
+      {
+	CFF2Subrs *dest = c.start_embed <CFF2Subrs> ();
+	if (unlikely (dest == nullptr)) return false;
+	if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i])))
+	{
+	  DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines");
+	  return false;
+	}
+      }
+    }
+  }
+
+  assert (c.head == c.end);
+  c.end_serialize ();
+
+  return true;
+}
+
+static bool
+_hb_subset_cff2 (const OT::cff2::accelerator_subset_t  &acc,
+		const char		      *data,
+		hb_subset_plan_t		*plan,
+		hb_blob_t		       **prime /* OUT */)
+{
+  cff2_subset_plan cff2_plan;
+
+  if (unlikely (!cff2_plan.create (acc, plan)))
+  {
+    DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff2 subsetting plan.");
+    return false;
+  }
+
+  unsigned int  cff2_prime_size = cff2_plan.get_final_size ();
+  char *cff2_prime_data = (char *) calloc (1, cff2_prime_size);
+
+  if (unlikely (!_write_cff2 (cff2_plan, acc, plan->glyphs,
+			      cff2_prime_size, cff2_prime_data))) {
+    DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2.");
+    free (cff2_prime_data);
+    return false;
+  }
+
+  *prime = hb_blob_create (cff2_prime_data,
+				cff2_prime_size,
+				HB_MEMORY_MODE_READONLY,
+				cff2_prime_data,
+				free);
+  return true;
+}
+
+/**
+ * hb_subset_cff2:
+ * Subsets the CFF2 table according to a provided plan.
+ *
+ * Return value: subsetted cff2 table.
+ **/
+bool
+hb_subset_cff2 (hb_subset_plan_t *plan,
+		hb_blob_t       **prime /* OUT */)
+{
+  hb_blob_t *cff2_blob = hb_sanitize_context_t().reference_table<CFF::cff2> (plan->source);
+  const char *data = hb_blob_get_data(cff2_blob, nullptr);
+
+  OT::cff2::accelerator_subset_t acc;
+  acc.init(plan->source);
+  bool result = likely (acc.is_valid ()) &&
+		_hb_subset_cff2 (acc, data, plan, prime);
+
+  hb_blob_destroy (cff2_blob);
+  acc.fini ();
+
+  return result;
+}
diff --git a/src/hb-subset-cff2.hh b/src/hb-subset-cff2.hh
new file mode 100644
index 0000000..a07dc29
--- /dev/null
+++ b/src/hb-subset-cff2.hh
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_SUBSET_CFF2_HH
+#define HB_SUBSET_CFF2_HH
+
+#include "hb.hh"
+
+#include "hb-subset-plan.hh"
+
+HB_INTERNAL bool
+hb_subset_cff2 (hb_subset_plan_t *plan,
+	       hb_blob_t       **cff2_prime /* OUT */);
+
+#endif /* HB_SUBSET_CFF2_HH */
diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc
index 499380a..2219d37 100644
--- a/src/hb-subset-glyf.cc
+++ b/src/hb-subset-glyf.cc
@@ -31,12 +31,12 @@
 
 static bool
 _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
-                                     hb_vector_t<hb_codepoint_t> &glyph_ids,
-                                     hb_bool_t drop_hints,
-                                     bool *use_short_loca /* OUT */,
-                                     unsigned int *glyf_size /* OUT */,
-                                     unsigned int *loca_size /* OUT */,
-                                     hb_vector_t<unsigned int> *instruction_ranges /* OUT */)
+				     hb_vector_t<hb_codepoint_t> &glyph_ids,
+				     hb_bool_t drop_hints,
+				     bool *use_short_loca /* OUT */,
+				     unsigned int *glyf_size /* OUT */,
+				     unsigned int *loca_size /* OUT */,
+				     hb_vector_t<unsigned int> *instruction_ranges /* OUT */)
 {
   unsigned int total = 0;
   for (unsigned int i = 0; i < glyph_ids.len; i++)
@@ -53,8 +53,8 @@
     *instruction_end = 0;
 
     unsigned int start_offset, end_offset;
-    if (unlikely (!(glyf.get_offsets(next_glyph, &start_offset, &end_offset)
-                    && glyf.remove_padding(start_offset, &end_offset))))
+    if (unlikely (!(glyf.get_offsets (next_glyph, &start_offset, &end_offset) &&
+		    glyf.remove_padding (start_offset, &end_offset))))
     {
       DEBUG_MSG(SUBSET, nullptr, "Invalid gid %d", next_glyph);
       continue;
@@ -64,11 +64,11 @@
 
     if (drop_hints)
     {
-      if (unlikely (!glyf.get_instruction_offsets(start_offset, end_offset,
-                                                  instruction_start, instruction_end)))
+      if (unlikely (!glyf.get_instruction_offsets (start_offset, end_offset,
+						   instruction_start, instruction_end)))
       {
-        DEBUG_MSG(SUBSET, nullptr, "Unable to get instruction offsets for %d", next_glyph);
-        return false;
+	DEBUG_MSG(SUBSET, nullptr, "Unable to get instruction offsets for %d", next_glyph);
+	return false;
       }
     }
 
@@ -80,21 +80,21 @@
   *glyf_size = total;
   *use_short_loca = (total <= 131070);
   *loca_size = (glyph_ids.len + 1)
-      * (*use_short_loca ? sizeof(OT::HBUINT16) : sizeof(OT::HBUINT32));
+      * (*use_short_loca ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32));
 
   DEBUG_MSG(SUBSET, nullptr, "preparing to subset glyf: final size %d, loca size %d, using %s loca",
-            total,
-            *loca_size,
-            *use_short_loca ? "short" : "long");
+	    total,
+	    *loca_size,
+	    *use_short_loca ? "short" : "long");
   return true;
 }
 
 static bool
 _write_loca_entry (unsigned int  id,
-                   unsigned int  offset,
-                   bool          is_short,
-                   void         *loca_prime,
-                   unsigned int  loca_size)
+		   unsigned int  offset,
+		   bool          is_short,
+		   void         *loca_prime,
+		   unsigned int  loca_size)
 {
   unsigned int entry_size = is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32);
   if ((id + 1) * entry_size <= loca_size)
@@ -108,11 +108,11 @@
   }
 
   // Offset was not written because the write is out of bounds.
-  DEBUG_MSG (SUBSET,
-             nullptr,
-             "WARNING: Attempted to write an out of bounds loca entry at index %d. Loca size is %d.",
-             id,
-             loca_size);
+  DEBUG_MSG(SUBSET,
+	    nullptr,
+	    "WARNING: Attempted to write an out of bounds loca entry at index %d. Loca size is %d.",
+	    id,
+	    loca_size);
   return false;
 }
 
@@ -130,15 +130,15 @@
     {
       hb_codepoint_t new_gid;
       if (!plan->new_gid_for_old_gid (iterator.current->glyphIndex,
-                                      &new_gid))
+				      &new_gid))
 	continue;
 
       ((OT::glyf::CompositeGlyphHeader *) iterator.current)->glyphIndex.set (new_gid);
-    } while (iterator.move_to_next());
+    } while (iterator.move_to_next ());
   }
 }
 
-static bool _remove_composite_instruction_flag(char *glyf_prime, unsigned int length)
+static bool _remove_composite_instruction_flag (char *glyf_prime, unsigned int length)
 {
   /* remove WE_HAVE_INSTRUCTIONS from flags in dest */
   OT::glyf::CompositeGlyphHeader::Iterator composite_it;
@@ -148,20 +148,20 @@
     glyph = composite_it.current;
     OT::HBUINT16 *flags = const_cast<OT::HBUINT16 *> (&glyph->flags);
     flags->set ( (uint16_t) *flags & ~OT::glyf::CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS);
-  } while (composite_it.move_to_next());
+  } while (composite_it.move_to_next ());
   return true;
 }
 
 static bool
 _write_glyf_and_loca_prime (hb_subset_plan_t              *plan,
 			    const OT::glyf::accelerator_t &glyf,
-                            const char                    *glyf_data,
-                            bool                           use_short_loca,
-                            hb_vector_t<unsigned int> &instruction_ranges,
-                            unsigned int                   glyf_prime_size,
-                            char                          *glyf_prime_data /* OUT */,
-                            unsigned int                   loca_prime_size,
-                            char                          *loca_prime_data /* OUT */)
+			    const char                    *glyf_data,
+			    bool                           use_short_loca,
+			    hb_vector_t<unsigned int> &instruction_ranges,
+			    unsigned int                   glyf_prime_size,
+			    char                          *glyf_prime_data /* OUT */,
+			    unsigned int                   loca_prime_size,
+			    char                          *loca_prime_data /* OUT */)
 {
   hb_vector_t<hb_codepoint_t> &glyph_ids = plan->glyphs;
   char *glyf_prime_data_next = glyf_prime_data;
@@ -170,8 +170,8 @@
   for (unsigned int i = 0; i < glyph_ids.len; i++)
   {
     unsigned int start_offset, end_offset;
-    if (unlikely (!(glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset)
-                    && glyf.remove_padding(start_offset, &end_offset))))
+    if (unlikely (!(glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset) &&
+		    glyf.remove_padding (start_offset, &end_offset))))
       end_offset = start_offset = 0;
 
     unsigned int instruction_start = instruction_ranges[i * 2];
@@ -181,10 +181,10 @@
 
     if (glyf_prime_data_next + length > glyf_prime_data + glyf_prime_size)
     {
-      DEBUG_MSG (SUBSET,
-                 nullptr,
-                 "WARNING: Attempted to write an out of bounds glyph entry for gid %d (length %d)",
-                 i, length);
+      DEBUG_MSG(SUBSET,
+		 nullptr,
+		 "WARNING: Attempted to write an out of bounds glyph entry for gid %d (length %d)",
+		 i, length);
       return false;
     }
 
@@ -197,18 +197,18 @@
       /* if the instructions end at the end this was a composite glyph, else simple */
       if (instruction_end == end_offset)
       {
-        if (unlikely (!_remove_composite_instruction_flag (glyf_prime_data_next, length))) return false;
+	if (unlikely (!_remove_composite_instruction_flag (glyf_prime_data_next, length))) return false;
       }
       else
-        /* zero instruction length, which is just before instruction_start */
-        memset (glyf_prime_data_next + instruction_start - start_offset - 2, 0, 2);
+	/* zero instruction length, which is just before instruction_start */
+	memset (glyf_prime_data_next + instruction_start - start_offset - 2, 0, 2);
     }
 
     success = success && _write_loca_entry (i,
-                                            glyf_prime_data_next - glyf_prime_data,
-                                            use_short_loca,
-                                            loca_prime_data,
-                                            loca_prime_size);
+					    glyf_prime_data_next - glyf_prime_data,
+					    use_short_loca,
+					    loca_prime_data,
+					    loca_prime_size);
     _update_components (plan, glyf_prime_data_next, length);
 
     // TODO: don't align to two bytes if using long loca.
@@ -216,20 +216,20 @@
   }
 
   success = success && _write_loca_entry (glyph_ids.len,
-                                          glyf_prime_data_next - glyf_prime_data,
-                                          use_short_loca,
-                                          loca_prime_data,
-                                          loca_prime_size);
+					  glyf_prime_data_next - glyf_prime_data,
+					  use_short_loca,
+					  loca_prime_data,
+					  loca_prime_size);
   return success;
 }
 
 static bool
 _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t  &glyf,
-                          const char                     *glyf_data,
-                          hb_subset_plan_t               *plan,
-                          bool                           *use_short_loca,
-                          hb_blob_t                     **glyf_prime /* OUT */,
-                          hb_blob_t                     **loca_prime /* OUT */)
+			  const char                     *glyf_data,
+			  hb_subset_plan_t               *plan,
+			  bool                           *use_short_loca,
+			  hb_blob_t                     **glyf_prime /* OUT */,
+			  hb_blob_t                     **loca_prime /* OUT */)
 {
   // TODO(grieger): Sanity check allocation size for the new table.
   hb_vector_t<hb_codepoint_t> &glyphs_to_retain = plan->glyphs;
@@ -237,43 +237,43 @@
   unsigned int glyf_prime_size;
   unsigned int loca_prime_size;
   hb_vector_t<unsigned int> instruction_ranges;
-  instruction_ranges.init();
+  instruction_ranges.init ();
 
   if (unlikely (!_calculate_glyf_and_loca_prime_size (glyf,
-                                                      glyphs_to_retain,
-                                                      plan->drop_hints,
-                                                      use_short_loca,
-                                                      &glyf_prime_size,
-                                                      &loca_prime_size,
-                                                      &instruction_ranges))) {
-    instruction_ranges.fini();
+						      glyphs_to_retain,
+						      plan->drop_hints,
+						      use_short_loca,
+						      &glyf_prime_size,
+						      &loca_prime_size,
+						      &instruction_ranges))) {
+    instruction_ranges.fini ();
     return false;
   }
 
   char *glyf_prime_data = (char *) calloc (1, glyf_prime_size);
   char *loca_prime_data = (char *) calloc (1, loca_prime_size);
   if (unlikely (!_write_glyf_and_loca_prime (plan, glyf, glyf_data,
-                                             *use_short_loca,
-                                             instruction_ranges,
-                                             glyf_prime_size, glyf_prime_data,
-                                             loca_prime_size, loca_prime_data))) {
+					     *use_short_loca,
+					     instruction_ranges,
+					     glyf_prime_size, glyf_prime_data,
+					     loca_prime_size, loca_prime_data))) {
     free (glyf_prime_data);
     free (loca_prime_data);
-    instruction_ranges.fini();
+    instruction_ranges.fini ();
     return false;
   }
-  instruction_ranges.fini();
+  instruction_ranges.fini ();
 
   *glyf_prime = hb_blob_create (glyf_prime_data,
-                                glyf_prime_size,
-                                HB_MEMORY_MODE_READONLY,
-                                glyf_prime_data,
-                                free);
+				glyf_prime_size,
+				HB_MEMORY_MODE_READONLY,
+				glyf_prime_data,
+				free);
   *loca_prime = hb_blob_create (loca_prime_data,
-                                loca_prime_size,
-                                HB_MEMORY_MODE_READONLY,
-                                loca_prime_data,
-                                free);
+				loca_prime_size,
+				HB_MEMORY_MODE_READONLY,
+				loca_prime_data,
+				free);
   return true;
 }
 
@@ -287,24 +287,24 @@
  **/
 bool
 hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
-                         bool             *use_short_loca, /* OUT */
-                         hb_blob_t       **glyf_prime, /* OUT */
-                         hb_blob_t       **loca_prime /* OUT */)
+			 bool             *use_short_loca, /* OUT */
+			 hb_blob_t       **glyf_prime, /* OUT */
+			 hb_blob_t       **loca_prime /* OUT */)
 {
   hb_blob_t *glyf_blob = hb_sanitize_context_t ().reference_table<OT::glyf> (plan->source);
-  const char *glyf_data = hb_blob_get_data(glyf_blob, nullptr);
+  const char *glyf_data = hb_blob_get_data (glyf_blob, nullptr);
 
   OT::glyf::accelerator_t glyf;
-  glyf.init(plan->source);
+  glyf.init (plan->source);
   bool result = _hb_subset_glyf_and_loca (glyf,
-                                          glyf_data,
-                                          plan,
-                                          use_short_loca,
-                                          glyf_prime,
-                                          loca_prime);
+					  glyf_data,
+					  plan,
+					  use_short_loca,
+					  glyf_prime,
+					  loca_prime);
 
   hb_blob_destroy (glyf_blob);
-  glyf.fini();
+  glyf.fini ();
 
   return result;
 }
diff --git a/src/hb-subset-glyf.hh b/src/hb-subset-glyf.hh
index 3109ecb..99cf8f0 100644
--- a/src/hb-subset-glyf.hh
+++ b/src/hb-subset-glyf.hh
@@ -33,8 +33,8 @@
 
 HB_INTERNAL bool
 hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
-                         bool             *use_short_loca, /* OUT */
-                         hb_blob_t       **glyf_prime /* OUT */,
-                         hb_blob_t       **loca_prime /* OUT */);
+			 bool             *use_short_loca, /* OUT */
+			 hb_blob_t       **glyf_prime      /* OUT */,
+			 hb_blob_t       **loca_prime      /* OUT */);
 
 #endif /* HB_SUBSET_GLYF_HH */
diff --git a/src/hb-subset-input.cc b/src/hb-subset-input.cc
index d59b5ba..f718a56 100644
--- a/src/hb-subset-input.cc
+++ b/src/hb-subset-input.cc
@@ -35,7 +35,7 @@
  * Since: 1.8.0
  **/
 hb_subset_input_t *
-hb_subset_input_create_or_fail (void)
+hb_subset_input_create_or_fail ()
 {
   hb_subset_input_t *input = hb_object_create<hb_subset_input_t>();
 
@@ -72,7 +72,7 @@
  * Since: 1.8.0
  **/
 void
-hb_subset_input_destroy(hb_subset_input_t *subset_input)
+hb_subset_input_destroy (hb_subset_input_t *subset_input)
 {
   if (!hb_object_destroy (subset_input)) return;
 
@@ -121,7 +121,7 @@
 
 HB_EXTERN void
 hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input,
-				hb_bool_t drop_layout)
+				 hb_bool_t drop_layout)
 {
   subset_input->drop_layout = drop_layout;
 }
@@ -131,3 +131,16 @@
 {
   return subset_input->drop_layout;
 }
+
+HB_EXTERN void
+hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input,
+        hb_bool_t desubroutinize)
+{
+  subset_input->desubroutinize = desubroutinize;
+}
+
+HB_EXTERN hb_bool_t
+hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input)
+{
+  return subset_input->desubroutinize;
+}
diff --git a/src/hb-subset-input.hh b/src/hb-subset-input.hh
index 9fc8615..8dad94f 100644
--- a/src/hb-subset-input.hh
+++ b/src/hb-subset-input.hh
@@ -37,13 +37,13 @@
 struct hb_subset_input_t
 {
   hb_object_header_t header;
-  ASSERT_POD ();
 
   hb_set_t *unicodes;
   hb_set_t *glyphs;
 
   bool drop_hints : 1;
   bool drop_layout : 1;
+  bool desubroutinize : 1;
   /* TODO
    *
    * features
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 0570060..5c0983b 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -30,6 +30,7 @@
 
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-glyf-table.hh"
+#include "hb-ot-cff1-table.hh"
 
 static void
 _add_gid_and_children (const OT::glyf::accelerator_t &glyf,
@@ -53,33 +54,59 @@
 }
 
 static void
-_gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain)
+_add_cff_seac_components (const OT::cff1::accelerator_t &cff,
+           hb_codepoint_t gid,
+           hb_set_t *gids_to_retain)
 {
-  hb_auto_t<hb_set_t> lookup_indices;
-  hb_ot_layout_collect_lookups (face,
-                                HB_OT_TAG_GSUB,
-                                nullptr,
-                                nullptr,
-                                nullptr,
-                                &lookup_indices);
-  hb_ot_layout_lookups_substitute_closure (face,
-                                           &lookup_indices,
-                                           gids_to_retain);
+  hb_codepoint_t base_gid, accent_gid;
+  if (cff.get_seac_components (gid, &base_gid, &accent_gid))
+  {
+    hb_set_add (gids_to_retain, base_gid);
+    hb_set_add (gids_to_retain, accent_gid);
+  }
 }
 
+static void
+_gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain)
+{
+  hb_set_t lookup_indices;
+  hb_ot_layout_collect_lookups (face,
+				HB_OT_TAG_GSUB,
+				nullptr,
+				nullptr,
+				nullptr,
+				&lookup_indices);
+  hb_ot_layout_lookups_substitute_closure (face,
+					   &lookup_indices,
+					   gids_to_retain);
+}
+
+static void
+_remove_invalid_gids (hb_set_t *glyphs,
+		      unsigned int num_glyphs)
+{
+  hb_codepoint_t gid = HB_SET_VALUE_INVALID;
+  while (glyphs->next (&gid))
+  {
+    if (gid >= num_glyphs)
+      glyphs->del (gid);
+  }
+}
 
 static hb_set_t *
 _populate_gids_to_retain (hb_face_t *face,
-                          const hb_set_t *unicodes,
-                          bool close_over_gsub,
-                          hb_set_t *unicodes_to_retain,
-                          hb_map_t *codepoint_to_glyph,
-                          hb_vector_t<hb_codepoint_t> *glyphs)
+			  const hb_set_t *unicodes,
+			  bool close_over_gsub,
+			  hb_set_t *unicodes_to_retain,
+			  hb_map_t *codepoint_to_glyph,
+			  hb_vector_t<hb_codepoint_t> *glyphs)
 {
   OT::cmap::accelerator_t cmap;
   OT::glyf::accelerator_t glyf;
+  OT::cff1::accelerator_t cff;
   cmap.init (face);
   glyf.init (face);
+  cff.init (face);
 
   hb_set_t *initial_gids_to_retain = hb_set_create ();
   initial_gids_to_retain->add (0); // Not-def
@@ -109,14 +136,19 @@
   while (initial_gids_to_retain->next (&gid))
   {
     _add_gid_and_children (glyf, gid, all_gids_to_retain);
+    if (cff.is_valid ())
+      _add_cff_seac_components (cff, gid, all_gids_to_retain);
   }
   hb_set_destroy (initial_gids_to_retain);
 
+  _remove_invalid_gids (all_gids_to_retain, face->get_num_glyphs ());
+
   glyphs->alloc (all_gids_to_retain->get_population ());
   gid = HB_SET_VALUE_INVALID;
   while (all_gids_to_retain->next (&gid))
     glyphs->push (gid);
 
+  cff.fini ();
   glyf.fini ();
   cmap.fini ();
 
@@ -125,7 +157,7 @@
 
 static void
 _create_old_gid_to_new_gid_map (const hb_vector_t<hb_codepoint_t> &glyphs,
-                                hb_map_t *glyph_map)
+				hb_map_t *glyph_map)
 {
   for (unsigned int i = 0; i < glyphs.len; i++) {
     glyph_map->set (glyphs[i], i);
@@ -144,12 +176,13 @@
  **/
 hb_subset_plan_t *
 hb_subset_plan_create (hb_face_t           *face,
-                       hb_subset_input_t   *input)
+		       hb_subset_input_t   *input)
 {
   hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> ();
 
   plan->drop_hints = input->drop_hints;
   plan->drop_layout = input->drop_layout;
+  plan->desubroutinize = input->desubroutinize;
   plan->unicodes = hb_set_create();
   plan->glyphs.init();
   plan->source = hb_face_reference (face);
@@ -163,7 +196,7 @@
 					     plan->codepoint_to_glyph,
 					     &plan->glyphs);
   _create_old_gid_to_new_gid_map (plan->glyphs,
-                                  plan->glyph_map);
+				  plan->glyph_map);
 
   return plan;
 }
@@ -179,7 +212,7 @@
   if (!hb_object_destroy (plan)) return;
 
   hb_set_destroy (plan->unicodes);
-  plan->glyphs.fini();
+  plan->glyphs.fini ();
   hb_face_destroy (plan->source);
   hb_face_destroy (plan->dest);
   hb_map_destroy (plan->codepoint_to_glyph);
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh
index c2c484a..a710a4d 100644
--- a/src/hb-subset-plan.hh
+++ b/src/hb-subset-plan.hh
@@ -37,10 +37,10 @@
 struct hb_subset_plan_t
 {
   hb_object_header_t header;
-  ASSERT_POD ();
 
   bool drop_hints : 1;
   bool drop_layout : 1;
+  bool desubroutinize : 1;
 
   // For each cp that we'd like to retain maps to the corresponding gid.
   hb_set_t *unicodes;
@@ -55,9 +55,8 @@
   hb_face_t *source;
   hb_face_t *dest;
 
-  inline bool
-  new_gid_for_codepoint (hb_codepoint_t codepoint,
-                         hb_codepoint_t *new_gid) const
+  bool new_gid_for_codepoint (hb_codepoint_t codepoint,
+			      hb_codepoint_t *new_gid) const
   {
     hb_codepoint_t old_gid = codepoint_to_glyph->get (codepoint);
     if (old_gid == HB_MAP_VALUE_INVALID)
@@ -66,9 +65,8 @@
     return new_gid_for_old_gid (old_gid, new_gid);
   }
 
-  inline bool
-  new_gid_for_old_gid (hb_codepoint_t old_gid,
-                      hb_codepoint_t *new_gid) const
+  bool new_gid_for_old_gid (hb_codepoint_t old_gid,
+			    hb_codepoint_t *new_gid) const
   {
     hb_codepoint_t gid = glyph_map->get (old_gid);
     if (gid == HB_MAP_VALUE_INVALID)
@@ -78,15 +76,15 @@
     return true;
   }
 
-  inline bool
+  bool
   add_table (hb_tag_t tag,
-             hb_blob_t *contents)
+	     hb_blob_t *contents)
   {
     hb_blob_t *source_blob = source->reference_table (tag);
     DEBUG_MSG(SUBSET, nullptr, "add table %c%c%c%c, dest %d bytes, source %d bytes",
-              HB_UNTAG(tag),
-              hb_blob_get_length (contents),
-              hb_blob_get_length (source_blob));
+	      HB_UNTAG(tag),
+	      hb_blob_get_length (contents),
+	      hb_blob_get_length (source_blob));
     hb_blob_destroy (source_blob);
     return hb_face_builder_add_table (dest, tag, contents);
   }
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 9f14b89..37e7cec 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -40,6 +40,9 @@
 #include "hb-ot-maxp-table.hh"
 #include "hb-ot-os2-table.hh"
 #include "hb-ot-post-table.hh"
+#include "hb-ot-cff1-table.hh"
+#include "hb-ot-cff2-table.hh"
+#include "hb-ot-vorg-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
 
@@ -68,25 +71,25 @@
   hb_bool_t result = false;
   if (source_blob->data)
   {
-    hb_auto_t<hb_vector_t<char> > buf;
+    hb_vector_t<char> buf;
     unsigned int buf_size = _plan_estimate_subset_table_size (plan, source_blob->length);
-    DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG(tag), buf_size);
+    DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size);
     if (unlikely (!buf.alloc (buf_size)))
     {
-      DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG(tag), buf_size);
+      DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size);
       return false;
     }
   retry:
-    hb_serialize_context_t serializer (buf.arrayZ(), buf_size);
+    hb_serialize_context_t serializer ((void *) buf, buf_size);
     hb_subset_context_t c (plan, &serializer);
     result = table->subset (&c);
-    if (serializer.ran_out_of_room)
+    if (serializer.in_error ())
     {
       buf_size += (buf_size >> 1) + 32;
-      DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", HB_UNTAG(tag), buf_size);
+      DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", HB_UNTAG (tag), buf_size);
       if (unlikely (!buf.alloc (buf_size)))
       {
-	DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", HB_UNTAG(tag), buf_size);
+	DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", HB_UNTAG (tag), buf_size);
 	return false;
       }
       goto retry;
@@ -94,21 +97,21 @@
     if (result)
     {
       hb_blob_t *dest_blob = serializer.copy_blob ();
-      DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG(tag), dest_blob->length);
+      DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length);
       result = c.plan->add_table (tag, dest_blob);
       hb_blob_destroy (dest_blob);
     }
     else
     {
-      DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG(tag));
+      DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag));
       result = true;
     }
   }
   else
-    DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG(tag));
+    DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag));
 
   hb_blob_destroy (source_blob);
-  DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG(tag), result ? "success" : "FAILED!");
+  DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!");
   return result;
 }
 
@@ -124,19 +127,19 @@
   if (source_blob->data)
     result = table->subset (plan);
   else
-    DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG(tag));
+    DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag));
 
   hb_blob_destroy (source_blob);
-  DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG(tag), result ? "success" : "FAILED!");
+  DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!");
   return result;
 }
 
 
 static bool
 _subset_table (hb_subset_plan_t *plan,
-               hb_tag_t          tag)
+	       hb_tag_t          tag)
 {
-  DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG(tag));
+  DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG (tag));
   bool result = true;
   switch (tag) {
     case HB_OT_TAG_glyf:
@@ -171,13 +174,24 @@
     case HB_OT_TAG_cmap:
       result = _subset<const OT::cmap> (plan);
       break;
-    case HB_OT_TAG_os2:
-      result = _subset<const OT::os2> (plan);
+    case HB_OT_TAG_OS2:
+      result = _subset<const OT::OS2> (plan);
       break;
     case HB_OT_TAG_post:
       result = _subset<const OT::post> (plan);
       break;
-
+    case HB_OT_TAG_cff1:
+      result = _subset<const OT::cff1> (plan);
+      break;
+    case HB_OT_TAG_cff2:
+      result = _subset<const OT::cff2> (plan);
+      break;
+    case HB_OT_TAG_VORG:
+      result = _subset<const OT::VORG> (plan);
+      break;
+    case HB_OT_TAG_GDEF:
+      result = _subset2<const OT::GDEF> (plan);
+      break;
     case HB_OT_TAG_GSUB:
       result = _subset2<const OT::GSUB> (plan);
       break;
@@ -186,20 +200,20 @@
       break;
 
     default:
-      hb_blob_t *source_table = hb_face_reference_table(plan->source, tag);
+      hb_blob_t *source_table = hb_face_reference_table (plan->source, tag);
       if (likely (source_table))
-        result = plan->add_table(tag, source_table);
+	result = plan->add_table (tag, source_table);
       else
-        result = false;
+	result = false;
       hb_blob_destroy (source_table);
       break;
   }
-  DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG(tag), result ? "ok" : "FAILED");
+  DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG (tag), result ? "ok" : "FAILED");
   return result;
 }
 
 static bool
-_should_drop_table(hb_subset_plan_t *plan, hb_tag_t tag)
+_should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag)
 {
   switch (tag) {
     case HB_TAG ('c', 'v', 'a', 'r'): /* hint table, fallthrough */
@@ -210,9 +224,9 @@
     case HB_TAG ('V', 'D', 'M', 'X'): /* hint table, fallthrough */
       return plan->drop_hints;
     // Drop Layout Tables if requested.
-    case HB_TAG ('G', 'D', 'E', 'F'): /* temporary */
-    case HB_TAG ('G', 'P', 'O', 'S'): /* temporary */
-    case HB_TAG ('G', 'S', 'U', 'B'): /* temporary */
+    case HB_OT_TAG_GDEF:
+    case HB_OT_TAG_GPOS:
+    case HB_OT_TAG_GSUB:
       return plan->drop_layout;
     // Drop these tables below by default, list pulled
     // from fontTools:
@@ -248,9 +262,9 @@
  **/
 hb_face_t *
 hb_subset (hb_face_t *source,
-           hb_subset_input_t *input)
+	   hb_subset_input_t *input)
 {
-  if (unlikely (!input || !source)) return hb_face_get_empty();
+  if (unlikely (!input || !source)) return hb_face_get_empty ();
 
   hb_subset_plan_t *plan = hb_subset_plan_create (source, input);
 
@@ -263,17 +277,17 @@
     for (unsigned int i = 0; i < count; i++)
     {
       hb_tag_t tag = table_tags[i];
-      if (_should_drop_table(plan, tag))
+      if (_should_drop_table (plan, tag))
       {
-        DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG(tag));
-        continue;
+	DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG (tag));
+	continue;
       }
       success = success && _subset_table (plan, tag);
     }
     offset += count;
   } while (success && count == ARRAY_LENGTH (table_tags));
 
-  hb_face_t *result = success ? hb_face_reference(plan->dest) : hb_face_get_empty();
+  hb_face_t *result = success ? hb_face_reference (plan->dest) : hb_face_get_empty ();
   hb_subset_plan_destroy (plan);
   return result;
 }
diff --git a/src/hb-subset.h b/src/hb-subset.h
index 8b07a45..f582e46 100644
--- a/src/hb-subset.h
+++ b/src/hb-subset.h
@@ -62,15 +62,19 @@
 
 HB_EXTERN void
 hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input,
-				hb_bool_t drop_layout);
+				 hb_bool_t drop_layout);
 HB_EXTERN hb_bool_t
 hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input);
 
+HB_EXTERN void
+hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input,
+        hb_bool_t desubroutinize);
+HB_EXTERN hb_bool_t
+hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input);
 
-/* hb_subset() */
+/* hb_subset () */
 HB_EXTERN hb_face_t *
-hb_subset (hb_face_t *source,
-           hb_subset_input_t *input);
+hb_subset (hb_face_t *source, hb_subset_input_t *input);
 
 
 HB_END_DECLS
diff --git a/src/hb-subset.hh b/src/hb-subset.hh
index 9cdd388..45cb763 100644
--- a/src/hb-subset.hh
+++ b/src/hb-subset.hh
@@ -39,11 +39,10 @@
 struct hb_subset_context_t :
        hb_dispatch_context_t<hb_subset_context_t, bool, HB_DEBUG_SUBSET>
 {
-  inline const char *get_name (void) { return "SUBSET"; }
+  const char *get_name () { return "SUBSET"; }
   template <typename T>
-  inline bool dispatch (const T &obj) { return obj.subset (this); }
-  static bool default_return_value (void) { return true; }
-  bool stop_sublookup_iteration (bool r) const { return false; }
+  bool dispatch (const T &obj) { return obj.subset (this); }
+  static bool default_return_value () { return true; }
 
   hb_subset_plan_t *plan;
   hb_serialize_context_t *serializer;
diff --git a/src/hb-ucdn.cc b/src/hb-ucdn.cc
index 3179683..534935f 100644
--- a/src/hb-ucdn.cc
+++ b/src/hb-ucdn.cc
@@ -222,13 +222,13 @@
 }
 
 
-#ifdef HB_USE_ATEXIT
-static void free_static_ucdn_funcs (void);
+#if HB_USE_ATEXIT
+static void free_static_ucdn_funcs ();
 #endif
 
 static struct hb_ucdn_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_ucdn_unicode_funcs_lazy_loader_t>
 {
-  static inline hb_unicode_funcs_t *create (void)
+  static hb_unicode_funcs_t *create ()
   {
     hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr);
 
@@ -241,7 +241,7 @@
 
     hb_unicode_funcs_make_immutable (funcs);
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
     atexit (free_static_ucdn_funcs);
 #endif
 
@@ -249,9 +249,9 @@
   }
 } static_ucdn_funcs;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
 static
-void free_static_ucdn_funcs (void)
+void free_static_ucdn_funcs ()
 {
   static_ucdn_funcs.free_instance ();
 }
@@ -259,10 +259,10 @@
 
 extern "C" HB_INTERNAL
 hb_unicode_funcs_t *
-hb_ucdn_get_unicode_funcs (void);
+hb_ucdn_get_unicode_funcs ();
 
 hb_unicode_funcs_t *
-hb_ucdn_get_unicode_funcs (void)
+hb_ucdn_get_unicode_funcs ()
 {
   return static_ucdn_funcs.get_unconst ();
 }
diff --git a/src/hb-ucdn/ucdn.h b/src/hb-ucdn/ucdn.h
index 9b0f9d4..05d46d2 100644
--- a/src/hb-ucdn/ucdn.h
+++ b/src/hb-ucdn/ucdn.h
@@ -456,8 +456,6 @@
  */
 int ucdn_compose(uint32_t *code, uint32_t a, uint32_t b);
 
-#ifdef __cplusplus
-}
-#endif
+HB_END_HEADER
 
 #endif
diff --git a/src/hb-unicode-emoji-table.hh b/src/hb-unicode-emoji-table.hh
index 41199de..1dd0b32 100644
--- a/src/hb-unicode-emoji-table.hh
+++ b/src/hb-unicode-emoji-table.hh
@@ -47,38 +47,9 @@
   {0x25FB, 0x25FE},
   {0x2600, 0x2605},
   {0x2607, 0x2612},
-  {0x2614, 0x2615},
-  {0x2616, 0x2617},
-  {0x2618, 0x2618},
-  {0x2619, 0x2619},
-  {0x261A, 0x266F},
-  {0x2670, 0x2671},
-  {0x2672, 0x267D},
-  {0x267E, 0x267F},
-  {0x2680, 0x2685},
-  {0x2690, 0x2691},
-  {0x2692, 0x269C},
-  {0x269D, 0x269D},
-  {0x269E, 0x269F},
-  {0x26A0, 0x26A1},
-  {0x26A2, 0x26B1},
-  {0x26B2, 0x26B2},
-  {0x26B3, 0x26BC},
-  {0x26BD, 0x26BF},
-  {0x26C0, 0x26C3},
-  {0x26C4, 0x26CD},
-  {0x26CE, 0x26CE},
-  {0x26CF, 0x26E1},
-  {0x26E2, 0x26E2},
-  {0x26E3, 0x26E3},
-  {0x26E4, 0x26E7},
-  {0x26E8, 0x26FF},
-  {0x2700, 0x2700},
-  {0x2701, 0x2704},
-  {0x2705, 0x2705},
-  {0x2708, 0x2709},
-  {0x270A, 0x270B},
-  {0x270C, 0x2712},
+  {0x2614, 0x2685},
+  {0x2690, 0x2705},
+  {0x2708, 0x2712},
   {0x2714, 0x2714},
   {0x2716, 0x2716},
   {0x271D, 0x271D},
@@ -105,163 +76,33 @@
   {0x303D, 0x303D},
   {0x3297, 0x3297},
   {0x3299, 0x3299},
-  {0x1F000, 0x1F02B},
-  {0x1F02C, 0x1F02F},
-  {0x1F030, 0x1F093},
-  {0x1F094, 0x1F09F},
-  {0x1F0A0, 0x1F0AE},
-  {0x1F0AF, 0x1F0B0},
-  {0x1F0B1, 0x1F0BE},
-  {0x1F0BF, 0x1F0BF},
-  {0x1F0C0, 0x1F0C0},
-  {0x1F0C1, 0x1F0CF},
-  {0x1F0D0, 0x1F0D0},
-  {0x1F0D1, 0x1F0DF},
-  {0x1F0E0, 0x1F0F5},
-  {0x1F0F6, 0x1F0FF},
+  {0x1F000, 0x1F0FF},
   {0x1F10D, 0x1F10F},
   {0x1F12F, 0x1F12F},
-  {0x1F16C, 0x1F16F},
-  {0x1F170, 0x1F171},
-  {0x1F17E, 0x1F17E},
-  {0x1F17F, 0x1F17F},
+  {0x1F16C, 0x1F171},
+  {0x1F17E, 0x1F17F},
   {0x1F18E, 0x1F18E},
   {0x1F191, 0x1F19A},
   {0x1F1AD, 0x1F1E5},
-  {0x1F201, 0x1F202},
-  {0x1F203, 0x1F20F},
+  {0x1F201, 0x1F20F},
   {0x1F21A, 0x1F21A},
   {0x1F22F, 0x1F22F},
   {0x1F232, 0x1F23A},
   {0x1F23C, 0x1F23F},
-  {0x1F249, 0x1F24F},
-  {0x1F250, 0x1F251},
-  {0x1F252, 0x1F25F},
-  {0x1F260, 0x1F265},
-  {0x1F266, 0x1F2FF},
-  {0x1F300, 0x1F320},
-  {0x1F321, 0x1F32C},
-  {0x1F32D, 0x1F32F},
-  {0x1F330, 0x1F335},
-  {0x1F336, 0x1F336},
-  {0x1F337, 0x1F37C},
-  {0x1F37D, 0x1F37D},
-  {0x1F37E, 0x1F37F},
-  {0x1F380, 0x1F393},
-  {0x1F394, 0x1F39F},
-  {0x1F3A0, 0x1F3C4},
-  {0x1F3C5, 0x1F3C5},
-  {0x1F3C6, 0x1F3CA},
-  {0x1F3CB, 0x1F3CE},
-  {0x1F3CF, 0x1F3D3},
-  {0x1F3D4, 0x1F3DF},
-  {0x1F3E0, 0x1F3F0},
-  {0x1F3F1, 0x1F3F7},
-  {0x1F3F8, 0x1F3FA},
-  {0x1F400, 0x1F43E},
-  {0x1F43F, 0x1F43F},
-  {0x1F440, 0x1F440},
-  {0x1F441, 0x1F441},
-  {0x1F442, 0x1F4F7},
-  {0x1F4F8, 0x1F4F8},
-  {0x1F4F9, 0x1F4FC},
-  {0x1F4FD, 0x1F4FE},
-  {0x1F4FF, 0x1F4FF},
-  {0x1F500, 0x1F53D},
-  {0x1F546, 0x1F54A},
-  {0x1F54B, 0x1F54F},
-  {0x1F550, 0x1F567},
-  {0x1F568, 0x1F579},
-  {0x1F57A, 0x1F57A},
-  {0x1F57B, 0x1F5A3},
-  {0x1F5A4, 0x1F5A4},
-  {0x1F5A5, 0x1F5FA},
-  {0x1F5FB, 0x1F5FF},
-  {0x1F600, 0x1F600},
-  {0x1F601, 0x1F610},
-  {0x1F611, 0x1F611},
-  {0x1F612, 0x1F614},
-  {0x1F615, 0x1F615},
-  {0x1F616, 0x1F616},
-  {0x1F617, 0x1F617},
-  {0x1F618, 0x1F618},
-  {0x1F619, 0x1F619},
-  {0x1F61A, 0x1F61A},
-  {0x1F61B, 0x1F61B},
-  {0x1F61C, 0x1F61E},
-  {0x1F61F, 0x1F61F},
-  {0x1F620, 0x1F625},
-  {0x1F626, 0x1F627},
-  {0x1F628, 0x1F62B},
-  {0x1F62C, 0x1F62C},
-  {0x1F62D, 0x1F62D},
-  {0x1F62E, 0x1F62F},
-  {0x1F630, 0x1F633},
-  {0x1F634, 0x1F634},
-  {0x1F635, 0x1F640},
-  {0x1F641, 0x1F642},
-  {0x1F643, 0x1F644},
-  {0x1F645, 0x1F64F},
-  {0x1F680, 0x1F6C5},
-  {0x1F6C6, 0x1F6CF},
-  {0x1F6D0, 0x1F6D0},
-  {0x1F6D1, 0x1F6D2},
-  {0x1F6D3, 0x1F6D4},
-  {0x1F6D5, 0x1F6DF},
-  {0x1F6E0, 0x1F6EC},
-  {0x1F6ED, 0x1F6EF},
-  {0x1F6F0, 0x1F6F3},
-  {0x1F6F4, 0x1F6F6},
-  {0x1F6F7, 0x1F6F8},
-  {0x1F6F9, 0x1F6F9},
-  {0x1F6FA, 0x1F6FF},
+  {0x1F249, 0x1F3FA},
+  {0x1F400, 0x1F53D},
+  {0x1F546, 0x1F64F},
+  {0x1F680, 0x1F6FF},
   {0x1F774, 0x1F77F},
-  {0x1F7D5, 0x1F7D8},
-  {0x1F7D9, 0x1F7FF},
+  {0x1F7D5, 0x1F7FF},
   {0x1F80C, 0x1F80F},
   {0x1F848, 0x1F84F},
   {0x1F85A, 0x1F85F},
   {0x1F888, 0x1F88F},
   {0x1F8AE, 0x1F8FF},
-  {0x1F90C, 0x1F90F},
-  {0x1F910, 0x1F918},
-  {0x1F919, 0x1F91E},
-  {0x1F91F, 0x1F91F},
-  {0x1F920, 0x1F927},
-  {0x1F928, 0x1F92F},
-  {0x1F930, 0x1F930},
-  {0x1F931, 0x1F932},
-  {0x1F933, 0x1F93A},
-  {0x1F93C, 0x1F93E},
-  {0x1F93F, 0x1F93F},
-  {0x1F940, 0x1F945},
-  {0x1F947, 0x1F94B},
-  {0x1F94C, 0x1F94C},
-  {0x1F94D, 0x1F94F},
-  {0x1F950, 0x1F95E},
-  {0x1F95F, 0x1F96B},
-  {0x1F96C, 0x1F970},
-  {0x1F971, 0x1F972},
-  {0x1F973, 0x1F976},
-  {0x1F977, 0x1F979},
-  {0x1F97A, 0x1F97A},
-  {0x1F97B, 0x1F97B},
-  {0x1F97C, 0x1F97F},
-  {0x1F980, 0x1F984},
-  {0x1F985, 0x1F991},
-  {0x1F992, 0x1F997},
-  {0x1F998, 0x1F9A2},
-  {0x1F9A3, 0x1F9AF},
-  {0x1F9B0, 0x1F9B9},
-  {0x1F9BA, 0x1F9BF},
-  {0x1F9C0, 0x1F9C0},
-  {0x1F9C1, 0x1F9C2},
-  {0x1F9C3, 0x1F9CF},
-  {0x1F9D0, 0x1F9E6},
-  {0x1F9E7, 0x1F9FF},
-  {0x1FA00, 0x1FA5F},
-  {0x1FA60, 0x1FA6D},
-  {0x1FA6E, 0x1FFFD},
+  {0x1F90C, 0x1F93A},
+  {0x1F93C, 0x1F945},
+  {0x1F947, 0x1FFFD},
 };
 
 #endif /* HB_UNICODE_EMOJI_TABLE_HH */
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index 7b821b4..4ac521d 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -33,6 +33,20 @@
 #include "hb-unicode.hh"
 
 
+/**
+ * SECTION: hb-unicode
+ * @title: hb-unicode
+ * @short_description: Unicode character property access
+ * @include: hb.h
+ *
+ * Unicode functions are used to access Unicode character properties.
+ * Client can pass its own Unicode functions to HarfBuzz, or access
+ * the built-in Unicode functions that come with HarfBuzz.
+ *
+ * With the Unicode functions, one can query variour Unicode character
+ * properties, such as General Category, Script, Combining Class, etc.
+ **/
+
 
 /*
  * hb_unicode_funcs_t
@@ -109,40 +123,23 @@
 }
 
 
-#define HB_UNICODE_FUNCS_IMPLEMENT_SET \
-  HB_UNICODE_FUNCS_IMPLEMENT (glib) \
-  HB_UNICODE_FUNCS_IMPLEMENT (icu) \
-  HB_UNICODE_FUNCS_IMPLEMENT (ucdn) \
-  HB_UNICODE_FUNCS_IMPLEMENT (nil) \
-  /* ^--- Add new callbacks before nil */
-
-#define hb_nil_get_unicode_funcs hb_unicode_funcs_get_empty
-
-/* Prototype them all */
-#define HB_UNICODE_FUNCS_IMPLEMENT(set) \
-extern "C" hb_unicode_funcs_t *hb_##set##_get_unicode_funcs (void);
-HB_UNICODE_FUNCS_IMPLEMENT_SET
-#undef HB_UNICODE_FUNCS_IMPLEMENT
-
+extern "C" hb_unicode_funcs_t *hb_glib_get_unicode_funcs ();
+extern "C" hb_unicode_funcs_t *hb_icu_get_unicode_funcs ();
+extern "C" hb_unicode_funcs_t *hb_ucdn_get_unicode_funcs ();
 
 hb_unicode_funcs_t *
-hb_unicode_funcs_get_default (void)
+hb_unicode_funcs_get_default ()
 {
-#define HB_UNICODE_FUNCS_IMPLEMENT(set) \
-  return hb_##set##_get_unicode_funcs ();
-
 #if defined(HAVE_UCDN)
-  HB_UNICODE_FUNCS_IMPLEMENT(ucdn)
+  return hb_ucdn_get_unicode_funcs ();
 #elif defined(HAVE_GLIB)
-  HB_UNICODE_FUNCS_IMPLEMENT(glib)
+  return hb_glib_get_unicode_funcs ();
 #elif defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN)
-  HB_UNICODE_FUNCS_IMPLEMENT(icu)
+  return hb_icu_get_unicode_funcs ();
 #else
 #define HB_UNICODE_FUNCS_NIL 1
-  HB_UNICODE_FUNCS_IMPLEMENT(nil)
+  return hb_unicode_funcs_get_empty ();
 #endif
-
-#undef HB_UNICODE_FUNCS_IMPLEMENT
 }
 
 #if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL)
@@ -154,7 +151,7 @@
  * hb_unicode_funcs_create: (Xconstructor)
  * @parent: (nullable):
  *
- * 
+ *
  *
  * Return value: (transfer full):
  *
@@ -190,7 +187,6 @@
   HB_OBJECT_HEADER_STATIC,
 
   nullptr, /* parent */
-  true, /* immutable */
   {
 #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil,
     HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
@@ -201,14 +197,14 @@
 /**
  * hb_unicode_funcs_get_empty:
  *
- * 
+ *
  *
  * Return value: (transfer full):
  *
  * Since: 0.9.2
  **/
 hb_unicode_funcs_t *
-hb_unicode_funcs_get_empty (void)
+hb_unicode_funcs_get_empty ()
 {
   return const_cast<hb_unicode_funcs_t *> (&Null(hb_unicode_funcs_t));
 }
@@ -217,7 +213,7 @@
  * hb_unicode_funcs_reference: (skip)
  * @ufuncs: Unicode functions.
  *
- * 
+ *
  *
  * Return value: (transfer full):
  *
@@ -233,7 +229,7 @@
  * hb_unicode_funcs_destroy: (skip)
  * @ufuncs: Unicode functions.
  *
- * 
+ *
  *
  * Since: 0.9.2
  **/
@@ -255,14 +251,14 @@
 /**
  * hb_unicode_funcs_set_user_data: (skip)
  * @ufuncs: Unicode functions.
- * @key: 
- * @data: 
- * @destroy: 
- * @replace: 
+ * @key:
+ * @data:
+ * @destroy:
+ * @replace:
  *
- * 
  *
- * Return value: 
+ *
+ * Return value:
  *
  * Since: 0.9.2
  **/
@@ -279,9 +275,9 @@
 /**
  * hb_unicode_funcs_get_user_data: (skip)
  * @ufuncs: Unicode functions.
- * @key: 
+ * @key:
  *
- * 
+ *
  *
  * Return value: (transfer none):
  *
@@ -299,44 +295,42 @@
  * hb_unicode_funcs_make_immutable:
  * @ufuncs: Unicode functions.
  *
- * 
+ *
  *
  * Since: 0.9.2
  **/
 void
 hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs)
 {
-  if (unlikely (hb_object_is_inert (ufuncs)))
-    return;
-  if (ufuncs->immutable)
+  if (hb_object_is_immutable (ufuncs))
     return;
 
-  ufuncs->immutable = true;
+  hb_object_make_immutable (ufuncs);
 }
 
 /**
  * hb_unicode_funcs_is_immutable:
  * @ufuncs: Unicode functions.
  *
- * 
  *
- * Return value: 
+ *
+ * Return value:
  *
  * Since: 0.9.2
  **/
 hb_bool_t
 hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs)
 {
-  return ufuncs->immutable;
+  return hb_object_is_immutable (ufuncs);
 }
 
 /**
  * hb_unicode_funcs_get_parent:
  * @ufuncs: Unicode functions.
  *
- * 
  *
- * Return value: 
+ *
+ * Return value:
  *
  * Since: 0.9.2
  **/
@@ -355,7 +349,7 @@
 				    void			   *user_data,	\
 				    hb_destroy_func_t		    destroy)	\
 {										\
-  if (ufuncs->immutable)							\
+  if (hb_object_is_immutable (ufuncs))						\
     return;									\
 										\
   if (ufuncs->destroy.name)							\
@@ -390,13 +384,13 @@
 /**
  * hb_unicode_compose:
  * @ufuncs: Unicode functions.
- * @a: 
- * @b: 
+ * @a:
+ * @b:
  * @ab: (out):
  *
- * 
  *
- * Return value: 
+ *
+ * Return value:
  *
  * Since: 0.9.2
  **/
@@ -412,13 +406,13 @@
 /**
  * hb_unicode_decompose:
  * @ufuncs: Unicode functions.
- * @ab: 
+ * @ab:
  * @a: (out):
  * @b: (out):
  *
- * 
  *
- * Return value: 
+ *
+ * Return value:
  *
  * Since: 0.9.2
  **/
@@ -434,14 +428,15 @@
 /**
  * hb_unicode_decompose_compatibility:
  * @ufuncs: Unicode functions.
- * @u: 
+ * @u:
  * @decomposed: (out):
  *
- * 
  *
- * Return value: 
+ *
+ * Return value:
  *
  * Since: 0.9.2
+ * Deprecated: 2.0.0
  **/
 unsigned int
 hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
@@ -575,8 +570,8 @@
 bool
 _hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp)
 {
-  return hb_bsearch_r (&cp, _hb_unicode_emoji_Extended_Pictographic_table,
-		       ARRAY_LENGTH (_hb_unicode_emoji_Extended_Pictographic_table),
-		       sizeof (hb_unicode_range_t),
-		       hb_unicode_range_t::cmp, nullptr);
+  return hb_bsearch (&cp, _hb_unicode_emoji_Extended_Pictographic_table,
+		     ARRAY_LENGTH (_hb_unicode_emoji_Extended_Pictographic_table),
+		     sizeof (hb_unicode_range_t),
+		     hb_unicode_range_t::cmp);
 }
diff --git a/src/hb-unicode.hh b/src/hb-unicode.hh
index 6d6a4fa..82ebb10 100644
--- a/src/hb-unicode.hh
+++ b/src/hb-unicode.hh
@@ -63,34 +63,31 @@
 struct hb_unicode_funcs_t
 {
   hb_object_header_t header;
-  ASSERT_POD ();
 
   hb_unicode_funcs_t *parent;
 
-  bool immutable;
-
 #define HB_UNICODE_FUNC_IMPLEMENT(return_type, name) \
-  inline return_type name (hb_codepoint_t unicode) { return func.name (this, unicode, user_data.name); }
+  return_type name (hb_codepoint_t unicode) { return func.name (this, unicode, user_data.name); }
 HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE
 #undef HB_UNICODE_FUNC_IMPLEMENT
 
-  inline hb_bool_t compose (hb_codepoint_t a, hb_codepoint_t b,
-			    hb_codepoint_t *ab)
+  hb_bool_t compose (hb_codepoint_t a, hb_codepoint_t b,
+		     hb_codepoint_t *ab)
   {
     *ab = 0;
     if (unlikely (!a || !b)) return false;
     return func.compose (this, a, b, ab, user_data.compose);
   }
 
-  inline hb_bool_t decompose (hb_codepoint_t ab,
-			      hb_codepoint_t *a, hb_codepoint_t *b)
+  hb_bool_t decompose (hb_codepoint_t ab,
+		       hb_codepoint_t *a, hb_codepoint_t *b)
   {
     *a = ab; *b = 0;
     return func.decompose (this, ab, a, b, user_data.decompose);
   }
 
-  inline unsigned int decompose_compatibility (hb_codepoint_t  u,
-					       hb_codepoint_t *decomposed)
+  unsigned int decompose_compatibility (hb_codepoint_t  u,
+					hb_codepoint_t *decomposed)
   {
     unsigned int ret = func.decompose_compatibility (this, u, decomposed, user_data.decompose_compatibility);
     if (ret == 1 && u == decomposed[0]) {
@@ -101,7 +98,7 @@
     return ret;
   }
 
-  inline unsigned int
+  unsigned int
   modified_combining_class (hb_codepoint_t u)
   {
     /* XXX This hack belongs to the Myanmar shaper. */
@@ -120,14 +117,14 @@
     return _hb_modified_combining_class[combining_class (u)];
   }
 
-  static inline hb_bool_t
+  static hb_bool_t
   is_variation_selector (hb_codepoint_t unicode)
   {
     /* U+180B..180D MONGOLIAN FREE VARIATION SELECTORs are handled in the
      * Arabic shaper.  No need to match them here. */
     return unlikely (hb_in_ranges<hb_codepoint_t> (unicode,
-				   0xFE00u, 0xFE0Fu, /* VARIATION SELECTOR-1..16 */
-				   0xE0100u, 0xE01EFu));  /* VARIATION SELECTOR-17..256 */
+						   0xFE00u, 0xFE0Fu, /* VARIATION SELECTOR-1..16 */
+						   0xE0100u, 0xE01EFu));  /* VARIATION SELECTOR-17..256 */
   }
 
   /* Default_Ignorable codepoints:
@@ -167,7 +164,7 @@
    * E0100..E01EF  # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
    * E01F0..E0FFF  # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
    */
-  static inline hb_bool_t
+  static hb_bool_t
   is_default_ignorable (hb_codepoint_t ch)
   {
     hb_codepoint_t plane = ch >> 16;
@@ -219,7 +216,7 @@
     SPACE_PUNCTUATION,
     SPACE_NARROW,
   };
-  static inline space_t
+  static space_t
   space_fallback_type (hb_codepoint_t u)
   {
     switch (u)
@@ -369,7 +366,7 @@
 struct hb_unicode_range_t
 {
   static int
-  cmp (const void *_key, const void *_item, void *_arg)
+  cmp (const void *_key, const void *_item)
   {
     hb_codepoint_t cp = *((hb_codepoint_t *) _key);
     const hb_unicode_range_t *range = (hb_unicode_range_t *) _item;
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 44a67ae..8d0473d 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -25,7 +25,6 @@
  */
 
 #include "hb.hh"
-#define HB_SHAPER uniscribe
 #include "hb-shaper-impl.hh"
 
 #include <windows.h>
@@ -36,7 +35,17 @@
 
 #include "hb-open-file.hh"
 #include "hb-ot-name-table.hh"
-#include "hb-ot-tag.h"
+#include "hb-ot-layout.h"
+
+
+/**
+ * SECTION:hb-uniscribe
+ * @title: hb-uniscribe
+ * @short_description: Windows integration
+ * @include: hb-uniscribe.h
+ *
+ * Functions for using HarfBuzz with the Windows fonts.
+ **/
 
 
 static inline uint16_t hb_uint16_swap (const uint16_t v)
@@ -196,7 +205,7 @@
   SSOT ScriptShapeOpenType;
   SPOT ScriptPlaceOpenType;
 
-  inline void init (void)
+  void init ()
   {
     HMODULE hinstLib;
     this->ScriptItemizeOpenType = nullptr;
@@ -223,12 +232,12 @@
 };
 
 
-static void free_static_uniscribe_shaper_funcs (void);
+static void free_static_uniscribe_shaper_funcs ();
 
 static struct hb_uniscribe_shaper_funcs_lazy_loader_t : hb_lazy_loader_t<hb_uniscribe_shaper_funcs_t,
 									 hb_uniscribe_shaper_funcs_lazy_loader_t>
 {
-  static inline hb_uniscribe_shaper_funcs_t *create (void)
+  static hb_uniscribe_shaper_funcs_t *create ()
   {
     hb_uniscribe_shaper_funcs_t *funcs = (hb_uniscribe_shaper_funcs_t *) calloc (1, sizeof (hb_uniscribe_shaper_funcs_t));
     if (unlikely (!funcs))
@@ -236,32 +245,32 @@
 
     funcs->init ();
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
     atexit (free_static_uniscribe_shaper_funcs);
 #endif
 
     return funcs;
   }
-  static inline void destroy (hb_uniscribe_shaper_funcs_t *p)
+  static void destroy (hb_uniscribe_shaper_funcs_t *p)
   {
     free ((void *) p);
   }
-  static inline hb_uniscribe_shaper_funcs_t *get_null (void)
+  static hb_uniscribe_shaper_funcs_t *get_null ()
   {
     return nullptr;
   }
 } static_uniscribe_shaper_funcs;
 
-#ifdef HB_USE_ATEXIT
+#if HB_USE_ATEXIT
 static
-void free_static_uniscribe_shaper_funcs (void)
+void free_static_uniscribe_shaper_funcs ()
 {
   static_uniscribe_shaper_funcs.free_instance ();
 }
 #endif
 
 static hb_uniscribe_shaper_funcs_t *
-hb_uniscribe_shaper_get_funcs (void)
+hb_uniscribe_shaper_get_funcs ()
 {
   return static_uniscribe_shaper_funcs.get_unconst ();
 }
@@ -279,9 +288,8 @@
 	   a->rec.lParameter < b->rec.lParameter ? -1 : a->rec.lParameter > b->rec.lParameter ? 1 :
 	   0;
   }
-  bool operator== (const active_feature_t *f) {
-    return cmp (this, f) == 0;
-  }
+  bool operator== (const active_feature_t *f)
+  { return cmp (this, f) == 0; }
 };
 
 struct feature_event_t {
@@ -289,7 +297,8 @@
   bool start;
   active_feature_t feature;
 
-  static int cmp (const void *pa, const void *pb) {
+  static int cmp (const void *pa, const void *pb)
+  {
     const feature_event_t *a = (const feature_event_t *) pa;
     const feature_event_t *b = (const feature_event_t *) pb;
     return a->index < b->index ? -1 : a->index > b->index ? 1 :
@@ -304,9 +313,6 @@
   unsigned int index_last;  /* == end - 1 */
 };
 
-HB_SHAPER_DATA_ENSURE_DEFINE(uniscribe, face)
-HB_SHAPER_DATA_ENSURE_DEFINE(uniscribe, font)
-
 
 /*
  * shaper face data
@@ -493,11 +499,12 @@
  * shaper font data
  */
 
-struct hb_uniscribe_font_data_t {
+struct hb_uniscribe_font_data_t
+{
   HDC hdc;
-  LOGFONTW log_font;
+  mutable LOGFONTW log_font;
   HFONT hfont;
-  SCRIPT_CACHE script_cache;
+  mutable SCRIPT_CACHE script_cache;
   double x_mult, y_mult; /* From LOGFONT space to HB space. */
 };
 
@@ -510,10 +517,7 @@
   lf->lfHeight = - (int) font_size;
   lf->lfCharSet = DEFAULT_CHARSET;
 
-  hb_face_t *face = font->face;
-  hb_uniscribe_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-
-  memcpy (lf->lfFaceName, face_data->face_name, sizeof (lf->lfFaceName));
+  memcpy (lf->lfFaceName, font->face->data.uniscribe->face_name, sizeof (lf->lfFaceName));
 
   return true;
 }
@@ -521,8 +525,6 @@
 hb_uniscribe_font_data_t *
 _hb_uniscribe_shaper_font_data_create (hb_font_t *font)
 {
-  if (unlikely (!hb_uniscribe_shaper_face_data_ensure (font->face))) return nullptr;
-
   hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) calloc (1, sizeof (hb_uniscribe_font_data_t));
   if (unlikely (!data))
     return nullptr;
@@ -576,39 +578,15 @@
 LOGFONTW *
 hb_uniscribe_font_get_logfontw (hb_font_t *font)
 {
-  if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return nullptr;
-  hb_uniscribe_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
-  return &font_data->log_font;
+  const hb_uniscribe_font_data_t *data =  font->data.uniscribe;
+  return data ? &data->log_font : nullptr;
 }
 
 HFONT
 hb_uniscribe_font_get_hfont (hb_font_t *font)
 {
-  if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return nullptr;
-  hb_uniscribe_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
-  return font_data->hfont;
-}
-
-
-/*
- * shaper shape_plan data
- */
-
-struct hb_uniscribe_shape_plan_data_t {};
-
-hb_uniscribe_shape_plan_data_t *
-_hb_uniscribe_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
-					     const hb_feature_t *user_features HB_UNUSED,
-					     unsigned int        num_user_features HB_UNUSED,
-					     const int          *coords HB_UNUSED,
-					     unsigned int        num_coords HB_UNUSED)
-{
-  return (hb_uniscribe_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
-}
-
-void
-_hb_uniscribe_shaper_shape_plan_data_destroy (hb_uniscribe_shape_plan_data_t *data HB_UNUSED)
-{
+  const hb_uniscribe_font_data_t *data =  font->data.uniscribe;
+  return data ? data->hfont : nullptr;
 }
 
 
@@ -625,19 +603,19 @@
 		     unsigned int        num_features)
 {
   hb_face_t *face = font->face;
-  hb_uniscribe_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-  hb_uniscribe_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
+  const hb_uniscribe_face_data_t *face_data = face->data.uniscribe;
+  const hb_uniscribe_font_data_t *font_data = font->data.uniscribe;
   hb_uniscribe_shaper_funcs_t *funcs = face_data->funcs;
 
   /*
    * Set up features.
    */
-  hb_auto_t<hb_vector_t<OPENTYPE_FEATURE_RECORD> > feature_records;
-  hb_auto_t<hb_vector_t<range_record_t> > range_records;
+  hb_vector_t<OPENTYPE_FEATURE_RECORD> feature_records;
+  hb_vector_t<range_record_t> range_records;
   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++)
     {
       active_feature_t feature;
@@ -672,7 +650,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++)
     {
@@ -717,7 +695,7 @@
       {
         active_feature_t *feature = active_features.find (&event->feature);
 	if (feature)
-	  active_features.remove (feature - active_features.arrayZ());
+	  active_features.remove (feature - active_features.arrayZ ());
       }
     }
 
@@ -728,7 +706,7 @@
     for (unsigned int i = 0; i < range_records.len; i++)
     {
       range_record_t *range = &range_records[i];
-      range->props.potfRecords = feature_records.arrayZ() + reinterpret_cast<uintptr_t> (range->props.potfRecords);
+      range->props.potfRecords = feature_records + reinterpret_cast<uintptr_t> (range->props.potfRecords);
     }
   }
 
@@ -843,9 +821,15 @@
 
 #undef MAX_ITEMS
 
-  OPENTYPE_TAG language_tag = hb_uint32_swap (hb_ot_tag_from_language (buffer->props.language));
-  hb_auto_t<hb_vector_t<TEXTRANGE_PROPERTIES*> > range_properties;
-  hb_auto_t<hb_vector_t<int> > range_char_counts;
+  hb_tag_t lang_tag;
+  unsigned int lang_count = 1;
+  hb_ot_tags_from_script_and_language (buffer->props.script,
+				       buffer->props.language,
+				       nullptr, nullptr,
+				       &lang_count, &lang_tag);
+  OPENTYPE_TAG language_tag = hb_uint32_swap (lang_count ? lang_tag : HB_TAG_NONE);
+  hb_vector_t<TEXTRANGE_PROPERTIES*> range_properties;
+  hb_vector_t<int> range_char_counts;
 
   unsigned int glyphs_offset = 0;
   unsigned int glyphs_len;
@@ -902,8 +886,8 @@
 				     &items[i].a,
 				     script_tags[i],
 				     language_tag,
-				     range_char_counts.arrayZ(),
-				     range_properties.arrayZ(),
+				     range_char_counts.arrayZ (),
+				     range_properties.arrayZ (),
 				     range_properties.len,
 				     pchars + chars_offset,
 				     item_chars_len,
@@ -943,8 +927,8 @@
 				     &items[i].a,
 				     script_tags[i],
 				     language_tag,
-				     range_char_counts.arrayZ(),
-				     range_properties.arrayZ(),
+				     range_char_counts.arrayZ (),
+				     range_properties.arrayZ (),
 				     range_properties.len,
 				     pchars + chars_offset,
 				     log_clusters + chars_offset,
diff --git a/src/hb-utf.hh b/src/hb-utf.hh
index eccb632..59ec75e 100644
--- a/src/hb-utf.hh
+++ b/src/hb-utf.hh
@@ -29,14 +29,16 @@
 
 #include "hb.hh"
 
+#include "hb-open-type.hh"
+
 
 struct hb_utf8_t
 {
   typedef uint8_t codepoint_t;
 
-  static inline const uint8_t *
-  next (const uint8_t *text,
-	const uint8_t *end,
+  static const codepoint_t *
+  next (const codepoint_t *text,
+	const codepoint_t *end,
 	hb_codepoint_t *unicode,
 	hb_codepoint_t replacement)
   {
@@ -103,13 +105,13 @@
     return text;
   }
 
-  static inline const uint8_t *
-  prev (const uint8_t *text,
-	const uint8_t *start,
+  static const codepoint_t *
+  prev (const codepoint_t *text,
+	const codepoint_t *start,
 	hb_codepoint_t *unicode,
 	hb_codepoint_t replacement)
   {
-    const uint8_t *end = text--;
+    const codepoint_t *end = text--;
     while (start < text && (*text & 0xc0) == 0x80 && end - text < 4)
       text--;
 
@@ -120,21 +122,70 @@
     return end - 1;
   }
 
-  static inline unsigned int
-  strlen (const uint8_t *text)
+  static unsigned int
+  strlen (const codepoint_t *text)
+  { return ::strlen ((const char *) text); }
+
+  static unsigned int
+  encode_len (hb_codepoint_t unicode)
   {
-    return ::strlen ((const char *) text);
+    if (unicode <   0x0080u) return 1;
+    if (unicode <   0x0800u) return 2;
+    if (unicode <  0x10000u) return 3;
+    if (unicode < 0x110000u) return 4;
+    return 3;
+  }
+
+  static codepoint_t *
+  encode (codepoint_t *text,
+	  const codepoint_t *end,
+	  hb_codepoint_t unicode)
+  {
+    if (unlikely (unicode >= 0xD800u && (unicode <= 0xDFFFu || unicode > 0x10FFFFu)))
+      unicode = 0xFFFDu;
+    if (unicode < 0x0080u)
+     *text++ = unicode;
+    else if (unicode < 0x0800u)
+    {
+      if (end - text >= 2)
+      {
+	*text++ =  0xC0u + (0x1Fu & (unicode >>  6));
+	*text++ =  0x80u + (0x3Fu & (unicode      ));
+      }
+    }
+    else if (unicode < 0x10000u)
+    {
+      if (end - text >= 3)
+      {
+	*text++ =  0xE0u + (0x0Fu & (unicode >> 12));
+	*text++ =  0x80u + (0x3Fu & (unicode >>  6));
+	*text++ =  0x80u + (0x3Fu & (unicode      ));
+      }
+    }
+    else
+    {
+      if (end - text >= 4)
+      {
+	*text++ =  0xF0u + (0x07u & (unicode >> 18));
+	*text++ =  0x80u + (0x3Fu & (unicode >> 12));
+	*text++ =  0x80u + (0x3Fu & (unicode >>  6));
+	*text++ =  0x80u + (0x3Fu & (unicode      ));
+      }
+    }
+    return text;
   }
 };
 
 
-struct hb_utf16_t
+template <typename TCodepoint>
+struct hb_utf16_xe_t
 {
-  typedef uint16_t codepoint_t;
+  static_assert (sizeof (TCodepoint) == 2, "");
+  typedef TCodepoint codepoint_t;
 
-  static inline const uint16_t *
-  next (const uint16_t *text,
-	const uint16_t *end,
+  static const codepoint_t *
+  next (const codepoint_t *text,
+	const codepoint_t *end,
 	hb_codepoint_t *unicode,
 	hb_codepoint_t replacement)
   {
@@ -164,9 +215,9 @@
     return text;
   }
 
-  static inline const uint16_t *
-  prev (const uint16_t *text,
-	const uint16_t *start,
+  static const codepoint_t *
+  prev (const codepoint_t *text,
+	const codepoint_t *start,
 	hb_codepoint_t *unicode,
 	hb_codepoint_t replacement)
   {
@@ -197,24 +248,52 @@
   }
 
 
-  static inline unsigned int
-  strlen (const uint16_t *text)
+  static unsigned int
+  strlen (const codepoint_t *text)
   {
     unsigned int l = 0;
     while (*text++) l++;
     return l;
   }
+
+  static unsigned int
+  encode_len (hb_codepoint_t unicode)
+  {
+    return unicode < 0x10000 ? 1 : 2;
+  }
+
+  static codepoint_t *
+  encode (codepoint_t *text,
+	  const codepoint_t *end,
+	  hb_codepoint_t unicode)
+  {
+    if (unlikely (unicode >= 0xD800u && (unicode <= 0xDFFFu || unicode > 0x10FFFFu)))
+      unicode = 0xFFFDu;
+    if (unicode < 0x10000u)
+     *text++ = unicode;
+    else if (end - text >= 2)
+    {
+      unicode -= 0x10000u;
+      *text++ =  0xD800u + (unicode >> 10);
+      *text++ =  0xDC00u + (unicode & 0x03FFu);
+    }
+    return text;
+  }
 };
 
+typedef hb_utf16_xe_t<uint16_t> hb_utf16_t;
+typedef hb_utf16_xe_t<OT::HBUINT16> hb_utf16_be_t;
 
-template <bool validate=true>
-struct hb_utf32_t
+
+template <typename TCodepoint, bool validate=true>
+struct hb_utf32_xe_t
 {
-  typedef uint32_t codepoint_t;
+  static_assert (sizeof (TCodepoint) == 4, "");
+  typedef TCodepoint codepoint_t;
 
-  static inline const uint32_t *
-  next (const uint32_t *text,
-	const uint32_t *end HB_UNUSED,
+  static const TCodepoint *
+  next (const TCodepoint *text,
+	const TCodepoint *end HB_UNUSED,
 	hb_codepoint_t *unicode,
 	hb_codepoint_t replacement)
   {
@@ -224,9 +303,9 @@
     return text;
   }
 
-  static inline const uint32_t *
-  prev (const uint32_t *text,
-	const uint32_t *start HB_UNUSED,
+  static const TCodepoint *
+  prev (const TCodepoint *text,
+	const TCodepoint *start HB_UNUSED,
 	hb_codepoint_t *unicode,
 	hb_codepoint_t replacement)
   {
@@ -236,23 +315,43 @@
     return text;
   }
 
-  static inline unsigned int
-  strlen (const uint32_t *text)
+  static unsigned int
+  strlen (const TCodepoint *text)
   {
     unsigned int l = 0;
     while (*text++) l++;
     return l;
   }
+
+  static unsigned int
+  encode_len (hb_codepoint_t unicode HB_UNUSED)
+  {
+    return 1;
+  }
+
+  static codepoint_t *
+  encode (codepoint_t *text,
+	  const codepoint_t *end HB_UNUSED,
+	  hb_codepoint_t unicode)
+  {
+    if (validate && unlikely (unicode >= 0xD800u && (unicode <= 0xDFFFu || unicode > 0x10FFFFu)))
+      unicode = 0xFFFDu;
+    *text++ = unicode;
+    return text;
+  }
 };
 
+typedef hb_utf32_xe_t<uint32_t> hb_utf32_t;
+typedef hb_utf32_xe_t<uint32_t, false> hb_utf32_novalidate_t;
+
 
 struct hb_latin1_t
 {
   typedef uint8_t codepoint_t;
 
-  static inline const uint8_t *
-  next (const uint8_t *text,
-	const uint8_t *end HB_UNUSED,
+  static const codepoint_t *
+  next (const codepoint_t *text,
+	const codepoint_t *end HB_UNUSED,
 	hb_codepoint_t *unicode,
 	hb_codepoint_t replacement HB_UNUSED)
   {
@@ -260,23 +359,95 @@
     return text;
   }
 
-  static inline const uint8_t *
-  prev (const uint8_t *text,
-	const uint8_t *start HB_UNUSED,
+  static const codepoint_t *
+  prev (const codepoint_t *text,
+	const codepoint_t *start HB_UNUSED,
 	hb_codepoint_t *unicode,
-	hb_codepoint_t replacement)
+	hb_codepoint_t replacement HB_UNUSED)
   {
     *unicode = *--text;
     return text;
   }
 
-  static inline unsigned int
-  strlen (const uint8_t *text)
+  static unsigned int
+  strlen (const codepoint_t *text)
   {
     unsigned int l = 0;
     while (*text++) l++;
     return l;
   }
+
+  static unsigned int
+  encode_len (hb_codepoint_t unicode HB_UNUSED)
+  {
+    return 1;
+  }
+
+  static codepoint_t *
+  encode (codepoint_t *text,
+	  const codepoint_t *end HB_UNUSED,
+	  hb_codepoint_t unicode)
+  {
+    if (unlikely (unicode >= 0x0100u))
+      unicode = '?';
+    *text++ = unicode;
+    return text;
+  }
+};
+
+
+struct hb_ascii_t
+{
+  typedef uint8_t codepoint_t;
+
+  static const codepoint_t *
+  next (const codepoint_t *text,
+	const codepoint_t *end HB_UNUSED,
+	hb_codepoint_t *unicode,
+	hb_codepoint_t replacement HB_UNUSED)
+  {
+    *unicode = *text++;
+    if (*unicode >= 0x0080u)
+      *unicode = replacement;
+    return text;
+  }
+
+  static const codepoint_t *
+  prev (const codepoint_t *text,
+	const codepoint_t *start HB_UNUSED,
+	hb_codepoint_t *unicode,
+	hb_codepoint_t replacement)
+  {
+    *unicode = *--text;
+    if (*unicode >= 0x0080u)
+      *unicode = replacement;
+    return text;
+  }
+
+  static unsigned int
+  strlen (const codepoint_t *text)
+  {
+    unsigned int l = 0;
+    while (*text++) l++;
+    return l;
+  }
+
+  static unsigned int
+  encode_len (hb_codepoint_t unicode HB_UNUSED)
+  {
+    return 1;
+  }
+
+  static codepoint_t *
+  encode (codepoint_t *text,
+	  const codepoint_t *end HB_UNUSED,
+	  hb_codepoint_t unicode)
+  {
+    if (unlikely (unicode >= 0x0080u))
+      unicode = '?';
+    *text++ = unicode;
+    return text;
+  }
 };
 
 #endif /* HB_UTF_HH */
diff --git a/src/hb-vector.hh b/src/hb-vector.hh
index 766e5fb..d8e3f4e 100644
--- a/src/hb-vector.hh
+++ b/src/hb-vector.hh
@@ -21,7 +21,6 @@
  * 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
  */
 
@@ -29,60 +28,120 @@
 #define HB_VECTOR_HH
 
 #include "hb.hh"
+#include "hb-array.hh"
 
 
-template <typename Type, unsigned int StaticSize=8>
+template <typename Type, unsigned int PreallocedCount=8>
 struct hb_vector_t
 {
+  typedef Type ItemType;
+  enum { item_size = hb_static_size (Type) };
+
+  HB_NO_COPY_ASSIGN_TEMPLATE2 (hb_vector_t, Type, PreallocedCount);
+  hb_vector_t ()  { init (); }
+  ~hb_vector_t () { fini (); }
+
   unsigned int len;
   private:
   unsigned int allocated; /* == 0 means allocation failed. */
   Type *arrayZ_;
-  Type static_array[StaticSize];
+  Type static_array[PreallocedCount];
   public:
 
-  void init (void)
+  void init ()
   {
     len = 0;
     allocated = ARRAY_LENGTH (static_array);
     arrayZ_ = nullptr;
   }
 
-  inline Type * arrayZ (void)
-  { return arrayZ_ ? arrayZ_ : static_array; }
-  inline const Type * arrayZ (void) const
-  { return arrayZ_ ? arrayZ_ : static_array; }
-
-  inline Type& operator [] (unsigned int i)
+  void fini ()
   {
+    if (arrayZ_)
+      free (arrayZ_);
+    arrayZ_ = nullptr;
+    allocated = len = 0;
+  }
+  void fini_deep ()
+  {
+    Type *array = arrayZ();
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+      array[i].fini ();
+    fini ();
+  }
+
+  Type * arrayZ ()             { return arrayZ_ ? arrayZ_ : static_array; }
+  const Type * arrayZ () const { return arrayZ_ ? arrayZ_ : static_array; }
+
+  Type& operator [] (int i_)
+  {
+    unsigned int i = (unsigned int) i_;
     if (unlikely (i >= len))
       return Crap (Type);
     return arrayZ()[i];
   }
-  inline const Type& operator [] (unsigned int i) const
+  const Type& operator [] (int i_) const
   {
+    unsigned int i = (unsigned int) i_;
     if (unlikely (i >= len))
       return Null(Type);
     return arrayZ()[i];
   }
 
-  inline Type *push (void)
+  hb_array_t<Type> as_array ()
+  { return hb_array (arrayZ(), len); }
+  hb_array_t<const Type> as_array () const
+  { return hb_array (arrayZ(), len); }
+
+  hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
+  { return as_array ().sub_array (start_offset, count);}
+  hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
+  { return as_array ().sub_array (start_offset, count);}
+  hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
+  { return as_array ().sub_array (start_offset, count);}
+  hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
+  { return as_array ().sub_array (start_offset, count);}
+
+  hb_sorted_array_t<Type> as_sorted_array ()
+  { return hb_sorted_array (arrayZ(), len); }
+  hb_sorted_array_t<const Type> as_sorted_array () const
+  { return hb_sorted_array (arrayZ(), len); }
+
+  hb_array_t<const Type> sorted_sub_array (unsigned int start_offset, unsigned int count) const
+  { return as_sorted_array ().sorted_sub_array (start_offset, count);}
+  hb_array_t<const Type> sorted_sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
+  { return as_sorted_array ().sorted_sub_array (start_offset, count);}
+  hb_array_t<Type> sorted_sub_array (unsigned int start_offset, unsigned int count)
+  { return as_sorted_array ().sorted_sub_array (start_offset, count);}
+  hb_array_t<Type> sorted_sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
+  { return as_sorted_array ().sorted_sub_array (start_offset, count);}
+
+  template <typename T> explicit_operator T * () { return arrayZ(); }
+  template <typename T> explicit_operator const T * () const { return arrayZ(); }
+  operator hb_array_t<Type> ()             { return as_array (); }
+  operator hb_array_t<const Type> () const { return as_array (); }
+
+  Type * operator  + (unsigned int i) { return arrayZ() + i; }
+  const Type * operator  + (unsigned int i) const { return arrayZ() + i; }
+
+  Type *push ()
   {
     if (unlikely (!resize (len + 1)))
       return &Crap(Type);
     return &arrayZ()[len - 1];
   }
-  inline Type *push (const Type& v)
+  Type *push (const Type& v)
   {
     Type *p = push ();
     *p = v;
     return p;
   }
 
-  inline bool in_error (void) const { return allocated == 0; }
+  bool in_error () const { return allocated == 0; }
 
   /* Allocate for size but don't adjust len. */
-  inline bool alloc (unsigned int size)
+  bool alloc (unsigned int size)
   {
     if (unlikely (!allocated))
       return false;
@@ -123,7 +182,7 @@
     return true;
   }
 
-  inline bool resize (int size_)
+  bool resize (int size_)
   {
     unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
     if (!alloc (size))
@@ -136,13 +195,13 @@
     return true;
   }
 
-  inline void pop (void)
+  void pop ()
   {
     if (!len) return;
     len--;
   }
 
-  inline void remove (unsigned int i)
+  void remove (unsigned int i)
   {
     if (unlikely (i >= len))
       return;
@@ -153,7 +212,7 @@
     len--;
   }
 
-  inline void shrink (int size_)
+  void shrink (int size_)
   {
     unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
      if (size < len)
@@ -161,7 +220,7 @@
   }
 
   template <typename T>
-  inline Type *find (T v)
+  Type *find (T v)
   {
     Type *array = arrayZ();
     for (unsigned int i = 0; i < len; i++)
@@ -170,7 +229,7 @@
     return nullptr;
   }
   template <typename T>
-  inline const Type *find (T v) const
+  const Type *find (T v) const
   {
     const Type *array = arrayZ();
     for (unsigned int i = 0; i < len; i++)
@@ -179,93 +238,29 @@
     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);
-  }
+  void qsort (int (*cmp)(const void*, const void*))
+  { as_array ().qsort (cmp); }
+  void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1)
+  { as_array ().qsort (start, end); }
 
   template <typename T>
-  inline Type *lsearch (const T &x)
-  {
-    Type *array = arrayZ();
-    for (unsigned int i = 0; i < len; i++)
-      if (0 == array[i].cmp (&x))
-	return &array[i];
-    return nullptr;
-  }
+  Type *lsearch (const T &x, Type *not_found = nullptr)
+  { return as_array ().lsearch (x, not_found); }
   template <typename T>
-  inline const Type *lsearch (const T &x) const
-  {
-    const Type *array = arrayZ();
-    for (unsigned int i = 0; i < len; i++)
-      if (0 == array[i].cmp (&x))
-	return &array[i];
-    return nullptr;
-  }
+  const Type *lsearch (const T &x, const Type *not_found = nullptr) const
+  { return as_array ().lsearch (x, not_found); }
 
   template <typename T>
-  inline Type *bsearch (const T &x)
-  {
-    unsigned int i;
-    return bfind (x, &i) ? &arrayZ()[i] : nullptr;
-  }
+  Type *bsearch (const T &x, Type *not_found = nullptr)
+  { return as_sorted_array ().bsearch (x, not_found); }
   template <typename T>
-  inline const Type *bsearch (const T &x) const
-  {
-    unsigned int i;
-    return bfind (x, &i) ? &arrayZ()[i] : nullptr;
-  }
+  const Type *bsearch (const T &x, const Type *not_found = nullptr) const
+  { return as_sorted_array ().bsearch (x, not_found); }
   template <typename T>
-  inline bool bfind (const T &x, unsigned int *i) const
-  {
-    int min = 0, max = (int) this->len - 1;
-    const Type *array = this->arrayZ();
-    while (min <= max)
-    {
-      int mid = (min + max) / 2;
-      int c = array[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 && array[max].cmp (&x) > 0))
-      max++;
-    *i = max;
-    return false;
-  }
-
-  inline void fini_deep (void)
-  {
-    Type *array = arrayZ();
-    unsigned int count = len;
-    for (unsigned int i = 0; i < count; i++)
-      array[i].fini ();
-    fini ();
-  }
-
-  inline void fini (void)
-  {
-    if (arrayZ_)
-      free (arrayZ_);
-    arrayZ_ = nullptr;
-    allocated = len = 0;
-  }
+  bool bfind (const T &x, unsigned int *i = nullptr,
+		     hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
+		     unsigned int to_store = (unsigned int) -1) const
+  { return as_sorted_array ().bfind (x, i, not_found, to_store); }
 };
 
 
diff --git a/src/hb-version.h b/src/hb-version.h
index a8db516..0c82d5b 100644
--- a/src/hb-version.h
+++ b/src/hb-version.h
@@ -37,10 +37,10 @@
 
 
 #define HB_VERSION_MAJOR 2
-#define HB_VERSION_MINOR 0
-#define HB_VERSION_MICRO 2
+#define HB_VERSION_MINOR 3
+#define HB_VERSION_MICRO 0
 
-#define HB_VERSION_STRING "2.0.2"
+#define HB_VERSION_STRING "2.3.0"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
diff --git a/src/hb.h b/src/hb.h
index fc75a69..c5e7072 100644
--- a/src/hb.h
+++ b/src/hb.h
@@ -28,10 +28,6 @@
 #define HB_H
 #define HB_H_IN
 
-#ifndef HB_EXTERN
-#define HB_EXTERN extern
-#endif
-
 #include "hb-blob.h"
 #include "hb-buffer.h"
 #include "hb-common.h"
diff --git a/src/hb.hh b/src/hb.hh
index 098b566..a55e92d 100644
--- a/src/hb.hh
+++ b/src/hb.hh
@@ -29,12 +29,36 @@
 #ifndef HB_HH
 #define HB_HH
 
-#define _GNU_SOURCE 1
-
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
+/*
+ * Following added based on what AC_USE_SYSTEM_EXTENSIONS adds to
+ * config.h.in.  Copied here for the convenience of those embedding
+ * HarfBuzz and not using our build system.
+ */
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
 #ifndef _POSIX_C_SOURCE
 #define _POSIX_C_SOURCE 200809L
 #endif
@@ -47,6 +71,10 @@
 #define HB_H_IN
 #include "hb-ot.h"
 #define HB_OT_H_IN
+#include "hb-aat.h"
+#define HB_AAT_H_IN
+
+#include "hb-aat.h"
 
 #include <math.h>
 #include <stdlib.h>
@@ -133,14 +161,14 @@
 
 /* https://github.com/harfbuzz/harfbuzz/issues/1127 */
 #ifndef explicit_operator
-#define explicit_operator
+#define explicit_operator operator
 #endif
 
 #else /* __cplusplus >= 201103L */
 
 /* https://github.com/harfbuzz/harfbuzz/issues/1127 */
 #ifndef explicit_operator
-#define explicit_operator explicit
+#define explicit_operator explicit operator
 #endif
 
 #endif /* __cplusplus < 201103L */
@@ -246,7 +274,7 @@
 #endif
 
 
-#if defined(_WIN32) || defined(__CYGWIN__)
+#ifdef _WIN32
    /* We need Windows Vista for both Uniscribe backend and for
     * MemoryBarrier.  We don't support compiling on Windows XP,
     * though we run on it fine. */
@@ -281,7 +309,7 @@
 #  endif
 #endif
 
-#if HAVE_ATEXIT
+#if defined(HAVE_ATEXIT) && !defined(HB_USE_ATEXIT)
 /* atexit() is only safe to be called from shared libraries on certain
  * platforms.  Whitelist.
  * https://bugs.freedesktop.org/show_bug.cgi?id=82246 */
@@ -313,6 +341,9 @@
 #ifdef HB_NO_ATEXIT
 #  undef HB_USE_ATEXIT
 #endif
+#ifndef HB_USE_ATEXIT
+#  define HB_USE_ATEXIT 0
+#endif
 
 #define HB_STMT_START do
 #define HB_STMT_END   while (0)
@@ -337,35 +368,40 @@
 static_assert ((sizeof (hb_var_int_t) == 4), "");
 
 
-/* We like our types POD */
+#if __cplusplus >= 201103L
 
-#define _ASSERT_TYPE_POD1(_line, _type)	union _type_##_type##_on_line_##_line##_is_not_POD { _type instance; }
-#define _ASSERT_TYPE_POD0(_line, _type)	_ASSERT_TYPE_POD1 (_line, _type)
-#define ASSERT_TYPE_POD(_type)		_ASSERT_TYPE_POD0 (__LINE__, _type)
+/* We only enable these with C++11 or later, since earlier language
+ * does not allow structs with constructors in unions, and we need
+ * those. */
 
-#ifdef __GNUC__
-# define _ASSERT_INSTANCE_POD1(_line, _instance) \
-	HB_STMT_START { \
-		typedef __typeof__(_instance) _type_##_line; \
-		_ASSERT_TYPE_POD1 (_line, _type_##_line); \
-	} HB_STMT_END
-#else
-# define _ASSERT_INSTANCE_POD1(_line, _instance)	typedef int _assertion_on_line_##_line##_not_tested
-#endif
-# define _ASSERT_INSTANCE_POD0(_line, _instance)	_ASSERT_INSTANCE_POD1 (_line, _instance)
-# define ASSERT_INSTANCE_POD(_instance)			_ASSERT_INSTANCE_POD0 (__LINE__, _instance)
-
-/* Check _assertion in a method environment */
-#define _ASSERT_POD1(_line) \
-	HB_UNUSED inline void _static_assertion_on_line_##_line (void) const \
-	{ _ASSERT_INSTANCE_POD1 (_line, *this); /* Make sure it's POD. */ }
-# define _ASSERT_POD0(_line)	_ASSERT_POD1 (_line)
-# define ASSERT_POD()		_ASSERT_POD0 (__LINE__)
-
-
-#define HB_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+#define HB_NO_COPY_ASSIGN(TypeName) \
   TypeName(const TypeName&); \
   void operator=(const TypeName&)
+#define HB_NO_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) \
+  TypeName(const TypeName<T1, T2>&); \
+  void operator=(const TypeName<T1, T2>&)
+#define HB_NO_CREATE_COPY_ASSIGN(TypeName) \
+  TypeName(); \
+  TypeName(const TypeName&); \
+  void operator=(const TypeName&)
+#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE(TypeName, T) \
+  TypeName(); \
+  TypeName(const TypeName<T>&); \
+  void operator=(const TypeName<T>&)
+#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) \
+  TypeName(); \
+  TypeName(const TypeName<T1, T2>&); \
+  void operator=(const TypeName<T1, T2>&)
+
+#else /* __cpluspplus >= 201103L */
+
+#define HB_NO_COPY_ASSIGN(TypeName) static_assert (true, "")
+#define HB_NO_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) static_assert (true, "")
+#define HB_NO_CREATE_COPY_ASSIGN(TypeName) static_assert (true, "")
+#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE(TypeName, T) static_assert (true, "")
+#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) static_assert (true, "")
+
+#endif /* __cpluspplus >= 201103L */
 
 
 /*
@@ -438,9 +474,11 @@
  * For example, for testing "x ∈ {x1, x2, x3}" use:
  * (FLAG_UNSAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3)))
  */
-#define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((unsigned int)(x) < 32) + (1U << (unsigned int)(x)))
-#define FLAG_UNSAFE(x) ((unsigned int)(x) < 32 ? (1U << (unsigned int)(x)) : 0)
+#define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((unsigned)(x) < 32) + (((uint32_t) 1U) << (unsigned)(x)))
+#define FLAG_UNSAFE(x) ((unsigned)(x) < 32 ? (((uint32_t) 1U) << (unsigned)(x)) : 0)
 #define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x))
+#define FLAG64(x) (ASSERT_STATIC_EXPR_ZERO ((unsigned)(x) < 64) + (((uint64_t) 1ULL) << (unsigned)(x)))
+#define FLAG64_UNSAFE(x) ((unsigned)(x) < 64 ? (((uint64_t) 1ULL) << (unsigned)(x)) : 0)
 
 
 /* Size signifying variable-sized array */
@@ -495,6 +533,18 @@
 #define HB_SCRIPT_MYANMAR_ZAWGYI	((hb_script_t) HB_TAG ('Q','a','a','g'))
 
 
+/* Some really basic things everyone wants. */
+template <typename T> struct hb_remove_const { typedef T value; };
+template <typename T> struct hb_remove_const<const T> { typedef T value; };
+#define hb_remove_const(T) hb_remove_const<T>::value
+template <typename T> struct hb_remove_reference { typedef T value; };
+template <typename T> struct hb_remove_reference<T &> { typedef T value; };
+#define hb_remove_reference(T) hb_remove_reference<T>::value
+template <typename T> struct hb_remove_pointer { typedef T value; };
+template <typename T> struct hb_remove_pointer<T *> { typedef T value; };
+#define hb_remove_pointer(T) hb_remove_pointer<T>::value
+
+
 /* Headers we include for everyone.  Keep sorted.  They express dependency amongst
  * themselves, but no other file should include them.*/
 #include "hb-atomic.hh"
diff --git a/src/test-name-table.cc b/src/test-name-table.cc
new file mode 100644
index 0000000..518e4eb
--- /dev/null
+++ b/src/test-name-table.cc
@@ -0,0 +1,69 @@
+/*
+ * 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
+ */
+
+#include "hb.hh"
+#include "hb-ot.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int
+main (int argc, char **argv)
+{
+  if (argc != 2) {
+    fprintf (stderr, "usage: %s font-file\n", argv[0]);
+    exit (1);
+  }
+
+  hb_blob_t *blob = hb_blob_create_from_file (argv[1]);
+  hb_face_t *face = hb_face_create (blob, 0 /* first face */);
+  hb_blob_destroy (blob);
+  blob = nullptr;
+
+  unsigned int count;
+  const hb_ot_name_entry_t *entries = hb_ot_name_list_names (face, &count);
+
+  for (unsigned int i = 0; i < count; i++)
+  {
+    printf ("%u	%s	",
+	    entries[i].name_id,
+	    hb_language_to_string (entries[i].language));
+
+    char buf[64];
+    unsigned int buf_size = sizeof (buf);
+    hb_ot_name_get_utf8 (face,
+			 entries[i].name_id,
+			 entries[i].language,
+			 &buf_size,
+			 buf);
+
+    printf ("%s\n", buf);
+  }
+
+  hb_face_destroy (face);
+
+  return count ? 0 : 1;
+}
diff --git a/src/test-ot-color.cc b/src/test-ot-color.cc
new file mode 100644
index 0000000..4050a66
--- /dev/null
+++ b/src/test-ot-color.cc
@@ -0,0 +1,336 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ * Copyright © 2018  Khaled Hosny
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb.h"
+#include "hb-ot.h"
+
+#include "hb-ft.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+#include <cairo.h>
+#include <cairo-ft.h>
+#include <cairo-svg.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+static void
+svg_dump (hb_face_t *face, unsigned int face_index)
+{
+  unsigned glyph_count = hb_face_get_glyph_count (face);
+
+  for (unsigned int glyph_id = 0; glyph_id < glyph_count; glyph_id++)
+  {
+    hb_blob_t *blob = hb_ot_color_glyph_reference_svg (face, glyph_id);
+
+    if (hb_blob_get_length (blob) == 0) continue;
+
+    unsigned int length;
+    const char *data = hb_blob_get_data (blob, &length);
+
+    char output_path[255];
+    sprintf (output_path, "out/svg-%u-%u.svg%s",
+	     glyph_id,
+	     face_index,
+	     // append "z" if the content is gzipped, https://stackoverflow.com/a/6059405
+	     (length > 2 && (data[0] == '\x1F') && (data[1] == '\x8B')) ? "z" : "");
+
+    FILE *f = fopen (output_path, "wb");
+    fwrite (data, 1, length, f);
+    fclose (f);
+
+    hb_blob_destroy (blob);
+  }
+}
+
+/* _png API is so easy to use unlike the below code, don't get confused */
+static void
+png_dump (hb_face_t *face, unsigned int face_index)
+{
+  unsigned glyph_count = hb_face_get_glyph_count (face);
+  hb_font_t *font = hb_font_create (face);
+
+  /* scans the font for strikes */
+  unsigned int sample_glyph_id;
+  /* we don't care about different strikes for different glyphs at this point */
+  for (sample_glyph_id = 0; sample_glyph_id < glyph_count; sample_glyph_id++)
+  {
+    hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, sample_glyph_id);
+    unsigned int blob_length = hb_blob_get_length (blob);
+    hb_blob_destroy (blob);
+    if (blob_length != 0)
+      break;
+  }
+
+  unsigned int upem = hb_face_get_upem (face);
+  unsigned int blob_length = 0;
+  unsigned int strike = 0;
+  for (unsigned int ppem = 1; ppem < upem; ppem++)
+  {
+    hb_font_set_ppem (font, ppem, ppem);
+    hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, sample_glyph_id);
+    unsigned int new_blob_length = hb_blob_get_length (blob);
+    hb_blob_destroy (blob);
+    if (new_blob_length != blob_length)
+    {
+      for (unsigned int glyph_id = 0; glyph_id < glyph_count; glyph_id++)
+      {
+	hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, glyph_id);
+
+	if (hb_blob_get_length (blob) == 0) continue;
+
+	unsigned int length;
+	const char *data = hb_blob_get_data (blob, &length);
+
+	char output_path[255];
+	sprintf (output_path, "out/png-%u-%u-%u.png", glyph_id, strike, face_index);
+
+	FILE *f = fopen (output_path, "wb");
+	fwrite (data, 1, length, f);
+	fclose (f);
+
+	hb_blob_destroy (blob);
+      }
+
+      strike++;
+      blob_length = new_blob_length;
+    }
+  }
+
+  hb_font_destroy (font);
+}
+
+static void
+layered_glyph_dump (hb_face_t *face, cairo_font_face_t *cairo_face, unsigned int face_index)
+{
+  unsigned int upem = hb_face_get_upem (face);
+
+  unsigned glyph_count = hb_face_get_glyph_count (face);
+  for (hb_codepoint_t gid = 0; gid < glyph_count; ++gid)
+  {
+    unsigned int num_layers = hb_ot_color_glyph_get_layers (face, gid, 0, NULL, NULL);
+    if (!num_layers)
+      continue;
+
+    hb_ot_color_layer_t *layers = (hb_ot_color_layer_t*) malloc (num_layers * sizeof (hb_ot_color_layer_t));
+
+    hb_ot_color_glyph_get_layers (face, gid, 0, &num_layers, layers);
+    if (num_layers)
+    {
+      // Measure
+      cairo_text_extents_t extents;
+      {
+	cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+	cairo_t *cr = cairo_create (surface);
+	cairo_set_font_face (cr, cairo_face);
+	cairo_set_font_size (cr, upem);
+
+	cairo_glyph_t *glyphs = (cairo_glyph_t *) calloc (num_layers, sizeof (cairo_glyph_t));
+	for (unsigned int j = 0; j < num_layers; ++j)
+	  glyphs[j].index = layers[j].glyph;
+	cairo_glyph_extents (cr, glyphs, num_layers, &extents);
+	free (glyphs);
+	cairo_surface_destroy (surface);
+	cairo_destroy (cr);
+      }
+
+      // Add a slight margin
+      extents.width += extents.width / 10;
+      extents.height += extents.height / 10;
+      extents.x_bearing -= extents.width / 20;
+      extents.y_bearing -= extents.height / 20;
+
+      // Render
+      unsigned int palette_count = hb_ot_color_palette_get_count (face);
+      for (unsigned int palette = 0; palette < palette_count; palette++)
+      {
+	unsigned int num_colors = hb_ot_color_palette_get_colors (face, palette, 0, NULL, NULL);
+	if (!num_colors)
+	  continue;
+
+	hb_color_t *colors = (hb_color_t*) calloc (num_colors, sizeof (hb_color_t));
+	hb_ot_color_palette_get_colors (face, palette, 0, &num_colors, colors);
+	if (num_colors)
+	{
+	  char output_path[255];
+	  sprintf (output_path, "out/colr-%u-%u-%u.svg", gid, palette, face_index);
+
+	  cairo_surface_t *surface = cairo_svg_surface_create (output_path, extents.width, extents.height);
+	  cairo_t *cr = cairo_create (surface);
+	  cairo_set_font_face (cr, cairo_face);
+	  cairo_set_font_size (cr, upem);
+
+	  for (unsigned int layer = 0; layer < num_layers; ++layer)
+	  {
+	    hb_color_t color = 0x000000FF;
+	    if (layers[layer].color_index != 0xFFFF)
+	      color = colors[layers[layer].color_index];
+	    cairo_set_source_rgba (cr,
+				   hb_color_get_red (color) / 255.,
+				   hb_color_get_green (color) / 255.,
+				   hb_color_get_blue (color) / 255.,
+				   hb_color_get_alpha (color) / 255.);
+
+	    cairo_glyph_t glyph;
+	    glyph.index = layers[layer].glyph;
+	    glyph.x = -extents.x_bearing;
+	    glyph.y = -extents.y_bearing;
+	    cairo_show_glyphs (cr, &glyph, 1);
+	  }
+
+	  cairo_surface_destroy (surface);
+	  cairo_destroy (cr);
+	}
+	free (colors);
+      }
+    }
+
+    free (layers);
+  }
+}
+
+static void
+dump_glyphs (cairo_font_face_t *cairo_face, unsigned int upem,
+	     unsigned int num_glyphs, unsigned int face_index)
+{
+  for (unsigned int i = 0; i < num_glyphs; ++i)
+  {
+    cairo_text_extents_t extents;
+    cairo_glyph_t glyph = {0};
+    glyph.index = i;
+
+    // Measure
+    {
+      cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+      cairo_t *cr = cairo_create (surface);
+      cairo_set_font_face (cr, cairo_face);
+      cairo_set_font_size (cr, upem);
+
+      cairo_glyph_extents (cr, &glyph, 1, &extents);
+      cairo_surface_destroy (surface);
+      cairo_destroy (cr);
+    }
+
+    // Add a slight margin
+    extents.width += extents.width / 10;
+    extents.height += extents.height / 10;
+    extents.x_bearing -= extents.width / 20;
+    extents.y_bearing -= extents.height / 20;
+
+    // Render
+    {
+      char output_path[255];
+      sprintf (output_path, "out/%u-%u.svg", face_index, i);
+      cairo_surface_t *surface = cairo_svg_surface_create (output_path, extents.width, extents.height);
+      cairo_t *cr = cairo_create (surface);
+      cairo_set_font_face (cr, cairo_face);
+      cairo_set_font_size (cr, upem);
+      glyph.x = -extents.x_bearing;
+      glyph.y = -extents.y_bearing;
+      cairo_show_glyphs (cr, &glyph, 1);
+      cairo_surface_destroy (surface);
+      cairo_destroy (cr);
+    }
+  }
+}
+
+int
+main (int argc, char **argv)
+{
+  if (argc != 2) {
+    fprintf (stderr, "usage: %s font-file.ttf\n"
+		     "run it like `rm -rf out && mkdir out && %s font-file.ttf`\n",
+		     argv[0], argv[0]);
+    exit (1);
+  }
+
+
+  FILE *font_name_file = fopen ("out/.dumped_font_name", "r");
+  if (font_name_file != NULL)
+  {
+    fprintf (stderr, "Purge or move ./out folder in order to run a new dump\n");
+    exit (1);
+  }
+
+  font_name_file = fopen ("out/.dumped_font_name", "w");
+  if (font_name_file == NULL)
+  {
+    fprintf (stderr, "./out is not accessible as a folder, create it please\n");
+    exit (1);
+  }
+  fwrite (argv[1], 1, strlen (argv[1]), font_name_file);
+  fclose (font_name_file);
+
+  hb_blob_t *blob = hb_blob_create_from_file (argv[1]);
+  unsigned int num_faces = hb_face_count (blob);
+  if (num_faces == 0)
+  {
+    fprintf (stderr, "error: The file (%s) was corrupted, empty or not found", argv[1]);
+    exit (1);
+  }
+
+  for (unsigned int face_index = 0; face_index < hb_face_count (blob); face_index++)
+  {
+    hb_face_t *face = hb_face_create (blob, face_index);
+    hb_font_t *font = hb_font_create (face);
+
+    if (hb_ot_color_has_png (face)) printf ("Dumping png (cbdt/sbix)...\n");
+    png_dump (face, face_index);
+
+    if (hb_ot_color_has_svg (face)) printf ("Dumping svg...\n");
+    svg_dump (face, face_index);
+
+    cairo_font_face_t *cairo_face;
+    {
+      FT_Library library;
+      FT_Init_FreeType (&library);
+      FT_Face ft_face;
+      FT_New_Face (library, argv[1], 0, &ft_face);
+      cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
+    }
+    if (hb_ot_color_has_layers (face) && hb_ot_color_has_palettes (face))
+      printf ("Dumping layered color glyphs...\n");
+    layered_glyph_dump (face, cairo_face, face_index);
+
+    unsigned int num_glyphs = hb_face_get_glyph_count (face);
+    unsigned int upem = hb_face_get_upem (face);
+
+    // disabled when color font as cairo rendering of NotoColorEmoji is soooo slow
+    if (!hb_ot_color_has_layers (face) &&
+        !hb_ot_color_has_png (face) &&
+        !hb_ot_color_has_svg (face))
+      dump_glyphs (cairo_face, upem, num_glyphs, face_index);
+
+    hb_font_destroy (font);
+    hb_face_destroy (face);
+    }
+
+  hb_blob_destroy (blob);
+
+  return 0;
+}
diff --git a/src/test-size-params.cc b/src/test-size-params.cc
index e53a47d..12eec61 100644
--- a/src/test-size-params.cc
+++ b/src/test-size-params.cc
@@ -46,7 +46,7 @@
   blob = nullptr;
 
   unsigned int p[5];
-  bool ret = hb_ot_layout_get_size_params (face, p, p+1, p+2, p+3, p+4);
+  bool ret = hb_ot_layout_get_size_params (face, p, p+1, (p+2), p+3, p+4);
 
   printf ("%g %u %u %g %g\n", p[0]/10., p[1], p[2], p[3]/10., p[4]/10.);
 
diff --git a/src/test-unicode-ranges.cc b/src/test-unicode-ranges.cc
index d9342d7..ec9d17a 100644
--- a/src/test-unicode-ranges.cc
+++ b/src/test-unicode-ranges.cc
@@ -42,7 +42,7 @@
 }
 
 static void
-test_get_unicode_range_bit (void)
+test_get_unicode_range_bit ()
 {
   test (0x0000, 0);
   test (0x0042, 0);
@@ -60,7 +60,7 @@
 }
 
 int
-main (void)
+main ()
 {
   test_get_unicode_range_bit ();
   return 0;
diff --git a/test/api/.valgrind-suppressions b/test/api/.valgrind-suppressions
deleted file mode 100644
index e69de29..0000000
--- a/test/api/.valgrind-suppressions
+++ /dev/null
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index 45a34e6..9aba2d1 100644
--- a/test/api/Makefile.am
+++ b/test/api/Makefile.am
@@ -28,6 +28,8 @@
 noinst_PROGRAMS = $(TEST_PROGS)
 
 TEST_PROGS = \
+	test-aat-layout \
+	test-baseline \
 	test-blob \
 	test-buffer \
 	test-collect-unicodes \
@@ -35,6 +37,7 @@
 	test-font \
 	test-map \
 	test-object \
+	test-ot-face \
 	test-set \
 	test-shape \
 	test-subset \
@@ -45,6 +48,8 @@
 	test-subset-os2 \
 	test-subset-post \
 	test-subset-vmtx \
+	test-subset-cff1 \
+	test-subset-cff2 \
 	test-unicode \
 	test-version \
 	$(NULL)
@@ -58,6 +63,8 @@
 test_subset_os2_LDADD  = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
 test_subset_post_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
 test_subset_vmtx_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
+test_subset_cff1_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
+test_subset_cff2_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
 
 test_unicode_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
@@ -72,8 +79,10 @@
 
 TEST_PROGS += \
 	test-ot-color \
+	test-ot-ligature-carets \
 	test-ot-name \
 	test-ot-tag \
+	test-ot-extents-cff \
 	$(NULL)
 
 
@@ -146,9 +155,9 @@
 
 # Check tests under valgrind.  Saves log to log-valgrind.txt
 VALGRIND_FLAGS = \
-	--tool=memcheck --suppressions=$(srcdir)/.valgrind-suppressions \
+	--tool=memcheck \
 	--track-origins=yes \
-	--leak-check=yes
+	--leak-check=yes \
 	$(EXTRA_VALGRIND_FLAGS)
 #	Can't do for now: --show-reachable=yes
 CLEANFILES +=  log-valgrind.txt
@@ -167,7 +176,7 @@
 	$(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \
 	| grep ' U hb_' | sed 's/.* U hb_/hb_/' \
 	| sort | uniq > $@.tmp && mv $@.tmp $@
-symbols-tested-or-deprecated.txt: symbols-tested.txt $(top_builddir)/src/harfbuzz-deprecated.def
+symbols-tested-or-deprecated.txt: symbols-tested.txt $(top_builddir)/src/harfbuzz-deprecated-symbols.txt
 	$(AM_V_GEN)cat $^ | sort | uniq > $@.tmp; mv $@.tmp $@
 symbols-exported.txt: $(top_builddir)/src/.libs/libharfbuzz.so
 	$(AM_V_GEN)$(top_builddir)/libtool --mode=execute nm $^ \
diff --git a/test/api/fonts/AdobeVFPrototype.abc.otf b/test/api/fonts/AdobeVFPrototype.abc.otf
new file mode 100644
index 0000000..cc47708
--- /dev/null
+++ b/test/api/fonts/AdobeVFPrototype.abc.otf
Binary files differ
diff --git a/test/api/fonts/AdobeVFPrototype.ac.nohints.otf b/test/api/fonts/AdobeVFPrototype.ac.nohints.otf
new file mode 100644
index 0000000..935bdbf
--- /dev/null
+++ b/test/api/fonts/AdobeVFPrototype.ac.nohints.otf
Binary files differ
diff --git a/test/api/fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf
new file mode 100644
index 0000000..85f6cf6
--- /dev/null
+++ b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf
Binary files differ
diff --git a/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf
new file mode 100644
index 0000000..ad4d53b
--- /dev/null
+++ b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf
Binary files differ
diff --git a/test/api/fonts/AdobeVFPrototype.ac.otf b/test/api/fonts/AdobeVFPrototype.ac.otf
new file mode 100644
index 0000000..beab7d5
--- /dev/null
+++ b/test/api/fonts/AdobeVFPrototype.ac.otf
Binary files differ
diff --git a/test/api/fonts/AdobeVFPrototype_vsindex.otf b/test/api/fonts/AdobeVFPrototype_vsindex.otf
new file mode 100644
index 0000000..3697b46
--- /dev/null
+++ b/test/api/fonts/AdobeVFPrototype_vsindex.otf
Binary files differ
diff --git a/test/api/fonts/README b/test/api/fonts/README
index 7e7783c..4830c47 100644
--- a/test/api/fonts/README
+++ b/test/api/fonts/README
@@ -1,3 +1,5 @@
 cmap-format12-only files created by ttx & remove all other cmap entries
 
 Inconsolata-Regular.abc.widerc.ttf has the hmtx width of "c" set to 600; everything else is 500. Subsetting out c should reduce numberOfHMetrics to 1.
+
+chromacheck-* fonts are from https://github.com/RoelN/ChromaCheck/tree/master/fonts and licensed under MIT by Roel Nieskens and Google.
diff --git a/test/api/fonts/SourceHanSans-Regular.41,3041,4C2E.otf b/test/api/fonts/SourceHanSans-Regular.41,3041,4C2E.otf
new file mode 100644
index 0000000..08bc0e0
--- /dev/null
+++ b/test/api/fonts/SourceHanSans-Regular.41,3041,4C2E.otf
Binary files differ
diff --git a/test/api/fonts/SourceHanSans-Regular.41,4C2E.nohints.otf b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nohints.otf
new file mode 100644
index 0000000..ec39590
--- /dev/null
+++ b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nohints.otf
Binary files differ
diff --git a/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.nohints.otf b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.nohints.otf
new file mode 100644
index 0000000..00a112f
--- /dev/null
+++ b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.nohints.otf
Binary files differ
diff --git a/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.otf b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.otf
new file mode 100644
index 0000000..6fe9bf3
--- /dev/null
+++ b/test/api/fonts/SourceHanSans-Regular.41,4C2E.nosubrs.otf
Binary files differ
diff --git a/test/api/fonts/SourceHanSans-Regular.41,4C2E.otf b/test/api/fonts/SourceHanSans-Regular.41,4C2E.otf
new file mode 100644
index 0000000..2c6cd9a
--- /dev/null
+++ b/test/api/fonts/SourceHanSans-Regular.41,4C2E.otf
Binary files differ
diff --git a/test/api/fonts/SourceSansPro-Regular.abc.otf b/test/api/fonts/SourceSansPro-Regular.abc.otf
new file mode 100644
index 0000000..7f51bd3
--- /dev/null
+++ b/test/api/fonts/SourceSansPro-Regular.abc.otf
Binary files differ
diff --git a/test/api/fonts/SourceSansPro-Regular.ac.nohints.otf b/test/api/fonts/SourceSansPro-Regular.ac.nohints.otf
new file mode 100644
index 0000000..14a3a5f
--- /dev/null
+++ b/test/api/fonts/SourceSansPro-Regular.ac.nohints.otf
Binary files differ
diff --git a/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.nohints.otf b/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.nohints.otf
new file mode 100644
index 0000000..69e244c
--- /dev/null
+++ b/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.nohints.otf
Binary files differ
diff --git a/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.otf b/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.otf
new file mode 100644
index 0000000..28edf13
--- /dev/null
+++ b/test/api/fonts/SourceSansPro-Regular.ac.nosubrs.otf
Binary files differ
diff --git a/test/api/fonts/SourceSansPro-Regular.ac.otf b/test/api/fonts/SourceSansPro-Regular.ac.otf
new file mode 100644
index 0000000..12d6d0f
--- /dev/null
+++ b/test/api/fonts/SourceSansPro-Regular.ac.otf
Binary files differ
diff --git a/test/api/fonts/aat-feat.ttf b/test/api/fonts/aat-feat.ttf
new file mode 100644
index 0000000..1ff99a2
--- /dev/null
+++ b/test/api/fonts/aat-feat.ttf
Binary files differ
diff --git a/test/api/fonts/aat-morx.ttf b/test/api/fonts/aat-morx.ttf
new file mode 100644
index 0000000..5827ec5
--- /dev/null
+++ b/test/api/fonts/aat-morx.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/TestTRAK.ttf b/test/api/fonts/aat-trak.ttf
similarity index 100%
copy from test/shaping/data/in-house/fonts/TestTRAK.ttf
copy to test/api/fonts/aat-trak.ttf
Binary files differ
diff --git a/test/api/fonts/base.ttf b/test/api/fonts/base.ttf
new file mode 100644
index 0000000..d984966
--- /dev/null
+++ b/test/api/fonts/base.ttf
Binary files differ
diff --git a/test/api/fonts/cff1_dotsect.nohints.otf b/test/api/fonts/cff1_dotsect.nohints.otf
new file mode 100644
index 0000000..faa932e
--- /dev/null
+++ b/test/api/fonts/cff1_dotsect.nohints.otf
Binary files differ
diff --git a/test/api/fonts/cff1_dotsect.otf b/test/api/fonts/cff1_dotsect.otf
new file mode 100644
index 0000000..dabd4b3
--- /dev/null
+++ b/test/api/fonts/cff1_dotsect.otf
Binary files differ
diff --git a/test/api/fonts/cff1_expert.2D,F6E9,FB00.otf b/test/api/fonts/cff1_expert.2D,F6E9,FB00.otf
new file mode 100644
index 0000000..8c198b7
--- /dev/null
+++ b/test/api/fonts/cff1_expert.2D,F6E9,FB00.otf
Binary files differ
diff --git a/test/api/fonts/cff1_expert.otf b/test/api/fonts/cff1_expert.otf
new file mode 100644
index 0000000..970ed67
--- /dev/null
+++ b/test/api/fonts/cff1_expert.otf
Binary files differ
diff --git a/test/api/fonts/cff1_flex.otf b/test/api/fonts/cff1_flex.otf
new file mode 100644
index 0000000..1ef59e1
--- /dev/null
+++ b/test/api/fonts/cff1_flex.otf
Binary files differ
diff --git a/test/api/fonts/cff1_seac.C0.otf b/test/api/fonts/cff1_seac.C0.otf
new file mode 100644
index 0000000..aed2fdd
--- /dev/null
+++ b/test/api/fonts/cff1_seac.C0.otf
Binary files differ
diff --git a/test/api/fonts/cff1_seac.otf b/test/api/fonts/cff1_seac.otf
new file mode 100644
index 0000000..bc7991c
--- /dev/null
+++ b/test/api/fonts/cff1_seac.otf
Binary files differ
diff --git a/test/api/fonts/chromacheck-cbdt.ttf b/test/api/fonts/chromacheck-cbdt.ttf
new file mode 100644
index 0000000..100c01a
--- /dev/null
+++ b/test/api/fonts/chromacheck-cbdt.ttf
Binary files differ
diff --git a/test/api/fonts/chromacheck-colr.ttf b/test/api/fonts/chromacheck-colr.ttf
new file mode 100644
index 0000000..626809c
--- /dev/null
+++ b/test/api/fonts/chromacheck-colr.ttf
Binary files differ
diff --git a/test/api/fonts/chromacheck-sbix.ttf b/test/api/fonts/chromacheck-sbix.ttf
new file mode 100644
index 0000000..b6f1fe9
--- /dev/null
+++ b/test/api/fonts/chromacheck-sbix.ttf
Binary files differ
diff --git a/test/api/fonts/chromacheck-svg.ttf b/test/api/fonts/chromacheck-svg.ttf
new file mode 100644
index 0000000..d39cc56
--- /dev/null
+++ b/test/api/fonts/chromacheck-svg.ttf
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5658272078495744 b/test/api/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5658272078495744
new file mode 100644
index 0000000..f3fa938
--- /dev/null
+++ b/test/api/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5658272078495744
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/e90374e5e439e00725b4fe7a8d73db57c5a97f82.ttf b/test/api/fonts/cpal-v0.ttf
similarity index 100%
rename from test/shaping/data/in-house/fonts/e90374e5e439e00725b4fe7a8d73db57c5a97f82.ttf
rename to test/api/fonts/cpal-v0.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/319f5d7ebffbefc5c5e6569f8cea73444d7a7268.ttf b/test/api/fonts/cpal-v1.ttf
similarity index 100%
rename from test/shaping/data/in-house/fonts/319f5d7ebffbefc5c5e6569f8cea73444d7a7268.ttf
rename to test/api/fonts/cpal-v1.ttf
Binary files differ
diff --git a/test/api/fonts/lcar.ttf b/test/api/fonts/lcar.ttf
new file mode 100644
index 0000000..4d17663
--- /dev/null
+++ b/test/api/fonts/lcar.ttf
Binary files differ
diff --git a/test/api/hb-subset-test.h b/test/api/hb-subset-test.h
index 5f5cd8d..cefa4e0 100644
--- a/test/api/hb-subset-test.h
+++ b/test/api/hb-subset-test.h
@@ -47,27 +47,6 @@
 
 HB_BEGIN_DECLS
 
-static inline hb_face_t *
-hb_subset_test_open_font (const char *font_path)
-{
-#if GLIB_CHECK_VERSION(2,37,2)
-  char *path = g_test_build_filename (G_TEST_DIST, font_path, NULL);
-#else
-  char *path = g_strdup (font_path);
-#endif
-
-  hb_blob_t *blob = hb_blob_create_from_file (path);
-  if (hb_blob_get_length (blob) == 0)
-    g_error ("Font not found.");
-
-  hb_face_t *face = hb_face_create (blob, 0);
-  hb_blob_destroy (blob);
-
-  g_free (path);
-
-  return face;
-}
-
 static inline hb_subset_input_t *
 hb_subset_test_create_input(const hb_set_t  *codepoints)
 {
@@ -94,7 +73,7 @@
                       hb_tag_t   table)
 {
   hb_blob_t *expected_blob, *actual_blob;
-  fprintf(stderr, "compare %c%c%c%c\n", HB_UNTAG(table));
+  //fprintf(stderr, "comparing %c%c%c%c ", HB_UNTAG(table));
   expected_blob = hb_face_reference_table (expected, table);
   actual_blob = hb_face_reference_table (actual, table);
   hb_test_assert_blobs_equal (expected_blob, actual_blob);
diff --git a/test/api/hb-test.h b/test/api/hb-test.h
index 39d091b..872f45c 100644
--- a/test/api/hb-test.h
+++ b/test/api/hb-test.h
@@ -277,6 +277,28 @@
 } G_STMT_END
 
 
+static inline hb_face_t *
+hb_test_open_font_file (const char *font_path)
+{
+#if GLIB_CHECK_VERSION(2,37,2)
+  char *path = g_test_build_filename (G_TEST_DIST, font_path, NULL);
+#else
+  char *path = g_strdup (font_path);
+#endif
+
+  hb_blob_t *blob = hb_blob_create_from_file (path);
+  hb_face_t *face;
+  if (hb_blob_get_length (blob) == 0)
+    g_error ("Font %s not found.", path);
+
+  face = hb_face_create (blob, 0);
+  hb_blob_destroy (blob);
+
+  g_free (path);
+
+  return face;
+}
+
 HB_END_DECLS
 
 #endif /* HB_TEST_H */
diff --git a/test/api/test-aat-layout.c b/test/api/test-aat-layout.c
new file mode 100644
index 0000000..1384556
--- /dev/null
+++ b/test/api/test-aat-layout.c
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#include "hb-test.h"
+
+#include <hb.h>
+#include <hb-ot.h>
+#include <hb-aat.h>
+
+/* Unit tests for hb-aat.h */
+
+static hb_face_t *face;
+static hb_face_t *sbix;
+
+static void
+test_aat_get_feature_types (void)
+{
+  hb_aat_layout_feature_type_t features[3];
+  unsigned int count = 3;
+  g_assert_cmpuint (11, ==, hb_aat_layout_get_feature_types (face, 0, &count, features));
+
+  g_assert_cmpuint (1, ==, features[0]);
+  g_assert_cmpuint (3, ==, features[1]);
+  g_assert_cmpuint (6, ==, features[2]);
+
+  g_assert_cmpuint (258, ==, hb_aat_layout_feature_type_get_name_id (face, features[0]));
+  g_assert_cmpuint (261, ==, hb_aat_layout_feature_type_get_name_id (face, features[1]));
+  g_assert_cmpuint (265, ==, hb_aat_layout_feature_type_get_name_id (face, features[2]));
+}
+
+static void
+test_aat_get_feature_selectors (void)
+{
+  unsigned int default_index;
+  hb_aat_layout_feature_selector_info_t settings[3];
+  unsigned int count = 3;
+
+  g_assert_cmpuint (4, ==, hb_aat_layout_feature_type_get_selector_infos (face,
+									  HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE,
+									  0, &count, settings,
+									  &default_index));
+  g_assert_cmpuint (3, ==, count);
+  g_assert_cmpuint (0, ==, default_index);
+
+  g_assert_cmpuint (0, ==, settings[0].enable);
+  g_assert_cmpuint (294, ==, settings[0].name_id);
+
+  g_assert_cmpuint (1, ==, settings[1].enable);
+  g_assert_cmpuint (295, ==, settings[1].name_id);
+
+  g_assert_cmpuint (2, ==, settings[2].enable);
+  g_assert_cmpuint (296, ==, settings[2].name_id);
+
+  count = 3;
+  g_assert_cmpuint (4, ==, hb_aat_layout_feature_type_get_selector_infos (face,
+									  HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE,
+									  3, &count, settings,
+									  &default_index));
+  g_assert_cmpuint (1, ==, count);
+  g_assert_cmpuint (0, ==, default_index);
+
+  g_assert_cmpuint (3, ==, settings[0].enable);
+  g_assert_cmpuint (297, ==, settings[0].name_id);
+
+  count = 1;
+  g_assert_cmpuint (1, ==, hb_aat_layout_feature_type_get_selector_infos (face,
+									  HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS,
+									  0, &count, settings,
+									  &default_index));
+  g_assert_cmpuint (1, ==, count);
+  g_assert_cmpuint (HB_AAT_LAYOUT_NO_SELECTOR_INDEX, ==, default_index);
+
+  g_assert_cmpuint (8, ==, settings[0].enable);
+  g_assert_cmpuint (308, ==, settings[0].name_id);
+
+  count = 100;
+  g_assert_cmpuint (0, ==, hb_aat_layout_feature_type_get_selector_infos (face, HB_AAT_LAYOUT_FEATURE_TYPE_INVALID,
+									  0, &count, settings,
+									  NULL));
+  g_assert_cmpuint (0, ==, count);
+}
+
+static void
+test_aat_has (void)
+{
+  hb_face_t *morx = hb_test_open_font_file ("fonts/aat-morx.ttf");
+  hb_face_t *trak;
+  g_assert (hb_aat_layout_has_substitution (morx));
+  hb_face_destroy (morx);
+
+  trak = hb_test_open_font_file ("fonts/aat-trak.ttf");
+  g_assert (hb_aat_layout_has_tracking (trak));
+  hb_face_destroy (trak);
+}
+
+int
+main (int argc, char **argv)
+{
+  unsigned int status;
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_aat_get_feature_types);
+  hb_test_add (test_aat_get_feature_selectors);
+  hb_test_add (test_aat_has);
+
+  face = hb_test_open_font_file ("fonts/aat-feat.ttf");
+  sbix = hb_test_open_font_file ("fonts/chromacheck-sbix.ttf");
+  status = hb_test_run ();
+  hb_face_destroy (sbix);
+  hb_face_destroy (face);
+  return status;
+}
diff --git a/test/api/test-baseline.c b/test/api/test-baseline.c
new file mode 100644
index 0000000..a120e14
--- /dev/null
+++ b/test/api/test-baseline.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb-test.h"
+
+#include <hb-ot.h>
+
+/* Unit tests for hb-ot-layout.h baseline */
+
+static void
+test_ot_layout_base (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/base.ttf");
+  hb_font_t *font = hb_font_create (face);
+
+#if 0
+  hb_position_t position;
+  g_assert (hb_ot_layout_get_baseline (font, HB_OT_LAYOUT_BASELINE_ICFB, HB_DIRECTION_TTB,
+				       HB_TAG ('h','a','n','i'),
+				       HB_TAG ('E','N','G',' '),
+				       &position));
+  g_assert_cmpint (46, ==, position);
+#endif
+
+  hb_font_destroy (font);
+  hb_face_destroy (face);
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_ot_layout_base);
+
+  return hb_test_run();
+}
diff --git a/test/api/test-buffer.c b/test/api/test-buffer.c
index 5fba3b2..64ab3db 100644
--- a/test/api/test-buffer.c
+++ b/test/api/test-buffer.c
@@ -379,14 +379,15 @@
 test_buffer_utf8_conversion (void)
 {
   hb_buffer_t *b;
-  hb_glyph_info_t *glyphs;
-  unsigned int bytes, chars, i, j, len;
+  unsigned int chars, i, j, len;
 
   b = hb_buffer_create ();
   hb_buffer_set_replacement_codepoint (b, (hb_codepoint_t) -1);
 
   for (i = 0; i < G_N_ELEMENTS (utf8_conversion_tests); i++)
   {
+    unsigned int bytes;
+    hb_glyph_info_t *glyphs;
     const utf8_conversion_test_t *test = &utf8_conversion_tests[i];
     char *escaped;
 
diff --git a/test/api/test-c.c b/test/api/test-c.c
index 061f35c..b4518ad 100644
--- a/test/api/test-c.c
+++ b/test/api/test-c.c
@@ -33,6 +33,7 @@
 
 #include <hb.h>
 #include <hb-ot.h>
+#include <hb-aat.h>
 
 #ifdef HAVE_GLIB
 #include <hb-glib.h>
diff --git a/test/api/test-collect-unicodes.c b/test/api/test-collect-unicodes.c
index f7a7813..50965a9 100644
--- a/test/api/test-collect-unicodes.c
+++ b/test/api/test-collect-unicodes.c
@@ -30,7 +30,7 @@
 static void
 test_collect_unicodes_format4 (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("fonts/Roboto-Regular.abc.format4.ttf");
+  hb_face_t *face = hb_test_open_font_file ("fonts/Roboto-Regular.abc.format4.ttf");
   hb_set_t *codepoints = hb_set_create();
   hb_codepoint_t cp;
 
@@ -52,7 +52,7 @@
 static void
 test_collect_unicodes_format12 (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("fonts/Roboto-Regular.abc.format12.ttf");
+  hb_face_t *face = hb_test_open_font_file ("fonts/Roboto-Regular.abc.format12.ttf");
   hb_set_t *codepoints = hb_set_create();
   hb_codepoint_t cp;
 
@@ -74,7 +74,7 @@
 static void
 test_collect_unicodes (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
   hb_set_t *codepoints = hb_set_create();
   hb_codepoint_t cp;
 
diff --git a/test/api/test-font.c b/test/api/test-font.c
index 3d81cf9..6690194 100644
--- a/test/api/test-font.c
+++ b/test/api/test-font.c
@@ -400,6 +400,7 @@
   hb_font_t *font0;
   hb_font_t *font1;
   hb_font_t *font2;
+  hb_codepoint_t glyph;
 
   blob = hb_blob_create (test_data, sizeof (test_data), HB_MEMORY_MODE_READONLY, NULL, NULL);
   face = hb_face_create (blob, 0);
@@ -424,7 +425,6 @@
   hb_font_funcs_destroy (ffuncs2);
 
   /* Just test that calling get_nominal_glyph doesn't infinite-loop. */
-  hb_codepoint_t glyph;
   hb_font_get_nominal_glyph (font2, 0x0020u, &glyph);
 
   hb_font_destroy (font2);
@@ -536,6 +536,11 @@
   g_assert_cmpint (x_ppem, ==, 17);
   g_assert_cmpint (y_ppem, ==, 19);
 
+  /* Check ptem */
+  g_assert_cmpint (hb_font_get_ptem (font), ==, 0);
+  hb_font_set_ptem (font, 42);
+  g_assert_cmpint (hb_font_get_ptem (font), ==, 42);
+
 
   /* Check immutable */
 
diff --git a/test/api/test-map.c b/test/api/test-map.c
index 0d8be0b..0911991 100644
--- a/test/api/test-map.c
+++ b/test/api/test-map.c
@@ -31,11 +31,12 @@
 test_map_basic (void)
 {
   hb_map_t *empty = hb_map_get_empty ();
+  hb_map_t *m;
   g_assert (hb_map_is_empty (empty));
   g_assert (!hb_map_allocation_successful (empty));
   hb_map_destroy (empty);
 
-  hb_map_t *m = hb_map_create ();
+  m = hb_map_create ();
   g_assert (hb_map_allocation_successful (m));
   g_assert (hb_map_is_empty (m));
 
@@ -68,11 +69,12 @@
 
   hb_user_data_key_t key[2];
   int *data = (int *) malloc (sizeof (int));
+  int *data2;
   *data = 3123;
   hb_map_set_user_data (m, &key[0], data, free, TRUE);
   g_assert_cmpint (*((int *) hb_map_get_user_data (m, &key[0])), ==, 3123);
 
-  int *data2 = (int *) malloc (sizeof (int));
+  data2 = (int *) malloc (sizeof (int));
   *data2 = 6343;
   hb_map_set_user_data (m, &key[0], data2, free, FALSE);
   g_assert_cmpint (*((int *) hb_map_get_user_data (m, &key[0])), ==, 3123);
@@ -86,10 +88,11 @@
 test_map_refcount (void)
 {
   hb_map_t *m = hb_map_create ();
+  hb_map_t *m2;
   hb_map_set (m, 213, 223);
   g_assert_cmpint (hb_map_get (m, 213), ==, 223);
 
-  hb_map_t *m2 = hb_map_reference (m);
+  m2 = hb_map_reference (m);
   hb_map_destroy (m);
 
   /* We copied its reference so it is still usable after one destroy */
diff --git a/test/api/test-multithread.c b/test/api/test-multithread.c
index 7b62a02..f946049 100644
--- a/test/api/test-multithread.c
+++ b/test/api/test-multithread.c
@@ -23,16 +23,13 @@
  *
  */
 
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
 #include <pthread.h>
 
 #include <hb.h>
 #include <hb-ft.h>
 #include <hb-ot.h>
-#include <glib.h>
+
+#include "hb-test.h"
 
 static const char *font_path = "fonts/Inconsolata-Regular.abc.ttf";
 static const char *text = "abc";
@@ -127,15 +124,9 @@
 int
 main (int argc, char **argv)
 {
-  g_test_init (&argc, &argv, NULL);
+  hb_test_init (&argc, &argv);
 
-#if GLIB_CHECK_VERSION(2,37,2)
-  gchar *default_path = g_test_build_filename (G_TEST_DIST, font_path, NULL);
-#else
-  gchar *default_path = g_strdup (font_path);
-#endif
-
-  char *path = argc > 1 && *argv[1] ? argv[1] : (char *) default_path;
+  char *path = argc > 1 && *argv[1] ? argv[1] : (char *) font_path;
   if (argc > 2)
     num_threads = atoi (argv[2]);
   if (argc > 3)
@@ -147,11 +138,7 @@
    * https://github.com/harfbuzz/harfbuzz/issues/1191 */
   hb_language_get_default ();
 
-  hb_blob_t *blob = hb_blob_create_from_file (path);
-  if (hb_blob_get_length (blob) == 0)
-    g_error ("Font not found.");
-
-  hb_face_t *face = hb_face_create (blob, 0);
+  hb_face_t *face = hb_test_open_font_file (path);
   font = hb_font_create (face);
 
   /* Fill the reference */
@@ -170,9 +157,6 @@
 
   hb_font_destroy (font);
   hb_face_destroy (face);
-  hb_blob_destroy (blob);
-
-  g_free (default_path);
 
   return 0;
 }
diff --git a/test/api/test-object.c b/test/api/test-object.c
index 02b9760..093615e 100644
--- a/test/api/test-object.c
+++ b/test/api/test-object.c
@@ -235,7 +235,7 @@
 
     {
       unsigned int j;
-      data_t data[2] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}};
+      data_t data[1000] = {{MAGIC0, FALSE}, {MAGIC1, FALSE}};
       deadlock_test_t deadlock_test;
 
       g_test_message ("Testing object %s", o->name);
diff --git a/test/api/test-ot-color.c b/test/api/test-ot-color.c
index 254f015..c0cbd77 100644
--- a/test/api/test-ot-color.c
+++ b/test/api/test-ot-color.c
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2016  Google, Inc.
+ * Copyright © 2018  Ebrahim Byagowi
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -27,8 +28,6 @@
 #include "hb-test.h"
 
 #include <hb-ot.h>
-#include <stdlib.h>
-#include <stdio.h>
 
 /* Unit tests for hb-ot-color.h */
 
@@ -98,138 +97,142 @@
 */
 static hb_face_t *cpal_v1 = NULL;
 
+static hb_face_t *cpal = NULL;
+static hb_face_t *cbdt = NULL;
+static hb_face_t *sbix = NULL;
+static hb_face_t *svg = NULL;
+static hb_face_t *empty = NULL;
 
-#if 0
 #define assert_color_rgba(colors, i, r, g, b, a) G_STMT_START {	\
-  const hb_ot_color_t *_colors = (colors); \
+  const hb_color_t *_colors = (colors); \
   const size_t _i = (i); \
   const uint8_t red = (r), green = (g), blue = (b), alpha = (a); \
-  if (_colors[_i].red != red) { \
+  if (hb_color_get_red (_colors[_i]) != red) { \
     g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
-				"colors[" #i "].red", _colors[_i].red, "==", red, 'x'); \
+				"colors[" #i "]", _colors[_i], "==", red, 'x'); \
   } \
-  if (_colors[_i].green != green) { \
+  if (hb_color_get_green (_colors[_i]) != green) { \
     g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
-				"colors[" #i "].green", _colors[_i].green, "==", green, 'x'); \
+				"colors[" #i "]", _colors[_i], "==", green, 'x'); \
   } \
-  if (_colors[_i].blue != blue) { \
+  if (hb_color_get_blue (_colors[_i]) != blue) { \
     g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
-				"colors[" #i "].blue", colors[i].blue, "==", blue, 'x'); \
+				"colors[" #i "]", colors[_i], "==", blue, 'x'); \
   } \
-  if (_colors[_i].alpha != alpha) { \
+  if (hb_color_get_alpha (_colors[_i]) != alpha) { \
     g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
-				"colors[" #i "].alpha", _colors[_i].alpha, "==", alpha, 'x'); \
+				"colors[" #i "]", _colors[_i], "==", alpha, 'x'); \
   } \
 } G_STMT_END
 
 
 static void
-test_hb_ot_color_get_palette_count (void)
+test_hb_ot_color_palette_get_count (void)
 {
-  g_assert_cmpint (hb_ot_color_get_palette_count (hb_face_get_empty()), ==, 0);
-  g_assert_cmpint (hb_ot_color_get_palette_count (cpal_v0), ==, 2);
-  g_assert_cmpint (hb_ot_color_get_palette_count (cpal_v1), ==, 3);
+  g_assert_cmpint (hb_ot_color_palette_get_count (hb_face_get_empty()), ==, 0);
+  g_assert_cmpint (hb_ot_color_palette_get_count (cpal_v0), ==, 2);
+  g_assert_cmpint (hb_ot_color_palette_get_count (cpal_v1), ==, 3);
 }
 
 
 static void
-test_hb_ot_color_get_palette_name_id_empty (void)
+test_hb_ot_color_palette_get_name_id_empty (void)
 {
   /* numPalettes=0, so all calls are for out-of-bounds palette indices */
-  g_assert_cmpint (hb_ot_color_get_palette_name_id (hb_face_get_empty(), 0), ==, 0xffff);
-  g_assert_cmpint (hb_ot_color_get_palette_name_id (hb_face_get_empty(), 1), ==, 0xffff);
+  g_assert_cmpint (hb_ot_color_palette_get_name_id (hb_face_get_empty(), 0), ==, HB_OT_NAME_ID_INVALID);
+  g_assert_cmpint (hb_ot_color_palette_get_name_id (hb_face_get_empty(), 1), ==, HB_OT_NAME_ID_INVALID);
 }
 
 
 static void
-test_hb_ot_color_get_palette_name_id_v0 (void)
+test_hb_ot_color_palette_get_name_id_v0 (void)
 {
-  g_assert_cmpint (hb_ot_color_get_palette_name_id (cpal_v0, 0), ==, 0xffff);
-  g_assert_cmpint (hb_ot_color_get_palette_name_id (cpal_v0, 1), ==, 0xffff);
+  g_assert_cmpint (hb_ot_color_palette_get_name_id (cpal_v0, 0), ==, HB_OT_NAME_ID_INVALID);
+  g_assert_cmpint (hb_ot_color_palette_get_name_id (cpal_v0, 1), ==, HB_OT_NAME_ID_INVALID);
 
   /* numPalettes=2, so palette #2 is out of bounds */
-  g_assert_cmpint (hb_ot_color_get_palette_name_id (cpal_v0, 2), ==, 0xffff);
+  g_assert_cmpint (hb_ot_color_palette_get_name_id (cpal_v0, 2), ==, HB_OT_NAME_ID_INVALID);
 }
 
 
 static void
-test_hb_ot_color_get_palette_name_id_v1 (void)
+test_hb_ot_color_palette_get_name_id_v1 (void)
 {
-  g_assert_cmpint (hb_ot_color_get_palette_name_id (cpal_v1, 0), ==, 257);
-  g_assert_cmpint (hb_ot_color_get_palette_name_id (cpal_v1, 1), ==, 0xffff);
-  g_assert_cmpint (hb_ot_color_get_palette_name_id (cpal_v1, 2), ==, 258);
+  g_assert_cmpint (hb_ot_color_palette_get_name_id (cpal_v1, 0), ==, 257);
+  g_assert_cmpint (hb_ot_color_palette_get_name_id (cpal_v1, 1), ==, HB_OT_NAME_ID_INVALID);
+  g_assert_cmpint (hb_ot_color_palette_get_name_id (cpal_v1, 2), ==, 258);
 
   /* numPalettes=3, so palette #3 is out of bounds */
-  g_assert_cmpint (hb_ot_color_get_palette_name_id (cpal_v1, 3), ==, 0xffff);
+  g_assert_cmpint (hb_ot_color_palette_get_name_id (cpal_v1, 3), ==, HB_OT_NAME_ID_INVALID);
 }
 
+
 static void
-test_hb_ot_color_get_palette_flags_empty (void)
+test_hb_ot_color_palette_get_flags_empty (void)
 {
   /* numPalettes=0, so all calls are for out-of-bounds palette indices */
-  g_assert_cmpint (hb_ot_color_get_palette_flags (hb_face_get_empty(), 0), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
-  g_assert_cmpint (hb_ot_color_get_palette_flags (hb_face_get_empty(), 1), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
+  g_assert_cmpint (hb_ot_color_palette_get_flags (hb_face_get_empty(), 0), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
+  g_assert_cmpint (hb_ot_color_palette_get_flags (hb_face_get_empty(), 1), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
 }
 
 
 static void
-test_hb_ot_color_get_palette_flags_v0 (void)
+test_hb_ot_color_palette_get_flags_v0 (void)
 {
-  g_assert_cmpint (hb_ot_color_get_palette_flags (cpal_v0, 0), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
-  g_assert_cmpint (hb_ot_color_get_palette_flags (cpal_v0, 1), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
+  g_assert_cmpint (hb_ot_color_palette_get_flags (cpal_v0, 0), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
+  g_assert_cmpint (hb_ot_color_palette_get_flags (cpal_v0, 1), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
 
   /* numPalettes=2, so palette #2 is out of bounds */
-  g_assert_cmpint (hb_ot_color_get_palette_flags (cpal_v0, 2), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
+  g_assert_cmpint (hb_ot_color_palette_get_flags (cpal_v0, 2), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
 }
 
 
 static void
-test_hb_ot_color_get_palette_flags_v1 (void)
+test_hb_ot_color_palette_get_flags_v1 (void)
 {
-  g_assert_cmpint (hb_ot_color_get_palette_flags (cpal_v1, 0), ==, HB_OT_COLOR_PALETTE_FLAG_FOR_DARK_BACKGROUND);
-  g_assert_cmpint (hb_ot_color_get_palette_flags (cpal_v1, 1), ==, HB_OT_COLOR_PALETTE_FLAG_FOR_LIGHT_BACKGROUND);
-  g_assert_cmpint (hb_ot_color_get_palette_flags (cpal_v0, 2), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
+  g_assert_cmpint (hb_ot_color_palette_get_flags (cpal_v1, 0), ==, HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND);
+  g_assert_cmpint (hb_ot_color_palette_get_flags (cpal_v1, 1), ==, HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND);
+  g_assert_cmpint (hb_ot_color_palette_get_flags (cpal_v0, 2), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
 
   /* numPalettes=3, so palette #3 is out of bounds */
-  g_assert_cmpint (hb_ot_color_get_palette_flags (cpal_v0, 3), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
+  g_assert_cmpint (hb_ot_color_palette_get_flags (cpal_v0, 3), ==, HB_OT_COLOR_PALETTE_FLAG_DEFAULT);
 }
 
 
 static void
-test_hb_ot_color_get_palette_colors_empty (void)
+test_hb_ot_color_palette_get_colors_empty (void)
 {
-  hb_face_t *empty = hb_face_get_empty ();
-  g_assert_cmpint (hb_ot_color_get_palette_colors (empty, 0, 0, NULL, NULL), ==, 0);
+  g_assert_cmpint (hb_ot_color_palette_get_colors (empty, 0, 0, NULL, NULL), ==, 0);
 }
 
 
 static void
-test_hb_ot_color_get_palette_colors_v0 (void)
+test_hb_ot_color_palette_get_colors_v0 (void)
 {
-  unsigned int num_colors = hb_ot_color_get_palette_colors (cpal_v0, 0, 0, NULL, NULL);
-  hb_ot_color_t *colors = (hb_ot_color_t*) alloca (num_colors * sizeof (hb_ot_color_t));
+  unsigned int num_colors = hb_ot_color_palette_get_colors (cpal_v0, 0, 0, NULL, NULL);
+  hb_color_t *colors = (hb_color_t*) alloca (num_colors * sizeof (hb_color_t));
   size_t colors_size = num_colors * sizeof(*colors);
   g_assert_cmpint (num_colors, ==, 2);
 
   /* Palette #0, start_index=0 */
-  g_assert_cmpint (hb_ot_color_get_palette_colors (cpal_v0, 0, 0, &num_colors, colors), ==, 2);
+  g_assert_cmpint (hb_ot_color_palette_get_colors (cpal_v0, 0, 0, &num_colors, colors), ==, 2);
   g_assert_cmpint (num_colors, ==, 2);
   assert_color_rgba (colors, 0, 0x00, 0x00, 0x00, 0xff);
   assert_color_rgba (colors, 1, 0x66, 0xcc, 0xff, 0xff);
 
   /* Palette #1, start_index=0 */
-  g_assert_cmpint (hb_ot_color_get_palette_colors (cpal_v0, 1, 0, &num_colors, colors), ==, 2);
+  g_assert_cmpint (hb_ot_color_palette_get_colors (cpal_v0, 1, 0, &num_colors, colors), ==, 2);
   g_assert_cmpint (num_colors, ==, 2);
   assert_color_rgba (colors, 0, 0x00, 0x00, 0x00, 0xff);
   assert_color_rgba (colors, 1, 0x80, 0x00, 0x00, 0xff);
 
   /* Palette #2 (there are only #0 and #1 in the font, so this is out of bounds) */
-  g_assert_cmpint (hb_ot_color_get_palette_colors (cpal_v0, 2, 0, &num_colors, colors), ==, 0);
+  g_assert_cmpint (hb_ot_color_palette_get_colors (cpal_v0, 2, 0, &num_colors, colors), ==, 0);
 
   /* Palette #0, start_index=1 */
   memset(colors, 0x33, colors_size);
   num_colors = 2;
-  g_assert_cmpint (hb_ot_color_get_palette_colors (cpal_v0, 0, 1, &num_colors, colors), ==, 2);
+  g_assert_cmpint (hb_ot_color_palette_get_colors (cpal_v0, 0, 1, &num_colors, colors), ==, 2);
   g_assert_cmpint (num_colors, ==, 1);
   assert_color_rgba (colors, 0, 0x66, 0xcc, 0xff, 0xff);
   assert_color_rgba (colors, 1, 0x33, 0x33, 0x33, 0x33);  /* untouched */
@@ -237,15 +240,15 @@
   /* Palette #0, start_index=0, pretend that we have only allocated space for 1 color */
   memset(colors, 0x44, colors_size);
   num_colors = 1;
-  g_assert_cmpint (hb_ot_color_get_palette_colors (cpal_v0, 0, 0, &num_colors, colors), ==, 2);
+  g_assert_cmpint (hb_ot_color_palette_get_colors (cpal_v0, 0, 0, &num_colors, colors), ==, 2);
   g_assert_cmpint (num_colors, ==, 1);
   assert_color_rgba (colors, 0, 0x00, 0x00, 0x00, 0xff);
   assert_color_rgba (colors, 1, 0x44, 0x44, 0x44, 0x44);  /* untouched */
 
   /* start_index > numPaletteEntries */
-  memset(colors, 0x44, colors_size);
+  memset (colors, 0x44, colors_size);
   num_colors = 2;
-  g_assert_cmpint (hb_ot_color_get_palette_colors (cpal_v0, 0, 9876, &num_colors, colors), ==, 2);
+  g_assert_cmpint (hb_ot_color_palette_get_colors (cpal_v0, 0, 9876, &num_colors, colors), ==, 2);
   g_assert_cmpint (num_colors, ==, 0);
   assert_color_rgba (colors, 0, 0x44, 0x44, 0x44, 0x44);  /* untouched */
   assert_color_rgba (colors, 1, 0x44, 0x44, 0x44, 0x44);  /* untouched */
@@ -253,46 +256,199 @@
 
 
 static void
-test_hb_ot_color_get_palette_colors_v1 (void)
+test_hb_ot_color_palette_get_colors_v1 (void)
 {
-  hb_ot_color_t colors[3];
-  unsigned int num_colors = hb_ot_color_get_palette_colors (cpal_v1, 0, 0, NULL, NULL);
-  size_t colors_size = 3 * sizeof(*colors);
+  hb_color_t colors[3];
+  unsigned int num_colors = hb_ot_color_palette_get_colors (cpal_v1, 0, 0, NULL, NULL);
+  size_t colors_size = 3 * sizeof (hb_color_t);
   g_assert_cmpint (num_colors, ==, 2);
 
   /* Palette #0, start_index=0 */
-  memset(colors, 0x77, colors_size);
-  g_assert_cmpint (hb_ot_color_get_palette_colors (cpal_v1, 0, 0, &num_colors, colors), ==, 2);
+  memset (colors, 0x77, colors_size);
+  g_assert_cmpint (hb_ot_color_palette_get_colors (cpal_v1, 0, 0, &num_colors, colors), ==, 2);
   g_assert_cmpint (num_colors, ==, 2);
   assert_color_rgba (colors, 0, 0x00, 0x00, 0x00, 0xff);
   assert_color_rgba (colors, 1, 0x66, 0xcc, 0xff, 0xff);
   assert_color_rgba (colors, 2, 0x77, 0x77, 0x77, 0x77);  /* untouched */
 
   /* Palette #1, start_index=0 */
-  memset(colors, 0x77, colors_size);
-  g_assert_cmpint (hb_ot_color_get_palette_colors (cpal_v1, 1, 0, &num_colors, colors), ==, 2);
+  memset (colors, 0x77, colors_size);
+  g_assert_cmpint (hb_ot_color_palette_get_colors (cpal_v1, 1, 0, &num_colors, colors), ==, 2);
   g_assert_cmpint (num_colors, ==, 2);
   assert_color_rgba (colors, 0, 0x00, 0x00, 0x00, 0xff);
   assert_color_rgba (colors, 1, 0xff, 0xcc, 0x66, 0xff);
   assert_color_rgba (colors, 2, 0x77, 0x77, 0x77, 0x77);  /* untouched */
 
   /* Palette #2, start_index=0 */
-  memset(colors, 0x77, colors_size);
-  g_assert_cmpint (hb_ot_color_get_palette_colors (cpal_v1, 2, 0, &num_colors, colors), ==, 2);
+  memset (colors, 0x77, colors_size);
+  g_assert_cmpint (hb_ot_color_palette_get_colors (cpal_v1, 2, 0, &num_colors, colors), ==, 2);
   g_assert_cmpint (num_colors, ==, 2);
   assert_color_rgba (colors, 0, 0x00, 0x00, 0x00, 0xff);
   assert_color_rgba (colors, 1, 0x80, 0x00, 0x00, 0xff);
   assert_color_rgba (colors, 2, 0x77, 0x77, 0x77, 0x77);  /* untouched */
 
   /* Palette #3 (out of bounds), start_index=0 */
-  memset(colors, 0x77, colors_size);
-  g_assert_cmpint (hb_ot_color_get_palette_colors (cpal_v1, 3, 0, &num_colors, colors), ==, 0);
+  memset (colors, 0x77, colors_size);
+  g_assert_cmpint (hb_ot_color_palette_get_colors (cpal_v1, 3, 0, &num_colors, colors), ==, 0);
   g_assert_cmpint (num_colors, ==, 0);
   assert_color_rgba (colors, 0, 0x77, 0x77, 0x77, 0x77);  /* untouched */
   assert_color_rgba (colors, 1, 0x77, 0x77, 0x77, 0x77);  /* untouched */
   assert_color_rgba (colors, 2, 0x77, 0x77, 0x77, 0x77);  /* untouched */
 }
-#endif
+
+
+static void
+test_hb_ot_color_palette_color_get_name_id (void)
+{
+  g_assert_cmpuint (hb_ot_color_palette_color_get_name_id (empty, 0), ==, HB_OT_NAME_ID_INVALID);
+  g_assert_cmpuint (hb_ot_color_palette_color_get_name_id (empty, 1), ==, HB_OT_NAME_ID_INVALID);
+  g_assert_cmpuint (hb_ot_color_palette_color_get_name_id (empty, 2), ==, HB_OT_NAME_ID_INVALID);
+  g_assert_cmpuint (hb_ot_color_palette_color_get_name_id (cpal_v0, 0), ==, HB_OT_NAME_ID_INVALID);
+  g_assert_cmpuint (hb_ot_color_palette_color_get_name_id (cpal_v0, 1), ==, HB_OT_NAME_ID_INVALID);
+  g_assert_cmpuint (hb_ot_color_palette_color_get_name_id (cpal_v0, 2), ==, HB_OT_NAME_ID_INVALID);
+  g_assert_cmpuint (hb_ot_color_palette_color_get_name_id (cpal_v1, 0), ==, HB_OT_NAME_ID_INVALID);
+  g_assert_cmpuint (hb_ot_color_palette_color_get_name_id (cpal_v1, 1), ==, 256);
+  g_assert_cmpuint (hb_ot_color_palette_color_get_name_id (cpal_v1, 2), ==, HB_OT_NAME_ID_INVALID);
+}
+
+
+static void
+test_hb_ot_color_glyph_get_layers (void)
+{
+  hb_ot_color_layer_t layers[1];
+  unsigned int count = 1;
+  unsigned int num_layers;
+
+  g_assert_cmpuint (hb_ot_color_glyph_get_layers (cpal_v1, 0, 0,
+						  NULL, NULL), ==, 0);
+  g_assert_cmpuint (hb_ot_color_glyph_get_layers (cpal_v1, 1, 0,
+						  NULL, NULL), ==, 0);
+  g_assert_cmpuint (hb_ot_color_glyph_get_layers (cpal_v1, 2, 0,
+						  NULL, NULL), ==, 2);
+
+  num_layers = hb_ot_color_glyph_get_layers (cpal_v1, 2, 0, &count, layers);
+
+  g_assert_cmpuint (num_layers, ==, 2);
+  g_assert_cmpuint (count, ==, 1);
+  g_assert_cmpuint (layers[0].glyph, ==, 3);
+  g_assert_cmpuint (layers[0].color_index, ==, 1);
+
+  count = 1;
+  hb_ot_color_glyph_get_layers (cpal_v1, 2, 1, &count, layers);
+
+  g_assert_cmpuint (num_layers, ==, 2);
+  g_assert_cmpuint (count, ==, 1);
+  g_assert_cmpuint (layers[0].glyph, ==, 4);
+  g_assert_cmpuint (layers[0].color_index, ==, 0);
+}
+
+static void
+test_hb_ot_color_has_data (void)
+{
+  g_assert (hb_ot_color_has_layers (empty) == FALSE);
+  g_assert (hb_ot_color_has_layers (cpal_v0) == TRUE);
+  g_assert (hb_ot_color_has_layers (cpal_v1) == TRUE);
+  g_assert (hb_ot_color_has_layers (cpal) == TRUE);
+  g_assert (hb_ot_color_has_layers (cbdt) == FALSE);
+  g_assert (hb_ot_color_has_layers (sbix) == FALSE);
+  g_assert (hb_ot_color_has_layers (svg) == FALSE);
+
+  g_assert (hb_ot_color_has_palettes (empty) == FALSE);
+  g_assert (hb_ot_color_has_palettes (cpal_v0) == TRUE);
+  g_assert (hb_ot_color_has_palettes (cpal_v1) == TRUE);
+  g_assert (hb_ot_color_has_palettes (cpal) == TRUE);
+  g_assert (hb_ot_color_has_palettes (cbdt) == FALSE);
+  g_assert (hb_ot_color_has_palettes (sbix) == FALSE);
+  g_assert (hb_ot_color_has_palettes (svg) == FALSE);
+
+  g_assert (hb_ot_color_has_svg (empty) == FALSE);
+  g_assert (hb_ot_color_has_svg (cpal_v0) == FALSE);
+  g_assert (hb_ot_color_has_svg (cpal_v1) == FALSE);
+  g_assert (hb_ot_color_has_svg (cpal) == FALSE);
+  g_assert (hb_ot_color_has_svg (cbdt) == FALSE);
+  g_assert (hb_ot_color_has_svg (sbix) == FALSE);
+  g_assert (hb_ot_color_has_svg (svg) == TRUE);
+
+  g_assert (hb_ot_color_has_png (empty) == FALSE);
+  g_assert (hb_ot_color_has_png (cpal_v0) == FALSE);
+  g_assert (hb_ot_color_has_png (cpal_v1) == FALSE);
+  g_assert (hb_ot_color_has_png (cpal) == FALSE);
+  g_assert (hb_ot_color_has_png (cbdt) == TRUE);
+  g_assert (hb_ot_color_has_png (sbix) == TRUE);
+  g_assert (hb_ot_color_has_png (svg) == FALSE);
+}
+
+static void
+test_hb_ot_color_svg (void)
+{
+  hb_blob_t *blob;
+  unsigned int length;
+  const char *data;
+
+  blob = hb_ot_color_glyph_reference_svg (svg, 0);
+  g_assert (hb_blob_get_length (blob) == 0);
+
+  blob = hb_ot_color_glyph_reference_svg (svg, 1);
+  data = hb_blob_get_data (blob, &length);
+  g_assert_cmpuint (length, ==, 146);
+  g_assert (strncmp (data, "<?xml", 4) == 0);
+  g_assert (strncmp (data + 140, "</svg>", 5) == 0);
+  hb_blob_destroy (blob);
+
+  blob = hb_ot_color_glyph_reference_svg (empty, 0);
+  g_assert (hb_blob_get_length (blob) == 0);
+}
+
+
+static void
+test_hb_ot_color_png (void)
+{
+  hb_blob_t *blob;
+  unsigned int length;
+  const char *data;
+  hb_glyph_extents_t extents;
+  hb_font_t *cbdt_font;
+
+  /* sbix */
+  hb_font_t *sbix_font;
+  sbix_font = hb_font_create (sbix);
+  blob = hb_ot_color_glyph_reference_png (sbix_font, 0);
+  hb_font_get_glyph_extents (sbix_font, 0, &extents);
+  g_assert_cmpint (extents.x_bearing, ==, 0);
+  g_assert_cmpint (extents.y_bearing, ==, 0);
+  g_assert_cmpint (extents.width, ==, 0);
+  g_assert_cmpint (extents.height, ==, 0);
+  g_assert (hb_blob_get_length (blob) == 0);
+
+  blob = hb_ot_color_glyph_reference_png (sbix_font, 1);
+  data = hb_blob_get_data (blob, &length);
+  g_assert_cmpuint (length, ==, 224);
+  g_assert (strncmp (data + 1, "PNG", 3) == 0);
+  hb_font_get_glyph_extents (sbix_font, 1, &extents);
+  g_assert_cmpint (extents.x_bearing, ==, 0);
+  g_assert_cmpint (extents.y_bearing, ==, 0);
+  g_assert_cmpint (extents.width, ==, 800);
+  g_assert_cmpint (extents.height, ==, 800);
+  hb_blob_destroy (blob);
+  hb_font_destroy (sbix_font);
+
+  /* cbdt */
+  cbdt_font = hb_font_create (cbdt);
+  blob = hb_ot_color_glyph_reference_png (cbdt_font, 0);
+  g_assert (hb_blob_get_length (blob) == 0);
+
+  blob = hb_ot_color_glyph_reference_png (cbdt_font, 1);
+  data = hb_blob_get_data (blob, &length);
+  g_assert_cmpuint (length, ==, 88);
+  g_assert (strncmp (data + 1, "PNG", 3) == 0);
+  hb_font_get_glyph_extents (cbdt_font, 1, &extents);
+  g_assert_cmpint (extents.x_bearing, ==, 0);
+  g_assert_cmpint (extents.y_bearing, ==, 1024);
+  g_assert_cmpint (extents.width, ==, 1024);
+  g_assert_cmpint (extents.height, ==, -1024);
+  hb_blob_destroy (blob);
+  hb_font_destroy (cbdt_font);
+}
 
 int
 main (int argc, char **argv)
@@ -300,20 +456,34 @@
   int status = 0;
 
   hb_test_init (&argc, &argv);
-  // cpal_v0 = hb_test_load_face ("../shaping/data/in-house/fonts/e90374e5e439e00725b4fe7a8d73db57c5a97f82.ttf");
-  // cpal_v1 = hb_test_load_face ("../shaping/data/in-house/fonts/319f5d7ebffbefc5c5e6569f8cea73444d7a7268.ttf");
-  // hb_test_add (test_hb_ot_color_get_palette_count);
-  // hb_test_add (test_hb_ot_color_get_palette_name_id_empty);
-  // hb_test_add (test_hb_ot_color_get_palette_name_id_v0);
-  // hb_test_add (test_hb_ot_color_get_palette_name_id_v1);
-  // hb_test_add (test_hb_ot_color_get_palette_flags_empty);
-  // hb_test_add (test_hb_ot_color_get_palette_flags_v0);
-  // hb_test_add (test_hb_ot_color_get_palette_flags_v1);
-  // hb_test_add (test_hb_ot_color_get_palette_colors_empty);
-  // hb_test_add (test_hb_ot_color_get_palette_colors_v0);
-  // hb_test_add (test_hb_ot_color_get_palette_colors_v1);
+  cpal_v0 = hb_test_open_font_file ("fonts/cpal-v0.ttf");
+  cpal_v1 = hb_test_open_font_file ("fonts/cpal-v1.ttf");
+  cpal = hb_test_open_font_file ("fonts/chromacheck-colr.ttf");
+  cbdt = hb_test_open_font_file ("fonts/chromacheck-cbdt.ttf");
+  sbix = hb_test_open_font_file ("fonts/chromacheck-sbix.ttf");
+  svg = hb_test_open_font_file ("fonts/chromacheck-svg.ttf");
+  empty = hb_face_get_empty ();
+  hb_test_add (test_hb_ot_color_palette_get_count);
+  hb_test_add (test_hb_ot_color_palette_get_name_id_empty);
+  hb_test_add (test_hb_ot_color_palette_get_name_id_v0);
+  hb_test_add (test_hb_ot_color_palette_get_name_id_v1);
+  hb_test_add (test_hb_ot_color_palette_get_flags_empty);
+  hb_test_add (test_hb_ot_color_palette_get_flags_v0);
+  hb_test_add (test_hb_ot_color_palette_get_flags_v1);
+  hb_test_add (test_hb_ot_color_palette_get_colors_empty);
+  hb_test_add (test_hb_ot_color_palette_get_colors_v0);
+  hb_test_add (test_hb_ot_color_palette_get_colors_v1);
+  hb_test_add (test_hb_ot_color_palette_color_get_name_id);
+  hb_test_add (test_hb_ot_color_glyph_get_layers);
+  hb_test_add (test_hb_ot_color_has_data);
+  hb_test_add (test_hb_ot_color_png);
+  hb_test_add (test_hb_ot_color_svg);
   status = hb_test_run();
   hb_face_destroy (cpal_v0);
   hb_face_destroy (cpal_v1);
+  hb_face_destroy (cpal);
+  hb_face_destroy (cbdt);
+  hb_face_destroy (sbix);
+  hb_face_destroy (svg);
   return status;
 }
diff --git a/test/api/test-ot-extents-cff.c b/test/api/test-ot-extents-cff.c
new file mode 100644
index 0000000..49b8799
--- /dev/null
+++ b/test/api/test-ot-extents-cff.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#include "hb-test.h"
+#include <hb-ot.h>
+
+/* Unit tests for CFF/CFF2 glyph extents */
+
+static void
+test_extents_cff1 (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf");
+  g_assert (face);
+  hb_font_t *font = hb_font_create (face);
+  hb_face_destroy (face);
+  g_assert (font);
+  hb_ot_font_set_funcs (font);
+
+  hb_glyph_extents_t  extents;
+  hb_bool_t result = hb_font_get_glyph_extents (font, 1, &extents);
+  g_assert (result);
+
+  g_assert_cmpint (extents.x_bearing, ==, 52);
+  g_assert_cmpint (extents.y_bearing, ==, 498);
+  g_assert_cmpint (extents.width, ==, 381);
+  g_assert_cmpint (extents.height, ==, -510);
+
+  hb_font_destroy (font);
+
+  hb_face_t *face_j = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf");
+  g_assert (face_j);
+  hb_font_t *font_j = hb_font_create (face_j);
+  hb_face_destroy (face_j);
+  g_assert (font_j);
+  hb_ot_font_set_funcs (font_j);
+
+  hb_bool_t result_j = hb_font_get_glyph_extents (font_j, 3, &extents);
+  g_assert (result_j);
+
+  g_assert_cmpint (extents.x_bearing, ==, 34);
+  g_assert_cmpint (extents.y_bearing, ==, 840);
+  g_assert_cmpint (extents.width, ==, 920);
+  g_assert_cmpint (extents.height, ==, -907);
+
+  hb_font_destroy (font_j);
+}
+
+static void
+test_extents_cff1_flex (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/cff1_flex.otf");
+  g_assert (face);
+  hb_font_t *font = hb_font_create (face);
+  hb_face_destroy (face);
+  g_assert (font);
+  hb_ot_font_set_funcs (font);
+
+  hb_glyph_extents_t  extents;
+  hb_bool_t result = hb_font_get_glyph_extents (font, 1, &extents);
+  g_assert (result);
+
+  g_assert_cmpint (extents.x_bearing, ==, -20);
+  g_assert_cmpint (extents.y_bearing, ==, 520);
+  g_assert_cmpint (extents.width, ==, 540);
+  g_assert_cmpint (extents.height, ==, -540);
+
+  hb_font_destroy (font);
+}
+
+static void
+test_extents_cff1_seac (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/cff1_seac.otf");
+  g_assert (face);
+  hb_font_t *font = hb_font_create (face);
+  hb_face_destroy (face);
+  g_assert (font);
+  hb_ot_font_set_funcs (font);
+
+  hb_glyph_extents_t  extents;
+  hb_bool_t result = hb_font_get_glyph_extents (font, 3, &extents); /* Agrave */
+  g_assert (result);
+
+  g_assert_cmpint (extents.x_bearing, ==, 3);
+  g_assert_cmpint (extents.y_bearing, ==, 861);
+  g_assert_cmpint (extents.width, ==, 538);
+  g_assert_cmpint (extents.height, ==, -861);
+
+  result = hb_font_get_glyph_extents (font, 4, &extents); /* Udieresis */
+  g_assert (result);
+
+  g_assert_cmpint (extents.x_bearing, ==, 87);
+  g_assert_cmpint (extents.y_bearing, ==, 827);
+  g_assert_cmpint (extents.width, ==, 471);
+  g_assert_cmpint (extents.height, ==, -839);
+
+  hb_font_destroy (font);
+}
+
+static void
+test_extents_cff2 (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf");
+  g_assert (face);
+  hb_font_t *font = hb_font_create (face);
+  hb_face_destroy (face);
+  g_assert (font);
+  hb_ot_font_set_funcs (font);
+
+  hb_glyph_extents_t  extents;
+  hb_bool_t result = hb_font_get_glyph_extents (font, 1, &extents);
+  g_assert (result);
+
+  g_assert_cmpint (extents.x_bearing, ==, 46);
+  g_assert_cmpint (extents.y_bearing, ==, 487);
+  g_assert_cmpint (extents.width, ==, 455);
+  g_assert_cmpint (extents.height, ==, -500);
+
+  float coords[2] = { 600.0f, 50.0f };
+  hb_font_set_var_coords_design (font, coords, 2);
+  result = hb_font_get_glyph_extents (font, 1, &extents);
+  g_assert (result);
+
+  g_assert_cmpint (extents.x_bearing, ==, 38);
+  g_assert_cmpint (extents.y_bearing, ==, 493);
+  g_assert_cmpint (extents.width, ==, 481);
+  g_assert_cmpint (extents.height, ==, -508);
+
+  hb_font_destroy (font);
+}
+
+static void
+test_extents_cff2_vsindex (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/AdobeVFPrototype_vsindex.otf");
+  g_assert (face);
+  hb_font_t *font = hb_font_create (face);
+  hb_face_destroy (face);
+  g_assert (font);
+  hb_ot_font_set_funcs (font);
+
+  hb_glyph_extents_t  extents;
+  float coords[2] = { 800.0f, 50.0f };
+  hb_font_set_var_coords_design (font, coords, 2);
+  hb_bool_t result = hb_font_get_glyph_extents (font, 1, &extents);
+  g_assert (result);
+
+  g_assert_cmpint (extents.x_bearing, ==, 11);
+  g_assert_cmpint (extents.y_bearing, ==, 656);
+  g_assert_cmpint (extents.width, ==, 653);
+  g_assert_cmpint (extents.height, ==, -656);
+
+  result = hb_font_get_glyph_extents (font, 2, &extents);
+  g_assert (result);
+
+  g_assert_cmpint (extents.x_bearing, ==, 7);
+  g_assert_cmpint (extents.y_bearing, ==, 669);
+  g_assert_cmpint (extents.width, ==, 650);
+  g_assert_cmpint (extents.height, ==, -669);
+
+  hb_font_destroy (font);
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_extents_cff1);
+  hb_test_add (test_extents_cff1_flex);
+  hb_test_add (test_extents_cff1_seac);
+  hb_test_add (test_extents_cff2);
+  hb_test_add (test_extents_cff2_vsindex);
+
+  return hb_test_run ();
+}
diff --git a/test/api/test-ot-face.c b/test/api/test-ot-face.c
new file mode 100644
index 0000000..f2d2fca
--- /dev/null
+++ b/test/api/test-ot-face.c
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef TEST_OT_FACE_NO_MAIN
+#include "hb-test.h"
+#endif
+#include <hb-ot.h>
+
+/* Unit tests for hb-ot-*.h */
+
+
+static void
+test_face (hb_face_t *face,
+	   hb_codepoint_t cp)
+{
+  hb_font_t *font = hb_font_create (face);
+  hb_set_t *set;
+  hb_codepoint_t g;
+  hb_position_t x, y;
+  char buf[5] = {0};
+  unsigned int len;
+  hb_glyph_extents_t extents;
+  hb_ot_font_set_funcs (font);
+
+  set = hb_set_create ();
+  hb_face_collect_unicodes (face, set);
+  hb_face_collect_variation_selectors (face, set);
+  hb_face_collect_variation_unicodes (face, cp, set);
+
+  hb_font_get_nominal_glyph (font, cp, &g);
+  hb_font_get_variation_glyph (font, cp, cp, &g);
+  hb_font_get_glyph_h_advance (font, cp);
+  hb_font_get_glyph_v_advance (font, cp);
+  hb_font_get_glyph_h_origin (font, cp, &x, &y);
+  hb_font_get_glyph_v_origin (font, cp, &x, &y);
+  hb_font_get_glyph_extents (font, cp, &extents);
+  hb_font_get_glyph_contour_point (font, cp, 0, &x, &y);
+  hb_font_get_glyph_name (font, cp, buf, sizeof (buf));
+  hb_font_get_glyph_from_name (font, buf, strlen (buf), &g);
+
+  hb_ot_color_has_palettes (face);
+  hb_ot_color_palette_get_count (face);
+  hb_ot_color_palette_get_name_id (face, cp);
+  hb_ot_color_palette_color_get_name_id (face, cp);
+  hb_ot_color_palette_get_flags (face, cp);
+  hb_ot_color_palette_get_colors (face, cp, 0, NULL, NULL);
+  hb_ot_color_has_layers (face);
+  hb_ot_color_glyph_get_layers (face, cp, 0, NULL, NULL);
+  hb_ot_color_has_svg (face);
+  hb_blob_destroy (hb_ot_color_glyph_reference_svg (face, cp));
+  hb_ot_color_has_png (face);
+  hb_blob_destroy (hb_ot_color_glyph_reference_png (font, cp));
+
+  hb_ot_layout_has_glyph_classes (face);
+  hb_ot_layout_has_substitution (face);
+  hb_ot_layout_has_positioning (face);
+
+  hb_ot_math_has_data (face);
+  hb_ot_math_get_constant (font, HB_OT_MATH_CONSTANT_MATH_LEADING);
+  hb_ot_math_get_glyph_italics_correction (font, cp);
+  hb_ot_math_get_glyph_top_accent_attachment (font, cp);
+  hb_ot_math_is_glyph_extended_shape (face, cp);
+  hb_ot_math_get_glyph_kerning (font, cp, HB_OT_MATH_KERN_BOTTOM_RIGHT, 0);
+  hb_ot_math_get_glyph_variants (font, cp, HB_DIRECTION_TTB, 0, NULL, NULL);
+  hb_ot_math_get_min_connector_overlap (font, HB_DIRECTION_RTL);
+  hb_ot_math_get_glyph_assembly (font, cp, HB_DIRECTION_BTT, 0, NULL, NULL, NULL);
+
+  len = sizeof (buf);
+  hb_ot_name_list_names (face, NULL);
+  hb_ot_name_get_utf8 (face, cp, NULL, &len, buf);
+  hb_ot_name_get_utf16 (face, cp, NULL, NULL, NULL);
+  hb_ot_name_get_utf32 (face, cp, NULL, NULL, NULL);
+
+  hb_ot_var_get_axis_count (face);
+  hb_ot_var_get_axis_infos (face, 0, NULL, NULL);
+  hb_ot_var_normalize_variations (face, NULL, 0, NULL, 0);
+  hb_ot_var_normalize_coords (face, 0, NULL, NULL);
+
+  hb_set_destroy (set);
+  hb_font_destroy (font);
+}
+
+#ifndef TEST_OT_FACE_NO_MAIN
+static void
+test_ot_face_empty (void)
+{
+  test_face (hb_face_get_empty (), 0);
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_ot_face_empty);
+
+  return hb_test_run();
+}
+#endif
diff --git a/test/api/test-ot-ligature-carets.c b/test/api/test-ot-ligature-carets.c
new file mode 100644
index 0000000..d842785
--- /dev/null
+++ b/test/api/test-ot-ligature-carets.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ */
+
+#include "hb-test.h"
+
+#include <hb-ot.h>
+
+static void
+test_ot_layout_feature_get_name_ids_and_characters (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/lcar.ttf");
+  hb_font_t *font = hb_font_create (face);
+  hb_font_set_scale (font, hb_face_get_upem (face) * 2, hb_face_get_upem (face) * 4);
+
+  hb_position_t caret_array[2];
+  unsigned int caret_count = 2;
+  g_assert_cmpuint (2, ==, hb_ot_layout_get_ligature_carets (font, HB_DIRECTION_RTL,
+							     98, 0, &caret_count,
+							     caret_array));
+
+  g_assert_cmpuint (2, ==, caret_count);
+  g_assert_cmpuint (1130, ==, caret_array[0]);
+  g_assert_cmpuint (2344, ==, caret_array[1]);
+
+  g_assert_cmpuint (2, ==, hb_ot_layout_get_ligature_carets (font, HB_DIRECTION_BTT,
+							     98, 0, &caret_count,
+							     caret_array));
+
+  g_assert_cmpuint (2, ==, caret_count);
+  g_assert_cmpuint (2260, ==, caret_array[0]);
+  g_assert_cmpuint (4688, ==, caret_array[1]);
+
+  hb_font_destroy (font);
+  hb_face_destroy (face);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  hb_test_add (test_ot_layout_feature_get_name_ids_and_characters);
+
+  return hb_test_run ();
+}
diff --git a/test/api/test-ot-name.c b/test/api/test-ot-name.c
index 2da504a..c2ae4fd 100644
--- a/test/api/test-ot-name.c
+++ b/test/api/test-ot-name.c
@@ -27,7 +27,6 @@
 
 #include <hb-ot.h>
 
-static const char *font_path = "fonts/cv01.otf";
 static hb_face_t *face;
 
 static void
@@ -35,6 +34,14 @@
 {
   hb_tag_t cv01 = HB_TAG ('c','v','0','1');
   unsigned int feature_index;
+  unsigned int num_named_parameters;
+  hb_ot_name_id_t label_id;
+  hb_ot_name_id_t tooltip_id;
+  hb_ot_name_id_t sample_id;
+  hb_ot_name_id_t first_param_id;
+  hb_codepoint_t characters[100];
+  unsigned int char_count = 100;
+  unsigned int all_chars;
   if (!hb_ot_layout_language_find_feature (face,
 					   HB_OT_TAG_GSUB,
 					   0,
@@ -43,11 +50,6 @@
 					   &feature_index))
      g_error ("Failed to find feature index");
 
-  hb_name_id_t label_id;
-  hb_name_id_t tooltip_id;
-  hb_name_id_t sample_id;
-  unsigned int num_named_parameters;
-  hb_name_id_t first_param_id;
   if (!hb_ot_layout_feature_get_name_ids (face, HB_OT_TAG_GSUB, feature_index,
 					  &label_id, &tooltip_id, &sample_id,
 					  &num_named_parameters, &first_param_id))
@@ -59,10 +61,6 @@
   g_assert_cmpint (num_named_parameters, ==, 2);
   g_assert_cmpint (first_param_id, ==, 259);
 
-  hb_codepoint_t characters[100];
-  unsigned int char_count = 100;
-
-  unsigned int all_chars;
   all_chars = hb_ot_layout_feature_get_characters (face, HB_OT_TAG_GSUB, feature_index,
 						   0, &char_count, characters);
 
@@ -72,31 +70,37 @@
   g_assert_cmpint (characters[1], ==, 24030);
 }
 
+static void
+test_ot_name (void)
+{
+  unsigned int num_entries;
+  const hb_ot_name_entry_t *entries;
+  hb_ot_name_id_t name_id;
+  hb_language_t lang;
+  char text[10];
+  unsigned int text_size = 10;
+  entries = hb_ot_name_list_names (face, &num_entries);
+  g_assert_cmpuint (12, ==, num_entries);
+  name_id = entries[3].name_id;
+  g_assert_cmpuint (3, ==, name_id);
+  lang = entries[3].language;
+  g_assert_cmpstr (hb_language_to_string (lang), ==, "en");
+  g_assert_cmpuint (27, ==, hb_ot_name_get_utf8 (face, name_id, lang, &text_size, text));
+  g_assert_cmpuint (9, ==, text_size);
+  g_assert_cmpstr (text, ==, "FontForge");
+}
+
 int
 main (int argc, char **argv)
 {
+  unsigned int status;
   g_test_init (&argc, &argv, NULL);
 
-#if GLIB_CHECK_VERSION(2,37,2)
-  gchar *default_path = g_test_build_filename (G_TEST_DIST, font_path, NULL);
-#else
-  gchar *default_path = g_strdup (font_path);
-#endif
-
-  hb_blob_t *blob;
-
-  char *path = argc > 1 && *argv[1] ? argv[1] : (char *) default_path;
-  blob = hb_blob_create_from_file (path);
-  if (hb_blob_get_length (blob) == 0)
-    g_error ("Font not found.");
-
-  face = hb_face_create (blob, 0);
-
   hb_test_add (test_ot_layout_feature_get_name_ids_and_characters);
+  hb_test_add (test_ot_name);
 
-  unsigned int result = hb_test_run ();
+  face = hb_test_open_font_file ("fonts/cv01.otf");
+  status = hb_test_run ();
   hb_face_destroy (face);
-  hb_blob_destroy (blob);
-  g_free (default_path);
-  return result;
+  return status;
 }
diff --git a/test/api/test-ot-tag.c b/test/api/test-ot-tag.c
index f89c25d..60231af 100644
--- a/test/api/test-ot-tag.c
+++ b/test/api/test-ot-tag.c
@@ -60,7 +60,7 @@
 {
   hb_script_t tag;
   unsigned int count = 1;
-  hb_script_t t;
+  hb_tag_t t;
 
   g_test_message ("Testing script %c%c%c%c: script tag %s, language tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s, lang_s);
   tag = hb_tag_from_string (s, -1);
@@ -78,7 +78,7 @@
 test_indic_tags (const char *s1, const char *s2, const char *s3, hb_script_t script)
 {
   hb_script_t tag1, tag2, tag3;
-  hb_script_t t[3];
+  hb_tag_t t[3];
   unsigned int count = 3;
 
   g_test_message ("Testing script %c%c%c%c: USE tag %s, new tag %s, old tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s1, s2, s3);
@@ -103,7 +103,7 @@
 static void
 test_ot_tag_script_degenerate (void)
 {
-  hb_script_t t[2];
+  hb_tag_t t[2];
   unsigned int count = 2;
 
   g_assert_cmphex (HB_TAG_CHAR4 ("DFLT"), ==, HB_OT_TAG_DEFAULT_SCRIPT);
@@ -202,11 +202,11 @@
 {
   hb_language_t lang = hb_language_from_string (lang_s, -1);
   hb_tag_t tag = hb_tag_from_string (tag_s, -1);
+  hb_tag_t tag2;
+  unsigned int count = 1;
 
   g_test_message ("Testing language %s <-> tag %s", lang_s, tag_s);
 
-  hb_tag_t tag2;
-  unsigned int count = 1;
   hb_ot_tags_from_script_and_language (HB_SCRIPT_INVALID,
 				       lang,
 				       NULL, NULL, &count, &tag2);
@@ -223,11 +223,11 @@
 {
   hb_language_t lang = hb_language_from_string (lang_s, -1);
   hb_tag_t tag = hb_tag_from_string (tag_s, -1);
+  hb_tag_t tag2;
+  unsigned int count = 1;
 
   g_test_message ("Testing language %s -> tag %s", lang_s, tag_s);
 
-  hb_tag_t tag2;
-  unsigned int count = 1;
   hb_ot_tags_from_script_and_language (HB_SCRIPT_INVALID,
 				       lang,
 				       NULL, NULL, &count, &tag2);
@@ -449,6 +449,9 @@
 
   /* A UN M.49 region code, not an extended language subtag */
   test_tag_from_language ("ARA", "ar-001");
+
+  /* An invalid tag */
+  test_tag_from_language ("TRK", "tr@foo=bar");
 }
 
 static void
@@ -464,9 +467,10 @@
   unsigned int i;
   hb_tag_t *script_tags = malloc (script_count * sizeof (hb_tag_t));
   hb_tag_t *language_tags = malloc (language_count * sizeof (hb_tag_t));
+  hb_language_t lang;
   g_assert (script_tags);
   g_assert (language_tags);
-  hb_language_t lang = hb_language_from_string (lang_s, -1);
+  lang = hb_language_from_string (lang_s, -1);
   va_start (expected_tags, expected_language_count);
 
   hb_ot_tags_from_script_and_language (script, lang, &script_count, script_tags, &language_count, language_tags);
diff --git a/test/api/test-subset-cff1.c b/test/api/test-subset-cff1.c
new file mode 100644
index 0000000..3ee2702
--- /dev/null
+++ b/test/api/test-subset-cff1.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#include "hb-test.h"
+#include "hb-subset-test.h"
+
+/* Unit tests for CFF subsetting */
+
+static void
+test_subset_cff1_noop (void)
+{
+  hb_face_t *face_abc = hb_test_open_font_file("fonts/SourceSansPro-Regular.abc.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'b');
+  hb_set_add (codepoints, 'c');
+  face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints));
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('C','F','F',' '));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+}
+
+static void
+test_subset_cff1 (void)
+{
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansPro-Regular.ac.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints));
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C','F','F',' '));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+static void
+test_subset_cff1_strip_hints (void)
+{
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansPro-Regular.ac.nohints.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_subset_input_t *input;
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_drop_hints (input, true);
+  face_abc_subset = hb_subset_test_create_subset (face_abc, input);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', ' '));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+static void
+test_subset_cff1_desubr (void)
+{
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansPro-Regular.ac.nosubrs.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_subset_input_t *input;
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_desubroutinize (input, true);
+  face_abc_subset = hb_subset_test_create_subset (face_abc, input);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C','F','F',' '));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+static void
+test_subset_cff1_desubr_strip_hints (void)
+{
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansPro-Regular.ac.nosubrs.nohints.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_subset_input_t *input;
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_drop_hints (input, true);
+  hb_subset_input_set_desubroutinize (input, true);
+  face_abc_subset = hb_subset_test_create_subset (face_abc, input);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', ' '));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+static void
+test_subset_cff1_j (void)
+{
+  hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf");
+  hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_face_t *face_41_3041_4c2e_subset;
+  hb_set_add (codepoints, 0x41);
+  hb_set_add (codepoints, 0x4C2E);
+  face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, hb_subset_test_create_input (codepoints));
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' '));
+
+  hb_face_destroy (face_41_3041_4c2e_subset);
+  hb_face_destroy (face_41_3041_4c2e);
+  hb_face_destroy (face_41_4c2e);
+}
+
+static void
+test_subset_cff1_j_strip_hints (void)
+{
+  hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf");
+  hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.nohints.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_face_t *face_41_3041_4c2e_subset;
+  hb_subset_input_t *input;
+  hb_set_add (codepoints, 0x41);
+  hb_set_add (codepoints, 0x4C2E);
+  input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_drop_hints (input, true);
+  face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, input);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' '));
+
+  hb_face_destroy (face_41_3041_4c2e_subset);
+  hb_face_destroy (face_41_3041_4c2e);
+  hb_face_destroy (face_41_4c2e);
+}
+
+static void
+test_subset_cff1_j_desubr (void)
+{
+  hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf");
+  hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.nosubrs.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_face_t *face_41_3041_4c2e_subset;
+  hb_subset_input_t *input;
+  hb_set_add (codepoints, 0x41);
+  hb_set_add (codepoints, 0x4C2E);
+  input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_desubroutinize (input, true);
+  face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, input);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' '));
+
+  hb_face_destroy (face_41_3041_4c2e_subset);
+  hb_face_destroy (face_41_3041_4c2e);
+  hb_face_destroy (face_41_4c2e);
+}
+
+static void
+test_subset_cff1_j_desubr_strip_hints (void)
+{
+  hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf");
+  hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.nosubrs.nohints.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_face_t *face_41_3041_4c2e_subset;
+  hb_subset_input_t *input;
+  hb_set_add (codepoints, 0x41);
+  hb_set_add (codepoints, 0x4C2E);
+  input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_drop_hints (input, true);
+  hb_subset_input_set_desubroutinize (input, true);
+  face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, input);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' '));
+
+  hb_face_destroy (face_41_3041_4c2e_subset);
+  hb_face_destroy (face_41_3041_4c2e);
+  hb_face_destroy (face_41_4c2e);
+}
+
+static void
+test_subset_cff1_expert (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/cff1_expert.otf");
+  hb_face_t *face_subset = hb_test_open_font_file ("fonts/cff1_expert.2D,F6E9,FB00.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_face_t *face_test;
+  hb_set_add (codepoints, 0x2D);
+  hb_set_add (codepoints, 0xF6E9);
+  hb_set_add (codepoints, 0xFB00);
+  face_test = hb_subset_test_create_subset (face, hb_subset_test_create_input (codepoints));
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_subset, face_test, HB_TAG ('C','F','F',' '));
+
+  hb_face_destroy (face_test);
+  hb_face_destroy (face_subset);
+  hb_face_destroy (face);
+}
+
+static void
+test_subset_cff1_seac (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/cff1_seac.otf");
+  hb_face_t *face_subset = hb_test_open_font_file ("fonts/cff1_seac.C0.otf");
+  hb_face_t *face_test;
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_set_add (codepoints, 0xC0);  /* Agrave */
+  face_test = hb_subset_test_create_subset (face, hb_subset_test_create_input (codepoints));
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_subset, face_test, HB_TAG ('C','F','F',' '));
+
+  hb_face_destroy (face_test);
+  hb_face_destroy (face_subset);
+  hb_face_destroy (face);
+}
+
+static void
+test_subset_cff1_dotsection (void)
+{
+  hb_face_t *face = hb_test_open_font_file ("fonts/cff1_dotsect.otf");
+  hb_face_t *face_subset = hb_test_open_font_file ("fonts/cff1_dotsect.nohints.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_subset_input_t *input;
+  hb_face_t *face_test;
+  hb_set_add (codepoints, 0x69);  /* i */
+  input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_drop_hints (input, true);
+  face_test = hb_subset_test_create_subset (face, input);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_subset, face_test, HB_TAG ('C','F','F',' '));
+
+  hb_face_destroy (face_test);
+  hb_face_destroy (face_subset);
+  hb_face_destroy (face);
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_subset_cff1_noop);
+  hb_test_add (test_subset_cff1);
+  hb_test_add (test_subset_cff1_strip_hints);
+  hb_test_add (test_subset_cff1_desubr);
+  hb_test_add (test_subset_cff1_desubr_strip_hints);
+  hb_test_add (test_subset_cff1_j);
+  hb_test_add (test_subset_cff1_j_strip_hints);
+  hb_test_add (test_subset_cff1_j_desubr);
+  hb_test_add (test_subset_cff1_j_desubr_strip_hints);
+  hb_test_add (test_subset_cff1_expert);
+  hb_test_add (test_subset_cff1_seac);
+  hb_test_add (test_subset_cff1_dotsection);
+
+  return hb_test_run ();
+}
diff --git a/test/api/test-subset-cff2.c b/test/api/test-subset-cff2.c
new file mode 100644
index 0000000..9367965
--- /dev/null
+++ b/test/api/test-subset-cff2.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright © 2018 Adobe 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.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#include "hb-test.h"
+#include "hb-subset-test.h"
+
+/* Unit tests for CFF2 subsetting */
+
+static void
+test_subset_cff2_noop (void)
+{
+  hb_face_t *face_abc = hb_test_open_font_file("fonts/AdobeVFPrototype.abc.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'b');
+  hb_set_add (codepoints, 'c');
+  face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints));
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('C','F','F','2'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+}
+
+static void
+test_subset_cff2 (void)
+{
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints));
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C','F','F','2'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+static void
+test_subset_cff2_strip_hints (void)
+{
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.nohints.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_subset_input_t *input;
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_drop_hints (input, true);
+  face_abc_subset = hb_subset_test_create_subset (face_abc, input);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+static void
+test_subset_cff2_desubr (void)
+{
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.nosubrs.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_subset_input_t *input;
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_desubroutinize (input, true);
+  face_abc_subset = hb_subset_test_create_subset (face_abc, input);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+static void
+test_subset_cff2_desubr_strip_hints (void)
+{
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_subset_input_t *input;
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_desubroutinize (input, true);
+  hb_subset_input_set_drop_hints (input, true);
+  face_abc_subset = hb_subset_test_create_subset (face_abc, input);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_subset_cff2_noop);
+  hb_test_add (test_subset_cff2);
+  hb_test_add (test_subset_cff2_strip_hints);
+  hb_test_add (test_subset_cff2_desubr);
+  hb_test_add (test_subset_cff2_desubr_strip_hints);
+
+  return hb_test_run ();
+}
diff --git a/test/api/test-subset-cmap.c b/test/api/test-subset-cmap.c
index 84d34bc..74e91ca 100644
--- a/test/api/test-subset-cmap.c
+++ b/test/api/test-subset-cmap.c
@@ -32,8 +32,8 @@
 static void
 test_subset_cmap (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf");
-  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/Roboto-Regular.ac.ttf");
 
   hb_set_t *codepoints = hb_set_create ();
   hb_face_t *face_abc_subset;
@@ -52,7 +52,7 @@
 static void
 test_subset_cmap_non_consecutive_glyphs (void)
 {
-  hb_face_t *face = hb_subset_test_open_font ("fonts/Roboto-Regular.D7,D8,D9,DA,DE.ttf");
+  hb_face_t *face = hb_test_open_font_file ("fonts/Roboto-Regular.D7,D8,D9,DA,DE.ttf");
 
   hb_set_t *codepoints = hb_set_create ();
   hb_face_t *face_subset;
@@ -74,7 +74,7 @@
 static void
 test_subset_cmap_noop (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
 
   hb_set_t *codepoints = hb_set_create();
   hb_face_t *face_abc_subset;
diff --git a/test/api/test-subset-glyf.c b/test/api/test-subset-glyf.c
index 05c7f8c..0e5c293 100644
--- a/test/api/test-subset-glyf.c
+++ b/test/api/test-subset-glyf.c
@@ -60,8 +60,8 @@
 static void
 test_subset_glyf (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf");
-  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/Roboto-Regular.ac.ttf");
 
   hb_set_t *codepoints = hb_set_create();
   hb_face_t *face_abc_subset;
@@ -82,8 +82,8 @@
 static void
 test_subset_glyf_with_components (void)
 {
-  hb_face_t *face_components = hb_subset_test_open_font ("fonts/Roboto-Regular.components.ttf");
-  hb_face_t *face_subset = hb_subset_test_open_font ("fonts/Roboto-Regular.components.subset.ttf");
+  hb_face_t *face_components = hb_test_open_font_file ("fonts/Roboto-Regular.components.ttf");
+  hb_face_t *face_subset = hb_test_open_font_file ("fonts/Roboto-Regular.components.subset.ttf");
 
   hb_set_t *codepoints = hb_set_create();
   hb_face_t *face_generated_subset;
@@ -103,8 +103,8 @@
 static void
 test_subset_glyf_with_gsub (void)
 {
-  hb_face_t *face_fil = hb_subset_test_open_font ("fonts/Roboto-Regular.gsub.fil.ttf");
-  hb_face_t *face_fi = hb_subset_test_open_font ("fonts/Roboto-Regular.gsub.fi.ttf");
+  hb_face_t *face_fil = hb_test_open_font_file ("fonts/Roboto-Regular.gsub.fil.ttf");
+  hb_face_t *face_fi = hb_test_open_font_file ("fonts/Roboto-Regular.gsub.fi.ttf");
   hb_subset_input_t *input;
   hb_face_t *face_subset;
 
@@ -130,8 +130,8 @@
 static void
 test_subset_glyf_without_gsub (void)
 {
-  hb_face_t *face_fil = hb_subset_test_open_font ("fonts/Roboto-Regular.gsub.fil.ttf");
-  hb_face_t *face_fi = hb_subset_test_open_font ("fonts/Roboto-Regular.nogsub.fi.ttf");
+  hb_face_t *face_fil = hb_test_open_font_file ("fonts/Roboto-Regular.gsub.fil.ttf");
+  hb_face_t *face_fi = hb_test_open_font_file ("fonts/Roboto-Regular.nogsub.fi.ttf");
   hb_subset_input_t *input;
   hb_face_t *face_subset;
 
@@ -157,7 +157,7 @@
 static void
 test_subset_glyf_noop (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
 
   hb_set_t *codepoints = hb_set_create();
   hb_face_t *face_abc_subset;
@@ -178,8 +178,8 @@
 static void
 test_subset_glyf_strip_hints_simple (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf");
-  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.nohints.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/Roboto-Regular.ac.nohints.ttf");
 
   hb_set_t *codepoints = hb_set_create();
   hb_subset_input_t *input;
@@ -203,8 +203,8 @@
 static void
 test_subset_glyf_strip_hints_composite (void)
 {
-  hb_face_t *face_components = hb_subset_test_open_font ("fonts/Roboto-Regular.components.ttf");
-  hb_face_t *face_subset = hb_subset_test_open_font ("fonts/Roboto-Regular.components.1fc.nohints.ttf");
+  hb_face_t *face_components = hb_test_open_font_file ("fonts/Roboto-Regular.components.ttf");
+  hb_face_t *face_subset = hb_test_open_font_file ("fonts/Roboto-Regular.components.1fc.nohints.ttf");
 
   hb_set_t *codepoints = hb_set_create();
   hb_subset_input_t *input;
@@ -228,7 +228,7 @@
 static void
 test_subset_glyf_strip_hints_invalid (void)
 {
-  hb_face_t *face = hb_subset_test_open_font ("../fuzzing/fonts/oom-ccc61c92d589f895174cdef6ff2e3b20e9999a1a");
+  hb_face_t *face = hb_test_open_font_file ("../fuzzing/fonts/oom-ccc61c92d589f895174cdef6ff2e3b20e9999a1a");
 
   hb_set_t *codepoints = hb_set_create();
   const hb_codepoint_t text[] =
diff --git a/test/api/test-subset-hdmx.c b/test/api/test-subset-hdmx.c
index 8496f9e..44e579a 100644
--- a/test/api/test-subset-hdmx.c
+++ b/test/api/test-subset-hdmx.c
@@ -33,8 +33,8 @@
 static void
 test_subset_hdmx_simple_subset (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf");
-  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/Roboto-Regular.ac.ttf");
 
   hb_set_t *codepoints = hb_set_create ();
   hb_face_t *face_abc_subset;
@@ -53,8 +53,8 @@
 static void
 test_subset_hdmx_multiple_device_records (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.multihdmx.abc.ttf");
-  hb_face_t *face_a = hb_subset_test_open_font ("fonts/Roboto-Regular.multihdmx.a.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.multihdmx.abc.ttf");
+  hb_face_t *face_a = hb_test_open_font_file ("fonts/Roboto-Regular.multihdmx.a.ttf");
 
   hb_set_t *codepoints = hb_set_create ();
   hb_face_t *face_abc_subset;
@@ -72,7 +72,7 @@
 static void
 test_subset_hdmx_invalid (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("../fuzzing/fonts/crash-ccc61c92d589f895174cdef6ff2e3b20e9999a1a");
+  hb_face_t *face = hb_test_open_font_file ("../fuzzing/fonts/crash-ccc61c92d589f895174cdef6ff2e3b20e9999a1a");
 
   hb_subset_input_t *input = hb_subset_input_create_or_fail ();
   hb_set_t *codepoints = hb_subset_input_unicode_set (input);
@@ -94,7 +94,7 @@
 static void
 test_subset_hdmx_fails_sanitize (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("../fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5609911946838016");
+  hb_face_t *face = hb_test_open_font_file ("../fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5609911946838016");
 
   hb_subset_input_t *input = hb_subset_input_create_or_fail ();
   hb_set_t *codepoints = hb_subset_input_unicode_set (input);
@@ -116,7 +116,7 @@
 static void
 test_subset_hdmx_noop (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
 
   hb_set_t *codepoints = hb_set_create();
   hb_face_t *face_abc_subset;
diff --git a/test/api/test-subset-hmtx.c b/test/api/test-subset-hmtx.c
index 1a5a44d..1b51dc2 100644
--- a/test/api/test-subset-hmtx.c
+++ b/test/api/test-subset-hmtx.c
@@ -47,8 +47,8 @@
 static void
 test_subset_hmtx_simple_subset (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf");
-  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/Roboto-Regular.ac.ttf");
 
   hb_set_t *codepoints = hb_set_create ();
   hb_face_t *face_abc_subset;
@@ -69,8 +69,8 @@
 static void
 test_subset_hmtx_monospace (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Inconsolata-Regular.abc.ttf");
-  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Inconsolata-Regular.ac.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Inconsolata-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/Inconsolata-Regular.ac.ttf");
 
   hb_set_t *codepoints = hb_set_create ();
   hb_face_t *face_abc_subset;
@@ -91,8 +91,8 @@
 static void
 test_subset_hmtx_keep_num_metrics (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Inconsolata-Regular.abc.widerc.ttf");
-  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Inconsolata-Regular.ac.widerc.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Inconsolata-Regular.abc.widerc.ttf");
+  hb_face_t *face_ac = hb_test_open_font_file ("fonts/Inconsolata-Regular.ac.widerc.ttf");
 
   hb_set_t *codepoints = hb_set_create ();
   hb_face_t *face_abc_subset;
@@ -112,8 +112,8 @@
 static void
 test_subset_hmtx_decrease_num_metrics (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Inconsolata-Regular.abc.widerc.ttf");
-  hb_face_t *face_ab = hb_subset_test_open_font ("fonts/Inconsolata-Regular.ab.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Inconsolata-Regular.abc.widerc.ttf");
+  hb_face_t *face_ab = hb_test_open_font_file ("fonts/Inconsolata-Regular.ab.ttf");
 
   hb_set_t *codepoints = hb_set_create ();
   hb_face_t *face_abc_subset;
@@ -133,7 +133,7 @@
 static void
 test_subset_hmtx_noop (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
 
   hb_set_t *codepoints = hb_set_create();
   hb_face_t *face_abc_subset;
@@ -153,7 +153,7 @@
 static void
 test_subset_invalid_hmtx (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("../fuzzing/fonts/crash-e4e0bb1458a91b692eba492c907ae1f94e635480");
+  hb_face_t *face = hb_test_open_font_file ("../fuzzing/fonts/crash-e4e0bb1458a91b692eba492c907ae1f94e635480");
   hb_face_t *subset;
 
   hb_subset_input_t *input = hb_subset_input_create_or_fail ();
diff --git a/test/api/test-subset-os2.c b/test/api/test-subset-os2.c
index de63a3f..dfc9461 100644
--- a/test/api/test-subset-os2.c
+++ b/test/api/test-subset-os2.c
@@ -31,8 +31,8 @@
 static void
 test_subset_os2 (void)
 {
-  hb_face_t *face_abc = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf");
-  hb_face_t *face_b = hb_subset_test_open_font("fonts/Roboto-Regular.b.ttf");
+  hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_b = hb_test_open_font_file ("fonts/Roboto-Regular.b.ttf");
 
   hb_set_t *codepoints = hb_set_create();
   hb_face_t *face_abc_subset;
diff --git a/test/api/test-subset-post.c b/test/api/test-subset-post.c
index c14741e..e31b01e 100644
--- a/test/api/test-subset-post.c
+++ b/test/api/test-subset-post.c
@@ -32,8 +32,8 @@
 static void
 test_post_drops_glyph_names (void)
 {
-  hb_face_t *face_full = hb_subset_test_open_font ("fonts/Mplus1p-Regular.660E,6975,73E0,5EA6,8F38,6E05.ttf");
-  hb_face_t *face_subset = hb_subset_test_open_font ("fonts/Mplus1p-Regular.660E.ttf");
+  hb_face_t *face_full = hb_test_open_font_file ("fonts/Mplus1p-Regular.660E,6975,73E0,5EA6,8F38,6E05.ttf");
+  hb_face_t *face_subset = hb_test_open_font_file ("fonts/Mplus1p-Regular.660E.ttf");
   hb_face_t *face_full_subset;
 
   hb_set_t *codepoints = hb_set_create ();
diff --git a/test/api/test-subset-vmtx.c b/test/api/test-subset-vmtx.c
index 40ea8f8..24a4a76 100644
--- a/test/api/test-subset-vmtx.c
+++ b/test/api/test-subset-vmtx.c
@@ -46,8 +46,8 @@
 static void
 test_subset_vmtx_simple_subset (void)
 {
-  hb_face_t *face_full = hb_subset_test_open_font ("fonts/Mplus1p-Regular.660E,6975,73E0,5EA6,8F38,6E05.ttf");
-  hb_face_t *face_subset = hb_subset_test_open_font ("fonts/Mplus1p-Regular.660E.ttf");
+  hb_face_t *face_full = hb_test_open_font_file ("fonts/Mplus1p-Regular.660E,6975,73E0,5EA6,8F38,6E05.ttf");
+  hb_face_t *face_subset = hb_test_open_font_file ("fonts/Mplus1p-Regular.660E.ttf");
   hb_face_t *face_full_subset;
 
   hb_set_t *codepoints = hb_set_create ();
@@ -67,7 +67,7 @@
 static void
 test_subset_vmtx_noop (void)
 {
-  hb_face_t *face_full = hb_subset_test_open_font ("fonts/Mplus1p-Regular.660E,6975,73E0,5EA6,8F38,6E05.ttf");
+  hb_face_t *face_full = hb_test_open_font_file ("fonts/Mplus1p-Regular.660E,6975,73E0,5EA6,8F38,6E05.ttf");
   hb_face_t *face_full_subset;
 
   hb_set_t *codepoints = hb_set_create();
diff --git a/test/api/test-subset.c b/test/api/test-subset.c
index aaed031..85e4fdf 100644
--- a/test/api/test-subset.c
+++ b/test/api/test-subset.c
@@ -32,7 +32,7 @@
 static void
 test_subset_32_tables (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("../fuzzing/fonts/oom-6ef8c96d3710262511bcc730dce9c00e722cb653");
+  hb_face_t *face = hb_test_open_font_file ("../fuzzing/fonts/oom-6ef8c96d3710262511bcc730dce9c00e722cb653");
 
   hb_subset_input_t *input = hb_subset_input_create_or_fail ();
   hb_set_t *codepoints = hb_subset_input_unicode_set (input);
@@ -54,7 +54,7 @@
 static void
 test_subset_no_inf_loop (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("../fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5521982557782016");
+  hb_face_t *face = hb_test_open_font_file ("../fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5521982557782016");
 
   hb_subset_input_t *input = hb_subset_input_create_or_fail ();
   hb_set_t *codepoints = hb_subset_input_unicode_set (input);
@@ -76,7 +76,7 @@
 static void
 test_subset_crash (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("../fuzzing/fonts/crash-4b60576767ee4d9fe1cc10959d89baf73d4e8249");
+  hb_face_t *face = hb_test_open_font_file ("../fuzzing/fonts/crash-4b60576767ee4d9fe1cc10959d89baf73d4e8249");
 
   hb_subset_input_t *input = hb_subset_input_create_or_fail ();
   hb_set_t *codepoints = hb_subset_input_unicode_set (input);
diff --git a/test/fuzzing/Makefile.am b/test/fuzzing/Makefile.am
index 2506088..a77df70 100644
--- a/test/fuzzing/Makefile.am
+++ b/test/fuzzing/Makefile.am
@@ -8,10 +8,12 @@
 
 # Convenience targets:
 lib:
-	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src fuzzing
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
+libs:
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src libs
 
-$(top_builddir)/src/libharfbuzz-fuzzing.la: lib
-$(top_builddir)/src/libharfbuzz-subset-fuzzing.la: lib
+$(top_builddir)/src/libharfbuzz.la: lib
+$(top_builddir)/src/libharfbuzz-subset.la: libs
 
 EXTRA_DIST += \
 	README \
@@ -31,24 +33,15 @@
 	-I$(top_srcdir)/src/ \
 	-I$(top_builddir)/src/ \
 	$(NULL)
-LDADD = \
-	$(top_builddir)/src/libharfbuzz-fuzzing.la \
-	$(NULL)
 
 hb_shape_fuzzer_SOURCES = \
 	hb-fuzzer.hh \
 	hb-shape-fuzzer.cc \
 	main.cc \
 	$(NULL)
-hb_shape_fuzzer_LDADD = \
-	$(LDADD) \
-	$(NULL)
-hb_shape_fuzzer_CPPFLAGS = \
-	$(AM_CPPFLAGS) \
-	$(NULL)
-hb_shape_fuzzer_DEPENDENCIES = \
-	lib \
-	$(NULL)
+hb_shape_fuzzer_LDADD = $(top_builddir)/src/libharfbuzz.la
+hb_shape_fuzzer_CPPFLAGS = $(AM_CPPFLAGS)
+hb_shape_fuzzer_DEPENDENCIES = $(top_builddir)/src/libharfbuzz.la
 
 hb_subset_fuzzer_SOURCES = \
 	hb-fuzzer.hh \
@@ -56,17 +49,15 @@
 	main.cc \
 	$(NULL)
 hb_subset_fuzzer_LDADD = \
-	$(top_builddir)/src/libharfbuzz-subset-fuzzing.la \
-	$(NULL)
-hb_subset_fuzzer_CPPFLAGS = \
-	$(AM_CPPFLAGS) \
-	$(NULL)
-hb_subset_fuzzer_DEPENDENCIES = \
-	lib \
-	$(NULL)
+	$(top_builddir)/src/libharfbuzz.la \
+	$(top_builddir)/src/libharfbuzz-subset.la
+hb_subset_fuzzer_CPPFLAGS = $(AM_CPPFLAGS)
+hb_subset_fuzzer_DEPENDENCIES = $(top_builddir)/src/libharfbuzz-subset.la
 
 check:
 	EXEEXT="$(EXEEXT)" srcdir="$(srcdir)" builddir="$(builddir)" $(srcdir)/run-shape-fuzzer-tests.py
 	EXEEXT="$(EXEEXT)" srcdir="$(srcdir)" builddir="$(builddir)" $(srcdir)/run-subset-fuzzer-tests.py
+check-valgrind:
+	$(AM_V_at)RUN_VALGRIND=1 $(MAKE) $(AM_MAKEFLGS) check
 
 -include $(top_srcdir)/git.mk
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5097734906839040 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5097734906839040
new file mode 100644
index 0000000..8b45452
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5097734906839040
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5643036478930944 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5643036478930944
new file mode 100644
index 0000000..9a52336
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5643036478930944
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5686186874503168 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5686186874503168
new file mode 100644
index 0000000..86f4ad7
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5686186874503168
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5762137968869376 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5762137968869376
new file mode 100644
index 0000000..dca0b7d
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5762137968869376
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5659690013556736 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5659690013556736
new file mode 100644
index 0000000..9293c46
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5659690013556736
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5175735354916864 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5175735354916864
new file mode 100644
index 0000000..72fdfc6
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5175735354916864
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5629524117553152 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5629524117553152
new file mode 100644
index 0000000..01ca517
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5629524117553152
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5634443633491968 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5634443633491968
new file mode 100644
index 0000000..c63bcc5
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5634443633491968
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5634620935110656 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5634620935110656
new file mode 100644
index 0000000..39f9c3c
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5634620935110656
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5647267827023872 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5647267827023872
new file mode 100644
index 0000000..068e7e8
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5647267827023872
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5686369209286656 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5686369209286656
new file mode 100644
index 0000000..9f47ca8
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5686369209286656
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5691469793329152 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5691469793329152
new file mode 100644
index 0000000..6c73900
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5691469793329152
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5700264032468992 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5700264032468992
new file mode 100644
index 0000000..82a462b
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5700264032468992
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5706010589659136 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5706010589659136
new file mode 100644
index 0000000..7e15f4b
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5706010589659136
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5716208469409792 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5716208469409792
new file mode 100644
index 0000000..00915d6
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5716208469409792
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5722888989048832 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5722888989048832
new file mode 100644
index 0000000..df1556b
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5722888989048832
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5725855502827520 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5725855502827520
new file mode 100644
index 0000000..5781bba
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5725855502827520
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5735679418433536 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5735679418433536
new file mode 100644
index 0000000..ff6ef6e
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5735679418433536
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5736657639178240 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5736657639178240
new file mode 100644
index 0000000..343429c
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5736657639178240
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5754863779053568 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5754863779053568
new file mode 100644
index 0000000..03f240f
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5754863779053568
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5768046065483776 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5768046065483776
new file mode 100644
index 0000000..0ab1447
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5768046065483776
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5768601332613120 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5768601332613120
new file mode 100644
index 0000000..385e670
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5768601332613120
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5067936541179904 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5067936541179904
new file mode 100644
index 0000000..9f57f7b
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5067936541179904
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5660711141769216 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5660711141769216
new file mode 100644
index 0000000..302a1c4
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5660711141769216
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5672006905757696 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5672006905757696
new file mode 100644
index 0000000..cb5fb83
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5672006905757696
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5672913680728064 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5672913680728064
new file mode 100644
index 0000000..fdb5bff
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5672913680728064
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5710107829075968 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5710107829075968
new file mode 100644
index 0000000..5fef2f8
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5710107829075968
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5725847365877760 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5725847365877760
new file mode 100644
index 0000000..3764bed
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5725847365877760
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5750420593442816 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5750420593442816
new file mode 100644
index 0000000..7f41718
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5750420593442816
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5768186323009536 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5768186323009536
new file mode 100644
index 0000000..858604d
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5768186323009536
Binary files differ
diff --git a/test/fuzzing/hb-shape-fuzzer.cc b/test/fuzzing/hb-shape-fuzzer.cc
index b5a6c12..5723db9 100644
--- a/test/fuzzing/hb-shape-fuzzer.cc
+++ b/test/fuzzing/hb-shape-fuzzer.cc
@@ -3,6 +3,10 @@
 #include <hb-ot.h>
 #include <string.h>
 
+#define TEST_OT_FACE_NO_MAIN 1
+#include "../api/test-ot-face.c"
+#undef TEST_OT_FACE_NO_MAIN
+
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
 {
   hb_blob_t *blob = hb_blob_create ((const char *)data, size,
@@ -21,29 +25,20 @@
     hb_buffer_destroy (buffer);
   }
 
-  uint32_t text32[16];
-  if (size > sizeof (text32)) {
-    memcpy(text32, data + size - sizeof (text32), sizeof (text32));
-    hb_buffer_t *buffer = hb_buffer_create ();
-    hb_buffer_add_utf32 (buffer, text32, sizeof (text32) / sizeof (text32[0]), 0, -1);
-    hb_buffer_guess_segment_properties (buffer);
-    hb_shape (font, buffer, NULL, 0);
+  uint32_t text32[16] = {0};
+  unsigned int len = sizeof (text32);
+  if (size < len)
+    len = size;
+  memcpy(text32, data + size - len, len);
 
-    unsigned int len = hb_buffer_get_length (buffer);
-    hb_glyph_info_t *infos = hb_buffer_get_glyph_infos (buffer, NULL);
-    //hb_glyph_position_t *positions = hb_buffer_get_glyph_positions (buffer, NULL);
-    for (unsigned int i = 0; i < len; i++)
-    {
-      hb_glyph_info_t info = infos[i];
-      //hb_glyph_position_t pos = positions[i];
+  hb_buffer_t *buffer = hb_buffer_create ();
+  hb_buffer_add_utf32 (buffer, text32, sizeof (text32) / sizeof (text32[0]), 0, -1);
+  hb_buffer_guess_segment_properties (buffer);
+  hb_shape (font, buffer, NULL, 0);
+  hb_buffer_destroy (buffer);
 
-      hb_glyph_extents_t extents;
-      hb_font_get_glyph_extents (font, info.codepoint, &extents);
-    }
-
-    hb_buffer_destroy (buffer);
-  }
-
+  /* Misc calls on face. */
+  test_face (face, text32[15]);
 
   hb_font_destroy (font);
   hb_face_destroy (face);
diff --git a/test/fuzzing/run-shape-fuzzer-tests.py b/test/fuzzing/run-shape-fuzzer-tests.py
index 53c4f50..e3d180f 100755
--- a/test/fuzzing/run-shape-fuzzer-tests.py
+++ b/test/fuzzing/run-shape-fuzzer-tests.py
@@ -76,7 +76,8 @@
 	path = os.path.join(parent_path, file)
 
 	text, returncode = cmd ([hb_shape_fuzzer, path])
-	print (text)
+	if text.strip ():
+		print (text)
 
 	failed = False
 	if returncode != 0 or 'error' in text:
diff --git a/test/fuzzing/run-subset-fuzzer-tests.py b/test/fuzzing/run-subset-fuzzer-tests.py
index d4e3487..7392a92 100755
--- a/test/fuzzing/run-subset-fuzzer-tests.py
+++ b/test/fuzzing/run-subset-fuzzer-tests.py
@@ -22,7 +22,6 @@
 
 def run_dir (parent_path):
 	global fails
-	print ("running subset fuzzer against fonts in %s" % parent_path)
 	for file in os.listdir (parent_path):
 		path = os.path.join(parent_path, file)
 
diff --git a/test/shaping/CMakeLists.txt b/test/shaping/CMakeLists.txt
index 7c2c999..8e33ede 100644
--- a/test/shaping/CMakeLists.txt
+++ b/test/shaping/CMakeLists.txt
@@ -5,6 +5,16 @@
     add_test (NAME ${test}
       COMMAND "${PYTHON_EXECUTABLE}" run-tests.py $<TARGET_FILE:hb-shape> "data/in-house/${test}"
       WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+    set_property (TEST ${test} PROPERTY SKIP_RETURN_CODE 77)
+  endforeach ()
+
+  file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/aots/Makefile.sources" INHOUSE)
+  extract_make_variable (TESTS ${INHOUSE})
+  foreach (test IN ITEMS ${TESTS})
+    add_test (NAME ${test}
+      COMMAND "${PYTHON_EXECUTABLE}" run-tests.py $<TARGET_FILE:hb-shape> "data/aots/${test}"
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+    set_property (TEST ${test} PROPERTY SKIP_RETURN_CODE 77)
   endforeach ()
 
   file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/text-rendering-tests/Makefile.sources" TEXTRENDERING)
@@ -13,5 +23,6 @@
     add_test (NAME ${test}
       COMMAND "${PYTHON_EXECUTABLE}" run-tests.py $<TARGET_FILE:hb-shape> "data/text-rendering-tests/${test}"
       WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+    set_property (TEST ${test} PROPERTY SKIP_RETURN_CODE 77)
   endforeach ()
 endif ()
diff --git a/test/shaping/Makefile.am b/test/shaping/Makefile.am
index 5ca9bc1..66272da 100644
--- a/test/shaping/Makefile.am
+++ b/test/shaping/Makefile.am
@@ -3,6 +3,8 @@
 NULL =
 EXTRA_DIST =
 CLEANFILES =
+DISTCLEANFILES =
+MAINTAINERCLEANFILES =
 SUBDIRS = data
 
 # Convenience targets:
diff --git a/test/shaping/README.md b/test/shaping/README.md
index 99498e6..f386fb9 100644
--- a/test/shaping/README.md
+++ b/test/shaping/README.md
@@ -25,10 +25,10 @@
   * If the outputs differ, recording fails.  Otherwise, it will move the
     subset font file into `data/in-house/fonts` and name it after its
     hash, and print out the test case input, which you can then redirect
-    to an existing or new test file in `data/in-house/tests` using `-o=`,
+    to an existing or new test file in `data/in-house/tests` using `-o`,
     e.g.:
 ```sh
-$ ./hb-unicode-encode 41 42 43 627 | ./record-test.sh -o=data/in-house/tests/test-name.test ../../util/hb-shape font.ttf
+$ ./hb-unicode-encode 41 42 43 627 | ./record-test.sh -o data/in-house/tests/test-name.test ../../util/hb-shape font.ttf
 ```
 
 If you created a new test file, add it to `data/in-house/Makefile.sources`
diff --git a/test/shaping/data/Makefile.am b/test/shaping/data/Makefile.am
index 4f2c113..01f6c5a 100644
--- a/test/shaping/data/Makefile.am
+++ b/test/shaping/data/Makefile.am
@@ -4,6 +4,7 @@
 SUBDIRS = \
 	in-house \
 	text-rendering-tests \
+	aots \
 	$(NULL)
 
 # Convenience targets:
diff --git a/test/shaping/data/aots/COPYING b/test/shaping/data/aots/COPYING
new file mode 100644
index 0000000..d000f23
--- /dev/null
+++ b/test/shaping/data/aots/COPYING
@@ -0,0 +1,13 @@
+Copyright 2000-2016 Adobe Systems Incorporated. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use these files except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/test/shaping/data/aots/Makefile.am b/test/shaping/data/aots/Makefile.am
new file mode 100644
index 0000000..3b1faee
--- /dev/null
+++ b/test/shaping/data/aots/Makefile.am
@@ -0,0 +1,37 @@
+# Process this file with automake to produce Makefile.in
+
+NULL =
+
+# Convenience targets:
+lib:
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
+
+EXTRA_DIST = \
+	COPYING \
+	fonts \
+	$(TESTS) \
+	$(NULL)
+
+TEST_EXTENSIONS = .tests
+TESTS_LOG_COMPILER = $(srcdir)/../../run-tests.py $(top_builddir)/util/hb-shape$(EXEEXT)
+
+init-aots:
+	git clone https://github.com/adobe-type-tools/aots $(srcdir)/aots
+	make -C$(srcdir)/aots
+	make -C$(srcdir)/aots/harfbuzz
+	touch $(srcdir)/init-aots
+
+update-tests: init-aots lib
+	cp $(srcdir)/hb-aots-tester.cpp $(srcdir)/aots/harfbuzz/hb-aots-tester.cpp
+	$(CXX) -Wno-narrowing $(srcdir)/aots/harfbuzz/hb-aots-tester.cpp \
+		-I$(top_srcdir)/src/ -o $(srcdir)/aots/harfbuzz/aots \
+		-L$(top_builddir)/src/.libs -lharfbuzz
+	rm -rf $(srcdir)/tests/
+	mkdir $(srcdir)/tests/
+	export LD_LIBRARY_PATH=$(realpath $(top_builddir)/src/.libs); cd $(srcdir)/aots/harfbuzz; ./aots
+
+.PHONY: update-tests
+
+include Makefile.sources
+
+-include $(top_srcdir)/git.mk
diff --git a/test/shaping/data/aots/Makefile.sources b/test/shaping/data/aots/Makefile.sources
new file mode 100644
index 0000000..9339682
--- /dev/null
+++ b/test/shaping/data/aots/Makefile.sources
@@ -0,0 +1,126 @@
+TESTS = \
+	tests/classdef1_empty.tests \
+	tests/classdef1_multiple.tests \
+	tests/classdef1_single.tests \
+	tests/classdef1.tests \
+	tests/classdef2_empty.tests \
+	tests/classdef2_multiple.tests \
+	tests/classdef2_single.tests \
+	tests/classdef2.tests \
+	tests/gpos_chaining1_boundary.tests \
+	tests/gpos_chaining1_lookupflag.tests \
+	tests/gpos_chaining1_multiple_subrules.tests \
+	tests/gpos_chaining1_next_glyph.tests \
+	tests/gpos_chaining1_simple.tests \
+	tests/gpos_chaining1_successive.tests \
+	tests/gpos_chaining2_boundary.tests \
+	tests/gpos_chaining2_lookupflag.tests \
+	tests/gpos_chaining2_multiple_subrules.tests \
+	tests/gpos_chaining2_next_glyph.tests \
+	tests/gpos_chaining2_simple.tests \
+	tests/gpos_chaining2_successive.tests \
+	tests/gpos_chaining3_boundary.tests \
+	tests/gpos_chaining3_lookupflag.tests \
+	tests/gpos_chaining3_next_glyph.tests \
+	tests/gpos_chaining3_simple.tests \
+	tests/gpos_chaining3_successive.tests \
+	tests/gpos_context1_boundary.tests \
+	tests/gpos_context1_expansion.tests \
+	tests/gpos_context1_lookupflag.tests \
+	tests/gpos_context1_multiple_subrules.tests \
+	tests/gpos_context1_next_glyph.tests \
+	tests/gpos_context1_simple.tests \
+	tests/gpos_context1_successive.tests \
+	tests/gpos_context2_boundary.tests \
+	tests/gpos_context2_classes.tests \
+	tests/gpos_context2_expansion.tests \
+	tests/gpos_context2_lookupflag.tests \
+	tests/gpos_context2_multiple_subrules.tests \
+	tests/gpos_context2_next_glyph.tests \
+	tests/gpos_context2_simple.tests \
+	tests/gpos_context2_successive.tests \
+	tests/gpos_context3_boundary.tests \
+	tests/gpos_context3_lookupflag.tests \
+	tests/gpos_context3_next_glyph.tests \
+	tests/gpos_context3_simple.tests \
+	tests/gpos_context3_successive.tests \
+	tests/gpos1_1_lookupflag.tests \
+	tests/gpos1_1_simple.tests \
+	tests/gpos1_2_lookupflag.tests \
+	tests/gpos1_2.tests \
+	tests/gpos2_1_lookupflag.tests \
+	tests/gpos2_1_next_glyph.tests \
+	tests/gpos2_1_simple.tests \
+	tests/gpos2_1.tests \
+	tests/gpos2_2.tests \
+	tests/gpos3_lookupflag.tests \
+	tests/gpos3.tests \
+	tests/gpos4_lookupflag.tests \
+	tests/gpos4_multiple_anchors.tests \
+	tests/gpos4_simple.tests \
+	tests/gpos5.tests \
+	tests/gpos6.tests \
+	tests/gpos7_1.tests \
+	tests/gpos9.tests \
+	tests/gsub_chaining1_boundary.tests \
+	tests/gsub_chaining1_lookupflag.tests \
+	tests/gsub_chaining1_multiple_subrules.tests \
+	tests/gsub_chaining1_next_glyph.tests \
+	tests/gsub_chaining1_simple.tests \
+	tests/gsub_chaining1_successive.tests \
+	tests/gsub_chaining2_boundary.tests \
+	tests/gsub_chaining2_lookupflag.tests \
+	tests/gsub_chaining2_multiple_subrules.tests \
+	tests/gsub_chaining2_next_glyph.tests \
+	tests/gsub_chaining2_simple.tests \
+	tests/gsub_chaining2_successive.tests \
+	tests/gsub_chaining3_boundary.tests \
+	tests/gsub_chaining3_lookupflag.tests \
+	tests/gsub_chaining3_next_glyph.tests \
+	tests/gsub_chaining3_simple.tests \
+	tests/gsub_chaining3_successive.tests \
+	tests/gsub_context1_boundary.tests \
+	tests/gsub_context1_expansion.tests \
+	tests/gsub_context1_lookupflag.tests \
+	tests/gsub_context1_multiple_subrules.tests \
+	tests/gsub_context1_next_glyph.tests \
+	tests/gsub_context1_simple.tests \
+	tests/gsub_context1_successive.tests \
+	tests/gsub_context2_boundary.tests \
+	tests/gsub_context2_classes.tests \
+	tests/gsub_context2_expansion.tests \
+	tests/gsub_context2_lookupflag.tests \
+	tests/gsub_context2_multiple_subrules.tests \
+	tests/gsub_context2_next_glyph.tests \
+	tests/gsub_context2_simple.tests \
+	tests/gsub_context2_successive.tests \
+	tests/gsub_context3_boundary.tests \
+	tests/gsub_context3_lookupflag.tests \
+	tests/gsub_context3_next_glyph.tests \
+	tests/gsub_context3_simple.tests \
+	tests/gsub_context3_successive.tests \
+	tests/gsub1_1_lookupflag.tests \
+	tests/gsub1_1_modulo.tests \
+	tests/gsub1_1_simple.tests \
+	tests/gsub1_2_lookupflag.tests \
+	tests/gsub1_2_simple.tests \
+	tests/gsub2_1_lookupflag.tests \
+	tests/gsub2_1_multiple_sequences.tests \
+	tests/gsub2_1_simple.tests \
+	tests/gsub3_1_lookupflag.tests \
+	tests/gsub3_1_multiple.tests \
+	tests/gsub3_1_simple.tests \
+	tests/gsub4_1_lookupflag.tests \
+	tests/gsub4_1_multiple_ligatures.tests \
+	tests/gsub4_1_multiple_ligsets.tests \
+	tests/gsub4_1_simple.tests \
+	tests/gsub7.tests \
+	tests/lookupflag_ignore_attach.tests \
+	tests/lookupflag_ignore_base.tests \
+	tests/lookupflag_ignore_combination.tests \
+	tests/lookupflag_ignore_ligatures.tests \
+	tests/lookupflag_ignore_marks.tests \
+	$(NULL)
+
+DISABLED_TESTS = \
+	$(NULL)
diff --git a/test/shaping/data/aots/fonts/classdef1_font1.otf b/test/shaping/data/aots/fonts/classdef1_font1.otf
new file mode 100644
index 0000000..f0add69
--- /dev/null
+++ b/test/shaping/data/aots/fonts/classdef1_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/classdef1_font2.otf b/test/shaping/data/aots/fonts/classdef1_font2.otf
new file mode 100644
index 0000000..f01876d
--- /dev/null
+++ b/test/shaping/data/aots/fonts/classdef1_font2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/classdef1_font3.otf b/test/shaping/data/aots/fonts/classdef1_font3.otf
new file mode 100644
index 0000000..2a0f9cc
--- /dev/null
+++ b/test/shaping/data/aots/fonts/classdef1_font3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/classdef1_font4.otf b/test/shaping/data/aots/fonts/classdef1_font4.otf
new file mode 100644
index 0000000..9c0f41c
--- /dev/null
+++ b/test/shaping/data/aots/fonts/classdef1_font4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/classdef2_font1.otf b/test/shaping/data/aots/fonts/classdef2_font1.otf
new file mode 100644
index 0000000..2e2faaf
--- /dev/null
+++ b/test/shaping/data/aots/fonts/classdef2_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/classdef2_font2.otf b/test/shaping/data/aots/fonts/classdef2_font2.otf
new file mode 100644
index 0000000..2e2a1af
--- /dev/null
+++ b/test/shaping/data/aots/fonts/classdef2_font2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/classdef2_font3.otf b/test/shaping/data/aots/fonts/classdef2_font3.otf
new file mode 100644
index 0000000..14c9119
--- /dev/null
+++ b/test/shaping/data/aots/fonts/classdef2_font3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/classdef2_font4.otf b/test/shaping/data/aots/fonts/classdef2_font4.otf
new file mode 100644
index 0000000..c75c883
--- /dev/null
+++ b/test/shaping/data/aots/fonts/classdef2_font4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap0_font1.otf b/test/shaping/data/aots/fonts/cmap0_font1.otf
new file mode 100644
index 0000000..772f9a7
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap0_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap10_font1.otf b/test/shaping/data/aots/fonts/cmap10_font1.otf
new file mode 100644
index 0000000..023e945
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap10_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap10_font2.otf b/test/shaping/data/aots/fonts/cmap10_font2.otf
new file mode 100644
index 0000000..5202f79
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap10_font2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap12_font1.otf b/test/shaping/data/aots/fonts/cmap12_font1.otf
new file mode 100644
index 0000000..2d74b3a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap12_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap14_font1.otf b/test/shaping/data/aots/fonts/cmap14_font1.otf
new file mode 100644
index 0000000..a8e941d
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap14_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap2_font1.otf b/test/shaping/data/aots/fonts/cmap2_font1.otf
new file mode 100644
index 0000000..a123d9c
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap2_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap4_font1.otf b/test/shaping/data/aots/fonts/cmap4_font1.otf
new file mode 100644
index 0000000..516ed8e
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap4_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap4_font2.otf b/test/shaping/data/aots/fonts/cmap4_font2.otf
new file mode 100644
index 0000000..0f678a3
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap4_font2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap4_font3.otf b/test/shaping/data/aots/fonts/cmap4_font3.otf
new file mode 100644
index 0000000..2034ecd
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap4_font3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap4_font4.otf b/test/shaping/data/aots/fonts/cmap4_font4.otf
new file mode 100644
index 0000000..450508e
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap4_font4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap6_font1.otf b/test/shaping/data/aots/fonts/cmap6_font1.otf
new file mode 100644
index 0000000..10b64a7
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap6_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap6_font2.otf b/test/shaping/data/aots/fonts/cmap6_font2.otf
new file mode 100644
index 0000000..2d2957f
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap6_font2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap8_font1.otf b/test/shaping/data/aots/fonts/cmap8_font1.otf
new file mode 100644
index 0000000..791b9e3
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap8_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap_composition_font1.otf b/test/shaping/data/aots/fonts/cmap_composition_font1.otf
new file mode 100644
index 0000000..c790717
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap_composition_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap_subtableselection_font1.otf b/test/shaping/data/aots/fonts/cmap_subtableselection_font1.otf
new file mode 100644
index 0000000..8929f8a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap_subtableselection_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap_subtableselection_font2.otf b/test/shaping/data/aots/fonts/cmap_subtableselection_font2.otf
new file mode 100644
index 0000000..2611092
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap_subtableselection_font2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap_subtableselection_font3.otf b/test/shaping/data/aots/fonts/cmap_subtableselection_font3.otf
new file mode 100644
index 0000000..9f39331
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap_subtableselection_font3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap_subtableselection_font4.otf b/test/shaping/data/aots/fonts/cmap_subtableselection_font4.otf
new file mode 100644
index 0000000..83ae88e
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap_subtableselection_font4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/cmap_subtableselection_font5.otf b/test/shaping/data/aots/fonts/cmap_subtableselection_font5.otf
new file mode 100644
index 0000000..8b614ad
--- /dev/null
+++ b/test/shaping/data/aots/fonts/cmap_subtableselection_font5.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos1_1_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gpos1_1_lookupflag_f1.otf
new file mode 100644
index 0000000..3245425
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos1_1_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos1_1_simple_f1.otf b/test/shaping/data/aots/fonts/gpos1_1_simple_f1.otf
new file mode 100644
index 0000000..c5f8888
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos1_1_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos1_1_simple_f2.otf b/test/shaping/data/aots/fonts/gpos1_1_simple_f2.otf
new file mode 100644
index 0000000..905d0a3
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos1_1_simple_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos1_1_simple_f3.otf b/test/shaping/data/aots/fonts/gpos1_1_simple_f3.otf
new file mode 100644
index 0000000..550be87
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos1_1_simple_f3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos1_1_simple_f4.otf b/test/shaping/data/aots/fonts/gpos1_1_simple_f4.otf
new file mode 100644
index 0000000..448bc8b
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos1_1_simple_f4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos1_2_font1.otf b/test/shaping/data/aots/fonts/gpos1_2_font1.otf
new file mode 100644
index 0000000..3e7b7bc
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos1_2_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos1_2_font2.otf b/test/shaping/data/aots/fonts/gpos1_2_font2.otf
new file mode 100644
index 0000000..ba9d224
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos1_2_font2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_1_font6.otf b/test/shaping/data/aots/fonts/gpos2_1_font6.otf
new file mode 100644
index 0000000..cd4ea94
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_1_font6.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_1_font7.otf b/test/shaping/data/aots/fonts/gpos2_1_font7.otf
new file mode 100644
index 0000000..2871acc
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_1_font7.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_1_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gpos2_1_lookupflag_f1.otf
new file mode 100644
index 0000000..6003782
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_1_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_1_lookupflag_f2.otf b/test/shaping/data/aots/fonts/gpos2_1_lookupflag_f2.otf
new file mode 100644
index 0000000..9d0a273
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_1_lookupflag_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_1_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gpos2_1_next_glyph_f1.otf
new file mode 100644
index 0000000..64d6c2c
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_1_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_1_next_glyph_f2.otf b/test/shaping/data/aots/fonts/gpos2_1_next_glyph_f2.otf
new file mode 100644
index 0000000..3d8c37a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_1_next_glyph_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_1_simple_f1.otf b/test/shaping/data/aots/fonts/gpos2_1_simple_f1.otf
new file mode 100644
index 0000000..c947776
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_1_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_2_font1.otf b/test/shaping/data/aots/fonts/gpos2_2_font1.otf
new file mode 100644
index 0000000..dde370a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_2_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_2_font2.otf b/test/shaping/data/aots/fonts/gpos2_2_font2.otf
new file mode 100644
index 0000000..63d874a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_2_font2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_2_font3.otf b/test/shaping/data/aots/fonts/gpos2_2_font3.otf
new file mode 100644
index 0000000..b530676
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_2_font3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_2_font4.otf b/test/shaping/data/aots/fonts/gpos2_2_font4.otf
new file mode 100644
index 0000000..b549e02
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_2_font4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos2_2_font5.otf b/test/shaping/data/aots/fonts/gpos2_2_font5.otf
new file mode 100644
index 0000000..64c40bb
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos2_2_font5.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos3_font1.otf b/test/shaping/data/aots/fonts/gpos3_font1.otf
new file mode 100644
index 0000000..9b6d39a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos3_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos3_font2.otf b/test/shaping/data/aots/fonts/gpos3_font2.otf
new file mode 100644
index 0000000..dee5785
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos3_font2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos3_font3.otf b/test/shaping/data/aots/fonts/gpos3_font3.otf
new file mode 100644
index 0000000..7522660
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos3_font3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos4_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gpos4_lookupflag_f1.otf
new file mode 100644
index 0000000..b141116
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos4_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos4_lookupflag_f2.otf b/test/shaping/data/aots/fonts/gpos4_lookupflag_f2.otf
new file mode 100644
index 0000000..84e4843
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos4_lookupflag_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos4_multiple_anchors_1.otf b/test/shaping/data/aots/fonts/gpos4_multiple_anchors_1.otf
new file mode 100644
index 0000000..025f69b
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos4_multiple_anchors_1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos4_simple_1.otf b/test/shaping/data/aots/fonts/gpos4_simple_1.otf
new file mode 100644
index 0000000..da54a1f
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos4_simple_1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos5_font1.otf b/test/shaping/data/aots/fonts/gpos5_font1.otf
new file mode 100644
index 0000000..8c48fb6
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos5_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos6_font1.otf b/test/shaping/data/aots/fonts/gpos6_font1.otf
new file mode 100644
index 0000000..f7f92cc
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos6_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos7_1_font1.otf b/test/shaping/data/aots/fonts/gpos7_1_font1.otf
new file mode 100644
index 0000000..ced8907
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos7_1_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos9_font1.otf b/test/shaping/data/aots/fonts/gpos9_font1.otf
new file mode 100644
index 0000000..e99c25a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos9_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos9_font2.otf b/test/shaping/data/aots/fonts/gpos9_font2.otf
new file mode 100644
index 0000000..9ae824b
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos9_font2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f1.otf
new file mode 100644
index 0000000..44c4117
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f2.otf b/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f2.otf
new file mode 100644
index 0000000..431b08f
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f3.otf b/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f3.otf
new file mode 100644
index 0000000..1bac49a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f4.otf b/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f4.otf
new file mode 100644
index 0000000..3d37782
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining1_boundary_f4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining1_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining1_lookupflag_f1.otf
new file mode 100644
index 0000000..a83342a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining1_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining1_multiple_subrules_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining1_multiple_subrules_f1.otf
new file mode 100644
index 0000000..07bf55c
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining1_multiple_subrules_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining1_multiple_subrules_f2.otf b/test/shaping/data/aots/fonts/gpos_chaining1_multiple_subrules_f2.otf
new file mode 100644
index 0000000..dc3754b
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining1_multiple_subrules_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining1_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining1_next_glyph_f1.otf
new file mode 100644
index 0000000..17852c2
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining1_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining1_simple_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining1_simple_f1.otf
new file mode 100644
index 0000000..31cbe77
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining1_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining1_simple_f2.otf b/test/shaping/data/aots/fonts/gpos_chaining1_simple_f2.otf
new file mode 100644
index 0000000..3293ad8
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining1_simple_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining1_successive_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining1_successive_f1.otf
new file mode 100644
index 0000000..4c86663
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining1_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f1.otf
new file mode 100644
index 0000000..49210fb
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f2.otf b/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f2.otf
new file mode 100644
index 0000000..456fc9b
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f3.otf b/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f3.otf
new file mode 100644
index 0000000..768492a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f4.otf b/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f4.otf
new file mode 100644
index 0000000..2670da6
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining2_boundary_f4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining2_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining2_lookupflag_f1.otf
new file mode 100644
index 0000000..e8cce56
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining2_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining2_multiple_subrules_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining2_multiple_subrules_f1.otf
new file mode 100644
index 0000000..f182c7f
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining2_multiple_subrules_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining2_multiple_subrules_f2.otf b/test/shaping/data/aots/fonts/gpos_chaining2_multiple_subrules_f2.otf
new file mode 100644
index 0000000..d24896a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining2_multiple_subrules_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining2_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining2_next_glyph_f1.otf
new file mode 100644
index 0000000..f6bbda4
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining2_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining2_simple_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining2_simple_f1.otf
new file mode 100644
index 0000000..1805a03
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining2_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining2_simple_f2.otf b/test/shaping/data/aots/fonts/gpos_chaining2_simple_f2.otf
new file mode 100644
index 0000000..1df12f5
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining2_simple_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining2_successive_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining2_successive_f1.otf
new file mode 100644
index 0000000..a3aadaf
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining2_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f1.otf
new file mode 100644
index 0000000..4f13bdd
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f2.otf b/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f2.otf
new file mode 100644
index 0000000..48be5dd
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f3.otf b/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f3.otf
new file mode 100644
index 0000000..a10068b
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f4.otf b/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f4.otf
new file mode 100644
index 0000000..8030ac0
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining3_boundary_f4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining3_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining3_lookupflag_f1.otf
new file mode 100644
index 0000000..7864ce0
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining3_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining3_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining3_next_glyph_f1.otf
new file mode 100644
index 0000000..20a7966
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining3_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining3_simple_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining3_simple_f1.otf
new file mode 100644
index 0000000..2c63328
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining3_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining3_simple_f2.otf b/test/shaping/data/aots/fonts/gpos_chaining3_simple_f2.otf
new file mode 100644
index 0000000..6ef8fb1
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining3_simple_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_chaining3_successive_f1.otf b/test/shaping/data/aots/fonts/gpos_chaining3_successive_f1.otf
new file mode 100644
index 0000000..8e8439d
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_chaining3_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context1_boundary_f1.otf b/test/shaping/data/aots/fonts/gpos_context1_boundary_f1.otf
new file mode 100644
index 0000000..22bb3ea
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context1_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context1_boundary_f2.otf b/test/shaping/data/aots/fonts/gpos_context1_boundary_f2.otf
new file mode 100644
index 0000000..7e4c0e1
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context1_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context1_expansion_f1.otf b/test/shaping/data/aots/fonts/gpos_context1_expansion_f1.otf
new file mode 100644
index 0000000..f7c60f6
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context1_expansion_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context1_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gpos_context1_lookupflag_f1.otf
new file mode 100644
index 0000000..e9b03ae
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context1_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context1_lookupflag_f2.otf b/test/shaping/data/aots/fonts/gpos_context1_lookupflag_f2.otf
new file mode 100644
index 0000000..b9998cd
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context1_lookupflag_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context1_multiple_subrules_f1.otf b/test/shaping/data/aots/fonts/gpos_context1_multiple_subrules_f1.otf
new file mode 100644
index 0000000..6f1aafa
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context1_multiple_subrules_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context1_multiple_subrules_f2.otf b/test/shaping/data/aots/fonts/gpos_context1_multiple_subrules_f2.otf
new file mode 100644
index 0000000..ed9a387
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context1_multiple_subrules_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context1_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gpos_context1_next_glyph_f1.otf
new file mode 100644
index 0000000..6007d54
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context1_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context1_simple_f1.otf b/test/shaping/data/aots/fonts/gpos_context1_simple_f1.otf
new file mode 100644
index 0000000..5e6cd9e
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context1_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context1_simple_f2.otf b/test/shaping/data/aots/fonts/gpos_context1_simple_f2.otf
new file mode 100644
index 0000000..b4fddb3
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context1_simple_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context1_successive_f1.otf b/test/shaping/data/aots/fonts/gpos_context1_successive_f1.otf
new file mode 100644
index 0000000..eb0f962
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context1_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_boundary_f1.otf b/test/shaping/data/aots/fonts/gpos_context2_boundary_f1.otf
new file mode 100644
index 0000000..3fce497
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_boundary_f2.otf b/test/shaping/data/aots/fonts/gpos_context2_boundary_f2.otf
new file mode 100644
index 0000000..5b4e012
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_classes_f1.otf b/test/shaping/data/aots/fonts/gpos_context2_classes_f1.otf
new file mode 100644
index 0000000..585b511
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_classes_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_classes_f2.otf b/test/shaping/data/aots/fonts/gpos_context2_classes_f2.otf
new file mode 100644
index 0000000..411d58e
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_classes_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_expansion_f1.otf b/test/shaping/data/aots/fonts/gpos_context2_expansion_f1.otf
new file mode 100644
index 0000000..4b2d36a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_expansion_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gpos_context2_lookupflag_f1.otf
new file mode 100644
index 0000000..1c0c480
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_lookupflag_f2.otf b/test/shaping/data/aots/fonts/gpos_context2_lookupflag_f2.otf
new file mode 100644
index 0000000..bf20d84
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_lookupflag_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_multiple_subrules_f1.otf b/test/shaping/data/aots/fonts/gpos_context2_multiple_subrules_f1.otf
new file mode 100644
index 0000000..05b6b73
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_multiple_subrules_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_multiple_subrules_f2.otf b/test/shaping/data/aots/fonts/gpos_context2_multiple_subrules_f2.otf
new file mode 100644
index 0000000..f79712c
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_multiple_subrules_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gpos_context2_next_glyph_f1.otf
new file mode 100644
index 0000000..1b5a256
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_simple_f1.otf b/test/shaping/data/aots/fonts/gpos_context2_simple_f1.otf
new file mode 100644
index 0000000..9aaec44
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_simple_f2.otf b/test/shaping/data/aots/fonts/gpos_context2_simple_f2.otf
new file mode 100644
index 0000000..c789b2e
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_simple_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context2_successive_f1.otf b/test/shaping/data/aots/fonts/gpos_context2_successive_f1.otf
new file mode 100644
index 0000000..b89bfb7
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context2_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context3_boundary_f1.otf b/test/shaping/data/aots/fonts/gpos_context3_boundary_f1.otf
new file mode 100644
index 0000000..f8949d4
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context3_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context3_boundary_f2.otf b/test/shaping/data/aots/fonts/gpos_context3_boundary_f2.otf
new file mode 100644
index 0000000..ceb7452
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context3_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context3_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gpos_context3_lookupflag_f1.otf
new file mode 100644
index 0000000..40b55ee
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context3_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context3_lookupflag_f2.otf b/test/shaping/data/aots/fonts/gpos_context3_lookupflag_f2.otf
new file mode 100644
index 0000000..bcf4d17
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context3_lookupflag_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context3_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gpos_context3_next_glyph_f1.otf
new file mode 100644
index 0000000..6f9d919
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context3_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context3_simple_f1.otf b/test/shaping/data/aots/fonts/gpos_context3_simple_f1.otf
new file mode 100644
index 0000000..470c2ed
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context3_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gpos_context3_successive_f1.otf b/test/shaping/data/aots/fonts/gpos_context3_successive_f1.otf
new file mode 100644
index 0000000..aeb9bbd
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gpos_context3_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub1_1_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gsub1_1_lookupflag_f1.otf
new file mode 100644
index 0000000..a539b95
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub1_1_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub1_1_modulo_f1.otf b/test/shaping/data/aots/fonts/gsub1_1_modulo_f1.otf
new file mode 100644
index 0000000..7ba2379
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub1_1_modulo_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub1_1_simple_f1.otf b/test/shaping/data/aots/fonts/gsub1_1_simple_f1.otf
new file mode 100644
index 0000000..c21fcd3
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub1_1_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub1_2_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gsub1_2_lookupflag_f1.otf
new file mode 100644
index 0000000..b13af6e
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub1_2_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub1_2_simple_f1.otf b/test/shaping/data/aots/fonts/gsub1_2_simple_f1.otf
new file mode 100644
index 0000000..d3851b3
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub1_2_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub2_1_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gsub2_1_lookupflag_f1.otf
new file mode 100644
index 0000000..8330ad4
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub2_1_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub2_1_multiple_sequences_f1.otf b/test/shaping/data/aots/fonts/gsub2_1_multiple_sequences_f1.otf
new file mode 100644
index 0000000..c912937
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub2_1_multiple_sequences_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub2_1_simple_f1.otf b/test/shaping/data/aots/fonts/gsub2_1_simple_f1.otf
new file mode 100644
index 0000000..584a7f5
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub2_1_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub3_1_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gsub3_1_lookupflag_f1.otf
new file mode 100644
index 0000000..4ccf55f
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub3_1_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub3_1_multiple_f1.otf b/test/shaping/data/aots/fonts/gsub3_1_multiple_f1.otf
new file mode 100644
index 0000000..075f196
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub3_1_multiple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub3_1_simple_f1.otf b/test/shaping/data/aots/fonts/gsub3_1_simple_f1.otf
new file mode 100644
index 0000000..201f0f2
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub3_1_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub4_1_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gsub4_1_lookupflag_f1.otf
new file mode 100644
index 0000000..08ec01a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub4_1_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub4_1_multiple_ligatures_f1.otf b/test/shaping/data/aots/fonts/gsub4_1_multiple_ligatures_f1.otf
new file mode 100644
index 0000000..90da331
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub4_1_multiple_ligatures_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub4_1_multiple_ligatures_f2.otf b/test/shaping/data/aots/fonts/gsub4_1_multiple_ligatures_f2.otf
new file mode 100644
index 0000000..4383ba9
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub4_1_multiple_ligatures_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub4_1_multiple_ligsets_f1.otf b/test/shaping/data/aots/fonts/gsub4_1_multiple_ligsets_f1.otf
new file mode 100644
index 0000000..cea1b1a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub4_1_multiple_ligsets_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub4_1_simple_f1.otf b/test/shaping/data/aots/fonts/gsub4_1_simple_f1.otf
new file mode 100644
index 0000000..50c713a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub4_1_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub7_font1.otf b/test/shaping/data/aots/fonts/gsub7_font1.otf
new file mode 100644
index 0000000..b920398
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub7_font1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub7_font2.otf b/test/shaping/data/aots/fonts/gsub7_font2.otf
new file mode 100644
index 0000000..c98bafb
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub7_font2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f1.otf
new file mode 100644
index 0000000..444d931
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f2.otf b/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f2.otf
new file mode 100644
index 0000000..2268647
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f3.otf b/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f3.otf
new file mode 100644
index 0000000..a592947
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f4.otf b/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f4.otf
new file mode 100644
index 0000000..f3f6b8c
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining1_boundary_f4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining1_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining1_lookupflag_f1.otf
new file mode 100644
index 0000000..47e4c64
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining1_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining1_multiple_subrules_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining1_multiple_subrules_f1.otf
new file mode 100644
index 0000000..741362c
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining1_multiple_subrules_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining1_multiple_subrules_f2.otf b/test/shaping/data/aots/fonts/gsub_chaining1_multiple_subrules_f2.otf
new file mode 100644
index 0000000..67801f2
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining1_multiple_subrules_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining1_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining1_next_glyph_f1.otf
new file mode 100644
index 0000000..655000a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining1_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining1_simple_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining1_simple_f1.otf
new file mode 100644
index 0000000..c770965
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining1_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining1_simple_f2.otf b/test/shaping/data/aots/fonts/gsub_chaining1_simple_f2.otf
new file mode 100644
index 0000000..8999e3f
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining1_simple_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining1_successive_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining1_successive_f1.otf
new file mode 100644
index 0000000..ad472ec
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining1_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f1.otf
new file mode 100644
index 0000000..845c256
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f2.otf b/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f2.otf
new file mode 100644
index 0000000..af0ad1f
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f3.otf b/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f3.otf
new file mode 100644
index 0000000..28679c8
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f4.otf b/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f4.otf
new file mode 100644
index 0000000..14746c6
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining2_boundary_f4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining2_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining2_lookupflag_f1.otf
new file mode 100644
index 0000000..2f4feed
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining2_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining2_multiple_subrules_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining2_multiple_subrules_f1.otf
new file mode 100644
index 0000000..6edeb6b
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining2_multiple_subrules_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining2_multiple_subrules_f2.otf b/test/shaping/data/aots/fonts/gsub_chaining2_multiple_subrules_f2.otf
new file mode 100644
index 0000000..ae0298f
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining2_multiple_subrules_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining2_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining2_next_glyph_f1.otf
new file mode 100644
index 0000000..878666f
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining2_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining2_simple_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining2_simple_f1.otf
new file mode 100644
index 0000000..e860930
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining2_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining2_simple_f2.otf b/test/shaping/data/aots/fonts/gsub_chaining2_simple_f2.otf
new file mode 100644
index 0000000..4fdde33
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining2_simple_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining2_successive_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining2_successive_f1.otf
new file mode 100644
index 0000000..90f9f73
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining2_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f1.otf
new file mode 100644
index 0000000..1aea8be
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f2.otf b/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f2.otf
new file mode 100644
index 0000000..97c92c3
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f3.otf b/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f3.otf
new file mode 100644
index 0000000..3b8513e
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f3.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f4.otf b/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f4.otf
new file mode 100644
index 0000000..e81d00e
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining3_boundary_f4.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining3_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining3_lookupflag_f1.otf
new file mode 100644
index 0000000..47c1007
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining3_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining3_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining3_next_glyph_f1.otf
new file mode 100644
index 0000000..9160eda
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining3_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining3_simple_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining3_simple_f1.otf
new file mode 100644
index 0000000..5982eb5
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining3_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining3_simple_f2.otf b/test/shaping/data/aots/fonts/gsub_chaining3_simple_f2.otf
new file mode 100644
index 0000000..359b126
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining3_simple_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_chaining3_successive_f1.otf b/test/shaping/data/aots/fonts/gsub_chaining3_successive_f1.otf
new file mode 100644
index 0000000..ae39d92
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_chaining3_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context1_boundary_f1.otf b/test/shaping/data/aots/fonts/gsub_context1_boundary_f1.otf
new file mode 100644
index 0000000..0267cab
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context1_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context1_boundary_f2.otf b/test/shaping/data/aots/fonts/gsub_context1_boundary_f2.otf
new file mode 100644
index 0000000..24b1716
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context1_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context1_expansion_f1.otf b/test/shaping/data/aots/fonts/gsub_context1_expansion_f1.otf
new file mode 100644
index 0000000..c1dda80
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context1_expansion_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context1_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gsub_context1_lookupflag_f1.otf
new file mode 100644
index 0000000..60676ab
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context1_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context1_lookupflag_f2.otf b/test/shaping/data/aots/fonts/gsub_context1_lookupflag_f2.otf
new file mode 100644
index 0000000..bcb56e4
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context1_lookupflag_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context1_multiple_subrules_f1.otf b/test/shaping/data/aots/fonts/gsub_context1_multiple_subrules_f1.otf
new file mode 100644
index 0000000..52cd861
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context1_multiple_subrules_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context1_multiple_subrules_f2.otf b/test/shaping/data/aots/fonts/gsub_context1_multiple_subrules_f2.otf
new file mode 100644
index 0000000..891356a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context1_multiple_subrules_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context1_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gsub_context1_next_glyph_f1.otf
new file mode 100644
index 0000000..2786ded
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context1_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context1_simple_f1.otf b/test/shaping/data/aots/fonts/gsub_context1_simple_f1.otf
new file mode 100644
index 0000000..ebdaf22
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context1_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context1_simple_f2.otf b/test/shaping/data/aots/fonts/gsub_context1_simple_f2.otf
new file mode 100644
index 0000000..d4a3fbf
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context1_simple_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context1_successive_f1.otf b/test/shaping/data/aots/fonts/gsub_context1_successive_f1.otf
new file mode 100644
index 0000000..ce2dce5
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context1_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_boundary_f1.otf b/test/shaping/data/aots/fonts/gsub_context2_boundary_f1.otf
new file mode 100644
index 0000000..6f46192
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_boundary_f2.otf b/test/shaping/data/aots/fonts/gsub_context2_boundary_f2.otf
new file mode 100644
index 0000000..ef19d87
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_classes_f1.otf b/test/shaping/data/aots/fonts/gsub_context2_classes_f1.otf
new file mode 100644
index 0000000..8507760
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_classes_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_classes_f2.otf b/test/shaping/data/aots/fonts/gsub_context2_classes_f2.otf
new file mode 100644
index 0000000..ceb74b2
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_classes_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_expansion_f1.otf b/test/shaping/data/aots/fonts/gsub_context2_expansion_f1.otf
new file mode 100644
index 0000000..c12f0ac
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_expansion_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gsub_context2_lookupflag_f1.otf
new file mode 100644
index 0000000..a0fada4
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_lookupflag_f2.otf b/test/shaping/data/aots/fonts/gsub_context2_lookupflag_f2.otf
new file mode 100644
index 0000000..cd40a5d
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_lookupflag_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_multiple_subrules_f1.otf b/test/shaping/data/aots/fonts/gsub_context2_multiple_subrules_f1.otf
new file mode 100644
index 0000000..53be20d
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_multiple_subrules_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_multiple_subrules_f2.otf b/test/shaping/data/aots/fonts/gsub_context2_multiple_subrules_f2.otf
new file mode 100644
index 0000000..6bcc0cb
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_multiple_subrules_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gsub_context2_next_glyph_f1.otf
new file mode 100644
index 0000000..4ca4e48
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_simple_f1.otf b/test/shaping/data/aots/fonts/gsub_context2_simple_f1.otf
new file mode 100644
index 0000000..16aae9d
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_simple_f2.otf b/test/shaping/data/aots/fonts/gsub_context2_simple_f2.otf
new file mode 100644
index 0000000..fc31262
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_simple_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context2_successive_f1.otf b/test/shaping/data/aots/fonts/gsub_context2_successive_f1.otf
new file mode 100644
index 0000000..cf1a89c
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context2_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context3_boundary_f1.otf b/test/shaping/data/aots/fonts/gsub_context3_boundary_f1.otf
new file mode 100644
index 0000000..01cd29d
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context3_boundary_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context3_boundary_f2.otf b/test/shaping/data/aots/fonts/gsub_context3_boundary_f2.otf
new file mode 100644
index 0000000..6fa5f05
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context3_boundary_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context3_lookupflag_f1.otf b/test/shaping/data/aots/fonts/gsub_context3_lookupflag_f1.otf
new file mode 100644
index 0000000..94371b4
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context3_lookupflag_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context3_lookupflag_f2.otf b/test/shaping/data/aots/fonts/gsub_context3_lookupflag_f2.otf
new file mode 100644
index 0000000..d8150df
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context3_lookupflag_f2.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context3_next_glyph_f1.otf b/test/shaping/data/aots/fonts/gsub_context3_next_glyph_f1.otf
new file mode 100644
index 0000000..93533b8
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context3_next_glyph_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context3_simple_f1.otf b/test/shaping/data/aots/fonts/gsub_context3_simple_f1.otf
new file mode 100644
index 0000000..a1cd98c
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context3_simple_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/gsub_context3_successive_f1.otf b/test/shaping/data/aots/fonts/gsub_context3_successive_f1.otf
new file mode 100644
index 0000000..d8b3d5c
--- /dev/null
+++ b/test/shaping/data/aots/fonts/gsub_context3_successive_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/lookupflag_ignore_attach_f1.otf b/test/shaping/data/aots/fonts/lookupflag_ignore_attach_f1.otf
new file mode 100644
index 0000000..80651f1
--- /dev/null
+++ b/test/shaping/data/aots/fonts/lookupflag_ignore_attach_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/lookupflag_ignore_base_f1.otf b/test/shaping/data/aots/fonts/lookupflag_ignore_base_f1.otf
new file mode 100644
index 0000000..3c242b0
--- /dev/null
+++ b/test/shaping/data/aots/fonts/lookupflag_ignore_base_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/lookupflag_ignore_combination_f1.otf b/test/shaping/data/aots/fonts/lookupflag_ignore_combination_f1.otf
new file mode 100644
index 0000000..b88359a
--- /dev/null
+++ b/test/shaping/data/aots/fonts/lookupflag_ignore_combination_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/lookupflag_ignore_ligatures_f1.otf b/test/shaping/data/aots/fonts/lookupflag_ignore_ligatures_f1.otf
new file mode 100644
index 0000000..1dc0c23
--- /dev/null
+++ b/test/shaping/data/aots/fonts/lookupflag_ignore_ligatures_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/fonts/lookupflag_ignore_marks_f1.otf b/test/shaping/data/aots/fonts/lookupflag_ignore_marks_f1.otf
new file mode 100644
index 0000000..aa429de
--- /dev/null
+++ b/test/shaping/data/aots/fonts/lookupflag_ignore_marks_f1.otf
Binary files differ
diff --git a/test/shaping/data/aots/hb-aots-tester.cpp b/test/shaping/data/aots/hb-aots-tester.cpp
new file mode 100644
index 0000000..bd46dec
--- /dev/null
+++ b/test/shaping/data/aots/hb-aots-tester.cpp
@@ -0,0 +1,344 @@
+/*____________________________________________________________________________
+
+    Copyright 2000-2016 Adobe Systems Incorporated. All Rights Reserved.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use these files except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+____________________________________________________________________________*/
+
+#include "stdlib.h"
+#include "stdio.h"
+#include "string.h"
+#include "hb.h"
+#include "hb-ot.h"
+
+static const bool verbose = true;
+
+
+hb_feature_t *gFeatures;
+int gNbFeatures;
+
+ hb_buffer_t *runTest(const char *testName,
+                      const char *fontfileName,
+                      unsigned int *in, int nbIn,
+                      unsigned int *select, int nbSelect)
+{
+    FILE *f = fopen (fontfileName, "rb");
+    fseek(f, 0, SEEK_END);
+    long fontsize = ftell(f);
+    fseek(f, 0, SEEK_SET);
+    char *fontdata = (char *)malloc (fontsize);
+    fread(fontdata, fontsize, 1, f);
+    fclose(f);
+
+    if (verbose) {
+        printf ("------------------------------- %s\n", testName);
+    }
+
+    // setup font
+    hb_blob_t *blob = hb_blob_create(fontdata, fontsize,
+                                     HB_MEMORY_MODE_WRITABLE,
+                                     0, 0);
+    hb_face_t *face = hb_face_create(blob, 0);
+    hb_font_t *font = hb_font_create(face);
+    unsigned int upem = hb_face_get_upem (face);
+
+    hb_font_set_scale(font, upem, upem);
+    hb_ot_font_set_funcs (font);
+
+    // setup buffer
+    hb_buffer_t *buffer = hb_buffer_create();
+    hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
+    hb_buffer_set_script(buffer, HB_SCRIPT_LATIN);
+    hb_buffer_set_language(buffer, hb_language_from_string("en", 2));
+
+    hb_buffer_add_utf32(buffer, in, nbIn, 0, nbIn);
+
+    // setup features
+    hb_feature_t *features;
+    int nbFeatures;
+
+    if (nbSelect == 0)
+    {
+        nbFeatures = 1;
+
+        features = (hb_feature_t *) malloc (sizeof (*features));
+        features[0].tag = HB_TAG('t', 'e', 's', 't');
+        features[0].value = 1;
+        features[0].start = 0;
+        features[0].end = 0xffffffff;
+    }
+    else
+    {
+        nbFeatures = 0;
+
+        features = (hb_feature_t *) malloc (sizeof (*features) * nbSelect);
+        for (int i = 0; i < nbSelect; i++) {
+            if (select[i] != -1) {
+                features[nbFeatures].tag = HB_TAG('t', 'e', 's', 't');
+                features[nbFeatures].value = select[i];
+                features[nbFeatures].start = i;
+                features[nbFeatures].end = i + 1;
+                nbFeatures++;
+            }
+        }
+    }
+    gFeatures = features;
+    gNbFeatures = nbFeatures;
+
+    // shape
+    hb_shape(font, buffer, features, nbFeatures);
+
+    hb_blob_destroy(blob);
+    hb_font_destroy(font);
+    hb_face_destroy(face);
+    //free(features);
+
+    return buffer;
+}
+
+
+void printArray (const char* s, int *a, int n)
+{
+    printf ("%s  %d : ", s, n);
+    for (int i = 0; i < n; i++) {
+        printf (" %d", a[i]);
+    }
+    printf ("\n");
+}
+
+void printUArray (const char* s, unsigned int *a, int n)
+{
+    printArray (s, (int *) a, n);
+}
+
+bool gsub_test(const char *testName,
+               const char *fontfileName,
+               int nbIn, unsigned int *in,
+               int nbSelect, unsigned int *select,
+               int nbExpected, unsigned int *expected)
+{
+    hb_buffer_t *buffer = runTest(testName,
+                                  fontfileName,
+                                  in, nbIn,
+                                  select, nbSelect);
+
+    // verify
+    hb_glyph_info_t *actual = hb_buffer_get_glyph_infos(buffer, 0);
+    unsigned int nbActual = hb_buffer_get_length(buffer);
+
+    bool ok = true;
+
+    if (nbActual != nbExpected)
+        ok = false;
+    else {
+        for (int i = 0; i < nbActual; i++) {
+            if (actual[i].codepoint != expected [i]) {
+                ok = false;
+                break;
+            }
+        }
+    }
+
+
+    char test_name[255];
+    sprintf (test_name, "../../tests/%.*s.tests", (int) (strrchr (testName, '_') - testName), testName);
+    FILE *tests_file = fopen (test_name, "a+");
+    if (!ok) fprintf (tests_file, "#");
+    fprintf (tests_file, "../fonts/%s:--features=\"", fontfileName + 9);
+    for (unsigned int i = 0; i < gNbFeatures; i++)
+    {
+        if (i != 0) fprintf (tests_file, ",");
+        char buf[255];
+        hb_feature_to_string (&gFeatures[i], buf, sizeof (buf));
+        fprintf (tests_file, "%s", buf);
+    }
+    free (gFeatures);
+    fprintf (tests_file, "\" --no-clusters --no-glyph-names --no-positions:");
+
+    for (unsigned int i = 0; i < nbIn; i++)
+    {
+        if (i != 0) fprintf (tests_file, ",");
+        fprintf (tests_file, "U+%04X", in[i]);
+    }
+
+    fprintf (tests_file, ":[");
+    for (unsigned int i = 0; i < nbActual; i++)
+    {
+        if (i != 0) fprintf (tests_file, "|");
+        fprintf (tests_file, "%d", expected[i]);
+    }
+    fprintf (tests_file, "]");
+
+    fprintf (tests_file, "\n");
+    fclose (tests_file);
+
+
+    if (! ok) {
+        printf ("******* GSUB %s\n", testName);
+
+        printf ("expected %d:", nbExpected);
+        for (int i = 0; i < nbExpected; i++) {
+            printf (" %d", expected[i]); }
+        printf ("\n");
+
+        printf ("  actual %d:", nbActual);
+        for (int i = 0; i < nbActual; i++) {
+            printf (" %d", actual[i].codepoint); }
+        printf ("\n");
+
+    }
+
+    hb_buffer_destroy(buffer);
+
+    return ok;
+}
+
+bool gpos_test(const char *testName,
+               const char *fontfileName,
+               int nbIn,
+               unsigned int *in,
+               int nbOut,
+               unsigned int *out,
+               int *x,
+               int *y)
+{
+    hb_buffer_t *buffer = runTest(testName,
+                                  fontfileName,
+                                  in, nbIn,
+                                  0, 0);
+
+    // verify
+    unsigned int nbActual;
+    hb_glyph_info_t *actual = hb_buffer_get_glyph_infos(buffer, &nbActual);
+    hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
+
+    unsigned int *actualG = (unsigned int *) malloc(sizeof(*actualG) * nbActual);
+    int *actualX = (int *) malloc(sizeof(*actualX) * nbActual);
+    int *actualY = (int *) malloc(sizeof(*actualY) * nbActual);
+    int curX = 0;
+    int curY = 0;
+    for (int i = 0; i < nbActual; i++) {
+        actualG[i] = actual[i].codepoint;
+        actualX[i] = curX + pos[i].x_offset;
+        actualY[i] = curY + pos[i].y_offset;
+
+        actualX[i] -= 1500 * i;
+
+        curX += pos[i].x_advance;
+        curY += pos[i].y_advance;
+    }
+
+    bool nbOk = true;
+    bool xOk = true;
+    bool yOk = true;
+
+    if (nbActual != nbOut)
+        nbOk = false;
+    else {
+        for (int i = 0; i < nbActual; i++) {
+            if (actualX[i] != x[i]) {
+                xOk = false;
+            }
+            if (actualY[i] != y[i]) {
+                yOk = false;
+            }
+        }
+    }
+
+    bool ok = (nbOk && xOk && yOk);
+    if (! ok) {
+        printf ("******* GPOS %s\n", testName);
+
+        if (! (nbOk && xOk)) {
+            printArray ("expectedX", x, nbOut);
+            printArray ("actualX  ", actualX, nbActual);
+
+            printf ("xadv/pos:");
+            for (int i = 0; i < nbOut; i++) {
+                printf (" %d/%d", pos[i].x_advance, pos[i].x_offset);
+            }
+            printf ("\n");
+        }
+
+        if (! (nbOk && yOk)) {
+            printArray ("expectedY", y, nbOut);
+            printArray ("actualY  ", actualY, nbActual);
+
+            printf ("yadv/pos:");
+            for (int i = 0; i < nbOut; i++) {
+                printf (" %d/%d", pos[i].y_advance, pos[i].y_offset);
+            }
+            printf ("\n");
+        }
+    }
+
+
+    char test_name[255];
+    sprintf (test_name, "../../tests/%.*s.tests", (int) (strrchr (testName, '_') - testName), testName);
+    FILE *tests_file = fopen (test_name, "a+");
+    if (!ok) fprintf (tests_file, "#");
+    fprintf (tests_file, "../fonts/%s:--features=\"", fontfileName + 9);
+    for (unsigned int i = 0; i < gNbFeatures; i++)
+    {
+        if (i != 0) fprintf (tests_file, ",");
+        char buf[255];
+        hb_feature_to_string (&gFeatures[i], buf, sizeof (buf));
+        fprintf (tests_file, "%s", buf);
+    }
+    free (gFeatures);
+    fprintf (tests_file, "\" --no-clusters --no-glyph-names --ned:");
+
+    for (unsigned int i = 0; i < nbIn; i++)
+    {
+        if (i != 0) fprintf (tests_file, ",");
+        fprintf (tests_file, "U+%04X", in[i]);
+    }
+
+    fprintf (tests_file, ":[");
+    for (unsigned int i = 0; i < nbActual; i++)
+    {
+        if (i != 0) fprintf (tests_file, "|");
+        fprintf (tests_file, "%d", /*it should be "out[i]"*/ actualG[i]);
+
+        int expected_x = x[i] + 1500*i;
+        int expected_y = y[i];
+        if (expected_x || expected_y) fprintf (tests_file, "@%d,%d", expected_x, expected_y);
+    }
+    fprintf (tests_file, "]");
+
+    fprintf (tests_file, "\n");
+    fclose (tests_file);
+
+
+    hb_buffer_destroy(buffer);
+
+    free(actualG);
+    free(actualX);
+    free(actualY);
+
+    return ok;
+}
+
+
+int main(int argc, char **argv)
+{
+    int failures = 0;
+    int pass = 0;
+
+#include "hb-aots-tester.h"
+
+    printf ("%d failures, %d pass\n", failures, pass);
+}
+
+
+
diff --git a/test/shaping/data/aots/tests/classdef1.tests b/test/shaping/data/aots/tests/classdef1.tests
new file mode 100644
index 0000000..40ded45
--- /dev/null
+++ b/test/shaping/data/aots/tests/classdef1.tests
@@ -0,0 +1 @@
+../fonts/classdef1_font4.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18|19|20|21]
diff --git a/test/shaping/data/aots/tests/classdef1_empty.tests b/test/shaping/data/aots/tests/classdef1_empty.tests
new file mode 100644
index 0000000..71d87f1
--- /dev/null
+++ b/test/shaping/data/aots/tests/classdef1_empty.tests
@@ -0,0 +1 @@
+../fonts/classdef1_font2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|23|24|25|21]
diff --git a/test/shaping/data/aots/tests/classdef1_multiple.tests b/test/shaping/data/aots/tests/classdef1_multiple.tests
new file mode 100644
index 0000000..c813f49
--- /dev/null
+++ b/test/shaping/data/aots/tests/classdef1_multiple.tests
@@ -0,0 +1 @@
+../fonts/classdef1_font3.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+001B,U+001C,U+001D,U+001E,U+001F,U+0020,U+0021,U+0022,U+0023,U+0024:[20|23|24|25|24|26|27|28|28|29|30|31|34|33|34|35|37|38|38|39]
diff --git a/test/shaping/data/aots/tests/classdef1_single.tests b/test/shaping/data/aots/tests/classdef1_single.tests
new file mode 100644
index 0000000..b0196d3
--- /dev/null
+++ b/test/shaping/data/aots/tests/classdef1_single.tests
@@ -0,0 +1 @@
+../fonts/classdef2_font1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|23|24|25|21]
diff --git a/test/shaping/data/aots/tests/classdef2.tests b/test/shaping/data/aots/tests/classdef2.tests
new file mode 100644
index 0000000..d8c7b14
--- /dev/null
+++ b/test/shaping/data/aots/tests/classdef2.tests
@@ -0,0 +1 @@
+../fonts/classdef2_font4.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18|19|20|21]
diff --git a/test/shaping/data/aots/tests/classdef2_empty.tests b/test/shaping/data/aots/tests/classdef2_empty.tests
new file mode 100644
index 0000000..a8fd629
--- /dev/null
+++ b/test/shaping/data/aots/tests/classdef2_empty.tests
@@ -0,0 +1 @@
+../fonts/classdef2_font2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|23|24|25|21]
diff --git a/test/shaping/data/aots/tests/classdef2_multiple.tests b/test/shaping/data/aots/tests/classdef2_multiple.tests
new file mode 100644
index 0000000..39e6835
--- /dev/null
+++ b/test/shaping/data/aots/tests/classdef2_multiple.tests
@@ -0,0 +1 @@
+../fonts/classdef2_font3.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+001B,U+001C,U+001D,U+001E,U+001F,U+0020,U+0021,U+0022,U+0023,U+0024:[20|23|24|25|24|26|27|28|28|29|30|31|34|33|34|35|37|38|38|39]
diff --git a/test/shaping/data/aots/tests/classdef2_single.tests b/test/shaping/data/aots/tests/classdef2_single.tests
new file mode 100644
index 0000000..b0196d3
--- /dev/null
+++ b/test/shaping/data/aots/tests/classdef2_single.tests
@@ -0,0 +1 @@
+../fonts/classdef2_font1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|23|24|25|21]
diff --git a/test/shaping/data/aots/tests/gpos1_1_lookupflag.tests b/test/shaping/data/aots/tests/gpos1_1_lookupflag.tests
new file mode 100644
index 0000000..88d7dd7
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos1_1_lookupflag.tests
@@ -0,0 +1 @@
+../fonts/gpos1_1_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18@1500,0|19@3000,0|20@4200,0|21@6000,0]
diff --git a/test/shaping/data/aots/tests/gpos1_1_simple.tests b/test/shaping/data/aots/tests/gpos1_1_simple.tests
new file mode 100644
index 0000000..101da9c
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos1_1_simple.tests
@@ -0,0 +1,4 @@
+../fonts/gpos1_1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18@1300,0|19@3000,0|20@4300,0|21@6000,0]
+../fonts/gpos1_1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18@1500,-200|19@3000,0|20@4500,-200|21@6000,0]
+../fonts/gpos1_1_simple_f3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18@1500,0|19@2800,0|20@4300,0|21@5600,0]
+#../fonts/gpos1_1_simple_f4.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18@1500,0|19@3000,-200|20@4500,-200|21@6000,-400]
diff --git a/test/shaping/data/aots/tests/gpos1_2.tests b/test/shaping/data/aots/tests/gpos1_2.tests
new file mode 100644
index 0000000..3ddfa44
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos1_2.tests
@@ -0,0 +1 @@
+../fonts/gpos1_2_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18@1300,0|19@3000,0|20@4200,0|21@6000,0]
diff --git a/test/shaping/data/aots/tests/gpos1_2_lookupflag.tests b/test/shaping/data/aots/tests/gpos1_2_lookupflag.tests
new file mode 100644
index 0000000..82bcc43
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos1_2_lookupflag.tests
@@ -0,0 +1 @@
+../fonts/gpos1_2_font2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18@1500,0|19@3000,0|20@4200,0|21@6000,0]
diff --git a/test/shaping/data/aots/tests/gpos2_1.tests b/test/shaping/data/aots/tests/gpos2_1.tests
new file mode 100644
index 0000000..4d8b5e9
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos2_1.tests
@@ -0,0 +1,2 @@
+../fonts/gpos2_1_font6.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0011,U+0012,U+0014,U+0011:[17|18@1300,0|19@3000,-100|17@4500,0|18@5700,0|20@7500,-400|17@9000,0]
+../fonts/gpos2_1_font7.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0011,U+0012,U+0014,U+0011,U+0015,U+0016,U+0011:[17|18@1300,0|19@3000,-100|17@4500,0|18@5700,0|20@7500,-400|17@9000,0|21@10000,0|22@12000,-600|17@13500,0]
diff --git a/test/shaping/data/aots/tests/gpos2_1_lookupflag.tests b/test/shaping/data/aots/tests/gpos2_1_lookupflag.tests
new file mode 100644
index 0000000..ce445a1
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos2_1_lookupflag.tests
@@ -0,0 +1,2 @@
+../fonts/gpos2_1_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0013,U+0014,U+0011,U+0013,U+0012,U+0014,U+0011:[17|19@1300,0|20@3000,-100|17@4500,0|19@5800,0|18@7500,0|20@9000,-100|17@10500,0]
+../fonts/gpos2_1_lookupflag_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0013,U+0014,U+0011,U+0013,U+0012,U+0014,U+0011:[17|19@1500,0|20@2800,-100|17@4300,0|19@5800,0|18@7100,0|20@8600,-100|17@10100,0]
diff --git a/test/shaping/data/aots/tests/gpos2_1_next_glyph.tests b/test/shaping/data/aots/tests/gpos2_1_next_glyph.tests
new file mode 100644
index 0000000..7f27eee
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos2_1_next_glyph.tests
@@ -0,0 +1,2 @@
+../fonts/gpos2_1_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0012,U+0012,U+0012,U+0012:[18@-100,0|18@1500,-100|18@2900,0|18@4500,-100]
+../fonts/gpos2_1_next_glyph_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0012,U+0012,U+0012,U+0012:[18@-100,0|18@1400,0|18@2900,0|18@4500,0]
diff --git a/test/shaping/data/aots/tests/gpos2_1_simple.tests b/test/shaping/data/aots/tests/gpos2_1_simple.tests
new file mode 100644
index 0000000..71e8c81
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos2_1_simple.tests
@@ -0,0 +1,2 @@
+../fonts/gpos2_1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0011,U+0012,U+0014:[17|18@1300,0|19@3000,-100|17@4500,0|18@6000,0|20@7500,0]
+../fonts/gpos2_1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012:[17|18@1500,0]
diff --git a/test/shaping/data/aots/tests/gpos2_2.tests b/test/shaping/data/aots/tests/gpos2_2.tests
new file mode 100644
index 0000000..7be07f7
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos2_2.tests
@@ -0,0 +1,5 @@
+../fonts/gpos2_2_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0011,U+0012,U+0014:[17|18@1300,0|19@3000,-100|17@4500,0|18@6000,0|20@7500,0]
+../fonts/gpos2_2_font2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0013,U+0014,U+0011,U+0013,U+0012,U+0014,U+0011:[17|19@1300,0|20@3000,-100|17@4500,0|19@5800,0|18@7500,0|20@9000,-100|17@10500,0]
+../fonts/gpos2_2_font3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0013,U+0014,U+0011,U+0013,U+0012,U+0014,U+0011:[17|19@1500,0|20@2800,-100|17@4300,0|19@5800,0|18@7100,0|20@8600,-100|17@10100,0]
+../fonts/gpos2_2_font4.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0012,U+0012,U+0012,U+0012:[18@-100,0|18@1500,-100|18@2900,0|18@4500,-100]
+../fonts/gpos2_2_font5.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0012,U+0012,U+0012,U+0012:[18@-100,0|18@1400,0|18@2900,0|18@4500,0]
diff --git a/test/shaping/data/aots/tests/gpos3.tests b/test/shaping/data/aots/tests/gpos3.tests
new file mode 100644
index 0000000..d6f37bf
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos3.tests
@@ -0,0 +1,11 @@
+#../fonts/gpos3_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0011:[17|18@1500,0|19@1599,99|17@4500,0]
+../fonts/gpos3_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0011,U+0013,U+0011:[17|18@1500,0|17@3000,0|19@4500,0|17@6000,0]
+#../fonts/gpos3_font3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0012,U+0011:[17|18@1500,0|18@1600,100|17@4500,0]
+#../fonts/gpos3_font3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0011:[17|18@1500,0|19@1599,99|17@4500,0]
+#../fonts/gpos3_font3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0014,U+0012,U+0011:[17|20@1500,0|18@1602,102|17@4500,0]
+#../fonts/gpos3_font3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0014,U+0013,U+0011:[17|20@1500,0|19@1601,101|17@4500,0]
+../fonts/gpos3_font3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0014,U+0011:[17|18@1500,0|20@3000,0|17@4500,0]
+../fonts/gpos3_font3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0013,U+0012,U+0011:[17|19@1500,0|18@3000,0|17@4500,0]
+../fonts/gpos3_font3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0013,U+0014,U+0011:[17|19@1500,0|20@3000,0|17@4500,0]
+../fonts/gpos3_font3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012:[17|18@1500,0]
+../fonts/gpos3_font3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0015,U+0015,U+0015:[17|18@1500,0|21@3000,0|21@4500,0|21@6000,0]
diff --git a/test/shaping/data/aots/tests/gpos3_lookupflag.tests b/test/shaping/data/aots/tests/gpos3_lookupflag.tests
new file mode 100644
index 0000000..13d593c
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos3_lookupflag.tests
@@ -0,0 +1,2 @@
+#../fonts/gpos3_font2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0015,U+0013,U+0011:[17|18@1500,0|21@3000,0|19@1599,99|17@6000,0]
+#../fonts/gpos3_font2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0015,U+0015,U+0015,U+0013,U+0011:[17|18@1500,0|21@3000,0|21@4500,0|21@6000,0|19@1599,99|17@9000,0]
diff --git a/test/shaping/data/aots/tests/gpos4_lookupflag.tests b/test/shaping/data/aots/tests/gpos4_lookupflag.tests
new file mode 100644
index 0000000..9d041bf
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos4_lookupflag.tests
@@ -0,0 +1,2 @@
+#../fonts/gpos4_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0011,U+0013,U+0011:[17|18@1500,0|17@3000,0|19@4500,0|17@6000,0]
+#../fonts/gpos4_lookupflag_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0011:[17|18@1500,0|19@3000,0|17@4500,0]
diff --git a/test/shaping/data/aots/tests/gpos4_multiple_anchors.tests b/test/shaping/data/aots/tests/gpos4_multiple_anchors.tests
new file mode 100644
index 0000000..af9a1f5
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos4_multiple_anchors.tests
@@ -0,0 +1 @@
+#../fonts/gpos4_multiple_anchors_1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0013,U+0014,U+0015,U+0016,U+0012,U+0013,U+0014,U+0015,U+0016:[17|19@-100,-80|20@-91,-71|21@-102,-82|22@-93,-73|18@7500,0|19@7420,-60|20@7429,-51|21@7418,-62|22@7427,-53]
diff --git a/test/shaping/data/aots/tests/gpos4_simple.tests b/test/shaping/data/aots/tests/gpos4_simple.tests
new file mode 100644
index 0000000..5d60507
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos4_simple.tests
@@ -0,0 +1,5 @@
+#../fonts/gpos4_simple_1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0011:[17|18@1500,0|19@1400,-80|17@4500,0]
+#../fonts/gpos4_simple_1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0011,U+0013,U+0011:[17|17@1500,0|19@3000,0|17@4500,0]
+#../fonts/gpos4_simple_1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0019,U+0019,U+0013,U+0011:[25|25@1500,0|19@3000,0|17@4500,0]
+#../fonts/gpos4_simple_1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0013,U+0011:[17|18@1500,0|19@1400,-80|19@1400,-80|17@6000,0]
+#../fonts/gpos4_simple_1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0014,U+0013,U+0011:[17|18@1500,0|20@3000,0|19@1400,-80|17@6000,0]
diff --git a/test/shaping/data/aots/tests/gpos5.tests b/test/shaping/data/aots/tests/gpos5.tests
new file mode 100644
index 0000000..a20a0b9
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos5.tests
@@ -0,0 +1,2 @@
+#../fonts/gpos5_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+001E,U+0013,U+001F,U+0011:[17|18@1500,0|19@1400,-80|17@4500,0]
+#../fonts/gpos5_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+001E,U+001F,U+0013,U+0011:[17|18@1500,0|19@1401,-79|17@4500,0]
diff --git a/test/shaping/data/aots/tests/gpos6.tests b/test/shaping/data/aots/tests/gpos6.tests
new file mode 100644
index 0000000..e5f9b3c
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos6.tests
@@ -0,0 +1,3 @@
+#../fonts/gpos6_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0011:[17|18@1500,0|19@1400,-80|17@4500,0]
+#../fonts/gpos6_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0011,U+0013,U+0011:[17|17@1500,0|19@3000,0|17@4500,0]
+#../fonts/gpos6_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0014,U+0014,U+0013,U+0011:[20|20@1500,0|19@3000,0|17@4500,0]
diff --git a/test/shaping/data/aots/tests/gpos7_1.tests b/test/shaping/data/aots/tests/gpos7_1.tests
new file mode 100644
index 0000000..954c8cb
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos7_1.tests
@@ -0,0 +1,2 @@
+../fonts/gpos7_1_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18@1600,0|19@3200,0|20@4800,0|21@6000,0]
+../fonts/gpos7_1_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0011,U+0012,U+0013,U+0011:[17|18@1500,0|17@3000,0|18@4500,0|19@6000,0|17@7500,0]
diff --git a/test/shaping/data/aots/tests/gpos9.tests b/test/shaping/data/aots/tests/gpos9.tests
new file mode 100644
index 0000000..cb20333
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos9.tests
@@ -0,0 +1,2 @@
+../fonts/gpos9_font1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18@1300,0|19@3000,0|20@4300,0|21@6000,0]
+../fonts/gpos9_font2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0011,U+0012,U+0013,U+0014,U+0015,U+0011:[17|18@1300,0|19@2700,0|20@4300,0|21@5700,0|17@7500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining1_boundary.tests b/test/shaping/data/aots/tests/gpos_chaining1_boundary.tests
new file mode 100644
index 0000000..646ff2c
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining1_boundary.tests
@@ -0,0 +1,4 @@
+../fonts/gpos_chaining1_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining1_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4500,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining1_boundary_f3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4500,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining1_boundary_f4.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3000,0|22@4520,0|23@6000,0|0@7500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining1_lookupflag.tests b/test/shaping/data/aots/tests/gpos_chaining1_lookupflag.tests
new file mode 100644
index 0000000..d0e6e2e
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining1_lookupflag.tests
@@ -0,0 +1 @@
+#../fonts/gpos_chaining1_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+005A,U+0015,U+005B,U+0016,U+005C,U+0017,U+005D,U+005E,U+0018,U+005A,U+0019,U+005B,U+001A,U+0000:[0|20@1500,0|90@3000,0|21@4500,0|91@6000,0|22@7500,0|92@9000,0|23@10520,0|93@12000,0|94@13500,0|24@15000,0|90@16500,0|25@18000,0|91@19500,0|26@21000,0|0@22500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining1_multiple_subrules.tests b/test/shaping/data/aots/tests/gpos_chaining1_multiple_subrules.tests
new file mode 100644
index 0000000..51bbe03
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining1_multiple_subrules.tests
@@ -0,0 +1,2 @@
+../fonts/gpos_chaining1_multiple_subrules_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4500,0|23@6000,0|24@7500,0|0@9000,0|20@10500,0|21@12000,0|22@13520,0|23@15000,0|0@16500,0]
+../fonts/gpos_chaining1_multiple_subrules_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3000,0|22@4520,0|23@6000,0|24@7500,0|0@9000,0|20@10500,0|21@12000,0|22@13520,0|23@15000,0|0@16500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining1_next_glyph.tests b/test/shaping/data/aots/tests/gpos_chaining1_next_glyph.tests
new file mode 100644
index 0000000..f8be404
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining1_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gpos_chaining1_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4520,0|23@6020,0|0@7500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining1_simple.tests b/test/shaping/data/aots/tests/gpos_chaining1_simple.tests
new file mode 100644
index 0000000..37efa11
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining1_simple.tests
@@ -0,0 +1,11 @@
+../fonts/gpos_chaining1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4520,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|20@1500,0|21@3000,0|22@4520,0|23@6000,0|24@7500,0|25@9000,0|26@10500,0|0@12000,0]
+../fonts/gpos_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+0000,U+0000:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0|25@9000,0|0@10500,0|0@12000,0]
+../fonts/gpos_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0|25@9000,0]
+../fonts/gpos_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0]
+../fonts/gpos_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0000,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|0@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0|25@9000,0|26@10500,0|0@12000,0]
+../fonts/gpos_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[21|22@1500,0|23@3000,0|24@4500,0|25@6000,0|26@7500,0|0@9000,0]
+../fonts/gpos_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[22|23@1500,0|24@3000,0|25@4500,0|26@6000,0|0@7500,0]
+../fonts/gpos_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000,U+0018,U+0019,U+001A,U+0000:[0|20@1500,0|21@3000,0|22@4500,0|0@6000,0|24@7500,0|25@9000,0|26@10500,0|0@12000,0]
+../fonts/gpos_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0]
+../fonts/gpos_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016:[0|20@1500,0|21@3000,0|22@4500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining1_successive.tests b/test/shaping/data/aots/tests/gpos_chaining1_successive.tests
new file mode 100644
index 0000000..7a829cf
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining1_successive.tests
@@ -0,0 +1 @@
+../fonts/gpos_chaining1_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0019,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000:[0|25@1500,0|20@3000,0|21@4520,0|22@6020,0|23@7500,0|24@9000,0|0@10500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining2_boundary.tests b/test/shaping/data/aots/tests/gpos_chaining2_boundary.tests
new file mode 100644
index 0000000..c35b8c7
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining2_boundary.tests
@@ -0,0 +1,4 @@
+../fonts/gpos_chaining2_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining2_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4500,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining2_boundary_f3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4500,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining2_boundary_f4.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3000,0|22@4520,0|23@6000,0|0@7500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining2_lookupflag.tests b/test/shaping/data/aots/tests/gpos_chaining2_lookupflag.tests
new file mode 100644
index 0000000..8b50e14
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining2_lookupflag.tests
@@ -0,0 +1 @@
+#../fonts/gpos_chaining2_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+005A,U+0015,U+005B,U+0016,U+005C,U+0017,U+005D,U+005E,U+0018,U+005A,U+0019,U+005B,U+001A,U+0000:[0|20@1500,0|90@3000,0|21@4500,0|91@6000,0|22@7500,0|92@9000,0|23@10520,0|93@12000,0|94@13500,0|24@15000,0|90@16500,0|25@18000,0|91@19500,0|26@21000,0|0@22500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining2_multiple_subrules.tests b/test/shaping/data/aots/tests/gpos_chaining2_multiple_subrules.tests
new file mode 100644
index 0000000..8ddc8b2
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining2_multiple_subrules.tests
@@ -0,0 +1,2 @@
+../fonts/gpos_chaining2_multiple_subrules_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4500,0|23@6000,0|24@7500,0|0@9000,0|20@10500,0|21@12000,0|22@13520,0|23@15000,0|0@16500,0]
+../fonts/gpos_chaining2_multiple_subrules_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3000,0|22@4520,0|23@6000,0|24@7500,0|0@9000,0|20@10500,0|21@12000,0|22@13520,0|23@15000,0|0@16500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining2_next_glyph.tests b/test/shaping/data/aots/tests/gpos_chaining2_next_glyph.tests
new file mode 100644
index 0000000..34170f2
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining2_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gpos_chaining2_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4520,0|23@6020,0|0@7500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining2_simple.tests b/test/shaping/data/aots/tests/gpos_chaining2_simple.tests
new file mode 100644
index 0000000..32fda1b
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining2_simple.tests
@@ -0,0 +1,11 @@
+../fonts/gpos_chaining2_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4520,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|20@1500,0|21@3000,0|22@4520,0|23@6000,0|24@7500,0|25@9000,0|26@10500,0|0@12000,0]
+../fonts/gpos_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+0000,U+0000:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0|25@9000,0|0@10500,0|0@12000,0]
+../fonts/gpos_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0|25@9000,0]
+../fonts/gpos_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0]
+../fonts/gpos_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0000,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|0@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0|25@9000,0|26@10500,0|0@12000,0]
+../fonts/gpos_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[21|22@1500,0|23@3000,0|24@4500,0|25@6000,0|26@7500,0|0@9000,0]
+../fonts/gpos_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[22|23@1500,0|24@3000,0|25@4500,0|26@6000,0|0@7500,0]
+../fonts/gpos_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000,U+0018,U+0019,U+001A,U+0000:[0|20@1500,0|21@3000,0|22@4500,0|0@6000,0|24@7500,0|25@9000,0|26@10500,0|0@12000,0]
+../fonts/gpos_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0]
+../fonts/gpos_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016:[0|20@1500,0|21@3000,0|22@4500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining2_successive.tests b/test/shaping/data/aots/tests/gpos_chaining2_successive.tests
new file mode 100644
index 0000000..e930863
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining2_successive.tests
@@ -0,0 +1 @@
+../fonts/gpos_chaining2_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0019,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000:[0|25@1500,0|20@3000,0|21@4520,0|22@6020,0|23@7500,0|24@9000,0|0@10500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining3_boundary.tests b/test/shaping/data/aots/tests/gpos_chaining3_boundary.tests
new file mode 100644
index 0000000..f74dedf
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining3_boundary.tests
@@ -0,0 +1,4 @@
+../fonts/gpos_chaining3_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining3_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4500,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining3_boundary_f3.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4500,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining3_boundary_f4.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3000,0|22@4520,0|23@6000,0|0@7500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining3_lookupflag.tests b/test/shaping/data/aots/tests/gpos_chaining3_lookupflag.tests
new file mode 100644
index 0000000..0165fb5
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining3_lookupflag.tests
@@ -0,0 +1 @@
+#../fonts/gpos_chaining3_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+005A,U+0015,U+005B,U+0016,U+005C,U+0017,U+005D,U+005E,U+0018,U+005A,U+0019,U+005B,U+001A,U+0000:[0|20@1500,0|90@3000,0|21@4500,0|91@6000,0|22@7500,0|92@9000,0|23@10520,0|93@12000,0|94@13500,0|24@15000,0|90@16500,0|25@18000,0|91@19500,0|26@21000,0|0@22500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining3_next_glyph.tests b/test/shaping/data/aots/tests/gpos_chaining3_next_glyph.tests
new file mode 100644
index 0000000..614bc2e
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining3_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gpos_chaining3_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0016,U+0015,U+0016,U+0015,U+0016,U+0015,U+0000:[0|22@1500,0|21@3020,0|22@4500,0|21@6020,0|22@7500,0|21@9000,0|0@10500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining3_simple.tests b/test/shaping/data/aots/tests/gpos_chaining3_simple.tests
new file mode 100644
index 0000000..f5977c2
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining3_simple.tests
@@ -0,0 +1,11 @@
+../fonts/gpos_chaining3_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4520,0|23@6000,0|0@7500,0]
+../fonts/gpos_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|20@1500,0|21@3000,0|22@4520,0|23@6000,0|24@7500,0|25@9000,0|26@10500,0|0@12000,0]
+../fonts/gpos_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+0000,U+0000:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0|25@9000,0|0@10500,0|0@12000,0]
+../fonts/gpos_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0|25@9000,0]
+../fonts/gpos_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0]
+../fonts/gpos_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0000,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|0@1500,0|21@3000,0|22@4500,0|23@6000,0|24@7500,0|25@9000,0|26@10500,0|0@12000,0]
+../fonts/gpos_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[21|22@1500,0|23@3000,0|24@4500,0|25@6000,0|26@7500,0|0@9000,0]
+../fonts/gpos_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[22|23@1500,0|24@3000,0|25@4500,0|26@6000,0|0@7500,0]
+../fonts/gpos_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000,U+0018,U+0019,U+001A,U+0000:[0|20@1500,0|21@3000,0|22@4500,0|0@6000,0|24@7500,0|25@9000,0|26@10500,0|0@12000,0]
+../fonts/gpos_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017:[0|20@1500,0|21@3000,0|22@4500,0|23@6000,0]
+../fonts/gpos_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016:[0|20@1500,0|21@3000,0|22@4500,0]
diff --git a/test/shaping/data/aots/tests/gpos_chaining3_successive.tests b/test/shaping/data/aots/tests/gpos_chaining3_successive.tests
new file mode 100644
index 0000000..fa5a50c
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_chaining3_successive.tests
@@ -0,0 +1 @@
+../fonts/gpos_chaining3_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0019,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000:[0|25@1500,0|20@3000,0|21@4520,0|22@6020,0|23@7500,0|24@9000,0|0@10500,0]
diff --git a/test/shaping/data/aots/tests/gpos_context1_boundary.tests b/test/shaping/data/aots/tests/gpos_context1_boundary.tests
new file mode 100644
index 0000000..1db8fef
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context1_boundary.tests
@@ -0,0 +1,2 @@
+../fonts/gpos_context1_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20@1500,0|20@3000,0|20@4500,0|20@6000,0|20@7500,0|0@9000,0]
+../fonts/gpos_context1_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20@1520,0|20@3020,0|20@4520,0|20@6020,0|20@7520,0|0@9000,0]
diff --git a/test/shaping/data/aots/tests/gpos_context1_expansion.tests b/test/shaping/data/aots/tests/gpos_context1_expansion.tests
new file mode 100644
index 0000000..2fc54d5
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context1_expansion.tests
@@ -0,0 +1 @@
+../fonts/gpos_context1_expansion_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000:[0|20@1500,0|21@3000,0|22@4500,0|0@6000,0]
diff --git a/test/shaping/data/aots/tests/gpos_context1_lookupflag.tests b/test/shaping/data/aots/tests/gpos_context1_lookupflag.tests
new file mode 100644
index 0000000..9e8fcd6
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context1_lookupflag.tests
@@ -0,0 +1,2 @@
+#../fonts/gpos_context1_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|20@1520,0|90@3000,0|21@4520,0|91@6000,0|92@7500,0|22@9020,0|0@10500,0]
+#../fonts/gpos_context1_lookupflag_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|20@1500,0|90@3000,0|21@4520,0|91@6000,0|92@7500,0|22@9000,0|0@10500,0]
diff --git a/test/shaping/data/aots/tests/gpos_context1_multiple_subrules.tests b/test/shaping/data/aots/tests/gpos_context1_multiple_subrules.tests
new file mode 100644
index 0000000..b994f04
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context1_multiple_subrules.tests
@@ -0,0 +1,2 @@
+../fonts/gpos_context1_multiple_subrules_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000,U+0014,U+0015,U+0000:[0|20@1520,0|21@3000,0|22@4500,0|0@6000,0|20@7500,0|21@9020,0|0@10500,0]
+../fonts/gpos_context1_multiple_subrules_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000,U+0014,U+0015,U+0000:[0|20@1500,0|21@3020,0|22@4500,0|0@6000,0|20@7500,0|21@9020,0|0@10500,0]
diff --git a/test/shaping/data/aots/tests/gpos_context1_next_glyph.tests b/test/shaping/data/aots/tests/gpos_context1_next_glyph.tests
new file mode 100644
index 0000000..e67d635
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context1_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gpos_context1_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20@1520,0|20@3000,0|20@4520,0|20@6000,0|20@7500,0|0@9000,0]
diff --git a/test/shaping/data/aots/tests/gpos_context1_simple.tests b/test/shaping/data/aots/tests/gpos_context1_simple.tests
new file mode 100644
index 0000000..4a88e0a
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context1_simple.tests
@@ -0,0 +1,3 @@
+../fonts/gpos_context1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000:[0|20@1520,0|21@3020,0|22@4520,0|0@6000,0]
+../fonts/gpos_context1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0000,U+0014,U+0015,U+0000:[0|20@1500,0|0@3000,0|20@4500,0|21@6000,0|0@7500,0]
+../fonts/gpos_context1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20@1500,0|20@3020,0|20@4500,0|20@6000,0|20@7500,0|0@9000,0]
diff --git a/test/shaping/data/aots/tests/gpos_context1_successive.tests b/test/shaping/data/aots/tests/gpos_context1_successive.tests
new file mode 100644
index 0000000..172d350
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context1_successive.tests
@@ -0,0 +1 @@
+../fonts/gpos_context1_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4520,0|23@6000,0|0@7500,0]
diff --git a/test/shaping/data/aots/tests/gpos_context2_boundary.tests b/test/shaping/data/aots/tests/gpos_context2_boundary.tests
new file mode 100644
index 0000000..ef63fbb
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context2_boundary.tests
@@ -0,0 +1,2 @@
+../fonts/gpos_context2_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20@1500,0|20@3000,0|20@4500,0|20@6000,0|20@7500,0|0@9000,0]
+../fonts/gpos_context2_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20@1520,0|20@3020,0|20@4520,0|20@6020,0|20@7520,0|0@9000,0]
diff --git a/test/shaping/data/aots/tests/gpos_context2_classes.tests b/test/shaping/data/aots/tests/gpos_context2_classes.tests
new file mode 100644
index 0000000..5a3d008
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context2_classes.tests
@@ -0,0 +1,2 @@
+../fonts/gpos_context2_classes_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+001A,U+001C,U+0018,U+0000,U+0015,U+001B,U+001A,U+0018,U+0000,U+0016,U+001B,U+001A,U+0018:[0|20@1500,0|26@3020,0|28@4500,0|24@6000,0|0@7500,0|21@9000,0|27@10520,0|26@12000,0|24@13500,0|0@15000,0|22@16500,0|27@18000,0|26@19500,0|24@21000,0]
+../fonts/gpos_context2_classes_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0016,U+001B,U+001A,U+0018,U+0000,U+0018,U+0018,U+001D,U+0016,U+0000,U+0016,U+001B,U+001A,U+0018:[0|22@1500,0|27@3020,0|26@4500,0|24@6000,0|0@7500,0|24@9000,0|24@10500,0|29@12020,0|22@13500,0|0@15000,0|22@16500,0|27@18020,0|26@19500,0|24@21000,0]
diff --git a/test/shaping/data/aots/tests/gpos_context2_expansion.tests b/test/shaping/data/aots/tests/gpos_context2_expansion.tests
new file mode 100644
index 0000000..67ed978
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context2_expansion.tests
@@ -0,0 +1 @@
+../fonts/gpos_context2_expansion_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000:[0|20@1500,0|21@3000,0|22@4500,0|0@6000,0]
diff --git a/test/shaping/data/aots/tests/gpos_context2_lookupflag.tests b/test/shaping/data/aots/tests/gpos_context2_lookupflag.tests
new file mode 100644
index 0000000..f48e825
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context2_lookupflag.tests
@@ -0,0 +1,2 @@
+#../fonts/gpos_context2_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|20@1520,0|90@3000,0|21@4520,0|91@6000,0|92@7500,0|22@9020,0|0@10500,0]
+#../fonts/gpos_context2_lookupflag_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|20@1500,0|90@3000,0|21@4520,0|91@6000,0|92@7500,0|22@9000,0|0@10500,0]
diff --git a/test/shaping/data/aots/tests/gpos_context2_multiple_subrules.tests b/test/shaping/data/aots/tests/gpos_context2_multiple_subrules.tests
new file mode 100644
index 0000000..4489372
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context2_multiple_subrules.tests
@@ -0,0 +1,2 @@
+../fonts/gpos_context2_multiple_subrules_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000,U+0014,U+0015,U+0000:[0|20@1520,0|21@3000,0|22@4500,0|0@6000,0|20@7500,0|21@9020,0|0@10500,0]
+../fonts/gpos_context2_multiple_subrules_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000,U+0014,U+0015,U+0000:[0|20@1500,0|21@3020,0|22@4500,0|0@6000,0|20@7500,0|21@9020,0|0@10500,0]
diff --git a/test/shaping/data/aots/tests/gpos_context2_next_glyph.tests b/test/shaping/data/aots/tests/gpos_context2_next_glyph.tests
new file mode 100644
index 0000000..e736b3b
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context2_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gpos_context2_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20@1520,0|20@3000,0|20@4520,0|20@6000,0|20@7500,0|0@9000,0]
diff --git a/test/shaping/data/aots/tests/gpos_context2_simple.tests b/test/shaping/data/aots/tests/gpos_context2_simple.tests
new file mode 100644
index 0000000..edbc0be
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context2_simple.tests
@@ -0,0 +1,3 @@
+../fonts/gpos_context2_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000:[0|20@1520,0|21@3020,0|22@4520,0|0@6000,0]
+../fonts/gpos_context2_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0000,U+0014,U+0015,U+0000:[0|20@1500,0|0@3000,0|20@4500,0|21@6000,0|0@7500,0]
+../fonts/gpos_context2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20@1500,0|20@3020,0|20@4500,0|20@6000,0|20@7500,0|0@9000,0]
diff --git a/test/shaping/data/aots/tests/gpos_context2_successive.tests b/test/shaping/data/aots/tests/gpos_context2_successive.tests
new file mode 100644
index 0000000..8b098d5
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context2_successive.tests
@@ -0,0 +1 @@
+../fonts/gpos_context2_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4520,0|23@6000,0|0@7500,0]
diff --git a/test/shaping/data/aots/tests/gpos_context3_boundary.tests b/test/shaping/data/aots/tests/gpos_context3_boundary.tests
new file mode 100644
index 0000000..de3c057
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context3_boundary.tests
@@ -0,0 +1,2 @@
+../fonts/gpos_context3_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20@1500,0|20@3000,0|20@4500,0|20@6000,0|20@7500,0|0@9000,0]
+../fonts/gpos_context3_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20@1520,0|20@3020,0|20@4520,0|20@6020,0|20@7520,0|0@9000,0]
diff --git a/test/shaping/data/aots/tests/gpos_context3_lookupflag.tests b/test/shaping/data/aots/tests/gpos_context3_lookupflag.tests
new file mode 100644
index 0000000..21f851b
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context3_lookupflag.tests
@@ -0,0 +1,2 @@
+#../fonts/gpos_context3_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|20@1520,0|90@3000,0|21@4520,0|91@6000,0|92@7500,0|22@9020,0|0@10500,0]
+#../fonts/gpos_context3_lookupflag_f2.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|20@1500,0|90@3000,0|21@4520,0|91@6000,0|92@7500,0|22@9000,0|0@10500,0]
diff --git a/test/shaping/data/aots/tests/gpos_context3_next_glyph.tests b/test/shaping/data/aots/tests/gpos_context3_next_glyph.tests
new file mode 100644
index 0000000..049b156
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context3_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gpos_context3_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20@1520,0|20@3000,0|20@4520,0|20@6000,0|20@7500,0|0@9000,0]
diff --git a/test/shaping/data/aots/tests/gpos_context3_simple.tests b/test/shaping/data/aots/tests/gpos_context3_simple.tests
new file mode 100644
index 0000000..3e544f0
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context3_simple.tests
@@ -0,0 +1,2 @@
+../fonts/gpos_context3_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0000:[0|20@1520,0|21@3020,0|22@4520,0|0@6000,0]
+../fonts/gpos_context3_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0000,U+0014,U+0015,U+0000,U+0014,U+0015,U+0016,U+0000:[0|20@1500,0|0@3000,0|20@4500,0|21@6000,0|0@7500,0|20@9020,0|21@10520,0|22@12020,0|0@13500,0]
diff --git a/test/shaping/data/aots/tests/gpos_context3_successive.tests b/test/shaping/data/aots/tests/gpos_context3_successive.tests
new file mode 100644
index 0000000..bfcf24a
--- /dev/null
+++ b/test/shaping/data/aots/tests/gpos_context3_successive.tests
@@ -0,0 +1 @@
+../fonts/gpos_context3_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --ned:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20@1500,0|21@3020,0|22@4520,0|23@6000,0|0@7500,0]
diff --git a/test/shaping/data/aots/tests/gsub1_1_lookupflag.tests b/test/shaping/data/aots/tests/gsub1_1_lookupflag.tests
new file mode 100644
index 0000000..8865af8
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub1_1_lookupflag.tests
@@ -0,0 +1 @@
+../fonts/gsub1_1_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18|24|20|21]
diff --git a/test/shaping/data/aots/tests/gsub1_1_modulo.tests b/test/shaping/data/aots/tests/gsub1_1_modulo.tests
new file mode 100644
index 0000000..bbfff5e
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub1_1_modulo.tests
@@ -0,0 +1 @@
+../fonts/gsub1_1_modulo_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015,U+0016,U+0017,U+0018:[17|18|17|24|23|18|23|24]
diff --git a/test/shaping/data/aots/tests/gsub1_1_simple.tests b/test/shaping/data/aots/tests/gsub1_1_simple.tests
new file mode 100644
index 0000000..a3a1385
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub1_1_simple.tests
@@ -0,0 +1 @@
+../fonts/gsub1_1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|23|24|20|21]
diff --git a/test/shaping/data/aots/tests/gsub1_2_lookupflag.tests b/test/shaping/data/aots/tests/gsub1_2_lookupflag.tests
new file mode 100644
index 0000000..887e047
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub1_2_lookupflag.tests
@@ -0,0 +1 @@
+../fonts/gsub1_2_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|18|19|25|21]
diff --git a/test/shaping/data/aots/tests/gsub1_2_simple.tests b/test/shaping/data/aots/tests/gsub1_2_simple.tests
new file mode 100644
index 0000000..d657897
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub1_2_simple.tests
@@ -0,0 +1 @@
+../fonts/gsub1_2_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|22|19|25|21]
diff --git a/test/shaping/data/aots/tests/gsub2_1_lookupflag.tests b/test/shaping/data/aots/tests/gsub2_1_lookupflag.tests
new file mode 100644
index 0000000..e28e59c
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub2_1_lookupflag.tests
@@ -0,0 +1 @@
+../fonts/gsub2_1_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0011:[17|18|22|23|17]
diff --git a/test/shaping/data/aots/tests/gsub2_1_multiple_sequences.tests b/test/shaping/data/aots/tests/gsub2_1_multiple_sequences.tests
new file mode 100644
index 0000000..12cbbf6
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub2_1_multiple_sequences.tests
@@ -0,0 +1 @@
+../fonts/gsub2_1_multiple_sequences_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0011:[17|20|21|22|23|17]
diff --git a/test/shaping/data/aots/tests/gsub2_1_simple.tests b/test/shaping/data/aots/tests/gsub2_1_simple.tests
new file mode 100644
index 0000000..d1d0969
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub2_1_simple.tests
@@ -0,0 +1,2 @@
+../fonts/gsub2_1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013:[17|20|21|22|19]
+../fonts/gsub2_1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0012:[17|20|21|22|19|20|21|22]
diff --git a/test/shaping/data/aots/tests/gsub3_1_lookupflag.tests b/test/shaping/data/aots/tests/gsub3_1_lookupflag.tests
new file mode 100644
index 0000000..193c5c4
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub3_1_lookupflag.tests
@@ -0,0 +1 @@
+../fonts/gsub3_1_lookupflag_f1.otf:--features="-test[4],test[5],test[6]=2,-test[7]" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0012,U+0012,U+0013,U+0013,U+0013,U+0013,U+0011:[17|18|18|18|19|22|23|19|17]
diff --git a/test/shaping/data/aots/tests/gsub3_1_multiple.tests b/test/shaping/data/aots/tests/gsub3_1_multiple.tests
new file mode 100644
index 0000000..7b1c032
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub3_1_multiple.tests
@@ -0,0 +1 @@
+../fonts/gsub3_1_multiple_f1.otf:--features="-test[1],test[2],test[3]=2,-test[4],-test[5],test[6],test[7]=2,-test[8]" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0012,U+0012,U+0012,U+0013,U+0013,U+0013,U+0013,U+0011:[17|18|20|21|18|19|22|23|19|17]
diff --git a/test/shaping/data/aots/tests/gsub3_1_simple.tests b/test/shaping/data/aots/tests/gsub3_1_simple.tests
new file mode 100644
index 0000000..b8a28d1
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub3_1_simple.tests
@@ -0,0 +1 @@
+#../fonts/gsub3_1_simple_f1.otf:--features="-test[1],test[3],test[5]=2,test[7]=3,-test[9],test[11]" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0011,U+0012,U+0011,U+0012,U+0011,U+0012,U+0011,U+0012,U+0011,U+0012,U+0011:[17|18|17|20|17|21|17|22|17|18|17|20|17]
diff --git a/test/shaping/data/aots/tests/gsub4_1_lookupflag.tests b/test/shaping/data/aots/tests/gsub4_1_lookupflag.tests
new file mode 100644
index 0000000..c2c5242
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub4_1_lookupflag.tests
@@ -0,0 +1 @@
+../fonts/gsub4_1_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0018,U+0012,U+0018,U+0013,U+0018,U+0018,U+0014,U+0018,U+0011,U+0012,U+0013,U+0016,U+0014:[17|24|23|24|24|24|24|17|18|19|22|20]
diff --git a/test/shaping/data/aots/tests/gsub4_1_multiple_ligatures.tests b/test/shaping/data/aots/tests/gsub4_1_multiple_ligatures.tests
new file mode 100644
index 0000000..33c1a09
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub4_1_multiple_ligatures.tests
@@ -0,0 +1,2 @@
+../fonts/gsub4_1_multiple_ligatures_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0011,U+0012,U+0013,U+0016,U+0014:[17|23|17|24|22|20]
+../fonts/gsub4_1_multiple_ligatures_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0011,U+0012,U+0013,U+0016,U+0014:[17|24|20|17|24|22|20]
diff --git a/test/shaping/data/aots/tests/gsub4_1_multiple_ligsets.tests b/test/shaping/data/aots/tests/gsub4_1_multiple_ligsets.tests
new file mode 100644
index 0000000..a63aeed
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub4_1_multiple_ligsets.tests
@@ -0,0 +1 @@
+../fonts/gsub4_1_multiple_ligsets_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0015,U+0014,U+0013,U+0016:[17|23|21|24|22]
diff --git a/test/shaping/data/aots/tests/gsub4_1_simple.tests b/test/shaping/data/aots/tests/gsub4_1_simple.tests
new file mode 100644
index 0000000..aa4bb4b
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub4_1_simple.tests
@@ -0,0 +1 @@
+../fonts/gsub4_1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0011,U+0012,U+0013,U+0016,U+0014:[17|23|17|18|19|22|20]
diff --git a/test/shaping/data/aots/tests/gsub7.tests b/test/shaping/data/aots/tests/gsub7.tests
new file mode 100644
index 0000000..e95b1c7
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub7.tests
@@ -0,0 +1,2 @@
+../fonts/gsub7_font1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|23|24|20|21]
+../fonts/gsub7_font2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|23|29|20|21]
diff --git a/test/shaping/data/aots/tests/gsub_chaining1_boundary.tests b/test/shaping/data/aots/tests/gsub_chaining1_boundary.tests
new file mode 100644
index 0000000..6d99d97
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining1_boundary.tests
@@ -0,0 +1,4 @@
+../fonts/gsub_chaining1_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|21|22|23|0]
+../fonts/gsub_chaining1_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|22|23|0]
+../fonts/gsub_chaining1_boundary_f3.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|22|23|0]
+../fonts/gsub_chaining1_boundary_f4.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|21|62|23|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining1_lookupflag.tests b/test/shaping/data/aots/tests/gsub_chaining1_lookupflag.tests
new file mode 100644
index 0000000..7883c0a
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining1_lookupflag.tests
@@ -0,0 +1 @@
+../fonts/gsub_chaining1_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+005A,U+0015,U+005B,U+0016,U+005C,U+0017,U+005D,U+005E,U+0018,U+005A,U+0019,U+005B,U+001A,U+0000:[0|20|90|21|91|22|92|63|93|94|24|90|25|91|26|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining1_multiple_subrules.tests b/test/shaping/data/aots/tests/gsub_chaining1_multiple_subrules.tests
new file mode 100644
index 0000000..28a5225
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining1_multiple_subrules.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_chaining1_multiple_subrules_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|22|23|24|0|20|21|62|23|0]
+../fonts/gsub_chaining1_multiple_subrules_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|21|62|23|24|0|20|21|62|23|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining1_next_glyph.tests b/test/shaping/data/aots/tests/gsub_chaining1_next_glyph.tests
new file mode 100644
index 0000000..82f9d95
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining1_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gsub_chaining1_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|62|63|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining1_simple.tests b/test/shaping/data/aots/tests/gsub_chaining1_simple.tests
new file mode 100644
index 0000000..23e091f
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining1_simple.tests
@@ -0,0 +1,11 @@
+../fonts/gsub_chaining1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|62|23|0]
+../fonts/gsub_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|20|21|62|23|24|25|26|0]
+../fonts/gsub_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+0000,U+0000:[0|20|21|22|23|24|25|0|0]
+../fonts/gsub_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019:[0|20|21|22|23|24|25]
+../fonts/gsub_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018:[0|20|21|22|23|24]
+../fonts/gsub_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0000,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|0|21|22|23|24|25|26|0]
+../fonts/gsub_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[21|22|23|24|25|26|0]
+../fonts/gsub_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[22|23|24|25|26|0]
+../fonts/gsub_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000,U+0018,U+0019,U+001A,U+0000:[0|20|21|22|0|24|25|26|0]
+../fonts/gsub_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017:[0|20|21|22|23]
+../fonts/gsub_chaining1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016:[0|20|21|22]
diff --git a/test/shaping/data/aots/tests/gsub_chaining1_successive.tests b/test/shaping/data/aots/tests/gsub_chaining1_successive.tests
new file mode 100644
index 0000000..ab3cfb1
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining1_successive.tests
@@ -0,0 +1 @@
+../fonts/gsub_chaining1_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0019,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000:[0|25|20|61|63|24|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining2_boundary.tests b/test/shaping/data/aots/tests/gsub_chaining2_boundary.tests
new file mode 100644
index 0000000..b06c620
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining2_boundary.tests
@@ -0,0 +1,4 @@
+../fonts/gsub_chaining2_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|21|22|23|0]
+../fonts/gsub_chaining2_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|22|23|0]
+../fonts/gsub_chaining2_boundary_f3.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|22|23|0]
+../fonts/gsub_chaining2_boundary_f4.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|21|62|23|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining2_lookupflag.tests b/test/shaping/data/aots/tests/gsub_chaining2_lookupflag.tests
new file mode 100644
index 0000000..372b343
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining2_lookupflag.tests
@@ -0,0 +1 @@
+../fonts/gsub_chaining2_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+005A,U+0015,U+005B,U+0016,U+005C,U+0017,U+005D,U+005E,U+0018,U+005A,U+0019,U+005B,U+001A,U+0000:[0|20|90|21|91|22|92|63|93|94|24|90|25|91|26|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining2_multiple_subrules.tests b/test/shaping/data/aots/tests/gsub_chaining2_multiple_subrules.tests
new file mode 100644
index 0000000..e2fbb5c
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining2_multiple_subrules.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_chaining2_multiple_subrules_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|22|23|24|0|20|21|62|23|0]
+../fonts/gsub_chaining2_multiple_subrules_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|21|62|23|24|0|20|21|62|23|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining2_next_glyph.tests b/test/shaping/data/aots/tests/gsub_chaining2_next_glyph.tests
new file mode 100644
index 0000000..84c8252
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining2_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gsub_chaining2_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|62|63|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining2_simple.tests b/test/shaping/data/aots/tests/gsub_chaining2_simple.tests
new file mode 100644
index 0000000..53fa7e8
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining2_simple.tests
@@ -0,0 +1,11 @@
+../fonts/gsub_chaining2_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|62|23|0]
+../fonts/gsub_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|20|21|62|23|24|25|26|0]
+../fonts/gsub_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+0000,U+0000:[0|20|21|22|23|24|25|0|0]
+../fonts/gsub_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019:[0|20|21|22|23|24|25]
+../fonts/gsub_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018:[0|20|21|22|23|24]
+../fonts/gsub_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0000,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|0|21|22|23|24|25|26|0]
+../fonts/gsub_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[21|22|23|24|25|26|0]
+../fonts/gsub_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[22|23|24|25|26|0]
+../fonts/gsub_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000,U+0018,U+0019,U+001A,U+0000:[0|20|21|22|0|24|25|26|0]
+../fonts/gsub_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017:[0|20|21|22|23]
+../fonts/gsub_chaining2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016:[0|20|21|22]
diff --git a/test/shaping/data/aots/tests/gsub_chaining2_successive.tests b/test/shaping/data/aots/tests/gsub_chaining2_successive.tests
new file mode 100644
index 0000000..71cbe0d
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining2_successive.tests
@@ -0,0 +1 @@
+../fonts/gsub_chaining2_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0019,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000:[0|25|20|61|63|24|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining3_boundary.tests b/test/shaping/data/aots/tests/gsub_chaining3_boundary.tests
new file mode 100644
index 0000000..c01dc4b
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining3_boundary.tests
@@ -0,0 +1,4 @@
+../fonts/gsub_chaining3_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|21|22|23|0]
+../fonts/gsub_chaining3_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|22|23|0]
+../fonts/gsub_chaining3_boundary_f3.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|22|23|0]
+../fonts/gsub_chaining3_boundary_f4.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|21|62|23|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining3_lookupflag.tests b/test/shaping/data/aots/tests/gsub_chaining3_lookupflag.tests
new file mode 100644
index 0000000..be2147b
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining3_lookupflag.tests
@@ -0,0 +1 @@
+../fonts/gsub_chaining3_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+005A,U+0015,U+005B,U+0016,U+005C,U+0017,U+005D,U+005E,U+0018,U+005A,U+0019,U+005B,U+001A,U+0000:[0|20|90|21|91|22|92|63|93|94|24|90|25|91|26|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining3_next_glyph.tests b/test/shaping/data/aots/tests/gsub_chaining3_next_glyph.tests
new file mode 100644
index 0000000..2493c1e
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining3_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gsub_chaining3_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0016,U+0015,U+0016,U+0015,U+0016,U+0015,U+0000:[0|22|61|22|61|22|21|0]
diff --git a/test/shaping/data/aots/tests/gsub_chaining3_simple.tests b/test/shaping/data/aots/tests/gsub_chaining3_simple.tests
new file mode 100644
index 0000000..eb24167
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining3_simple.tests
@@ -0,0 +1,11 @@
+../fonts/gsub_chaining3_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|62|23|0]
+../fonts/gsub_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|20|21|62|23|24|25|26|0]
+../fonts/gsub_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019,U+0000,U+0000:[0|20|21|22|23|24|25|0|0]
+../fonts/gsub_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018,U+0019:[0|20|21|22|23|24|25]
+../fonts/gsub_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0018:[0|20|21|22|23|24]
+../fonts/gsub_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0000,U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[0|0|21|22|23|24|25|26|0]
+../fonts/gsub_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0015,U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[21|22|23|24|25|26|0]
+../fonts/gsub_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0016,U+0017,U+0018,U+0019,U+001A,U+0000:[22|23|24|25|26|0]
+../fonts/gsub_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000,U+0018,U+0019,U+001A,U+0000:[0|20|21|22|0|24|25|26|0]
+../fonts/gsub_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017:[0|20|21|22|23]
+../fonts/gsub_chaining3_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016:[0|20|21|22]
diff --git a/test/shaping/data/aots/tests/gsub_chaining3_successive.tests b/test/shaping/data/aots/tests/gsub_chaining3_successive.tests
new file mode 100644
index 0000000..edcade1
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_chaining3_successive.tests
@@ -0,0 +1 @@
+../fonts/gsub_chaining3_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0019,U+0014,U+0015,U+0016,U+0017,U+0018,U+0000:[0|25|20|61|63|24|0]
diff --git a/test/shaping/data/aots/tests/gsub_context1_boundary.tests b/test/shaping/data/aots/tests/gsub_context1_boundary.tests
new file mode 100644
index 0000000..9b11892
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context1_boundary.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_context1_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20|20|20|20|20|0]
+../fonts/gsub_context1_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|60|60|60|60|60|0]
diff --git a/test/shaping/data/aots/tests/gsub_context1_expansion.tests b/test/shaping/data/aots/tests/gsub_context1_expansion.tests
new file mode 100644
index 0000000..92714c5
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context1_expansion.tests
@@ -0,0 +1 @@
+../fonts/gsub_context1_expansion_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000:[0|20|61|62|63|22|0]
diff --git a/test/shaping/data/aots/tests/gsub_context1_lookupflag.tests b/test/shaping/data/aots/tests/gsub_context1_lookupflag.tests
new file mode 100644
index 0000000..c5d9760
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context1_lookupflag.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_context1_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|60|90|61|91|92|62|0]
+../fonts/gsub_context1_lookupflag_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|20|90|61|91|92|0]
diff --git a/test/shaping/data/aots/tests/gsub_context1_multiple_subrules.tests b/test/shaping/data/aots/tests/gsub_context1_multiple_subrules.tests
new file mode 100644
index 0000000..febc419
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context1_multiple_subrules.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_context1_multiple_subrules_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000,U+0014,U+0015,U+0000:[0|60|21|22|0|20|61|0]
+../fonts/gsub_context1_multiple_subrules_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000,U+0014,U+0015,U+0000:[0|20|61|22|0|20|61|0]
diff --git a/test/shaping/data/aots/tests/gsub_context1_next_glyph.tests b/test/shaping/data/aots/tests/gsub_context1_next_glyph.tests
new file mode 100644
index 0000000..12414c3
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context1_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gsub_context1_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|60|20|60|20|20|0]
diff --git a/test/shaping/data/aots/tests/gsub_context1_simple.tests b/test/shaping/data/aots/tests/gsub_context1_simple.tests
new file mode 100644
index 0000000..44252ec
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context1_simple.tests
@@ -0,0 +1,3 @@
+../fonts/gsub_context1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000:[0|60|61|62|0]
+../fonts/gsub_context1_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0000,U+0014,U+0015,U+0000:[0|20|0|20|21|0]
+../fonts/gsub_context1_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20|60|20|20|20|0]
diff --git a/test/shaping/data/aots/tests/gsub_context1_successive.tests b/test/shaping/data/aots/tests/gsub_context1_successive.tests
new file mode 100644
index 0000000..e68d6b2
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context1_successive.tests
@@ -0,0 +1 @@
+../fonts/gsub_context1_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|63|0]
diff --git a/test/shaping/data/aots/tests/gsub_context2_boundary.tests b/test/shaping/data/aots/tests/gsub_context2_boundary.tests
new file mode 100644
index 0000000..2054277
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context2_boundary.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_context2_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20|20|20|20|20|0]
+../fonts/gsub_context2_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|60|60|60|60|60|0]
diff --git a/test/shaping/data/aots/tests/gsub_context2_classes.tests b/test/shaping/data/aots/tests/gsub_context2_classes.tests
new file mode 100644
index 0000000..2e44007
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context2_classes.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_context2_classes_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+001A,U+001C,U+0018,U+0000,U+0015,U+001B,U+001A,U+0018,U+0000,U+0016,U+001B,U+001A,U+0018:[0|20|66|28|24|0|21|67|26|24|0|22|27|26|24]
+../fonts/gsub_context2_classes_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0016,U+001B,U+001A,U+0018,U+0000,U+0018,U+0018,U+001D,U+0016,U+0000,U+0016,U+001B,U+001A,U+0018:[0|22|67|26|24|0|24|24|69|22|0|22|67|26|24]
diff --git a/test/shaping/data/aots/tests/gsub_context2_expansion.tests b/test/shaping/data/aots/tests/gsub_context2_expansion.tests
new file mode 100644
index 0000000..af0ce71
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context2_expansion.tests
@@ -0,0 +1 @@
+../fonts/gsub_context2_expansion_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000:[0|20|61|62|63|22|0]
diff --git a/test/shaping/data/aots/tests/gsub_context2_lookupflag.tests b/test/shaping/data/aots/tests/gsub_context2_lookupflag.tests
new file mode 100644
index 0000000..ac41949
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context2_lookupflag.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_context2_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|60|90|61|91|92|62|0]
+../fonts/gsub_context2_lookupflag_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|20|90|61|91|92|0]
diff --git a/test/shaping/data/aots/tests/gsub_context2_multiple_subrules.tests b/test/shaping/data/aots/tests/gsub_context2_multiple_subrules.tests
new file mode 100644
index 0000000..75225cd
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context2_multiple_subrules.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_context2_multiple_subrules_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000,U+0014,U+0015,U+0000:[0|60|21|22|0|20|61|0]
+../fonts/gsub_context2_multiple_subrules_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000,U+0014,U+0015,U+0000:[0|20|61|22|0|20|61|0]
diff --git a/test/shaping/data/aots/tests/gsub_context2_next_glyph.tests b/test/shaping/data/aots/tests/gsub_context2_next_glyph.tests
new file mode 100644
index 0000000..020d05f
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context2_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gsub_context2_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|60|20|60|20|20|0]
diff --git a/test/shaping/data/aots/tests/gsub_context2_simple.tests b/test/shaping/data/aots/tests/gsub_context2_simple.tests
new file mode 100644
index 0000000..5863605
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context2_simple.tests
@@ -0,0 +1,3 @@
+../fonts/gsub_context2_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000:[0|60|61|62|0]
+../fonts/gsub_context2_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0000,U+0014,U+0015,U+0000:[0|20|0|20|21|0]
+../fonts/gsub_context2_simple_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20|60|20|20|20|0]
diff --git a/test/shaping/data/aots/tests/gsub_context2_successive.tests b/test/shaping/data/aots/tests/gsub_context2_successive.tests
new file mode 100644
index 0000000..9aeeac7
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context2_successive.tests
@@ -0,0 +1 @@
+../fonts/gsub_context2_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|63|0]
diff --git a/test/shaping/data/aots/tests/gsub_context3_boundary.tests b/test/shaping/data/aots/tests/gsub_context3_boundary.tests
new file mode 100644
index 0000000..8b40afd
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context3_boundary.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_context3_boundary_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|20|20|20|20|20|0]
+../fonts/gsub_context3_boundary_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|60|60|60|60|60|0]
diff --git a/test/shaping/data/aots/tests/gsub_context3_lookupflag.tests b/test/shaping/data/aots/tests/gsub_context3_lookupflag.tests
new file mode 100644
index 0000000..03c0647
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context3_lookupflag.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_context3_lookupflag_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|60|90|61|91|92|62|0]
+../fonts/gsub_context3_lookupflag_f2.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+005A,U+0015,U+005B,U+005C,U+0016,U+0000:[0|20|90|61|91|92|0]
diff --git a/test/shaping/data/aots/tests/gsub_context3_next_glyph.tests b/test/shaping/data/aots/tests/gsub_context3_next_glyph.tests
new file mode 100644
index 0000000..b28381b
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context3_next_glyph.tests
@@ -0,0 +1 @@
+../fonts/gsub_context3_next_glyph_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0014,U+0014,U+0014,U+0014,U+0000:[0|60|20|60|20|20|0]
diff --git a/test/shaping/data/aots/tests/gsub_context3_simple.tests b/test/shaping/data/aots/tests/gsub_context3_simple.tests
new file mode 100644
index 0000000..ec264ea
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context3_simple.tests
@@ -0,0 +1,2 @@
+../fonts/gsub_context3_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0000:[0|60|61|62|0]
+../fonts/gsub_context3_simple_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0000,U+0014,U+0015,U+0000,U+0014,U+0015,U+0016,U+0000:[0|20|0|20|21|0|60|61|62|0]
diff --git a/test/shaping/data/aots/tests/gsub_context3_successive.tests b/test/shaping/data/aots/tests/gsub_context3_successive.tests
new file mode 100644
index 0000000..b987a61
--- /dev/null
+++ b/test/shaping/data/aots/tests/gsub_context3_successive.tests
@@ -0,0 +1 @@
+../fonts/gsub_context3_successive_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0000,U+0014,U+0015,U+0016,U+0017,U+0000:[0|20|61|63|0]
diff --git a/test/shaping/data/aots/tests/lookupflag_ignore_attach.tests b/test/shaping/data/aots/tests/lookupflag_ignore_attach.tests
new file mode 100644
index 0000000..55ae538
--- /dev/null
+++ b/test/shaping/data/aots/tests/lookupflag_ignore_attach.tests
@@ -0,0 +1,5 @@
+#../fonts/lookupflag_ignore_attach_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+000A,U+000B,U+000D,U+001A,U+000A:[10|15|10]
+#../fonts/lookupflag_ignore_attach_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+000A,U+000B,U+0015,U+000D,U+0016,U+0017,U+001D,U+001A,U+000A:[10|15|21|22|23|29|10]
+#../fonts/lookupflag_ignore_attach_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+000A,U+000B,U+0015,U+000D,U+0016,U+001B,U+001A,U+000A:[10|11|21|13|22|27|26|10]
+#../fonts/lookupflag_ignore_attach_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+000A,U+000B,U+001B,U+000D,U+0016,U+0017,U+001A,U+000A:[10|11|27|13|22|23|26|10]
+#../fonts/lookupflag_ignore_attach_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+000A,U+000B,U+001B,U+000D,U+000E,U+0017,U+001A,U+000A:[10|11|27|13|14|23|26|10]
diff --git a/test/shaping/data/aots/tests/lookupflag_ignore_base.tests b/test/shaping/data/aots/tests/lookupflag_ignore_base.tests
new file mode 100644
index 0000000..5f0bfdb
--- /dev/null
+++ b/test/shaping/data/aots/tests/lookupflag_ignore_base.tests
@@ -0,0 +1,2 @@
+../fonts/lookupflag_ignore_base_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0013,U+0014,U+0015:[17|23|21]
+../fonts/lookupflag_ignore_base_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+0018,U+0018,U+0013,U+0019,U+0014,U+0015:[17|23|24|24|25|21]
diff --git a/test/shaping/data/aots/tests/lookupflag_ignore_combination.tests b/test/shaping/data/aots/tests/lookupflag_ignore_combination.tests
new file mode 100644
index 0000000..d34f16a
--- /dev/null
+++ b/test/shaping/data/aots/tests/lookupflag_ignore_combination.tests
@@ -0,0 +1,3 @@
+../fonts/lookupflag_ignore_combination_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+001A,U+0013,U+0014,U+0015:[17|23|26|21]
+../fonts/lookupflag_ignore_combination_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+001A,U+0013,U+0018,U+001E,U+001F,U+0014,U+0015:[17|23|26|24|30|31|21]
+../fonts/lookupflag_ignore_combination_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+001A,U+0013,U+0018,U+001E,U+0020,U+0014,U+0015:[17|18|26|19|24|30|32|20|21]
diff --git a/test/shaping/data/aots/tests/lookupflag_ignore_ligatures.tests b/test/shaping/data/aots/tests/lookupflag_ignore_ligatures.tests
new file mode 100644
index 0000000..feb31d8
--- /dev/null
+++ b/test/shaping/data/aots/tests/lookupflag_ignore_ligatures.tests
@@ -0,0 +1,3 @@
+../fonts/lookupflag_ignore_ligatures_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+001A,U+001B,U+0013,U+001B,U+0014,U+0015:[17|23|26|27|27|21]
+../fonts/lookupflag_ignore_ligatures_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+001A,U+0018,U+0013,U+001B,U+0014,U+0015:[17|18|26|24|19|27|20|21]
+../fonts/lookupflag_ignore_ligatures_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+001A,U+002A,U+0013,U+001B,U+0014,U+0015:[17|18|26|42|19|27|20|21]
diff --git a/test/shaping/data/aots/tests/lookupflag_ignore_marks.tests b/test/shaping/data/aots/tests/lookupflag_ignore_marks.tests
new file mode 100644
index 0000000..9626599
--- /dev/null
+++ b/test/shaping/data/aots/tests/lookupflag_ignore_marks.tests
@@ -0,0 +1 @@
+../fonts/lookupflag_ignore_marks_f1.otf:--features="test" --no-clusters --no-glyph-names --no-positions:U+0011,U+0012,U+001C,U+001D,U+0013,U+001D,U+0014,U+0015:[17|23|28|29|29|21]
diff --git a/test/shaping/data/in-house/Makefile.sources b/test/shaping/data/in-house/Makefile.sources
index 6e21ddd..0e9a3a2 100644
--- a/test/shaping/data/in-house/Makefile.sources
+++ b/test/shaping/data/in-house/Makefile.sources
@@ -1,5 +1,6 @@
 TESTS = \
 	tests/aat-trak.tests \
+	tests/aat-morx.tests \
 	tests/arabic-fallback-shaping.tests \
 	tests/arabic-feature-order.tests \
 	tests/arabic-like-joining.tests \
@@ -28,10 +29,12 @@
 	tests/indic-special-cases.tests \
 	tests/indic-syllable.tests \
 	tests/indic-vowel-letter-spoofing.tests \
+	tests/kern-format2.tests \
 	tests/khmer-mark-order.tests \
 	tests/khmer-misc.tests \
 	tests/language-tags.tests \
 	tests/ligature-id.tests \
+	tests/macos.tests \
 	tests/mark-attachment.tests \
 	tests/mark-filtering-sets.tests \
 	tests/mongolian-variation-selector.tests \
diff --git a/test/shaping/data/in-house/fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf b/test/shaping/data/in-house/fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf
new file mode 100644
index 0000000..7d488a3
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/4afb0e8b9a86bb9bd73a1247de4e33fbe3c1fd93.ttf b/test/shaping/data/in-house/fonts/4afb0e8b9a86bb9bd73a1247de4e33fbe3c1fd93.ttf
new file mode 100644
index 0000000..274fc08
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/4afb0e8b9a86bb9bd73a1247de4e33fbe3c1fd93.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/4cbbc461be066fccc611dcc634af6e8cb2705537.ttf b/test/shaping/data/in-house/fonts/4cbbc461be066fccc611dcc634af6e8cb2705537.ttf
new file mode 100644
index 0000000..03166b0
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/4cbbc461be066fccc611dcc634af6e8cb2705537.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/86cdd983c4e4c4d7f27dd405d6ceb7d4b9ed3d35.ttf b/test/shaping/data/in-house/fonts/86cdd983c4e4c4d7f27dd405d6ceb7d4b9ed3d35.ttf
new file mode 100644
index 0000000..5a47a39
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/86cdd983c4e4c4d7f27dd405d6ceb7d4b9ed3d35.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/TestDFONT.dfont b/test/shaping/data/in-house/fonts/DFONT.dfont
similarity index 100%
rename from test/shaping/data/in-house/fonts/TestDFONT.dfont
rename to test/shaping/data/in-house/fonts/DFONT.dfont
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/MORXTwentyeight.ttf b/test/shaping/data/in-house/fonts/MORXTwentyeight.ttf
new file mode 100644
index 0000000..edabb43
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/MORXTwentyeight.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/TestTRAK.ttf b/test/shaping/data/in-house/fonts/TRAK.ttf
similarity index 100%
rename from test/shaping/data/in-house/fonts/TestTRAK.ttf
rename to test/shaping/data/in-house/fonts/TRAK.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/TestTTC.ttc b/test/shaping/data/in-house/fonts/TTC.ttc
similarity index 100%
rename from test/shaping/data/in-house/fonts/TestTTC.ttc
rename to test/shaping/data/in-house/fonts/TTC.ttc
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/dcf774ca21062e7439f98658b18974ea8b956d0c.ttf b/test/shaping/data/in-house/fonts/dcf774ca21062e7439f98658b18974ea8b956d0c.ttf
new file mode 100644
index 0000000..4d3e11d
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/dcf774ca21062e7439f98658b18974ea8b956d0c.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/e39391c77a6321c2ac7a2d644de0396470cd4bfe.ttf b/test/shaping/data/in-house/fonts/e39391c77a6321c2ac7a2d644de0396470cd4bfe.ttf
new file mode 100644
index 0000000..b1605c4
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/e39391c77a6321c2ac7a2d644de0396470cd4bfe.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/tests/aat-morx.tests b/test/shaping/data/in-house/tests/aat-morx.tests
new file mode 100644
index 0000000..27f5bcc
--- /dev/null
+++ b/test/shaping/data/in-house/tests/aat-morx.tests
@@ -0,0 +1 @@
+../fonts/MORXTwentyeight.ttf::U+0041,U+0078,U+0045,U+0079,U+0044,U+0079,U+0079:[A_E_D=0+1394|x=0+529|y=0+510|y=5+510|y=6+510]
diff --git a/test/shaping/data/in-house/tests/aat-trak.tests b/test/shaping/data/in-house/tests/aat-trak.tests
index 48b224f..4bbe729 100644
--- a/test/shaping/data/in-house/tests/aat-trak.tests
+++ b/test/shaping/data/in-house/tests/aat-trak.tests
@@ -1,8 +1,11 @@
-../fonts/TestTRAK.ttf::U+0041,U+0042,U+0043:[A.alt=0+1000|B=1+1000|C.alt=2+1000]
-../fonts/TestTRAK.ttf:--font-ptem=.5:U+0041,U+0042,U+0043:[A.alt=0@100,0+1200|B=1@100,0+1200|C.alt=2@100,0+1200]
-../fonts/TestTRAK.ttf:--font-ptem=1:U+0041,U+0042,U+0043:[A.alt=0@100,0+1200|B=1@100,0+1200|C.alt=2@100,0+1200]
-../fonts/TestTRAK.ttf:--font-ptem=2:U+0041,U+0042,U+0043:[A.alt=0@93,0+1187|B=1@93,0+1187|C.alt=2@93,0+1187]
-../fonts/TestTRAK.ttf:--font-ptem=9:U+0041,U+0042,U+0043:[A.alt=0+1000|B=1+1000|C.alt=2+1000]
-../fonts/TestTRAK.ttf:--font-ptem=24:U+0041,U+0042,U+0043:[A.alt=0@-12,0+976|B=1@-12,0+976|C.alt=2@-12,0+976]
-../fonts/TestTRAK.ttf:--font-ptem=72:U+0041,U+0042,U+0043:[A.alt=0@-50,0+900|B=1@-50,0+900|C.alt=2@-50,0+900]
-../fonts/TestTRAK.ttf:--font-ptem=144:U+0041,U+0042,U+0043:[A.alt=0@-107,0+786|B=1@-107,0+786|C.alt=2@-107,0+786]
+../fonts/TRAK.ttf::U+0041,U+0042,U+0043:[A.alt=0+1000|B=1+1000|C.alt=2+1000]
+../fonts/TRAK.ttf:--font-ptem=.5:U+0041,U+0042,U+0043:[A.alt=0@100,0+1200|B=1@100,0+1200|C.alt=2@100,0+1200]
+../fonts/TRAK.ttf:--font-ptem=1:U+0041,U+0042,U+0043:[A.alt=0@100,0+1200|B=1@100,0+1200|C.alt=2@100,0+1200]
+../fonts/TRAK.ttf:--font-ptem=2:U+0041,U+0042,U+0043:[A.alt=0@93,0+1187|B=1@93,0+1187|C.alt=2@93,0+1187]
+../fonts/TRAK.ttf:--font-ptem=9:U+0041,U+0042,U+0043:[A.alt=0+1000|B=1+1000|C.alt=2+1000]
+../fonts/TRAK.ttf:--font-ptem=24:U+0041,U+0042,U+0043:[A.alt=0@-12,0+976|B=1@-12,0+976|C.alt=2@-12,0+976]
+../fonts/TRAK.ttf:--font-ptem=72:U+0041,U+0042,U+0043:[A.alt=0@-50,0+900|B=1@-50,0+900|C.alt=2@-50,0+900]
+../fonts/TRAK.ttf:--font-ptem=144:U+0041,U+0042,U+0043:[A.alt=0@-107,0+786|B=1@-107,0+786|C.alt=2@-107,0+786]
+../fonts/TRAK.ttf:--font-ptem=144:U+0041,U+0042,U+0043:[A.alt=0@-107,0+786|B=1@-107,0+786|C.alt=2@-107,0+786]
+../fonts/TRAK.ttf:--font-ptem=144 --features=-trak:U+0041,U+0042,U+0043:[A.alt=0+1000|B=1+1000|C.alt=2+1000]
+../fonts/TRAK.ttf:--font-ptem=144 --features=-trak[1;3]:U+0041,U+0042,U+0043,U+0041,U+0042,U+0043:[A.alt=0@-107,0+786|B=1+1000|C.alt=2+1000|A.alt=3@-107,0+786|B=4@-107,0+786|C.alt=5@-107,0+786]
diff --git a/test/shaping/data/in-house/tests/collections.tests b/test/shaping/data/in-house/tests/collections.tests
index 85653c5..07dac9d 100644
--- a/test/shaping/data/in-house/tests/collections.tests
+++ b/test/shaping/data/in-house/tests/collections.tests
@@ -1,6 +1,6 @@
-../fonts/TestDFONT.dfont:--face-index=0 --font-funcs=ot:U+2026,U+0020,U+002E:[ellipsis=0+723|space=1+250|period=2+241]
-../fonts/TestDFONT.dfont:--face-index=1 --font-funcs=ot:U+2026,U+0020,U+002E:[gid0=0+1000|gid0=1+1000|gid0=2+1000]
-../fonts/TestDFONT.dfont:--face-index=2 --font-funcs=ot:U+2026,U+0020,U+002E:[gid0=0+1000|gid0=1+1000|gid0=2+1000]
-../fonts/TestTTC.ttc:--face-index=0 --font-funcs=ot:U+2026,U+0020,U+002E:[ellipsis=0+723|space=1+250|period=2+241]
-../fonts/TestTTC.ttc:--face-index=1 --font-funcs=ot:U+2026,U+0020,U+002E:[ellipsis=0+723|space=1+250|period=2+241]
-../fonts/TestTTC.ttc:--face-index=2 --font-funcs=ot:U+2026,U+0020,U+002E:[gid0=0+1000|gid0=1+1000|gid0=2+1000]
+../fonts/DFONT.dfont:--face-index=0 --font-funcs=ot:U+2026,U+0020,U+002E:[ellipsis=0+723|space=1+250|period=2+241]
+../fonts/DFONT.dfont:--face-index=1 --font-funcs=ot:U+2026,U+0020,U+002E:[gid0=0+1000|gid0=1+1000|gid0=2+1000]
+../fonts/DFONT.dfont:--face-index=2 --font-funcs=ot:U+2026,U+0020,U+002E:[gid0=0+1000|gid0=1+1000|gid0=2+1000]
+../fonts/TTC.ttc:--face-index=0 --font-funcs=ot:U+2026,U+0020,U+002E:[ellipsis=0+723|space=1+250|period=2+241]
+../fonts/TTC.ttc:--face-index=1 --font-funcs=ot:U+2026,U+0020,U+002E:[ellipsis=0+723|space=1+250|period=2+241]
+../fonts/TTC.ttc:--face-index=2 --font-funcs=ot:U+2026,U+0020,U+002E:[gid0=0+1000|gid0=1+1000|gid0=2+1000]
diff --git a/test/shaping/data/in-house/tests/color-fonts.tests b/test/shaping/data/in-house/tests/color-fonts.tests
index e7311bc..b325d78 100644
--- a/test/shaping/data/in-house/tests/color-fonts.tests
+++ b/test/shaping/data/in-house/tests/color-fonts.tests
@@ -1 +1 @@
-../fonts/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf:--font-funcs=ot --show-extents:U+1F42F:[gid1=0+2963<0,2178,2963,-2788>]
+../fonts/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf:--font-funcs=ot --show-extents:U+1F42F:[gid1=0+2963<0,2179,2963,-2789>]
diff --git a/test/shaping/data/in-house/tests/kern-format2.tests b/test/shaping/data/in-house/tests/kern-format2.tests
new file mode 100644
index 0000000..f7cd840
--- /dev/null
+++ b/test/shaping/data/in-house/tests/kern-format2.tests
@@ -0,0 +1,3 @@
+../fonts/e39391c77a6321c2ac7a2d644de0396470cd4bfe.ttf::U+0061,U+0062,U+0063,U+0064,U+0065,U+0066,U+0067,U+0068,U+0069,U+006A,U+006B,U+006C,U+006D,U+006E,U+006F,U+0070:[a=0+626|b=1+672|c=2+564|d=3@-15,0+657|e=4+621|f=5+403|g=6@-10,0+662|h=7+666|i=8+316|j=9+316|k=10+591|l=11+316|m=12+1021|n=13+666|o=14+644|p=15+672]
+../fonts/e39391c77a6321c2ac7a2d644de0396470cd4bfe.ttf::U+0063,U+006B,U+0063,U+006B,U+0063,U+006B:[c=0+579|k=1+591|c=2+579|k=3+591|c=4+579|k=5+591]
+../fonts/e39391c77a6321c2ac7a2d644de0396470cd4bfe.ttf::U+0041,U+0056:[A=0+701|V=1@-40,0+703]
diff --git a/test/shaping/data/in-house/tests/macos.tests b/test/shaping/data/in-house/tests/macos.tests
new file mode 100644
index 0000000..859992c
--- /dev/null
+++ b/test/shaping/data/in-house/tests/macos.tests
@@ -0,0 +1,35 @@
+# 10.12.6 https://gist.github.com/ebraminio/1704341fa16b06979e605aafd88198cf
+/System/Library/Fonts/Helvetica.dfont@c7bec2785a4c402b7809b5e35337c3d24c18e281:--font-funcs ot:U+006D,U+0300:[m=0+1706|gravecmb=0@-284,10+0]
+/System/Library/Fonts/LucidaGrande.ttc@d89a9d7e57767bfe3b5a4cfd22bb1e9dbe03a062:--font-funcs ot:U+006D,U+0300:[mgrave=0+1912]
+/System/Library/Fonts/Times.dfont@39c954614d3f3317b28564db06d5b7b7a6ff0e39:--font-funcs ot:U+0066,U+0069:[fi=0+1139]
+/Library/Fonts/Khmer MN.ttc@5f5b1072df99b7355d3066ea85fe82969d13c94a:--font-funcs ot:U+17A2,U+1780,U+17D2,U+179F,U+179A,U+1781,U+17D2,U+1798,U+17C2,U+179A:[km_qa=0+1025|km_ka=1+1025|km_sa.sub=1+517|km_ro=4+593|km_vs_ae=5+605|km_kha=5+1025|km_mo.sub=5+0|km_ro=9+593]
+/Library/Fonts/Tamil MN.ttc@37a2020c3f86ebcc45e02c1de5fdf81e2676989d:--font-funcs ot:U+0BA4,U+0BCA,U+0B95,U+0BC1,U+0B95,U+0BCD,U+0B95,U+0BAA,U+0BCD,U+0BAA,U+0B9F,U+0BCD,U+0B9F,U+0BC1:[tgm_e=0+1702|tgc_ta=0+1598|tgm_aa=0+1149|tgc_ku=2+1962|tgc_k=4+1592|tgc_ka=6+1592|tgc_p=7+1370|tgc_pa=9+1370|tgc_tt=10+1596|tgc_ttu=12+1833]
+/System/Library/Fonts/Times.dfont@39c954614d3f3317b28564db06d5b7b7a6ff0e39:--font-funcs ot:U+0041,U+0066,U+0300,U+0066,U+0069,U+005A:[A=0+1479|f=1+682|gravecmb=1@-480,588+0|fi=3+1139|Z=5+1251]
+/System/Library/Fonts/LucidaGrande.ttc@d89a9d7e57767bfe3b5a4cfd22bb1e9dbe03a062:--font-funcs ot:U+05E1,U+05B0:[shevahebrew=0@-7,0+0|samekhhebrew=0+1361]
+/Library/Fonts/Apple Chancery.ttf@5fc49ae9bce39e2105864323183b68ea34c9e562:--font-funcs ot:U+0054,U+0068,U+0020,U+0074,U+0068,U+0020,U+006C,U+006C,U+0020,U+0074,U+0065,U+0020,U+0074,U+006F,U+0020,U+0074,U+0072,U+0020,U+0066,U+0072,U+0020,U+0066,U+0075,U+0020,U+0066,U+006A:[T_h=0+2308|space=2+569|t_h=3+1687|space=5+569|l_l=6+1108|space=8+569|t_e=9+1408|space=11+569|t_o=12+1531|space=14+569|t_r=15+1385|space=17+569|f_r=18+1432|space=20+569|f_u=21+1733|space=23+569|f_j=24+1098]
+/Library/Fonts/Apple Chancery.ttf@5fc49ae9bce39e2105864323183b68ea34c9e562:--font-funcs ot:U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064:[T=0+1497|e=1@-62,0+699|space=2+569|A=3+1431|V=4@-37,0+1377|space=5+569|T=6+1510|r=7@-50,0+803|space=8+569|V=9+1376|a=10@-37,0+1014|space=11+569|r=12+853|T=13+1560|space=14+569|e=15+761|T=16+1560|space=17+569|T=18+1515|d=19@-45,0+1006]
+/System/Library/Fonts/GeezaPro.ttc@f43ee7151c2e9f1dddfbc26cfc148609eb5c5820:--font-funcs ot:U+0627,U+0644,U+0623,U+064E,U+0628,U+0652,U+062C,U+064E,U+062F,U+0650,U+064A,U+064E,U+0651,U+0629,U+0640,U+0627,U+0644,U+0639,U+064E,U+0631,U+064E,U+0628,U+0650,U+064A,U+064E,U+0651,U+0629:[u0629.final.tehMarbuta=26+713|u064e_u0651.shaddaFatha=23@0,-200+0|u064a.medial.yeh=23+656|u0650.kasra=21@80,290+80|u0628.initial.beh=21@-80,0+576|u064e.fatha=19@200,-570+200|u0631.final.reh=19@-200,0+702|u064e.fatha=17@200,-200+200|u0639.medial.ain=17@-200,0+738|u0644.initial.lam=16+515|u0627.final.alef=15+647|u0640.tatweel=14+449|u0629.final.tehMarbuta=13+713|u064e_u0651.shaddaFatha=10@0,-200+0|u064a.initial.yeh=10+656|u0650.kasra=8@80,570+80|u062f.final.dal=8@-80,0+822|u064e.fatha=6@290,-160+290|u062c.medial.jeem=6@-290,0+1069|u0652.sukun=4@0,-200+0|u0628.initial.beh=4+656|u064e.fatha=1@-252,120+-252|u0644_u0623.isolated.lamHamzaOnAlef=1@120,0+1282|u0627.alef=0+647]
+/System/Library/Fonts/GeezaPro.ttc@f43ee7151c2e9f1dddfbc26cfc148609eb5c5820:--font-funcs ot:U+0628,U+064A,U+064E,U+0651,U+0629:[u0629.final.tehMarbuta=4+713|u064e_u0651.shaddaFatha=1@0,-200+0|u064a.medial.yeh=1+656|u0628.initial.beh=0+656]
+/System/Library/Fonts/GeezaPro.ttc@f43ee7151c2e9f1dddfbc26cfc148609eb5c5820:--font-funcs ot:U+0631,U+0628:[u0628.beh=1+1415|u0631.reh=0@-202,0+700]
+/System/Library/Fonts/GeezaPro.ttc@f43ee7151c2e9f1dddfbc26cfc148609eb5c5820:--font-funcs ot:U+0628,U+064F:[u064f.damma=0@250,-250+250|u0628.beh=0@-250,0+1165]
+/System/Library/Fonts/SFNSDisplay.ttf@92787c30716672737e9059bc367c15d04fbc1ced:--font-funcs ot:U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064:[gid225=0+1105|gid584=1@-105,0+979|gid3=2+490|gid4=3+1227|gid265=4@-65,0+1227|gid3=5+490|gid225=6+1130|gid728=7@-80,0+569|gid3=8+490|gid265=9+1227|gid505=10@-65,0+997|gid3=11+490|gid728=12+609|gid225=13@-40,0+1170|gid3=14+490|gid584=15+1004|gid225=16@-80,0+1130|gid3=17+490|gid225=18+1105|gid576=19@-105,0+1068]
+/System/Library/Fonts/SFNSDisplay.ttf@92787c30716672737e9059bc367c15d04fbc1ced:--font-ptem 9 --font-funcs ot:U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064:[gid225=0@46,0+1197|gid584=1@-59,0+1071|gid3=2@46,0+582|gid4=3@46,0+1319|gid265=4@-19,0+1319|gid3=5@46,0+582|gid225=6@46,0+1222|gid728=7@-34,0+661|gid3=8@46,0+582|gid265=9@46,0+1319|gid505=10@-19,0+1089|gid3=11@46,0+582|gid728=12@46,0+701|gid225=13@6,0+1262|gid3=14@46,0+582|gid584=15@46,0+1096|gid225=16@-34,0+1222|gid3=17@46,0+582|gid225=18@46,0+1197|gid576=19@-59,0+1160]
+/System/Library/Fonts/Apple Color Emoji.ttc@d2fe8a134483aa48a43a9d1e4b7204d37a4abdf5:--remove-default-ignorables --font-funcs ot:U+1F468,U+200D,U+1F469,U+200D,U+1F467,U+200D,U+1F466:[u1F46A.MWGB=0+800]
+
+# 10.13.6 https://gist.github.com/ebraminio/d432e831b3f7ebe30245dde5775e1c7e
+/System/Library/Fonts/Helvetica.ttc@8a928f9866299d2455f41360202b7a3b48503a5e:--font-funcs ot:U+006D,U+0300:[m=0+1706|gravecmb=0@-284,10+0]
+/System/Library/Fonts/LucidaGrande.ttc@63ba1b1de4709bd832ca76bd62368dd99fc34269:--font-funcs ot:U+006D,U+0300:[mgrave=0+1912]
+/System/Library/Fonts/Times.ttc@896098b6979306ad84355025459f7c68b029139c:--font-funcs ot:U+0066,U+0069:[fi=0+1139]
+/Library/Fonts/Khmer MN.ttc@782ba6cf3fca0512ab348dfe08345a2d5dc5bf2c:--font-funcs ot:U+17A2,U+1780,U+17D2,U+179F,U+179A,U+1781,U+17D2,U+1798,U+17C2,U+179A:[km_qa=0+1025|km_ka=1+1025|km_sa.sub=1+517|km_ro=4+593|km_vs_ae=5+605|km_kha=5+1025|km_mo.sub=5+0|km_ro=9+593]
+/Library/Fonts/Tamil MN.ttc@3de37f3f8f3cb6015b093fbd6e9d323daaf6fb1d:--font-funcs ot:U+0BA4,U+0BCA,U+0B95,U+0BC1,U+0B95,U+0BCD,U+0B95,U+0BAA,U+0BCD,U+0BAA,U+0B9F,U+0BCD,U+0B9F,U+0BC1:[tgm_e=0+1702|tgc_ta=0+1598|tgm_aa=0+1149|tgc_ku=2+1962|tgc_k=4+1592|tgc_ka=6+1592|tgc_p=7+1370|tgc_pa=9+1370|tgc_tt=10+1596|tgc_ttu=12+1833]
+/System/Library/Fonts/Times.ttc@896098b6979306ad84355025459f7c68b029139c:--font-funcs ot:U+0041,U+0066,U+0300,U+0066,U+0069,U+005A:[A=0+1479|f=1+682|gravecmb=1@-480,588+0|fi=3+1139|Z=5+1251]
+/System/Library/Fonts/LucidaGrande.ttc@63ba1b1de4709bd832ca76bd62368dd99fc34269:--font-funcs ot:U+05E1,U+05B0:[shevahebrew=0@-7,0+0|samekhhebrew=0+1361]
+/Library/Fonts/Apple Chancery.ttf@4ec49cba0d4e68d025ada0498c4df1b2f9fd57ac:--font-funcs ot:U+0054,U+0068,U+0020,U+0074,U+0068,U+0020,U+006C,U+006C,U+0020,U+0074,U+0065,U+0020,U+0074,U+006F,U+0020,U+0074,U+0072,U+0020,U+0066,U+0072,U+0020,U+0066,U+0075,U+0020,U+0066,U+006A:[T_h=0+2308|space=2+569|t_h=3+1687|space=5+569|l_l=6+1108|space=8+569|t_e=9+1408|space=11+569|t_o=12+1531|space=14+569|t_r=15+1385|space=17+569|f_r=18+1432|space=20+569|f_u=21+1733|space=23+569|f_j=24+1098]
+/Library/Fonts/Apple Chancery.ttf@4ec49cba0d4e68d025ada0498c4df1b2f9fd57ac:--font-funcs ot:U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064:[T=0+1497|e=1@-62,0+699|space=2+569|A=3+1431|V=4@-37,0+1377|space=5+569|T=6+1510|r=7@-50,0+803|space=8+569|V=9+1376|a=10@-37,0+1014|space=11+569|r=12+853|T=13+1560|space=14+569|e=15+761|T=16+1560|space=17+569|T=18+1515|d=19@-45,0+1006]
+/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334:--font-funcs ot:U+0627,U+0644,U+0623,U+064E,U+0628,U+0652,U+062C,U+064E,U+062F,U+0650,U+064A,U+064E,U+0651,U+0629,U+0640,U+0627,U+0644,U+0639,U+064E,U+0631,U+064E,U+0628,U+0650,U+064A,U+064E,U+0651,U+0629:[u0629.final.tehMarbuta=26+713|u064e_u0651.shaddaFatha=23@0,-200+0|u064a.medial.yeh=23+656|u0650.kasra=21@80,290+80|u0628.initial.beh=21@-80,0+576|u064e.fatha=19@200,-570+200|u0631.final.reh=19@-200,0+702|u064e.fatha=17@200,-200+200|u0639.medial.ain=17@-200,0+738|u0644.initial.lam=16+515|u0627.final.alef=15+647|u0640.tatweel=14+449|u0629.final.tehMarbuta=13+713|u064e_u0651.shaddaFatha=10@0,-200+0|u064a.initial.yeh=10+656|u0650.kasra=8@80,570+80|u062f.final.dal=8@-80,0+822|u064e.fatha=6@290,-160+290|u062c.medial.jeem=6@-290,0+1069|u0652.sukun=4@0,-200+0|u0628.initial.beh=4+656|u064e.fatha=1@-252,120+-252|u0644_u0623.isolated.lamHamzaOnAlef=1@120,0+1282|u0627.alef=0+647]
+/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334:--font-funcs ot:U+0628,U+064A,U+064E,U+0651,U+0629:[u0629.final.tehMarbuta=4+713|u064e_u0651.shaddaFatha=1@0,-200+0|u064a.medial.yeh=1+656|u0628.initial.beh=0+656]
+/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334:--font-funcs ot:U+0631,U+0628:[u0628.beh=1+1415|u0631.reh=0@-202,0+700]
+/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334:--font-funcs ot:U+0628,U+064F:[u064f.damma=0@250,-250+250|u0628.beh=0@-250,0+1165]
+/System/Library/Fonts/SFNSDisplay.ttf@c8948f464ff822a5f9bbf2e12d0e4e32268815aa:--font-funcs ot:U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064:[gid282=0+1055|gid658=1@-135,0+914|gid3=2+420|gid4=3+1227|gid332=4@-65,0+1227|gid3=5+420|gid282=6+1075|gid813=7@-115,0+516|gid3=8+420|gid332=9+1217|gid572=10@-75,0+953|gid3=11+420|gid813=12+546|gid282=13@-85,0+1105|gid3=14+420|gid658=15+914|gid282=16@-135,0+1055|gid3=17+420|gid282=18+1055|gid649=19@-135,0+999]
+/System/Library/Fonts/SFNSDisplay.ttf@c8948f464ff822a5f9bbf2e12d0e4e32268815aa:--font-ptem 9 --font-funcs ot:U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064:[gid282=0@46,0+1147|gid658=1@-89,0+1006|gid3=2@46,0+512|gid4=3@46,0+1319|gid332=4@-19,0+1319|gid3=5@46,0+512|gid282=6@46,0+1167|gid813=7@-69,0+608|gid3=8@46,0+512|gid332=9@46,0+1309|gid572=10@-29,0+1045|gid3=11@46,0+512|gid813=12@46,0+638|gid282=13@-39,0+1197|gid3=14@46,0+512|gid658=15@46,0+1006|gid282=16@-89,0+1147|gid3=17@46,0+512|gid282=18@46,0+1147|gid649=19@-89,0+1091]
+/System/Library/Fonts/Apple Color Emoji.ttc@2e09b1f3d42c3821cc6c4ac5b6ce16237ab0d496:--remove-default-ignorables --font-funcs ot:U+1F468,U+200D,U+1F469,U+200D,U+1F467,U+200D,U+1F466:[u1F46A.MWGB=0+800]
diff --git a/test/shaping/data/in-house/tests/spaces.tests b/test/shaping/data/in-house/tests/spaces.tests
index 3ebaac5..ea90998 100644
--- a/test/shaping/data/in-house/tests/spaces.tests
+++ b/test/shaping/data/in-house/tests/spaces.tests
@@ -15,3 +15,20 @@
 ../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+202F:[gid1=0+280]
 ../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+205F:[gid1=0+455]
 ../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+3000:[gid1=0+2048]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+0020:[gid1=0@-280,0+0,-2048]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+00A0:[gid1=0@-280,0+0,-2048]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+1680:[gid0=0@-346,0+0,-2048]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+2000:[gid1=0@-280,0+0,-1024]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+2001:[gid1=0@-280,0+0,-2048]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+2002:[gid1=0@-280,0+0,-1024]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+2003:[gid1=0@-280,0+0,-2048]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+2004:[gid1=0@-280,0+0,-683]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+2005:[gid1=0@-280,0+0,-512]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+2006:[gid1=0@-280,0+0,-341]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+2007:[gid1=0@-280,0+0,-2048]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+2008:[gid1=0@-280,0+0,-2048]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+2009:[gid1=0@-280,0+0,-410]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+200A:[gid1=0@-280,0+0,-128]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+202F:[gid1=0@-280,0+0,-1024]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+205F:[gid1=0@-280,0+0,-455]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot --direction=ttb:U+3000:[gid1=0@-280,0+0,-2048]
diff --git a/test/shaping/data/in-house/tests/use-syllable.tests b/test/shaping/data/in-house/tests/use-syllable.tests
index d8f1dff..6a247ed 100644
--- a/test/shaping/data/in-house/tests/use-syllable.tests
+++ b/test/shaping/data/in-house/tests/use-syllable.tests
@@ -9,3 +9,4 @@
 ../fonts/28f497629c04ceb15546c9a70e0730125ed6698d.ttf::U+11013,U+11042,U+11046:[brm_KA=0+754|brm_vowelEE=0@-383,0+0|brm_virama=0@-524,0+0]
 ../fonts/28f497629c04ceb15546c9a70e0730125ed6698d.ttf::U+11013,U+11044,U+11046:[brm_KA=0+754|brm_vowelOO=0@-647,0+0|brm_virama=0@-524,0+0]
 ../fonts/28f497629c04ceb15546c9a70e0730125ed6698d.ttf::U+11013,U+1103C:[brm_KA=0+754|brm_vowelU=0@-403,0+0]
+../fonts/86cdd983c4e4c4d7f27dd405d6ceb7d4b9ed3d35.ttf::U+111C8,U+111C9,U+111C9:[u111C8=0+500|u111C9=0@-500,0+0|u111C9=0@-500,0+0]
diff --git a/test/shaping/data/in-house/tests/use-vowel-letter-spoofing.tests b/test/shaping/data/in-house/tests/use-vowel-letter-spoofing.tests
new file mode 100644
index 0000000..45cf80e
--- /dev/null
+++ b/test/shaping/data/in-house/tests/use-vowel-letter-spoofing.tests
@@ -0,0 +1,94 @@
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+093A:[uni0905=0+500|uni25CC=0+500|uni093A=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+093B:[uni0905=0+500|uni25CC=0+500|uni093B=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+093E:[uni0905=0+500|uni25CC=0+500|uni093E=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+0945:[uni0905=0+500|uni25CC=0+500|uni0945=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+0946:[uni0905=0+500|uni25CC=0+500|uni0946=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+0949:[uni0905=0+500|uni25CC=0+500|uni0949=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+094A:[uni0905=0+500|uni25CC=0+500|uni094A=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+094B:[uni0905=0+500|uni25CC=0+500|uni094B=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+094C:[uni0905=0+500|uni25CC=0+500|uni094C=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+094F:[uni0905=0+500|uni25CC=0+500|uni094F=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+0956:[uni0905=0+500|uni25CC=0+500|uni0956=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0905,U+0957:[uni0905=0+500|uni25CC=0+500|uni0957=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0906,U+093A:[uni0906=0+500|uni25CC=0+500|uni093A=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0906,U+0945:[uni0906=0+500|uni25CC=0+500|uni0945=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0906,U+0946:[uni0906=0+500|uni25CC=0+500|uni0946=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0906,U+0947:[uni0906=0+500|uni25CC=0+500|uni0947=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0906,U+0948:[uni0906=0+500|uni25CC=0+500|uni0948=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0909,U+0941:[uni0909=0+500|uni25CC=0+500|uni0941=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+090F,U+0945:[uni090F=0+500|uni25CC=0+500|uni0945=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+090F,U+0946:[uni090F=0+500|uni25CC=0+500|uni0946=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+090F,U+0947:[uni090F=0+500|uni25CC=0+500|uni0947=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0930,U+094D,U+0907:[uni0930=0+500|uni094D=0+500|uni25CC=2+500|uni0907=2+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0985,U+09BE:[uni0985=0+500|uni25CC=0+500|.notdef=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+098B,U+09C3:[uni098B=0+500|uni25CC=0+500|uni09C3=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+098C,U+09E2:[uni098C=0+500|uni25CC=0+500|uni09E2=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A05,U+0A3E:[uni0A05=0+500|uni25CC=0+500|uni0A3E=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A05,U+0A48:[uni0A05=0+500|uni25CC=0+500|uni0A48=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A05,U+0A4C:[uni0A05=0+500|uni25CC=0+500|uni0A4C=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A72,U+0A3F:[uni0A72=0+500|uni0A3F=0+500|uni25CC=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A72,U+0A40:[uni0A72=0+500|uni25CC=0+500|uni0A40=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A72,U+0A47:[uni0A72=0+500|uni25CC=0+500|uni0A47=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A73,U+0A41:[uni0A73=0+500|uni25CC=0+500|uni0A41=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A73,U+0A42:[uni0A73=0+500|uni25CC=0+500|uni0A42=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A73,U+0A4B:[uni0A73=0+500|uni25CC=0+500|uni0A4B=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0ABE,U+0AC5:[uni0A85=0+500|uni25CC=0+500|uni0ABE=0+500|uni25CC=0+500|uni0AC5=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0ABE,U+0AC8:[uni0A85=0+500|uni25CC=0+500|uni0ABE=0+500|uni25CC=0+500|uni0AC8=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0ABE:[uni0A85=0+500|uni25CC=0+500|uni0ABE=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0AC5:[uni0A85=0+500|uni25CC=0+500|uni0AC5=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0AC7:[uni0A85=0+500|uni25CC=0+500|uni0AC7=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0AC8:[uni0A85=0+500|uni25CC=0+500|uni0AC8=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0AC9:[uni0A85=0+500|uni25CC=0+500|uni0AC9=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0ACB:[uni0A85=0+500|uni25CC=0+500|uni0ACB=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0A85,U+0ACC:[uni0A85=0+500|uni25CC=0+500|uni0ACC=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0AC5,U+0ABE:[uni25CC=0+500|uni0AC5=0+500|uni25CC=0+500|uni0ABE=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0B05,U+0B3E:[uni0B05=0+500|uni25CC=0+500|uni0B3E=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0B0F,U+0B57:[uni0B0F=0+500|uni25CC=0+500|uni0B57=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0B13,U+0B57:[uni0B13=0+500|uni25CC=0+500|uni0B57=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C12,U+0C4C:[uni0C12=0+500|uni25CC=0+500|uni0C4C=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C12,U+0C55:[uni0C12=0+500|uni25CC=0+500|uni0C55=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C3F,U+0C55:[uni25CC=0+500|uni0C3F=0+500|uni25CC=0+500|uni0C55=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C46,U+0C55:[uni25CC=0+500|uni0C46=0+500|uni25CC=0+500|uni0C55=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C4A,U+0C55:[uni25CC=0+500|uni0C4A=0+500|uni25CC=0+500|uni0C55=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C89,U+0CBE:[uni0C89=0+500|uni25CC=0+500|uni0CBE=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C8B,U+0CBE:[uni0C8B=0+500|uni25CC=0+500|uni0CBE=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0C92,U+0CCC:[uni0C92=0+500|uni25CC=0+500|uni0CCC=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D07,U+0D57:[uni0D07=0+500|uni25CC=0+500|uni0D57=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D09,U+0D57:[uni0D09=0+500|uni25CC=0+500|uni0D57=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D0E,U+0D46:[uni0D0E=0+500|uni0D46=0+500|uni25CC=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D12,U+0D3E:[uni0D12=0+500|uni25CC=0+500|uni0D3E=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D12,U+0D57:[uni0D12=0+500|uni25CC=0+500|uni0D57=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D85,U+0DCF:[uni0D85=0+500|uni25CC=0+500|uni0DCF=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D85,U+0DD0:[uni0D85=0+500|uni25CC=0+500|uni0DD0=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D85,U+0DD1:[uni0D85=0+500|uni25CC=0+500|uni0DD1=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D8B,U+0DDF:[uni0D8B=0+500|uni25CC=0+500|uni0DDF=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D8D,U+0DD8:[uni0D8D=0+500|uni25CC=0+500|uni0DD8=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D8F,U+0DDF:[uni0D8F=0+500|uni25CC=0+500|uni0DDF=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DCA:[uni0D91=0+500|uni25CC=0+500|uni0DCA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DD9:[uni0D91=0+500|uni0DD9=0+500|uni25CC=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DDA:[uni0D91=0+500|uni0DD9=0+500|uni25CC=0+500|uni0DCA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DDC:[uni0D91=0+500|uni0DD9=0+500|uni25CC=0+500|uni0DCF=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DDD:[uni0D91=0+500|uni0DD9=0+500|uni25CC=0+500|uni0DCF=0+500|uni0DCA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D91,U+0DDD:[uni0D91=0+500|uni0DD9=0+500|uni25CC=0+500|uni0DCF=0+500|uni0DCA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+0D94,U+0DDF:[uni0D94=0+500|uni25CC=0+500|uni0DDF=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11005,U+11038:[u11005=0+500|uni25CC=0+500|u11038=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+1100B,U+1103E:[u1100B=0+500|uni25CC=0+500|u1103E=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+1100F,U+11042:[u1100F=0+500|uni25CC=0+500|u11042=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+112B0,U+112E0:[u112B0=0+500|uni25CC=0+500|u112E0=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+112B0,U+112E5:[u112B0=0+500|uni25CC=0+500|u112E5=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+112B0,U+112E6:[u112B0=0+500|uni25CC=0+500|u112E6=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+112B0,U+112E7:[u112B0=0+500|uni25CC=0+500|u112E7=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+112B0,U+112E8:[u112B0=0+500|uni25CC=0+500|u112E8=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11481,U+114B0:[u11481=0+500|uni25CC=0+500|u114B0=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+1148B,U+114BA:[u1148B=0+500|uni25CC=0+500|u114BA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+1148D,U+114BA:[u1148D=0+500|uni25CC=0+500|u114BA=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+114AA,U+114B5:[u114AA=0+500|uni25CC=0+500|u114B5=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+114AA,U+114B6:[u114AA=0+500|uni25CC=0+500|u114B6=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11600,U+11639:[u11600=0+500|uni25CC=0+500|u11639=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11600,U+1163A:[u11600=0+500|uni25CC=0+500|u1163A=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11601,U+11639:[u11601=0+500|uni25CC=0+500|u11639=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11601,U+1163A:[u11601=0+500|uni25CC=0+500|u1163A=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11680,U+116AD:[u11680=0+500|uni25CC=0+500|u116AD=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11680,U+116B4:[u11680=0+500|uni25CC=0+500|u116B4=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11680,U+116B5:[u11680=0+500|uni25CC=0+500|u116B5=0+500]
+../fonts/46669c8860cbfea13562a6ca0d83130ee571137b.ttf::U+11686,U+116B2:[u11686=0+500|uni25CC=0+500|u116B2=0+500]
diff --git a/test/shaping/data/in-house/tests/use.tests b/test/shaping/data/in-house/tests/use.tests
index 4b46620..dd2a3a2 100644
--- a/test/shaping/data/in-house/tests/use.tests
+++ b/test/shaping/data/in-house/tests/use.tests
@@ -8,3 +8,7 @@
 ../fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf::U+11124,U+11134,U+11131:[u11124=0+514|u11134=0+0|u11131=0+0]
 ../fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf::U+11124,U+11131,U+11134:[u11124=0+514|u11131=0+0|uni25CC=0+547|u11134=0+0]
 ../fonts/573d3a3177c9a8646e94c8a0d7b224334340946a.ttf:--font-funcs=ft:U+11410,U+11442,U+11411,U+11440,U+11443,U+11410,U+11442,U+11411,U+11441,U+11443:[E_dv.alt=0+275|Ga.icd=0+367|Gha.diag=0@100,0+386|AA_dv.alt=0+208|Candrabindu=0@17,-8+0|E_dv.alt=5+275|Ga.icd=5+367|Gha.diag=5@100,0+386|AU_dv_part.alt=5+213|Candrabindu.sm=5@-52,179+0]
+../fonts/dcf774ca21062e7439f98658b18974ea8b956d0c.ttf::U+11328,U+1134D,U+1CF4:[gid1=0+793|gid2=0+0|gid3=0+0]
+../fonts/4afb0e8b9a86bb9bd73a1247de4e33fbe3c1fd93.ttf::U+1C00,U+1C27,U+1C28,U+1C34,U+1C35:[uni1C35=0+500|uni1C34=0+500|uni1C28=0+500|uni1C27=0+500|uni1C00=0+500]
+../fonts/4afb0e8b9a86bb9bd73a1247de4e33fbe3c1fd93.ttf::U+0D4E,U+0D15,U+0D4D,U+0D15,U+0D46:[uni0D15=0+500|uni0D4E=0+500|uni0D4D=0+500|uni0D46=3+500|uni0D15=3+500]
+../fonts/4afb0e8b9a86bb9bd73a1247de4e33fbe3c1fd93.ttf::U+1102D,U+11046,U+11013,U+11046,U+11013,U+11046:[u11013=0+500|u11046_u11013=0+500|u1102D_u11046=0+500|u11046=0+500]
diff --git a/test/shaping/data/in-house/tests/vertical.tests b/test/shaping/data/in-house/tests/vertical.tests
index b181192..3958813 100644
--- a/test/shaping/data/in-house/tests/vertical.tests
+++ b/test/shaping/data/in-house/tests/vertical.tests
@@ -1,3 +1,4 @@
 ../fonts/191826b9643e3f124d865d617ae609db6a2ce203.ttf:--direction=t --font-funcs=ft:U+300C:[uni300C.vert=0@-512,-578+0,-1024]
 ../fonts/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf:--direction=t --font-funcs=ft:U+0041,U+0042:[gid1=0@-654,-2128+0,-2789|gid2=1@-665,-2125+0,-2789]
 ../fonts/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf:--direction=t --font-funcs=ot:U+0041,U+0042:[gid1=0@-654,-1468+0,-2048|gid2=1@-665,-1462+0,-2048]
+../fonts/4cbbc461be066fccc611dcc634af6e8cb2705537.ttf:--direction=t --font-funcs=ot:U+FF38:[gid2=0@-500,-867+0,-1000]
diff --git a/test/shaping/data/text-rendering-tests/DISABLED b/test/shaping/data/text-rendering-tests/DISABLED
index ef987a4..b071904 100644
--- a/test/shaping/data/text-rendering-tests/DISABLED
+++ b/test/shaping/data/text-rendering-tests/DISABLED
@@ -1,3 +1,4 @@
+tests/MORX-31.tests
 tests/MORX-41.tests
 
 # Non-Unicode cmap
diff --git a/test/shaping/data/text-rendering-tests/Makefile.sources b/test/shaping/data/text-rendering-tests/Makefile.sources
index 5e0db6b..052a612 100644
--- a/test/shaping/data/text-rendering-tests/Makefile.sources
+++ b/test/shaping/data/text-rendering-tests/Makefile.sources
@@ -51,7 +51,6 @@
 	tests/MORX-29.tests \
 	tests/MORX-2.tests \
 	tests/MORX-30.tests \
-	tests/MORX-31.tests \
 	tests/MORX-32.tests \
 	tests/MORX-33.tests \
 	tests/MORX-34.tests \
@@ -73,8 +72,9 @@
 	$(NULL)
 
 DISBALED_TESTS = \
-	tests/MORX-41.tests \
 	tests/CMAP-3.tests \
+	tests/MORX-31.tests \
+	tests/MORX-41.tests \
 	tests/SHARAN-1.tests \
 	tests/SHBALI-1.tests \
 	tests/SHBALI-2.tests \
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGVAR-Composite-0.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGVAR-Composite-0.ttf
new file mode 100644
index 0000000..07e6882
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGVAR-Composite-0.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGVAR-Composite-Missing.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGVAR-Composite-Missing.ttf
new file mode 100644
index 0000000..58dd961
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGVAR-Composite-Missing.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestTRAKOne.ttf b/test/shaping/data/text-rendering-tests/fonts/TestTRAKOne.ttf
new file mode 100644
index 0000000..425bce6
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestTRAKOne.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-31.tests b/test/shaping/data/text-rendering-tests/tests/MORX-31.tests
index 6cc40b6..ac09e27 100644
--- a/test/shaping/data/text-rendering-tests/tests/MORX-31.tests
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-31.tests
@@ -1,8 +1,8 @@
-../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0058,U+0041,U+0059,U+0059,U+0041,U+005A,U+005A:[X|X@364,0|I@728,0|N@1558,0|S@2388,0|A@3218,0|Y@4048,0|Y@4380,0|A@4712,0|Z@5542,0|Z@5864,0]
-../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0058,U+0041,U+0059,U+0059,U+0042,U+0059,U+0059:[X|X@364,0|A@728,0|I@1558,0|N@2388,0|S@3218,0|Y@4048,0|Y@4380,0|B@4712,0|Y@5542,0|Y@5874,0]
-../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0058,U+0042,U+0059,U+0059,U+0041,U+005A,U+005A:[X|X@364,0|I@728,0|N@1558,0|S@2388,0|B@3218,0|Y@4048,0|Y@4380,0|A@4712,0|Z@5542,0|Z@5864,0]
-../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0058,U+0042,U+0059,U+0059,U+0042,U+005A,U+005A:[X|X@364,0|B@728,0|I@1558,0|N@2388,0|S@3218,0|Y@4048,0|Y@4380,0|B@4712,0|Z@5542,0|Z@5864,0]
-../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004D,U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0041:[I|N@830,0|S@1660,0|M@2490,0|P@3320,0|Q@3653,0|R@4019,0|I@4370,0|N@5200,0|S@6030,0|A@6860,0|X@7690,0|Y@8054,0|Z@8386,0|A@8708,0]
-../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004D,U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0042:[I|N@830,0|S@1660,0|M@2490,0|P@3320,0|Q@3653,0|R@4019,0|A@4370,0|I@5200,0|N@6030,0|S@6860,0|X@7690,0|Y@8054,0|Z@8386,0|B@8708,0]
-../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004D,U+0050,U+0051,U+0052,U+0042,U+0058,U+0059,U+005A,U+0041:[M|I@830,0|N@1660,0|S@2490,0|P@3320,0|Q@3653,0|R@4019,0|I@4370,0|N@5200,0|S@6030,0|B@6860,0|X@7690,0|Y@8054,0|Z@8386,0|A@8708,0]
-../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004D,U+0050,U+0051,U+0052,U+0042,U+0058,U+0059,U+005A,U+0042:[M|I@830,0|N@1660,0|S@2490,0|P@3320,0|Q@3653,0|R@4019,0|B@4370,0|I@5200,0|N@6030,0|S@6860,0|X@7690,0|Y@8054,0|Z@8386,0|B@8708,0]
+../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0058,U+0041,U+0059,U+0059,U+0041,U+005A,U+005A:[I|N@830,0|I@1660,0|N@2490,0|S@3320,0|S@4150,0|X@4980,0|X@5344,0|A@5708,0|Y@6538,0|Y@6870,0|A@7202,0|Z@8032,0|Z@8354,0]
+../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0058,U+0041,U+0059,U+0059,U+0042,U+0059,U+0059:[I|N@830,0|S@1660,0|I@2490,0|N@3320,0|S@4150,0|X@4980,0|X@5344,0|A@5708,0|Y@6538,0|Y@6870,0|B@7202,0|Y@8032,0|Y@8364,0]
+../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0058,U+0042,U+0059,U+0059,U+0041,U+005A,U+005A:[X|I@364,0|I@1194,0|N@2024,0|S@2854,0|N@3684,0|S@4514,0|X@5344,0|B@5708,0|Y@6538,0|Y@6870,0|A@7202,0|Z@8032,0|Z@8354,0]
+../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0058,U+0042,U+0059,U+0059,U+0042,U+005A,U+005A:[X|I@364,0|N@1194,0|I@2024,0|N@2854,0|S@3684,0|S@4514,0|X@5344,0|B@5708,0|Y@6538,0|Y@6870,0|B@7202,0|Z@8032,0|Z@8354,0]
+../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004D,U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0041:[I|N@830,0|S@1660,0|M@2490,0|I@3320,0|N@4150,0|S@4980,0|P@5810,0|Q@6143,0|R@6509,0|A@6860,0|X@7690,0|Y@8054,0|Z@8386,0|A@8708,0]
+../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004D,U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0042:[I|N@830,0|S@1660,0|M@2490,0|P@3320,0|I@3653,0|N@4483,0|S@5313,0|Q@6143,0|R@6509,0|A@6860,0|X@7690,0|Y@8054,0|Z@8386,0|B@8708,0]
+../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004D,U+0050,U+0051,U+0052,U+0042,U+0058,U+0059,U+005A,U+0041:[M|I@830,0|N@1660,0|S@2490,0|I@3320,0|N@4150,0|S@4980,0|P@5810,0|Q@6143,0|R@6509,0|B@6860,0|X@7690,0|Y@8054,0|Z@8386,0|A@8708,0]
+../fonts/TestMORXThirtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004D,U+0050,U+0051,U+0052,U+0042,U+0058,U+0059,U+005A,U+0042:[M|I@830,0|N@1660,0|S@2490,0|P@3320,0|I@3653,0|N@4483,0|S@5313,0|Q@6143,0|R@6509,0|B@6860,0|X@7690,0|Y@8054,0|Z@8386,0|B@8708,0]
diff --git a/test/shaping/hb_test_tools.py b/test/shaping/hb_test_tools.py
index c9a4403..feff70a 100644
--- a/test/shaping/hb_test_tools.py
+++ b/test/shaping/hb_test_tools.py
@@ -60,10 +60,10 @@
 						yield 0x10000 + (high_surrogate - 0xD800) * 0x400 + (cp - 0xDC00)
 						high_surrogate = None
 					else:
-						yield 0xFFFC
+						yield 0xFFFD
 				else:
 					if high_surrogate:
-						yield 0xFFFC
+						yield 0xFFFD
 						high_surrogate = None
 					if 0xD800 <= cp <= 0xDBFF:
 						high_surrogate = cp
@@ -71,7 +71,7 @@
 						yield cp
 						high_surrogate = None
 			if high_surrogate:
-				yield 0xFFFC
+				yield 0xFFFD
 
 except NameError:
 	unichr = chr
diff --git a/test/shaping/run-tests.py b/test/shaping/run-tests.py
index f77a17c..26853e4 100755
--- a/test/shaping/run-tests.py
+++ b/test/shaping/run-tests.py
@@ -2,30 +2,35 @@
 
 from __future__ import print_function, division, absolute_import
 
-import sys, os, subprocess, tempfile
-
+import sys, os, subprocess, hashlib, tempfile, shutil
 
 def cmd(command):
-	# https://stackoverflow.com/a/4408409
-	with tempfile.TemporaryFile() as tempf:
-		p = subprocess.Popen (command, stdout=tempf, stderr=sys.stdout)
-		p.wait ()
-		tempf.seek(0)
-		return tempf.read().decode ("utf-8").strip (), p.returncode
+	global process
+	process.stdin.write ((' '.join (command) + '\n').encode ("utf-8"))
+	process.stdin.flush ()
+	return process.stdout.readline().decode ("utf-8").strip ()
 
 args = sys.argv[1:]
-if not args or sys.argv[1].find('hb-shape') == -1 or not os.path.exists (sys.argv[1]):
-	print ("""First argument does not seem to point to usable hb-shape.""")
-	sys.exit (1)
-hb_shape, args = args[0], args[1:]
-
-fails = 0
 
 reference = False
 if len (args) and args[0] == "--reference":
 	reference = True
 	args = args[1:]
 
+if not args or args[0].find('hb-shape') == -1 or not os.path.exists (args[0]):
+	print ("""First argument does not seem to point to usable hb-shape.""")
+	sys.exit (1)
+hb_shape, args = args[0], args[1:]
+
+process = subprocess.Popen ([hb_shape, '--batch'],
+			    stdin=subprocess.PIPE,
+			    stdout=subprocess.PIPE,
+			    stderr=sys.stdout)
+
+passes = 0
+fails = 0
+skips = 0
+
 if not len (args):
 	args = ['-']
 
@@ -42,46 +47,73 @@
 		f = open (filename)
 
 	for line in f:
+		comment = False
+		if line.startswith ("#"):
+			comment = True
+			line = line[1:]
+
+			if line.startswith (' '):
+				if not reference:
+					print ("#%s" % line)
+				continue
+
+		line = line.strip ()
+		if not line:
+			continue
+
 		fontfile, options, unicodes, glyphs_expected = line.split (":")
-		cwd = os.path.dirname(filename)
-		fontfile = os.path.normpath (os.path.join (cwd, fontfile))
+		if fontfile.startswith ('/') or fontfile.startswith ('"/'):
+			fontfile, expected_hash = fontfile.split('@')
+
+			try:
+				with open (fontfile, 'rb') as ff:
+					actual_hash = hashlib.sha1 (ff.read()).hexdigest ().strip ()
+					if actual_hash != expected_hash:
+						print ('different version of %s found; Expected hash %s, got %s; skipping.' %
+							   (fontfile, expected_hash, actual_hash))
+						skips += 1
+						continue
+			except:
+				print ('%s not found, skip.' % fontfile)
+				skips += 1
+				continue
+		else:
+			cwd = os.path.dirname(filename)
+			fontfile = os.path.normpath (os.path.join (cwd, fontfile))
 
 		extra_options = ["--shaper=ot"]
-		glyphs_expected = glyphs_expected.strip()
 		if glyphs_expected != '*':
 			extra_options.append("--verify")
 
-		if line.startswith ("#"):
+		if comment:
 			if not reference:
-				print ("# %s %s --unicodes %s" % (hb_shape, fontfile, unicodes))
+				print ('# %s "%s" --unicodes %s' % (hb_shape, fontfile, unicodes))
 			continue
 
 		if not reference:
-			print ("%s %s %s %s --unicodes %s" %
+			print ('%s "%s" %s %s --unicodes %s' %
 					 (hb_shape, fontfile, ' '.join(extra_options), options, unicodes))
 
-		glyphs1, returncode = cmd ([hb_shape, "--font-funcs=ft",
+		# hack to support fonts with space on run-tests.py, after several other tries...
+		if ' ' in fontfile:
+			new_fontfile = os.path.join (tempfile.gettempdir (), 'tmpfile')
+			shutil.copyfile(fontfile, new_fontfile)
+			fontfile = new_fontfile
+
+		glyphs1 = cmd ([hb_shape, "--font-funcs=ft",
 			fontfile] + extra_options + ["--unicodes",
 			unicodes] + (options.split (' ') if options else []))
 
-		if returncode:
-			print ("ERROR: hb-shape --font-funcs=ft failed.") # file=sys.stderr
-			fails = fails + 1
-			#continue
-
-		glyphs2, returncode = cmd ([hb_shape, "--font-funcs=ot",
+		glyphs2 = cmd ([hb_shape, "--font-funcs=ot",
 			fontfile] + extra_options + ["--unicodes",
 			unicodes] + (options.split (' ') if options else []))
 
-		if returncode:
-			print ("ERROR: hb-shape --font-funcs=ot failed.") # file=sys.stderr
-			fails = fails + 1
-			#continue
-
 		if glyphs1 != glyphs2 and glyphs_expected != '*':
 			print ("FT funcs: " + glyphs1) # file=sys.stderr
 			print ("OT funcs: " + glyphs2) # file=sys.stderr
-			fails = fails + 1
+			fails += 1
+		else:
+			passes += 1
 
 		if reference:
 			print (":".join ([fontfile, options, unicodes, glyphs1]))
@@ -90,13 +122,20 @@
 		if glyphs1.strip() != glyphs_expected and glyphs_expected != '*':
 			print ("Actual:   " + glyphs1) # file=sys.stderr
 			print ("Expected: " + glyphs_expected) # file=sys.stderr
-			fails = fails + 1
+			fails += 1
+		else:
+			passes += 1
 
-if fails != 0:
-	if not reference:
-		print (str (fails) + " tests failed.") # file=sys.stderr
-	sys.exit (1)
-
-else:
-	if not reference:
+if not reference:
+	print ("%d tests passed; %d failed; %d skipped." % (passes, fails, skips)) # file=sys.stderr
+	if not (fails + passes):
+		print ("No tests ran.")
+	elif not (fails + skips):
 		print ("All tests passed.")
+
+if fails:
+	sys.exit (1)
+elif passes:
+	sys.exit (0)
+else:
+	sys.exit (77)
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.default.1FC,21,41,20,62,63.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.default.1FC,21,41,20,62,63.otf
new file mode 100644
index 0000000..1bd287d
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.default.1FC,21,41,20,62,63.otf
Binary files differ
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.default.61,62,63.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.default.61,62,63.otf
new file mode 100644
index 0000000..328c6ee
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.default.61,62,63.otf
Binary files differ
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.default.D7,D8,D9,DA,DE.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.default.D7,D8,D9,DA,DE.otf
new file mode 100644
index 0000000..4602847
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.default.D7,D8,D9,DA,DE.otf
Binary files differ
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.desubroutinize.1FC,21,41,20,62,63.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.desubroutinize.1FC,21,41,20,62,63.otf
new file mode 100644
index 0000000..18a9bcc
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.desubroutinize.1FC,21,41,20,62,63.otf
Binary files differ
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.desubroutinize.61,62,63.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.desubroutinize.61,62,63.otf
new file mode 100644
index 0000000..edd389b
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.desubroutinize.61,62,63.otf
Binary files differ
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.desubroutinize.D7,D8,D9,DA,DE.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.desubroutinize.D7,D8,D9,DA,DE.otf
new file mode 100644
index 0000000..65fa1da
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.desubroutinize.D7,D8,D9,DA,DE.otf
Binary files differ
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.1FC,21,41,20,62,63.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.1FC,21,41,20,62,63.otf
new file mode 100644
index 0000000..53109e2
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.1FC,21,41,20,62,63.otf
Binary files differ
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.61,62,63.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.61,62,63.otf
new file mode 100644
index 0000000..dd908c2
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.61,62,63.otf
Binary files differ
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.D7,D8,D9,DA,DE.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.D7,D8,D9,DA,DE.otf
new file mode 100644
index 0000000..5422d32
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.D7,D8,D9,DA,DE.otf
Binary files differ
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.desubroutinize.1FC,21,41,20,62,63.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.desubroutinize.1FC,21,41,20,62,63.otf
new file mode 100644
index 0000000..604e140
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.desubroutinize.1FC,21,41,20,62,63.otf
Binary files differ
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.desubroutinize.61,62,63.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.desubroutinize.61,62,63.otf
new file mode 100644
index 0000000..a0b2c2f
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.desubroutinize.61,62,63.otf
Binary files differ
diff --git a/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.desubroutinize.D7,D8,D9,DA,DE.otf b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.desubroutinize.D7,D8,D9,DA,DE.otf
new file mode 100644
index 0000000..8055328
--- /dev/null
+++ b/test/subset/data/expected/full-font/SourceSansPro-Regular.drop-hints.desubroutinize.D7,D8,D9,DA,DE.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.default.3042,3044,3046,3048,304A,304B.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.default.3042,3044,3046,3048,304A,304B.otf
new file mode 100644
index 0000000..8d717cc
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.default.3042,3044,3046,3048,304A,304B.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.default.3042,3044,3046,73E0,5EA6,8F38.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.default.3042,3044,3046,73E0,5EA6,8F38.otf
new file mode 100644
index 0000000..3d570cd
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.default.3042,3044,3046,73E0,5EA6,8F38.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.default.61,63,65,6B.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.default.61,63,65,6B.otf
new file mode 100644
index 0000000..216ed17
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.default.61,63,65,6B.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.default.660E,6975,73E0,5EA6,8F38,6E05.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.default.660E,6975,73E0,5EA6,8F38,6E05.otf
new file mode 100644
index 0000000..f548f48
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.default.660E,6975,73E0,5EA6,8F38,6E05.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.default.660E.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.default.660E.otf
new file mode 100644
index 0000000..6362d21
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.default.660E.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize..otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize..otf
new file mode 100644
index 0000000..7c0c5fd
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize..otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.3042,3044,3046,3048,304A,304B.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.3042,3044,3046,3048,304A,304B.otf
new file mode 100644
index 0000000..e51866a
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.3042,3044,3046,3048,304A,304B.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.3042,3044,3046,73E0,5EA6,8F38.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.3042,3044,3046,73E0,5EA6,8F38.otf
new file mode 100644
index 0000000..c4f6bb2
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.3042,3044,3046,73E0,5EA6,8F38.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.61,63,65,6B.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.61,63,65,6B.otf
new file mode 100644
index 0000000..62ddb60
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.61,63,65,6B.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.660E,6975,73E0,5EA6,8F38,6E05.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.660E,6975,73E0,5EA6,8F38,6E05.otf
new file mode 100644
index 0000000..7ce9d40
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.660E,6975,73E0,5EA6,8F38,6E05.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.660E.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.660E.otf
new file mode 100644
index 0000000..35d9eea
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.desubroutinize.660E.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.3042,3044,3046,3048,304A,304B.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.3042,3044,3046,3048,304A,304B.otf
new file mode 100644
index 0000000..9e1041d
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.3042,3044,3046,3048,304A,304B.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.3042,3044,3046,73E0,5EA6,8F38.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.3042,3044,3046,73E0,5EA6,8F38.otf
new file mode 100644
index 0000000..6a3bff1
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.3042,3044,3046,73E0,5EA6,8F38.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.61,63,65,6B.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.61,63,65,6B.otf
new file mode 100644
index 0000000..06d28b6
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.61,63,65,6B.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.660E,6975,73E0,5EA6,8F38,6E05.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.660E,6975,73E0,5EA6,8F38,6E05.otf
new file mode 100644
index 0000000..ce9d287
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.660E,6975,73E0,5EA6,8F38,6E05.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.660E.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.660E.otf
new file mode 100644
index 0000000..27c4676
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.660E.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize..otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize..otf
new file mode 100644
index 0000000..a73617a
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize..otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.3042,3044,3046,3048,304A,304B.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.3042,3044,3046,3048,304A,304B.otf
new file mode 100644
index 0000000..790b714
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.3042,3044,3046,3048,304A,304B.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.3042,3044,3046,73E0,5EA6,8F38.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.3042,3044,3046,73E0,5EA6,8F38.otf
new file mode 100644
index 0000000..c707bcd
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.3042,3044,3046,73E0,5EA6,8F38.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.61,63,65,6B.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.61,63,65,6B.otf
new file mode 100644
index 0000000..591d139
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.61,63,65,6B.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.660E,6975,73E0,5EA6,8F38,6E05.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.660E,6975,73E0,5EA6,8F38,6E05.otf
new file mode 100644
index 0000000..efc98b6
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.660E,6975,73E0,5EA6,8F38,6E05.otf
Binary files differ
diff --git a/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.660E.otf b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.660E.otf
new file mode 100644
index 0000000..27c4676
--- /dev/null
+++ b/test/subset/data/expected/japanese/SourceHanSans-Regular.drop-hints.desubroutinize.660E.otf
Binary files differ
diff --git a/test/subset/data/fonts/SourceHanSans-Regular.otf b/test/subset/data/fonts/SourceHanSans-Regular.otf
new file mode 100755
index 0000000..dd807db
--- /dev/null
+++ b/test/subset/data/fonts/SourceHanSans-Regular.otf
Binary files differ
diff --git a/test/subset/data/fonts/SourceSansPro-Regular.otf b/test/subset/data/fonts/SourceSansPro-Regular.otf
new file mode 100644
index 0000000..279e691
--- /dev/null
+++ b/test/subset/data/fonts/SourceSansPro-Regular.otf
Binary files differ
diff --git a/test/subset/data/profiles/desubroutinize.txt b/test/subset/data/profiles/desubroutinize.txt
new file mode 100644
index 0000000..67f3a84
--- /dev/null
+++ b/test/subset/data/profiles/desubroutinize.txt
@@ -0,0 +1 @@
+--desubroutinize
diff --git a/test/subset/data/profiles/drop-hints-desubroutinize.txt b/test/subset/data/profiles/drop-hints-desubroutinize.txt
new file mode 100644
index 0000000..279d466
--- /dev/null
+++ b/test/subset/data/profiles/drop-hints-desubroutinize.txt
@@ -0,0 +1,2 @@
+--no-hinting
+--desubroutinize
diff --git a/test/subset/data/tests/full-font.tests b/test/subset/data/tests/full-font.tests
index ff195ce..f422ff5 100644
--- a/test/subset/data/tests/full-font.tests
+++ b/test/subset/data/tests/full-font.tests
@@ -1,5 +1,6 @@
 FONTS:
 Roboto-Regular.ttf
+SourceSansPro-Regular.otf
 
 PROFILES:
 default.txt
diff --git a/test/subset/data/tests/japanese.tests b/test/subset/data/tests/japanese.tests
index 5a04380..fc58646 100644
--- a/test/subset/data/tests/japanese.tests
+++ b/test/subset/data/tests/japanese.tests
@@ -1,5 +1,6 @@
 FONTS:
 Mplus1p-Regular.ttf
+SourceHanSans-Regular.otf
 
 PROFILES:
 default.txt
diff --git a/test/subset/run-tests.py b/test/subset/run-tests.py
index bc0d082..fb4684c 100755
--- a/test/subset/run-tests.py
+++ b/test/subset/run-tests.py
@@ -62,7 +62,7 @@
 	return 1
 
 def run_test(test, should_check_ots):
-	out_file = os.path.join(tempfile.mkdtemp (), test.get_font_name () + '-subset.ttf')
+	out_file = os.path.join(tempfile.mkdtemp (), test.get_font_name () + '-subset' + test.get_font_extension ())
 	cli_args = [hb_subset,
 		    "--font-file=" + test.font_path,
 		    "--output-file=" + out_file,
@@ -107,7 +107,7 @@
 def strip_check_sum (ttx_string):
 	return re.sub ('checkSumAdjustment value=["]0x([0-9a-fA-F])+["]',
 		       'checkSumAdjustment value="0x00000000"',
-		       ttx_string.decode (), count=1)
+		       ttx_string.decode ("utf-8"), count=1)
 
 def has_ots ():
 	if not ots_sanitize:
diff --git a/test/subset/subset_test_suite.py b/test/subset/subset_test_suite.py
index 3538650..73ac3d2 100644
--- a/test/subset/subset_test_suite.py
+++ b/test/subset/subset_test_suite.py
@@ -28,6 +28,14 @@
 				       self.unicodes(),
 				       font_base_name_parts[1])
 
+	def get_font_extension(self):
+		font_base_name = os.path.basename(self.font_path)
+		font_base_name_parts = os.path.splitext(font_base_name)
+		return font_base_name_parts[1]
+
+	def applicable(self):
+		return self.profile_path.find("desubroutinize") < 0 or self.get_font_extension() == "otf"
+
 # A group of tests to perform on the subsetter. Each test
 # Identifies a font a subsetting profile, and a subset to be cut.
 class SubsetTestSuite:
@@ -57,7 +65,9 @@
 			for profile in self.profiles:
 				profile = os.path.join(self._base_path(), "profiles", profile)
 				for subset in self.subsets:
-					yield Test(font, profile, subset)
+					test = Test(font, profile, subset)
+					if test.applicable():
+						yield test
 
 	def _base_path(self):
 		return os.path.dirname(os.path.dirname(self.test_path))
diff --git a/util/Makefile.am b/util/Makefile.am
index 85f9eda..e24a6f3 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -11,6 +11,8 @@
 # Convenience targets:
 lib:
 	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
+libs:
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src libs
 
 bin_PROGRAMS =
 
diff --git a/util/ansi-print.cc b/util/ansi-print.cc
index 5e2845c..49a0477 100644
--- a/util/ansi-print.cc
+++ b/util/ansi-print.cc
@@ -74,7 +74,7 @@
     color_t c = {(0xFFu<<24) | ((0xFFu*(x&1))<<16) | ((0xFFu*((x >> 1)&1))<<8) | (0xFFu*((x >> 2)&1))};
     return c;
   }
-  unsigned int to_ansi (void)
+  unsigned int to_ansi ()
   {
     return ((v >> 23) & 1) | ((v >> 14)&2) | ((v >> 5)&4);
   }
@@ -110,7 +110,7 @@
 		own_data (true),
 		data ((color_t *) malloc (sizeof (data[0]) * width * height)),
 		stride (width) {}
-  ~image_t (void)
+  ~image_t ()
   { if (own_data) free (data); }
 
   color_t &operator () (unsigned int x, unsigned int y)
@@ -161,7 +161,7 @@
 		height (height),
 		bg (0), fg (0), unicolor (true),
 		data ((uint8_t *) malloc (sizeof (data[0]) * width * height)) {}
-  ~biimage_t (void)
+  ~biimage_t ()
   { free (data); }
 
   void set (const image_t &image)
diff --git a/util/hb-fc.cc b/util/hb-fc.cc
index cb89991..2fe0146 100644
--- a/util/hb-fc.cc
+++ b/util/hb-fc.cc
@@ -72,7 +72,7 @@
 }
 
 static hb_font_funcs_t *
-_hb_fc_get_font_funcs (void)
+_hb_fc_get_font_funcs ()
 {
   static const hb_font_funcs_t *fc_ffuncs;
 
diff --git a/util/hb-shape.cc b/util/hb-shape.cc
index 337cd43..3ae3fa1 100644
--- a/util/hb-shape.cc
+++ b/util/hb-shape.cc
@@ -83,10 +83,7 @@
     if (format.trace)
       hb_buffer_set_message_func (buffer, message_func, this, nullptr);
   }
-  void new_line (void)
-  {
-    line_no++;
-  }
+  void new_line () { line_no++; }
   void consume_text (hb_buffer_t  *buffer,
 		     const char   *text,
 		     unsigned int  text_len,
@@ -160,6 +157,34 @@
 int
 main (int argc, char **argv)
 {
+  if (argc == 2 && !strcmp (argv[1], "--batch"))
+  {
+    unsigned int ret = 0;
+    char buf[4092];
+    while (fgets (buf, sizeof (buf), stdin))
+    {
+      size_t l = strlen (buf);
+      if (l && buf[l - 1] == '\n') buf[l - 1] = '\0';
+      main_font_text_t<shape_consumer_t<output_buffer_t>, FONT_SIZE_UPEM, 0> driver;
+      char *args[32];
+      argc = 0;
+      char *p = buf, *e;
+      args[argc++] = p;
+      while ((e = strchr (p, ' ')) && argc < (int) (int) ARRAY_LENGTH (args))
+      {
+	*e++ = '\0';
+	while (*e == ' ')
+	  e++;
+	args[argc++] = p = e;
+      }
+      ret |= driver.main (argc, args);
+      fflush (stdout);
+
+      if (ret)
+        break;
+    }
+    return ret;
+  }
   main_font_text_t<shape_consumer_t<output_buffer_t>, FONT_SIZE_UPEM, 0> driver;
   return driver.main (argc, argv);
 }
diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index 3f0963c..b7d9eb9 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -89,11 +89,13 @@
 
   void finish (const font_options_t *font_opts)
   {
+    hb_subset_input_set_drop_layout (input, !subset_options.keep_layout);
     hb_subset_input_set_drop_hints (input, subset_options.drop_hints);
+    hb_subset_input_set_desubroutinize (input, subset_options.desubroutinize);
 
     hb_face_t *face = hb_font_get_face (font);
 
-    hb_face_t *new_face = hb_subset(face, input);
+    hb_face_t *new_face = hb_subset (face, input);
     hb_blob_t *result = hb_face_reference_blob (new_face);
 
     failed = !hb_blob_get_length (result);
diff --git a/util/helper-cairo.cc b/util/helper-cairo.cc
index 7a698f3..b4f94a9 100644
--- a/util/helper-cairo.cc
+++ b/util/helper-cairo.cc
@@ -66,7 +66,7 @@
 
 #ifdef HAVE_ATEXIT
 static inline
-void free_ft_library (void)
+void free_ft_library ()
 {
   FT_Done_FreeType (ft_library);
 }
diff --git a/util/helper-cairo.hh b/util/helper-cairo.hh
index 1613ce4..5bfbf7b 100644
--- a/util/helper-cairo.hh
+++ b/util/helper-cairo.hh
@@ -60,7 +60,7 @@
   unsigned int num_clusters;
   cairo_text_cluster_flags_t cluster_flags;
 
-  void finish (void) {
+  void finish () {
     if (glyphs)
       cairo_glyph_free (glyphs);
     if (clusters)
diff --git a/util/main-font-text.hh b/util/main-font-text.hh
index 01bd2d4..36b654b 100644
--- a/util/main-font-text.hh
+++ b/util/main-font-text.hh
@@ -50,7 +50,7 @@
 template <typename consumer_t, int default_font_size, int subpixel_bits>
 struct main_font_text_t
 {
-  main_font_text_t (void)
+  main_font_text_t ()
 		  : options ("[FONT-FILE] [TEXT]"),
 		    font_opts (&options, default_font_size, subpixel_bits),
 		    input (&options),
diff --git a/util/options.cc b/util/options.cc
index 5661cd0..04ddcf6 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -62,7 +62,7 @@
 
 
 static gchar *
-shapers_to_string (void)
+shapers_to_string ()
 {
   GString *shapers = g_string_new (nullptr);
   const char **shaper_list = hb_shape_list_shapers ();
@@ -95,7 +95,7 @@
 
 
 void
-option_parser_t::add_main_options (void)
+option_parser_t::add_main_options ()
 {
   GOptionEntry entries[] =
   {
@@ -325,6 +325,7 @@
     return false;
   }
 
+  text_opts->text_len = -1;
   text_opts->text = g_strdup (arg);
   return true;
 }
@@ -370,6 +371,7 @@
     s = p;
   }
 
+  text_opts->text_len = gs->len;
   text_opts->text = g_string_free (gs, FALSE);
   return true;
 }
@@ -636,7 +638,7 @@
 
 
 hb_font_t *
-font_options_t::get_font (void) const
+font_options_t::get_font () const
 {
   if (font)
     return font;
@@ -727,7 +729,11 @@
 text_options_t::get_line (unsigned int *len)
 {
   if (text) {
-    if (!line) line = text;
+    if (!line)
+    {
+      line = text;
+      line_len = text_len;
+    }
     if (line_len == (unsigned int) -1)
       line_len = strlen (line);
 
@@ -789,7 +795,7 @@
 
 
 FILE *
-output_options_t::get_file_handle (void)
+output_options_t::get_file_handle ()
 {
   if (fp)
     return fp;
@@ -969,7 +975,10 @@
 {
   GOptionEntry entries[] =
   {
+    {"layout", 0, 0, G_OPTION_ARG_NONE,  &this->keep_layout,   "Keep OpenType Layout tables",   nullptr},
     {"no-hinting", 0, 0, G_OPTION_ARG_NONE,  &this->drop_hints,   "Whether to drop hints",   nullptr},
+    {"desubroutinize", 0, 0, G_OPTION_ARG_NONE,  &this->desubroutinize,   "Remove CFF/CFF2 use of subroutines",   nullptr},
+
     {nullptr}
   };
   parser->add_group (entries,
diff --git a/util/options.hh b/util/options.hh
index dd62859..d704519 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -54,7 +54,7 @@
 
 struct option_group_t
 {
-  virtual ~option_group_t (void) {}
+  virtual ~option_group_t () {}
 
   virtual void add_options (struct option_parser_t *parser) = 0;
 
@@ -74,14 +74,14 @@
 
     add_main_options ();
   }
-  ~option_parser_t (void)
+  ~option_parser_t ()
   {
     g_option_context_free (context);
     g_ptr_array_foreach (to_free, (GFunc) g_free, nullptr);
     g_ptr_array_free (to_free, TRUE);
   }
 
-  void add_main_options (void);
+  void add_main_options ();
 
   void add_group (GOptionEntry   *entries,
 		  const gchar    *name,
@@ -95,7 +95,7 @@
 
   void parse (int *argc, char ***argv);
 
-  G_GNUC_NORETURN void usage (void) {
+  G_GNUC_NORETURN void usage () {
     g_printerr ("Usage: %s [OPTION...] %s\n", g_get_prgname (), usage_str);
     exit (1);
   }
@@ -125,7 +125,7 @@
 
     add_options (parser);
   }
-  virtual ~view_options_t (void)
+  virtual ~view_options_t ()
   {
     g_free (fore);
     g_free (back);
@@ -161,7 +161,7 @@
 
     add_options (parser);
   }
-  virtual ~shape_options_t (void)
+  virtual ~shape_options_t ()
   {
     g_free (direction);
     g_free (language);
@@ -467,7 +467,7 @@
 
     add_options (parser);
   }
-  virtual ~font_options_t (void)
+  virtual ~font_options_t ()
   {
     g_free (font_file);
     free (variations);
@@ -477,7 +477,7 @@
 
   void add_options (option_parser_t *parser);
 
-  hb_font_t *get_font (void) const;
+  hb_font_t *get_font () const;
 
   char *font_file;
   mutable hb_blob_t *blob;
@@ -506,6 +506,7 @@
     text_before = nullptr;
     text_after = nullptr;
 
+    text_len = -1;
     text = nullptr;
     text_file = nullptr;
 
@@ -516,7 +517,7 @@
 
     add_options (parser);
   }
-  virtual ~text_options_t (void)
+  virtual ~text_options_t ()
   {
     g_free (text_before);
     g_free (text_after);
@@ -524,7 +525,7 @@
     g_free (text_file);
     if (gs)
       g_string_free (gs, true);
-    if (fp)
+    if (fp && fp != stdin)
       fclose (fp);
   }
 
@@ -535,13 +536,14 @@
       g_set_error (error,
 		   G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
 		   "Only one of text and text-file can be set");
-  };
+  }
 
   const char *get_line (unsigned int *len);
 
   char *text_before;
   char *text_after;
 
+  int text_len;
   char *text;
   char *text_file;
 
@@ -566,11 +568,11 @@
 
     add_options (parser);
   }
-  virtual ~output_options_t (void)
+  virtual ~output_options_t ()
   {
     g_free (output_file);
     g_free (output_format);
-    if (fp)
+    if (fp && fp != stdout)
       fclose (fp);
   }
 
@@ -594,7 +596,7 @@
       output_file = nullptr; /* STDOUT */
   }
 
-  FILE *get_file_handle (void);
+  FILE *get_file_handle ();
 
   char *output_file;
   char *output_format;
@@ -668,14 +670,18 @@
 {
   subset_options_t (option_parser_t *parser)
   {
+    keep_layout = false;
     drop_hints = false;
+    desubroutinize = false;
 
     add_options (parser);
   }
 
   void add_options (option_parser_t *parser);
 
+  hb_bool_t keep_layout;
   hb_bool_t drop_hints;
+  hb_bool_t desubroutinize;
 };
 
 /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */
diff --git a/util/view-cairo.hh b/util/view-cairo.hh
index 5be3523..1f51f0e 100644
--- a/util/view-cairo.hh
+++ b/util/view-cairo.hh
@@ -39,7 +39,7 @@
 		 view_options (parser),
 		 direction (HB_DIRECTION_INVALID),
 		 lines (0), scale_bits (0) {}
-  ~view_cairo_t (void) {
+  ~view_cairo_t () {
     cairo_debug_reset_static_data ();
   }
 
@@ -48,19 +48,13 @@
     lines = g_array_new (false, false, sizeof (helper_cairo_line_t));
     scale_bits = - (int) font_opts->subpixel_bits;
   }
-  void new_line (void)
-  {
-  }
+  void new_line () {}
   void consume_text (hb_buffer_t  *buffer,
 		     const char   *text,
 		     unsigned int  text_len,
-		     hb_bool_t     utf8_clusters)
-  {
-  }
+		     hb_bool_t     utf8_clusters) {}
   void error (const char *message)
-  {
-    g_printerr ("%s: %s\n", g_get_prgname (), message);
-  }
+  { g_printerr ("%s: %s\n", g_get_prgname (), message); }
   void consume_glyphs (hb_buffer_t  *buffer,
 		       const char   *text,
 		       unsigned int  text_len,