Merge "Revert "Revert "Update HarfBuzz to 2.0.2"""
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 5ad1ae1..e73f53c 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -27,15 +27,26 @@
       # Ignoring assembler complains, https://stackoverflow.com/a/39867021
       - run: make 2>&1 | grep -v -e '^/var/folders/*' -e '^[[:space:]]*\.section' -e '^[[:space:]]*\^[[:space:]]*~*'
 
+  macos-notest-ios:
+    macos:
+      xcode: "10.0.0"
+    steps:
+      - checkout
+      - run: brew update-reset
+      - run: 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
+      - run: cd build && xcodebuild -sdk iphoneos12.0 -configuration Release build -arch arm64
+
   distcheck:
     docker:
       - image: ubuntu:17.10
     steps:
       - checkout
-      - run: apt update && apt install -y ninja-build binutils libtool autoconf automake make cmake gcc g++ pkg-config ragel gtk-doc-tools libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
+      - run: apt update && apt install -y ninja-build binutils libtool autoconf automake make cmake gcc g++ pkg-config ragel gtk-doc-tools libfontconfig1-dev libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
       - run: pip install fonttools
       - run: ./autogen.sh
-      - run: make
+      - run: make -j32
       - run: make distcheck || .ci/fail.sh
       - run: rm -rf harfbuzz-*
       - run: make distdir && cd harfbuzz-* && cmake -DHB_CHECK=ON -Bbuild -H. -GNinja && ninja -Cbuild && CTEST_OUTPUT_ON_FAILURE=1 ninja -Cbuild test && ninja -Cbuild install
@@ -48,7 +59,7 @@
       - run: apk update && apk add ragel make pkgconfig libtool autoconf automake gettext gcc g++ glib-dev freetype-dev cairo-dev
       # C??FLAGS are not needed for a regular build
       - run: CFLAGS="-O3" CXXFLAGS="-O3 -DHB_NO_MMAP" ./autogen.sh
-      - run: make
+      - run: make -j32
       - run: make check || .ci/fail.sh
 
   archlinux-debug-O0-py3:
@@ -60,7 +71,7 @@
       - run: pip install fonttools
       # C??FLAGS are not needed for a regular build
       - run: CFLAGS="-O0" CXXFLAGS="-O0" CPPFLAGS="-DHB_DEBUG" ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2
-      - run: make
+      - run: make -j32
       - run: make check || .ci/fail.sh
 
   clang-O3-O0:
@@ -69,16 +80,114 @@
     steps:
       - checkout
       - run: apt update || true
-      - run: apt install -y ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
+      - run: apt install -y ragel libfreetype6-dev libfontconfig1-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
       - run: pip install fonttools
-      - run: wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure && make -j4 && cd ..
-      - run: CFLAGS="-O3" CXXFLAGS="-O3" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
-      - run: make
+      - run: wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure && make -j32 && cd ..
+      - run: CFLAGS="-O3" CXXFLAGS="-O3" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-fontconfig --with-glib --with-cairo --with-icu --with-graphite2
+      - run: make -j32
       - run: LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" make check || .ci/fail.sh
-      - run: CFLAGS="-O0" CXXFLAGS="-O0" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
-      - run: make
+      - run: CFLAGS="-O0" CXXFLAGS="-O0" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-fontconfig --with-glib --with-cairo --with-icu --with-graphite2
+      - run: make -j32
       - run: LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" make check || .ci/fail.sh
 
+  gcc-valgrind:
+    docker:
+      - image: ubuntu:18.10
+    steps:
+      - checkout
+      - run: apt update || true
+      - run: apt install -y gcc 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 valgrind
+      - run: pip install fonttools
+      - run: ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 --with-fontconfig
+      - run: make -j32
+      # run-shape-fuzzer-tests.py automatically runs valgrind if see available
+      # but test/api runs it by request, we probably should normalize the approaches
+      - run: RUN_VALGRIND=1 make check && make -Ctest/api check-valgrind || .ci/fail.sh
+      # informational for now
+      - run: make -Ctest/api check-symbols || true
+
+  clang-everything:
+    docker:
+      - image: ubuntu:18.10
+    steps:
+      - checkout
+      - run: apt update || true; apt install -y wget gnupg
+      - run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
+      - run: echo "deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdev.list
+      - run: echo "deb-src http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdevsrc.list
+      - 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: make -j32 CPPFLAGS="-Werror"
+      - run: make check CPPFLAGS="-Werror" || .ci/fail.sh
+
+  clang-asan:
+    docker:
+      - image: ubuntu:18.10
+    steps:
+      - checkout
+      - run: apt update || true; apt install -y wget gnupg
+      - run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
+      - run: echo "deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdev.list
+      - run: echo "deb-src http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdevsrc.list
+      - run: apt update || true
+      - run: apt install -y clang lld binutils libtool autoconf automake make pkg-config gtk-doc-tools ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
+      - run: pip install fonttools
+      - run: CPPFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=address -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=address -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
+      - run: make -j32
+      - run: make check || .ci/fail.sh | asan_symbolize | c++filt
+
+  clang-msan:
+    docker:
+      - image: ubuntu:18.10
+    steps:
+      - checkout
+      - run: apt update || true; apt install -y wget gnupg
+      - run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
+      - run: echo "deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdev.list
+      - run: echo "deb-src http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdevsrc.list
+      - run: apt update || true
+      - run: apt install -y clang lld binutils libtool autoconf automake gtk-doc-tools gettext make pkg-config ragel libcairo2-dev libicu-dev libmount-dev libgraphite2-dev python python-pip
+      - run: pip install fonttools
+      - run: update-alternatives --install "/usr/bin/ld" "ld" "/usr/bin/ld.lld" 10
+      - run: wget https://ftp.gnome.org/pub/gnome/sources/glib/2.58/glib-2.58.1.tar.xz && tar xf glib-2.58.1.tar.xz && cd glib-2.58.1 && ./autogen.sh --with-pcre CPPFLAGS="-fsanitize=memory" LDFLAGS="-fsanitize=memory" CFLAGS="-fsanitize=memory" CXXFLAGS="-fsanitize=memory" LD=ld.lld CC=clang CXX=clang++ && make -j32 && make install && cd ..
+      - run: wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure CPPFLAGS="-fsanitize=memory" LDFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ && make -j32 && make install && cd ..
+      - run: CPPFLAGS="-fsanitize=memory -fsanitize-memory-track-origins" LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --without-icu
+      - run: make -j32 && MSAN_OPTIONS=exitcode=42 make check || .ci/fail.sh | asan_symbolize | c++filt
+
+  clang-tsan:
+    docker:
+      - image: ubuntu:18.10
+    steps:
+      - checkout
+      - run: apt update || true; apt install -y wget gnupg
+      - run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
+      - run: echo "deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdev.list
+      - run: echo "deb-src http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdevsrc.list
+      - run: apt update || true
+      - run: apt install -y clang lld binutils libtool autoconf automake make pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
+      - run: pip install fonttools
+      - run: CPPFLAGS="-fsanitize=thread" LDFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
+      - run: make -j32
+      - run: make check || .ci/fail.sh | asan_symbolize | c++filt
+
+  clang-ubsan:
+    docker:
+      - image: ubuntu:18.10
+    steps:
+      - checkout
+      - run: apt update || true; apt install -y wget gnupg
+      - run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
+      - run: echo "deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdev.list
+      - run: echo "deb-src http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdevsrc.list
+      - run: apt update || true
+      - run: apt install -y clang lld binutils libtool autoconf automake make pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
+      - run: pip install fonttools
+      - run: CPPFLAGS="-fsanitize=undefined" LDFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
+      - run: make -j32
+      - run: make check || .ci/fail.sh | asan_symbolize | c++filt
+
   fedora-outoftreebuild:
     docker:
       - image: fedora
@@ -108,7 +217,7 @@
       - run: dnf install -y gcc ragel cmake make which glib2-devel freetype-devel cairo-devel libicu-devel graphite2-devel wget tar bzip2 python libnsl || true
       - run: wget http://$ODSUSER:$ODSPASS@behdad.org/harfbuzz-private/OracleDeveloperStudio12.6-linux-x86-bin.tar.bz2 && tar xf OracleDeveloperStudio12.6-linux-x86-bin.tar.bz2 --owner root --group root --no-same-owner
       - run: CC=/root/project/OracleDeveloperStudio12.6-linux-x86-bin/developerstudio12.6/bin/suncc CXX=/root/project/OracleDeveloperStudio12.6-linux-x86-bin/developerstudio12.6/bin/sunCC cmake -DHB_HAVE_GRAPHITE2=ON -DHB_BUILTIN_UCDN=ON -DHB_HAVE_GLIB=ON -DHB_HAVE_FREETYPE=ON -Bbuild -H.
-      - run: make -Cbuild
+      - run: make -Cbuild -j32
       - run: CTEST_OUTPUT_ON_FAILURE=1 make -Cbuild test
       - run: make -Cbuild install
 
@@ -119,7 +228,7 @@
       - checkout
       - run: apt update && apt install -y ragel pkg-config libtool autoconf
       - run: CFLAGS="-Wno-attributes" CXXFLAGS="-Wno-attributes" ./autogen.sh --prefix=/usr/local/djgpp --host=i586-pc-msdosdjgpp
-      - run: make
+      - run: make -j32
 
   crosscompile-notest-freebsd9:
     docker:
@@ -128,7 +237,7 @@
       - checkout
       - run: apt update && apt install -y pkg-config ragel
       - run: ./autogen.sh --prefix=/freebsd --host=x86_64-pc-freebsd9
-      - run: make
+      - run: make -j32
 
   crosscompile-notest-psvita:
     docker:
@@ -138,7 +247,7 @@
       - run: apt update && apt install ragel
       - run: git clone https://github.com/vitasdk/vdpm && cd vdpm && ./bootstrap-vitasdk.sh
       - run: ./autogen.sh --prefix=/usr/local/vitasdk/arm-vita-eabi --host=arm-vita-eabi
-      - run: make
+      - run: make -j32
 
   crosscompile-cmake-notest-android-arm:
     docker:
@@ -192,6 +301,7 @@
       # macOS
       - macos-llvm-gcc-4.2
       - macos-notest-apple-gcc-i686-4.2
+      - macos-notest-ios
 
       # both autotools and cmake
       - distcheck
@@ -199,7 +309,13 @@
       # autotools based builds
       - alpine-O3-NOMMAP
       - archlinux-debug-O0-py3
+      - gcc-valgrind
       - clang-O3-O0
+      - clang-everything
+      - clang-asan
+      - clang-msan
+      - clang-tsan
+      - clang-ubsan
       - fedora-outoftreebuild
 
       # cmake based builds
diff --git a/.travis.yml b/.travis.yml
index 83b479e..eadfa76 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -47,8 +47,6 @@
     - os: osx
       compiler: clang
       install:
-          # https://github.com/harfbuzz/harfbuzz/issues/345
-        - export CXXFLAGS="$CXXFLAGS -Wno-deprecated-declarations"
         - brew update;
           # Workaround Travis/brew bug
         - brew uninstall libtool && brew install libtool
diff --git a/AUTHORS b/AUTHORS
index 81cdc4c..0763761 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,9 +1,11 @@
 Behdad Esfahbod
-Simon Hausmann
-Martin Hosken
-Jonathan Kew
-Lars Knoll
-Werner Lemberg
-Roozbeh Pournader
-Owen Taylor
 David Turner
+Ebrahim Byagowi
+Jonathan Kew
+Khaled Hosny
+Lars Knoll
+Martin Hosken
+Owen Taylor
+Roozbeh Pournader
+Simon Hausmann
+Werner Lemberg
diff --git a/Android.bp b/Android.bp
index a205af8..14cecf8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -60,6 +60,7 @@
         },
     },
     srcs: [
+        "src/hb-aat-layout.cc",
         "src/hb-blob.cc",
         "src/hb-buffer-serialize.cc",
         "src/hb-buffer.cc",
@@ -74,6 +75,7 @@
         "src/hb-static.cc",
         "src/hb-unicode.cc",
         "src/hb-warning.cc",
+        "src/hb-ot-face.cc",
         "src/hb-ot-font.cc",
         "src/hb-ot-layout.cc",
         "src/hb-ot-map.cc",
@@ -88,7 +90,6 @@
         "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-tibetan.cc",
         "src/hb-ot-shape-complex-use.cc",
         "src/hb-ot-shape-complex-use-table.cc",
         "src/hb-ot-shape-normalize.cc",
diff --git a/BUILD.md b/BUILD.md
index 8a6b569..4c1c306 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -26,7 +26,7 @@
 library in `src/`, and a few utility programs including `hb-view` and `hb-shape`
 under `util/`.
 
-If you are bootstraping from git, you need a few more tools before you can
+If you are bootstrapping from git, you need a few more tools before you can
 run `autogen.sh` for the first time. Namely, `pkg-config` and `ragel`.
 
 Again, on Ubuntu / Debian:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e881dbd..4eb23af 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -82,12 +82,21 @@
   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
 )
 
-add_definitions(-DHAVE_OT)
 add_definitions(-DHAVE_FALLBACK)
 
 # We need PYTHON_EXECUTABLE to be set for running the tests...
@@ -98,10 +107,10 @@
 include (CheckIncludeFile)
 macro (check_funcs) # Similar to AC_CHECK_FUNCS of autotools
   foreach (func_name ${ARGN})
-    string(TOUPPER ${func_name} definiton_to_add)
-    check_function_exists(${func_name} HAVE_${definiton_to_add})
-    if (${HAVE_${definiton_to_add}})
-      add_definitions(-DHAVE_${definiton_to_add})
+    string(TOUPPER ${func_name} definition_to_add)
+    check_function_exists(${func_name} HAVE_${definition_to_add})
+    if (${HAVE_${definition_to_add}})
+      add_definitions(-DHAVE_${definition_to_add})
     endif ()
   endforeach ()
 endmacro ()
@@ -359,12 +368,32 @@
   list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-coretext.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-coretext.h)
 
-  find_library(APPLICATION_SERVICES_FRAMEWORK ApplicationServices)
-  if (APPLICATION_SERVICES_FRAMEWORK)
-    list(APPEND THIRD_PARTY_LIBS ${APPLICATION_SERVICES_FRAMEWORK})
-  endif (APPLICATION_SERVICES_FRAMEWORK)
+  if (HB_IOS)
+    find_library(COREFOUNDATION CoreFoundation)
+    if (COREFOUNDATION)
+      list(APPEND THIRD_PARTY_LIBS ${COREFOUNDATION})
+    endif ()
+    mark_as_advanced(COREFOUNDATION)
 
-  mark_as_advanced(APPLICATION_SERVICES_FRAMEWORK)
+    find_library(CORETEXT CoreText)
+    if (CORETEXT)
+      list(APPEND THIRD_PARTY_LIBS ${CORETEXT})
+    endif ()
+    mark_as_advanced(CORETEXT)
+
+    find_library(COREGRAPHICS CoreGraphics)
+    if (COREGRAPHICS)
+      list(APPEND THIRD_PARTY_LIBS ${COREGRAPHICS})
+    endif ()
+    mark_as_advanced(COREGRAPHICS)
+  else ()
+    find_library(APPLICATION_SERVICES_FRAMEWORK ApplicationServices)
+    if (APPLICATION_SERVICES_FRAMEWORK)
+      list(APPEND THIRD_PARTY_LIBS ${APPLICATION_SERVICES_FRAMEWORK})
+    endif ()
+
+    mark_as_advanced(APPLICATION_SERVICES_FRAMEWORK)
+  endif ()
 endif ()
 
 if (WIN32 AND HB_HAVE_UNISCRIBE)
@@ -527,12 +556,14 @@
 target_link_libraries(harfbuzz ${THIRD_PARTY_LIBS})
 
 ## Define harfbuzz-subset library
-add_library(harfbuzz-subset ${subset_project_sources} ${subset_project_headers})
-add_dependencies(harfbuzz-subset harfbuzz)
-target_link_libraries(harfbuzz-subset harfbuzz ${THIRD_PARTY_LIBS})
+if (NOT HB_DISABLE_SUBSET)
+  add_library(harfbuzz-subset ${subset_project_sources} ${subset_project_headers})
+  add_dependencies(harfbuzz-subset harfbuzz)
+  target_link_libraries(harfbuzz-subset harfbuzz ${THIRD_PARTY_LIBS})
 
-if (BUILD_SHARED_LIBS)
-  set_target_properties(harfbuzz harfbuzz-subset PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
+  if (BUILD_SHARED_LIBS)
+    set_target_properties(harfbuzz harfbuzz-subset PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
+  endif ()
 endif ()
 
 if (UNIX OR MINGW)
@@ -549,7 +580,9 @@
     set (CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "m") # libm
     set (CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "")
     set_target_properties(harfbuzz PROPERTIES LINKER_LANGUAGE C)
-    set_target_properties(harfbuzz-subset PROPERTIES LINKER_LANGUAGE C)
+    if (NOT HB_DISABLE_SUBSET)
+      set_target_properties(harfbuzz-subset PROPERTIES LINKER_LANGUAGE C)
+    endif ()
 
     # No threadsafe statics as we do it ourselves
     set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics")
@@ -574,7 +607,7 @@
 endif ()
 
 if (BUILD_SHARED_LIBS AND WIN32 AND NOT MINGW)
-  add_definitions("-DHB_EXTERN=__declspec(dllexport) extern")
+  add_definitions("-DHB_DLL_EXPORT")
 endif ()
 
 # On Windows, g-ir-scanner requires a DLL build in order for it to work
@@ -761,12 +794,22 @@
 
 if (NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL)
   install(TARGETS harfbuzz
+    EXPORT harfbuzzConfig
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     FRAMEWORK DESTINATION Library/Frameworks
   )
+  install(EXPORT harfbuzzConfig
+      NAMESPACE harfbuzz::
+      DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/harfbuzz
+  )
   if (HB_BUILD_UTILS)
+    if (WIN32 AND BUILD_SHARED_LIBS)
+      install(TARGETS harfbuzz-subset
+        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+      )
+    endif ()
     install(TARGETS hb-view
       RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     )
@@ -818,51 +861,53 @@
 endif ()
 
 
-## src/ executables
-foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges)
-  set (prog_name ${prog})
-  if (${prog_name} STREQUAL "test")
-    # test can not be used as a valid executable name on cmake, lets special case it
-    set (prog_name test-test)
-  endif ()
-  add_executable(${prog_name} ${PROJECT_SOURCE_DIR}/src/${prog}.cc)
-  target_link_libraries(${prog_name} harfbuzz ${THIRD_PARTY_LIBS})
-endforeach ()
-set_target_properties(hb-ot-tag PROPERTIES COMPILE_FLAGS "-DMAIN")
+if (NOT HB_DISABLE_TESTS)
+  ## src/ executables
+  foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges)
+    set (prog_name ${prog})
+    if (${prog_name} STREQUAL "test")
+      # test can not be used as a valid executable name on cmake, lets special case it
+      set (prog_name test-test)
+    endif ()
+    add_executable(${prog_name} ${PROJECT_SOURCE_DIR}/src/${prog}.cc)
+    target_link_libraries(${prog_name} harfbuzz ${THIRD_PARTY_LIBS})
+  endforeach ()
+  set_target_properties(hb-ot-tag PROPERTIES COMPILE_FLAGS "-DMAIN")
 
-## Tests
-if (UNIX OR MINGW)
-  if (BUILD_SHARED_LIBS)
-    # generate harfbuzz.def after build completion
-    add_custom_command(TARGET harfbuzz POST_BUILD
-      COMMAND "${PYTHON_EXECUTABLE}" ${PROJECT_SOURCE_DIR}/src/gen-def.py ${PROJECT_BINARY_DIR}/harfbuzz.def ${project_headers}
-      WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src)
+  ## Tests
+  if (UNIX OR MINGW)
+    if (BUILD_SHARED_LIBS)
+      # generate harfbuzz.def after build completion
+      add_custom_command(TARGET harfbuzz POST_BUILD
+        COMMAND "${PYTHON_EXECUTABLE}" ${PROJECT_SOURCE_DIR}/src/gen-def.py ${PROJECT_BINARY_DIR}/harfbuzz.def ${project_headers}
+        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src)
 
-    add_test(NAME check-static-inits.sh
-      COMMAND ${PROJECT_SOURCE_DIR}/src/check-static-inits.sh
-      WORKING_DIRECTORY ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/harfbuzz.dir/src # ugly hack
-    )
-    add_test(NAME check-libstdc++.sh COMMAND ${PROJECT_SOURCE_DIR}/src/check-libstdc++.sh)
-    add_test(NAME check-symbols.sh COMMAND ${PROJECT_SOURCE_DIR}/src/check-symbols.sh)
+      add_test(NAME check-static-inits.sh
+        COMMAND ${PROJECT_SOURCE_DIR}/src/check-static-inits.sh
+        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/harfbuzz.dir/src # ugly hack
+      )
+      add_test(NAME check-libstdc++.sh COMMAND ${PROJECT_SOURCE_DIR}/src/check-libstdc++.sh)
+      add_test(NAME check-symbols.sh COMMAND ${PROJECT_SOURCE_DIR}/src/check-symbols.sh)
 
+      set_tests_properties(
+        check-static-inits.sh check-libstdc++.sh check-symbols.sh
+        PROPERTIES
+          ENVIRONMENT "libs=.;srcdir=${PROJECT_SOURCE_DIR}/src"
+          SKIP_RETURN_CODE 77)
+    endif ()
+
+    add_test(NAME check-c-linkage-decls.sh COMMAND ./check-c-linkage-decls.sh)
+    add_test(NAME check-header-guards.sh COMMAND ./check-header-guards.sh)
+    add_test(NAME check-externs.sh COMMAND ./check-externs.sh)
+    add_test(NAME check-includes.sh COMMAND ./check-includes.sh)
     set_tests_properties(
-      check-static-inits.sh check-libstdc++.sh check-symbols.sh
+      check-c-linkage-decls.sh check-header-guards.sh check-externs.sh check-includes.sh
       PROPERTIES
-        ENVIRONMENT "libs=.;srcdir=${PROJECT_SOURCE_DIR}/src"
+        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src
         SKIP_RETURN_CODE 77)
   endif ()
 
-  add_test(NAME check-c-linkage-decls.sh COMMAND ./check-c-linkage-decls.sh)
-  add_test(NAME check-header-guards.sh COMMAND ./check-header-guards.sh)
-  add_test(NAME check-externs.sh COMMAND ./check-externs.sh)
-  add_test(NAME check-includes.sh COMMAND ./check-includes.sh)
-  set_tests_properties(
-    check-c-linkage-decls.sh check-header-guards.sh check-externs.sh check-includes.sh
-    PROPERTIES
-      WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src
-      SKIP_RETURN_CODE 77)
+  # Needs to come last so that variables defined above are passed to
+  # subdirectories.
+  add_subdirectory(test)
 endif ()
-
-# Needs to come last so that variables defined above are passed to
-# subdirectories.
-add_subdirectory(test)
diff --git a/NEWS b/NEWS
index 3ae857e..b8d3640 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,109 @@
+Overview of changes leading to 2.0.2
+Saturday, October 20, 2018
+====================================
+- Fix two minor memory access issues in AAT tables.
+
+
+Overview of changes leading to 2.0.1
+Friday, October 19, 2018
+====================================
+- Fix hb-version.h reported release version that went wrong (1.8.0)
+  with previous release.
+- Fix extrapolation in 'trak' table.
+- Fix hb-font infinite-recursion issue with some font funcs and
+  subclassed fonts.
+- Implement variation-kerning format in kerx table, although without
+  variation.
+- Fix return value of hb_map_is_empty().
+
+
+Overview of changes leading to 2.0.0
+Thursday, October 18, 2018
+====================================
+- Added AAT shaping support (morx/kerx/trak).
+  Automatically used if GSUB/GPOS are not available respectively.
+  Set HB_OPTIONS=aat env var to have morx/kerx preferred over
+  GSUB/GPOS.
+- Apply TrueType kern table internally, instead of relying on
+  hb_font_t callbacks.
+- Khmer shaper significantly rewritten to better match Uniscribe.
+- Indic3 tags ('dev3', etc) are passed to USE shaper.
+- .dfont Mac font containers implemented.
+- Script- and language-mapping revamped to better use BCP 47.
+- Misc USE and Indic fixes.
+- Misc everything fixes.
+- Too many things to list.  Biggest release since 0.9.1, with
+  over 500 commits in just over 5 weeks!  Didn't intend it to
+  be a big release.  Just happened to become.
+- hb-ft now locks underlying FT_Face during use.
+
+API changes:
+
+- Newly-created hb_font_t's now have our internal "hb-ot-font"
+  callbacks set on them, so they should work out of the box
+  without any callbacks set.  If callbacks are set, everything
+  is back to what it was before, the fallback callbacks are
+  null.  If you to get the internal implementation modified,
+  sub_font it.
+
+- New hb_font_funcs_set_nominal_glyphs_func() allows speeding
+  up character to glyph mapping.
+
+New API:
++HB_FEATURE_GLOBAL_START
++HB_FEATURE_GLOBAL_END
++hb_buffer_set_invisible_glyph()
++hb_buffer_get_invisible_glyph()
++hb_font_funcs_set_nominal_glyphs_func()
++hb_ot_layout_table_select_script()
++hb_ot_layout_script_select_language()
++hb_ot_layout_feature_get_name_ids()
++hb_ot_layout_feature_get_characters()
++hb_name_id_t
++HB_NAME_ID_INVALID
++HB_OT_MAX_TAGS_PER_SCRIPT
++hb_ot_tags_from_script_and_language()
++hb_ot_tags_to_script_and_language()
+
+Deprecated API:
+-hb_font_funcs_set_glyph_func()
+-hb_unicode_eastasian_width_func_t
+-hb_unicode_funcs_set_eastasian_width_func()
+-hb_unicode_eastasian_width()
+-hb_unicode_decompose_compatibility_func_t
+-HB_UNICODE_MAX_DECOMPOSITION_LEN
+-hb_unicode_funcs_set_decompose_compatibility_func()
+-hb_unicode_decompose_compatibility()
+-hb_font_funcs_set_glyph_h_kerning_func()
+-hb_font_funcs_set_glyph_v_kerning_func()
+-hb_font_get_glyph_h_kerning()
+-hb_font_get_glyph_v_kerning()
+-hb_font_get_glyph_kerning_for_direction()
+-hb_ot_layout_table_choose_script()
+-hb_ot_layout_script_find_language()
+-hb_ot_tags_from_script()
+-hb_ot_tag_from_language()
+
+
+Overview of changes leading to 1.9.0
+Monday, September 10, 2018
+====================================
+- Added 'cmap' API to hb_face_t.
+- Face-builder API.
+- hb-ot-font re-creation should be much leaner now, as the
+  font tables it uses are cached on hb_face_t now.
+- Internal source header file name changes:
+  hb-*-private.hh is renamed to hb-*.hh.
+
+New API:
++HB_UNICODE_MAX
++hb_face_collect_unicodes()
++hb_face_collect_variation_selectors()
++hb_face_collect_variation_unicodes()
++hb_face_builder_create()
++hb_face_builder_add_table()
+
+
 Overview of changes leading to 1.8.8
 Tuesday, August 14, 2018
 ====================================
diff --git a/README.python.md b/README.python.md
index 4c0ba9b..7cf091a 100644
--- a/README.python.md
+++ b/README.python.md
@@ -23,7 +23,7 @@
 $prefix/lib/girepository-* directory.
 
 Make sure you have pygobject installed.  Then check that the following
-import works in your Python interpretter:
+import works in your Python interpreter:
 
 ```python
 from gi.repository import HarfBuzz
diff --git a/README.version b/README.version
index 457bf26..1781d25 100644
--- a/README.version
+++ b/README.version
@@ -1,3 +1,3 @@
-URL: https://github.com/harfbuzz/harfbuzz/commit/63be5dcdde61275822d931b2924425478bc1dac1
-Version: 1.8.8
+URL: https://github.com/harfbuzz/harfbuzz/commit/0a3b7a0fb0734a66926dfda5d95d3cacea8890ce
+Version: 2.0.2
 BugComponent: 25699
diff --git a/README.wine.md b/README.wine.md
index 851d2bf..799eb63 100644
--- a/README.wine.md
+++ b/README.wine.md
@@ -1,6 +1,6 @@
 For the development of HarfBuzz, the Microsoft shaping technology, Uniscribe,
 as a widely used and tested shaper is used as more-or-less OpenType reference
-implemenetation and that specially is important where OpenType specification
+implementation and that specially is important where OpenType specification
 is or wasn't that clear. For having access to Uniscribe on Linux/macOS these
 steps are recommended:
 
@@ -27,8 +27,8 @@
 Now you can use hb-shape using `wine winbuild/util/hb-shape.exe` but if you like to
 to use the original Uniscribe,
 
-8. Bring a 32bit version of `usp10.dll` for youself from `C:\Windows\SysWOW64\usp10.dll` of your
-   Windows installation (asuming you have a 64-bit installation, otherwise `C:\Windows\System32\usp10.dll`)
+8. Bring a 32bit version of `usp10.dll` for yourself from `C:\Windows\SysWOW64\usp10.dll` of your
+   Windows installation (assuming you have a 64-bit installation, otherwise `C:\Windows\System32\usp10.dll`)
    that it is not a DirectWrite proxy ([for more info](https://en.wikipedia.org/wiki/Uniscribe)).
    Rule of thumb, your `usp10.dll` should have a size more than 500kb, otherwise
    it is designed to work with DirectWrite which Wine can't work with its original one.
diff --git a/RELEASING.md b/RELEASING.md
index d431871..4f5705e 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -27,7 +27,10 @@
    Otherwise, fix things and commit them separately before making release,
    Note: Check src/hb-version.h and make sure the new version number is
    there.  Sometimes, it does not get updated.  If that's the case,
-   "touch configure.ac" and rebuild.  TODO: debug.
+   "touch configure.ac" and rebuild.  Also check that there is no hb-version.h
+   in your build/src file. Typically it will fail the distcheck if there is.
+   That's what happened to 2.0.0 going out with 1.8.0 hb-version.h...  So, that's
+   a clue.
 
 7. "make release-files".  Enter your GPG password.  This creates a sha256 hash
    and signs it.
diff --git a/TODO b/TODO
index 6dac0be..d8e4105 100644
--- a/TODO
+++ b/TODO
@@ -1,9 +1,3 @@
-General fixes:
-=============
-
-- Implement 'rand' feature.
-
-
 API issues:
 ===========
 
@@ -19,11 +13,7 @@
 
 - Add hb-cairo glue
 
-- Add sanitize API (and a cached version, that saves result on blob user-data)
-
-- BCP 47 language handling / API (language_matches?)
-
-- Add hb_font_create_unscaled()?
+- Add sanitize API.
 
 - Add query / enumeration API for aalt-like features?
 
diff --git a/appveyor.yml b/appveyor.yml
index cc4acec..bf98219 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -28,9 +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 -Syu --noconfirm"'
+  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --force --noconfirm -Sy && pacman --noconfirm --force -S pacman-mirrors && pacman --force -Syu --noconfirm"'
   - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-ragel"
+  - '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'
 
 build_script:
   - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" vcpkg install glib:%triplet% freetype:%triplet% cairo:%triplet%'
@@ -44,13 +54,18 @@
   - '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 -Syyu mingw-w64-$MSYS2_ARCH-gcc"'
-  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S --needed mingw-w64-$MSYS2_ARCH-{freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config,python2}"'
+  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "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"'
+
 cache:
   - c:\tools\vcpkg\installed\
+  - '%CYGWIN_PREFIX%\var\cache\setup'
 
 notifications:
   - provider: Email
diff --git a/configure.ac b/configure.ac
index 55cc12b..a2d0992 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.8.8],
+        [2.0.2],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -12,6 +12,7 @@
 AM_INIT_AUTOMAKE([1.13.0 gnits tar-ustar dist-bzip2 no-dist-gzip -Wall no-define color-tests -Wno-portability])
 AM_SILENT_RULES([yes])
 AX_CODE_COVERAGE
+AC_USE_SYSTEM_EXTENSIONS
 
 # Initialize libtool
 m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
@@ -19,7 +20,6 @@
 LT_INIT([disable-static])
 
 # Check for programs
-AC_USE_SYSTEM_EXTENSIONS
 AC_PROG_CC
 AC_PROG_CC_C99
 AM_PROG_CC_C_O
@@ -148,12 +148,6 @@
 
 dnl ==========================================================================
 
-have_ot=true
-if $have_ot; then
-	AC_DEFINE(HAVE_OT, 1, [Have native OpenType Layout backend])
-fi
-AM_CONDITIONAL(HAVE_OT, $have_ot)
-
 have_fallback=true
 if $have_fallback; then
 	AC_DEFINE(HAVE_FALLBACK, 1, [Have simple TrueType Layout backend])
@@ -330,7 +324,7 @@
 			[Use the graphite2 library @<:@default=no@:>@])],,
 	[with_graphite2=no])
 have_graphite2=false
-GRAPHITE2_DEPS="graphite2"
+GRAPHITE2_DEPS="graphite2 >= 1.2.0"
 AC_SUBST(GRAPHITE2_DEPS)
 if test "x$with_graphite2" = "xyes" -o "x$with_graphite2" = "xauto"; then
 	PKG_CHECK_MODULES(GRAPHITE2, $GRAPHITE2_DEPS, have_graphite2=true, :)
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index 16b6627..fccfcb0 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -67,6 +67,8 @@
 hb_buffer_get_user_data
 hb_buffer_get_glyph_infos
 hb_buffer_get_glyph_positions
+hb_buffer_get_invisible_glyph
+hb_buffer_set_invisible_glyph
 hb_buffer_set_replacement_codepoint
 hb_buffer_get_replacement_codepoint
 hb_buffer_normalize_glyphs
@@ -153,7 +155,26 @@
 HB_SCRIPT_CANADIAN_ABORIGINAL
 hb_font_funcs_set_glyph_func
 hb_font_get_glyph_func_t
+hb_ot_layout_table_choose_script
+hb_ot_layout_table_find_script
+hb_ot_tag_from_language
+hb_ot_tags_from_script
 hb_set_invert
+hb_unicode_eastasian_width_func_t
+hb_unicode_eastasian_width
+hb_unicode_funcs_set_eastasian_width_func
+HB_UNICODE_MAX_DECOMPOSITION_LEN
+hb_unicode_decompose_compatibility_func_t
+hb_unicode_decompose_compatibility
+hb_unicode_funcs_set_decompose_compatibility_func
+hb_font_funcs_set_glyph_h_kerning_func
+hb_font_funcs_set_glyph_v_kerning_func
+hb_font_get_glyph_h_kerning
+hb_font_get_glyph_h_kerning_func_t
+hb_font_get_glyph_kerning_for_direction
+hb_font_get_glyph_kerning_func_t
+hb_font_get_glyph_v_kerning
+hb_font_get_glyph_v_kerning_func_t
 </SECTION>
 
 <SECTION>
@@ -170,6 +191,7 @@
 <SECTION>
 <FILE>hb-face</FILE>
 hb_face_count
+hb_face_t
 hb_face_create
 hb_face_create_for_tables
 hb_face_destroy
@@ -188,7 +210,11 @@
 hb_face_set_index
 hb_face_set_upem
 hb_face_set_user_data
-hb_face_t
+hb_face_collect_unicodes
+hb_face_collect_variation_selectors
+hb_face_collect_variation_unicodes
+hb_face_builder_create
+hb_face_builder_add_table
 </SECTION>
 
 <SECTION>
@@ -209,14 +235,13 @@
 hb_font_funcs_set_glyph_from_name_func
 hb_font_funcs_set_glyph_h_advance_func
 hb_font_funcs_set_glyph_h_advances_func
-hb_font_funcs_set_glyph_h_kerning_func
 hb_font_funcs_set_glyph_h_origin_func
 hb_font_funcs_set_glyph_name_func
 hb_font_funcs_set_glyph_v_advance_func
 hb_font_funcs_set_glyph_v_advances_func
-hb_font_funcs_set_glyph_v_kerning_func
 hb_font_funcs_set_glyph_v_origin_func
 hb_font_funcs_set_nominal_glyph_func
+hb_font_funcs_set_nominal_glyphs_func
 hb_font_funcs_set_user_data
 hb_font_funcs_set_variation_glyph_func
 hb_font_funcs_t
@@ -226,6 +251,7 @@
 hb_font_get_glyph_advance_for_direction
 hb_font_get_glyph_advance_func_t
 hb_font_get_glyph_advances_for_direction
+hb_font_get_glyph_advances_func_t
 hb_font_get_glyph_contour_point
 hb_font_get_glyph_contour_point_for_origin
 hb_font_get_glyph_contour_point_func_t
@@ -238,12 +264,8 @@
 hb_font_get_glyph_h_advance_func_t
 hb_font_get_glyph_h_advances
 hb_font_get_glyph_h_advances_func_t
-hb_font_get_glyph_h_kerning
-hb_font_get_glyph_h_kerning_func_t
 hb_font_get_glyph_h_origin
 hb_font_get_glyph_h_origin_func_t
-hb_font_get_glyph_kerning_for_direction
-hb_font_get_glyph_kerning_func_t
 hb_font_get_glyph_name
 hb_font_get_glyph_name_func_t
 hb_font_get_glyph_origin_for_direction
@@ -252,12 +274,12 @@
 hb_font_get_glyph_v_advance_func_t
 hb_font_get_glyph_v_advances
 hb_font_get_glyph_v_advances_func_t
-hb_font_get_glyph_v_kerning
-hb_font_get_glyph_v_kerning_func_t
 hb_font_get_glyph_v_origin
 hb_font_get_glyph_v_origin_func_t
 hb_font_get_nominal_glyph
 hb_font_get_nominal_glyph_func_t
+hb_font_get_nominal_glyphs
+hb_font_get_nominal_glyphs_func_t
 hb_font_get_parent
 hb_font_get_ppem
 hb_font_get_ptem
@@ -457,7 +479,9 @@
 HB_OT_TAG_JSTF
 hb_ot_layout_collect_lookups
 hb_ot_layout_collect_features
+hb_ot_layout_feature_get_characters
 hb_ot_layout_feature_get_lookups
+hb_ot_layout_feature_get_name_ids
 hb_ot_layout_feature_with_variations_get_lookups
 hb_ot_layout_get_attach_points
 hb_ot_layout_get_glyph_class
@@ -479,12 +503,12 @@
 hb_ot_layout_lookup_would_substitute
 hb_ot_layout_script_find_language
 hb_ot_layout_script_get_language_tags
-hb_ot_layout_table_choose_script
+hb_ot_layout_script_select_language
 hb_ot_layout_table_find_feature_variations
-hb_ot_layout_table_find_script
 hb_ot_layout_table_get_feature_tags
 hb_ot_layout_table_get_script_tags
 hb_ot_layout_table_get_lookup_count
+hb_ot_layout_table_select_script
 hb_ot_shape_plan_collect_lookups
 hb_ot_layout_language_get_required_feature_index
 <SUBSECTION Private>
@@ -532,12 +556,14 @@
 
 <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_from_language
 hb_ot_tag_to_language
 hb_ot_tag_to_script
-hb_ot_tags_from_script
+hb_ot_tags_from_script_and_language
+hb_ot_tags_to_script_and_language
 </SECTION>
 
 <SECTION>
@@ -576,6 +602,8 @@
 
 <SECTION>
 <FILE>hb-shape</FILE>
+HB_FEATURE_GLOBAL_END
+HB_FEATURE_GLOBAL_START
 hb_feature_t
 hb_feature_from_string
 hb_feature_to_string
@@ -602,16 +630,14 @@
 
 <SECTION>
 <FILE>hb-unicode</FILE>
-HB_UNICODE_MAX_DECOMPOSITION_LEN
+HB_UNICODE_MAX
 hb_unicode_combining_class
 hb_unicode_combining_class_func_t
 hb_unicode_combining_class_t
 hb_unicode_compose
 hb_unicode_compose_func_t
 hb_unicode_decompose
-hb_unicode_decompose_compatibility
 hb_unicode_decompose_func_t
-hb_unicode_eastasian_width
 hb_unicode_funcs_create
 hb_unicode_funcs_destroy
 hb_unicode_funcs_get_default
@@ -623,9 +649,7 @@
 hb_unicode_funcs_reference
 hb_unicode_funcs_set_combining_class_func
 hb_unicode_funcs_set_compose_func
-hb_unicode_funcs_set_decompose_compatibility_func
 hb_unicode_funcs_set_decompose_func
-hb_unicode_funcs_set_eastasian_width_func
 hb_unicode_funcs_set_general_category_func
 hb_unicode_funcs_set_mirroring_func
 hb_unicode_funcs_set_script_func
diff --git a/m4/pkg.m4 b/m4/pkg.m4
deleted file mode 100644
index 0048a3f..0000000
--- a/m4/pkg.m4
+++ /dev/null
@@ -1,157 +0,0 @@
-# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
-# 
-# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-# PKG_PROG_PKG_CONFIG([MIN-VERSION])
-# ----------------------------------
-AC_DEFUN([PKG_PROG_PKG_CONFIG],
-[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
-m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
-AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
-if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
-	AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
-fi
-if test -n "$PKG_CONFIG"; then
-	_pkg_min_version=m4_default([$1], [0.9.0])
-	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
-	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
-		AC_MSG_RESULT([yes])
-	else
-		AC_MSG_RESULT([no])
-		PKG_CONFIG=""
-	fi
-		
-fi[]dnl
-])# PKG_PROG_PKG_CONFIG
-
-# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
-#
-# Check to see whether a particular set of modules exists.  Similar
-# to PKG_CHECK_MODULES(), but does not set variables or print errors.
-#
-#
-# Similar to PKG_CHECK_MODULES, make sure that the first instance of
-# this or PKG_CHECK_MODULES is called, or make sure to call
-# PKG_CHECK_EXISTS manually
-# --------------------------------------------------------------
-AC_DEFUN([PKG_CHECK_EXISTS],
-[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
-if test -n "$PKG_CONFIG" && \
-    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
-  m4_ifval([$2], [$2], [:])
-m4_ifvaln([$3], [else
-  $3])dnl
-fi])
-
-
-# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
-# ---------------------------------------------
-m4_define([_PKG_CONFIG],
-[if test -n "$PKG_CONFIG"; then
-    if test -n "$$1"; then
-        pkg_cv_[]$1="$$1"
-    else
-        PKG_CHECK_EXISTS([$3],
-                         [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
-			 [pkg_failed=yes])
-    fi
-else
-	pkg_failed=untried
-fi[]dnl
-])# _PKG_CONFIG
-
-# _PKG_SHORT_ERRORS_SUPPORTED
-# -----------------------------
-AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
-[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
-        _pkg_short_errors_supported=yes
-else
-        _pkg_short_errors_supported=no
-fi[]dnl
-])# _PKG_SHORT_ERRORS_SUPPORTED
-
-
-# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
-# [ACTION-IF-NOT-FOUND])
-#
-#
-# Note that if there is a possibility the first call to
-# PKG_CHECK_MODULES might not happen, you should be sure to include an
-# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
-#
-#
-# --------------------------------------------------------------
-AC_DEFUN([PKG_CHECK_MODULES],
-[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
-AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
-AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
-
-pkg_failed=no
-AC_MSG_CHECKING([for $1])
-
-_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
-_PKG_CONFIG([$1][_LIBS], [libs], [$2])
-
-m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
-and $1[]_LIBS to avoid the need to call pkg-config.
-See the pkg-config man page for more details.])
-
-if test $pkg_failed = yes; then
-        _PKG_SHORT_ERRORS_SUPPORTED
-        if test $_pkg_short_errors_supported = yes; then
-	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"`
-        else 
-	        $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
-        fi
-	# Put the nasty error message in config.log where it belongs
-	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
-
-	ifelse([$4], , [AC_MSG_ERROR(dnl
-[Package requirements ($2) were not met:
-
-$$1_PKG_ERRORS
-
-Consider adjusting the PKG_CONFIG_PATH environment variable if you
-installed software in a non-standard prefix.
-
-_PKG_TEXT
-])],
-		[AC_MSG_RESULT([no])
-                $4])
-elif test $pkg_failed = untried; then
-	ifelse([$4], , [AC_MSG_FAILURE(dnl
-[The pkg-config script could not be found or is too old.  Make sure it
-is in your PATH or set the PKG_CONFIG environment variable to the full
-path to pkg-config.
-
-_PKG_TEXT
-
-To get pkg-config, see <http://pkg-config.freedesktop.org/>.])],
-		[$4])
-else
-	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
-	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
-        AC_MSG_RESULT([yes])
-	ifelse([$3], , :, [$3])
-fi[]dnl
-])# PKG_CHECK_MODULES
diff --git a/src/Makefile.am b/src/Makefile.am
index 9e7fd29..e0ea1c5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,7 +13,7 @@
 check_PROGRAMS =
 
 # Convenience targets:
-lib: $(BUILT_SOURCES) libharfbuzz.la libharfbuzz-subset.la
+lib: $(BUILT_SOURCES) libharfbuzz.la
 libs: $(BUILT_SOURCES) $(lib_LTLIBRARIES)
 fuzzing: $(BUILT_SOURCES) libharfbuzz-fuzzing.la libharfbuzz-subset-fuzzing.la
 
@@ -29,11 +29,9 @@
 HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources)
 HBHEADERS = $(HB_BASE_headers)
 
-if HAVE_OT
 HBSOURCES += $(HB_OT_sources)
 HBSOURCES += $(HB_OT_RAGEL_GENERATED_sources)
 HBHEADERS += $(HB_OT_headers)
-endif
 
 if HAVE_FALLBACK
 HBSOURCES += $(HB_FALLBACK_sources)
@@ -271,7 +269,7 @@
 CLEANFILES += $(pkgconfig_DATA)
 
 
-DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def
+DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def harfbuzz-deprecated.def
 if HAVE_GOBJECT
 DEF_FILES += harfbuzz-gobject.def
 endif
@@ -285,17 +283,22 @@
 	$(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 "$@" $^
 
 
 GENERATORS = \
 	gen-arabic-table.py \
-	gen-indic-table.py \
-	gen-use-table.py \
 	gen-def.py \
+	gen-emoji-table.py \
+	gen-indic-table.py \
+	gen-os2-unicode-ranges.py \
+	gen-tag-table.py \
+	gen-use-table.py \
 	$(NULL)
 EXTRA_DIST += $(GENERATORS)
 
-unicode-tables: arabic-table indic-table use-table
+unicode-tables: arabic-table indic-table tag-table use-table emoji-table
 
 arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-arabic-table.hh \
@@ -305,13 +308,21 @@
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-indic-table.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-indic-table.cc; false)
 
+tag-table: gen-tag-table.py languagetags language-subtag-registry
+	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-tag-table.hh \
+	|| ($(RM) $(srcdir)/hb-ot-tag-table.hh; false)
+
 use-table: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-use-table.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-use-table.cc; false)
 
+emoji-table: gen-emoji-table.py emoji-data.txt
+	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-unicode-emoji-table.hh \
+	|| ($(RM) $(srcdir)/hb-unicode-emoji-table.hh; false)
+
 built-sources: $(BUILT_SOURCES)
 
-.PHONY: unicode-tables arabic-table indic-table use-table built-sources
+.PHONY: unicode-tables arabic-table indic-table tag-table use-table emoji-table built-sources
 
 RAGEL_GENERATED = \
 	$(patsubst %,$(srcdir)/%,$(HB_BASE_RAGEL_GENERATED_sources)) \
diff --git a/src/Makefile.sources b/src/Makefile.sources
index b981b0e..59cde6b 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -1,28 +1,29 @@
 # Base and default-included sources and headers
 
 HB_BASE_sources = \
-	hb-atomic-private.hh \
-	hb-blob-private.hh \
+	hb-atomic.hh \
+	hb-blob.hh \
 	hb-blob.cc \
-	hb-buffer-private.hh \
+	hb-buffer.hh \
 	hb-buffer-serialize.cc \
 	hb-buffer.cc \
+	hb-cache.hh \
 	hb-common.cc \
 	hb-debug.hh \
 	hb-dsalgs.hh \
-	hb-face-private.hh \
+	hb-face.hh \
 	hb-face.cc \
-	hb-font-private.hh \
+	hb-font.hh \
 	hb-font.cc \
-	hb-iter-private.hh \
-	hb-map-private.hh \
+	hb-iter.hh \
+	hb-map.hh \
 	hb-map.cc \
-	hb-machinery-private.hh \
-	hb-mutex-private.hh \
+	hb-machinery.hh \
+	hb-mutex.hh \
 	hb-null.hh \
-	hb-object-private.hh \
-	hb-open-file-private.hh \
-	hb-open-type-private.hh \
+	hb-object.hh \
+	hb-open-file.hh \
+	hb-open-type.hh \
 	hb-ot-color-cbdt-table.hh \
 	hb-ot-cmap-table.hh \
 	hb-ot-glyf-table.hh \
@@ -37,24 +38,26 @@
 	hb-ot-os2-unicode-ranges.hh \
 	hb-ot-post-macroman.hh \
 	hb-ot-post-table.hh \
+	hb-ot-tag-table.hh \
 	hb-ot-tag.cc \
-	hb-private.hh \
-	hb-set-digest-private.hh \
-	hb-set-private.hh \
+	hb.hh \
+	hb-set-digest.hh \
+	hb-set.hh \
 	hb-set.cc \
 	hb-shape.cc \
-	hb-shape-plan-private.hh \
+	hb-shape-plan.hh \
 	hb-shape-plan.cc \
 	hb-shaper-list.hh \
-	hb-shaper-impl-private.hh \
-	hb-shaper-private.hh \
+	hb-shaper-impl.hh \
+	hb-shaper.hh \
 	hb-shaper.cc \
 	hb-static.cc \
 	hb-string-array.hh \
-	hb-unicode-private.hh \
+	hb-unicode.hh \
+	hb-unicode-emoji-table.hh \
 	hb-unicode.cc \
-	hb-vector-private.hh \
-	hb-utf-private.hh \
+	hb-vector.hh \
+	hb-utf.hh \
 	hb-warning.cc \
 	$(NULL)
 
@@ -89,61 +92,62 @@
 
 HB_OT_sources = \
 	hb-aat-layout.cc \
-	hb-aat-layout-common-private.hh \
+	hb-aat-layout-common.hh \
 	hb-aat-layout-ankr-table.hh \
 	hb-aat-layout-bsln-table.hh \
 	hb-aat-layout-feat-table.hh \
 	hb-aat-layout-kerx-table.hh \
 	hb-aat-layout-morx-table.hh \
 	hb-aat-layout-trak-table.hh \
-	hb-aat-layout-private.hh \
+	hb-aat-layout.hh \
 	hb-aat-ltag-table.hh \
+	hb-ot-face.hh \
+	hb-ot-face.cc \
 	hb-ot-font.cc \
 	hb-ot-layout.cc \
 	hb-ot-layout-base-table.hh \
-	hb-ot-layout-common-private.hh \
+	hb-ot-layout-common.hh \
 	hb-ot-layout-gdef-table.hh \
 	hb-ot-layout-gpos-table.hh \
-	hb-ot-layout-gsubgpos-private.hh \
+	hb-ot-layout-gsubgpos.hh \
 	hb-ot-layout-gsub-table.hh \
 	hb-ot-layout-jstf-table.hh \
-	hb-ot-layout-private.hh \
+	hb-ot-layout.hh \
 	hb-ot-color.cc \
 	hb-ot-color-colr-table.hh \
 	hb-ot-color-cpal-table.hh \
 	hb-ot-color-sbix-table.hh \
 	hb-ot-color-svg-table.hh \
 	hb-ot-map.cc \
-	hb-ot-map-private.hh \
+	hb-ot-map.hh \
 	hb-ot-math.cc \
 	hb-ot-math-table.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-complex-arabic.cc \
 	hb-ot-shape-complex-arabic-fallback.hh \
-	hb-ot-shape-complex-arabic-private.hh \
+	hb-ot-shape-complex-arabic.hh \
 	hb-ot-shape-complex-arabic-table.hh \
 	hb-ot-shape-complex-arabic-win1256.hh \
 	hb-ot-shape-complex-default.cc \
 	hb-ot-shape-complex-hangul.cc \
 	hb-ot-shape-complex-hebrew.cc \
 	hb-ot-shape-complex-indic.cc \
-	hb-ot-shape-complex-indic-private.hh \
+	hb-ot-shape-complex-indic.hh \
 	hb-ot-shape-complex-indic-table.cc \
-	hb-ot-shape-complex-khmer-private.hh \
+	hb-ot-shape-complex-khmer.hh \
 	hb-ot-shape-complex-khmer.cc \
-	hb-ot-shape-complex-myanmar-private.hh \
+	hb-ot-shape-complex-myanmar.hh \
 	hb-ot-shape-complex-myanmar.cc \
 	hb-ot-shape-complex-thai.cc \
-	hb-ot-shape-complex-tibetan.cc \
 	hb-ot-shape-complex-use.cc \
-	hb-ot-shape-complex-use-private.hh \
+	hb-ot-shape-complex-use.hh \
 	hb-ot-shape-complex-use-table.cc \
-	hb-ot-shape-complex-private.hh \
-	hb-ot-shape-normalize-private.hh \
+	hb-ot-shape-complex.hh \
+	hb-ot-shape-normalize.hh \
 	hb-ot-shape-normalize.cc \
-	hb-ot-shape-fallback-private.hh \
+	hb-ot-shape-fallback.hh \
 	hb-ot-shape-fallback.cc \
-	hb-ot-shape-private.hh \
+	hb-ot-shape.hh \
 	hb-ot-var.cc \
 	hb-ot-var-avar-table.hh \
 	hb-ot-var-fvar-table.hh \
@@ -169,6 +173,7 @@
 	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 \
@@ -207,16 +212,17 @@
 HB_SUBSET_sources = \
 	hb-static.cc \
 	hb-subset.cc \
+	hb-subset.hh \
 	hb-subset-glyf.cc \
+	hb-subset-glyf.hh \
 	hb-subset-input.cc \
+	hb-subset-input.hh \
 	hb-subset-plan.cc \
+	hb-subset-plan.hh \
 	$(NULL)
 
 HB_SUBSET_headers = \
 	hb-subset.h \
-	hb-subset-glyf.hh \
-	hb-subset-plan.hh \
-	hb-subset-private.hh \
 	$(NULL)
 
 HB_GOBJECT_DIST_sources = hb-gobject-structs.cc
diff --git a/src/check-includes.sh b/src/check-includes.sh
index fd565da..f938f70 100755
--- a/src/check-includes.sh
+++ b/src/check-includes.sh
@@ -23,14 +23,14 @@
 grep . >&2 && stat=1
 
 
-echo 'Checking that source files #include "hb-*private.hh" first (or none)'
+echo 'Checking that source files #include a private header first (or none)'
 
 for x in $HBSOURCES; do
 	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
-	grep '#.*\<include\>' "$x" /dev/null | grep -v 'include _' | head -n 1
+	grep '#.*\<include\>' "$x" /dev/null | head -n 1
 done |
-grep -v '"hb-.*private[.]hh"' |
-grep -v 'hb-private[.]hh:' |
+grep -v '"hb-.*[.]hh"' |
+grep -v 'hb[.]hh' |
 grep . >&2 && stat=1
 
 
diff --git a/src/check-static-inits.sh b/src/check-static-inits.sh
index 71551cb..def25c7 100755
--- a/src/check-static-inits.sh
+++ b/src/check-static-inits.sh
@@ -7,7 +7,6 @@
 test -z "$libs" && libs=.libs
 stat=0
 
-
 if which objdump 2>/dev/null >/dev/null; then
 	:
 else
@@ -31,7 +30,8 @@
 
 echo "Checking that no object file has lazy static C++ constructors/destructors or other such stuff"
 for obj in $OBJS; do
-	if objdump -t "$obj" | grep '__cxa_'; then
+	if objdump -t "$obj" | grep -q '__cxa_' && ! objdump -t "$obj" | grep -q __ubsan_handle; then
+		objdump -t "$obj" | grep '__cxa_'
 		echo "Ouch, $obj has lazy static C++ constructors/destructors or other such stuff"
 		stat=1
 	fi
diff --git a/src/check-symbols.sh b/src/check-symbols.sh
index d4eca50..cea8684 100755
--- a/src/check-symbols.sh
+++ b/src/check-symbols.sh
@@ -26,7 +26,7 @@
 		symprefix=
 		if test $suffix = dylib; then symprefix=_; fi
 
-		EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`"
+		EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRST] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`"
 
 		prefix=$symprefix`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
 
diff --git a/src/dump-emoji.cc b/src/dump-emoji.cc
index 6521469..f45bc31 100644
--- a/src/dump-emoji.cc
+++ b/src/dump-emoji.cc
@@ -45,8 +45,8 @@
 #include <stdlib.h>
 #include <stdio.h>
 
-void cbdt_callback (const uint8_t* data, unsigned int length,
-                    unsigned int group, unsigned int gid)
+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);
@@ -55,8 +55,8 @@
   fclose (f);
 }
 
-void sbix_callback (const uint8_t* data, unsigned int length,
-                    unsigned int group, unsigned int gid)
+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);
@@ -65,8 +65,8 @@
   fclose (f);
 }
 
-void svg_callback (const uint8_t* data, unsigned int length,
-                   unsigned int start_glyph, unsigned int end_glyph)
+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)
@@ -83,8 +83,8 @@
   fclose (f);
 }
 
-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)
+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)
   {
@@ -146,7 +146,7 @@
 	  int r = (color >> 8) & 0xFF;
 	  int g = (color >> 16) & 0xFF;
 	  int b = (color >> 24) & 0xFF;
-	  cairo_set_source_rgba (cr, r / 255.f, g / 255.f, b / 255.f, alpha);
+	  cairo_set_source_rgba (cr, r / 255., g / 255., b / 255., alpha);
 
 	  cairo_glyph_t glyph;
 	  glyph.index = glyph_id;
@@ -162,7 +162,8 @@
   }
 }
 
-void dump_glyphs (cairo_font_face_t *cairo_face, unsigned int upem, unsigned int num_glyphs)
+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
@@ -210,10 +211,29 @@
 int main (int argc, char **argv)
 {
   if (argc != 2) {
-    fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]);
+    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);
diff --git a/src/dump-indic-data.cc b/src/dump-indic-data.cc
index d574138..a506889 100644
--- a/src/dump-indic-data.cc
+++ b/src/dump-indic-data.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-indic-private.hh"
+#include "hb-ot-shape-complex-indic.hh"
 
 int
 main (void)
diff --git a/src/dump-khmer-data.cc b/src/dump-khmer-data.cc
index 7dd09b2..1e79a97 100644
--- a/src/dump-khmer-data.cc
+++ b/src/dump-khmer-data.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-khmer-private.hh"
+#include "hb-ot-shape-complex-khmer.hh"
 
 int
 main (void)
@@ -34,10 +34,8 @@
     hb_glyph_info_t info;
     info.codepoint = u;
     set_khmer_properties (info);
-    if (info.khmer_category() != INDIC_SYLLABIC_CATEGORY_OTHER ||
-	info.khmer_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE)
-      printf("U+%04X	%u	%u\n", u,
-	     info.khmer_category(),
-	     info.khmer_position());
+    if (info.khmer_category() != INDIC_SYLLABIC_CATEGORY_OTHER)
+      printf("U+%04X	%u\n", u,
+	     info.khmer_category());
   }
 }
diff --git a/src/dump-myanmar-data.cc b/src/dump-myanmar-data.cc
index 2df9cd9..9f8b12e 100644
--- a/src/dump-myanmar-data.cc
+++ b/src/dump-myanmar-data.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-myanmar-private.hh"
+#include "hb-ot-shape-complex-myanmar.hh"
 
 int
 main (void)
diff --git a/src/dump-use-data.cc b/src/dump-use-data.cc
index 0e64688..4a8b258 100644
--- a/src/dump-use-data.cc
+++ b/src/dump-use-data.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-use-private.hh"
+#include "hb-ot-shape-complex-use.hh"
 
 int
 main (void)
diff --git a/src/gen-emoji-table.py b/src/gen-emoji-table.py
new file mode 100755
index 0000000..278e0b2
--- /dev/null
+++ b/src/gen-emoji-table.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+
+from __future__ import print_function, division, absolute_import
+import sys
+import os.path
+from collections import OrderedDict
+
+if len (sys.argv) != 2:
+	print("usage: ./gen-emoji-table.py emoji-data.txt", file=sys.stderr)
+	sys.exit (1)
+
+f = open(sys.argv[1])
+header = [f.readline () for _ in range(10)]
+
+sets = OrderedDict()
+for line in f.readlines():
+	line = line.strip()
+	if not line or line[0] == '#':
+		continue
+	rang, typ = [s.strip() for s in line.split('#')[0].split(';')[:2]]
+
+	rang = [int(s, 16) for s in rang.split('..')]
+	if len(rang) > 1:
+		start, end = rang
+	else:
+		start = end = rang[0]
+
+	if typ not in sets:
+		sets[typ] = set()
+	sets[typ].add((start, end))
+
+
+
+print ("/* == Start of generated table == */")
+print ("/*")
+print (" * The following tables are generated by running:")
+print (" *")
+print (" *   ./gen-emoji-table.py emoji-data.txt")
+print (" *")
+print (" * on file with this header:")
+print (" *")
+for l in header:
+	print (" * %s" % (l.strip()))
+print (" */")
+print ()
+print ("#ifndef HB_UNICODE_EMOJI_TABLE_HH")
+print ("#define HB_UNICODE_EMOJI_TABLE_HH")
+print ()
+print ('#include "hb-unicode.hh"')
+print ()
+
+for typ,s in sets.items():
+	if typ != "Extended_Pictographic": continue
+	print()
+	print("static const struct hb_unicode_range_t _hb_unicode_emoji_%s_table[] =" % typ)
+	print("{")
+	for pair in sorted(s):
+		print("  {0x%04X, 0x%04X}," % pair)
+	print("};")
+
+print ()
+print ("#endif /* HB_UNICODE_EMOJI_TABLE_HH */")
+print ()
+print ("/* == End of generated table == */")
diff --git a/src/gen-indic-table.py b/src/gen-indic-table.py
index 6252664..e65b981 100755
--- a/src/gen-indic-table.py
+++ b/src/gen-indic-table.py
@@ -102,7 +102,7 @@
 		print (" * %s" % (l.strip()))
 print (" */")
 print ()
-print ('#include "hb-ot-shape-complex-indic-private.hh"')
+print ('#include "hb-ot-shape-complex-indic.hh"')
 print ()
 
 # Shorten values
diff --git a/src/gen-unicode-ranges.py b/src/gen-os2-unicode-ranges.py
similarity index 96%
rename from src/gen-unicode-ranges.py
rename to src/gen-os2-unicode-ranges.py
index 30249a8..d768313 100644
--- a/src/gen-unicode-ranges.py
+++ b/src/gen-os2-unicode-ranges.py
@@ -13,7 +13,7 @@
 reload(sys)
 sys.setdefaultencoding('utf-8')
 
-print ("""static Range os2UnicodeRangesSorted[] =
+print ("""static OS2Range _hb_os2_unicode_ranges[] =
 {""")
 
 args = sys.argv[1:]
diff --git a/src/gen-tag-table.py b/src/gen-tag-table.py
new file mode 100755
index 0000000..1300462
--- /dev/null
+++ b/src/gen-tag-table.py
@@ -0,0 +1,1126 @@
+#!/usr/bin/python
+
+"""Generator of the mapping from OpenType tags to BCP 47 tags and vice
+versa.
+
+It creates a ``const LangTag[]``, matching the tags from the OpenType
+languages system tag list to the language subtags of the BCP 47 language
+subtag registry, with some manual adjustments. The mappings are
+supplemented with macrolanguages' sublanguages and retired codes'
+replacements, according to BCP 47 and some manual additions where BCP 47
+omits a retired code entirely.
+
+Also generated is a function, ``hb_ot_ambiguous_tag_to_language``,
+intended for use by ``hb_ot_tag_to_language``. It maps OpenType tags
+back to BCP 47 tags. Ambiguous OpenType tags (those that correspond to
+multiple BCP 47 tags) are listed here, except when the alphabetically
+first BCP 47 tag happens to be the chosen disambiguated tag. In that
+case, the fallback behavior will choose the right tag anyway.
+"""
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import collections
+try:
+	from HTMLParser import HTMLParser
+	def write (s):
+		print (s.encode ('utf-8'), end='')
+except ImportError:
+	from html.parser import HTMLParser
+	def write (s):
+		sys.stdout.flush ()
+		sys.stdout.buffer.write (s.encode ('utf-8'))
+import io
+import itertools
+import re
+import sys
+import unicodedata
+
+if len (sys.argv) != 3:
+	print ('usage: ./gen-tag-table.py languagetags language-subtag-registry', file=sys.stderr)
+	sys.exit (1)
+
+try:
+	from html import unescape
+	def html_unescape (parser, entity):
+		return unescape (entity)
+except ImportError:
+	def html_unescape (parser, entity):
+		return parser.unescape (entity)
+
+def expect (condition, message=None):
+	if not condition:
+		if message is None:
+			raise AssertionError
+		raise AssertionError (message)
+
+# from http://www-01.sil.org/iso639-3/iso-639-3.tab
+ISO_639_3_TO_1 = {
+	'aar': 'aa',
+	'abk': 'ab',
+	'afr': 'af',
+	'aka': 'ak',
+	'amh': 'am',
+	'ara': 'ar',
+	'arg': 'an',
+	'asm': 'as',
+	'ava': 'av',
+	'ave': 'ae',
+	'aym': 'ay',
+	'aze': 'az',
+	'bak': 'ba',
+	'bam': 'bm',
+	'bel': 'be',
+	'ben': 'bn',
+	'bis': 'bi',
+	'bod': 'bo',
+	'bos': 'bs',
+	'bre': 'br',
+	'bul': 'bg',
+	'cat': 'ca',
+	'ces': 'cs',
+	'cha': 'ch',
+	'che': 'ce',
+	'chu': 'cu',
+	'chv': 'cv',
+	'cor': 'kw',
+	'cos': 'co',
+	'cre': 'cr',
+	'cym': 'cy',
+	'dan': 'da',
+	'deu': 'de',
+	'div': 'dv',
+	'dzo': 'dz',
+	'ell': 'el',
+	'eng': 'en',
+	'epo': 'eo',
+	'est': 'et',
+	'eus': 'eu',
+	'ewe': 'ee',
+	'fao': 'fo',
+	'fas': 'fa',
+	'fij': 'fj',
+	'fin': 'fi',
+	'fra': 'fr',
+	'fry': 'fy',
+	'ful': 'ff',
+	'gla': 'gd',
+	'gle': 'ga',
+	'glg': 'gl',
+	'glv': 'gv',
+	'grn': 'gn',
+	'guj': 'gu',
+	'hat': 'ht',
+	'hau': 'ha',
+	'hbs': 'sh',
+	'heb': 'he',
+	'her': 'hz',
+	'hin': 'hi',
+	'hmo': 'ho',
+	'hrv': 'hr',
+	'hun': 'hu',
+	'hye': 'hy',
+	'ibo': 'ig',
+	'ido': 'io',
+	'iii': 'ii',
+	'iku': 'iu',
+	'ile': 'ie',
+	'ina': 'ia',
+	'ind': 'id',
+	'ipk': 'ik',
+	'isl': 'is',
+	'ita': 'it',
+	'jav': 'jv',
+	'jpn': 'ja',
+	'kal': 'kl',
+	'kan': 'kn',
+	'kas': 'ks',
+	'kat': 'ka',
+	'kau': 'kr',
+	'kaz': 'kk',
+	'khm': 'km',
+	'kik': 'ki',
+	'kin': 'rw',
+	'kir': 'ky',
+	'kom': 'kv',
+	'kon': 'kg',
+	'kor': 'ko',
+	'kua': 'kj',
+	'kur': 'ku',
+	'lao': 'lo',
+	'lat': 'la',
+	'lav': 'lv',
+	'lim': 'li',
+	'lin': 'ln',
+	'lit': 'lt',
+	'ltz': 'lb',
+	'lub': 'lu',
+	'lug': 'lg',
+	'mah': 'mh',
+	'mal': 'ml',
+	'mar': 'mr',
+	'mkd': 'mk',
+	'mlg': 'mg',
+	'mlt': 'mt',
+	'mol': 'mo',
+	'mon': 'mn',
+	'mri': 'mi',
+	'msa': 'ms',
+	'mya': 'my',
+	'nau': 'na',
+	'nav': 'nv',
+	'nbl': 'nr',
+	'nde': 'nd',
+	'ndo': 'ng',
+	'nep': 'ne',
+	'nld': 'nl',
+	'nno': 'nn',
+	'nob': 'nb',
+	'nor': 'no',
+	'nya': 'ny',
+	'oci': 'oc',
+	'oji': 'oj',
+	'ori': 'or',
+	'orm': 'om',
+	'oss': 'os',
+	'pan': 'pa',
+	'pli': 'pi',
+	'pol': 'pl',
+	'por': 'pt',
+	'pus': 'ps',
+	'que': 'qu',
+	'roh': 'rm',
+	'ron': 'ro',
+	'run': 'rn',
+	'rus': 'ru',
+	'sag': 'sg',
+	'san': 'sa',
+	'sin': 'si',
+	'slk': 'sk',
+	'slv': 'sl',
+	'sme': 'se',
+	'smo': 'sm',
+	'sna': 'sn',
+	'snd': 'sd',
+	'som': 'so',
+	'sot': 'st',
+	'spa': 'es',
+	'sqi': 'sq',
+	'srd': 'sc',
+	'srp': 'sr',
+	'ssw': 'ss',
+	'sun': 'su',
+	'swa': 'sw',
+	'swe': 'sv',
+	'tah': 'ty',
+	'tam': 'ta',
+	'tat': 'tt',
+	'tel': 'te',
+	'tgk': 'tg',
+	'tgl': 'tl',
+	'tha': 'th',
+	'tir': 'ti',
+	'ton': 'to',
+	'tsn': 'tn',
+	'tso': 'ts',
+	'tuk': 'tk',
+	'tur': 'tr',
+	'twi': 'tw',
+	'uig': 'ug',
+	'ukr': 'uk',
+	'urd': 'ur',
+	'uzb': 'uz',
+	'ven': 've',
+	'vie': 'vi',
+	'vol': 'vo',
+	'wln': 'wa',
+	'wol': 'wo',
+	'xho': 'xh',
+	'yid': 'yi',
+	'yor': 'yo',
+	'zha': 'za',
+	'zho': 'zh',
+	'zul': 'zu',
+}
+
+class LanguageTag (object):
+	"""A BCP 47 language tag.
+
+	Attributes:
+		subtags (List[str]): The list of subtags in this tag.
+		grandfathered (bool): Whether this tag is grandfathered. If
+			``true``, the entire lowercased tag is the ``language``
+			and the other subtag fields are empty.
+		language (str): The language subtag.
+		script (str): The script subtag.
+		region (str): The region subtag.
+		variant (str): The variant subtag.
+
+	Args:
+		tag (str): A BCP 47 language tag.
+
+	"""
+	def __init__ (self, tag):
+		global bcp_47
+		self.subtags = tag.lower ().split ('-')
+		self.grandfathered = tag.lower () in bcp_47.grandfathered
+		if self.grandfathered:
+			self.language = tag.lower ()
+			self.script = ''
+			self.region = ''
+			self.variant = ''
+		else:
+			self.language = self.subtags[0]
+			self.script = self._find_first (lambda s: len (s) == 4 and s[0] > '9', self.subtags)
+			self.region = self._find_first (lambda s: len (s) == 2 and s[0] > '9' or len (s) == 3 and s[0] <= '9', self.subtags[1:])
+			self.variant = self._find_first (lambda s: len (s) > 4 or len (s) == 4 and s[0] <= '9', self.subtags)
+
+	def __str__(self):
+		return '-'.join(self.subtags)
+
+	def __repr__ (self):
+		return 'LanguageTag(%r)' % str(self)
+
+	@staticmethod
+	def _find_first (function, sequence):
+		try:
+			return next (iter (filter (function, sequence)))
+		except StopIteration:
+			return None
+
+	def is_complex (self):
+		"""Return whether this tag is too complex to represent as a
+		``LangTag`` in the generated code.
+
+		Complex tags need to be handled in
+		``hb_ot_tags_from_complex_language``.
+
+		Returns:
+			Whether this tag is complex.
+		"""
+		return not (len (self.subtags) == 1
+			or self.grandfathered
+			and len (self.subtags[1]) != 3
+			and ot.from_bcp_47[self.subtags[0]] == ot.from_bcp_47[self.language])
+
+	def get_group (self):
+		"""Return the group into which this tag should be categorized in
+		``hb_ot_tags_from_complex_language``.
+
+		The group is the first letter of the tag, or ``'und'`` if this tag
+		should not be matched in a ``switch`` statement in the generated
+		code.
+
+		Returns:
+			This tag's group.
+		"""
+		return ('und'
+			if (self.language == 'und'
+				or self.variant in bcp_47.prefixes and len (bcp_47.prefixes[self.variant]) == 1)
+			else self.language[0])
+
+class OpenTypeRegistryParser (HTMLParser):
+	"""A parser for the OpenType language system tag registry.
+
+	Attributes:
+		header (str): The "last updated" line of the registry.
+		names (Mapping[str, str]): A map of language system tags to the
+			names they are given in the registry.
+		ranks (DefaultDict[str, int]): A map of language system tags to
+			numbers. If a single BCP 47 tag corresponds to multiple
+			OpenType tags, the tags are ordered in increasing order by
+			rank. The rank is based on the number of BCP 47 tags
+			associated with a tag, though it may be manually modified.
+		to_bcp_47 (DefaultDict[str, AbstractSet[str]]): A map of
+			OpenType language system tags to sets of BCP 47 tags.
+		from_bcp_47 (DefaultDict[str, AbstractSet[str]]): ``to_bcp_47``
+			inverted. Its values start as unsorted sets;
+			``sort_languages`` converts them to sorted lists.
+
+	"""
+	def __init__ (self):
+		HTMLParser.__init__ (self)
+		self.header = ''
+		self.names = {}
+		self.ranks = collections.defaultdict (int)
+		self.to_bcp_47 = collections.defaultdict (set)
+		self.from_bcp_47 = collections.defaultdict (set)
+		# Whether the parser is in a <td> element
+		self._td = False
+		# The text of the <td> elements of the current <tr> element.
+		self._current_tr = []
+
+	def handle_starttag (self, tag, attrs):
+		if tag == 'meta':
+			for attr, value in attrs:
+				if attr == 'name' and value == 'updated_at':
+					self.header = self.get_starttag_text ()
+					break
+		elif tag == 'td':
+			self._td = True
+			self._current_tr.append ('')
+		elif tag == 'tr':
+			self._current_tr = []
+
+	def handle_endtag (self, tag):
+		if tag == 'td':
+			self._td = False
+		elif tag == 'tr' and self._current_tr:
+			expect (2 <= len (self._current_tr) <= 3)
+			name = self._current_tr[0].strip ()
+			tag = self._current_tr[1].strip ("\t\n\v\f\r '")
+			rank = 0
+			if len (tag) > 4:
+				expect (tag.endswith (' (deprecated)'), 'ill-formed OpenType tag: %s' % tag)
+				name += ' (deprecated)'
+				tag = tag.split (' ')[0]
+				rank = 1
+			self.names[tag] = re.sub (' languages$', '', name)
+			if not self._current_tr[2]:
+				return
+			iso_codes = self._current_tr[2].strip ()
+			self.to_bcp_47[tag].update (ISO_639_3_TO_1.get (code, code) for code in iso_codes.replace (' ', '').split (','))
+			rank += 2 * len (self.to_bcp_47[tag])
+			self.ranks[tag] = rank
+
+	def handle_data (self, data):
+		if self._td:
+			self._current_tr[-1] += data
+
+	def handle_charref (self, name):
+		self.handle_data (html_unescape (self, '&#%s;' % name))
+
+	def handle_entityref (self, name):
+		self.handle_data (html_unescape (self, '&%s;' % name))
+
+	def parse (self, filename):
+		"""Parse the OpenType language system tag registry.
+
+		Args:
+			filename (str): The file name of the registry.
+		"""
+		with io.open (filename, encoding='utf-8') as f:
+			self.feed (f.read ())
+		expect (self.header)
+		for tag, iso_codes in self.to_bcp_47.items ():
+			for iso_code in iso_codes:
+				self.from_bcp_47[iso_code].add (tag)
+
+	def add_language (self, bcp_47_tag, ot_tag):
+		"""Add a language as if it were in the registry.
+
+		Args:
+			bcp_47_tag (str): A BCP 47 tag. If the tag is more than just
+				a language subtag, and if the language subtag is a
+				macrolanguage, then new languages are added corresponding
+				to the macrolanguages' individual languages with the
+				remainder of the tag appended.
+			ot_tag (str): An OpenType language system tag.
+		"""
+		global bcp_47
+		self.to_bcp_47[ot_tag].add (bcp_47_tag)
+		self.from_bcp_47[bcp_47_tag].add (ot_tag)
+		if bcp_47_tag.lower () not in bcp_47.grandfathered:
+			try:
+				[macrolanguage, suffix] = bcp_47_tag.split ('-', 1)
+				if macrolanguage in bcp_47.macrolanguages:
+					s = set ()
+					for language in bcp_47.macrolanguages[macrolanguage]:
+						if language.lower () not in bcp_47.grandfathered:
+							s.add ('%s-%s' % (language, suffix))
+					bcp_47.macrolanguages['%s-%s' % (macrolanguage, suffix)] = s
+			except ValueError:
+				pass
+
+	@staticmethod
+	def _remove_language (tag_1, dict_1, dict_2):
+		for tag_2 in dict_1.pop (tag_1):
+			dict_2[tag_2].remove (tag_1)
+			if not dict_2[tag_2]:
+				del dict_2[tag_2]
+
+	def remove_language_ot (self, ot_tag):
+		"""Remove an OpenType tag from the registry.
+
+		Args:
+			ot_tag (str): An OpenType tag.
+		"""
+		self._remove_language (ot_tag, self.to_bcp_47, self.from_bcp_47)
+
+	def remove_language_bcp_47 (self, bcp_47_tag):
+		"""Remove a BCP 47 tag from the registry.
+
+		Args:
+			bcp_47_tag (str): A BCP 47 tag.
+		"""
+		self._remove_language (bcp_47_tag, self.from_bcp_47, self.to_bcp_47)
+
+	def inherit_from_macrolanguages (self):
+		"""Copy mappings from macrolanguages to individual languages.
+
+		If a BCP 47 tag for an individual mapping has no OpenType
+		mapping but its macrolanguage does, the mapping is copied to
+		the individual language. For example, als (Tosk Albanian) has no
+		explicit mapping, so it inherits from sq (Albanian) the mapping
+		to SQI.
+
+		If a BCP 47 tag for a macrolanguage has no OpenType mapping but
+		all of its individual languages do and they all map to the same
+		tags, the mapping is copied to the macrolanguage.
+		"""
+		global bcp_47
+		original_ot_from_bcp_47 = dict (self.from_bcp_47)
+		for macrolanguage, languages in dict (bcp_47.macrolanguages).items ():
+			ot_macrolanguages = set (original_ot_from_bcp_47.get (macrolanguage, set ()))
+			if ot_macrolanguages:
+				for ot_macrolanguage in ot_macrolanguages:
+					for language in languages:
+						# Remove the following condition if e.g. nn should map to NYN,NOR
+						# instead of just NYN.
+						if language not in original_ot_from_bcp_47:
+							self.add_language (language, ot_macrolanguage)
+							self.ranks[ot_macrolanguage] += 1
+			else:
+				for language in languages:
+					if language in original_ot_from_bcp_47:
+						if ot_macrolanguages:
+							ml = original_ot_from_bcp_47[language]
+							if ml:
+								ot_macrolanguages &= ml
+							else:
+								pass
+						else:
+							ot_macrolanguages |= original_ot_from_bcp_47[language]
+					else:
+						ot_macrolanguages.clear ()
+					if not ot_macrolanguages:
+						break
+				for ot_macrolanguage in ot_macrolanguages:
+					self.add_language (macrolanguage, ot_macrolanguage)
+
+	def sort_languages (self):
+		"""Sort the values of ``from_bcp_47`` in ascending rank order."""
+		for language, tags in self.from_bcp_47.items ():
+			self.from_bcp_47[language] = sorted (tags,
+					key=lambda t: (self.ranks[t] + rank_delta (language, t), t))
+
+ot = OpenTypeRegistryParser ()
+
+class BCP47Parser (object):
+	"""A parser for the BCP 47 subtag registry.
+
+	Attributes:
+		header (str): The "File-Date" line of the registry.
+		names (Mapping[str, str]): A map of subtags to the names they
+			are given in the registry. Each value is a
+			``'\\n'``-separated list of names.
+		scopes (Mapping[str, str]): A map of language subtags to strings
+			suffixed to language names, including suffixes to explain
+			language scopes.
+		macrolanguages (DefaultDict[str, AbstractSet[str]]): A map of
+			language subtags to the sets of language subtags which
+			inherit from them. See
+			``OpenTypeRegistryParser.inherit_from_macrolanguages``.
+		prefixes (DefaultDict[str, AbstractSet[str]]): A map of variant
+			subtags to their prefixes.
+		grandfathered (AbstractSet[str]): The set of grandfathered tags,
+			normalized to lowercase.
+
+	"""
+	def __init__ (self):
+		self.header = ''
+		self.names = {}
+		self.scopes = {}
+		self.macrolanguages = collections.defaultdict (set)
+		self.prefixes = collections.defaultdict (set)
+		self.grandfathered = set ()
+
+	def parse (self, filename):
+		"""Parse the BCP 47 subtag registry.
+
+		Args:
+			filename (str): The file name of the registry.
+		"""
+		with io.open (filename, encoding='utf-8') as f:
+			subtag_type = None
+			subtag = None
+			deprecated = False
+			has_preferred_value = False
+			line_buffer = ''
+			for line in itertools.chain (f, ['']):
+				line = line.rstrip ()
+				if line.startswith (' '):
+					line_buffer += line[1:]
+					continue
+				line, line_buffer = line_buffer, line
+				if line.startswith ('Type: '):
+					subtag_type = line.split (' ')[1]
+					deprecated = False
+					has_preferred_value = False
+				elif line.startswith ('Subtag: ') or line.startswith ('Tag: '):
+					subtag = line.split (' ')[1]
+					if subtag_type == 'grandfathered':
+						self.grandfathered.add (subtag.lower ())
+				elif line.startswith ('Description: '):
+					description = line.split (' ', 1)[1].replace (' (individual language)', '')
+					description = re.sub (' (\((individual |macro)language\)|languages)$', '',
+							description)
+					if subtag in self.names:
+						self.names[subtag] += '\n' + description
+					else:
+						self.names[subtag] = description
+				elif subtag_type == 'language' or subtag_type == 'grandfathered':
+					if line.startswith ('Scope: '):
+						scope = line.split (' ')[1]
+						if scope == 'macrolanguage':
+							scope = ' [macrolanguage]'
+						elif scope == 'collection':
+							scope = ' [family]'
+						else:
+							continue
+						self.scopes[subtag] = scope
+					elif line.startswith ('Deprecated: '):
+						self.scopes[subtag] = ' (retired code)' + self.scopes.get (subtag, '')
+						deprecated = True
+					elif deprecated and line.startswith ('Comments: see '):
+						# If a subtag is split into multiple replacement subtags,
+						# it essentially represents a macrolanguage.
+						for language in line.replace (',', '').split (' ')[2:]:
+							self._add_macrolanguage (subtag, language)
+					elif line.startswith ('Preferred-Value: '):
+						# If a subtag is deprecated in favor of a single replacement subtag,
+						# it is either a dialect or synonym of the preferred subtag. Either
+						# way, it is close enough to the truth to consider the replacement
+						# the macrolanguage of the deprecated language.
+						has_preferred_value = True
+						macrolanguage = line.split (' ')[1]
+						self._add_macrolanguage (macrolanguage, subtag)
+					elif not has_preferred_value and line.startswith ('Macrolanguage: '):
+						self._add_macrolanguage (line.split (' ')[1], subtag)
+				elif subtag_type == 'variant':
+					if line.startswith ('Prefix: '):
+						self.prefixes[subtag].add (line.split (' ')[1])
+				elif line.startswith ('File-Date: '):
+					self.header = line
+		expect (self.header)
+
+	def _add_macrolanguage (self, macrolanguage, language):
+		global ot
+		if language not in ot.from_bcp_47:
+			for l in self.macrolanguages.get (language, set ()):
+				self._add_macrolanguage (macrolanguage, l)
+		if macrolanguage not in ot.from_bcp_47:
+			for ls in list (self.macrolanguages.values ()):
+				if macrolanguage in ls:
+					ls.add (language)
+					return
+		self.macrolanguages[macrolanguage].add (language)
+
+	def remove_extra_macrolanguages (self):
+		"""Make every language have at most one macrolanguage."""
+		inverted = collections.defaultdict (list)
+		for macrolanguage, languages in self.macrolanguages.items ():
+			for language in languages:
+				inverted[language].append (macrolanguage)
+		for language, macrolanguages in inverted.items ():
+			if len (macrolanguages) > 1:
+				macrolanguages.sort (key=lambda ml: len (self.macrolanguages[ml]))
+				biggest_macrolanguage = macrolanguages.pop ()
+				for macrolanguage in macrolanguages:
+					self._add_macrolanguage (biggest_macrolanguage, macrolanguage)
+
+	def get_name (self, lt):
+		"""Return the names of the subtags in a language tag.
+
+		Args:
+			lt (LanguageTag): A BCP 47 language tag.
+
+		Returns:
+			The name form of ``lt``.
+		"""
+		name = self.names[lt.language].split ('\n')[0]
+		if lt.script:
+			name += '; ' + self.names[lt.script.title ()].split ('\n')[0]
+		if lt.region:
+			name += '; ' + self.names[lt.region.upper ()].split ('\n')[0]
+		if lt.variant:
+			name += '; ' + self.names[lt.variant].split ('\n')[0]
+		return name
+
+bcp_47 = BCP47Parser ()
+
+ot.parse (sys.argv[1])
+bcp_47.parse (sys.argv[2])
+
+ot.add_language ('ary', 'MOR')
+
+ot.add_language ('ath', 'ATH')
+
+ot.add_language ('bai', 'BML')
+
+ot.ranks['BAL'] = ot.ranks['KAR'] + 1
+
+ot.add_language ('ber', 'BBR')
+
+ot.remove_language_ot ('PGR')
+ot.add_language ('el-polyton', 'PGR')
+
+bcp_47.macrolanguages['et'] = {'ekk'}
+
+bcp_47.names['flm'] = 'Falam Chin'
+bcp_47.scopes['flm'] = ' (retired code)'
+bcp_47.macrolanguages['flm'] = {'cfm'}
+
+ot.ranks['FNE'] = ot.ranks['TNE'] + 1
+
+ot.add_language ('und-fonipa', 'IPPH')
+
+ot.add_language ('und-fonnapa', 'APPH')
+
+ot.remove_language_ot ('IRT')
+ot.add_language ('ga-Latg', 'IRT')
+
+ot.remove_language_ot ('KGE')
+ot.add_language ('und-Geok', 'KGE')
+
+ot.add_language ('guk', 'GUK')
+ot.names['GUK'] = 'Gumuz (SIL fonts)'
+ot.ranks['GUK'] = ot.ranks['GMZ'] + 1
+
+bcp_47.macrolanguages['id'] = {'in'}
+
+bcp_47.macrolanguages['ijo'] = {'ijc'}
+
+ot.add_language ('kht', 'KHN')
+ot.names['KHN'] = ot.names['KHT'] + ' (Microsoft fonts)'
+ot.names['KHT'] = ot.names['KHT'] + ' (OpenType spec and SIL fonts)'
+ot.ranks['KHN'] = ot.ranks['KHT']
+ot.ranks['KHT'] += 1
+
+ot.ranks['LCR'] = ot.ranks['MCR'] + 1
+
+ot.names['MAL'] = 'Malayalam Traditional'
+ot.ranks['MLR'] += 1
+
+bcp_47.names['mhv'] = 'Arakanese'
+bcp_47.scopes['mhv'] = ' (retired code)'
+
+ot.add_language ('no', 'NOR')
+
+ot.add_language ('oc-provenc', 'PRO')
+
+ot.add_language ('qu', 'QUZ')
+ot.add_language ('qub', 'QWH')
+ot.add_language ('qud', 'QVI')
+ot.add_language ('qug', 'QVI')
+ot.add_language ('qup', 'QVI')
+ot.add_language ('qur', 'QWH')
+ot.add_language ('qus', 'QUH')
+ot.add_language ('quw', 'QVI')
+ot.add_language ('qux', 'QWH')
+ot.add_language ('qva', 'QWH')
+ot.add_language ('qvh', 'QWH')
+ot.add_language ('qvj', 'QVI')
+ot.add_language ('qvl', 'QWH')
+ot.add_language ('qvm', 'QWH')
+ot.add_language ('qvn', 'QWH')
+ot.add_language ('qvo', 'QVI')
+ot.add_language ('qvp', 'QWH')
+ot.add_language ('qvw', 'QWH')
+ot.add_language ('qvz', 'QVI')
+ot.add_language ('qwa', 'QWH')
+ot.add_language ('qws', 'QWH')
+ot.add_language ('qxa', 'QWH')
+ot.add_language ('qxc', 'QWH')
+ot.add_language ('qxh', 'QWH')
+ot.add_language ('qxl', 'QVI')
+ot.add_language ('qxn', 'QWH')
+ot.add_language ('qxo', 'QWH')
+ot.add_language ('qxr', 'QVI')
+ot.add_language ('qxt', 'QWH')
+ot.add_language ('qxw', 'QWH')
+
+bcp_47.macrolanguages['ro'].remove ('mo')
+bcp_47.macrolanguages['ro-MD'].add ('mo')
+
+ot.add_language ('sgw', 'SGW')
+ot.names['SGW'] = ot.names['CHG'] + ' (SIL fonts)'
+ot.ranks['SGW'] = ot.ranks['CHG'] + 1
+
+ot.remove_language_ot ('SYRE')
+ot.remove_language_ot ('SYRJ')
+ot.remove_language_ot ('SYRN')
+ot.add_language ('und-Syre', 'SYRE')
+ot.add_language ('und-Syrj', 'SYRJ')
+ot.add_language ('und-Syrn', 'SYRN')
+
+bcp_47.names['xst'] = u"Silt'e"
+bcp_47.scopes['xst'] = ' (retired code)'
+bcp_47.macrolanguages['xst'] = {'stv', 'wle'}
+
+ot.add_language ('xwo', 'TOD')
+
+ot.remove_language_ot ('ZHH')
+ot.remove_language_ot ('ZHP')
+ot.remove_language_ot ('ZHT')
+bcp_47.macrolanguages['zh'].remove ('lzh')
+bcp_47.macrolanguages['zh'].remove ('yue')
+ot.add_language ('zh-Hant-MO', 'ZHH')
+ot.add_language ('zh-Hant-HK', 'ZHH')
+ot.add_language ('zh-Hans', 'ZHS')
+ot.add_language ('zh-Hant', 'ZHT')
+ot.add_language ('zh-HK', 'ZHH')
+ot.add_language ('zh-MO', 'ZHH')
+ot.add_language ('zh-TW', 'ZHT')
+ot.add_language ('lzh', 'ZHT')
+ot.add_language ('lzh-Hans', 'ZHS')
+ot.add_language ('yue', 'ZHH')
+ot.add_language ('yue-Hans', 'ZHS')
+
+bcp_47.macrolanguages['zom'] = {'yos'}
+
+def rank_delta (bcp_47, ot):
+	"""Return a delta to apply to a BCP 47 tag's rank.
+
+	Most OpenType tags have a constant rank, but a few have ranks that
+	depend on the BCP 47 tag.
+
+	Args:
+		bcp_47 (str): A BCP 47 tag.
+		ot (str): An OpenType tag to.
+
+	Returns:
+		A number to add to ``ot``'s rank when sorting ``bcp_47``'s
+		OpenType equivalents.
+	"""
+	if bcp_47 == 'ak' and ot == 'AKA':
+		return -1
+	if bcp_47 == 'tw' and ot == 'TWI':
+		return -1
+	return 0
+
+disambiguation = {
+	'ALT': 'alt',
+	'ARK': 'rki',
+	'BHI': 'bhb',
+	'BLN': 'bjt',
+	'BTI': 'beb',
+	'CCHN': 'cco',
+	'CMR': 'swb',
+	'CPP': 'crp',
+	'CRR': 'crx',
+	'DUJ': 'dwu',
+	'ECR': 'crj',
+	'HAL': 'cfm',
+	'HND': 'hnd',
+	'KIS': 'kqs',
+	'LRC': 'bqi',
+	'NDB': 'nd',
+	'NIS': 'njz',
+	'PLG': 'pce',
+	'PRO': 'pro',
+	'QIN': 'bgr',
+	'QUH': 'quh',
+	'QVI': 'qvi',
+	'QWH': 'qwh',
+	'SIG': 'stv',
+	'TNE': 'yrk',
+	'ZHH': 'zh-HK',
+	'ZHS': 'zh-Hans',
+	'ZHT': 'zh-Hant',
+}
+
+ot.inherit_from_macrolanguages ()
+bcp_47.remove_extra_macrolanguages ()
+ot.inherit_from_macrolanguages ()
+ot.sort_languages ()
+
+print ('/* == Start of generated table == */')
+print ('/*')
+print (' * The following table is generated by running:')
+print (' *')
+print (' *   %s languagetags language-subtag-registry' % sys.argv[0])
+print (' *')
+print (' * on files with these headers:')
+print (' *')
+print (' * %s' % ot.header.strip ())
+print (' * %s' % bcp_47.header)
+print (' */')
+print ()
+print ('#ifndef HB_OT_TAG_TABLE_HH')
+print ('#define HB_OT_TAG_TABLE_HH')
+print ()
+print ('static const LangTag ot_languages[] = {')
+
+def hb_tag (tag):
+	"""Convert a tag to ``HB_TAG`` form.
+
+	Args:
+		tag (str): An OpenType tag.
+
+	Returns:
+		A snippet of C++ representing ``tag``.
+	"""
+	return u"HB_TAG('%s','%s','%s','%s')" % tuple (('%-4s' % tag)[:4])
+
+def get_variant_set (name):
+	"""Return a set of variant language names from a name.
+
+	Args:
+		name (str): A list of language names from the BCP 47 registry,
+			joined on ``'\\n'``.
+
+	Returns:
+		A set of normalized language names.
+	"""
+	return set (unicodedata.normalize ('NFD', n.replace ('\u2019', u"'"))
+			.encode ('ASCII', 'ignore')
+			.strip ()
+			for n in re.split ('[\n(),]', name) if n)
+
+def language_name_intersection (a, b):
+	"""Return the names in common between two language names.
+
+	Args:
+		a (str): A list of language names from the BCP 47 registry,
+			joined on ``'\\n'``.
+		b (str): A list of language names from the BCP 47 registry,
+			joined on ``'\\n'``.
+
+	Returns:
+		The normalized language names shared by ``a`` and ``b``.
+	"""
+	return get_variant_set (a).intersection (get_variant_set (b))
+
+def get_matching_language_name (intersection, candidates):
+	return next (iter (c for c in candidates if not intersection.isdisjoint (get_variant_set (c))))
+
+maximum_tags = 0
+for language, tags in sorted (ot.from_bcp_47.items ()):
+	if language == '' or '-' in language:
+		continue
+	print ('  {\"%s\",\t{' % language, end='')
+	maximum_tags = max (maximum_tags, len (tags))
+	tag_count = len (tags)
+	for i, tag in enumerate (tags, start=1):
+		if i > 1:
+			print ('\t\t ', end='')
+		print (hb_tag (tag), end='')
+		if i == tag_count:
+			print ('}}', end='')
+		print (',\t/* ', end='')
+		bcp_47_name = bcp_47.names.get (language, '')
+		bcp_47_name_candidates = bcp_47_name.split ('\n')
+		intersection = language_name_intersection (bcp_47_name, ot.names[tag])
+		scope = bcp_47.scopes.get (language, '')
+		if not intersection:
+			write ('%s%s -> %s' % (bcp_47_name_candidates[0], scope, ot.names[tag]))
+		else:
+			name = get_matching_language_name (intersection, bcp_47_name_candidates)
+			bcp_47.names[language] = name
+			write ('%s%s' % (name if len (name) > len (ot.names[tag]) else ot.names[tag], scope))
+		print (' */')
+
+print ('};')
+print ()
+print ('static_assert (HB_OT_MAX_TAGS_PER_LANGUAGE == %iu, "");' % maximum_tags)
+print ()
+
+print ('/**')
+print (' * hb_ot_tags_from_complex_language:')
+print (' * @lang_str: a BCP 47 language tag to convert.')
+print (' * @limit: a pointer to the end of the substring of @lang_str to consider for')
+print (' * conversion.')
+print (' * @count: maximum number of language tags to retrieve (IN) and actual number of')
+print (' * language tags retrieved (OUT). If no tags are retrieved, it is not modified.')
+print (' * @tags: array of size at least @language_count to store the language tag')
+print (' * results')
+print (' *')
+print (' * Converts a multi-subtag BCP 47 language tag to language tags.')
+print (' *')
+print (' * Return value: Whether any language systems were retrieved.')
+print (' **/')
+print ('static bool')
+print ('hb_ot_tags_from_complex_language (const char   *lang_str,')
+print ('\t\t\t\t  const char   *limit,')
+print ('\t\t\t\t  unsigned int *count /* IN/OUT */,')
+print ('\t\t\t\t  hb_tag_t     *tags /* OUT */)')
+print ('{')
+
+def print_subtag_matches (subtag, new_line):
+	if subtag:
+		if new_line:
+			print ()
+			print ('\t&& ', end='')
+		print ('subtag_matches (lang_str, limit, "-%s")' % subtag, end='')
+
+complex_tags = collections.defaultdict (list)
+for initial, group in itertools.groupby ((lt_tags for lt_tags in [
+			(LanguageTag (language), tags)
+			for language, tags in sorted (ot.from_bcp_47.items (),
+				key=lambda i: (-len (i[0]), i[0]))
+		] if lt_tags[0].is_complex ()),
+		key=lambda lt_tags: lt_tags[0].get_group ()):
+	complex_tags[initial] += group
+
+for initial, items in sorted (complex_tags.items ()):
+	if initial != 'und':
+		continue
+	for lt, tags in items:
+		if lt.variant in bcp_47.prefixes:
+			expect (next (iter (bcp_47.prefixes[lt.variant])) == lt.language,
+					'%s is not a valid prefix of %s' % (lt.language, lt.variant))
+		print ('  if (', end='')
+		print_subtag_matches (lt.script, False)
+		print_subtag_matches (lt.region, False)
+		print_subtag_matches (lt.variant, False)
+		print (')')
+		print ('  {')
+		write ('    /* %s */' % bcp_47.get_name (lt))
+		print ()
+		if len (tags) == 1:
+			write ('    tags[0] = %s;  /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]]))
+			print ()
+			print ('    *count = 1;')
+		else:
+			print ('    hb_tag_t possible_tags[] = {')
+			for tag in tags:
+				write ('      %s,  /* %s */' % (hb_tag (tag), ot.names[tag]))
+				print ()
+			print ('    };')
+			print ('    for (i = 0; i < %s && i < *count; i++)' % len (tags))
+			print ('      tags[i] = possible_tags[i];')
+			print ('    *count = i;')
+		print ('    return true;')
+		print ('  }')
+
+print ('  switch (lang_str[0])')
+print ('  {')
+for initial, items in sorted (complex_tags.items ()):
+	if initial == 'und':
+		continue
+	print ("  case '%s':" % initial)
+	for lt, tags in items:
+		print ('    if (', end='')
+		if lt.grandfathered:
+			print ('0 == strcmp (&lang_str[1], "%s")' % lt.language[1:], end='')
+		else:
+			string_literal = lt.language[1:] + '-'
+			if lt.script:
+				string_literal += lt.script
+				lt.script = None
+				if lt.region:
+					string_literal += '-' + lt.region
+					lt.region = None
+			if string_literal[-1] == '-':
+				print ('0 == strncmp (&lang_str[1], "%s", %i)' % (string_literal, len (string_literal)), end='')
+			else:
+				print ('lang_matches (&lang_str[1], "%s")' % string_literal, end='')
+		print_subtag_matches (lt.script, True)
+		print_subtag_matches (lt.region, True)
+		print_subtag_matches (lt.variant, True)
+		print (')')
+		print ('    {')
+		write ('      /* %s */' % bcp_47.get_name (lt))
+		print ()
+		if len (tags) == 1:
+			write ('      tags[0] = %s;  /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]]))
+			print ()
+			print ('      *count = 1;')
+		else:
+			print ('      unsigned int i;')
+			print ('      hb_tag_t possible_tags[] = {')
+			for tag in tags:
+				write ('\t%s,  /* %s */' % (hb_tag (tag), ot.names[tag]))
+				print ()
+			print ('      };')
+			print ('      for (i = 0; i < %s && i < *count; i++)' % len (tags))
+			print ('\ttags[i] = possible_tags[i];')
+			print ('      *count = i;')
+		print ('      return true;')
+		print ('    }')
+	print ('    break;')
+
+print ('  }')
+print ('  return false;')
+print ('}')
+print ()
+print ('/**')
+print (' * hb_ot_ambiguous_tag_to_language')
+print (' * @tag: A language tag.')
+print (' *')
+print (' * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to')
+print (' * many language tags) and the best tag is not the alphabetically first, or if')
+print (' * the best tag consists of multiple subtags.')
+print (' *')
+print (' * Return value: The #hb_language_t corresponding to the BCP 47 language tag,')
+print (' * or #HB_LANGUAGE_INVALID if @tag is not ambiguous.')
+print (' **/')
+print ('static hb_language_t')
+print ('hb_ot_ambiguous_tag_to_language (hb_tag_t tag)')
+print ('{')
+print ('  switch (tag)')
+print ('  {')
+
+def verify_disambiguation_dict ():
+	"""Verify and normalize ``disambiguation``.
+
+	``disambiguation`` is a map of ambiguous OpenType language system
+	tags to the particular BCP 47 tags they correspond to. This function
+	checks that all its keys really are ambiguous and that each key's
+	value is valid for that key. It checks that no ambiguous tag is
+	missing, except when it can figure out which BCP 47 tag is the best
+	by itself.
+
+	It modifies ``disambiguation`` to remove keys whose values are the
+	same as those that the fallback would return anyway, and to add
+	ambiguous keys whose disambiguations it determined automatically.
+
+	Raises:
+		AssertionError: Verification failed.
+	"""
+	global bcp_47
+	global disambiguation
+	global ot
+	for ot_tag, bcp_47_tags in ot.to_bcp_47.items ():
+		primary_tags = list (t for t in bcp_47_tags if t not in bcp_47.grandfathered and ot.from_bcp_47.get (t)[0] == ot_tag)
+		if len (primary_tags) == 1:
+			expect (ot_tag not in disambiguation, 'unnecessary disambiguation for OT tag: %s' % ot_tag)
+			if '-' in primary_tags[0]:
+				disambiguation[ot_tag] = primary_tags[0]
+		elif len (primary_tags) == 0:
+			expect (ot_tag not in disambiguation, 'There is no possible valid disambiguation for %s' % ot_tag)
+		else:
+			macrolanguages = list (t for t in primary_tags if bcp_47.scopes.get (t) == ' [macrolanguage]')
+			if len (macrolanguages) != 1:
+				macrolanguages = list (t for t in primary_tags if bcp_47.scopes.get (t) == ' [family]')
+			if len (macrolanguages) != 1:
+				macrolanguages = list (t for t in primary_tags if 'retired code' not in bcp_47.scopes.get (t, ''))
+			if len (macrolanguages) != 1:
+				expect (ot_tag in disambiguation, 'ambiguous OT tag: %s %s' % (ot_tag, str (macrolanguages)))
+				expect (disambiguation[ot_tag] in bcp_47_tags,
+						'%s is not a valid disambiguation for %s' % (disambiguation[ot_tag], ot_tag))
+			elif ot_tag not in disambiguation:
+				disambiguation[ot_tag] = macrolanguages[0]
+			if disambiguation[ot_tag] == sorted (primary_tags)[0] and '-' not in disambiguation[ot_tag]:
+				del disambiguation[ot_tag]
+	for ot_tag in disambiguation.keys ():
+		expect (ot_tag in ot.to_bcp_47, 'unknown OT tag: %s' % ot_tag)
+
+verify_disambiguation_dict ()
+for ot_tag, bcp_47_tag in sorted (disambiguation.items ()):
+	write ('  case %s:  /* %s */' % (hb_tag (ot_tag), ot.names[ot_tag]))
+	print ()
+	write ('    return hb_language_from_string (\"%s\", -1);  /* %s */' % (bcp_47_tag, bcp_47.get_name (LanguageTag (bcp_47_tag))))
+	print ()
+
+print ('  default:')
+print ('    return HB_LANGUAGE_INVALID;')
+print ('  }')
+print ('}')
+
+print ()
+print ('#endif /* HB_OT_TAG_TABLE_HH */')
+print ()
+print ('/* == End of generated table == */')
+
diff --git a/src/gen-use-table.py b/src/gen-use-table.py
index c742eba..ebfae6f 100755
--- a/src/gen-use-table.py
+++ b/src/gen-use-table.py
@@ -8,7 +8,7 @@
 	print ("usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt", file=sys.stderr)
 	sys.exit (1)
 
-BLACKLISTED_BLOCKS = ["Thai", "Lao", "Tibetan"]
+BLACKLISTED_BLOCKS = ["Thai", "Lao"]
 
 files = [io.open (x, encoding='utf-8') for x in sys.argv[1:]]
 
@@ -197,7 +197,10 @@
 def is_CONS_WITH_STACKER(U, UISC, UGC):
 	return UISC == Consonant_With_Stacker
 def is_HALANT(U, UISC, UGC):
-	return UISC in [Virama, Invisible_Stacker]
+	return UISC in [Virama, Invisible_Stacker] and not is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC)
+def is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC):
+	# https://github.com/harfbuzz/harfbuzz/issues/1102
+	return U == 0x11046
 def is_HALANT_NUM(U, UISC, UGC):
 	return UISC == Number_Joiner
 def is_ZWNJ(U, UISC, UGC):
@@ -248,6 +251,7 @@
 	'SUB':	is_CONS_SUB,
 	'CS':	is_CONS_WITH_STACKER,
 	'H':	is_HALANT,
+	'HVM':	is_HALANT_OR_VOWEL_MODIFIER,
 	'HN':	is_HALANT_NUM,
 	'ZWNJ':	is_ZWNJ,
 	'ZWJ':	is_ZWJ,
@@ -281,8 +285,8 @@
 	'V': {
 		'Abv': [Top, Top_And_Bottom, Top_And_Bottom_And_Right, Top_And_Right],
 		'Blw': [Bottom, Overstruck, Bottom_And_Right],
-		'Pst': [Right],
-		'Pre': [Left, Top_And_Left, Top_And_Left_And_Right, Left_And_Right],
+		'Pst': [Right, Top_And_Left, Top_And_Left_And_Right, Left_And_Right],
+		'Pre': [Left],
 	},
 	'VM': {
 		'Abv': [Top],
@@ -295,6 +299,7 @@
 		'Blw': [Bottom],
 	},
 	'H': None,
+	'HVM': None,
 	'B': None,
 	'FM': None,
 	'SUB': None,
@@ -307,11 +312,28 @@
 
 		# Resolve Indic_Syllabic_Category
 
-		# TODO: These don't have UISC assigned in Unicode 8.0, but
-		# have UIPC
+		# TODO: These don't have UISC assigned in Unicode 8.0, but have UIPC
 		if U == 0x17DD: UISC = Vowel_Dependent
 		if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark
 
+		# Tibetan:
+		# TODO: These don't have UISC assigned in Unicode 11.0, but have UIPC
+		if 0x0F18 <= U <= 0x0F19 or 0x0F3E <= U <= 0x0F3F: UISC = Vowel_Dependent
+		if 0x0F86 <= U <= 0x0F87: UISC = Tone_Mark
+		# Overrides to allow NFC order matching syllable
+		# https://github.com/harfbuzz/harfbuzz/issues/1012
+		if UBlock == 'Tibetan' and is_VOWEL (U, UISC, UGC):
+			if UIPC == Top:
+				UIPC = Bottom
+
+		# TODO: https://github.com/harfbuzz/harfbuzz/pull/982
+		# also  https://github.com/harfbuzz/harfbuzz/issues/1012
+		if UBlock == 'Chakma' and is_VOWEL (U, UISC, UGC):
+			if UIPC == Top:
+				UIPC = Bottom
+			elif UIPC == Bottom:
+				UIPC = Top
+
 		# TODO: https://github.com/harfbuzz/harfbuzz/pull/627
 		if 0x1BF2 <= U <= 0x1BF3: UISC = Nukta; UIPC = Bottom
 
@@ -359,13 +381,6 @@
 		# https://github.com/roozbehp/unicode-data/issues/8
 		if U == 0x0A51: UIPC = Bottom
 
-		# TODO: https://github.com/harfbuzz/harfbuzz/pull/982
-		if UBlock == 'Chakma' and is_VOWEL (U, UISC, UGC):
-			if UIPC == Top:
-				UIPC = Bottom
-			elif UIPC == Bottom:
-				UIPC = Top
-
 		assert (UIPC in [Not_Applicable, Visual_Order_Left] or
 			USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC)
 
@@ -394,7 +409,7 @@
 		print (" * %s" % (l.strip()))
 print (" */")
 print ()
-print ('#include "hb-ot-shape-complex-use-private.hh"')
+print ('#include "hb-ot-shape-complex-use.hh"')
 print ()
 
 total = 0
diff --git a/src/harfbuzz-config.cmake.in b/src/harfbuzz-config.cmake.in
index 87b1572..304410d 100644
--- a/src/harfbuzz-config.cmake.in
+++ b/src/harfbuzz-config.cmake.in
@@ -12,7 +12,11 @@
 string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_libdir "${_harfbuzz_libdir}")
 set(_harfbuzz_libdir_iter "${_harfbuzz_libdir}")
 while (_harfbuzz_libdir_iter)
+  set(_harfbuzz_libdir_prev_iter "${_harfbuzz_libdir_iter}")
   get_filename_component(_harfbuzz_libdir_iter "${_harfbuzz_libdir_iter}" DIRECTORY)
+  if (_harfbuzz_libdir_prev_iter STREQUAL _harfbuzz_libdir_iter)
+    break()
+  endif ()
   get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY)
 endwhile ()
 unset(_harfbuzz_libdir_iter)
diff --git a/src/hb-aat-layout-ankr-table.hh b/src/hb-aat-layout-ankr-table.hh
index 3b7912b..5f7656d 100644
--- a/src/hb-aat-layout-ankr-table.hh
+++ b/src/hb-aat-layout-ankr-table.hh
@@ -25,7 +25,7 @@
 #ifndef HB_AAT_LAYOUT_ANKR_TABLE_HH
 #define HB_AAT_LAYOUT_ANKR_TABLE_HH
 
-#include "hb-aat-layout-common-private.hh"
+#include "hb-aat-layout-common.hh"
 
 /*
  * ankr -- Anchor Point
@@ -45,32 +45,49 @@
     return_trace (c->check_struct (this));
   }
 
+  public:
   FWORD		xCoordinate;
   FWORD		yCoordinate;
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
+typedef LArrayOf<Anchor> GlyphAnchors;
+
 struct ankr
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_ankr;
 
+  inline const Anchor &get_anchor (hb_codepoint_t glyph_id,
+				   unsigned int i,
+				   unsigned int num_glyphs,
+				   const char *end) const
+  {
+    const Offset<HBUINT16, false> *offset = (this+lookupTable).get_value (glyph_id, num_glyphs);
+    if (!offset)
+      return Null(Anchor);
+    const GlyphAnchors &anchors = StructAtOffset<GlyphAnchors> (&(this+anchorData), *offset);
+    /* TODO Use sanitizer; to avoid overflows and more. */
+    if (unlikely ((const char *) &anchors + anchors.get_size () > end))
+      return Null(Anchor);
+    return anchors[i];
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
 			  version == 0 &&
-			  lookupTable.sanitize (c, this) &&
-			  anchors.sanitize (c, this)));
+			  lookupTable.sanitize (c, this)));
   }
 
   protected:
   HBUINT16	version; 	/* Version number (set to zero) */
   HBUINT16	flags;		/* Flags (currently unused; set to zero) */
-  LOffsetTo<Lookup<HBUINT16> >
+  LOffsetTo<Lookup<Offset<HBUINT16, false> >, false>
 		lookupTable;	/* Offset to the table's lookup table */
-  LOffsetTo<LArrayOf<Anchor> >
-		anchors;	/* Offset to the glyph data table */
+  LOffsetTo<HBUINT8, false>
+		anchorData;	/* Offset to the glyph data table */
 
   public:
   DEFINE_SIZE_STATIC (12);
diff --git a/src/hb-aat-layout-bsln-table.hh b/src/hb-aat-layout-bsln-table.hh
index df2bf5b..b864086 100644
--- a/src/hb-aat-layout-bsln-table.hh
+++ b/src/hb-aat-layout-bsln-table.hh
@@ -25,7 +25,7 @@
 #ifndef HB_AAT_LAYOUT_BSLN_TABLE_HH
 #define HB_AAT_LAYOUT_BSLN_TABLE_HH
 
-#include "hb-aat-layout-common-private.hh"
+#include "hb-aat-layout-common.hh"
 
 /*
  * bsln -- Baseline
diff --git a/src/hb-aat-layout-common-private.hh b/src/hb-aat-layout-common.hh
similarity index 71%
rename from src/hb-aat-layout-common-private.hh
rename to src/hb-aat-layout-common.hh
index d7f3505..a99ccaf 100644
--- a/src/hb-aat-layout-common-private.hh
+++ b/src/hb-aat-layout-common.hh
@@ -24,10 +24,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_AAT_LAYOUT_COMMON_PRIVATE_HH
-#define HB_AAT_LAYOUT_COMMON_PRIVATE_HH
+#ifndef HB_AAT_LAYOUT_COMMON_HH
+#define HB_AAT_LAYOUT_COMMON_HH
 
-#include "hb-aat-layout-private.hh"
+#include "hb-aat-layout.hh"
 
 
 namespace AAT {
@@ -36,111 +36,6 @@
 
 
 /*
- * Binary Searching Tables
- */
-
-struct BinSearchHeader
-{
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
-  }
-
-  HBUINT16	unitSize;	/* Size of a lookup unit for this search in bytes. */
-  HBUINT16	nUnits;		/* Number of units of the preceding size to be searched. */
-  HBUINT16	searchRange;	/* The value of unitSize times the largest power of 2
-				 * that is less than or equal to the value of nUnits. */
-  HBUINT16	entrySelector;	/* The log base 2 of the largest power of 2 less than
-				 * or equal to the value of nUnits. */
-  HBUINT16	rangeShift;	/* The value of unitSize times the difference of the
-				 * value of nUnits minus the largest power of 2 less
-				 * than or equal to the value of nUnits. */
-  public:
-  DEFINE_SIZE_STATIC (10);
-};
-
-template <typename Type>
-struct BinSearchArrayOf
-{
-  inline const Type& operator [] (unsigned int i) const
-  {
-    if (unlikely (i >= header.nUnits)) return Null(Type);
-    return StructAtOffset<Type> (bytesZ, i * header.unitSize);
-  }
-  inline Type& operator [] (unsigned int i)
-  {
-    return StructAtOffset<Type> (bytesZ, i * header.unitSize);
-  }
-  inline unsigned int get_size (void) const
-  { return header.static_size + header.nUnits * header.unitSize; }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!sanitize_shallow (c))) return_trace (false);
-
-    /* Note: for structs that do not reference other structs,
-     * we do not need to call their sanitize() as we already did
-     * a bound check on the aggregate array size.  We just include
-     * a small unreachable expression to make sure the structs
-     * pointed to do have a simple sanitize(), ie. they do not
-     * reference other structs via offsets.
-     */
-    (void) (false && StructAtOffset<Type> (bytesZ, 0).sanitize (c));
-
-    return_trace (true);
-  }
-  inline 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;
-    for (unsigned int i = 0; i < count; i++)
-      if (unlikely (!(*this)[i].sanitize (c, base)))
-        return_trace (false);
-    return_trace (true);
-  }
-
-  template <typename T>
-  inline const Type *bsearch (const T &key) const
-  {
-    unsigned int size = header.unitSize;
-    int min = 0, max = (int) header.nUnits - 1;
-    while (min <= max)
-    {
-      int mid = (min + 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;
-    }
-    return nullptr;
-  }
-
-  private:
-  inline 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, header.unitSize, header.nUnits));
-  }
-
-  protected:
-  BinSearchHeader	header;
-  HBUINT8		bytesZ[VAR];
-  public:
-  DEFINE_SIZE_ARRAY (10, bytesZ);
-};
-
-
-/*
  * Lookup Table
  */
 
@@ -213,7 +108,7 @@
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 2 */
-  BinSearchArrayOf<LookupSegmentSingle<T> >
+  VarSizedBinSearchArrayOf<LookupSegmentSingle<T> >
 		segments;	/* The actual segments. These must already be sorted,
 				 * according to the first word in each one (the last
 				 * glyph in each segment). */
@@ -243,7 +138,7 @@
 
   GlyphID	last;		/* Last GlyphID in this segment */
   GlyphID	first;		/* First GlyphID in this segment */
-  OffsetTo<UnsizedArrayOf<T> >
+  OffsetTo<UnsizedArrayOf<T>, HBUINT16, false>
 		valuesZ;	/* A 16-bit offset from the start of
 				 * the table to the data. */
   public:
@@ -269,8 +164,8 @@
   }
 
   protected:
-  HBUINT16	format;		/* Format identifier--format = 2 */
-  BinSearchArrayOf<LookupSegmentArray<T> >
+  HBUINT16	format;		/* Format identifier--format = 4 */
+  VarSizedBinSearchArrayOf<LookupSegmentArray<T> >
 		segments;	/* The actual segments. These must already be sorted,
 				 * according to the first word in each one (the last
 				 * glyph in each segment). */
@@ -292,7 +187,7 @@
   GlyphID	glyph;		/* Last GlyphID */
   T		value;		/* The lookup value (only one) */
   public:
-  DEFINE_SIZE_STATIC (4 + T::static_size);
+  DEFINE_SIZE_STATIC (2 + T::static_size);
 };
 
 template <typename T>
@@ -315,7 +210,7 @@
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 6 */
-  BinSearchArrayOf<LookupSingle<T> >
+  VarSizedBinSearchArrayOf<LookupSingle<T> >
 		entries;	/* The actual entries, sorted by glyph index. */
   public:
   DEFINE_SIZE_ARRAY (8, entries);
@@ -329,7 +224,8 @@
   private:
   inline const T* get_value (hb_codepoint_t glyph_id) const
   {
-    return firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount ? &valueArrayZ[glyph_id - firstGlyph] : nullptr;
+    return firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount ?
+	   &valueArrayZ[glyph_id - firstGlyph] : nullptr;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -339,7 +235,7 @@
   }
 
   protected:
-  HBUINT16	format;		/* Format identifier--format = 6 */
+  HBUINT16	format;		/* Format identifier--format = 8 */
   GlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
   HBUINT16	glyphCount;	/* Total number of glyphs (equivalent to the last
 				 * glyph minus the value of firstGlyph plus 1). */
@@ -351,6 +247,48 @@
 };
 
 template <typename T>
+struct LookupFormat10
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const typename T::type get_value_or_null (hb_codepoint_t glyph_id) const
+  {
+    if (!(firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount))
+      return Null(T);
+
+    const HBUINT8 *p = &valueArrayZ[(glyph_id - firstGlyph) * valueSize];
+
+    unsigned int v = 0;
+    unsigned int count = valueSize;
+    for (unsigned int i = 0; i < count; i++)
+      v = (v << 8) | *p++;
+
+    return v;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  valueSize <= 4 &&
+		  valueArrayZ.sanitize (c, glyphCount * valueSize));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 8 */
+  HBUINT16	valueSize;	/* Byte size of each value. */
+  GlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
+  HBUINT16	glyphCount;	/* Total number of glyphs (equivalent to the last
+				 * glyph minus the value of firstGlyph plus 1). */
+  UnsizedArrayOf<HBUINT8>
+		valueArrayZ;	/* The lookup values (indexed by the glyph index
+				 * minus the value of firstGlyph). */
+  public:
+  DEFINE_SIZE_ARRAY (8, valueArrayZ);
+};
+
+template <typename T>
 struct Lookup
 {
   inline const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
@@ -365,6 +303,17 @@
     }
   }
 
+  inline const typename T::type get_value_or_null (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  {
+    switch (u.format) {
+      /* Format 10 cannot return a pointer. */
+      case 10: return u.format10.get_value_or_null (glyph_id);
+      default:
+      const T *v = get_value (glyph_id, num_glyphs);
+      return v ? *v : Null(T);
+    }
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -375,6 +324,7 @@
     case 4: return_trace (u.format4.sanitize (c));
     case 6: return_trace (u.format6.sanitize (c));
     case 8: return_trace (u.format8.sanitize (c));
+    case 10: return_trace (u.format10.sanitize (c));
     default:return_trace (true);
     }
   }
@@ -387,10 +337,30 @@
   LookupFormat4<T>	format4;
   LookupFormat6<T>	format6;
   LookupFormat8<T>	format8;
+  LookupFormat10<T>	format10;
   } u;
   public:
   DEFINE_SIZE_UNION (2, format);
 };
+/* Lookup 0 has unbounded size (dependant on num_glyphs).  So we need to defined
+ * special NULL objects for Lookup<> objects, but since it's template our macros
+ * don't work.  So we have to hand-code them here.  UGLY. */
+} /* Close namespace. */
+/* Ugly hand-coded null objects for template Lookup<> :(. */
+extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2];
+template <>
+/*static*/ inline const AAT::Lookup<OT::HBUINT16>& Null<AAT::Lookup<OT::HBUINT16> > (void) {
+  return *reinterpret_cast<const AAT::Lookup<OT::HBUINT16> *> (_hb_Null_AAT_Lookup);
+}
+template <>
+/*static*/ inline const AAT::Lookup<OT::HBUINT32>& Null<AAT::Lookup<OT::HBUINT32> > (void) {
+  return *reinterpret_cast<const AAT::Lookup<OT::HBUINT32> *> (_hb_Null_AAT_Lookup);
+}
+template <>
+/*static*/ inline const AAT::Lookup<OT::Offset<OT::HBUINT16, false> >& Null<AAT::Lookup<OT::Offset<OT::HBUINT16, false> > > (void) {
+  return *reinterpret_cast<const AAT::Lookup<OT::Offset<OT::HBUINT16, false> > *> (_hb_Null_AAT_Lookup);
+}
+namespace AAT {
 
 
 /*
@@ -439,10 +409,23 @@
 template <typename Extra>
 struct StateTable
 {
+  enum State
+  {
+    STATE_START_OF_TEXT = 0,
+    STATE_START_OF_LINE = 1,
+  };
+  enum Class
+  {
+    CLASS_END_OF_TEXT = 0,
+    CLASS_OUT_OF_BOUNDS = 1,
+    CLASS_DELETED_GLYPH = 2,
+    CLASS_END_OF_LINE = 3,
+  };
+
   inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
   {
     const HBUINT16 *v = (this+classTable).get_value (glyph_id, num_glyphs);
-    return v ? *v : 1;
+    return v ? (unsigned) *v : (unsigned) CLASS_OUT_OF_BOUNDS;
   }
 
   inline const Entry<Extra> *get_entries () const
@@ -472,6 +455,8 @@
     const HBUINT16 *states = (this+stateArrayTable).arrayZ;
     const Entry<Extra> *entries = (this+entryTable).arrayZ;
 
+    unsigned int num_classes = nClasses;
+
     unsigned int num_states = 1;
     unsigned int num_entries = 0;
 
@@ -479,20 +464,25 @@
     unsigned int entry = 0;
     while (state < num_states)
     {
+      if (unlikely (hb_unsigned_mul_overflows (num_classes, states[0].static_size)))
+	return_trace (false);
+
       if (unlikely (!c->check_array (states,
-				     states[0].static_size * nClasses,
-				     num_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 * nClasses];
-	for (const HBUINT16 *p = &states[state * nClasses]; p < stop; p++)
+	const HBUINT16 *stop = &states[num_states * num_classes];
+	for (const HBUINT16 *p = &states[state * num_classes]; p < stop; p++)
 	  num_entries = MAX<unsigned int> (num_entries, *p + 1);
 	state = num_states;
       }
 
-      if (unlikely (!c->check_array (entries,
-				     entries[0].static_size,
-				     num_entries)))
+      if (unlikely (!c->check_array (entries, num_entries)))
+	return_trace (false);
+      if ((c->max_ops -= num_entries - entry) < 0)
 	return_trace (false);
       { /* Sweep new entries. */
 	const Entry<Extra> *stop = &entries[num_entries];
@@ -511,11 +501,11 @@
   protected:
   HBUINT32	nClasses;	/* Number of classes, which is the number of indices
 				 * in a single line in the state array. */
-  LOffsetTo<Lookup<HBUINT16> >
+  LOffsetTo<Lookup<HBUINT16>, false>
 		classTable;	/* Offset to the class table. */
-  LOffsetTo<UnsizedArrayOf<HBUINT16> >
+  LOffsetTo<UnsizedArrayOf<HBUINT16>, false>
 		stateArrayTable;/* Offset to the state array. */
-  LOffsetTo<UnsizedArrayOf<Entry<Extra> > >
+  LOffsetTo<UnsizedArrayOf<Entry<Extra> >, false>
 		entryTable;	/* Offset to the entry array. */
 
   public:
@@ -535,31 +525,32 @@
   template <typename context_t>
   inline void drive (context_t *c)
   {
-    hb_glyph_info_t *info = buffer->info;
-
     if (!c->in_place)
       buffer->clear_output ();
 
-    unsigned int state = 0;
+    unsigned int state = StateTable<EntryData>::STATE_START_OF_TEXT;
     bool last_was_dont_advance = false;
     for (buffer->idx = 0;;)
     {
       unsigned int klass = buffer->idx < buffer->len ?
-			   machine.get_class (info[buffer->idx].codepoint, num_glyphs) :
-			   0 /* End of text */;
+			   machine.get_class (buffer->info[buffer->idx].codepoint, num_glyphs) :
+			   (unsigned) StateTable<EntryData>::CLASS_END_OF_TEXT;
       const Entry<EntryData> *entry = machine.get_entryZ (state, klass);
       if (unlikely (!entry))
 	break;
 
       /* Unsafe-to-break before this if not in state 0, as things might
-       * go differently if we start from state 0 here. */
-      if (state && buffer->idx)
+       * go differently if we start from state 0 here.
+       *
+       * Ugh.  The indexing here is ugly... */
+      if (state && buffer->backtrack_len () && buffer->idx < buffer->len)
       {
 	/* If there's no action and we're just epsilon-transitioning to state 0,
 	 * safe to break. */
 	if (c->is_actionable (this, entry) ||
-	    !(entry->newState == 0 && entry->flags == context_t::DontAdvance))
-	  buffer->unsafe_to_break (buffer->idx - 1, buffer->idx + 1);
+	    !(entry->newState == StateTable<EntryData>::STATE_START_OF_TEXT &&
+	      entry->flags == context_t::DontAdvance))
+	  buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
       }
 
       /* Unsafe-to-break if end-of-text would kick in here. */
@@ -573,6 +564,8 @@
       if (unlikely (!c->transition (this, entry)))
         break;
 
+      if (unlikely (!buffer->successful)) return;
+
       last_was_dont_advance = (entry->flags & context_t::DontAdvance) && buffer->max_ops-- > 0;
 
       state = entry->newState;
@@ -586,9 +579,10 @@
 
     if (!c->in_place)
     {
-      for (; buffer->idx < buffer->len;)
-        buffer->next_glyph ();
-      buffer->swap_buffers ();
+      for (; buffer->successful && buffer->idx < buffer->len;)
+	buffer->next_glyph ();
+      if (likely (buffer->successful))
+	buffer->swap_buffers ();
     }
   }
 
@@ -599,6 +593,7 @@
 };
 
 
+struct ankr;
 
 struct hb_aat_apply_context_t :
        hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
@@ -609,24 +604,33 @@
   static return_t default_return_value (void) { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
 
+  hb_ot_shape_plan_t *plan;
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t *buffer;
   hb_sanitize_context_t sanitizer;
+  const ankr &ankr_table;
+  const char *ankr_end;
 
   /* Unused. For debug tracing only. */
   unsigned int lookup_index;
   unsigned int debug_depth;
 
-  inline hb_aat_apply_context_t (hb_font_t *font_,
+  inline hb_aat_apply_context_t (hb_ot_shape_plan_t *plan_,
+				 hb_font_t *font_,
 				 hb_buffer_t *buffer_,
-				 hb_blob_t *table) :
-		font (font_), face (font->face), buffer (buffer_),
-		sanitizer (), lookup_index (0), debug_depth (0)
+				 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 (table);
+    sanitizer.init (blob);
     sanitizer.set_num_glyphs (face->get_num_glyphs ());
     sanitizer.start_processing ();
+    sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
   }
 
   inline void set_lookup_index (unsigned int i) { lookup_index = i; }
@@ -641,4 +645,4 @@
 } /* namespace AAT */
 
 
-#endif /* HB_AAT_LAYOUT_COMMON_PRIVATE_HH */
+#endif /* HB_AAT_LAYOUT_COMMON_HH */
diff --git a/src/hb-aat-layout-feat-table.hh b/src/hb-aat-layout-feat-table.hh
index 3e070d7..b670caa 100644
--- a/src/hb-aat-layout-feat-table.hh
+++ b/src/hb-aat-layout-feat-table.hh
@@ -25,7 +25,7 @@
 #ifndef HB_AAT_LAYOUT_FEAT_TABLE_HH
 #define HB_AAT_LAYOUT_FEAT_TABLE_HH
 
-#include "hb-aat-layout-common-private.hh"
+#include "hb-aat-layout-common.hh"
 
 /*
  * feat -- Feature Name
@@ -78,7 +78,7 @@
   protected:
   HBUINT16	feature;	/* Feature type. */
   HBUINT16	nSettings;	/* The number of records in the setting name array. */
-  LOffsetTo<UnsizedArrayOf<SettingName> >
+  LOffsetTo<UnsizedArrayOf<SettingName>, false>
 		settingTable;	/* Offset in bytes from the beginning of this table to
 				 * this feature's setting name array. The actual type of
 				 * record this offset refers to will depend on the
diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index cc03d62..e8eb43b 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -28,9 +28,10 @@
 #ifndef HB_AAT_LAYOUT_KERX_TABLE_HH
 #define HB_AAT_LAYOUT_KERX_TABLE_HH
 
-#include "hb-open-type-private.hh"
-#include "hb-aat-layout-common-private.hh"
-#include "hb-aat-layout-ankr-table.hh"
+#include "hb-open-type.hh"
+#include "hb-aat-layout-common.hh"
+#include "hb-ot-layout-gpos-table.hh"
+#include "hb-ot-kern-table.hh"
 
 /*
  * kerx -- Extended Kerning
@@ -44,7 +45,22 @@
 using namespace OT;
 
 
-struct KerxFormat0Records
+static inline int
+kerxTupleKern (int value,
+	       unsigned int tupleCount,
+	       const void *base,
+	       hb_aat_apply_context_t *c)
+{
+  if (likely (!tupleCount)) return value;
+
+  unsigned int offset = value;
+  const FWORD *pv = &StructAtOffset<FWORD> (base, offset);
+  if (unlikely (!pv->sanitize (&c->sanitizer))) return 0;
+  return *pv;
+}
+
+
+struct KerxSubTableHeader
 {
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -52,217 +68,573 @@
     return_trace (likely (c->check_struct (this)));
   }
 
-  protected:
-  GlyphID	left;
-  GlyphID	right;
-  FWORD		value;
   public:
-  DEFINE_SIZE_STATIC (6);
+  HBUINT32	length;
+  HBUINT32	coverage;
+  HBUINT32	tupleCount;
+  public:
+  DEFINE_SIZE_STATIC (12);
 };
 
 struct KerxSubTableFormat0
 {
-  // TODO(ebraminio) Enable when we got suitable BinSearchArrayOf
-  // inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
-  // {
-  //   hb_glyph_pair_t pair = {left, right};
-  //   int i = pairs.bsearch (pair);
-  //   if (i == -1)
-  //     return 0;
-  //   return pairs[i].get_kerning ();
-  // }
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) 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 ();
+  }
+
+  inline 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);
+
+    machine.kern (c->font, c->buffer, c->plan->kern_mask);
+
+    return_trace (true);
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
-			  recordsZ.sanitize (c, nPairs)));
+			  pairs.sanitize (c)));
   }
 
   protected:
-  // TODO(ebraminio): A custom version of "BinSearchArrayOf<KerxPair> pairs;" is
-  // needed here to use HBUINT32 instead
-  HBUINT32	nPairs;		/* The number of kerning pairs in this subtable */
-  HBUINT32	searchRange;	/* The largest power of two less than or equal to the value of nPairs,
-				 * multiplied by the size in bytes of an entry in the subtable. */
-  HBUINT32	entrySelector;	/* This is calculated as log2 of the largest power of two less
-				 * than or equal to the value of nPairs. */
-  HBUINT32	rangeShift;	/* The value of nPairs minus the largest power of two less than or equal to nPairs. */
-  UnsizedArrayOf<KerxFormat0Records>
-		recordsZ;	/* VAR=nPairs */
+  KerxSubTableHeader	header;
+  BinSearchArrayOf<KernPair, HBUINT32>
+			pairs;	/* Sorted kern records. */
   public:
-  DEFINE_SIZE_ARRAY (16, recordsZ);
+  DEFINE_SIZE_ARRAY (28, pairs);
 };
 
 struct KerxSubTableFormat1
 {
+  struct EntryData
+  {
+    HBUINT16	kernActionIndex;/* Index into the kerning value array. If
+				 * this index is 0xFFFF, then no kerning
+				 * is to be performed. */
+    public:
+    DEFINE_SIZE_STATIC (2);
+  };
+
+  struct driver_context_t
+  {
+    static const bool in_place = true;
+    enum Flags
+    {
+      Push		= 0x8000,	/* If set, push this glyph on the kerning stack. */
+      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph
+					 * before going to the new state. */
+      Reset		= 0x2000,	/* If set, reset the kerning data (clear the stack) */
+      Reserved		= 0x1FFF,	/* Not used; set to 0. */
+    };
+
+    inline driver_context_t (const KerxSubTableFormat1 *table,
+			     hb_aat_apply_context_t *c_) :
+	c (c_),
+	/* Apparently the offset kernAction is from the beginning of the state-machine,
+	 * similar to offsets in morx table, NOT from beginning of this table, like
+	 * other subtables in kerx.  Discovered via testing. */
+	kernAction (&table->machine + table->kernAction),
+	depth (0) {}
+
+    inline bool is_actionable (StateTableDriver<EntryData> *driver,
+			       const Entry<EntryData> *entry)
+    {
+      return entry->data.kernActionIndex != 0xFFFF;
+    }
+    inline bool transition (StateTableDriver<EntryData> *driver,
+			    const Entry<EntryData> *entry)
+    {
+      hb_buffer_t *buffer = driver->buffer;
+      unsigned int flags = entry->flags;
+
+      if (flags & Reset)
+      {
+	depth = 0;
+      }
+
+      if (flags & Push)
+      {
+	if (likely (depth < ARRAY_LENGTH (stack)))
+	  stack[depth++] = buffer->idx;
+	else
+	  depth = 0; /* Probably not what CoreText does, but better? */
+      }
+
+      if (entry->data.kernActionIndex != 0xFFFF)
+      {
+	const FWORD *actions = &kernAction[entry->data.kernActionIndex];
+	if (!c->sanitizer.check_array (actions, depth))
+	{
+	  depth = 0;
+	  return false;
+	}
+
+	hb_mask_t kern_mask = c->plan->kern_mask;
+	for (unsigned int i = 0; i < depth; i++)
+	{
+	  /* Apparently, when spec says "Each pops one glyph from the kerning stack
+	   * and applies the kerning value to it.", it doesn't mean it in that order.
+	   * The deepest item in the stack corresponds to the first item in the action
+	   * list.  Discovered by testing. */
+	  unsigned int idx = stack[i];
+	  int v = *actions++;
+	  if (idx < buffer->len && buffer->info[idx].mask & kern_mask)
+	  {
+	    /* XXX Non-forward direction... */
+	    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
+	      buffer->pos[idx].x_advance += c->font->em_scale_x (v);
+	    else
+	      buffer->pos[idx].y_advance += c->font->em_scale_y (v);
+	  }
+	}
+	depth = 0;
+      }
+
+      return true;
+    }
+
+    private:
+    hb_aat_apply_context_t *c;
+    const UnsizedArrayOf<FWORD> &kernAction;
+    unsigned int stack[8];
+    unsigned int depth;
+  };
+
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    if (!c->plan->requested_kerning)
+      return false;
+
+    if (header.tupleCount)
+      return_trace (false); /* TODO kerxTupleKern */
+
+    driver_context_t dc (this, c);
+
+    StateTableDriver<EntryData> driver (machine, c->buffer, c->font->face);
+    driver.drive (&dc);
+
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
+    /* The rest of array sanitizations are done at run-time. */
     return_trace (likely (c->check_struct (this) &&
-			  stateHeader.sanitize (c)));
+			  machine.sanitize (c)));
   }
 
   protected:
-  StateTable<HBUINT16>		stateHeader;
-  LOffsetTo<ArrayOf<HBUINT16> >	valueTable;
+  KerxSubTableHeader				header;
+  StateTable<EntryData>				machine;
+  LOffsetTo<UnsizedArrayOf<FWORD>, false>	kernAction;
   public:
-  DEFINE_SIZE_STATIC (20);
-};
-
-// TODO(ebraminio): Maybe this can be replaced with Lookup<HBUINT16>?
-struct KerxClassTable
-{
-  inline unsigned int get_class (hb_codepoint_t g) const { return classes[g - firstGlyph]; }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (firstGlyph.sanitize (c) &&
-			  classes.sanitize (c)));
-  }
-
-  protected:
-  HBUINT16		firstGlyph;	/* First glyph in class range. */
-  ArrayOf<HBUINT16>	classes;	/* Glyph classes. */
-  public:
-  DEFINE_SIZE_ARRAY (4, classes);
+  DEFINE_SIZE_STATIC (32);
 };
 
 struct KerxSubTableFormat2
 {
-  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
+			  hb_aat_apply_context_t *c) const
   {
-    unsigned int l = (this+leftClassTable).get_class (left);
-    unsigned int r = (this+leftClassTable).get_class (left);
-    unsigned int offset = l * rowWidth + r * sizeof (FWORD);
-    const FWORD *arr = &(this+array);
-    if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end))
-      return 0;
-    const FWORD *v = &StructAtOffset<FWORD> (arr, offset);
-    if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end))
-      return 0;
-    return *v;
+    unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
+    unsigned int l = (this+leftClassTable).get_value_or_null (left, num_glyphs);
+    unsigned int r = (this+rightClassTable).get_value_or_null (right, num_glyphs);
+    unsigned int offset = l + r;
+    const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
+    if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
+    return kerxTupleKern (*v, header.tupleCount, this, c);
+  }
+
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    if (!c->plan->requested_kerning)
+      return false;
+
+    accelerator_t accel (*this, c);
+    hb_kern_machine_t<accelerator_t> machine (accel);
+    machine.kern (c->font, c->buffer, c->plan->kern_mask);
+
+    return_trace (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
-			  rowWidth.sanitize (c) &&
 			  leftClassTable.sanitize (c, this) &&
 			  rightClassTable.sanitize (c, this) &&
-			  array.sanitize (c, this)));
+			  c->check_range (this, array)));
   }
 
+  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:
-  HBUINT32	rowWidth;	/* The width, in bytes, of a row in the table. */
-  LOffsetTo<KerxClassTable>
-		leftClassTable;	/* Offset from beginning of this subtable to
-				 * left-hand class table. */
-  LOffsetTo<KerxClassTable>
-		rightClassTable;/* Offset from beginning of this subtable to
-				 * right-hand class table. */
-  LOffsetTo<FWORD>
-		array;		/* Offset from beginning of this subtable to
-				 * the start of the kerning array. */
+  KerxSubTableHeader	header;
+  HBUINT32		rowWidth;	/* The width, in bytes, of a row in the table. */
+  LOffsetTo<Lookup<HBUINT16>, false>
+			leftClassTable;	/* Offset from beginning of this subtable to
+					 * left-hand class table. */
+  LOffsetTo<Lookup<HBUINT16>, false>
+			rightClassTable;/* Offset from beginning of this subtable to
+					 * right-hand class table. */
+  LOffsetTo<UnsizedArrayOf<FWORD>, false>
+			 array;		/* Offset from beginning of this subtable to
+					 * the start of the kerning array. */
   public:
-  DEFINE_SIZE_STATIC (16);
+  DEFINE_SIZE_STATIC (28);
 };
 
 struct KerxSubTableFormat4
 {
+  struct EntryData
+  {
+    HBUINT16	ankrActionIndex;/* Either 0xFFFF (for no action) or the index of
+				 * the action to perform. */
+    public:
+    DEFINE_SIZE_STATIC (2);
+  };
+
+  struct driver_context_t
+  {
+    static const bool in_place = true;
+    enum Flags
+    {
+      Mark		= 0x8000,	/* If set, remember this glyph as the marked glyph. */
+      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
+					 * going to the new state. */
+      Reserved		= 0x3FFF,	/* Not used; set to 0. */
+    };
+
+    enum SubTableFlags
+    {
+      ActionType	= 0xC0000000,	/* A two-bit field containing the action type. */
+      Unused		= 0x3F000000,	/* Unused - must be zero. */
+      Offset		= 0x00FFFFFF,	/* Masks the offset in bytes from the beginning
+					 * of the subtable to the beginning of the control
+					 * point table. */
+    };
+
+    inline driver_context_t (const KerxSubTableFormat4 *table,
+			     hb_aat_apply_context_t *c_) :
+	c (c_),
+	action_type ((table->flags & ActionType) >> 30),
+	ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
+	mark_set (false),
+	mark (0) {}
+
+    inline bool is_actionable (StateTableDriver<EntryData> *driver,
+			       const Entry<EntryData> *entry)
+    {
+      return entry->data.ankrActionIndex != 0xFFFF;
+    }
+    inline bool transition (StateTableDriver<EntryData> *driver,
+			    const Entry<EntryData> *entry)
+    {
+      hb_buffer_t *buffer = driver->buffer;
+      unsigned int flags = entry->flags;
+
+      if (mark_set && entry->data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
+      {
+	hb_glyph_position_t &o = buffer->cur_pos();
+	switch (action_type)
+	{
+	  case 0: /* Control Point Actions.*/
+	  {
+	    /* indexed into glyph outline. */
+	    const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
+	    if (!c->sanitizer.check_array (data, 2))
+	      return false;
+	    HB_UNUSED unsigned int markControlPoint = *data++;
+	    HB_UNUSED unsigned int currControlPoint = *data++;
+	    hb_position_t markX = 0;
+	    hb_position_t markY = 0;
+	    hb_position_t currX = 0;
+	    hb_position_t currY = 0;
+	    if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint,
+							      markControlPoint,
+							      HB_DIRECTION_LTR /*XXX*/,
+							      &markX, &markY) ||
+		!c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint,
+							      currControlPoint,
+							      HB_DIRECTION_LTR /*XXX*/,
+							      &currX, &currY))
+	      return true; /* True, such that the machine continues. */
+
+	    o.x_offset = markX - currX;
+	    o.y_offset = markY - currY;
+	  }
+	  break;
+
+	  case 1: /* Anchor Point Actions. */
+	  {
+	   /* Indexed into 'ankr' table. */
+	    const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
+	    if (!c->sanitizer.check_array (data, 2))
+	      return false;
+	    unsigned int markAnchorPoint = *data++;
+	    unsigned int currAnchorPoint = *data++;
+	    const Anchor markAnchor = c->ankr_table.get_anchor (c->buffer->info[mark].codepoint,
+								markAnchorPoint,
+								c->sanitizer.get_num_glyphs (),
+								c->ankr_end);
+	    const Anchor currAnchor = c->ankr_table.get_anchor (c->buffer->cur ().codepoint,
+								currAnchorPoint,
+								c->sanitizer.get_num_glyphs (),
+								c->ankr_end);
+
+	    o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate);
+	    o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate);
+	  }
+	  break;
+
+	  case 2: /* Control Point Coordinate Actions. */
+	  {
+	    const FWORD *data = (const FWORD *) &ankrData[entry->data.ankrActionIndex];
+	    if (!c->sanitizer.check_array (data, 4))
+	      return false;
+	    int markX = *data++;
+	    int markY = *data++;
+	    int currX = *data++;
+	    int currY = *data++;
+
+	    o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX);
+	    o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY);
+	  }
+	  break;
+	}
+	o.attach_type() = ATTACH_TYPE_MARK;
+	o.attach_chain() = (int) mark - (int) buffer->idx;
+	buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+      }
+
+      if (flags & Mark)
+      {
+	mark_set = true;
+	mark = buffer->idx;
+      }
+
+      return true;
+    }
+
+    private:
+    hb_aat_apply_context_t *c;
+    unsigned int action_type;
+    const HBUINT16 *ankrData;
+    bool mark_set;
+    unsigned int mark;
+  };
+
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    driver_context_t dc (this, c);
+
+    StateTableDriver<EntryData> driver (machine, c->buffer, c->font->face);
+    driver.drive (&dc);
+
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
+    /* The rest of array sanitizations are done at run-time. */
     return_trace (likely (c->check_struct (this) &&
-			  rowWidth.sanitize (c) &&
-			  leftClassTable.sanitize (c, this) &&
-			  rightClassTable.sanitize (c, this) &&
-			  array.sanitize (c, this)));
+			  machine.sanitize (c)));
   }
 
   protected:
-  HBUINT32	rowWidth;	/* The width, in bytes, of a row in the table. */
-  LOffsetTo<KerxClassTable>
-		leftClassTable;	/* Offset from beginning of this subtable to
-				 * left-hand class table. */
-  LOffsetTo<KerxClassTable>
-		rightClassTable;/* Offset from beginning of this subtable to
-				 * right-hand class table. */
-  LOffsetTo<FWORD>
-		array;		/* Offset from beginning of this subtable to
-				 * the start of the kerning array. */
+  KerxSubTableHeader	header;
+  StateTable<EntryData>	machine;
+  HBUINT32		flags;
   public:
-  DEFINE_SIZE_STATIC (16);
+  DEFINE_SIZE_STATIC (32);
 };
 
 struct KerxSubTableFormat6
 {
+  enum Flags
+  {
+    ValuesAreLong	= 0x00000001,
+  };
+
+  inline bool is_long (void) const { return flags & ValuesAreLong; }
+
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
+			  hb_aat_apply_context_t *c) const
+  {
+    unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
+    if (is_long ())
+    {
+      const U::Long &t = u.l;
+      unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
+      unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
+      unsigned int offset = l + r;
+      if (unlikely (offset < l)) return 0; /* Addition overflow. */
+      if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0;
+      const FWORD32 *v = &StructAtOffset<FWORD32> (&(this+t.array), offset * sizeof (FWORD32));
+      if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
+      return kerxTupleKern (*v, header.tupleCount, &(this+vector), c);
+    }
+    else
+    {
+      const U::Short &t = u.s;
+      unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
+      unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
+      unsigned int offset = l + r;
+      const FWORD *v = &StructAtOffset<FWORD> (&(this+t.array), offset * sizeof (FWORD));
+      if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
+      return kerxTupleKern (*v, header.tupleCount, &(this+vector), c);
+    }
+  }
+
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    if (!c->plan->requested_kerning)
+      return false;
+
+    accelerator_t accel (*this, c);
+    hb_kern_machine_t<accelerator_t> machine (accel);
+    machine.kern (c->font, c->buffer, c->plan->kern_mask);
+
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
-			  rowIndexTable.sanitize (c, this) &&
-			  columnIndexTable.sanitize (c, this) &&
-			  kerningArray.sanitize (c, this) &&
-			  kerningVector.sanitize (c, this)));
+			  (is_long () ?
+			   (
+			     u.l.rowIndexTable.sanitize (c, this) &&
+			     u.l.columnIndexTable.sanitize (c, this) &&
+			     c->check_range (this, u.l.array)
+			   ) : (
+			     u.s.rowIndexTable.sanitize (c, this) &&
+			     u.s.columnIndexTable.sanitize (c, this) &&
+			     c->check_range (this, u.s.array)
+			   )) &&
+			  (header.tupleCount == 0 ||
+			   c->check_range (this, vector))));
   }
 
-  protected:
-  HBUINT32	flags;
-  HBUINT16	rowCount;
-  HBUINT16	columnCount;
-  LOffsetTo<Lookup<HBUINT16> >	rowIndexTable;
-  LOffsetTo<Lookup<HBUINT16> >	columnIndexTable;
-  LOffsetTo<Lookup<HBUINT16> >	kerningArray;
-  LOffsetTo<Lookup<HBUINT16> >	kerningVector;
-  public:
-  DEFINE_SIZE_STATIC (24);
-};
+  struct accelerator_t
+  {
+    const KerxSubTableFormat6 &table;
+    hb_aat_apply_context_t *c;
 
-enum coverage_flags_t
-{
-  COVERAGE_VERTICAL_FLAG	= 0x80u,
-  COVERAGE_CROSSSTREAM_FLAG	= 0x40u,
-  COVERAGE_VARIATION_FLAG	= 0x20u,
-  COVERAGE_PROCESS_DIRECTION	= 0x10u,
+    inline accelerator_t (const KerxSubTableFormat6 &table_,
+			  hb_aat_apply_context_t *c_) :
+			    table (table_), c (c_) {}
+
+    inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+    { return table.get_kerning (left, right, c); }
+  };
+
+  protected:
+  KerxSubTableHeader		header;
+  HBUINT32			flags;
+  HBUINT16			rowCount;
+  HBUINT16			columnCount;
+  union U
+  {
+    struct Long
+    {
+      LOffsetTo<Lookup<HBUINT32>, false>	rowIndexTable;
+      LOffsetTo<Lookup<HBUINT32>, false>	columnIndexTable;
+      LOffsetTo<UnsizedArrayOf<FWORD32>, false>	array;
+    } l;
+    struct Short
+    {
+      LOffsetTo<Lookup<HBUINT16>, false>	rowIndexTable;
+      LOffsetTo<Lookup<HBUINT16>, false>	columnIndexTable;
+      LOffsetTo<UnsizedArrayOf<FWORD>, false>	array;
+    } s;
+  } u;
+  LOffsetTo<UnsizedArrayOf<FWORD>, false>	vector;
+  public:
+  DEFINE_SIZE_STATIC (36);
 };
 
 struct KerxTable
 {
-  inline bool apply (hb_aat_apply_context_t *c, const AAT::ankr *ankr) const
-  {
-    TRACE_APPLY (this);
-    /* TODO */
-    return_trace (false);
-  }
+  friend struct kerx;
 
-  inline unsigned int get_size (void) const { return length; }
+  inline unsigned int get_size (void) const { return u.header.length; }
+  inline unsigned int get_type (void) const { return u.header.coverage & SubtableType; }
+
+  enum Coverage
+  {
+    Vertical		= 0x80000000,	/* Set if table has vertical kerning values. */
+    CrossStream		= 0x40000000,	/* Set if table has cross-stream kerning values. */
+    Variation		= 0x20000000,	/* Set if table has variation kerning values. */
+    Backwards		= 0x10000000,	/* If clear, process the glyphs forwards, that
+					 * is, from first to last in the glyph stream.
+					 * If we, process them from last to first.
+					 * This flag only applies to state-table based
+					 * 'kerx' subtables (types 1 and 4). */
+    Reserved		= 0x0FFFFF00,	/* Reserved, set to zero. */
+    SubtableType	= 0x000000FF,	/* Subtable type. */
+  };
+
+  template <typename context_t>
+  inline typename context_t::return_t dispatch (context_t *c) const
+  {
+    unsigned int subtable_type = get_type ();
+    TRACE_DISPATCH (this, subtable_type);
+    switch (subtable_type) {
+    case 0	:		return_trace (c->dispatch (u.format0));
+    case 1	:		return_trace (c->dispatch (u.format1));
+    case 2	:		return_trace (c->dispatch (u.format2));
+    case 4	:		return_trace (c->dispatch (u.format4));
+    case 6	:		return_trace (c->dispatch (u.format6));
+    default:			return_trace (c->default_return_value ());
+    }
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (this)))
+    if (!u.header.sanitize (c) ||
+	!c->check_range (this, u.header.length))
       return_trace (false);
 
-    switch (format) {
-    case 0: return u.format0.sanitize (c);
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    case 4: return u.format4.sanitize (c);
-    case 6: return u.format6.sanitize (c);
-    default:return_trace (false);
-    }
+    return_trace (dispatch (c));
   }
 
 protected:
-  HBUINT32	length;
-  HBUINT8	coverage;
-  HBUINT16	unused;
-  HBUINT8	format;
-  HBUINT32	tupleIndex;
   union {
+  KerxSubTableHeader	header;
   KerxSubTableFormat0	format0;
   KerxSubTableFormat1	format1;
   KerxSubTableFormat2	format2;
@@ -273,71 +645,90 @@
   DEFINE_SIZE_MIN (12);
 };
 
-struct SubtableGlyphCoverageArray
-{
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
-  }
 
-  protected:
-  HBUINT32	length;
-  HBUINT32	coverage;
-  HBUINT32	tupleCount;
-  public:
-  DEFINE_SIZE_STATIC (12);
-};
+/*
+ * The 'kerx' Table
+ */
 
 struct kerx
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
 
-  inline bool apply (hb_aat_apply_context_t *c, const AAT::ankr *ankr) const
+  inline bool has_data (void) const { return version != 0; }
+
+  inline void apply (hb_aat_apply_context_t *c) const
   {
-    TRACE_APPLY (this);
-    const KerxTable &table = StructAfter<KerxTable> (*this);
-    return_trace (table.apply (c, ankr));
+    c->set_lookup_index (0);
+    const KerxTable *table = &firstTable;
+    unsigned int count = tableCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      bool reverse;
+
+      if (table->u.header.coverage & (KerxTable::CrossStream))
+	goto skip; /* We do NOT handle cross-stream. */
+
+      if (HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
+	  bool (table->u.header.coverage & KerxTable::Vertical))
+	goto skip;
+
+      reverse = bool (table->u.header.coverage & KerxTable::Backwards) !=
+		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
+
+      if (!c->buffer->message (c->font, "start kerx subtable %d", c->lookup_index))
+	goto skip;
+
+      if (reverse)
+	c->buffer->reverse ();
+
+      c->sanitizer.set_object (*table);
+
+      /* XXX Reverse-kern is not working yet...
+       * hb_kern_machine_t would need to know that it's reverse-kerning.
+       * Or better yet, make it work in reverse as well, so we don't have
+       * to reverse and reverse back? */
+      table->dispatch (c);
+
+      if (reverse)
+	c->buffer->reverse ();
+
+      (void) c->buffer->message (c->font, "end kerx subtable %d", c->lookup_index);
+
+    skip:
+      table = &StructAfter<KerxTable> (*table);
+    }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!(c->check_struct (this))))
-     return_trace (false);
-
-    /* TODO: Something like `morx`s ChainSubtable should be done here instead */
-    const KerxTable *table = &StructAfter<KerxTable> (*this);
-    if (unlikely (!(table->sanitize (c))))
+    if (!version.sanitize (c) || version < 2 ||
+	!tableCount.sanitize (c))
       return_trace (false);
 
-    for (unsigned int i = 0; i < nTables - 1; ++i)
+    const KerxTable *table = &firstTable;
+    unsigned int count = tableCount;
+    for (unsigned int i = 0; i < count; i++)
     {
+      if (!table->sanitize (c))
+	return_trace (false);
       table = &StructAfter<KerxTable> (*table);
-      if (unlikely (!(table->sanitize (c))))
-        return_trace (false);
     }
 
-    // If version is less than 3, we are done here; otherwise better to check footer also
-    if (version < 3)
-      return_trace (true);
-
-    // TODO: Investigate why this just work on some fonts no matter of version
-    // const SubtableGlyphCoverageArray &footer =
-    //   StructAfter<SubtableGlyphCoverageArray> (*table);
-    // return_trace (footer.sanitize (c));
-
     return_trace (true);
   }
 
   protected:
-  HBUINT16		version;
-  HBUINT16		padding;
-  HBUINT32		nTables;
-/*KerxTable tablesZ[VAR]; XXX ArrayOf??? */
-/*SubtableGlyphCoverageArray coverage_array;*/
+  HBUINT16	version;	/* The version number of the extended kerning table
+				 * (currently 2, 3, or 4). */
+  HBUINT16	unused;		/* Set to 0. */
+  HBUINT32	tableCount;	/* The number of subtables included in the extended kerning
+				 * table. */
+  KerxTable	firstTable;	/* Subtables. */
+/*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */
+
   public:
-  DEFINE_SIZE_STATIC (8);
+  DEFINE_SIZE_MIN (8);
 };
 
 } /* namespace AAT */
diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
index f258424..651af21 100644
--- a/src/hb-aat-layout-morx-table.hh
+++ b/src/hb-aat-layout-morx-table.hh
@@ -27,9 +27,9 @@
 #ifndef HB_AAT_LAYOUT_MORX_TABLE_HH
 #define HB_AAT_LAYOUT_MORX_TABLE_HH
 
-#include "hb-open-type-private.hh"
-#include "hb-aat-layout-common-private.hh"
-#include "hb-ot-layout-common-private.hh"
+#include "hb-open-type.hh"
+#include "hb-aat-layout-common.hh"
+#include "hb-ot-layout-common.hh"
 
 /*
  * morx -- Extended Glyph Metamorphosis
@@ -50,7 +50,8 @@
   struct driver_context_t
   {
     static const bool in_place = true;
-    enum Flags {
+    enum Flags
+    {
       MarkFirst		= 0x8000,	/* If set, make the current glyph the first
 					 * glyph to be rearranged. */
       DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph
@@ -163,7 +164,7 @@
 
     driver_context_t dc (this);
 
-    StateTableDriver<void> driver (machine, c->buffer, c->face);
+    StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
     driver.drive (&dc);
 
     return_trace (dc.ret);
@@ -196,7 +197,8 @@
   struct driver_context_t
   {
     static const bool in_place = true;
-    enum Flags {
+    enum Flags
+    {
       SetMark		= 0x8000,	/* If set, make the current glyph the marked glyph. */
       DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
 					 * going to the new state. */
@@ -268,7 +270,7 @@
     private:
     bool mark_set;
     unsigned int mark;
-    const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> &subs;
+    const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32, false> &subs;
   };
 
   inline bool apply (hb_aat_apply_context_t *c) const
@@ -309,7 +311,7 @@
   protected:
   StateTable<EntryData>
 		machine;
-  LOffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> >
+  LOffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32, false>, false>
 		substitutionTables;
   public:
   DEFINE_SIZE_STATIC (20);
@@ -329,7 +331,8 @@
   struct driver_context_t
   {
     static const bool in_place = false;
-    enum Flags {
+    enum Flags
+    {
       SetComponent	= 0x8000,	/* Push this glyph onto the component stack for
 					 * eventual processing. */
       DontAdvance	= 0x4000,	/* Leave the glyph pointer at this glyph for the
@@ -338,7 +341,8 @@
 					 * group. */
       Reserved		= 0x1FFF,	/* These bits are reserved and should be set to 0. */
     };
-    enum LigActionFlags {
+    enum LigActionFlags
+    {
       LigActionLast	= 0x80000000,	/* This is the last action in the list. This also
 					 * implies storage. */
       LigActionStore	= 0x40000000,	/* Store the ligature at the current cumulated index
@@ -361,7 +365,7 @@
     inline bool is_actionable (StateTableDriver<EntryData> *driver,
 			       const Entry<EntryData> *entry)
     {
-      return !!(entry->flags & PerformAction);
+      return entry->flags & PerformAction;
     }
     inline bool transition (StateTableDriver<EntryData> *driver,
 			    const Entry<EntryData> *entry)
@@ -387,12 +391,20 @@
 	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);
+
+	unsigned int cursor = match_length;
         do
 	{
-	  if (unlikely (!match_length))
-	    return false;
+	  if (unlikely (!cursor))
+	    break;
 
-	  buffer->move_to (match_positions[--match_length]);
+	  buffer->move_to (match_positions[--cursor]);
 
 	  const HBUINT32 &actionData = ligAction[action_idx];
 	  if (unlikely (!actionData.sanitize (&c->sanitizer))) return false;
@@ -400,8 +412,10 @@
 
 	  uint32_t uoffset = action & LigActionOffset;
 	  if (uoffset & 0x20000000)
-	    uoffset += 0xC0000000;
+	    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;
 
 	  const HBUINT16 &componentData = component[component_idx];
@@ -414,21 +428,21 @@
 	    if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false;
 	    hb_codepoint_t lig = ligatureData;
 
-	    match_positions[match_length++] = buffer->out_len;
 	    buffer->replace_glyph (lig);
 
-	    //ligature_idx = 0; // XXX Yes or no?
+	    /* Now go and delete all subsequent components. */
+	    while (match_length - 1 > cursor)
+	    {
+	      buffer->move_to (match_positions[--match_length]);
+	      buffer->skip_glyph ();
+	      end--;
+	    }
 	  }
-	  else
-	  {
-	    buffer->skip_glyph ();
-	    end--;
-	  }
-	  /* TODO merge_clusters / unsafe_to_break */
 
 	  action_idx++;
 	}
 	while (!(action & LigActionLast));
+	match_length = 0;
 	buffer->move_to (end);
       }
 
@@ -469,11 +483,11 @@
   protected:
   StateTable<EntryData>
 		machine;
-  LOffsetTo<UnsizedArrayOf<HBUINT32> >
+  LOffsetTo<UnsizedArrayOf<HBUINT32>, false>
 		ligAction;	/* Offset to the ligature action table. */
-  LOffsetTo<UnsizedArrayOf<HBUINT16> >
+  LOffsetTo<UnsizedArrayOf<HBUINT16>, false>
 		component;	/* Offset to the component table. */
-  LOffsetTo<UnsizedArrayOf<GlyphID> >
+  LOffsetTo<UnsizedArrayOf<GlyphID>, false>
 		ligature;	/* Offset to the actual ligature lists. */
   public:
   DEFINE_SIZE_STATIC (28);
@@ -517,19 +531,201 @@
 
 struct InsertionSubtable
 {
+  struct EntryData
+  {
+    HBUINT16	currentInsertIndex;	/* Zero-based index into the insertion glyph table.
+					 * The number of glyphs to be inserted is contained
+					 * in the currentInsertCount field in the flags.
+					 * A value of 0xFFFF indicates no insertion is to
+					 * be done. */
+    HBUINT16	markedInsertIndex;	/* Zero-based index into the insertion glyph table.
+					 * The number of glyphs to be inserted is contained
+					 * in the markedInsertCount field in the flags.
+					 * A value of 0xFFFF indicates no insertion is to
+					 * be done. */
+    public:
+    DEFINE_SIZE_STATIC (4);
+  };
+
+  struct driver_context_t
+  {
+    static const bool in_place = false;
+    enum Flags
+    {
+      SetMark		= 0x8000,	/* If set, mark the current glyph. */
+      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
+					 * going to the new state.  This does not mean
+					 * that the glyph pointed to is the same one as
+					 * before. If you've made insertions immediately
+					 * downstream of the current glyph, the next glyph
+					 * processed would in fact be the first one
+					 * inserted. */
+      CurrentIsKashidaLike= 0x2000,	/* If set, and the currentInsertList is nonzero,
+					 * then the specified glyph list will be inserted
+					 * as a kashida-like insertion, either before or
+					 * after the current glyph (depending on the state
+					 * of the currentInsertBefore flag). If clear, and
+					 * the currentInsertList is nonzero, then the
+					 * specified glyph list will be inserted as a
+					 * split-vowel-like insertion, either before or
+					 * after the current glyph (depending on the state
+					 * of the currentInsertBefore flag). */
+      MarkedIsKashidaLike= 0x1000,	/* If set, and the markedInsertList is nonzero,
+					 * then the specified glyph list will be inserted
+					 * as a kashida-like insertion, either before or
+					 * after the marked glyph (depending on the state
+					 * of the markedInsertBefore flag). If clear, and
+					 * the markedInsertList is nonzero, then the
+					 * specified glyph list will be inserted as a
+					 * split-vowel-like insertion, either before or
+					 * after the marked glyph (depending on the state
+					 * of the markedInsertBefore flag). */
+      CurrentInsertBefore= 0x0800,	/* If set, specifies that insertions are to be made
+					 * to the left of the current glyph. If clear,
+					 * they're made to the right of the current glyph. */
+      MarkedInsertBefore= 0x0400,	/* If set, specifies that insertions are to be
+					 * made to the left of the marked glyph. If clear,
+					 * they're made to the right of the marked glyph. */
+      CurrentInsertCount= 0x3E0,	/* This 5-bit field is treated as a count of the
+					 * number of glyphs to insert at the current
+					 * position. Since zero means no insertions, the
+					 * largest number of insertions at any given
+					 * current location is 31 glyphs. */
+      MarkedInsertCount= 0x001F,	/* This 5-bit field is treated as a count of the
+					 * number of glyphs to insert at the marked
+					 * position. Since zero means no insertions, the
+					 * largest number of insertions at any given
+					 * marked location is 31 glyphs. */
+    };
+
+    inline driver_context_t (const InsertionSubtable *table,
+			     hb_aat_apply_context_t *c_) :
+	ret (false),
+	c (c_),
+	mark_set (false),
+	mark (0),
+	insertionAction (table+table->insertionAction) {}
+
+    inline bool is_actionable (StateTableDriver<EntryData> *driver,
+			       const Entry<EntryData> *entry)
+    {
+      return (entry->flags & (CurrentInsertCount | MarkedInsertCount)) &&
+	     (entry->data.currentInsertIndex != 0xFFFF ||entry->data.markedInsertIndex != 0xFFFF);
+    }
+    inline bool transition (StateTableDriver<EntryData> *driver,
+			    const Entry<EntryData> *entry)
+    {
+      hb_buffer_t *buffer = driver->buffer;
+      unsigned int flags = entry->flags;
+
+      if (entry->data.markedInsertIndex != 0xFFFF && mark_set)
+      {
+	unsigned int count = (flags & MarkedInsertCount);
+	unsigned int start = entry->data.markedInsertIndex;
+	const GlyphID *glyphs = &insertionAction[start];
+	if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false;
+
+	bool before = flags & MarkedInsertBefore;
+
+	unsigned int end = buffer->out_len;
+	buffer->move_to (mark);
+
+	if (buffer->idx < buffer->len && !before)
+	  buffer->copy_glyph ();
+	/* TODO We ignore KashidaLike setting. */
+	for (unsigned int i = 0; i < count; i++)
+	  buffer->output_glyph (glyphs[i]);
+	if (buffer->idx < buffer->len && !before)
+	  buffer->skip_glyph ();
+
+	buffer->move_to (end + count);
+
+	buffer->unsafe_to_break_from_outbuffer (mark, MIN (buffer->idx + 1, buffer->len));
+      }
+
+      if (entry->data.currentInsertIndex != 0xFFFF)
+      {
+	unsigned int count = (flags & CurrentInsertCount) >> 5;
+	unsigned int start = entry->data.currentInsertIndex;
+	const GlyphID *glyphs = &insertionAction[start];
+	if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false;
+
+	bool before = flags & CurrentInsertBefore;
+
+	unsigned int end = buffer->out_len;
+
+	if (buffer->idx < buffer->len && !before)
+	  buffer->copy_glyph ();
+	/* TODO We ignore KashidaLike setting. */
+	for (unsigned int i = 0; i < count; i++)
+	  buffer->output_glyph (glyphs[i]);
+	if (buffer->idx < buffer->len && !before)
+	  buffer->skip_glyph ();
+
+	/* Humm. Not sure where to move to.  There's this wording under
+	 * DontAdvance flag:
+	 *
+	 * "If set, don't update the glyph index before going to the new state.
+	 * This does not mean that the glyph pointed to is the same one as
+	 * before. If you've made insertions immediately downstream of the
+	 * current glyph, the next glyph processed would in fact be the first
+	 * one inserted."
+	 *
+	 * This suggests that if DontAdvance is NOT set, we should move to
+	 * end+count.  If it *was*, then move to end, such that newly inserted
+	 * glyphs are now visible.
+	 *
+	 * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417
+	 */
+	buffer->move_to ((flags & DontAdvance) ? end : end + count);
+      }
+
+      if (flags & SetMark)
+      {
+	mark_set = true;
+	mark = buffer->out_len;
+      }
+
+      return true;
+    }
+
+    public:
+    bool ret;
+    private:
+    hb_aat_apply_context_t *c;
+    bool mark_set;
+    unsigned int mark;
+    const UnsizedArrayOf<GlyphID> &insertionAction;
+  };
+
   inline bool apply (hb_aat_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    /* TODO */
-    return_trace (false);
+
+    driver_context_t dc (this, c);
+
+    StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
+    driver.drive (&dc);
+
+    return_trace (dc.ret);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    /* TODO */
-    return_trace (true);
+    /* The rest of array sanitizations are done at run-time. */
+    return_trace (c->check_struct (this) && machine.sanitize (c) &&
+		  insertionAction);
   }
+
+  protected:
+  StateTable<EntryData>
+		machine;
+  LOffsetTo<UnsizedArrayOf<GlyphID>, false>
+		insertionAction;	/* Byte offset from stateHeader to the start of
+					 * the insertion glyph table. */
+  public:
+  DEFINE_SIZE_STATIC (20);
 };
 
 
@@ -559,9 +755,27 @@
   friend struct Chain;
 
   inline unsigned int get_size (void) const { return length; }
-  inline unsigned int get_type (void) const { return coverage & 0xFF; }
+  inline unsigned int get_type (void) const { return coverage & SubtableType; }
 
-  enum Type {
+  enum Coverage
+  {
+    Vertical		= 0x80000000,	/* If set, this subtable will only be applied
+					 * to vertical text. If clear, this subtable
+					 * will only be applied to horizontal text. */
+    Backwards		= 0x40000000,	/* If set, this subtable will process glyphs
+					 * in descending order. If clear, it will
+					 * process the glyphs in ascending order. */
+    AllDirections	= 0x20000000,	/* If set, this subtable will be applied to
+					 * both horizontal and vertical text (i.e.
+					 * the state of bit 0x80000000 is ignored). */
+    Logical		= 0x10000000,	/* If set, this subtable will process glyphs
+					 * in logical order (or reverse logical order,
+					 * depending on the value of bit 0x80000000). */
+    Reserved		= 0x0FFFFF00,	/* Reserved, set to zero. */
+    SubtableType	= 0x000000FF,	/* Subtable type; see following table. */
+  };
+  enum Type
+  {
     Rearrangement	= 0,
     Contextual		= 1,
     Ligature		= 2,
@@ -569,11 +783,6 @@
     Insertion		= 5
   };
 
-  inline void apply (hb_aat_apply_context_t *c) const
-  {
-    dispatch (c);
-  }
-
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
@@ -619,28 +828,94 @@
 {
   inline void apply (hb_aat_apply_context_t *c) const
   {
-    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
+    uint32_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... */
+	{
+	  flags &= feature.disableFlags;
+	  flags |= feature.enableFlags;
+	}
+      }
+    }
+
+    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (&featureZ, featureZ[0].static_size * featureCount);
     unsigned int count = subtableCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
-      {
-	c->set_lookup_index (c->lookup_index + 1);
-	continue;
-      }
+      bool reverse;
 
-      subtable->apply (c);
-      subtable = &StructAfter<ChainSubtable> (*subtable);
+      if (!(subtable->subFeatureFlags & flags))
+        goto skip;
+
+      if (!(subtable->coverage & ChainSubtable::AllDirections) &&
+	  HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
+	  bool (subtable->coverage & ChainSubtable::Vertical))
+        goto skip;
+
+      /* Buffer contents is always in logical direction.  Determine if
+       * we need to reverse before applying this subtable.  We reverse
+       * back after if we did reverse indeed.
+       *
+       * Quoting the spac:
+       * """
+       * Bits 28 and 30 of the coverage field control the order in which
+       * glyphs are processed when the subtable is run by the layout engine.
+       * Bit 28 is used to indicate if the glyph processing direction is
+       * the same as logical order or layout order. Bit 30 is used to
+       * indicate whether glyphs are processed forwards or backwards within
+       * that order.
+
+		Bit 30	Bit 28	Interpretation for Horizontal Text
+		0	0	The subtable is processed in layout order
+				(the same order as the glyphs, which is
+				always left-to-right).
+		1	0	The subtable is processed in reverse layout order
+				(the order opposite that of the glyphs, which is
+				always right-to-left).
+		0	1	The subtable is processed in logical order
+				(the same order as the characters, which may be
+				left-to-right or right-to-left).
+		1	1	The subtable is processed in reverse logical order
+				(the order opposite that of the characters, which
+				may be right-to-left or left-to-right).
+       */
+      reverse = subtable->coverage & ChainSubtable::Logical ?
+		bool (subtable->coverage & ChainSubtable::Backwards) :
+		bool (subtable->coverage & ChainSubtable::Backwards) !=
+		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
+
+      if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
+        goto skip;
+
+      if (reverse)
+        c->buffer->reverse ();
+
+      c->sanitizer.set_object (*subtable);
+
+      subtable->dispatch (c);
+
+      if (reverse)
+        c->buffer->reverse ();
 
       (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);
 
+      if (unlikely (!c->buffer->successful)) return;
+
+    skip:
+      subtable = &StructAfter<ChainSubtable> (*subtable);
       c->set_lookup_index (c->lookup_index + 1);
     }
   }
 
   inline unsigned int get_size (void) const { return length; }
 
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int major) const
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int version) const
   {
     TRACE_SANITIZE (this);
     if (!length.sanitize (c) ||
@@ -648,10 +923,10 @@
 	!c->check_range (this, length))
       return_trace (false);
 
-    if (!c->check_array (featureZ, featureZ[0].static_size, featureCount))
+    if (!c->check_array (featureZ.arrayZ, featureCount))
       return_trace (false);
 
-    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
+    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (&featureZ, featureZ[0].static_size * featureCount);
     unsigned int count = subtableCount;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -669,9 +944,9 @@
   HBUINT32	featureCount;	/* Number of feature subtable entries. */
   HBUINT32	subtableCount;	/* The number of subtables in the chain. */
 
-  Feature	featureZ[VAR];	/* Features. */
-/*ChainSubtable	subtableX[VAR];*//* Subtables. */
-/*subtableGlyphCoverageArray*/	/* Only if major == 3. */
+  UnsizedArrayOf<Feature>	featureZ;	/* Features. */
+/*ChainSubtable	firstSubtable;*//* Subtables. */
+/*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */
 
   public:
   DEFINE_SIZE_MIN (16);
@@ -679,21 +954,25 @@
 
 
 /*
- * The 'mort'/'morx' Tables
+ * The 'morx' Table
  */
 
 struct morx
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_morx;
 
+  inline bool has_data (void) const { return version != 0; }
+
   inline void apply (hb_aat_apply_context_t *c) const
   {
+    if (unlikely (!c->buffer->successful)) return;
     c->set_lookup_index (0);
-    const Chain *chain = chainsZ;
+    const Chain *chain = &firstChain;
     unsigned int count = chainCount;
     for (unsigned int i = 0; i < count; i++)
     {
       chain->apply (c);
+      if (unlikely (!c->buffer->successful)) return;
       chain = &StructAfter<Chain> (*chain);
     }
   }
@@ -701,16 +980,15 @@
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!version.sanitize (c) ||
-	(version.major >> (sizeof (HBUINT32) == 4 ? 1 : 0)) != 1 ||
+    if (!version.sanitize (c) || version < 2 ||
 	!chainCount.sanitize (c))
       return_trace (false);
 
-    const Chain *chain = chainsZ;
+    const Chain *chain = &firstChain;
     unsigned int count = chainCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (!chain->sanitize (c, version.major))
+      if (!chain->sanitize (c, version))
 	return_trace (false);
       chain = &StructAfter<Chain> (*chain);
     }
@@ -719,11 +997,12 @@
   }
 
   protected:
-  FixedVersion<>version;	/* Version number of the glyph metamorphosis table.
-				 * 1 for mort, 2 or 3 for morx. */
+  HBUINT16	version;	/* Version number of the glyph metamorphosis table.
+				 * 2 or 3. */
+  HBUINT16	unused;		/* Set to 0. */
   HBUINT32	chainCount;	/* Number of metamorphosis chains contained in this
 				 * table. */
-  Chain		chainsZ[VAR];	/* Chains. */
+  Chain		firstChain;	/* Chains. */
 
   public:
   DEFINE_SIZE_MIN (8);
diff --git a/src/hb-aat-layout-private.hh b/src/hb-aat-layout-private.hh
deleted file mode 100644
index ce75c8e..0000000
--- a/src/hb-aat-layout-private.hh
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright © 2017  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_AAT_LAYOUT_PRIVATE_HH
-#define HB_AAT_LAYOUT_PRIVATE_HH
-
-#include "hb-private.hh"
-
-#include "hb-font-private.hh"
-#include "hb-buffer-private.hh"
-#include "hb-open-type-private.hh"
-
-
-HB_INTERNAL void
-hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer);
-
-HB_INTERNAL void
-hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer);
-
-#endif /* HB_AAT_LAYOUT_PRIVATE_HH */
diff --git a/src/hb-aat-layout-trak-table.hh b/src/hb-aat-layout-trak-table.hh
index f5dc558..16729d1 100644
--- a/src/hb-aat-layout-trak-table.hh
+++ b/src/hb-aat-layout-trak-table.hh
@@ -28,9 +28,9 @@
 #ifndef HB_AAT_LAYOUT_TRAK_TABLE_HH
 #define HB_AAT_LAYOUT_TRAK_TABLE_HH
 
-#include "hb-aat-layout-common-private.hh"
-#include "hb-ot-layout-private.hh"
-#include "hb-open-type-private.hh"
+#include "hb-aat-layout-common.hh"
+#include "hb-ot-layout.hh"
+#include "hb-open-type.hh"
 
 /*
  * trak -- Tracking
@@ -46,29 +46,33 @@
 {
   friend struct TrackData;
 
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base,
-			unsigned int size) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) &&
-			  (valuesZ.sanitize (c, base, size))));
-  }
-
-  private:
   inline float get_track_value () const
   {
     return track.to_float ();
   }
 
-  inline int get_value (const void *base, unsigned int index) const
+  inline int get_value (const void *base,
+			unsigned int index,
+			unsigned int nSizes) const
   {
-    return (base+valuesZ)[index];
+    return hb_array_t<FWORD> ((base+valuesZ).arrayZ, nSizes)[index];
+  }
+
+  public:
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base,
+			unsigned int nSizes) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  (valuesZ.sanitize (c, base, nSizes))));
   }
 
   protected:
   Fixed		track;		/* Track value for this record. */
-  NameID	trackNameID;	/* The 'name' table index for this track */
-  OffsetTo<UnsizedArrayOf<FWORD> >
+  NameID	trackNameID;	/* The 'name' table index for this track.
+				 * (a short word or phrase like "loose"
+				 * or "very tight") */
+  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT16, false>
 		valuesZ;	/* Offset from start of tracking table to
 				 * per-size tracking values for this track. */
 
@@ -78,6 +82,68 @@
 
 struct TrackData
 {
+  inline float interpolate_at (unsigned int idx,
+			       float target_size,
+			       const TrackTableEntry &trackTableEntry,
+			       const void *base) const
+  {
+    unsigned int sizes = nSizes;
+    hb_array_t<Fixed> size_table ((base+sizeTable).arrayZ, sizes);
+
+    float s0 = size_table[idx].to_float ();
+    float s1 = size_table[idx + 1].to_float ();
+    float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0);
+    return t * trackTableEntry.get_value (base, idx + 1, sizes) +
+	   (1.f - t) * trackTableEntry.get_value (base, idx, sizes);
+  }
+
+  inline int get_tracking (const void *base, float ptem) const
+  {
+    /* CoreText points are CSS pixels (96 per inch),
+     * NOT typographic points (72 per inch).
+     *
+     * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
+     */
+    float csspx = ptem * 96.f / 72.f;
+
+    /*
+     * Choose track.
+     */
+    const TrackTableEntry *trackTableEntry = nullptr;
+    unsigned int count = nTracks;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      /* Note: Seems like the track entries are sorted by values.  But the
+       * spec doesn't explicitly say that.  It just mentions it in the example. */
+
+      /* For now we only seek for track entries with zero tracking value */
+
+      if (trackTable[i].get_track_value () == 0.f)
+      {
+	trackTableEntry = &trackTable[i];
+	break;
+      }
+    }
+    if (!trackTableEntry) return 0.;
+
+    /*
+     * Choose size.
+     */
+    unsigned int sizes = nSizes;
+    if (!sizes) return 0.;
+    if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
+
+    /* TODO bfind() */
+    hb_array_t<Fixed> size_table ((base+sizeTable).arrayZ, sizes);
+    unsigned int size_index;
+    for (size_index = 0; size_index < sizes - 1; size_index++)
+      if (size_table[size_index].to_float () >= csspx)
+        break;
+
+    return round (interpolate_at (size_index ? size_index - 1 : 0, csspx,
+				  *trackTableEntry, base));
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
@@ -86,56 +152,12 @@
 		  trackTable.sanitize (c, nTracks, base, nSizes));
   }
 
-  inline float get_tracking (const void *base, float ptem) const
-  {
-    /* CoreText points are CSS pixels (96 per inch),
-     * NOT typographic points (72 per inch).
-     *
-     * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
-     */
-    float csspx = ptem * 96.f / 72.f;
-    Fixed fixed_size;
-    fixed_size.set_float (csspx);
-
-    /* XXX Clean this up. Make it work with nSizes==1 and 0. */
-
-    unsigned int sizes = nSizes;
-
-    const TrackTableEntry *trackTableEntry = nullptr;
-    for (unsigned int i = 0; i < sizes; ++i)
-      // For now we only seek for track entries with zero tracking value
-      if (trackTable[i].get_track_value () == 0.f)
-        trackTableEntry = &trackTable[0];
-
-    // We couldn't match any, exit
-    if (!trackTableEntry) return 0.;
-
-    /* TODO bfind() */
-    unsigned int size_index;
-    UnsizedArrayOf<Fixed> size_table = base+sizeTable;
-    for (size_index = 0; size_index < sizes; ++size_index)
-      if (size_table[size_index] >= fixed_size)
-        break;
-
-    // TODO(ebraminio): We don't attempt to extrapolate to larger or
-    // smaller values for now but we should do, per spec
-    if (size_index == sizes)
-      return trackTableEntry->get_value (base, sizes - 1);
-    if (size_index == 0 || size_table[size_index] == fixed_size)
-      return trackTableEntry->get_value (base, size_index);
-
-    float s0 = size_table[size_index - 1].to_float ();
-    float s1 = size_table[size_index].to_float ();
-    float t = (csspx - s0) / (s1 - s0);
-    return (float) t * trackTableEntry->get_value (base, size_index) +
-	   ((float) 1.0 - t) * trackTableEntry->get_value (base, size_index - 1);
-  }
-
   protected:
   HBUINT16	nTracks;	/* Number of separate tracks included in this table. */
   HBUINT16	nSizes;		/* Number of point sizes included in this table. */
-  LOffsetTo<UnsizedArrayOf<Fixed> >
-		sizeTable;	/* Offset to array[nSizes] of size values. */
+  LOffsetTo<UnsizedArrayOf<Fixed>, false>
+		sizeTable;	/* Offset from start of the tracking table to
+				 * Array[nSizes] of size values.. */
   UnsizedArrayOf<TrackTableEntry>
 		trackTable;	/* Array[nTracks] of TrackTableEntry records. */
 
@@ -147,6 +169,8 @@
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_trak;
 
+  inline bool has_data (void) const { return version.to_int () != 0; }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -168,25 +192,25 @@
     if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
     {
       const TrackData &trackData = this+horizData;
-      float tracking = trackData.get_tracking (this, ptem);
-      hb_position_t advance_to_add = c->font->em_scalef_x (tracking / 2);
+      int tracking = trackData.get_tracking (this, ptem);
+      hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2);
+      hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
       foreach_grapheme (buffer, start, end)
       {
-	buffer->pos[start].x_offset += advance_to_add;
 	buffer->pos[start].x_advance += advance_to_add;
-	buffer->pos[end].x_advance += advance_to_add;
+	buffer->pos[start].x_offset += offset_to_add;
       }
     }
     else
     {
       const TrackData &trackData = this+vertData;
-      float tracking = trackData.get_tracking (this, ptem);
-      hb_position_t advance_to_add = c->font->em_scalef_y (tracking / 2);
+      int tracking = trackData.get_tracking (this, ptem);
+      hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2);
+      hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
       foreach_grapheme (buffer, start, end)
       {
-	buffer->pos[start].y_offset += advance_to_add;
 	buffer->pos[start].y_advance += advance_to_add;
-	buffer->pos[end].y_advance += advance_to_add;
+	buffer->pos[start].y_offset += offset_to_add;
       }
     }
 
@@ -194,15 +218,17 @@
   }
 
   protected:
-  FixedVersion<>	version;	/* Version of the tracking table--currently
-					 * 0x00010000u for version 1.0. */
-  HBUINT16		format; 	/* Format of the tracking table */
-  OffsetTo<TrackData>	horizData;	/* TrackData for horizontal text */
-  OffsetTo<TrackData>	vertData;	/* TrackData for vertical text */
+  FixedVersion<>	version;	/* Version of the tracking table
+					 * (0x00010000u for version 1.0). */
+  HBUINT16		format; 	/* Format of the tracking table (set to 0). */
+  OffsetTo<TrackData>	horizData;	/* Offset from start of tracking table to TrackData
+					 * for horizontal text (or 0 if none). */
+  OffsetTo<TrackData>	vertData;	/* Offset from start of tracking table to TrackData
+					 * for vertical text (or 0 if none). */
   HBUINT16		reserved;	/* Reserved. Set to 0. */
 
   public:
-  DEFINE_SIZE_MIN (12);
+  DEFINE_SIZE_STATIC (12);
 };
 
 } /* namespace AAT */
diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc
index 36d4037..e9da850 100644
--- a/src/hb-aat-layout.cc
+++ b/src/hb-aat-layout.cc
@@ -24,12 +24,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
-#include "hb-ot-layout-private.hh"
-#include "hb-ot-layout-gsubgpos-private.hh"
-
-#include "hb-aat-layout-private.hh"
+#include "hb-ot-face.hh"
+#include "hb-aat-layout.hh"
 #include "hb-aat-layout-ankr-table.hh"
 #include "hb-aat-layout-bsln-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-layout-feat-table.hh" // Just so we compile it; unused otherwise.
@@ -38,8 +36,101 @@
 #include "hb-aat-layout-trak-table.hh"
 #include "hb-aat-ltag-table.hh" // Just so we compile it; unused otherwise.
 
+
+/* Table data courtesy of Apple.  Converted from mnemonics to integers
+ * when moving to this file. */
+static const hb_aat_feature_mapping_t feature_mappings[] =
+{
+  {HB_TAG ('c','2','p','c'),	38/*kUpperCaseType*/,			2/*kUpperCasePetiteCapsSelector*/,		0/*kDefaultUpperCaseSelector*/},
+  {HB_TAG ('c','2','s','c'),	38/*kUpperCaseType*/,			1/*kUpperCaseSmallCapsSelector*/,		0/*kDefaultUpperCaseSelector*/},
+  {HB_TAG ('c','a','l','t'),	36/*kContextualAlternatesType*/,	0/*kContextualAlternatesOnSelector*/,		1/*kContextualAlternatesOffSelector*/},
+  {HB_TAG ('c','a','s','e'),	33/*kCaseSensitiveLayoutType*/,		0/*kCaseSensitiveLayoutOnSelector*/,		1/*kCaseSensitiveLayoutOffSelector*/},
+  {HB_TAG ('c','l','i','g'),	1/*kLigaturesType*/,			18/*kContextualLigaturesOnSelector*/,		19/*kContextualLigaturesOffSelector*/},
+  {HB_TAG ('c','p','s','p'),	33/*kCaseSensitiveLayoutType*/,		2/*kCaseSensitiveSpacingOnSelector*/,		3/*kCaseSensitiveSpacingOffSelector*/},
+  {HB_TAG ('c','s','w','h'),	36/*kContextualAlternatesType*/,	4/*kContextualSwashAlternatesOnSelector*/,	5/*kContextualSwashAlternatesOffSelector*/},
+  {HB_TAG ('d','l','i','g'),	1/*kLigaturesType*/,			4/*kRareLigaturesOnSelector*/,			5/*kRareLigaturesOffSelector*/},
+  {HB_TAG ('e','x','p','t'),	20/*kCharacterShapeType*/,		10/*kExpertCharactersSelector*/,		16},
+  {HB_TAG ('f','r','a','c'),	11/*kFractionsType*/,			2/*kDiagonalFractionsSelector*/,		0/*kNoFractionsSelector*/},
+  {HB_TAG ('f','w','i','d'),	22/*kTextSpacingType*/,			1/*kMonospacedTextSelector*/,			7},
+  {HB_TAG ('h','a','l','t'),	22/*kTextSpacingType*/,			6/*kAltHalfWidthTextSelector*/,			7},
+  {HB_TAG ('h','i','s','t'),	1/*kLigaturesType*/,			20/*kHistoricalLigaturesOnSelector*/,		21/*kHistoricalLigaturesOffSelector*/},
+  {HB_TAG ('h','k','n','a'),	34/*kAlternateKanaType*/,		0/*kAlternateHorizKanaOnSelector*/,		1/*kAlternateHorizKanaOffSelector*/,	},
+  {HB_TAG ('h','l','i','g'),	1/*kLigaturesType*/,			20/*kHistoricalLigaturesOnSelector*/,		21/*kHistoricalLigaturesOffSelector*/},
+  {HB_TAG ('h','n','g','l'),	23/*kTransliterationType*/,		1/*kHanjaToHangulSelector*/,			0/*kNoTransliterationSelector*/},
+  {HB_TAG ('h','o','j','o'),	20/*kCharacterShapeType*/,		12/*kHojoCharactersSelector*/,			16},
+  {HB_TAG ('h','w','i','d'),	22/*kTextSpacingType*/,			2/*kHalfWidthTextSelector*/,			7},
+  {HB_TAG ('i','t','a','l'),	32/*kItalicCJKRomanType*/,		2/*kCJKItalicRomanOnSelector*/,			3/*kCJKItalicRomanOffSelector*/},
+  {HB_TAG ('j','p','0','4'),	20/*kCharacterShapeType*/,		11/*kJIS2004CharactersSelector*/,		16},
+  {HB_TAG ('j','p','7','8'),	20/*kCharacterShapeType*/,		2/*kJIS1978CharactersSelector*/,		16},
+  {HB_TAG ('j','p','8','3'),	20/*kCharacterShapeType*/,		3/*kJIS1983CharactersSelector*/,		16},
+  {HB_TAG ('j','p','9','0'),	20/*kCharacterShapeType*/,		4/*kJIS1990CharactersSelector*/,		16},
+  {HB_TAG ('l','i','g','a'),	1/*kLigaturesType*/,			2/*kCommonLigaturesOnSelector*/,		3/*kCommonLigaturesOffSelector*/},
+  {HB_TAG ('l','n','u','m'),	21/*kNumberCaseType*/,			1/*kUpperCaseNumbersSelector*/,			2},
+  {HB_TAG ('m','g','r','k'),	15/*kMathematicalExtrasType*/,		10/*kMathematicalGreekOnSelector*/,		11/*kMathematicalGreekOffSelector*/},
+  {HB_TAG ('n','l','c','k'),	20/*kCharacterShapeType*/,		13/*kNLCCharactersSelector*/,			16},
+  {HB_TAG ('o','n','u','m'),	21/*kNumberCaseType*/,			0/*kLowerCaseNumbersSelector*/,			2},
+  {HB_TAG ('o','r','d','n'),	10/*kVerticalPositionType*/,		3/*kOrdinalsSelector*/,				0/*kNormalPositionSelector*/},
+  {HB_TAG ('p','a','l','t'),	22/*kTextSpacingType*/,			5/*kAltProportionalTextSelector*/,		7},
+  {HB_TAG ('p','c','a','p'),	37/*kLowerCaseType*/,			2/*kLowerCasePetiteCapsSelector*/,		0/*kDefaultLowerCaseSelector*/},
+  {HB_TAG ('p','k','n','a'),	22/*kTextSpacingType*/,			0/*kProportionalTextSelector*/,			7},
+  {HB_TAG ('p','n','u','m'),	6/*kNumberSpacingType*/,		1/*kProportionalNumbersSelector*/,		4},
+  {HB_TAG ('p','w','i','d'),	22/*kTextSpacingType*/,			0/*kProportionalTextSelector*/,			7},
+  {HB_TAG ('q','w','i','d'),	22/*kTextSpacingType*/,			4/*kQuarterWidthTextSelector*/,			7},
+  {HB_TAG ('r','u','b','y'),	28/*kRubyKanaType*/,			2/*kRubyKanaOnSelector*/,			3/*kRubyKanaOffSelector*/},
+  {HB_TAG ('s','i','n','f'),	10/*kVerticalPositionType*/,		4/*kScientificInferiorsSelector*/,		0/*kNormalPositionSelector*/},
+  {HB_TAG ('s','m','c','p'),	37/*kLowerCaseType*/,			1/*kLowerCaseSmallCapsSelector*/,		0/*kDefaultLowerCaseSelector*/},
+  {HB_TAG ('s','m','p','l'),	20/*kCharacterShapeType*/,		1/*kSimplifiedCharactersSelector*/,		16},
+  {HB_TAG ('s','s','0','1'),	35/*kStylisticAlternativesType*/,	2/*kStylisticAltOneOnSelector*/,		3/*kStylisticAltOneOffSelector*/},
+  {HB_TAG ('s','s','0','2'),	35/*kStylisticAlternativesType*/,	4/*kStylisticAltTwoOnSelector*/,		5/*kStylisticAltTwoOffSelector*/},
+  {HB_TAG ('s','s','0','3'),	35/*kStylisticAlternativesType*/,	6/*kStylisticAltThreeOnSelector*/,		7/*kStylisticAltThreeOffSelector*/},
+  {HB_TAG ('s','s','0','4'),	35/*kStylisticAlternativesType*/,	8/*kStylisticAltFourOnSelector*/,		9/*kStylisticAltFourOffSelector*/},
+  {HB_TAG ('s','s','0','5'),	35/*kStylisticAlternativesType*/,	10/*kStylisticAltFiveOnSelector*/,		11/*kStylisticAltFiveOffSelector*/},
+  {HB_TAG ('s','s','0','6'),	35/*kStylisticAlternativesType*/,	12/*kStylisticAltSixOnSelector*/,		13/*kStylisticAltSixOffSelector*/},
+  {HB_TAG ('s','s','0','7'),	35/*kStylisticAlternativesType*/,	14/*kStylisticAltSevenOnSelector*/,		15/*kStylisticAltSevenOffSelector*/},
+  {HB_TAG ('s','s','0','8'),	35/*kStylisticAlternativesType*/,	16/*kStylisticAltEightOnSelector*/,		17/*kStylisticAltEightOffSelector*/},
+  {HB_TAG ('s','s','0','9'),	35/*kStylisticAlternativesType*/,	18/*kStylisticAltNineOnSelector*/,		19/*kStylisticAltNineOffSelector*/},
+  {HB_TAG ('s','s','1','0'),	35/*kStylisticAlternativesType*/,	20/*kStylisticAltTenOnSelector*/,		21/*kStylisticAltTenOffSelector*/},
+  {HB_TAG ('s','s','1','1'),	35/*kStylisticAlternativesType*/,	22/*kStylisticAltElevenOnSelector*/,		23/*kStylisticAltElevenOffSelector*/},
+  {HB_TAG ('s','s','1','2'),	35/*kStylisticAlternativesType*/,	24/*kStylisticAltTwelveOnSelector*/,		25/*kStylisticAltTwelveOffSelector*/},
+  {HB_TAG ('s','s','1','3'),	35/*kStylisticAlternativesType*/,	26/*kStylisticAltThirteenOnSelector*/,		27/*kStylisticAltThirteenOffSelector*/},
+  {HB_TAG ('s','s','1','4'),	35/*kStylisticAlternativesType*/,	28/*kStylisticAltFourteenOnSelector*/,		29/*kStylisticAltFourteenOffSelector*/},
+  {HB_TAG ('s','s','1','5'),	35/*kStylisticAlternativesType*/,	30/*kStylisticAltFifteenOnSelector*/,		31/*kStylisticAltFifteenOffSelector*/},
+  {HB_TAG ('s','s','1','6'),	35/*kStylisticAlternativesType*/,	32/*kStylisticAltSixteenOnSelector*/,		33/*kStylisticAltSixteenOffSelector*/},
+  {HB_TAG ('s','s','1','7'),	35/*kStylisticAlternativesType*/,	34/*kStylisticAltSeventeenOnSelector*/,		35/*kStylisticAltSeventeenOffSelector*/},
+  {HB_TAG ('s','s','1','8'),	35/*kStylisticAlternativesType*/,	36/*kStylisticAltEighteenOnSelector*/,		37/*kStylisticAltEighteenOffSelector*/},
+  {HB_TAG ('s','s','1','9'),	35/*kStylisticAlternativesType*/,	38/*kStylisticAltNineteenOnSelector*/,		39/*kStylisticAltNineteenOffSelector*/},
+  {HB_TAG ('s','s','2','0'),	35/*kStylisticAlternativesType*/,	40/*kStylisticAltTwentyOnSelector*/,		41/*kStylisticAltTwentyOffSelector*/},
+  {HB_TAG ('s','u','b','s'),	10/*kVerticalPositionType*/,		2/*kInferiorsSelector*/,			0/*kNormalPositionSelector*/},
+  {HB_TAG ('s','u','p','s'),	10/*kVerticalPositionType*/,		1/*kSuperiorsSelector*/,			0/*kNormalPositionSelector*/},
+  {HB_TAG ('s','w','s','h'),	36/*kContextualAlternatesType*/,	2/*kSwashAlternatesOnSelector*/,		3/*kSwashAlternatesOffSelector*/},
+  {HB_TAG ('t','i','t','l'),	19/*kStyleOptionsType*/,		4/*kTitlingCapsSelector*/,			0/*kNoStyleOptionsSelector*/},
+  {HB_TAG ('t','n','a','m'),	20/*kCharacterShapeType*/,		14/*kTraditionalNamesCharactersSelector*/,	16},
+  {HB_TAG ('t','n','u','m'),	6/*kNumberSpacingType*/,		0/*kMonospacedNumbersSelector*/,		4},
+  {HB_TAG ('t','r','a','d'),	20/*kCharacterShapeType*/,		0/*kTraditionalCharactersSelector*/,		16},
+  {HB_TAG ('t','w','i','d'),	22/*kTextSpacingType*/,			3/*kThirdWidthTextSelector*/,			7},
+  {HB_TAG ('u','n','i','c'),	3/*kLetterCaseType*/,			14,						15},
+  {HB_TAG ('v','a','l','t'),	22/*kTextSpacingType*/,			5/*kAltProportionalTextSelector*/,		7},
+  {HB_TAG ('v','e','r','t'),	4/*kVerticalSubstitutionType*/,		0/*kSubstituteVerticalFormsOnSelector*/,	1/*kSubstituteVerticalFormsOffSelector*/},
+  {HB_TAG ('v','h','a','l'),	22/*kTextSpacingType*/,			6/*kAltHalfWidthTextSelector*/,			7},
+  {HB_TAG ('v','k','n','a'),	34/*kAlternateKanaType*/,		2/*kAlternateVertKanaOnSelector*/,		3/*kAlternateVertKanaOffSelector*/},
+  {HB_TAG ('v','p','a','l'),	22/*kTextSpacingType*/,			5/*kAltProportionalTextSelector*/,		7},
+  {HB_TAG ('v','r','t','2'),	4/*kVerticalSubstitutionType*/,		0/*kSubstituteVerticalFormsOnSelector*/,	1/*kSubstituteVerticalFormsOffSelector*/},
+  {HB_TAG ('z','e','r','o'),	14/*kTypographicExtrasType*/,		4/*kSlashedZeroOnSelector*/,			5/*kSlashedZeroOffSelector*/},
+};
+
+const hb_aat_feature_mapping_t *
+hb_aat_layout_find_feature_mapping (hb_tag_t tag)
+{
+  return (const hb_aat_feature_mapping_t *) bsearch (&tag,
+						     feature_mappings,
+						     ARRAY_LENGTH (feature_mappings),
+						     sizeof (feature_mappings[0]),
+						     hb_aat_feature_mapping_t::cmp);
+}
+
+
 /*
- * morx/kerx/trak/ankr
+ * morx/kerx/trak
  */
 
 static inline const AAT::morx&
@@ -51,46 +142,101 @@
       *blob = hb_blob_get_empty ();
     return Null(AAT::morx);
   }
-  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  const AAT::morx& morx = *(layout->table.morx.get ());
+  const AAT::morx& morx = *(hb_ot_face_data (face)->morx.get ());
   if (blob)
-    *blob = layout->table.morx.get_blob ();
+    *blob = hb_ot_face_data (face)->morx.get_blob ();
   return morx;
 }
+static inline const AAT::kerx&
+_get_kerx (hb_face_t *face, hb_blob_t **blob = nullptr)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
+  {
+    if (blob)
+      *blob = hb_blob_get_empty ();
+    return Null(AAT::kerx);
+  }
+  const AAT::kerx& kerx = *(hb_ot_face_data (face)->kerx.get ());
+  if (blob)
+    *blob = hb_ot_face_data (face)->kerx.get_blob ();
+  return kerx;
+}
+static inline const AAT::ankr&
+_get_ankr (hb_face_t *face, hb_blob_t **blob = nullptr)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
+  {
+    if (blob)
+      *blob = hb_blob_get_empty ();
+    return Null(AAT::ankr);
+  }
+  const AAT::ankr& ankr = *(hb_ot_face_data (face)->ankr.get ());
+  if (blob)
+    *blob = hb_ot_face_data (face)->ankr.get_blob ();
+  return ankr;
+}
+static inline const AAT::trak&
+_get_trak (hb_face_t *face)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(AAT::trak);
+  return *(hb_ot_face_data (face)->trak.get ());
+}
 
-// static inline void
-// _hb_aat_layout_create (hb_face_t *face)
-// {
-//   hb_blob_t *morx_blob = hb_sanitize_context_t ().reference_table<AAT::morx> (face);
-//   morx_blob->as<AAT::morx> ();
 
-//   if (0)
-//   {
-//     morx_blob->as<AAT::Lookup<OT::GlyphID> > ()->get_value (1, face->get_num_glyphs ());
-//   }
-// }
+hb_bool_t
+hb_aat_layout_has_substitution (hb_face_t *face)
+{
+  return _get_morx (face).has_data ();
+}
 
 void
-hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer)
+hb_aat_layout_substitute (hb_ot_shape_plan_t *plan,
+			  hb_font_t *font,
+			  hb_buffer_t *buffer)
 {
   hb_blob_t *blob;
   const AAT::morx& morx = _get_morx (font->face, &blob);
 
-  AAT::hb_aat_apply_context_t c (font, buffer, blob);
+  AAT::hb_aat_apply_context_t c (plan, font, buffer, blob);
   morx.apply (&c);
 }
 
-void
-hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer)
-{
-#if 0
-  hb_blob_t *blob;
-  const AAT::ankr& ankr = _get_ankr (font->face, &blob);
-  const AAT::kerx& kerx = _get_kerx (font->face, &blob);
-  const AAT::trak& trak = _get_trak (font->face, &blob);
 
-  AAT::hb_aat_apply_context_t c (font, buffer, blob);
-  kerx.apply (&c, &ankr);
+hb_bool_t
+hb_aat_layout_has_positioning (hb_face_t *face)
+{
+  return _get_kerx (face).has_data ();
+}
+
+void
+hb_aat_layout_position (hb_ot_shape_plan_t *plan,
+			hb_font_t *font,
+			hb_buffer_t *buffer)
+{
+  hb_blob_t *blob;
+  const AAT::kerx& kerx = _get_kerx (font->face, &blob);
+
+  hb_blob_t *ankr_blob;
+  const AAT::ankr& ankr = _get_ankr (font->face, &ankr_blob);
+
+  AAT::hb_aat_apply_context_t c (plan, font, buffer, blob,
+				 ankr, ankr_blob->data + ankr_blob->length);
+  kerx.apply (&c);
+}
+
+hb_bool_t
+hb_aat_layout_has_tracking (hb_face_t *face)
+{
+  return _get_trak (face).has_data ();
+}
+
+void
+hb_aat_layout_track (hb_ot_shape_plan_t *plan,
+		     hb_font_t *font,
+		     hb_buffer_t *buffer)
+{
+  const AAT::trak& trak = _get_trak (font->face);
+
+  AAT::hb_aat_apply_context_t c (plan, font, buffer);
   trak.apply (&c);
-#endif
 }
diff --git a/src/hb-aat-layout.hh b/src/hb-aat-layout.hh
new file mode 100644
index 0000000..d0eb019
--- /dev/null
+++ b/src/hb-aat-layout.hh
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_AAT_LAYOUT_HH
+#define HB_AAT_LAYOUT_HH
+
+#include "hb.hh"
+
+#include "hb-ot-shape.hh"
+
+
+struct hb_aat_feature_mapping_t
+{
+  hb_tag_t otFeatureTag;
+  uint16_t aatFeatureType;
+  uint16_t selectorToEnable;
+  uint16_t selectorToDisable;
+
+  static inline int cmp (const void *key_, const void *entry_)
+  {
+    hb_tag_t key = * (unsigned int *) key_;
+    const hb_aat_feature_mapping_t * entry = (const hb_aat_feature_mapping_t *) entry_;
+    return key < entry->otFeatureTag ? -1 :
+	   key > entry->otFeatureTag ? 1 :
+	   0;
+  }
+};
+
+HB_INTERNAL const hb_aat_feature_mapping_t *
+hb_aat_layout_find_feature_mapping (hb_tag_t tag);
+
+
+HB_INTERNAL hb_bool_t
+hb_aat_layout_has_substitution (hb_face_t *face);
+
+HB_INTERNAL void
+hb_aat_layout_substitute (hb_ot_shape_plan_t *plan,
+			  hb_font_t *font,
+			  hb_buffer_t *buffer);
+
+HB_INTERNAL hb_bool_t
+hb_aat_layout_has_positioning (hb_face_t *face);
+
+HB_INTERNAL void
+hb_aat_layout_position (hb_ot_shape_plan_t *plan,
+			hb_font_t *font,
+			hb_buffer_t *buffer);
+
+HB_INTERNAL hb_bool_t
+hb_aat_layout_has_tracking (hb_face_t *face);
+
+HB_INTERNAL void
+hb_aat_layout_track (hb_ot_shape_plan_t *plan,
+		     hb_font_t *font,
+		     hb_buffer_t *buffer);
+
+#endif /* HB_AAT_LAYOUT_HH */
diff --git a/src/hb-aat-ltag-table.hh b/src/hb-aat-ltag-table.hh
index 15c4e89..08a1b51 100644
--- a/src/hb-aat-ltag-table.hh
+++ b/src/hb-aat-ltag-table.hh
@@ -25,7 +25,7 @@
 #ifndef HB_AAT_LTAG_TABLE_HH
 #define HB_AAT_LTAG_TABLE_HH
 
-#include "hb-aat-layout-common-private.hh"
+#include "hb-aat-layout-common.hh"
 
 /*
  * ltag -- Language Tag
@@ -46,7 +46,7 @@
   }
 
   protected:
-  OffsetTo<UnsizedArrayOf<HBUINT8> >
+  OffsetTo<UnsizedArrayOf<HBUINT8>, HBUINT16, false>
 		tag;		/* Offset from the start of the table to
 				 * the beginning of the string */
   HBUINT16	length;		/* String length (in bytes) */
diff --git a/src/hb-atomic-private.hh b/src/hb-atomic.hh
similarity index 81%
rename from src/hb-atomic-private.hh
rename to src/hb-atomic.hh
index 297c646..5cb7ca5 100644
--- a/src/hb-atomic-private.hh
+++ b/src/hb-atomic.hh
@@ -29,10 +29,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_ATOMIC_PRIVATE_HH
-#define HB_ATOMIC_PRIVATE_HH
+#ifndef HB_ATOMIC_HH
+#define HB_ATOMIC_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 /*
@@ -49,17 +49,21 @@
 /* Defined externally, i.e. in config.h. */
 
 
-#elif !defined(HB_NO_MT) && defined(__ATOMIC_CONSUME)
+#elif !defined(HB_NO_MT) && defined(__ATOMIC_ACQUIRE)
 
 /* C++11-style GCC primitives. */
 
+#define _hb_memory_barrier()			__sync_synchronize ()
+
 #define hb_atomic_int_impl_add(AI, V)		__atomic_fetch_add ((AI), (V), __ATOMIC_ACQ_REL)
 #define hb_atomic_int_impl_set_relaxed(AI, V)	__atomic_store_n ((AI), (V), __ATOMIC_RELAXED)
+#define hb_atomic_int_impl_set(AI, V)		__atomic_store_n ((AI), (V), __ATOMIC_RELEASE)
 #define hb_atomic_int_impl_get_relaxed(AI)	__atomic_load_n ((AI), __ATOMIC_RELAXED)
+#define hb_atomic_int_impl_get(AI)		__atomic_load_n ((AI), __ATOMIC_ACQUIRE)
 
 #define hb_atomic_ptr_impl_set_relaxed(P, V)	__atomic_store_n ((P), (V), __ATOMIC_RELAXED)
 #define hb_atomic_ptr_impl_get_relaxed(P)	__atomic_load_n ((P), __ATOMIC_RELAXED)
-#define hb_atomic_ptr_impl_get(P)		__atomic_load_n ((P), __ATOMIC_CONSUME)
+#define hb_atomic_ptr_impl_get(P)		__atomic_load_n ((P), __ATOMIC_ACQUIRE)
 static inline bool
 _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
 {
@@ -74,13 +78,19 @@
 
 #include <atomic>
 
+#define _hb_memory_barrier()			std::atomic_thread_fence(std::memory_order_ack_rel)
+#define _hb_memory_r_barrier()			std::atomic_thread_fence(std::memory_order_acquire)
+#define _hb_memory_w_barrier()			std::atomic_thread_fence(std::memory_order_release)
+
 #define hb_atomic_int_impl_add(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
 #define hb_atomic_int_impl_set_relaxed(AI, V)	(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_relaxed))
+#define hb_atomic_int_impl_set(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_release))
 #define hb_atomic_int_impl_get_relaxed(AI)	(reinterpret_cast<std::atomic<int> *> (AI)->load (std::memory_order_relaxed))
+#define hb_atomic_int_impl_get(AI)		(reinterpret_cast<std::atomic<int> *> (AI)->load (std::memory_order_acquire))
 
 #define hb_atomic_ptr_impl_set_relaxed(P, V)	(reinterpret_cast<std::atomic<void*> *> (P)->store ((V), std::memory_order_relaxed))
 #define hb_atomic_ptr_impl_get_relaxed(P)	(reinterpret_cast<std::atomic<void*> *> (P)->load (std::memory_order_relaxed))
-#define hb_atomic_ptr_impl_get(P)		(reinterpret_cast<std::atomic<void*> *> (P)->load (std::memory_order_consume))
+#define hb_atomic_ptr_impl_get(P)		(reinterpret_cast<std::atomic<void*> *> (P)->load (std::memory_order_acquire))
 static inline bool
 _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
 {
@@ -98,7 +108,7 @@
 {
 #if !defined(MemoryBarrier)
   /* MinGW has a convoluted history of supporting MemoryBarrier. */
-  long dummy = 0;
+  LONG dummy = 0;
   InterlockedExchange (&dummy, 1);
 #else
   MemoryBarrier ();
@@ -106,9 +116,10 @@
 }
 #define _hb_memory_barrier()			_hb_memory_barrier ()
 
-#define hb_atomic_int_impl_add(AI, V)		InterlockedExchangeAdd ((unsigned *) (AI), (V))
+#define hb_atomic_int_impl_add(AI, V)		InterlockedExchangeAdd ((LONG *) (AI), (V))
+static_assert ((sizeof (LONG) == sizeof (int)), "");
 
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)	(InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O))
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	(InterlockedCompareExchangePointer ((P), (N), (O)) == (O))
 
 
 #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
@@ -136,17 +147,17 @@
   _hb_memory_r_barrier ();
   return result;
 }
-static inline bool _hb_compare_and_swap_ptr (const void **P, const void *O, const void *N)
+static inline bool _hb_compare_and_swap_ptr (void **P, void *O, void *N)
 {
   _hb_memory_w_barrier ();
-  int result = atomic_cas_ptr ((void **) P, (void *) O, (void *) N) == (void *) O;
+  bool result = atomic_cas_ptr (P, O, N) == O;
   _hb_memory_r_barrier ();
   return result;
 }
 
 #define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add ((AI), (V))
 
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swap_ptr ((const void **) (P), (O), (N))
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swap_ptr ((P), (O), (N))
 
 
 #elif !defined(HB_NO_MT) && defined(__APPLE__)
@@ -163,12 +174,12 @@
 #define hb_atomic_int_impl_add(AI, V)		(OSAtomicAdd32Barrier ((V), (AI)) - (V))
 
 #if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100)
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P))
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwapPtrBarrier ((O), (N), (P))
 #else
 #if __ppc64__ || __x86_64__ || __aarch64__
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap64Barrier ((int64_t) (void *) (O), (int64_t) (void *) (N), (int64_t*) (P))
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P))
 #else
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap32Barrier ((int32_t) (void *) (O), (int32_t) (void *) (N), (int32_t*) (P))
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P))
 #endif
 #endif
 
@@ -242,6 +253,12 @@
 #ifndef hb_atomic_ptr_impl_get_relaxed
 #define hb_atomic_ptr_impl_get_relaxed(P)	(*(P))
 #endif
+#ifndef hb_atomic_int_impl_set
+inline void hb_atomic_int_impl_set (int *AI, int v)	{ _hb_memory_w_barrier (); *AI = v; }
+#endif
+#ifndef hb_atomic_int_impl_get
+inline int hb_atomic_int_impl_get (int *AI)	{ int v = *AI; _hb_memory_r_barrier (); return v; }
+#endif
 #ifndef hb_atomic_ptr_impl_get
 inline void *hb_atomic_ptr_impl_get (void **P)	{ void *v = *P; _hb_memory_r_barrier (); return v; }
 #endif
@@ -251,7 +268,9 @@
 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); }
 
@@ -270,12 +289,12 @@
 
   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 hb_atomic_ptr_impl_get_relaxed (&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 (&v, old, new_); }
+  inline bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }
 
   mutable T *v;
 };
 
 
-#endif /* HB_ATOMIC_PRIVATE_HH */
+#endif /* HB_ATOMIC_HH */
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index 25c3e05..368491c 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -25,13 +25,8 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-/* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */
-#ifndef _POSIX_C_SOURCE
-#define _POSIX_C_SOURCE 200809L
-#endif
-
-#include "hb-private.hh"
-#include "hb-blob-private.hh"
+#include "hb.hh"
+#include "hb-blob.hh"
 
 #ifdef HAVE_SYS_MMAN_H
 #ifdef HAVE_UNISTD_H
@@ -293,6 +288,8 @@
 {
   if (hb_object_is_inert (blob))
     return;
+  if (blob->immutable)
+    return;
 
   blob->immutable = true;
 }
@@ -490,8 +487,8 @@
 #if defined(_WIN32) || defined(__CYGWIN__)
 # include <windows.h>
 #else
-# ifndef _O_BINARY
-#  define _O_BINARY 0
+# ifndef O_BINARY
+#  define O_BINARY 0
 # endif
 #endif
 
@@ -510,8 +507,9 @@
 
 #if (defined(HAVE_MMAP) || defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_MMAP)
 static void
-_hb_mapped_file_destroy (hb_mapped_file_t *file)
+_hb_mapped_file_destroy (void *file_)
 {
+  hb_mapped_file_t *file = (hb_mapped_file_t *) file_;
 #ifdef HAVE_MMAP
   munmap (file->contents, file->length);
 #elif defined(_WIN32) || defined(__CYGWIN__)
@@ -542,7 +540,7 @@
   hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t));
   if (unlikely (!file)) return hb_blob_get_empty ();
 
-  int fd = open (file_name, O_RDONLY | _O_BINARY, 0);
+  int fd = open (file_name, O_RDONLY | O_BINARY, 0);
   if (unlikely (fd == -1)) goto fail_without_close;
 
   struct stat st;
@@ -574,18 +572,45 @@
   wchar_t * wchar_file_name = (wchar_t *) malloc (sizeof (wchar_t) * size);
   if (unlikely (wchar_file_name == nullptr)) goto fail_without_close;
   mbstowcs (wchar_file_name, file_name, size);
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
+  {
+    CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 };
+    ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
+    ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF;
+    ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000;
+    ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000;
+    ceparams.lpSecurityAttributes = nullptr;
+    ceparams.hTemplateFile = nullptr;
+    fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ,
+                      OPEN_EXISTING, &ceparams);
+  }
+#else
   fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr,
 		    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
 		    nullptr);
+#endif
   free (wchar_file_name);
 
   if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close;
 
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
+  {
+    LARGE_INTEGER length;
+    GetFileSizeEx (fd, &length);
+    file->length = length.LowPart;
+    file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr);
+  }
+#else
   file->length = (unsigned long) GetFileSize (fd, nullptr);
   file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr);
+#endif
   if (unlikely (file->mapping == nullptr)) goto fail;
 
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
+  file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0);
+#else
   file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0);
+#endif
   if (unlikely (file->contents == nullptr)) goto fail;
 
   CloseHandle (fd);
diff --git a/src/hb-blob-private.hh b/src/hb-blob.hh
similarity index 91%
rename from src/hb-blob-private.hh
rename to src/hb-blob.hh
index 49ad68e..bee8c97 100644
--- a/src/hb-blob-private.hh
+++ b/src/hb-blob.hh
@@ -26,10 +26,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_BLOB_PRIVATE_HH
-#define HB_BLOB_PRIVATE_HH
+#ifndef HB_BLOB_HH
+#define HB_BLOB_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 /*
@@ -62,6 +62,10 @@
   {
     return unlikely (!data) ? &Null(Type) : reinterpret_cast<const Type *> (data);
   }
+  inline hb_bytes_t as_bytes (void) const
+  {
+    return hb_bytes_t (data, length);
+  }
 
   public:
   hb_object_header_t header;
@@ -79,4 +83,4 @@
 DECLARE_NULL_INSTANCE (hb_blob_t);
 
 
-#endif /* HB_BLOB_PRIVATE_HH */
+#endif /* HB_BLOB_HH */
diff --git a/src/hb-buffer-deserialize-json.hh b/src/hb-buffer-deserialize-json.hh
index 380f3c5..1f9e2e9 100644
--- a/src/hb-buffer-deserialize-json.hh
+++ b/src/hb-buffer-deserialize-json.hh
@@ -29,7 +29,7 @@
 #ifndef HB_BUFFER_DESERIALIZE_JSON_HH
 #define HB_BUFFER_DESERIALIZE_JSON_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 #line 36 "hb-buffer-deserialize-json.hh"
diff --git a/src/hb-buffer-deserialize-json.rl b/src/hb-buffer-deserialize-json.rl
index ec9bc7c..f3abb02 100644
--- a/src/hb-buffer-deserialize-json.rl
+++ b/src/hb-buffer-deserialize-json.rl
@@ -27,7 +27,7 @@
 #ifndef HB_BUFFER_DESERIALIZE_JSON_HH
 #define HB_BUFFER_DESERIALIZE_JSON_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 %%{
 
diff --git a/src/hb-buffer-deserialize-text.hh b/src/hb-buffer-deserialize-text.hh
index 5bca369..67f0a12 100644
--- a/src/hb-buffer-deserialize-text.hh
+++ b/src/hb-buffer-deserialize-text.hh
@@ -29,7 +29,7 @@
 #ifndef HB_BUFFER_DESERIALIZE_TEXT_HH
 #define HB_BUFFER_DESERIALIZE_TEXT_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 #line 36 "hb-buffer-deserialize-text.hh"
diff --git a/src/hb-buffer-deserialize-text.rl b/src/hb-buffer-deserialize-text.rl
index 1d90979..6268a6c 100644
--- a/src/hb-buffer-deserialize-text.rl
+++ b/src/hb-buffer-deserialize-text.rl
@@ -27,7 +27,7 @@
 #ifndef HB_BUFFER_DESERIALIZE_TEXT_HH
 #define HB_BUFFER_DESERIALIZE_TEXT_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 %%{
 
diff --git a/src/hb-buffer-serialize.cc b/src/hb-buffer-serialize.cc
index 1147194..c1d82ab 100644
--- a/src/hb-buffer-serialize.cc
+++ b/src/hb-buffer-serialize.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-buffer-private.hh"
+#include "hb-buffer.hh"
 
 
 static const char *serialize_formats[] = {
@@ -440,8 +440,8 @@
 hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
 			      const char *buf,
 			      int buf_len, /* -1 means nul-terminated */
-			      const char **end_ptr, /* May be nullptr */
-			      hb_font_t *font, /* May be nullptr */
+			      const char **end_ptr, /* May be NULL */
+			      hb_font_t *font, /* May be NULL */
 			      hb_buffer_serialize_format_t format)
 {
   const char *end;
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index eb7b01c..ce9b053 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -27,8 +27,8 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-buffer-private.hh"
-#include "hb-utf-private.hh"
+#include "hb-buffer.hh"
+#include "hb-utf.hh"
 
 
 /**
@@ -182,7 +182,11 @@
   if (idx + count > len)
   {
     /* Under memory failure we might expose this area.  At least
-     * clean it up.  Oh well... */
+     * clean it up.  Oh well...
+     *
+     * Ideally, we should at least set Default_Ignorable bits on
+     * these, as well as consistent cluster values.  But the former
+     * is layering violation... */
     memset (info + len, 0, (idx + count - len) * sizeof (info[0]));
   }
   len += count;
@@ -219,6 +223,7 @@
   unicode = hb_unicode_funcs_reference (hb_unicode_funcs_get_default ());
   flags = HB_BUFFER_FLAG_DEFAULT;
   replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
+  invisible = 0;
 
   clear ();
 }
@@ -354,6 +359,8 @@
 {
   if (unlikely (!make_room_for (num_in, num_out))) return;
 
+  assert (idx + num_in <= len);
+
   merge_clusters (idx, idx + num_in);
 
   hb_glyph_info_t orig_info = info[idx];
@@ -369,37 +376,6 @@
   out_len += num_out;
 }
 
-void
-hb_buffer_t::output_glyph (hb_codepoint_t glyph_index)
-{
-  if (unlikely (!make_room_for (0, 1))) return;
-
-  out_info[out_len] = info[idx];
-  out_info[out_len].codepoint = glyph_index;
-
-  out_len++;
-}
-
-void
-hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info)
-{
-  if (unlikely (!make_room_for (0, 1))) return;
-
-  out_info[out_len] = glyph_info;
-
-  out_len++;
-}
-
-void
-hb_buffer_t::copy_glyph (void)
-{
-  if (unlikely (!make_room_for (0, 1))) return;
-
-  out_info[out_len] = info[idx];
-
-  out_len++;
-}
-
 bool
 hb_buffer_t::move_to (unsigned int i)
 {
@@ -429,8 +405,14 @@
     unsigned int count = out_len - i;
 
     /* This will blow in our face if memory allocation fails later
-     * in this same lookup... */
-    if (unlikely (idx < count && !shift_forward (count + 32))) return false;
+     * in this same lookup...
+     *
+     * We used to shift with extra 32 items, instead of the 0 below.
+     * But that would leave empty slots in the buffer in case of allocation
+     * failures.  Setting to zero for now to avoid other problems (see
+     * comments in shift_forward().  This can cause O(N^2) behavior more
+     * severely than adding 32 empty slots can... */
+    if (unlikely (idx < count && !shift_forward (count + 0))) return false;
 
     assert (idx >= count);
 
@@ -442,19 +424,6 @@
   return true;
 }
 
-void
-hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index)
-{
-  if (unlikely (out_info != info || out_len != idx)) {
-    if (unlikely (!make_room_for (1, 1))) return;
-    out_info[out_len] = info[idx];
-  }
-  out_info[out_len].codepoint = glyph_index;
-
-  idx++;
-  out_len++;
-}
-
 
 void
 hb_buffer_t::set_masks (hb_mask_t    value,
@@ -709,6 +678,7 @@
   HB_BUFFER_FLAG_DEFAULT,
   HB_BUFFER_CLUSTER_LEVEL_DEFAULT,
   HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
+  0, /* invisible */
   HB_BUFFER_SCRATCH_FLAG_DEFAULT,
   HB_BUFFER_MAX_LEN_DEFAULT,
   HB_BUFFER_MAX_OPS_DEFAULT,
@@ -1028,7 +998,7 @@
  * are orthogonal to the scripts, and though they are related, they are
  * different concepts and should not be confused with each other.
  *
- * Use hb_language_from_string() to convert from ISO 639 language codes to
+ * Use hb_language_from_string() to convert from BCP 47 language tags to
  * #hb_language_t.
  *
  * Since: 0.9.2
@@ -1211,6 +1181,46 @@
 
 
 /**
+ * hb_buffer_set_invisible_glyph:
+ * @buffer: an #hb_buffer_t.
+ * @invisible: the invisible #hb_codepoint_t
+ *
+ * Sets the #hb_codepoint_t that replaces invisible characters in
+ * the shaping result.  If set to zero (default), the glyph for the
+ * U+0020 SPACE character is used.  Otherwise, this value is used
+ * verbatim.
+ *
+ * Since: 2.0.0
+ **/
+void
+hb_buffer_set_invisible_glyph (hb_buffer_t    *buffer,
+			       hb_codepoint_t  invisible)
+{
+  if (unlikely (hb_object_is_inert (buffer)))
+    return;
+
+  buffer->invisible = invisible;
+}
+
+/**
+ * hb_buffer_get_invisible_glyph:
+ * @buffer: an #hb_buffer_t.
+ *
+ * See hb_buffer_set_invisible_glyph().
+ *
+ * Return value: 
+ * The @buffer invisible #hb_codepoint_t.
+ *
+ * Since: 2.0.0
+ **/
+hb_codepoint_t
+hb_buffer_get_invisible_glyph (hb_buffer_t    *buffer)
+{
+  return buffer->invisible;
+}
+
+
+/**
  * hb_buffer_reset:
  * @buffer: an #hb_buffer_t.
  *
@@ -1499,6 +1509,8 @@
  * it will be set to the process's default language as returned by
  * hb_language_get_default().  This may change in the future by
  * taking buffer script into consideration when choosing a language.
+ * Note that hb_language_get_default() is NOT threadsafe the first time
+ * it is called.  See documentation for that function for details.
  *
  * Since: 0.9.7
  **/
@@ -1887,6 +1899,10 @@
 
 /**
  * hb_buffer_diff:
+ * @buffer: a buffer.
+ * @reference: other buffer to compare to.
+ * @dottedcircle_glyph: glyph id of U+25CC DOTTED CIRCLE, or (hb_codepont_t) -1.
+ * @position_fuzz: allowed absolute difference in position values.
  *
  * If dottedcircle_glyph is (hb_codepoint_t) -1 then %HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
  * and %HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned.  This should be used by most
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index 8a2d3e8..d0aed02 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -44,7 +44,6 @@
  * hb_glyph_info_t:
  * @codepoint: either a Unicode code point (before shaping) or a glyph index
  *             (after shaping).
- * @mask: 
  * @cluster: the index of the character in the original text that corresponds
  *           to this #hb_glyph_info_t, or whatever the client passes to
  *           hb_buffer_add(). More than one #hb_glyph_info_t can have the same
@@ -59,11 +58,13 @@
  *
  * The #hb_glyph_info_t is the structure that holds information about the
  * glyphs and their relation to input text.
- *
  */
-typedef struct hb_glyph_info_t {
+typedef struct hb_glyph_info_t
+{
   hb_codepoint_t codepoint;
-  hb_mask_t      mask; /* Holds hb_glyph_flags_t after hb_shape(), plus other things. */
+  /*< private >*/
+  hb_mask_t      mask;
+  /*< public >*/
   uint32_t       cluster;
 
   /*< private >*/
@@ -92,7 +93,7 @@
 typedef enum { /*< flags >*/
   HB_GLYPH_FLAG_UNSAFE_TO_BREAK		= 0x00000001,
 
-  HB_GLYPH_FLAG_DEFINED			= 0x00000001 /* OR of all defined flags */
+  HB_GLYPH_FLAG_DEFINED			= 0x00000001 /*< skip >*/ /* OR of all defined flags */
 } hb_glyph_flags_t;
 
 HB_EXTERN hb_glyph_flags_t
@@ -298,7 +299,15 @@
 HB_EXTERN hb_buffer_flags_t
 hb_buffer_get_flags (hb_buffer_t *buffer);
 
-/*
+/**
+ * hb_buffer_cluster_level_t:
+ * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES: Return cluster values grouped by graphemes into
+ *   monotone order.
+ * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS: Return cluster values grouped into monotone order.
+ * @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: Don't group cluster values.
+ * @HB_BUFFER_CLUSTER_LEVEL_DEFAULT: Default cluster level,
+ *   equal to @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES.
+ *
  * Since: 0.9.42
  */
 typedef enum {
@@ -332,6 +341,13 @@
 HB_EXTERN hb_codepoint_t
 hb_buffer_get_replacement_codepoint (hb_buffer_t    *buffer);
 
+HB_EXTERN void
+hb_buffer_set_invisible_glyph (hb_buffer_t    *buffer,
+			       hb_codepoint_t  invisible);
+
+HB_EXTERN hb_codepoint_t
+hb_buffer_get_invisible_glyph (hb_buffer_t    *buffer);
+
 
 HB_EXTERN void
 hb_buffer_reset (hb_buffer_t *buffer);
diff --git a/src/hb-buffer-private.hh b/src/hb-buffer.hh
similarity index 87%
rename from src/hb-buffer-private.hh
rename to src/hb-buffer.hh
index a6c4b69..0d888e1 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer.hh
@@ -27,11 +27,11 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_BUFFER_PRIVATE_HH
-#define HB_BUFFER_PRIVATE_HH
+#ifndef HB_BUFFER_HH
+#define HB_BUFFER_HH
 
-#include "hb-private.hh"
-#include "hb-unicode-private.hh"
+#include "hb.hh"
+#include "hb-unicode.hh"
 
 
 #ifndef HB_BUFFER_MAX_LEN_FACTOR
@@ -93,6 +93,7 @@
   hb_buffer_flags_t flags; /* BOT / EOT / etc. */
   hb_buffer_cluster_level_t cluster_level;
   hb_codepoint_t replacement; /* U+FFFD or something else. */
+  hb_codepoint_t invisible; /* 0 or something else. */
   hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */
   unsigned int max_len; /* Maximum allowed len. */
   int max_ops; /* Maximum allowed operations. */
@@ -119,7 +120,7 @@
   /* Text before / after the main buffer contents.
    * Always in Unicode, and ordered outward.
    * Index 0 is for "pre-context", 1 for "post-context". */
-  static const unsigned int CONTEXT_LENGTH = 5;
+  enum { CONTEXT_LENGTH = 5 };
   hb_codepoint_t context[2][CONTEXT_LENGTH];
   unsigned int context_len[2];
 
@@ -212,13 +213,49 @@
 				   unsigned int num_out,
 				   const hb_codepoint_t *glyph_data);
 
-  HB_INTERNAL void replace_glyph (hb_codepoint_t glyph_index);
+  inline void replace_glyph (hb_codepoint_t glyph_index)
+  {
+    if (unlikely (out_info != info || out_len != idx)) {
+      if (unlikely (!make_room_for (1, 1))) return;
+      out_info[out_len] = info[idx];
+    }
+    out_info[out_len].codepoint = glyph_index;
+
+    idx++;
+    out_len++;
+  }
   /* Makes a copy of the glyph at idx to output and replace glyph_index */
-  HB_INTERNAL void output_glyph (hb_codepoint_t glyph_index);
-  HB_INTERNAL void output_info (const hb_glyph_info_t &glyph_info);
+  inline hb_glyph_info_t & output_glyph (hb_codepoint_t glyph_index)
+  {
+    if (unlikely (!make_room_for (0, 1))) return Crap(hb_glyph_info_t);
+
+    if (unlikely (idx == len && !out_len))
+      return Crap(hb_glyph_info_t);
+
+    out_info[out_len] = idx < len ? info[idx] : out_info[out_len - 1];
+    out_info[out_len].codepoint = glyph_index;
+
+    out_len++;
+
+    return out_info[out_len - 1];
+  }
+  inline void output_info (const hb_glyph_info_t &glyph_info)
+  {
+    if (unlikely (!make_room_for (0, 1))) return;
+
+    out_info[out_len] = glyph_info;
+
+    out_len++;
+  }
   /* Copies glyph at idx to output but doesn't advance idx */
-  HB_INTERNAL void copy_glyph (void);
-  HB_INTERNAL bool move_to (unsigned int i); /* i is output-buffer index. */
+  inline void copy_glyph (void)
+  {
+    if (unlikely (!make_room_for (0, 1))) return;
+
+    out_info[out_len] = info[idx];
+
+    out_len++;
+  }
   /* Copies glyph at idx to output and advance idx.
    * If there's no output, just advance idx. */
   inline void
@@ -226,7 +263,8 @@
   {
     if (have_output)
     {
-      if (unlikely (out_info != info || out_len != idx)) {
+      if (out_info != info || out_len != idx)
+      {
 	if (unlikely (!make_room_for (1, 1))) return;
 	out_info[out_len] = info[idx];
       }
@@ -235,10 +273,28 @@
 
     idx++;
   }
+  /* Copies n glyphs at idx to output and advance idx.
+   * If there's no output, just advance idx. */
+  inline void
+  next_glyphs (unsigned int n)
+  {
+    if (have_output)
+    {
+      if (out_info != info || out_len != idx)
+      {
+	if (unlikely (!make_room_for (n, n))) return;
+	memmove (out_info + out_len, info + idx, n * sizeof (out_info[0]));
+      }
+      out_len += n;
+    }
 
+    idx += n;
+  }
   /* Advance idx without copying to output. */
-  inline void skip_glyph (void) { idx++; }
-
+  inline void skip_glyph (void)
+  {
+    idx++;
+  }
   inline void reset_masks (hb_mask_t mask)
   {
     for (unsigned int j = 0; j < len; j++)
@@ -275,6 +331,8 @@
 
 
   /* Internal methods */
+  HB_INTERNAL bool move_to (unsigned int i); /* i is output-buffer index. */
+
   HB_INTERNAL bool enlarge (unsigned int size);
 
   inline bool ensure (unsigned int size)
@@ -386,4 +444,4 @@
 #define HB_BUFFER_ASSERT_VAR(b, var)		HB_BUFFER_XALLOCATE_VAR (b, assert_var,     var ())
 
 
-#endif /* HB_BUFFER_PRIVATE_HH */
+#endif /* HB_BUFFER_HH */
diff --git a/src/hb-cache.hh b/src/hb-cache.hh
new file mode 100644
index 0000000..eb48f18
--- /dev/null
+++ b/src/hb-cache.hh
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_CACHE_HH
+#define HB_CACHE_HH
+
+#include "hb.hh"
+
+
+/* Implements a lock-free cache for int->int functions. */
+
+template <unsigned int key_bits, unsigned int value_bits, unsigned int cache_bits>
+struct hb_cache_t
+{
+  static_assert ((key_bits >= cache_bits), "");
+  static_assert ((key_bits + value_bits - cache_bits <= 8 * sizeof (hb_atomic_int_t)), "");
+  static_assert (sizeof (hb_atomic_int_t) == sizeof (unsigned int), "");
+
+  inline void init (void) { clear (); }
+  inline void fini (void) {}
+
+  inline void clear (void)
+  {
+    for (unsigned i = 0; i < ARRAY_LENGTH (values); i++)
+      values[i].set_relaxed (-1);
+  }
+
+  inline bool get (unsigned int key, unsigned int *value) const
+  {
+    unsigned int k = key & ((1u<<cache_bits)-1);
+    unsigned int v = values[k].get_relaxed ();
+    if ((key_bits + value_bits - cache_bits == 8 * sizeof (hb_atomic_int_t) && v == (unsigned int) -1) ||
+	(v >> value_bits) != (key >> cache_bits))
+      return false;
+    *value = v & ((1u<<value_bits)-1);
+    return true;
+  }
+
+  inline bool set (unsigned int key, unsigned int value)
+  {
+    if (unlikely ((key >> key_bits) || (value >> value_bits)))
+      return false; /* Overflows */
+    unsigned int k = key & ((1u<<cache_bits)-1);
+    unsigned int v = ((key>>cache_bits)<<value_bits) | value;
+    values[k].set_relaxed (v);
+    return true;
+  }
+
+  private:
+  hb_atomic_int_t values[1u<<cache_bits];
+};
+
+typedef hb_cache_t<21, 16, 8> hb_cmap_cache_t;
+typedef hb_cache_t<16, 24, 8> hb_advance_cache_t;
+
+
+#endif /* HB_CACHE_HH */
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 22dd52f..ba48dd5 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -26,9 +26,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-machinery-private.hh"
+#include "hb-machinery.hh"
 
 #include <locale.h>
 #ifdef HAVE_XLOCALE_H
@@ -45,10 +45,29 @@
 {
   hb_options_union_t u;
   u.i = 0;
-  u.opts.initialized = 1;
+  u.opts.initialized = true;
 
-  char *c = getenv ("HB_OPTIONS");
-  u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
+  const char *c = getenv ("HB_OPTIONS");
+  if (c)
+  {
+    while (*c)
+    {
+      const char *p = strchr (c, ':');
+      if (!p)
+        p = c + strlen (c);
+
+#define OPTION(name, symbol) \
+	if (0 == strncmp (c, name, p - c) && strlen (name) == p - c) u.opts.symbol = true;
+
+      OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
+      OPTION ("aat", aat);
+
+#undef OPTION
+
+      c = *p ? p + 1 : p;
+    }
+
+  }
 
   /* This is idempotent and threadsafe. */
   _hb_options.set_relaxed (u.i);
@@ -306,14 +325,14 @@
 /**
  * hb_language_from_string:
  * @str: (array length=len) (element-type uint8_t): a string representing
- *       ISO 639 language code
+ *       a BCP 47 language tag
  * @len: length of the @str, or -1 if it is %NULL-terminated.
  *
- * Converts @str representing an ISO 639 language code to the corresponding
+ * Converts @str representing a BCP 47 language tag to the corresponding
  * #hb_language_t.
  *
  * Return value: (transfer none):
- * The #hb_language_t corresponding to the ISO 639 language code.
+ * The #hb_language_t corresponding to the BCP 47 language tag.
  *
  * Since: 0.9.2
  **/
@@ -361,7 +380,14 @@
 /**
  * hb_language_get_default:
  *
+ * Get default language from current locale.
  *
+ * Note that the first time this function is called, it calls
+ * "setlocale (LC_CTYPE, nullptr)" to fetch current locale.  The underlying
+ * setlocale function is, in many implementations, NOT threadsafe.  To avoid
+ * problems, call this function once before multiple threads can call it.
+ * This function is only used from hb_buffer_guess_segment_properties() by
+ * HarfBuzz itself.
  *
  * Return value: (transfer none):
  *
@@ -531,7 +557,6 @@
 
     /* Unicode-8.0 additions */
     case HB_SCRIPT_HATRAN:
-    case HB_SCRIPT_OLD_HUNGARIAN:
 
     /* Unicode-9.0 additions */
     case HB_SCRIPT_ADLAM:
@@ -545,6 +570,7 @@
 
 
     /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
+    case HB_SCRIPT_OLD_HUNGARIAN:
     case HB_SCRIPT_OLD_ITALIC:
     case HB_SCRIPT_RUNIC:
 
@@ -731,8 +757,9 @@
 
 #ifdef USE_XLOCALE
 
-
+#ifdef HB_USE_ATEXIT
 static void free_static_C_locale (void);
+#endif
 
 static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_ptr_t<HB_LOCALE_T>::value,
 							  hb_C_locale_lazy_loader_t>
@@ -876,8 +903,8 @@
 
   bool has_start;
 
-  feature->start = 0;
-  feature->end = (unsigned int) -1;
+  feature->start = HB_FEATURE_GLOBAL_START;
+  feature->end = HB_FEATURE_GLOBAL_END;
 
   if (!parse_char (pp, end, '['))
     return true;
@@ -1064,7 +1091,7 @@
   while (len && s[len - 1] == ' ')
     len--;
   s[len++] = '=';
-  len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
+  len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
 
   assert (len < ARRAY_LENGTH (s));
   len = MIN (len, size - 1);
diff --git a/src/hb-common.h b/src/hb-common.h
index 5dc1ebc..2f09f43 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -63,6 +63,23 @@
 #  include <stdint.h>
 #endif
 
+#if    __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+#define HB_DEPRECATED __attribute__((__deprecated__))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1300)
+#define HB_DEPRECATED __declspec(deprecated)
+#else
+#define HB_DEPRECATED
+#endif
+
+#if    __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+#define HB_DEPRECATED_FOR(f) __attribute__((__deprecated__("Use '" #f "' instead")))
+#elif defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320)
+#define HB_DEPRECATED_FOR(f) __declspec(deprecated("is deprecated. Use '" #f "' instead"))
+#else
+#define HB_DEPRECATED_FOR(f) HB_DEPRECATED
+#endif
+
+
 HB_BEGIN_DECLS
 
 
@@ -86,8 +103,8 @@
 
 typedef uint32_t hb_tag_t;
 
-#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4))))
-#define HB_UNTAG(tag)   ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag))
+#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint32_t)(c1)&0xFF)<<24)|(((uint32_t)(c2)&0xFF)<<16)|(((uint32_t)(c3)&0xFF)<<8)|((uint32_t)(c4)&0xFF)))
+#define HB_UNTAG(tag)   (((tag)>>24)&0xFF), (((tag)>>16)&0xFF), (((tag)>>8)&0xFF), ((tag)&0xFF)
 
 #define HB_TAG_NONE HB_TAG(0,0,0,0)
 #define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff)
@@ -340,13 +357,15 @@
   HB_SCRIPT_INVALID				= HB_TAG_NONE,
 
   /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t
-   * without risking undefined behavior.  Include both a signed and unsigned max,
-   * since technically enums are int, and indeed, hb_script_t ends up being signed.
+   * without risking undefined behavior.  We have two, for historical reasons.
+   * HB_TAG_MAX used to be unsigned, but that was invalid Ansi C, so was changed
+   * to _HB_SCRIPT_MAX_VALUE to be equal to HB_TAG_MAX_SIGNED as well.
+   *
    * See this thread for technicalities:
    *
    *   https://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html
    */
-  _HB_SCRIPT_MAX_VALUE				= HB_TAG_MAX, /*< skip >*/
+  _HB_SCRIPT_MAX_VALUE				= HB_TAG_MAX_SIGNED, /*< skip >*/
   _HB_SCRIPT_MAX_VALUE_SIGNED			= HB_TAG_MAX_SIGNED /*< skip >*/
 
 } hb_script_t;
@@ -379,6 +398,19 @@
 
 /* Font features and variations. */
 
+/**
+ * HB_FEATURE_GLOBAL_START
+ *
+ * Since: 2.0.0
+ */
+#define HB_FEATURE_GLOBAL_START	0
+/**
+ * HB_FEATURE_GLOBAL_END
+ *
+ * Since: 2.0.0
+ */
+#define HB_FEATURE_GLOBAL_END	((unsigned int) -1)
+
 typedef struct hb_feature_t {
   hb_tag_t      tag;
   uint32_t      value;
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc
index 1b916a5..9f7745d 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -28,10 +28,11 @@
 
 #define HB_SHAPER coretext
 
-#include "hb-private.hh"
-#include "hb-shaper-impl-private.hh"
+#include "hb.hh"
+#include "hb-shaper-impl.hh"
 
 #include "hb-coretext.h"
+#include "hb-aat-layout.hh"
 #include <math.h>
 
 /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
@@ -210,7 +211,7 @@
   }
 
   CFURLRef original_url = nullptr;
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+#if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   ATSFontRef atsFont;
   FSRef fsref;
   OSStatus status;
@@ -240,7 +241,7 @@
        * process in Blink. This can be detected by the new file URL location
        * that the newly found font points to. */
       CFURLRef new_url = nullptr;
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+#if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
       atsFont = CTFontGetPlatformFont (new_ct_font, NULL);
       status = ATSFontGetFileReference (atsFont, &fsref);
       if (status == noErr)
@@ -431,183 +432,6 @@
 };
 
 
-/* The following enum members are added in OS X 10.8. */
-#define kAltHalfWidthTextSelector		6
-#define kAltProportionalTextSelector		5
-#define kAlternateHorizKanaOffSelector		1
-#define kAlternateHorizKanaOnSelector		0
-#define kAlternateKanaType			34
-#define kAlternateVertKanaOffSelector		3
-#define kAlternateVertKanaOnSelector		2
-#define kCaseSensitiveLayoutOffSelector		1
-#define kCaseSensitiveLayoutOnSelector		0
-#define kCaseSensitiveLayoutType		33
-#define kCaseSensitiveSpacingOffSelector	3
-#define kCaseSensitiveSpacingOnSelector		2
-#define kContextualAlternatesOffSelector	1
-#define kContextualAlternatesOnSelector		0
-#define kContextualAlternatesType		36
-#define kContextualLigaturesOffSelector		19
-#define kContextualLigaturesOnSelector		18
-#define kContextualSwashAlternatesOffSelector	5
-#define kContextualSwashAlternatesOnSelector	4
-#define kDefaultLowerCaseSelector		0
-#define kDefaultUpperCaseSelector		0
-#define kHistoricalLigaturesOffSelector		21
-#define kHistoricalLigaturesOnSelector		20
-#define kHojoCharactersSelector			12
-#define kJIS2004CharactersSelector		11
-#define kLowerCasePetiteCapsSelector		2
-#define kLowerCaseSmallCapsSelector		1
-#define kLowerCaseType				37
-#define kMathematicalGreekOffSelector		11
-#define kMathematicalGreekOnSelector		10
-#define kNLCCharactersSelector			13
-#define kQuarterWidthTextSelector		4
-#define kScientificInferiorsSelector		4
-#define kStylisticAltEightOffSelector		17
-#define kStylisticAltEightOnSelector		16
-#define kStylisticAltEighteenOffSelector	37
-#define kStylisticAltEighteenOnSelector		36
-#define kStylisticAltElevenOffSelector		23
-#define kStylisticAltElevenOnSelector		22
-#define kStylisticAltFifteenOffSelector		31
-#define kStylisticAltFifteenOnSelector		30
-#define kStylisticAltFiveOffSelector		11
-#define kStylisticAltFiveOnSelector		10
-#define kStylisticAltFourOffSelector		9
-#define kStylisticAltFourOnSelector		8
-#define kStylisticAltFourteenOffSelector	29
-#define kStylisticAltFourteenOnSelector		28
-#define kStylisticAltNineOffSelector		19
-#define kStylisticAltNineOnSelector		18
-#define kStylisticAltNineteenOffSelector	39
-#define kStylisticAltNineteenOnSelector		38
-#define kStylisticAltOneOffSelector		3
-#define kStylisticAltOneOnSelector		2
-#define kStylisticAltSevenOffSelector		15
-#define kStylisticAltSevenOnSelector		14
-#define kStylisticAltSeventeenOffSelector	35
-#define kStylisticAltSeventeenOnSelector	34
-#define kStylisticAltSixOffSelector		13
-#define kStylisticAltSixOnSelector		12
-#define kStylisticAltSixteenOffSelector		33
-#define kStylisticAltSixteenOnSelector		32
-#define kStylisticAltTenOffSelector		21
-#define kStylisticAltTenOnSelector		20
-#define kStylisticAltThirteenOffSelector	27
-#define kStylisticAltThirteenOnSelector		26
-#define kStylisticAltThreeOffSelector		7
-#define kStylisticAltThreeOnSelector		6
-#define kStylisticAltTwelveOffSelector		25
-#define kStylisticAltTwelveOnSelector		24
-#define kStylisticAltTwentyOffSelector		41
-#define kStylisticAltTwentyOnSelector		40
-#define kStylisticAltTwoOffSelector		5
-#define kStylisticAltTwoOnSelector		4
-#define kStylisticAlternativesType		35
-#define kSwashAlternatesOffSelector		3
-#define kSwashAlternatesOnSelector		2
-#define kThirdWidthTextSelector			3
-#define kTraditionalNamesCharactersSelector	14
-#define kUpperCasePetiteCapsSelector		2
-#define kUpperCaseSmallCapsSelector		1
-#define kUpperCaseType				38
-
-/* Table data courtesy of Apple. */
-static const struct feature_mapping_t {
-    FourCharCode otFeatureTag;
-    uint16_t aatFeatureType;
-    uint16_t selectorToEnable;
-    uint16_t selectorToDisable;
-} feature_mappings[] = {
-    { 'c2pc',   kUpperCaseType,             kUpperCasePetiteCapsSelector,           kDefaultUpperCaseSelector },
-    { 'c2sc',   kUpperCaseType,             kUpperCaseSmallCapsSelector,            kDefaultUpperCaseSelector },
-    { 'calt',   kContextualAlternatesType,  kContextualAlternatesOnSelector,        kContextualAlternatesOffSelector },
-    { 'case',   kCaseSensitiveLayoutType,   kCaseSensitiveLayoutOnSelector,         kCaseSensitiveLayoutOffSelector },
-    { 'clig',   kLigaturesType,             kContextualLigaturesOnSelector,         kContextualLigaturesOffSelector },
-    { 'cpsp',   kCaseSensitiveLayoutType,   kCaseSensitiveSpacingOnSelector,        kCaseSensitiveSpacingOffSelector },
-    { 'cswh',   kContextualAlternatesType,  kContextualSwashAlternatesOnSelector,   kContextualSwashAlternatesOffSelector },
-    { 'dlig',   kLigaturesType,             kRareLigaturesOnSelector,               kRareLigaturesOffSelector },
-    { 'expt',   kCharacterShapeType,        kExpertCharactersSelector,              16 },
-    { 'frac',   kFractionsType,             kDiagonalFractionsSelector,             kNoFractionsSelector },
-    { 'fwid',   kTextSpacingType,           kMonospacedTextSelector,                7 },
-    { 'halt',   kTextSpacingType,           kAltHalfWidthTextSelector,              7 },
-    { 'hist',   kLigaturesType,             kHistoricalLigaturesOnSelector,         kHistoricalLigaturesOffSelector },
-    { 'hkna',   kAlternateKanaType,         kAlternateHorizKanaOnSelector,          kAlternateHorizKanaOffSelector, },
-    { 'hlig',   kLigaturesType,             kHistoricalLigaturesOnSelector,         kHistoricalLigaturesOffSelector },
-    { 'hngl',   kTransliterationType,       kHanjaToHangulSelector,                 kNoTransliterationSelector },
-    { 'hojo',   kCharacterShapeType,        kHojoCharactersSelector,                16 },
-    { 'hwid',   kTextSpacingType,           kHalfWidthTextSelector,                 7 },
-    { 'ital',   kItalicCJKRomanType,        kCJKItalicRomanOnSelector,              kCJKItalicRomanOffSelector },
-    { 'jp04',   kCharacterShapeType,        kJIS2004CharactersSelector,             16 },
-    { 'jp78',   kCharacterShapeType,        kJIS1978CharactersSelector,             16 },
-    { 'jp83',   kCharacterShapeType,        kJIS1983CharactersSelector,             16 },
-    { 'jp90',   kCharacterShapeType,        kJIS1990CharactersSelector,             16 },
-    { 'liga',   kLigaturesType,             kCommonLigaturesOnSelector,             kCommonLigaturesOffSelector },
-    { 'lnum',   kNumberCaseType,            kUpperCaseNumbersSelector,              2 },
-    { 'mgrk',   kMathematicalExtrasType,    kMathematicalGreekOnSelector,           kMathematicalGreekOffSelector },
-    { 'nlck',   kCharacterShapeType,        kNLCCharactersSelector,                 16 },
-    { 'onum',   kNumberCaseType,            kLowerCaseNumbersSelector,              2 },
-    { 'ordn',   kVerticalPositionType,      kOrdinalsSelector,                      kNormalPositionSelector },
-    { 'palt',   kTextSpacingType,           kAltProportionalTextSelector,           7 },
-    { 'pcap',   kLowerCaseType,             kLowerCasePetiteCapsSelector,           kDefaultLowerCaseSelector },
-    { 'pkna',   kTextSpacingType,           kProportionalTextSelector,              7 },
-    { 'pnum',   kNumberSpacingType,         kProportionalNumbersSelector,           4 },
-    { 'pwid',   kTextSpacingType,           kProportionalTextSelector,              7 },
-    { 'qwid',   kTextSpacingType,           kQuarterWidthTextSelector,              7 },
-    { 'ruby',   kRubyKanaType,              kRubyKanaOnSelector,                    kRubyKanaOffSelector },
-    { 'sinf',   kVerticalPositionType,      kScientificInferiorsSelector,           kNormalPositionSelector },
-    { 'smcp',   kLowerCaseType,             kLowerCaseSmallCapsSelector,            kDefaultLowerCaseSelector },
-    { 'smpl',   kCharacterShapeType,        kSimplifiedCharactersSelector,          16 },
-    { 'ss01',   kStylisticAlternativesType, kStylisticAltOneOnSelector,             kStylisticAltOneOffSelector },
-    { 'ss02',   kStylisticAlternativesType, kStylisticAltTwoOnSelector,             kStylisticAltTwoOffSelector },
-    { 'ss03',   kStylisticAlternativesType, kStylisticAltThreeOnSelector,           kStylisticAltThreeOffSelector },
-    { 'ss04',   kStylisticAlternativesType, kStylisticAltFourOnSelector,            kStylisticAltFourOffSelector },
-    { 'ss05',   kStylisticAlternativesType, kStylisticAltFiveOnSelector,            kStylisticAltFiveOffSelector },
-    { 'ss06',   kStylisticAlternativesType, kStylisticAltSixOnSelector,             kStylisticAltSixOffSelector },
-    { 'ss07',   kStylisticAlternativesType, kStylisticAltSevenOnSelector,           kStylisticAltSevenOffSelector },
-    { 'ss08',   kStylisticAlternativesType, kStylisticAltEightOnSelector,           kStylisticAltEightOffSelector },
-    { 'ss09',   kStylisticAlternativesType, kStylisticAltNineOnSelector,            kStylisticAltNineOffSelector },
-    { 'ss10',   kStylisticAlternativesType, kStylisticAltTenOnSelector,             kStylisticAltTenOffSelector },
-    { 'ss11',   kStylisticAlternativesType, kStylisticAltElevenOnSelector,          kStylisticAltElevenOffSelector },
-    { 'ss12',   kStylisticAlternativesType, kStylisticAltTwelveOnSelector,          kStylisticAltTwelveOffSelector },
-    { 'ss13',   kStylisticAlternativesType, kStylisticAltThirteenOnSelector,        kStylisticAltThirteenOffSelector },
-    { 'ss14',   kStylisticAlternativesType, kStylisticAltFourteenOnSelector,        kStylisticAltFourteenOffSelector },
-    { 'ss15',   kStylisticAlternativesType, kStylisticAltFifteenOnSelector,         kStylisticAltFifteenOffSelector },
-    { 'ss16',   kStylisticAlternativesType, kStylisticAltSixteenOnSelector,         kStylisticAltSixteenOffSelector },
-    { 'ss17',   kStylisticAlternativesType, kStylisticAltSeventeenOnSelector,       kStylisticAltSeventeenOffSelector },
-    { 'ss18',   kStylisticAlternativesType, kStylisticAltEighteenOnSelector,        kStylisticAltEighteenOffSelector },
-    { 'ss19',   kStylisticAlternativesType, kStylisticAltNineteenOnSelector,        kStylisticAltNineteenOffSelector },
-    { 'ss20',   kStylisticAlternativesType, kStylisticAltTwentyOnSelector,          kStylisticAltTwentyOffSelector },
-    { 'subs',   kVerticalPositionType,      kInferiorsSelector,                     kNormalPositionSelector },
-    { 'sups',   kVerticalPositionType,      kSuperiorsSelector,                     kNormalPositionSelector },
-    { 'swsh',   kContextualAlternatesType,  kSwashAlternatesOnSelector,             kSwashAlternatesOffSelector },
-    { 'titl',   kStyleOptionsType,          kTitlingCapsSelector,                   kNoStyleOptionsSelector },
-    { 'tnam',   kCharacterShapeType,        kTraditionalNamesCharactersSelector,    16 },
-    { 'tnum',   kNumberSpacingType,         kMonospacedNumbersSelector,             4 },
-    { 'trad',   kCharacterShapeType,        kTraditionalCharactersSelector,         16 },
-    { 'twid',   kTextSpacingType,           kThirdWidthTextSelector,                7 },
-    { 'unic',   kLetterCaseType,            14,                                     15 },
-    { 'valt',   kTextSpacingType,           kAltProportionalTextSelector,           7 },
-    { 'vert',   kVerticalSubstitutionType,  kSubstituteVerticalFormsOnSelector,     kSubstituteVerticalFormsOffSelector },
-    { 'vhal',   kTextSpacingType,           kAltHalfWidthTextSelector,              7 },
-    { 'vkna',   kAlternateKanaType,         kAlternateVertKanaOnSelector,           kAlternateVertKanaOffSelector },
-    { 'vpal',   kTextSpacingType,           kAltProportionalTextSelector,           7 },
-    { 'vrt2',   kVerticalSubstitutionType,  kSubstituteVerticalFormsOnSelector,     kSubstituteVerticalFormsOffSelector },
-    { 'zero',   kTypographicExtrasType,     kSlashedZeroOnSelector,                 kSlashedZeroOffSelector },
-};
-
-static int
-_hb_feature_mapping_cmp (const void *key_, const void *entry_)
-{
-  unsigned int key = * (unsigned int *) key_;
-  const feature_mapping_t * entry = (const feature_mapping_t *) entry_;
-  return key < entry->otFeatureTag ? -1 :
-	 key > entry->otFeatureTag ? 1 :
-	 0;
-}
-
 hb_bool_t
 _hb_coretext_shape (hb_shape_plan_t    *shape_plan,
 		    hb_font_t          *font,
@@ -624,7 +448,7 @@
   CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size;
 
   /* Attach marks to their bases, to match the 'ot' shaper.
-   * Adapted from hb-ot-shape:hb_form_clusters().
+   * Adapted from a very old version of hb-ot-shape:hb_form_clusters().
    * Note that this only makes us be closer to the 'ot' shaper,
    * but by no means the same.  For example, if there's
    * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will
@@ -653,11 +477,7 @@
     hb_auto_t<hb_vector_t<feature_event_t> > feature_events;
     for (unsigned int i = 0; i < num_features; i++)
     {
-      const feature_mapping_t * mapping = (const feature_mapping_t *) bsearch (&features[i].tag,
-									       feature_mappings,
-									       ARRAY_LENGTH (feature_mappings),
-									       sizeof (feature_mappings[0]),
-									       _hb_feature_mapping_cmp);
+      const hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag);
       if (!mapping)
         continue;
 
@@ -766,7 +586,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());
       }
     }
   }
@@ -823,7 +643,7 @@
   CFStringRef string_ref = nullptr;
   CTLineRef line = nullptr;
 
-  if (0)
+  if (false)
   {
 resize_and_retry:
     DEBUG_MSG (CORETEXT, buffer, "Buffer resize");
@@ -1234,7 +1054,7 @@
      *
      * https://crbug.com/419769
      */
-    if (0)
+    if (false)
     {
       /* Make sure all runs had the expected direction. */
       bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
diff --git a/src/hb-debug.hh b/src/hb-debug.hh
index 9056ffc..58c190d 100644
--- a/src/hb-debug.hh
+++ b/src/hb-debug.hh
@@ -27,8 +27,8 @@
 #ifndef HB_DEBUG_HH
 #define HB_DEBUG_HH
 
-#include "hb-private.hh"
-#include "hb-atomic-private.hh"
+#include "hb.hh"
+#include "hb-atomic.hh"
 #include "hb-dsalgs.hh"
 
 
@@ -43,9 +43,10 @@
 
 struct hb_options_t
 {
-  unsigned int unused : 1; /* In-case sign bit is here. */
-  unsigned int initialized : 1;
-  unsigned int uniscribe_bug_compatible : 1;
+  bool unused : 1; /* In-case sign bit is here. */
+  bool initialized : 1;
+  bool uniscribe_bug_compatible : 1;
+  bool aat : 1;
 };
 
 union hb_options_union_t {
@@ -67,7 +68,10 @@
   u.i = _hb_options.get_relaxed ();
 
   if (unlikely (!u.i))
+  {
     _hb_options_init ();
+    u.i = _hb_options.get_relaxed ();
+  }
 
   return u.opts;
 }
diff --git a/src/hb-deprecated.h b/src/hb-deprecated.h
index eac7efb..5af9bdb 100644
--- a/src/hb-deprecated.h
+++ b/src/hb-deprecated.h
@@ -50,14 +50,191 @@
 					       hb_codepoint_t *glyph,
 					       void *user_data);
 
-HB_EXTERN void
+HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func or hb_font_funcs_set_variation_glyph_func) void
 hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
 			      hb_font_get_glyph_func_t func,
 			      void *user_data, hb_destroy_func_t destroy);
 
-HB_EXTERN void
+HB_EXTERN HB_DEPRECATED void
 hb_set_invert (hb_set_t *set);
 
+/**
+ * hb_unicode_eastasian_width_func_t:
+ *
+ * Deprecated: 2.0.0
+ */
+typedef unsigned int			(*hb_unicode_eastasian_width_func_t)	(hb_unicode_funcs_t *ufuncs,
+										 hb_codepoint_t      unicode,
+										 void               *user_data);
+
+/**
+ * hb_unicode_funcs_set_eastasian_width_func:
+ * @ufuncs: a Unicode function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 0.9.2
+ * Deprecated: 2.0.0
+ **/
+HB_EXTERN HB_DEPRECATED void
+hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs,
+					   hb_unicode_eastasian_width_func_t func,
+					   void *user_data, hb_destroy_func_t destroy);
+
+/**
+ * hb_unicode_eastasian_width:
+ *
+ * Since: 0.9.2
+ * Deprecated: 2.0.0
+ **/
+HB_EXTERN HB_DEPRECATED unsigned int
+hb_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs,
+			    hb_codepoint_t unicode);
+
+
+/**
+ * hb_unicode_decompose_compatibility_func_t:
+ * @ufuncs: a Unicode function structure
+ * @u: codepoint to decompose
+ * @decomposed: address of codepoint array (of length %HB_UNICODE_MAX_DECOMPOSITION_LEN) to write decomposition into
+ * @user_data: user data pointer as passed to hb_unicode_funcs_set_decompose_compatibility_func()
+ *
+ * Fully decompose @u to its Unicode compatibility decomposition. The codepoints of the decomposition will be written to @decomposed.
+ * The complete length of the decomposition will be returned.
+ *
+ * If @u has no compatibility decomposition, zero should be returned.
+ *
+ * The Unicode standard guarantees that a buffer of length %HB_UNICODE_MAX_DECOMPOSITION_LEN codepoints will always be sufficient for any
+ * compatibility decomposition plus an terminating value of 0.  Consequently, @decompose must be allocated by the caller to be at least this length.  Implementations
+ * of this function type must ensure that they do not write past the provided array.
+ *
+ * Return value: number of codepoints in the full compatibility decomposition of @u, or 0 if no decomposition available.
+ *
+ * Deprecated: 2.0.0
+ */
+typedef unsigned int			(*hb_unicode_decompose_compatibility_func_t)	(hb_unicode_funcs_t *ufuncs,
+											 hb_codepoint_t      u,
+											 hb_codepoint_t     *decomposed,
+											 void               *user_data);
+
+/**
+ * HB_UNICODE_MAX_DECOMPOSITION_LEN:
+ *
+ * See Unicode 6.1 for details on the maximum decomposition length.
+ *
+ * Deprecated: 2.0.0
+ */
+#define HB_UNICODE_MAX_DECOMPOSITION_LEN (18+1) /* codepoints */
+
+/**
+ * hb_unicode_funcs_set_decompose_compatibility_func:
+ * @ufuncs: a Unicode function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 0.9.2
+ * Deprecated: 2.0.0
+ **/
+HB_EXTERN HB_DEPRECATED void
+hb_unicode_funcs_set_decompose_compatibility_func (hb_unicode_funcs_t *ufuncs,
+						   hb_unicode_decompose_compatibility_func_t func,
+						   void *user_data, hb_destroy_func_t destroy);
+
+/**
+ * hb_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,
+				    hb_codepoint_t     *decomposed);
+
+
+typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data,
+							   hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
+							   void *user_data);
+typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t;
+typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t;
+
+/**
+ * hb_font_funcs_set_glyph_h_kerning_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 0.9.2
+ * Deprecated: 2.0.0
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_h_kerning_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
+/**
+ * hb_font_funcs_set_glyph_v_kerning_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ * 
+ *
+ * Since: 0.9.2
+ * Deprecated: 2.0.0
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_v_kerning_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
+HB_EXTERN hb_position_t
+hb_font_get_glyph_h_kerning (hb_font_t *font,
+			     hb_codepoint_t left_glyph, hb_codepoint_t right_glyph);
+HB_EXTERN hb_position_t
+hb_font_get_glyph_v_kerning (hb_font_t *font,
+			     hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph);
+
+HB_EXTERN void
+hb_font_get_glyph_kerning_for_direction (hb_font_t *font,
+					 hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
+					 hb_direction_t direction,
+					 hb_position_t *x, hb_position_t *y);
+
+/* Like hb_ot_layout_table_find_script, but takes zero-terminated array of scripts to test */
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) hb_bool_t
+hb_ot_layout_table_choose_script (hb_face_t      *face,
+				  hb_tag_t        table_tag,
+				  const hb_tag_t *script_tags,
+				  unsigned int   *script_index,
+				  hb_tag_t       *chosen_script);
+
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_script_select_language) hb_bool_t
+hb_ot_layout_script_find_language (hb_face_t    *face,
+				   hb_tag_t      table_tag,
+				   unsigned int  script_index,
+				   hb_tag_t      language_tag,
+				   unsigned int *language_index);
+
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) void
+hb_ot_tags_from_script (hb_script_t  script,
+			hb_tag_t    *script_tag_1,
+			hb_tag_t    *script_tag_2);
+
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) hb_tag_t
+hb_ot_tag_from_language (hb_language_t language);
+
+
 #endif
 
 HB_END_DECLS
diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc
index baad818..35197c2 100644
--- a/src/hb-directwrite.cc
+++ b/src/hb-directwrite.cc
@@ -22,9 +22,9 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 #define HB_SHAPER directwrite
-#include "hb-shaper-impl-private.hh"
+#include "hb-shaper-impl.hh"
 
 #include <DWrite_1.h>
 
diff --git a/src/hb-dsalgs.hh b/src/hb-dsalgs.hh
index 8cbe658..eb15c08 100644
--- a/src/hb-dsalgs.hh
+++ b/src/hb-dsalgs.hh
@@ -27,7 +27,9 @@
 #ifndef HB_DSALGS_HH
 #define HB_DSALGS_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
+
+#include "hb-null.hh"
 
 
 /* Void! For when we need a expression-type of void. */
@@ -492,23 +494,55 @@
 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) : bytes (nullptr), len (0) {}
-  inline hb_bytes_t (const char *bytes_, unsigned int len_) : bytes (bytes_), len (len_) {}
+  inline hb_bytes_t (void) : arrayZ (nullptr), len (0) {}
+  inline hb_bytes_t (const char *bytes_, unsigned int len_) : arrayZ (bytes_), len (len_) {}
+  inline hb_bytes_t (const void *bytes_, unsigned int len_) : arrayZ ((const char *) bytes_), len (len_) {}
+  template <typename T>
+  inline hb_bytes_t (const T& array) : arrayZ ((const char *) array.arrayZ), len (array.len) {}
+
+  inline void free (void) { ::free ((void *) arrayZ); arrayZ = nullptr; len = 0; }
 
   inline int cmp (const hb_bytes_t &a) const
   {
     if (len != a.len)
       return (int) a.len - (int) len;
 
-    return memcmp (a.bytes, bytes, len);
+    return memcmp (a.arrayZ, arrayZ, len);
   }
   static inline int cmp (const void *pa, const void *pb)
   {
@@ -517,7 +551,7 @@
     return b->cmp (*a);
   }
 
-  const char *bytes;
+  const char *arrayZ;
   unsigned int len;
 };
 
diff --git a/src/hb-face.cc b/src/hb-face.cc
index 49f29d3..bba1ee3 100644
--- a/src/hb-face.cc
+++ b/src/hb-face.cc
@@ -26,20 +26,22 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-face-private.hh"
-#include "hb-blob-private.hh"
-#include "hb-open-file-private.hh"
+#include "hb-face.hh"
+#include "hb-blob.hh"
+#include "hb-open-file.hh"
+#include "hb-ot-face.hh"
+#include "hb-ot-cmap-table.hh"
 
 
 /**
- * hb_face_count: Get number of faces on the blob
- * @blob:
+ * hb_face_count:
+ * @blob: a blob.
  *
+ * Get number of faces in a blob.
  *
- *
- * Return value: Number of faces on the blob
+ * Return value: Number of faces in @blob
  *
  * Since: 1.7.7
  **/
@@ -161,11 +163,12 @@
     return hb_blob_reference (data->blob);
 
   const OT::OpenTypeFontFile &ot_file = *data->blob->as<OT::OpenTypeFontFile> ();
-  const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
+  unsigned int base_offset;
+  const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index, &base_offset);
 
   const OT::OpenTypeTable &table = ot_face.get_table_by_tag (tag);
 
-  hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, table.offset, table.length);
+  hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, base_offset + table.offset, table.length);
 
   return blob;
 }
@@ -322,6 +325,8 @@
 {
   if (unlikely (hb_object_is_inert (face)))
     return;
+  if (face->immutable)
+    return;
 
   face->immutable = true;
 }
@@ -485,6 +490,9 @@
 /**
  * hb_face_get_table_tags:
  * @face: a face.
+ * @start_offset: index of first tag to return.
+ * @table_count: input length of @table_tags array, output number of items written.
+ * @table_tags: array to write tags into.
  *
  * Retrieves table tags for a face, if possible.
  *
@@ -512,3 +520,208 @@
 
   return ot_face.get_table_tags (start_offset, table_count, table_tags);
 }
+
+
+/*
+ * Character set.
+ */
+
+
+/**
+ * hb_face_collect_unicodes:
+ * @face: font face.
+ * @out: set to add Unicode characters covered by @face to.
+ *
+ * Since: 1.9.0
+ */
+void
+hb_face_collect_unicodes (hb_face_t *face,
+			  hb_set_t  *out)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
+  hb_ot_face_data (face)->cmap->collect_unicodes (out);
+}
+
+/**
+ * hb_face_collect_variation_selectors:
+ * @face: font face.
+ * @out: set to add Variation Selector characters covered by @face to.
+ *
+ *
+ *
+ * Since: 1.9.0
+ */
+void
+hb_face_collect_variation_selectors (hb_face_t *face,
+				     hb_set_t  *out)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
+  hb_ot_face_data (face)->cmap->collect_variation_selectors (out);
+}
+
+/**
+ * hb_face_collect_variation_unicodes:
+ * @face: font face.
+ * @out: set to add Unicode characters for @variation_selector covered by @face to.
+ *
+ *
+ *
+ * Since: 1.9.0
+ */
+void
+hb_face_collect_variation_unicodes (hb_face_t *face,
+				    hb_codepoint_t variation_selector,
+				    hb_set_t  *out)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
+  hb_ot_face_data (face)->cmap->collect_variation_unicodes (variation_selector, out);
+}
+
+
+
+/*
+ * face-builder: A face that has add_table().
+ */
+
+struct hb_face_builder_data_t
+{
+  struct table_entry_t
+  {
+    inline int cmp (const hb_tag_t *t) const
+    {
+      if (*t < tag) return -1;
+      if (*t > tag) return -1;
+      return 0;
+    }
+
+    hb_tag_t   tag;
+    hb_blob_t *blob;
+  };
+
+  hb_vector_t<table_entry_t, 32> tables;
+};
+
+static hb_face_builder_data_t *
+_hb_face_builder_data_create (void)
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) calloc (1, sizeof (hb_face_builder_data_t));
+  if (unlikely (!data))
+    return nullptr;
+
+  data->tables.init ();
+
+  return data;
+}
+
+static void
+_hb_face_builder_data_destroy (void *user_data)
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
+
+  for (unsigned int i = 0; i < data->tables.len; i++)
+    hb_blob_destroy (data->tables[i].blob);
+
+  data->tables.fini ();
+
+  free (data);
+}
+
+static hb_blob_t *
+_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
+{
+
+  unsigned int table_count = data->tables.len;
+  unsigned int face_length = table_count * 16 + 12;
+
+  for (unsigned int i = 0; i < table_count; i++)
+    face_length += hb_ceil_to_4 (hb_blob_get_length (data->tables[i].blob));
+
+  char *buf = (char *) malloc (face_length);
+  if (unlikely (!buf))
+    return nullptr;
+
+  hb_serialize_context_t c (buf, face_length);
+  OT::OpenTypeFontFile *f = c.start_serialize<OT::OpenTypeFontFile> ();
+
+  bool is_cff = data->tables.lsearch (HB_TAG ('C','F','F',' ')) || data->tables.lsearch (HB_TAG ('C','F','F','2'));
+  hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
+
+  Supplier<hb_tag_t>    tags_supplier  (&data->tables[0].tag, table_count, sizeof (data->tables[0]));
+  Supplier<hb_blob_t *> blobs_supplier (&data->tables[0].blob, table_count, sizeof (data->tables[0]));
+  bool ret = f->serialize_single (&c,
+				  sfnt_tag,
+				  tags_supplier,
+				  blobs_supplier,
+				  table_count);
+
+  c.end_serialize ();
+
+  if (unlikely (!ret))
+  {
+    free (buf);
+    return nullptr;
+  }
+
+  return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, free);
+}
+
+static hb_blob_t *
+_hb_face_builder_reference_table (hb_face_t *face, hb_tag_t tag, void *user_data)
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
+
+  if (!tag)
+    return _hb_face_builder_data_reference_blob (data);
+
+  hb_face_builder_data_t::table_entry_t *entry = data->tables.lsearch (tag);
+  if (entry)
+    return hb_blob_reference (entry->blob);
+
+  return nullptr;
+}
+
+
+/**
+ * hb_face_builder_create:
+ *
+ * Creates a #hb_face_t that can be used with hb_face_builder_add_table().
+ * After tables are added to the face, it can be compiled to a binary
+ * font file by calling hb_face_reference_blob().
+ *
+ * Return value: (transfer full): New face.
+ *
+ * Since: 1.9.0
+ **/
+hb_face_t *
+hb_face_builder_create (void)
+{
+  hb_face_builder_data_t *data = _hb_face_builder_data_create ();
+  if (unlikely (!data)) return hb_face_get_empty ();
+
+  return hb_face_create_for_tables (_hb_face_builder_reference_table,
+				    data,
+				    _hb_face_builder_data_destroy);
+}
+
+/**
+ * hb_face_builder_add_table:
+ *
+ * Add table for @tag with data provided by @blob to the face.  @face must
+ * be created using hb_face_builder_create().
+ *
+ * Since: 1.9.0
+ **/
+hb_bool_t
+hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
+{
+  if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy))
+    return false;
+
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
+  hb_face_builder_data_t::table_entry_t *entry = data->tables.push ();
+
+  entry->tag = tag;
+  entry->blob = hb_blob_reference (blob);
+
+  return true;
+}
diff --git a/src/hb-face.h b/src/hb-face.h
index 208092e..e8ff090 100644
--- a/src/hb-face.h
+++ b/src/hb-face.h
@@ -33,6 +33,7 @@
 
 #include "hb-common.h"
 #include "hb-blob.h"
+#include "hb-set.h"
 
 HB_BEGIN_DECLS
 
@@ -120,6 +121,38 @@
 			unsigned int *table_count, /* IN/OUT */
 			hb_tag_t     *table_tags /* OUT */);
 
+
+/*
+ * Character set.
+ */
+
+HB_EXTERN void
+hb_face_collect_unicodes (hb_face_t *face,
+			  hb_set_t  *out);
+
+HB_EXTERN void
+hb_face_collect_variation_selectors (hb_face_t *face,
+				     hb_set_t  *out);
+
+HB_EXTERN void
+hb_face_collect_variation_unicodes (hb_face_t *face,
+				    hb_codepoint_t variation_selector,
+				    hb_set_t  *out);
+
+
+/*
+ * Builder face.
+ */
+
+HB_EXTERN hb_face_t *
+hb_face_builder_create (void);
+
+HB_EXTERN hb_bool_t
+hb_face_builder_add_table (hb_face_t *face,
+			   hb_tag_t   tag,
+			   hb_blob_t *blob);
+
+
 HB_END_DECLS
 
 #endif /* HB_FACE_H */
diff --git a/src/hb-face-private.hh b/src/hb-face.hh
similarity index 93%
rename from src/hb-face-private.hh
rename to src/hb-face.hh
index 086ce6e..f90453d 100644
--- a/src/hb-face-private.hh
+++ b/src/hb-face.hh
@@ -26,13 +26,13 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_FACE_PRIVATE_HH
-#define HB_FACE_PRIVATE_HH
+#ifndef HB_FACE_HH
+#define HB_FACE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-shaper-private.hh"
-#include "hb-shape-plan-private.hh"
+#include "hb-shaper.hh"
+#include "hb-shape-plan.hh"
 
 
 /*
@@ -105,4 +105,4 @@
 #undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 
 
-#endif /* HB_FACE_PRIVATE_HH */
+#endif /* HB_FACE_HH */
diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc
index eff20f7..dc8536c 100644
--- a/src/hb-fallback-shape.cc
+++ b/src/hb-fallback-shape.cc
@@ -25,7 +25,7 @@
  */
 
 #define HB_SHAPER fallback
-#include "hb-shaper-impl-private.hh"
+#include "hb-shaper-impl.hh"
 
 
 HB_SHAPER_DATA_ENSURE_DEFINE(fallback, face)
diff --git a/src/hb-font.cc b/src/hb-font.cc
index 857b8f5..b6b668d 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -26,10 +26,12 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-font-private.hh"
-#include "hb-machinery-private.hh"
+#include "hb-font.hh"
+#include "hb-machinery.hh"
+
+#include "hb-ot.h"
 
 
 /*
@@ -101,9 +103,42 @@
 				   hb_codepoint_t *glyph,
 				   void *user_data HB_UNUSED)
 {
+  if (font->has_nominal_glyphs_func_set ())
+  {
+    return font->get_nominal_glyphs (1, &unicode, 0, glyph, 0);
+  }
   return font->parent->get_nominal_glyph (unicode, glyph);
 }
 
+#define hb_font_get_nominal_glyphs_nil hb_font_get_nominal_glyphs_default
+static unsigned int
+hb_font_get_nominal_glyphs_default (hb_font_t *font,
+				    void *font_data HB_UNUSED,
+				    unsigned int count,
+				    const hb_codepoint_t *first_unicode,
+				    unsigned int unicode_stride,
+				    hb_codepoint_t *first_glyph,
+				    unsigned int glyph_stride,
+				    void *user_data HB_UNUSED)
+{
+  if (font->has_nominal_glyph_func_set ())
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (!font->get_nominal_glyph (*first_unicode, first_glyph))
+        return i;
+
+      first_unicode = &StructAtOffset<hb_codepoint_t> (first_unicode, unicode_stride);
+      first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+    }
+    return count;
+  }
+
+  return font->parent->get_nominal_glyphs (count,
+					   first_unicode, unicode_stride,
+					   first_glyph, glyph_stride);
+}
+
 static hb_bool_t
 hb_font_get_variation_glyph_nil (hb_font_t *font HB_UNUSED,
 				 void *font_data HB_UNUSED,
@@ -141,6 +176,12 @@
 				     hb_codepoint_t glyph,
 				     void *user_data HB_UNUSED)
 {
+  if (font->has_glyph_h_advances_func_set ())
+  {
+    hb_position_t ret;
+    font->get_glyph_h_advances (1, &glyph, 0, &ret, 0);
+    return ret;
+  }
   return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph));
 }
 
@@ -159,6 +200,12 @@
 				     hb_codepoint_t glyph,
 				     void *user_data HB_UNUSED)
 {
+  if (font->has_glyph_v_advances_func_set ())
+  {
+    hb_position_t ret;
+    font->get_glyph_v_advances (1, &glyph, 0, &ret, 0);
+    return ret;
+  }
   return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph));
 }
 
@@ -167,13 +214,13 @@
 hb_font_get_glyph_h_advances_default (hb_font_t* font,
 				      void* font_data HB_UNUSED,
 				      unsigned int count,
-				      hb_codepoint_t *first_glyph,
+				      const hb_codepoint_t *first_glyph,
 				      unsigned int glyph_stride,
 				      hb_position_t *first_advance,
 				      unsigned int advance_stride,
 				      void *user_data HB_UNUSED)
 {
-  if (font->has_glyph_h_advance_func ())
+  if (font->has_glyph_h_advance_func_set ())
   {
     for (unsigned int i = 0; i < count; i++)
     {
@@ -199,13 +246,13 @@
 hb_font_get_glyph_v_advances_default (hb_font_t* font,
 				      void* font_data HB_UNUSED,
 				      unsigned int count,
-				      hb_codepoint_t *first_glyph,
+				      const hb_codepoint_t *first_glyph,
 				      unsigned int glyph_stride,
 				      hb_position_t *first_advance,
 				      unsigned int advance_stride,
 				      void *user_data HB_UNUSED)
 {
-  if (font->has_glyph_v_advance_func ())
+  if (font->has_glyph_v_advance_func_set ())
   {
     for (unsigned int i = 0; i < count; i++)
     {
@@ -586,6 +633,8 @@
 {
   if (unlikely (hb_object_is_inert (ffuncs)))
     return;
+  if (ffuncs->immutable)
+    return;
 
   ffuncs->immutable = true;
 }
@@ -639,9 +688,15 @@
 #undef HB_FONT_FUNC_IMPLEMENT
 
 bool
+hb_font_t::has_func_set (unsigned int i)
+{
+  return this->klass->get.array[i] != _hb_font_funcs_default.get.array[i];
+}
+
+bool
 hb_font_t::has_func (unsigned int i)
 {
-  return (this->klass->get.array[i] != _hb_font_funcs_default.get.array[i]) ||
+  return has_func_set (i) ||
 	 (parent && parent != &_hb_Null_hb_font_t && parent->has_func (i));
 }
 
@@ -793,8 +848,8 @@
  **/
 void
 hb_font_get_glyph_h_advances (hb_font_t* font,
-			      unsigned count,
-			      hb_codepoint_t *first_glyph,
+			      unsigned int count,
+			      const hb_codepoint_t *first_glyph,
 			      unsigned glyph_stride,
 			      hb_position_t *first_advance,
 			      unsigned advance_stride)
@@ -811,8 +866,8 @@
  **/
 void
 hb_font_get_glyph_v_advances (hb_font_t* font,
-			      unsigned count,
-			      hb_codepoint_t *first_glyph,
+			      unsigned int count,
+			      const hb_codepoint_t *first_glyph,
 			      unsigned glyph_stride,
 			      hb_position_t *first_advance,
 			      unsigned advance_stride)
@@ -873,6 +928,7 @@
  * Return value: 
  *
  * Since: 0.9.2
+ * Deprecated: 2.0.0
  **/
 hb_position_t
 hb_font_get_glyph_h_kerning (hb_font_t *font,
@@ -892,6 +948,7 @@
  * Return value: 
  *
  * Since: 0.9.2
+ * Deprecated: 2.0.0
  **/
 hb_position_t
 hb_font_get_glyph_v_kerning (hb_font_t *font,
@@ -991,7 +1048,7 @@
  * hb_font_get_extents_for_direction:
  * @font: a font.
  * @direction:
- * @extents:
+ * @extents: (out):
  *
  *
  *
@@ -1036,8 +1093,8 @@
 HB_EXTERN void
 hb_font_get_glyph_advances_for_direction (hb_font_t* font,
 					  hb_direction_t direction,
-					  unsigned count,
-					  hb_codepoint_t *first_glyph,
+					  unsigned int count,
+					  const hb_codepoint_t *first_glyph,
 					  unsigned glyph_stride,
 					  hb_position_t *first_advance,
 					  unsigned advance_stride)
@@ -1120,6 +1177,7 @@
  * 
  *
  * Since: 0.9.2
+ * Deprecated: 2.0.0
  **/
 void
 hb_font_get_glyph_kerning_for_direction (hb_font_t *font,
@@ -1254,18 +1312,8 @@
 };
 
 
-/**
- * hb_font_create: (Xconstructor)
- * @face: a face.
- *
- * 
- *
- * Return value: (transfer full): 
- *
- * Since: 0.9.2
- **/
-hb_font_t *
-hb_font_create (hb_face_t *face)
+static hb_font_t *
+_hb_font_create (hb_face_t *face)
 {
   hb_font_t *font;
 
@@ -1285,6 +1333,27 @@
 }
 
 /**
+ * hb_font_create: (Xconstructor)
+ * @face: a face.
+ *
+ * 
+ *
+ * Return value: (transfer full): 
+ *
+ * Since: 0.9.2
+ **/
+hb_font_t *
+hb_font_create (hb_face_t *face)
+{
+  hb_font_t *font = _hb_font_create (face);
+
+  /* Install our in-house, very lightweight, funcs. */
+  hb_ot_font_set_funcs (font);
+
+  return font;
+}
+
+/**
  * hb_font_create_sub_font:
  * @parent: parent font.
  *
@@ -1300,7 +1369,7 @@
   if (unlikely (!parent))
     parent = hb_font_get_empty ();
 
-  hb_font_t *font = hb_font_create (parent->face);
+  hb_font_t *font = _hb_font_create (parent->face);
 
   if (unlikely (hb_object_is_inert (font)))
     return font;
@@ -1444,6 +1513,8 @@
 {
   if (unlikely (hb_object_is_inert (font)))
     return;
+  if (font->immutable)
+    return;
 
   if (font->parent)
     hb_font_make_immutable (font->parent);
@@ -1703,9 +1774,11 @@
 /**
  * hb_font_set_ptem:
  * @font: a font.
- * @ptem: 
+ * @ptem: font size in points.
  *
- * Sets "point size" of the font.
+ * Sets "point size" of the font.  Set to 0 to unset.
+ *
+ * There are 72 points in an inch.
  *
  * Since: 1.6.0
  **/
@@ -1843,8 +1916,6 @@
 }
 
 
-#ifndef HB_DISABLE_DEPRECATED
-
 /*
  * Deprecated get_glyph_func():
  */
@@ -1931,9 +2002,9 @@
 /**
  * hb_font_funcs_set_glyph_func:
  * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @func: (closure user_data) (destroy destroy) (scope notified): callback function.
+ * @user_data: data to pass to @func.
+ * @destroy: function to call when @user_data is not needed anymore.
  *
  * Deprecated.  Use hb_font_funcs_set_nominal_glyph_func() and
  * hb_font_funcs_set_variation_glyph_func() instead.
@@ -1967,5 +2038,3 @@
 					  trampoline,
 					  trampoline_destroy);
 }
-
-#endif /* HB_DISABLE_DEPRECATED */
diff --git a/src/hb-font.h b/src/hb-font.h
index 6cd4869..74c61ab 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -125,6 +125,14 @@
 							 hb_codepoint_t *glyph,
 							 void *user_data);
 
+typedef unsigned int (*hb_font_get_nominal_glyphs_func_t) (hb_font_t *font, void *font_data,
+							   unsigned int count,
+							   const hb_codepoint_t *first_unicode,
+							   unsigned int unicode_stride,
+							   hb_codepoint_t *first_glyph,
+							   unsigned int glyph_stride,
+							   void *user_data);
+
 
 typedef hb_position_t (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, void *font_data,
 							   hb_codepoint_t glyph,
@@ -133,8 +141,8 @@
 typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t;
 
 typedef void (*hb_font_get_glyph_advances_func_t) (hb_font_t* font, void* font_data,
-						   unsigned count,
-						   hb_codepoint_t *first_glyph,
+						   unsigned int count,
+						   const hb_codepoint_t *first_glyph,
 						   unsigned glyph_stride,
 						   hb_position_t *first_advance,
 						   unsigned advance_stride,
@@ -149,12 +157,6 @@
 typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t;
 typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t;
 
-typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data,
-							   hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
-							   void *user_data);
-typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t;
-typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t;
-
 
 typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data,
 						       hb_codepoint_t glyph,
@@ -227,6 +229,22 @@
 				      void *user_data, hb_destroy_func_t destroy);
 
 /**
+ * hb_font_funcs_set_nominal_glyphs_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Since: 2.0.0
+ **/
+HB_EXTERN void
+hb_font_funcs_set_nominal_glyphs_func (hb_font_funcs_t *ffuncs,
+				       hb_font_get_nominal_glyphs_func_t func,
+				       void *user_data, hb_destroy_func_t destroy);
+
+/**
  * hb_font_funcs_set_variation_glyph_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
@@ -339,38 +357,6 @@
 				       void *user_data, hb_destroy_func_t destroy);
 
 /**
- * hb_font_funcs_set_glyph_h_kerning_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
- *
- * 
- *
- * Since: 0.9.2
- **/
-HB_EXTERN void
-hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs,
-					hb_font_get_glyph_h_kerning_func_t func,
-					void *user_data, hb_destroy_func_t destroy);
-
-/**
- * hb_font_funcs_set_glyph_v_kerning_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
- *
- * 
- *
- * Since: 0.9.2
- **/
-HB_EXTERN void
-hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs,
-					hb_font_get_glyph_v_kerning_func_t func,
-					void *user_data, hb_destroy_func_t destroy);
-
-/**
  * hb_font_funcs_set_glyph_extents_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
@@ -461,15 +447,15 @@
 
 HB_EXTERN void
 hb_font_get_glyph_h_advances (hb_font_t* font,
-			      unsigned count,
-			      hb_codepoint_t *first_glyph,
+			      unsigned int count,
+			      const hb_codepoint_t *first_glyph,
 			      unsigned glyph_stride,
 			      hb_position_t *first_advance,
 			      unsigned advance_stride);
 HB_EXTERN void
 hb_font_get_glyph_v_advances (hb_font_t* font,
-			      unsigned count,
-			      hb_codepoint_t *first_glyph,
+			      unsigned int count,
+			      const hb_codepoint_t *first_glyph,
 			      unsigned glyph_stride,
 			      hb_position_t *first_advance,
 			      unsigned advance_stride);
@@ -483,13 +469,6 @@
 			    hb_codepoint_t glyph,
 			    hb_position_t *x, hb_position_t *y);
 
-HB_EXTERN hb_position_t
-hb_font_get_glyph_h_kerning (hb_font_t *font,
-			     hb_codepoint_t left_glyph, hb_codepoint_t right_glyph);
-HB_EXTERN hb_position_t
-hb_font_get_glyph_v_kerning (hb_font_t *font,
-			     hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph);
-
 HB_EXTERN hb_bool_t
 hb_font_get_glyph_extents (hb_font_t *font,
 			   hb_codepoint_t glyph,
@@ -531,8 +510,8 @@
 HB_EXTERN void
 hb_font_get_glyph_advances_for_direction (hb_font_t* font,
 					  hb_direction_t direction,
-					  unsigned count,
-					  hb_codepoint_t *first_glyph,
+					  unsigned int count,
+					  const hb_codepoint_t *first_glyph,
 					  unsigned glyph_stride,
 					  hb_position_t *first_advance,
 					  unsigned advance_stride);
@@ -552,12 +531,6 @@
 					     hb_direction_t direction,
 					     hb_position_t *x, hb_position_t *y);
 
-HB_EXTERN void
-hb_font_get_glyph_kerning_for_direction (hb_font_t *font,
-					 hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
-					 hb_direction_t direction,
-					 hb_position_t *x, hb_position_t *y);
-
 HB_EXTERN hb_bool_t
 hb_font_get_glyph_extents_for_origin (hb_font_t *font,
 				      hb_codepoint_t glyph,
diff --git a/src/hb-font-private.hh b/src/hb-font.hh
similarity index 94%
rename from src/hb-font-private.hh
rename to src/hb-font.hh
index d3a4138..2df5e42 100644
--- a/src/hb-font-private.hh
+++ b/src/hb-font.hh
@@ -26,13 +26,13 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_FONT_PRIVATE_HH
-#define HB_FONT_PRIVATE_HH
+#ifndef HB_FONT_HH
+#define HB_FONT_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-face-private.hh"
-#include "hb-shaper-private.hh"
+#include "hb-face.hh"
+#include "hb-shaper.hh"
 
 
 /*
@@ -43,6 +43,7 @@
   HB_FONT_FUNC_IMPLEMENT (font_h_extents) \
   HB_FONT_FUNC_IMPLEMENT (font_v_extents) \
   HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \
+  HB_FONT_FUNC_IMPLEMENT (nominal_glyphs) \
   HB_FONT_FUNC_IMPLEMENT (variation_glyph) \
   HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \
   HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \
@@ -170,6 +171,7 @@
   /* Public getters */
 
   HB_INTERNAL bool has_func (unsigned int i);
+  HB_INTERNAL bool has_func_set (unsigned int i);
 
   /* has_* ... */
 #define HB_FONT_FUNC_IMPLEMENT(name) \
@@ -179,6 +181,13 @@
     hb_font_funcs_t *funcs = this->klass; \
     unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \
     return has_func (i); \
+  } \
+  bool \
+  has_##name##_func_set (void) \
+  { \
+    hb_font_funcs_t *funcs = this->klass; \
+    unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \
+    return has_func_set (i); \
   }
   HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
@@ -212,6 +221,18 @@
 				       unicode, glyph,
 				       klass->user_data.nominal_glyph);
   }
+  inline unsigned int get_nominal_glyphs (unsigned int count,
+					  const hb_codepoint_t *first_unicode,
+					  unsigned int unicode_stride,
+					  hb_codepoint_t *first_glyph,
+					  unsigned int glyph_stride)
+  {
+    return klass->get.f.nominal_glyphs (this, user_data,
+					count,
+					first_unicode, unicode_stride,
+					first_glyph, glyph_stride,
+					klass->user_data.nominal_glyphs);
+  }
 
   inline hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector,
 					hb_codepoint_t *glyph)
@@ -237,7 +258,7 @@
   }
 
   inline void get_glyph_h_advances (unsigned int count,
-				    hb_codepoint_t *first_glyph,
+				    const hb_codepoint_t *first_glyph,
 				    unsigned int glyph_stride,
 				    hb_position_t *first_advance,
 				    unsigned int advance_stride)
@@ -250,7 +271,7 @@
   }
 
   inline void get_glyph_v_advances (unsigned int count,
-				    hb_codepoint_t *first_glyph,
+				    const hb_codepoint_t *first_glyph,
 				    unsigned int glyph_stride,
 				    hb_position_t *first_advance,
 				    unsigned int advance_stride)
@@ -377,8 +398,8 @@
       *y = get_glyph_v_advance (glyph);
   }
   inline void get_glyph_advances_for_direction (hb_direction_t direction,
-						unsigned count,
-						hb_codepoint_t *first_glyph,
+						unsigned int count,
+						const hb_codepoint_t *first_glyph,
 						unsigned glyph_stride,
 						hb_position_t *first_advance,
 						unsigned advance_stride)
@@ -502,8 +523,8 @@
 					       hb_position_t *x, hb_position_t *y)
   {
     if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) {
-      *x = get_glyph_h_kerning (first_glyph, second_glyph);
       *y = 0;
+      *x = get_glyph_h_kerning (first_glyph, second_glyph);
     } else {
       *x = 0;
       *y = get_glyph_v_kerning (first_glyph, second_glyph);
@@ -601,4 +622,4 @@
 #undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 
 
-#endif /* HB_FONT_PRIVATE_HH */
+#endif /* HB_FONT_HH */
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index 01341b6..18fb72a 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -27,12 +27,13 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb-ft.h"
 
-#include "hb-font-private.hh"
-#include "hb-machinery-private.hh"
+#include "hb-font.hh"
+#include "hb-machinery.hh"
+#include "hb-cache.hh"
 
 #include FT_ADVANCES_H
 #include FT_MULTIPLE_MASTERS_H
@@ -44,30 +45,29 @@
  * In general, this file does a fine job of what it's supposed to do.
  * There are, however, things that need more work:
  *
- *   - I remember seeing FT_Get_Advance() without the NO_HINTING flag to be buggy.
- *     Have not investigated.
- *
  *   - FreeType works in 26.6 mode.  Clients can decide to use that mode, and everything
  *     would work fine.  However, we also abuse this API for performing in font-space,
  *     but don't pass the correct flags to FreeType.  We just abuse the no-hinting mode
  *     for that, such that no rounding etc happens.  As such, we don't set ppem, and
  *     pass NO_HINTING as load_flags.  Would be much better to use NO_SCALE, and scale
- *     ourselves, like we do in uniscribe, etc.
+ *     ourselves.
  *
  *   - We don't handle / allow for emboldening / obliqueing.
  *
  *   - In the future, we should add constructors to create fonts in font space?
- *
- *   - FT_Load_Glyph() is extremely costly.  Do something about it?
  */
 
 
 struct hb_ft_font_t
 {
+  mutable hb_mutex_t lock;
   FT_Face ft_face;
   int load_flags;
   bool symbol; /* Whether selected cmap is symbol cmap. */
   bool unref; /* Whether to destroy ft_face when done. */
+
+  mutable hb_atomic_int_t cached_x_scale;
+  mutable hb_advance_cache_t advance_cache;
 };
 
 static hb_ft_font_t *
@@ -78,12 +78,16 @@
   if (unlikely (!ft_font))
     return nullptr;
 
+  ft_font->lock.init ();
   ft_font->ft_face = ft_face;
   ft_font->symbol = symbol;
   ft_font->unref = unref;
 
   ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
 
+  ft_font->cached_x_scale.set (0);
+  ft_font->advance_cache.init ();
+
   return ft_font;
 }
 
@@ -98,9 +102,13 @@
 {
   hb_ft_font_t *ft_font = (hb_ft_font_t *) data;
 
+  ft_font->advance_cache.fini ();
+
   if (ft_font->unref)
     _hb_ft_face_destroy (ft_font->ft_face);
 
+  ft_font->lock.fini ();
+
   free (ft_font);
 }
 
@@ -168,6 +176,7 @@
 			 void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode);
 
   if (unlikely (!g))
@@ -191,6 +200,32 @@
   return true;
 }
 
+static unsigned int
+hb_ft_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
+			  void *font_data,
+			  unsigned int count,
+			  const hb_codepoint_t *first_unicode,
+			  unsigned int unicode_stride,
+			  hb_codepoint_t *first_glyph,
+			  unsigned int glyph_stride,
+			  void *user_data HB_UNUSED)
+{
+  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
+  unsigned int done;
+  for (done = 0;
+       done < count && (*first_glyph = FT_Get_Char_Index (ft_font->ft_face, *first_unicode));
+       done++)
+  {
+    first_unicode = &StructAtOffset<hb_codepoint_t> (first_unicode, unicode_stride);
+    first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+  }
+  /* We don't need to do ft_font->symbol dance here, since HB calls the singular
+   * nominal_glyph() for what we don't handle here. */
+  return done;
+}
+
+
 static hb_bool_t
 hb_ft_get_variation_glyph (hb_font_t *font HB_UNUSED,
 			   void *font_data,
@@ -200,6 +235,7 @@
 			   void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   unsigned int g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector);
 
   if (unlikely (!g))
@@ -209,22 +245,45 @@
   return true;
 }
 
-static hb_position_t
-hb_ft_get_glyph_h_advance (hb_font_t *font,
-			   void *font_data,
-			   hb_codepoint_t glyph,
-			   void *user_data HB_UNUSED)
+static void
+hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data,
+			    unsigned count,
+			    const hb_codepoint_t *first_glyph,
+			    unsigned glyph_stride,
+			    hb_position_t *first_advance,
+			    unsigned advance_stride,
+			    void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
-  FT_Fixed v;
+  hb_lock_t lock (ft_font->lock);
+  FT_Face ft_face = ft_font->ft_face;
+  int load_flags = ft_font->load_flags;
+  int mult = font->x_scale < 0 ? -1 : +1;
 
-  if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags, &v)))
-    return 0;
+  if (font->x_scale != ft_font->cached_x_scale.get ())
+  {
+    ft_font->advance_cache.clear ();
+    ft_font->cached_x_scale.set (font->x_scale);
+  }
 
-  if (font->x_scale < 0)
-    v = -v;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    FT_Fixed v = 0;
+    hb_codepoint_t glyph = *first_glyph;
 
-  return (v + (1<<9)) >> 10;
+    unsigned int cv;
+    if (ft_font->advance_cache.get (glyph, &cv))
+      v = cv;
+    else
+    {
+      FT_Get_Advance (ft_face, glyph, load_flags, &v);
+      ft_font->advance_cache.set (glyph, v);
+    }
+
+    *first_advance = (v * mult + (1<<9)) >> 10;
+    first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+    first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+  }
 }
 
 static hb_position_t
@@ -234,6 +293,7 @@
 			   void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Fixed v;
 
   if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v)))
@@ -256,6 +316,7 @@
 			  void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
@@ -274,23 +335,6 @@
   return true;
 }
 
-static hb_position_t
-hb_ft_get_glyph_h_kerning (hb_font_t *font,
-			   void *font_data,
-			   hb_codepoint_t left_glyph,
-			   hb_codepoint_t right_glyph,
-			   void *user_data HB_UNUSED)
-{
-  const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
-  FT_Vector kerningv;
-
-  FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED;
-  if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv))
-    return 0;
-
-  return kerningv.x;
-}
-
 static hb_bool_t
 hb_ft_get_glyph_extents (hb_font_t *font,
 			 void *font_data,
@@ -299,6 +343,7 @@
 			 void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
@@ -331,6 +376,7 @@
 			       void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
 
   if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
@@ -356,8 +402,10 @@
 		      void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
+  FT_Face ft_face = ft_font->ft_face;
 
-  hb_bool_t ret = !FT_Get_Glyph_Name (ft_font->ft_face, glyph, name, size);
+  hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size);
   if (ret && (size && !*name))
     ret = false;
 
@@ -372,6 +420,7 @@
 			   void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
 
   if (len < 0)
@@ -404,6 +453,7 @@
 			  void *user_data HB_UNUSED)
 {
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data;
+  hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
   metrics->ascender = ft_face->size->metrics.ascender;
   metrics->descender = ft_face->size->metrics.descender;
@@ -417,7 +467,9 @@
   return true;
 }
 
+#ifdef HB_USE_ATEXIT
 static void free_static_ft_funcs (void);
+#endif
 
 static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ft_font_funcs_lazy_loader_t>
 {
@@ -428,13 +480,12 @@
     hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr);
     //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, nullptr, nullptr);
     hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, nullptr, nullptr);
+    hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ft_get_nominal_glyphs, nullptr, nullptr);
     hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, nullptr, nullptr);
-    hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ft_get_glyph_h_advance, nullptr, nullptr);
+    hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ft_get_glyph_h_advances, nullptr, nullptr);
     hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, nullptr, nullptr);
     //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, nullptr, nullptr);
     hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, nullptr, nullptr);
-    hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, nullptr, nullptr);
-    //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ft_get_glyph_v_kerning, nullptr, nullptr);
     hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, nullptr, nullptr);
     hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, nullptr, nullptr);
     hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, nullptr, nullptr);
@@ -682,8 +733,9 @@
   return hb_ft_font_create (ft_face, _hb_ft_face_destroy);
 }
 
-
+#ifdef HB_USE_ATEXIT
 static void free_static_ft_library (void);
+#endif
 
 static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_ptr_t<FT_Library>::value,
 							     hb_ft_library_lazy_loader_t>
diff --git a/src/hb-glib.cc b/src/hb-glib.cc
index 809b22f..a34acbb 100644
--- a/src/hb-glib.cc
+++ b/src/hb-glib.cc
@@ -26,12 +26,11 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb-glib.h"
 
-#include "hb-unicode-private.hh"
-#include "hb-machinery-private.hh"
+#include "hb-machinery.hh"
 
 
 #if !GLIB_CHECK_VERSION(2,29,14)
@@ -202,14 +201,6 @@
   return (hb_unicode_combining_class_t) g_unichar_combining_class (unicode);
 }
 
-static unsigned int
-hb_glib_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs HB_UNUSED,
-				 hb_codepoint_t      unicode,
-				 void               *user_data HB_UNUSED)
-{
-  return g_unichar_iswide (unicode) ? 2 : 1;
-}
-
 static hb_unicode_general_category_t
 hb_glib_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 				  hb_codepoint_t      unicode,
@@ -334,40 +325,10 @@
   return ret;
 }
 
-static unsigned int
-hb_glib_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED,
-					 hb_codepoint_t      u,
-					 hb_codepoint_t     *decomposed,
-					 void               *user_data HB_UNUSED)
-{
-#if GLIB_CHECK_VERSION(2,29,12)
-  return g_unichar_fully_decompose (u, true, decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN);
-#endif
 
-  /* If the user doesn't have GLib >= 2.29.12 we have to perform
-   * a round trip to UTF-8 and the associated memory management dance. */
-  gchar utf8[6];
-  gchar *utf8_decomposed, *c;
-  gsize utf8_len, utf8_decomposed_len, i;
-
-  /* Convert @u to UTF-8 and normalise it in NFKD mode. This performs the compatibility decomposition. */
-  utf8_len = g_unichar_to_utf8 (u, utf8);
-  utf8_decomposed = g_utf8_normalize (utf8, utf8_len, G_NORMALIZE_NFKD);
-  utf8_decomposed_len = g_utf8_strlen (utf8_decomposed, -1);
-
-  assert (utf8_decomposed_len <= HB_UNICODE_MAX_DECOMPOSITION_LEN);
-
-  for (i = 0, c = utf8_decomposed; i < utf8_decomposed_len; i++, c = g_utf8_next_char (c))
-    *decomposed++ = g_utf8_get_char (c);
-
-  g_free (utf8_decomposed);
-
-  return utf8_decomposed_len;
-}
-
-
-
+#ifdef HB_USE_ATEXIT
 static void free_static_glib_funcs (void);
+#endif
 
 static struct hb_glib_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_glib_unicode_funcs_lazy_loader_t>
 {
@@ -375,10 +336,12 @@
   {
     hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr);
 
-#define HB_UNICODE_FUNC_IMPLEMENT(name) \
-    hb_unicode_funcs_set_##name##_func (funcs, hb_glib_unicode_##name, nullptr, nullptr);
-      HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_UNICODE_FUNC_IMPLEMENT
+    hb_unicode_funcs_set_combining_class_func (funcs, hb_glib_unicode_combining_class, nullptr, nullptr);
+    hb_unicode_funcs_set_general_category_func (funcs, hb_glib_unicode_general_category, nullptr, nullptr);
+    hb_unicode_funcs_set_mirroring_func (funcs, hb_glib_unicode_mirroring, nullptr, nullptr);
+    hb_unicode_funcs_set_script_func (funcs, hb_glib_unicode_script, nullptr, nullptr);
+    hb_unicode_funcs_set_compose_func (funcs, hb_glib_unicode_compose, nullptr, nullptr);
+    hb_unicode_funcs_set_decompose_func (funcs, hb_glib_unicode_decompose, nullptr, nullptr);
 
     hb_unicode_funcs_make_immutable (funcs);
 
diff --git a/src/hb-gobject-enums.cc.tmpl b/src/hb-gobject-enums.cc.tmpl
index ca458a3..e056df4 100644
--- a/src/hb-gobject-enums.cc.tmpl
+++ b/src/hb-gobject-enums.cc.tmpl
@@ -25,7 +25,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 /* g++ didn't like older gtype.h gcc-only code path. */
 #include <glib.h>
diff --git a/src/hb-gobject-structs.cc b/src/hb-gobject-structs.cc
index 0c8e557..1b87585 100644
--- a/src/hb-gobject-structs.cc
+++ b/src/hb-gobject-structs.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 /* g++ didn't like older gtype.h gcc-only code path. */
 #include <glib.h>
diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index 1f42a36..6b2b6f1 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -27,12 +27,14 @@
  */
 
 #define HB_SHAPER graphite2
-#include "hb-shaper-impl-private.hh"
+#include "hb-shaper-impl.hh"
 
 #include "hb-graphite2.h"
 
 #include <graphite2/Segment.h>
 
+#include "hb-ot-tag.h"
+
 
 HB_SHAPER_DATA_ENSURE_DEFINE(graphite2, face)
 HB_SHAPER_DATA_ENSURE_DEFINE(graphite2, font)
@@ -95,6 +97,32 @@
   return d;
 }
 
+static void hb_graphite2_release_table(const void *data, const void *table_buffer)
+{
+  hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data;
+  hb_graphite2_tablelist_t *tlist = face_data->tlist.get();
+
+  hb_graphite2_tablelist_t *prev = nullptr;
+  hb_graphite2_tablelist_t *curr = tlist;
+  while (curr)
+  {
+    if (hb_blob_get_data(curr->blob, nullptr) == table_buffer)
+    {
+      if (prev == nullptr)
+        face_data->tlist.cmpexch(tlist, curr->next);
+      else
+        prev->next = curr->next;
+      hb_blob_destroy(curr->blob);
+      free(curr);
+      break;
+    }
+    prev = curr;
+    curr = curr->next;
+  }
+}
+
+static gr_face_ops hb_graphite2_face_ops = { sizeof(gr_face_ops), hb_graphite2_get_table, hb_graphite2_release_table };
+
 hb_graphite2_face_data_t *
 _hb_graphite2_shaper_face_data_create (hb_face_t *face)
 {
@@ -113,7 +141,7 @@
     return nullptr;
 
   data->face = face;
-  data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll);
+  data->grface = gr_make_face_with_ops (data, &hb_graphite2_face_ops, gr_face_preloadAll);
 
   if (unlikely (!data->grface)) {
     free (data);
@@ -251,11 +279,16 @@
 
   /* TODO ensure_native_direction. */
 
-  hb_tag_t script_tag[2];
-  hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]);
+  hb_tag_t script_tag[HB_OT_MAX_TAGS_PER_SCRIPT];
+  unsigned int count = HB_OT_MAX_TAGS_PER_SCRIPT;
+  hb_ot_tags_from_script_and_language (hb_buffer_get_script (buffer),
+				       HB_LANGUAGE_INVALID,
+				       &count,
+				       script_tag,
+				       nullptr, nullptr);
 
   seg = gr_make_seg (nullptr, grface,
-		     script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1],
+		     count ? script_tag[count - 1] : HB_OT_TAG_DEFAULT_SCRIPT,
 		     feats,
 		     gr_utf32, chars, buffer->len,
 		     2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0));
diff --git a/src/hb-graphite2.h b/src/hb-graphite2.h
index 05c55de..1720191 100644
--- a/src/hb-graphite2.h
+++ b/src/hb-graphite2.h
@@ -41,7 +41,7 @@
 
 #ifndef HB_DISABLE_DEPRECATED
 
-HB_EXTERN gr_font *
+HB_EXTERN HB_DEPRECATED_FOR (hb_graphite2_face_get_gr_face) gr_font *
 hb_graphite2_font_get_gr_font (hb_font_t *font);
 
 #endif
diff --git a/src/hb-icu.cc b/src/hb-icu.cc
index 2e3ad36..e012314 100644
--- a/src/hb-icu.cc
+++ b/src/hb-icu.cc
@@ -27,12 +27,11 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb-icu.h"
 
-#include "hb-unicode-private.hh"
-#include "hb-machinery-private.hh"
+#include "hb-machinery.hh"
 
 #include <unicode/uchar.h>
 #include <unicode/unorm2.h>
@@ -73,25 +72,6 @@
   return (hb_unicode_combining_class_t) u_getCombiningClass (unicode);
 }
 
-static unsigned int
-hb_icu_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs HB_UNUSED,
-				hb_codepoint_t      unicode,
-				void               *user_data HB_UNUSED)
-{
-  switch (u_getIntPropertyValue(unicode, UCHAR_EAST_ASIAN_WIDTH))
-  {
-  case U_EA_WIDE:
-  case U_EA_FULLWIDTH:
-    return 2;
-  case U_EA_NEUTRAL:
-  case U_EA_AMBIGUOUS:
-  case U_EA_HALFWIDTH:
-  case U_EA_NARROW:
-    return 1;
-  }
-  return 1;
-}
-
 static hb_unicode_general_category_t
 hb_icu_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 				 hb_codepoint_t      unicode,
@@ -309,42 +289,10 @@
   return ret;
 }
 
-static unsigned int
-hb_icu_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED,
-					hb_codepoint_t      u,
-					hb_codepoint_t     *decomposed,
-					void               *user_data HB_UNUSED)
-{
-  UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1];
-  unsigned int len;
-  int32_t utf32_len;
-  hb_bool_t err;
-  UErrorCode icu_err;
 
-  /* Copy @u into a UTF-16 array to be passed to ICU. */
-  len = 0;
-  err = false;
-  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), u, err);
-  if (err)
-    return 0;
-
-  /* Normalise the codepoint using NFKD mode. */
-  icu_err = U_ZERO_ERROR;
-  len = unorm2_normalize (unorm2_getNFKDInstance (&icu_err), utf16, len, normalized, ARRAY_LENGTH (normalized), &icu_err);
-  if (U_FAILURE (icu_err))
-    return 0;
-
-  /* Convert the decomposed form from UTF-16 to UTF-32. */
-  icu_err = U_ZERO_ERROR;
-  u_strToUTF32 ((UChar32*) decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN, &utf32_len, normalized, len, &icu_err);
-  if (U_FAILURE (icu_err))
-    return 0;
-
-  return utf32_len;
-}
-
-
+#ifdef HB_USE_ATEXIT
 static void free_static_icu_funcs (void);
+#endif
 
 static struct hb_icu_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_icu_unicode_funcs_lazy_loader_t>
 {
@@ -359,10 +307,12 @@
 
     hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr);
 
-#define HB_UNICODE_FUNC_IMPLEMENT(name) \
-    hb_unicode_funcs_set_##name##_func (funcs, hb_icu_unicode_##name, user_data, nullptr);
-      HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_UNICODE_FUNC_IMPLEMENT
+    hb_unicode_funcs_set_combining_class_func (funcs, hb_icu_unicode_combining_class, nullptr, nullptr);
+    hb_unicode_funcs_set_general_category_func (funcs, hb_icu_unicode_general_category, nullptr, nullptr);
+    hb_unicode_funcs_set_mirroring_func (funcs, hb_icu_unicode_mirroring, nullptr, nullptr);
+    hb_unicode_funcs_set_script_func (funcs, hb_icu_unicode_script, nullptr, nullptr);
+    hb_unicode_funcs_set_compose_func (funcs, hb_icu_unicode_compose, user_data, nullptr);
+    hb_unicode_funcs_set_decompose_func (funcs, hb_icu_unicode_decompose, user_data, nullptr);
 
     hb_unicode_funcs_make_immutable (funcs);
 
diff --git a/src/hb-iter-private.hh b/src/hb-iter.hh
similarity index 96%
rename from src/hb-iter-private.hh
rename to src/hb-iter.hh
index 314133a..6fd6aed 100644
--- a/src/hb-iter-private.hh
+++ b/src/hb-iter.hh
@@ -24,10 +24,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_ITER_PRIVATE_HH
-#define HB_ITER_PRIVATE_HH
+#ifndef HB_ITER_HH
+#define HB_ITER_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 /* Unified iterator object.
@@ -146,4 +146,4 @@
    }
 }
 
-#endif /* HB_ITER_PRIVATE_HH */
+#endif /* HB_ITER_HH */
diff --git a/src/hb-machinery-private.hh b/src/hb-machinery.hh
similarity index 76%
rename from src/hb-machinery-private.hh
rename to src/hb-machinery.hh
index 99ef485..ae34c92 100644
--- a/src/hb-machinery-private.hh
+++ b/src/hb-machinery.hh
@@ -26,13 +26,14 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_MACHINERY_PRIVATE_HH
-#define HB_MACHINERY_PRIVATE_HH
+#ifndef HB_MACHINERY_HH
+#define HB_MACHINERY_HH
 
-#include "hb-private.hh"
-#include "hb-blob-private.hh"
+#include "hb.hh"
+#include "hb-blob.hh"
 
-#include "hb-iter-private.hh"
+#include "hb-iter.hh"
+#include "hb-vector.hh"
 
 
 /*
@@ -98,8 +99,8 @@
 
 #define DEFINE_SIZE_STATIC(size) \
   DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \
-  static const unsigned int static_size = (size); \
-  static const unsigned int min_size = (size); \
+  enum { static_size = (size) }; \
+  enum { min_size = (size) }; \
   inline unsigned int get_size (void) const { return (size); }
 
 #define DEFINE_SIZE_UNION(size, _member) \
@@ -111,9 +112,13 @@
   static const unsigned int min_size = (size)
 
 #define DEFINE_SIZE_ARRAY(size, array) \
-  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \
+  DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + VAR * sizeof (array[0])); \
   DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \
-  static const unsigned int min_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])); \
@@ -128,7 +133,7 @@
 template <typename Context, typename Return, unsigned int MaxDebugDepth>
 struct hb_dispatch_context_t
 {
-  static const unsigned int max_debug_depth = MaxDebugDepth;
+  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; }
@@ -138,6 +143,68 @@
 
 /*
  * Sanitize
+ *
+ *
+ * === Introduction ===
+ *
+ * The sanitize machinery is at the core of our zero-cost font loading.  We
+ * mmap() font file into memory and create a blob out of it.  Font subtables
+ * are returned as a readonly sub-blob of the main font blob.  These table
+ * blobs are then sanitized before use, to ensure invalid memory access does
+ * not happen.  The toplevel sanitize API use is like, eg. to load the 'head'
+ * table:
+ *
+ *   hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<OT::head> (face);
+ *
+ * The blob then can be converted to a head table struct with:
+ *
+ *   const head *head_table = head_blob->as<head> ();
+ *
+ * What the reference_table does is, to call hb_face_reference_table() to load
+ * the table blob, sanitize it and return either the sanitized blob, or empty
+ * blob if sanitization failed.  The blob->as() function returns the null
+ * object of its template type argument if the blob is empty.  Otherwise, it
+ * just casts the blob contents to the desired type.
+ *
+ * Sanitizing a blob of data with a type T works as follows (with minor
+ * simplification):
+ *
+ *   - Cast blob content to T*, call sanitize() method of it,
+ *   - If sanitize succeeded, return blob.
+ *   - Otherwise, if blob is not writable, try making it writable,
+ *     or copy if cannot be made writable in-place,
+ *   - Call sanitize() again.  Return blob if sanitize succeeded.
+ *   - Return empty blob otherwise.
+ *
+ *
+ * === The sanitize() contract ===
+ *
+ * The sanitize() method of each object type shall return true if it's safe to
+ * call other methods of the object, and false otherwise.
+ *
+ * Note that what sanitize() checks for might align with what the specification
+ * describes as valid table data, but does not have to be.  In particular, we
+ * do NOT want to be pedantic and concern ourselves with validity checks that
+ * are irrelevant to our use of the table.  On the contrary, we want to be
+ * lenient with error handling and accept invalid data to the extent that it
+ * does not impose extra burden on us.
+ *
+ * Based on the sanitize contract, one can see that what we check for depends
+ * on how we use the data in other table methods.  Ie. if other table methods
+ * assume that offsets do NOT point out of the table data block, then that's
+ * something sanitize() must check for (GSUB/GPOS/GDEF/etc work this way).  On
+ * the other hand, if other methods do such checks themselves, then sanitize()
+ * does not have to bother with them (glyf/local work this way).  The choice
+ * depends on the table structure and sanitize() performance.  For example, to
+ * check glyf/loca offsets in sanitize() would cost O(num-glyphs).  We try hard
+ * to avoid such costs during font loading.  By postponing such checks to the
+ * actual glyph loading, we reduce the sanitize cost to O(1) and total runtime
+ * cost to O(used-glyphs).  As such, this is preferred.
+ *
+ * The same argument can be made re GSUB/GPOS/GDEF, but there, the table
+ * structure is so complicated that by checking all offsets at sanitize() time,
+ * we make the code much simpler in other methods, as offsets and referenced
+ * objects do not need to be validated at each use site.
  */
 
 /* This limits sanitizing time on really broken fonts. */
@@ -150,6 +217,9 @@
 #ifndef HB_SANITIZE_MAX_OPS_MIN
 #define HB_SANITIZE_MAX_OPS_MIN 16384
 #endif
+#ifndef HB_SANITIZE_MAX_OPS_MAX
+#define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF
+#endif
 
 struct hb_sanitize_context_t :
        hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
@@ -157,7 +227,8 @@
   inline hb_sanitize_context_t (void) :
 	debug_depth (0),
 	start (nullptr), end (nullptr),
-	writable (false), edit_count (0), max_ops (0),
+	max_ops (0),
+	writable (false), edit_count (0),
 	blob (nullptr),
 	num_glyphs (65536),
 	num_glyphs_set (false) {}
@@ -185,6 +256,23 @@
   }
   inline unsigned int get_num_glyphs (void) { return num_glyphs; }
 
+  inline 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)
+  {
+    this->start = (const char *) &obj;
+    this->end = (const char *) &obj + obj.get_size ();
+    assert (this->start <= this->end); /* Must not overflow. */
+  }
+
   inline void start_processing (void)
   {
     this->start = this->blob->data;
@@ -215,10 +303,10 @@
   inline bool check_range (const void *base, unsigned int len) const
   {
     const char *p = (const char *) base;
-    bool ok = this->max_ops-- > 0 &&
-	      this->start <= p &&
+    bool ok = this->start <= p &&
 	      p <= this->end &&
-	      (unsigned int) (this->end - p) >= len;
+	      (unsigned int) (this->end - p) >= len &&
+	      this->max_ops-- > 0;
 
     DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
        "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s",
@@ -229,7 +317,8 @@
     return likely (ok);
   }
 
-  inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
+  template <typename T>
+  inline bool check_array (const T *base, unsigned int len, unsigned int record_size = T::static_size) const
   {
     const char *p = (const char *) base;
     bool overflows = hb_unsigned_mul_overflows (len, record_size);
@@ -355,10 +444,10 @@
 
   mutable unsigned int debug_depth;
   const char *start, *end;
+  mutable int max_ops;
   private:
   bool writable;
   unsigned int edit_count;
-  mutable int max_ops;
   hb_blob_t *blob;
   unsigned int num_glyphs;
   bool  num_glyphs_set;
@@ -375,12 +464,19 @@
   {
     this->start = (char *) start_;
     this->end = this->start + size;
+    reset ();
+  }
 
+  inline void reset (void)
+  {
     this->ran_out_of_room = false;
     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; }
+
+  /* To be called around main operation. */
   template <typename Type>
   inline Type *start_serialize (void)
   {
@@ -391,7 +487,6 @@
 
     return start_embed<Type> ();
   }
-
   inline void end_serialize (void)
   {
     DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1,
@@ -401,15 +496,20 @@
 		     this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room");
   }
 
-  template <typename Type>
-  inline Type *copy (void)
+  inline unsigned int length (void) const { return this->head - this->start; }
+
+  inline void align (unsigned int alignment)
   {
-    assert (!this->ran_out_of_room);
-    unsigned int len = this->head - this->start;
-    void *p = malloc (len);
-    if (p)
-      memcpy (p, this->start, len);
-    return reinterpret_cast<Type *> (p);
+    unsigned int l = length () % alignment;
+    if (l)
+      allocate_size<void> (alignment - l);
+  }
+
+  template <typename Type>
+  inline Type *start_embed (void) const
+  {
+    Type *ret = reinterpret_cast<Type *> (this->head);
+    return ret;
   }
 
   template <typename Type>
@@ -432,19 +532,12 @@
   }
 
   template <typename Type>
-  inline Type *start_embed (void)
-  {
-    Type *ret = reinterpret_cast<Type *> (this->head);
-    return ret;
-  }
-
-  template <typename Type>
   inline Type *embed (const Type &obj)
   {
     unsigned int size = obj.get_size ();
     Type *ret = this->allocate_size<Type> (size);
     if (unlikely (!ret)) return nullptr;
-    memcpy (ret, obj, size);
+    memcpy (ret, &obj, size);
     return ret;
   }
 
@@ -466,6 +559,38 @@
     return reinterpret_cast<Type *> (&obj);
   }
 
+  /* Output routines. */
+  template <typename Type>
+  inline Type *copy (void) const
+  {
+    assert (!this->ran_out_of_room);
+    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
+  {
+    assert (!this->ran_out_of_room);
+    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);
+  }
+  inline hb_blob_t *copy_blob (void) const
+  {
+    assert (!this->ran_out_of_room);
+    return hb_blob_create (this->start,
+			   this->head - this->start,
+			   HB_MEMORY_MODE_DUPLICATE,
+			   nullptr, nullptr);
+  }
+
+  public:
   unsigned int debug_depth;
   char *start, *end, *head;
   bool ran_out_of_room;
@@ -479,12 +604,19 @@
 template <typename Type>
 struct Supplier
 {
-  inline Supplier (const Type *array, unsigned int len_, unsigned int stride_=sizeof(Type))
+  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 ();
@@ -520,6 +652,7 @@
 struct BEInt<Type, 1>
 {
   public:
+  typedef Type type;
   inline void set (Type V)
   {
     v = V;
@@ -534,6 +667,7 @@
 struct BEInt<Type, 2>
 {
   public:
+  typedef Type type;
   inline void set (Type V)
   {
     v[0] = (V >>  8) & 0xFF;
@@ -550,6 +684,7 @@
 struct BEInt<Type, 3>
 {
   public:
+  typedef Type type;
   inline void set (Type V)
   {
     v[0] = (V >> 16) & 0xFF;
@@ -568,6 +703,7 @@
 struct BEInt<Type, 4>
 {
   public:
+  typedef Type type;
   inline void set (Type V)
   {
     v[0] = (V >> 24) & 0xFF;
@@ -682,6 +818,10 @@
     }
     return p;
   }
+  inline Stored * get_stored_relaxed (void) const
+  {
+    return this->instance.get_relaxed ();
+  }
 
   inline void set_stored (Stored *instance_)
   {
@@ -695,6 +835,7 @@
   }
 
   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 ())); }
 
   /* To be possibly overloaded by subclasses. */
@@ -729,9 +870,9 @@
 
 /* Specializations. */
 
-template <unsigned int WheresFace, typename T>
+template <typename T, unsigned int WheresFace>
 struct hb_face_lazy_loader_t : hb_lazy_loader_t<T,
-						hb_face_lazy_loader_t<WheresFace, T>,
+						hb_face_lazy_loader_t<T, WheresFace>,
 						hb_face_t, WheresFace> {};
 
 template <typename T, unsigned int WheresFace>
@@ -789,4 +930,4 @@
 };
 
 
-#endif /* HB_MACHINERY_PRIVATE_HH */
+#endif /* HB_MACHINERY_HH */
diff --git a/src/hb-map.cc b/src/hb-map.cc
index 138a856..225f37b 100644
--- a/src/hb-map.cc
+++ b/src/hb-map.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-map-private.hh"
+#include "hb-map.hh"
 
 
 /* Public API */
diff --git a/src/hb-map-private.hh b/src/hb-map.hh
similarity index 97%
rename from src/hb-map-private.hh
rename to src/hb-map.hh
index 00f089e..b55e3a9 100644
--- a/src/hb-map-private.hh
+++ b/src/hb-map.hh
@@ -24,10 +24,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_MAP_PRIVATE_HH
-#define HB_MAP_PRIVATE_HH
+#ifndef HB_MAP_HH
+#define HB_MAP_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 template <typename T>
@@ -172,7 +172,7 @@
 
   inline bool is_empty (void) const
   {
-    return population != 0;
+    return population == 0;
   }
 
   inline unsigned int get_population () const
@@ -251,4 +251,4 @@
 };
 
 
-#endif /* HB_MAP_PRIVATE_HH */
+#endif /* HB_MAP_HH */
diff --git a/src/hb-mutex-private.hh b/src/hb-mutex.hh
similarity index 94%
rename from src/hb-mutex-private.hh
rename to src/hb-mutex.hh
index 14bde31..75b89ad 100644
--- a/src/hb-mutex-private.hh
+++ b/src/hb-mutex.hh
@@ -29,10 +29,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_MUTEX_PRIVATE_HH
-#define HB_MUTEX_PRIVATE_HH
+#ifndef HB_MUTEX_HH
+#define HB_MUTEX_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 /* mutex */
@@ -137,5 +137,13 @@
   inline void fini (void) { 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 (); }
+  private:
+  hb_mutex_t &mutex;
+};
 
-#endif /* HB_MUTEX_PRIVATE_HH */
+
+#endif /* HB_MUTEX_HH */
diff --git a/src/hb-null.hh b/src/hb-null.hh
index 91efee6..8766226 100644
--- a/src/hb-null.hh
+++ b/src/hb-null.hh
@@ -27,7 +27,7 @@
 #ifndef HB_NULL_HH
 #define HB_NULL_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 /*
@@ -36,7 +36,7 @@
 
 /* Global nul-content Null pool.  Enlarge as necessary. */
 
-#define HB_NULL_POOL_SIZE 264
+#define HB_NULL_POOL_SIZE 1024
 
 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)];
@@ -49,29 +49,29 @@
 }
 #define Null(Type) Null<Type>()
 
-/* Specializaitons for arbitrary-content Null objects expressed in bytes. */
+/* 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]; \
-template <> \
-/*static*/ inline const Namespace::Type& Null<Namespace::Type> (void) { \
-  return *reinterpret_cast<const Namespace::Type *> (_hb_Null_##Namespace##_##Type); \
-} \
-namespace Namespace { \
-static_assert (true, "Just so we take semicolon after.")
+	} /* Close namespace. */ \
+	extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::min_size]; \
+	template <> \
+	/*static*/ inline const Namespace::Type& Null<Namespace::Type> (void) { \
+	  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::min_size]
 
-/* Specializaitons for arbitrary-content Null objects expressed as struct initializer. */
+/* 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) { \
-  return _hb_Null_##Type; \
-} \
+	extern HB_INTERNAL const Type _hb_Null_##Type; \
+	template <> \
+	/*static*/ inline const Type& Null<Type> (void) { \
+	  return _hb_Null_##Type; \
+	} \
 static_assert (true, "Just so we take semicolon after.")
 #define DEFINE_NULL_INSTANCE(Type) \
-const Type _hb_Null_##Type
+	const Type _hb_Null_##Type
 
 /* Global writable pool.  Enlarge as necessary. */
 
diff --git a/src/hb-object-private.hh b/src/hb-object.hh
similarity index 96%
rename from src/hb-object-private.hh
rename to src/hb-object.hh
index 4955a68..ca85af6 100644
--- a/src/hb-object-private.hh
+++ b/src/hb-object.hh
@@ -29,13 +29,13 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OBJECT_PRIVATE_HH
-#define HB_OBJECT_PRIVATE_HH
+#ifndef HB_OBJECT_HH
+#define HB_OBJECT_HH
 
-#include "hb-private.hh"
-#include "hb-atomic-private.hh"
-#include "hb-mutex-private.hh"
-#include "hb-vector-private.hh"
+#include "hb.hh"
+#include "hb-atomic.hh"
+#include "hb-mutex.hh"
+#include "hb-vector.hh"
 
 
 /*
@@ -322,4 +322,4 @@
 }
 
 
-#endif /* HB_OBJECT_PRIVATE_HH */
+#endif /* HB_OBJECT_HH */
diff --git a/src/hb-open-file-private.hh b/src/hb-open-file.hh
similarity index 69%
rename from src/hb-open-file-private.hh
rename to src/hb-open-file.hh
index d47bff6..817791a 100644
--- a/src/hb-open-file-private.hh
+++ b/src/hb-open-file.hh
@@ -26,10 +26,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OPEN_FILE_PRIVATE_HH
-#define HB_OPEN_FILE_PRIVATE_HH
+#ifndef HB_OPEN_FILE_HH
+#define HB_OPEN_FILE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 #include "hb-ot-head-table.hh"
 
 
@@ -115,7 +115,7 @@
      * table list. */
     int i = tables.len < 64 ? tables.lsearch (t) : tables.bsearch (t);
     if (table_index)
-      *table_index = i == -1 ? Index::NOT_FOUND_INDEX : (unsigned int) i;
+      *table_index = i == -1 ? (unsigned) Index::NOT_FOUND_INDEX : (unsigned) i;
     return i != -1;
   }
   inline const TableRecord& get_table_by_tag (hb_tag_t tag) const
@@ -160,9 +160,8 @@
 
       memcpy (start, hb_blob_get_data (blob, nullptr), rec.length);
 
-      /* 4-byte allignment. */
-      if (rec.length % 4)
-	c->allocate_size<void> (4 - rec.length % 4);
+      /* 4-byte alignment. */
+      c->align (4);
       const char *end = (const char *) c->head;
 
       if (tags[i] == HB_OT_TAG_head && end - start >= head::static_size)
@@ -288,188 +287,159 @@
 
 /*
  * Mac Resource Fork
+ *
+ * http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html
  */
 
-struct ResourceRefItem
+struct ResourceRecord
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  inline const OpenTypeFontFace & get_face (const void *data_base) const
+  { return CastR<OpenTypeFontFace> ((data_base+offset).arrayZ); }
+
+  inline bool sanitize (hb_sanitize_context_t *c,
+			const void *data_base) const
   {
     TRACE_SANITIZE (this);
-    // actual data sanitization is done on ResourceForkHeader sanitizer
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this) &&
+		  offset.sanitize (c, data_base) &&
+		  get_face (data_base).sanitize (c));
   }
 
-  HBINT16	id;		/* Resource ID, is really should be signed? */
+  protected:
+  HBUINT16	id;		/* Resource ID. */
   HBINT16	nameOffset;	/* Offset from beginning of resource name list
-				 * to resource name, minus means there is no */
-  HBUINT8	attr;		/* Resource attributes */
-  HBUINT24	dataOffset;	/* Offset from beginning of resource data to
+				 * to resource name, -1 means there is none. */
+  HBUINT8	attrs;		/* Resource attributes */
+  OffsetTo<LArrayOf<HBUINT8>, HBUINT24, false>
+		offset;		/* Offset from beginning of data block to
 				 * data for this resource */
   HBUINT32	reserved;	/* Reserved for handle to resource */
   public:
   DEFINE_SIZE_STATIC (12);
 };
 
-struct ResourceTypeItem
+#define HB_TAG_sfnt HB_TAG ('s','f','n','t')
+
+struct ResourceTypeRecord
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  inline unsigned int get_resource_count (void) const
+  { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; }
+
+  inline bool is_sfnt (void) 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];
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c,
+			const void *type_base,
+			const void *data_base) const
   {
     TRACE_SANITIZE (this);
-    // RefList sanitization is done on ResourceMap sanitizer
-    return_trace (likely (c->check_struct (this)));
-  }
-
-  inline unsigned int get_resource_count () const
-  {
-    return numRes + 1;
-  }
-
-  inline bool is_sfnt () const
-  {
-    return type == HB_TAG ('s','f','n','t');
-  }
-
-  inline const ResourceRefItem& get_ref_item (const void *base,
-					      unsigned int i) const
-  {
-    return (base+refList)[i];
+    return_trace (c->check_struct (this) &&
+		  resourcesZ.sanitize (c, type_base,
+				       get_resource_count (),
+				       data_base));
   }
 
   protected:
-  Tag		type;		/* Resource type */
-  HBUINT16	numRes;		/* Number of resource this type in map minus 1 */
-  OffsetTo<UnsizedArrayOf<ResourceRefItem> >
-		refList;	/* Offset from beginning of resource type list
-				 * to reference list for this type */
+  Tag		tag;		/* Resource type. */
+  HBUINT16	resCountM1;	/* Number of resources minus 1. */
+  OffsetTo<UnsizedArrayOf<ResourceRecord>, HBUINT16, false>
+		resourcesZ;	/* Offset from beginning of resource type list
+				 * to reference item list for this type. */
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
 struct ResourceMap
 {
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  inline unsigned int get_face_count (void) const
   {
-    TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (this)))
-      return_trace (false);
-    for (unsigned int i = 0; i < get_types_count (); ++i)
+    unsigned int count = get_type_count ();
+    for (unsigned int i = 0; i < count; i++)
     {
-      const ResourceTypeItem& type = get_type (i);
-      if (unlikely (!type.sanitize (c)))
-        return_trace (false);
-      for (unsigned int j = 0; j < type.get_resource_count (); ++j)
-	if (unlikely (!get_ref_item (type, j).sanitize (c)))
-	  return_trace (false);
-    }
-    return_trace (true);
-  }
-
-  inline const ResourceTypeItem& get_type (unsigned int i) const
-  {
-    // Why offset from the second byte of the object? I'm not sure
-    return ((&reserved[2])+typeList)[i];
-  }
-
-  inline unsigned int get_types_count () const
-  {
-    return nTypes + 1;
-  }
-
-  inline const ResourceRefItem &get_ref_item (const ResourceTypeItem &type,
-					      unsigned int i) const
-  {
-    return type.get_ref_item (&(this+typeList), i);
-  }
-
-  inline const PString& get_name (const ResourceRefItem &item,
-				  unsigned int i) const
-  {
-    if (item.nameOffset == -1)
-      return Null (PString);
-
-    return StructAtOffset<PString> (this, nameList + item.nameOffset);
-  }
-
-  protected:
-  HBUINT8	reserved[16];	/* Reserved for copy of resource header */
-  LOffsetTo<ResourceMap>
-		reserved1;	/* Reserved for handle to next resource map */
-  HBUINT16	reserved2;	/* Reserved for file reference number */
-  HBUINT16	attr;		/* Resource fork attribute */
-  OffsetTo<UnsizedArrayOf<ResourceTypeItem> >
-		typeList;	/* Offset from beginning of map to
-				 * resource type list */
-  HBUINT16	nameList;	/* Offset from beginning of map to
-				 * resource name list */
-  HBUINT16	nTypes;		/* Number of types in the map minus 1 */
-  public:
-  DEFINE_SIZE_STATIC (30);
-};
-
-struct ResourceForkHeader
-{
-  inline unsigned int get_face_count () const
-  {
-    const ResourceMap &resource_map = this+map;
-    for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
-    {
-      const ResourceTypeItem& type = resource_map.get_type (i);
+      const ResourceTypeRecord& type = get_type_record (i);
       if (type.is_sfnt ())
 	return type.get_resource_count ();
     }
     return 0;
   }
 
-  inline const LArrayOf<HBUINT8>& get_data (const ResourceTypeItem& type,
-					    unsigned int idx) const
+  inline const OpenTypeFontFace& get_face (unsigned int idx,
+					   const void *data_base) const
   {
-    const ResourceMap &resource_map = this+map;
-    unsigned int offset = dataOffset;
-    offset += resource_map.get_ref_item (type, idx).dataOffset;
-    return StructAtOffset<LArrayOf<HBUINT8> > (this, offset);
-  }
-
-  inline const OpenTypeFontFace& get_face (unsigned int idx) const
-  {
-    const ResourceMap &resource_map = this+map;
-    for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
+    unsigned int count = get_type_count ();
+    for (unsigned int i = 0; i < count; i++)
     {
-      const ResourceTypeItem& type = resource_map.get_type (i);
+      const ResourceTypeRecord& type = get_type_record (i);
+      /* The check for idx < count is here because ResourceRecord is NOT null-safe.
+       * Because an offset of 0 there does NOT mean null. */
       if (type.is_sfnt () && idx < type.get_resource_count ())
-	return (OpenTypeFontFace&) get_data (type, idx).arrayZ;
+	return type.get_resource_record (idx, &(this+typeList)).get_face (data_base);
     }
     return Null (OpenTypeFontFace);
   }
 
+  inline bool sanitize (hb_sanitize_context_t *c, const void *data_base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  typeList.sanitize (c, this,
+				     &(this+typeList),
+				     data_base));
+  }
+
+  private:
+  inline unsigned int get_type_count (void) const { return (this+typeList).lenM1 + 1; }
+
+  inline const ResourceTypeRecord& get_type_record (unsigned int i) const
+  { return (this+typeList)[i]; }
+
+  protected:
+  HBUINT8	reserved0[16];	/* Reserved for copy of resource header */
+  HBUINT32	reserved1;	/* Reserved for handle to next resource map */
+  HBUINT16	resreved2;	/* Reserved for file reference number */
+  HBUINT16	attrs;		/* Resource fork attribute */
+  OffsetTo<ArrayOfM1<ResourceTypeRecord>, HBUINT16, false>
+		typeList;	/* Offset from beginning of map to
+				 * resource type list */
+  Offset16	nameList;	/* Offset from beginning of map to
+				 * resource name list */
+  public:
+  DEFINE_SIZE_STATIC (28);
+};
+
+struct ResourceForkHeader
+{
+  inline unsigned int get_face_count (void) const
+  { return (this+map).get_face_count (); }
+
+  inline 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)
+      *base_offset = (const char *) &face - (const char *) this;
+    return face;
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (this)))
-      return_trace (false);
-
-    const ResourceMap &resource_map = this+map;
-    if (unlikely (!resource_map.sanitize (c)))
-      return_trace (false);
-
-    for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
-    {
-      const ResourceTypeItem& type = resource_map.get_type (i);
-      for (unsigned int j = 0; j < type.get_resource_count (); ++j)
-      {
-        const LArrayOf<HBUINT8>& data = get_data (type, j);
-	if (unlikely (!(data.sanitize (c) &&
-			((OpenTypeFontFace&) data.arrayZ).sanitize (c))))
-	  return_trace (false);
-      }
-    }
-
-    return_trace (true);
+    return_trace (c->check_struct (this) &&
+		  data.sanitize (c, this, dataLen) &&
+		  map.sanitize (c, this, &(this+data)));
   }
 
   protected:
-  HBUINT32	dataOffset;	/* Offset from beginning of resource fork
+  LOffsetTo<UnsizedArrayOf<HBUINT8>, false>
+		data;		/* Offset from beginning of resource fork
 				 * to resource data */
-  LOffsetTo<ResourceMap>
+  LOffsetTo<ResourceMap, false>
 		map;		/* Offset from beginning of resource fork
 				 * to resource map */
   HBUINT32	dataLen;	/* Length of resource data */
@@ -486,7 +456,7 @@
 {
   enum {
     CFFTag		= HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */
-    TrueTypeTag	= HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */
+    TrueTypeTag		= HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */
     TTCTag		= HB_TAG ('t','t','c','f'), /* TrueType Collection */
     DFontTag		= HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */
     TrueTag		= HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */
@@ -503,12 +473,14 @@
     case Typ1Tag:
     case TrueTypeTag:	return 1;
     case TTCTag:	return u.ttcHeader.get_face_count ();
-//    case DFontTag:	return u.rfHeader.get_face_count ();
+    case DFontTag:	return u.rfHeader.get_face_count ();
     default:		return 0;
     }
   }
-  inline const OpenTypeFontFace& get_face (unsigned int i) const
+  inline const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const
   {
+    if (base_offset)
+      *base_offset = 0;
     switch (u.tag) {
     /* Note: for non-collection SFNT data we ignore index.  This is because
      * Apple dfont container is a container of SFNT's.  So each SFNT is a
@@ -518,7 +490,7 @@
     case Typ1Tag:
     case TrueTypeTag:	return u.fontFace;
     case TTCTag:	return u.ttcHeader.get_face (i);
-//    case DFontTag:	return u.rfHeader.get_face (i);
+    case DFontTag:	return u.rfHeader.get_face (i, base_offset);
     default:		return Null(OpenTypeFontFace);
     }
   }
@@ -545,7 +517,7 @@
     case Typ1Tag:
     case TrueTypeTag:	return_trace (u.fontFace.sanitize (c));
     case TTCTag:	return_trace (u.ttcHeader.sanitize (c));
-//    case DFontTag:	return_trace (u.rfHeader.sanitize (c));
+    case DFontTag:	return_trace (u.rfHeader.sanitize (c));
     default:		return_trace (true);
     }
   }
@@ -565,4 +537,4 @@
 } /* namespace OT */
 
 
-#endif /* HB_OPEN_FILE_PRIVATE_HH */
+#endif /* HB_OPEN_FILE_HH */
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type.hh
similarity index 65%
rename from src/hb-open-type-private.hh
rename to src/hb-open-type.hh
index 5580565..08e7206 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type.hh
@@ -26,13 +26,14 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OPEN_TYPE_PRIVATE_HH
-#define HB_OPEN_TYPE_PRIVATE_HH
+#ifndef HB_OPEN_TYPE_HH
+#define HB_OPEN_TYPE_HH
 
-#include "hb-private.hh"
-#include "hb-blob-private.hh"
-#include "hb-face-private.hh"
-#include "hb-machinery-private.hh"
+#include "hb.hh"
+#include "hb-blob.hh"
+#include "hb-face.hh"
+#include "hb-machinery.hh"
+#include "hb-subset.hh"
 
 
 namespace OT {
@@ -55,6 +56,7 @@
 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; }
@@ -91,6 +93,9 @@
 /* 16-bit signed integer (HBINT16) that describes a quantity in FUnits. */
 typedef HBINT16 FWORD;
 
+/* 32-bit signed integer (HBINT32) that describes a quantity in FUnits. */
+typedef HBINT32 FWORD32;
+
 /* 16-bit unsigned integer (HBUINT16) that describes a quantity in FUnits. */
 typedef HBUINT16 UFWORD;
 
@@ -149,15 +154,17 @@
 
 /* Script/language-system/feature index */
 struct Index : HBUINT16 {
-  static const unsigned int NOT_FOUND_INDEX = 0xFFFFu;
+  enum { NOT_FOUND_INDEX = 0xFFFFu };
 };
 DECLARE_NULL_NAMESPACE_BYTES (OT, Index);
 
 /* Offset, Null offset = 0 */
-template <typename Type>
+template <typename Type, bool has_null=true>
 struct Offset : Type
 {
-  inline bool is_null (void) const { return 0 == *this; }
+  typedef Type type;
+
+  inline bool is_null (void) const { return has_null && 0 == *this; }
 
   inline void *serialize (hb_serialize_context_t *c, const void *base)
   {
@@ -225,20 +232,23 @@
  * Use: (base+offset)
  */
 
-template <typename Type, typename OffsetType=HBUINT16>
-struct OffsetTo : Offset<OffsetType>
+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, 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
   {
-    unsigned int offset = *this;
-    if (unlikely (!offset)) return Null(Type);
-    return StructAtOffset<const Type> (base, offset);
+    if (unlikely (this->is_null ())) return Null(Type);
+    return StructAtOffset<const Type> (base, *this);
   }
   inline Type& operator () (void *base) const
   {
-    unsigned int offset = *this;
-    if (unlikely (!offset)) return Crap(Type);
-    return StructAtOffset<Type> (base, offset);
+    if (unlikely (this->is_null ())) return Crap(Type);
+    return StructAtOffset<Type> (base, *this);
   }
 
   inline Type& serialize (hb_serialize_context_t *c, const void *base)
@@ -246,46 +256,83 @@
     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)
+  {
+    if (&src == &Null(T))
+    {
+      this->set (0);
+      return;
+    }
+    serialize (c->serializer, base);
+    if (!src.subset (c))
+      this->set (0);
+  }
+
+  inline bool sanitize_shallow (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this))) return_trace (false);
+    if (unlikely (this->is_null ())) return_trace (true);
+    if (unlikely (!c->check_range (base, *this))) return_trace (false);
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (this))) return_trace (false);
-    unsigned int offset = *this;
-    if (unlikely (!offset)) return_trace (true);
-    if (unlikely (!c->check_range (base, offset))) return_trace (false);
-    const Type &obj = StructAtOffset<Type> (base, offset);
-    return_trace (likely (obj.sanitize (c)) || neuter (c));
+    return_trace (sanitize_shallow (c, base) &&
+		  (this->is_null () ||
+		   StructAtOffset<Type> (base, *this).sanitize (c) ||
+		   neuter (c)));
   }
-  template <typename T>
-  inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
+  template <typename T1>
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (this))) return_trace (false);
-    unsigned int offset = *this;
-    if (unlikely (!offset)) return_trace (true);
-    if (unlikely (!c->check_range (base, offset))) return_trace (false);
-    const Type &obj = StructAtOffset<Type> (base, offset);
-    return_trace (likely (obj.sanitize (c, user_data)) || neuter (c));
+    return_trace (sanitize_shallow (c, base) &&
+		  (this->is_null () ||
+		   StructAtOffset<Type> (base, *this).sanitize (c, d1) ||
+		   neuter (c)));
+  }
+  template <typename T1, typename T2>
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (sanitize_shallow (c, base) &&
+		  (this->is_null () ||
+		   StructAtOffset<Type> (base, *this).sanitize (c, d1, d2) ||
+		   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
+  {
+    TRACE_SANITIZE (this);
+    return_trace (sanitize_shallow (c, base) &&
+		  (this->is_null () ||
+		   StructAtOffset<Type> (base, *this).sanitize (c, d1, d2, d3) ||
+		   neuter (c)));
   }
 
   /* Set the offset to Null */
-  inline bool neuter (hb_sanitize_context_t *c) const {
+  inline bool neuter (hb_sanitize_context_t *c) const
+  {
+    if (!has_null) return false;
     return c->try_set (this, 0);
   }
   DEFINE_SIZE_STATIC (sizeof(OffsetType));
 };
-template <typename Type> struct LOffsetTo : OffsetTo<Type, HBUINT32> {};
-template <typename Base, typename OffsetType, typename Type>
-static inline const Type& operator + (const Base &base, const OffsetTo<Type, OffsetType> &offset) { return offset (base); }
-template <typename Base, typename OffsetType, typename Type>
-static inline Type& operator + (Base &base, OffsetTo<Type, OffsetType> &offset) { return offset (base); }
+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>
+static inline Type& operator + (Base &base, OffsetTo<Type, OffsetType, has_null> &offset) { return offset (base); }
 
 
 /*
  * Array Types
  */
 
-/* TODO Use it in ArrayOf, HeadlessArrayOf, and other places around the code base?? */
 template <typename Type>
 struct UnsizedArrayOf
 {
@@ -331,22 +378,22 @@
   inline bool sanitize_shallow (hb_sanitize_context_t *c, unsigned int count) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_array (arrayZ, arrayZ[0].static_size, count));
+    return_trace (c->check_array (arrayZ, count));
   }
 
   public:
-  Type	arrayZ[VAR];
+  Type		arrayZ[VAR];
   public:
   DEFINE_SIZE_ARRAY (0, arrayZ);
 };
 
 /* Unsized array of offset's */
-template <typename Type, typename OffsetType>
-struct UnsizedOffsetArrayOf : UnsizedArrayOf<OffsetTo<Type, OffsetType> > {};
+template <typename Type, typename OffsetType, bool has_null=true>
+struct UnsizedOffsetArrayOf : UnsizedArrayOf<OffsetTo<Type, OffsetType, has_null> > {};
 
 /* Unsized array of offsets relative to the beginning of the array itself. */
-template <typename Type, typename OffsetType>
-struct UnsizedOffsetListOf : UnsizedOffsetArrayOf<Type, OffsetType>
+template <typename Type, typename OffsetType, bool has_null=true>
+struct UnsizedOffsetListOf : UnsizedOffsetArrayOf<Type, OffsetType, has_null>
 {
   inline const Type& operator [] (unsigned int i) const
   {
@@ -356,13 +403,13 @@
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
   {
     TRACE_SANITIZE (this);
-    return_trace ((UnsizedOffsetArrayOf<Type, OffsetType>::sanitize (c, count, 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
   {
     TRACE_SANITIZE (this);
-    return_trace ((UnsizedOffsetArrayOf<Type, OffsetType>::sanitize (c, count, this, user_data)));
+    return_trace ((UnsizedOffsetArrayOf<Type, OffsetType, has_null>::sanitize (c, count, this, user_data)));
   }
 };
 
@@ -404,7 +451,6 @@
     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)
@@ -474,12 +520,12 @@
   inline bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (len.sanitize (c) && c->check_array (arrayZ, Type::static_size, len));
+    return_trace (len.sanitize (c) && c->check_array (arrayZ, len));
   }
 
   public:
-  LenType len;
-  Type arrayZ[VAR];
+  LenType	len;
+  Type		arrayZ[VAR];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
 };
@@ -505,6 +551,17 @@
     return this+this->arrayZ[i];
   }
 
+  inline bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    struct OffsetListOf<Type> *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+    unsigned int count = this->len;
+    for (unsigned int i = 0; i < count; i++)
+      out->arrayZ[i].serialize_subset (c, (*this)[i], out);
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -524,16 +581,16 @@
 {
   inline const Type& operator [] (unsigned int i) const
   {
-    if (unlikely (i >= len || !i)) return Null(Type);
+    if (unlikely (i >= lenP1 || !i)) return Null(Type);
     return arrayZ[i-1];
   }
   inline Type& operator [] (unsigned int i)
   {
-    if (unlikely (i >= len || !i)) return Crap(Type);
+    if (unlikely (i >= lenP1 || !i)) return Crap(Type);
     return arrayZ[i-1];
   }
   inline unsigned int get_size (void) const
-  { return len.static_size + (len ? len - 1 : 0) * Type::static_size; }
+  { return lenP1.static_size + (lenP1 ? lenP1 - 1 : 0) * Type::static_size; }
 
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<Type> &items,
@@ -541,7 +598,7 @@
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    len.set (items_len); /* TODO(serialize) Overflow? */
+    lenP1.set (items_len); /* TODO(serialize) Overflow? */
     if (unlikely (!items_len)) return_trace (true);
     if (unlikely (!c->extend (*this))) return_trace (false);
     for (unsigned int i = 0; i < items_len - 1; i++)
@@ -571,13 +628,57 @@
   inline bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (len.sanitize (c) &&
-		  (!len || c->check_array (arrayZ, Type::static_size, len - 1)));
+    return_trace (lenP1.sanitize (c) &&
+		  (!lenP1 || c->check_array (arrayZ, lenP1 - 1)));
   }
 
   public:
-  LenType len;
-  Type arrayZ[VAR];
+  LenType	lenP1;
+  Type		arrayZ[VAR];
+  public:
+  DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
+};
+
+/* An array storing length-1. */
+template <typename Type, typename LenType=HBUINT16>
+struct ArrayOfM1
+{
+  inline const Type& operator [] (unsigned int i) const
+  {
+    if (unlikely (i > lenM1)) return Null(Type);
+    return arrayZ[i];
+  }
+  inline Type& operator [] (unsigned int i)
+  {
+    if (unlikely (i > lenM1)) return Crap(Type);
+    return arrayZ[i];
+  }
+  inline unsigned int get_size (void) 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
+  {
+    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 (true);
+  }
+
+  private:
+  inline bool sanitize_shallow (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (lenM1.sanitize (c) &&
+		  (c->check_array (arrayZ, lenM1 + 1)));
+  }
+
+  public:
+  LenType	lenM1;
+  Type		arrayZ[VAR];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
 };
@@ -607,7 +708,11 @@
   }
 };
 
-/* Binary-search arrays */
+/*
+ * Binary-search arrays
+ */
+
+template <typename LenType=HBUINT16>
 struct BinSearchHeader
 {
   inline operator uint32_t (void) const { return len; }
@@ -630,20 +735,120 @@
   }
 
   protected:
-  HBUINT16	len;
-  HBUINT16	searchRange;
-  HBUINT16	entrySelector;
-  HBUINT16	rangeShift;
+  LenType	len;
+  LenType	searchRange;
+  LenType	entrySelector;
+  LenType	rangeShift;
 
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
+template <typename Type, typename LenType=HBUINT16>
+struct BinSearchArrayOf : SortedArrayOf<Type, BinSearchHeader<LenType> > {};
+
+struct VarSizedBinSearchHeader
+{
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  HBUINT16	unitSize;	/* Size of a lookup unit for this search in bytes. */
+  HBUINT16	nUnits;		/* Number of units of the preceding size to be searched. */
+  HBUINT16	searchRange;	/* The value of unitSize times the largest power of 2
+				 * that is less than or equal to the value of nUnits. */
+  HBUINT16	entrySelector;	/* The log base 2 of the largest power of 2 less than
+				 * or equal to the value of nUnits. */
+  HBUINT16	rangeShift;	/* The value of unitSize times the difference of the
+				 * value of nUnits minus the largest power of 2 less
+				 * than or equal to the value of nUnits. */
+  public:
+  DEFINE_SIZE_STATIC (10);
+};
+
 template <typename Type>
-struct BinSearchArrayOf : SortedArrayOf<Type, BinSearchHeader> {};
+struct VarSizedBinSearchArrayOf
+{
+  inline const Type& operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= header.nUnits)) return Null(Type);
+    return StructAtOffset<Type> (&bytesZ, i * header.unitSize);
+  }
+  inline Type& operator [] (unsigned int i)
+  {
+    return StructAtOffset<Type> (&bytesZ, i * header.unitSize);
+  }
+  inline unsigned int get_size (void) const
+  { return header.static_size + header.nUnits * header.unitSize; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!sanitize_shallow (c))) return_trace (false);
+
+    /* Note: for structs that do not reference other structs,
+     * we do not need to call their sanitize() as we already did
+     * a bound check on the aggregate array size.  We just include
+     * a small unreachable expression to make sure the structs
+     * pointed to do have a simple sanitize(), ie. they do not
+     * reference other structs via offsets.
+     */
+    (void) (false && StructAtOffset<Type> (&bytesZ, 0).sanitize (c));
+
+    return_trace (true);
+  }
+  inline 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;
+    for (unsigned int i = 0; i < count; i++)
+      if (unlikely (!(*this)[i].sanitize (c, base)))
+        return_trace (false);
+    return_trace (true);
+  }
+
+  template <typename T>
+  inline const Type *bsearch (const T &key) const
+  {
+    unsigned int size = header.unitSize;
+    int min = 0, max = (int) header.nUnits - 1;
+    while (min <= max)
+    {
+      int mid = (min + 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;
+    }
+    return nullptr;
+  }
+
+  private:
+  inline 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));
+  }
+
+  protected:
+  VarSizedBinSearchHeader	header;
+  UnsizedArrayOf<HBUINT8>	bytesZ;
+  public:
+  DEFINE_SIZE_ARRAY (10, bytesZ);
+};
 
 
 } /* namespace OT */
 
 
-#endif /* HB_OPEN_TYPE_PRIVATE_HH */
+#endif /* HB_OPEN_TYPE_HH */
diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index 67a9c7d..e5793c3 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -27,9 +27,8 @@
 #ifndef HB_OT_CMAP_TABLE_HH
 #define HB_OT_CMAP_TABLE_HH
 
-#include "hb-open-type-private.hh"
-#include "hb-set-private.hh"
-#include "hb-subset-plan.hh"
+#include "hb-open-type.hh"
+#include "hb-set.hh"
 
 /*
  * cmap -- Character to Glyph Index Mapping
@@ -37,10 +36,6 @@
  */
 #define HB_OT_TAG_cmap HB_TAG('c','m','a','p')
 
-#ifndef HB_MAX_UNICODE_CODEPOINT_VALUE
-#define HB_MAX_UNICODE_CODEPOINT_VALUE 0x10FFFF
-#endif
-
 namespace OT {
 
 
@@ -54,6 +49,12 @@
     *glyph = gid;
     return true;
   }
+  inline void collect_unicodes (hb_set_t *out) const
+  {
+    for (unsigned int i = 0; i < 256; i++)
+      if (glyphIdArray[i])
+        out->add (i);
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -230,22 +231,21 @@
     inline void init (const CmapSubtableFormat4 *subtable)
     {
       segCount = subtable->segCountX2 / 2;
-      endCount = subtable->values;
+      endCount = subtable->values.arrayZ;
       startCount = endCount + segCount + 1;
       idDelta = startCount + segCount;
       idRangeOffset = idDelta + segCount;
       glyphIdArray = idRangeOffset + segCount;
       glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2;
     }
+    inline void fini (void) {}
 
-    static inline bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph)
+    inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
     {
-      const accelerator_t *thiz = (const accelerator_t *) obj;
-
       /* Custom two-array bsearch. */
-      int min = 0, max = (int) thiz->segCount - 1;
-      const HBUINT16 *startCount = thiz->startCount;
-      const HBUINT16 *endCount = thiz->endCount;
+      int min = 0, max = (int) this->segCount - 1;
+      const HBUINT16 *startCount = this->startCount;
+      const HBUINT16 *endCount = this->endCount;
       unsigned int i;
       while (min <= max)
       {
@@ -264,33 +264,55 @@
 
     found:
       hb_codepoint_t gid;
-      unsigned int rangeOffset = thiz->idRangeOffset[i];
+      unsigned int rangeOffset = this->idRangeOffset[i];
       if (rangeOffset == 0)
-	gid = codepoint + thiz->idDelta[i];
+	gid = codepoint + this->idDelta[i];
       else
       {
 	/* Somebody has been smoking... */
-	unsigned int index = rangeOffset / 2 + (codepoint - thiz->startCount[i]) + i - thiz->segCount;
-	if (unlikely (index >= thiz->glyphIdArrayLength))
+	unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount;
+	if (unlikely (index >= this->glyphIdArrayLength))
 	  return false;
-	gid = thiz->glyphIdArray[index];
+	gid = this->glyphIdArray[index];
 	if (unlikely (!gid))
 	  return false;
-	gid += thiz->idDelta[i];
+	gid += this->idDelta[i];
       }
-
-      *glyph = gid & 0xFFFFu;
+      gid &= 0xFFFFu;
+      if (!gid)
+	return false;
+      *glyph = gid;
       return true;
     }
-
-    static inline void get_all_codepoints_func (const void *obj, hb_set_t *out)
+    static inline bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph)
     {
-      const accelerator_t *thiz = (const accelerator_t *) obj;
-      for (unsigned int i = 0; i < thiz->segCount; i++)
+      return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph);
+    }
+    inline void collect_unicodes (hb_set_t *out) const
+    {
+      unsigned int count = this->segCount;
+      if (count && this->startCount[count - 1] == 0xFFFFu)
+        count--; /* Skip sentinel segment. */
+      for (unsigned int i = 0; i < count; i++)
       {
-	if (thiz->startCount[i] != 0xFFFFu
-	    || thiz->endCount[i] != 0xFFFFu) // Skip the last segment (0xFFFF)
-	  hb_set_add_range (out, thiz->startCount[i], thiz->endCount[i]);
+	unsigned int rangeOffset = this->idRangeOffset[i];
+	if (rangeOffset == 0)
+	  out->add_range (this->startCount[i], this->endCount[i]);
+	else
+	{
+	  for (hb_codepoint_t codepoint = this->startCount[i];
+	       codepoint <= this->endCount[i];
+	       codepoint++)
+	  {
+	    unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount;
+	    if (unlikely (index >= this->glyphIdArrayLength))
+	      break;
+	    hb_codepoint_t gid = this->glyphIdArray[index];
+	    if (unlikely (!gid))
+	      continue;
+	    out->add (codepoint);
+	  }
+	}
       }
     }
 
@@ -305,10 +327,14 @@
 
   inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
-    accelerator_t accel;
-    accel.init (this);
+    hb_auto_t<accelerator_t> accel (this);
     return accel.get_glyph_func (&accel, codepoint, glyph);
   }
+  inline void collect_unicodes (hb_set_t *out) const
+  {
+    hb_auto_t<accelerator_t> accel (this);
+    accel.collect_unicodes (out);
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -343,7 +369,8 @@
   HBUINT16	entrySelector;	/* log2(searchRange/2) */
   HBUINT16	rangeShift;	/* 2 x segCount - searchRange */
 
-  HBUINT16	values[VAR];
+  UnsizedArrayOf<HBUINT16>
+		values;
 #if 0
   HBUINT16	endCount[segCount];	/* End characterCode for each segment,
 					 * last=0xFFFFu. */
@@ -351,7 +378,8 @@
   HBUINT16	startCount[segCount];	/* Start character code for each segment. */
   HBINT16		idDelta[segCount];	/* Delta for all character codes in segment. */
   HBUINT16	idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */
-  HBUINT16	glyphIdArray[VAR];	/* Glyph index array (arbitrary length) */
+  UnsizedArrayOf<HBUINT16>
+		glyphIdArray;	/* Glyph index array (arbitrary length) */
 #endif
 
   public:
@@ -383,7 +411,7 @@
   HBUINT32		startCharCode;	/* First character code in this group. */
   HBUINT32		endCharCode;	/* Last character code in this group. */
   HBUINT32		glyphID;	/* Glyph index; interpretation depends on
-				 * subtable format. */
+					 * subtable format. */
   public:
   DEFINE_SIZE_STATIC (12);
 };
@@ -400,6 +428,14 @@
     *glyph = gid;
     return true;
   }
+  inline 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);
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -432,18 +468,19 @@
     int i = groups.bsearch (codepoint);
     if (i == -1)
       return false;
-    *glyph = T::group_get_glyph (groups[i], codepoint);
+    hb_codepoint_t gid = T::group_get_glyph (groups[i], codepoint);
+    if (!gid)
+      return false;
+    *glyph = gid;
     return true;
   }
 
-  inline void get_all_codepoints (hb_set_t *out) const
+  inline void collect_unicodes (hb_set_t *out) const
   {
     for (unsigned int i = 0; i < this->groups.len; i++) {
-      hb_set_add_range (out,
-			MIN ((unsigned int) this->groups[i].startCharCode,
-			     (unsigned int) HB_MAX_UNICODE_CODEPOINT_VALUE),
-			MIN ((unsigned int) this->groups[i].endCharCode,
-			     (unsigned int) HB_MAX_UNICODE_CODEPOINT_VALUE));
+      out->add_range (this->groups[i].startCharCode,
+		      MIN ((hb_codepoint_t) this->groups[i].endCharCode,
+			   (hb_codepoint_t) HB_UNICODE_MAX));
     }
   }
 
@@ -458,7 +495,7 @@
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    Supplier<CmapSubtableLongGroup> supplier (group_data.arrayZ, group_data.len);
+    Supplier<CmapSubtableLongGroup> supplier (group_data.arrayZ(), group_data.len);
     if (unlikely (!groups.serialize (c, supplier, group_data.len))) return_trace (false);
     return true;
   }
@@ -574,13 +611,29 @@
   }
 
   HBUINT24	startUnicodeValue;	/* First value in this range. */
-  HBUINT8		additionalCount;	/* Number of additional values in this
+  HBUINT8	additionalCount;	/* Number of additional values in this
 					 * range. */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
-typedef SortedArrayOf<UnicodeValueRange, HBUINT32> DefaultUVS;
+struct DefaultUVS : SortedArrayOf<UnicodeValueRange, HBUINT32>
+{
+  inline void collect_unicodes (hb_set_t *out) const
+  {
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      hb_codepoint_t first = arrayZ[i].startUnicodeValue;
+      hb_codepoint_t last = MIN ((hb_codepoint_t) (first + arrayZ[i].additionalCount),
+				 (hb_codepoint_t) HB_UNICODE_MAX);
+      out->add_range (first, last);
+    }
+  }
+
+  public:
+  DEFINE_SIZE_ARRAY (4, arrayZ);
+};
 
 struct UVSMapping
 {
@@ -601,7 +654,18 @@
   DEFINE_SIZE_STATIC (5);
 };
 
-typedef SortedArrayOf<UVSMapping, HBUINT32> NonDefaultUVS;
+struct NonDefaultUVS : SortedArrayOf<UVSMapping, HBUINT32>
+{
+  inline void collect_unicodes (hb_set_t *out) const
+  {
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+      out->add (arrayZ[i].glyphID);
+  }
+
+  public:
+  DEFINE_SIZE_ARRAY (4, arrayZ);
+};
 
 struct VariationSelectorRecord
 {
@@ -616,7 +680,7 @@
       return GLYPH_VARIANT_USE_DEFAULT;
     const NonDefaultUVS &nonDefaults = base+nonDefaultUVS;
     i = nonDefaults.bsearch (codepoint);
-    if (i != -1)
+    if (i != -1 && nonDefaults[i].glyphID)
     {
       *glyph = nonDefaults[i].glyphID;
        return GLYPH_VARIANT_FOUND;
@@ -624,6 +688,12 @@
     return GLYPH_VARIANT_NOT_FOUND;
   }
 
+  inline 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
   {
     return varSelector.cmp (variation_selector);
@@ -639,9 +709,9 @@
 
   HBUINT24	varSelector;	/* Variation selector. */
   LOffsetTo<DefaultUVS>
-		defaultUVS;	/* Offset to Default UVS Table. May be 0. */
+		defaultUVS;	/* Offset to Default UVS Table.  May be 0. */
   LOffsetTo<NonDefaultUVS>
-		nonDefaultUVS;	/* Offset to Non-Default UVS Table. May be 0. */
+		nonDefaultUVS;	/* Offset to Non-Default UVS Table.  May be 0. */
   public:
   DEFINE_SIZE_STATIC (11);
 };
@@ -652,7 +722,19 @@
 					    hb_codepoint_t variation_selector,
 					    hb_codepoint_t *glyph) const
   {
-    return record[record.bsearch(variation_selector)].get_glyph (codepoint, glyph, this);
+    return record[record.bsearch (variation_selector)].get_glyph (codepoint, glyph, this);
+  }
+
+  inline 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
+  {
+    record[record.bsearch (variation_selector)].collect_unicodes (out, this);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -690,6 +772,19 @@
     default: return false;
     }
   }
+  inline void collect_unicodes (hb_set_t *out) const
+  {
+    switch (u.format) {
+    case  0: u.format0 .collect_unicodes (out); return;
+    case  4: u.format4 .collect_unicodes (out); return;
+    case  6: u.format6 .collect_unicodes (out); return;
+    case 10: u.format10.collect_unicodes (out); return;
+    case 12: u.format12.collect_unicodes (out); return;
+    case 13: u.format13.collect_unicodes (out); return;
+    case 14:
+    default: return;
+    }
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -754,7 +849,8 @@
 {
   static const hb_tag_t tableTag	= HB_OT_TAG_cmap;
 
-  struct subset_plan {
+  struct subset_plan
+  {
     subset_plan(void)
     {
       format4_segments.init();
@@ -895,33 +991,44 @@
     return result;
   }
 
+  const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const
+  {
+    if (symbol) *symbol = false;
+
+    const CmapSubtable *subtable;
+
+    /* 32-bit subtables. */
+    if ((subtable = this->find_subtable (3, 10))) return subtable;
+    if ((subtable = this->find_subtable (0, 6))) return subtable;
+    if ((subtable = this->find_subtable (0, 4))) return subtable;
+
+    /* 16-bit subtables. */
+    if ((subtable = this->find_subtable (3, 1))) return subtable;
+    if ((subtable = this->find_subtable (0, 3))) return subtable;
+    if ((subtable = this->find_subtable (0, 2))) return subtable;
+    if ((subtable = this->find_subtable (0, 1))) return subtable;
+    if ((subtable = this->find_subtable (0, 0))) return subtable;
+
+    /* Symbol subtable. */
+    if ((subtable = this->find_subtable (3, 0)))
+    {
+      if (symbol) *symbol = true;
+      return subtable;
+    }
+
+    /* Meh. */
+    return &Null(CmapSubtable);
+  }
+
   struct accelerator_t
   {
     inline void init (hb_face_t *face)
     {
       this->blob = hb_sanitize_context_t().reference_table<cmap> (face);
       const cmap *table = this->blob->as<cmap> ();
-      const CmapSubtable *subtable = nullptr;
       const CmapSubtableFormat14 *subtable_uvs = nullptr;
-
-      bool symbol = false;
-      /* 32-bit subtables. */
-      if (!subtable) subtable = table->find_subtable (3, 10);
-      if (!subtable) subtable = table->find_subtable (0, 6);
-      if (!subtable) subtable = table->find_subtable (0, 4);
-      /* 16-bit subtables. */
-      if (!subtable) subtable = table->find_subtable (3, 1);
-      if (!subtable) subtable = table->find_subtable (0, 3);
-      if (!subtable) subtable = table->find_subtable (0, 2);
-      if (!subtable) subtable = table->find_subtable (0, 1);
-      if (!subtable) subtable = table->find_subtable (0, 0);
-      if (!subtable)
-      {
-	subtable = table->find_subtable (3, 0);
-	if (subtable) symbol = true;
-      }
-      /* Meh. */
-      if (!subtable) subtable = &Null(CmapSubtable);
+      bool symbol;
+      subtable = table->find_best_subtable (&symbol);
 
       /* UVS subtable. */
       if (!subtable_uvs)
@@ -933,30 +1040,26 @@
       /* Meh. */
       if (!subtable_uvs) subtable_uvs = &Null(CmapSubtableFormat14);
 
-      this->uvs_table = subtable_uvs;
+      this->subtable_uvs = subtable_uvs;
 
       this->get_glyph_data = subtable;
       if (unlikely (symbol))
       {
 	this->get_glyph_func = get_glyph_from_symbol<CmapSubtable>;
-	this->get_all_codepoints_func = null_get_all_codepoints_func;
       } else {
 	switch (subtable->u.format) {
 	/* Accelerate format 4 and format 12. */
 	default:
 	  this->get_glyph_func = get_glyph_from<CmapSubtable>;
-	  this->get_all_codepoints_func = null_get_all_codepoints_func;
 	  break;
 	case 12:
 	  this->get_glyph_func = get_glyph_from<CmapSubtableFormat12>;
-	  this->get_all_codepoints_func = get_all_codepoints_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_all_codepoints_func = this->format4_accel.get_all_codepoints_func;
 	  }
 	  break;
 	}
@@ -978,9 +1081,9 @@
 				     hb_codepoint_t  variation_selector,
 				     hb_codepoint_t *glyph) const
     {
-      switch (this->uvs_table->get_glyph_variant (unicode,
-						  variation_selector,
-						  glyph))
+      switch (this->subtable_uvs->get_glyph_variant (unicode,
+						     variation_selector,
+						     glyph))
       {
 	case GLYPH_VARIANT_NOT_FOUND:	return false;
 	case GLYPH_VARIANT_FOUND:	return true;
@@ -990,22 +1093,24 @@
       return get_nominal_glyph (unicode, glyph);
     }
 
-    inline void get_all_codepoints (hb_set_t *out) const
+    inline void collect_unicodes (hb_set_t *out) const
     {
-      this->get_all_codepoints_func (get_glyph_data, out);
+      subtable->collect_unicodes (out);
+    }
+    inline 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
+    {
+      subtable_uvs->collect_variation_unicodes (variation_selector, out);
     }
 
     protected:
     typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
 					      hb_codepoint_t codepoint,
 					      hb_codepoint_t *glyph);
-    typedef void (*hb_cmap_get_all_codepoints_func_t) (const void *obj,
-						       hb_set_t *out);
-
-    static inline void null_get_all_codepoints_func (const void *obj, hb_set_t *out)
-    {
-      // NOOP
-    }
 
     template <typename Type>
     static inline bool get_glyph_from (const void *obj,
@@ -1017,14 +1122,6 @@
     }
 
     template <typename Type>
-    static inline void get_all_codepoints_from (const void *obj,
-						hb_set_t *out)
-    {
-      const Type *typed_obj = (const Type *) obj;
-      typed_obj->get_all_codepoints (out);
-    }
-
-    template <typename Type>
     static inline bool get_glyph_from_symbol (const void *obj,
 					      hb_codepoint_t codepoint,
 					      hb_codepoint_t *glyph)
@@ -1047,13 +1144,14 @@
     }
 
     private:
+    const CmapSubtable *subtable;
+    const CmapSubtableFormat14 *subtable_uvs;
+
     hb_cmap_get_glyph_func_t get_glyph_func;
     const void *get_glyph_data;
-    hb_cmap_get_all_codepoints_func_t get_all_codepoints_func;
 
     CmapSubtableFormat4::accelerator_t format4_accel;
 
-    const CmapSubtableFormat14 *uvs_table;
     hb_blob_t *blob;
   };
 
@@ -1066,10 +1164,7 @@
     key.platformID.set (platform_id);
     key.encodingID.set (encoding_id);
 
-    /* Note: We can use bsearch, but since it has no performance
-     * implications, we use lsearch and as such accept fonts with
-     * unsorted subtable list. */
-    int result = encodingRecord./*bsearch*/lsearch (key);
+    int result = encodingRecord.bsearch (key);
     if (result == -1 || !encodingRecord[result].subtable)
       return nullptr;
 
@@ -1084,6 +1179,7 @@
   DEFINE_SIZE_ARRAY (4, encodingRecord);
 };
 
+struct cmap_accelerator_t : cmap::accelerator_t {};
 
 } /* namespace OT */
 
diff --git a/src/hb-ot-color-cbdt-table.hh b/src/hb-ot-color-cbdt-table.hh
index 21c2f47..1e1fe09 100644
--- a/src/hb-ot-color-cbdt-table.hh
+++ b/src/hb-ot-color-cbdt-table.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OT_COLOR_CBDT_TABLE_HH
 #define HB_OT_COLOR_CBDT_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
 /*
  * CBLC -- Color Bitmap Location
@@ -128,7 +128,7 @@
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  c->check_array (offsetArrayZ, offsetArrayZ[0].static_size, glyph_count + 1));
+		  offsetArrayZ.sanitize (c, glyph_count + 1));
   }
 
   bool get_image_data (unsigned int idx,
@@ -144,7 +144,8 @@
   }
 
   IndexSubtableHeader	header;
-  Offset<OffsetType>	offsetArrayZ[VAR];
+  UnsizedArrayOf<Offset<OffsetType> >
+ 			offsetArrayZ;
   public:
   DEFINE_SIZE_ARRAY(8, offsetArrayZ);
 };
@@ -205,24 +206,23 @@
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  firstGlyphIndex <= lastGlyphIndex &&
-		  offsetToSubtable.sanitize (c, this, lastGlyphIndex - firstGlyphIndex + 1));
+		  offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1));
   }
 
-  inline bool get_extents (hb_glyph_extents_t *extents) const
+  inline bool get_extents (hb_glyph_extents_t *extents,
+			   const void *base) const
   {
-    return (this+offsetToSubtable).get_extents (extents);
+    return (base+offsetToSubtable).get_extents (extents);
   }
 
-  bool get_image_data (unsigned int gid,
+  bool get_image_data (unsigned int  gid,
+		       const void   *base,
 		       unsigned int *offset,
 		       unsigned int *length,
 		       unsigned int *format) const
   {
-    if (gid < firstGlyphIndex || gid > lastGlyphIndex)
-    {
-      return false;
-    }
-    return (this+offsetToSubtable).get_image_data (gid - firstGlyphIndex,
+    if (gid < firstGlyphIndex || gid > lastGlyphIndex) return false;
+    return (base+offsetToSubtable).get_image_data (gid - firstGlyphIndex,
 						   offset, length, format);
   }
 
@@ -240,12 +240,7 @@
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!c->check_array (&indexSubtablesZ, indexSubtablesZ[0].static_size, count)))
-      return_trace (false);
-    for (unsigned int i = 0; i < count; i++)
-      if (unlikely (!indexSubtablesZ[i].sanitize (c, this)))
-	return_trace (false);
-    return_trace (true);
+    return_trace (indexSubtablesZ.sanitize (c, count, this));
   }
 
   public:
@@ -255,17 +250,14 @@
     {
       unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex;
       unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex;
-      if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) {
+      if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex)
         return &indexSubtablesZ[i];
-      }
     }
     return nullptr;
   }
 
   protected:
-  IndexSubtableRecord	indexSubtablesZ[VAR];
-  public:
-  DEFINE_SIZE_ARRAY(0, indexSubtablesZ);
+  UnsizedArrayOf<IndexSubtableRecord>	indexSubtablesZ;
 };
 
 struct BitmapSizeTable
@@ -278,18 +270,20 @@
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) &&
-		  c->check_range (&(base+indexSubtableArrayOffset), indexTablesSize) &&
 		  horizontal.sanitize (c) &&
 		  vertical.sanitize (c));
   }
 
-  const IndexSubtableRecord *find_table (hb_codepoint_t glyph, const void *base) const
+  const IndexSubtableRecord *find_table (hb_codepoint_t glyph,
+					 const void *base,
+					 const void **out_base) const
   {
+    *out_base = &(base+indexSubtableArrayOffset);
     return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables);
   }
 
   protected:
-  LOffsetTo<IndexSubtableArray>
+  LOffsetTo<IndexSubtableArray, false>
 			indexSubtableArrayOffset;
   HBUINT32		indexTablesSize;
   HBUINT32		numberOfIndexSubtables;
@@ -350,7 +344,8 @@
 
   protected:
   const IndexSubtableRecord *find_table (hb_codepoint_t glyph,
-					 unsigned int *x_ppem, unsigned int *y_ppem) const
+					 unsigned int *x_ppem, unsigned int *y_ppem,
+					 const void **base) const
   {
     /* TODO: Make it possible to select strike. */
 
@@ -363,7 +358,7 @@
       {
 	*x_ppem = sizeTables[i].ppemX;
 	*y_ppem = sizeTables[i].ppemY;
-	return sizeTables[i].find_table (glyph, this);
+	return sizeTables[i].find_table (glyph, this, base);
       }
     }
 
@@ -421,15 +416,16 @@
       if (!cblc)
 	return false;  // Not a color bitmap font.
 
-      const IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem);
+      const void *base;
+      const IndexSubtableRecord *subtable_record = this->cblc->find_table (glyph, &x_ppem, &y_ppem, &base);
       if (!subtable_record || !x_ppem || !y_ppem)
 	return false;
 
-      if (subtable_record->get_extents (extents))
+      if (subtable_record->get_extents (extents, base))
 	return true;
 
       unsigned int image_offset = 0, image_length = 0, image_format = 0;
-      if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format))
+      if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format))
 	return false;
 
       {
@@ -480,7 +476,7 @@
           {
             unsigned int image_offset = 0, image_length = 0, image_format = 0;
 
-            if (!subtable_record.get_image_data (gid,
+            if (!subtable_record.get_image_data (gid, &subtable_array,
                   &image_offset, &image_length, &image_format))
               continue;
 
@@ -527,12 +523,14 @@
 
 
   protected:
-  FixedVersion<>	version;
-  HBUINT8		dataZ[VAR];
+  FixedVersion<>		version;
+  UnsizedArrayOf<HBUINT8>	dataZ;
   public:
   DEFINE_SIZE_ARRAY(4, dataZ);
 };
 
+struct CBDT_accelerator_t : CBDT::accelerator_t {};
+
 } /* namespace OT */
 
 #endif /* HB_OT_COLOR_CBDT_TABLE_HH */
diff --git a/src/hb-ot-color-colr-table.hh b/src/hb-ot-color-colr-table.hh
index ce6702d..a59d2bf 100644
--- a/src/hb-ot-color-colr-table.hh
+++ b/src/hb-ot-color-colr-table.hh
@@ -25,7 +25,7 @@
 #ifndef HB_OT_COLOR_COLR_TABLE_HH
 #define HB_OT_COLOR_COLR_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
 /*
  * COLR -- Color
@@ -129,9 +129,9 @@
   protected:
   HBUINT16	version;	/* Table version number */
   HBUINT16	numBaseGlyphs;	/* Number of Base Glyph Records */
-  LOffsetTo<UnsizedArrayOf<BaseGlyphRecord> >
+  LOffsetTo<UnsizedArrayOf<BaseGlyphRecord>, false>
 		baseGlyphsZ;	/* Offset to Base Glyph records. */
-  LOffsetTo<UnsizedArrayOf<LayerRecord> >
+  LOffsetTo<UnsizedArrayOf<LayerRecord>, false>
 		layersZ;	/* Offset to Layer Records */
   HBUINT16	numLayers;	/* Number of Layer Records */
   public:
diff --git a/src/hb-ot-color-cpal-table.hh b/src/hb-ot-color-cpal-table.hh
index 2c31274..e354ced 100644
--- a/src/hb-ot-color-cpal-table.hh
+++ b/src/hb-ot-color-cpal-table.hh
@@ -28,7 +28,7 @@
 #ifndef HB_OT_COLOR_CPAL_TABLE_HH
 #define HB_OT_COLOR_CPAL_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
 
 /*
@@ -118,15 +118,15 @@
   }
 
   protected:
-  LOffsetTo<UnsizedArrayOf<HBUINT32> >
+  LOffsetTo<UnsizedArrayOf<HBUINT32>, false>
 		paletteFlagsZ;		/* Offset from the beginning of CPAL table to
 					 * the Palette Type Array. Set to 0 if no array
 					 * is provided. */
-  LOffsetTo<UnsizedArrayOf<HBUINT16> >
+  LOffsetTo<UnsizedArrayOf<HBUINT16>, false>
 		paletteLabelZ;		/* Offset from the beginning of CPAL table to
 					 * the Palette Labels Array. Set to 0 if no
 					 * array is provided. */
-  LOffsetTo<UnsizedArrayOf<HBUINT16> >
+  LOffsetTo<UnsizedArrayOf<HBUINT16>, false>
 		paletteEntryLabelZ;	/* Offset from the beginning of CPAL table to
 					 * the Palette Entry Label Array. Set to 0
 					 * if no array is provided. */
@@ -207,7 +207,7 @@
   HBUINT16	numPalettes;		/* Number of palettes in the table. */
   HBUINT16	numColorRecords;	/* Total number of color records, combined for
 					 * all palettes. */
-  LOffsetTo<UnsizedArrayOf<BGRAColor> >
+  LOffsetTo<UnsizedArrayOf<BGRAColor>, false>
 		colorRecordsZ;		/* Offset from the beginning of CPAL table to
 					 * the first ColorRecord. */
   UnsizedArrayOf<HBUINT16>
diff --git a/src/hb-ot-color-sbix-table.hh b/src/hb-ot-color-sbix-table.hh
index 0461afa..1b643c7 100644
--- a/src/hb-ot-color-sbix-table.hh
+++ b/src/hb-ot-color-sbix-table.hh
@@ -25,7 +25,7 @@
 #ifndef HB_OT_COLOR_SBIX_TABLE_HH
 #define HB_OT_COLOR_SBIX_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
 /*
  * sbix -- Standard Bitmap Graphics
diff --git a/src/hb-ot-color-svg-table.hh b/src/hb-ot-color-svg-table.hh
index 3976694..53d4668 100644
--- a/src/hb-ot-color-svg-table.hh
+++ b/src/hb-ot-color-svg-table.hh
@@ -25,7 +25,7 @@
 #ifndef HB_OT_COLOR_SVG_TABLE_HH
 #define HB_OT_COLOR_SVG_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
 /*
  * SVG -- SVG (Scalable Vector Graphics)
@@ -54,7 +54,7 @@
 				 * this index entry. */
   HBUINT16	endGlyphID;	/* The last glyph ID in the range described by
 				 * this index entry. Must be >= startGlyphID. */
-  LOffsetTo<UnsizedArrayOf<HBUINT8> >
+  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.
diff --git a/src/hb-ot-color.cc b/src/hb-ot-color.cc
index 86171c6..7cdff38 100644
--- a/src/hb-ot-color.cc
+++ b/src/hb-ot-color.cc
@@ -25,7 +25,7 @@
  * Google Author(s): Sascha Brawer
  */
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 #include "hb-ot-color-colr-table.hh"
 #include "hb-ot-color-cpal-table.hh"
 #include "hb-ot.h"
@@ -33,8 +33,8 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "hb-ot-layout-private.hh"
-#include "hb-shaper-private.hh"
+#include "hb-ot-layout.hh"
+#include "hb-shaper.hh"
 
 #if 0
 HB_MARK_AS_FLAG_T (hb_ot_color_palette_flags_t)
@@ -45,16 +45,14 @@
 _get_colr (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::COLR);
-  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  return *(layout->colr.get ());
+  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);
-  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  return *(layout->cpal.get ());
+  return *(hb_ot_face_data (face)->cpal.get ());
 }
 
 
diff --git a/src/hb-ot-face.cc b/src/hb-ot-face.cc
new file mode 100644
index 0000000..1bc68d3
--- /dev/null
+++ b/src/hb-ot-face.cc
@@ -0,0 +1,76 @@
+/*
+ * 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-face.hh"
+
+#include "hb-ot-cmap-table.hh"
+#include "hb-ot-glyf-table.hh"
+#include "hb-ot-hmtx-table.hh"
+#include "hb-ot-kern-table.hh"
+#include "hb-ot-post-table.hh"
+#include "hb-ot-color-cbdt-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)
+{
+  this->face = face;
+#define HB_OT_TABLE(Namespace, Type) Type.init0 ();
+#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type)
+  HB_OT_TABLES
+#undef HB_OT_ACCELERATOR
+#undef HB_OT_TABLE
+}
+void hb_ot_face_data_t::fini (void)
+{
+#define HB_OT_TABLE(Namespace, Type) Type.fini ();
+#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type)
+  HB_OT_TABLES
+#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
new file mode 100644
index 0000000..e305922
--- /dev/null
+++ b/src/hb-ot-face.hh
@@ -0,0 +1,116 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2012,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
+ */
+
+#ifndef HB_OT_FACE_HH
+#define HB_OT_FACE_HH
+
+#include "hb.hh"
+
+#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
+ */
+
+/* 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 shaping. */ \
+    HB_OT_TABLE(OT, JSTF) \
+    HB_OT_TABLE(OT, BASE) \
+    /* AAT shaping. */ \
+    HB_OT_TABLE(AAT, morx) \
+    HB_OT_TABLE(AAT, kerx) \
+    HB_OT_TABLE(AAT, ankr) \
+    HB_OT_TABLE(AAT, trak) \
+    /* 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) \
+    HB_OT_ACCELERATOR(OT, CBDT) \
+    /* */
+
+/* Declare tables. */
+#define HB_OT_TABLE(Namespace, Type) namespace Namespace { struct Type; }
+#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type##_accelerator_t)
+HB_OT_TABLES
+#undef HB_OT_ACCELERATOR
+#undef HB_OT_TABLE
+
+struct hb_ot_face_data_t
+{
+  HB_INTERNAL void init0 (hb_face_t *face);
+  HB_INTERNAL void fini (void);
+
+#define HB_OT_TABLE_ORDER(Namespace, Type) \
+    HB_PASTE (ORDER_, HB_PASTE (Namespace, HB_PASTE (_, Type)))
+  enum order_t
+  {
+    ORDER_ZERO,
+#define HB_OT_TABLE(Namespace, Type) HB_OT_TABLE_ORDER (Namespace, Type),
+#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type)
+    HB_OT_TABLES
+#undef HB_OT_ACCELERATOR
+#undef HB_OT_TABLE
+  };
+
+  hb_face_t *face; /* MUST be JUST before the lazy loaders. */
+#define HB_OT_TABLE(Namespace, Type) \
+  hb_table_lazy_loader_t<Namespace::Type, HB_OT_TABLE_ORDER (Namespace, Type)> Type;
+#define HB_OT_ACCELERATOR(Namespace, Type) \
+  hb_face_lazy_loader_t<Namespace::Type##_accelerator_t, HB_OT_TABLE_ORDER (Namespace, Type)> Type;
+  HB_OT_TABLES
+#undef HB_OT_ACCELERATOR
+#undef HB_OT_TABLE
+};
+
+
+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 9da8fc7..e6df038 100644
--- a/src/hb-ot-font.cc
+++ b/src/hb-ot-font.cc
@@ -24,94 +24,54 @@
  * Google Author(s): Behdad Esfahbod, Roozbeh Pournader
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb-ot.h"
 
-#include "hb-font-private.hh"
-#include "hb-machinery-private.hh"
+#include "hb-font.hh"
+#include "hb-machinery.hh"
+#include "hb-ot-face.hh"
 
 #include "hb-ot-cmap-table.hh"
-#include "hb-ot-glyf-table.hh"
 #include "hb-ot-hmtx-table.hh"
 #include "hb-ot-kern-table.hh"
 #include "hb-ot-post-table.hh"
-
+#include "hb-ot-glyf-table.hh"
 #include "hb-ot-color-cbdt-table.hh"
 
 
-struct hb_ot_font_t
-{
-  inline void init (hb_face_t *face)
-  {
-    cmap.init (face);
-    h_metrics.init (face);
-    v_metrics.init (face, h_metrics.ascender - h_metrics.descender); /* TODO Can we do this lazily? */
-
-    this->face = face;
-    glyf.init ();
-    cbdt.init ();
-    post.init ();
-    kern.init ();
-  }
-  inline void fini (void)
-  {
-    cmap.fini ();
-    h_metrics.fini ();
-    v_metrics.fini ();
-
-    glyf.fini ();
-    cbdt.fini ();
-    post.fini ();
-    kern.fini ();
-  }
-
-  OT::cmap::accelerator_t cmap;
-  OT::hmtx::accelerator_t h_metrics;
-  OT::vmtx::accelerator_t v_metrics;
-
-  hb_face_t *face; /* MUST be JUST before the lazy loaders. */
-  hb_face_lazy_loader_t<1, OT::glyf::accelerator_t> glyf;
-  hb_face_lazy_loader_t<2, OT::CBDT::accelerator_t> cbdt;
-  hb_face_lazy_loader_t<3, OT::post::accelerator_t> post;
-  hb_face_lazy_loader_t<4, OT::kern::accelerator_t> kern;
-};
-
-
-static hb_ot_font_t *
-_hb_ot_font_create (hb_face_t *face)
-{
-  hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t));
-
-  if (unlikely (!ot_font))
-    return nullptr;
-
-  ot_font->init (face);
-
-  return ot_font;
-}
-
-static void
-_hb_ot_font_destroy (void *data)
-{
-  hb_ot_font_t *ot_font = (hb_ot_font_t *) data;
-
-  ot_font->fini ();
-
-  free (ot_font);
-}
-
-
 static hb_bool_t
 hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED,
 			 void *font_data,
 			 hb_codepoint_t unicode,
 			 hb_codepoint_t *glyph,
 			 void *user_data HB_UNUSED)
-
 {
-  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return ot_font->cmap.get_nominal_glyph (unicode, glyph);
+  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);
+}
+
+static unsigned int
+hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
+			  void *font_data,
+			  unsigned int count,
+			  const hb_codepoint_t *first_unicode,
+			  unsigned int unicode_stride,
+			  hb_codepoint_t *first_glyph,
+			  unsigned int glyph_stride,
+			  void *user_data HB_UNUSED)
+{
+  const hb_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;
 }
 
 static hb_bool_t
@@ -122,39 +82,77 @@
 			   hb_codepoint_t *glyph,
 			   void *user_data HB_UNUSED)
 {
-  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return ot_font->cmap.get_variation_glyph (unicode, variation_selector, glyph);
+  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);
 }
 
-static hb_position_t
-hb_ot_get_glyph_h_advance (hb_font_t *font,
-			   void *font_data,
-			   hb_codepoint_t glyph,
-			   void *user_data HB_UNUSED)
+static void
+hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
+			    unsigned count,
+			    const hb_codepoint_t *first_glyph,
+			    unsigned glyph_stride,
+			    hb_position_t *first_advance,
+			    unsigned advance_stride,
+			    void *user_data HB_UNUSED)
 {
-  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return font->em_scale_x (ot_font->h_metrics.get_advance (glyph, font));
+  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 ();
+
+  for (unsigned int i = 0; i < count; i++)
+  {
+    *first_advance = font->em_scale_x (hmtx.get_advance (*first_glyph, font));
+    first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+    first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+  }
 }
 
-static hb_position_t
-hb_ot_get_glyph_v_advance (hb_font_t *font,
-			   void *font_data,
-			   hb_codepoint_t glyph,
-			   void *user_data HB_UNUSED)
+static void
+hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
+			    unsigned count,
+			    const hb_codepoint_t *first_glyph,
+			    unsigned glyph_stride,
+			    hb_position_t *first_advance,
+			    unsigned advance_stride,
+			    void *user_data HB_UNUSED)
 {
-  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph, font));
+  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 ();
+
+  for (unsigned int i = 0; i < count; i++)
+  {
+    *first_advance = font->em_scale_y (-(int) vmtx.get_advance (*first_glyph, font));
+    first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+    first_advance = &StructAtOffset<hb_position_t> (first_advance, advance_stride);
+  }
 }
 
-static hb_position_t
-hb_ot_get_glyph_h_kerning (hb_font_t *font,
-			   void *font_data,
-			   hb_codepoint_t left_glyph,
-			   hb_codepoint_t right_glyph,
-			   void *user_data HB_UNUSED)
+static hb_bool_t
+hb_ot_get_glyph_v_origin (hb_font_t *font,
+			  void *font_data,
+			  hb_codepoint_t glyph,
+			  hb_position_t *x,
+			  hb_position_t *y,
+			  void *user_data HB_UNUSED)
 {
-  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return font->em_scale_x (ot_font->kern->get_h_kerning (left_glyph, right_glyph));
+  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_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::vmtx_accelerator_t &vmtx = *ot_face->vmtx.get ();
+    hb_position_t tsb = vmtx.get_side_bearing (glyph);
+    *y = font->em_scale_y (extents.y_bearing + tsb);
+    return true;
+  }
+
+  hb_font_extents_t font_extents;
+  font->get_h_extents_with_fallback (&font_extents);
+  *y = font_extents.ascender;
+
+  return true;
 }
 
 static hb_bool_t
@@ -164,10 +162,10 @@
 			 hb_glyph_extents_t *extents,
 			 void *user_data HB_UNUSED)
 {
-  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  bool ret = ot_font->glyf->get_extents (glyph, extents);
+  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);
   if (!ret)
-    ret = ot_font->cbdt->get_extents (glyph, extents);
+    ret = ot_face->CBDT->get_extents (glyph, extents);
   // TODO Hook up side-bearings variations.
   extents->x_bearing = font->em_scale_x (extents->x_bearing);
   extents->y_bearing = font->em_scale_y (extents->y_bearing);
@@ -183,8 +181,8 @@
                       char *name, unsigned int size,
                       void *user_data HB_UNUSED)
 {
-  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return ot_font->post->get_glyph_name (glyph, name, size);
+  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
+  return ot_face->post->get_glyph_name (glyph, name, size);
 }
 
 static hb_bool_t
@@ -194,8 +192,8 @@
                            hb_codepoint_t *glyph,
                            void *user_data HB_UNUSED)
 {
-  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return ot_font->post->get_glyph_from_name (name, len, glyph);
+  const hb_ot_face_data_t *ot_face = (const hb_ot_face_data_t *) font_data;
+  return ot_face->post->get_glyph_from_name (name, len, glyph);
 }
 
 static hb_bool_t
@@ -204,12 +202,13 @@
 			  hb_font_extents_t *metrics,
 			  void *user_data HB_UNUSED)
 {
-  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  metrics->ascender = font->em_scale_y (ot_font->h_metrics.ascender);
-  metrics->descender = font->em_scale_y (ot_font->h_metrics.descender);
-  metrics->line_gap = font->em_scale_y (ot_font->h_metrics.line_gap);
+  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 ();
+  metrics->ascender = font->em_scale_y (hmtx.ascender);
+  metrics->descender = font->em_scale_y (hmtx.descender);
+  metrics->line_gap = font->em_scale_y (hmtx.line_gap);
   // TODO Hook up variations.
-  return ot_font->h_metrics.has_font_extents;
+  return hmtx.has_font_extents;
 }
 
 static hb_bool_t
@@ -218,16 +217,18 @@
 			  hb_font_extents_t *metrics,
 			  void *user_data HB_UNUSED)
 {
-  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  metrics->ascender = font->em_scale_x (ot_font->v_metrics.ascender);
-  metrics->descender = font->em_scale_x (ot_font->v_metrics.descender);
-  metrics->line_gap = font->em_scale_x (ot_font->v_metrics.line_gap);
+  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 ();
+  metrics->ascender = font->em_scale_x (vmtx.ascender);
+  metrics->descender = font->em_scale_x (vmtx.descender);
+  metrics->line_gap = font->em_scale_x (vmtx.line_gap);
   // TODO Hook up variations.
-  return ot_font->v_metrics.has_font_extents;
+  return vmtx.has_font_extents;
 }
 
-
+#ifdef HB_USE_ATEXIT
 static void free_static_ot_funcs (void);
+#endif
 
 static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ot_font_funcs_lazy_loader_t>
 {
@@ -238,13 +239,12 @@
     hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, nullptr, nullptr);
     hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr);
     hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, nullptr, nullptr);
+    hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ot_get_nominal_glyphs, nullptr, nullptr);
     hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, nullptr, nullptr);
-    hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ot_get_glyph_h_advance, nullptr, nullptr);
-    hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ot_get_glyph_v_advance, nullptr, nullptr);
+    hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ot_get_glyph_h_advances, nullptr, nullptr);
+    hb_font_funcs_set_glyph_v_advances_func (funcs, hb_ot_get_glyph_v_advances, nullptr, nullptr);
     //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, nullptr, nullptr);
-    //hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr);
-    hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ot_get_glyph_h_kerning, nullptr, nullptr);
-    //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ot_get_glyph_v_kerning, nullptr, nullptr);
+    hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr);
     hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, nullptr, nullptr);
     //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, nullptr, nullptr);
     hb_font_funcs_set_glyph_name_func (funcs, hb_ot_get_glyph_name, nullptr, nullptr);
@@ -283,12 +283,11 @@
 void
 hb_ot_font_set_funcs (hb_font_t *font)
 {
-  hb_ot_font_t *ot_font = _hb_ot_font_create (font->face);
-  if (unlikely (!ot_font))
-    return;
+  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_font,
-		     _hb_ot_font_destroy);
+		     ot_face,
+		     nullptr);
 }
diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh
index bcf89e4..2145ac0 100644
--- a/src/hb-ot-glyf-table.hh
+++ b/src/hb-ot-glyf-table.hh
@@ -27,11 +27,9 @@
 #ifndef HB_OT_GLYF_TABLE_HH
 #define HB_OT_GLYF_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 #include "hb-ot-head-table.hh"
 #include "hb-subset-glyf.hh"
-#include "hb-subset-plan.hh"
-#include "hb-subset-private.hh"
 
 namespace OT {
 
@@ -56,7 +54,7 @@
   }
 
   protected:
-  HBUINT8		dataZ[VAR];		/* Location data. */
+  UnsizedArrayOf<HBUINT8>	dataZ;		/* Location data. */
   DEFINE_SIZE_ARRAY (0, dataZ);
 };
 
@@ -377,13 +375,13 @@
 
       if (short_offset)
       {
-        const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ;
+        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;
+        const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ;
 
 	*start_offset = offsets[glyph];
 	*end_offset   = offsets[glyph + 1];
@@ -420,7 +418,7 @@
         } while (composite_it.move_to_next());
 
         if ( (uint16_t) last->flags & CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS)
-          *instruction_start = ((char *) last - (char *) glyf_table->dataZ) + last->get_size();
+          *instruction_start = ((char *) last - (char *) glyf_table->dataZ.arrayZ) + last->get_size();
         else
           *instruction_start = end_offset;
         *instruction_end = end_offset;
@@ -485,11 +483,13 @@
   };
 
   protected:
-  HBUINT8		dataZ[VAR];		/* Glyphs data. */
+  UnsizedArrayOf<HBUINT8>	dataZ;		/* Glyphs data. */
 
   DEFINE_SIZE_ARRAY (0, dataZ);
 };
 
+struct glyf_accelerator_t : glyf::accelerator_t {};
+
 } /* namespace OT */
 
 
diff --git a/src/hb-ot-hdmx-table.hh b/src/hb-ot-hdmx-table.hh
index 0951871..04511b5 100644
--- a/src/hb-ot-hdmx-table.hh
+++ b/src/hb-ot-hdmx-table.hh
@@ -27,8 +27,7 @@
 #ifndef HB_OT_HDMX_TABLE_HH
 #define HB_OT_HDMX_TABLE_HH
 
-#include "hb-open-type-private.hh"
-#include "hb-subset-plan.hh"
+#include "hb-open-type.hh"
 
 /*
  * hdmx -- Horizontal Device Metrics
@@ -67,7 +66,7 @@
       if (unlikely (i >= len())) return nullptr;
       hb_codepoint_t gid = this->subset_plan->glyphs [i];
 
-      const HBUINT8* width = &(this->source_device_record->widths[gid]);
+      const HBUINT8* width = &(this->source_device_record->widthsZ[gid]);
 
       if (width < ((const HBUINT8 *) this->source_device_record) + size_device_record)
 	return width;
@@ -78,19 +77,20 @@
 
   static inline unsigned int get_size (unsigned int count)
   {
-    unsigned int raw_size = min_size + count * HBUINT8::static_size;
-    if (raw_size % 4)
-      /* Align to 32 bits */
-      return raw_size + (4 - (raw_size % 4));
-    return raw_size;
+    return hb_ceil_to_4 (min_size + count * HBUINT8::static_size);
   }
 
   inline bool serialize (hb_serialize_context_t *c, const SubsetView &subset_view)
   {
     TRACE_SERIALIZE (this);
 
-    if (unlikely (!c->allocate_size<DeviceRecord> (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);
       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);
@@ -103,7 +103,7 @@
 	DEBUG_MSG(SUBSET, nullptr, "HDMX width for new gid %d is missing.", i);
 	return_trace (false);
       }
-      widths[i].set (*width);
+      widthsZ[i].set (*width);
     }
 
     return_trace (true);
@@ -116,11 +116,11 @@
 			  c->check_range (this, size_device_record)));
   }
 
-  HBUINT8 pixel_size;   /* Pixel size for following widths (as ppem). */
-  HBUINT8 max_width;    /* Maximum width. */
-  HBUINT8 widths[VAR];  /* Array of widths (numGlyphs is from the 'maxp' table). */
+  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). */
   public:
-  DEFINE_SIZE_ARRAY (2, widths);
+  DEFINE_SIZE_ARRAY (2, widthsZ);
 };
 
 
@@ -136,7 +136,7 @@
   inline const DeviceRecord& operator [] (unsigned int i) const
   {
     if (unlikely (i >= num_records)) return Null(DeviceRecord);
-    return StructAtOffset<DeviceRecord> (this->data, i * size_device_record);
+    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)
@@ -161,14 +161,14 @@
     return_trace (true);
   }
 
-  static inline size_t get_subsetted_size (hb_subset_plan_t *plan)
+  static inline size_t get_subsetted_size (const hdmx *source_hdmx, hb_subset_plan_t *plan)
   {
-    return min_size + DeviceRecord::get_size (plan->glyphs.len);
+    return min_size + source_hdmx->num_records * DeviceRecord::get_size (plan->glyphs.len);
   }
 
   inline bool subset (hb_subset_plan_t *plan) const
   {
-    size_t dest_size = get_subsetted_size (plan);
+    size_t dest_size = get_subsetted_size (this, plan);
     hdmx *dest = (hdmx *) malloc (dest_size);
     if (unlikely (!dest))
     {
@@ -178,8 +178,10 @@
 
     hb_serialize_context_t c (dest, dest_size);
     hdmx *hdmx_prime = c.start_serialize<hdmx> ();
-    if (!hdmx_prime || !hdmx_prime->serialize (&c, this, plan)) {
+    if (!hdmx_prime || !hdmx_prime->serialize (&c, this, plan))
+    {
       free (dest);
+      DEBUG_MSG(SUBSET, nullptr, "Failed to serialize write new hdmx.");
       return false;
     }
     c.end_serialize ();
@@ -205,12 +207,12 @@
   }
 
   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. */
-  HBUINT8	data[VAR];		/* Array of device records. */
+  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. */
   public:
-  DEFINE_SIZE_ARRAY (8, data);
+  DEFINE_SIZE_ARRAY (8, dataZ);
 };
 
 } /* namespace OT */
diff --git a/src/hb-ot-head-table.hh b/src/hb-ot-head-table.hh
index fded120..602e365 100644
--- a/src/hb-ot-head-table.hh
+++ b/src/hb-ot-head-table.hh
@@ -29,7 +29,7 @@
 #ifndef HB_OT_HEAD_TABLE_HH
 #define HB_OT_HEAD_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
 /*
  * head -- Font Header
diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh
index efb42b6..3336cad 100644
--- a/src/hb-ot-hhea-table.hh
+++ b/src/hb-ot-hhea-table.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OT_HHEA_TABLE_HH
 #define HB_OT_HHEA_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
 /*
  * hhea -- Horizontal Header
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index 13fa9d6..5293fda 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -27,11 +27,10 @@
 #ifndef HB_OT_HMTX_TABLE_HH
 #define HB_OT_HMTX_TABLE_HH
 
-#include "hb-open-type-private.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"
-#include "hb-subset-plan.hh"
 
 /*
  * hmtx -- Horizontal Metrics
@@ -49,7 +48,7 @@
 struct LongMetric
 {
   UFWORD	advance; /* Advance width/height. */
-  FWORD		lsb; /* Leading (left/top) side bearing. */
+  FWORD		sb; /* Leading (left/top) side bearing. */
   public:
   DEFINE_SIZE_STATIC (4);
 };
@@ -135,8 +134,8 @@
         }
         else
         {
-          /* dest just lsb */
-          *((FWORD *) dest_pos) = src_metric->lsb;
+          /* dest just sb */
+          *((FWORD *) dest_pos) = src_metric->sb;
         }
       }
       else
@@ -148,18 +147,18 @@
 	  failed = true;
 	  break;
 	}
-	FWORD src_lsb = *(lsbs + gids[i] - _mtx.num_advances);
+	FWORD src_sb = *(lsbs + gids[i] - _mtx.num_advances);
         if (i < num_advances)
         {
           /* dest needs a full LongMetric */
           LongMetric *metric = (LongMetric *)dest_pos;
           metric->advance = src_metric->advance;
-          metric->lsb = src_lsb;
+          metric->sb = src_sb;
         }
         else
         {
-          /* dest just needs an lsb */
-          *((FWORD *) dest_pos) = src_lsb;
+          /* dest just needs an sb */
+          *((FWORD *) dest_pos) = src_sb;
         }
       }
       dest_pos += (i < num_advances ? 4 : 2);
@@ -250,20 +249,33 @@
       hb_blob_destroy (var_blob);
     }
 
-    inline unsigned int get_advance (hb_codepoint_t  glyph) const
+    /* TODO Add variations version. */
+    inline unsigned int get_side_bearing (hb_codepoint_t glyph) const
+    {
+      if (glyph < num_advances)
+        return table->longMetricZ[glyph].sb;
+
+      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
     {
       if (unlikely (glyph >= num_metrics))
       {
-        /* If num_metrics is zero, it means we don't have the metrics table
-         * for this direction: return default advance.  Otherwise, it means that the
-         * glyph index is out of bound: return zero. */
-        if (num_metrics)
-          return 0;
-        else
-          return default_advance;
+	/* If num_metrics is zero, it means we don't have the metrics table
+	 * for this direction: return default advance.  Otherwise, it means that the
+	 * glyph index is out of bound: return zero. */
+	if (num_metrics)
+	  return 0;
+	else
+	  return default_advance;
       }
 
-      return table->longMetric[MIN (glyph, (uint32_t) num_advances - 1)].advance;
+      return table->longMetricZ[MIN (glyph, (uint32_t) num_advances - 1)].advance;
     }
 
     inline unsigned int get_advance (hb_codepoint_t  glyph,
@@ -272,7 +284,7 @@
       unsigned int advance = get_advance (glyph);
       if (likely(glyph < num_metrics))
       {
-        advance += (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?!
+	advance += (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?!
       }
       return advance;
     }
@@ -296,7 +308,7 @@
   };
 
   protected:
-  LongMetric	longMetric[VAR];	/* Paired advance width and leading
+  UnsizedArrayOf<LongMetric>longMetricZ;/* Paired advance width and leading
 					 * bearing values for each glyph. The
 					 * value numOfHMetrics comes from
 					 * the 'hhea' table. If the font is
@@ -304,7 +316,7 @@
 					 * be in the array, but that entry is
 					 * required. The last entry applies to
 					 * all subsequent glyphs. */
-/*FWORD		leadingBearingX[VAR];*/	/* Here the advance is assumed
+/*UnsizedArrayOf<FWORD>	leadingBearingX;*//* Here the advance is assumed
 					 * to be the same as the advance
 					 * for the last entry above. The
 					 * number of entries in this array is
@@ -318,7 +330,7 @@
 					 * font to vary the side bearing
 					 * values for each glyph. */
   public:
-  DEFINE_SIZE_ARRAY (0, longMetric);
+  DEFINE_SIZE_ARRAY (0, longMetricZ);
 };
 
 struct hmtx : hmtxvmtx<hmtx, hhea> {
@@ -332,6 +344,9 @@
   static const hb_tag_t os2Tag		= HB_TAG_NONE;
 };
 
+struct hmtx_accelerator_t : hmtx::accelerator_t {};
+struct vmtx_accelerator_t : vmtx::accelerator_t {};
+
 } /* namespace OT */
 
 
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index b4d8109..63551d3 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -27,7 +27,89 @@
 #ifndef HB_OT_KERN_TABLE_HH
 #define HB_OT_KERN_TABLE_HH
 
-#include "hb-open-type-private.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;
+};
+
 
 /*
  * kern -- Kerning
@@ -116,14 +198,18 @@
 {
   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 * rowWidth + r * sizeof (FWORD);
-    const FWORD *arr = &(this+array);
-    if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end))
-      return 0;
-    const FWORD *v = &StructAtOffset<FWORD> (arr, offset);
-    if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end))
+    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;
   }
@@ -131,6 +217,7 @@
   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) &&
@@ -190,10 +277,10 @@
   inline const T* thiz (void) const { return static_cast<const T *> (this); }
 
   inline bool is_horizontal (void) const
-  { return (thiz()->coverage & T::COVERAGE_CHECK_FLAGS) == T::COVERAGE_CHECK_HORIZONTAL; }
+  { return (thiz()->coverage & T::CheckFlags) == T::CheckHorizontal; }
 
   inline bool is_override (void) const
-  { return bool (thiz()->coverage & T::COVERAGE_OVERRIDE_FLAG); }
+  { 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); }
@@ -208,7 +295,7 @@
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (thiz()) &&
 		  thiz()->length >= T::min_size &&
-		  c->check_array (thiz(), 1, thiz()->length) &&
+		  c->check_range (thiz(), thiz()->length) &&
 		  thiz()->subtable.sanitize (c, thiz()->format));
   }
 };
@@ -219,16 +306,16 @@
   /* 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, unsigned int table_length) const
+  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()->data);
+    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, table_length + (const char *) this);
+      v += st->get_h_kerning (left, right, st->length + (const char *) st);
       st = &StructAfter<typename T::SubTableWrapper> (*st);
     }
     return v;
@@ -241,7 +328,7 @@
 		  thiz()->version != T::VERSION))
       return_trace (false);
 
-    const typename T::SubTableWrapper *st = CastP<typename T::SubTableWrapper> (thiz()->data);
+    const typename T::SubTableWrapper *st = CastP<typename T::SubTableWrapper> (&thiz()->dataZ);
     unsigned int count = thiz()->nTables;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -262,18 +349,20 @@
 
   struct SubTableWrapper : KernSubTableWrapper<SubTableWrapper>
   {
+    friend struct KernTable<KernOT>;
     friend struct KernSubTableWrapper<SubTableWrapper>;
 
-    enum coverage_flags_t {
-      COVERAGE_DIRECTION_FLAG	= 0x01u,
-      COVERAGE_MINIMUM_FLAG	= 0x02u,
-      COVERAGE_CROSSSTREAM_FLAG	= 0x04u,
-      COVERAGE_OVERRIDE_FLAG	= 0x08u,
+    enum Coverage
+    {
+      Direction		= 0x01u,
+      Minimum		= 0x02u,
+      CrossStream	= 0x04u,
+      Override		= 0x08u,
 
-      COVERAGE_VARIATION_FLAG	= 0x00u, /* Not supported. */
+      Variation		= 0x00u, /* Not supported. */
 
-      COVERAGE_CHECK_FLAGS	= 0x07u,
-      COVERAGE_CHECK_HORIZONTAL	= 0x01u
+      CheckFlags	= 0x07u,
+      CheckHorizontal	= 0x01u
     };
 
     protected:
@@ -287,11 +376,11 @@
   };
 
   protected:
-  HBUINT16	version;	/* Version--0x0000u */
-  HBUINT16	nTables;	/* Number of subtables in the kerning table. */
-  HBUINT8		data[VAR];
+  HBUINT16			version;	/* Version--0x0000u */
+  HBUINT16			nTables;	/* Number of subtables in the kerning table. */
+  UnsizedArrayOf<HBUINT8>	dataZ;
   public:
-  DEFINE_SIZE_ARRAY (4, data);
+  DEFINE_SIZE_ARRAY (4, dataZ);
 };
 
 struct KernAAT : KernTable<KernAAT>
@@ -302,17 +391,19 @@
 
   struct SubTableWrapper : KernSubTableWrapper<SubTableWrapper>
   {
+    friend struct KernTable<KernAAT>;
     friend struct KernSubTableWrapper<SubTableWrapper>;
 
-    enum coverage_flags_t {
-      COVERAGE_DIRECTION_FLAG	= 0x80u,
-      COVERAGE_CROSSSTREAM_FLAG	= 0x40u,
-      COVERAGE_VARIATION_FLAG	= 0x20u,
+    enum Coverage
+    {
+      Direction		= 0x80u,
+      CrossStream	= 0x40u,
+      Variation		= 0x20u,
 
-      COVERAGE_OVERRIDE_FLAG	= 0x00u, /* Not supported. */
+      Override		= 0x00u, /* Not supported. */
 
-      COVERAGE_CHECK_FLAGS	= 0xE0u,
-      COVERAGE_CHECK_HORIZONTAL	= 0x00u
+      CheckFlags	= 0xE0u,
+      CheckHorizontal	= 0x00u
     };
 
     protected:
@@ -327,22 +418,25 @@
   };
 
   protected:
-  HBUINT32		version;	/* Version--0x00010000u */
-  HBUINT32		nTables;	/* Number of subtables in the kerning table. */
-  HBUINT8		data[VAR];
+  HBUINT32			version;	/* Version--0x00010000u */
+  HBUINT32			nTables;	/* Number of subtables in the kerning table. */
+  UnsizedArrayOf<HBUINT8>	dataZ;
   public:
-  DEFINE_SIZE_ARRAY (8, data);
+  DEFINE_SIZE_ARRAY (8, dataZ);
 };
 
 struct kern
 {
   static const hb_tag_t tableTag = HB_OT_TAG_kern;
 
-  inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const
+  inline bool has_data (void) const
+  { return u.version32 != 0; }
+
+  inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
   {
     switch (u.major) {
-    case 0: return u.ot.get_h_kerning (left, right, table_length);
-    case 1: return u.aat.get_h_kerning (left, right, table_length);
+    case 0: return u.ot.get_h_kerning (left, right);
+    case 1: return u.aat.get_h_kerning (left, right);
     default:return 0;
     }
   }
@@ -350,7 +444,7 @@
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!u.major.sanitize (c)) return_trace (false);
+    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));
@@ -364,32 +458,52 @@
     {
       blob = hb_sanitize_context_t().reference_table<kern> (face);
       table = blob->as<kern> ();
-      table_length = blob->length;
     }
     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, table_length); }
+    { 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;
-    unsigned int table_length;
   };
 
   protected:
   union {
+  HBUINT32		version32;
   HBUINT16		major;
   KernOT		ot;
   KernAAT		aat;
   } u;
   public:
-  DEFINE_SIZE_UNION (2, major);
+  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 96da07f..449e745 100644
--- a/src/hb-ot-layout-base-table.hh
+++ b/src/hb-ot-layout-base-table.hh
@@ -28,8 +28,8 @@
 #ifndef HB_OT_LAYOUT_BASE_TABLE_HH
 #define HB_OT_LAYOUT_BASE_TABLE_HH
 
-#include "hb-open-type-private.hh"
-#include "hb-ot-layout-common-private.hh"
+#include "hb-open-type.hh"
+#include "hb-ot-layout-common.hh"
 
 namespace OT {
 
diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common.hh
similarity index 84%
rename from src/hb-ot-layout-common-private.hh
rename to src/hb-ot-layout-common.hh
index 89d5eae..98f6a07 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common.hh
@@ -26,13 +26,13 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH
-#define HB_OT_LAYOUT_COMMON_PRIVATE_HH
+#ifndef HB_OT_LAYOUT_COMMON_HH
+#define HB_OT_LAYOUT_COMMON_HH
 
-#include "hb-private.hh"
-#include "hb-ot-layout-private.hh"
-#include "hb-open-type-private.hh"
-#include "hb-set-private.hh"
+#include "hb.hh"
+#include "hb-ot-layout.hh"
+#include "hb-open-type.hh"
+#include "hb-set.hh"
 
 
 #ifndef HB_MAX_NESTING_LEVEL
@@ -70,6 +70,11 @@
  * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList
  */
 
+struct Record_sanitize_closure_t {
+  hb_tag_t tag;
+  const void *list_base;
+};
+
 template <typename Type>
 struct Record
 {
@@ -77,14 +82,10 @@
     return tag.cmp (a);
   }
 
-  struct sanitize_closure_t {
-    hb_tag_t tag;
-    const void *list_base;
-  };
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
-    const sanitize_closure_t closure = {tag, base};
+    const Record_sanitize_closure_t closure = {tag, base};
     return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure));
   }
 
@@ -97,15 +98,14 @@
 };
 
 template <typename Type>
-struct RecordArrayOf : SortedArrayOf<Record<Type> > {
+struct RecordArrayOf : SortedArrayOf<Record<Type> >
+{
+  inline const OffsetTo<Type>& get_offset (unsigned int i) const
+  { return (*this)[i].offset; }
+  inline OffsetTo<Type>& get_offset (unsigned int i)
+  { return (*this)[i].offset; }
   inline const Tag& get_tag (unsigned int i) const
-  {
-    /* We cheat slightly and don't define separate Null objects
-     * for Record types.  Instead, we return the correct Null(Tag)
-     * here. */
-    if (unlikely (i >= this->len)) return Null(Tag);
-    return (*this)[i].tag;
-  }
+  { 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
@@ -136,7 +136,18 @@
 struct RecordListOf : RecordArrayOf<Type>
 {
   inline const Type& operator [] (unsigned int i) const
-  { return this+RecordArrayOf<Type>::operator [](i).offset; }
+  { return this+this->get_offset (i); }
+
+  inline bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    struct RecordListOf<Type> *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+    unsigned int count = this->len;
+    for (unsigned int i = 0; i < count; i++)
+      out->get_offset (i).serialize_subset (c, (*this)[i], out);
+    return_trace (true);
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -158,9 +169,8 @@
     return_trace (c->check_struct (this));
   }
 
-  inline bool intersects (const hb_set_t *glyphs) const {
-    return glyphs->intersects (start, end);
-  }
+  inline 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 {
@@ -224,8 +234,14 @@
    return reqFeatureIndex;;
   }
 
+  inline 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<LangSys>::sanitize_closure_t * = nullptr) const
+			const Record_sanitize_closure_t * = nullptr) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && featureIndex.sanitize (c));
@@ -238,7 +254,7 @@
 				 * = 0xFFFFu */
   IndexArray	featureIndex;	/* Array of indices into the FeatureList */
   public:
-  DEFINE_SIZE_ARRAY (6, featureIndex);
+  DEFINE_SIZE_ARRAY_SIZED (6, featureIndex);
 };
 DECLARE_NULL_NAMESPACE_BYTES (OT, LangSys);
 
@@ -263,8 +279,20 @@
   inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; }
   inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
 
+  inline bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    struct Script *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+    out->defaultLangSys.serialize_subset (c, this+defaultLangSys, out);
+    unsigned int count = langSys.len;
+    for (unsigned int i = 0; i < count; i++)
+      out->langSys.arrayZ[i].offset.serialize_subset (c, this+langSys[i].offset, out);
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<Script>::sanitize_closure_t * = nullptr) const
+			const Record_sanitize_closure_t * = nullptr) const
   {
     TRACE_SANITIZE (this);
     return_trace (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
@@ -278,7 +306,7 @@
 		langSys;	/* Array of LangSysRecords--listed
 				 * alphabetically by LangSysTag */
   public:
-  DEFINE_SIZE_ARRAY (4, langSys);
+  DEFINE_SIZE_ARRAY_SIZED (4, langSys);
 };
 
 typedef RecordListOf<Script> ScriptList;
@@ -372,7 +400,7 @@
 				 * same subfamily value. If this value is
 				 * zero, the remaining fields in the array
 				 * will be ignored. */
-  HBUINT16	subfamilyNameID;/* If the preceding value is non-zero, this
+  NameID	subfamilyNameID;/* If the preceding value is non-zero, this
 				 * value must be set in the range 256 - 32767
 				 * (inclusive). It records the value of a
 				 * field in the name table, which must
@@ -445,7 +473,7 @@
 					 * specifies a string (or strings,
 					 * for multiple languages) for a
 					 * user-interface label for this
-					 * feature. (May be nullptr.) */
+					 * feature. (May be NULL.) */
   NameID	featUITooltipTextNameID;/* The ‘name’ table name ID that
 					 * specifies a string (or strings,
 					 * for multiple languages) that an
@@ -455,7 +483,7 @@
   NameID	sampleTextNameID;	/* The ‘name’ table name ID that
 					 * specifies sample text that
 					 * illustrates the effect of this
-					 * feature. (May be nullptr.) */
+					 * feature. (May be NULL.) */
   HBUINT16	numNamedParameters;	/* Number of named parameters. (May
 					 * be zero.) */
   NameID	firstParamUILabelNameID;/* The first ‘name’ table name ID
@@ -493,12 +521,27 @@
     return Null(FeatureParamsSize);
   }
 
+  inline 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);
+  }
+
+  inline 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);
+  }
+
   private:
   union {
   FeatureParamsSize			size;
   FeatureParamsStylisticSet		stylisticSet;
   FeatureParamsCharacterVariants	characterVariants;
   } u;
+  public:
   DEFINE_SIZE_STATIC (17);
 };
 
@@ -516,8 +559,17 @@
   inline const FeatureParams &get_feature_params (void) const
   { return this+featureParams; }
 
+  inline bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    struct Feature *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+    out->featureParams.set (0); /* TODO(subset) FeatureParams. */
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<Feature>::sanitize_closure_t *closure = nullptr) const
+			const Record_sanitize_closure_t *closure = nullptr) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c))))
@@ -567,7 +619,7 @@
 				 * if not required */
   IndexArray	 lookupIndex;	/* Array of LookupList indices */
   public:
-  DEFINE_SIZE_ARRAY (4, lookupIndex);
+  DEFINE_SIZE_ARRAY_SIZED (4, lookupIndex);
 };
 
 typedef RecordListOf<Feature> FeatureList;
@@ -598,16 +650,16 @@
 {
   inline unsigned int get_subtable_count (void) const { return subTable.len; }
 
-  template <typename SubTableType>
-  inline const SubTableType& get_subtable (unsigned int i) const
-  { return this+CastR<OffsetArrayOf<SubTableType> > (subTable)[i]; }
+  template <typename TSubTable>
+  inline const TSubTable& get_subtable (unsigned int i) const
+  { return this+CastR<OffsetArrayOf<TSubTable> > (subTable)[i]; }
 
-  template <typename SubTableType>
-  inline const OffsetArrayOf<SubTableType>& get_subtables (void) const
-  { return CastR<OffsetArrayOf<SubTableType> > (subTable); }
-  template <typename SubTableType>
-  inline OffsetArrayOf<SubTableType>& get_subtables (void)
-  { return CastR<OffsetArrayOf<SubTableType> > (subTable); }
+  template <typename TSubTable>
+  inline const OffsetArrayOf<TSubTable>& get_subtables (void) const
+  { return CastR<OffsetArrayOf<TSubTable> > (subTable); }
+  template <typename TSubTable>
+  inline OffsetArrayOf<TSubTable>& get_subtables (void)
+  { return CastR<OffsetArrayOf<TSubTable> > (subTable); }
 
   inline unsigned int get_size (void) const
   {
@@ -633,14 +685,14 @@
     return flag;
   }
 
-  template <typename SubTableType, typename context_t>
+  template <typename TSubTable, typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     unsigned int lookup_type = get_type ();
     TRACE_DISPATCH (this, lookup_type);
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++) {
-      typename context_t::return_t r = get_subtable<SubTableType> (i).dispatch (c, lookup_type);
+      typename context_t::return_t r = get_subtable<TSubTable> (i).dispatch (c, lookup_type);
       if (c->stop_sublookup_iteration (r))
         return_trace (r);
     }
@@ -666,16 +718,72 @@
     return_trace (true);
   }
 
+  /* Older compilers need this to NOT be locally defined in a function. */
+  template <typename TSubTable>
+  struct SubTableSubsetWrapper
+  {
+    inline SubTableSubsetWrapper (const TSubTable &subtable_,
+				  unsigned int lookup_type_) :
+				    subtable (subtable_),
+				    lookup_type (lookup_type_) {}
+
+    inline bool subset (hb_subset_context_t *c) const
+    { return subtable.dispatch (c, lookup_type); }
+
+    private:
+    const TSubTable &subtable;
+    unsigned int lookup_type;
+  };
+
+  template <typename TSubTable>
+  inline bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    struct Lookup *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    /* Subset the actual subtables. */
+    /* TODO Drop empty ones, either by calling intersects() beforehand,
+     * or just dropping null offsets after. */
+    const OffsetArrayOf<TSubTable>& subtables = get_subtables<TSubTable> ();
+    OffsetArrayOf<TSubTable>& out_subtables = out->get_subtables<TSubTable> ();
+    unsigned int count = subTable.len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      SubTableSubsetWrapper<TSubTable> wrapper (this+subtables[i], get_type ());
+
+      out_subtables[i].serialize_subset (c, wrapper, out);
+    }
+
+    return_trace (true);
+  }
+
+  template <typename TSubTable>
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    /* Real sanitize of the subtables is done by GSUB/GPOS/... */
     if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false);
     if (lookupFlag & LookupFlag::UseMarkFilteringSet)
     {
       const HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable);
       if (!markFilteringSet.sanitize (c)) return_trace (false);
     }
+
+    if (unlikely (!dispatch<TSubTable> (c))) return_trace (false);
+
+    if (unlikely (get_type () == TSubTable::Extension))
+    {
+      /* The spec says all subtables of an Extension lookup should
+       * have the same type, which shall not be the Extension type
+       * itself (but we already checked for that).
+       * This is specially important if one has a reverse type! */
+      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)
+	  return_trace (false);
+    }
+    return_trace (true);
     return_trace (true);
   }
 
@@ -730,9 +838,17 @@
     return_trace (glyphArray.sanitize (c));
   }
 
-  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
-    return glyphs->has (glyphArray[index]);
+  inline 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 false;
   }
+  inline 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 {
@@ -743,6 +859,7 @@
   /* 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]; }
@@ -819,7 +936,17 @@
     return_trace (rangeRecord.sanitize (c));
   }
 
-  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
+  inline 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 false;
+  }
+  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+  {
     unsigned int i;
     unsigned int count = rangeRecord.len;
     for (i = 0; i < count; i++) {
@@ -859,6 +986,7 @@
         i = c->rangeRecord.len;
       }
     }
+    inline void fini (void) {};
     inline bool more (void) { return i < c->rangeRecord.len; }
     inline void next (void)
     {
@@ -924,7 +1052,8 @@
       if (glyphs[i - 1] + 1 != glyphs[i])
         num_ranges++;
     u.format.set (num_glyphs * 2 < num_ranges * 3 ? 1 : 2);
-    switch (u.format) {
+    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));
     default:return_trace (false);
@@ -935,25 +1064,27 @@
   {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return_trace (false);
-    switch (u.format) {
+    switch (u.format)
+    {
     case 1: return_trace (u.format1.sanitize (c));
     case 2: return_trace (u.format2.sanitize (c));
     default:return_trace (true);
     }
   }
 
-  inline bool intersects (const hb_set_t *glyphs) const {
-    /* TODO speed this up */
-    Coverage::Iter iter;
-    for (iter.init (*this); iter.more (); iter.next ()) {
-      if (glyphs->has (iter.get_glyph ()))
-        return true;
+  inline 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;
     }
-    return false;
   }
-
-  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
-    switch (u.format) {
+  inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+  {
+    switch (u.format)
+    {
     case 1: return u.format1.intersects_coverage (glyphs, index);
     case 2: return u.format2.intersects_coverage (glyphs, index);
     default:return false;
@@ -963,47 +1094,61 @@
   /* 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 {
-    switch (u.format) {
+  inline 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);
     default:return false;
     }
   }
 
-  struct Iter {
+  struct Iter
+  {
     Iter (void) : format (0), u () {};
-    inline void init (const Coverage &c_) {
+    inline void init (const Coverage &c_)
+    {
       format = c_.u.format;
-      switch (format) {
+      switch (format)
+      {
       case 1: u.format1.init (c_.u.format1); return;
       case 2: u.format2.init (c_.u.format2); return;
-      default:                               return;
+      default:				     return;
       }
     }
-    inline bool more (void) {
-      switch (format) {
+    inline void fini (void) {}
+    inline bool more (void)
+    {
+      switch (format)
+      {
       case 1: return u.format1.more ();
       case 2: return u.format2.more ();
       default:return false;
       }
     }
-    inline void next (void) {
-      switch (format) {
+    inline void next (void)
+    {
+      switch (format)
+      {
       case 1: u.format1.next (); break;
       case 2: u.format2.next (); break;
-      default:                   break;
+      default:			 break;
       }
     }
-    inline hb_codepoint_t get_glyph (void) {
-      switch (format) {
+    inline hb_codepoint_t get_glyph (void)
+    {
+      switch (format)
+      {
       case 1: return u.format1.get_glyph ();
       case 2: return u.format2.get_glyph ();
       default:return 0;
       }
     }
-    inline unsigned int get_coverage (void) {
-      switch (format) {
+    inline unsigned int get_coverage (void)
+    {
+      switch (format)
+      {
       case 1: return u.format1.get_coverage ();
       case 2: return u.format2.get_coverage ();
       default:return -1;
@@ -1085,6 +1230,17 @@
     return true;
   }
 
+  inline 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;
+    return false;
+  }
   inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
     unsigned int count = classValue.len;
     if (klass == 0)
@@ -1135,7 +1291,8 @@
   }
 
   template <typename set_t>
-  inline bool add_coverage (set_t *glyphs) const {
+  inline bool add_coverage (set_t *glyphs) const
+  {
     unsigned int count = rangeRecord.len;
     for (unsigned int i = 0; i < count; i++)
       if (rangeRecord[i].value)
@@ -1145,7 +1302,8 @@
   }
 
   template <typename set_t>
-  inline bool add_class (set_t *glyphs, unsigned int klass) const {
+  inline bool add_class (set_t *glyphs, unsigned int klass) const
+  {
     unsigned int count = rangeRecord.len;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -1156,7 +1314,17 @@
     return true;
   }
 
-  inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
+  inline 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 false;
+  }
+  inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const
+  {
     unsigned int count = rangeRecord.len;
     if (klass == 0)
     {
@@ -1233,6 +1401,13 @@
     }
   }
 
+  inline 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 {
     switch (u.format) {
     case 1: return u.format1.intersects_class (glyphs, klass);
@@ -1300,7 +1475,7 @@
 struct VarRegionList
 {
   inline float evaluate (unsigned int region_index,
-			 int *coords, unsigned int coord_len) const
+			 const int *coords, unsigned int coord_len) const
   {
     if (unlikely (region_index >= regionCount))
       return 0.;
@@ -1345,7 +1520,7 @@
   { return itemCount * get_row_size (); }
 
   inline float get_delta (unsigned int inner,
-			  int *coords, unsigned int coord_count,
+			  const int *coords, unsigned int coord_count,
 			  const VarRegionList &regions) const
   {
     if (unlikely (inner >= itemCount))
@@ -1383,14 +1558,14 @@
 		  regionIndices.sanitize(c) &&
 		  shortCount <= regionIndices.len &&
 		  c->check_array (&StructAfter<HBUINT8> (regionIndices),
-				  get_row_size (), itemCount));
+				  itemCount, get_row_size ()));
   }
 
   protected:
   HBUINT16		itemCount;
   HBUINT16		shortCount;
   ArrayOf<HBUINT16>	regionIndices;
-  HBUINT8		bytesX[VAR];
+  UnsizedArrayOf<HBUINT8>bytesX;
   public:
   DEFINE_SIZE_ARRAY2 (6, regionIndices, bytesX);
 };
@@ -1398,7 +1573,7 @@
 struct VariationStore
 {
   inline float get_delta (unsigned int outer, unsigned int inner,
-			  int *coords, unsigned int coord_count) const
+			  const int *coords, unsigned int coord_count) const
   {
     if (unlikely (outer >= dataSets.len))
       return 0.;
@@ -1409,7 +1584,7 @@
   }
 
   inline float get_delta (unsigned int index,
-			  int *coords, unsigned int coord_count) const
+			  const int *coords, unsigned int coord_count) const
   {
     unsigned int outer = index >> 16;
     unsigned int inner = index & 0xFFFF;
@@ -1584,7 +1759,7 @@
 
 struct FeatureVariations
 {
-  static const unsigned int NOT_FOUND_INDEX = 0xFFFFFFFFu;
+  enum { NOT_FOUND_INDEX = 0xFFFFFFFFu };
 
   inline bool find_index (const int *coords, unsigned int coord_len,
 			  unsigned int *index) const
@@ -1610,6 +1785,12 @@
     return (this+record.substitutions).find_substitute (feature_index);
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -1623,7 +1804,7 @@
   LArrayOf<FeatureVariationRecord>
 			varRecords;
   public:
-  DEFINE_SIZE_ARRAY (8, varRecords);
+  DEFINE_SIZE_ARRAY_SIZED (8, varRecords);
 };
 
 
@@ -1679,7 +1860,7 @@
 
     unsigned int s = ppem_size - startSize;
 
-    unsigned int byte = deltaValue[s >> (4 - f)];
+    unsigned int byte = deltaValueZ[s >> (4 - f)];
     unsigned int bits = (byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f)));
     unsigned int mask = (0xFFFFu >> (16 - (1 << f)));
 
@@ -1699,9 +1880,10 @@
 					 * 2	Signed 4-bit value, 4 values per uint16
 					 * 3	Signed 8-bit value, 2 values per uint16
 					 */
-  HBUINT16	deltaValue[VAR];	/* Array of compressed data */
+  UnsizedArrayOf<HBUINT16>
+		deltaValueZ;		/* Array of compressed data */
   public:
-  DEFINE_SIZE_ARRAY (6, deltaValue);
+  DEFINE_SIZE_ARRAY (6, deltaValueZ);
 };
 
 struct VariationDevice
@@ -1803,4 +1985,4 @@
 } /* namespace OT */
 
 
-#endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */
+#endif /* HB_OT_LAYOUT_COMMON_HH */
diff --git a/src/hb-ot-layout-gdef-table.hh b/src/hb-ot-layout-gdef-table.hh
index d2b41a8..7570908 100644
--- a/src/hb-ot-layout-gdef-table.hh
+++ b/src/hb-ot-layout-gdef-table.hh
@@ -29,9 +29,9 @@
 #ifndef HB_OT_LAYOUT_GDEF_TABLE_HH
 #define HB_OT_LAYOUT_GDEF_TABLE_HH
 
-#include "hb-ot-layout-common-private.hh"
+#include "hb-ot-layout-common.hh"
 
-#include "hb-font-private.hh"
+#include "hb-font.hh"
 
 
 namespace OT {
@@ -337,6 +337,7 @@
  * https://docs.microsoft.com/en-us/typography/opentype/spec/gdef
  */
 
+
 struct GDEF
 {
   static const hb_tag_t tableTag	= HB_OT_TAG_GDEF;
@@ -386,21 +387,8 @@
   inline const VariationStore &get_var_store (void) const
   { return version.to_int () >= 0x00010003u ? this+varStore : Null(VariationStore); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (version.sanitize (c) &&
-		  likely (version.major == 1) &&
-		  glyphClassDef.sanitize (c, this) &&
-		  attachList.sanitize (c, this) &&
-		  ligCaretList.sanitize (c, this) &&
-		  markAttachClassDef.sanitize (c, this) &&
-		  (version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) &&
-		  (version.to_int () < 0x00010003u || varStore.sanitize (c, this)));
-  }
-
   /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing
-   * glyph class and other bits, and high 8-bit gthe mark attachment type (if any).
+   * 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
   {
@@ -420,6 +408,38 @@
     }
   }
 
+  struct accelerator_t
+  {
+    HB_INTERNAL inline void init (hb_face_t *face);
+
+    inline void fini (void)
+    {
+      hb_blob_destroy (this->blob);
+    }
+
+    hb_blob_t *blob;
+    const GDEF *table;
+  };
+
+  inline unsigned int get_size (void) 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
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+		  likely (version.major == 1) &&
+		  glyphClassDef.sanitize (c, this) &&
+		  attachList.sanitize (c, this) &&
+		  ligCaretList.sanitize (c, this) &&
+		  markAttachClassDef.sanitize (c, this) &&
+		  (version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) &&
+		  (version.to_int () < 0x00010003u || varStore.sanitize (c, this)));
+  }
 
   protected:
   FixedVersion<>version;		/* Version of the GDEF table--currently
@@ -454,6 +474,7 @@
   DEFINE_SIZE_MIN (12);
 };
 
+struct GDEF_accelerator_t : GDEF::accelerator_t {};
 
 } /* namespace OT */
 
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index fe34a32..dad6c4e 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -29,7 +29,7 @@
 #ifndef HB_OT_LAYOUT_GPOS_TABLE_HH
 #define HB_OT_LAYOUT_GPOS_TABLE_HH
 
-#include "hb-ot-layout-gsubgpos-private.hh"
+#include "hb-ot-layout-gsubgpos.hh"
 
 
 namespace OT {
@@ -199,7 +199,7 @@
     TRACE_SANITIZE (this);
     unsigned int len = get_len ();
 
-    if (!c->check_array (values, get_size (), count)) return_trace (false);
+    if (!c->check_array (values, count, get_size ())) return_trace (false);
 
     if (!has_device ()) return_trace (true);
 
@@ -376,7 +376,7 @@
     if (!c->check_struct (this)) return_trace (false);
     if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
     unsigned int count = rows * cols;
-    if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return_trace (false);
+    if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false);
     for (unsigned int i = 0; i < count; i++)
       if (!matrixZ[i].sanitize (c, this)) return_trace (false);
     return_trace (true);
@@ -384,8 +384,8 @@
 
   HBUINT16	rows;			/* Number of rows */
   protected:
-  OffsetTo<Anchor>
-		matrixZ[VAR];		/* Matrix of offsets to Anchor tables--
+  UnsizedArrayOf<OffsetTo<Anchor> >
+		matrixZ;		/* Matrix of offsets to Anchor tables--
 					 * from beginning of AnchorMatrix table */
   public:
   DEFINE_SIZE_ARRAY (2, matrixZ);
@@ -459,6 +459,9 @@
 
 struct SinglePosFormat1
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
@@ -466,9 +469,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -483,6 +484,13 @@
     return_trace (true);
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -507,6 +515,9 @@
 
 struct SinglePosFormat2
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
@@ -514,9 +525,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -535,6 +544,13 @@
     return_trace (true);
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -598,6 +614,24 @@
 {
   friend struct PairPosFormat1;
 
+  inline bool intersects (const hb_set_t *glyphs,
+			  const ValueFormat *valueFormats) const
+  {
+    unsigned int len1 = valueFormats[0].get_len ();
+    unsigned int len2 = valueFormats[1].get_len ();
+    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (glyphs->has (record->secondGlyph))
+        return true;
+      record = &StructAtOffset<const PairValueRecord> (record, record_size);
+    }
+    return false;
+  }
+
   inline void collect_glyphs (hb_collect_glyphs_context_t *c,
 			      const ValueFormat *valueFormats) const
   {
@@ -606,7 +640,7 @@
     unsigned int len2 = valueFormats[1].get_len ();
     unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
 
-    const PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
+    const PairValueRecord *record = &firstPairValueRecord;
     c->input->add_array (&record->secondGlyph, len, record_size);
   }
 
@@ -620,7 +654,6 @@
     unsigned int len2 = valueFormats[1].get_len ();
     unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
 
-    const PairValueRecord *record_array = CastP<PairValueRecord> (arrayZ);
     unsigned int count = len;
 
     /* Hand-coded bsearch. */
@@ -631,7 +664,7 @@
     while (min <= max)
     {
       int mid = (min + max) / 2;
-      const PairValueRecord *record = &StructAtOffset<PairValueRecord> (record_array, record_size * mid);
+      const PairValueRecord *record = &StructAtOffset<PairValueRecord> (&firstPairValueRecord, record_size * mid);
       hb_codepoint_t mid_x = record->secondGlyph;
       if (x < mid_x)
         max = mid - 1;
@@ -652,7 +685,8 @@
     return_trace (false);
   }
 
-  struct sanitize_closure_t {
+  struct sanitize_closure_t
+  {
     const void *base;
     const ValueFormat *valueFormats;
     unsigned int len1; /* valueFormats[0].get_len() */
@@ -663,24 +697,39 @@
   {
     TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
-       && c->check_array (arrayZ, HBUINT16::static_size * closure->stride, len))) return_trace (false);
+       && c->check_array (&firstPairValueRecord, len, HBUINT16::static_size * closure->stride))) return_trace (false);
 
     unsigned int count = len;
-    const PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
+    const PairValueRecord *record = &firstPairValueRecord;
     return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) &&
 		  closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
   }
 
   protected:
-  HBUINT16	len;			/* Number of PairValueRecords */
-  HBUINT16	arrayZ[VAR];		/* Array of PairValueRecords--ordered
-					 * by GlyphID of the second glyph */
+  HBUINT16		len;	/* Number of PairValueRecords */
+  PairValueRecord	firstPairValueRecord;
+				/* Array of PairValueRecords--ordered
+				 * by GlyphID of the second glyph */
   public:
-  DEFINE_SIZE_ARRAY (2, arrayZ);
+  DEFINE_SIZE_MIN (2);
 };
 
 struct PairPosFormat1
 {
+  inline 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 ())
+    {
+      if (unlikely (iter.get_coverage () >= count))
+        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 false;
+  }
+
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
@@ -691,9 +740,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -709,6 +756,13 @@
     return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -717,7 +771,8 @@
 
     unsigned int len1 = valueFormat[0].get_len ();
     unsigned int len2 = valueFormat[1].get_len ();
-    PairSet::sanitize_closure_t closure = {
+    PairSet::sanitize_closure_t closure =
+    {
       this,
       valueFormat,
       len1,
@@ -747,6 +802,12 @@
 
 struct PairPosFormat2
 {
+  inline 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
   {
     TRACE_COLLECT_GLYPHS (this);
@@ -755,9 +816,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -790,6 +849,13 @@
     return_trace (true);
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -803,7 +869,7 @@
     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, record_size, count) &&
+    return_trace (c->check_array (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));
   }
@@ -889,6 +955,9 @@
 
 struct CursivePosFormat1
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
@@ -896,9 +965,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -906,22 +973,22 @@
     hb_buffer_t *buffer = c->buffer;
 
     const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (buffer->cur().codepoint)];
-    if (!this_record.exitAnchor) return_trace (false);
+    if (!this_record.entryAnchor) return_trace (false);
 
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
-    if (!skippy_iter.next ()) return_trace (false);
+    if (!skippy_iter.prev ()) return_trace (false);
 
-    const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint)];
-    if (!next_record.entryAnchor) return_trace (false);
+    const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint)];
+    if (!prev_record.exitAnchor) return_trace (false);
 
-    unsigned int i = buffer->idx;
-    unsigned int j = skippy_iter.idx;
+    unsigned int i = skippy_iter.idx;
+    unsigned int j = buffer->idx;
 
     buffer->unsafe_to_break (i, j);
     float entry_x, entry_y, exit_x, exit_y;
-    (this+this_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
-    (this+next_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
+    (this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
+    (this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
 
     hb_glyph_position_t *pos = buffer->pos;
 
@@ -968,7 +1035,7 @@
      * parent.
      *
      * Optimize things for the case of RightToLeft, as that's most common in
-     * Arabinc. */
+     * Arabic. */
     unsigned int child  = i;
     unsigned int parent = j;
     hb_position_t x_offset = entry_x - exit_x;
@@ -997,10 +1064,17 @@
     else
       pos[child].x_offset = x_offset;
 
-    buffer->idx = j;
+    buffer->idx++;
     return_trace (true);
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -1047,6 +1121,10 @@
 
 struct MarkBasePosFormat1
 {
+  inline 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
   {
     TRACE_COLLECT_GLYPHS (this);
@@ -1055,9 +1133,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+markCoverage;
-  }
+  { return this+markCoverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -1088,7 +1164,7 @@
 	   ))
 	break;
       skippy_iter.reject ();
-    } while (1);
+    } while (true);
 
     /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
     //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
@@ -1099,6 +1175,13 @@
     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
+  {
+    TRACE_SUBSET (this);
+    // TODO(subset)
+    return_trace (false);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -1161,6 +1244,10 @@
 
 struct MarkLigPosFormat1
 {
+  inline 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
   {
     TRACE_COLLECT_GLYPHS (this);
@@ -1169,9 +1256,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+markCoverage;
-  }
+  { return this+markCoverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -1216,6 +1301,13 @@
     return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -1274,6 +1366,10 @@
 
 struct MarkMarkPosFormat1
 {
+  inline 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
   {
     TRACE_COLLECT_GLYPHS (this);
@@ -1282,9 +1378,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+mark1Coverage;
-  }
+  { return this+mark1Coverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -1330,6 +1424,13 @@
     return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -1388,7 +1489,7 @@
 
 struct ExtensionPos : Extension<ExtensionPos>
 {
-  typedef struct PosLookupSubTable LookupSubTable;
+  typedef struct PosLookupSubTable SubTable;
 };
 
 
@@ -1400,6 +1501,7 @@
 
 struct PosLookupSubTable
 {
+  friend struct Lookup;
   friend struct PosLookup;
 
   enum Type {
@@ -1453,8 +1555,10 @@
 
 struct PosLookup : Lookup
 {
-  inline const PosLookupSubTable& get_subtable (unsigned int i) const
-  { return Lookup::get_subtable<PosLookupSubTable> (i); }
+  typedef struct PosLookupSubTable SubTable;
+
+  inline const SubTable& get_subtable (unsigned int i) const
+  { return Lookup::get_subtable<SubTable> (i); }
 
   inline bool is_reverse (void) const
   {
@@ -1467,6 +1571,12 @@
     return_trace (dispatch (c));
   }
 
+  inline 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
   {
     TRACE_COLLECT_GLYPHS (this);
@@ -1487,18 +1597,15 @@
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
-  { return Lookup::dispatch<PosLookupSubTable> (c); }
+  { return Lookup::dispatch<SubTable> (c); }
+
+  inline bool subset (hb_subset_context_t *c) const
+  { return Lookup::subset<SubTable> (c); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!Lookup::sanitize (c))) return_trace (false);
-    return_trace (dispatch (c));
-  }
+  { return Lookup::sanitize<SubTable> (c); }
 };
 
-typedef OffsetListOf<PosLookup> PosLookupList;
-
 /*
  * GPOS -- Glyph Positioning
  * https://docs.microsoft.com/en-us/typography/opentype/spec/gpos
@@ -1515,13 +1622,13 @@
   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
+  { return GSUBGPOS::subset<PosLookup> (c); }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!GSUBGPOS::sanitize (c))) return_trace (false);
-    const OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
-    return_trace (list.sanitize (c, this));
-  }
+  { return GSUBGPOS::sanitize<PosLookup> (c); }
+
+  typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t;
 };
 
 
@@ -1551,7 +1658,10 @@
   pos[j].attach_type() = type;
 }
 static void
-propagate_attachment_offsets (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction)
+propagate_attachment_offsets (hb_glyph_position_t *pos,
+			      unsigned int len,
+			      unsigned int i,
+			      hb_direction_t direction)
 {
   /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate
    * offset of glyph they are attached to. */
@@ -1559,11 +1669,14 @@
   if (likely (!chain))
     return;
 
-  unsigned int j = (int) i + chain;
-
   pos[i].attach_chain() = 0;
 
-  propagate_attachment_offsets (pos, j, direction);
+  unsigned int j = (int) i + chain;
+
+  if (unlikely (j >= len))
+    return;
+
+  propagate_attachment_offsets (pos, len, j, direction);
 
   assert (!!(type & ATTACH_TYPE_MARK) ^ !!(type & ATTACH_TYPE_CURSIVE));
 
@@ -1619,7 +1732,7 @@
   /* Handle attachments */
   if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT)
     for (unsigned int i = 0; i < len; i++)
-      propagate_attachment_offsets (pos, i, direction);
+      propagate_attachment_offsets (pos, len, i, direction);
 }
 
 
@@ -1628,15 +1741,13 @@
 template <typename context_t>
 /*static*/ inline typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
 {
-  const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->table.GPOS);
-  const PosLookup &l = gpos.get_lookup (lookup_index);
+  const PosLookup &l = _get_gpos_relaxed (c->face)->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 GPOS &gpos = *(hb_ot_layout_from_face (c->face)->table.GPOS);
-  const PosLookup &l = gpos.get_lookup (lookup_index);
+  const PosLookup &l = _get_gpos_relaxed (c->face).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);
@@ -1647,9 +1758,7 @@
   return ret;
 }
 
-
-#undef attach_chain
-#undef attach_type
+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 1d5a02a..2ce52a1 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -29,19 +29,26 @@
 #ifndef HB_OT_LAYOUT_GSUB_TABLE_HH
 #define HB_OT_LAYOUT_GSUB_TABLE_HH
 
-#include "hb-ot-layout-gsubgpos-private.hh"
+#include "hb-ot-layout-gsubgpos.hh"
 
 
 namespace OT {
 
 
+static inline void SingleSubst_serialize (hb_serialize_context_t *c,
+					  Supplier<GlyphID> &glyphs,
+					  Supplier<GlyphID> &substitutes,
+					  unsigned int num_glyphs);
+
 struct SingleSubstFormat1
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
-    Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (hb_auto_t<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 */
@@ -55,8 +62,7 @@
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
-    Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (hb_auto_t<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 */
@@ -66,9 +72,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
@@ -99,10 +103,34 @@
     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);
-    deltaGlyphID.set (delta); /* TODO(serilaize) overflow? */
+    deltaGlyphID.set (delta); /* TODO(serialize) overflow? */
     return_trace (true);
   }
 
+  inline 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;
+    hb_codepoint_t delta = deltaGlyphID;
+    for (hb_auto_t<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);
+    }
+    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);
+    return_trace (from.len);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -122,12 +150,14 @@
 
 struct SingleSubstFormat2
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
-    Coverage::Iter iter;
     unsigned int count = substitute.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (hb_auto_t<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 */
@@ -140,9 +170,8 @@
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
-    Coverage::Iter iter;
     unsigned int count = substitute.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (hb_auto_t<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 */
@@ -151,9 +180,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
@@ -164,14 +191,12 @@
   inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
-    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
     if (unlikely (index >= substitute.len)) return_trace (false);
 
-    glyph_id = substitute[index];
-    c->replace_glyph (glyph_id);
+    c->replace_glyph (substitute[index]);
 
     return_trace (true);
   }
@@ -188,6 +213,29 @@
     return_trace (true);
   }
 
+  inline 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 ())
+    {
+      if (!c->plan->glyphset->has (iter.get_glyph ()))
+        continue;
+      from.push ()->set (iter.get_glyph ());
+      to.push ()->set (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);
+    return_trace (from.len);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -255,6 +303,17 @@
   } u;
 };
 
+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);
+}
 
 struct Sequence
 {
@@ -329,12 +388,14 @@
 
 struct MultipleSubstFormat1
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
-    Coverage::Iter iter;
     unsigned int count = sequence.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (hb_auto_t<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 */
@@ -349,13 +410,11 @@
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
     unsigned int count = sequence.len;
     for (unsigned int i = 0; i < count; i++)
-	(this+sequence[i]).collect_glyphs (c);
+      (this+sequence[i]).collect_glyphs (c);
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
@@ -391,6 +450,13 @@
     return_trace (true);
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -445,27 +511,86 @@
   } u;
 };
 
-
-typedef ArrayOf<GlyphID> AlternateSet;	/* Array of alternate GlyphIDs--in
-					 * arbitrary order */
-
-struct AlternateSubstFormat1
+struct AlternateSet
 {
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
-    Coverage::Iter iter;
+    unsigned int count = alternates.len;
+    for (unsigned int i = 0; i < count; i++)
+      c->out->add (alternates[i]);
+  }
+
+  inline 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
+  {
+    TRACE_APPLY (this);
+    unsigned int count = alternates.len;
+
+    if (unlikely (!count)) return_trace (false);
+
+    hb_mask_t glyph_mask = c->buffer->cur().mask;
+    hb_mask_t lookup_mask = c->lookup_mask;
+
+    /* Note: This breaks badly if two features enabled this lookup together. */
+    unsigned int shift = hb_ctz (lookup_mask);
+    unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
+
+    /* If alt_index is MAX, randomize feature if it is the rand feature. */
+    if (alt_index == HB_OT_MAP_MAX_VALUE && c->random)
+      alt_index = c->random_number () % count + 1;
+
+    if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
+
+    c->replace_glyph (alternates[alt_index - 1]);
+
+    return_trace (true);
+  }
+
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 unsigned int num_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);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (alternates.sanitize (c));
+  }
+
+  protected:
+  ArrayOf<GlyphID>
+		alternates;		/* Array of alternate GlyphIDs--in
+					 * arbitrary order */
+  public:
+  DEFINE_SIZE_ARRAY (2, alternates);
+};
+
+struct AlternateSubstFormat1
+{
+  inline bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
+  inline void closure (hb_closure_context_t *c) const
+  {
+    TRACE_CLOSURE (this);
     unsigned int count = alternateSet.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (hb_auto_t<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 */
-      if (c->glyphs->has (iter.get_glyph ())) {
-	const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
-	unsigned int count = alt_set.len;
-	for (unsigned int i = 0; i < count; i++)
-	  c->out->add (alt_set[i]);
-      }
+      if (c->glyphs->has (iter.get_glyph ()))
+	(this+alternateSet[iter.get_coverage ()]).closure (c);
     }
   }
 
@@ -473,21 +598,17 @@
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
-    Coverage::Iter iter;
     unsigned int count = alternateSet.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (hb_auto_t<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 */
-      const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
-      c->output->add_array (alt_set.arrayZ, alt_set.len);
+      (this+alternateSet[iter.get_coverage ()]).collect_glyphs (c);
     }
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
@@ -498,29 +619,11 @@
   inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
-    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
-    const AlternateSet &alt_set = this+alternateSet[index];
-
-    if (unlikely (!alt_set.len)) return_trace (false);
-
-    hb_mask_t glyph_mask = c->buffer->cur().mask;
-    hb_mask_t lookup_mask = c->lookup_mask;
-
-    /* Note: This breaks badly if two features enabled this lookup together. */
-    unsigned int shift = hb_ctz (lookup_mask);
-    unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
-
-    if (unlikely (alt_index > alt_set.len || alt_index == 0)) return_trace (false);
-
-    glyph_id = alt_set[alt_index - 1];
-
-    c->replace_glyph (glyph_id);
-
-    return_trace (true);
+    return_trace ((this+alternateSet[index]).apply (c));
   }
 
   inline bool serialize (hb_serialize_context_t *c,
@@ -541,6 +644,13 @@
     return_trace (true);
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -598,10 +708,19 @@
 
 struct Ligature
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  {
+    unsigned int count = component.lenP1;
+    for (unsigned int i = 1; i < count; i++)
+      if (!glyphs->has (component[i]))
+        return false;
+    return true;
+  }
+
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
-    unsigned int count = component.len;
+    unsigned int count = component.lenP1;
     for (unsigned int i = 1; i < count; i++)
       if (!c->glyphs->has (component[i]))
         return;
@@ -611,14 +730,14 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    c->input->add_array (component.arrayZ, component.len ? component.len - 1 : 0);
+    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
   {
     TRACE_WOULD_APPLY (this);
-    if (c->len != component.len)
+    if (c->len != component.lenP1)
       return_trace (false);
 
     for (unsigned int i = 1; i < c->len; i++)
@@ -631,7 +750,7 @@
   inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    unsigned int count = component.len;
+    unsigned int count = component.lenP1;
 
     if (unlikely (!count)) return_trace (false);
 
@@ -643,7 +762,6 @@
       return_trace (true);
     }
 
-    bool is_mark_ligature = false;
     unsigned int total_component_count = 0;
 
     unsigned int match_length = 0;
@@ -655,7 +773,6 @@
 			      nullptr,
 			      &match_length,
 			      match_positions,
-			      &is_mark_ligature,
 			      &total_component_count)))
       return_trace (false);
 
@@ -664,7 +781,6 @@
 		  match_positions,
 		  match_length,
 		  ligGlyph,
-		  is_mark_ligature,
 		  total_component_count);
 
     return_trace (true);
@@ -701,6 +817,15 @@
 
 struct LigatureSet
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  {
+    unsigned int num_ligs = ligature.len;
+    for (unsigned int i = 0; i < num_ligs; i++)
+      if ((this+ligature[i]).intersects (glyphs))
+        return true;
+    return false;
+  }
+
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
@@ -778,12 +903,25 @@
 
 struct LigatureSubstFormat1
 {
+  inline 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 ())
+    {
+      if (unlikely (iter.get_coverage () >= count))
+        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
+      if (glyphs->has (iter.get_glyph ()) &&
+	  (this+ligatureSet[iter.get_coverage ()]).intersects (glyphs))
+        return true;
+    }
+    return false;
+  }
+
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
-    Coverage::Iter iter;
     unsigned int count = ligatureSet.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (hb_auto_t<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 */
@@ -796,9 +934,8 @@
   {
     TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
-    Coverage::Iter iter;
     unsigned int count = ligatureSet.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (hb_auto_t<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 */
@@ -807,9 +944,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
@@ -824,9 +959,8 @@
   inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
-    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
     const LigatureSet &lig_set = this+ligatureSet[index];
@@ -855,6 +989,13 @@
     return_trace (true);
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -924,7 +1065,7 @@
 
 struct ExtensionSubst : Extension<ExtensionSubst>
 {
-  typedef struct SubstLookupSubTable LookupSubTable;
+  typedef struct SubstLookupSubTable SubTable;
 
   inline bool is_reverse (void) const;
 };
@@ -932,6 +1073,28 @@
 
 struct ReverseChainSingleSubstFormat1
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  {
+    if (!(this+coverage).intersects (glyphs))
+      return false;
+
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+    unsigned int count;
+
+    count = backtrack.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+backtrack[i]).intersects (glyphs))
+        return false;
+
+    count = lookahead.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+lookahead[i]).intersects (glyphs))
+        return false;
+
+    return true;
+  }
+
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
@@ -950,9 +1113,8 @@
         return;
 
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
-    Coverage::Iter iter;
     count = substitute.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (hb_auto_t<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 */
@@ -983,9 +1145,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
@@ -1026,6 +1186,13 @@
     return_trace (false);
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -1045,7 +1212,7 @@
 					 * beginning of table */
   OffsetArrayOf<Coverage>
 		backtrack;		/* Array of coverage tables
-					 * in backtracking sequence, in  glyph
+					 * in backtracking sequence, in glyph
 					 * sequence order */
   OffsetArrayOf<Coverage>
 		lookaheadX;		/* Array of coverage tables
@@ -1086,6 +1253,7 @@
 
 struct SubstLookupSubTable
 {
+  friend struct Lookup;
   friend struct SubstLookup;
 
   enum Type {
@@ -1136,16 +1304,18 @@
 
 struct SubstLookup : Lookup
 {
-  inline const SubstLookupSubTable& get_subtable (unsigned int i) const
-  { return Lookup::get_subtable<SubstLookupSubTable> (i); }
+  typedef SubstLookupSubTable SubTable;
+
+  inline 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)
-  { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
+  { return lookup_type == SubTable::ReverseChainSingle; }
 
   inline bool is_reverse (void) const
   {
     unsigned int type = get_type ();
-    if (unlikely (type == SubstLookupSubTable::Extension))
+    if (unlikely (type == SubTable::Extension))
       return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
     return lookup_type_is_reverse (type);
   }
@@ -1156,6 +1326,12 @@
     return_trace (dispatch (c));
   }
 
+  inline 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
   {
     TRACE_CLOSURE (this);
@@ -1196,9 +1372,9 @@
 
   static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
 
-  inline SubstLookupSubTable& serialize_subtable (hb_serialize_context_t *c,
-						  unsigned int i)
-  { return get_subtables<SubstLookupSubTable> ()[i].serialize (c, this); }
+  inline 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,
@@ -1207,7 +1383,7 @@
 			        unsigned int num_glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Single, lookup_props, 1))) return_trace (false);
+    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));
   }
 
@@ -1219,7 +1395,7 @@
 				  Supplier<GlyphID> &substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Multiple, lookup_props, 1))) return_trace (false);
+    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,
@@ -1235,7 +1411,7 @@
 				   Supplier<GlyphID> &alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Alternate, lookup_props, 1))) return_trace (false);
+    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,
@@ -1253,7 +1429,7 @@
 				  Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Ligature, lookup_props, 1))) return_trace (false);
+    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,
@@ -1280,32 +1456,15 @@
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
-  { return Lookup::dispatch<SubstLookupSubTable> (c); }
+  { return Lookup::dispatch<SubTable> (c); }
+
+  inline bool subset (hb_subset_context_t *c) const
+  { return Lookup::subset<SubTable> (c); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!Lookup::sanitize (c))) return_trace (false);
-    if (unlikely (!dispatch (c))) return_trace (false);
-
-    if (unlikely (get_type () == SubstLookupSubTable::Extension))
-    {
-      /* The spec says all subtables of an Extension lookup should
-       * have the same type, which shall not be the Extension type
-       * itself (but we already checked for that).
-       * This is specially important if one has a reverse type! */
-      unsigned int type = get_subtable (0).u.extension.get_type ();
-      unsigned int count = get_subtable_count ();
-      for (unsigned int i = 1; i < count; i++)
-        if (get_subtable (i).u.extension.get_type () != type)
-	  return_trace (false);
-    }
-    return_trace (true);
-  }
+  { return Lookup::sanitize<SubTable> (c); }
 };
 
-typedef OffsetListOf<SubstLookup> SubstLookupList;
-
 /*
  * GSUB -- Glyph Substitution
  * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
@@ -1318,56 +1477,36 @@
   inline const SubstLookup& get_lookup (unsigned int i) const
   { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
 
-  static inline void substitute_start (hb_font_t *font, hb_buffer_t *buffer);
+  inline bool subset (hb_subset_context_t *c) const
+  { return GSUBGPOS::subset<SubstLookup> (c); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!GSUBGPOS::sanitize (c))) return_trace (false);
-    const OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
-    return_trace (list.sanitize (c, this));
-  }
+  { return GSUBGPOS::sanitize<SubstLookup> (c); }
+
+  typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
 };
 
 
-void
-GSUB::substitute_start (hb_font_t *font, hb_buffer_t *buffer)
-{
-  _hb_buffer_assert_gsubgpos_vars (buffer);
-
-  const GDEF &gdef = *hb_ot_layout_from_face (font->face)->table.GDEF;
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-  {
-    _hb_glyph_info_set_glyph_props (&buffer->info[i], gdef.get_glyph_props (buffer->info[i].codepoint));
-    _hb_glyph_info_clear_lig_props (&buffer->info[i]);
-    buffer->info[i].syllable() = 0;
-  }
-}
-
-
 /* Out-of-class implementation for methods recursing */
 
 /*static*/ inline bool ExtensionSubst::is_reverse (void) const
 {
   unsigned int type = get_type ();
-  if (unlikely (type == SubstLookupSubTable::Extension))
-    return CastR<ExtensionSubst> (get_subtable<LookupSubTable>()).is_reverse ();
+  if (unlikely (type == SubTable::Extension))
+    return CastR<ExtensionSubst> (get_subtable<SubTable>()).is_reverse ();
   return SubstLookup::lookup_type_is_reverse (type);
 }
 
 template <typename context_t>
 /*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
 {
-  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->table.GSUB);
-  const SubstLookup &l = gsub.get_lookup (lookup_index);
+  const SubstLookup &l = _get_gsub_relaxed (c->face).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 GSUB &gsub = *(hb_ot_layout_from_face (c->face)->table.GSUB);
-  const SubstLookup &l = gsub.get_lookup (lookup_index);
+  const SubstLookup &l = _get_gsub_relaxed (c->face).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);
@@ -1378,6 +1517,7 @@
   return ret;
 }
 
+struct GSUB_accelerator_t : GSUB::accelerator_t {};
 
 } /* namespace OT */
 
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos.hh
similarity index 79%
rename from src/hb-ot-layout-gsubgpos-private.hh
rename to src/hb-ot-layout-gsubgpos.hh
index 40a2fc4..a406626 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos.hh
@@ -26,19 +26,38 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
-#define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
+#ifndef HB_OT_LAYOUT_GSUBGPOS_HH
+#define HB_OT_LAYOUT_GSUBGPOS_HH
 
-#include "hb-private.hh"
-#include "hb-buffer-private.hh"
-#include "hb-map-private.hh"
+#include "hb.hh"
+#include "hb-buffer.hh"
+#include "hb-map.hh"
+#include "hb-set.hh"
+#include "hb-ot-map.hh"
+#include "hb-ot-layout-common.hh"
 #include "hb-ot-layout-gdef-table.hh"
-#include "hb-set-private.hh"
 
 
 namespace OT {
 
 
+struct hb_intersects_context_t :
+       hb_dispatch_context_t<hb_intersects_context_t, bool, 0>
+{
+  inline const char *get_name (void) { 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; }
+  bool stop_sublookup_iteration (return_t r) const { return r; }
+
+  const hb_set_t *glyphs;
+  unsigned int debug_depth;
+
+  hb_intersects_context_t (const hb_set_t *glyphs_) :
+			     glyphs (glyphs_),
+			     debug_depth (0) {}
+};
+
 struct hb_closure_context_t :
        hb_dispatch_context_t<hb_closure_context_t, hb_void_t, HB_DEBUG_CLOSURE>
 {
@@ -48,15 +67,14 @@
   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 recurse (unsigned int lookup_index)
+  void recurse (unsigned int lookup_index)
   {
     if (unlikely (nesting_level_left == 0 || !recurse_func))
-      return default_return_value ();
+      return;
 
     nesting_level_left--;
     recurse_func (this, lookup_index);
     nesting_level_left++;
-    return HB_VOID;
   }
 
   bool should_visit_lookup (unsigned int lookup_index)
@@ -145,10 +163,10 @@
   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 recurse (unsigned int lookup_index)
+  void recurse (unsigned int lookup_index)
   {
     if (unlikely (nesting_level_left == 0 || !recurse_func))
-      return default_return_value ();
+      return;
 
     /* Note that GPOS sets recurse_func to nullptr already, so it doesn't get
      * past the previous check.  For GSUB, we only want to collect the output
@@ -161,11 +179,11 @@
      */
 
     if (output == hb_set_get_empty ())
-      return HB_VOID;
+      return;
 
     /* Return if new lookup was recursed to before. */
     if (recursed_lookups->has (lookup_index))
-      return HB_VOID;
+      return;
 
     hb_set_t *old_before = before;
     hb_set_t *old_input  = input;
@@ -181,8 +199,6 @@
     after  = old_after;
 
     recursed_lookups->add (lookup_index);
-
-    return HB_VOID;
   }
 
   hb_face_t *face;
@@ -196,10 +212,10 @@
   unsigned int debug_depth;
 
   hb_collect_glyphs_context_t (hb_face_t *face_,
-			       hb_set_t  *glyphs_before, /* OUT. May be nullptr */
-			       hb_set_t  *glyphs_input,  /* OUT. May be nullptr */
-			       hb_set_t  *glyphs_after,  /* OUT. May be nullptr */
-			       hb_set_t  *glyphs_output, /* OUT. May be nullptr */
+			       hb_set_t  *glyphs_before, /* OUT.  May be NULL */
+			       hb_set_t  *glyphs_input,  /* OUT.  May be NULL */
+			       hb_set_t  *glyphs_after,  /* OUT.  May be NULL */
+			       hb_set_t  *glyphs_output, /* OUT.  May be NULL */
 			       unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
 			      face (face_),
 			      before (glyphs_before ? glyphs_before : hb_set_get_empty ()),
@@ -207,24 +223,16 @@
 			      after  (glyphs_after  ? glyphs_after  : hb_set_get_empty ()),
 			      output (glyphs_output ? glyphs_output : hb_set_get_empty ()),
 			      recurse_func (nullptr),
-			      recursed_lookups (nullptr),
+			      recursed_lookups (hb_set_create ()),
 			      nesting_level_left (nesting_level_left_),
-			      debug_depth (0)
-  {
-    recursed_lookups = hb_set_create ();
-  }
-  ~hb_collect_glyphs_context_t (void)
-  {
-    hb_set_destroy (recursed_lookups);
-  }
+			      debug_depth (0) {}
+  ~hb_collect_glyphs_context_t (void) { hb_set_destroy (recursed_lookups); }
 
   void set_recurse_func (recurse_func_t func) { recurse_func = func; }
 };
 
 
 
-/* XXX Can we remove this? */
-
 template <typename set_t>
 struct hb_add_coverage_context_t :
        hb_dispatch_context_t<hb_add_coverage_context_t<set_t>, const Coverage &, HB_DEBUG_GET_COVERAGE>
@@ -334,10 +342,10 @@
       match_glyph_data = nullptr;
       matcher.set_match_func (nullptr, nullptr);
       matcher.set_lookup_props (c->lookup_props);
-      /* Ignore ZWNJ if we are matching GSUB context, or matching GPOS. */
+      /* Ignore ZWNJ if we are matching GPOS, or matching GSUB context and asked to. */
       matcher.set_ignore_zwnj (c->table_index == 1 || (context_match && c->auto_zwnj));
-      /* Ignore ZWJ if we are matching GSUB context, or matching GPOS, or if asked to. */
-      matcher.set_ignore_zwj  (c->table_index == 1 || (context_match || c->auto_zwj));
+      /* Ignore ZWJ if we are matching context, or asked to. */
+      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)
@@ -469,9 +477,12 @@
   unsigned int nesting_level_left;
   unsigned int debug_depth;
 
+  bool has_glyph_classes;
   bool auto_zwnj;
   bool auto_zwj;
-  bool has_glyph_classes;
+  bool random;
+
+  uint32_t random_state;
 
 
   hb_ot_apply_context_t (unsigned int table_index_,
@@ -480,7 +491,7 @@
 			iter_input (), iter_context (),
 			font (font_), face (font->face), buffer (buffer_),
 			recurse_func (nullptr),
-			gdef (*hb_ot_layout_from_face (face)->table.GDEF),
+			gdef (_get_gdef (face)),
 			var_store (gdef.get_var_store ()),
 			direction (buffer_->props.direction),
 			lookup_mask (1),
@@ -489,22 +500,33 @@
 			lookup_props (0),
 			nesting_level_left (HB_MAX_NESTING_LEVEL),
 			debug_depth (0),
+			has_glyph_classes (gdef.has_glyph_classes ()),
 			auto_zwnj (true),
 			auto_zwj (true),
-			has_glyph_classes (gdef.has_glyph_classes ()) {}
+			random (false),
+			random_state (1) { init_iters (); }
 
-  inline void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; }
-  inline void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; }
-  inline void set_auto_zwnj (bool auto_zwnj_) { auto_zwnj = auto_zwnj_; }
-  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_)
+  inline void init_iters (void)
   {
-    lookup_props = lookup_props_;
     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 (); }
+
+  inline uint32_t random_number (void)
+  {
+    /* 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,
@@ -558,7 +580,7 @@
       add_in |= HB_OT_LAYOUT_GLYPH_PROPS_LIGATED;
       /* In the only place that the MULTIPLIED bit is used, Uniscribe
        * seems to only care about the "last" transformation between
-       * Ligature and Multiple substitions.  Ie. if you ligate, expand,
+       * Ligature and Multiple substitutions.  Ie. if you ligate, expand,
        * and ligate again, it forgives the multiplication and acts as
        * if only ligation happened.  As such, clear MULTIPLIED bit.
        */
@@ -597,8 +619,66 @@
 };
 
 
+struct hb_get_subtables_context_t :
+       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)
+  {
+    const Type *typed_obj = (const Type *) obj;
+    return typed_obj->apply (c);
+  }
 
-typedef bool (*intersects_func_t) (hb_set_t *glyphs, const HBUINT16 &value, const void *data);
+  typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_ot_apply_context_t *c);
+
+  struct hb_applicable_t
+  {
+    template <typename T>
+    inline void init (const T &obj_, hb_apply_func_t apply_func_)
+    {
+      obj = &obj_;
+      apply_func = apply_func_;
+      digest.init ();
+      obj_.get_coverage ().add_coverage (&digest);
+    }
+
+    inline bool apply (OT::hb_ot_apply_context_t *c) const
+    {
+      return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c);
+    }
+
+    private:
+    const void *obj;
+    hb_apply_func_t apply_func;
+    hb_set_digest_t digest;
+  };
+
+  typedef hb_vector_t<hb_applicable_t, 2> array_t;
+
+  /* Dispatch interface. */
+  inline const char *get_name (void) { return "GET_SUBTABLES"; }
+  template <typename T>
+  inline 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; }
+
+  hb_get_subtables_context_t (array_t &array_) :
+			      array (array_),
+			      debug_depth (0) {}
+
+  array_t &array;
+  unsigned int debug_depth;
+};
+
+
+
+
+typedef bool (*intersects_func_t) (const hb_set_t *glyphs, const HBUINT16 &value, const void *data);
 typedef void (*collect_glyphs_func_t) (hb_set_t *glyphs, const HBUINT16 &value, const void *data);
 typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data);
 
@@ -616,29 +696,29 @@
 };
 
 
-static inline bool intersects_glyph (hb_set_t *glyphs, const HBUINT16 &value, const void *data HB_UNUSED)
+static inline bool intersects_glyph (const hb_set_t *glyphs, const HBUINT16 &value, const void *data HB_UNUSED)
 {
   return glyphs->has (value);
 }
-static inline bool intersects_class (hb_set_t *glyphs, const HBUINT16 &value, const void *data)
+static inline bool intersects_class (const hb_set_t *glyphs, const HBUINT16 &value, const void *data)
 {
   const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
   return class_def.intersects_class (glyphs, value);
 }
-static inline bool intersects_coverage (hb_set_t *glyphs, const HBUINT16 &value, const void *data)
+static inline bool intersects_coverage (const hb_set_t *glyphs, const HBUINT16 &value, const void *data)
 {
   const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
   return (data+coverage).intersects (glyphs);
 }
 
-static inline bool intersects_array (hb_closure_context_t *c,
+static inline bool intersects_array (const hb_set_t *glyphs,
 				     unsigned int count,
 				     const HBUINT16 values[],
 				     intersects_func_t intersects_func,
 				     const void *intersects_data)
 {
   for (unsigned int i = 0; i < count; i++)
-    if (likely (!intersects_func (c->glyphs, values[i], intersects_data)))
+    if (likely (!intersects_func (glyphs, values[i], intersects_data)))
       return false;
   return true;
 }
@@ -707,7 +787,6 @@
 				const void *match_data,
 				unsigned int *end_offset,
 				unsigned int match_positions[HB_MAX_CONTEXT_LENGTH],
-				bool *p_is_mark_ligature = nullptr,
 				unsigned int *p_total_component_count = nullptr)
 {
   TRACE_APPLY (nullptr);
@@ -744,8 +823,6 @@
    *     https://github.com/harfbuzz/harfbuzz/issues/545
    */
 
-  bool is_mark_ligature = _hb_glyph_info_is_mark (&buffer->cur());
-
   unsigned int total_component_count = 0;
   total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->cur());
 
@@ -812,15 +889,11 @@
 	return_trace (false);
     }
 
-    is_mark_ligature = is_mark_ligature && _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx]);
     total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->info[skippy_iter.idx]);
   }
 
   *end_offset = skippy_iter.idx - buffer->idx + 1;
 
-  if (p_is_mark_ligature)
-    *p_is_mark_ligature = is_mark_ligature;
-
   if (p_total_component_count)
     *p_total_component_count = total_component_count;
 
@@ -828,10 +901,9 @@
 }
 static inline bool ligate_input (hb_ot_apply_context_t *c,
 				 unsigned int count, /* Including the first glyph */
-				 unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */
+				 const unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */
 				 unsigned int match_length,
 				 hb_codepoint_t lig_glyph,
-				 bool is_mark_ligature,
 				 unsigned int total_component_count)
 {
   TRACE_APPLY (nullptr);
@@ -840,11 +912,15 @@
 
   buffer->merge_clusters (buffer->idx, buffer->idx + match_length);
 
-  /*
-   * - If it *is* a mark ligature, we don't allocate a new ligature id, and leave
+  /* - If a base and one or more marks ligate, consider that as a base, NOT
+   *   ligature, such that all following marks can still attach to it.
+   *   https://github.com/harfbuzz/harfbuzz/issues/1109
+   *
+   * - If all components of the ligature were marks, we call this a mark ligature.
+   *   If it *is* a mark ligature, we don't allocate a new ligature id, and leave
    *   the ligature to keep its old ligature id.  This will allow it to attach to
    *   a base ligature in GPOS.  Eg. if the sequence is: LAM,LAM,SHADDA,FATHA,HEH,
-   *   and LAM,LAM,HEH for a ligature, they will leave SHADDA and FATHA wit a
+   *   and LAM,LAM,HEH for a ligature, they will leave SHADDA and FATHA with a
    *   ligature id and component value of 2.  Then if SHADDA,FATHA form a ligature
    *   later, we don't want them to lose their ligature id/component, otherwise
    *   GPOS will fail to correctly position the mark ligature on top of the
@@ -868,13 +944,24 @@
    *   https://bugzilla.gnome.org/show_bug.cgi?id=437633
    */
 
-  unsigned int klass = is_mark_ligature ? 0 : HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE;
-  unsigned int lig_id = is_mark_ligature ? 0 : _hb_allocate_lig_id (buffer);
+  bool is_base_ligature = _hb_glyph_info_is_base_glyph (&buffer->info[match_positions[0]]);
+  bool is_mark_ligature = _hb_glyph_info_is_mark (&buffer->info[match_positions[0]]);
+  for (unsigned int i = 1; i < count; i++)
+    if (!_hb_glyph_info_is_mark (&buffer->info[match_positions[i]]))
+    {
+      is_base_ligature = false;
+      is_mark_ligature = false;
+      break;
+    }
+  bool is_ligature = !is_base_ligature && !is_mark_ligature;
+
+  unsigned int klass = is_ligature ? HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE : 0;
+  unsigned int lig_id = is_ligature ? _hb_allocate_lig_id (buffer) : 0;
   unsigned int last_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur());
   unsigned int last_num_components = _hb_glyph_info_get_lig_num_comps (&buffer->cur());
   unsigned int components_so_far = last_num_components;
 
-  if (!is_mark_ligature)
+  if (is_ligature)
   {
     _hb_glyph_info_set_lig_props_for_ligature (&buffer->cur(), lig_id, total_component_count);
     if (_hb_glyph_info_get_general_category (&buffer->cur()) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
@@ -888,7 +975,8 @@
   {
     while (buffer->idx < match_positions[i] && buffer->successful)
     {
-      if (!is_mark_ligature) {
+      if (is_ligature)
+      {
         unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
 	if (this_comp == 0)
 	  this_comp = last_num_components;
@@ -987,7 +1075,6 @@
   DEFINE_SIZE_STATIC (4);
 };
 
-
 template <typename context_t>
 static inline void recurse_lookups (context_t *c,
 				    unsigned int lookupCount,
@@ -1140,6 +1227,16 @@
   const void *match_data;
 };
 
+static inline bool context_intersects (const hb_set_t *glyphs,
+				       unsigned int inputCount, /* Including the first glyph (not matched) */
+				       const HBUINT16 input[], /* Array of input values--start with second glyph */
+				       ContextClosureLookupContext &lookup_context)
+{
+  return intersects_array (glyphs,
+			   inputCount ? inputCount - 1 : 0, input,
+			   lookup_context.funcs.intersects, lookup_context.intersects_data);
+}
+
 static inline void context_closure_lookup (hb_closure_context_t *c,
 					   unsigned int inputCount, /* Including the first glyph (not matched) */
 					   const HBUINT16 input[], /* Array of input values--start with second glyph */
@@ -1147,9 +1244,9 @@
 					   const LookupRecord lookupRecord[],
 					   ContextClosureLookupContext &lookup_context)
 {
-  if (intersects_array (c,
-			inputCount ? inputCount - 1 : 0, input,
-			lookup_context.funcs.intersects, lookup_context.intersects_data))
+  if (context_intersects (c->glyphs,
+			  inputCount, input,
+			  lookup_context))
     recurse_lookups (c,
 		     lookupCount, lookupRecord);
 }
@@ -1201,38 +1298,45 @@
 
 struct Rule
 {
+  inline 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
   {
     TRACE_CLOSURE (this);
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
+    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAtOffset<UnsizedArrayOf<LookupRecord> > (inputZ.arrayZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
     context_closure_lookup (c,
-			    inputCount, inputZ,
-			    lookupCount, lookupRecord,
+			    inputCount, inputZ.arrayZ,
+			    lookupCount, lookupRecord.arrayZ,
 			    lookup_context);
   }
 
   inline void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
+    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAtOffset<UnsizedArrayOf<LookupRecord> > (inputZ.arrayZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
     context_collect_glyphs_lookup (c,
-				   inputCount, inputZ,
-				   lookupCount, lookupRecord,
+				   inputCount, inputZ.arrayZ,
+				   lookupCount, lookupRecord.arrayZ,
 				   lookup_context);
   }
 
   inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_WOULD_APPLY (this);
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
-    return_trace (context_would_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context));
+    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAtOffset<UnsizedArrayOf<LookupRecord> > (inputZ.arrayZ, inputZ[0].static_size * (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
   {
     TRACE_APPLY (this);
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
-    return_trace (context_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context));
+    const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAtOffset<UnsizedArrayOf<LookupRecord> > (inputZ.arrayZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
+    return_trace (context_apply_lookup (c, inputCount, inputZ.arrayZ, lookupCount, lookupRecord.arrayZ, lookup_context));
   }
 
   public:
@@ -1241,8 +1345,8 @@
     TRACE_SANITIZE (this);
     return_trace (inputCount.sanitize (c) &&
 		  lookupCount.sanitize (c) &&
-		  c->check_range (inputZ,
-				  inputZ[0].static_size * inputCount +
+		  c->check_range (inputZ.arrayZ,
+				  inputZ[0].static_size * (inputCount ? inputCount - 1 : 0) +
 				  LookupRecord::static_size * lookupCount));
   }
 
@@ -1251,9 +1355,11 @@
 					 * glyph sequence--includes the first
 					 * glyph */
   HBUINT16	lookupCount;		/* Number of LookupRecords */
-  HBUINT16	inputZ[VAR];		/* Array of match inputs--start with
+  UnsizedArrayOf<HBUINT16>
+ 		inputZ;			/* Array of match inputs--start with
 					 * second glyph */
-/*LookupRecord	lookupRecordX[VAR];*/	/* Array of LookupRecords--in
+/*UnsizedArrayOf<LookupRecord>
+		lookupRecordX;*/	/* Array of LookupRecords--in
 					 * design order */
   public:
   DEFINE_SIZE_ARRAY (4, inputZ);
@@ -1261,6 +1367,15 @@
 
 struct RuleSet
 {
+  inline 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 false;
+  }
+
   inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
@@ -1318,23 +1433,42 @@
 
 struct ContextFormat1
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  {
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_glyph},
+      nullptr
+    };
+
+    unsigned int count = ruleSet.len;
+    for (hb_auto_t<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 */
+      if (glyphs->has (iter.get_glyph ()) &&
+	  (this+ruleSet[iter.get_coverage ()]).intersects (glyphs, lookup_context))
+        return true;
+    }
+    return false;
+  }
+
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
 
-    const Coverage &cov = (this+coverage);
-
     struct ContextClosureLookupContext lookup_context = {
       {intersects_glyph},
       nullptr
     };
 
     unsigned int count = ruleSet.len;
-    for (unsigned int i = 0; i < count; i++)
-      if (cov.intersects_coverage (c->glyphs, i)) {
-	const RuleSet &rule_set = this+ruleSet[i];
-	rule_set.closure (c, lookup_context);
-      }
+    for (hb_auto_t<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 */
+      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
@@ -1365,9 +1499,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -1384,6 +1516,13 @@
     return_trace (rule_set.apply (c, lookup_context));
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -1405,6 +1544,27 @@
 
 struct ContextFormat2
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  {
+    if (!(this+coverage).intersects (glyphs))
+      return false;
+
+    const ClassDef &class_def = this+classDef;
+
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_class},
+      &class_def
+    };
+
+    unsigned int count = ruleSet.len;
+    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 false;
+  }
+
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
@@ -1457,9 +1617,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -1477,6 +1635,13 @@
     return_trace (rule_set.apply (c, lookup_context));
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -1501,19 +1666,33 @@
 
 struct ContextFormat3
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  {
+    if (!(this+coverageZ[0]).intersects (glyphs))
+      return false;
+
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_coverage},
+      this
+    };
+    return context_intersects (glyphs,
+			       glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1),
+			       lookup_context);
+  }
+
   inline 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, coverageZ[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ.arrayZ, coverageZ[0].static_size * glyphCount);
     struct ContextClosureLookupContext lookup_context = {
       {intersects_coverage},
       this
     };
     context_closure_lookup (c,
-			    glyphCount, (const HBUINT16 *) (coverageZ + 1),
+			    glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1),
 			    lookupCount, lookupRecord,
 			    lookup_context);
   }
@@ -1523,14 +1702,14 @@
     TRACE_COLLECT_GLYPHS (this);
     (this+coverageZ[0]).add_coverage (c->input);
 
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ.arrayZ, coverageZ[0].static_size * glyphCount);
     struct ContextCollectGlyphsLookupContext lookup_context = {
       {collect_coverage},
       this
     };
 
     context_collect_glyphs_lookup (c,
-				   glyphCount, (const HBUINT16 *) (coverageZ + 1),
+				   glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1),
 				   lookupCount, lookupRecord,
 				   lookup_context);
   }
@@ -1539,18 +1718,16 @@
   {
     TRACE_WOULD_APPLY (this);
 
-    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ.arrayZ, coverageZ[0].static_size * glyphCount);
     struct ContextApplyLookupContext lookup_context = {
       {match_coverage},
       this
     };
-    return_trace (context_would_apply_lookup (c, glyphCount, (const HBUINT16 *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
+    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];
-  }
+  { return this+coverageZ[0]; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -1558,12 +1735,19 @@
     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, coverageZ[0].static_size * glyphCount);
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ.arrayZ, coverageZ[0].static_size * glyphCount);
     struct ContextApplyLookupContext lookup_context = {
       {match_coverage},
       this
     };
-    return_trace (context_apply_lookup (c, glyphCount, (const HBUINT16 *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
+    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
+  {
+    TRACE_SUBSET (this);
+    // TODO(subset)
+    return_trace (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -1572,11 +1756,11 @@
     if (!c->check_struct (this)) return_trace (false);
     unsigned int count = glyphCount;
     if (!count) return_trace (false); /* We want to access coverageZ[0] freely. */
-    if (!c->check_array (coverageZ, coverageZ[0].static_size, count)) return_trace (false);
+    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, coverageZ[0].static_size * count);
-    return_trace (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ.arrayZ, coverageZ[0].static_size * count);
+    return_trace (c->check_array (lookupRecord, lookupCount));
   }
 
   protected:
@@ -1584,10 +1768,11 @@
   HBUINT16	glyphCount;		/* Number of glyphs in the input glyph
 					 * sequence */
   HBUINT16	lookupCount;		/* Number of LookupRecords */
-  OffsetTo<Coverage>
-		coverageZ[VAR];		/* Array of offsets to Coverage
+  UnsizedArrayOf<OffsetTo<Coverage> >
+		coverageZ;		/* Array of offsets to Coverage
 					 * table in glyph sequence order */
-/*LookupRecord	lookupRecordX[VAR];*/	/* Array of LookupRecords--in
+/*UnsizedArrayOf<LookupRecord>
+		lookupRecordX;*/	/* Array of LookupRecords--in
 					 * design order */
   public:
   DEFINE_SIZE_ARRAY (6, coverageZ);
@@ -1638,6 +1823,26 @@
   const void *match_data[3];
 };
 
+static inline bool chain_context_intersects (const hb_set_t *glyphs,
+					     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[],
+					     ChainContextClosureLookupContext &lookup_context)
+{
+  return intersects_array (glyphs,
+			   backtrackCount, backtrack,
+			   lookup_context.funcs.intersects, lookup_context.intersects_data[0])
+      && intersects_array (glyphs,
+			   inputCount ? inputCount - 1 : 0, input,
+			   lookup_context.funcs.intersects, lookup_context.intersects_data[1])
+      && intersects_array (glyphs,
+			  lookaheadCount, lookahead,
+			  lookup_context.funcs.intersects, lookup_context.intersects_data[2]);
+}
+
 static inline void chain_context_closure_lookup (hb_closure_context_t *c,
 						 unsigned int backtrackCount,
 						 const HBUINT16 backtrack[],
@@ -1649,15 +1854,11 @@
 						 const LookupRecord lookupRecord[],
 						 ChainContextClosureLookupContext &lookup_context)
 {
-  if (intersects_array (c,
-			backtrackCount, backtrack,
-			lookup_context.funcs.intersects, lookup_context.intersects_data[0])
-   && intersects_array (c,
-			inputCount ? inputCount - 1 : 0, input,
-			lookup_context.funcs.intersects, lookup_context.intersects_data[1])
-   && intersects_array (c,
-		       lookaheadCount, lookahead,
-		       lookup_context.funcs.intersects, lookup_context.intersects_data[2]))
+  if (chain_context_intersects (c->glyphs,
+				backtrackCount, backtrack,
+				inputCount, input,
+				lookaheadCount, lookahead,
+				lookup_context))
     recurse_lookups (c,
 		     lookupCount, lookupRecord);
 }
@@ -1737,6 +1938,17 @@
 
 struct ChainRule
 {
+  inline 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);
+    return chain_context_intersects (glyphs,
+				     backtrack.len, backtrack.arrayZ,
+				     input.lenP1, input.arrayZ,
+				     lookahead.len, lookahead.arrayZ,
+				     lookup_context);
+  }
+
   inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
@@ -1745,7 +1957,7 @@
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     chain_context_closure_lookup (c,
 				  backtrack.len, backtrack.arrayZ,
-				  input.len, input.arrayZ,
+				  input.lenP1, input.arrayZ,
 				  lookahead.len, lookahead.arrayZ,
 				  lookup.len, lookup.arrayZ,
 				  lookup_context);
@@ -1759,7 +1971,7 @@
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     chain_context_collect_glyphs_lookup (c,
 					 backtrack.len, backtrack.arrayZ,
-					 input.len, input.arrayZ,
+					 input.lenP1, input.arrayZ,
 					 lookahead.len, lookahead.arrayZ,
 					 lookup.len, lookup.arrayZ,
 					 lookup_context);
@@ -1773,7 +1985,7 @@
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     return_trace (chain_context_would_apply_lookup (c,
 						    backtrack.len, backtrack.arrayZ,
-						    input.len, input.arrayZ,
+						    input.lenP1, input.arrayZ,
 						    lookahead.len, lookahead.arrayZ, lookup.len,
 						    lookup.arrayZ, lookup_context));
   }
@@ -1786,7 +1998,7 @@
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     return_trace (chain_context_apply_lookup (c,
 					      backtrack.len, backtrack.arrayZ,
-					      input.len, input.arrayZ,
+					      input.lenP1, input.arrayZ,
 					      lookahead.len, lookahead.arrayZ, lookup.len,
 					      lookup.arrayZ, lookup_context));
   }
@@ -1823,6 +2035,14 @@
 
 struct ChainRuleSet
 {
+  inline 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 false;
+  }
   inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
@@ -1877,10 +2097,28 @@
 
 struct ChainContextFormat1
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  {
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_glyph},
+      {nullptr, nullptr, nullptr}
+    };
+
+    unsigned int count = ruleSet.len;
+    for (hb_auto_t<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 */
+      if (glyphs->has (iter.get_glyph ()) &&
+	  (this+ruleSet[iter.get_coverage ()]).intersects (glyphs, lookup_context))
+        return true;
+    }
+    return false;
+  }
+
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
-    const Coverage &cov = (this+coverage);
 
     struct ChainContextClosureLookupContext lookup_context = {
       {intersects_glyph},
@@ -1888,11 +2126,13 @@
     };
 
     unsigned int count = ruleSet.len;
-    for (unsigned int i = 0; i < count; i++)
-      if (cov.intersects_coverage (c->glyphs, i)) {
-	const ChainRuleSet &rule_set = this+ruleSet[i];
-	rule_set.closure (c, lookup_context);
-      }
+    for (hb_auto_t<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 */
+      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
@@ -1923,9 +2163,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -1941,6 +2179,13 @@
     return_trace (rule_set.apply (c, lookup_context));
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -1961,6 +2206,30 @@
 
 struct ChainContextFormat2
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  {
+    if (!(this+coverage).intersects (glyphs))
+      return false;
+
+    const ClassDef &backtrack_class_def = this+backtrackClassDef;
+    const ClassDef &input_class_def = this+inputClassDef;
+    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
+
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_class},
+      {&backtrack_class_def,
+       &input_class_def,
+       &lookahead_class_def}
+    };
+
+    unsigned int count = ruleSet.len;
+    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 false;
+  }
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
@@ -2027,9 +2296,7 @@
   }
 
   inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  { return this+coverage; }
 
   inline bool apply (hb_ot_apply_context_t *c) const
   {
@@ -2052,6 +2319,13 @@
     return_trace (rule_set.apply (c, lookup_context));
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -2088,6 +2362,25 @@
 
 struct ChainContextFormat3
 {
+  inline bool intersects (const hb_set_t *glyphs) const
+  {
+    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+    if (!(this+input[0]).intersects (glyphs))
+      return false;
+
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_coverage},
+      {this, this, this}
+    };
+    return chain_context_intersects (glyphs,
+				     backtrack.len, (const HBUINT16 *) backtrack.arrayZ,
+				     input.len, (const HBUINT16 *) input.arrayZ + 1,
+				     lookahead.len, (const HBUINT16 *) lookahead.arrayZ,
+				     lookup_context);
+  }
+
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
@@ -2176,6 +2469,13 @@
 					      lookup.len, lookup.arrayZ, lookup_context));
   }
 
+  inline 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
   {
     TRACE_SANITIZE (this);
@@ -2244,8 +2544,8 @@
   inline const X& get_subtable (void) const
   {
     unsigned int offset = extensionOffset;
-    if (unlikely (!offset)) return Null(typename T::LookupSubTable);
-    return StructAtOffset<typename T::LookupSubTable> (this, offset);
+    if (unlikely (!offset)) return Null(typename T::SubTable);
+    return StructAtOffset<typename T::SubTable> (this, offset);
   }
 
   template <typename context_t>
@@ -2253,7 +2553,7 @@
   {
     TRACE_DISPATCH (this, format);
     if (unlikely (!c->may_dispatch (this, this))) return_trace (c->no_dispatch_return_value ());
-    return_trace (get_subtable<typename T::LookupSubTable> ().dispatch (c, get_type ()));
+    return_trace (get_subtable<typename T::SubTable> ().dispatch (c, get_type ()));
   }
 
   /* This is called from may_dispatch() above with hb_sanitize_context_t. */
@@ -2262,7 +2562,7 @@
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  extensionOffset != 0 &&
-		  extensionLookupType != T::LookupSubTable::Extension);
+		  extensionLookupType != T::SubTable::Extension);
   }
 
   protected:
@@ -2290,8 +2590,8 @@
   inline const X& get_subtable (void) const
   {
     switch (u.format) {
-    case 1: return u.format1.template get_subtable<typename T::LookupSubTable> ();
-    default:return Null(typename T::LookupSubTable);
+    case 1: return u.format1.template get_subtable<typename T::SubTable> ();
+    default:return Null(typename T::SubTable);
     }
   }
 
@@ -2318,6 +2618,39 @@
  * GSUB/GPOS Common
  */
 
+struct hb_ot_layout_lookup_accelerator_t
+{
+  template <typename TLookup>
+  inline void init (const TLookup &lookup)
+  {
+    digest.init ();
+    lookup.add_coverage (&digest);
+
+    subtables.init ();
+    OT::hb_get_subtables_context_t c_get_subtables (subtables);
+    lookup.dispatch (&c_get_subtables);
+  }
+  inline void fini (void)
+  {
+    subtables.fini ();
+  }
+
+  inline bool may_have (hb_codepoint_t g) const
+  { return digest.may_have (g); }
+
+  inline 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;
+  }
+
+  private:
+  hb_set_digest_t digest;
+  hb_get_subtables_context_t::array_t subtables;
+};
+
 struct GSUBGPOS
 {
   inline bool has_data (void) const { return version.to_int () != 0; }
@@ -2370,17 +2703,78 @@
     return get_feature (feature_index);
   }
 
+  template <typename TLookup>
+  inline 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);
+
+    typedef OffsetListOf<TLookup> TLookupList;
+    /* TODO Use intersects() to count how many subtables survive? */
+    CastR<OffsetTo<TLookupList> > (out->lookupList)
+      .serialize_subset (c,
+			 this+CastR<const OffsetTo<TLookupList> > (lookupList),
+			 out);
+
+    if (version.to_int () >= 0x00010001u)
+     out->featureVars.serialize_subset (c, this+featureVars, out);
+    return_trace (true);
+  }
+
+  inline unsigned int get_size (void) const
+  {
+    return min_size +
+	   (version.to_int () >= 0x00010001u ? featureVars.static_size : 0);
+  }
+
+  template <typename TLookup>
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
+    typedef OffsetListOf<TLookup> TLookupList;
     return_trace (version.sanitize (c) &&
 		  likely (version.major == 1) &&
 		  scriptList.sanitize (c, this) &&
 		  featureList.sanitize (c, this) &&
-		  lookupList.sanitize (c, this) &&
+		  CastR<OffsetTo<TLookupList> > (lookupList).sanitize (c, this) &&
 		  (version.to_int () < 0x00010001u || featureVars.sanitize (c, this)));
   }
 
+  template <typename T>
+  struct accelerator_t
+  {
+    inline void init (hb_face_t *face)
+    {
+      this->blob = hb_sanitize_context_t().reference_table<T> (face);
+      table = this->blob->template as<T> ();
+
+      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;
+
+      for (unsigned int i = 0; i < this->lookup_count; i++)
+	this->accels[i].init (table->get_lookup (i));
+    }
+
+    inline void fini (void)
+    {
+      for (unsigned int i = 0; i < this->lookup_count; i++)
+	this->accels[i].fini ();
+      free (this->accels);
+      hb_blob_destroy (this->blob);
+    }
+
+    hb_blob_t *blob;
+    const T *table;
+    unsigned int lookup_count;
+    hb_ot_layout_lookup_accelerator_t *accels;
+  };
+
   protected:
   FixedVersion<>version;	/* Version of the GSUB/GPOS table--initially set
 				 * to 0x00010000u */
@@ -2403,4 +2797,4 @@
 } /* namespace OT */
 
 
-#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
+#endif /* HB_OT_LAYOUT_GSUBGPOS_HH */
diff --git a/src/hb-ot-layout-jstf-table.hh b/src/hb-ot-layout-jstf-table.hh
index 7fabdeb..2fb23cb 100644
--- a/src/hb-ot-layout-jstf-table.hh
+++ b/src/hb-ot-layout-jstf-table.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OT_LAYOUT_JSTF_TABLE_HH
 #define HB_OT_LAYOUT_JSTF_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 #include "hb-ot-layout-gpos-table.hh"
 
 
@@ -124,7 +124,7 @@
 struct JstfLangSys : OffsetListOf<JstfPriority>
 {
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<JstfLangSys>::sanitize_closure_t * = nullptr) const
+			const Record_sanitize_closure_t * = nullptr) const
   {
     TRACE_SANITIZE (this);
     return_trace (OffsetListOf<JstfPriority>::sanitize (c));
@@ -165,7 +165,7 @@
   inline const JstfLangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
 
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<JstfScript>::sanitize_closure_t * = nullptr) const
+			const Record_sanitize_closure_t * = nullptr) const
   {
     TRACE_SANITIZE (this);
     return_trace (extenderGlyphs.sanitize (c, this) &&
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 09ff0e6..fb1d9b1 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -28,10 +28,11 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-open-type-private.hh"
-#include "hb-ot-layout-private.hh"
-#include "hb-ot-map-private.hh"
-#include "hb-map-private.hh"
+#include "hb-open-type.hh"
+#include "hb-ot-layout.hh"
+#include "hb-ot-face.hh"
+#include "hb-ot-map.hh"
+#include "hb-map.hh"
 
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
@@ -44,9 +45,73 @@
 #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-name-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;
+}
+
+
+/*
+ * kern
+ */
+
+hb_bool_t
+hb_ot_layout_has_kerning (hb_face_t *face)
+{
+  return _get_kern (face).has_data ();
+}
+
+void
+hb_ot_layout_kern (hb_font_t *font,
+		   hb_buffer_t  *buffer,
+		   hb_mask_t kern_mask)
+{
+  _get_kern (font->face).apply (font, buffer, kern_mask);
+}
+
+
+/*
+ * GDEF
+ */
+
 static bool
 _hb_ot_blacklist_gdef (unsigned int gdef_len,
 		       unsigned int gsub_len,
@@ -149,106 +214,39 @@
   return false;
 }
 
-void hb_ot_layout_t::tables_t::init0 (hb_face_t *face)
+inline void
+OT::GDEF::accelerator_t::init (hb_face_t *face)
 {
-  this->face = face;
-#define HB_OT_LAYOUT_TABLE(Namespace, Type) Type.init0 ();
-  HB_OT_LAYOUT_TABLES
-#undef HB_OT_LAYOUT_TABLE
-}
-void hb_ot_layout_t::tables_t::fini (void)
-{
-#define HB_OT_LAYOUT_TABLE(Namespace, Type) Type.fini ();
-  HB_OT_LAYOUT_TABLES
-#undef HB_OT_LAYOUT_TABLE
-}
+  this->blob = hb_sanitize_context_t().reference_table<GDEF> (face);
 
-hb_ot_layout_t *
-_hb_ot_layout_create (hb_face_t *face)
-{
-  hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
-  if (unlikely (!layout))
-    return nullptr;
-
-  layout->table.init0 (face);
-
-  const OT::GSUB &gsub = *layout->table.GSUB;
-  const OT::GPOS &gpos = *layout->table.GPOS;
-
-  if (unlikely (_hb_ot_blacklist_gdef (layout->table.GDEF.get_blob ()->length,
-				       layout->table.GSUB.get_blob ()->length,
-				       layout->table.GPOS.get_blob ()->length)))
-    layout->table.GDEF.set_stored (hb_blob_get_empty ());
-
-  unsigned int gsub_lookup_count = layout->gsub_lookup_count = gsub.get_lookup_count ();
-  unsigned int gpos_lookup_count = layout->gpos_lookup_count = gpos.get_lookup_count ();
-
-  layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (gsub_lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t));
-  layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (gpos_lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t));
-
-  if (unlikely ((gsub_lookup_count && !layout->gsub_accels) ||
-		(gpos_lookup_count && !layout->gpos_accels)))
+  if (unlikely (_hb_ot_blacklist_gdef (this->blob->length,
+				       _get_gsub_blob (face)->length,
+				       _get_gpos_blob (face)->length)))
   {
-    _hb_ot_layout_destroy (layout);
-    return nullptr;
+    hb_blob_destroy (this->blob);
+    this->blob = hb_blob_get_empty ();
   }
 
-  for (unsigned int i = 0; i < gsub_lookup_count; i++)
-    layout->gsub_accels[i].init (gsub.get_lookup (i));
-  for (unsigned int i = 0; i < gpos_lookup_count; i++)
-    layout->gpos_accels[i].init (gpos.get_lookup (i));
-
-  return layout;
+  table = this->blob->as<GDEF> ();
 }
 
-void
-_hb_ot_layout_destroy (hb_ot_layout_t *layout)
+static void
+_hb_ot_layout_set_glyph_props (hb_font_t *font,
+			       hb_buffer_t *buffer)
 {
-  if (layout->gsub_accels)
-    for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
-      layout->gsub_accels[i].fini ();
-  if (layout->gpos_accels)
-    for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
-      layout->gpos_accels[i].fini ();
+  _hb_buffer_assert_gsubgpos_vars (buffer);
 
-  free (layout->gsub_accels);
-  free (layout->gpos_accels);
-
-  layout->table.fini ();
-
-  free (layout);
+  const OT::GDEF &gdef = _get_gdef (font->face);
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    _hb_glyph_info_set_glyph_props (&buffer->info[i], gdef.get_glyph_props (buffer->info[i].codepoint));
+    _hb_glyph_info_clear_lig_props (&buffer->info[i]);
+    buffer->info[i].syllable() = 0;
+  }
 }
 
-// static inline const OT::BASE&
-// _get_base (hb_face_t *face)
-// {
-//   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::BASE);
-//   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-//   return *(layout->base.get ());
-// }
-
-static inline 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_layout_from_face (face)->table.GDEF;
-}
-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_layout_from_face (face)->table.GSUB;
-}
-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_layout_from_face (face)->table.GPOS;
-}
-
-/*
- * GDEF
- */
+/* Public API */
 
 hb_bool_t
 hb_ot_layout_has_glyph_classes (hb_face_t *face)
@@ -324,7 +322,7 @@
 				    hb_tag_t      table_tag,
 				    unsigned int  start_offset,
 				    unsigned int *script_count /* IN/OUT */,
-				    hb_tag_t     *script_tags /* OUT */)
+				    hb_tag_t     *script_tags  /* OUT */)
 {
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
@@ -370,17 +368,36 @@
 				  unsigned int   *script_index,
 				  hb_tag_t       *chosen_script)
 {
+  const hb_tag_t *t;
+  for (t = script_tags; *t; t++);
+  return hb_ot_layout_table_select_script (face, table_tag, t - script_tags, script_tags, script_index, chosen_script);
+}
+
+/**
+ * hb_ot_layout_table_select_script:
+ *
+ * Since: 2.0.0
+ **/
+hb_bool_t
+hb_ot_layout_table_select_script (hb_face_t      *face,
+				  hb_tag_t        table_tag,
+				  unsigned int    script_count,
+				  const hb_tag_t *script_tags,
+				  unsigned int   *script_index  /* OUT */,
+				  hb_tag_t       *chosen_script /* OUT */)
+{
   static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), "");
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+  unsigned int i;
 
-  while (*script_tags)
+  for (i = 0; i < script_count; i++)
   {
-    if (g.find_script_index (*script_tags, script_index)) {
+    if (g.find_script_index (script_tags[i], script_index))
+    {
       if (chosen_script)
-        *chosen_script = *script_tags;
+        *chosen_script = script_tags[i];
       return true;
     }
-    script_tags++;
   }
 
   /* try finding 'DFLT' */
@@ -416,7 +433,7 @@
 				     hb_tag_t      table_tag,
 				     unsigned int  start_offset,
 				     unsigned int *feature_count /* IN/OUT */,
-				     hb_tag_t     *feature_tags /* OUT */)
+				     hb_tag_t     *feature_tags  /* OUT */)
 {
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
@@ -452,7 +469,7 @@
 				       unsigned int  script_index,
 				       unsigned int  start_offset,
 				       unsigned int *language_count /* IN/OUT */,
-				       hb_tag_t     *language_tags /* OUT */)
+				       hb_tag_t     *language_tags  /* OUT */)
 {
   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
 
@@ -466,13 +483,33 @@
 				   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);
+}
+
+/**
+ * hb_ot_layout_script_select_language:
+ *
+ * Since: 2.0.0
+ **/
+hb_bool_t
+hb_ot_layout_script_select_language (hb_face_t      *face,
+				     hb_tag_t        table_tag,
+				     unsigned int    script_index,
+				     unsigned int    language_count,
+				     const hb_tag_t *language_tags,
+				     unsigned int   *language_index /* OUT */)
+{
   static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX), "");
   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
+  unsigned int i;
 
-  if (s.find_lang_sys_index (language_tag, language_index))
-    return true;
+  for (i = 0; i < language_count; i++)
+  {
+    if (s.find_lang_sys_index (language_tags[i], language_index))
+      return true;
+  }
 
-  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
+  /* try finding 'dflt' */
   if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
     return false;
 
@@ -537,7 +574,7 @@
 					   unsigned int  script_index,
 					   unsigned int  language_index,
 					   unsigned int  start_offset,
-					   unsigned int *feature_count /* IN/OUT */,
+					   unsigned int *feature_count   /* IN/OUT */,
 					   unsigned int *feature_indexes /* OUT */)
 {
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
@@ -553,7 +590,7 @@
 					unsigned int  language_index,
 					unsigned int  start_offset,
 					unsigned int *feature_count /* IN/OUT */,
-					hb_tag_t     *feature_tags /* OUT */)
+					hb_tag_t     *feature_tags  /* 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);
@@ -607,7 +644,7 @@
 				  hb_tag_t      table_tag,
 				  unsigned int  feature_index,
 				  unsigned int  start_offset,
-				  unsigned int *lookup_count /* IN/OUT */,
+				  unsigned int *lookup_count   /* IN/OUT */,
 				  unsigned int *lookup_indexes /* OUT */)
 {
   return hb_ot_layout_feature_with_variations_get_lookups (face,
@@ -633,11 +670,11 @@
   {
     case HB_OT_TAG_GSUB:
     {
-      return hb_ot_layout_from_face (face)->gsub_lookup_count;
+      return hb_ot_face_data (face)->GSUB->lookup_count;
     }
     case HB_OT_TAG_GPOS:
     {
-      return hb_ot_layout_from_face (face)->gpos_lookup_count;
+      return hb_ot_face_data (face)->GPOS->lookup_count;
     }
   }
   return 0;
@@ -745,11 +782,12 @@
     for (; *languages; languages++)
     {
       unsigned int language_index;
-      if (hb_ot_layout_script_find_language (face,
-					     table_tag,
-					     script_index,
-					     *languages,
-					     &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,
@@ -834,10 +872,10 @@
 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
 				    hb_tag_t      table_tag,
 				    unsigned int  lookup_index,
-				    hb_set_t     *glyphs_before, /* OUT. May be nullptr */
-				    hb_set_t     *glyphs_input,  /* OUT. May be nullptr */
-				    hb_set_t     *glyphs_after,  /* OUT. May be nullptr */
-				    hb_set_t     *glyphs_output  /* OUT. May be nullptr */)
+				    hb_set_t     *glyphs_before, /* OUT.  May be NULL */
+				    hb_set_t     *glyphs_input,  /* OUT.  May be NULL */
+				    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;
 
@@ -851,13 +889,13 @@
   {
     case HB_OT_TAG_GSUB:
     {
-      const OT::SubstLookup& l = hb_ot_layout_from_face (face)->table.GSUB->get_lookup (lookup_index);
+      const OT::SubstLookup& l = hb_ot_face_data (face)->GSUB->table->get_lookup (lookup_index);
       l.collect_glyphs (&c);
       return;
     }
     case HB_OT_TAG_GPOS:
     {
-      const OT::PosLookup& l = hb_ot_layout_from_face (face)->table.GPOS->get_lookup (lookup_index);
+      const OT::PosLookup& l = hb_ot_face_data (face)->GPOS->table->get_lookup (lookup_index);
       l.collect_glyphs (&c);
       return;
     }
@@ -930,18 +968,19 @@
 					   unsigned int          glyphs_length,
 					   hb_bool_t             zero_context)
 {
-  if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
+  if (unlikely (lookup_index >= hb_ot_face_data (face)->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_layout_from_face (face)->table.GSUB->get_lookup (lookup_index);
+  const OT::SubstLookup& l = hb_ot_face_data (face)->GSUB->table->get_lookup (lookup_index);
 
-  return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index]);
+  return l.would_apply (&c, &hb_ot_face_data (face)->GSUB->accels[lookup_index]);
 }
 
 void
-hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
+hb_ot_layout_substitute_start (hb_font_t    *font,
+			       hb_buffer_t  *buffer)
 {
-  OT::GSUB::substitute_start (font, buffer);
+_hb_ot_layout_set_glyph_props (font, buffer);
 }
 
 /**
@@ -1034,11 +1073,11 @@
  **/
 hb_bool_t
 hb_ot_layout_get_size_params (hb_face_t    *face,
-			      unsigned int *design_size,       /* OUT.  May be nullptr */
-			      unsigned int *subfamily_id,      /* OUT.  May be nullptr */
-			      unsigned int *subfamily_name_id, /* OUT.  May be nullptr */
-			      unsigned int *range_start,       /* OUT.  May be nullptr */
-			      unsigned int *range_end          /* OUT.  May be nullptr */)
+			      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 */)
 {
   const OT::GPOS &gpos = _get_gpos (face);
   const hb_tag_t tag = HB_TAG ('s','i','z','e');
@@ -1053,30 +1092,152 @@
 
       if (params.designSize)
       {
-#define PARAM(a, A) if (a) *a = params.A
-	PARAM (design_size, designSize);
-	PARAM (subfamily_id, subfamilyID);
-	PARAM (subfamily_name_id, subfamilyNameID);
-	PARAM (range_start, rangeStart);
-	PARAM (range_end, rangeEnd);
-#undef PARAM
+	if (design_size) *design_size = params.designSize;
+	if (subfamily_id) *subfamily_id = params.subfamilyID;
+	if (subfamily_name_id) *subfamily_name_id = params.subfamilyNameID;
+	if (range_start) *range_start = params.rangeStart;
+	if (range_end) *range_end = params.rangeEnd;
 
 	return true;
       }
     }
   }
 
-#define PARAM(a, A) if (a) *a = 0
-  PARAM (design_size, designSize);
-  PARAM (subfamily_id, subfamilyID);
-  PARAM (subfamily_name_id, subfamilyNameID);
-  PARAM (range_start, rangeStart);
-  PARAM (range_end, rangeEnd);
-#undef PARAM
+  if (design_size) *design_size = 0;
+  if (subfamily_id) *subfamily_id = 0;
+  if (subfamily_name_id) *subfamily_name_id = 0;
+  if (range_start) *range_start = 0;
+  if (range_end) *range_end = 0;
 
   return false;
 }
 
+/**
+ * hb_ot_layout_feature_get_name_ids:
+ * @face: #hb_face_t to work upon
+ * @table_tag:
+ * @feature_index:
+ * @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
+ *              that an application can use for tooltip text for this
+ *              feature. (May be NULL.)
+ * @sample_id: (out) (allow-none): The ‘name’ table name ID that specifies sample text
+ *             that illustrates the effect of this feature. (May be NULL.)
+ * @num_named_parameters: (out) (allow-none):  Number of named parameters. (May be zero.)
+ * @first_param_id: (out) (allow-none): The first ‘name’ table name ID used to specify
+ *                  strings for user-interface labels for the feature
+ *                  parameters. (Must be zero if numParameters is zero.)
+ *
+ * Fetches name indices from feature parameters for "Stylistic Set" ('ssXX') or
+ * "Character Variant" ('cvXX') features.
+ *
+ * Return value: true if data found, false otherwise
+ *
+ * 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 */)
+{
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+
+  hb_tag_t feature_tag = g.get_feature_tag (feature_index);
+  const OT::Feature &f = g.get_feature (feature_index);
+
+  const OT::FeatureParams &feature_params = f.get_feature_params ();
+  if (&feature_params != &Null (OT::FeatureParams))
+  {
+    const OT::FeatureParamsStylisticSet& ss_params =
+      feature_params.get_stylistic_set_params (feature_tag);
+    if (&ss_params != &Null (OT::FeatureParamsStylisticSet)) /* ssXX */
+    {
+      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 (num_named_parameters) *num_named_parameters = 0;
+      if (first_param_id) *first_param_id = HB_NAME_ID_INVALID;
+      return true;
+    }
+    const OT::FeatureParamsCharacterVariants& cv_params =
+      feature_params.get_character_variants_params (feature_tag);
+    if (&cv_params != &Null (OT::FeatureParamsCharacterVariants)) /* cvXX */
+    {
+      if (label_id) *label_id = cv_params.featUILableNameID;
+      if (tooltip_id) *tooltip_id = cv_params.featUITooltipTextNameID;
+      if (sample_id) *sample_id = cv_params.sampleTextNameID;
+      if (num_named_parameters) *num_named_parameters = cv_params.numNamedParameters;
+      if (first_param_id) *first_param_id = cv_params.firstParamUILabelNameID;
+      return true;
+    }
+  }
+
+  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 (num_named_parameters) *num_named_parameters = 0;
+  if (first_param_id) *first_param_id = HB_NAME_ID_INVALID;
+  return false;
+}
+
+/**
+ * hb_ot_layout_feature_get_characters::
+ * @face: #hb_face_t to work upon
+ * @table_tag:
+ * @feature_index:
+ * @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
+ *                lower than input buffer (or consider using just a bigger buffer for
+ *                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
+ *              of the characters for which this feature provides glyph variants.
+ *
+ * Fetches characters listed by designer under feature parameters for "Character
+ * Variant" ("cvXX") features.
+ *
+ * Return value: Number of total sample characters in the cvXX feature.
+ *
+ * Since: 2.0.0
+ **/
+unsigned int
+hb_ot_layout_feature_get_characters (hb_face_t      *face,
+				     hb_tag_t        table_tag,
+				     unsigned int    feature_index,
+				     unsigned int    start_offset,
+				     unsigned int   *char_count, /* IN/OUT.  May be NULL */
+				     hb_codepoint_t *characters  /* OUT.     May be NULL */)
+{
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+
+  hb_tag_t feature_tag = g.get_feature_tag (feature_index);
+  const OT::Feature &f = g.get_feature (feature_index);
+
+  const OT::FeatureParams &feature_params = f.get_feature_params ();
+
+  const OT::FeatureParamsCharacterVariants& cv_params =
+    feature_params.get_character_variants_params(feature_tag);
+
+  unsigned int len = 0;
+  if (char_count && characters && start_offset < cv_params.characters.len)
+  {
+    len = MIN (cv_params.characters.len - start_offset, *char_count);
+    for (unsigned int i = 0; i < len; ++i)
+      characters[i] = cv_params.characters[start_offset + i];
+  }
+  if (char_count) *char_count = len;
+  return cv_params.characters.len;
+}
+
 
 /*
  * Parts of different types are implemented here such that they have direct
@@ -1086,86 +1247,36 @@
 
 struct GSUBProxy
 {
-  static const unsigned int table_index = 0;
+  enum { table_index = 0 };
   static const bool inplace = false;
   typedef OT::SubstLookup Lookup;
 
   GSUBProxy (hb_face_t *face) :
-    table (*hb_ot_layout_from_face (face)->table.GSUB),
-    accels (hb_ot_layout_from_face (face)->gsub_accels) {}
+    table (*hb_ot_face_data (face)->GSUB->table),
+    accels (hb_ot_face_data (face)->GSUB->accels) {}
 
   const OT::GSUB &table;
-  const hb_ot_layout_lookup_accelerator_t *accels;
+  const OT::hb_ot_layout_lookup_accelerator_t *accels;
 };
 
 struct GPOSProxy
 {
-  static const unsigned int table_index = 1;
+  enum { table_index = 1 };
   static const bool inplace = true;
   typedef OT::PosLookup Lookup;
 
   GPOSProxy (hb_face_t *face) :
-    table (*hb_ot_layout_from_face (face)->table.GPOS),
-    accels (hb_ot_layout_from_face (face)->gpos_accels) {}
+    table (*hb_ot_face_data (face)->GPOS->table),
+    accels (hb_ot_face_data (face)->GPOS->accels) {}
 
   const OT::GPOS &table;
-  const hb_ot_layout_lookup_accelerator_t *accels;
+  const OT::hb_ot_layout_lookup_accelerator_t *accels;
 };
 
 
-struct hb_get_subtables_context_t :
-       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)
-  {
-    const Type *typed_obj = (const Type *) obj;
-    return typed_obj->apply (c);
-  }
-
-  typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_ot_apply_context_t *c);
-
-  struct hb_applicable_t
-  {
-    inline void init (const void *obj_, hb_apply_func_t apply_func_)
-    {
-      obj = obj_;
-      apply_func = apply_func_;
-    }
-
-    inline bool apply (OT::hb_ot_apply_context_t *c) const { return apply_func (obj, c); }
-
-    private:
-    const void *obj;
-    hb_apply_func_t apply_func;
-  };
-
-  typedef hb_auto_t<hb_vector_t<hb_applicable_t> > array_t;
-
-  /* Dispatch interface. */
-  inline const char *get_name (void) { return "GET_SUBTABLES"; }
-  template <typename T>
-  inline 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; }
-
-  hb_get_subtables_context_t (array_t &array_) :
-			      array (array_),
-			      debug_depth (0) {}
-
-  array_t &array;
-  unsigned int debug_depth;
-};
-
 static inline bool
 apply_forward (OT::hb_ot_apply_context_t *c,
-	       const hb_ot_layout_lookup_accelerator_t &accel,
-	       const hb_get_subtables_context_t::array_t &subtables)
+	       const OT::hb_ot_layout_lookup_accelerator_t &accel)
 {
   bool ret = false;
   hb_buffer_t *buffer = c->buffer;
@@ -1176,12 +1287,7 @@
 	(buffer->cur().mask & c->lookup_mask) &&
 	c->check_glyph_property (&buffer->cur(), c->lookup_props))
      {
-       for (unsigned int i = 0; i < subtables.len; i++)
-         if (subtables[i].apply (c))
-	 {
-	   applied = true;
-	   break;
-	 }
+       applied = accel.apply (c);
      }
 
     if (applied)
@@ -1194,8 +1300,7 @@
 
 static inline bool
 apply_backward (OT::hb_ot_apply_context_t *c,
-	       const hb_ot_layout_lookup_accelerator_t &accel,
-	       const hb_get_subtables_context_t::array_t &subtables)
+	       const OT::hb_ot_layout_lookup_accelerator_t &accel)
 {
   bool ret = false;
   hb_buffer_t *buffer = c->buffer;
@@ -1205,12 +1310,8 @@
 	(buffer->cur().mask & c->lookup_mask) &&
 	c->check_glyph_property (&buffer->cur(), c->lookup_props))
     {
-     for (unsigned int i = 0; i < subtables.len; i++)
-       if (subtables[i].apply (c))
-       {
-	 ret = true;
-	 break;
-       }
+     if (accel.apply (c))
+       ret = true;
     }
     /* The reverse lookup doesn't "advance" cursor (for good reason). */
     buffer->idx--;
@@ -1224,7 +1325,7 @@
 static inline void
 apply_string (OT::hb_ot_apply_context_t *c,
 	      const typename Proxy::Lookup &lookup,
-	      const hb_ot_layout_lookup_accelerator_t &accel)
+	      const OT::hb_ot_layout_lookup_accelerator_t &accel)
 {
   hb_buffer_t *buffer = c->buffer;
 
@@ -1233,10 +1334,6 @@
 
   c->set_lookup_props (lookup.get_props ());
 
-  hb_get_subtables_context_t::array_t subtables;
-  hb_get_subtables_context_t c_get_subtables (subtables);
-  lookup.dispatch (&c_get_subtables);
-
   if (likely (!lookup.is_reverse ()))
   {
     /* in/out forward substitution/positioning */
@@ -1245,7 +1342,7 @@
     buffer->idx = 0;
 
     bool ret;
-    ret = apply_forward (c, accel, subtables);
+    ret = apply_forward (c, accel);
     if (ret)
     {
       if (!Proxy::inplace)
@@ -1261,7 +1358,7 @@
       buffer->remove_output ();
     buffer->idx = buffer->len - 1;
 
-    apply_backward (c, accel, subtables);
+    apply_backward (c, accel);
   }
 }
 
@@ -1286,6 +1383,11 @@
       c.set_lookup_mask (lookups[table_index][i].mask);
       c.set_auto_zwj (lookups[table_index][i].auto_zwj);
       c.set_auto_zwnj (lookups[table_index][i].auto_zwnj);
+      if (lookups[table_index][i].random)
+      {
+	c.set_random (true);
+	buffer->unsafe_to_break_all ();
+      }
       apply_string<Proxy> (&c,
 			   proxy.table.get_lookup (lookup_index),
 			   proxy.accels[lookup_index]);
@@ -1315,31 +1417,7 @@
 void
 hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c,
 				const OT::SubstLookup &lookup,
-				const hb_ot_layout_lookup_accelerator_t &accel)
+				const OT::hb_ot_layout_lookup_accelerator_t &accel)
 {
   apply_string<GSUBProxy> (c, lookup, accel);
 }
-
-
-
-
-/*
- * OT::BASE
- */
-
-// /**
-//  * hb_ot_base_has_data:
-//  * @face: #hb_face_t to test
-//  *
-//  * This function allows to verify the presence of an OpenType BASE table on the
-//  * face.
-//  *
-//  * Return value: true if face has a BASE table, false otherwise
-//  *
-//  * Since: XXX
-//  **/
-// hb_bool_t
-// hb_ot_base_has_data (hb_face_t *face)
-// {
-//   return _get_base (face).has_data ();
-// }
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index 586fb15..9bd18c8 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -34,6 +34,7 @@
 #include "hb.h"
 
 #include "hb-ot-tag.h"
+#include "hb-ot-name.h"
 
 HB_BEGIN_DECLS
 
@@ -111,13 +112,13 @@
 				hb_tag_t      script_tag,
 				unsigned int *script_index);
 
-/* Like find_script, but takes zero-terminated array of scripts to test */
 HB_EXTERN hb_bool_t
-hb_ot_layout_table_choose_script (hb_face_t      *face,
+hb_ot_layout_table_select_script (hb_face_t      *face,
 				  hb_tag_t        table_tag,
+				  unsigned int    script_count,
 				  const hb_tag_t *script_tags,
-				  unsigned int   *script_index,
-				  hb_tag_t       *chosen_script);
+				  unsigned int   *script_index /* OUT */,
+				  hb_tag_t       *chosen_script /* OUT */);
 
 HB_EXTERN unsigned int
 hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
@@ -135,11 +136,12 @@
 				       hb_tag_t     *language_tags /* OUT */);
 
 HB_EXTERN 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_ot_layout_script_select_language (hb_face_t      *face,
+				     hb_tag_t        table_tag,
+				     unsigned int    script_index,
+				     unsigned int    language_count,
+				     const hb_tag_t *language_tags,
+				     unsigned int   *language_index /* OUT */);
 
 HB_EXTERN hb_bool_t
 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
@@ -214,10 +216,10 @@
 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
 				    hb_tag_t      table_tag,
 				    unsigned int  lookup_index,
-				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
-				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
-				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
-				    hb_set_t     *glyphs_output  /* OUT. May be NULL */);
+				    hb_set_t     *glyphs_before, /* OUT.  May be NULL */
+				    hb_set_t     *glyphs_input,  /* OUT.  May be NULL */
+				    hb_set_t     *glyphs_after,  /* OUT.  May be NULL */
+				    hb_set_t     *glyphs_output  /* OUT.  May be NULL */);
 
 #ifdef HB_NOT_IMPLEMENTED
 typedef struct
@@ -325,11 +327,30 @@
 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 */
+			      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_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_EXTERN unsigned int
+hb_ot_layout_feature_get_characters (hb_face_t      *face,
+				     hb_tag_t        table_tag,
+				     unsigned int    feature_index,
+				     unsigned int    start_offset,
+				     unsigned int   *char_count    /* IN/OUT.  May be NULL */,
+				     hb_codepoint_t *characters    /* OUT.     May be NULL */);
+
 /*
  * BASE
  */
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout.hh
similarity index 77%
rename from src/hb-ot-layout-private.hh
rename to src/hb-ot-layout.hh
index 612bc7f..64b3d74 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout.hh
@@ -26,15 +26,40 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_LAYOUT_PRIVATE_HH
-#define HB_OT_LAYOUT_PRIVATE_HH
+#ifndef HB_OT_LAYOUT_HH
+#define HB_OT_LAYOUT_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-font-private.hh"
-#include "hb-buffer-private.hh"
-#include "hb-set-digest-private.hh"
-#include "hb-open-type-private.hh"
+#include "hb-font.hh"
+#include "hb-buffer.hh"
+#include "hb-open-type.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);
+
+
+/*
+ * kern
+ */
+
+HB_INTERNAL hb_bool_t
+hb_ot_layout_has_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);
 
 
 /* Private API corresponding to hb-ot-layout.h: */
@@ -87,17 +112,16 @@
 			       hb_buffer_t  *buffer);
 
 
-struct hb_ot_layout_lookup_accelerator_t;
-
 namespace OT {
   struct hb_ot_apply_context_t;
   struct SubstLookup;
+  struct hb_ot_layout_lookup_accelerator_t;
 }
 
 HB_INTERNAL void
 hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c,
 				const OT::SubstLookup &lookup,
-				const hb_ot_layout_lookup_accelerator_t &accel);
+				const OT::hb_ot_layout_lookup_accelerator_t &accel);
 
 
 /* Should be called before all the position_lookup's are done. */
@@ -116,112 +140,6 @@
 				      hb_buffer_t  *buffer);
 
 
-
-/*
- * hb_ot_layout_t
- */
-
-struct hb_ot_layout_lookup_accelerator_t
-{
-  template <typename TLookup>
-  inline void init (const TLookup &lookup)
-  {
-    digest.init ();
-    lookup.add_coverage (&digest);
-  }
-
-  inline void fini (void)
-  {
-  }
-
-  inline bool may_have (hb_codepoint_t g) const {
-    return digest.may_have (g);
-  }
-
-  private:
-  hb_set_digest_t digest;
-};
-
-/* 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_LAYOUT_TABLES \
-    /* OpenType shaping. */ \
-    HB_OT_LAYOUT_TABLE(OT, GDEF) \
-    HB_OT_LAYOUT_TABLE(OT, GSUB) \
-    HB_OT_LAYOUT_TABLE(OT, GPOS) \
-    HB_OT_LAYOUT_TABLE(OT, JSTF) \
-    HB_OT_LAYOUT_TABLE(OT, BASE) \
-    /* AAT shaping. */ \
-    HB_OT_LAYOUT_TABLE(AAT, morx) \
-    HB_OT_LAYOUT_TABLE(AAT, kerx) \
-    HB_OT_LAYOUT_TABLE(AAT, ankr) \
-    HB_OT_LAYOUT_TABLE(AAT, trak) \
-    /* OpenType variations. */ \
-    HB_OT_LAYOUT_TABLE(OT, fvar) \
-    HB_OT_LAYOUT_TABLE(OT, avar) \
-    HB_OT_LAYOUT_TABLE(OT, MVAR) \
-    /* OpenType color. */ \
-    HB_OT_LAYOUT_TABLE(OT, COLR) \
-    HB_OT_LAYOUT_TABLE(OT, CPAL) \
-    HB_OT_LAYOUT_TABLE(OT, CBDT) \
-    HB_OT_LAYOUT_TABLE(OT, CBLC) \
-    HB_OT_LAYOUT_TABLE(OT, sbix) \
-    HB_OT_LAYOUT_TABLE(OT, svg) \
-    /* OpenType math. */ \
-    HB_OT_LAYOUT_TABLE(OT, MATH) \
-    /* OpenType fundamentals. */ \
-    HB_OT_LAYOUT_TABLE(OT, post) \
-    /* */
-
-/* Declare tables. */
-#define HB_OT_LAYOUT_TABLE(Namespace, Type) namespace Namespace { struct Type; }
-HB_OT_LAYOUT_TABLES
-#undef HB_OT_LAYOUT_TABLE
-
-struct hb_ot_layout_t
-{
-  unsigned int gsub_lookup_count;
-  unsigned int gpos_lookup_count;
-
-  hb_ot_layout_lookup_accelerator_t *gsub_accels;
-  hb_ot_layout_lookup_accelerator_t *gpos_accels;
-
-  /* Various non-shaping tables. */
-  struct tables_t
-  {
-    HB_INTERNAL void init0 (hb_face_t *face);
-    HB_INTERNAL void fini (void);
-
-#define HB_OT_LAYOUT_TABLE_ORDER(Namespace, Type) \
-      HB_PASTE (ORDER_, HB_PASTE (Namespace, HB_PASTE (_, Type)))
-    enum order_t
-    {
-      ORDER_ZERO,
-#define HB_OT_LAYOUT_TABLE(Namespace, Type) \
-	HB_OT_LAYOUT_TABLE_ORDER (Namespace, Type),
-      HB_OT_LAYOUT_TABLES
-#undef HB_OT_LAYOUT_TABLE
-    };
-
-    hb_face_t *face; /* MUST be JUST before the lazy loaders. */
-#define HB_OT_LAYOUT_TABLE(Namespace, Type) \
-    hb_table_lazy_loader_t<struct Namespace::Type, HB_OT_LAYOUT_TABLE_ORDER (Namespace, Type)> Type;
-    HB_OT_LAYOUT_TABLES
-#undef HB_OT_LAYOUT_TABLE
-  } table;
-};
-
-
-HB_INTERNAL hb_ot_layout_t *
-_hb_ot_layout_create (hb_face_t *face);
-
-HB_INTERNAL void
-_hb_ot_layout_destroy (hb_ot_layout_t *layout);
-
-
-#define hb_ot_layout_from_face(face) ((hb_ot_layout_t *) face->shaper_data.ot.get_relaxed ())
-
-
 /*
  * Buffer var routines.
  */
@@ -239,12 +157,12 @@
 #define foreach_syllable(buffer, start, end) \
   for (unsigned int \
        _count = buffer->len, \
-       start = 0, end = _count ? _next_syllable (buffer, 0) : 0; \
+       start = 0, end = _count ? _hb_next_syllable (buffer, 0) : 0; \
        start < _count; \
-       start = end, end = _next_syllable (buffer, start))
+       start = end, end = _hb_next_syllable (buffer, start))
 
 static inline unsigned int
-_next_syllable (hb_buffer_t *buffer, unsigned int start)
+_hb_next_syllable (hb_buffer_t *buffer, unsigned int start)
 {
   hb_glyph_info_t *info = buffer->info;
   unsigned int count = buffer->len;
@@ -267,7 +185,7 @@
  *   * Whether it's one of the three Mongolian Free Variation Selectors,
  *     CGJ, or other characters that are hidden but should not be ignored
  *     like most other Default_Ignorable()s do during matching.
- *   * One free bit right now.
+ *   * Whether it's a grapheme continuation.
  *
  * The high-byte has different meanings, switched by the Gen-Cat:
  * - For Mn,Mc,Me: the modified Combining_Class.
@@ -279,8 +197,8 @@
 enum hb_unicode_props_flags_t {
   UPROPS_MASK_GEN_CAT	= 0x001Fu,
   UPROPS_MASK_IGNORABLE	= 0x0020u,
-  UPROPS_MASK_HIDDEN	= 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..3,
-                                    * or TAG characters */
+  UPROPS_MASK_HIDDEN	= 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..3, or TAG characters */
+  UPROPS_MASK_CONTINUATION=0x0080u,
 
   /* If GEN_CAT=FORMAT, top byte masks: */
   UPROPS_MASK_Cf_ZWJ	= 0x0100u,
@@ -299,6 +217,7 @@
   if (u >= 0x80)
   {
     buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII;
+
     if (unlikely (unicode->is_default_ignorable (u)))
     {
       buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES;
@@ -324,35 +243,11 @@
 	props |= UPROPS_MASK_HIDDEN;
       }
     }
-    else if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_NON_ENCLOSING_MARK_OR_MODIFIER_SYMBOL (gen_cat)))
+
+    if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (gen_cat)))
     {
-      /* The above check is just an optimization to let in only things we need further
-       * processing on. */
-
-      /* Only Mn and Mc can have non-zero ccc:
-       * https://unicode.org/policies/stability_policy.html#Property_Value
-       * """
-       * Canonical_Combining_Class, General_Category
-       * All characters other than those with General_Category property values
-       * Spacing_Mark (Mc) and Nonspacing_Mark (Mn) have the Canonical_Combining_Class
-       * property value 0.
-       * 1.1.5+
-       * """
-       *
-       * Also, all Mn's that are Default_Ignorable, have ccc=0, hence
-       * the "else if".
-       */
-      props |= unicode->modified_combining_class (info->codepoint)<<8;
-
-      /* Recategorize emoji skin-tone modifiers as Unicode mark, so they
-       * behave correctly in non-native directionality.  They originally
-       * are MODIFIER_SYMBOL.  Fixes:
-       * https://github.com/harfbuzz/harfbuzz/issues/169
-       */
-      if (unlikely (hb_in_range (u, 0x1F3FBu, 0x1F3FFu)))
-      {
-	props = gen_cat = HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK;
-      }
+      props |= UPROPS_MASK_CONTINUATION;
+      props |= unicode->modified_combining_class (u)<<8;
     }
   }
 
@@ -391,29 +286,6 @@
 {
   return _hb_glyph_info_is_unicode_mark (info) ? info->unicode_props()>>8 : 0;
 }
-
-
-/* Loop over grapheme. Based on foreach_cluster(). */
-#define foreach_grapheme(buffer, start, end) \
-  for (unsigned int \
-       _count = buffer->len, \
-       start = 0, end = _count ? _next_grapheme (buffer, 0) : 0; \
-       start < _count; \
-       start = end, end = _next_grapheme (buffer, start))
-
-static inline unsigned int
-_next_grapheme (hb_buffer_t *buffer, unsigned int start)
-{
-  hb_glyph_info_t *info = buffer->info;
-  unsigned int count = buffer->len;
-
-  while (++start < count && _hb_glyph_info_is_unicode_mark (&info[start]))
-    ;
-
-  return start;
-}
-
-
 #define info_cc(info) (_hb_glyph_info_get_modified_combining_class (&(info)))
 
 static inline bool
@@ -458,6 +330,41 @@
   info->unicode_props() &= ~ UPROPS_MASK_HIDDEN;
 }
 
+static inline void
+_hb_glyph_info_set_continuation (hb_glyph_info_t *info)
+{
+  info->unicode_props() |= UPROPS_MASK_CONTINUATION;
+}
+static inline void
+_hb_glyph_info_reset_continuation (hb_glyph_info_t *info)
+{
+  info->unicode_props() &= ~ UPROPS_MASK_CONTINUATION;
+}
+static inline bool
+_hb_glyph_info_is_continuation (const hb_glyph_info_t *info)
+{
+  return info->unicode_props() & UPROPS_MASK_CONTINUATION;
+}
+/* Loop over grapheme. Based on foreach_cluster(). */
+#define foreach_grapheme(buffer, start, end) \
+  for (unsigned int \
+       _count = buffer->len, \
+       start = 0, end = _count ? _hb_next_grapheme (buffer, 0) : 0; \
+       start < _count; \
+       start = end, end = _hb_next_grapheme (buffer, start))
+
+static inline unsigned int
+_hb_next_grapheme (hb_buffer_t *buffer, unsigned int start)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+
+  while (++start < count && _hb_glyph_info_is_continuation (&info[start]))
+    ;
+
+  return start;
+}
+
 static inline bool
 _hb_glyph_info_is_unicode_format (const hb_glyph_info_t *info)
 {
@@ -701,4 +608,4 @@
 #undef lig_props
 #undef glyph_props
 
-#endif /* HB_OT_LAYOUT_PRIVATE_HH */
+#endif /* HB_OT_LAYOUT_HH */
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index f26cac9..45d7dbd 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -26,9 +26,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-map-private.hh"
+#include "hb-ot-map.hh"
 
-#include "hb-ot-layout-private.hh"
+#include "hb-ot-layout.hh"
 
 
 void hb_ot_map_t::collect_lookups (unsigned int table_index, hb_set_t *lookups_out) const
@@ -54,16 +54,17 @@
   /* Fetch script/language indices for GSUB/GPOS.  We need these later to skip
    * features not available in either table and not waste precious bits for them. */
 
-  hb_tag_t script_tags[3] = {HB_TAG_NONE, HB_TAG_NONE, HB_TAG_NONE};
-  hb_tag_t language_tag;
+  unsigned int script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
+  unsigned int language_count = HB_OT_MAX_TAGS_PER_LANGUAGE;
+  hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
+  hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
 
-  hb_ot_tags_from_script (props.script, &script_tags[0], &script_tags[1]);
-  language_tag = hb_ot_tag_from_language (props.language);
+  hb_ot_tags_from_script_and_language (props.script, props.language, &script_count, script_tags, &language_count, language_tags);
 
   for (unsigned int table_index = 0; table_index < 2; table_index++) {
     hb_tag_t table_tag = table_tags[table_index];
-    found_script[table_index] = (bool) hb_ot_layout_table_choose_script (face, table_tag, script_tags, &script_index[table_index], &chosen_script[table_index]);
-    hb_ot_layout_script_find_language (face, table_tag, script_index[table_index], language_tag, &language_index[table_index]);
+    found_script[table_index] = (bool) hb_ot_layout_table_select_script (face, table_tag, script_count, script_tags, &script_index[table_index], &chosen_script[table_index]);
+    hb_ot_layout_script_select_language (face, table_tag, script_index[table_index], language_count, language_tags, &language_index[table_index]);
   }
 }
 
@@ -74,8 +75,9 @@
     stages[table_index].fini ();
 }
 
-void hb_ot_map_builder_t::add_feature (hb_tag_t tag, unsigned int value,
-				       hb_ot_map_feature_flags_t flags)
+void hb_ot_map_builder_t::add_feature (hb_tag_t tag,
+				       hb_ot_map_feature_flags_t flags,
+				       unsigned int value)
 {
   feature_info_t *info = feature_infos.push();
   if (unlikely (!tag)) return;
@@ -95,7 +97,8 @@
 				  unsigned int  variations_index,
 				  hb_mask_t     mask,
 				  bool          auto_zwnj,
-				  bool          auto_zwj)
+				  bool          auto_zwj,
+				  bool          random)
 {
   unsigned int lookup_indices[32];
   unsigned int offset, len;
@@ -122,6 +125,7 @@
       lookup->index = lookup_indices[i];
       lookup->auto_zwnj = auto_zwnj;
       lookup->auto_zwj = auto_zwj;
+      lookup->random = random;
     }
 
     offset += len;
@@ -208,8 +212,8 @@
       /* Uses the global bit */
       bits_needed = 0;
     else
-      /* Limit to 8 bits per feature. */
-      bits_needed = MIN(8u, hb_bit_storage (info->max_value));
+      /* Limit bits per feature. */
+      bits_needed = MIN(HB_OT_MAP_MAX_BITS, hb_bit_storage (info->max_value));
 
     if (!info->max_value || next_bit + bits_needed > 8 * sizeof (hb_mask_t))
       continue; /* Feature disabled, or not enough bits. */
@@ -252,6 +256,7 @@
     map->stage[1] = info->stage[1];
     map->auto_zwnj = !(info->flags & F_MANUAL_ZWNJ);
     map->auto_zwj = !(info->flags & F_MANUAL_ZWJ);
+    map->random = !!(info->flags & F_RANDOM);
     if ((info->flags & F_GLOBAL) && info->max_value == 1) {
       /* Uses the global bit */
       map->shift = global_bit_shift;
@@ -301,7 +306,8 @@
 		       variations_index,
 		       m.features[i].mask,
 		       m.features[i].auto_zwnj,
-		       m.features[i].auto_zwj);
+		       m.features[i].auto_zwj,
+		       m.features[i].random);
 
       /* Sort lookups and merge duplicates */
       if (last_num_lookups < m.lookups[table_index].len)
diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map.hh
similarity index 86%
rename from src/hb-ot-map-private.hh
rename to src/hb-ot-map.hh
index 4aaf328..40b9921 100644
--- a/src/hb-ot-map-private.hh
+++ b/src/hb-ot-map.hh
@@ -26,12 +26,15 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_MAP_PRIVATE_HH
-#define HB_OT_MAP_PRIVATE_HH
+#ifndef HB_OT_MAP_HH
+#define HB_OT_MAP_HH
 
-#include "hb-buffer-private.hh"
+#include "hb-buffer.hh"
 
 
+#define HB_OT_MAP_MAX_BITS 8u
+#define HB_OT_MAP_MAX_VALUE ((1u << HB_OT_MAP_MAX_BITS) - 1u)
+
 struct hb_ot_shape_plan_t;
 
 static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS};
@@ -52,6 +55,7 @@
     unsigned int needs_fallback : 1;
     unsigned int auto_zwnj : 1;
     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; }
@@ -61,6 +65,7 @@
     unsigned short index;
     unsigned short auto_zwnj : 1;
     unsigned short auto_zwj : 1;
+    unsigned short random : 1;
     hb_mask_t mask;
 
     static int cmp (const void *pa, const void *pb)
@@ -161,17 +166,27 @@
   hb_vector_t<stage_map_t, 4> stages[2]; /* GSUB/GPOS */
 };
 
-enum hb_ot_map_feature_flags_t {
+enum hb_ot_map_feature_flags_t
+{
   F_NONE		= 0x0000u,
   F_GLOBAL		= 0x0001u, /* Feature applies to all characters; results in no mask allocated for it. */
   F_HAS_FALLBACK	= 0x0002u, /* Has fallback implementation, so include mask bit even if feature not found. */
   F_MANUAL_ZWNJ		= 0x0004u, /* Don't skip over ZWNJ when matching **context**. */
   F_MANUAL_ZWJ		= 0x0008u, /* Don't skip over ZWJ when matching **input**. */
-  F_GLOBAL_SEARCH	= 0x0010u  /* If feature not found in LangSys, look for it in global feature list and pick one. */
+  F_MANUAL_JOINERS	= F_MANUAL_ZWNJ | F_MANUAL_ZWJ,
+  F_GLOBAL_MANUAL_JOINERS= F_GLOBAL | F_MANUAL_JOINERS,
+  F_GLOBAL_HAS_FALLBACK = F_GLOBAL | F_HAS_FALLBACK,
+  F_GLOBAL_SEARCH	= 0x0010u, /* If feature not found in LangSys, look for it in global feature list and pick one. */
+  F_RANDOM		= 0x0020u  /* Randomly select a glyph from an AlternateSubstFormat1 subtable. */
 };
 HB_MARK_AS_FLAG_T (hb_ot_map_feature_flags_t);
-/* Macro version for where const is desired. */
-#define F_COMBINE(l,r) (hb_ot_map_feature_flags_t ((unsigned int) (l) | (unsigned int) (r)))
+
+
+struct hb_ot_map_feature_t
+{
+  hb_tag_t tag;
+  hb_ot_map_feature_flags_t flags;
+};
 
 
 struct hb_ot_map_builder_t
@@ -183,11 +198,20 @@
 
   HB_INTERNAL ~hb_ot_map_builder_t (void);
 
-  HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value,
-				hb_ot_map_feature_flags_t flags);
+  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_global_bool_feature (hb_tag_t tag)
-  { add_feature (tag, 1, F_GLOBAL); }
+  inline void add_feature (const hb_ot_map_feature_t &feat)
+  { add_feature (feat.tag, feat.flags); }
+
+  inline 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)
+  { add_feature (tag, F_GLOBAL, 0); }
 
   inline void add_gsub_pause (hb_ot_map_t::pause_func_t pause_func)
   { add_pause (0, pause_func); }
@@ -206,7 +230,8 @@
 				unsigned int  variations_index,
 				hb_mask_t     mask,
 				bool          auto_zwnj = true,
-				bool          auto_zwj = true);
+				bool          auto_zwj = true,
+				bool          random = false);
 
   struct feature_info_t {
     hb_tag_t tag;
@@ -250,4 +275,4 @@
 
 
 
-#endif /* HB_OT_MAP_PRIVATE_HH */
+#endif /* HB_OT_MAP_HH */
diff --git a/src/hb-ot-math-table.hh b/src/hb-ot-math-table.hh
index 2dd7145..2f87112 100644
--- a/src/hb-ot-math-table.hh
+++ b/src/hb-ot-math-table.hh
@@ -27,8 +27,8 @@
 #ifndef HB_OT_MATH_TABLE_HH
 #define HB_OT_MATH_TABLE_HH
 
-#include "hb-open-type-private.hh"
-#include "hb-ot-layout-common-private.hh"
+#include "hb-open-type.hh"
+#include "hb-ot-layout-common.hh"
 #include "hb-ot-math.h"
 
 namespace OT {
@@ -50,7 +50,7 @@
   protected:
   HBINT16			value;		/* The X or Y value in design units */
   OffsetTo<Device>	deviceTable;	/* Offset to the device table - from the
-					 * beginning of parent table. May be nullptr.
+					 * beginning of parent table.  May be NULL.
 					 * Suggested format for device table is 1. */
 
   public:
@@ -234,7 +234,7 @@
     TRACE_SANITIZE (this);
     unsigned int count = 2 * heightCount + 1;
     for (unsigned int i = 0; i < count; i++)
-      if (!mathValueRecords[i].sanitize (c, this)) return_trace (false);
+      if (!mathValueRecordsZ.arrayZ[i].sanitize (c, this)) return_trace (false);
     return_trace (true);
   }
 
@@ -242,16 +242,14 @@
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  c->check_array (mathValueRecords,
-				  mathValueRecords[0].static_size,
-				  2 * heightCount + 1) &&
+		  c->check_array (mathValueRecordsZ.arrayZ, 2 * heightCount + 1) &&
 		  sanitize_math_value_records (c));
   }
 
   inline hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const
   {
-    const MathValueRecord* correctionHeight = mathValueRecords;
-    const MathValueRecord* kernValue = mathValueRecords + heightCount;
+    const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ;
+    const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
     int sign = font->y_scale < 0 ? -1 : +1;
 
     /* The description of the MathKern table is a ambiguous, but interpreting
@@ -279,18 +277,19 @@
   }
 
   protected:
-  HBUINT16	  heightCount;
-  MathValueRecord mathValueRecords[VAR]; /* Array of correction heights at
-					  * which the kern value changes.
-					  * Sorted by the height value in
-					  * design units (heightCount entries),
-					  * Followed by:
-					  * Array of kern values corresponding
-					  * to heights. (heightCount+1 entries).
-					  */
+  HBUINT16	heightCount;
+  UnsizedArrayOf<MathValueRecord>
+		mathValueRecordsZ;	/* Array of correction heights at
+					 * which the kern value changes.
+					 * Sorted by the height value in
+					 * design units (heightCount entries),
+					 * Followed by:
+					 * Array of kern values corresponding
+					 * to heights. (heightCount+1 entries).
+					 */
 
   public:
-  DEFINE_SIZE_ARRAY (2, mathValueRecords);
+  DEFINE_SIZE_ARRAY (2, mathValueRecordsZ);
 };
 
 struct MathKernInfoRecord
@@ -319,7 +318,7 @@
 
   protected:
   /* Offset to MathKern table for each corner -
-   * from the beginning of MathKernInfo table. May be nullptr. */
+   * from the beginning of MathKernInfo table.  May be NULL. */
   OffsetTo<MathKern> mathKern[4];
 
   public:
@@ -402,7 +401,7 @@
    * from the beginning of MathGlyphInfo table. When the left or right glyph of
    * a box is an extended shape variant, the (ink) box (and not the default
    * position defined by values in MathConstants table) should be used for
-   * vertical positioning purposes. May be nullptr.. */
+   * vertical positioning purposes.  May be NULL.. */
   OffsetTo<Coverage> extendedShapeCoverage;
 
    /* Offset to MathKernInfo table -
@@ -571,7 +570,7 @@
 
   protected:
   /* Offset to MathGlyphAssembly table for this shape - from the beginning of
-     MathGlyphConstruction table. May be nullptr. */
+     MathGlyphConstruction table.  May be NULL. */
   OffsetTo<MathGlyphAssembly>	  glyphAssembly;
 
   /* MathGlyphVariantRecords for alternative variants of the glyphs. */
@@ -588,7 +587,7 @@
     TRACE_SANITIZE (this);
     unsigned int count = vertGlyphCount + horizGlyphCount;
     for (unsigned int i = 0; i < count; i++)
-      if (!glyphConstruction[i].sanitize (c, this)) return_trace (false);
+      if (!glyphConstruction.arrayZ[i].sanitize (c, this)) return_trace (false);
     return_trace (true);
   }
 
@@ -598,9 +597,7 @@
     return_trace (c->check_struct (this) &&
 		  vertGlyphCoverage.sanitize (c, this) &&
 		  horizGlyphCoverage.sanitize (c, this) &&
-		  c->check_array (glyphConstruction,
-				  glyphConstruction[0].static_size,
-				  vertGlyphCount + horizGlyphCount) &&
+		  c->check_array (glyphConstruction.arrayZ, vertGlyphCount + horizGlyphCount) &&
 		  sanitize_offsets (c));
   }
 
@@ -670,7 +667,8 @@
   /* Array of offsets to MathGlyphConstruction tables - from the beginning of
      the MathVariants table, for shapes growing in vertical/horizontal
      direction. */
-  OffsetTo<MathGlyphConstruction> glyphConstruction[VAR];
+  UnsizedArrayOf<OffsetTo<MathGlyphConstruction> >
+ 			glyphConstruction;
 
   public:
   DEFINE_SIZE_ARRAY (10, glyphConstruction);
diff --git a/src/hb-ot-math.cc b/src/hb-ot-math.cc
index 66ce207..c693f48 100644
--- a/src/hb-ot-math.cc
+++ b/src/hb-ot-math.cc
@@ -24,17 +24,17 @@
  * Igalia Author(s): Frédéric Wang
  */
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
-#include "hb-ot-layout-private.hh"
+#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_layout_t * layout = hb_ot_layout_from_face (face);
-  return *(layout->table.MATH.get ());
+  hb_ot_face_data_t * data = hb_ot_face_data (face);
+  return *(data->MATH.get ());
 }
 
 /*
diff --git a/src/hb-ot-maxp-table.hh b/src/hb-ot-maxp-table.hh
index 75aac4f..2572ad2 100644
--- a/src/hb-ot-maxp-table.hh
+++ b/src/hb-ot-maxp-table.hh
@@ -27,8 +27,7 @@
 #ifndef HB_OT_MAXP_TABLE_HH
 #define HB_OT_MAXP_TABLE_HH
 
-#include "hb-open-type-private.hh"
-#include "hb-subset-plan.hh"
+#include "hb-open-type.hh"
 
 namespace OT {
 
@@ -137,7 +136,7 @@
   FixedVersion<>version;		/* Version of the maxp table (0.5 or 1.0),
 					 * 0x00005000u or 0x00010000u. */
   HBUINT16	numGlyphs;		/* The number of glyphs in the font. */
-/*maxpV1Tail v1Tail[VAR]; */
+/*maxpV1Tail	v1Tail[VAR]; */
   public:
   DEFINE_SIZE_STATIC (6);
 };
diff --git a/src/hb-ot-name-table.hh b/src/hb-ot-name-table.hh
index bff85df..bb49c2c 100644
--- a/src/hb-ot-name-table.hh
+++ b/src/hb-ot-name-table.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OT_NAME_TABLE_HH
 #define HB_OT_NAME_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
 
 namespace OT {
@@ -91,7 +91,7 @@
     key.encodingID.set (encoding_id);
     key.languageID.set (language_id);
     key.nameID.set (name_id);
-    NameRecord *match = (NameRecord *) bsearch (&key, nameRecord, count, sizeof (nameRecord[0]), NameRecord::cmp);
+    NameRecord *match = (NameRecord *) bsearch (&key, nameRecordZ.arrayZ, count, sizeof (nameRecordZ[0]), NameRecord::cmp);
 
     if (!match)
       return 0;
@@ -102,14 +102,14 @@
   }
 
   inline unsigned int get_size (void) const
-  { return min_size + count * nameRecord[0].min_size; }
+  { 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;
     unsigned int _count = count;
     for (unsigned int i = 0; i < _count; i++)
-      if (!nameRecord[i].sanitize (c, string_pool)) return_trace (false);
+      if (!nameRecordZ[i].sanitize (c, string_pool)) return_trace (false);
     return_trace (true);
   }
 
@@ -118,7 +118,7 @@
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  likely (format == 0 || format == 1) &&
-		  c->check_array (nameRecord, nameRecord[0].static_size, count) &&
+		  c->check_array (nameRecordZ.arrayZ, count) &&
 		  sanitize_records (c));
   }
 
@@ -126,9 +126,10 @@
   HBUINT16	format;			/* Format selector (=0/1). */
   HBUINT16	count;			/* Number of name records. */
   Offset16	stringOffset;		/* Offset to start of string storage (from start of table). */
-  NameRecord	nameRecord[VAR];	/* The name records where count is the number of records. */
+  UnsizedArrayOf<NameRecord>
+		nameRecordZ;		/* The name records where count is the number of records. */
   public:
-  DEFINE_SIZE_ARRAY (6, nameRecord);
+  DEFINE_SIZE_ARRAY (6, nameRecordZ);
 };
 
 
diff --git a/src/hb-shaper-impl-private.hh b/src/hb-ot-name.h
similarity index 70%
copy from src/hb-shaper-impl-private.hh
copy to src/hb-ot-name.h
index 4a10279..49423e8 100644
--- a/src/hb-shaper-impl-private.hh
+++ b/src/hb-ot-name.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2012  Google, Inc.
+ * Copyright © 2018  Ebrahim Byagowi.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -20,24 +20,34 @@
  * 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_SHAPER_IMPL_PRIVATE_HH
-#define HB_SHAPER_IMPL_PRIVATE_HH
-
-#include "hb-private.hh"
-
-#include "hb-shaper-private.hh"
-#include "hb-shape-plan-private.hh"
-#include "hb-font-private.hh"
-#include "hb-buffer-private.hh"
-
-
-#ifdef HB_SHAPER
-#define HB_SHAPER_DATA_GET(object) HB_SHAPER_DATA (HB_SHAPER, object).get ()
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
 #endif
 
+#ifndef HB_OT_NAME_H
+#define HB_OT_NAME_H
 
-#endif /* HB_SHAPER_IMPL_PRIVATE_HH */
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+
+/**
+ * hb_name_id_t:
+ *
+ * Since: 2.0.0
+ */
+typedef unsigned int hb_name_id_t;
+
+/**
+ * HB_NAME_ID_INVALID
+ *
+ * Since: 2.0.0
+ **/
+#define HB_NAME_ID_INVALID 0xFFFF
+
+HB_END_DECLS
+
+#endif /* HB_OT_NAME_H */
diff --git a/src/hb-ot-os2-table.hh b/src/hb-ot-os2-table.hh
index 56bbab7..71d2bf5 100644
--- a/src/hb-ot-os2-table.hh
+++ b/src/hb-ot-os2-table.hh
@@ -27,9 +27,8 @@
 #ifndef HB_OT_OS2_TABLE_HH
 #define HB_OT_OS2_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 #include "hb-ot-os2-unicode-ranges.hh"
-#include "hb-subset-plan.hh"
 
 namespace OT {
 
@@ -82,7 +81,7 @@
 
     hb_codepoint_t cp = HB_SET_VALUE_INVALID;
     while (codepoints->next (&cp)) {
-      unsigned int bit = hb_get_unicode_range_bit (cp);
+      unsigned int bit = _hb_ot_os2_get_unicode_range_bit (cp);
       if (bit < 128)
       {
         unsigned int block = bit / 32;
diff --git a/src/hb-ot-os2-unicode-ranges.hh b/src/hb-ot-os2-unicode-ranges.hh
index cb12607..1978008 100644
--- a/src/hb-ot-os2-unicode-ranges.hh
+++ b/src/hb-ot-os2-unicode-ranges.hh
@@ -27,18 +27,33 @@
 #ifndef HB_OT_OS2_UNICODE_RANGES_HH
 #define HB_OT_OS2_UNICODE_RANGES_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 namespace OT {
 
-struct Range {
+struct OS2Range
+{
+  static int
+  cmp (const void *_key, const void *_item, void *_arg)
+  {
+    hb_codepoint_t cp = *((hb_codepoint_t *) _key);
+    const OS2Range *range = (OS2Range *) _item;
+
+    if (cp < range->start)
+      return -1;
+    else if (cp <= range->end)
+      return 0;
+    else
+      return +1;
+  }
+
   hb_codepoint_t start;
   hb_codepoint_t end;
   unsigned int bit;
 };
 
-/* Note: The contents of this array was generated using src/gen-unicode-ranges.py. */
-static Range os2UnicodeRangesSorted[] =
+/* Note: The contents of this array was generated using gen-os2-unicode-ranges.py. */
+static const OS2Range _hb_os2_unicode_ranges[] =
 {
   {     0x0,     0x7F,   0}, // Basic Latin
   {    0x80,     0xFF,   1}, // Latin-1 Supplement
@@ -211,31 +226,17 @@
   {0x100000, 0x10FFFD,  90}, // Private Use (plane 16)
 };
 
-static int
-_compare_range (const void *_key, const void *_item, void *_arg)
-{
-  hb_codepoint_t cp = *((hb_codepoint_t *) _key);
-  const Range *range = (Range *) _item;
-
-  if (cp < range->start)
-    return -1;
-  else if (cp <= range->end)
-    return 0;
-  else
-    return 1;
-}
-
 /**
- * hb_get_unicode_range_bit:
- * Returns the bit to be set in os/2 ulUnicodeRange for a given codepoint.
+ * _hb_ot_os2_get_unicode_range_bit:
+ * Returns the bit to be set in os/2 ulUnicodeOS2Range for a given codepoint.
  **/
 static unsigned int
-hb_get_unicode_range_bit (hb_codepoint_t cp)
+_hb_ot_os2_get_unicode_range_bit (hb_codepoint_t cp)
 {
-  Range *range = (Range*) hb_bsearch_r (&cp, os2UnicodeRangesSorted,
-                                        sizeof (os2UnicodeRangesSorted) / sizeof(Range),
-                                        sizeof(Range),
-                                        _compare_range, nullptr);
+  OS2Range *range = (OS2Range*) hb_bsearch_r (&cp, _hb_os2_unicode_ranges,
+					      ARRAY_LENGTH (_hb_os2_unicode_ranges),
+					      sizeof (OS2Range),
+					      OS2Range::cmp, nullptr);
   if (range != nullptr)
     return range->bit;
   return -1;
diff --git a/src/hb-ot-post-macroman.hh b/src/hb-ot-post-macroman.hh
index dbbb97e..b4df8aa 100644
--- a/src/hb-ot-post-macroman.hh
+++ b/src/hb-ot-post-macroman.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OT_POST_MACROMAN_HH
 #if 0 /* Make checks happy. */
 #define HB_OT_POST_MACROMAN_HH
-#include "hb-private.hh"
+#include "hb.hh"
 #endif
 
 
diff --git a/src/hb-ot-post-table.hh b/src/hb-ot-post-table.hh
index 4f08a51..bd049f9 100644
--- a/src/hb-ot-post-table.hh
+++ b/src/hb-ot-post-table.hh
@@ -27,8 +27,7 @@
 #ifndef HB_OT_POST_TABLE_HH
 #define HB_OT_POST_TABLE_HH
 
-#include "hb-open-type-private.hh"
-#include "hb-subset-plan.hh"
+#include "hb-open-type.hh"
 
 #define HB_STRING_ARRAY_NAME format1_names
 #define HB_STRING_ARRAY_LIST "hb-ot-post-macroman.hh"
@@ -56,10 +55,11 @@
     return_trace (glyphNameIndex.sanitize (c));
   }
 
-  ArrayOf<HBUINT16>glyphNameIndex;	/* This is not an offset, but is the
+  ArrayOf<HBUINT16>	glyphNameIndex;	/* This is not an offset, but is the
 					 * ordinal number of the glyph in 'post'
 					 * string tables. */
-  HBUINT8		namesX[VAR];		/* Glyph names with length bytes [variable]
+  UnsizedArrayOf<HBUINT8>
+			namesX;		/* Glyph names with length bytes [variable]
 					 * (a Pascal string). */
 
   DEFINE_SIZE_ARRAY2 (2, glyphNameIndex, namesX);
@@ -131,6 +131,7 @@
     {
       index_to_offset.fini ();
       free (gids_sorted_by_name.get ());
+      hb_blob_destroy (blob);
     }
 
     inline bool get_glyph_name (hb_codepoint_t glyph,
@@ -143,7 +144,7 @@
 	return true;
       if (buf_len <= s.len) /* What to do with truncation? Returning false for now. */
         return false;
-      strncpy (buf, s.bytes, s.len);
+      strncpy (buf, s.arrayZ, s.len);
       buf[s.len] = '\0';
       return true;
     }
@@ -241,7 +242,7 @@
 
       if (index >= index_to_offset.len)
 	return hb_bytes_t ();
-      unsigned int offset = index_to_offset.arrayZ[index];
+      unsigned int offset = index_to_offset[index];
 
       const uint8_t *data = pool + offset;
       unsigned int name_length = *data;
@@ -295,6 +296,8 @@
   DEFINE_SIZE_STATIC (32);
 };
 
+struct post_accelerator_t : post::accelerator_t {};
+
 } /* namespace OT */
 
 
diff --git a/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh
index a55511a..2aa0367 100644
--- a/src/hb-ot-shape-complex-arabic-fallback.hh
+++ b/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -27,9 +27,9 @@
 #ifndef HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
 #define HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-ot-shape-private.hh"
+#include "hb-ot-shape.hh"
 #include "hb-ot-layout-gsub-table.hh"
 
 
@@ -173,7 +173,6 @@
 					 ligatures_supplier,
 					 component_count_supplier,
 					 component_supplier);
-
   c.end_serialize ();
   /* TODO sanitize the results? */
 
@@ -202,7 +201,7 @@
 
   hb_mask_t mask_array[ARABIC_FALLBACK_MAX_LOOKUPS];
   OT::SubstLookup *lookup_array[ARABIC_FALLBACK_MAX_LOOKUPS];
-  hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS];
+  OT::hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS];
 };
 
 #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_WIN1256)
diff --git a/src/hb-ot-shape-complex-arabic-win1256.hh b/src/hb-ot-shape-complex-arabic-win1256.hh
index 54c6cdc..b15e145 100644
--- a/src/hb-ot-shape-complex-arabic-win1256.hh
+++ b/src/hb-ot-shape-complex-arabic-win1256.hh
@@ -313,7 +313,7 @@
  * Include a second time to get the table data...
  */
 #if 0
-#include "hb-private.hh" /* Make check-includes.sh happy. */
+#include "hb.hh" /* Make check-includes.sh happy. */
 #endif
 #ifdef OT_MEASURE
 #include "hb-ot-shape-complex-arabic-win1256.hh"
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index f4b397b..b564439 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -24,9 +24,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
-#include "hb-ot-shape-complex-arabic-private.hh"
-#include "hb-ot-shape-private.hh"
+#include "hb.hh"
+#include "hb-ot-shape-complex-arabic.hh"
+#include "hb-ot-shape.hh"
 
 
 /* buffer var allocations */
@@ -159,11 +159,6 @@
 
 
 static void
-nuke_joiners (const hb_ot_shape_plan_t *plan,
-	      hb_font_t *font,
-	      hb_buffer_t *buffer);
-
-static void
 arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
 		       hb_font_t *font,
 		       hb_buffer_t *buffer);
@@ -200,32 +195,38 @@
    * work correctly.  See https://github.com/harfbuzz/harfbuzz/issues/505
    */
 
-  map->add_gsub_pause (nuke_joiners);
 
-  map->add_global_bool_feature (HB_TAG('s','t','c','h'));
+  map->enable_feature (HB_TAG('s','t','c','h'));
   map->add_gsub_pause (record_stch);
 
-  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
-  map->add_global_bool_feature (HB_TAG('l','o','c','l'));
+  map->enable_feature (HB_TAG('c','c','m','p'));
+  map->enable_feature (HB_TAG('l','o','c','l'));
 
   map->add_gsub_pause (nullptr);
 
   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++)
   {
     bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SYRIAC (arabic_features[i]);
-    map->add_feature (arabic_features[i], 1, has_fallback ? F_HAS_FALLBACK : F_NONE);
+    map->add_feature (arabic_features[i], has_fallback ? F_HAS_FALLBACK : F_NONE);
     map->add_gsub_pause (nullptr);
   }
 
-  map->add_feature (HB_TAG('r','l','i','g'), 1, F_GLOBAL|F_HAS_FALLBACK);
+  /* Normally, Unicode says a ZWNJ means "don't ligate".  In Arabic script
+   * however, it says a ZWJ should also mean "don't ligate".  So we run
+   * the main ligating features as MANUAL_ZWJ. */
+
+  map->enable_feature (HB_TAG('r','l','i','g'), F_MANUAL_ZWJ | F_HAS_FALLBACK);
+
   if (plan->props.script == HB_SCRIPT_ARABIC)
     map->add_gsub_pause (arabic_fallback_shape);
 
   /* No pause after rclt.  See 98460779bae19e4d64d29461ff154b3527bf8420. */
-  map->add_global_bool_feature (HB_TAG('r','c','l','t'));
-  map->add_global_bool_feature (HB_TAG('c','a','l','t'));
+  map->enable_feature (HB_TAG('r','c','l','t'), F_MANUAL_ZWJ);
+  map->enable_feature (HB_TAG('c','a','l','t'), F_MANUAL_ZWJ);
   map->add_gsub_pause (nullptr);
 
+  /* And undo here. */
+
   /* The spec includes 'cswh'.  Earlier versions of Windows
    * used to enable this by default, but testing suggests
    * that Windows 8 and later do not enable it by default,
@@ -234,8 +235,8 @@
    * Note that IranNastaliq uses this feature extensively
    * to fixup broken glyph sequences.  Oh well...
    * Test case: U+0643,U+0640,U+0631. */
-  //map->add_global_bool_feature (HB_TAG('c','s','w','h'));
-  map->add_global_bool_feature (HB_TAG('m','s','e','t'));
+  //map->enable_feature (HB_TAG('c','s','w','h'));
+  map->enable_feature (HB_TAG('m','s','e','t'));
 }
 
 #include "hb-ot-shape-complex-arabic-fallback.hh"
@@ -379,19 +380,6 @@
   setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script);
 }
 
-
-static void
-nuke_joiners (const hb_ot_shape_plan_t *plan HB_UNUSED,
-	      hb_font_t *font HB_UNUSED,
-	      hb_buffer_t *buffer)
-{
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  for (unsigned int i = 0; i < count; i++)
-    if (_hb_glyph_info_is_zwj (&info[i]))
-      _hb_glyph_info_flip_joiners (&info[i]);
-}
-
 static void
 arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
 		       hb_font_t *font,
@@ -470,9 +458,9 @@
 
   int sign = font->x_scale < 0 ? -1 : +1;
   unsigned int extra_glyphs_needed = 0; // Set during MEASURE, used during CUT
-  typedef enum { MEASURE, CUT } step_t;
+  enum { MEASURE, CUT } /* step_t */;
 
-  for (step_t step = MEASURE; step <= CUT; step = (step_t) (step + 1))
+  for (unsigned int step = MEASURE; step <= CUT; step = step + 1)
   {
     unsigned int count = buffer->len;
     hb_glyph_info_t *info = buffer->info;
@@ -611,7 +599,7 @@
   HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
 }
 
-/* https://unicode.org/reports/tr53/tr53-1.pdf */
+/* http://www.unicode.org/reports/tr53/ */
 
 static hb_codepoint_t
 modifier_combining_marks[] =
@@ -623,6 +611,7 @@
   0x06E3u, /* ARABIC SMALL LOW SEEN */
   0x06E7u, /* ARABIC SMALL HIGH YEH */
   0x06E8u, /* ARABIC SMALL HIGH NOON */
+  0x08D3u, /* ARABIC SMALL LOW WAW */
   0x08F3u, /* ARABIC SMALL HIGH WAW */
 };
 
@@ -714,7 +703,7 @@
   nullptr, /* decompose */
   nullptr, /* compose */
   setup_masks_arabic,
-  nullptr, /* disable_otl */
+  HB_TAG_NONE, /* gpos_tag */
   reorder_marks_arabic,
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
diff --git a/src/hb-ot-shape-complex-arabic-private.hh b/src/hb-ot-shape-complex-arabic.hh
similarity index 87%
rename from src/hb-ot-shape-complex-arabic-private.hh
rename to src/hb-ot-shape-complex-arabic.hh
index fcedc7d..5bf6ff6 100644
--- a/src/hb-ot-shape-complex-arabic-private.hh
+++ b/src/hb-ot-shape-complex-arabic.hh
@@ -26,12 +26,12 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH
-#define HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH
+#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_HH
+#define HB_OT_SHAPE_COMPLEX_ARABIC_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-complex.hh"
 
 
 struct arabic_shape_plan_t;
@@ -47,4 +47,4 @@
 			 hb_buffer_t               *buffer,
 			 hb_script_t                script);
 
-#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_PRIVATE_HH */
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_HH */
diff --git a/src/hb-ot-shape-complex-default.cc b/src/hb-ot-shape-complex-default.cc
index 68a62a1..97923ec 100644
--- a/src/hb-ot-shape-complex-default.cc
+++ b/src/hb-ot-shape-complex-default.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-complex.hh"
 
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
@@ -39,7 +39,7 @@
   nullptr, /* decompose */
   nullptr, /* compose */
   nullptr, /* setup_masks */
-  nullptr, /* disable_otl */
+  HB_TAG_NONE, /* gpos_tag */
   nullptr, /* reorder_marks */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
diff --git a/src/hb-ot-shape-complex-hangul.cc b/src/hb-ot-shape-complex-hangul.cc
index 7420c5d..9595402 100644
--- a/src/hb-ot-shape-complex-hangul.cc
+++ b/src/hb-ot-shape-complex-hangul.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-complex.hh"
 
 
 /* Hangul shaper */
@@ -56,7 +56,7 @@
   hb_ot_map_builder_t *map = &plan->map;
 
   for (unsigned int i = FIRST_HANGUL_FEATURE; i < HANGUL_FEATURE_COUNT; i++)
-    map->add_feature (hangul_features[i], 1, F_NONE);
+    map->add_feature (hangul_features[i]);
 }
 
 static void
@@ -65,7 +65,7 @@
   /* Uniscribe does not apply 'calt' for Hangul, and certain fonts
    * (Noto Sans CJK, Source Sans Han, etc) apply all of jamo lookups
    * in calt, which is not desirable. */
-  plan->map.add_feature (HB_TAG('c','a','l','t'), 0, F_GLOBAL);
+  plan->map.disable_feature (HB_TAG('c','a','l','t'));
 }
 
 struct hangul_shape_plan_t
@@ -345,13 +345,6 @@
 	{
 	  unsigned int s_len = tindex ? 3 : 2;
 	  buffer->replace_glyphs (1, s_len, decomposed);
-	  if (unlikely (!buffer->successful))
-	    return;
-
-	  /* We decomposed S: apply jamo features to the individual glyphs
-	   * that are now in buffer->out_info.
-	   */
-	  hb_glyph_info_t *info = buffer->out_info;
 
 	  /* If we decomposed an LV because of a non-combining T following,
 	   * we want to include this T in the syllable.
@@ -361,6 +354,14 @@
             buffer->next_glyph ();
             s_len++;
           }
+
+	  if (unlikely (!buffer->successful))
+	    return;
+
+	  /* We decomposed S: apply jamo features to the individual glyphs
+	   * that are now in buffer->out_info.
+	   */
+	  hb_glyph_info_t *info = buffer->out_info;
           end = start + s_len;
 
 	  unsigned int i = start;
@@ -368,6 +369,7 @@
 	  info[i++].hangul_shaping_feature() = VJMO;
 	  if (i < end)
 	    info[i++].hangul_shaping_feature() = TJMO;
+
 	  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
 	    buffer->merge_out_clusters (start, end);
 	  continue;
@@ -424,7 +426,7 @@
   nullptr, /* decompose */
   nullptr, /* compose */
   setup_masks_hangul,
-  nullptr, /* disable_otl */
+  HB_TAG_NONE, /* gpos_tag */
   nullptr, /* reorder_marks */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   false, /* fallback_position */
diff --git a/src/hb-ot-shape-complex-hebrew.cc b/src/hb-ot-shape-complex-hebrew.cc
index 34cf28b..90c36c0 100644
--- a/src/hb-ot-shape-complex-hebrew.cc
+++ b/src/hb-ot-shape-complex-hebrew.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-complex.hh"
 
 
 static bool
@@ -70,7 +70,7 @@
 
   bool found = (bool) c->unicode->compose (a, b, ab);
 
-  if (!found && !c->plan->has_mark)
+  if (!found && !c->plan->has_gpos_mark)
   {
       /* Special-case Hebrew presentation forms that are excluded from
        * standard normalization, but wanted for old fonts. */
@@ -154,18 +154,6 @@
   return found;
 }
 
-static bool
-disable_otl_hebrew (const hb_ot_shape_plan_t *plan)
-{
-  /* For Hebrew shaper, use fallback if GPOS does not have 'hebr'
-   * script.  This matches Uniscribe better, and makes fonts like
-   * Arial that have GSUB/GPOS/GDEF but no data for Hebrew work.
-   * See:
-   * https://github.com/harfbuzz/harfbuzz/issues/347#issuecomment-267838368
-   */
-  return plan->map.chosen_script[1] != HB_TAG ('h','e','b','r');
-}
-
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew =
 {
@@ -179,7 +167,7 @@
   nullptr, /* decompose */
   compose_hebrew,
   nullptr, /* setup_masks */
-  disable_otl_hebrew,
+  HB_TAG ('h','e','b','r'), /* gpos_tag. https://github.com/harfbuzz/harfbuzz/issues/347#issuecomment-267838368 */
   nullptr, /* reorder_marks */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
diff --git a/src/hb-ot-shape-complex-indic-machine.hh b/src/hb-ot-shape-complex-indic-machine.hh
index 73f9d58..e2ecfb8 100644
--- a/src/hb-ot-shape-complex-indic-machine.hh
+++ b/src/hb-ot-shape-complex-indic-machine.hh
@@ -29,892 +29,714 @@
 #ifndef HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 #line 36 "hb-ot-shape-complex-indic-machine.hh"
 static const unsigned char _indic_syllable_machine_trans_keys[] = {
-	8u, 8u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 
+	8u, 8u, 4u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 
 	5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 
 	4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 
-	16u, 16u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 
-	4u, 13u, 4u, 8u, 4u, 13u, 8u, 8u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 
-	5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 
-	4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 
-	16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 
-	4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 8u, 8u, 5u, 8u, 
-	5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 
-	5u, 8u, 5u, 7u, 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 
-	16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 
-	4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 
-	8u, 8u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 
-	5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 
-	4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 
-	16u, 16u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 
-	4u, 13u, 4u, 8u, 4u, 13u, 4u, 13u, 5u, 8u, 5u, 8u, 5u, 7u, 5u, 8u, 
-	5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 
-	8u, 8u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 
-	6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 8u, 8u, 1u, 19u, 3u, 17u, 
-	3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 
-	3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 
-	3u, 17u, 4u, 17u, 5u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 
-	5u, 10u, 3u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 
-	3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 
-	3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 
-	1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 
-	1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 
-	3u, 17u, 3u, 17u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 
-	4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 
-	4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 5u, 10u, 5u, 10u, 5u, 10u, 
-	10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 
-	3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 
-	5u, 10u, 3u, 10u, 4u, 10u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 
-	1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 
-	3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 
-	1u, 16u, 1u, 16u, 1u, 16u, 4u, 8u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 
-	3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 
-	3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 5u, 10u, 
-	5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 3u, 10u, 
+	16u, 16u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 
+	4u, 8u, 4u, 13u, 8u, 8u, 4u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 
+	7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 4u, 8u, 
+	6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 
+	4u, 8u, 6u, 6u, 16u, 16u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 
+	4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 8u, 8u, 4u, 8u, 5u, 7u, 7u, 7u, 
+	5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 
+	7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 
+	6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 13u, 4u, 8u, 4u, 13u, 
+	4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 8u, 8u, 4u, 8u, 5u, 7u, 
+	7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 
+	5u, 7u, 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 
+	4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 13u, 4u, 8u, 
+	4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 13u, 
+	5u, 8u, 8u, 8u, 1u, 19u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 
+	3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 
+	3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 
+	5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 
 	4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 
-	3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 3u, 17u, 3u, 17u, 1u, 16u, 
-	1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 
-	1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 
-	3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 4u, 13u, 3u, 17u, 4u, 8u, 
-	3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 
-	3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 
-	3u, 17u, 3u, 17u, 4u, 17u, 5u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 
-	10u, 10u, 5u, 10u, 3u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 
+	3u, 10u, 4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 
+	3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 
+	1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 
+	3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 
+	3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 
+	3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 
+	10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 
+	4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 3u, 10u, 
+	3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 
+	1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 
+	1u, 16u, 1u, 16u, 1u, 16u, 4u, 8u, 3u, 10u, 3u, 10u, 4u, 10u, 1u, 16u, 
+	3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 
+	3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 
+	5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 
+	3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 
+	5u, 10u, 3u, 10u, 4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 
+	3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 
+	1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 4u, 13u, 
+	3u, 10u, 4u, 8u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 
+	4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 
+	4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 5u, 10u, 
+	10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 
 	5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 
-	4u, 10u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 
-	3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 
-	1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 
-	1u, 16u, 3u, 17u, 1u, 17u, 3u, 17u, 1u, 17u, 4u, 13u, 5u, 10u, 10u, 10u, 
-	10u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 
-	4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 
+	4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 
+	1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 
+	3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 1u, 16u, 3u, 13u, 
+	1u, 16u, 4u, 13u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 
 	3u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 
 	0
 };
 
 static const char _indic_syllable_machine_key_spans[] = {
-	1, 4, 3, 1, 4, 3, 1, 4, 
+	1, 5, 3, 1, 4, 3, 1, 4, 
 	3, 1, 4, 3, 1, 5, 1, 1, 
 	5, 1, 1, 5, 1, 1, 5, 1, 
-	1, 5, 10, 5, 10, 5, 10, 5, 
-	10, 5, 10, 1, 4, 3, 1, 4, 
-	3, 1, 4, 3, 1, 4, 3, 1, 
-	5, 1, 1, 5, 1, 1, 5, 1, 
-	1, 5, 1, 1, 5, 10, 5, 10, 
-	5, 10, 5, 10, 5, 10, 1, 4, 
-	3, 1, 4, 3, 1, 4, 3, 1, 
-	4, 3, 1, 5, 1, 1, 5, 1, 
+	1, 10, 5, 10, 5, 10, 5, 10, 
+	5, 10, 1, 5, 3, 1, 4, 3, 
+	1, 4, 3, 1, 4, 3, 1, 5, 
+	1, 1, 5, 1, 1, 5, 1, 1, 
+	5, 1, 1, 10, 5, 10, 5, 10, 
+	5, 10, 5, 10, 1, 5, 3, 1, 
+	4, 3, 1, 4, 3, 1, 4, 3, 
 	1, 5, 1, 1, 5, 1, 1, 5, 
-	10, 5, 10, 5, 10, 5, 10, 5, 
+	1, 1, 5, 1, 1, 10, 5, 10, 
+	5, 10, 5, 10, 5, 1, 5, 3, 
 	1, 4, 3, 1, 4, 3, 1, 4, 
-	3, 1, 4, 3, 1, 5, 1, 1, 
-	5, 1, 1, 5, 1, 1, 5, 1, 
-	1, 5, 10, 5, 10, 5, 10, 5, 
-	10, 5, 10, 10, 4, 4, 3, 4, 
-	3, 1, 4, 3, 1, 4, 3, 1, 
-	1, 5, 1, 1, 5, 1, 1, 5, 
-	1, 1, 5, 1, 1, 1, 19, 15, 
-	15, 14, 16, 15, 15, 14, 16, 15, 
-	15, 14, 16, 15, 15, 14, 16, 15, 
-	15, 14, 6, 6, 6, 1, 1, 1, 
-	6, 8, 8, 7, 6, 8, 7, 6, 
-	8, 7, 6, 8, 7, 6, 8, 7, 
-	15, 15, 16, 16, 16, 16, 15, 15, 
-	16, 16, 16, 16, 15, 15, 16, 16, 
-	16, 16, 15, 15, 16, 16, 16, 16, 
-	15, 15, 15, 15, 14, 16, 15, 15, 
-	14, 16, 15, 15, 14, 16, 15, 15, 
-	14, 16, 15, 15, 14, 6, 6, 6, 
-	1, 1, 1, 6, 8, 8, 7, 6, 
-	8, 7, 6, 8, 7, 6, 8, 7, 
-	6, 8, 7, 15, 15, 16, 16, 16, 
-	16, 15, 15, 16, 16, 16, 16, 15, 
-	15, 16, 16, 16, 16, 15, 15, 16, 
-	16, 16, 16, 5, 15, 15, 14, 16, 
-	15, 15, 14, 16, 15, 15, 14, 16, 
-	15, 15, 14, 16, 15, 15, 14, 6, 
-	6, 6, 1, 1, 1, 6, 8, 8, 
+	3, 1, 5, 1, 1, 5, 1, 1, 
+	5, 1, 1, 5, 1, 1, 10, 5, 
+	10, 5, 10, 5, 10, 5, 10, 10, 
+	4, 1, 19, 11, 8, 7, 16, 11, 
+	8, 7, 16, 11, 8, 7, 16, 11, 
+	8, 7, 16, 11, 8, 7, 6, 6, 
+	6, 1, 1, 1, 6, 8, 6, 8, 
 	7, 6, 8, 7, 6, 8, 7, 6, 
-	8, 7, 6, 8, 7, 15, 15, 16, 
-	16, 16, 16, 15, 15, 16, 16, 16, 
-	16, 15, 15, 16, 16, 16, 16, 15, 
-	15, 16, 16, 16, 16, 10, 15, 5, 
-	15, 15, 14, 16, 15, 15, 14, 16, 
-	15, 15, 14, 16, 15, 15, 14, 16, 
-	15, 15, 14, 6, 6, 6, 1, 1, 
-	1, 6, 8, 8, 7, 6, 8, 7, 
+	8, 7, 8, 11, 16, 16, 16, 8, 
+	11, 16, 16, 16, 8, 11, 16, 16, 
+	16, 8, 11, 16, 16, 16, 8, 11, 
+	11, 8, 7, 16, 11, 8, 7, 16, 
+	11, 8, 7, 16, 11, 8, 7, 16, 
+	11, 8, 7, 6, 6, 6, 1, 1, 
+	1, 6, 8, 6, 8, 7, 6, 8, 
+	7, 6, 8, 7, 6, 8, 7, 8, 
+	11, 16, 16, 16, 8, 11, 16, 16, 
+	16, 8, 11, 16, 16, 16, 8, 11, 
+	16, 16, 16, 5, 8, 8, 7, 16, 
+	11, 8, 7, 16, 11, 8, 7, 16, 
+	11, 8, 7, 16, 11, 8, 7, 6, 
+	6, 6, 1, 1, 1, 6, 8, 6, 
+	8, 7, 6, 8, 7, 6, 8, 7, 
+	6, 8, 7, 8, 11, 16, 16, 16, 
+	8, 11, 16, 16, 16, 8, 11, 16, 
+	16, 16, 8, 11, 16, 16, 16, 10, 
+	8, 5, 11, 8, 7, 16, 11, 8, 
+	7, 16, 11, 8, 7, 16, 11, 8, 
+	7, 16, 11, 8, 7, 6, 6, 6, 
+	1, 1, 1, 6, 8, 6, 8, 7, 
 	6, 8, 7, 6, 8, 7, 6, 8, 
-	7, 15, 15, 16, 16, 16, 16, 15, 
-	15, 16, 16, 16, 16, 15, 15, 16, 
-	16, 16, 16, 15, 15, 16, 16, 16, 
-	16, 15, 17, 15, 17, 10, 6, 1, 
-	1, 1, 6, 16, 8, 7, 6, 8, 
-	7, 6, 8, 7, 6, 8, 7, 6, 
+	7, 8, 11, 16, 16, 16, 8, 11, 
+	16, 16, 16, 8, 11, 16, 16, 16, 
+	8, 11, 16, 16, 16, 8, 16, 11, 
+	16, 10, 6, 1, 1, 1, 6, 16, 
 	8, 6, 6, 1, 1, 1, 6, 16
 };
 
 static const short _indic_syllable_machine_index_offsets[] = {
-	0, 2, 7, 11, 13, 18, 22, 24, 
-	29, 33, 35, 40, 44, 46, 52, 54, 
-	56, 62, 64, 66, 72, 74, 76, 82, 
-	84, 86, 92, 103, 109, 120, 126, 137, 
-	143, 154, 160, 171, 173, 178, 182, 184, 
-	189, 193, 195, 200, 204, 206, 211, 215, 
-	217, 223, 225, 227, 233, 235, 237, 243, 
-	245, 247, 253, 255, 257, 263, 274, 280, 
-	291, 297, 308, 314, 325, 331, 342, 344, 
-	349, 353, 355, 360, 364, 366, 371, 375, 
-	377, 382, 386, 388, 394, 396, 398, 404, 
-	406, 408, 414, 416, 418, 424, 426, 428, 
-	434, 445, 451, 462, 468, 479, 485, 496, 
-	502, 504, 509, 513, 515, 520, 524, 526, 
-	531, 535, 537, 542, 546, 548, 554, 556, 
-	558, 564, 566, 568, 574, 576, 578, 584, 
-	586, 588, 594, 605, 611, 622, 628, 639, 
-	645, 656, 662, 673, 684, 689, 694, 698, 
-	703, 707, 709, 714, 718, 720, 725, 729, 
-	731, 733, 739, 741, 743, 749, 751, 753, 
-	759, 761, 763, 769, 771, 773, 775, 795, 
-	811, 827, 842, 859, 875, 891, 906, 923, 
-	939, 955, 970, 987, 1003, 1019, 1034, 1051, 
-	1067, 1083, 1098, 1105, 1112, 1119, 1121, 1123, 
-	1125, 1132, 1141, 1150, 1158, 1165, 1174, 1182, 
-	1189, 1198, 1206, 1213, 1222, 1230, 1237, 1246, 
-	1254, 1270, 1286, 1303, 1320, 1337, 1354, 1370, 
-	1386, 1403, 1420, 1437, 1454, 1470, 1486, 1503, 
-	1520, 1537, 1554, 1570, 1586, 1603, 1620, 1637, 
-	1654, 1670, 1686, 1702, 1718, 1733, 1750, 1766, 
-	1782, 1797, 1814, 1830, 1846, 1861, 1878, 1894, 
-	1910, 1925, 1942, 1958, 1974, 1989, 1996, 2003, 
-	2010, 2012, 2014, 2016, 2023, 2032, 2041, 2049, 
-	2056, 2065, 2073, 2080, 2089, 2097, 2104, 2113, 
-	2121, 2128, 2137, 2145, 2161, 2177, 2194, 2211, 
-	2228, 2245, 2261, 2277, 2294, 2311, 2328, 2345, 
-	2361, 2377, 2394, 2411, 2428, 2445, 2461, 2477, 
-	2494, 2511, 2528, 2545, 2551, 2567, 2583, 2598, 
-	2615, 2631, 2647, 2662, 2679, 2695, 2711, 2726, 
-	2743, 2759, 2775, 2790, 2807, 2823, 2839, 2854, 
-	2861, 2868, 2875, 2877, 2879, 2881, 2888, 2897, 
-	2906, 2914, 2921, 2930, 2938, 2945, 2954, 2962, 
-	2969, 2978, 2986, 2993, 3002, 3010, 3026, 3042, 
-	3059, 3076, 3093, 3110, 3126, 3142, 3159, 3176, 
-	3193, 3210, 3226, 3242, 3259, 3276, 3293, 3310, 
-	3326, 3342, 3359, 3376, 3393, 3410, 3421, 3437, 
-	3443, 3459, 3475, 3490, 3507, 3523, 3539, 3554, 
-	3571, 3587, 3603, 3618, 3635, 3651, 3667, 3682, 
-	3699, 3715, 3731, 3746, 3753, 3760, 3767, 3769, 
-	3771, 3773, 3780, 3789, 3798, 3806, 3813, 3822, 
-	3830, 3837, 3846, 3854, 3861, 3870, 3878, 3885, 
-	3894, 3902, 3918, 3934, 3951, 3968, 3985, 4002, 
-	4018, 4034, 4051, 4068, 4085, 4102, 4118, 4134, 
-	4151, 4168, 4185, 4202, 4218, 4234, 4251, 4268, 
-	4285, 4302, 4318, 4336, 4352, 4370, 4381, 4388, 
-	4390, 4392, 4394, 4401, 4418, 4427, 4435, 4442, 
-	4451, 4459, 4466, 4475, 4483, 4490, 4499, 4507, 
-	4514, 4523, 4530, 4537, 4539, 4541, 4543, 4550
+	0, 2, 8, 12, 14, 19, 23, 25, 
+	30, 34, 36, 41, 45, 47, 53, 55, 
+	57, 63, 65, 67, 73, 75, 77, 83, 
+	85, 87, 98, 104, 115, 121, 132, 138, 
+	149, 155, 166, 168, 174, 178, 180, 185, 
+	189, 191, 196, 200, 202, 207, 211, 213, 
+	219, 221, 223, 229, 231, 233, 239, 241, 
+	243, 249, 251, 253, 264, 270, 281, 287, 
+	298, 304, 315, 321, 332, 334, 340, 344, 
+	346, 351, 355, 357, 362, 366, 368, 373, 
+	377, 379, 385, 387, 389, 395, 397, 399, 
+	405, 407, 409, 415, 417, 419, 430, 436, 
+	447, 453, 464, 470, 481, 487, 489, 495, 
+	499, 501, 506, 510, 512, 517, 521, 523, 
+	528, 532, 534, 540, 542, 544, 550, 552, 
+	554, 560, 562, 564, 570, 572, 574, 585, 
+	591, 602, 608, 619, 625, 636, 642, 653, 
+	664, 669, 671, 691, 703, 712, 720, 737, 
+	749, 758, 766, 783, 795, 804, 812, 829, 
+	841, 850, 858, 875, 887, 896, 904, 911, 
+	918, 925, 927, 929, 931, 938, 947, 954, 
+	963, 971, 978, 987, 995, 1002, 1011, 1019, 
+	1026, 1035, 1043, 1052, 1064, 1081, 1098, 1115, 
+	1124, 1136, 1153, 1170, 1187, 1196, 1208, 1225, 
+	1242, 1259, 1268, 1280, 1297, 1314, 1331, 1340, 
+	1352, 1364, 1373, 1381, 1398, 1410, 1419, 1427, 
+	1444, 1456, 1465, 1473, 1490, 1502, 1511, 1519, 
+	1536, 1548, 1557, 1565, 1572, 1579, 1586, 1588, 
+	1590, 1592, 1599, 1608, 1615, 1624, 1632, 1639, 
+	1648, 1656, 1663, 1672, 1680, 1687, 1696, 1704, 
+	1713, 1725, 1742, 1759, 1776, 1785, 1797, 1814, 
+	1831, 1848, 1857, 1869, 1886, 1903, 1920, 1929, 
+	1941, 1958, 1975, 1992, 1998, 2007, 2016, 2024, 
+	2041, 2053, 2062, 2070, 2087, 2099, 2108, 2116, 
+	2133, 2145, 2154, 2162, 2179, 2191, 2200, 2208, 
+	2215, 2222, 2229, 2231, 2233, 2235, 2242, 2251, 
+	2258, 2267, 2275, 2282, 2291, 2299, 2306, 2315, 
+	2323, 2330, 2339, 2347, 2356, 2368, 2385, 2402, 
+	2419, 2428, 2440, 2457, 2474, 2491, 2500, 2512, 
+	2529, 2546, 2563, 2572, 2584, 2601, 2618, 2635, 
+	2646, 2655, 2661, 2673, 2682, 2690, 2707, 2719, 
+	2728, 2736, 2753, 2765, 2774, 2782, 2799, 2811, 
+	2820, 2828, 2845, 2857, 2866, 2874, 2881, 2888, 
+	2895, 2897, 2899, 2901, 2908, 2917, 2924, 2933, 
+	2941, 2948, 2957, 2965, 2972, 2981, 2989, 2996, 
+	3005, 3013, 3022, 3034, 3051, 3068, 3085, 3094, 
+	3106, 3123, 3140, 3157, 3166, 3178, 3195, 3212, 
+	3229, 3238, 3250, 3267, 3284, 3301, 3310, 3327, 
+	3339, 3356, 3367, 3374, 3376, 3378, 3380, 3387, 
+	3404, 3413, 3420, 3427, 3429, 3431, 3433, 3440
 };
 
 static const short _indic_syllable_machine_indicies[] = {
-	1, 0, 2, 2, 3, 1, 0, 4, 
-	4, 3, 0, 3, 0, 5, 5, 6, 
-	1, 0, 7, 7, 6, 0, 6, 0, 
-	8, 8, 9, 1, 0, 10, 10, 9, 
-	0, 9, 0, 11, 11, 12, 1, 0, 
-	13, 13, 12, 0, 12, 0, 14, 0, 
-	0, 0, 1, 0, 15, 0, 16, 0, 
-	17, 11, 11, 12, 1, 0, 18, 0, 
-	19, 0, 20, 8, 8, 9, 1, 0, 
-	21, 0, 22, 0, 23, 5, 5, 6, 
-	1, 0, 24, 0, 25, 0, 26, 2, 
-	2, 3, 1, 0, 26, 2, 2, 3, 
-	1, 0, 0, 0, 0, 27, 0, 28, 
-	2, 2, 3, 1, 0, 28, 2, 2, 
-	3, 1, 0, 0, 0, 0, 29, 0, 
-	30, 2, 2, 3, 1, 0, 30, 2, 
-	2, 3, 1, 0, 0, 0, 0, 31, 
-	0, 32, 2, 2, 3, 1, 0, 32, 
-	2, 2, 3, 1, 0, 0, 0, 0, 
-	33, 0, 34, 2, 2, 3, 1, 0, 
-	34, 2, 2, 3, 1, 0, 0, 0, 
-	0, 35, 0, 37, 36, 38, 38, 39, 
-	37, 36, 40, 40, 39, 36, 39, 36, 
-	41, 41, 42, 37, 36, 43, 43, 42, 
-	36, 42, 36, 44, 44, 45, 37, 36, 
-	46, 46, 45, 36, 45, 36, 47, 47, 
-	48, 37, 36, 49, 49, 48, 36, 48, 
-	36, 50, 36, 36, 36, 37, 36, 51, 
-	36, 52, 36, 53, 47, 47, 48, 37, 
-	36, 54, 36, 55, 36, 56, 44, 44, 
-	45, 37, 36, 57, 36, 58, 36, 59, 
-	41, 41, 42, 37, 36, 60, 36, 61, 
-	36, 62, 38, 38, 39, 37, 36, 62, 
-	38, 38, 39, 37, 36, 36, 36, 36, 
-	63, 36, 64, 38, 38, 39, 37, 36, 
-	64, 38, 38, 39, 37, 36, 36, 36, 
-	36, 65, 36, 66, 38, 38, 39, 37, 
-	36, 66, 38, 38, 39, 37, 36, 36, 
-	36, 36, 67, 36, 68, 38, 38, 39, 
-	37, 36, 68, 38, 38, 39, 37, 36, 
-	36, 36, 36, 69, 36, 70, 38, 38, 
-	39, 37, 36, 70, 38, 38, 39, 37, 
-	36, 36, 36, 36, 71, 36, 73, 72, 
-	74, 74, 75, 73, 72, 77, 77, 75, 
-	76, 75, 76, 78, 78, 79, 73, 72, 
-	80, 80, 79, 72, 79, 72, 81, 81, 
-	82, 73, 72, 83, 83, 82, 72, 82, 
-	72, 84, 84, 85, 73, 72, 86, 86, 
-	85, 72, 85, 72, 87, 72, 72, 72, 
-	73, 72, 88, 72, 89, 72, 90, 84, 
-	84, 85, 73, 72, 91, 72, 92, 72, 
-	93, 81, 81, 82, 73, 72, 94, 72, 
-	95, 72, 96, 78, 78, 79, 73, 72, 
-	97, 72, 98, 72, 99, 74, 74, 75, 
-	73, 72, 99, 74, 74, 75, 73, 72, 
-	72, 72, 72, 100, 72, 101, 74, 74, 
-	75, 73, 72, 101, 74, 74, 75, 73, 
-	72, 72, 72, 72, 102, 72, 103, 74, 
-	74, 75, 73, 72, 103, 74, 74, 75, 
-	73, 72, 72, 72, 72, 104, 72, 105, 
-	74, 74, 75, 73, 72, 105, 74, 74, 
-	75, 73, 72, 72, 72, 72, 106, 72, 
-	107, 74, 74, 75, 73, 72, 109, 108, 
-	110, 110, 111, 109, 108, 112, 112, 111, 
-	108, 111, 108, 113, 113, 114, 109, 108, 
-	115, 115, 114, 108, 114, 108, 116, 116, 
-	117, 109, 108, 118, 118, 117, 108, 117, 
-	108, 119, 119, 120, 109, 108, 121, 121, 
-	120, 108, 120, 108, 122, 108, 108, 108, 
-	109, 108, 123, 108, 124, 108, 125, 119, 
-	119, 120, 109, 108, 126, 108, 127, 108, 
-	128, 116, 116, 117, 109, 108, 129, 108, 
-	130, 108, 131, 113, 113, 114, 109, 108, 
-	132, 108, 133, 108, 134, 110, 110, 111, 
-	109, 108, 134, 110, 110, 111, 109, 108, 
-	108, 108, 108, 135, 108, 136, 110, 110, 
-	111, 109, 108, 136, 110, 110, 111, 109, 
-	108, 108, 108, 108, 137, 108, 138, 110, 
-	110, 111, 109, 108, 138, 110, 110, 111, 
-	109, 108, 108, 108, 108, 139, 108, 140, 
-	110, 110, 111, 109, 108, 140, 110, 110, 
-	111, 109, 108, 108, 108, 108, 141, 108, 
-	142, 110, 110, 111, 109, 108, 142, 110, 
-	110, 111, 109, 108, 108, 108, 108, 143, 
-	108, 107, 74, 74, 75, 73, 72, 72, 
-	72, 72, 144, 72, 77, 77, 75, 1, 
-	0, 145, 145, 146, 1, 0, 4, 4, 
-	146, 0, 147, 147, 148, 149, 0, 150, 
-	150, 148, 0, 148, 0, 151, 151, 152, 
-	149, 0, 153, 153, 152, 0, 152, 0, 
-	154, 154, 155, 149, 0, 156, 156, 155, 
-	0, 155, 0, 149, 0, 157, 0, 0, 
-	0, 149, 0, 158, 0, 159, 0, 160, 
-	154, 154, 155, 149, 0, 161, 0, 162, 
-	0, 163, 151, 151, 152, 149, 0, 164, 
-	0, 165, 0, 166, 147, 147, 148, 149, 
-	0, 167, 0, 168, 0, 170, 169, 172, 
-	173, 174, 175, 176, 177, 75, 73, 171, 
-	178, 179, 179, 144, 171, 180, 181, 182, 
-	183, 184, 171, 186, 187, 188, 189, 3, 
-	1, 185, 190, 185, 185, 35, 185, 185, 
-	185, 191, 185, 192, 187, 193, 193, 3, 
-	1, 185, 190, 185, 185, 185, 185, 185, 
-	185, 191, 185, 187, 193, 193, 3, 1, 
-	185, 190, 185, 185, 185, 185, 185, 185, 
-	191, 185, 194, 185, 185, 185, 16, 195, 
-	185, 1, 185, 190, 185, 185, 185, 185, 
-	185, 194, 185, 196, 197, 198, 199, 3, 
-	1, 185, 190, 185, 185, 33, 185, 185, 
-	185, 191, 185, 200, 197, 201, 201, 3, 
-	1, 185, 190, 185, 185, 185, 185, 185, 
-	185, 191, 185, 197, 201, 201, 3, 1, 
-	185, 190, 185, 185, 185, 185, 185, 185, 
-	191, 185, 202, 185, 185, 185, 16, 203, 
-	185, 1, 185, 190, 185, 185, 185, 185, 
-	185, 202, 185, 204, 205, 206, 207, 3, 
-	1, 185, 190, 185, 185, 31, 185, 185, 
-	185, 191, 185, 208, 205, 209, 209, 3, 
-	1, 185, 190, 185, 185, 185, 185, 185, 
-	185, 191, 185, 205, 209, 209, 3, 1, 
-	185, 190, 185, 185, 185, 185, 185, 185, 
-	191, 185, 210, 185, 185, 185, 16, 211, 
-	185, 1, 185, 190, 185, 185, 185, 185, 
-	185, 210, 185, 212, 213, 214, 215, 3, 
-	1, 185, 190, 185, 185, 29, 185, 185, 
-	185, 191, 185, 216, 213, 217, 217, 3, 
-	1, 185, 190, 185, 185, 185, 185, 185, 
-	185, 191, 185, 213, 217, 217, 3, 1, 
-	185, 190, 185, 185, 185, 185, 185, 185, 
-	191, 185, 218, 185, 185, 185, 16, 219, 
-	185, 1, 185, 190, 185, 185, 185, 185, 
-	185, 218, 185, 220, 221, 222, 223, 3, 
-	1, 185, 190, 185, 185, 27, 185, 185, 
-	185, 191, 185, 224, 221, 225, 225, 3, 
-	1, 185, 190, 185, 185, 185, 185, 185, 
-	185, 191, 185, 221, 225, 225, 3, 1, 
-	185, 190, 185, 185, 185, 185, 185, 185, 
-	191, 185, 16, 226, 185, 1, 185, 190, 
-	185, 227, 227, 185, 1, 185, 190, 185, 
-	228, 185, 185, 229, 185, 190, 185, 190, 
-	185, 230, 185, 231, 185, 228, 185, 185, 
-	185, 185, 190, 185, 16, 185, 232, 232, 
-	3, 1, 185, 190, 185, 233, 25, 234, 
-	235, 6, 1, 185, 190, 185, 25, 234, 
-	235, 6, 1, 185, 190, 185, 234, 234, 
-	6, 1, 185, 190, 185, 236, 22, 237, 
-	238, 9, 1, 185, 190, 185, 22, 237, 
-	238, 9, 1, 185, 190, 185, 237, 237, 
-	9, 1, 185, 190, 185, 239, 19, 240, 
-	241, 12, 1, 185, 190, 185, 19, 240, 
-	241, 12, 1, 185, 190, 185, 240, 240, 
-	12, 1, 185, 190, 185, 242, 16, 227, 
-	243, 185, 1, 185, 190, 185, 16, 227, 
-	243, 185, 1, 185, 190, 185, 227, 244, 
-	185, 1, 185, 190, 185, 16, 185, 227, 
-	227, 185, 1, 185, 190, 185, 221, 225, 
-	225, 3, 1, 185, 190, 185, 220, 221, 
-	225, 225, 3, 1, 185, 190, 185, 185, 
-	185, 185, 185, 185, 191, 185, 220, 221, 
-	222, 225, 3, 1, 185, 190, 185, 185, 
-	27, 185, 185, 185, 191, 185, 218, 185, 
-	245, 185, 232, 232, 3, 1, 185, 190, 
-	185, 185, 185, 185, 185, 218, 185, 218, 
-	185, 185, 185, 227, 227, 185, 1, 185, 
-	190, 185, 185, 185, 185, 185, 218, 185, 
-	218, 185, 185, 185, 227, 246, 185, 1, 
-	185, 190, 185, 185, 185, 185, 185, 218, 
-	185, 218, 185, 245, 185, 227, 227, 185, 
-	1, 185, 190, 185, 185, 185, 185, 185, 
-	218, 185, 212, 213, 217, 217, 3, 1, 
-	185, 190, 185, 185, 185, 185, 185, 185, 
-	191, 185, 212, 213, 214, 217, 3, 1, 
-	185, 190, 185, 185, 29, 185, 185, 185, 
-	191, 185, 210, 185, 247, 185, 232, 232, 
-	3, 1, 185, 190, 185, 185, 185, 185, 
-	185, 210, 185, 210, 185, 185, 185, 227, 
-	227, 185, 1, 185, 190, 185, 185, 185, 
-	185, 185, 210, 185, 210, 185, 185, 185, 
-	227, 248, 185, 1, 185, 190, 185, 185, 
-	185, 185, 185, 210, 185, 210, 185, 247, 
-	185, 227, 227, 185, 1, 185, 190, 185, 
-	185, 185, 185, 185, 210, 185, 204, 205, 
-	209, 209, 3, 1, 185, 190, 185, 185, 
-	185, 185, 185, 185, 191, 185, 204, 205, 
-	206, 209, 3, 1, 185, 190, 185, 185, 
-	31, 185, 185, 185, 191, 185, 202, 185, 
-	249, 185, 232, 232, 3, 1, 185, 190, 
-	185, 185, 185, 185, 185, 202, 185, 202, 
-	185, 185, 185, 227, 227, 185, 1, 185, 
-	190, 185, 185, 185, 185, 185, 202, 185, 
-	202, 185, 185, 185, 227, 250, 185, 1, 
-	185, 190, 185, 185, 185, 185, 185, 202, 
-	185, 202, 185, 249, 185, 227, 227, 185, 
-	1, 185, 190, 185, 185, 185, 185, 185, 
-	202, 185, 196, 197, 201, 201, 3, 1, 
-	185, 190, 185, 185, 185, 185, 185, 185, 
-	191, 185, 196, 197, 198, 201, 3, 1, 
-	185, 190, 185, 185, 33, 185, 185, 185, 
-	191, 185, 194, 185, 251, 185, 232, 232, 
-	3, 1, 185, 190, 185, 185, 185, 185, 
-	185, 194, 185, 194, 185, 185, 185, 227, 
-	227, 185, 1, 185, 190, 185, 185, 185, 
-	185, 185, 194, 185, 194, 185, 185, 185, 
-	227, 252, 185, 1, 185, 190, 185, 185, 
-	185, 185, 185, 194, 185, 194, 185, 251, 
-	185, 227, 227, 185, 1, 185, 190, 185, 
-	185, 185, 185, 185, 194, 185, 186, 187, 
-	193, 193, 3, 1, 185, 190, 185, 185, 
-	185, 185, 185, 185, 191, 185, 186, 187, 
-	188, 193, 3, 1, 185, 190, 185, 185, 
-	35, 185, 185, 185, 191, 185, 254, 255, 
-	256, 257, 39, 37, 253, 258, 253, 253, 
-	71, 253, 253, 253, 259, 253, 260, 255, 
-	261, 257, 39, 37, 253, 258, 253, 253, 
-	253, 253, 253, 253, 259, 253, 255, 261, 
-	257, 39, 37, 253, 258, 253, 253, 253, 
-	253, 253, 253, 259, 253, 262, 253, 253, 
-	253, 52, 263, 253, 37, 253, 258, 253, 
-	253, 253, 253, 253, 262, 253, 264, 265, 
-	266, 267, 39, 37, 253, 258, 253, 253, 
-	69, 253, 253, 253, 259, 253, 268, 265, 
-	269, 269, 39, 37, 253, 258, 253, 253, 
-	253, 253, 253, 253, 259, 253, 265, 269, 
-	269, 39, 37, 253, 258, 253, 253, 253, 
-	253, 253, 253, 259, 253, 270, 253, 253, 
-	253, 52, 271, 253, 37, 253, 258, 253, 
-	253, 253, 253, 253, 270, 253, 272, 273, 
-	274, 275, 39, 37, 253, 258, 253, 253, 
-	67, 253, 253, 253, 259, 253, 276, 273, 
-	277, 277, 39, 37, 253, 258, 253, 253, 
-	253, 253, 253, 253, 259, 253, 273, 277, 
-	277, 39, 37, 253, 258, 253, 253, 253, 
-	253, 253, 253, 259, 253, 278, 253, 253, 
-	253, 52, 279, 253, 37, 253, 258, 253, 
-	253, 253, 253, 253, 278, 253, 280, 281, 
-	282, 283, 39, 37, 253, 258, 253, 253, 
-	65, 253, 253, 253, 259, 253, 284, 281, 
-	285, 285, 39, 37, 253, 258, 253, 253, 
-	253, 253, 253, 253, 259, 253, 281, 285, 
-	285, 39, 37, 253, 258, 253, 253, 253, 
-	253, 253, 253, 259, 253, 286, 253, 253, 
-	253, 52, 287, 253, 37, 253, 258, 253, 
-	253, 253, 253, 253, 286, 253, 288, 289, 
-	290, 291, 39, 37, 253, 258, 253, 253, 
-	63, 253, 253, 253, 259, 253, 292, 289, 
-	293, 293, 39, 37, 253, 258, 253, 253, 
-	253, 253, 253, 253, 259, 253, 289, 293, 
-	293, 39, 37, 253, 258, 253, 253, 253, 
-	253, 253, 253, 259, 253, 52, 294, 253, 
-	37, 253, 258, 253, 295, 295, 253, 37, 
-	253, 258, 253, 296, 253, 253, 297, 253, 
-	258, 253, 258, 253, 298, 253, 299, 253, 
-	296, 253, 253, 253, 253, 258, 253, 52, 
-	253, 300, 300, 39, 37, 253, 258, 253, 
-	301, 61, 302, 303, 42, 37, 253, 258, 
-	253, 61, 302, 303, 42, 37, 253, 258, 
-	253, 302, 302, 42, 37, 253, 258, 253, 
-	304, 58, 305, 306, 45, 37, 253, 258, 
-	253, 58, 305, 306, 45, 37, 253, 258, 
-	253, 305, 305, 45, 37, 253, 258, 253, 
-	307, 55, 308, 309, 48, 37, 253, 258, 
-	253, 55, 308, 309, 48, 37, 253, 258, 
-	253, 308, 308, 48, 37, 253, 258, 253, 
-	310, 52, 295, 311, 253, 37, 253, 258, 
-	253, 52, 295, 311, 253, 37, 253, 258, 
-	253, 295, 312, 253, 37, 253, 258, 253, 
-	52, 253, 295, 295, 253, 37, 253, 258, 
-	253, 289, 293, 293, 39, 37, 253, 258, 
-	253, 288, 289, 293, 293, 39, 37, 253, 
-	258, 253, 253, 253, 253, 253, 253, 259, 
-	253, 288, 289, 290, 293, 39, 37, 253, 
-	258, 253, 253, 63, 253, 253, 253, 259, 
-	253, 286, 253, 313, 253, 300, 300, 39, 
-	37, 253, 258, 253, 253, 253, 253, 253, 
-	286, 253, 286, 253, 253, 253, 295, 295, 
-	253, 37, 253, 258, 253, 253, 253, 253, 
-	253, 286, 253, 286, 253, 253, 253, 295, 
-	314, 253, 37, 253, 258, 253, 253, 253, 
-	253, 253, 286, 253, 286, 253, 313, 253, 
-	295, 295, 253, 37, 253, 258, 253, 253, 
-	253, 253, 253, 286, 253, 280, 281, 285, 
-	285, 39, 37, 253, 258, 253, 253, 253, 
-	253, 253, 253, 259, 253, 280, 281, 282, 
-	285, 39, 37, 253, 258, 253, 253, 65, 
-	253, 253, 253, 259, 253, 278, 253, 315, 
-	253, 300, 300, 39, 37, 253, 258, 253, 
-	253, 253, 253, 253, 278, 253, 278, 253, 
-	253, 253, 295, 295, 253, 37, 253, 258, 
-	253, 253, 253, 253, 253, 278, 253, 278, 
-	253, 253, 253, 295, 316, 253, 37, 253, 
-	258, 253, 253, 253, 253, 253, 278, 253, 
-	278, 253, 315, 253, 295, 295, 253, 37, 
-	253, 258, 253, 253, 253, 253, 253, 278, 
-	253, 272, 273, 277, 277, 39, 37, 253, 
-	258, 253, 253, 253, 253, 253, 253, 259, 
-	253, 272, 273, 274, 277, 39, 37, 253, 
-	258, 253, 253, 67, 253, 253, 253, 259, 
-	253, 270, 253, 317, 253, 300, 300, 39, 
-	37, 253, 258, 253, 253, 253, 253, 253, 
-	270, 253, 270, 253, 253, 253, 295, 295, 
-	253, 37, 253, 258, 253, 253, 253, 253, 
-	253, 270, 253, 270, 253, 253, 253, 295, 
-	318, 253, 37, 253, 258, 253, 253, 253, 
-	253, 253, 270, 253, 270, 253, 317, 253, 
-	295, 295, 253, 37, 253, 258, 253, 253, 
-	253, 253, 253, 270, 253, 264, 265, 269, 
-	269, 39, 37, 253, 258, 253, 253, 253, 
-	253, 253, 253, 259, 253, 264, 265, 266, 
-	269, 39, 37, 253, 258, 253, 253, 69, 
-	253, 253, 253, 259, 253, 262, 253, 319, 
-	253, 300, 300, 39, 37, 253, 258, 253, 
-	253, 253, 253, 253, 262, 253, 262, 253, 
-	253, 253, 295, 295, 253, 37, 253, 258, 
-	253, 253, 253, 253, 253, 262, 253, 262, 
-	253, 253, 253, 295, 320, 253, 37, 253, 
-	258, 253, 253, 253, 253, 253, 262, 253, 
-	262, 253, 319, 253, 295, 295, 253, 37, 
-	253, 258, 253, 253, 253, 253, 253, 262, 
-	253, 70, 38, 38, 39, 37, 253, 254, 
-	255, 261, 257, 39, 37, 253, 258, 253, 
-	253, 253, 253, 253, 253, 259, 253, 322, 
-	175, 323, 323, 75, 73, 321, 178, 321, 
-	321, 321, 321, 321, 321, 182, 321, 175, 
-	323, 323, 75, 73, 321, 178, 321, 321, 
-	321, 321, 321, 321, 182, 321, 324, 321, 
-	321, 321, 89, 325, 321, 73, 321, 178, 
-	321, 321, 321, 321, 321, 324, 321, 326, 
-	327, 328, 329, 75, 73, 321, 178, 321, 
-	321, 106, 321, 321, 321, 182, 321, 330, 
-	327, 331, 331, 75, 73, 321, 178, 321, 
-	321, 321, 321, 321, 321, 182, 321, 327, 
-	331, 331, 75, 73, 321, 178, 321, 321, 
-	321, 321, 321, 321, 182, 321, 332, 321, 
-	321, 321, 89, 333, 321, 73, 321, 178, 
-	321, 321, 321, 321, 321, 332, 321, 334, 
-	335, 336, 337, 75, 73, 321, 178, 321, 
-	321, 104, 321, 321, 321, 182, 321, 338, 
-	335, 339, 339, 75, 73, 321, 178, 321, 
-	321, 321, 321, 321, 321, 182, 321, 335, 
-	339, 339, 75, 73, 321, 178, 321, 321, 
-	321, 321, 321, 321, 182, 321, 340, 321, 
-	321, 321, 89, 341, 321, 73, 321, 178, 
-	321, 321, 321, 321, 321, 340, 321, 342, 
-	343, 344, 345, 75, 73, 321, 178, 321, 
-	321, 102, 321, 321, 321, 182, 321, 346, 
-	343, 347, 347, 75, 73, 321, 178, 321, 
-	321, 321, 321, 321, 321, 182, 321, 343, 
-	347, 347, 75, 73, 321, 178, 321, 321, 
-	321, 321, 321, 321, 182, 321, 348, 321, 
-	321, 321, 89, 349, 321, 73, 321, 178, 
-	321, 321, 321, 321, 321, 348, 321, 350, 
-	351, 352, 353, 75, 73, 321, 178, 321, 
-	321, 100, 321, 321, 321, 182, 321, 354, 
-	351, 355, 355, 75, 73, 321, 178, 321, 
-	321, 321, 321, 321, 321, 182, 321, 351, 
-	355, 355, 75, 73, 321, 178, 321, 321, 
-	321, 321, 321, 321, 182, 321, 89, 356, 
-	321, 73, 321, 178, 321, 357, 357, 321, 
-	73, 321, 178, 321, 358, 321, 321, 359, 
-	321, 178, 321, 178, 321, 360, 321, 361, 
-	321, 358, 321, 321, 321, 321, 178, 321, 
-	89, 321, 362, 362, 75, 73, 321, 178, 
-	321, 363, 98, 364, 365, 79, 73, 321, 
-	178, 321, 98, 364, 365, 79, 73, 321, 
-	178, 321, 364, 364, 79, 73, 321, 178, 
-	321, 366, 95, 367, 368, 82, 73, 321, 
-	178, 321, 95, 367, 368, 82, 73, 321, 
-	178, 321, 367, 367, 82, 73, 321, 178, 
-	321, 369, 92, 370, 371, 85, 73, 321, 
-	178, 321, 92, 370, 371, 85, 73, 321, 
-	178, 321, 370, 370, 85, 73, 321, 178, 
-	321, 372, 89, 357, 373, 321, 73, 321, 
-	178, 321, 89, 357, 373, 321, 73, 321, 
-	178, 321, 357, 374, 321, 73, 321, 178, 
-	321, 89, 321, 357, 357, 321, 73, 321, 
-	178, 321, 351, 355, 355, 75, 73, 321, 
-	178, 321, 350, 351, 355, 355, 75, 73, 
-	321, 178, 321, 321, 321, 321, 321, 321, 
-	182, 321, 350, 351, 352, 355, 75, 73, 
-	321, 178, 321, 321, 100, 321, 321, 321, 
-	182, 321, 348, 321, 375, 321, 362, 362, 
-	75, 73, 321, 178, 321, 321, 321, 321, 
-	321, 348, 321, 348, 321, 321, 321, 357, 
-	357, 321, 73, 321, 178, 321, 321, 321, 
-	321, 321, 348, 321, 348, 321, 321, 321, 
-	357, 376, 321, 73, 321, 178, 321, 321, 
-	321, 321, 321, 348, 321, 348, 321, 375, 
-	321, 357, 357, 321, 73, 321, 178, 321, 
-	321, 321, 321, 321, 348, 321, 342, 343, 
-	347, 347, 75, 73, 321, 178, 321, 321, 
-	321, 321, 321, 321, 182, 321, 342, 343, 
-	344, 347, 75, 73, 321, 178, 321, 321, 
-	102, 321, 321, 321, 182, 321, 340, 321, 
-	377, 321, 362, 362, 75, 73, 321, 178, 
-	321, 321, 321, 321, 321, 340, 321, 340, 
-	321, 321, 321, 357, 357, 321, 73, 321, 
-	178, 321, 321, 321, 321, 321, 340, 321, 
-	340, 321, 321, 321, 357, 378, 321, 73, 
-	321, 178, 321, 321, 321, 321, 321, 340, 
-	321, 340, 321, 377, 321, 357, 357, 321, 
-	73, 321, 178, 321, 321, 321, 321, 321, 
-	340, 321, 334, 335, 339, 339, 75, 73, 
-	321, 178, 321, 321, 321, 321, 321, 321, 
-	182, 321, 334, 335, 336, 339, 75, 73, 
-	321, 178, 321, 321, 104, 321, 321, 321, 
-	182, 321, 332, 321, 379, 321, 362, 362, 
-	75, 73, 321, 178, 321, 321, 321, 321, 
-	321, 332, 321, 332, 321, 321, 321, 357, 
-	357, 321, 73, 321, 178, 321, 321, 321, 
-	321, 321, 332, 321, 332, 321, 321, 321, 
-	357, 380, 321, 73, 321, 178, 321, 321, 
-	321, 321, 321, 332, 321, 332, 321, 379, 
-	321, 357, 357, 321, 73, 321, 178, 321, 
-	321, 321, 321, 321, 332, 321, 326, 327, 
-	331, 331, 75, 73, 321, 178, 321, 321, 
-	321, 321, 321, 321, 182, 321, 326, 327, 
-	328, 331, 75, 73, 321, 178, 321, 321, 
-	106, 321, 321, 321, 182, 321, 324, 321, 
-	381, 321, 362, 362, 75, 73, 321, 178, 
-	321, 321, 321, 321, 321, 324, 321, 324, 
-	321, 321, 321, 357, 357, 321, 73, 321, 
-	178, 321, 321, 321, 321, 321, 324, 321, 
-	324, 321, 321, 321, 357, 382, 321, 73, 
-	321, 178, 321, 321, 321, 321, 321, 324, 
-	321, 324, 321, 381, 321, 357, 357, 321, 
-	73, 321, 178, 321, 321, 321, 321, 321, 
-	324, 321, 107, 74, 74, 75, 73, 383, 
-	383, 383, 383, 144, 383, 174, 175, 323, 
-	323, 75, 73, 321, 178, 321, 321, 321, 
-	321, 321, 321, 182, 321, 107, 74, 74, 
-	75, 73, 383, 385, 386, 387, 388, 111, 
-	109, 384, 389, 384, 384, 143, 384, 384, 
-	384, 390, 384, 391, 386, 388, 388, 111, 
-	109, 384, 389, 384, 384, 384, 384, 384, 
-	384, 390, 384, 386, 388, 388, 111, 109, 
-	384, 389, 384, 384, 384, 384, 384, 384, 
-	390, 384, 392, 384, 384, 384, 124, 393, 
-	384, 109, 384, 389, 384, 384, 384, 384, 
-	384, 392, 384, 394, 395, 396, 397, 111, 
-	109, 384, 389, 384, 384, 141, 384, 384, 
-	384, 390, 384, 398, 395, 399, 399, 111, 
-	109, 384, 389, 384, 384, 384, 384, 384, 
-	384, 390, 384, 395, 399, 399, 111, 109, 
-	384, 389, 384, 384, 384, 384, 384, 384, 
-	390, 384, 400, 384, 384, 384, 124, 401, 
-	384, 109, 384, 389, 384, 384, 384, 384, 
-	384, 400, 384, 402, 403, 404, 405, 111, 
-	109, 384, 389, 384, 384, 139, 384, 384, 
-	384, 390, 384, 406, 403, 407, 407, 111, 
-	109, 384, 389, 384, 384, 384, 384, 384, 
-	384, 390, 384, 403, 407, 407, 111, 109, 
-	384, 389, 384, 384, 384, 384, 384, 384, 
-	390, 384, 408, 384, 384, 384, 124, 409, 
-	384, 109, 384, 389, 384, 384, 384, 384, 
-	384, 408, 384, 410, 411, 412, 413, 111, 
-	109, 384, 389, 384, 384, 137, 384, 384, 
-	384, 390, 384, 414, 411, 415, 415, 111, 
-	109, 384, 389, 384, 384, 384, 384, 384, 
-	384, 390, 384, 411, 415, 415, 111, 109, 
-	384, 389, 384, 384, 384, 384, 384, 384, 
-	390, 384, 416, 384, 384, 384, 124, 417, 
-	384, 109, 384, 389, 384, 384, 384, 384, 
-	384, 416, 384, 418, 419, 420, 421, 111, 
-	109, 384, 389, 384, 384, 135, 384, 384, 
-	384, 390, 384, 422, 419, 423, 423, 111, 
-	109, 384, 389, 384, 384, 384, 384, 384, 
-	384, 390, 384, 419, 423, 423, 111, 109, 
-	384, 389, 384, 384, 384, 384, 384, 384, 
-	390, 384, 124, 424, 384, 109, 384, 389, 
-	384, 425, 425, 384, 109, 384, 389, 384, 
-	426, 384, 384, 427, 384, 389, 384, 389, 
-	384, 428, 384, 429, 384, 426, 384, 384, 
-	384, 384, 389, 384, 124, 384, 430, 430, 
-	111, 109, 384, 389, 384, 431, 133, 432, 
-	433, 114, 109, 384, 389, 384, 133, 432, 
-	433, 114, 109, 384, 389, 384, 432, 432, 
-	114, 109, 384, 389, 384, 434, 130, 435, 
-	436, 117, 109, 384, 389, 384, 130, 435, 
-	436, 117, 109, 384, 389, 384, 435, 435, 
-	117, 109, 384, 389, 384, 437, 127, 438, 
-	439, 120, 109, 384, 389, 384, 127, 438, 
-	439, 120, 109, 384, 389, 384, 438, 438, 
-	120, 109, 384, 389, 384, 440, 124, 425, 
-	441, 384, 109, 384, 389, 384, 124, 425, 
-	441, 384, 109, 384, 389, 384, 425, 442, 
-	384, 109, 384, 389, 384, 124, 384, 425, 
-	425, 384, 109, 384, 389, 384, 419, 423, 
-	423, 111, 109, 384, 389, 384, 418, 419, 
-	423, 423, 111, 109, 384, 389, 384, 384, 
-	384, 384, 384, 384, 390, 384, 418, 419, 
-	420, 423, 111, 109, 384, 389, 384, 384, 
-	135, 384, 384, 384, 390, 384, 416, 384, 
-	443, 384, 430, 430, 111, 109, 384, 389, 
-	384, 384, 384, 384, 384, 416, 384, 416, 
-	384, 384, 384, 425, 425, 384, 109, 384, 
-	389, 384, 384, 384, 384, 384, 416, 384, 
-	416, 384, 384, 384, 425, 444, 384, 109, 
-	384, 389, 384, 384, 384, 384, 384, 416, 
-	384, 416, 384, 443, 384, 425, 425, 384, 
-	109, 384, 389, 384, 384, 384, 384, 384, 
-	416, 384, 410, 411, 415, 415, 111, 109, 
-	384, 389, 384, 384, 384, 384, 384, 384, 
-	390, 384, 410, 411, 412, 415, 111, 109, 
-	384, 389, 384, 384, 137, 384, 384, 384, 
-	390, 384, 408, 384, 445, 384, 430, 430, 
-	111, 109, 384, 389, 384, 384, 384, 384, 
-	384, 408, 384, 408, 384, 384, 384, 425, 
-	425, 384, 109, 384, 389, 384, 384, 384, 
-	384, 384, 408, 384, 408, 384, 384, 384, 
-	425, 446, 384, 109, 384, 389, 384, 384, 
-	384, 384, 384, 408, 384, 408, 384, 445, 
-	384, 425, 425, 384, 109, 384, 389, 384, 
-	384, 384, 384, 384, 408, 384, 402, 403, 
-	407, 407, 111, 109, 384, 389, 384, 384, 
-	384, 384, 384, 384, 390, 384, 402, 403, 
-	404, 407, 111, 109, 384, 389, 384, 384, 
-	139, 384, 384, 384, 390, 384, 400, 384, 
-	447, 384, 430, 430, 111, 109, 384, 389, 
-	384, 384, 384, 384, 384, 400, 384, 400, 
-	384, 384, 384, 425, 425, 384, 109, 384, 
-	389, 384, 384, 384, 384, 384, 400, 384, 
-	400, 384, 384, 384, 425, 448, 384, 109, 
-	384, 389, 384, 384, 384, 384, 384, 400, 
-	384, 400, 384, 447, 384, 425, 425, 384, 
-	109, 384, 389, 384, 384, 384, 384, 384, 
-	400, 384, 394, 395, 399, 399, 111, 109, 
-	384, 389, 384, 384, 384, 384, 384, 384, 
-	390, 384, 394, 395, 396, 399, 111, 109, 
-	384, 389, 384, 384, 141, 384, 384, 384, 
-	390, 384, 392, 384, 449, 384, 430, 430, 
-	111, 109, 384, 389, 384, 384, 384, 384, 
-	384, 392, 384, 392, 384, 384, 384, 425, 
-	425, 384, 109, 384, 389, 384, 384, 384, 
-	384, 384, 392, 384, 392, 384, 384, 384, 
-	425, 450, 384, 109, 384, 389, 384, 384, 
-	384, 384, 384, 392, 384, 392, 384, 449, 
-	384, 425, 425, 384, 109, 384, 389, 384, 
-	384, 384, 384, 384, 392, 384, 385, 386, 
-	388, 388, 111, 109, 384, 389, 384, 384, 
-	384, 384, 384, 384, 390, 384, 172, 173, 
-	174, 175, 451, 323, 75, 73, 321, 178, 
-	179, 179, 144, 321, 321, 172, 182, 321, 
-	186, 452, 188, 189, 3, 1, 185, 190, 
-	185, 185, 35, 185, 185, 185, 191, 185, 
-	194, 173, 174, 175, 453, 454, 75, 149, 
-	185, 455, 185, 179, 144, 185, 185, 194, 
-	182, 185, 107, 456, 456, 75, 149, 185, 
-	190, 185, 185, 144, 185, 457, 185, 185, 
-	458, 185, 455, 185, 455, 185, 459, 185, 
-	231, 185, 457, 185, 185, 185, 185, 455, 
-	185, 194, 185, 251, 107, 460, 460, 146, 
-	149, 185, 190, 185, 185, 185, 185, 185, 
-	194, 185, 461, 168, 462, 463, 148, 149, 
-	185, 455, 185, 168, 462, 463, 148, 149, 
-	185, 455, 185, 462, 462, 148, 149, 185, 
-	455, 185, 464, 165, 465, 466, 152, 149, 
-	185, 455, 185, 165, 465, 466, 152, 149, 
-	185, 455, 185, 465, 465, 152, 149, 185, 
-	455, 185, 467, 162, 468, 469, 155, 149, 
-	185, 455, 185, 162, 468, 469, 155, 149, 
-	185, 455, 185, 468, 468, 155, 149, 185, 
-	455, 185, 470, 159, 471, 472, 185, 149, 
-	185, 455, 185, 159, 471, 472, 185, 149, 
-	185, 455, 185, 471, 471, 185, 149, 185, 
-	455, 185, 474, 473, 475, 475, 473, 170, 
-	473, 476, 473, 475, 475, 473, 170, 473, 
-	476, 473, 477, 473, 473, 478, 473, 476, 
-	473, 476, 473, 479, 473, 480, 473, 477, 
-	473, 473, 473, 473, 476, 473, 172, 383, 
-	383, 383, 383, 383, 383, 383, 383, 383, 
-	179, 383, 383, 383, 383, 172, 383, 0
+	1, 0, 2, 3, 3, 4, 1, 0, 
+	5, 5, 4, 0, 4, 0, 6, 6, 
+	7, 1, 0, 8, 8, 7, 0, 7, 
+	0, 9, 9, 10, 1, 0, 11, 11, 
+	10, 0, 10, 0, 12, 12, 13, 1, 
+	0, 14, 14, 13, 0, 13, 0, 15, 
+	0, 0, 0, 1, 0, 16, 0, 17, 
+	0, 18, 12, 12, 13, 1, 0, 19, 
+	0, 20, 0, 21, 9, 9, 10, 1, 
+	0, 22, 0, 23, 0, 24, 6, 6, 
+	7, 1, 0, 25, 0, 26, 0, 2, 
+	3, 3, 4, 1, 0, 0, 0, 0, 
+	27, 0, 28, 3, 3, 4, 1, 0, 
+	28, 3, 3, 4, 1, 0, 0, 0, 
+	0, 29, 0, 30, 3, 3, 4, 1, 
+	0, 30, 3, 3, 4, 1, 0, 0, 
+	0, 0, 31, 0, 32, 3, 3, 4, 
+	1, 0, 32, 3, 3, 4, 1, 0, 
+	0, 0, 0, 33, 0, 34, 3, 3, 
+	4, 1, 0, 34, 3, 3, 4, 1, 
+	0, 0, 0, 0, 35, 0, 37, 36, 
+	38, 39, 39, 40, 37, 36, 41, 41, 
+	40, 36, 40, 36, 42, 42, 43, 37, 
+	36, 44, 44, 43, 36, 43, 36, 45, 
+	45, 46, 37, 36, 47, 47, 46, 36, 
+	46, 36, 48, 48, 49, 37, 36, 50, 
+	50, 49, 36, 49, 36, 51, 36, 36, 
+	36, 37, 36, 52, 36, 53, 36, 54, 
+	48, 48, 49, 37, 36, 55, 36, 56, 
+	36, 57, 45, 45, 46, 37, 36, 58, 
+	36, 59, 36, 60, 42, 42, 43, 37, 
+	36, 61, 36, 62, 36, 38, 39, 39, 
+	40, 37, 36, 36, 36, 36, 63, 36, 
+	64, 39, 39, 40, 37, 36, 64, 39, 
+	39, 40, 37, 36, 36, 36, 36, 65, 
+	36, 66, 39, 39, 40, 37, 36, 66, 
+	39, 39, 40, 37, 36, 36, 36, 36, 
+	67, 36, 68, 39, 39, 40, 37, 36, 
+	68, 39, 39, 40, 37, 36, 36, 36, 
+	36, 69, 36, 70, 39, 39, 40, 37, 
+	36, 70, 39, 39, 40, 37, 36, 36, 
+	36, 36, 71, 36, 73, 72, 74, 75, 
+	75, 76, 73, 72, 78, 78, 76, 77, 
+	76, 77, 79, 79, 80, 73, 72, 81, 
+	81, 80, 72, 80, 72, 82, 82, 83, 
+	73, 72, 84, 84, 83, 72, 83, 72, 
+	85, 85, 86, 73, 72, 87, 87, 86, 
+	72, 86, 72, 88, 72, 72, 72, 73, 
+	72, 89, 72, 90, 72, 91, 85, 85, 
+	86, 73, 72, 92, 72, 93, 72, 94, 
+	82, 82, 83, 73, 72, 95, 72, 96, 
+	72, 97, 79, 79, 80, 73, 72, 98, 
+	72, 99, 72, 74, 75, 75, 76, 73, 
+	72, 72, 72, 72, 100, 72, 101, 75, 
+	75, 76, 73, 72, 101, 75, 75, 76, 
+	73, 72, 72, 72, 72, 102, 72, 103, 
+	75, 75, 76, 73, 72, 103, 75, 75, 
+	76, 73, 72, 72, 72, 72, 104, 72, 
+	105, 75, 75, 76, 73, 72, 105, 75, 
+	75, 76, 73, 72, 72, 72, 72, 106, 
+	72, 107, 75, 75, 76, 73, 72, 109, 
+	108, 110, 111, 111, 112, 109, 108, 113, 
+	113, 112, 108, 112, 108, 114, 114, 115, 
+	109, 108, 116, 116, 115, 108, 115, 108, 
+	117, 117, 118, 109, 108, 119, 119, 118, 
+	108, 118, 108, 120, 120, 121, 109, 108, 
+	122, 122, 121, 108, 121, 108, 123, 108, 
+	108, 108, 109, 108, 124, 108, 125, 108, 
+	126, 120, 120, 121, 109, 108, 127, 108, 
+	128, 108, 129, 117, 117, 118, 109, 108, 
+	130, 108, 131, 108, 132, 114, 114, 115, 
+	109, 108, 133, 108, 134, 108, 110, 111, 
+	111, 112, 109, 108, 108, 108, 108, 135, 
+	108, 136, 111, 111, 112, 109, 108, 136, 
+	111, 111, 112, 109, 108, 108, 108, 108, 
+	137, 108, 138, 111, 111, 112, 109, 108, 
+	138, 111, 111, 112, 109, 108, 108, 108, 
+	108, 139, 108, 140, 111, 111, 112, 109, 
+	108, 140, 111, 111, 112, 109, 108, 108, 
+	108, 108, 141, 108, 142, 111, 111, 112, 
+	109, 108, 142, 111, 111, 112, 109, 108, 
+	108, 108, 108, 143, 108, 107, 75, 75, 
+	76, 73, 72, 72, 72, 72, 144, 72, 
+	78, 78, 76, 1, 0, 146, 145, 148, 
+	149, 150, 151, 152, 153, 76, 73, 147, 
+	154, 155, 155, 144, 147, 156, 157, 147, 
+	158, 159, 147, 161, 162, 163, 164, 4, 
+	1, 160, 165, 160, 160, 35, 160, 166, 
+	162, 167, 167, 4, 1, 160, 165, 160, 
+	162, 167, 167, 4, 1, 160, 165, 160, 
+	168, 160, 160, 160, 17, 169, 160, 1, 
+	160, 165, 160, 160, 160, 160, 160, 168, 
+	160, 170, 171, 172, 173, 4, 1, 160, 
+	165, 160, 160, 33, 160, 174, 171, 175, 
+	175, 4, 1, 160, 165, 160, 171, 175, 
+	175, 4, 1, 160, 165, 160, 176, 160, 
+	160, 160, 17, 177, 160, 1, 160, 165, 
+	160, 160, 160, 160, 160, 176, 160, 178, 
+	179, 180, 181, 4, 1, 160, 165, 160, 
+	160, 31, 160, 182, 179, 183, 183, 4, 
+	1, 160, 165, 160, 179, 183, 183, 4, 
+	1, 160, 165, 160, 184, 160, 160, 160, 
+	17, 185, 160, 1, 160, 165, 160, 160, 
+	160, 160, 160, 184, 160, 186, 187, 188, 
+	189, 4, 1, 160, 165, 160, 160, 29, 
+	160, 190, 187, 191, 191, 4, 1, 160, 
+	165, 160, 187, 191, 191, 4, 1, 160, 
+	165, 160, 192, 160, 160, 160, 17, 193, 
+	160, 1, 160, 165, 160, 160, 160, 160, 
+	160, 192, 160, 194, 195, 196, 197, 4, 
+	1, 160, 165, 160, 160, 27, 160, 198, 
+	195, 199, 199, 4, 1, 160, 165, 160, 
+	195, 199, 199, 4, 1, 160, 165, 160, 
+	17, 200, 160, 1, 160, 165, 160, 201, 
+	201, 160, 1, 160, 165, 160, 202, 160, 
+	160, 203, 160, 165, 160, 165, 160, 204, 
+	160, 205, 160, 202, 160, 160, 160, 160, 
+	165, 160, 17, 160, 201, 201, 160, 1, 
+	160, 165, 160, 201, 200, 160, 1, 160, 
+	165, 160, 206, 26, 207, 208, 7, 1, 
+	160, 165, 160, 26, 207, 208, 7, 1, 
+	160, 165, 160, 207, 207, 7, 1, 160, 
+	165, 160, 209, 23, 210, 211, 10, 1, 
+	160, 165, 160, 23, 210, 211, 10, 1, 
+	160, 165, 160, 210, 210, 10, 1, 160, 
+	165, 160, 212, 20, 213, 214, 13, 1, 
+	160, 165, 160, 20, 213, 214, 13, 1, 
+	160, 165, 160, 213, 213, 13, 1, 160, 
+	165, 160, 215, 17, 201, 216, 160, 1, 
+	160, 165, 160, 17, 201, 216, 160, 1, 
+	160, 165, 160, 194, 195, 199, 199, 4, 
+	1, 160, 165, 160, 194, 195, 196, 199, 
+	4, 1, 160, 165, 160, 160, 27, 160, 
+	192, 160, 217, 160, 201, 201, 160, 1, 
+	160, 165, 160, 160, 160, 160, 160, 192, 
+	160, 192, 160, 160, 160, 201, 201, 160, 
+	1, 160, 165, 160, 160, 160, 160, 160, 
+	192, 160, 192, 160, 160, 160, 201, 193, 
+	160, 1, 160, 165, 160, 160, 160, 160, 
+	160, 192, 160, 186, 187, 191, 191, 4, 
+	1, 160, 165, 160, 186, 187, 188, 191, 
+	4, 1, 160, 165, 160, 160, 29, 160, 
+	184, 160, 218, 160, 201, 201, 160, 1, 
+	160, 165, 160, 160, 160, 160, 160, 184, 
+	160, 184, 160, 160, 160, 201, 201, 160, 
+	1, 160, 165, 160, 160, 160, 160, 160, 
+	184, 160, 184, 160, 160, 160, 201, 185, 
+	160, 1, 160, 165, 160, 160, 160, 160, 
+	160, 184, 160, 178, 179, 183, 183, 4, 
+	1, 160, 165, 160, 178, 179, 180, 183, 
+	4, 1, 160, 165, 160, 160, 31, 160, 
+	176, 160, 219, 160, 201, 201, 160, 1, 
+	160, 165, 160, 160, 160, 160, 160, 176, 
+	160, 176, 160, 160, 160, 201, 201, 160, 
+	1, 160, 165, 160, 160, 160, 160, 160, 
+	176, 160, 176, 160, 160, 160, 201, 177, 
+	160, 1, 160, 165, 160, 160, 160, 160, 
+	160, 176, 160, 170, 171, 175, 175, 4, 
+	1, 160, 165, 160, 170, 171, 172, 175, 
+	4, 1, 160, 165, 160, 160, 33, 160, 
+	168, 160, 220, 160, 201, 201, 160, 1, 
+	160, 165, 160, 160, 160, 160, 160, 168, 
+	160, 168, 160, 160, 160, 201, 201, 160, 
+	1, 160, 165, 160, 160, 160, 160, 160, 
+	168, 160, 168, 160, 160, 160, 201, 169, 
+	160, 1, 160, 165, 160, 160, 160, 160, 
+	160, 168, 160, 161, 162, 167, 167, 4, 
+	1, 160, 165, 160, 161, 162, 163, 167, 
+	4, 1, 160, 165, 160, 160, 35, 160, 
+	222, 223, 224, 225, 40, 37, 221, 226, 
+	221, 221, 71, 221, 227, 223, 228, 225, 
+	40, 37, 221, 226, 221, 223, 228, 225, 
+	40, 37, 221, 226, 221, 229, 221, 221, 
+	221, 53, 230, 221, 37, 221, 226, 221, 
+	221, 221, 221, 221, 229, 221, 231, 232, 
+	233, 234, 40, 37, 221, 226, 221, 221, 
+	69, 221, 235, 232, 236, 236, 40, 37, 
+	221, 226, 221, 232, 236, 236, 40, 37, 
+	221, 226, 221, 237, 221, 221, 221, 53, 
+	238, 221, 37, 221, 226, 221, 221, 221, 
+	221, 221, 237, 221, 239, 240, 241, 242, 
+	40, 37, 221, 226, 221, 221, 67, 221, 
+	243, 240, 244, 244, 40, 37, 221, 226, 
+	221, 240, 244, 244, 40, 37, 221, 226, 
+	221, 245, 221, 221, 221, 53, 246, 221, 
+	37, 221, 226, 221, 221, 221, 221, 221, 
+	245, 221, 247, 248, 249, 250, 40, 37, 
+	221, 226, 221, 221, 65, 221, 251, 248, 
+	252, 252, 40, 37, 221, 226, 221, 248, 
+	252, 252, 40, 37, 221, 226, 221, 253, 
+	221, 221, 221, 53, 254, 221, 37, 221, 
+	226, 221, 221, 221, 221, 221, 253, 221, 
+	255, 256, 257, 258, 40, 37, 221, 226, 
+	221, 221, 63, 221, 259, 256, 260, 260, 
+	40, 37, 221, 226, 221, 256, 260, 260, 
+	40, 37, 221, 226, 221, 53, 261, 221, 
+	37, 221, 226, 221, 262, 262, 221, 37, 
+	221, 226, 221, 263, 221, 221, 264, 221, 
+	226, 221, 226, 221, 265, 221, 266, 221, 
+	263, 221, 221, 221, 221, 226, 221, 53, 
+	221, 262, 262, 221, 37, 221, 226, 221, 
+	262, 261, 221, 37, 221, 226, 221, 267, 
+	62, 268, 269, 43, 37, 221, 226, 221, 
+	62, 268, 269, 43, 37, 221, 226, 221, 
+	268, 268, 43, 37, 221, 226, 221, 270, 
+	59, 271, 272, 46, 37, 221, 226, 221, 
+	59, 271, 272, 46, 37, 221, 226, 221, 
+	271, 271, 46, 37, 221, 226, 221, 273, 
+	56, 274, 275, 49, 37, 221, 226, 221, 
+	56, 274, 275, 49, 37, 221, 226, 221, 
+	274, 274, 49, 37, 221, 226, 221, 276, 
+	53, 262, 277, 221, 37, 221, 226, 221, 
+	53, 262, 277, 221, 37, 221, 226, 221, 
+	255, 256, 260, 260, 40, 37, 221, 226, 
+	221, 255, 256, 257, 260, 40, 37, 221, 
+	226, 221, 221, 63, 221, 253, 221, 278, 
+	221, 262, 262, 221, 37, 221, 226, 221, 
+	221, 221, 221, 221, 253, 221, 253, 221, 
+	221, 221, 262, 262, 221, 37, 221, 226, 
+	221, 221, 221, 221, 221, 253, 221, 253, 
+	221, 221, 221, 262, 254, 221, 37, 221, 
+	226, 221, 221, 221, 221, 221, 253, 221, 
+	247, 248, 252, 252, 40, 37, 221, 226, 
+	221, 247, 248, 249, 252, 40, 37, 221, 
+	226, 221, 221, 65, 221, 245, 221, 279, 
+	221, 262, 262, 221, 37, 221, 226, 221, 
+	221, 221, 221, 221, 245, 221, 245, 221, 
+	221, 221, 262, 262, 221, 37, 221, 226, 
+	221, 221, 221, 221, 221, 245, 221, 245, 
+	221, 221, 221, 262, 246, 221, 37, 221, 
+	226, 221, 221, 221, 221, 221, 245, 221, 
+	239, 240, 244, 244, 40, 37, 221, 226, 
+	221, 239, 240, 241, 244, 40, 37, 221, 
+	226, 221, 221, 67, 221, 237, 221, 280, 
+	221, 262, 262, 221, 37, 221, 226, 221, 
+	221, 221, 221, 221, 237, 221, 237, 221, 
+	221, 221, 262, 262, 221, 37, 221, 226, 
+	221, 221, 221, 221, 221, 237, 221, 237, 
+	221, 221, 221, 262, 238, 221, 37, 221, 
+	226, 221, 221, 221, 221, 221, 237, 221, 
+	231, 232, 236, 236, 40, 37, 221, 226, 
+	221, 231, 232, 233, 236, 40, 37, 221, 
+	226, 221, 221, 69, 221, 229, 221, 281, 
+	221, 262, 262, 221, 37, 221, 226, 221, 
+	221, 221, 221, 221, 229, 221, 229, 221, 
+	221, 221, 262, 262, 221, 37, 221, 226, 
+	221, 221, 221, 221, 221, 229, 221, 229, 
+	221, 221, 221, 262, 230, 221, 37, 221, 
+	226, 221, 221, 221, 221, 221, 229, 221, 
+	70, 39, 39, 40, 37, 221, 222, 223, 
+	228, 225, 40, 37, 221, 226, 221, 283, 
+	151, 284, 284, 76, 73, 282, 154, 282, 
+	151, 284, 284, 76, 73, 282, 154, 282, 
+	285, 282, 282, 282, 90, 286, 282, 73, 
+	282, 154, 282, 282, 282, 282, 282, 285, 
+	282, 287, 288, 289, 290, 76, 73, 282, 
+	154, 282, 282, 106, 282, 291, 288, 292, 
+	292, 76, 73, 282, 154, 282, 288, 292, 
+	292, 76, 73, 282, 154, 282, 293, 282, 
+	282, 282, 90, 294, 282, 73, 282, 154, 
+	282, 282, 282, 282, 282, 293, 282, 295, 
+	296, 297, 298, 76, 73, 282, 154, 282, 
+	282, 104, 282, 299, 296, 300, 300, 76, 
+	73, 282, 154, 282, 296, 300, 300, 76, 
+	73, 282, 154, 282, 301, 282, 282, 282, 
+	90, 302, 282, 73, 282, 154, 282, 282, 
+	282, 282, 282, 301, 282, 303, 304, 305, 
+	306, 76, 73, 282, 154, 282, 282, 102, 
+	282, 307, 304, 308, 308, 76, 73, 282, 
+	154, 282, 304, 308, 308, 76, 73, 282, 
+	154, 282, 309, 282, 282, 282, 90, 310, 
+	282, 73, 282, 154, 282, 282, 282, 282, 
+	282, 309, 282, 311, 312, 313, 314, 76, 
+	73, 282, 154, 282, 282, 100, 282, 315, 
+	312, 316, 316, 76, 73, 282, 154, 282, 
+	312, 316, 316, 76, 73, 282, 154, 282, 
+	90, 317, 282, 73, 282, 154, 282, 318, 
+	318, 282, 73, 282, 154, 282, 319, 282, 
+	282, 320, 282, 154, 282, 154, 282, 321, 
+	282, 322, 282, 319, 282, 282, 282, 282, 
+	154, 282, 90, 282, 318, 318, 282, 73, 
+	282, 154, 282, 318, 317, 282, 73, 282, 
+	154, 282, 323, 99, 324, 325, 80, 73, 
+	282, 154, 282, 99, 324, 325, 80, 73, 
+	282, 154, 282, 324, 324, 80, 73, 282, 
+	154, 282, 326, 96, 327, 328, 83, 73, 
+	282, 154, 282, 96, 327, 328, 83, 73, 
+	282, 154, 282, 327, 327, 83, 73, 282, 
+	154, 282, 329, 93, 330, 331, 86, 73, 
+	282, 154, 282, 93, 330, 331, 86, 73, 
+	282, 154, 282, 330, 330, 86, 73, 282, 
+	154, 282, 332, 90, 318, 333, 282, 73, 
+	282, 154, 282, 90, 318, 333, 282, 73, 
+	282, 154, 282, 311, 312, 316, 316, 76, 
+	73, 282, 154, 282, 311, 312, 313, 316, 
+	76, 73, 282, 154, 282, 282, 100, 282, 
+	309, 282, 334, 282, 318, 318, 282, 73, 
+	282, 154, 282, 282, 282, 282, 282, 309, 
+	282, 309, 282, 282, 282, 318, 318, 282, 
+	73, 282, 154, 282, 282, 282, 282, 282, 
+	309, 282, 309, 282, 282, 282, 318, 310, 
+	282, 73, 282, 154, 282, 282, 282, 282, 
+	282, 309, 282, 303, 304, 308, 308, 76, 
+	73, 282, 154, 282, 303, 304, 305, 308, 
+	76, 73, 282, 154, 282, 282, 102, 282, 
+	301, 282, 335, 282, 318, 318, 282, 73, 
+	282, 154, 282, 282, 282, 282, 282, 301, 
+	282, 301, 282, 282, 282, 318, 318, 282, 
+	73, 282, 154, 282, 282, 282, 282, 282, 
+	301, 282, 301, 282, 282, 282, 318, 302, 
+	282, 73, 282, 154, 282, 282, 282, 282, 
+	282, 301, 282, 295, 296, 300, 300, 76, 
+	73, 282, 154, 282, 295, 296, 297, 300, 
+	76, 73, 282, 154, 282, 282, 104, 282, 
+	293, 282, 336, 282, 318, 318, 282, 73, 
+	282, 154, 282, 282, 282, 282, 282, 293, 
+	282, 293, 282, 282, 282, 318, 318, 282, 
+	73, 282, 154, 282, 282, 282, 282, 282, 
+	293, 282, 293, 282, 282, 282, 318, 294, 
+	282, 73, 282, 154, 282, 282, 282, 282, 
+	282, 293, 282, 287, 288, 292, 292, 76, 
+	73, 282, 154, 282, 287, 288, 289, 292, 
+	76, 73, 282, 154, 282, 282, 106, 282, 
+	285, 282, 337, 282, 318, 318, 282, 73, 
+	282, 154, 282, 282, 282, 282, 282, 285, 
+	282, 285, 282, 282, 282, 318, 318, 282, 
+	73, 282, 154, 282, 282, 282, 282, 282, 
+	285, 282, 285, 282, 282, 282, 318, 286, 
+	282, 73, 282, 154, 282, 282, 282, 282, 
+	282, 285, 282, 107, 75, 75, 76, 73, 
+	338, 338, 338, 338, 144, 338, 150, 151, 
+	284, 284, 76, 73, 282, 154, 282, 107, 
+	75, 75, 76, 73, 338, 340, 341, 342, 
+	343, 112, 109, 339, 344, 339, 339, 143, 
+	339, 345, 341, 343, 343, 112, 109, 339, 
+	344, 339, 341, 343, 343, 112, 109, 339, 
+	344, 339, 346, 339, 339, 339, 125, 347, 
+	339, 109, 339, 344, 339, 339, 339, 339, 
+	339, 346, 339, 348, 349, 350, 351, 112, 
+	109, 339, 344, 339, 339, 141, 339, 352, 
+	349, 353, 353, 112, 109, 339, 344, 339, 
+	349, 353, 353, 112, 109, 339, 344, 339, 
+	354, 339, 339, 339, 125, 355, 339, 109, 
+	339, 344, 339, 339, 339, 339, 339, 354, 
+	339, 356, 357, 358, 359, 112, 109, 339, 
+	344, 339, 339, 139, 339, 360, 357, 361, 
+	361, 112, 109, 339, 344, 339, 357, 361, 
+	361, 112, 109, 339, 344, 339, 362, 339, 
+	339, 339, 125, 363, 339, 109, 339, 344, 
+	339, 339, 339, 339, 339, 362, 339, 364, 
+	365, 366, 367, 112, 109, 339, 344, 339, 
+	339, 137, 339, 368, 365, 369, 369, 112, 
+	109, 339, 344, 339, 365, 369, 369, 112, 
+	109, 339, 344, 339, 370, 339, 339, 339, 
+	125, 371, 339, 109, 339, 344, 339, 339, 
+	339, 339, 339, 370, 339, 372, 373, 374, 
+	375, 112, 109, 339, 344, 339, 339, 135, 
+	339, 376, 373, 377, 377, 112, 109, 339, 
+	344, 339, 373, 377, 377, 112, 109, 339, 
+	344, 339, 125, 378, 339, 109, 339, 344, 
+	339, 379, 379, 339, 109, 339, 344, 339, 
+	380, 339, 339, 381, 339, 344, 339, 344, 
+	339, 382, 339, 383, 339, 380, 339, 339, 
+	339, 339, 344, 339, 125, 339, 379, 379, 
+	339, 109, 339, 344, 339, 379, 378, 339, 
+	109, 339, 344, 339, 384, 134, 385, 386, 
+	115, 109, 339, 344, 339, 134, 385, 386, 
+	115, 109, 339, 344, 339, 385, 385, 115, 
+	109, 339, 344, 339, 387, 131, 388, 389, 
+	118, 109, 339, 344, 339, 131, 388, 389, 
+	118, 109, 339, 344, 339, 388, 388, 118, 
+	109, 339, 344, 339, 390, 128, 391, 392, 
+	121, 109, 339, 344, 339, 128, 391, 392, 
+	121, 109, 339, 344, 339, 391, 391, 121, 
+	109, 339, 344, 339, 393, 125, 379, 394, 
+	339, 109, 339, 344, 339, 125, 379, 394, 
+	339, 109, 339, 344, 339, 372, 373, 377, 
+	377, 112, 109, 339, 344, 339, 372, 373, 
+	374, 377, 112, 109, 339, 344, 339, 339, 
+	135, 339, 370, 339, 395, 339, 379, 379, 
+	339, 109, 339, 344, 339, 339, 339, 339, 
+	339, 370, 339, 370, 339, 339, 339, 379, 
+	379, 339, 109, 339, 344, 339, 339, 339, 
+	339, 339, 370, 339, 370, 339, 339, 339, 
+	379, 371, 339, 109, 339, 344, 339, 339, 
+	339, 339, 339, 370, 339, 364, 365, 369, 
+	369, 112, 109, 339, 344, 339, 364, 365, 
+	366, 369, 112, 109, 339, 344, 339, 339, 
+	137, 339, 362, 339, 396, 339, 379, 379, 
+	339, 109, 339, 344, 339, 339, 339, 339, 
+	339, 362, 339, 362, 339, 339, 339, 379, 
+	379, 339, 109, 339, 344, 339, 339, 339, 
+	339, 339, 362, 339, 362, 339, 339, 339, 
+	379, 363, 339, 109, 339, 344, 339, 339, 
+	339, 339, 339, 362, 339, 356, 357, 361, 
+	361, 112, 109, 339, 344, 339, 356, 357, 
+	358, 361, 112, 109, 339, 344, 339, 339, 
+	139, 339, 354, 339, 397, 339, 379, 379, 
+	339, 109, 339, 344, 339, 339, 339, 339, 
+	339, 354, 339, 354, 339, 339, 339, 379, 
+	379, 339, 109, 339, 344, 339, 339, 339, 
+	339, 339, 354, 339, 354, 339, 339, 339, 
+	379, 355, 339, 109, 339, 344, 339, 339, 
+	339, 339, 339, 354, 339, 348, 349, 353, 
+	353, 112, 109, 339, 344, 339, 348, 349, 
+	350, 353, 112, 109, 339, 344, 339, 339, 
+	141, 339, 346, 339, 398, 339, 379, 379, 
+	339, 109, 339, 344, 339, 339, 339, 339, 
+	339, 346, 339, 346, 339, 339, 339, 379, 
+	379, 339, 109, 339, 344, 339, 339, 339, 
+	339, 339, 346, 339, 346, 339, 339, 339, 
+	379, 347, 339, 109, 339, 344, 339, 339, 
+	339, 339, 339, 346, 339, 340, 341, 343, 
+	343, 112, 109, 339, 344, 339, 148, 149, 
+	150, 151, 399, 284, 76, 73, 282, 154, 
+	155, 155, 144, 282, 282, 148, 282, 161, 
+	400, 163, 164, 4, 1, 160, 165, 160, 
+	160, 35, 160, 168, 149, 150, 151, 401, 
+	402, 76, 403, 160, 404, 160, 155, 144, 
+	160, 160, 168, 160, 107, 405, 405, 76, 
+	403, 160, 165, 160, 160, 144, 160, 406, 
+	160, 160, 407, 160, 404, 160, 404, 160, 
+	408, 160, 205, 160, 406, 160, 160, 160, 
+	160, 404, 160, 168, 160, 220, 107, 405, 
+	405, 76, 403, 160, 165, 160, 160, 160, 
+	160, 160, 168, 160, 410, 409, 411, 411, 
+	409, 146, 409, 412, 409, 411, 411, 409, 
+	146, 409, 412, 409, 413, 409, 409, 414, 
+	409, 412, 409, 412, 409, 415, 409, 416, 
+	409, 413, 409, 409, 409, 409, 412, 409, 
+	148, 338, 338, 338, 338, 338, 338, 338, 
+	338, 338, 155, 338, 338, 338, 338, 148, 
+	338, 0
 };
 
 static const short _indic_syllable_machine_trans_targs[] = {
-	166, 188, 2, 194, 3, 5, 197, 6, 
-	8, 200, 9, 11, 203, 12, 14, 15, 
-	187, 17, 18, 202, 20, 21, 199, 23, 
-	24, 196, 205, 208, 212, 214, 218, 220, 
-	224, 226, 230, 232, 166, 255, 37, 261, 
-	38, 40, 264, 41, 43, 267, 44, 46, 
-	270, 47, 49, 50, 254, 52, 53, 269, 
-	55, 56, 266, 58, 59, 263, 272, 275, 
-	279, 281, 285, 287, 291, 293, 297, 300, 
-	166, 321, 72, 327, 166, 73, 75, 330, 
-	76, 78, 333, 79, 81, 336, 82, 84, 
-	85, 320, 87, 88, 335, 90, 91, 332, 
-	93, 94, 329, 338, 341, 345, 347, 351, 
-	353, 357, 359, 363, 166, 389, 106, 395, 
-	107, 109, 398, 110, 112, 401, 113, 115, 
-	404, 116, 118, 119, 388, 121, 122, 403, 
-	124, 125, 400, 127, 128, 397, 406, 409, 
-	413, 415, 419, 421, 425, 427, 431, 433, 
-	366, 142, 444, 144, 447, 438, 145, 147, 
-	450, 148, 150, 453, 151, 154, 155, 455, 
-	157, 158, 452, 160, 161, 449, 163, 164, 
-	446, 166, 458, 166, 167, 234, 301, 303, 
-	365, 367, 323, 368, 434, 435, 340, 456, 
-	463, 166, 168, 170, 34, 233, 190, 207, 
-	169, 33, 171, 228, 172, 174, 32, 227, 
-	173, 31, 175, 222, 176, 178, 30, 221, 
-	177, 29, 179, 216, 180, 182, 28, 215, 
-	181, 27, 183, 210, 184, 186, 26, 209, 
-	185, 25, 193, 0, 189, 192, 191, 166, 
-	1, 195, 4, 22, 198, 7, 19, 201, 
-	10, 16, 204, 13, 206, 211, 213, 217, 
-	219, 223, 225, 229, 231, 166, 235, 237, 
-	69, 299, 257, 274, 236, 68, 238, 295, 
-	239, 241, 67, 294, 240, 66, 242, 289, 
-	243, 245, 65, 288, 244, 64, 246, 283, 
-	247, 249, 63, 282, 248, 62, 250, 277, 
-	251, 253, 61, 276, 252, 60, 260, 35, 
-	256, 259, 258, 166, 36, 262, 39, 57, 
-	265, 42, 54, 268, 45, 51, 271, 48, 
-	273, 278, 280, 284, 286, 290, 292, 296, 
-	298, 166, 302, 103, 304, 361, 305, 307, 
-	102, 360, 306, 101, 308, 355, 309, 311, 
-	100, 354, 310, 99, 312, 349, 313, 315, 
-	98, 348, 314, 97, 316, 343, 317, 319, 
-	96, 342, 318, 95, 326, 70, 322, 325, 
-	324, 166, 71, 328, 74, 92, 331, 77, 
-	89, 334, 80, 86, 337, 83, 339, 344, 
-	346, 350, 352, 356, 358, 362, 364, 166, 
-	166, 369, 371, 138, 137, 391, 408, 370, 
-	372, 429, 373, 375, 136, 428, 374, 135, 
-	376, 423, 377, 379, 134, 422, 378, 133, 
-	380, 417, 381, 383, 132, 416, 382, 131, 
-	384, 411, 385, 387, 130, 410, 386, 129, 
-	394, 104, 390, 393, 392, 166, 105, 396, 
-	108, 126, 399, 111, 123, 402, 114, 120, 
-	405, 117, 407, 412, 414, 418, 420, 424, 
-	426, 430, 432, 139, 436, 437, 443, 440, 
-	140, 439, 442, 441, 141, 445, 143, 162, 
-	448, 146, 159, 451, 149, 156, 454, 152, 
-	153, 166, 457, 165, 460, 459, 462, 461, 
-	166
+	138, 160, 166, 2, 167, 3, 5, 170, 
+	6, 8, 173, 9, 11, 176, 12, 14, 
+	15, 159, 17, 18, 175, 20, 21, 172, 
+	23, 24, 169, 178, 182, 183, 187, 188, 
+	192, 193, 197, 198, 138, 221, 227, 36, 
+	228, 37, 39, 231, 40, 42, 234, 43, 
+	45, 237, 46, 48, 49, 220, 51, 52, 
+	236, 54, 55, 233, 57, 58, 230, 239, 
+	243, 244, 248, 249, 253, 254, 258, 260, 
+	138, 281, 287, 70, 288, 138, 71, 73, 
+	291, 74, 76, 294, 77, 79, 297, 80, 
+	82, 83, 280, 85, 86, 296, 88, 89, 
+	293, 91, 92, 290, 299, 303, 304, 308, 
+	309, 313, 314, 318, 138, 343, 349, 103, 
+	350, 104, 106, 353, 107, 109, 356, 110, 
+	112, 359, 113, 115, 116, 342, 118, 119, 
+	358, 121, 122, 355, 124, 125, 352, 361, 
+	365, 366, 370, 371, 375, 376, 380, 381, 
+	320, 138, 394, 138, 139, 200, 261, 263, 
+	319, 321, 283, 322, 382, 383, 392, 399, 
+	138, 140, 142, 33, 199, 162, 141, 32, 
+	143, 195, 144, 146, 31, 194, 145, 30, 
+	147, 190, 148, 150, 29, 189, 149, 28, 
+	151, 185, 152, 154, 27, 184, 153, 26, 
+	155, 180, 156, 158, 25, 179, 157, 1, 
+	165, 0, 161, 164, 163, 138, 168, 4, 
+	22, 171, 7, 19, 174, 10, 16, 177, 
+	13, 181, 186, 191, 196, 138, 201, 203, 
+	67, 259, 223, 202, 66, 204, 256, 205, 
+	207, 65, 255, 206, 64, 208, 251, 209, 
+	211, 63, 250, 210, 62, 212, 246, 213, 
+	215, 61, 245, 214, 60, 216, 241, 217, 
+	219, 59, 240, 218, 35, 226, 34, 222, 
+	225, 224, 138, 229, 38, 56, 232, 41, 
+	53, 235, 44, 50, 238, 47, 242, 247, 
+	252, 257, 138, 262, 100, 264, 316, 265, 
+	267, 99, 315, 266, 98, 268, 311, 269, 
+	271, 97, 310, 270, 96, 272, 306, 273, 
+	275, 95, 305, 274, 94, 276, 301, 277, 
+	279, 93, 300, 278, 69, 286, 68, 282, 
+	285, 284, 138, 289, 72, 90, 292, 75, 
+	87, 295, 78, 84, 298, 81, 302, 307, 
+	312, 317, 138, 138, 323, 325, 134, 133, 
+	345, 324, 326, 378, 327, 329, 132, 377, 
+	328, 131, 330, 373, 331, 333, 130, 372, 
+	332, 129, 334, 368, 335, 337, 128, 367, 
+	336, 127, 338, 363, 339, 341, 126, 362, 
+	340, 102, 348, 101, 344, 347, 346, 138, 
+	351, 105, 123, 354, 108, 120, 357, 111, 
+	117, 360, 114, 364, 369, 374, 379, 135, 
+	384, 385, 391, 386, 388, 136, 387, 390, 
+	389, 138, 393, 137, 396, 395, 398, 397, 
+	138
 };
 
 static const char _indic_syllable_machine_trans_actions[] = {
-	1, 0, 0, 2, 0, 0, 2, 0, 
-	0, 2, 0, 0, 2, 0, 0, 0, 
-	2, 0, 0, 2, 0, 0, 2, 0, 
-	0, 2, 2, 2, 2, 2, 2, 2, 
-	2, 2, 2, 2, 3, 0, 0, 2, 
-	0, 0, 2, 0, 0, 2, 0, 0, 
-	2, 0, 0, 0, 2, 0, 0, 2, 
-	0, 0, 2, 0, 0, 2, 2, 2, 
-	2, 2, 2, 2, 2, 2, 2, 2, 
-	4, 0, 0, 2, 5, 0, 0, 2, 
+	1, 0, 2, 0, 2, 0, 0, 2, 
 	0, 0, 2, 0, 0, 2, 0, 0, 
 	0, 2, 0, 0, 2, 0, 0, 2, 
-	0, 0, 2, 2, 6, 2, 6, 2, 
-	6, 2, 6, 2, 7, 0, 0, 2, 
-	0, 0, 2, 0, 0, 2, 0, 0, 
-	2, 0, 0, 0, 2, 0, 0, 2, 
-	0, 0, 2, 0, 0, 2, 2, 2, 
-	2, 2, 2, 2, 2, 2, 2, 2, 
-	6, 0, 2, 0, 2, 0, 0, 0, 
-	2, 0, 0, 2, 0, 0, 0, 2, 
-	0, 0, 2, 0, 0, 2, 0, 0, 
-	2, 8, 0, 11, 2, 2, 6, 0, 
-	12, 12, 0, 2, 6, 2, 6, 2, 
-	0, 13, 2, 0, 0, 2, 0, 2, 
-	2, 0, 2, 2, 2, 0, 0, 2, 
-	2, 0, 2, 2, 2, 0, 0, 2, 
-	2, 0, 2, 2, 2, 0, 0, 2, 
-	2, 0, 2, 2, 2, 0, 0, 2, 
-	2, 0, 2, 0, 0, 0, 0, 14, 
-	0, 2, 0, 0, 2, 0, 0, 2, 
-	0, 0, 2, 0, 2, 2, 2, 2, 
-	2, 2, 2, 2, 2, 15, 2, 0, 
-	0, 2, 0, 2, 2, 0, 2, 2, 
-	2, 0, 0, 2, 2, 0, 2, 2, 
-	2, 0, 0, 2, 2, 0, 2, 2, 
-	2, 0, 0, 2, 2, 0, 2, 2, 
-	2, 0, 0, 2, 2, 0, 2, 0, 
-	0, 0, 0, 16, 0, 2, 0, 0, 
+	0, 0, 2, 2, 2, 2, 2, 2, 
+	2, 2, 2, 2, 3, 0, 2, 0, 
 	2, 0, 0, 2, 0, 0, 2, 0, 
+	0, 2, 0, 0, 0, 2, 0, 0, 
+	2, 0, 0, 2, 0, 0, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 17, 6, 0, 6, 6, 6, 0, 
-	0, 6, 6, 0, 6, 6, 6, 0, 
-	0, 6, 6, 0, 6, 6, 6, 0, 
-	0, 6, 6, 0, 6, 6, 6, 0, 
-	0, 6, 6, 0, 6, 0, 0, 0, 
-	0, 18, 0, 2, 0, 0, 2, 0, 
+	4, 0, 2, 0, 2, 5, 0, 0, 
+	2, 0, 0, 2, 0, 0, 2, 0, 
+	0, 0, 2, 0, 0, 2, 0, 0, 
+	2, 0, 0, 2, 6, 2, 6, 2, 
+	6, 2, 6, 2, 7, 0, 2, 0, 
+	2, 0, 0, 2, 0, 0, 2, 0, 
+	0, 2, 0, 0, 0, 2, 0, 0, 
+	2, 0, 0, 2, 0, 0, 2, 2, 
+	2, 2, 2, 2, 2, 2, 2, 2, 
+	6, 8, 0, 11, 2, 2, 6, 0, 
+	12, 12, 0, 2, 6, 2, 2, 0, 
+	13, 2, 0, 0, 2, 0, 2, 0, 
+	2, 2, 2, 0, 0, 2, 2, 0, 
+	2, 2, 2, 0, 0, 2, 2, 0, 
+	2, 2, 2, 0, 0, 2, 2, 0, 
+	2, 2, 2, 0, 0, 2, 2, 0, 
+	2, 0, 0, 0, 0, 14, 2, 0, 
+	0, 2, 0, 0, 2, 0, 0, 2, 
+	0, 2, 2, 2, 2, 15, 2, 0, 
+	0, 2, 0, 2, 0, 2, 2, 2, 
+	0, 0, 2, 2, 0, 2, 2, 2, 
+	0, 0, 2, 2, 0, 2, 2, 2, 
+	0, 0, 2, 2, 0, 2, 2, 2, 
+	0, 0, 2, 2, 0, 2, 0, 0, 
+	0, 0, 16, 2, 0, 0, 2, 0, 
 	0, 2, 0, 0, 2, 0, 2, 2, 
-	2, 2, 2, 2, 2, 2, 2, 19, 
-	20, 2, 0, 0, 0, 0, 2, 2, 
-	2, 2, 2, 0, 0, 2, 2, 0, 
-	2, 2, 2, 0, 0, 2, 2, 0, 
-	2, 2, 2, 0, 0, 2, 2, 0, 
-	2, 2, 2, 0, 0, 2, 2, 0, 
-	2, 0, 0, 0, 0, 21, 0, 2, 
-	0, 0, 2, 0, 0, 2, 0, 0, 
-	2, 0, 2, 2, 2, 2, 2, 2, 
-	2, 2, 2, 0, 0, 22, 2, 0, 
-	0, 0, 0, 0, 0, 2, 0, 0, 
+	2, 2, 17, 6, 0, 6, 2, 6, 
+	0, 0, 6, 6, 0, 6, 2, 6, 
+	0, 0, 6, 6, 0, 6, 2, 6, 
+	0, 0, 6, 6, 0, 6, 2, 6, 
+	0, 0, 6, 6, 0, 2, 0, 0, 
+	0, 0, 18, 2, 0, 0, 2, 0, 
+	0, 2, 0, 0, 2, 0, 2, 2, 
+	2, 2, 19, 20, 2, 0, 0, 0, 
+	0, 2, 2, 2, 2, 0, 0, 2, 
+	2, 0, 2, 2, 2, 0, 0, 2, 
+	2, 0, 2, 2, 2, 0, 0, 2, 
+	2, 0, 2, 2, 2, 0, 0, 2, 
+	2, 0, 2, 0, 0, 0, 0, 21, 
 	2, 0, 0, 2, 0, 0, 2, 0, 
+	0, 2, 0, 2, 2, 2, 2, 0, 
+	0, 22, 22, 0, 0, 0, 0, 0, 
 	0, 23, 2, 0, 0, 0, 0, 0, 
 	24
 };
@@ -937,15 +759,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, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 9, 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, 9, 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, 
@@ -998,15 +812,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, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 10, 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, 10, 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, 
@@ -1046,67 +852,59 @@
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 37, 37, 37, 37, 37, 
+	1, 1, 37, 37, 37, 37, 37, 37, 
 	37, 37, 37, 37, 37, 37, 37, 37, 
 	37, 37, 37, 37, 37, 37, 37, 37, 
 	37, 37, 37, 37, 37, 37, 37, 37, 
-	37, 37, 37, 37, 37, 37, 73, 73, 
-	77, 77, 73, 73, 73, 73, 73, 73, 
+	37, 37, 37, 37, 73, 73, 78, 78, 
 	73, 73, 73, 73, 73, 73, 73, 73, 
 	73, 73, 73, 73, 73, 73, 73, 73, 
 	73, 73, 73, 73, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 109, 109, 109, 
 	109, 109, 109, 109, 109, 109, 109, 109, 
 	109, 109, 109, 109, 109, 109, 109, 109, 
 	109, 109, 109, 109, 109, 109, 109, 109, 
-	109, 109, 109, 109, 109, 109, 109, 109, 
-	109, 109, 109, 73, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 170, 0, 186, 
-	186, 186, 186, 186, 186, 186, 186, 186, 
-	186, 186, 186, 186, 186, 186, 186, 186, 
-	186, 186, 186, 186, 186, 186, 186, 186, 
-	186, 186, 186, 186, 186, 186, 186, 186, 
-	186, 186, 186, 186, 186, 186, 186, 186, 
-	186, 186, 186, 186, 186, 186, 186, 186, 
-	186, 186, 186, 186, 186, 186, 186, 186, 
-	186, 186, 186, 186, 186, 186, 186, 186, 
-	186, 186, 254, 254, 254, 254, 254, 254, 
-	254, 254, 254, 254, 254, 254, 254, 254, 
-	254, 254, 254, 254, 254, 254, 254, 254, 
-	254, 254, 254, 254, 254, 254, 254, 254, 
-	254, 254, 254, 254, 254, 254, 254, 254, 
-	254, 254, 254, 254, 254, 254, 254, 254, 
-	254, 254, 254, 254, 254, 254, 254, 254, 
-	254, 254, 254, 254, 254, 254, 254, 254, 
-	254, 254, 254, 254, 254, 322, 322, 322, 
-	322, 322, 322, 322, 322, 322, 322, 322, 
-	322, 322, 322, 322, 322, 322, 322, 322, 
-	322, 322, 322, 322, 322, 322, 322, 322, 
-	322, 322, 322, 322, 322, 322, 322, 322, 
-	322, 322, 322, 322, 322, 322, 322, 322, 
-	322, 322, 322, 322, 322, 322, 322, 322, 
-	322, 322, 322, 322, 322, 322, 322, 322, 
-	322, 322, 322, 322, 322, 384, 322, 384, 
-	385, 385, 385, 385, 385, 385, 385, 385, 
-	385, 385, 385, 385, 385, 385, 385, 385, 
-	385, 385, 385, 385, 385, 385, 385, 385, 
-	385, 385, 385, 385, 385, 385, 385, 385, 
-	385, 385, 385, 385, 385, 385, 385, 385, 
-	385, 385, 385, 385, 385, 385, 385, 385, 
-	385, 385, 385, 385, 385, 385, 385, 385, 
-	385, 385, 385, 385, 385, 385, 385, 385, 
-	385, 385, 322, 186, 186, 186, 186, 186, 
-	186, 186, 186, 186, 186, 186, 186, 186, 
-	186, 186, 186, 186, 186, 186, 186, 186, 
-	474, 474, 474, 474, 474, 474, 474, 384
+	109, 109, 109, 109, 109, 109, 109, 73, 
+	1, 146, 0, 161, 161, 161, 161, 161, 
+	161, 161, 161, 161, 161, 161, 161, 161, 
+	161, 161, 161, 161, 161, 161, 161, 161, 
+	161, 161, 161, 161, 161, 161, 161, 161, 
+	161, 161, 161, 161, 161, 161, 161, 161, 
+	161, 161, 161, 161, 161, 161, 161, 161, 
+	161, 161, 161, 161, 161, 161, 161, 161, 
+	161, 161, 161, 161, 161, 161, 161, 161, 
+	222, 222, 222, 222, 222, 222, 222, 222, 
+	222, 222, 222, 222, 222, 222, 222, 222, 
+	222, 222, 222, 222, 222, 222, 222, 222, 
+	222, 222, 222, 222, 222, 222, 222, 222, 
+	222, 222, 222, 222, 222, 222, 222, 222, 
+	222, 222, 222, 222, 222, 222, 222, 222, 
+	222, 222, 222, 222, 222, 222, 222, 222, 
+	222, 222, 222, 222, 222, 283, 283, 283, 
+	283, 283, 283, 283, 283, 283, 283, 283, 
+	283, 283, 283, 283, 283, 283, 283, 283, 
+	283, 283, 283, 283, 283, 283, 283, 283, 
+	283, 283, 283, 283, 283, 283, 283, 283, 
+	283, 283, 283, 283, 283, 283, 283, 283, 
+	283, 283, 283, 283, 283, 283, 283, 283, 
+	283, 283, 283, 283, 283, 283, 283, 339, 
+	283, 339, 340, 340, 340, 340, 340, 340, 
+	340, 340, 340, 340, 340, 340, 340, 340, 
+	340, 340, 340, 340, 340, 340, 340, 340, 
+	340, 340, 340, 340, 340, 340, 340, 340, 
+	340, 340, 340, 340, 340, 340, 340, 340, 
+	340, 340, 340, 340, 340, 340, 340, 340, 
+	340, 340, 340, 340, 340, 340, 340, 340, 
+	340, 340, 340, 340, 340, 340, 283, 161, 
+	161, 161, 161, 161, 161, 161, 161, 161, 
+	410, 410, 410, 410, 410, 410, 410, 339
 };
 
-static const int indic_syllable_machine_start = 166;
-static const int indic_syllable_machine_first_final = 166;
+static const int indic_syllable_machine_start = 138;
+static const int indic_syllable_machine_first_final = 138;
 static const int indic_syllable_machine_error = -1;
 
-static const int indic_syllable_machine_en_main = 166;
+static const int indic_syllable_machine_en_main = 138;
 
 
 #line 36 "hb-ot-shape-complex-indic-machine.rl"
@@ -1118,10 +916,9 @@
 
 #define found_syllable(syllable_type) \
   HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+    for (unsigned int i = ts; i < te; i++) \
       info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
@@ -1129,11 +926,11 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts HB_UNUSED, te, act;
+  unsigned int p, pe, eof, ts, te, act;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 1137 "hb-ot-shape-complex-indic-machine.hh"
+#line 934 "hb-ot-shape-complex-indic-machine.hh"
 	{
 	cs = indic_syllable_machine_start;
 	ts = 0;
@@ -1141,16 +938,15 @@
 	act = 0;
 	}
 
-#line 113 "hb-ot-shape-complex-indic-machine.rl"
+#line 112 "hb-ot-shape-complex-indic-machine.rl"
 
 
   p = 0;
   pe = eof = buffer->len;
 
-  unsigned int last = 0;
   unsigned int syllable_serial = 1;
   
-#line 1154 "hb-ot-shape-complex-indic-machine.hh"
+#line 950 "hb-ot-shape-complex-indic-machine.hh"
 	{
 	int _slen;
 	int _trans;
@@ -1164,7 +960,7 @@
 #line 1 "NONE"
 	{ts = p;}
 	break;
-#line 1168 "hb-ot-shape-complex-indic-machine.hh"
+#line 964 "hb-ot-shape-complex-indic-machine.hh"
 	}
 
 	_keys = _indic_syllable_machine_trans_keys + (cs<<1);
@@ -1287,7 +1083,7 @@
 #line 88 "hb-ot-shape-complex-indic-machine.rl"
 	{act = 6;}
 	break;
-#line 1291 "hb-ot-shape-complex-indic-machine.hh"
+#line 1087 "hb-ot-shape-complex-indic-machine.hh"
 	}
 
 _again:
@@ -1296,7 +1092,7 @@
 #line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 1300 "hb-ot-shape-complex-indic-machine.hh"
+#line 1096 "hb-ot-shape-complex-indic-machine.hh"
 	}
 
 	if ( ++p != pe )
@@ -1312,7 +1108,7 @@
 
 	}
 
-#line 122 "hb-ot-shape-complex-indic-machine.rl"
+#line 120 "hb-ot-shape-complex-indic-machine.rl"
 
 }
 
diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index 35e7ce9..c5d945d 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -27,7 +27,7 @@
 #ifndef HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 %%{
   machine indic_syllable_machine;
@@ -52,7 +52,6 @@
 RS    = 13;
 Repha = 15;
 Ra    = 16;
-CM    = 17;
 Symbol= 18;
 CS    = 19;
 
@@ -68,15 +67,16 @@
 syllable_tail = (z?.SM.SM?.ZWNJ?)? A{0,3}?;
 halant_group = (z?.H.(ZWJ.N?)?);
 final_halant_group = halant_group | H.ZWNJ;
-medial_group = CM?;
-halant_or_matra_group = (final_halant_group | (H.ZWJ)? matra_group{0,4});
+halant_or_matra_group = (final_halant_group | matra_group{0,4});
+
+complex_syllable_tail = (halant_group.cn){0,4} halant_or_matra_group syllable_tail;
 
 
-consonant_syllable =	(Repha|CS)? (cn.halant_group){0,4} cn medial_group halant_or_matra_group syllable_tail;
-vowel_syllable =	reph? V.n? (ZWJ | (halant_group.cn){0,4} medial_group halant_or_matra_group syllable_tail);
-standalone_cluster =	((Repha|CS)? PLACEHOLDER | reph? DOTTEDCIRCLE).n? (halant_group.cn){0,4} medial_group halant_or_matra_group syllable_tail;
+consonant_syllable =	(Repha|CS)? cn complex_syllable_tail;
+vowel_syllable =	reph? V.n? (ZWJ | complex_syllable_tail);
+standalone_cluster =	((Repha|CS)? PLACEHOLDER | reph? DOTTEDCIRCLE).n? complex_syllable_tail;
 symbol_cluster = 	symbol syllable_tail;
-broken_cluster =	reph? n? (halant_group.cn){0,4} medial_group halant_or_matra_group syllable_tail;
+broken_cluster =	reph? n? complex_syllable_tail;
 other =			any;
 
 main := |*
@@ -93,10 +93,9 @@
 
 #define found_syllable(syllable_type) \
   HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+    for (unsigned int i = ts; i < te; i++) \
       info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
@@ -104,7 +103,7 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts HB_UNUSED, te, act;
+  unsigned int p, pe, eof, ts, te, act;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   %%{
@@ -115,7 +114,6 @@
   p = 0;
   pe = eof = buffer->len;
 
-  unsigned int last = 0;
   unsigned int syllable_serial = 1;
   %%{
     write exec;
diff --git a/src/hb-ot-shape-complex-indic-table.cc b/src/hb-ot-shape-complex-indic-table.cc
index 54291bc..b7e4e7c 100644
--- a/src/hb-ot-shape-complex-indic-table.cc
+++ b/src/hb-ot-shape-complex-indic-table.cc
@@ -14,7 +14,7 @@
  * # Date: 2017-10-16, 24:39:00 GMT [KW]
  */
 
-#include "hb-ot-shape-complex-indic-private.hh"
+#include "hb-ot-shape-complex-indic.hh"
 
 
 #define ISC_A	INDIC_SYLLABIC_CATEGORY_AVAGRAHA		/*  16 chars; Avagraha */
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index b52656f..f1ae303 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -24,8 +24,8 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-indic-private.hh"
-#include "hb-ot-layout-private.hh"
+#include "hb-ot-shape-complex-indic.hh"
+#include "hb-ot-layout.hh"
 
 
 /*
@@ -95,42 +95,40 @@
  * Indic shaper.
  */
 
-struct feature_list_t {
-  hb_tag_t tag;
-  hb_ot_map_feature_flags_t flags;
-};
-
-static const feature_list_t
+static const hb_ot_map_feature_t
 indic_features[] =
 {
   /*
    * Basic features.
    * These features are applied in order, one at a time, after initial_reordering.
    */
-  {HB_TAG('n','u','k','t'), F_GLOBAL},
-  {HB_TAG('a','k','h','n'), F_GLOBAL},
-  {HB_TAG('r','p','h','f'), F_NONE},
-  {HB_TAG('r','k','r','f'), F_GLOBAL},
-  {HB_TAG('p','r','e','f'), F_NONE},
-  {HB_TAG('b','l','w','f'), F_NONE},
-  {HB_TAG('a','b','v','f'), F_NONE},
-  {HB_TAG('h','a','l','f'), F_NONE},
-  {HB_TAG('p','s','t','f'), F_NONE},
-  {HB_TAG('v','a','t','u'), F_GLOBAL},
-  {HB_TAG('c','j','c','t'), F_GLOBAL},
+  {HB_TAG('n','u','k','t'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('a','k','h','n'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('r','p','h','f'),        F_MANUAL_JOINERS},
+  {HB_TAG('r','k','r','f'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('p','r','e','f'),        F_MANUAL_JOINERS},
+  {HB_TAG('b','l','w','f'),        F_MANUAL_JOINERS},
+  {HB_TAG('a','b','v','f'),        F_MANUAL_JOINERS},
+  {HB_TAG('h','a','l','f'),        F_MANUAL_JOINERS},
+  {HB_TAG('p','s','t','f'),        F_MANUAL_JOINERS},
+  {HB_TAG('v','a','t','u'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('c','j','c','t'), F_GLOBAL_MANUAL_JOINERS},
   /*
    * Other features.
    * These features are applied all at once, after final_reordering.
    * Default Bengali font in Windows for example has intermixed
    * lookups for init,pres,abvs,blws features.
    */
-  {HB_TAG('i','n','i','t'), F_NONE},
-  {HB_TAG('p','r','e','s'), F_GLOBAL},
-  {HB_TAG('a','b','v','s'), F_GLOBAL},
-  {HB_TAG('b','l','w','s'), F_GLOBAL},
-  {HB_TAG('p','s','t','s'), F_GLOBAL},
-  {HB_TAG('h','a','l','n'), F_GLOBAL},
-  /* Positioning features, though we don't care about the types. */
+  {HB_TAG('i','n','i','t'),        F_MANUAL_JOINERS},
+  {HB_TAG('p','r','e','s'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('a','b','v','s'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('h','a','l','n'), F_GLOBAL_MANUAL_JOINERS},
+  /*
+   * Positioning features.
+   * We don't care about the types.
+   */
   {HB_TAG('d','i','s','t'), F_GLOBAL},
   {HB_TAG('a','b','v','m'), F_GLOBAL},
   {HB_TAG('b','l','w','m'), F_GLOBAL},
@@ -158,12 +156,13 @@
   _BLWS,
   _PSTS,
   _HALN,
+
   _DIST,
   _ABVM,
   _BLWM,
 
   INDIC_NUM_FEATURES,
-  INDIC_BASIC_FEATURES = INIT /* Don't forget to update this! */
+  INDIC_BASIC_FEATURES = INIT, /* Don't forget to update this! */
 };
 
 static void
@@ -191,25 +190,27 @@
   /* Do this before any lookups have been applied. */
   map->add_gsub_pause (setup_syllables);
 
-  map->add_global_bool_feature (HB_TAG('l','o','c','l'));
+  map->enable_feature (HB_TAG('l','o','c','l'));
   /* The Indic specs do not require ccmp, but we apply it here since if
    * there is a use of it, it's typically at the beginning. */
-  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
+  map->enable_feature (HB_TAG('c','c','m','p'));
 
 
   unsigned int i = 0;
   map->add_gsub_pause (initial_reordering);
+
   for (; i < INDIC_BASIC_FEATURES; i++) {
-    map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
+    map->add_feature (indic_features[i]);
     map->add_gsub_pause (nullptr);
   }
-  map->add_gsub_pause (final_reordering);
-  for (; i < INDIC_NUM_FEATURES; i++) {
-    map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
-  }
 
-  map->add_global_bool_feature (HB_TAG('c','a','l','t'));
-  map->add_global_bool_feature (HB_TAG('c','l','i','g'));
+  map->add_gsub_pause (final_reordering);
+
+  for (; i < INDIC_NUM_FEATURES; i++)
+    map->add_feature (indic_features[i]);
+
+  map->enable_feature (HB_TAG('c','a','l','t'));
+  map->enable_feature (HB_TAG('c','l','i','g'));
 
   map->add_gsub_pause (clear_syllables);
 }
@@ -217,7 +218,7 @@
 static void
 override_features_indic (hb_ot_shape_planner_t *plan)
 {
-  plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
+  plan->map.disable_feature (HB_TAG('l','i','g','a'));
 }
 
 
@@ -273,6 +274,7 @@
   const indic_config_t *config;
 
   bool is_old_spec;
+  bool uniscribe_bug_compatible;
   mutable hb_atomic_int_t virama_glyph;
 
   would_substitute_feature_t rphf;
@@ -298,6 +300,7 @@
     }
 
   indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2');
+  indic_plan->uniscribe_bug_compatible = hb_options ().uniscribe_bug_compatible;
   indic_plan->virama_glyph.set_relaxed (-1);
 
   /* Use zero-context would_substitute() matching for new-spec of the main
@@ -328,6 +331,275 @@
   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,
@@ -717,7 +989,7 @@
     indic_position_t last_pos = POS_START;
     for (unsigned int i = start; i < end; i++)
     {
-      if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | FLAG (OT_H))))
+      if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | FLAG (OT_H))))
       {
 	info[i].indic_position() = last_pos;
 	if (unlikely (info[i].indic_category() == OT_H &&
@@ -913,10 +1185,12 @@
 				       hb_buffer_t *buffer,
 				       unsigned int start, unsigned int end)
 {
+  const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
+
   /* We treat placeholder/dotted-circle as if they are consonants, so we
    * should just chain.  Only if not in compatibility mode that is... */
 
-  if (hb_options ().uniscribe_bug_compatible)
+  if (indic_plan->uniscribe_bug_compatible)
   {
     /* For dotted-circle, this is what Uniscribe does:
      * If dotted-circle is the last glyph, it just does nothing.
@@ -958,7 +1232,8 @@
 		       hb_font_t *font,
 		       hb_buffer_t *buffer)
 {
-  /* Note: This loop is extra overhead, but should not be measurable. */
+  /* Note: This loop is extra overhead, but should not be measurable.
+   * TODO Use a buffer scratch flag to remove the loop. */
   bool has_broken_syllables = false;
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
@@ -1356,7 +1631,7 @@
        * Uniscribe doesn't do this.
        * TEST: U+0930,U+094D,U+0915,U+094B,U+094D
        */
-      if (!hb_options ().uniscribe_bug_compatible &&
+      if (!indic_plan->uniscribe_bug_compatible &&
 	  unlikely (is_halant (info[new_reph_pos]))) {
 	for (unsigned int i = base + 1; i < new_reph_pos; i++)
 	  if (info[i].indic_category() == OT_M) {
@@ -1462,7 +1737,7 @@
   /*
    * Finish off the clusters and go home!
    */
-  if (hb_options ().uniscribe_bug_compatible)
+  if (indic_plan->uniscribe_bug_compatible)
   {
     switch ((hb_tag_t) plan->props.script)
     {
@@ -1609,13 +1884,13 @@
   override_features_indic,
   data_create_indic,
   data_destroy_indic,
-  nullptr, /* preprocess_text */
+  preprocess_text_indic,
   nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   decompose_indic,
   compose_indic,
   setup_masks_indic,
-  nullptr, /* disable_otl */
+  HB_TAG_NONE, /* gpos_tag */
   nullptr, /* reorder_marks */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   false, /* fallback_position */
diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic.hh
similarity index 96%
rename from src/hb-ot-shape-complex-indic-private.hh
rename to src/hb-ot-shape-complex-indic.hh
index bb7fff3..dcc2a7a 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic.hh
@@ -24,14 +24,12 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH
-#define HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH
+#ifndef HB_OT_SHAPE_COMPLEX_INDIC_HH
+#define HB_OT_SHAPE_COMPLEX_INDIC_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-
-#include "hb-ot-shape-complex-private.hh"
-#include "hb-ot-shape-private.hh" /* XXX Remove */
+#include "hb-ot-shape-complex.hh"
 
 
 /* buffer var allocations */
@@ -64,19 +62,17 @@
   OT_Coeng = 14, /* Khmer-style Virama. */
   OT_Repha = 15, /* Atomically-encoded logical or visual repha. */
   OT_Ra = 16,
-  OT_CM = 17,  /* Consonant-Medial. */
+  OT_CM = 17,  /* Consonant-Medial; Unused by Indic shaper. */
   OT_Symbol = 18, /* Avagraha, etc that take marks (SM,A,VD). */
   OT_CS = 19
 };
 
-#define MEDIAL_FLAGS (FLAG (OT_CM))
-
 /* Note:
  *
  * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
  * cannot happen in a consonant syllable.  The plus side however is, we can call the
  * consonant syllable logic from the vowel syllable function and get it all right! */
-#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CS) | FLAG (OT_Ra) | MEDIAL_FLAGS | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE))
+#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CS) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE))
 #define JOINER_FLAGS (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ))
 
 
@@ -125,7 +121,7 @@
   INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA	= OT_Repha,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED		= OT_X, /* Don't care. */
   INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED		= OT_CM,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA	= OT_N,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA	= OT_CM,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER	= OT_CS,
   INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK		= OT_SM, /* https://github.com/harfbuzz/harfbuzz/issues/552 */
   INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER		= OT_Coeng,
@@ -400,4 +396,4 @@
 }
 
 
-#endif /* HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH */
+#endif /* HB_OT_SHAPE_COMPLEX_INDIC_HH */
diff --git a/src/hb-ot-shape-complex-khmer-machine.hh b/src/hb-ot-shape-complex-khmer-machine.hh
index d001021..2bc8ca6 100644
--- a/src/hb-ot-shape-complex-khmer-machine.hh
+++ b/src/hb-ot-shape-complex-khmer-machine.hh
@@ -29,143 +29,212 @@
 #ifndef HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 #line 36 "hb-ot-shape-complex-khmer-machine.hh"
 static const unsigned char _khmer_syllable_machine_trans_keys[] = {
-	7u, 7u, 1u, 16u, 13u, 13u, 1u, 16u, 7u, 13u, 7u, 7u, 1u, 16u, 13u, 13u, 
-	1u, 16u, 7u, 13u, 1u, 16u, 3u, 14u, 3u, 14u, 5u, 14u, 3u, 14u, 5u, 14u, 
-	8u, 8u, 3u, 13u, 3u, 8u, 8u, 8u, 3u, 8u, 3u, 14u, 3u, 14u, 5u, 14u, 
-	3u, 14u, 5u, 14u, 8u, 8u, 3u, 13u, 3u, 8u, 8u, 8u, 3u, 8u, 3u, 14u, 
-	3u, 14u, 7u, 13u, 7u, 7u, 1u, 16u, 0
+	5u, 26u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, 5u, 21u, 
+	5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 
+	5u, 26u, 5u, 21u, 5u, 26u, 5u, 21u, 5u, 26u, 1u, 16u, 1u, 29u, 5u, 29u, 
+	5u, 29u, 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 5u, 29u, 5u, 26u, 
+	5u, 29u, 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 29u, 
+	5u, 29u, 0
 };
 
 static const char _khmer_syllable_machine_key_spans[] = {
-	1, 16, 1, 16, 7, 1, 16, 1, 
-	16, 7, 16, 12, 12, 10, 12, 10, 
-	1, 11, 6, 1, 6, 12, 12, 10, 
-	12, 10, 1, 11, 6, 1, 6, 12, 
-	12, 7, 1, 16
+	22, 17, 22, 17, 16, 17, 22, 17, 
+	22, 17, 16, 17, 22, 17, 16, 17, 
+	22, 17, 22, 17, 22, 16, 29, 25, 
+	25, 25, 1, 18, 25, 25, 25, 22, 
+	25, 25, 1, 18, 25, 25, 16, 25, 
+	25
 };
 
 static const short _khmer_syllable_machine_index_offsets[] = {
-	0, 2, 19, 21, 38, 46, 48, 65, 
-	67, 84, 92, 109, 122, 135, 146, 159, 
-	170, 172, 184, 191, 193, 200, 213, 226, 
-	237, 250, 261, 263, 275, 282, 284, 291, 
-	304, 317, 325, 327
+	0, 23, 41, 64, 82, 99, 117, 140, 
+	158, 181, 199, 216, 234, 257, 275, 292, 
+	310, 333, 351, 374, 392, 415, 432, 462, 
+	488, 514, 540, 542, 561, 587, 613, 639, 
+	662, 688, 714, 716, 735, 761, 787, 804, 
+	830
 };
 
 static const char _khmer_syllable_machine_indicies[] = {
-	1, 0, 2, 2, 0, 0, 0, 0, 
+	1, 1, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 2, 
+	3, 0, 0, 0, 0, 4, 0, 1, 
+	1, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 3, 
+	0, 1, 1, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 2, 0, 3, 0, 4, 4, 0, 
+	0, 3, 0, 0, 0, 0, 4, 0, 
+	5, 5, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 4, 0, 1, 0, 
-	0, 0, 0, 0, 5, 0, 7, 6, 
-	8, 8, 6, 6, 6, 6, 6, 6, 
-	6, 6, 6, 6, 6, 6, 6, 8, 
-	6, 9, 6, 10, 10, 6, 6, 6, 
-	6, 6, 6, 6, 6, 6, 6, 6, 
-	6, 6, 10, 6, 7, 6, 6, 6, 
-	6, 6, 11, 6, 4, 4, 13, 12, 
-	14, 15, 7, 16, 12, 12, 4, 4, 
-	11, 17, 12, 4, 12, 19, 18, 20, 
-	21, 1, 22, 18, 18, 18, 18, 5, 
-	23, 18, 24, 18, 21, 21, 1, 22, 
-	18, 18, 18, 18, 18, 23, 18, 21, 
-	21, 1, 22, 18, 18, 18, 18, 18, 
-	23, 18, 25, 18, 21, 21, 1, 22, 
-	18, 18, 18, 18, 18, 26, 18, 21, 
-	21, 1, 22, 18, 18, 18, 18, 18, 
-	26, 18, 27, 18, 28, 18, 29, 18, 
-	18, 22, 18, 18, 18, 18, 3, 18, 
-	30, 18, 18, 18, 18, 22, 18, 22, 
-	18, 28, 18, 18, 18, 18, 22, 18, 
-	19, 18, 21, 21, 1, 22, 18, 18, 
-	18, 18, 18, 23, 18, 32, 31, 33, 
-	33, 7, 16, 31, 31, 31, 31, 31, 
-	34, 31, 33, 33, 7, 16, 31, 31, 
-	31, 31, 31, 34, 31, 35, 31, 33, 
-	33, 7, 16, 31, 31, 31, 31, 31, 
-	36, 31, 33, 33, 7, 16, 31, 31, 
-	31, 31, 31, 36, 31, 37, 31, 38, 
-	31, 39, 31, 31, 16, 31, 31, 31, 
-	31, 9, 31, 40, 31, 31, 31, 31, 
-	16, 31, 16, 31, 38, 31, 31, 31, 
-	31, 16, 31, 13, 31, 41, 33, 7, 
-	16, 31, 31, 31, 31, 11, 34, 31, 
-	13, 31, 33, 33, 7, 16, 31, 31, 
-	31, 31, 31, 34, 31, 7, 42, 42, 
-	42, 42, 42, 11, 42, 7, 42, 10, 
-	10, 42, 42, 42, 42, 42, 42, 42, 
-	42, 42, 42, 42, 42, 42, 10, 42, 
+	4, 0, 6, 6, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 6, 0, 7, 7, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 8, 0, 9, 9, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 10, 0, 0, 
+	0, 0, 4, 0, 9, 9, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 10, 0, 11, 11, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 12, 0, 
+	0, 0, 0, 4, 0, 11, 11, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 12, 0, 13, 
+	13, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 13, 0, 
+	15, 15, 14, 14, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	16, 14, 15, 15, 17, 17, 17, 17, 
+	17, 17, 17, 17, 17, 17, 17, 17, 
+	17, 17, 16, 17, 17, 17, 17, 18, 
+	17, 19, 19, 17, 17, 17, 17, 17, 
+	17, 17, 17, 17, 17, 17, 17, 17, 
+	17, 18, 17, 20, 20, 17, 17, 17, 
+	17, 17, 17, 17, 17, 17, 17, 17, 
+	17, 17, 20, 17, 21, 21, 17, 17, 
+	17, 17, 17, 17, 17, 17, 17, 17, 
+	17, 17, 17, 17, 22, 17, 23, 23, 
+	17, 17, 17, 17, 17, 17, 17, 17, 
+	17, 17, 17, 17, 17, 17, 24, 17, 
+	17, 17, 17, 18, 17, 23, 23, 17, 
+	17, 17, 17, 17, 17, 17, 17, 17, 
+	17, 17, 17, 17, 17, 24, 17, 25, 
+	25, 17, 17, 17, 17, 17, 17, 17, 
+	17, 17, 17, 17, 17, 17, 17, 26, 
+	17, 17, 17, 17, 18, 17, 25, 25, 
+	17, 17, 17, 17, 17, 17, 17, 17, 
+	17, 17, 17, 17, 17, 17, 26, 17, 
+	15, 15, 17, 17, 17, 17, 17, 17, 
+	17, 17, 17, 17, 17, 17, 17, 27, 
+	16, 17, 17, 17, 17, 18, 17, 28, 
+	28, 17, 17, 17, 17, 17, 17, 17, 
+	17, 17, 17, 17, 17, 17, 28, 17, 
+	13, 13, 29, 29, 30, 30, 29, 29, 
+	29, 29, 2, 2, 29, 31, 29, 13, 
+	29, 29, 29, 29, 16, 20, 29, 29, 
+	29, 18, 24, 26, 22, 29, 33, 33, 
+	32, 32, 32, 32, 32, 32, 32, 34, 
+	32, 32, 32, 32, 32, 2, 3, 6, 
+	32, 32, 32, 4, 10, 12, 8, 32, 
+	35, 35, 32, 32, 32, 32, 32, 32, 
+	32, 36, 32, 32, 32, 32, 32, 32, 
+	3, 6, 32, 32, 32, 4, 10, 12, 
+	8, 32, 5, 5, 32, 32, 32, 32, 
+	32, 32, 32, 36, 32, 32, 32, 32, 
+	32, 32, 4, 6, 32, 32, 32, 32, 
+	32, 32, 8, 32, 6, 32, 7, 7, 
+	32, 32, 32, 32, 32, 32, 32, 36, 
+	32, 32, 32, 32, 32, 32, 8, 6, 
+	32, 37, 37, 32, 32, 32, 32, 32, 
+	32, 32, 36, 32, 32, 32, 32, 32, 
+	32, 10, 6, 32, 32, 32, 4, 32, 
+	32, 8, 32, 38, 38, 32, 32, 32, 
+	32, 32, 32, 32, 36, 32, 32, 32, 
+	32, 32, 32, 12, 6, 32, 32, 32, 
+	4, 10, 32, 8, 32, 35, 35, 32, 
+	32, 32, 32, 32, 32, 32, 34, 32, 
+	32, 32, 32, 32, 32, 3, 6, 32, 
+	32, 32, 4, 10, 12, 8, 32, 15, 
+	15, 39, 39, 39, 39, 39, 39, 39, 
+	39, 39, 39, 39, 39, 39, 39, 16, 
+	39, 39, 39, 39, 18, 39, 41, 41, 
+	40, 40, 40, 40, 40, 40, 40, 42, 
+	40, 40, 40, 40, 40, 40, 16, 20, 
+	40, 40, 40, 18, 24, 26, 22, 40, 
+	19, 19, 40, 40, 40, 40, 40, 40, 
+	40, 42, 40, 40, 40, 40, 40, 40, 
+	18, 20, 40, 40, 40, 40, 40, 40, 
+	22, 40, 20, 40, 21, 21, 40, 40, 
+	40, 40, 40, 40, 40, 42, 40, 40, 
+	40, 40, 40, 40, 22, 20, 40, 43, 
+	43, 40, 40, 40, 40, 40, 40, 40, 
+	42, 40, 40, 40, 40, 40, 40, 24, 
+	20, 40, 40, 40, 18, 40, 40, 22, 
+	40, 44, 44, 40, 40, 40, 40, 40, 
+	40, 40, 42, 40, 40, 40, 40, 40, 
+	40, 26, 20, 40, 40, 40, 18, 24, 
+	40, 22, 40, 28, 28, 39, 39, 39, 
+	39, 39, 39, 39, 39, 39, 39, 39, 
+	39, 39, 28, 39, 45, 45, 40, 40, 
+	40, 40, 40, 40, 40, 46, 40, 40, 
+	40, 40, 40, 27, 16, 20, 40, 40, 
+	40, 18, 24, 26, 22, 40, 41, 41, 
+	40, 40, 40, 40, 40, 40, 40, 46, 
+	40, 40, 40, 40, 40, 40, 16, 20, 
+	40, 40, 40, 18, 24, 26, 22, 40, 
 	0
 };
 
 static const char _khmer_syllable_machine_trans_targs[] = {
-	10, 14, 17, 20, 11, 21, 10, 24, 
-	27, 30, 31, 32, 10, 22, 33, 34, 
-	26, 35, 10, 12, 4, 0, 16, 3, 
-	13, 15, 1, 10, 18, 2, 19, 10, 
-	23, 5, 8, 25, 6, 10, 28, 7, 
-	29, 9, 10
+	22, 1, 30, 24, 25, 3, 26, 5, 
+	27, 7, 28, 9, 29, 23, 22, 11, 
+	32, 22, 33, 13, 34, 15, 35, 17, 
+	36, 19, 37, 40, 39, 22, 31, 38, 
+	22, 0, 10, 2, 4, 6, 8, 22, 
+	22, 12, 14, 16, 18, 20, 21
 };
 
 static const char _khmer_syllable_machine_trans_actions[] = {
-	1, 2, 2, 0, 2, 2, 3, 2, 
-	2, 0, 2, 2, 6, 2, 0, 0, 
-	0, 0, 7, 2, 0, 0, 0, 0, 
-	2, 2, 0, 8, 0, 0, 0, 9, 
-	2, 0, 0, 2, 0, 10, 0, 0, 
-	0, 0, 11
+	1, 0, 2, 2, 2, 0, 0, 0, 
+	2, 0, 2, 0, 2, 2, 3, 0, 
+	4, 5, 2, 0, 0, 0, 2, 0, 
+	2, 0, 2, 4, 4, 8, 9, 0, 
+	10, 0, 0, 0, 0, 0, 0, 11, 
+	12, 0, 0, 0, 0, 0, 0
 };
 
 static const char _khmer_syllable_machine_to_state_actions[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 4, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 6, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0
+	0
 };
 
 static const char _khmer_syllable_machine_from_state_actions[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 5, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 7, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0
+	0
 };
 
 static const unsigned char _khmer_syllable_machine_eof_trans[] = {
-	1, 1, 1, 1, 1, 7, 7, 7, 
-	7, 7, 0, 19, 19, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 32, 32, 
-	32, 32, 32, 32, 32, 32, 32, 32, 
-	32, 43, 43, 43
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 15, 18, 18, 18, 18, 
+	18, 18, 18, 18, 18, 18, 0, 33, 
+	33, 33, 33, 33, 33, 33, 33, 40, 
+	41, 41, 41, 41, 41, 41, 40, 41, 
+	41
 };
 
-static const int khmer_syllable_machine_start = 10;
-static const int khmer_syllable_machine_first_final = 10;
+static const int khmer_syllable_machine_start = 22;
+static const int khmer_syllable_machine_first_final = 22;
 static const int khmer_syllable_machine_error = -1;
 
-static const int khmer_syllable_machine_en_main = 10;
+static const int khmer_syllable_machine_en_main = 22;
 
 
 #line 36 "hb-ot-shape-complex-khmer-machine.rl"
 
 
 
-#line 74 "hb-ot-shape-complex-khmer-machine.rl"
+#line 80 "hb-ot-shape-complex-khmer-machine.rl"
 
 
 #define found_syllable(syllable_type) \
   HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+    for (unsigned int i = ts; i < te; i++) \
       info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
@@ -173,11 +242,11 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts HB_UNUSED, te, act HB_UNUSED;
+  unsigned int p, pe, eof, ts, te, act HB_UNUSED;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 181 "hb-ot-shape-complex-khmer-machine.hh"
+#line 250 "hb-ot-shape-complex-khmer-machine.hh"
 	{
 	cs = khmer_syllable_machine_start;
 	ts = 0;
@@ -185,16 +254,15 @@
 	act = 0;
 	}
 
-#line 95 "hb-ot-shape-complex-khmer-machine.rl"
+#line 100 "hb-ot-shape-complex-khmer-machine.rl"
 
 
   p = 0;
   pe = eof = buffer->len;
 
-  unsigned int last = 0;
   unsigned int syllable_serial = 1;
   
-#line 198 "hb-ot-shape-complex-khmer-machine.hh"
+#line 266 "hb-ot-shape-complex-khmer-machine.hh"
 	{
 	int _slen;
 	int _trans;
@@ -204,11 +272,11 @@
 		goto _test_eof;
 _resume:
 	switch ( _khmer_syllable_machine_from_state_actions[cs] ) {
-	case 5:
+	case 7:
 #line 1 "NONE"
 	{ts = p;}
 	break;
-#line 212 "hb-ot-shape-complex-khmer-machine.hh"
+#line 280 "hb-ot-shape-complex-khmer-machine.hh"
 	}
 
 	_keys = _khmer_syllable_machine_trans_keys + (cs<<1);
@@ -231,47 +299,63 @@
 	{te = p+1;}
 	break;
 	case 8:
-#line 68 "hb-ot-shape-complex-khmer-machine.rl"
-	{te = p+1;{ found_syllable (consonant_syllable); }}
-	break;
-	case 10:
-#line 69 "hb-ot-shape-complex-khmer-machine.rl"
-	{te = p+1;{ found_syllable (broken_cluster); }}
-	break;
-	case 6:
-#line 70 "hb-ot-shape-complex-khmer-machine.rl"
+#line 76 "hb-ot-shape-complex-khmer-machine.rl"
 	{te = p+1;{ found_syllable (non_khmer_cluster); }}
 	break;
-	case 7:
-#line 68 "hb-ot-shape-complex-khmer-machine.rl"
+	case 10:
+#line 74 "hb-ot-shape-complex-khmer-machine.rl"
 	{te = p;p--;{ found_syllable (consonant_syllable); }}
 	break;
-	case 9:
-#line 69 "hb-ot-shape-complex-khmer-machine.rl"
+	case 12:
+#line 75 "hb-ot-shape-complex-khmer-machine.rl"
 	{te = p;p--;{ found_syllable (broken_cluster); }}
 	break;
 	case 11:
-#line 70 "hb-ot-shape-complex-khmer-machine.rl"
+#line 76 "hb-ot-shape-complex-khmer-machine.rl"
 	{te = p;p--;{ found_syllable (non_khmer_cluster); }}
 	break;
 	case 1:
-#line 68 "hb-ot-shape-complex-khmer-machine.rl"
+#line 74 "hb-ot-shape-complex-khmer-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
 	break;
-	case 3:
-#line 69 "hb-ot-shape-complex-khmer-machine.rl"
+	case 5:
+#line 75 "hb-ot-shape-complex-khmer-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
 	break;
-#line 266 "hb-ot-shape-complex-khmer-machine.hh"
+	case 3:
+#line 1 "NONE"
+	{	switch( act ) {
+	case 2:
+	{{p = ((te))-1;} found_syllable (broken_cluster); }
+	break;
+	case 3:
+	{{p = ((te))-1;} found_syllable (non_khmer_cluster); }
+	break;
+	}
+	}
+	break;
+	case 4:
+#line 1 "NONE"
+	{te = p+1;}
+#line 75 "hb-ot-shape-complex-khmer-machine.rl"
+	{act = 2;}
+	break;
+	case 9:
+#line 1 "NONE"
+	{te = p+1;}
+#line 76 "hb-ot-shape-complex-khmer-machine.rl"
+	{act = 3;}
+	break;
+#line 350 "hb-ot-shape-complex-khmer-machine.hh"
 	}
 
 _again:
 	switch ( _khmer_syllable_machine_to_state_actions[cs] ) {
-	case 4:
+	case 6:
 #line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 275 "hb-ot-shape-complex-khmer-machine.hh"
+#line 359 "hb-ot-shape-complex-khmer-machine.hh"
 	}
 
 	if ( ++p != pe )
@@ -287,7 +371,7 @@
 
 	}
 
-#line 104 "hb-ot-shape-complex-khmer-machine.rl"
+#line 108 "hb-ot-shape-complex-khmer-machine.rl"
 
 }
 
diff --git a/src/hb-ot-shape-complex-khmer-machine.rl b/src/hb-ot-shape-complex-khmer-machine.rl
index 54644d8..4c596ab 100644
--- a/src/hb-ot-shape-complex-khmer-machine.rl
+++ b/src/hb-ot-shape-complex-khmer-machine.rl
@@ -27,7 +27,7 @@
 #ifndef HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 %%{
   machine khmer_syllable_machine;
@@ -40,28 +40,34 @@
 # Same order as enum khmer_category_t.  Not sure how to avoid duplication.
 C    = 1;
 V    = 2;
-N    = 3;
 ZWNJ = 5;
 ZWJ  = 6;
-M    = 7;
-SM   = 8;
 PLACEHOLDER = 11;
 DOTTEDCIRCLE = 12;
-RS    = 13;
-Coeng = 14;
-Ra    = 16;
+Coeng= 14;
+Ra   = 16;
+Robatic = 20;
+Xgroup  = 21;
+Ygroup  = 22;
+VAbv = 26;
+VBlw = 27;
+VPre = 28;
+VPst = 29;
 
-c = (C | Ra | V);		# is_consonant
-n = ((ZWNJ?.RS)? (N.N?)?);	# is_consonant_modifier
-z = ZWJ|ZWNJ;			# is_joiner
+c = (C | Ra | V);
+cn = c.((ZWJ|ZWNJ)?.Robatic)?;
+joiner = (ZWJ | ZWNJ);
+xgroup = (joiner*.Xgroup)*;
+ygroup = Ygroup*;
 
-cn = c.n?;
-matra_group = z?.M.N?;
-syllable_tail = (SM.SM?)?;
+# This grammar was experimentally extracted from what Uniscribe allows.
+
+matra_group = VPre? xgroup VBlw? xgroup (joiner?.VAbv)? xgroup VPst?;
+syllable_tail = xgroup matra_group xgroup (Coeng.c)? ygroup;
 
 
-broken_cluster =	n? (Coeng.cn)* matra_group* (Coeng.cn)? syllable_tail;
-consonant_syllable =	(c|PLACEHOLDER|DOTTEDCIRCLE) broken_cluster;
+broken_cluster =	(Coeng.cn)* syllable_tail;
+consonant_syllable =	(cn|PLACEHOLDER|DOTTEDCIRCLE) broken_cluster;
 other =			any;
 
 main := |*
@@ -75,10 +81,9 @@
 
 #define found_syllable(syllable_type) \
   HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+    for (unsigned int i = ts; i < te; i++) \
       info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
@@ -86,7 +91,7 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts HB_UNUSED, te, act HB_UNUSED;
+  unsigned int p, pe, eof, ts, te, act HB_UNUSED;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   %%{
@@ -97,7 +102,6 @@
   p = 0;
   pe = eof = buffer->len;
 
-  unsigned int last = 0;
   unsigned int syllable_serial = 1;
   %%{
     write exec;
diff --git a/src/hb-ot-shape-complex-khmer-private.hh b/src/hb-ot-shape-complex-khmer-private.hh
deleted file mode 100644
index f90ef96..0000000
--- a/src/hb-ot-shape-complex-khmer-private.hh
+++ /dev/null
@@ -1,124 +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_OT_SHAPE_COMPLEX_KHMER_PRIVATE_HH
-#define HB_OT_SHAPE_COMPLEX_KHMER_PRIVATE_HH
-
-#include "hb-private.hh"
-
-#include "hb-ot-shape-complex-indic-private.hh"
-
-
-/* buffer var allocations */
-#define khmer_category() indic_category() /* khmer_category_t */
-#define khmer_position() indic_position() /* khmer_position_t */
-
-
-typedef indic_category_t khmer_category_t;
-typedef indic_position_t khmer_position_t;
-
-
-static inline khmer_position_t
-matra_position_khmer (khmer_position_t side)
-{
-  switch ((int) side)
-  {
-    case POS_PRE_C:
-      return POS_PRE_M;
-
-    case POS_POST_C:
-    case POS_ABOVE_C:
-    case POS_BELOW_C:
-      return POS_AFTER_POST;
-
-    default:
-      return side;
-  };
-}
-
-static inline bool
-is_consonant_or_vowel (const hb_glyph_info_t &info)
-{
-  return is_one_of (info, CONSONANT_FLAGS | FLAG (OT_V));
-}
-
-static inline bool
-is_coeng (const hb_glyph_info_t &info)
-{
-  return is_one_of (info, FLAG (OT_Coeng));
-}
-
-static inline void
-set_khmer_properties (hb_glyph_info_t &info)
-{
-  hb_codepoint_t u = info.codepoint;
-  unsigned int type = hb_indic_get_categories (u);
-  khmer_category_t cat = (khmer_category_t) (type & 0x7Fu);
-  khmer_position_t pos = (khmer_position_t) (type >> 8);
-
-
-  /*
-   * Re-assign category
-   */
-
-  if (unlikely (u == 0x17C6u)) cat = OT_N; /* Khmer Bindu doesn't like to be repositioned. */
-  else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x17CDu, 0x17D1u) ||
-		     u == 0x17CBu || u == 0x17D3u || u == 0x17DDu)) /* Khmer Various signs */
-  {
-    /* These can occur mid-syllable (eg. before matras), even though Unicode marks them as Syllable_Modifier.
-     * https://github.com/roozbehp/unicode-data/issues/5 */
-    cat = OT_M;
-    pos = POS_ABOVE_C;
-  }
-  else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x2010u, 0x2011u))) cat = OT_PLACEHOLDER;
-  else if (unlikely (u == 0x25CCu)) cat = OT_DOTTEDCIRCLE;
-
-
-  /*
-   * Re-assign position.
-   */
-
-  if ((FLAG_UNSAFE (cat) & CONSONANT_FLAGS))
-  {
-    pos = POS_BASE_C;
-    if (u == 0x179Au)
-      cat = OT_Ra;
-  }
-  else if (cat == OT_M)
-  {
-    pos = matra_position_khmer (pos);
-  }
-  else if ((FLAG_UNSAFE (cat) & (FLAG (OT_SM) | FLAG (OT_A) | FLAG (OT_Symbol))))
-  {
-    pos = POS_SMVD;
-  }
-
-  info.khmer_category() = cat;
-  info.khmer_position() = pos;
-}
-
-
-#endif /* HB_OT_SHAPE_COMPLEX_KHMER_PRIVATE_HH */
diff --git a/src/hb-ot-shape-complex-khmer.cc b/src/hb-ot-shape-complex-khmer.cc
index 264515e..88d1626 100644
--- a/src/hb-ot-shape-complex-khmer.cc
+++ b/src/hb-ot-shape-complex-khmer.cc
@@ -24,40 +24,38 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-khmer-private.hh"
-#include "hb-ot-layout-private.hh"
+#include "hb-ot-shape-complex-khmer.hh"
+#include "hb-ot-layout.hh"
 
 
 /*
  * Khmer shaper.
  */
 
-struct feature_list_t {
-  hb_tag_t tag;
-  hb_ot_map_feature_flags_t flags;
-};
-
-static const feature_list_t
+static const hb_ot_map_feature_t
 khmer_features[] =
 {
   /*
    * Basic features.
    * These features are applied in order, one at a time, after reordering.
    */
-  {HB_TAG('p','r','e','f'), F_NONE},
-  {HB_TAG('b','l','w','f'), F_NONE},
-  {HB_TAG('a','b','v','f'), F_NONE},
-  {HB_TAG('p','s','t','f'), F_NONE},
-  {HB_TAG('c','f','a','r'), F_NONE},
+  {HB_TAG('p','r','e','f'), F_MANUAL_JOINERS},
+  {HB_TAG('b','l','w','f'), F_MANUAL_JOINERS},
+  {HB_TAG('a','b','v','f'), F_MANUAL_JOINERS},
+  {HB_TAG('p','s','t','f'), F_MANUAL_JOINERS},
+  {HB_TAG('c','f','a','r'), F_MANUAL_JOINERS},
   /*
    * Other features.
    * These features are applied all at once.
    */
-  {HB_TAG('p','r','e','s'), F_GLOBAL},
-  {HB_TAG('a','b','v','s'), F_GLOBAL},
-  {HB_TAG('b','l','w','s'), F_GLOBAL},
-  {HB_TAG('p','s','t','s'), F_GLOBAL},
-  /* Positioning features, though we don't care about the types. */
+  {HB_TAG('p','r','e','s'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('a','b','v','s'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS},
+  /*
+   * Positioning features.
+   * We don't care about the types.
+   */
   {HB_TAG('d','i','s','t'), F_GLOBAL},
   {HB_TAG('a','b','v','m'), F_GLOBAL},
   {HB_TAG('b','l','w','m'), F_GLOBAL},
@@ -77,12 +75,13 @@
   _ABVS,
   _BLWS,
   _PSTS,
+
   _DIST,
   _ABVM,
   _BLWM,
 
   KHMER_NUM_FEATURES,
-  KHMER_BASIC_FEATURES = _PRES /* Don't forget to update this! */
+  KHMER_BASIC_FEATURES = _PRES, /* Don't forget to update this! */
 };
 
 static void
@@ -117,22 +116,20 @@
    *
    * https://github.com/harfbuzz/harfbuzz/issues/974
    */
-  map->add_global_bool_feature (HB_TAG('l','o','c','l'));
-  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
+  map->enable_feature (HB_TAG('l','o','c','l'));
+  map->enable_feature (HB_TAG('c','c','m','p'));
 
   unsigned int i = 0;
-  for (; i < KHMER_BASIC_FEATURES; i++) {
-    map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
-  }
+  for (; i < KHMER_BASIC_FEATURES; i++)
+    map->add_feature (khmer_features[i]);
 
   map->add_gsub_pause (clear_syllables);
 
-  for (; i < KHMER_NUM_FEATURES; i++) {
-    map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
-  }
+  for (; i < KHMER_NUM_FEATURES; i++)
+    map->add_feature (khmer_features[i]);
 
-  map->add_global_bool_feature (HB_TAG('c','a','l','t'));
-  map->add_global_bool_feature (HB_TAG('c','l','i','g'));
+  map->enable_feature (HB_TAG('c','a','l','t'));
+  map->enable_feature (HB_TAG('c','l','i','g'));
 
 }
 
@@ -142,10 +139,10 @@
   /* Uniscribe does not apply 'kern' in Khmer. */
   if (hb_options ().uniscribe_bug_compatible)
   {
-    plan->map.add_feature (HB_TAG('k','e','r','n'), 0, F_GLOBAL);
+    plan->map.disable_feature (HB_TAG('k','e','r','n'));
   }
 
-  plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
+  plan->map.disable_feature (HB_TAG('l','i','g','a'));
 }
 
 
@@ -244,7 +241,6 @@
 		   hb_font_t                *font HB_UNUSED)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, khmer_category);
-  HB_BUFFER_ALLOCATE_VAR (buffer, khmer_position);
 
   /* We cannot setup masks here.  We save information about characters
    * and setup masks later on in a pause-callback. */
@@ -333,7 +329,7 @@
     }
 
     /* Reorder left matra piece. */
-    else if (info[i].khmer_position() == POS_PRE_M)
+    else if (info[i].khmer_category() == OT_VPre)
     {
       /* Move to the start. */
       buffer->merge_clusters (start, i + 1);
@@ -435,7 +431,6 @@
     initial_reordering_syllable (plan, font->face, buffer, start, end);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category);
-  HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_position);
 }
 
 static void
@@ -501,7 +496,7 @@
   decompose_khmer,
   compose_khmer,
   setup_masks_khmer,
-  nullptr, /* disable_otl */
+  HB_TAG_NONE, /* gpos_tag */
   nullptr, /* reorder_marks */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   false, /* fallback_position */
diff --git a/src/hb-ot-shape-complex-khmer.hh b/src/hb-ot-shape-complex-khmer.hh
new file mode 100644
index 0000000..6222945
--- /dev/null
+++ b/src/hb-ot-shape-complex-khmer.hh
@@ -0,0 +1,114 @@
+/*
+ * 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_KHMER_HH
+#define HB_OT_SHAPE_COMPLEX_KHMER_HH
+
+#include "hb.hh"
+
+#include "hb-ot-shape-complex-indic.hh"
+
+
+/* buffer var allocations */
+#define khmer_category() indic_category() /* khmer_category_t */
+
+
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum khmer_category_t
+{
+  OT_Robatic = 20,
+  OT_Xgroup  = 21,
+  OT_Ygroup  = 22,
+
+  OT_VAbv    = 26,
+  OT_VBlw    = 27,
+  OT_VPre    = 28,
+  OT_VPst    = 29,
+};
+
+static inline void
+set_khmer_properties (hb_glyph_info_t &info)
+{
+  hb_codepoint_t u = info.codepoint;
+  unsigned int type = hb_indic_get_categories (u);
+  khmer_category_t cat = (khmer_category_t) (type & 0x7Fu);
+  indic_position_t pos = (indic_position_t) (type >> 8);
+
+
+  /*
+   * Re-assign category
+   *
+   * These categories are experimentally extracted from what Uniscribe allows.
+   */
+  switch (u)
+  {
+    case 0x179Au:
+      cat = (khmer_category_t) OT_Ra;
+      break;
+
+    case 0x17CCu:
+    case 0x17C9u:
+    case 0x17CAu:
+      cat = OT_Robatic;
+      break;
+
+    case 0x17C6u:
+    case 0x17CBu:
+    case 0x17CDu:
+    case 0x17CEu:
+    case 0x17CFu:
+    case 0x17D0u:
+    case 0x17D1u:
+      cat = OT_Xgroup;
+      break;
+
+    case 0x17C7u:
+    case 0x17C8u:
+    case 0x17DDu:
+    case 0x17D3u: /* Just guessing. Uniscribe doesn't categorize it. */
+      cat = OT_Ygroup;
+      break;
+  }
+
+  /*
+   * Re-assign position.
+   */
+  if (cat == (khmer_category_t) OT_M)
+    switch ((int) pos)
+    {
+      case POS_PRE_C:	cat = OT_VPre; break;
+      case POS_BELOW_C:	cat = OT_VBlw; break;
+      case POS_ABOVE_C:	cat = OT_VAbv; break;
+      case POS_POST_C:	cat = OT_VPst; break;
+      default: assert (0);
+    };
+
+  info.khmer_category() = cat;
+}
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_KHMER_HH */
diff --git a/src/hb-ot-shape-complex-myanmar-machine.hh b/src/hb-ot-shape-complex-myanmar-machine.hh
index fb67dd4..0c19e4f 100644
--- a/src/hb-ot-shape-complex-myanmar-machine.hh
+++ b/src/hb-ot-shape-complex-myanmar-machine.hh
@@ -29,7 +29,7 @@
 #ifndef HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 #line 36 "hb-ot-shape-complex-myanmar-machine.hh"
@@ -283,10 +283,9 @@
 
 #define found_syllable(syllable_type) \
   HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+    for (unsigned int i = ts; i < te; i++) \
       info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
@@ -294,11 +293,11 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  unsigned int p, pe, eof, ts, te, act HB_UNUSED;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 302 "hb-ot-shape-complex-myanmar-machine.hh"
+#line 301 "hb-ot-shape-complex-myanmar-machine.hh"
 	{
 	cs = myanmar_syllable_machine_start;
 	ts = 0;
@@ -306,16 +305,15 @@
 	act = 0;
 	}
 
-#line 115 "hb-ot-shape-complex-myanmar-machine.rl"
+#line 114 "hb-ot-shape-complex-myanmar-machine.rl"
 
 
   p = 0;
   pe = eof = buffer->len;
 
-  unsigned int last = 0;
   unsigned int syllable_serial = 1;
   
-#line 319 "hb-ot-shape-complex-myanmar-machine.hh"
+#line 317 "hb-ot-shape-complex-myanmar-machine.hh"
 	{
 	int _slen;
 	int _trans;
@@ -329,7 +327,7 @@
 #line 1 "NONE"
 	{ts = p;}
 	break;
-#line 333 "hb-ot-shape-complex-myanmar-machine.hh"
+#line 331 "hb-ot-shape-complex-myanmar-machine.hh"
 	}
 
 	_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
@@ -379,7 +377,7 @@
 #line 90 "hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p;p--;{ found_syllable (non_myanmar_cluster); }}
 	break;
-#line 383 "hb-ot-shape-complex-myanmar-machine.hh"
+#line 381 "hb-ot-shape-complex-myanmar-machine.hh"
 	}
 
 _again:
@@ -388,7 +386,7 @@
 #line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 392 "hb-ot-shape-complex-myanmar-machine.hh"
+#line 390 "hb-ot-shape-complex-myanmar-machine.hh"
 	}
 
 	if ( ++p != pe )
@@ -404,7 +402,7 @@
 
 	}
 
-#line 124 "hb-ot-shape-complex-myanmar-machine.rl"
+#line 122 "hb-ot-shape-complex-myanmar-machine.rl"
 
 }
 
diff --git a/src/hb-ot-shape-complex-myanmar-machine.rl b/src/hb-ot-shape-complex-myanmar-machine.rl
index 0cd84fa..7845a86 100644
--- a/src/hb-ot-shape-complex-myanmar-machine.rl
+++ b/src/hb-ot-shape-complex-myanmar-machine.rl
@@ -27,7 +27,7 @@
 #ifndef HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 %%{
   machine myanmar_syllable_machine;
@@ -95,10 +95,9 @@
 
 #define found_syllable(syllable_type) \
   HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+    for (unsigned int i = ts; i < te; i++) \
       info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
@@ -106,7 +105,7 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  unsigned int p, pe, eof, ts, te, act HB_UNUSED;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   %%{
@@ -117,7 +116,6 @@
   p = 0;
   pe = eof = buffer->len;
 
-  unsigned int last = 0;
   unsigned int syllable_serial = 1;
   %%{
     write exec;
diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc
index e4214b8..e201a23 100644
--- a/src/hb-ot-shape-complex-myanmar.cc
+++ b/src/hb-ot-shape-complex-myanmar.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-myanmar-private.hh"
+#include "hb-ot-shape-complex-myanmar.hh"
 
 
 /*
@@ -54,7 +54,14 @@
   HB_TAG('a','b','v','s'),
   HB_TAG('b','l','w','s'),
   HB_TAG('p','s','t','s'),
-  /* Positioning features, though we don't care about the types. */
+};
+static const hb_tag_t
+positioning_features[] =
+{
+  /*
+   * Positioning features.
+   * We don't care about the types.
+   */
   HB_TAG('d','i','s','t'),
   /* Pre-release version of Windows 8 Myanmar font had abvm,blwm
    * features.  The released Windows 8 version of the font (as well
@@ -89,27 +96,33 @@
   /* Do this before any lookups have been applied. */
   map->add_gsub_pause (setup_syllables);
 
-  map->add_global_bool_feature (HB_TAG('l','o','c','l'));
+  map->enable_feature (HB_TAG('l','o','c','l'));
   /* The Indic specs do not require ccmp, but we apply it here since if
    * there is a use of it, it's typically at the beginning. */
-  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
+  map->enable_feature (HB_TAG('c','c','m','p'));
 
 
   map->add_gsub_pause (initial_reordering);
+
   for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
   {
-    map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+    map->enable_feature (basic_features[i], F_MANUAL_ZWJ);
     map->add_gsub_pause (nullptr);
   }
+
   map->add_gsub_pause (final_reordering);
+
   for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
-    map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+    map->enable_feature (other_features[i], F_MANUAL_ZWJ);
+
+  for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++)
+    map->enable_feature (positioning_features[i]);
 }
 
 static void
 override_features_myanmar (hb_ot_shape_planner_t *plan)
 {
-  plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
+  plan->map.disable_feature (HB_TAG('l','i','g','a'));
 }
 
 
@@ -362,6 +375,25 @@
 }
 
 
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
+{
+  collect_features_myanmar,
+  override_features_myanmar,
+  nullptr, /* data_create */
+  nullptr, /* data_destroy */
+  nullptr, /* preprocess_text */
+  nullptr, /* postprocess_glyphs */
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
+  nullptr, /* decompose */
+  nullptr, /* compose */
+  setup_masks_myanmar,
+  HB_TAG_NONE, /* gpos_tag */
+  nullptr, /* reorder_marks */
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
+  false, /* fallback_position */
+};
+
+
 /* 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 =
@@ -376,26 +408,30 @@
   nullptr, /* decompose */
   nullptr, /* compose */
   nullptr, /* setup_masks */
-  nullptr, /* disable_otl */
+  HB_TAG_NONE, /* gpos_tag */
   nullptr, /* reorder_marks */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
 };
 
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
+
+/* Ugly Zawgyi encoding.
+ * Disable all auto processing.
+ * https://github.com/harfbuzz/harfbuzz/issues/1162 */
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_zawgyi =
 {
-  collect_features_myanmar,
-  override_features_myanmar,
+  nullptr, /* collect_features */
+  nullptr, /* override_features */
   nullptr, /* data_create */
   nullptr, /* data_destroy */
   nullptr, /* preprocess_text */
   nullptr, /* postprocess_glyphs */
-  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
+  HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
   nullptr, /* decompose */
   nullptr, /* compose */
-  setup_masks_myanmar,
-  nullptr, /* disable_otl */
+  nullptr, /* setup_masks */
+  HB_TAG_NONE, /* gpos_tag */
   nullptr, /* reorder_marks */
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   false, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-myanmar-private.hh b/src/hb-ot-shape-complex-myanmar.hh
similarity index 73%
rename from src/hb-ot-shape-complex-myanmar-private.hh
rename to src/hb-ot-shape-complex-myanmar.hh
index 14d011d..3e9537a 100644
--- a/src/hb-ot-shape-complex-myanmar-private.hh
+++ b/src/hb-ot-shape-complex-myanmar.hh
@@ -24,12 +24,12 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_MYANMAR_PRIVATE_HH
-#define HB_OT_SHAPE_COMPLEX_MYANMAR_PRIVATE_HH
+#ifndef HB_OT_SHAPE_COMPLEX_MYANMAR_HH
+#define HB_OT_SHAPE_COMPLEX_MYANMAR_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-ot-shape-complex-indic-private.hh"
+#include "hb-ot-shape-complex-indic.hh"
 
 
 /* buffer var allocations */
@@ -64,42 +64,42 @@
 {
   hb_codepoint_t u = info.codepoint;
   unsigned int type = hb_indic_get_categories (u);
-  indic_category_t cat = (indic_category_t) (type & 0x7Fu);
+  unsigned int cat = type & 0x7Fu;
   indic_position_t pos = (indic_position_t) (type >> 8);
 
   /* Myanmar
    * https://docs.microsoft.com/en-us/typography/script-development/myanmar#analyze
    */
   if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)))
-    cat = (indic_category_t) OT_VS;
+    cat = OT_VS;
 
   switch (u)
   {
     case 0x104Eu:
-      cat = (indic_category_t) OT_C; /* The spec says C, IndicSyllableCategory doesn't have. */
+      cat = OT_C; /* The spec says C, IndicSyllableCategory doesn't have. */
       break;
 
     case 0x002Du: case 0x00A0u: case 0x00D7u: case 0x2012u:
     case 0x2013u: case 0x2014u: case 0x2015u: case 0x2022u:
     case 0x25CCu: case 0x25FBu: case 0x25FCu: case 0x25FDu:
     case 0x25FEu:
-      cat = (indic_category_t) OT_GB;
+      cat = OT_GB;
       break;
 
     case 0x1004u: case 0x101Bu: case 0x105Au:
-      cat = (indic_category_t) OT_Ra;
+      cat = OT_Ra;
       break;
 
     case 0x1032u: case 0x1036u:
-      cat = (indic_category_t) OT_A;
+      cat = OT_A;
       break;
 
     case 0x1039u:
-      cat = (indic_category_t) OT_H;
+      cat = OT_H;
       break;
 
     case 0x103Au:
-      cat = (indic_category_t) OT_As;
+      cat = OT_As;
       break;
 
     case 0x1041u: case 0x1042u: case 0x1043u: case 0x1044u:
@@ -107,47 +107,47 @@
     case 0x1049u: case 0x1090u: case 0x1091u: case 0x1092u:
     case 0x1093u: case 0x1094u: case 0x1095u: case 0x1096u:
     case 0x1097u: case 0x1098u: case 0x1099u:
-      cat = (indic_category_t) OT_D;
+      cat = OT_D;
       break;
 
     case 0x1040u:
-      cat = (indic_category_t) OT_D; /* XXX The spec says D0, but Uniscribe doesn't seem to do. */
+      cat = OT_D; /* XXX The spec says D0, but Uniscribe doesn't seem to do. */
       break;
 
     case 0x103Eu: case 0x1060u:
-      cat = (indic_category_t) OT_MH;
+      cat = OT_MH;
       break;
 
     case 0x103Cu:
-      cat = (indic_category_t) OT_MR;
+      cat = OT_MR;
       break;
 
     case 0x103Du: case 0x1082u:
-      cat = (indic_category_t) OT_MW;
+      cat = OT_MW;
       break;
 
     case 0x103Bu: case 0x105Eu: case 0x105Fu:
-      cat = (indic_category_t) OT_MY;
+      cat = OT_MY;
       break;
 
     case 0x1063u: case 0x1064u: case 0x1069u: case 0x106Au:
     case 0x106Bu: case 0x106Cu: case 0x106Du: case 0xAA7Bu:
-      cat = (indic_category_t) OT_PT;
+      cat = OT_PT;
       break;
 
     case 0x1038u: case 0x1087u: case 0x1088u: case 0x1089u:
     case 0x108Au: case 0x108Bu: case 0x108Cu: case 0x108Du:
     case 0x108Fu: case 0x109Au: case 0x109Bu: case 0x109Cu:
-      cat = (indic_category_t) OT_SM;
+      cat = OT_SM;
       break;
 
     case 0x104Au: case 0x104Bu:
-      cat = (indic_category_t) OT_P;
+      cat = OT_P;
       break;
 
     case 0xAA74u: case 0xAA75u: case 0xAA76u:
       /* https://github.com/roozbehp/unicode-data/issues/3 */
-      cat = (indic_category_t) OT_C;
+      cat = OT_C;
       break;
   }
 
@@ -155,17 +155,17 @@
   {
     switch ((int) pos)
     {
-      case POS_PRE_C:	cat = (indic_category_t) OT_VPre;
-			pos = POS_PRE_M;                  break;
-      case POS_ABOVE_C:	cat = (indic_category_t) OT_VAbv; break;
-      case POS_BELOW_C:	cat = (indic_category_t) OT_VBlw; break;
-      case POS_POST_C:	cat = (indic_category_t) OT_VPst; break;
+      case POS_PRE_C:	cat = OT_VPre;
+			pos = POS_PRE_M; break;
+      case POS_ABOVE_C:	cat = OT_VAbv;   break;
+      case POS_BELOW_C:	cat = OT_VBlw;   break;
+      case POS_POST_C:	cat = OT_VPst;   break;
     }
   }
 
-  info.myanmar_category() = (myanmar_category_t) cat;
+  info.myanmar_category() = cat;
   info.myanmar_position() = pos;
 }
 
 
-#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_PRIVATE_HH */
+#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_HH */
diff --git a/src/hb-ot-shape-complex-thai.cc b/src/hb-ot-shape-complex-thai.cc
index 02d78ac..b687fe6 100644
--- a/src/hb-ot-shape-complex-thai.cc
+++ b/src/hb-ot-shape-complex-thai.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-complex.hh"
 
 
 /* Thai / Lao shaper */
@@ -324,9 +324,9 @@
     }
 
     /* Is SARA AM. Decompose and reorder. */
-    hb_codepoint_t decomposed[2] = {hb_codepoint_t (NIKHAHIT_FROM_SARA_AM (u)),
-				    hb_codepoint_t (SARA_AA_FROM_SARA_AM (u))};
-    buffer->replace_glyphs (1, 2, decomposed);
+    hb_glyph_info_t &nikhahit = buffer->output_glyph (NIKHAHIT_FROM_SARA_AM (u));
+    _hb_glyph_info_set_continuation (&nikhahit);
+    buffer->replace_glyph (SARA_AA_FROM_SARA_AM (u));
     if (unlikely (!buffer->successful))
       return;
 
@@ -357,7 +357,8 @@
 	buffer->merge_out_clusters (start - 1, end);
     }
   }
-  buffer->swap_buffers ();
+  if (likely (buffer->successful))
+    buffer->swap_buffers ();
 
   /* If font has Thai GSUB, we are done. */
   if (plan->props.script == HB_SCRIPT_THAI && !plan->map.found_script[0])
@@ -376,7 +377,7 @@
   nullptr, /* decompose */
   nullptr, /* compose */
   nullptr, /* setup_masks */
-  nullptr, /* disable_otl */
+  HB_TAG_NONE, /* gpos_tag */
   nullptr, /* reorder_marks */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   false,/* fallback_position */
diff --git a/src/hb-ot-shape-complex-tibetan.cc b/src/hb-ot-shape-complex-tibetan.cc
deleted file mode 100644
index eaac0bf..0000000
--- a/src/hb-ot-shape-complex-tibetan.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright © 2010,2012  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#include "hb-ot-shape-complex-private.hh"
-
-
-static const hb_tag_t tibetan_features[] =
-{
-  HB_TAG('a','b','v','s'),
-  HB_TAG('b','l','w','s'),
-  HB_TAG('a','b','v','m'),
-  HB_TAG('b','l','w','m'),
-  HB_TAG_NONE
-};
-
-static void
-collect_features_tibetan (hb_ot_shape_planner_t *plan)
-{
-  for (const hb_tag_t *script_features = tibetan_features; script_features && *script_features; script_features++)
-    plan->map.add_global_bool_feature (*script_features);
-}
-
-
-const hb_ot_complex_shaper_t _hb_ot_complex_shaper_tibetan =
-{
-  collect_features_tibetan,
-  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 */
-  nullptr, /* disable_otl */
-  nullptr, /* reorder_marks */
-  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
-  true, /* fallback_position */
-};
diff --git a/src/hb-ot-shape-complex-use-machine.hh b/src/hb-ot-shape-complex-use-machine.hh
index 0ec805a..c9410e4 100644
--- a/src/hb-ot-shape-complex-use-machine.hh
+++ b/src/hb-ot-shape-complex-use-machine.hh
@@ -31,232 +31,271 @@
 #ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 #line 38 "hb-ot-shape-complex-use-machine.hh"
 static const unsigned char _use_syllable_machine_trans_keys[] = {
-	12u, 12u, 1u, 15u, 1u, 1u, 12u, 12u, 0u, 43u, 21u, 21u, 8u, 39u, 8u, 39u, 
-	1u, 15u, 1u, 1u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u, 
-	8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
-	8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 13u, 21u, 4u, 4u, 13u, 13u, 8u, 39u, 
-	8u, 39u, 8u, 39u, 8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u, 8u, 39u, 8u, 39u, 
-	8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
-	8u, 39u, 1u, 15u, 12u, 12u, 1u, 39u, 8u, 39u, 21u, 42u, 41u, 42u, 42u, 42u, 
-	1u, 5u, 0
+	12u, 44u, 1u, 15u, 1u, 1u, 12u, 44u, 0u, 44u, 21u, 21u, 8u, 44u, 8u, 44u,
+	1u, 15u, 1u, 1u, 8u, 44u, 8u, 44u, 8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u,
+	8u, 39u, 8u, 39u, 8u, 39u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u,
+	8u, 44u, 8u, 44u, 8u, 44u, 1u, 39u, 8u, 44u, 13u, 21u, 4u, 4u, 13u, 13u,
+	8u, 44u, 8u, 44u, 8u, 44u, 8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u, 8u, 39u,
+	8u, 39u, 8u, 39u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u,
+	8u, 44u, 8u, 44u, 1u, 39u, 1u, 15u, 12u, 44u, 1u, 44u, 8u, 44u, 21u, 42u,
+	41u, 42u, 42u, 42u, 1u, 5u, 0
 };
 
 static const char _use_syllable_machine_key_spans[] = {
-	1, 15, 1, 1, 44, 1, 32, 32, 
-	15, 1, 32, 32, 32, 19, 19, 19, 
-	32, 32, 32, 32, 32, 32, 32, 32, 
-	32, 32, 32, 32, 9, 1, 1, 32, 
-	32, 32, 32, 19, 19, 19, 32, 32, 
-	32, 32, 32, 32, 32, 32, 32, 32, 
-	32, 15, 1, 39, 32, 22, 2, 1, 
-	5
+	33, 15, 1, 33, 45, 1, 37, 37,
+	15, 1, 37, 37, 32, 19, 19, 19,
+	32, 32, 32, 37, 37, 37, 37, 37,
+	37, 37, 37, 39, 37, 9, 1, 1,
+	37, 37, 37, 32, 19, 19, 19, 32,
+	32, 32, 37, 37, 37, 37, 37, 37,
+	37, 37, 39, 15, 33, 44, 37, 22,
+	2, 1, 5
 };
 
 static const short _use_syllable_machine_index_offsets[] = {
-	0, 2, 18, 20, 22, 67, 69, 102, 
-	135, 151, 153, 186, 219, 252, 272, 292, 
-	312, 345, 378, 411, 444, 477, 510, 543, 
-	576, 609, 642, 675, 708, 718, 720, 722, 
-	755, 788, 821, 854, 874, 894, 914, 947, 
-	980, 1013, 1046, 1079, 1112, 1145, 1178, 1211, 
-	1244, 1277, 1293, 1295, 1335, 1368, 1391, 1394, 
-	1396
+	0, 34, 50, 52, 86, 132, 134, 172,
+	210, 226, 228, 266, 304, 337, 357, 377,
+	397, 430, 463, 496, 534, 572, 610, 648,
+	686, 724, 762, 800, 840, 878, 888, 890,
+	892, 930, 968, 1006, 1039, 1059, 1079, 1099,
+	1132, 1165, 1198, 1236, 1274, 1312, 1350, 1388,
+	1426, 1464, 1502, 1542, 1558, 1592, 1637, 1675,
+	1698, 1701, 1703
 };
 
 static const char _use_syllable_machine_indicies[] = {
+	1, 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, 0, 3, 2, 2, 2, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	4, 2, 3, 2, 6, 5, 7, 8, 
+	4, 2, 3, 2, 6, 5, 5, 5,
+	5, 5, 5, 5, 5, 5, 5, 5,
+	5, 5, 5, 5, 5, 5, 5, 5,
+	5, 5, 5, 5, 5, 5, 5, 5,
+	5, 5, 5, 5, 6, 5, 7, 8,
 	9, 7, 10, 8, 9, 9, 11, 9, 
 	9, 3, 12, 9, 9, 13, 7, 7, 
 	14, 15, 9, 9, 16, 17, 18, 19, 
 	20, 21, 22, 16, 23, 24, 25, 26, 
 	27, 28, 9, 29, 30, 31, 9, 9, 
-	9, 32, 9, 34, 33, 36, 35, 35, 
-	37, 1, 35, 35, 38, 35, 35, 35, 
-	35, 35, 39, 40, 41, 42, 43, 44, 
-	45, 46, 40, 47, 39, 48, 49, 50, 
-	51, 35, 52, 53, 54, 35, 36, 35, 
-	35, 37, 1, 35, 35, 38, 35, 35, 
-	35, 35, 35, 55, 40, 41, 42, 43, 
-	44, 45, 46, 40, 47, 48, 48, 49, 
-	50, 51, 35, 52, 53, 54, 35, 37, 
-	56, 56, 56, 56, 56, 56, 56, 56, 
-	56, 56, 56, 56, 56, 57, 56, 37, 
-	56, 36, 35, 35, 37, 1, 35, 35, 
-	38, 35, 35, 35, 35, 35, 35, 40, 
-	41, 42, 43, 44, 45, 46, 40, 47, 
-	48, 48, 49, 50, 51, 35, 52, 53, 
-	54, 35, 36, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	40, 41, 42, 43, 44, 35, 35, 35, 
-	35, 35, 35, 49, 50, 51, 35, 52, 
-	53, 54, 35, 36, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 41, 42, 43, 44, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	52, 53, 54, 35, 36, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 42, 43, 44, 35, 
-	36, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 43, 44, 35, 36, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 44, 35, 
-	36, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	42, 43, 44, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 52, 53, 54, 
-	35, 36, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 42, 43, 44, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 53, 
-	54, 35, 36, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 42, 43, 44, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 54, 35, 36, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 41, 42, 43, 44, 35, 35, 
-	35, 35, 35, 35, 49, 50, 51, 35, 
-	52, 53, 54, 35, 36, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 41, 42, 43, 44, 35, 
-	35, 35, 35, 35, 35, 35, 50, 51, 
-	35, 52, 53, 54, 35, 36, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 41, 42, 43, 44, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	51, 35, 52, 53, 54, 35, 36, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 40, 41, 42, 43, 
-	44, 35, 46, 40, 35, 35, 35, 49, 
-	50, 51, 35, 52, 53, 54, 35, 36, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 40, 41, 42, 
-	43, 44, 35, 58, 40, 35, 35, 35, 
-	49, 50, 51, 35, 52, 53, 54, 35, 
-	36, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 40, 41, 
-	42, 43, 44, 35, 35, 40, 35, 35, 
-	35, 49, 50, 51, 35, 52, 53, 54, 
-	35, 36, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 40, 
-	41, 42, 43, 44, 45, 46, 40, 35, 
-	35, 35, 49, 50, 51, 35, 52, 53, 
-	54, 35, 36, 35, 35, 37, 1, 35, 
-	35, 38, 35, 35, 35, 35, 35, 35, 
-	40, 41, 42, 43, 44, 45, 46, 40, 
-	47, 35, 48, 49, 50, 51, 35, 52, 
-	53, 54, 35, 36, 35, 35, 37, 1, 
-	35, 35, 38, 35, 35, 35, 35, 35, 
-	35, 40, 41, 42, 43, 44, 45, 46, 
-	40, 47, 39, 48, 49, 50, 51, 35, 
-	52, 53, 54, 35, 60, 59, 59, 59, 
-	59, 59, 59, 59, 61, 59, 10, 62, 
-	60, 59, 11, 63, 63, 3, 6, 63, 
-	63, 64, 63, 63, 63, 63, 63, 65, 
+	9, 32, 33, 9, 35, 34, 37, 36,
+	36, 38, 1, 36, 36, 39, 36, 36,
+	36, 36, 36, 40, 41, 42, 43, 44,
+	45, 46, 47, 41, 48, 40, 49, 50,
+	51, 52, 36, 53, 54, 55, 36, 36,
+	36, 36, 56, 36, 37, 36, 36, 38,
+	1, 36, 36, 39, 36, 36, 36, 36,
+	36, 57, 41, 42, 43, 44, 45, 46,
+	47, 41, 48, 49, 49, 50, 51, 52,
+	36, 53, 54, 55, 36, 36, 36, 36,
+	56, 36, 38, 58, 58, 58, 58, 58,
+	58, 58, 58, 58, 58, 58, 58, 58,
+	59, 58, 38, 58, 37, 36, 36, 38,
+	1, 36, 36, 39, 36, 36, 36, 36,
+	36, 36, 41, 42, 43, 44, 45, 46,
+	47, 41, 48, 49, 49, 50, 51, 52,
+	36, 53, 54, 55, 36, 36, 36, 36,
+	56, 36, 37, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	41, 42, 43, 44, 45, 36, 36, 36,
+	36, 36, 36, 50, 51, 52, 36, 53,
+	54, 55, 36, 36, 36, 36, 42, 36,
+	37, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 36, 42,
+	43, 44, 45, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 53, 54, 55,
+	36, 37, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	36, 43, 44, 45, 36, 37, 36, 36,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 44, 45,
+	36, 37, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 45, 36, 37, 36, 36,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 43, 44, 45,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 53, 54, 55, 36, 37, 36,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 43, 44,
+	45, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 54, 55, 36, 37,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 36, 43,
+	44, 45, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 55, 36,
+	37, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 36, 42,
+	43, 44, 45, 36, 36, 36, 36, 36,
+	36, 50, 51, 52, 36, 53, 54, 55,
+	36, 36, 36, 36, 42, 36, 37, 36,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 42, 43, 44,
+	45, 36, 36, 36, 36, 36, 36, 36,
+	51, 52, 36, 53, 54, 55, 36, 36,
+	36, 36, 42, 36, 37, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 42, 43, 44, 45, 36,
+	36, 36, 36, 36, 36, 36, 36, 52,
+	36, 53, 54, 55, 36, 36, 36, 36,
+	42, 36, 37, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	41, 42, 43, 44, 45, 36, 47, 41,
+	36, 36, 36, 50, 51, 52, 36, 53,
+	54, 55, 36, 36, 36, 36, 42, 36,
+	37, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 41, 42,
+	43, 44, 45, 36, 60, 41, 36, 36,
+	36, 50, 51, 52, 36, 53, 54, 55,
+	36, 36, 36, 36, 42, 36, 37, 36,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 36, 36, 41, 42, 43, 44,
+	45, 36, 36, 41, 36, 36, 36, 50,
+	51, 52, 36, 53, 54, 55, 36, 36,
+	36, 36, 42, 36, 37, 36, 36, 36,
+	36, 36, 36, 36, 36, 36, 36, 36,
+	36, 36, 41, 42, 43, 44, 45, 46,
+	47, 41, 36, 36, 36, 50, 51, 52,
+	36, 53, 54, 55, 36, 36, 36, 36,
+	42, 36, 37, 36, 36, 38, 1, 36,
+	36, 39, 36, 36, 36, 36, 36, 36,
+	41, 42, 43, 44, 45, 46, 47, 41,
+	48, 36, 49, 50, 51, 52, 36, 53,
+	54, 55, 36, 36, 36, 36, 56, 36,
+	38, 58, 58, 58, 58, 58, 58, 37,
+	58, 58, 58, 58, 58, 58, 59, 58,
+	58, 58, 58, 58, 58, 58, 42, 43,
+	44, 45, 58, 58, 58, 58, 58, 58,
+	58, 58, 58, 58, 53, 54, 55, 58,
+	37, 36, 36, 38, 1, 36, 36, 39,
+	36, 36, 36, 36, 36, 36, 41, 42,
+	43, 44, 45, 46, 47, 41, 48, 40,
+	49, 50, 51, 52, 36, 53, 54, 55,
+	36, 36, 36, 36, 56, 36, 62, 61,
+	61, 61, 61, 61, 61, 61, 63, 61,
+	10, 64, 62, 61, 11, 65, 65, 3,
+	6, 65, 65, 66, 65, 65, 65, 65,
+	65, 67, 16, 17, 18, 19, 20, 21,
+	22, 16, 23, 25, 25, 26, 27, 28,
+	65, 29, 30, 31, 65, 65, 65, 65,
+	33, 65, 11, 65, 65, 3, 6, 65,
+	65, 66, 65, 65, 65, 65, 65, 65,
 	16, 17, 18, 19, 20, 21, 22, 16, 
-	23, 25, 25, 26, 27, 28, 63, 29, 
-	30, 31, 63, 11, 63, 63, 3, 6, 
-	63, 63, 64, 63, 63, 63, 63, 63, 
-	63, 16, 17, 18, 19, 20, 21, 22, 
-	16, 23, 25, 25, 26, 27, 28, 63, 
-	29, 30, 31, 63, 11, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 16, 17, 18, 19, 20, 63, 
-	63, 63, 63, 63, 63, 26, 27, 28, 
-	63, 29, 30, 31, 63, 11, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 17, 18, 19, 20, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 29, 30, 31, 63, 11, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 18, 19, 
-	20, 63, 11, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 19, 20, 63, 11, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	20, 63, 11, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 18, 19, 20, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 29, 
-	30, 31, 63, 11, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 18, 19, 20, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 30, 31, 63, 11, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 18, 19, 20, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 31, 63, 11, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 17, 18, 19, 20, 
-	63, 63, 63, 63, 63, 63, 26, 27, 
-	28, 63, 29, 30, 31, 63, 11, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 17, 18, 19, 
-	20, 63, 63, 63, 63, 63, 63, 63, 
-	27, 28, 63, 29, 30, 31, 63, 11, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 17, 18, 
-	19, 20, 63, 63, 63, 63, 63, 63, 
-	63, 63, 28, 63, 29, 30, 31, 63, 
-	11, 63, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 16, 17, 
-	18, 19, 20, 63, 22, 16, 63, 63, 
-	63, 26, 27, 28, 63, 29, 30, 31, 
-	63, 11, 63, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 16, 
-	17, 18, 19, 20, 63, 66, 16, 63, 
-	63, 63, 26, 27, 28, 63, 29, 30, 
-	31, 63, 11, 63, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	16, 17, 18, 19, 20, 63, 63, 16, 
-	63, 63, 63, 26, 27, 28, 63, 29, 
-	30, 31, 63, 11, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 63, 63, 63, 
-	63, 16, 17, 18, 19, 20, 21, 22, 
-	16, 63, 63, 63, 26, 27, 28, 63, 
-	29, 30, 31, 63, 11, 63, 63, 3, 
-	6, 63, 63, 64, 63, 63, 63, 63, 
-	63, 63, 16, 17, 18, 19, 20, 21, 
-	22, 16, 23, 63, 25, 26, 27, 28, 
-	63, 29, 30, 31, 63, 3, 67, 67, 
-	67, 67, 67, 67, 67, 67, 67, 67, 
-	67, 67, 67, 4, 67, 6, 67, 8, 
-	63, 63, 63, 8, 63, 63, 11, 63, 
-	63, 3, 6, 63, 63, 64, 63, 63, 
-	63, 63, 63, 63, 16, 17, 18, 19, 
-	20, 21, 22, 16, 23, 24, 25, 26, 
-	27, 28, 63, 29, 30, 31, 63, 11, 
-	63, 63, 3, 6, 63, 63, 64, 63, 
-	63, 63, 63, 63, 63, 16, 17, 18, 
+	23, 25, 25, 26, 27, 28, 65, 29,
+	30, 31, 65, 65, 65, 65, 33, 65,
+	11, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 16, 17,
+	18, 19, 20, 65, 65, 65, 65, 65,
+	65, 26, 27, 28, 65, 29, 30, 31,
+	65, 65, 65, 65, 17, 65, 11, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 17, 18, 19,
+	20, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 29, 30, 31, 65, 11,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 18,
+	19, 20, 65, 11, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 19, 20, 65, 11,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 20, 65, 11, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 18, 19, 20, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	29, 30, 31, 65, 11, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 18, 19, 20, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 30, 31, 65, 11, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 18, 19, 20,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 31, 65, 11, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 17, 18, 19,
+	20, 65, 65, 65, 65, 65, 65, 26,
+	27, 28, 65, 29, 30, 31, 65, 65,
+	65, 65, 17, 65, 11, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 17, 18, 19, 20, 65,
+	65, 65, 65, 65, 65, 65, 27, 28,
+	65, 29, 30, 31, 65, 65, 65, 65,
+	17, 65, 11, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 17, 18, 19, 20, 65, 65, 65,
+	65, 65, 65, 65, 65, 28, 65, 29,
+	30, 31, 65, 65, 65, 65, 17, 65,
+	11, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 16, 17,
+	18, 19, 20, 65, 22, 16, 65, 65,
+	65, 26, 27, 28, 65, 29, 30, 31,
+	65, 65, 65, 65, 17, 65, 11, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 16, 17, 18, 19,
+	20, 65, 68, 16, 65, 65, 65, 26,
+	27, 28, 65, 29, 30, 31, 65, 65,
+	65, 65, 17, 65, 11, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 16, 17, 18, 19, 20, 65,
+	65, 16, 65, 65, 65, 26, 27, 28,
+	65, 29, 30, 31, 65, 65, 65, 65,
+	17, 65, 11, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	16, 17, 18, 19, 20, 21, 22, 16,
+	65, 65, 65, 26, 27, 28, 65, 29,
+	30, 31, 65, 65, 65, 65, 17, 65,
+	11, 65, 65, 3, 6, 65, 65, 66,
+	65, 65, 65, 65, 65, 65, 16, 17,
+	18, 19, 20, 21, 22, 16, 23, 65,
+	25, 26, 27, 28, 65, 29, 30, 31,
+	65, 65, 65, 65, 33, 65, 3, 65,
+	65, 65, 65, 65, 65, 11, 65, 65,
+	65, 65, 65, 65, 4, 65, 65, 65,
+	65, 65, 65, 65, 17, 18, 19, 20,
+	65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 29, 30, 31, 65, 3, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 4, 69, 6, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 6, 69,
+	8, 65, 65, 65, 8, 65, 65, 11,
+	65, 65, 3, 6, 65, 65, 66, 65,
+	65, 65, 65, 65, 65, 16, 17, 18,
 	19, 20, 21, 22, 16, 23, 24, 25, 
-	26, 27, 28, 63, 29, 30, 31, 63, 
-	69, 68, 68, 68, 68, 68, 68, 68, 
-	68, 68, 68, 68, 68, 68, 68, 68, 
-	68, 68, 68, 68, 69, 70, 68, 69, 
-	70, 68, 70, 68, 8, 67, 67, 67, 
-	8, 67, 0
+	26, 27, 28, 65, 29, 30, 31, 65,
+	65, 65, 65, 33, 65, 11, 65, 65,
+	3, 6, 65, 65, 66, 65, 65, 65,
+	65, 65, 65, 16, 17, 18, 19, 20,
+	21, 22, 16, 23, 24, 25, 26, 27,
+	28, 65, 29, 30, 31, 65, 65, 65,
+	65, 33, 65, 71, 70, 70, 70, 70,
+	70, 70, 70, 70, 70, 70, 70, 70,
+	70, 70, 70, 70, 70, 70, 70, 71,
+	72, 70, 71, 72, 70, 72, 70, 8,
+	69, 69, 69, 8, 69, 0
 };
 
 static const char _use_syllable_machine_trans_targs[] = {
-	4, 8, 4, 31, 2, 4, 1, 5, 
-	6, 4, 28, 4, 49, 50, 51, 53, 
-	33, 34, 35, 36, 37, 44, 45, 47, 
-	52, 48, 41, 42, 43, 38, 39, 40, 
-	56, 4, 4, 4, 4, 7, 0, 27, 
-	11, 12, 13, 14, 15, 22, 23, 25, 
-	26, 19, 20, 21, 16, 17, 18, 10, 
-	4, 9, 24, 4, 29, 30, 4, 4, 
-	3, 32, 46, 4, 4, 54, 55
+	4, 8, 4, 32, 2, 4, 1, 5,
+	6, 4, 29, 4, 51, 52, 53, 55,
+	34, 35, 36, 37, 38, 45, 46, 48,
+	54, 49, 42, 43, 44, 39, 40, 41,
+	58, 50, 4, 4, 4, 4, 7, 0,
+	28, 11, 12, 13, 14, 15, 22, 23,
+	25, 26, 19, 20, 21, 16, 17, 18,
+	27, 10, 4, 9, 24, 4, 30, 31,
+	4, 4, 3, 33, 47, 4, 4, 56,
+	57
 };
 
 static const char _use_syllable_machine_trans_actions[] = {
@@ -264,11 +303,12 @@
 	7, 8, 0, 9, 10, 10, 3, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	3, 3, 0, 0, 0, 0, 0, 0, 
-	0, 11, 12, 13, 14, 7, 0, 7, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	7, 0, 0, 0, 0, 0, 0, 7, 
-	15, 0, 0, 16, 0, 0, 17, 18, 
-	0, 3, 0, 19, 20, 0, 0
+	0, 3, 11, 12, 13, 14, 7, 0,
+	7, 0, 0, 0, 0, 0, 0, 0,
+	0, 7, 0, 0, 0, 0, 0, 0,
+	0, 7, 15, 0, 0, 16, 0, 0,
+	17, 18, 0, 3, 0, 19, 20, 0,
+	0
 };
 
 static const char _use_syllable_machine_to_state_actions[] = {
@@ -279,7 +319,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
 };
 
 static const char _use_syllable_machine_from_state_actions[] = {
@@ -290,18 +330,18 @@
 	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
 };
 
 static const short _use_syllable_machine_eof_trans[] = {
-	1, 3, 3, 6, 0, 34, 36, 36, 
-	57, 57, 36, 36, 36, 36, 36, 36, 
-	36, 36, 36, 36, 36, 36, 36, 36, 
-	36, 36, 36, 36, 60, 63, 60, 64, 
-	64, 64, 64, 64, 64, 64, 64, 64, 
-	64, 64, 64, 64, 64, 64, 64, 64, 
-	64, 68, 68, 64, 64, 69, 69, 69, 
-	68
+	1, 3, 3, 6, 0, 35, 37, 37,
+	59, 59, 37, 37, 37, 37, 37, 37,
+	37, 37, 37, 37, 37, 37, 37, 37,
+	37, 37, 37, 59, 37, 62, 65, 62,
+	66, 66, 66, 66, 66, 66, 66, 66,
+	66, 66, 66, 66, 66, 66, 66, 66,
+	66, 66, 66, 70, 70, 66, 66, 71,
+	71, 71, 70
 };
 
 static const int use_syllable_machine_start = 4;
@@ -315,15 +355,14 @@
 
 
 
-#line 141 "hb-ot-shape-complex-use-machine.rl"
+#line 143 "hb-ot-shape-complex-use-machine.rl"
 
 
 #define found_syllable(syllable_type) \
   HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+    for (unsigned int i = ts; i < te; i++) \
       info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
@@ -331,11 +370,11 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts HB_UNUSED, te, act;
+  unsigned int p, pe, eof, ts, te, act;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 339 "hb-ot-shape-complex-use-machine.hh"
+#line 378 "hb-ot-shape-complex-use-machine.hh"
 	{
 	cs = use_syllable_machine_start;
 	ts = 0;
@@ -343,16 +382,15 @@
 	act = 0;
 	}
 
-#line 162 "hb-ot-shape-complex-use-machine.rl"
+#line 163 "hb-ot-shape-complex-use-machine.rl"
 
 
   p = 0;
   pe = eof = buffer->len;
 
-  unsigned int last = 0;
   unsigned int syllable_serial = 1;
   
-#line 356 "hb-ot-shape-complex-use-machine.hh"
+#line 394 "hb-ot-shape-complex-use-machine.hh"
 	{
 	int _slen;
 	int _trans;
@@ -366,7 +404,7 @@
 #line 1 "NONE"
 	{ts = p;}
 	break;
-#line 370 "hb-ot-shape-complex-use-machine.hh"
+#line 408 "hb-ot-shape-complex-use-machine.hh"
 	}
 
 	_keys = _use_syllable_machine_trans_keys + (cs<<1);
@@ -389,59 +427,59 @@
 	{te = p+1;}
 	break;
 	case 12:
-#line 130 "hb-ot-shape-complex-use-machine.rl"
+#line 132 "hb-ot-shape-complex-use-machine.rl"
 	{te = p+1;{ found_syllable (independent_cluster); }}
 	break;
 	case 14:
-#line 132 "hb-ot-shape-complex-use-machine.rl"
+#line 134 "hb-ot-shape-complex-use-machine.rl"
 	{te = p+1;{ found_syllable (standard_cluster); }}
 	break;
 	case 9:
-#line 136 "hb-ot-shape-complex-use-machine.rl"
+#line 138 "hb-ot-shape-complex-use-machine.rl"
 	{te = p+1;{ found_syllable (broken_cluster); }}
 	break;
 	case 8:
-#line 137 "hb-ot-shape-complex-use-machine.rl"
+#line 139 "hb-ot-shape-complex-use-machine.rl"
 	{te = p+1;{ found_syllable (non_cluster); }}
 	break;
 	case 11:
-#line 130 "hb-ot-shape-complex-use-machine.rl"
+#line 132 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (independent_cluster); }}
 	break;
 	case 15:
-#line 131 "hb-ot-shape-complex-use-machine.rl"
+#line 133 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (virama_terminated_cluster); }}
 	break;
 	case 13:
-#line 132 "hb-ot-shape-complex-use-machine.rl"
+#line 134 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (standard_cluster); }}
 	break;
 	case 17:
-#line 133 "hb-ot-shape-complex-use-machine.rl"
+#line 135 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (number_joiner_terminated_cluster); }}
 	break;
 	case 16:
-#line 134 "hb-ot-shape-complex-use-machine.rl"
+#line 136 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (numeral_cluster); }}
 	break;
 	case 20:
-#line 135 "hb-ot-shape-complex-use-machine.rl"
+#line 137 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (symbol_cluster); }}
 	break;
 	case 18:
-#line 136 "hb-ot-shape-complex-use-machine.rl"
+#line 138 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (broken_cluster); }}
 	break;
 	case 19:
-#line 137 "hb-ot-shape-complex-use-machine.rl"
+#line 139 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (non_cluster); }}
 	break;
 	case 1:
-#line 132 "hb-ot-shape-complex-use-machine.rl"
+#line 134 "hb-ot-shape-complex-use-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (standard_cluster); }}
 	break;
 	case 4:
-#line 136 "hb-ot-shape-complex-use-machine.rl"
+#line 138 "hb-ot-shape-complex-use-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
 	break;
 	case 2:
@@ -459,16 +497,16 @@
 	case 3:
 #line 1 "NONE"
 	{te = p+1;}
-#line 136 "hb-ot-shape-complex-use-machine.rl"
+#line 138 "hb-ot-shape-complex-use-machine.rl"
 	{act = 7;}
 	break;
 	case 10:
 #line 1 "NONE"
 	{te = p+1;}
-#line 137 "hb-ot-shape-complex-use-machine.rl"
+#line 139 "hb-ot-shape-complex-use-machine.rl"
 	{act = 8;}
 	break;
-#line 472 "hb-ot-shape-complex-use-machine.hh"
+#line 510 "hb-ot-shape-complex-use-machine.hh"
 	}
 
 _again:
@@ -477,7 +515,7 @@
 #line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 481 "hb-ot-shape-complex-use-machine.hh"
+#line 519 "hb-ot-shape-complex-use-machine.hh"
 	}
 
 	if ( ++p != pe )
diff --git a/src/hb-ot-shape-complex-use-machine.rl b/src/hb-ot-shape-complex-use-machine.rl
index 7ec8a7f..7702cd9 100644
--- a/src/hb-ot-shape-complex-use-machine.rl
+++ b/src/hb-ot-shape-complex-use-machine.rl
@@ -29,7 +29,7 @@
 #ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 %%{
   machine use_syllable_machine;
@@ -88,36 +88,38 @@
 SMBlw	= 42; # SYM_MOD_BELOW
 CS	= 43; # CONS_WITH_STACKER
 
+HVM	= 44; # HALANT_OR_VOWEL_MODIFIER
+
+h = H | HVM; # https://github.com/harfbuzz/harfbuzz/issues/1102
 
 # Override: Adhoc ZWJ placement. https://github.com/harfbuzz/harfbuzz/issues/542#issuecomment-353169729
-consonant_modifiers = CMAbv* CMBlw* ((ZWJ?.H.ZWJ? B | SUB) VS? CMAbv? CMBlw*)*;
+consonant_modifiers = CMAbv* CMBlw* ((ZWJ?.h.ZWJ? B | SUB) VS? CMAbv? CMBlw*)*;
 # Override: Allow two MBlw. https://github.com/harfbuzz/harfbuzz/issues/376
 medial_consonants = MPre? MAbv? MBlw?.MBlw? MPst?;
 dependent_vowels = VPre* VAbv* VBlw* VPst*;
-vowel_modifiers = VMPre* VMAbv* VMBlw* VMPst*;
+vowel_modifiers = HVM? VMPre* VMAbv* VMBlw* VMPst*;
 final_consonants = FAbv* FBlw* FPst* FM?;
 
+complex_syllable_tail =
+	consonant_modifiers
+	medial_consonants
+	dependent_vowels
+	vowel_modifiers
+	final_consonants
+;
+
 virama_terminated_cluster =
 	(R|CS)? (B | GB) VS?
 	consonant_modifiers
-	ZWJ?.H.ZWJ?
+	ZWJ?.h.ZWJ?
 ;
 standard_cluster =
 	(R|CS)? (B | GB) VS?
-	consonant_modifiers
-	medial_consonants
-	dependent_vowels
-	vowel_modifiers
-	final_consonants
+	complex_syllable_tail
 ;
-
 broken_cluster =
 	R?
-	consonant_modifiers
-	medial_consonants
-	dependent_vowels
-	vowel_modifiers
-	final_consonants
+	complex_syllable_tail
 ;
 
 number_joiner_terminated_cluster = N VS? (HN N VS?)* HN;
@@ -142,10 +144,9 @@
 
 #define found_syllable(syllable_type) \
   HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
-    for (unsigned int i = last; i < p+1; i++) \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+    for (unsigned int i = ts; i < te; i++) \
       info[i].syllable() = (syllable_serial << 4) | syllable_type; \
-    last = p+1; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
@@ -153,7 +154,7 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts HB_UNUSED, te, act;
+  unsigned int p, pe, eof, ts, te, act;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   %%{
@@ -164,7 +165,6 @@
   p = 0;
   pe = eof = buffer->len;
 
-  unsigned int last = 0;
   unsigned int syllable_serial = 1;
   %%{
     write exec;
diff --git a/src/hb-ot-shape-complex-use-table.cc b/src/hb-ot-shape-complex-use-table.cc
index 9c796f0..e9c88ae 100644
--- a/src/hb-ot-shape-complex-use-table.cc
+++ b/src/hb-ot-shape-complex-use-table.cc
@@ -15,7 +15,7 @@
  * UnicodeData.txt does not have a header.
  */
 
-#include "hb-ot-shape-complex-use-private.hh"
+#include "hb-ot-shape-complex-use.hh"
 
 #define B	USE_B	/* BASE */
 #define CGJ	USE_CGJ	/* CGJ */
@@ -24,6 +24,7 @@
 #define GB	USE_GB	/* BASE_OTHER */
 #define H	USE_H	/* HALANT */
 #define HN	USE_HN	/* HALANT_NUM */
+#define HVM	USE_HVM	/* HALANT_OR_VOWEL_MODIFIER */
 #define IND	USE_IND	/* BASE_IND */
 #define N	USE_N	/* BASE_NUM */
 #define O	USE_O	/* OTHER */
@@ -101,7 +102,7 @@
   /* 0990 */     B,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 09A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
   /* 09B0 */     B,     O,     B,     O,     O,     O,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VPre,
-  /* 09C0 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,   IND,     O,
+  /* 09C0 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,     O,     O,  VPst,  VPst,     H,   IND,     O,
   /* 09D0 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     B,     B,     O,     B,
   /* 09E0 */     B,     B,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 09F0 */     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     B,     O,    FM,     O,
@@ -134,7 +135,7 @@
   /* 0B10 */     B,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0B20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
   /* 0B30 */     B,     O,     B,     B,     O,     B,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VAbv,
-  /* 0B40 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,     O,     O,
+  /* 0B40 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPst,     O,     O,  VPst,  VPst,     H,     O,     O,
   /* 0B50 */     O,     O,     O,     O,     O,     O,  VAbv,  VAbv,     O,     O,     O,     O,     B,     B,     O,     B,
   /* 0B60 */     B,     B,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0B70 */     O,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
@@ -145,7 +146,7 @@
   /* 0B90 */     B,     O,     B,     B,     B,     B,     O,     O,     O,     B,     B,     O,     B,     O,     B,     B,
   /* 0BA0 */     O,     O,     O,     B,     B,     O,     O,     O,     B,     B,     B,     O,     O,     O,     B,     B,
   /* 0BB0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,  VPst,  VPst,
-  /* 0BC0 */  VAbv,  VPst,  VPst,     O,     O,     O,  VPre,  VPre,  VPre,     O,  VPre,  VPre,  VPre,     H,     O,     O,
+  /* 0BC0 */  VAbv,  VPst,  VPst,     O,     O,     O,  VPre,  VPre,  VPre,     O,  VPst,  VPst,  VPst,     H,     O,     O,
   /* 0BD0 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 0BE0 */     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0BF0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
@@ -178,7 +179,7 @@
   /* 0D10 */     B,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0D20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0D30 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,     B,  VPst,  VPst,
-  /* 0D40 */  VPst,  VPst,  VPst,  VBlw,  VBlw,     O,  VPre,  VPre,  VPre,     O,  VPre,  VPre,  VPre,     H,     R,     O,
+  /* 0D40 */  VPst,  VPst,  VPst,  VBlw,  VBlw,     O,  VPre,  VPre,  VPre,     O,  VPst,  VPst,  VPst,     H,     R,     O,
   /* 0D50 */     O,     O,     O,     O,   IND,   IND,   IND,  VPst,     O,     O,     O,     O,     O,     O,     O,     B,
   /* 0D60 */     B,     B,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0D70 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,   IND,   IND,   IND,   IND,   IND,   IND,
@@ -190,11 +191,28 @@
   /* 0DA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0DB0 */     B,     B,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     O,     O,
   /* 0DC0 */     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     H,     O,     O,     O,     O,  VPst,
-  /* 0DD0 */  VPst,  VPst,  VAbv,  VAbv,  VBlw,     O,  VBlw,     O,  VPst,  VPre,  VPre,  VPre,  VPre,  VPre,  VPre,  VPst,
+  /* 0DD0 */  VPst,  VPst,  VAbv,  VAbv,  VBlw,     O,  VBlw,     O,  VPst,  VPre,  VPst,  VPre,  VPst,  VPst,  VPst,  VPst,
   /* 0DE0 */     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0DF0 */     O,     O,  VPst,  VPst,     O,     O,     O,     O,
 
-#define use_offset_0x1000u 1360
+#define use_offset_0x0f18u 1360
+
+
+  /* Tibetan */
+                                                                      VBlw,  VBlw,     O,     O,     O,     O,     O,     O,
+  /* 0F20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0F30 */     B,     B,     B,     B,     O,    FM,     O,    FM,     O, CMAbv,     O,     O,     O,     O,  VPst,  VPre,
+  /* 0F40 */     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,     B,
+  /* 0F50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 0F60 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,
+  /* 0F70 */     O,  VBlw,  VBlw,  VAbv,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw, VMAbv, VMPst,
+  /* 0F80 */  VBlw,  VAbv, VMAbv, VMAbv,  VBlw,   IND, VMAbv, VMAbv,     B,     B,     B,     B,     B,   SUB,   SUB,   SUB,
+  /* 0F90 */   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,     O,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,
+  /* 0FA0 */   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,
+  /* 0FB0 */   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,     O,     O,     O,
+  /* 0FC0 */     O,     O,     O,     O,     O,     O,    FM,     O,
+
+#define use_offset_0x1000u 1536
 
 
   /* Myanmar */
@@ -210,7 +228,7 @@
   /* 1080 */     B,     B,  MBlw,  VPst,  VPre,  VAbv,  VAbv, VMPst, VMPst, VMPst, VMPst, VMPst, VMPst, VMBlw,     B, VMPst,
   /* 1090 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B, VMPst, VMPst,  VPst,  VAbv,     O,     O,
 
-#define use_offset_0x1700u 1520
+#define use_offset_0x1700u 1696
 
 
   /* Tagalog */
@@ -238,12 +256,12 @@
   /* 1780 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 1790 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 17A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* 17B0 */     B,     B,     B,     B,     O,     O,  VPst,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VPre,  VPre,
-  /* 17C0 */  VPre,  VPre,  VPre,  VPre,  VPre,  VPre, VMAbv, VMPst,  VPst, VMAbv, VMAbv,    FM,  FAbv, CMAbv,    FM,    FM,
+  /* 17B0 */     B,     B,     B,     B,     O,     O,  VPst,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VPst,  VPst,
+  /* 17C0 */  VPst,  VPre,  VPre,  VPre,  VPst,  VPst, VMAbv, VMPst,  VPst, VMAbv, VMAbv,    FM,  FAbv, CMAbv,    FM,    FM,
   /* 17D0 */    FM,  VAbv,     H,    FM,     O,     O,     O,     O,     O,     O,     O,     O,     B,  VAbv,     O,     O,
   /* 17E0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x1900u 1760
+#define use_offset_0x1900u 1936
 
 
   /* Limbu */
@@ -287,7 +305,7 @@
   /* 1A80 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
   /* 1A90 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x1b00u 2176
+#define use_offset_0x1b00u 2352
 
 
   /* Balinese */
@@ -296,7 +314,7 @@
   /* 1B10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 1B20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 1B30 */     B,     B,     B,     B, CMAbv,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VPre,  VPre,
-  /* 1B40 */  VPre,  VPre,  VAbv,  VAbv,     H,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,
+  /* 1B40 */  VPst,  VPst,  VAbv,  VAbv,     H,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,
   /* 1B50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
   /* 1B60 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv,
   /* 1B70 */ SMAbv, SMAbv, SMAbv, SMAbv,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
@@ -319,11 +337,11 @@
 
   /* 1C00 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 1C10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* 1C20 */     B,     B,     B,     B,   SUB,   SUB,  VPst,  VPre,  VPre,  VPre,  VPst,  VPst,  VBlw,  FAbv,  FAbv,  FAbv,
+  /* 1C20 */     B,     B,     B,     B,   SUB,   SUB,  VPst,  VPre,  VPre,  VPst,  VPst,  VPst,  VBlw,  FAbv,  FAbv,  FAbv,
   /* 1C30 */  FAbv,  FAbv,  FAbv,  FAbv, VMPre, VMPre,    FM, CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 1C40 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     B,     B,     B,
 
-#define use_offset_0x1cd0u 2512
+#define use_offset_0x1cd0u 2688
 
 
   /* Vedic Extensions */
@@ -332,20 +350,20 @@
   /* 1CE0 */ VMAbv, VMPst, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw,     O,     O,     O,     O, VMBlw,     O,     O,
   /* 1CF0 */     O,     O, VMPst, VMPst, VMAbv,    CS,    CS, VMPst, VMAbv, VMAbv,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x1df8u 2560
+#define use_offset_0x1df8u 2736
 
 
   /* Combining Diacritical Marks Supplement */
                                                                          O,     O,     O,    FM,     O,     O,     O,     O,
 
-#define use_offset_0x2008u 2568
+#define use_offset_0x2008u 2744
 
 
   /* General Punctuation */
                                                                          O,     O,     O,     O,  ZWNJ,   ZWJ,     O,     O,
   /* 2010 */    GB,    GB,    GB,    GB,    GB,     O,     O,     O,
 
-#define use_offset_0x2060u 2584
+#define use_offset_0x2060u 2760
 
   /* 2060 */    WJ,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
 
@@ -354,20 +372,20 @@
   /* 2070 */     O,     O,     O,     O,    FM,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 2080 */     O,     O,    FM,    FM,    FM,     O,     O,     O,
 
-#define use_offset_0x20f0u 2624
+#define use_offset_0x20f0u 2800
 
 
   /* Combining Diacritical Marks for Symbols */
 
   /* 20F0 */ VMAbv,     O,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x25c8u 2632
+#define use_offset_0x25c8u 2808
 
 
   /* Geometric Shapes */
                                                                          O,     O,     O,     O,    GB,     O,     O,     O,
 
-#define use_offset_0xa800u 2640
+#define use_offset_0xa800u 2816
 
 
   /* Syloti Nagri */
@@ -454,7 +472,7 @@
   /* AAE0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPre,  VBlw,  VAbv,  VPre,  VPst,
   /* AAF0 */     O,     O,     O,     O,     O, VMPst,     H,     O,
 
-#define use_offset_0xabc0u 3400
+#define use_offset_0xabc0u 3576
 
 
   /* Meetei Mayek */
@@ -464,14 +482,14 @@
   /* ABE0 */     B,     B,     B,  VPst,  VPst,  VAbv,  VPst,  VPst,  VBlw,  VPst,  VPst,     O, VMPst,  VBlw,     O,     O,
   /* ABF0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0xfe00u 3464
+#define use_offset_0xfe00u 3640
 
 
   /* Variation Selectors */
 
   /* FE00 */    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,
 
-#define use_offset_0x10a00u 3480
+#define use_offset_0x10a00u 3656
 
 
   /* Kharoshthi */
@@ -482,7 +500,7 @@
   /* 10A30 */     B,     B,     B,     B,     B,     B,     O,     O, CMAbv, CMBlw, CMBlw,     O,     O,     O,     O,     H,
   /* 10A40 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x11000u 3560
+#define use_offset_0x11000u 3736
 
 
   /* Brahmi */
@@ -491,7 +509,7 @@
   /* 11010 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11020 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11030 */     B,     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw,
-  /* 11040 */  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,     H,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11040 */  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,   HVM,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 11050 */     O,     O,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,     N,
   /* 11060 */     N,     N,     N,     N,     N,     N,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11070 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,    HN,
@@ -503,7 +521,7 @@
   /* 110A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 110B0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VAbv,  VAbv,  VPst,  VPst,     H, CMBlw,     O,     O,     O,     O,     O,
 
-#define use_offset_0x11100u 3752
+#define use_offset_0x11100u 3928
 
 
   /* Chakma */
@@ -511,7 +529,7 @@
   /* 11100 */ VMAbv, VMAbv, VMAbv,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11110 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11120 */     B,     B,     B,     B,     B,     B,     B,  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VPre,  VBlw,  VAbv,  VAbv,
-  /* 11130 */  VBlw,  VAbv,  VAbv,     H, CMAbv,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11130 */  VBlw,  VAbv,  VAbv,     H, CMBlw,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11140 */     O,     O,     O,     O,     B,  VPst,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,     O,
 
   /* Mahajani */
@@ -541,7 +559,7 @@
   /* 11220 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPst,  VPst,  VPst,  VBlw,
   /* 11230 */  VAbv,  VAbv,  VAbv,  VAbv, VMAbv,     H, CMAbv, CMAbv,     O,     O,     O,     O,     O,     O, VMAbv,     O,
 
-#define use_offset_0x11280u 4072
+#define use_offset_0x11280u 4248
 
 
   /* Multani */
@@ -564,12 +582,12 @@
   /* 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,  VPre,  VPre,     H,     O,     O,
+  /* 11340 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,     O,  VPre,  VPre,     O,     O,  VPst,  VPst,     H,     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,
 
-#define use_offset_0x11400u 4320
+#define use_offset_0x11400u 4496
 
 
   /* Newa */
@@ -588,11 +606,11 @@
   /* 11480 */     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11490 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 114A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* 114B0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VPre,  VAbv,  VPre,  VPre,  VPst,  VPre, VMAbv,
+  /* 114B0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VPre,  VAbv,  VPst,  VPst,  VPst,  VPst, VMAbv,
   /* 114C0 */ VMAbv, VMPst,     H, CMBlw,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 114D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x11580u 4544
+#define use_offset_0x11580u 4720
 
 
   /* Siddham */
@@ -600,7 +618,7 @@
   /* 11580 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11590 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 115A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPst,
-  /* 115B0 */  VPre,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,  VPre,  VPre, VMAbv, VMAbv, VMPst,     H,
+  /* 115B0 */  VPre,  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPst,  VPst,  VPst, VMAbv, VMAbv, VMPst,     H,
   /* 115C0 */ CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 115D0 */     O,     O,     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,  VBlw,  VBlw,     O,     O,
   /* 115E0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
@@ -635,7 +653,7 @@
   /* 11720 */  VPst,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VAbv,  VBlw,  VAbv,  VAbv,  VAbv,     O,     O,     O,     O,
   /* 11730 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,
 
-#define use_offset_0x11800u 4992
+#define use_offset_0x11800u 5168
 
 
   /* Dogra */
@@ -645,7 +663,7 @@
   /* 11820 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPst,  VPre,  VPst,  VBlw,
   /* 11830 */  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv, VMAbv, VMPst,     H, CMBlw,     O,     O,     O,     O,     O,
 
-#define use_offset_0x11a00u 5056
+#define use_offset_0x11a00u 5232
 
 
   /* Zanabazar Square */
@@ -664,7 +682,7 @@
   /* 11A80 */     B,     B,     B,     B,     O,     O,     R,     R,     R,     R,  FBlw,  FBlw,  FBlw,  FBlw,  FBlw,  FBlw,
   /* 11A90 */  FBlw,  FBlw,  FBlw,  FBlw,  FBlw,  FBlw, VMAbv, VMPst, CMAbv,     H,     O,     O,     O,     B,     O,     O,
 
-#define use_offset_0x11c00u 5216
+#define use_offset_0x11c00u 5392
 
 
   /* Bhaiksuki */
@@ -685,7 +703,7 @@
   /* 11CA0 */   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,     O,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,
   /* 11CB0 */  VBlw,  VPre,  VBlw,  VAbv,  VPst, VMAbv, VMAbv,     O,
 
-#define use_offset_0x11d00u 5400
+#define use_offset_0x11d00u 5576
 
 
   /* Masaram Gondi */
@@ -705,7 +723,7 @@
   /* 11D90 */  VAbv,  VAbv,     O,  VPst,  VPst, VMAbv, VMPst,     H,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 11DA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x11ee0u 5576
+#define use_offset_0x11ee0u 5752
 
 
   /* Makasar */
@@ -713,7 +731,7 @@
   /* 11EE0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11EF0 */     B,     B,    GB,  VAbv,  VBlw,  VPre,  VPst,     O,
 
-}; /* Table items: 5600; occupancy: 73% */
+}; /* Table items: 5776; occupancy: 74% */
 
 USE_TABLE_ELEMENT_TYPE
 hb_use_get_category (hb_codepoint_t u)
@@ -725,6 +743,7 @@
       if (hb_in_range<hb_codepoint_t> (u, 0x00A0u, 0x00D7u)) return use_table[u - 0x00A0u + use_offset_0x00a0u];
       if (hb_in_range<hb_codepoint_t> (u, 0x0348u, 0x034Fu)) return use_table[u - 0x0348u + use_offset_0x0348u];
       if (hb_in_range<hb_codepoint_t> (u, 0x0900u, 0x0DF7u)) return use_table[u - 0x0900u + use_offset_0x0900u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x0F18u, 0x0FC7u)) return use_table[u - 0x0F18u + use_offset_0x0f18u];
       break;
 
     case 0x1u:
@@ -782,6 +801,7 @@
 #undef GB
 #undef H
 #undef HN
+#undef HVM
 #undef IND
 #undef N
 #undef O
diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc
index f8e86c9..f9a580c 100644
--- a/src/hb-ot-shape-complex-use.cc
+++ b/src/hb-ot-shape-complex-use.cc
@@ -26,8 +26,8 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-use-private.hh"
-#include "hb-ot-shape-complex-arabic-private.hh"
+#include "hb-ot-shape-complex-use.hh"
+#include "hb-ot-shape-complex-arabic.hh"
 
 /* buffer var allocations */
 #define use_category() complex_var_u8_0()
@@ -86,7 +86,14 @@
   HB_TAG('h','a','l','n'),
   HB_TAG('p','r','e','s'),
   HB_TAG('p','s','t','s'),
-  /* Positioning features, though we don't care about the types. */
+};
+static const hb_tag_t
+positioning_features[] =
+{
+  /*
+   * Positioning features.
+   * We don't care about the types.
+   */
   HB_TAG('d','i','s','t'),
   HB_TAG('a','b','v','m'),
   HB_TAG('b','l','w','m'),
@@ -122,33 +129,37 @@
   map->add_gsub_pause (setup_syllables);
 
   /* "Default glyph pre-processing group" */
-  map->add_global_bool_feature (HB_TAG('l','o','c','l'));
-  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
-  map->add_global_bool_feature (HB_TAG('n','u','k','t'));
-  map->add_global_bool_feature (HB_TAG('a','k','h','n'));
+  map->enable_feature (HB_TAG('l','o','c','l'));
+  map->enable_feature (HB_TAG('c','c','m','p'));
+  map->enable_feature (HB_TAG('n','u','k','t'));
+  map->enable_feature (HB_TAG('a','k','h','n'), F_MANUAL_ZWJ);
 
   /* "Reordering group" */
   map->add_gsub_pause (clear_substitution_flags);
-  map->add_feature (HB_TAG('r','p','h','f'), 1, F_MANUAL_ZWJ);
+  map->add_feature (HB_TAG('r','p','h','f'), F_MANUAL_ZWJ);
   map->add_gsub_pause (record_rphf);
   map->add_gsub_pause (clear_substitution_flags);
-  map->add_feature (HB_TAG('p','r','e','f'), 1, F_GLOBAL | F_MANUAL_ZWJ);
+  map->enable_feature (HB_TAG('p','r','e','f'), F_MANUAL_ZWJ);
   map->add_gsub_pause (record_pref);
 
   /* "Orthographic unit shaping group" */
   for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
-    map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+    map->enable_feature (basic_features[i], F_MANUAL_ZWJ);
 
   map->add_gsub_pause (reorder);
 
   /* "Topographical features" */
   for (unsigned int i = 0; i < ARRAY_LENGTH (arabic_features); i++)
-    map->add_feature (arabic_features[i], 1, F_NONE);
+    map->add_feature (arabic_features[i]);
   map->add_gsub_pause (nullptr);
 
-  /* "Standard typographic presentation" and "Positional feature application" */
+  /* "Standard typographic presentation" */
   for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
-    map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+    map->enable_feature (other_features[i], F_MANUAL_ZWJ);
+
+  /* "Positional feature application" */
+  for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++)
+    map->enable_feature (positioning_features[i]);
 }
 
 struct use_shape_plan_t
@@ -586,7 +597,7 @@
   nullptr, /* decompose */
   compose_use,
   setup_masks_use,
-  nullptr, /* disable_otl */
+  HB_TAG_NONE, /* gpos_tag */
   nullptr, /* reorder_marks */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
   false, /* fallback_position */
diff --git a/src/hb-ot-shape-complex-use-private.hh b/src/hb-ot-shape-complex-use.hh
similarity index 90%
rename from src/hb-ot-shape-complex-use-private.hh
rename to src/hb-ot-shape-complex-use.hh
index b4bda8b..ab56e1b 100644
--- a/src/hb-ot-shape-complex-use-private.hh
+++ b/src/hb-ot-shape-complex-use.hh
@@ -26,13 +26,13 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH
-#define HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH
+#ifndef HB_OT_SHAPE_COMPLEX_USE_HH
+#define HB_OT_SHAPE_COMPLEX_USE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
-#include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-complex.hh"
 
 
 #define USE_TABLE_ELEMENT_TYPE uint8_t
@@ -88,10 +88,13 @@
   USE_VMPre	= 23,	/* VOWEL_MOD_PRE */
   USE_SMAbv	= 41,	/* SYM_MOD_ABOVE */
   USE_SMBlw	= 42,	/* SYM_MOD_BELOW */
-  USE_CS	= 43	/* CONS_WITH_STACKER */
+  USE_CS	= 43,	/* CONS_WITH_STACKER */
+
+  /* https://github.com/harfbuzz/harfbuzz/issues/1102 */
+  USE_HVM	= 44,	/* HALANT_OR_VOWEL_MODIFIER */
 };
 
 HB_INTERNAL USE_TABLE_ELEMENT_TYPE
 hb_use_get_category (hb_codepoint_t u);
 
-#endif /* HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH */
+#endif /* HB_OT_SHAPE_COMPLEX_USE_HH */
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex.hh
similarity index 90%
rename from src/hb-ot-shape-complex-private.hh
rename to src/hb-ot-shape-complex.hh
index 37a4d91..2944d74 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex.hh
@@ -24,14 +24,14 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_PRIVATE_HH
-#define HB_OT_SHAPE_COMPLEX_PRIVATE_HH
+#ifndef HB_OT_SHAPE_COMPLEX_HH
+#define HB_OT_SHAPE_COMPLEX_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-ot-shape-private.hh"
-#include "hb-ot-shape-normalize-private.hh"
-
+#include "hb-ot-layout.hh"
+#include "hb-ot-shape.hh"
+#include "hb-ot-shape-normalize.hh"
 
 
 /* buffer var allocations, used by complex shapers */
@@ -58,8 +58,8 @@
   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 (tibetan) \
   HB_COMPLEX_SHAPER_IMPLEMENT (use) \
   /* ^--- Add new shapers here */
 
@@ -69,7 +69,7 @@
   /* collect_features()
    * Called during shape_plan().
    * Shapers should use plan->map to add their features and callbacks.
-   * May be nullptr.
+   * May be NULL.
    */
   void (*collect_features) (hb_ot_shape_planner_t *plan);
 
@@ -77,7 +77,7 @@
    * Called during shape_plan().
    * Shapers should use plan->map to override features and add callbacks after
    * common features are added.
-   * May be nullptr.
+   * May be NULL.
    */
   void (*override_features) (hb_ot_shape_planner_t *plan);
 
@@ -93,7 +93,7 @@
    * Called when the shape_plan is being destroyed.
    * plan->data is passed here for destruction.
    * If nullptr is returned, means a plan failure.
-   * May be nullptr.
+   * May be NULL.
    */
   void (*data_destroy) (void *data);
 
@@ -101,7 +101,7 @@
   /* preprocess_text()
    * Called during shape().
    * Shapers can use to modify text before shaping starts.
-   * May be nullptr.
+   * May be NULL.
    */
   void (*preprocess_text) (const hb_ot_shape_plan_t *plan,
 			   hb_buffer_t              *buffer,
@@ -110,7 +110,7 @@
   /* postprocess_glyphs()
    * Called during shape().
    * Shapers can use to modify glyphs after shaping ends.
-   * May be nullptr.
+   * May be NULL.
    */
   void (*postprocess_glyphs) (const hb_ot_shape_plan_t *plan,
 			      hb_buffer_t              *buffer,
@@ -121,7 +121,7 @@
 
   /* decompose()
    * Called during shape()'s normalization.
-   * May be nullptr.
+   * May be NULL.
    */
   bool (*decompose) (const hb_ot_shape_normalize_context_t *c,
 		     hb_codepoint_t  ab,
@@ -130,7 +130,7 @@
 
   /* compose()
    * Called during shape()'s normalization.
-   * May be nullptr.
+   * May be NULL.
    */
   bool (*compose) (const hb_ot_shape_normalize_context_t *c,
 		   hb_codepoint_t  a,
@@ -141,24 +141,22 @@
    * Called during shape().
    * Shapers should use map to get feature masks and set on buffer.
    * Shapers may NOT modify characters.
-   * May be nullptr.
+   * May be NULL.
    */
   void (*setup_masks) (const hb_ot_shape_plan_t *plan,
 		       hb_buffer_t              *buffer,
 		       hb_font_t                *font);
 
-  /* disable_otl()
-   * Called during shape().
-   * If set and returns true, GDEF/GSUB/GPOS of the font are ignored
-   * and fallback operations used.
-   * May be nullptr.
+  /* gpos_tag()
+   * If not HB_TAG_NONE, then must match found GPOS script tag for
+   * GPOS to be applied.  Otherwise, fallback positioning will be used.
    */
-  bool (*disable_otl) (const hb_ot_shape_plan_t *plan);
+  hb_tag_t gpos_tag;
 
   /* reorder_marks()
    * Called during shape().
    * Shapers can use to modify ordering of combining marks.
-   * May be nullptr.
+   * May be NULL.
    */
   void (*reorder_marks) (const hb_ot_shape_plan_t *plan,
 			 hb_buffer_t              *buffer,
@@ -234,25 +232,12 @@
       return &_hb_ot_complex_shaper_hangul;
 
 
-    /* Unicode-2.0 additions */
-    case HB_SCRIPT_TIBETAN:
-
-      return &_hb_ot_complex_shaper_tibetan;
-
-
     /* Unicode-1.1 additions */
     case HB_SCRIPT_HEBREW:
 
       return &_hb_ot_complex_shaper_hebrew;
 
 
-    /* ^--- Add new shapers here */
-
-#if 0
-    /* Unicode-4.1 additions */
-    case HB_SCRIPT_NEW_TAI_LUE:
-#endif
-
     /* Unicode-1.1 additions */
     case HB_SCRIPT_BENGALI:
     case HB_SCRIPT_DEVANAGARI:
@@ -270,11 +255,13 @@
       /* 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.
-       * Note that for some simple scripts, there may not be *any*
-       * GSUB/GPOS needed, so there may be no scripts found! */
+       *
+       * If it's indy3 tag, send to USE. */
       if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') ||
 	  planner->map.chosen_script[0] == HB_TAG ('l','a','t','n'))
 	return &_hb_ot_complex_shaper_default;
+      else if ((planner->map.chosen_script[0] & 0x000000FF) == '3')
+	return &_hb_ot_complex_shaper_use;
       else
 	return &_hb_ot_complex_shaper_indic;
 
@@ -291,7 +278,7 @@
 
 
     /* Unicode-2.0 additions */
-    //case HB_SCRIPT_TIBETAN:
+    case HB_SCRIPT_TIBETAN:
 
     /* Unicode-3.0 additions */
     //case HB_SCRIPT_MONGOLIAN:
@@ -359,9 +346,9 @@
 
     /* Unicode-8.0 additions */
     case HB_SCRIPT_AHOM:
-    //case HB_SCRIPT_MULTANI:
 
     /* Unicode-9.0 additions */
+    //case HB_SCRIPT_ADLAM:
     case HB_SCRIPT_BHAIKSUKI:
     case HB_SCRIPT_MARCHEN:
     case HB_SCRIPT_NEWA:
@@ -374,7 +361,9 @@
     /* Unicode-11.0 additions */
     case HB_SCRIPT_DOGRA:
     case HB_SCRIPT_GUNJALA_GONDI:
+    //case HB_SCRIPT_HANIFI_ROHINGYA:
     case HB_SCRIPT_MAKASAR:
+    //case HB_SCRIPT_SOGDIAN:
 
       /* If the designer designed the font for the 'DFLT' script,
        * (or we ended up arbitrarily pick 'latn'), use the default shaper.
@@ -386,8 +375,12 @@
 	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;
   }
 }
 
 
-#endif /* HB_OT_SHAPE_COMPLEX_PRIVATE_HH */
+#endif /* HB_OT_SHAPE_COMPLEX_HH */
diff --git a/src/hb-ot-shape-fallback.cc b/src/hb-ot-shape-fallback.cc
index fbf31ab..4c5ccc9 100644
--- a/src/hb-ot-shape-fallback.cc
+++ b/src/hb-ot-shape-fallback.cc
@@ -24,8 +24,8 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-fallback-private.hh"
-#include "hb-ot-layout-gsubgpos-private.hh"
+#include "hb-ot-shape-fallback.hh"
+#include "hb-ot-kern-table.hh"
 
 static unsigned int
 recategorize_combining_class (hb_codepoint_t u,
@@ -162,9 +162,9 @@
 }
 
 void
-_hb_ot_shape_fallback_position_recategorize_marks (const hb_ot_shape_plan_t *plan HB_UNUSED,
-						   hb_font_t *font HB_UNUSED,
-						   hb_buffer_t  *buffer)
+_hb_ot_shape_fallback_mark_position_recategorize_marks (const hb_ot_shape_plan_t *plan HB_UNUSED,
+						        hb_font_t *font HB_UNUSED,
+						        hb_buffer_t  *buffer)
 {
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
@@ -417,9 +417,9 @@
 }
 
 void
-_hb_ot_shape_fallback_position (const hb_ot_shape_plan_t *plan,
-				hb_font_t *font,
-				hb_buffer_t  *buffer)
+_hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan,
+				     hb_font_t *font,
+				     hb_buffer_t  *buffer)
 {
   _hb_buffer_assert_gsubgpos_vars (buffer);
 
@@ -435,60 +435,38 @@
 }
 
 
-/* Performs old-style TrueType kerning. */
+struct hb_ot_shape_fallback_kern_driver_t
+{
+  hb_ot_shape_fallback_kern_driver_t (hb_font_t   *font_,
+				      hb_buffer_t *buffer) :
+    font (font_), direction (buffer->props.direction) {}
+
+  hb_position_t get_kerning (hb_codepoint_t first, hb_codepoint_t second) const
+  {
+    hb_position_t kern = 0;
+    font->get_glyph_kerning_for_direction (first, second,
+					   direction,
+					   &kern, &kern);
+    return kern;
+  }
+
+  hb_font_t *font;
+  hb_direction_t direction;
+};
+
+/* Performs font-assisted kerning. */
 void
 _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
 			    hb_font_t *font,
-			    hb_buffer_t  *buffer)
+			    hb_buffer_t *buffer)
 {
-  if (!plan->has_kern) return;
-
-  OT::hb_ot_apply_context_t c (1, font, buffer);
-  c.set_lookup_mask (plan->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);
-
-  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;)
-  {
-    skippy_iter.reset (idx, 1);
-    if (!skippy_iter.next ())
-    {
-      idx++;
-      continue;
-    }
-
-    hb_position_t x_kern, y_kern;
-    font->get_glyph_kerning_for_direction (info[idx].codepoint,
-					   info[skippy_iter.idx].codepoint,
-					   buffer->props.direction,
-					   &x_kern, &y_kern);
-
-    if (x_kern)
-    {
-      hb_position_t kern1 = x_kern >> 1;
-      hb_position_t kern2 = x_kern - kern1;
-      pos[idx].x_advance += kern1;
-      pos[skippy_iter.idx].x_advance += kern2;
-      pos[skippy_iter.idx].x_offset += kern2;
-      buffer->unsafe_to_break (idx, skippy_iter.idx + 1);
-    }
-
-    if (y_kern)
-    {
-      hb_position_t kern1 = y_kern >> 1;
-      hb_position_t kern2 = y_kern - kern1;
-      pos[idx].y_advance += kern1;
-      pos[skippy_iter.idx].y_advance += kern2;
-      pos[skippy_iter.idx].y_offset += kern2;
-      buffer->unsafe_to_break (idx, skippy_iter.idx + 1);
-    }
-
-    idx = skippy_iter.idx;
-  }
+  if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction) ?
+      !font->has_glyph_h_kerning_func () :
+      !font->has_glyph_v_kerning_func ())
+    return;
+  hb_ot_shape_fallback_kern_driver_t driver (font, buffer);
+  hb_kern_machine_t<hb_ot_shape_fallback_kern_driver_t> machine (driver);
+  machine.kern (font, buffer, plan->kern_mask, false);
 }
 
 
diff --git a/src/hb-ot-shape-fallback-private.hh b/src/hb-ot-shape-fallback.hh
similarity index 74%
rename from src/hb-ot-shape-fallback-private.hh
rename to src/hb-ot-shape-fallback.hh
index e134224..12f18ed 100644
--- a/src/hb-ot-shape-fallback-private.hh
+++ b/src/hb-ot-shape-fallback.hh
@@ -24,21 +24,21 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_FALLBACK_PRIVATE_HH
-#define HB_OT_SHAPE_FALLBACK_PRIVATE_HH
+#ifndef HB_OT_SHAPE_FALLBACK_HH
+#define HB_OT_SHAPE_FALLBACK_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-ot-shape-private.hh"
+#include "hb-ot-shape.hh"
 
 
-HB_INTERNAL void _hb_ot_shape_fallback_position (const hb_ot_shape_plan_t *plan,
-						 hb_font_t *font,
-						 hb_buffer_t  *buffer);
+HB_INTERNAL void _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan,
+						      hb_font_t *font,
+						      hb_buffer_t  *buffer);
 
-HB_INTERNAL void _hb_ot_shape_fallback_position_recategorize_marks (const hb_ot_shape_plan_t *plan,
-								    hb_font_t *font,
-								    hb_buffer_t  *buffer);
+HB_INTERNAL void _hb_ot_shape_fallback_mark_position_recategorize_marks (const hb_ot_shape_plan_t *plan,
+									 hb_font_t *font,
+									 hb_buffer_t  *buffer);
 
 
 HB_INTERNAL void _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
@@ -50,4 +50,4 @@
 					       hb_buffer_t  *buffer);
 
 
-#endif /* HB_OT_SHAPE_FALLBACK_PRIVATE_HH */
+#endif /* HB_OT_SHAPE_FALLBACK_HH */
diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index 358450e..a8229a9 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -24,9 +24,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-normalize-private.hh"
-#include "hb-ot-shape-complex-private.hh"
-#include "hb-ot-shape-private.hh"
+#include "hb-ot-shape-normalize.hh"
+#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shape.hh"
 
 
 /*
@@ -264,15 +264,6 @@
     decompose_current_character (c, short_circuit);
 }
 
-static inline void
-decompose_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool might_short_circuit, bool always_short_circuit)
-{
-  if (likely (c->buffer->idx + 1 == end))
-    decompose_current_character (c, might_short_circuit);
-  else
-    decompose_multi_char_cluster (c, end, always_short_circuit);
-}
-
 
 static int
 compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
@@ -294,6 +285,16 @@
   _hb_buffer_assert_unicode_vars (buffer);
 
   hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference;
+  if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_AUTO)
+  {
+    if (plan->has_gpos_mark)
+      // https://github.com/harfbuzz/harfbuzz/issues/653#issuecomment-423905920
+      //mode = HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED;
+      mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
+    else
+      mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
+  }
+
   const hb_ot_shape_normalize_context_t c = {
     plan,
     buffer,
@@ -318,105 +319,81 @@
 
   /* First round, decompose */
 
-  buffer->clear_output ();
-  count = buffer->len;
-  for (buffer->idx = 0; buffer->idx < count && buffer->successful;)
+  bool all_simple = true;
   {
-    unsigned int end;
-    for (end = buffer->idx + 1; end < count; end++)
-      if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end]))))
-        break;
+    buffer->clear_output ();
+    count = buffer->len;
+    buffer->idx = 0;
+    do
+    {
+      unsigned int end;
+      for (end = buffer->idx + 1; end < count; end++)
+	if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end]))))
+	  break;
 
-    decompose_cluster (&c, end, might_short_circuit, always_short_circuit);
+      if (end < count)
+	end--; /* Leave one base for the marks to cluster with. */
+
+      /* From idx to end are simple clusters. */
+      if (might_short_circuit)
+      {
+        unsigned int done = font->get_nominal_glyphs (end - buffer->idx,
+						      &buffer->cur().codepoint,
+						      sizeof (buffer->info[0]),
+						      &buffer->cur().glyph_index(),
+						      sizeof (buffer->info[0]));
+	buffer->next_glyphs (done);
+      }
+      while (buffer->idx < end && buffer->successful)
+	decompose_current_character (&c, might_short_circuit);
+
+      if (buffer->idx == count || !buffer->successful)
+	break;
+
+      all_simple = false;
+
+      /* Find all the marks now. */
+      for (end = buffer->idx + 1; end < count; end++)
+	if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end])))
+	  break;
+
+      /* idx to end is one non-simple cluster. */
+      decompose_multi_char_cluster (&c, end, always_short_circuit);
+    }
+    while (buffer->idx < count && buffer->successful);
+    buffer->swap_buffers ();
   }
-  buffer->swap_buffers ();
 
 
   /* Second round, reorder (inplace) */
 
-  count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
+  if (!all_simple)
   {
-    if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0)
-      continue;
-
-    unsigned int end;
-    for (end = i + 1; end < count; end++)
-      if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0)
-        break;
-
-    /* We are going to do a O(n^2).  Only do this if the sequence is short. */
-    if (end - i > HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS) {
-      i = end;
-      continue;
-    }
-
-    buffer->sort (i, end, compare_combining_class);
-
-    if (plan->shaper->reorder_marks)
-      plan->shaper->reorder_marks (plan, buffer, i, end);
-
-    i = end;
-  }
-
-
-  if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE ||
-      mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED)
-    return;
-
-  /* Third round, recompose */
-
-  /* As noted in the comment earlier, we don't try to combine
-   * ccc=0 chars with their previous Starter. */
-
-  buffer->clear_output ();
-  count = buffer->len;
-  unsigned int starter = 0;
-  buffer->next_glyph ();
-  while (buffer->idx < count && buffer->successful)
-  {
-    hb_codepoint_t composed, glyph;
-    if (/* We don't try to compose a non-mark character with it's preceding starter.
-	 * This is both an optimization to avoid trying to compose every two neighboring
-	 * glyphs in most scripts AND a desired feature for Hangul.  Apparently Hangul
-	 * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */
-	HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur())))
+    count = buffer->len;
+    for (unsigned int i = 0; i < count; i++)
     {
-      if (/* If there's anything between the starter and this char, they should have CCC
-	   * smaller than this character's. */
-	  (starter == buffer->out_len - 1 ||
-	   info_cc (buffer->prev()) < info_cc (buffer->cur())) &&
-	  /* And compose. */
-	  c.compose (&c,
-		     buffer->out_info[starter].codepoint,
-		     buffer->cur().codepoint,
-		     &composed) &&
-	  /* And the font has glyph for the composite. */
-	  font->get_nominal_glyph (composed, &glyph))
-      {
-	/* Composes. */
-	buffer->next_glyph (); /* Copy to out-buffer. */
-	if (unlikely (!buffer->successful))
-	  return;
-	buffer->merge_out_clusters (starter, buffer->out_len);
-	buffer->out_len--; /* Remove the second composable. */
-	/* Modify starter and carry on. */
-	buffer->out_info[starter].codepoint = composed;
-	buffer->out_info[starter].glyph_index() = glyph;
-	_hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer);
+      if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0)
+	continue;
 
+      unsigned int end;
+      for (end = i + 1; end < count; end++)
+	if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0)
+	  break;
+
+      /* We are going to do a O(n^2).  Only do this if the sequence is short. */
+      if (end - i > HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS) {
+	i = end;
 	continue;
       }
+
+      buffer->sort (i, end, compare_combining_class);
+
+      if (plan->shaper->reorder_marks)
+	plan->shaper->reorder_marks (plan, buffer, i, end);
+
+      i = end;
     }
-
-    /* Blocked, or doesn't compose. */
-    buffer->next_glyph ();
-
-    if (info_cc (buffer->prev()) == 0)
-      starter = buffer->out_len - 1;
   }
-  buffer->swap_buffers ();
-
   if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CGJ)
   {
     /* For all CGJ, check if it prevented any reordering at all.
@@ -430,4 +407,63 @@
 	_hb_glyph_info_unhide (&buffer->info[i]);
       }
   }
+
+
+  /* Third round, recompose */
+
+  if (!all_simple &&
+      (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS ||
+       mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT))
+  {
+    /* As noted in the comment earlier, we don't try to combine
+     * ccc=0 chars with their previous Starter. */
+
+    buffer->clear_output ();
+    count = buffer->len;
+    unsigned int starter = 0;
+    buffer->next_glyph ();
+    while (buffer->idx < count && buffer->successful)
+    {
+      hb_codepoint_t composed, glyph;
+      if (/* We don't try to compose a non-mark character with it's preceding starter.
+	   * This is both an optimization to avoid trying to compose every two neighboring
+	   * glyphs in most scripts AND a desired feature for Hangul.  Apparently Hangul
+	   * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */
+	  HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur())))
+      {
+	if (/* If there's anything between the starter and this char, they should have CCC
+	     * smaller than this character's. */
+	    (starter == buffer->out_len - 1 ||
+	     info_cc (buffer->prev()) < info_cc (buffer->cur())) &&
+	    /* And compose. */
+	    c.compose (&c,
+		       buffer->out_info[starter].codepoint,
+		       buffer->cur().codepoint,
+		       &composed) &&
+	    /* And the font has glyph for the composite. */
+	    font->get_nominal_glyph (composed, &glyph))
+	{
+	  /* Composes. */
+	  buffer->next_glyph (); /* Copy to out-buffer. */
+	  if (unlikely (!buffer->successful))
+	    return;
+	  buffer->merge_out_clusters (starter, buffer->out_len);
+	  buffer->out_len--; /* Remove the second composable. */
+	  /* Modify starter and carry on. */
+	  buffer->out_info[starter].codepoint = composed;
+	  buffer->out_info[starter].glyph_index() = glyph;
+	  _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer);
+
+	  continue;
+	}
+      }
+
+      /* Blocked, or doesn't compose. */
+      buffer->next_glyph ();
+
+      if (info_cc (buffer->prev()) == 0)
+	starter = buffer->out_len - 1;
+    }
+    buffer->swap_buffers ();
+  }
 }
diff --git a/src/hb-ot-shape-normalize-private.hh b/src/hb-ot-shape-normalize.hh
similarity index 85%
rename from src/hb-ot-shape-normalize-private.hh
rename to src/hb-ot-shape-normalize.hh
index c744e26..04f1a80 100644
--- a/src/hb-ot-shape-normalize-private.hh
+++ b/src/hb-ot-shape-normalize.hh
@@ -24,10 +24,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_NORMALIZE_PRIVATE_HH
-#define HB_OT_SHAPE_NORMALIZE_PRIVATE_HH
+#ifndef HB_OT_SHAPE_NORMALIZE_HH
+#define HB_OT_SHAPE_NORMALIZE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 /* buffer var allocations, used during the normalization process */
@@ -38,10 +38,11 @@
 enum hb_ot_shape_normalization_mode_t {
   HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
   HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED,
-  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* never composes base-to-base */
-  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* always fully decomposes and then recompose back */
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* Never composes base-to-base */
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* Always fully decomposes and then recompose back */
 
-  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS
+  HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, /* See hb-ot-shape-normalize.cc for logic. */
+  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_AUTO
 };
 
 HB_INTERNAL void _hb_ot_shape_normalize (const hb_ot_shape_plan_t *shaper,
@@ -66,4 +67,4 @@
 };
 
 
-#endif /* HB_OT_SHAPE_NORMALIZE_PRIVATE_HH */
+#endif /* HB_OT_SHAPE_NORMALIZE_HH */
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 71632b5..a553887 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -27,42 +27,121 @@
  */
 
 #define HB_SHAPER ot
-#define hb_ot_face_data_t hb_ot_layout_t
 #define hb_ot_shape_plan_data_t hb_ot_shape_plan_t
-#include "hb-shaper-impl-private.hh"
+#include "hb-shaper-impl.hh"
 
-#include "hb-ot-shape-private.hh"
-#include "hb-ot-shape-complex-private.hh"
-#include "hb-ot-shape-fallback-private.hh"
-#include "hb-ot-shape-normalize-private.hh"
+#include "hb-ot-shape.hh"
+#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shape-fallback.hh"
+#include "hb-ot-shape-normalize.hh"
 
-#include "hb-ot-layout-private.hh"
-#include "hb-unicode-private.hh"
-#include "hb-set-private.hh"
+#include "hb-ot-face.hh"
 
-#include "hb-ot-layout-gsubgpos-private.hh"
-#include "hb-aat-layout-private.hh"
+#include "hb-set.hh"
 
-static hb_tag_t common_features[] = {
-  HB_TAG('c','c','m','p'),
-  HB_TAG('l','o','c','l'),
-  HB_TAG('m','a','r','k'),
-  HB_TAG('m','k','m','k'),
-  HB_TAG('r','l','i','g'),
+#include "hb-aat-layout.hh"
+
+
+static bool
+_hb_apply_morx (hb_face_t *face)
+{
+  if (hb_options ().aat &&
+      hb_aat_layout_has_substitution (face))
+    return true;
+
+  return !hb_ot_layout_has_substitution (face) &&
+	 hb_aat_layout_has_substitution (face);
+}
+
+void
+hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
+				const int          *coords,
+				unsigned int        num_coords)
+{
+  plan.props = props;
+  plan.shaper = shaper;
+  map.compile (plan.map, coords, num_coords);
+
+  plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m'));
+  plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c'));
+  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) ?
+		      HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n');
+  plan.kern_mask = plan.map.get_mask (kern_tag);
+
+  plan.requested_kerning = !!plan.kern_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];
+
+  /*
+   * Decide who provides glyph classes. GDEF or Unicode.
+   */
+
+  if (!hb_ot_layout_has_glyph_classes (face))
+    plan.fallback_glyph_classes = true;
+
+  /*
+   * Decide who does substitutions. GSUB, morx, or fallback.
+   */
+
+  plan.apply_morx = _hb_apply_morx (face);
+
+  /*
+   * Decide who does positioning. GPOS, kerx, kern, or fallback.
+   */
+
+  if (hb_options ().aat && hb_aat_layout_has_positioning (face))
+    plan.apply_kerx = true;
+  else if (!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)
+  {
+    /* 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))
+      plan.apply_kern = true;
+    else
+      plan.fallback_kerning = true;
+  }
+
+  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;
+
+  /* Currently we always apply trak. */
+  plan.apply_trak = hb_aat_layout_has_tracking (face);
+}
+
+
+static const hb_ot_map_feature_t
+common_features[] =
+{
+  {HB_TAG('c','c','m','p'), F_GLOBAL},
+  {HB_TAG('l','o','c','l'), F_GLOBAL},
+  {HB_TAG('m','a','r','k'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('m','k','m','k'), F_GLOBAL_MANUAL_JOINERS},
+  {HB_TAG('r','l','i','g'), F_GLOBAL},
 };
 
 
-static hb_tag_t horizontal_features[] = {
-  HB_TAG('c','a','l','t'),
-  HB_TAG('c','l','i','g'),
-  HB_TAG('c','u','r','s'),
-  HB_TAG('k','e','r','n'),
-  HB_TAG('l','i','g','a'),
-  HB_TAG('r','c','l','t'),
+static const hb_ot_map_feature_t
+horizontal_features[] =
+{
+  {HB_TAG('c','a','l','t'), F_GLOBAL},
+  {HB_TAG('c','l','i','g'), F_GLOBAL},
+  {HB_TAG('c','u','r','s'), F_GLOBAL},
+  {HB_TAG('k','e','r','n'), F_GLOBAL_HAS_FALLBACK},
+  {HB_TAG('l','i','g','a'), F_GLOBAL},
+  {HB_TAG('r','c','l','t'), F_GLOBAL},
 };
 
-
-
 static void
 hb_ot_shape_collect_features (hb_ot_shape_planner_t          *planner,
 			      const hb_segment_properties_t  *props,
@@ -71,17 +150,17 @@
 {
   hb_ot_map_builder_t *map = &planner->map;
 
-  map->add_global_bool_feature (HB_TAG('r','v','r','n'));
+  map->enable_feature (HB_TAG('r','v','r','n'));
   map->add_gsub_pause (nullptr);
 
   switch (props->direction) {
     case HB_DIRECTION_LTR:
-      map->add_global_bool_feature (HB_TAG ('l','t','r','a'));
-      map->add_global_bool_feature (HB_TAG ('l','t','r','m'));
+      map->enable_feature (HB_TAG ('l','t','r','a'));
+      map->enable_feature (HB_TAG ('l','t','r','m'));
       break;
     case HB_DIRECTION_RTL:
-      map->add_global_bool_feature (HB_TAG ('r','t','l','a'));
-      map->add_feature (HB_TAG ('r','t','l','m'), 1, F_NONE);
+      map->enable_feature (HB_TAG ('r','t','l','a'));
+      map->add_feature (HB_TAG ('r','t','l','m'));
       break;
     case HB_DIRECTION_TTB:
     case HB_DIRECTION_BTT:
@@ -90,38 +169,46 @@
       break;
   }
 
-  map->add_feature (HB_TAG ('f','r','a','c'), 1, F_NONE);
-  map->add_feature (HB_TAG ('n','u','m','r'), 1, F_NONE);
-  map->add_feature (HB_TAG ('d','n','o','m'), 1, F_NONE);
+  /* Automatic fractions. */
+  map->add_feature (HB_TAG ('f','r','a','c'));
+  map->add_feature (HB_TAG ('n','u','m','r'));
+  map->add_feature (HB_TAG ('d','n','o','m'));
+
+  /* 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'));
 
   if (planner->shaper->collect_features)
     planner->shaper->collect_features (planner);
 
+  map->enable_feature (HB_TAG('B','U','Z','Z'));
+
   for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++)
-    map->add_global_bool_feature (common_features[i]);
+    map->add_feature (common_features[i]);
 
   if (HB_DIRECTION_IS_HORIZONTAL (props->direction))
     for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++)
-      map->add_feature (horizontal_features[i], 1, F_GLOBAL |
-			(horizontal_features[i] == HB_TAG('k','e','r','n') ?
-			 F_HAS_FALLBACK : F_NONE));
+      map->add_feature (horizontal_features[i]);
   else
   {
     /* We really want to find a 'vert' feature if there's any in the font, no
      * matter which script/langsys it is listed (or not) under.
      * See various bugs referenced from:
      * https://github.com/harfbuzz/harfbuzz/issues/63 */
-    map->add_feature (HB_TAG ('v','e','r','t'), 1, F_GLOBAL | F_GLOBAL_SEARCH);
+    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++) {
+  for (unsigned int i = 0; i < num_user_features; i++)
+  {
     const hb_feature_t *feature = &user_features[i];
-    map->add_feature (feature->tag, feature->value,
-		      (feature->start == 0 && feature->end == (unsigned int) -1) ?
-		       F_GLOBAL : F_NONE);
+    map->add_feature (feature->tag,
+		      (feature->start == HB_FEATURE_GLOBAL_START &&
+		       feature->end == HB_FEATURE_GLOBAL_END) ?  F_GLOBAL : F_NONE,
+		      feature->value);
   }
 }
 
@@ -135,13 +222,13 @@
 hb_ot_face_data_t *
 _hb_ot_shaper_face_data_create (hb_face_t *face)
 {
-  return _hb_ot_layout_create (face);
+  return _hb_ot_face_data_create (face);
 }
 
 void
 _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data)
 {
-  _hb_ot_layout_destroy (data);
+  _hb_ot_face_data_destroy (data);
 }
 
 
@@ -184,7 +271,12 @@
 
   hb_ot_shape_planner_t planner (shape_plan);
 
-  planner.shaper = hb_ot_shape_complex_categorize (&planner);
+  /* 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);
@@ -229,8 +321,6 @@
   unsigned int        num_user_features;
 
   /* Transient stuff */
-  bool fallback_positioning;
-  bool fallback_glyph_classes;
   hb_direction_t target_direction;
 };
 
@@ -244,10 +334,39 @@
 static void
 hb_set_unicode_props (hb_buffer_t *buffer)
 {
+  /* Implement enough of Unicode Graphemes here that shaping
+   * in reverse-direction wouldn't break graphemes.  Namely,
+   * we mark all marks and ZWJ and ZWJ,Extended_Pictographic
+   * sequences as continuations.  The foreach_grapheme()
+   * macro uses this bit.
+   *
+   * https://www.unicode.org/reports/tr29/#Regex_Definitions
+   */
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
+  {
     _hb_glyph_info_set_unicode_props (&info[i], buffer);
+
+    /* Marks are already set as continuation by the above line.
+     * Handle Emoji_Modifier and ZWJ-continuation. */
+    if (unlikely (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL &&
+		  hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x1F3FBu, 0x1F3FFu)))
+    {
+	_hb_glyph_info_set_continuation (&info[i]);
+    }
+    else if (unlikely (_hb_glyph_info_is_zwj (&info[i])))
+    {
+      _hb_glyph_info_set_continuation (&info[i]);
+      if (i + 1 < count &&
+	  _hb_unicode_is_emoji_Extended_Pictographic (info[i + 1].codepoint))
+      {
+        i++;
+	_hb_glyph_info_set_unicode_props (&info[i], buffer);
+	_hb_glyph_info_set_continuation (&info[i]);
+      }
+    }
+  }
 }
 
 static void
@@ -255,8 +374,7 @@
 {
   if (!(buffer->flags & HB_BUFFER_FLAG_BOT) ||
       buffer->context_len[0] ||
-      _hb_glyph_info_get_general_category (&buffer->info[0]) !=
-      HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+      !_hb_glyph_info_is_unicode_mark (&buffer->info[0]))
     return;
 
   if (!font->has_glyph (0x25CCu))
@@ -285,26 +403,12 @@
   if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII))
     return;
 
-  /* Loop duplicated in hb_ensure_native_direction(), and in _hb-coretext.cc */
-  unsigned int base = 0;
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  for (unsigned int i = 1; i < count; i++)
-  {
-    if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])) &&
-		!_hb_glyph_info_is_joiner (&info[i])))
-    {
-      if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
-	buffer->merge_clusters (base, i);
-      else
-	buffer->unsafe_to_break (base, i);
-      base = i;
-    }
-  }
   if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
-    buffer->merge_clusters (base, count);
+    foreach_grapheme (buffer, start, end)
+      buffer->merge_clusters (start, end);
   else
-    buffer->unsafe_to_break (base, count);
+    foreach_grapheme (buffer, start, end)
+      buffer->unsafe_to_break (start, end);
 }
 
 static void
@@ -322,25 +426,17 @@
       (HB_DIRECTION_IS_VERTICAL   (direction) &&
        direction != HB_DIRECTION_TTB))
   {
-    /* Same loop as hb_form_clusters().
-     * Since form_clusters() merged clusters already, we don't merge. */
-    unsigned int base = 0;
-    unsigned int count = buffer->len;
-    hb_glyph_info_t *info = buffer->info;
-    for (unsigned int i = 1; i < count; i++)
-    {
-      if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))))
-      {
-	if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
-	  buffer->merge_clusters (base, i);
-	buffer->reverse_range (base, i);
 
-	base = i;
-      }
-    }
     if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
-      buffer->merge_clusters (base, count);
-    buffer->reverse_range (base, count);
+      foreach_grapheme (buffer, start, end)
+      {
+	buffer->merge_clusters (start, end);
+	buffer->reverse_range (start, end);
+      }
+    else
+      foreach_grapheme (buffer, start, end)
+	/* form_clusters() merged clusters already, we don't merge. */
+	buffer->reverse_range (start, end);
 
     buffer->reverse ();
 
@@ -352,7 +448,7 @@
 /* Substitute */
 
 static inline void
-hb_ot_mirror_chars (hb_ot_shape_context_t *c)
+hb_ot_mirror_chars (const hb_ot_shape_context_t *c)
 {
   if (HB_DIRECTION_IS_FORWARD (c->target_direction))
     return;
@@ -373,7 +469,7 @@
 }
 
 static inline void
-hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c)
+hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c)
 {
   if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) ||
       !c->plan->has_frac)
@@ -423,7 +519,7 @@
 }
 
 static inline void
-hb_ot_shape_initialize_masks (hb_ot_shape_context_t *c)
+hb_ot_shape_initialize_masks (const hb_ot_shape_context_t *c)
 {
   hb_ot_map_t *map = &c->plan->map;
   hb_buffer_t *buffer = c->buffer;
@@ -433,7 +529,7 @@
 }
 
 static inline void
-hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
+hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c)
 {
   hb_ot_map_t *map = &c->plan->map;
   hb_buffer_t *buffer = c->buffer;
@@ -455,7 +551,7 @@
 }
 
 static void
-hb_ot_zero_width_default_ignorables (hb_ot_shape_context_t *c)
+hb_ot_zero_width_default_ignorables (const hb_ot_shape_context_t *c)
 {
   hb_buffer_t *buffer = c->buffer;
 
@@ -474,7 +570,7 @@
 }
 
 static void
-hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c)
+hb_ot_hide_default_ignorables (const hb_ot_shape_context_t *c)
 {
   hb_buffer_t *buffer = c->buffer;
 
@@ -485,34 +581,24 @@
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   hb_glyph_position_t *pos = buffer->pos;
-  unsigned int i = 0;
-  for (i = 0; i < count; i++)
-  {
-    if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i])))
-      break;
-  }
 
-  /* No default-ignorables found; return. */
-  if (i == count)
-    return;
-
-  hb_codepoint_t space;
+  hb_codepoint_t invisible = c->buffer->invisible;
   if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) &&
-      c->font->get_nominal_glyph (' ', &space))
+      (invisible || c->font->get_nominal_glyph (' ', &invisible)))
   {
-    /* Replace default-ignorables with a zero-advance space glyph. */
-    for (/*continue*/; i < count; i++)
+    /* Replace default-ignorables with a zero-advance invisible glyph. */
+    for (unsigned int i = 0; i < count; i++)
     {
       if (_hb_glyph_info_is_default_ignorable (&info[i]))
-	info[i].codepoint = space;
+	info[i].codepoint = invisible;
     }
   }
   else
   {
     /* Merge clusters and delete default-ignorables.
      * NOTE! We can't use out-buffer as we have positioning data. */
-    unsigned int j = i;
-    for (; i < count; i++)
+    unsigned int j = 0;
+    for (unsigned int i = 0; i < count; i++)
     {
       if (_hb_glyph_info_is_default_ignorable (&info[i]))
       {
@@ -567,7 +653,7 @@
 }
 
 static inline void
-hb_synthesize_glyph_classes (hb_ot_shape_context_t *c)
+hb_synthesize_glyph_classes (const hb_ot_shape_context_t *c)
 {
   unsigned int count = c->buffer->len;
   hb_glyph_info_t *info = c->buffer->info;
@@ -593,7 +679,7 @@
 }
 
 static inline void
-hb_ot_substitute_default (hb_ot_shape_context_t *c)
+hb_ot_substitute_default (const hb_ot_shape_context_t *c)
 {
   hb_buffer_t *buffer = c->buffer;
 
@@ -606,8 +692,8 @@
   hb_ot_shape_setup_masks (c);
 
   /* This is unfortunate to go here, but necessary... */
-  if (c->fallback_positioning)
-    _hb_ot_shape_fallback_position_recategorize_marks (c->plan, c->font, buffer);
+  if (c->plan->fallback_mark_positioning)
+    _hb_ot_shape_fallback_mark_position_recategorize_marks (c->plan, c->font, buffer);
 
   hb_ot_map_glyphs_fast (buffer);
 
@@ -615,23 +701,23 @@
 }
 
 static inline void
-hb_ot_substitute_complex (hb_ot_shape_context_t *c)
+hb_ot_substitute_complex (const hb_ot_shape_context_t *c)
 {
   hb_buffer_t *buffer = c->buffer;
 
   hb_ot_layout_substitute_start (c->font, buffer);
 
-  if (!hb_ot_layout_has_glyph_classes (c->face))
+  if (c->plan->fallback_glyph_classes)
     hb_synthesize_glyph_classes (c);
 
-  c->plan->substitute (c->font, buffer);
-
-  if (0) /* XXX Call morx instead. */
-    hb_aat_layout_substitute (c->font, 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);
 }
 
 static inline void
-hb_ot_substitute (hb_ot_shape_context_t *c)
+hb_ot_substitute (const hb_ot_shape_context_t *c)
 {
   hb_ot_substitute_default (c);
 
@@ -671,7 +757,7 @@
 }
 
 static inline void
-hb_ot_position_default (hb_ot_shape_context_t *c)
+hb_ot_position_default (const hb_ot_shape_context_t *c)
 {
   hb_direction_t direction = c->buffer->props.direction;
   unsigned int count = c->buffer->len;
@@ -705,7 +791,7 @@
 }
 
 static inline void
-hb_ot_position_complex (hb_ot_shape_context_t *c)
+hb_ot_position_complex (const hb_ot_shape_context_t *c)
 {
   unsigned int count = c->buffer->len;
   hb_glyph_info_t *info = c->buffer->info;
@@ -720,7 +806,7 @@
    * If fallback positinoing happens or GPOS is present, we don't
    * care.
    */
-  bool adjust_offsets_when_zeroing = c->fallback_positioning &&
+  bool adjust_offsets_when_zeroing = c->plan->fallback_mark_positioning &&
 				     !c->plan->shaper->fallback_position &&
 				     HB_DIRECTION_IS_FORWARD (c->buffer->props.direction);
 
@@ -735,32 +821,39 @@
 
   hb_ot_layout_position_start (c->font, c->buffer);
 
-  switch (c->plan->shaper->zero_width_marks)
-  {
-    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
-      zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
-      break;
+  if (!c->plan->apply_kerx)
+    switch (c->plan->shaper->zero_width_marks)
+    {
+      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
+	zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
+	break;
 
-    default:
-    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
-    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
-      break;
-  }
+      default:
+      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
+      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
+	break;
+    }
 
-  if (likely (!c->fallback_positioning))
+  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);
 
-  switch (c->plan->shaper->zero_width_marks)
-  {
-    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
-      zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
-      break;
+  if (c->plan->apply_trak)
+    hb_aat_layout_track (c->plan, c->font, c->buffer);
 
-    default:
-    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
-    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
-      break;
-  }
+  if (!c->plan->apply_kerx)
+    switch (c->plan->shaper->zero_width_marks)
+    {
+      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
+	zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing);
+	break;
+
+      default:
+      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
+      case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
+	break;
+    }
 
   /* Finishing off GPOS has to follow a certain order. */
   hb_ot_layout_position_finish_advances (c->font, c->buffer);
@@ -776,7 +869,7 @@
 }
 
 static inline void
-hb_ot_position (hb_ot_shape_context_t *c)
+hb_ot_position (const hb_ot_shape_context_t *c)
 {
   c->buffer->clear_positions ();
 
@@ -784,20 +877,20 @@
 
   hb_ot_position_complex (c);
 
-  if (c->fallback_positioning && c->plan->shaper->fallback_position)
-    _hb_ot_shape_fallback_position (c->plan, c->font, c->buffer);
+  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->fallback_positioning)
+  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);
-
-  //hb_aat_layout_position (c->font, c->buffer);
 }
 
 static inline void
@@ -844,11 +937,6 @@
 			      (unsigned) HB_BUFFER_MAX_OPS_MIN);
   }
 
-  bool disable_otl = c->plan->shaper->disable_otl && c->plan->shaper->disable_otl (c->plan);
-  //c->fallback_substitute     = disable_otl || !hb_ot_layout_has_substitution (c->face);
-  c->fallback_positioning    = disable_otl || !hb_ot_layout_has_positioning (c->face);
-  c->fallback_glyph_classes  = disable_otl || !hb_ot_layout_has_glyph_classes (c->face);
-
   /* Save the original direction, we use it later. */
   c->target_direction = c->buffer->props.direction;
 
diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape.hh
similarity index 68%
rename from src/hb-ot-shape-private.hh
rename to src/hb-ot-shape.hh
index d689826..c9c0d3e 100644
--- a/src/hb-ot-shape-private.hh
+++ b/src/hb-ot-shape.hh
@@ -24,13 +24,13 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_SHAPE_PRIVATE_HH
-#define HB_OT_SHAPE_PRIVATE_HH
+#ifndef HB_OT_SHAPE_HH
+#define HB_OT_SHAPE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-ot-map-private.hh"
-#include "hb-ot-layout-private.hh"
+#include "hb-ot-map.hh"
+#include "hb-shape-plan.hh"
 
 
 
@@ -42,9 +42,20 @@
   const void *data;
   hb_mask_t rtlm_mask, frac_mask, numr_mask, dnom_mask;
   hb_mask_t kern_mask;
-  unsigned int has_frac : 1;
-  unsigned int has_kern : 1;
-  unsigned int has_mark : 1;
+
+  bool requested_kerning : 1;
+  bool has_frac : 1;
+  bool has_gpos_mark : 1;
+  bool fallback_glyph_classes : 1;
+  bool fallback_kerning : 1;
+  bool fallback_mark_positioning : 1;
+
+  bool apply_gpos : 1;
+  bool apply_kerx : 1;
+  bool apply_kern : 1;
+  bool apply_morx : 1;
+  bool apply_trak : 1;
+
 
   inline void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const
   {
@@ -83,30 +94,13 @@
 			 shaper (nullptr),
 			 map (face, &props) {}
 
-  inline void compile (hb_ot_shape_plan_t &plan,
-		       const int          *coords,
-		       unsigned int        num_coords)
-  {
-    plan.props = props;
-    plan.shaper = shaper;
-    map.compile (plan.map, coords, num_coords);
-
-    plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m'));
-    plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c'));
-    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.kern_mask = plan.map.get_mask (HB_DIRECTION_IS_HORIZONTAL (plan.props.direction) ?
-					HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'));
-
-    plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask);
-    plan.has_kern = !!plan.kern_mask;
-    plan.has_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k'));
-  }
+  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);
 };
 
 
-#endif /* HB_OT_SHAPE_PRIVATE_HH */
+#endif /* HB_OT_SHAPE_HH */
diff --git a/src/hb-ot-tag-table.hh b/src/hb-ot-tag-table.hh
new file mode 100644
index 0000000..b7090a0
--- /dev/null
+++ b/src/hb-ot-tag-table.hh
@@ -0,0 +1,2064 @@
+/* == Start of generated table == */
+/*
+ * The following table is generated by running:
+ *
+ *   ./gen-tag-table.py languagetags language-subtag-registry
+ *
+ * on files with these headers:
+ *
+ * <meta name="updated_at" content="2018-09-07 07:45 PM" />
+ * File-Date: 2018-08-08
+ */
+
+#ifndef HB_OT_TAG_TABLE_HH
+#define HB_OT_TAG_TABLE_HH
+
+static const LangTag ot_languages[] = {
+  {"aa",	{HB_TAG('A','F','R',' ')}},	/* Afar */
+  {"aae",	{HB_TAG('S','Q','I',' ')}},	/* Arbëreshë Albanian -> Albanian */
+  {"aao",	{HB_TAG('A','R','A',' ')}},	/* Algerian Saharan Arabic -> Arabic */
+  {"aat",	{HB_TAG('S','Q','I',' ')}},	/* Arvanitika Albanian -> Albanian */
+  {"ab",	{HB_TAG('A','B','K',' ')}},	/* Abkhazian */
+  {"abh",	{HB_TAG('A','R','A',' ')}},	/* Tajiki Arabic -> Arabic */
+  {"abq",	{HB_TAG('A','B','A',' ')}},	/* Abaza */
+  {"abv",	{HB_TAG('A','R','A',' ')}},	/* Baharna Arabic -> Arabic */
+  {"acf",	{HB_TAG('F','A','N',' ')}},	/* Saint Lucian Creole French -> French Antillean */
+  {"ach",	{HB_TAG('A','C','H',' ')}},	/* Acoli -> Acholi */
+  {"acm",	{HB_TAG('A','R','A',' ')}},	/* Mesopotamian Arabic -> Arabic */
+  {"acq",	{HB_TAG('A','R','A',' ')}},	/* Ta'izzi-Adeni Arabic -> Arabic */
+  {"acr",	{HB_TAG('A','C','R',' ')}},	/* Achi */
+  {"acw",	{HB_TAG('A','R','A',' ')}},	/* Hijazi Arabic -> Arabic */
+  {"acx",	{HB_TAG('A','R','A',' ')}},	/* Omani Arabic -> Arabic */
+  {"acy",	{HB_TAG('A','R','A',' ')}},	/* Cypriot Arabic -> Arabic */
+  {"ada",	{HB_TAG('D','N','G',' ')}},	/* Adangme -> Dangme */
+  {"adf",	{HB_TAG('A','R','A',' ')}},	/* Dhofari Arabic -> Arabic */
+  {"adp",	{HB_TAG('D','Z','N',' ')}},	/* Adap (retired code) -> Dzongkha */
+  {"ady",	{HB_TAG('A','D','Y',' ')}},	/* Adyghe */
+  {"aeb",	{HB_TAG('A','R','A',' ')}},	/* Tunisian Arabic -> Arabic */
+  {"aec",	{HB_TAG('A','R','A',' ')}},	/* Saidi Arabic -> Arabic */
+  {"af",	{HB_TAG('A','F','K',' ')}},	/* Afrikaans */
+  {"afb",	{HB_TAG('A','R','A',' ')}},	/* Gulf Arabic -> Arabic */
+  {"ahg",	{HB_TAG('A','G','W',' ')}},	/* Qimant -> Agaw */
+  {"aht",	{HB_TAG('A','T','H',' ')}},	/* Ahtena -> Athapaskan */
+  {"aii",	{HB_TAG('S','W','A',' '),	/* Assyrian Neo-Aramaic -> Swadaya Aramaic */
+		 HB_TAG('S','Y','R',' ')}},	/* Assyrian Neo-Aramaic -> Syriac */
+  {"aio",	{HB_TAG('A','I','O',' ')}},	/* Aiton */
+  {"aiw",	{HB_TAG('A','R','I',' ')}},	/* Aari */
+  {"ajp",	{HB_TAG('A','R','A',' ')}},	/* South Levantine Arabic -> Arabic */
+  {"ak",	{HB_TAG('A','K','A',' '),	/* Akan [macrolanguage] */
+		 HB_TAG('T','W','I',' ')}},	/* Akan [macrolanguage] -> Twi */
+  {"aln",	{HB_TAG('S','Q','I',' ')}},	/* Gheg Albanian -> Albanian */
+  {"als",	{HB_TAG('S','Q','I',' ')}},	/* Tosk Albanian -> Albanian */
+  {"alt",	{HB_TAG('A','L','T',' ')}},	/* Southern Altai -> Altai */
+  {"am",	{HB_TAG('A','M','H',' ')}},	/* Amharic */
+  {"amf",	{HB_TAG('H','B','N',' ')}},	/* Hamer-Banna -> Hammer-Banna */
+  {"amw",	{HB_TAG('S','Y','R',' ')}},	/* Western Neo-Aramaic -> Syriac */
+  {"an",	{HB_TAG('A','R','G',' ')}},	/* Aragonese */
+  {"ang",	{HB_TAG('A','N','G',' ')}},	/* Old English (ca. 450-1100) -> Anglo-Saxon */
+  {"apc",	{HB_TAG('A','R','A',' ')}},	/* North Levantine Arabic -> Arabic */
+  {"apd",	{HB_TAG('A','R','A',' ')}},	/* Sudanese Arabic -> Arabic */
+  {"apj",	{HB_TAG('A','T','H',' ')}},	/* Jicarilla Apache -> Athapaskan */
+  {"apk",	{HB_TAG('A','T','H',' ')}},	/* Kiowa Apache -> Athapaskan */
+  {"apl",	{HB_TAG('A','T','H',' ')}},	/* Lipan Apache -> Athapaskan */
+  {"apm",	{HB_TAG('A','T','H',' ')}},	/* Mescalero-Chiricahua Apache -> Athapaskan */
+  {"apw",	{HB_TAG('A','T','H',' ')}},	/* Western Apache -> Athapaskan */
+  {"ar",	{HB_TAG('A','R','A',' ')}},	/* Arabic [macrolanguage] */
+  {"arb",	{HB_TAG('A','R','A',' ')}},	/* Standard Arabic -> Arabic */
+  {"arn",	{HB_TAG('M','A','P',' ')}},	/* Mapudungun */
+  {"arq",	{HB_TAG('A','R','A',' ')}},	/* Algerian Arabic -> Arabic */
+  {"ars",	{HB_TAG('A','R','A',' ')}},	/* Najdi Arabic -> Arabic */
+  {"ary",	{HB_TAG('M','O','R',' ')}},	/* Moroccan Arabic -> Moroccan */
+  {"arz",	{HB_TAG('A','R','A',' ')}},	/* Egyptian Arabic -> Arabic */
+  {"as",	{HB_TAG('A','S','M',' ')}},	/* Assamese */
+  {"ast",	{HB_TAG('A','S','T',' ')}},	/* Asturian */
+  {"ath",	{HB_TAG('A','T','H',' ')}},	/* Athapascan [family] -> Athapaskan */
+  {"atj",	{HB_TAG('R','C','R',' ')}},	/* Atikamekw -> R-Cree */
+  {"atv",	{HB_TAG('A','L','T',' ')}},	/* Northern Altai -> Altai */
+  {"auz",	{HB_TAG('A','R','A',' ')}},	/* Uzbeki Arabic -> Arabic */
+  {"av",	{HB_TAG('A','V','R',' ')}},	/* Avaric -> Avar */
+  {"avl",	{HB_TAG('A','R','A',' ')}},	/* Eastern Egyptian Bedawi Arabic -> Arabic */
+  {"awa",	{HB_TAG('A','W','A',' ')}},	/* Awadhi */
+  {"ay",	{HB_TAG('A','Y','M',' ')}},	/* Aymara [macrolanguage] */
+  {"ayc",	{HB_TAG('A','Y','M',' ')}},	/* Southern Aymara -> Aymara */
+  {"ayh",	{HB_TAG('A','R','A',' ')}},	/* Hadrami Arabic -> Arabic */
+  {"ayl",	{HB_TAG('A','R','A',' ')}},	/* Libyan Arabic -> Arabic */
+  {"ayn",	{HB_TAG('A','R','A',' ')}},	/* Sanaani Arabic -> Arabic */
+  {"ayp",	{HB_TAG('A','R','A',' ')}},	/* North Mesopotamian Arabic -> Arabic */
+  {"ayr",	{HB_TAG('A','Y','M',' ')}},	/* Central Aymara -> Aymara */
+  {"az",	{HB_TAG('A','Z','E',' ')}},	/* Azerbaijani [macrolanguage] */
+  {"azb",	{HB_TAG('A','Z','B',' ')}},	/* South Azerbaijani -> Torki */
+  {"azj",	{HB_TAG('A','Z','E',' ')}},	/* North Azerbaijani -> Azerbaijani */
+  {"ba",	{HB_TAG('B','S','H',' ')}},	/* Bashkir */
+  {"bad",	{HB_TAG('B','A','D','0')}},	/* Banda [family] */
+  {"bai",	{HB_TAG('B','M','L',' ')}},	/* Bamileke [family] */
+  {"bal",	{HB_TAG('B','L','I',' ')}},	/* Baluchi [macrolanguage] */
+  {"ban",	{HB_TAG('B','A','N',' ')}},	/* Balinese */
+  {"bar",	{HB_TAG('B','A','R',' ')}},	/* Bavarian */
+  {"bbc",	{HB_TAG('B','B','C',' ')}},	/* Batak Toba */
+  {"bbz",	{HB_TAG('A','R','A',' ')}},	/* Babalia Creole Arabic -> Arabic */
+  {"bcc",	{HB_TAG('B','L','I',' ')}},	/* Southern Balochi -> Baluchi */
+  {"bci",	{HB_TAG('B','A','U',' ')}},	/* Baoulé -> Baulé */
+  {"bcl",	{HB_TAG('B','I','K',' ')}},	/* Central Bikol -> Bikol */
+  {"bcq",	{HB_TAG('B','C','H',' ')}},	/* Bench */
+  {"bcr",	{HB_TAG('A','T','H',' ')}},	/* Babine -> Athapaskan */
+  {"bdy",	{HB_TAG('B','D','Y',' ')}},	/* Bandjalang */
+  {"be",	{HB_TAG('B','E','L',' ')}},	/* Belarusian -> Belarussian */
+  {"bea",	{HB_TAG('A','T','H',' ')}},	/* Beaver -> Athapaskan */
+  {"beb",	{HB_TAG('B','T','I',' ')}},	/* Bebele -> Beti */
+  {"bem",	{HB_TAG('B','E','M',' ')}},	/* Bemba (Zambia) */
+  {"ber",	{HB_TAG('B','B','R',' ')}},	/* Berber [family] */
+  {"bfq",	{HB_TAG('B','A','D',' ')}},	/* Badaga */
+  {"bft",	{HB_TAG('B','L','T',' ')}},	/* Balti */
+  {"bfu",	{HB_TAG('L','A','H',' ')}},	/* Gahri -> Lahuli */
+  {"bfy",	{HB_TAG('B','A','G',' ')}},	/* Bagheli -> Baghelkhandi */
+  {"bg",	{HB_TAG('B','G','R',' ')}},	/* Bulgarian */
+  {"bgc",	{HB_TAG('B','G','C',' ')}},	/* Haryanvi */
+  {"bgn",	{HB_TAG('B','L','I',' ')}},	/* Western Balochi -> Baluchi */
+  {"bgp",	{HB_TAG('B','L','I',' ')}},	/* Eastern Balochi -> Baluchi */
+  {"bgq",	{HB_TAG('B','G','Q',' ')}},	/* Bagri */
+  {"bgr",	{HB_TAG('Q','I','N',' ')}},	/* Bawm Chin -> Chin */
+  {"bhb",	{HB_TAG('B','H','I',' ')}},	/* Bhili */
+  {"bhi",	{HB_TAG('B','H','I',' ')}},	/* Bhilali -> Bhili */
+  {"bhk",	{HB_TAG('B','I','K',' ')}},	/* Albay Bicolano (retired code) -> Bikol */
+  {"bho",	{HB_TAG('B','H','O',' ')}},	/* Bhojpuri */
+  {"bhr",	{HB_TAG('M','L','G',' ')}},	/* Bara Malagasy -> Malagasy */
+  {"bi",	{HB_TAG('B','I','S',' ')}},	/* Bislama */
+  {"bik",	{HB_TAG('B','I','K',' ')}},	/* Bikol [macrolanguage] */
+  {"bin",	{HB_TAG('E','D','O',' ')}},	/* Edo */
+  {"bjj",	{HB_TAG('B','J','J',' ')}},	/* Kanauji */
+  {"bjn",	{HB_TAG('M','L','Y',' ')}},	/* Banjar -> Malay */
+  {"bjq",	{HB_TAG('M','L','G',' ')}},	/* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */
+  {"bjt",	{HB_TAG('B','L','N',' ')}},	/* Balanta-Ganja -> Balante */
+  {"bla",	{HB_TAG('B','K','F',' ')}},	/* Siksika -> Blackfoot */
+  {"ble",	{HB_TAG('B','L','N',' ')}},	/* Balanta-Kentohe -> Balante */
+  {"blk",	{HB_TAG('B','L','K',' ')}},	/* Pa'o Karen */
+  {"bln",	{HB_TAG('B','I','K',' ')}},	/* Southern Catanduanes Bikol -> Bikol */
+  {"bm",	{HB_TAG('B','M','B',' ')}},	/* Bambara (Bamanankan) */
+  {"bmm",	{HB_TAG('M','L','G',' ')}},	/* Northern Betsimisaraka Malagasy -> Malagasy */
+  {"bn",	{HB_TAG('B','E','N',' ')}},	/* Bengali */
+  {"bo",	{HB_TAG('T','I','B',' ')}},	/* Tibetan */
+  {"bpy",	{HB_TAG('B','P','Y',' ')}},	/* Bishnupriya -> Bishnupriya Manipuri */
+  {"bqi",	{HB_TAG('L','R','C',' ')}},	/* Bakhtiari -> Luri */
+  {"br",	{HB_TAG('B','R','E',' ')}},	/* Breton */
+  {"bra",	{HB_TAG('B','R','I',' ')}},	/* Braj -> Braj Bhasha */
+  {"brh",	{HB_TAG('B','R','H',' ')}},	/* Brahui */
+  {"brx",	{HB_TAG('B','R','X',' ')}},	/* Bodo (India) */
+  {"bs",	{HB_TAG('B','O','S',' ')}},	/* Bosnian */
+  {"bsk",	{HB_TAG('B','S','K',' ')}},	/* Burushaski */
+  {"btb",	{HB_TAG('B','T','I',' ')}},	/* Beti (Cameroon) (retired code) */
+  {"btj",	{HB_TAG('M','L','Y',' ')}},	/* Bacanese Malay -> Malay */
+  {"bto",	{HB_TAG('B','I','K',' ')}},	/* Rinconada Bikol -> Bikol */
+  {"bts",	{HB_TAG('B','T','S',' ')}},	/* Batak Simalungun */
+  {"bug",	{HB_TAG('B','U','G',' ')}},	/* Buginese -> Bugis */
+  {"bum",	{HB_TAG('B','T','I',' ')}},	/* Bulu (Cameroon) -> Beti */
+  {"bve",	{HB_TAG('M','L','Y',' ')}},	/* Berau Malay -> Malay */
+  {"bvu",	{HB_TAG('M','L','Y',' ')}},	/* Bukit Malay -> Malay */
+  {"bxk",	{HB_TAG('L','U','H',' ')}},	/* Bukusu -> Luyia */
+  {"bxp",	{HB_TAG('B','T','I',' ')}},	/* Bebil -> Beti */
+  {"bxr",	{HB_TAG('R','B','U',' ')}},	/* Russia Buriat -> Russian Buriat */
+  {"byn",	{HB_TAG('B','I','L',' ')}},	/* Bilin -> Bilen */
+  {"byv",	{HB_TAG('B','Y','V',' ')}},	/* Medumba */
+  {"bzc",	{HB_TAG('M','L','G',' ')}},	/* Southern Betsimisaraka Malagasy -> Malagasy */
+  {"ca",	{HB_TAG('C','A','T',' ')}},	/* Catalan */
+  {"caf",	{HB_TAG('C','R','R',' '),	/* Southern Carrier -> Carrier */
+		 HB_TAG('A','T','H',' ')}},	/* Southern Carrier -> Athapaskan */
+  {"cak",	{HB_TAG('C','A','K',' ')}},	/* Kaqchikel */
+  {"cbk",	{HB_TAG('C','B','K',' ')}},	/* Chavacano -> Zamboanga Chavacano */
+  {"cbl",	{HB_TAG('Q','I','N',' ')}},	/* Bualkhaw Chin -> Chin */
+  {"cco",	{HB_TAG('C','C','H','N')}},	/* Comaltepec Chinantec -> Chinantec */
+  {"ccq",	{HB_TAG('A','R','K',' ')}},	/* Chaungtha (retired code) -> Rakhine */
+  {"cdo",	{HB_TAG('Z','H','S',' ')}},	/* Min Dong Chinese -> Chinese Simplified */
+  {"ce",	{HB_TAG('C','H','E',' ')}},	/* Chechen */
+  {"ceb",	{HB_TAG('C','E','B',' ')}},	/* Cebuano */
+  {"cfm",	{HB_TAG('H','A','L',' ')}},	/* Halam (Falam Chin) */
+  {"cgg",	{HB_TAG('C','G','G',' ')}},	/* Chiga */
+  {"ch",	{HB_TAG('C','H','A',' ')}},	/* Chamorro */
+  {"chj",	{HB_TAG('C','C','H','N')}},	/* Ojitlán Chinantec -> Chinantec */
+  {"chk",	{HB_TAG('C','H','K','0')}},	/* Chuukese */
+  {"cho",	{HB_TAG('C','H','O',' ')}},	/* Choctaw */
+  {"chp",	{HB_TAG('C','H','P',' '),	/* Chipewyan */
+		 HB_TAG('S','A','Y',' '),	/* Chipewyan -> Sayisi */
+		 HB_TAG('A','T','H',' ')}},	/* Chipewyan -> Athapaskan */
+  {"chq",	{HB_TAG('C','C','H','N')}},	/* Quiotepec Chinantec -> Chinantec */
+  {"chr",	{HB_TAG('C','H','R',' ')}},	/* Cherokee */
+  {"chy",	{HB_TAG('C','H','Y',' ')}},	/* Cheyenne */
+  {"chz",	{HB_TAG('C','C','H','N')}},	/* Ozumacín Chinantec -> Chinantec */
+  {"ciw",	{HB_TAG('O','J','B',' ')}},	/* Chippewa -> Ojibway */
+  {"cja",	{HB_TAG('C','J','A',' ')}},	/* Western Cham */
+  {"cjm",	{HB_TAG('C','J','M',' ')}},	/* Eastern Cham */
+  {"cjy",	{HB_TAG('Z','H','S',' ')}},	/* Jinyu Chinese -> Chinese Simplified */
+  {"cka",	{HB_TAG('Q','I','N',' ')}},	/* Khumi Awa Chin (retired code) -> Chin */
+  {"ckb",	{HB_TAG('K','U','R',' ')}},	/* Central Kurdish -> Kurdish */
+  {"ckt",	{HB_TAG('C','H','K',' ')}},	/* Chukot -> Chukchi */
+  {"clc",	{HB_TAG('A','T','H',' ')}},	/* Chilcotin -> Athapaskan */
+  {"cld",	{HB_TAG('S','Y','R',' ')}},	/* Chaldean Neo-Aramaic -> Syriac */
+  {"cle",	{HB_TAG('C','C','H','N')}},	/* Lealao Chinantec -> Chinantec */
+  {"cmn",	{HB_TAG('Z','H','S',' ')}},	/* Mandarin Chinese -> Chinese Simplified */
+  {"cmr",	{HB_TAG('Q','I','N',' ')}},	/* Mro-Khimi Chin -> Chin */
+  {"cnb",	{HB_TAG('Q','I','N',' ')}},	/* Chinbon Chin -> Chin */
+  {"cnh",	{HB_TAG('Q','I','N',' ')}},	/* Hakha Chin -> Chin */
+  {"cnk",	{HB_TAG('Q','I','N',' ')}},	/* Khumi Chin -> Chin */
+  {"cnl",	{HB_TAG('C','C','H','N')}},	/* Lalana Chinantec -> Chinantec */
+  {"cnt",	{HB_TAG('C','C','H','N')}},	/* Tepetotutla Chinantec -> Chinantec */
+  {"cnw",	{HB_TAG('Q','I','N',' ')}},	/* Ngawn Chin -> Chin */
+  {"co",	{HB_TAG('C','O','S',' ')}},	/* Corsican */
+  {"coa",	{HB_TAG('M','L','Y',' ')}},	/* Cocos Islands Malay -> Malay */
+  {"cop",	{HB_TAG('C','O','P',' ')}},	/* Coptic */
+  {"coq",	{HB_TAG('A','T','H',' ')}},	/* Coquille -> Athapaskan */
+  {"cpa",	{HB_TAG('C','C','H','N')}},	/* Palantla Chinantec -> Chinantec */
+  {"cpe",	{HB_TAG('C','P','P',' ')}},	/* English-based creoles and pidgins [family] -> Creoles */
+  {"cpf",	{HB_TAG('C','P','P',' ')}},	/* French-based creoles and pidgins [family] -> Creoles */
+  {"cpp",	{HB_TAG('C','P','P',' ')}},	/* Portuguese-based creoles and pidgins [family] -> Creoles */
+  {"cpx",	{HB_TAG('Z','H','S',' ')}},	/* Pu-Xian Chinese -> Chinese Simplified */
+  {"cqd",	{HB_TAG('H','M','N',' ')}},	/* Chuanqiandian Cluster Miao -> Hmong */
+  {"cqu",	{HB_TAG('Q','U','H',' ')}},	/* Chilean Quechua (retired code) -> Quechua (Bolivia) */
+  {"cr",	{HB_TAG('C','R','E',' '),	/* Cree [macrolanguage] */
+		 HB_TAG('Y','C','R',' ')}},	/* Cree [macrolanguage] -> Y-Cree */
+  {"crh",	{HB_TAG('C','R','T',' ')}},	/* Crimean Tatar */
+  {"crj",	{HB_TAG('E','C','R',' ')}},	/* Southern East Cree -> Eastern Cree */
+  {"crk",	{HB_TAG('W','C','R',' ')}},	/* Plains Cree -> West-Cree */
+  {"crl",	{HB_TAG('E','C','R',' ')}},	/* Northern East Cree -> Eastern Cree */
+  {"crm",	{HB_TAG('M','C','R',' '),	/* Moose Cree */
+		 HB_TAG('L','C','R',' ')}},	/* Moose Cree -> L-Cree */
+  {"crp",	{HB_TAG('C','P','P',' ')}},	/* Creoles and pidgins [family] -> Creoles */
+  {"crx",	{HB_TAG('C','R','R',' '),	/* Carrier */
+		 HB_TAG('A','T','H',' ')}},	/* Carrier -> Athapaskan */
+  {"cs",	{HB_TAG('C','S','Y',' ')}},	/* Czech */
+  {"csa",	{HB_TAG('C','C','H','N')}},	/* Chiltepec Chinantec -> Chinantec */
+  {"csb",	{HB_TAG('C','S','B',' ')}},	/* Kashubian */
+  {"csh",	{HB_TAG('Q','I','N',' ')}},	/* Asho Chin -> Chin */
+  {"cso",	{HB_TAG('C','C','H','N')}},	/* Sochiapam Chinantec -> Chinantec */
+  {"csw",	{HB_TAG('N','C','R',' '),	/* Swampy Cree -> N-Cree */
+		 HB_TAG('N','H','C',' ')}},	/* Swampy Cree -> Norway House Cree */
+  {"csy",	{HB_TAG('Q','I','N',' ')}},	/* Siyin Chin -> Chin */
+  {"ctc",	{HB_TAG('A','T','H',' ')}},	/* Chetco -> Athapaskan */
+  {"ctd",	{HB_TAG('Q','I','N',' ')}},	/* Tedim Chin -> Chin */
+  {"cte",	{HB_TAG('C','C','H','N')}},	/* Tepinapa Chinantec -> Chinantec */
+  {"ctg",	{HB_TAG('C','T','G',' ')}},	/* Chittagonian */
+  {"ctl",	{HB_TAG('C','C','H','N')}},	/* Tlacoatzintepec Chinantec -> Chinantec */
+  {"cts",	{HB_TAG('B','I','K',' ')}},	/* Northern Catanduanes Bikol -> Bikol */
+  {"cu",	{HB_TAG('C','S','L',' ')}},	/* Church Slavonic */
+  {"cuc",	{HB_TAG('C','C','H','N')}},	/* Usila Chinantec -> Chinantec */
+  {"cuk",	{HB_TAG('C','U','K',' ')}},	/* San Blas Kuna */
+  {"cv",	{HB_TAG('C','H','U',' ')}},	/* Chuvash */
+  {"cvn",	{HB_TAG('C','C','H','N')}},	/* Valle Nacional Chinantec -> Chinantec */
+  {"cwd",	{HB_TAG('D','C','R',' '),	/* Woods Cree */
+		 HB_TAG('T','C','R',' ')}},	/* Woods Cree -> TH-Cree */
+  {"cy",	{HB_TAG('W','E','L',' ')}},	/* Welsh */
+  {"czh",	{HB_TAG('Z','H','S',' ')}},	/* Huizhou Chinese -> Chinese Simplified */
+  {"czo",	{HB_TAG('Z','H','S',' ')}},	/* Min Zhong Chinese -> Chinese Simplified */
+  {"czt",	{HB_TAG('Q','I','N',' ')}},	/* Zotung Chin -> Chin */
+  {"da",	{HB_TAG('D','A','N',' ')}},	/* Danish */
+  {"dao",	{HB_TAG('Q','I','N',' ')}},	/* Daai Chin -> Chin */
+  {"dap",	{HB_TAG('N','I','S',' ')}},	/* Nisi (India) (retired code) */
+  {"dar",	{HB_TAG('D','A','R',' ')}},	/* Dargwa */
+  {"dax",	{HB_TAG('D','A','X',' ')}},	/* Dayi */
+  {"de",	{HB_TAG('D','E','U',' ')}},	/* German */
+  {"den",	{HB_TAG('S','L','A',' '),	/* Slave (Athapascan) [macrolanguage] -> Slavey */
+		 HB_TAG('A','T','H',' ')}},	/* Slave (Athapascan) [macrolanguage] -> Athapaskan */
+  {"dgo",	{HB_TAG('D','G','O',' ')}},	/* Dogri */
+  {"dgr",	{HB_TAG('A','T','H',' ')}},	/* Dogrib -> Athapaskan */
+  {"dhd",	{HB_TAG('M','A','W',' ')}},	/* Dhundari -> Marwari */
+  {"dhg",	{HB_TAG('D','H','G',' ')}},	/* Dhangu */
+  {"dib",	{HB_TAG('D','N','K',' ')}},	/* South Central Dinka -> Dinka */
+  {"dik",	{HB_TAG('D','N','K',' ')}},	/* Southwestern Dinka -> Dinka */
+  {"din",	{HB_TAG('D','N','K',' ')}},	/* Dinka [macrolanguage] */
+  {"dip",	{HB_TAG('D','N','K',' ')}},	/* Northeastern Dinka -> Dinka */
+  {"diq",	{HB_TAG('D','I','Q',' ')}},	/* Dimli */
+  {"diw",	{HB_TAG('D','N','K',' ')}},	/* Northwestern Dinka -> Dinka */
+  {"dje",	{HB_TAG('D','J','R',' ')}},	/* Zarma */
+  {"djr",	{HB_TAG('D','J','R','0')}},	/* Djambarrpuyngu */
+  {"dks",	{HB_TAG('D','N','K',' ')}},	/* Southeastern Dinka -> Dinka */
+  {"dng",	{HB_TAG('D','U','N',' ')}},	/* Dungan */
+  {"dnj",	{HB_TAG('D','N','J',' ')}},	/* Dan */
+  {"doi",	{HB_TAG('D','G','R',' ')}},	/* Dogri [macrolanguage] */
+  {"drh",	{HB_TAG('M','N','G',' ')}},	/* Darkhat (retired code) -> Mongolian */
+  {"drw",	{HB_TAG('D','R','I',' ')}},	/* Darwazi (retired code) -> Dari */
+  {"dsb",	{HB_TAG('L','S','B',' ')}},	/* Lower Sorbian */
+  {"dty",	{HB_TAG('N','E','P',' ')}},	/* Dotyali -> Nepali */
+  {"duj",	{HB_TAG('D','U','J',' ')}},	/* Dhuwal (retired code) */
+  {"dup",	{HB_TAG('M','L','Y',' ')}},	/* Duano -> Malay */
+  {"dv",	{HB_TAG('D','I','V',' '),	/* Divehi (Dhivehi, Maldivian) */
+		 HB_TAG('D','H','V',' ')}},	/* Divehi (Dhivehi, Maldivian) (deprecated) */
+  {"dwu",	{HB_TAG('D','U','J',' ')}},	/* Dhuwal */
+  {"dwy",	{HB_TAG('D','U','J',' ')}},	/* Dhuwaya -> Dhuwal */
+  {"dyu",	{HB_TAG('J','U','L',' ')}},	/* Dyula -> Jula */
+  {"dz",	{HB_TAG('D','Z','N',' ')}},	/* Dzongkha */
+  {"ee",	{HB_TAG('E','W','E',' ')}},	/* Ewe */
+  {"efi",	{HB_TAG('E','F','I',' ')}},	/* Efik */
+  {"ekk",	{HB_TAG('E','T','I',' ')}},	/* Standard Estonian -> Estonian */
+  {"el",	{HB_TAG('E','L','L',' ')}},	/* Modern Greek (1453-) -> Greek */
+  {"emk",	{HB_TAG('E','M','K',' '),	/* Eastern Maninkakan */
+		 HB_TAG('M','N','K',' ')}},	/* Eastern Maninkakan -> Maninka */
+  {"en",	{HB_TAG('E','N','G',' ')}},	/* English */
+  {"enb",	{HB_TAG('K','A','L',' ')}},	/* Markweeta -> Kalenjin */
+  {"enf",	{HB_TAG('F','N','E',' ')}},	/* Forest Enets -> Forest Nenets */
+  {"enh",	{HB_TAG('T','N','E',' ')}},	/* Tundra Enets -> Tundra Nenets */
+  {"eo",	{HB_TAG('N','T','O',' ')}},	/* Esperanto */
+  {"es",	{HB_TAG('E','S','P',' ')}},	/* Spanish */
+  {"esg",	{HB_TAG('G','O','N',' ')}},	/* Aheri Gondi -> Gondi */
+  {"esi",	{HB_TAG('I','P','K',' ')}},	/* North Alaskan Inupiatun -> Inupiat */
+  {"esk",	{HB_TAG('I','P','K',' ')}},	/* Northwest Alaska Inupiatun -> Inupiat */
+  {"esu",	{HB_TAG('E','S','U',' ')}},	/* Central Yupik */
+  {"et",	{HB_TAG('E','T','I',' ')}},	/* Estonian [macrolanguage] */
+  {"eto",	{HB_TAG('B','T','I',' ')}},	/* Eton (Cameroon) -> Beti */
+  {"eu",	{HB_TAG('E','U','Q',' ')}},	/* Basque */
+  {"eve",	{HB_TAG('E','V','N',' ')}},	/* Even */
+  {"evn",	{HB_TAG('E','V','K',' ')}},	/* Evenki */
+  {"ewo",	{HB_TAG('B','T','I',' ')}},	/* Ewondo -> Beti */
+  {"eyo",	{HB_TAG('K','A','L',' ')}},	/* Keiyo -> Kalenjin */
+  {"fa",	{HB_TAG('F','A','R',' ')}},	/* Persian [macrolanguage] */
+  {"fan",	{HB_TAG('F','A','N','0')}},	/* Fang (Equatorial Guinea) */
+  {"fat",	{HB_TAG('F','A','T',' ')}},	/* Fanti */
+  {"fbl",	{HB_TAG('B','I','K',' ')}},	/* West Albay Bikol -> Bikol */
+  {"ff",	{HB_TAG('F','U','L',' ')}},	/* Fulah [macrolanguage] */
+  {"ffm",	{HB_TAG('F','U','L',' ')}},	/* Maasina Fulfulde -> Fulah */
+  {"fi",	{HB_TAG('F','I','N',' ')}},	/* Finnish */
+  {"fil",	{HB_TAG('P','I','L',' ')}},	/* Filipino */
+  {"fj",	{HB_TAG('F','J','I',' ')}},	/* Fijian */
+  {"flm",	{HB_TAG('H','A','L',' '),	/* Halam (Falam Chin) (retired code) */
+		 HB_TAG('Q','I','N',' ')}},	/* Falam Chin (retired code) -> Chin */
+  {"fmp",	{HB_TAG('F','M','P',' ')}},	/* Fe'fe' */
+  {"fo",	{HB_TAG('F','O','S',' ')}},	/* Faroese */
+  {"fon",	{HB_TAG('F','O','N',' ')}},	/* Fon */
+  {"fr",	{HB_TAG('F','R','A',' ')}},	/* French */
+  {"frc",	{HB_TAG('F','R','C',' ')}},	/* Cajun French */
+  {"frp",	{HB_TAG('F','R','P',' ')}},	/* Arpitan */
+  {"fub",	{HB_TAG('F','U','L',' ')}},	/* Adamawa Fulfulde -> Fulah */
+  {"fuc",	{HB_TAG('F','U','L',' ')}},	/* Pulaar -> Fulah */
+  {"fue",	{HB_TAG('F','U','L',' ')}},	/* Borgu Fulfulde -> Fulah */
+  {"fuf",	{HB_TAG('F','T','A',' ')}},	/* Pular -> Futa */
+  {"fuh",	{HB_TAG('F','U','L',' ')}},	/* Western Niger Fulfulde -> Fulah */
+  {"fui",	{HB_TAG('F','U','L',' ')}},	/* Bagirmi Fulfulde -> Fulah */
+  {"fuq",	{HB_TAG('F','U','L',' ')}},	/* Central-Eastern Niger Fulfulde -> Fulah */
+  {"fur",	{HB_TAG('F','R','L',' ')}},	/* Friulian */
+  {"fuv",	{HB_TAG('F','U','V',' ')}},	/* Nigerian Fulfulde */
+  {"fy",	{HB_TAG('F','R','I',' ')}},	/* Western Frisian -> Frisian */
+  {"ga",	{HB_TAG('I','R','I',' ')}},	/* Irish */
+  {"gaa",	{HB_TAG('G','A','D',' ')}},	/* Ga */
+  {"gag",	{HB_TAG('G','A','G',' ')}},	/* Gagauz */
+  {"gan",	{HB_TAG('Z','H','S',' ')}},	/* Gan Chinese -> Chinese Simplified */
+  {"gax",	{HB_TAG('O','R','O',' ')}},	/* Borana-Arsi-Guji Oromo -> Oromo */
+  {"gaz",	{HB_TAG('O','R','O',' ')}},	/* West Central Oromo -> Oromo */
+  {"gbm",	{HB_TAG('G','A','W',' ')}},	/* Garhwali */
+  {"gce",	{HB_TAG('A','T','H',' ')}},	/* Galice -> Athapaskan */
+  {"gd",	{HB_TAG('G','A','E',' ')}},	/* Scottish Gaelic (Gaelic) */
+  {"gda",	{HB_TAG('R','A','J',' ')}},	/* Gade Lohar -> Rajasthani */
+  {"gez",	{HB_TAG('G','E','Z',' ')}},	/* Geez */
+  {"ggo",	{HB_TAG('G','O','N',' ')}},	/* Southern Gondi (retired code) -> Gondi */
+  {"gih",	{HB_TAG('G','I','H',' ')}},	/* Githabul */
+  {"gil",	{HB_TAG('G','I','L','0')}},	/* Kiribati (Gilbertese) */
+  {"gju",	{HB_TAG('R','A','J',' ')}},	/* Gujari -> Rajasthani */
+  {"gkp",	{HB_TAG('G','K','P',' ')}},	/* Guinea Kpelle -> Kpelle (Guinea) */
+  {"gl",	{HB_TAG('G','A','L',' ')}},	/* Galician */
+  {"gld",	{HB_TAG('N','A','N',' ')}},	/* Nanai */
+  {"glk",	{HB_TAG('G','L','K',' ')}},	/* Gilaki */
+  {"gn",	{HB_TAG('G','U','A',' ')}},	/* Guarani [macrolanguage] */
+  {"gnn",	{HB_TAG('G','N','N',' ')}},	/* Gumatj */
+  {"gno",	{HB_TAG('G','O','N',' ')}},	/* Northern Gondi -> Gondi */
+  {"gnw",	{HB_TAG('G','U','A',' ')}},	/* Western Bolivian Guaraní -> Guarani */
+  {"gog",	{HB_TAG('G','O','G',' ')}},	/* Gogo */
+  {"gom",	{HB_TAG('K','O','K',' ')}},	/* Goan Konkani -> Konkani */
+  {"gon",	{HB_TAG('G','O','N',' ')}},	/* Gondi [macrolanguage] */
+  {"grt",	{HB_TAG('G','R','O',' ')}},	/* Garo */
+  {"gru",	{HB_TAG('S','O','G',' ')}},	/* Kistane -> Sodo Gurage */
+  {"gsw",	{HB_TAG('A','L','S',' ')}},	/* Alsatian */
+  {"gu",	{HB_TAG('G','U','J',' ')}},	/* Gujarati */
+  {"guc",	{HB_TAG('G','U','C',' ')}},	/* Wayuu */
+  {"guf",	{HB_TAG('G','U','F',' ')}},	/* Gupapuyngu */
+  {"gug",	{HB_TAG('G','U','A',' ')}},	/* Paraguayan Guaraní -> Guarani */
+  {"gui",	{HB_TAG('G','U','A',' ')}},	/* Eastern Bolivian Guaraní -> Guarani */
+  {"guk",	{HB_TAG('G','M','Z',' '),	/* Gumuz */
+		 HB_TAG('G','U','K',' ')}},	/* Gumuz (SIL fonts) */
+  {"gun",	{HB_TAG('G','U','A',' ')}},	/* Mbyá Guaraní -> Guarani */
+  {"guz",	{HB_TAG('G','U','Z',' ')}},	/* Gusii */
+  {"gv",	{HB_TAG('M','N','X',' ')}},	/* Manx */
+  {"gwi",	{HB_TAG('A','T','H',' ')}},	/* Gwichʼin -> Athapaskan */
+  {"ha",	{HB_TAG('H','A','U',' ')}},	/* Hausa */
+  {"haa",	{HB_TAG('A','T','H',' ')}},	/* Han -> Athapaskan */
+  {"hae",	{HB_TAG('O','R','O',' ')}},	/* Eastern Oromo -> Oromo */
+  {"hak",	{HB_TAG('Z','H','S',' ')}},	/* Hakka Chinese -> Chinese Simplified */
+  {"har",	{HB_TAG('H','R','I',' ')}},	/* Harari */
+  {"haw",	{HB_TAG('H','A','W',' ')}},	/* Hawaiian */
+  {"hay",	{HB_TAG('H','A','Y',' ')}},	/* Haya */
+  {"haz",	{HB_TAG('H','A','Z',' ')}},	/* Hazaragi */
+  {"he",	{HB_TAG('I','W','R',' ')}},	/* Hebrew */
+  {"hea",	{HB_TAG('H','M','N',' ')}},	/* Northern Qiandong Miao -> Hmong */
+  {"hi",	{HB_TAG('H','I','N',' ')}},	/* Hindi */
+  {"hil",	{HB_TAG('H','I','L',' ')}},	/* Hiligaynon */
+  {"hji",	{HB_TAG('M','L','Y',' ')}},	/* Haji -> Malay */
+  {"hlt",	{HB_TAG('Q','I','N',' ')}},	/* Matu Chin -> Chin */
+  {"hma",	{HB_TAG('H','M','N',' ')}},	/* Southern Mashan Hmong -> Hmong */
+  {"hmc",	{HB_TAG('H','M','N',' ')}},	/* Central Huishui Hmong -> Hmong */
+  {"hmd",	{HB_TAG('H','M','N',' ')}},	/* Large Flowery Miao -> Hmong */
+  {"hme",	{HB_TAG('H','M','N',' ')}},	/* Eastern Huishui Hmong -> Hmong */
+  {"hmg",	{HB_TAG('H','M','N',' ')}},	/* Southwestern Guiyang Hmong -> Hmong */
+  {"hmh",	{HB_TAG('H','M','N',' ')}},	/* Southwestern Huishui Hmong -> Hmong */
+  {"hmi",	{HB_TAG('H','M','N',' ')}},	/* Northern Huishui Hmong -> Hmong */
+  {"hmj",	{HB_TAG('H','M','N',' ')}},	/* Ge -> Hmong */
+  {"hml",	{HB_TAG('H','M','N',' ')}},	/* Luopohe Hmong -> Hmong */
+  {"hmm",	{HB_TAG('H','M','N',' ')}},	/* Central Mashan Hmong -> Hmong */
+  {"hmn",	{HB_TAG('H','M','N',' ')}},	/* Hmong [macrolanguage] */
+  {"hmp",	{HB_TAG('H','M','N',' ')}},	/* Northern Mashan Hmong -> Hmong */
+  {"hmq",	{HB_TAG('H','M','N',' ')}},	/* Eastern Qiandong Miao -> Hmong */
+  {"hms",	{HB_TAG('H','M','N',' ')}},	/* Southern Qiandong Miao -> Hmong */
+  {"hmw",	{HB_TAG('H','M','N',' ')}},	/* Western Mashan Hmong -> Hmong */
+  {"hmy",	{HB_TAG('H','M','N',' ')}},	/* Southern Guiyang Hmong -> Hmong */
+  {"hmz",	{HB_TAG('H','M','N',' ')}},	/* Hmong Shua -> Hmong */
+  {"hnd",	{HB_TAG('H','N','D',' ')}},	/* Southern Hindko -> Hindko */
+  {"hne",	{HB_TAG('C','H','H',' ')}},	/* Chhattisgarhi -> Chattisgarhi */
+  {"hnj",	{HB_TAG('H','M','N',' ')}},	/* Hmong Njua -> Hmong */
+  {"hno",	{HB_TAG('H','N','D',' ')}},	/* Northern Hindko -> Hindko */
+  {"ho",	{HB_TAG('H','M','O',' ')}},	/* Hiri Motu */
+  {"hoc",	{HB_TAG('H','O',' ',' ')}},	/* Ho */
+  {"hoi",	{HB_TAG('A','T','H',' ')}},	/* Holikachuk -> Athapaskan */
+  {"hoj",	{HB_TAG('H','A','R',' ')}},	/* Hadothi -> Harauti */
+  {"hr",	{HB_TAG('H','R','V',' ')}},	/* Croatian */
+  {"hrm",	{HB_TAG('H','M','N',' ')}},	/* Horned Miao -> Hmong */
+  {"hsb",	{HB_TAG('U','S','B',' ')}},	/* Upper Sorbian */
+  {"hsn",	{HB_TAG('Z','H','S',' ')}},	/* Xiang Chinese -> Chinese Simplified */
+  {"ht",	{HB_TAG('H','A','I',' ')}},	/* Haitian (Haitian Creole) */
+  {"hu",	{HB_TAG('H','U','N',' ')}},	/* Hungarian */
+  {"huj",	{HB_TAG('H','M','N',' ')}},	/* Northern Guiyang Hmong -> Hmong */
+  {"hup",	{HB_TAG('A','T','H',' ')}},	/* Hupa -> Athapaskan */
+  {"hy",	{HB_TAG('H','Y','E','0'),	/* Armenian -> Armenian East */
+		 HB_TAG('H','Y','E',' ')}},	/* Armenian */
+  {"hyw",	{HB_TAG('H','Y','E',' ')}},	/* Western Armenian -> Armenian */
+  {"hz",	{HB_TAG('H','E','R',' ')}},	/* Herero */
+  {"ia",	{HB_TAG('I','N','A',' ')}},	/* Interlingua (International Auxiliary Language Association) */
+  {"iba",	{HB_TAG('I','B','A',' ')}},	/* Iban */
+  {"ibb",	{HB_TAG('I','B','B',' ')}},	/* Ibibio */
+  {"id",	{HB_TAG('I','N','D',' ')}},	/* Indonesian */
+  {"ida",	{HB_TAG('L','U','H',' ')}},	/* Idakho-Isukha-Tiriki -> Luyia */
+  {"ie",	{HB_TAG('I','L','E',' ')}},	/* Interlingue */
+  {"ig",	{HB_TAG('I','B','O',' ')}},	/* Igbo */
+  {"igb",	{HB_TAG('E','B','I',' ')}},	/* Ebira */
+  {"ii",	{HB_TAG('Y','I','M',' ')}},	/* Sichuan Yi -> Yi Modern */
+  {"ijc",	{HB_TAG('I','J','O',' ')}},	/* Izon -> Ijo */
+  {"ijo",	{HB_TAG('I','J','O',' ')}},	/* Ijo [family] */
+  {"ik",	{HB_TAG('I','P','K',' ')}},	/* Inupiaq [macrolanguage] -> Inupiat */
+  {"ike",	{HB_TAG('I','N','U',' ')}},	/* Eastern Canadian Inuktitut -> Inuktitut */
+  {"ikt",	{HB_TAG('I','N','U',' ')}},	/* Inuinnaqtun -> Inuktitut */
+  {"ilo",	{HB_TAG('I','L','O',' ')}},	/* Iloko -> Ilokano */
+  {"in",	{HB_TAG('I','N','D',' ')}},	/* Indonesian (retired code) */
+  {"ing",	{HB_TAG('A','T','H',' ')}},	/* Degexit'an -> Athapaskan */
+  {"inh",	{HB_TAG('I','N','G',' ')}},	/* Ingush */
+  {"io",	{HB_TAG('I','D','O',' ')}},	/* Ido */
+  {"is",	{HB_TAG('I','S','L',' ')}},	/* Icelandic */
+  {"it",	{HB_TAG('I','T','A',' ')}},	/* Italian */
+  {"iu",	{HB_TAG('I','N','U',' ')}},	/* Inuktitut [macrolanguage] */
+  {"iw",	{HB_TAG('I','W','R',' ')}},	/* Hebrew (retired code) */
+  {"ja",	{HB_TAG('J','A','N',' ')}},	/* Japanese */
+  {"jak",	{HB_TAG('M','L','Y',' ')}},	/* Jakun -> Malay */
+  {"jam",	{HB_TAG('J','A','M',' ')}},	/* Jamaican Creole English -> Jamaican Creole */
+  {"jax",	{HB_TAG('M','L','Y',' ')}},	/* Jambi Malay -> Malay */
+  {"jbo",	{HB_TAG('J','B','O',' ')}},	/* Lojban */
+  {"jct",	{HB_TAG('J','C','T',' ')}},	/* Krymchak */
+  {"ji",	{HB_TAG('J','I','I',' ')}},	/* Yiddish (retired code) */
+  {"jv",	{HB_TAG('J','A','V',' ')}},	/* Javanese */
+  {"jw",	{HB_TAG('J','A','V',' ')}},	/* Javanese (retired code) */
+  {"ka",	{HB_TAG('K','A','T',' ')}},	/* Georgian */
+  {"kaa",	{HB_TAG('K','R','K',' ')}},	/* Kara-Kalpak -> Karakalpak */
+  {"kab",	{HB_TAG('K','A','B','0')}},	/* Kabyle */
+  {"kam",	{HB_TAG('K','M','B',' ')}},	/* Kamba (Kenya) */
+  {"kar",	{HB_TAG('K','R','N',' ')}},	/* Karen [family] */
+  {"kbd",	{HB_TAG('K','A','B',' ')}},	/* Kabardian */
+  {"kby",	{HB_TAG('K','N','R',' ')}},	/* Manga Kanuri -> Kanuri */
+  {"kca",	{HB_TAG('K','H','K',' '),	/* Khanty -> Khanty-Kazim */
+		 HB_TAG('K','H','S',' '),	/* Khanty -> Khanty-Shurishkar */
+		 HB_TAG('K','H','V',' ')}},	/* Khanty -> Khanty-Vakhi */
+  {"kde",	{HB_TAG('K','D','E',' ')}},	/* Makonde */
+  {"kdr",	{HB_TAG('K','R','M',' ')}},	/* Karaim */
+  {"kdt",	{HB_TAG('K','U','Y',' ')}},	/* Kuy */
+  {"kea",	{HB_TAG('K','E','A',' ')}},	/* Kabuverdianu (Crioulo) */
+  {"kek",	{HB_TAG('K','E','K',' ')}},	/* Kekchi */
+  {"kex",	{HB_TAG('K','K','N',' ')}},	/* Kukna -> Kokni */
+  {"kfa",	{HB_TAG('K','O','D',' ')}},	/* Kodava -> Kodagu */
+  {"kfr",	{HB_TAG('K','A','C',' ')}},	/* Kachhi -> Kachchi */
+  {"kfx",	{HB_TAG('K','U','L',' ')}},	/* Kullu Pahari -> Kulvi */
+  {"kfy",	{HB_TAG('K','M','N',' ')}},	/* Kumaoni */
+  {"kg",	{HB_TAG('K','O','N','0')}},	/* Kongo [macrolanguage] */
+  {"kha",	{HB_TAG('K','S','I',' ')}},	/* Khasi */
+  {"khb",	{HB_TAG('X','B','D',' ')}},	/* Lü */
+  {"khk",	{HB_TAG('M','N','G',' ')}},	/* Halh Mongolian -> Mongolian */
+  {"kht",	{HB_TAG('K','H','N',' '),	/* Khamti -> Khamti Shan (Microsoft fonts) */
+		 HB_TAG('K','H','T',' ')}},	/* Khamti -> Khamti Shan (OpenType spec and SIL fonts) */
+  {"khw",	{HB_TAG('K','H','W',' ')}},	/* Khowar */
+  {"ki",	{HB_TAG('K','I','K',' ')}},	/* Kikuyu (Gikuyu) */
+  {"kiu",	{HB_TAG('K','I','U',' ')}},	/* Kirmanjki */
+  {"kj",	{HB_TAG('K','U','A',' ')}},	/* Kuanyama */
+  {"kjd",	{HB_TAG('K','J','D',' ')}},	/* Southern Kiwai */
+  {"kjh",	{HB_TAG('K','H','A',' ')}},	/* Khakas -> Khakass */
+  {"kjp",	{HB_TAG('K','J','P',' ')}},	/* Pwo Eastern Karen -> Eastern Pwo Karen */
+  {"kjz",	{HB_TAG('K','J','Z',' ')}},	/* Bumthangkha */
+  {"kk",	{HB_TAG('K','A','Z',' ')}},	/* Kazakh */
+  {"kkz",	{HB_TAG('A','T','H',' ')}},	/* Kaska -> Athapaskan */
+  {"kl",	{HB_TAG('G','R','N',' ')}},	/* Greenlandic */
+  {"kln",	{HB_TAG('K','A','L',' ')}},	/* Kalenjin [macrolanguage] */
+  {"km",	{HB_TAG('K','H','M',' ')}},	/* Khmer */
+  {"kmb",	{HB_TAG('M','B','N',' ')}},	/* Kimbundu -> Mbundu */
+  {"kmr",	{HB_TAG('K','U','R',' ')}},	/* Northern Kurdish -> Kurdish */
+  {"kmw",	{HB_TAG('K','M','O',' ')}},	/* Komo (Democratic Republic of Congo) */
+  {"kmz",	{HB_TAG('K','M','Z',' ')}},	/* Khorasani Turkish -> Khorasani Turkic */
+  {"kn",	{HB_TAG('K','A','N',' ')}},	/* Kannada */
+  {"knc",	{HB_TAG('K','N','R',' ')}},	/* Central Kanuri -> Kanuri */
+  {"kng",	{HB_TAG('K','O','N','0')}},	/* Koongo -> Kongo */
+  {"knn",	{HB_TAG('K','O','K',' ')}},	/* Konkani */
+  {"ko",	{HB_TAG('K','O','R',' ')}},	/* Korean */
+  {"koi",	{HB_TAG('K','O','P',' ')}},	/* Komi-Permyak */
+  {"kok",	{HB_TAG('K','O','K',' ')}},	/* Konkani [macrolanguage] */
+  {"kos",	{HB_TAG('K','O','S',' ')}},	/* Kosraean */
+  {"koy",	{HB_TAG('A','T','H',' ')}},	/* Koyukon -> Athapaskan */
+  {"kpe",	{HB_TAG('K','P','L',' ')}},	/* Kpelle [macrolanguage] */
+  {"kpv",	{HB_TAG('K','O','Z',' ')}},	/* Komi-Zyrian */
+  {"kpy",	{HB_TAG('K','Y','K',' ')}},	/* Koryak */
+  {"kqs",	{HB_TAG('K','I','S',' ')}},	/* Northern Kissi -> Kisii */
+  {"kqy",	{HB_TAG('K','R','T',' ')}},	/* Koorete */
+  {"kr",	{HB_TAG('K','N','R',' ')}},	/* Kanuri [macrolanguage] */
+  {"krc",	{HB_TAG('K','A','R',' '),	/* Karachay-Balkar -> Karachay */
+		 HB_TAG('B','A','L',' ')}},	/* Karachay-Balkar -> Balkar */
+  {"kri",	{HB_TAG('K','R','I',' ')}},	/* Krio */
+  {"krl",	{HB_TAG('K','R','L',' ')}},	/* Karelian */
+  {"krt",	{HB_TAG('K','N','R',' ')}},	/* Tumari Kanuri -> Kanuri */
+  {"kru",	{HB_TAG('K','U','U',' ')}},	/* Kurukh */
+  {"ks",	{HB_TAG('K','S','H',' ')}},	/* Kashmiri */
+  {"ksh",	{HB_TAG('K','S','H','0')}},	/* Kölsch -> Ripuarian */
+  {"kss",	{HB_TAG('K','I','S',' ')}},	/* Southern Kisi -> Kisii */
+  {"ksw",	{HB_TAG('K','S','W',' ')}},	/* S’gaw Karen */
+  {"ktb",	{HB_TAG('K','E','B',' ')}},	/* Kambaata -> Kebena */
+  {"ktu",	{HB_TAG('K','O','N',' ')}},	/* Kituba (Democratic Republic of Congo) -> Kikongo */
+  {"ktw",	{HB_TAG('A','T','H',' ')}},	/* Kato -> Athapaskan */
+  {"ku",	{HB_TAG('K','U','R',' ')}},	/* Kurdish [macrolanguage] */
+  {"kum",	{HB_TAG('K','U','M',' ')}},	/* Kumyk */
+  {"kuu",	{HB_TAG('A','T','H',' ')}},	/* Upper Kuskokwim -> Athapaskan */
+  {"kv",	{HB_TAG('K','O','M',' ')}},	/* Komi [macrolanguage] */
+  {"kvb",	{HB_TAG('M','L','Y',' ')}},	/* Kubu -> Malay */
+  {"kvr",	{HB_TAG('M','L','Y',' ')}},	/* Kerinci -> Malay */
+  {"kw",	{HB_TAG('C','O','R',' ')}},	/* Cornish */
+  {"kwy",	{HB_TAG('K','O','N','0')}},	/* San Salvador Kongo -> Kongo */
+  {"kxc",	{HB_TAG('K','M','S',' ')}},	/* Konso -> Komso */
+  {"kxd",	{HB_TAG('M','L','Y',' ')}},	/* Brunei -> Malay */
+  {"kxu",	{HB_TAG('K','U','I',' ')}},	/* Kui (India) */
+  {"ky",	{HB_TAG('K','I','R',' ')}},	/* Kirghiz (Kyrgyz) */
+  {"kyu",	{HB_TAG('K','Y','U',' ')}},	/* Western Kayah */
+  {"la",	{HB_TAG('L','A','T',' ')}},	/* Latin */
+  {"lad",	{HB_TAG('J','U','D',' ')}},	/* Ladino */
+  {"lb",	{HB_TAG('L','T','Z',' ')}},	/* Luxembourgish */
+  {"lbe",	{HB_TAG('L','A','K',' ')}},	/* Lak */
+  {"lbj",	{HB_TAG('L','D','K',' ')}},	/* Ladakhi */
+  {"lbl",	{HB_TAG('B','I','K',' ')}},	/* Libon Bikol -> Bikol */
+  {"lce",	{HB_TAG('M','L','Y',' ')}},	/* Loncong -> Malay */
+  {"lcf",	{HB_TAG('M','L','Y',' ')}},	/* Lubu -> Malay */
+  {"ldi",	{HB_TAG('K','O','N','0')}},	/* Laari -> Kongo */
+  {"lez",	{HB_TAG('L','E','Z',' ')}},	/* Lezghian -> Lezgi */
+  {"lg",	{HB_TAG('L','U','G',' ')}},	/* Ganda */
+  {"li",	{HB_TAG('L','I','M',' ')}},	/* Limburgish */
+  {"lif",	{HB_TAG('L','M','B',' ')}},	/* Limbu */
+  {"lij",	{HB_TAG('L','I','J',' ')}},	/* Ligurian */
+  {"lis",	{HB_TAG('L','I','S',' ')}},	/* Lisu */
+  {"liw",	{HB_TAG('M','L','Y',' ')}},	/* Col -> Malay */
+  {"ljp",	{HB_TAG('L','J','P',' ')}},	/* Lampung Api -> Lampung */
+  {"lkb",	{HB_TAG('L','U','H',' ')}},	/* Kabras -> Luyia */
+  {"lki",	{HB_TAG('L','K','I',' ')}},	/* Laki */
+  {"lko",	{HB_TAG('L','U','H',' ')}},	/* Khayo -> Luyia */
+  {"lks",	{HB_TAG('L','U','H',' ')}},	/* Kisa -> Luyia */
+  {"lld",	{HB_TAG('L','A','D',' ')}},	/* Ladin */
+  {"lmn",	{HB_TAG('L','A','M',' ')}},	/* Lambadi -> Lambani */
+  {"lmo",	{HB_TAG('L','M','O',' ')}},	/* Lombard */
+  {"ln",	{HB_TAG('L','I','N',' ')}},	/* Lingala */
+  {"lo",	{HB_TAG('L','A','O',' ')}},	/* Lao */
+  {"lom",	{HB_TAG('L','O','M',' ')}},	/* Loma (Liberia) */
+  {"lrc",	{HB_TAG('L','R','C',' ')}},	/* Northern Luri -> Luri */
+  {"lri",	{HB_TAG('L','U','H',' ')}},	/* Marachi -> Luyia */
+  {"lrm",	{HB_TAG('L','U','H',' ')}},	/* Marama -> Luyia */
+  {"lsm",	{HB_TAG('L','U','H',' ')}},	/* Saamia -> Luyia */
+  {"lt",	{HB_TAG('L','T','H',' ')}},	/* Lithuanian */
+  {"ltg",	{HB_TAG('L','V','I',' ')}},	/* Latgalian -> Latvian */
+  {"lto",	{HB_TAG('L','U','H',' ')}},	/* Tsotso -> Luyia */
+  {"lts",	{HB_TAG('L','U','H',' ')}},	/* Tachoni -> Luyia */
+  {"lu",	{HB_TAG('L','U','B',' ')}},	/* Luba-Katanga */
+  {"lua",	{HB_TAG('L','U','A',' ')}},	/* Luba-Lulua */
+  {"luo",	{HB_TAG('L','U','O',' ')}},	/* Luo (Kenya and Tanzania) */
+  {"lus",	{HB_TAG('M','I','Z',' ')}},	/* Lushai -> Mizo */
+  {"luy",	{HB_TAG('L','U','H',' ')}},	/* Luyia [macrolanguage] */
+  {"luz",	{HB_TAG('L','R','C',' ')}},	/* Southern Luri -> Luri */
+  {"lv",	{HB_TAG('L','V','I',' ')}},	/* Latvian [macrolanguage] */
+  {"lvs",	{HB_TAG('L','V','I',' ')}},	/* Standard Latvian -> Latvian */
+  {"lwg",	{HB_TAG('L','U','H',' ')}},	/* Wanga -> Luyia */
+  {"lzh",	{HB_TAG('Z','H','T',' ')}},	/* Literary Chinese -> Chinese Traditional */
+  {"lzz",	{HB_TAG('L','A','Z',' ')}},	/* Laz */
+  {"mad",	{HB_TAG('M','A','D',' ')}},	/* Madurese -> Madura */
+  {"mag",	{HB_TAG('M','A','G',' ')}},	/* Magahi */
+  {"mai",	{HB_TAG('M','T','H',' ')}},	/* Maithili */
+  {"mak",	{HB_TAG('M','K','R',' ')}},	/* Makasar */
+  {"mam",	{HB_TAG('M','A','M',' ')}},	/* Mam */
+  {"man",	{HB_TAG('M','N','K',' ')}},	/* Mandingo [macrolanguage] -> Maninka */
+  {"max",	{HB_TAG('M','L','Y',' ')}},	/* North Moluccan Malay -> Malay */
+  {"mbo",	{HB_TAG('M','B','O',' ')}},	/* Mbo (Cameroon) */
+  {"mct",	{HB_TAG('B','T','I',' ')}},	/* Mengisa -> Beti */
+  {"mdf",	{HB_TAG('M','O','K',' ')}},	/* Moksha */
+  {"mdr",	{HB_TAG('M','D','R',' ')}},	/* Mandar */
+  {"mdy",	{HB_TAG('M','L','E',' ')}},	/* Male (Ethiopia) */
+  {"men",	{HB_TAG('M','D','E',' ')}},	/* Mende (Sierra Leone) */
+  {"meo",	{HB_TAG('M','L','Y',' ')}},	/* Kedah Malay -> Malay */
+  {"mer",	{HB_TAG('M','E','R',' ')}},	/* Meru */
+  {"mfa",	{HB_TAG('M','F','A',' ')}},	/* Pattani Malay */
+  {"mfb",	{HB_TAG('M','L','Y',' ')}},	/* Bangka -> Malay */
+  {"mfe",	{HB_TAG('M','F','E',' ')}},	/* Morisyen */
+  {"mg",	{HB_TAG('M','L','G',' ')}},	/* Malagasy [macrolanguage] */
+  {"mh",	{HB_TAG('M','A','H',' ')}},	/* Marshallese */
+  {"mhr",	{HB_TAG('L','M','A',' ')}},	/* Eastern Mari -> Low Mari */
+  {"mhv",	{HB_TAG('A','R','K',' ')}},	/* Arakanese (retired code) -> Rakhine */
+  {"mi",	{HB_TAG('M','R','I',' ')}},	/* Maori */
+  {"min",	{HB_TAG('M','I','N',' ')}},	/* Minangkabau */
+  {"mk",	{HB_TAG('M','K','D',' ')}},	/* Macedonian */
+  {"mku",	{HB_TAG('M','N','K',' ')}},	/* Konyanka Maninka -> Maninka */
+  {"mkw",	{HB_TAG('M','K','W',' ')}},	/* Kituba (Congo) */
+  {"ml",	{HB_TAG('M','A','L',' '),	/* Malayalam -> Malayalam Traditional */
+		 HB_TAG('M','L','R',' ')}},	/* Malayalam -> Malayalam Reformed */
+  {"mlq",	{HB_TAG('M','L','N',' '),	/* Western Maninkakan -> Malinke */
+		 HB_TAG('M','N','K',' ')}},	/* Western Maninkakan -> Maninka */
+  {"mmr",	{HB_TAG('H','M','N',' ')}},	/* Western Xiangxi Miao -> Hmong */
+  {"mn",	{HB_TAG('M','N','G',' ')}},	/* Mongolian [macrolanguage] */
+  {"mnc",	{HB_TAG('M','C','H',' ')}},	/* Manchu */
+  {"mni",	{HB_TAG('M','N','I',' ')}},	/* Manipuri */
+  {"mnk",	{HB_TAG('M','N','D',' '),	/* Mandinka */
+		 HB_TAG('M','N','K',' ')}},	/* Mandinka -> Maninka */
+  {"mnp",	{HB_TAG('Z','H','S',' ')}},	/* Min Bei Chinese -> Chinese Simplified */
+  {"mns",	{HB_TAG('M','A','N',' ')}},	/* Mansi */
+  {"mnw",	{HB_TAG('M','O','N',' ')}},	/* Mon */
+  {"mo",	{HB_TAG('M','O','L',' ')}},	/* Moldavian (retired code) */
+  {"moh",	{HB_TAG('M','O','H',' ')}},	/* Mohawk */
+  {"mos",	{HB_TAG('M','O','S',' ')}},	/* Mossi */
+  {"mpe",	{HB_TAG('M','A','J',' ')}},	/* Majang */
+  {"mqg",	{HB_TAG('M','L','Y',' ')}},	/* Kota Bangun Kutai Malay -> Malay */
+  {"mr",	{HB_TAG('M','A','R',' ')}},	/* Marathi */
+  {"mrh",	{HB_TAG('Q','I','N',' ')}},	/* Mara Chin -> Chin */
+  {"mrj",	{HB_TAG('H','M','A',' ')}},	/* Western Mari -> High Mari */
+  {"ms",	{HB_TAG('M','L','Y',' ')}},	/* Malay [macrolanguage] */
+  {"msc",	{HB_TAG('M','N','K',' ')}},	/* Sankaran Maninka -> Maninka */
+  {"msh",	{HB_TAG('M','L','G',' ')}},	/* Masikoro Malagasy -> Malagasy */
+  {"msi",	{HB_TAG('M','L','Y',' ')}},	/* Sabah Malay -> Malay */
+  {"mt",	{HB_TAG('M','T','S',' ')}},	/* Maltese */
+  {"mtr",	{HB_TAG('M','A','W',' ')}},	/* Mewari -> Marwari */
+  {"mui",	{HB_TAG('M','L','Y',' ')}},	/* Musi -> Malay */
+  {"mup",	{HB_TAG('R','A','J',' ')}},	/* Malvi -> Rajasthani */
+  {"muq",	{HB_TAG('H','M','N',' ')}},	/* Eastern Xiangxi Miao -> Hmong */
+  {"mus",	{HB_TAG('M','U','S',' ')}},	/* Creek -> Muscogee */
+  {"mvb",	{HB_TAG('A','T','H',' ')}},	/* Mattole -> Athapaskan */
+  {"mve",	{HB_TAG('M','A','W',' ')}},	/* Marwari (Pakistan) */
+  {"mvf",	{HB_TAG('M','N','G',' ')}},	/* Peripheral Mongolian -> Mongolian */
+  {"mwk",	{HB_TAG('M','N','K',' ')}},	/* Kita Maninkakan -> Maninka */
+  {"mwl",	{HB_TAG('M','W','L',' ')}},	/* Mirandese */
+  {"mwr",	{HB_TAG('M','A','W',' ')}},	/* Marwari [macrolanguage] */
+  {"mww",	{HB_TAG('M','W','W',' ')}},	/* Hmong Daw */
+  {"my",	{HB_TAG('B','R','M',' ')}},	/* Burmese */
+  {"mym",	{HB_TAG('M','E','N',' ')}},	/* Me'en */
+  {"myn",	{HB_TAG('M','Y','N',' ')}},	/* Mayan [family] */
+  {"myq",	{HB_TAG('M','N','K',' ')}},	/* Forest Maninka (retired code) -> Maninka */
+  {"myv",	{HB_TAG('E','R','Z',' ')}},	/* Erzya */
+  {"mzn",	{HB_TAG('M','Z','N',' ')}},	/* Mazanderani */
+  {"na",	{HB_TAG('N','A','U',' ')}},	/* Nauru -> Nauruan */
+  {"nag",	{HB_TAG('N','A','G',' ')}},	/* Naga Pidgin -> Naga-Assamese */
+  {"nah",	{HB_TAG('N','A','H',' ')}},	/* Nahuatl [family] */
+  {"nan",	{HB_TAG('Z','H','S',' ')}},	/* Min Nan Chinese -> Chinese Simplified */
+  {"nap",	{HB_TAG('N','A','P',' ')}},	/* Neapolitan */
+  {"nb",	{HB_TAG('N','O','R',' ')}},	/* Norwegian Bokmål -> Norwegian */
+  {"nd",	{HB_TAG('N','D','B',' ')}},	/* North Ndebele -> Ndebele */
+  {"ndc",	{HB_TAG('N','D','C',' ')}},	/* Ndau */
+  {"nds",	{HB_TAG('N','D','S',' ')}},	/* Low Saxon */
+  {"ne",	{HB_TAG('N','E','P',' ')}},	/* Nepali [macrolanguage] */
+  {"new",	{HB_TAG('N','E','W',' ')}},	/* Newari */
+  {"ng",	{HB_TAG('N','D','G',' ')}},	/* Ndonga */
+  {"nga",	{HB_TAG('N','G','A',' ')}},	/* Ngbaka */
+  {"ngl",	{HB_TAG('L','M','W',' ')}},	/* Lomwe */
+  {"ngo",	{HB_TAG('S','X','T',' ')}},	/* Ngoni -> Sutu */
+  {"nhd",	{HB_TAG('G','U','A',' ')}},	/* Chiripá -> Guarani */
+  {"niq",	{HB_TAG('K','A','L',' ')}},	/* Nandi -> Kalenjin */
+  {"niu",	{HB_TAG('N','I','U',' ')}},	/* Niuean */
+  {"niv",	{HB_TAG('G','I','L',' ')}},	/* Gilyak */
+  {"njz",	{HB_TAG('N','I','S',' ')}},	/* Nyishi -> Nisi */
+  {"nl",	{HB_TAG('N','L','D',' ')}},	/* Dutch */
+  {"nle",	{HB_TAG('L','U','H',' ')}},	/* East Nyala -> Luyia */
+  {"nn",	{HB_TAG('N','Y','N',' ')}},	/* Norwegian Nynorsk (Nynorsk, Norwegian) */
+  {"no",	{HB_TAG('N','O','R',' ')}},	/* Norwegian [macrolanguage] */
+  {"nod",	{HB_TAG('N','T','A',' ')}},	/* Northern Thai -> Northern Tai */
+  {"noe",	{HB_TAG('N','O','E',' ')}},	/* Nimadi */
+  {"nog",	{HB_TAG('N','O','G',' ')}},	/* Nogai */
+  {"nov",	{HB_TAG('N','O','V',' ')}},	/* Novial */
+  {"npi",	{HB_TAG('N','E','P',' ')}},	/* Nepali */
+  {"nqo",	{HB_TAG('N','K','O',' ')}},	/* N'Ko */
+  {"nr",	{HB_TAG('N','D','B',' ')}},	/* South Ndebele -> Ndebele */
+  {"nsk",	{HB_TAG('N','A','S',' ')}},	/* Naskapi */
+  {"nso",	{HB_TAG('N','S','O',' ')}},	/* Pedi -> Sotho, Northern */
+  {"nv",	{HB_TAG('N','A','V',' '),	/* Navajo */
+		 HB_TAG('A','T','H',' ')}},	/* Navajo -> Athapaskan */
+  {"ny",	{HB_TAG('C','H','I',' ')}},	/* Chichewa (Chewa, Nyanja) */
+  {"nyd",	{HB_TAG('L','U','H',' ')}},	/* Nyore -> Luyia */
+  {"nym",	{HB_TAG('N','Y','M',' ')}},	/* Nyamwezi */
+  {"nyn",	{HB_TAG('N','K','L',' ')}},	/* Nyankole */
+  {"nza",	{HB_TAG('N','Z','A',' ')}},	/* Tigon Mbembe -> Mbembe Tigon */
+  {"oc",	{HB_TAG('O','C','I',' ')}},	/* Occitan (post 1500) */
+  {"oj",	{HB_TAG('O','J','B',' ')}},	/* Ojibwa [macrolanguage] -> Ojibway */
+  {"ojb",	{HB_TAG('O','J','B',' ')}},	/* Northwestern Ojibwa -> Ojibway */
+  {"ojc",	{HB_TAG('O','J','B',' ')}},	/* Central Ojibwa -> Ojibway */
+  {"ojg",	{HB_TAG('O','J','B',' ')}},	/* Eastern Ojibwa -> Ojibway */
+  {"ojs",	{HB_TAG('O','C','R',' ')}},	/* Severn Ojibwa -> Oji-Cree */
+  {"ojw",	{HB_TAG('O','J','B',' ')}},	/* Western Ojibwa -> Ojibway */
+  {"oki",	{HB_TAG('K','A','L',' ')}},	/* Okiek -> Kalenjin */
+  {"okm",	{HB_TAG('K','O','H',' ')}},	/* Middle Korean (10th-16th cent.) -> Korean Old Hangul */
+  {"om",	{HB_TAG('O','R','O',' ')}},	/* Oromo [macrolanguage] */
+  {"or",	{HB_TAG('O','R','I',' ')}},	/* Odia (formerly Oriya) [macrolanguage] */
+  {"orc",	{HB_TAG('O','R','O',' ')}},	/* Orma -> Oromo */
+  {"orn",	{HB_TAG('M','L','Y',' ')}},	/* Orang Kanaq -> Malay */
+  {"ors",	{HB_TAG('M','L','Y',' ')}},	/* Orang Seletar -> Malay */
+  {"ory",	{HB_TAG('O','R','I',' ')}},	/* Odia (formerly Oriya) */
+  {"os",	{HB_TAG('O','S','S',' ')}},	/* Ossetian */
+  {"otw",	{HB_TAG('O','J','B',' ')}},	/* Ottawa -> Ojibway */
+  {"pa",	{HB_TAG('P','A','N',' ')}},	/* Punjabi */
+  {"pag",	{HB_TAG('P','A','G',' ')}},	/* Pangasinan */
+  {"pam",	{HB_TAG('P','A','M',' ')}},	/* Pampanga -> Pampangan */
+  {"pap",	{HB_TAG('P','A','P','0')}},	/* Papiamento -> Papiamentu */
+  {"pau",	{HB_TAG('P','A','U',' ')}},	/* Palauan */
+  {"pbt",	{HB_TAG('P','A','S',' ')}},	/* Southern Pashto -> Pashto */
+  {"pbu",	{HB_TAG('P','A','S',' ')}},	/* Northern Pashto -> Pashto */
+  {"pcc",	{HB_TAG('P','C','C',' ')}},	/* Bouyei */
+  {"pcd",	{HB_TAG('P','C','D',' ')}},	/* Picard */
+  {"pce",	{HB_TAG('P','L','G',' ')}},	/* Ruching Palaung -> Palaung */
+  {"pck",	{HB_TAG('Q','I','N',' ')}},	/* Paite Chin -> Chin */
+  {"pdc",	{HB_TAG('P','D','C',' ')}},	/* Pennsylvania German */
+  {"pel",	{HB_TAG('M','L','Y',' ')}},	/* Pekal -> Malay */
+  {"pes",	{HB_TAG('F','A','R',' ')}},	/* Iranian Persian -> Persian */
+  {"pga",	{HB_TAG('A','R','A',' ')}},	/* Sudanese Creole Arabic -> Arabic */
+  {"phk",	{HB_TAG('P','H','K',' ')}},	/* Phake */
+  {"pi",	{HB_TAG('P','A','L',' ')}},	/* Pali */
+  {"pih",	{HB_TAG('P','I','H',' ')}},	/* Pitcairn-Norfolk -> Norfolk */
+  {"pko",	{HB_TAG('K','A','L',' ')}},	/* Pökoot -> Kalenjin */
+  {"pl",	{HB_TAG('P','L','K',' ')}},	/* Polish */
+  {"pll",	{HB_TAG('P','L','G',' ')}},	/* Shwe Palaung -> Palaung */
+  {"plp",	{HB_TAG('P','A','P',' ')}},	/* Palpa */
+  {"plt",	{HB_TAG('M','L','G',' ')}},	/* Plateau Malagasy -> Malagasy */
+  {"pms",	{HB_TAG('P','M','S',' ')}},	/* Piemontese */
+  {"pnb",	{HB_TAG('P','N','B',' ')}},	/* Western Panjabi */
+  {"poh",	{HB_TAG('P','O','H',' ')}},	/* Poqomchi' -> Pocomchi */
+  {"pon",	{HB_TAG('P','O','N',' ')}},	/* Pohnpeian */
+  {"ppa",	{HB_TAG('B','A','G',' ')}},	/* Pao (retired code) -> Baghelkhandi */
+  {"pro",	{HB_TAG('P','R','O',' ')}},	/* Old Provençal (to 1500) -> Provençal / Old Provençal */
+  {"prs",	{HB_TAG('D','R','I',' ')}},	/* Dari */
+  {"ps",	{HB_TAG('P','A','S',' ')}},	/* Pashto [macrolanguage] */
+  {"pse",	{HB_TAG('M','L','Y',' ')}},	/* Central Malay -> Malay */
+  {"pst",	{HB_TAG('P','A','S',' ')}},	/* Central Pashto -> Pashto */
+  {"pt",	{HB_TAG('P','T','G',' ')}},	/* Portuguese */
+  {"pwo",	{HB_TAG('P','W','O',' ')}},	/* Pwo Western Karen -> Western Pwo Karen */
+  {"qu",	{HB_TAG('Q','U','Z',' ')}},	/* Quechua [macrolanguage] */
+  {"qub",	{HB_TAG('Q','W','H',' ')}},	/* Huallaga Huánuco Quechua -> Quechua (Peru) */
+  {"quc",	{HB_TAG('Q','U','C',' ')}},	/* K’iche’ */
+  {"qud",	{HB_TAG('Q','V','I',' ')}},	/* Calderón Highland Quichua -> Quechua (Ecuador) */
+  {"quf",	{HB_TAG('Q','U','Z',' ')}},	/* Lambayeque Quechua -> Quechua */
+  {"qug",	{HB_TAG('Q','V','I',' ')}},	/* Chimborazo Highland Quichua -> Quechua (Ecuador) */
+  {"quh",	{HB_TAG('Q','U','H',' ')}},	/* South Bolivian Quechua -> Quechua (Bolivia) */
+  {"quk",	{HB_TAG('Q','U','Z',' ')}},	/* Chachapoyas Quechua -> Quechua */
+  {"qul",	{HB_TAG('Q','U','Z',' ')}},	/* North Bolivian Quechua -> Quechua */
+  {"qup",	{HB_TAG('Q','V','I',' ')}},	/* Southern Pastaza Quechua -> Quechua (Ecuador) */
+  {"qur",	{HB_TAG('Q','W','H',' ')}},	/* Yanahuanca Pasco Quechua -> Quechua (Peru) */
+  {"qus",	{HB_TAG('Q','U','H',' ')}},	/* Santiago del Estero Quichua -> Quechua (Bolivia) */
+  {"quw",	{HB_TAG('Q','V','I',' ')}},	/* Tena Lowland Quichua -> Quechua (Ecuador) */
+  {"qux",	{HB_TAG('Q','W','H',' ')}},	/* Yauyos Quechua -> Quechua (Peru) */
+  {"quy",	{HB_TAG('Q','U','Z',' ')}},	/* Ayacucho Quechua -> Quechua */
+  {"quz",	{HB_TAG('Q','U','Z',' ')}},	/* Cusco Quechua -> Quechua */
+  {"qva",	{HB_TAG('Q','W','H',' ')}},	/* Ambo-Pasco Quechua -> Quechua (Peru) */
+  {"qvc",	{HB_TAG('Q','U','Z',' ')}},	/* Cajamarca Quechua -> Quechua */
+  {"qve",	{HB_TAG('Q','U','Z',' ')}},	/* Eastern Apurímac Quechua -> Quechua */
+  {"qvh",	{HB_TAG('Q','W','H',' ')}},	/* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */
+  {"qvi",	{HB_TAG('Q','V','I',' ')}},	/* Imbabura Highland Quichua -> Quechua (Ecuador) */
+  {"qvj",	{HB_TAG('Q','V','I',' ')}},	/* Loja Highland Quichua -> Quechua (Ecuador) */
+  {"qvl",	{HB_TAG('Q','W','H',' ')}},	/* Cajatambo North Lima Quechua -> Quechua (Peru) */
+  {"qvm",	{HB_TAG('Q','W','H',' ')}},	/* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */
+  {"qvn",	{HB_TAG('Q','W','H',' ')}},	/* North Junín Quechua -> Quechua (Peru) */
+  {"qvo",	{HB_TAG('Q','V','I',' ')}},	/* Napo Lowland Quechua -> Quechua (Ecuador) */
+  {"qvp",	{HB_TAG('Q','W','H',' ')}},	/* Pacaraos Quechua -> Quechua (Peru) */
+  {"qvs",	{HB_TAG('Q','U','Z',' ')}},	/* San Martín Quechua -> Quechua */
+  {"qvw",	{HB_TAG('Q','W','H',' ')}},	/* Huaylla Wanca Quechua -> Quechua (Peru) */
+  {"qvz",	{HB_TAG('Q','V','I',' ')}},	/* Northern Pastaza Quichua -> Quechua (Ecuador) */
+  {"qwa",	{HB_TAG('Q','W','H',' ')}},	/* Corongo Ancash Quechua -> Quechua (Peru) */
+  {"qwc",	{HB_TAG('Q','U','Z',' ')}},	/* Classical Quechua -> Quechua */
+  {"qwh",	{HB_TAG('Q','W','H',' ')}},	/* Huaylas Ancash Quechua -> Quechua (Peru) */
+  {"qws",	{HB_TAG('Q','W','H',' ')}},	/* Sihuas Ancash Quechua -> Quechua (Peru) */
+  {"qxa",	{HB_TAG('Q','W','H',' ')}},	/* Chiquián Ancash Quechua -> Quechua (Peru) */
+  {"qxc",	{HB_TAG('Q','W','H',' ')}},	/* Chincha Quechua -> Quechua (Peru) */
+  {"qxh",	{HB_TAG('Q','W','H',' ')}},	/* Panao Huánuco Quechua -> Quechua (Peru) */
+  {"qxl",	{HB_TAG('Q','V','I',' ')}},	/* Salasaca Highland Quichua -> Quechua (Ecuador) */
+  {"qxn",	{HB_TAG('Q','W','H',' ')}},	/* Northern Conchucos Ancash Quechua -> Quechua (Peru) */
+  {"qxo",	{HB_TAG('Q','W','H',' ')}},	/* Southern Conchucos Ancash Quechua -> Quechua (Peru) */
+  {"qxp",	{HB_TAG('Q','U','Z',' ')}},	/* Puno Quechua -> Quechua */
+  {"qxr",	{HB_TAG('Q','V','I',' ')}},	/* Cañar Highland Quichua -> Quechua (Ecuador) */
+  {"qxt",	{HB_TAG('Q','W','H',' ')}},	/* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */
+  {"qxu",	{HB_TAG('Q','U','Z',' ')}},	/* Arequipa-La Unión Quechua -> Quechua */
+  {"qxw",	{HB_TAG('Q','W','H',' ')}},	/* Jauja Wanca Quechua -> Quechua (Peru) */
+  {"rag",	{HB_TAG('L','U','H',' ')}},	/* Logooli -> Luyia */
+  {"raj",	{HB_TAG('R','A','J',' ')}},	/* Rajasthani [macrolanguage] */
+  {"rar",	{HB_TAG('R','A','R',' ')}},	/* Rarotongan */
+  {"rbb",	{HB_TAG('P','L','G',' ')}},	/* Rumai Palaung -> Palaung */
+  {"rbl",	{HB_TAG('B','I','K',' ')}},	/* Miraya Bikol -> Bikol */
+  {"rej",	{HB_TAG('R','E','J',' ')}},	/* Rejang */
+  {"ria",	{HB_TAG('R','I','A',' ')}},	/* Riang (India) */
+  {"rif",	{HB_TAG('R','I','F',' ')}},	/* Tarifit */
+  {"rit",	{HB_TAG('R','I','T',' ')}},	/* Ritarungo */
+  {"rki",	{HB_TAG('A','R','K',' ')}},	/* Rakhine */
+  {"rkw",	{HB_TAG('R','K','W',' ')}},	/* Arakwal */
+  {"rm",	{HB_TAG('R','M','S',' ')}},	/* Romansh */
+  {"rmc",	{HB_TAG('R','O','Y',' ')}},	/* Carpathian Romani -> Romany */
+  {"rmf",	{HB_TAG('R','O','Y',' ')}},	/* Kalo Finnish Romani -> Romany */
+  {"rml",	{HB_TAG('R','O','Y',' ')}},	/* Baltic Romani -> Romany */
+  {"rmn",	{HB_TAG('R','O','Y',' ')}},	/* Balkan Romani -> Romany */
+  {"rmo",	{HB_TAG('R','O','Y',' ')}},	/* Sinte Romani -> Romany */
+  {"rmw",	{HB_TAG('R','O','Y',' ')}},	/* Welsh Romani -> Romany */
+  {"rmy",	{HB_TAG('R','M','Y',' ')}},	/* Vlax Romani */
+  {"rmz",	{HB_TAG('A','R','K',' ')}},	/* Marma -> Rakhine */
+  {"rn",	{HB_TAG('R','U','N',' ')}},	/* Rundi */
+  {"rnl",	{HB_TAG('H','A','L',' ')}},	/* Ranglong -> Halam (Falam Chin) */
+  {"ro",	{HB_TAG('R','O','M',' ')}},	/* Romanian */
+  {"rom",	{HB_TAG('R','O','Y',' ')}},	/* Romany [macrolanguage] */
+  {"rtm",	{HB_TAG('R','T','M',' ')}},	/* Rotuman */
+  {"ru",	{HB_TAG('R','U','S',' ')}},	/* Russian */
+  {"rue",	{HB_TAG('R','S','Y',' ')}},	/* Rusyn */
+  {"rup",	{HB_TAG('R','U','P',' ')}},	/* Aromanian */
+  {"rw",	{HB_TAG('R','U','A',' ')}},	/* Kinyarwanda */
+  {"rwr",	{HB_TAG('M','A','W',' ')}},	/* Marwari (India) */
+  {"sa",	{HB_TAG('S','A','N',' ')}},	/* Sanskrit */
+  {"sah",	{HB_TAG('Y','A','K',' ')}},	/* Yakut -> Sakha */
+  {"sam",	{HB_TAG('P','A','A',' ')}},	/* Samaritan Aramaic -> Palestinian Aramaic */
+  {"sas",	{HB_TAG('S','A','S',' ')}},	/* Sasak */
+  {"sat",	{HB_TAG('S','A','T',' ')}},	/* Santali */
+  {"sc",	{HB_TAG('S','R','D',' ')}},	/* Sardinian [macrolanguage] */
+  {"sck",	{HB_TAG('S','A','D',' ')}},	/* Sadri */
+  {"scn",	{HB_TAG('S','C','N',' ')}},	/* Sicilian */
+  {"sco",	{HB_TAG('S','C','O',' ')}},	/* Scots */
+  {"scs",	{HB_TAG('S','C','S',' '),	/* North Slavey */
+		 HB_TAG('S','L','A',' '),	/* North Slavey -> Slavey */
+		 HB_TAG('A','T','H',' ')}},	/* North Slavey -> Athapaskan */
+  {"sd",	{HB_TAG('S','N','D',' ')}},	/* Sindhi */
+  {"sdc",	{HB_TAG('S','R','D',' ')}},	/* Sassarese Sardinian -> Sardinian */
+  {"sdh",	{HB_TAG('K','U','R',' ')}},	/* Southern Kurdish -> Kurdish */
+  {"sdn",	{HB_TAG('S','R','D',' ')}},	/* Gallurese Sardinian -> Sardinian */
+  {"se",	{HB_TAG('N','S','M',' ')}},	/* Northern Sami */
+  {"seh",	{HB_TAG('S','N','A',' ')}},	/* Sena */
+  {"sek",	{HB_TAG('A','T','H',' ')}},	/* Sekani -> Athapaskan */
+  {"sel",	{HB_TAG('S','E','L',' ')}},	/* Selkup */
+  {"sez",	{HB_TAG('Q','I','N',' ')}},	/* Senthang Chin -> Chin */
+  {"sfm",	{HB_TAG('H','M','N',' ')}},	/* Small Flowery Miao -> Hmong */
+  {"sg",	{HB_TAG('S','G','O',' ')}},	/* Sango */
+  {"sga",	{HB_TAG('S','G','A',' ')}},	/* Old Irish (to 900) */
+  {"sgc",	{HB_TAG('K','A','L',' ')}},	/* Kipsigis -> Kalenjin */
+  {"sgs",	{HB_TAG('S','G','S',' ')}},	/* Samogitian */
+  {"sgw",	{HB_TAG('C','H','G',' '),	/* Sebat Bet Gurage -> Chaha Gurage */
+		 HB_TAG('S','G','W',' ')}},	/* Sebat Bet Gurage -> Chaha Gurage (SIL fonts) */
+  {"shi",	{HB_TAG('S','H','I',' ')}},	/* Tachelhit */
+  {"shn",	{HB_TAG('S','H','N',' ')}},	/* Shan */
+  {"shu",	{HB_TAG('A','R','A',' ')}},	/* Chadian Arabic -> Arabic */
+  {"si",	{HB_TAG('S','N','H',' ')}},	/* Sinhala (Sinhalese) */
+  {"sid",	{HB_TAG('S','I','D',' ')}},	/* Sidamo */
+  {"sjd",	{HB_TAG('K','S','M',' ')}},	/* Kildin Sami */
+  {"sjo",	{HB_TAG('S','I','B',' ')}},	/* Xibe -> Sibe */
+  {"sk",	{HB_TAG('S','K','Y',' ')}},	/* Slovak */
+  {"skg",	{HB_TAG('M','L','G',' ')}},	/* Sakalava Malagasy -> Malagasy */
+  {"skr",	{HB_TAG('S','R','K',' ')}},	/* Saraiki */
+  {"sl",	{HB_TAG('S','L','V',' ')}},	/* Slovenian */
+  {"sm",	{HB_TAG('S','M','O',' ')}},	/* Samoan */
+  {"sma",	{HB_TAG('S','S','M',' ')}},	/* Southern Sami */
+  {"smj",	{HB_TAG('L','S','M',' ')}},	/* Lule Sami */
+  {"smn",	{HB_TAG('I','S','M',' ')}},	/* Inari Sami */
+  {"sms",	{HB_TAG('S','K','S',' ')}},	/* Skolt Sami */
+  {"sn",	{HB_TAG('S','N','A','0')}},	/* Shona */
+  {"snk",	{HB_TAG('S','N','K',' ')}},	/* Soninke */
+  {"so",	{HB_TAG('S','M','L',' ')}},	/* Somali */
+  {"sop",	{HB_TAG('S','O','P',' ')}},	/* Songe */
+  {"spv",	{HB_TAG('O','R','I',' ')}},	/* Sambalpuri -> Odia (formerly Oriya) */
+  {"spy",	{HB_TAG('K','A','L',' ')}},	/* Sabaot -> Kalenjin */
+  {"sq",	{HB_TAG('S','Q','I',' ')}},	/* Albanian [macrolanguage] */
+  {"sr",	{HB_TAG('S','R','B',' ')}},	/* Serbian */
+  {"src",	{HB_TAG('S','R','D',' ')}},	/* Logudorese Sardinian -> Sardinian */
+  {"sro",	{HB_TAG('S','R','D',' ')}},	/* Campidanese Sardinian -> Sardinian */
+  {"srr",	{HB_TAG('S','R','R',' ')}},	/* Serer */
+  {"srs",	{HB_TAG('A','T','H',' ')}},	/* Sarsi -> Athapaskan */
+  {"ss",	{HB_TAG('S','W','Z',' ')}},	/* Swati */
+  {"ssh",	{HB_TAG('A','R','A',' ')}},	/* Shihhi Arabic -> Arabic */
+  {"st",	{HB_TAG('S','O','T',' ')}},	/* Southern Sotho -> Sotho, Southern */
+  {"stq",	{HB_TAG('S','T','Q',' ')}},	/* Saterfriesisch -> Saterland Frisian */
+  {"stv",	{HB_TAG('S','I','G',' ')}},	/* Silt'e -> Silte Gurage */
+  {"su",	{HB_TAG('S','U','N',' ')}},	/* Sundanese */
+  {"suk",	{HB_TAG('S','U','K',' ')}},	/* Sukuma */
+  {"suq",	{HB_TAG('S','U','R',' ')}},	/* Suri */
+  {"sv",	{HB_TAG('S','V','E',' ')}},	/* Swedish */
+  {"sva",	{HB_TAG('S','V','A',' ')}},	/* Svan */
+  {"sw",	{HB_TAG('S','W','K',' ')}},	/* Swahili [macrolanguage] */
+  {"swb",	{HB_TAG('C','M','R',' ')}},	/* Maore Comorian -> Comorian */
+  {"swc",	{HB_TAG('S','W','K',' ')}},	/* Congo Swahili -> Swahili */
+  {"swh",	{HB_TAG('S','W','K',' ')}},	/* Swahili */
+  {"swv",	{HB_TAG('M','A','W',' ')}},	/* Shekhawati -> Marwari */
+  {"sxu",	{HB_TAG('S','X','U',' ')}},	/* Upper Saxon */
+  {"syc",	{HB_TAG('S','Y','R',' ')}},	/* Classical Syriac -> Syriac */
+  {"syl",	{HB_TAG('S','Y','L',' ')}},	/* Sylheti */
+  {"syr",	{HB_TAG('S','Y','R',' ')}},	/* Syriac [macrolanguage] */
+  {"szl",	{HB_TAG('S','Z','L',' ')}},	/* Silesian */
+  {"ta",	{HB_TAG('T','A','M',' ')}},	/* Tamil */
+  {"taa",	{HB_TAG('A','T','H',' ')}},	/* Lower Tanana -> Athapaskan */
+  {"tab",	{HB_TAG('T','A','B',' ')}},	/* Tabassaran -> Tabasaran */
+  {"taq",	{HB_TAG('T','M','H',' ')}},	/* Tamasheq -> Tamashek */
+  {"tau",	{HB_TAG('A','T','H',' ')}},	/* Upper Tanana -> Athapaskan */
+  {"tcb",	{HB_TAG('A','T','H',' ')}},	/* Tanacross -> Athapaskan */
+  {"tce",	{HB_TAG('A','T','H',' ')}},	/* Southern Tutchone -> Athapaskan */
+  {"tcp",	{HB_TAG('Q','I','N',' ')}},	/* Tawr Chin -> Chin */
+  {"tcy",	{HB_TAG('T','U','L',' ')}},	/* Tulu -> Tumbuka */
+  {"tcz",	{HB_TAG('Q','I','N',' ')}},	/* Thado Chin -> Chin */
+  {"tdd",	{HB_TAG('T','D','D',' ')}},	/* Tai Nüa -> Dehong Dai */
+  {"tdx",	{HB_TAG('M','L','G',' ')}},	/* Tandroy-Mahafaly Malagasy -> Malagasy */
+  {"te",	{HB_TAG('T','E','L',' ')}},	/* Telugu */
+  {"tec",	{HB_TAG('K','A','L',' ')}},	/* Terik -> Kalenjin */
+  {"tem",	{HB_TAG('T','M','N',' ')}},	/* Timne -> Temne */
+  {"tet",	{HB_TAG('T','E','T',' ')}},	/* Tetum */
+  {"tfn",	{HB_TAG('A','T','H',' ')}},	/* Tanaina -> Athapaskan */
+  {"tg",	{HB_TAG('T','A','J',' ')}},	/* Tajik -> Tajiki */
+  {"tgj",	{HB_TAG('N','I','S',' ')}},	/* Tagin -> Nisi */
+  {"tgx",	{HB_TAG('A','T','H',' ')}},	/* Tagish -> Athapaskan */
+  {"th",	{HB_TAG('T','H','A',' ')}},	/* Thai */
+  {"tht",	{HB_TAG('A','T','H',' ')}},	/* Tahltan -> Athapaskan */
+  {"thv",	{HB_TAG('T','M','H',' ')}},	/* Tahaggart Tamahaq -> Tamashek */
+  {"thz",	{HB_TAG('T','M','H',' ')}},	/* Tayart Tamajeq -> Tamashek */
+  {"ti",	{HB_TAG('T','G','Y',' ')}},	/* Tigrinya */
+  {"tig",	{HB_TAG('T','G','R',' ')}},	/* Tigre */
+  {"tiv",	{HB_TAG('T','I','V',' ')}},	/* Tiv */
+  {"tk",	{HB_TAG('T','K','M',' ')}},	/* Turkmen */
+  {"tkg",	{HB_TAG('M','L','G',' ')}},	/* Tesaka Malagasy -> Malagasy */
+  {"tl",	{HB_TAG('T','G','L',' ')}},	/* Tagalog */
+  {"tmh",	{HB_TAG('T','M','H',' ')}},	/* Tamashek [macrolanguage] */
+  {"tmw",	{HB_TAG('M','L','Y',' ')}},	/* Temuan -> Malay */
+  {"tn",	{HB_TAG('T','N','A',' ')}},	/* Tswana */
+  {"tnf",	{HB_TAG('D','R','I',' ')}},	/* Tangshewi (retired code) -> Dari */
+  {"to",	{HB_TAG('T','G','N',' ')}},	/* Tonga (Tonga Islands) -> Tongan */
+  {"tod",	{HB_TAG('T','O','D','0')}},	/* Toma */
+  {"toi",	{HB_TAG('T','N','G',' ')}},	/* Tonga (Zambia) */
+  {"tol",	{HB_TAG('A','T','H',' ')}},	/* Tolowa -> Athapaskan */
+  {"tpi",	{HB_TAG('T','P','I',' ')}},	/* Tok Pisin */
+  {"tr",	{HB_TAG('T','R','K',' ')}},	/* Turkish */
+  {"tru",	{HB_TAG('T','U','A',' '),	/* Turoyo -> Turoyo Aramaic */
+		 HB_TAG('S','Y','R',' ')}},	/* Turoyo -> Syriac */
+  {"ts",	{HB_TAG('T','S','G',' ')}},	/* Tsonga */
+  {"tsj",	{HB_TAG('T','S','J',' ')}},	/* Tshangla */
+  {"tt",	{HB_TAG('T','A','T',' ')}},	/* Tatar */
+  {"ttm",	{HB_TAG('A','T','H',' ')}},	/* Northern Tutchone -> Athapaskan */
+  {"ttq",	{HB_TAG('T','M','H',' ')}},	/* Tawallammat Tamajaq -> Tamashek */
+  {"tum",	{HB_TAG('T','U','M',' ')}},	/* Tumbuka -> Tulu */
+  {"tuu",	{HB_TAG('A','T','H',' ')}},	/* Tututni -> Athapaskan */
+  {"tuy",	{HB_TAG('K','A','L',' ')}},	/* Tugen -> Kalenjin */
+  {"tvl",	{HB_TAG('T','V','L',' ')}},	/* Tuvalu */
+  {"tw",	{HB_TAG('T','W','I',' '),	/* Twi */
+		 HB_TAG('A','K','A',' ')}},	/* Twi -> Akan */
+  {"txc",	{HB_TAG('A','T','H',' ')}},	/* Tsetsaut -> Athapaskan */
+  {"txy",	{HB_TAG('M','L','G',' ')}},	/* Tanosy Malagasy -> Malagasy */
+  {"ty",	{HB_TAG('T','H','T',' ')}},	/* Tahitian */
+  {"tyv",	{HB_TAG('T','U','V',' ')}},	/* Tuvinian -> Tuvin */
+  {"tyz",	{HB_TAG('T','Y','Z',' ')}},	/* Tày */
+  {"tzm",	{HB_TAG('T','Z','M',' ')}},	/* Central Atlas Tamazight -> Tamazight */
+  {"tzo",	{HB_TAG('T','Z','O',' ')}},	/* Tzotzil */
+  {"ubl",	{HB_TAG('B','I','K',' ')}},	/* Buhi'non Bikol -> Bikol */
+  {"udm",	{HB_TAG('U','D','M',' ')}},	/* Udmurt */
+  {"ug",	{HB_TAG('U','Y','G',' ')}},	/* Uyghur */
+  {"uk",	{HB_TAG('U','K','R',' ')}},	/* Ukrainian */
+  {"umb",	{HB_TAG('U','M','B',' ')}},	/* Umbundu */
+  {"unr",	{HB_TAG('M','U','N',' ')}},	/* Mundari */
+  {"ur",	{HB_TAG('U','R','D',' ')}},	/* Urdu */
+  {"urk",	{HB_TAG('M','L','Y',' ')}},	/* Urak Lawoi' -> Malay */
+  {"uz",	{HB_TAG('U','Z','B',' ')}},	/* Uzbek [macrolanguage] */
+  {"uzn",	{HB_TAG('U','Z','B',' ')}},	/* Northern Uzbek -> Uzbek */
+  {"uzs",	{HB_TAG('U','Z','B',' ')}},	/* Southern Uzbek -> Uzbek */
+  {"ve",	{HB_TAG('V','E','N',' ')}},	/* Venda */
+  {"vec",	{HB_TAG('V','E','C',' ')}},	/* Venetian */
+  {"vi",	{HB_TAG('V','I','T',' ')}},	/* Vietnamese */
+  {"vkk",	{HB_TAG('M','L','Y',' ')}},	/* Kaur -> Malay */
+  {"vkt",	{HB_TAG('M','L','Y',' ')}},	/* Tenggarong Kutai Malay -> Malay */
+  {"vls",	{HB_TAG('F','L','E',' ')}},	/* Vlaams -> Dutch (Flemish) */
+  {"vmw",	{HB_TAG('M','A','K',' ')}},	/* Makhuwa */
+  {"vo",	{HB_TAG('V','O','L',' ')}},	/* Volapük */
+  {"vro",	{HB_TAG('V','R','O',' ')}},	/* Võro */
+  {"wa",	{HB_TAG('W','L','N',' ')}},	/* Walloon */
+  {"war",	{HB_TAG('W','A','R',' ')}},	/* Waray (Philippines) -> Waray-Waray */
+  {"wbm",	{HB_TAG('W','A',' ',' ')}},	/* Wa */
+  {"wbr",	{HB_TAG('W','A','G',' ')}},	/* Wagdi */
+  {"wlc",	{HB_TAG('C','M','R',' ')}},	/* Mwali Comorian -> Comorian */
+  {"wle",	{HB_TAG('S','I','G',' ')}},	/* Wolane -> Silte Gurage */
+  {"wlk",	{HB_TAG('A','T','H',' ')}},	/* Wailaki -> Athapaskan */
+  {"wni",	{HB_TAG('C','M','R',' ')}},	/* Ndzwani Comorian -> Comorian */
+  {"wo",	{HB_TAG('W','L','F',' ')}},	/* Wolof */
+  {"wry",	{HB_TAG('M','A','W',' ')}},	/* Merwari -> Marwari */
+  {"wsg",	{HB_TAG('G','O','N',' ')}},	/* Adilabad Gondi -> Gondi */
+  {"wtm",	{HB_TAG('W','T','M',' ')}},	/* Mewati */
+  {"wuu",	{HB_TAG('Z','H','S',' ')}},	/* Wu Chinese -> Chinese Simplified */
+  {"xal",	{HB_TAG('K','L','M',' '),	/* Kalmyk */
+		 HB_TAG('T','O','D',' ')}},	/* Kalmyk -> Todo */
+  {"xan",	{HB_TAG('S','E','K',' ')}},	/* Xamtanga -> Sekota */
+  {"xh",	{HB_TAG('X','H','S',' ')}},	/* Xhosa */
+  {"xjb",	{HB_TAG('X','J','B',' ')}},	/* Minjungbal -> Minjangbal */
+  {"xkf",	{HB_TAG('X','K','F',' ')}},	/* Khengkha */
+  {"xmm",	{HB_TAG('M','L','Y',' ')}},	/* Manado Malay -> Malay */
+  {"xmv",	{HB_TAG('M','L','G',' ')}},	/* Antankarana Malagasy -> Malagasy */
+  {"xmw",	{HB_TAG('M','L','G',' ')}},	/* Tsimihety Malagasy -> Malagasy */
+  {"xnr",	{HB_TAG('D','G','R',' ')}},	/* Kangri -> Dogri */
+  {"xog",	{HB_TAG('X','O','G',' ')}},	/* Soga */
+  {"xpe",	{HB_TAG('X','P','E',' ')}},	/* Liberia Kpelle -> Kpelle (Liberia) */
+  {"xsl",	{HB_TAG('S','S','L',' '),	/* South Slavey */
+		 HB_TAG('S','L','A',' '),	/* South Slavey -> Slavey */
+		 HB_TAG('A','T','H',' ')}},	/* South Slavey -> Athapaskan */
+  {"xst",	{HB_TAG('S','I','G',' ')}},	/* Silt'e (retired code) -> Silte Gurage */
+  {"xwo",	{HB_TAG('T','O','D',' ')}},	/* Written Oirat -> Todo */
+  {"yao",	{HB_TAG('Y','A','O',' ')}},	/* Yao */
+  {"yap",	{HB_TAG('Y','A','P',' ')}},	/* Yapese */
+  {"ybd",	{HB_TAG('A','R','K',' ')}},	/* Yangbye (retired code) -> Rakhine */
+  {"ydd",	{HB_TAG('J','I','I',' ')}},	/* Eastern Yiddish -> Yiddish */
+  {"yi",	{HB_TAG('J','I','I',' ')}},	/* Yiddish [macrolanguage] */
+  {"yih",	{HB_TAG('J','I','I',' ')}},	/* Western Yiddish -> Yiddish */
+  {"yo",	{HB_TAG('Y','B','A',' ')}},	/* Yoruba */
+  {"yos",	{HB_TAG('Q','I','N',' ')}},	/* Yos (retired code) -> Chin */
+  {"yrk",	{HB_TAG('T','N','E',' '),	/* Nenets -> Tundra Nenets */
+		 HB_TAG('F','N','E',' ')}},	/* Nenets -> Forest Nenets */
+  {"yue",	{HB_TAG('Z','H','H',' ')}},	/* Yue Chinese -> Chinese, Hong Kong SAR */
+  {"za",	{HB_TAG('Z','H','A',' ')}},	/* Zhuang [macrolanguage] */
+  {"zch",	{HB_TAG('Z','H','A',' ')}},	/* Central Hongshuihe Zhuang -> Zhuang */
+  {"zdj",	{HB_TAG('C','M','R',' ')}},	/* Ngazidja Comorian -> Comorian */
+  {"zea",	{HB_TAG('Z','E','A',' ')}},	/* Zeeuws -> Zealandic */
+  {"zeh",	{HB_TAG('Z','H','A',' ')}},	/* Eastern Hongshuihe Zhuang -> Zhuang */
+  {"zgb",	{HB_TAG('Z','H','A',' ')}},	/* Guibei Zhuang -> Zhuang */
+  {"zgh",	{HB_TAG('Z','G','H',' ')}},	/* Standard Moroccan Tamazight */
+  {"zgm",	{HB_TAG('Z','H','A',' ')}},	/* Minz Zhuang -> Zhuang */
+  {"zgn",	{HB_TAG('Z','H','A',' ')}},	/* Guibian Zhuang -> Zhuang */
+  {"zh",	{HB_TAG('Z','H','S',' ')}},	/* Chinese [macrolanguage] -> Chinese Simplified */
+  {"zhd",	{HB_TAG('Z','H','A',' ')}},	/* Dai Zhuang -> Zhuang */
+  {"zhn",	{HB_TAG('Z','H','A',' ')}},	/* Nong Zhuang -> Zhuang */
+  {"zlj",	{HB_TAG('Z','H','A',' ')}},	/* Liujiang Zhuang -> Zhuang */
+  {"zlm",	{HB_TAG('M','L','Y',' ')}},	/* Malay */
+  {"zln",	{HB_TAG('Z','H','A',' ')}},	/* Lianshan Zhuang -> Zhuang */
+  {"zlq",	{HB_TAG('Z','H','A',' ')}},	/* Liuqian Zhuang -> Zhuang */
+  {"zmi",	{HB_TAG('M','L','Y',' ')}},	/* Negeri Sembilan Malay -> Malay */
+  {"zne",	{HB_TAG('Z','N','D',' ')}},	/* Zande */
+  {"zom",	{HB_TAG('Q','I','N',' ')}},	/* Zou -> Chin */
+  {"zqe",	{HB_TAG('Z','H','A',' ')}},	/* Qiubei Zhuang -> Zhuang */
+  {"zsm",	{HB_TAG('M','L','Y',' ')}},	/* Standard Malay -> Malay */
+  {"zu",	{HB_TAG('Z','U','L',' ')}},	/* Zulu */
+  {"zum",	{HB_TAG('L','R','C',' ')}},	/* Kumzari -> Luri */
+  {"zyb",	{HB_TAG('Z','H','A',' ')}},	/* Yongbei Zhuang -> Zhuang */
+  {"zyg",	{HB_TAG('Z','H','A',' ')}},	/* Yang Zhuang -> Zhuang */
+  {"zyj",	{HB_TAG('Z','H','A',' ')}},	/* Youjiang Zhuang -> Zhuang */
+  {"zyn",	{HB_TAG('Z','H','A',' ')}},	/* Yongnan Zhuang -> Zhuang */
+  {"zza",	{HB_TAG('Z','Z','A',' ')}},	/* Zazaki [macrolanguage] */
+  {"zzj",	{HB_TAG('Z','H','A',' ')}},	/* Zuojiang Zhuang -> Zhuang */
+};
+
+static_assert (HB_OT_MAX_TAGS_PER_LANGUAGE == 3u, "");
+
+/**
+ * hb_ot_tags_from_complex_language:
+ * @lang_str: a BCP 47 language tag to convert.
+ * @limit: a pointer to the end of the substring of @lang_str to consider for
+ * conversion.
+ * @count: maximum number of language tags to retrieve (IN) and actual number of
+ * language tags retrieved (OUT). If no tags are retrieved, it is not modified.
+ * @tags: array of size at least @language_count to store the language tag
+ * results
+ *
+ * Converts a multi-subtag BCP 47 language tag to language tags.
+ *
+ * Return value: Whether any language systems were retrieved.
+ **/
+static bool
+hb_ot_tags_from_complex_language (const char   *lang_str,
+				  const char   *limit,
+				  unsigned int *count /* IN/OUT */,
+				  hb_tag_t     *tags /* OUT */)
+{
+  if (subtag_matches (lang_str, limit, "-fonnapa"))
+  {
+    /* Undetermined; North American Phonetic Alphabet */
+    tags[0] = HB_TAG('A','P','P','H');  /* Phonetic transcription—Americanist conventions */
+    *count = 1;
+    return true;
+  }
+  if (subtag_matches (lang_str, limit, "-polyton"))
+  {
+    /* Modern Greek (1453-); Polytonic Greek */
+    tags[0] = HB_TAG('P','G','R',' ');  /* Polytonic Greek */
+    *count = 1;
+    return true;
+  }
+  if (subtag_matches (lang_str, limit, "-provenc"))
+  {
+    /* Occitan (post 1500); Provençal */
+    tags[0] = HB_TAG('P','R','O',' ');  /* Provençal / Old Provençal */
+    *count = 1;
+    return true;
+  }
+  if (subtag_matches (lang_str, limit, "-fonipa"))
+  {
+    /* Undetermined; International Phonetic Alphabet */
+    tags[0] = HB_TAG('I','P','P','H');  /* Phonetic transcription—IPA conventions */
+    *count = 1;
+    return true;
+  }
+  if (subtag_matches (lang_str, limit, "-geok"))
+  {
+    /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */
+    tags[0] = HB_TAG('K','G','E',' ');  /* Khutsuri Georgian */
+    *count = 1;
+    return true;
+  }
+  if (subtag_matches (lang_str, limit, "-syre"))
+  {
+    /* Undetermined; Syriac (Estrangelo variant) */
+    tags[0] = HB_TAG('S','Y','R','E');  /* Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre') */
+    *count = 1;
+    return true;
+  }
+  if (subtag_matches (lang_str, limit, "-syrj"))
+  {
+    /* Undetermined; Syriac (Western variant) */
+    tags[0] = HB_TAG('S','Y','R','J');  /* Syriac, Western script-variant (equivalent to ISO 15924 'Syrj') */
+    *count = 1;
+    return true;
+  }
+  if (subtag_matches (lang_str, limit, "-syrn"))
+  {
+    /* Undetermined; Syriac (Eastern variant) */
+    tags[0] = HB_TAG('S','Y','R','N');  /* Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn') */
+    *count = 1;
+    return true;
+  }
+  switch (lang_str[0])
+  {
+  case 'a':
+    if (0 == strcmp (&lang_str[1], "rt-lojban"))
+    {
+      /* Lojban */
+      tags[0] = HB_TAG('J','B','O',' ');  /* Lojban */
+      *count = 1;
+      return true;
+    }
+    break;
+  case 'c':
+    if (lang_matches (&lang_str[1], "do-hant-hk"))
+    {
+      /* Min Dong Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "do-hant-mo"))
+    {
+      /* Min Dong Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "jy-hant-hk"))
+    {
+      /* Jinyu Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "jy-hant-mo"))
+    {
+      /* Jinyu Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "mn-hant-hk"))
+    {
+      /* Mandarin Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "mn-hant-mo"))
+    {
+      /* Mandarin Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "px-hant-hk"))
+    {
+      /* Pu-Xian Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "px-hant-mo"))
+    {
+      /* Pu-Xian Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "zh-hant-hk"))
+    {
+      /* Huizhou Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "zh-hant-mo"))
+    {
+      /* Huizhou Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "zo-hant-hk"))
+    {
+      /* Min Zhong Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "zo-hant-mo"))
+    {
+      /* Min Zhong Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "do-hans"))
+    {
+      /* Min Dong Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "do-hant"))
+    {
+      /* Min Dong Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "jy-hans"))
+    {
+      /* Jinyu Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "jy-hant"))
+    {
+      /* Jinyu Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "mn-hans"))
+    {
+      /* Mandarin Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "mn-hant"))
+    {
+      /* Mandarin Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "px-hans"))
+    {
+      /* Pu-Xian Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "px-hant"))
+    {
+      /* Pu-Xian Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "zh-hans"))
+    {
+      /* Huizhou Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "zh-hant"))
+    {
+      /* Huizhou Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "zo-hans"))
+    {
+      /* Min Zhong Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "zo-hant"))
+    {
+      /* Min Zhong Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "do-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Min Dong Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "do-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Min Dong Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "do-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Min Dong Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "jy-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Jinyu Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "jy-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Jinyu Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "jy-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Jinyu Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "mn-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Mandarin Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "mn-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Mandarin Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "mn-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Mandarin Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "px-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Pu-Xian Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "px-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Pu-Xian Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "px-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Pu-Xian Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "zh-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Huizhou Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "zh-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Huizhou Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "zh-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Huizhou Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "zo-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Min Zhong Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "zo-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Min Zhong Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "zo-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Min Zhong Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    break;
+  case 'g':
+    if (lang_matches (&lang_str[1], "an-hant-hk"))
+    {
+      /* Gan Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "an-hant-mo"))
+    {
+      /* Gan Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "an-hans"))
+    {
+      /* Gan Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "an-hant"))
+    {
+      /* Gan Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "a-latg"))
+    {
+      /* Irish */
+      tags[0] = HB_TAG('I','R','T',' ');  /* Irish Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "an-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Gan Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "an-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Gan Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "an-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Gan Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    break;
+  case 'h':
+    if (lang_matches (&lang_str[1], "ak-hant-hk"))
+    {
+      /* Hakka Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "ak-hant-mo"))
+    {
+      /* Hakka Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "sn-hant-hk"))
+    {
+      /* Xiang Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "sn-hant-mo"))
+    {
+      /* Xiang Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "ak-hans"))
+    {
+      /* Hakka Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "ak-hant"))
+    {
+      /* Hakka Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "sn-hans"))
+    {
+      /* Xiang Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "sn-hant"))
+    {
+      /* Xiang Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "ak-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Hakka Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "ak-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Hakka Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "ak-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Hakka Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "sn-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Xiang Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "sn-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Xiang Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "sn-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Xiang Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    break;
+  case 'i':
+    if (0 == strcmp (&lang_str[1], "-navajo"))
+    {
+      /* Navajo */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('N','A','V',' '),  /* Navajo */
+	HB_TAG('A','T','H',' '),  /* Athapaskan */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
+    if (0 == strcmp (&lang_str[1], "-hak"))
+    {
+      /* Hakka */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (0 == strcmp (&lang_str[1], "-lux"))
+    {
+      /* Luxembourgish */
+      tags[0] = HB_TAG('L','T','Z',' ');  /* Luxembourgish */
+      *count = 1;
+      return true;
+    }
+    break;
+  case 'l':
+    if (lang_matches (&lang_str[1], "zh-hans"))
+    {
+      /* Literary Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    break;
+  case 'm':
+    if (lang_matches (&lang_str[1], "np-hant-hk"))
+    {
+      /* Min Bei Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "np-hant-mo"))
+    {
+      /* Min Bei Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "np-hans"))
+    {
+      /* Min Bei Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "np-hant"))
+    {
+      /* Min Bei Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "np-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Min Bei Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "np-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Min Bei Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "np-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Min Bei Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    break;
+  case 'n':
+    if (lang_matches (&lang_str[1], "an-hant-hk"))
+    {
+      /* Min Nan Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "an-hant-mo"))
+    {
+      /* Min Nan Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "an-hans"))
+    {
+      /* Min Nan Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "an-hant"))
+    {
+      /* Min Nan Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "an-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Min Nan Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "an-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Min Nan Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "an-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Min Nan Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strcmp (&lang_str[1], "o-bok"))
+    {
+      /* Norwegian Bokmal */
+      tags[0] = HB_TAG('N','O','R',' ');  /* Norwegian */
+      *count = 1;
+      return true;
+    }
+    if (0 == strcmp (&lang_str[1], "o-nyn"))
+    {
+      /* Norwegian Nynorsk */
+      tags[0] = HB_TAG('N','Y','N',' ');  /* Norwegian Nynorsk (Nynorsk, Norwegian) */
+      *count = 1;
+      return true;
+    }
+    break;
+  case 'r':
+    if (0 == strncmp (&lang_str[1], "o-", 2)
+	&& subtag_matches (lang_str, limit, "-md"))
+    {
+      /* Romanian; Moldova */
+      tags[0] = HB_TAG('M','O','L',' ');  /* Moldavian */
+      *count = 1;
+      return true;
+    }
+    break;
+  case 'w':
+    if (lang_matches (&lang_str[1], "uu-hant-hk"))
+    {
+      /* Wu Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "uu-hant-mo"))
+    {
+      /* Wu Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "uu-hans"))
+    {
+      /* Wu Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "uu-hant"))
+    {
+      /* Wu Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "uu-", 3)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Wu Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "uu-", 3)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Wu Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "uu-", 3)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Wu Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    break;
+  case 'y':
+    if (lang_matches (&lang_str[1], "ue-hans"))
+    {
+      /* Yue Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    break;
+  case 'z':
+    if (lang_matches (&lang_str[1], "h-hant-hk"))
+    {
+      /* Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "h-hant-mo"))
+    {
+      /* Chinese */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strcmp (&lang_str[1], "h-min-nan"))
+    {
+      /* Minnan, Hokkien, Amoy, Taiwanese, Southern Min, Southern Fujian, Hoklo, Southern Fukien, Ho-lo */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "h-hans"))
+    {
+      /* Chinese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], "h-hant"))
+    {
+      /* Chinese */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strcmp (&lang_str[1], "h-min"))
+    {
+      /* Min, Fuzhou, Hokkien, Amoy, or Taiwanese */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "h-", 2)
+	&& subtag_matches (lang_str, limit, "-hk"))
+    {
+      /* Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "h-", 2)
+	&& subtag_matches (lang_str, limit, "-mo"))
+    {
+      /* Chinese; Macao */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "h-", 2)
+	&& subtag_matches (lang_str, limit, "-tw"))
+    {
+      /* Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      *count = 1;
+      return true;
+    }
+    break;
+  }
+  return false;
+}
+
+/**
+ * hb_ot_ambiguous_tag_to_language
+ * @tag: A language tag.
+ *
+ * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to
+ * many language tags) and the best tag is not the alphabetically first, or if
+ * the best tag consists of multiple subtags.
+ *
+ * Return value: The #hb_language_t corresponding to the BCP 47 language tag,
+ * or #HB_LANGUAGE_INVALID if @tag is not ambiguous.
+ **/
+static hb_language_t
+hb_ot_ambiguous_tag_to_language (hb_tag_t tag)
+{
+  switch (tag)
+  {
+  case HB_TAG('A','P','P','H'):  /* Phonetic transcription—Americanist conventions */
+    return hb_language_from_string ("und-fonnapa", -1);  /* Undetermined; North American Phonetic Alphabet */
+  case HB_TAG('A','R','A',' '):  /* Arabic */
+    return hb_language_from_string ("ar", -1);  /* Arabic */
+  case HB_TAG('A','R','K',' '):  /* Rakhine */
+    return hb_language_from_string ("rki", -1);  /* Rakhine */
+  case HB_TAG('A','T','H',' '):  /* Athapaskan */
+    return hb_language_from_string ("ath", -1);  /* Athapascan */
+  case HB_TAG('B','I','K',' '):  /* Bikol */
+    return hb_language_from_string ("bik", -1);  /* Bikol */
+  case HB_TAG('C','P','P',' '):  /* Creoles */
+    return hb_language_from_string ("crp", -1);  /* Creoles and pidgins */
+  case HB_TAG('C','R','R',' '):  /* Carrier */
+    return hb_language_from_string ("crx", -1);  /* Carrier */
+  case HB_TAG('D','N','K',' '):  /* Dinka */
+    return hb_language_from_string ("din", -1);  /* Dinka */
+  case HB_TAG('D','R','I',' '):  /* Dari */
+    return hb_language_from_string ("prs", -1);  /* Dari */
+  case HB_TAG('D','U','J',' '):  /* Dhuwal */
+    return hb_language_from_string ("dwu", -1);  /* Dhuwal */
+  case HB_TAG('D','Z','N',' '):  /* Dzongkha */
+    return hb_language_from_string ("dz", -1);  /* Dzongkha */
+  case HB_TAG('E','T','I',' '):  /* Estonian */
+    return hb_language_from_string ("et", -1);  /* Estonian */
+  case HB_TAG('G','O','N',' '):  /* Gondi */
+    return hb_language_from_string ("gon", -1);  /* Gondi */
+  case HB_TAG('H','M','N',' '):  /* Hmong */
+    return hb_language_from_string ("hmn", -1);  /* Hmong */
+  case HB_TAG('I','J','O',' '):  /* Ijo */
+    return hb_language_from_string ("ijo", -1);  /* Ijo */
+  case HB_TAG('I','N','U',' '):  /* Inuktitut */
+    return hb_language_from_string ("iu", -1);  /* Inuktitut */
+  case HB_TAG('I','P','K',' '):  /* Inupiat */
+    return hb_language_from_string ("ik", -1);  /* Inupiaq */
+  case HB_TAG('I','P','P','H'):  /* Phonetic transcription—IPA conventions */
+    return hb_language_from_string ("und-fonipa", -1);  /* Undetermined; International Phonetic Alphabet */
+  case HB_TAG('I','R','T',' '):  /* Irish Traditional */
+    return hb_language_from_string ("ga-Latg", -1);  /* Irish; Latin (Gaelic variant) */
+  case HB_TAG('J','I','I',' '):  /* Yiddish */
+    return hb_language_from_string ("yi", -1);  /* Yiddish */
+  case HB_TAG('K','A','L',' '):  /* Kalenjin */
+    return hb_language_from_string ("kln", -1);  /* Kalenjin */
+  case HB_TAG('K','G','E',' '):  /* Khutsuri Georgian */
+    return hb_language_from_string ("und-Geok", -1);  /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */
+  case HB_TAG('K','N','R',' '):  /* Kanuri */
+    return hb_language_from_string ("kr", -1);  /* Kanuri */
+  case HB_TAG('K','O','K',' '):  /* Konkani */
+    return hb_language_from_string ("kok", -1);  /* Konkani */
+  case HB_TAG('K','U','R',' '):  /* Kurdish */
+    return hb_language_from_string ("ku", -1);  /* Kurdish */
+  case HB_TAG('L','U','H',' '):  /* Luyia */
+    return hb_language_from_string ("luy", -1);  /* Luyia */
+  case HB_TAG('L','V','I',' '):  /* Latvian */
+    return hb_language_from_string ("lv", -1);  /* Latvian */
+  case HB_TAG('M','A','W',' '):  /* Marwari */
+    return hb_language_from_string ("mwr", -1);  /* Marwari */
+  case HB_TAG('M','L','G',' '):  /* Malagasy */
+    return hb_language_from_string ("mg", -1);  /* Malagasy */
+  case HB_TAG('M','L','Y',' '):  /* Malay */
+    return hb_language_from_string ("ms", -1);  /* Malay */
+  case HB_TAG('M','N','G',' '):  /* Mongolian */
+    return hb_language_from_string ("mn", -1);  /* Mongolian */
+  case HB_TAG('M','O','L',' '):  /* Moldavian */
+    return hb_language_from_string ("ro-MD", -1);  /* Romanian; Moldova */
+  case HB_TAG('N','E','P',' '):  /* Nepali */
+    return hb_language_from_string ("ne", -1);  /* Nepali */
+  case HB_TAG('N','I','S',' '):  /* Nisi */
+    return hb_language_from_string ("njz", -1);  /* Nyishi */
+  case HB_TAG('N','O','R',' '):  /* Norwegian */
+    return hb_language_from_string ("no", -1);  /* Norwegian */
+  case HB_TAG('O','J','B',' '):  /* Ojibway */
+    return hb_language_from_string ("oj", -1);  /* Ojibwa */
+  case HB_TAG('O','R','O',' '):  /* Oromo */
+    return hb_language_from_string ("om", -1);  /* Oromo */
+  case HB_TAG('P','A','S',' '):  /* Pashto */
+    return hb_language_from_string ("ps", -1);  /* Pashto */
+  case HB_TAG('P','G','R',' '):  /* Polytonic Greek */
+    return hb_language_from_string ("el-polyton", -1);  /* Modern Greek (1453-); Polytonic Greek */
+  case HB_TAG('P','R','O',' '):  /* Provençal / Old Provençal */
+    return hb_language_from_string ("pro", -1);  /* Old Provençal (to 1500) */
+  case HB_TAG('Q','U','H',' '):  /* Quechua (Bolivia) */
+    return hb_language_from_string ("quh", -1);  /* South Bolivian Quechua */
+  case HB_TAG('Q','V','I',' '):  /* Quechua (Ecuador) */
+    return hb_language_from_string ("qvi", -1);  /* Imbabura Highland Quichua */
+  case HB_TAG('Q','W','H',' '):  /* Quechua (Peru) */
+    return hb_language_from_string ("qwh", -1);  /* Huaylas Ancash Quechua */
+  case HB_TAG('R','A','J',' '):  /* Rajasthani */
+    return hb_language_from_string ("raj", -1);  /* Rajasthani */
+  case HB_TAG('R','O','Y',' '):  /* Romany */
+    return hb_language_from_string ("rom", -1);  /* Romany */
+  case HB_TAG('S','Q','I',' '):  /* Albanian */
+    return hb_language_from_string ("sq", -1);  /* Albanian */
+  case HB_TAG('S','Y','R',' '):  /* Syriac */
+    return hb_language_from_string ("syr", -1);  /* Syriac */
+  case HB_TAG('S','Y','R','E'):  /* Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre') */
+    return hb_language_from_string ("und-Syre", -1);  /* Undetermined; Syriac (Estrangelo variant) */
+  case HB_TAG('S','Y','R','J'):  /* Syriac, Western script-variant (equivalent to ISO 15924 'Syrj') */
+    return hb_language_from_string ("und-Syrj", -1);  /* Undetermined; Syriac (Western variant) */
+  case HB_TAG('S','Y','R','N'):  /* Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn') */
+    return hb_language_from_string ("und-Syrn", -1);  /* Undetermined; Syriac (Eastern variant) */
+  case HB_TAG('T','M','H',' '):  /* Tamashek */
+    return hb_language_from_string ("tmh", -1);  /* Tamashek */
+  case HB_TAG('T','N','E',' '):  /* Tundra Nenets */
+    return hb_language_from_string ("yrk", -1);  /* Nenets */
+  case HB_TAG('Z','H','H',' '):  /* Chinese, Hong Kong SAR */
+    return hb_language_from_string ("zh-HK", -1);  /* Chinese; Hong Kong */
+  case HB_TAG('Z','H','S',' '):  /* Chinese Simplified */
+    return hb_language_from_string ("zh-Hans", -1);  /* Chinese; Han (Simplified variant) */
+  case HB_TAG('Z','H','T',' '):  /* Chinese Traditional */
+    return hb_language_from_string ("zh-Hant", -1);  /* Chinese; Han (Traditional variant) */
+  default:
+    return HB_LANGUAGE_INVALID;
+  }
+}
+
+#endif /* HB_OT_TAG_TABLE_HH */
+
+/* == End of generated table == */
diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc
index 991d8e7..4dba9c3 100644
--- a/src/hb-ot-tag.cc
+++ b/src/hb-ot-tag.cc
@@ -26,7 +26,7 @@
  * Google Author(s): Behdad Esfahbod, Roozbeh Pournader
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 /* hb_script_t */
@@ -36,7 +36,8 @@
 {
   /* This seems to be accurate as of end of 2012. */
 
-  switch ((hb_tag_t) script) {
+  switch ((hb_tag_t) script)
+  {
     case HB_SCRIPT_INVALID:		return HB_OT_TAG_DEFAULT_SCRIPT;
 
     /* KATAKANA and HIRAGANA both map to 'kana' */
@@ -49,8 +50,6 @@
     case HB_SCRIPT_NKO:			return HB_TAG('n','k','o',' ');
     /* Unicode-5.1 additions */
     case HB_SCRIPT_VAI:			return HB_TAG('v','a','i',' ');
-    /* Unicode-5.2 additions */
-    /* Unicode-6.0 additions */
   }
 
   /* Else, just change first char to lowercase and return */
@@ -114,6 +113,18 @@
   return HB_SCRIPT_UNKNOWN;
 }
 
+void
+hb_ot_tags_from_script (hb_script_t  script,
+			hb_tag_t    *script_tag_1,
+			hb_tag_t    *script_tag_2)
+{
+  unsigned int count = 2;
+  hb_tag_t tags[2];
+  hb_ot_tags_from_script_and_language (script, HB_LANGUAGE_INVALID, &count, tags, nullptr, nullptr);
+  *script_tag_1 = count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_SCRIPT;
+  *script_tag_2 = count > 1 ? tags[1] : HB_OT_TAG_DEFAULT_SCRIPT;
+}
+
 /*
  * Complete list at:
  * https://docs.microsoft.com/en-us/typography/opentype/spec/scripttags
@@ -122,28 +133,37 @@
  * So we just do that, and handle the exceptional cases in a switch.
  */
 
-void
-hb_ot_tags_from_script (hb_script_t  script,
-			hb_tag_t    *script_tag_1,
-			hb_tag_t    *script_tag_2)
+static void
+hb_ot_all_tags_from_script (hb_script_t   script,
+			    unsigned int *count /* IN/OUT */,
+			    hb_tag_t     *tags /* OUT */)
 {
-  hb_tag_t new_tag;
+  unsigned int i = 0;
 
-  *script_tag_2 = HB_OT_TAG_DEFAULT_SCRIPT;
-  *script_tag_1 = hb_ot_old_tag_from_script (script);
-
-  new_tag = hb_ot_new_tag_from_script (script);
-  if (unlikely (new_tag != HB_OT_TAG_DEFAULT_SCRIPT)) {
-    *script_tag_2 = *script_tag_1;
-    *script_tag_1 = new_tag;
+  hb_tag_t new_tag = hb_ot_new_tag_from_script (script);
+  if (unlikely (new_tag != HB_OT_TAG_DEFAULT_SCRIPT))
+  {
+    tags[i++] = new_tag | '3';
+    if (*count > i)
+      tags[i++] = new_tag;
   }
+
+  if (*count > i)
+  {
+    hb_tag_t old_tag = hb_ot_old_tag_from_script (script);
+    if (old_tag != HB_OT_TAG_DEFAULT_SCRIPT)
+      tags[i++] = old_tag;
+  }
+
+  *count = i;
 }
 
 hb_script_t
 hb_ot_tag_to_script (hb_tag_t tag)
 {
-  if (unlikely ((tag & 0x000000FFu) == '2'))
-    return hb_ot_new_tag_to_script (tag);
+  unsigned char digit = tag & 0x000000FFu;
+  if (unlikely (digit == '2' || digit == '3'))
+    return hb_ot_new_tag_to_script (tag & 0xFFFFFF32);
 
   return hb_ot_old_tag_to_script (tag);
 }
@@ -151,732 +171,6 @@
 
 /* hb_language_t */
 
-typedef struct {
-  char language[4];
-  hb_tag_t tag;
-} LangTag;
-
-/*
- * Complete list at:
- * https://docs.microsoft.com/en-us/typography/opentype/spec/languagetags
- *
- * Generated by intersecting the OpenType language tag list from
- * Draft OpenType 1.5 spec, with with the ISO 639-3 codes from
- * 2008-08-04, matching on name, and finally adjusted manually.
- *
- * Updated on 2012-12-07 with more research into remaining codes.
- *
- * Updated on 2013-11-23 based on usage in SIL and Microsoft fonts,
- * the new proposal from Microsoft, and latest ISO 639-3 names.
- *
- * Some items still missing.  Those are commented out at the end.
- * Keep sorted for bsearch.
- *
- * Updated as of 2015-05-06: OT1.7 on MS website has some newer
- * items that we don't have here, eg. Zazaki.  This is the new
- * items in OpenType 1.7 (red items), most of which we have:
- * https://docs.microsoft.com/en-us/typography/opentype/spec/languagetags
- */
-
-static const LangTag ot_languages[] = {
-  {"aa",	HB_TAG('A','F','R',' ')},	/* Afar */
-  {"ab",	HB_TAG('A','B','K',' ')},	/* Abkhazian */
-  {"abq",	HB_TAG('A','B','A',' ')},	/* Abaza */
-  {"acf",	HB_TAG('F','A','N',' ')},	/* French Antillean */
-  {"ach",	HB_TAG('A','C','H',' ')},	/* Acoli */
-  {"acr",	HB_TAG('A','C','R',' ')},	/* Achi */
-  {"ada",	HB_TAG('D','N','G',' ')},	/* Dangme */
-  {"ady",	HB_TAG('A','D','Y',' ')},	/* Adyghe */
-  {"af",	HB_TAG('A','F','K',' ')},	/* Afrikaans */
-  {"ahg",	HB_TAG('A','G','W',' ')},	/* Agaw */
-  {"aii",	HB_TAG('S','W','A',' ')},	/* Swadaya Aramaic */
-  {"aio",	HB_TAG('A','I','O',' ')},	/* Aiton */
-  {"aiw",	HB_TAG('A','R','I',' ')},	/* Aari */
-  {"ak",	HB_TAG('T','W','I',' ')},	/* Akan [macrolanguage] */
-  {"aka",	HB_TAG('A','K','A',' ')},	/* Akan */
-  {"alt",	HB_TAG('A','L','T',' ')},	/* [Southern] Altai */
-  {"am",	HB_TAG('A','M','H',' ')},	/* Amharic */
-  {"amf",	HB_TAG('H','B','N',' ')},	/* Hammer-Banna */
-  {"amw",	HB_TAG('S','Y','R',' ')},	/* Western Neo-Aramaic */
-  {"an",	HB_TAG('A','R','G',' ')},	/* Aragonese */
-  {"ang",	HB_TAG('A','N','G',' ')},	/* Old English (ca. 450-1100) */
-  {"ar",	HB_TAG('A','R','A',' ')},	/* Arabic [macrolanguage] */
-  {"arb",	HB_TAG('A','R','A',' ')},	/* Standard Arabic */
-  {"arn",	HB_TAG('M','A','P',' ')},	/* Mapudungun */
-  {"ary",	HB_TAG('M','O','R',' ')},	/* Moroccan Arabic */
-  {"as",	HB_TAG('A','S','M',' ')},	/* Assamese */
-  {"ast",	HB_TAG('A','S','T',' ')},	/* Asturian/Asturleonese/Bable/Leonese */
-  {"ath",	HB_TAG('A','T','H',' ')},	/* Athapaskan [family] */
-  {"atj",	HB_TAG('R','C','R',' ')},	/* R-Cree */
-  {"atv",	HB_TAG('A','L','T',' ')},	/* [Northern] Altai */
-  {"av",	HB_TAG('A','V','R',' ')},	/* Avaric */
-  {"awa",	HB_TAG('A','W','A',' ')},	/* Awadhi */
-  {"ay",	HB_TAG('A','Y','M',' ')},	/* Aymara [macrolanguage] */
-  {"az",	HB_TAG('A','Z','E',' ')},	/* Azerbaijani [macrolanguage] */
-  {"azb",	HB_TAG('A','Z','B',' ')},	/* South Azerbaijani */
-  {"azj",	HB_TAG('A','Z','E',' ')},	/* North Azerbaijani */
-  {"ba",	HB_TAG('B','S','H',' ')},	/* Bashkir */
-  {"bad",	HB_TAG('B','A','D','0')},	/* Banda */
-  {"bai",	HB_TAG('B','M','L',' ')},	/* Bamileke [family] */
-  {"bal",	HB_TAG('B','L','I',' ')},	/* Baluchi [macrolangauge] */
-  {"ban",	HB_TAG('B','A','N',' ')},	/* Balinese */
-  {"bar",	HB_TAG('B','A','R',' ')},	/* Bavarian */
-  {"bbc",	HB_TAG('B','B','C',' ')},	/* Batak Toba */
-  {"bci",	HB_TAG('B','A','U',' ')},	/* Baoulé */
-  {"bcl",	HB_TAG('B','I','K',' ')},	/* Central Bikol */
-  {"bcq",	HB_TAG('B','C','H',' ')},	/* Bench */
-  {"bdy",	HB_TAG('B','D','Y',' ')},	/* Bandjalang */
-  {"be",	HB_TAG('B','E','L',' ')},	/* Belarusian */
-  {"bem",	HB_TAG('B','E','M',' ')},	/* Bemba (Zambia) */
-  {"ber",	HB_TAG('B','E','R',' ')},	/* Berber [family] */
-  {"bfq",	HB_TAG('B','A','D',' ')},	/* Badaga */
-  {"bft",	HB_TAG('B','L','T',' ')},	/* Balti */
-  {"bfu",	HB_TAG('L','A','H',' ')},	/* Lahuli */
-  {"bfy",	HB_TAG('B','A','G',' ')},	/* Baghelkhandi */
-  {"bg",	HB_TAG('B','G','R',' ')},	/* Bulgarian */
-  {"bgc",	HB_TAG('B','G','C',' ')},	/* Haryanvi */
-  {"bgq",	HB_TAG('B','G','Q',' ')},	/* Bagri */
-  {"bgr",	HB_TAG('Q','I','N',' ')},	/* Bawm Chin */
-  {"bhb",	HB_TAG('B','H','I',' ')},	/* Bhili */
-  {"bhk",	HB_TAG('B','I','K',' ')},	/* Albay Bicolano (retired code) */
-  {"bho",	HB_TAG('B','H','O',' ')},	/* Bhojpuri */
-  {"bi",	HB_TAG('B','I','S',' ')},	/* Bislama */
-  {"bik",	HB_TAG('B','I','K',' ')},	/* Bikol [macrolanguage] */
-  {"bin",	HB_TAG('E','D','O',' ')},	/* Bini */
-  {"bjj",	HB_TAG('B','J','J',' ')},	/* Kanauji */
-  {"bjt",	HB_TAG('B','L','N',' ')},	/* Balanta-Ganja */
-  {"bla",	HB_TAG('B','K','F',' ')},	/* Blackfoot */
-  {"ble",	HB_TAG('B','L','N',' ')},	/* Balanta-Kentohe */
-  {"blk",	HB_TAG('B','L','K',' ')},	/* Pa'O/Pa'o Karen */
-  {"bln",	HB_TAG('B','I','K',' ')},	/* Southern Catanduanes Bikol */
-  {"bm",	HB_TAG('B','M','B',' ')},	/* Bambara */
-  {"bn",	HB_TAG('B','E','N',' ')},	/* Bengali */
-  {"bo",	HB_TAG('T','I','B',' ')},	/* Tibetan */
-  {"bpy",	HB_TAG('B','P','Y',' ')},	/* Bishnupriya */
-  {"bqi",	HB_TAG('L','R','C',' ')},	/* Bakhtiari */
-  {"br",	HB_TAG('B','R','E',' ')},	/* Breton */
-  {"bra",	HB_TAG('B','R','I',' ')},	/* Braj Bhasha */
-  {"brh",	HB_TAG('B','R','H',' ')},	/* Brahui */
-  {"brx",	HB_TAG('B','R','X',' ')},	/* Bodo (India) */
-  {"bs",	HB_TAG('B','O','S',' ')},	/* Bosnian */
-  {"btb",	HB_TAG('B','T','I',' ')},	/* Beti (Cameroon) */
-  {"bto",	HB_TAG('B','I','K',' ')},	/* Rinconada Bikol */
-  {"bts",	HB_TAG('B','T','S',' ')},	/* Batak Simalungun */
-  {"bug",	HB_TAG('B','U','G',' ')},	/* Buginese */
-  {"bxr",	HB_TAG('R','B','U',' ')},	/* Russian Buriat */
-  {"byn",	HB_TAG('B','I','L',' ')},	/* Bilen */
-  {"ca",	HB_TAG('C','A','T',' ')},	/* Catalan */
-  {"cak",	HB_TAG('C','A','K',' ')},	/* Kaqchikel */
-  {"cbk",	HB_TAG('C','B','K',' ')},	/* Chavacano */
-  {"cbl",	HB_TAG('Q','I','N',' ')},	/* Bualkhaw Chin */
-  {"cco",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"ce",	HB_TAG('C','H','E',' ')},	/* Chechen */
-  {"ceb",	HB_TAG('C','E','B',' ')},	/* Cebuano */
-  {"cfm",	HB_TAG('H','A','L',' ')},	/* Halam/Falam Chin */
-  {"cgg",	HB_TAG('C','G','G',' ')},	/* Chiga */
-  {"ch",	HB_TAG('C','H','A',' ')},	/* Chamorro */
-  {"chj",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"chk",	HB_TAG('C','H','K','0')},	/* Chuukese */
-  {"cho",	HB_TAG('C','H','O',' ')},	/* Choctaw */
-  {"chp",	HB_TAG('C','H','P',' ')},	/* Chipewyan */
-  {"chq",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"chr",	HB_TAG('C','H','R',' ')},	/* Cherokee */
-  {"chy",	HB_TAG('C','H','Y',' ')},	/* Cheyenne */
-  {"chz",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"cja",	HB_TAG('C','J','A',' ')},	/* Western Cham */
-  {"cjm",	HB_TAG('C','J','M',' ')},	/* Eastern Cham */
-  {"cka",	HB_TAG('Q','I','N',' ')},	/* Khumi Awa Chin */
-  {"ckb",	HB_TAG('K','U','R',' ')},	/* Central Kurdish (Sorani) */
-  {"ckt",	HB_TAG('C','H','K',' ')},	/* Chukchi */
-  {"cld",	HB_TAG('S','Y','R',' ')},	/* Chaldean Neo-Aramaic */
-  {"cle",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"cmr",	HB_TAG('Q','I','N',' ')},	/* Mro-Khimi Chin */
-  {"cnb",	HB_TAG('Q','I','N',' ')},	/* Chinbon Chin */
-  {"cnh",	HB_TAG('Q','I','N',' ')},	/* Hakha Chin */
-  {"cnk",	HB_TAG('Q','I','N',' ')},	/* Khumi Chin */
-  {"cnl",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"cnt",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"cnw",	HB_TAG('Q','I','N',' ')},	/* Ngawn Chin */
-  {"cop",	HB_TAG('C','O','P',' ')},	/* Coptic */
-  {"cpa",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"cpp",	HB_TAG('C','P','P',' ')},	/* Creoles */
-  {"cr",	HB_TAG('C','R','E',' ')},	/* Cree */
-  {"cre",	HB_TAG('Y','C','R',' ')},	/* Y-Cree */
-  {"crh",	HB_TAG('C','R','T',' ')},	/* Crimean Tatar */
-  {"crj",	HB_TAG('E','C','R',' ')},	/* [Southern] East Cree */
-  {"crk",	HB_TAG('W','C','R',' ')},	/* West-Cree */
-  {"crl",	HB_TAG('E','C','R',' ')},	/* [Northern] East Cree */
-  {"crm",	HB_TAG('M','C','R',' ')},	/* Moose Cree */
-  {"crx",	HB_TAG('C','R','R',' ')},	/* Carrier */
-  {"cs",	HB_TAG('C','S','Y',' ')},	/* Czech */
-  {"csa",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"csb",	HB_TAG('C','S','B',' ')},	/* Kashubian */
-  {"csh",	HB_TAG('Q','I','N',' ')},	/* Asho Chin */
-  {"cso",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"csy",	HB_TAG('Q','I','N',' ')},	/* Siyin Chin */
-  {"ctd",	HB_TAG('Q','I','N',' ')},	/* Tedim Chin */
-  {"cte",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"ctg",	HB_TAG('C','T','G',' ')},	/* Chittagonian */
-  {"ctl",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"cts",	HB_TAG('B','I','K',' ')},	/* Northern Catanduanes Bikol */
-  {"cu",	HB_TAG('C','S','L',' ')},	/* Church Slavic */
-  {"cuc",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"cuk",	HB_TAG('C','U','K',' ')},	/* San Blas Kuna */
-  {"cv",	HB_TAG('C','H','U',' ')},	/* Chuvash */
-  {"cvn",	HB_TAG('C','C','H','N')},	/* Chinantec */
-  {"cwd",	HB_TAG('D','C','R',' ')},	/* Woods Cree */
-  {"cy",	HB_TAG('W','E','L',' ')},	/* Welsh */
-  {"czt",	HB_TAG('Q','I','N',' ')},	/* Zotung Chin */
-  {"da",	HB_TAG('D','A','N',' ')},	/* Danish */
-  {"dao",	HB_TAG('Q','I','N',' ')},	/* Daai Chin */
-  {"dap",	HB_TAG('N','I','S',' ')},	/* Nisi (India) */
-  {"dar",	HB_TAG('D','A','R',' ')},	/* Dargwa */
-  {"dax",	HB_TAG('D','A','X',' ')},	/* Dayi */
-  {"de",	HB_TAG('D','E','U',' ')},	/* German */
-  {"dgo",	HB_TAG('D','G','O',' ')},	/* Dogri */
-  {"dhd",	HB_TAG('M','A','W',' ')},	/* Dhundari */
-  {"dhg",	HB_TAG('D','H','G',' ')},	/* Dhangu */
-  {"din",	HB_TAG('D','N','K',' ')},	/* Dinka [macrolanguage] */
-  {"diq",	HB_TAG('D','I','Q',' ')},	/* Dimli */
-  {"dje",	HB_TAG('D','J','R',' ')},	/* Zarma */
-  {"djr",	HB_TAG('D','J','R','0')},	/* Djambarrpuyngu */
-  {"dng",	HB_TAG('D','U','N',' ')},	/* Dungan */
-  {"dnj",	HB_TAG('D','N','J',' ')},	/* Dan */
-  {"doi",	HB_TAG('D','G','R',' ')},	/* Dogri [macrolanguage] */
-  {"dsb",	HB_TAG('L','S','B',' ')},	/* Lower Sorbian */
-  {"duj",	HB_TAG('D','U','J',' ')},	/* Dhuwal */
-  {"dv",	HB_TAG('D','I','V',' ')},	/* Dhivehi/Divehi/Maldivian */
-  {"dyu",	HB_TAG('J','U','L',' ')},	/* Jula */
-  {"dz",	HB_TAG('D','Z','N',' ')},	/* Dzongkha */
-  {"ee",	HB_TAG('E','W','E',' ')},	/* Ewe */
-  {"efi",	HB_TAG('E','F','I',' ')},	/* Efik */
-  {"ekk",	HB_TAG('E','T','I',' ')},	/* Standard Estonian */
-  {"el",	HB_TAG('E','L','L',' ')},	/* Modern Greek (1453-) */
-  {"emk",	HB_TAG('M','N','K',' ')},	/* Eastern Maninkakan */
-  {"en",	HB_TAG('E','N','G',' ')},	/* English */
-  {"enf",	HB_TAG('F','N','E',' ')},	/* Forest Nenets */
-  {"enh",	HB_TAG('T','N','E',' ')},	/* Tundra Nenets */
-  {"eo",	HB_TAG('N','T','O',' ')},	/* Esperanto */
-  {"eot",	HB_TAG('B','T','I',' ')},	/* Beti (Côte d'Ivoire) */
-  {"es",	HB_TAG('E','S','P',' ')},	/* Spanish */
-  {"esu",	HB_TAG('E','S','U',' ')},	/* Central Yupik */
-  {"et",	HB_TAG('E','T','I',' ')},	/* Estonian [macrolanguage] */
-  {"eu",	HB_TAG('E','U','Q',' ')},	/* Basque */
-  {"eve",	HB_TAG('E','V','N',' ')},	/* Even */
-  {"evn",	HB_TAG('E','V','K',' ')},	/* Evenki */
-  {"fa",	HB_TAG('F','A','R',' ')},	/* Persian [macrolanguage] */
-  {"fan",	HB_TAG('F','A','N','0')},	/* Fang */
-  {"fat",	HB_TAG('F','A','T',' ')},	/* Fanti */
-  {"ff",	HB_TAG('F','U','L',' ')},	/* Fulah [macrolanguage] */
-  {"fi",	HB_TAG('F','I','N',' ')},	/* Finnish */
-  {"fil",	HB_TAG('P','I','L',' ')},	/* Filipino */
-  {"fj",	HB_TAG('F','J','I',' ')},	/* Fijian */
-  {"flm",	HB_TAG('H','A','L',' ')},	/* Halam/Falam Chin [retired ISO639 code] */
-  {"fo",	HB_TAG('F','O','S',' ')},	/* Faroese */
-  {"fon",	HB_TAG('F','O','N',' ')},	/* Fon */
-  {"fr",	HB_TAG('F','R','A',' ')},	/* French */
-  {"frc",	HB_TAG('F','R','C',' ')},	/* Cajun French */
-  {"frp",	HB_TAG('F','R','P',' ')},	/* Arpitan/Francoprovençal */
-  {"fuf",	HB_TAG('F','T','A',' ')},	/* Futa */
-  {"fur",	HB_TAG('F','R','L',' ')},	/* Friulian */
-  {"fuv",	HB_TAG('F','U','V',' ')},	/* Nigerian Fulfulde */
-  {"fy",	HB_TAG('F','R','I',' ')},	/* Western Frisian */
-  {"ga",	HB_TAG('I','R','I',' ')},	/* Irish */
-  {"gaa",	HB_TAG('G','A','D',' ')},	/* Ga */
-  {"gag",	HB_TAG('G','A','G',' ')},	/* Gagauz */
-  {"gbm",	HB_TAG('G','A','W',' ')},	/* Garhwali */
-  {"gd",	HB_TAG('G','A','E',' ')},	/* Scottish Gaelic */
-  {"gez",	HB_TAG('G','E','Z',' ')},	/* Ge'ez */
-  {"ggo",	HB_TAG('G','O','N',' ')},	/* Southern Gondi */
-  {"gih",	HB_TAG('G','I','H',' ')},	/* Githabul */
-  {"gil",	HB_TAG('G','I','L','0')},	/* Kiribati (Gilbertese) */
-  {"gkp",	HB_TAG('G','K','P',' ')},	/* Kpelle (Guinea) */
-  {"gl",	HB_TAG('G','A','L',' ')},	/* Galician */
-  {"gld",	HB_TAG('N','A','N',' ')},	/* Nanai */
-  {"glk",	HB_TAG('G','L','K',' ')},	/* Gilaki */
-  {"gn",	HB_TAG('G','U','A',' ')},	/* Guarani [macrolanguage] */
-  {"gnn",	HB_TAG('G','N','N',' ')},	/* Gumatj */
-  {"gno",	HB_TAG('G','O','N',' ')},	/* Northern Gondi */
-  {"gog",	HB_TAG('G','O','G',' ')},	/* Gogo */
-  {"gon",	HB_TAG('G','O','N',' ')},	/* Gondi [macrolanguage] */
-  {"grt",	HB_TAG('G','R','O',' ')},	/* Garo */
-  {"gru",	HB_TAG('S','O','G',' ')},	/* Sodo Gurage */
-  {"gsw",	HB_TAG('A','L','S',' ')},	/* Alsatian */
-  {"gu",	HB_TAG('G','U','J',' ')},	/* Gujarati */
-  {"guc",	HB_TAG('G','U','C',' ')},	/* Wayuu */
-  {"guf",	HB_TAG('G','U','F',' ')},	/* Gupapuyngu */
-  {"guk",	HB_TAG('G','M','Z',' ')},	/* Gumuz */
-/*{"guk",	HB_TAG('G','U','K',' ')},*/	/* Gumuz (in SIL fonts) */
-  {"guz",	HB_TAG('G','U','Z',' ')},	/* Ekegusii/Gusii */
-  {"gv",	HB_TAG('M','N','X',' ')},	/* Manx */
-  {"ha",	HB_TAG('H','A','U',' ')},	/* Hausa */
-  {"har",	HB_TAG('H','R','I',' ')},	/* Harari */
-  {"haw",	HB_TAG('H','A','W',' ')},	/* Hawaiian */
-  {"hay",	HB_TAG('H','A','Y',' ')},	/* Haya */
-  {"haz",	HB_TAG('H','A','Z',' ')},	/* Hazaragi */
-  {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
-  {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
-  {"hil",	HB_TAG('H','I','L',' ')},	/* Hiligaynon */
-  {"hlt",	HB_TAG('Q','I','N',' ')},	/* Matu Chin */
-  {"hmn",	HB_TAG('H','M','N',' ')},	/* Hmong */
-  {"hnd",	HB_TAG('H','N','D',' ')},	/* [Southern] Hindko */
-  {"hne",	HB_TAG('C','H','H',' ')},	/* Chattisgarhi */
-  {"hno",	HB_TAG('H','N','D',' ')},	/* [Northern] Hindko */
-  {"ho",	HB_TAG('H','M','O',' ')},	/* Hiri Motu */
-  {"hoc",	HB_TAG('H','O',' ',' ')},	/* Ho */
-  {"hoj",	HB_TAG('H','A','R',' ')},	/* Harauti */
-  {"hr",	HB_TAG('H','R','V',' ')},	/* Croatian */
-  {"hsb",	HB_TAG('U','S','B',' ')},	/* Upper Sorbian */
-  {"ht",	HB_TAG('H','A','I',' ')},	/* Haitian/Haitian Creole */
-  {"hu",	HB_TAG('H','U','N',' ')},	/* Hungarian */
-  {"hy",	HB_TAG('H','Y','E',' ')},	/* Armenian */
-  {"hz",	HB_TAG('H','E','R',' ')},	/* Herero */
-  {"ia",	HB_TAG('I','N','A',' ')},	/* Interlingua (International Auxiliary Language Association) */
-  {"iba",	HB_TAG('I','B','A',' ')},	/* Iban */
-  {"ibb",	HB_TAG('I','B','B',' ')},	/* Ibibio */
-  {"id",	HB_TAG('I','N','D',' ')},	/* Indonesian */
-  {"ie",	HB_TAG('I','L','E',' ')},	/* Interlingue/Occidental */
-  {"ig",	HB_TAG('I','B','O',' ')},	/* Igbo */
-  {"igb",	HB_TAG('E','B','I',' ')},	/* Ebira */
-  {"ii",	HB_TAG('Y','I','M',' ')},	/* Yi Modern */
-  {"ijc",	HB_TAG('I','J','O',' ')},	/* Izon */
-  {"ijo",	HB_TAG('I','J','O',' ')},	/* Ijo [family] */
-  {"ik",	HB_TAG('I','P','K',' ')},	/* Inupiaq [macrolanguage] */
-  {"ilo",	HB_TAG('I','L','O',' ')},	/* Ilokano */
-  {"inh",	HB_TAG('I','N','G',' ')},	/* Ingush */
-  {"io",	HB_TAG('I','D','O',' ')},	/* Ido */
-  {"is",	HB_TAG('I','S','L',' ')},	/* Icelandic */
-  {"it",	HB_TAG('I','T','A',' ')},	/* Italian */
-  {"iu",	HB_TAG('I','N','U',' ')},	/* Inuktitut [macrolanguage] */
-  {"ja",	HB_TAG('J','A','N',' ')},	/* Japanese */
-  {"jam",	HB_TAG('J','A','M',' ')},	/* Jamaican Creole English */
-  {"jbo",	HB_TAG('J','B','O',' ')},	/* Lojban */
-  {"jv",	HB_TAG('J','A','V',' ')},	/* Javanese */
-  {"ka",	HB_TAG('K','A','T',' ')},	/* Georgian */
-  {"kaa",	HB_TAG('K','R','K',' ')},	/* Karakalpak */
-  {"kab",	HB_TAG('K','A','B','0')},	/* Kabyle */
-  {"kam",	HB_TAG('K','M','B',' ')},	/* Kamba (Kenya) */
-  {"kar",	HB_TAG('K','R','N',' ')},	/* Karen [family] */
-  {"kat",	HB_TAG('K','G','E',' ')},	/* Khutsuri Georgian */
-  {"kbd",	HB_TAG('K','A','B',' ')},	/* Kabardian */
-  {"kde",	HB_TAG('K','D','E',' ')},	/* Makonde */
-  {"kdr",	HB_TAG('K','R','M',' ')},	/* Karaim */
-  {"kdt",	HB_TAG('K','U','Y',' ')},	/* Kuy */
-  {"kea",	HB_TAG('K','E','A',' ')},	/* Kabuverdianu (Crioulo) */
-  {"kek",	HB_TAG('K','E','K',' ')},	/* Kekchi */
-  {"kex",	HB_TAG('K','K','N',' ')},	/* Kokni */
-  {"kfa",	HB_TAG('K','O','D',' ')},	/* Kodagu */
-  {"kfr",	HB_TAG('K','A','C',' ')},	/* Kachchi */
-  {"kfx",	HB_TAG('K','U','L',' ')},	/* Kulvi */
-  {"kfy",	HB_TAG('K','M','N',' ')},	/* Kumaoni */
-  {"kg",	HB_TAG('K','O','N',' ')},	/* Kongo [macrolanguage] */
-  {"kha",	HB_TAG('K','S','I',' ')},	/* Khasi */
-  {"khb",	HB_TAG('X','B','D',' ')},	/* Lü */
-  {"kht",	HB_TAG('K','H','N',' ')},	/* Khamti (Microsoft fonts) */
-/*{"kht",	HB_TAG('K','H','T',' ')},*/	/* Khamti (OpenType spec and SIL fonts) */
-  {"khw",	HB_TAG('K','H','W',' ')},	/* Khowar */
-  {"ki",	HB_TAG('K','I','K',' ')},	/* Gikuyu/Kikuyu */
-  {"kiu",	HB_TAG('K','I','U',' ')},	/* Kirmanjki */
-  {"kj",	HB_TAG('K','U','A',' ')},	/* Kuanyama/Kwanyama */
-  {"kjd",	HB_TAG('K','J','D',' ')},	/* Southern Kiwai */
-  {"kjh",	HB_TAG('K','H','A',' ')},	/* Khakass */
-  {"kjp",	HB_TAG('K','J','P',' ')},	/* Pwo Eastern Karen */
-  {"kk",	HB_TAG('K','A','Z',' ')},	/* Kazakh */
-  {"kl",	HB_TAG('G','R','N',' ')},	/* Kalaallisut */
-  {"kln",	HB_TAG('K','A','L',' ')},	/* Kalenjin */
-  {"km",	HB_TAG('K','H','M',' ')},	/* Central Khmer */
-  {"kmb",	HB_TAG('M','B','N',' ')},	/* Kimbundu */
-  {"kmw",	HB_TAG('K','M','O',' ')},	/* Komo (Democratic Republic of Congo) */
-  {"kn",	HB_TAG('K','A','N',' ')},	/* Kannada */
-  {"knn",	HB_TAG('K','O','K',' ')},	/* Konkani */
-  {"ko",	HB_TAG('K','O','R',' ')},	/* Korean */
-  {"koi",	HB_TAG('K','O','P',' ')},	/* Komi-Permyak */
-  {"kok",	HB_TAG('K','O','K',' ')},	/* Konkani [macrolanguage] */
-  {"kon",	HB_TAG('K','O','N','0')},	/* Kongo */
-  {"kos",	HB_TAG('K','O','S',' ')},	/* Kosraean */
-  {"kpe",	HB_TAG('K','P','L',' ')},	/* Kpelle [macrolanguage] */
-  {"kpv",	HB_TAG('K','O','Z',' ')},	/* Komi-Zyrian */
-  {"kpy",	HB_TAG('K','Y','K',' ')},	/* Koryak */
-  {"kqy",	HB_TAG('K','R','T',' ')},	/* Koorete */
-  {"kr",	HB_TAG('K','N','R',' ')},	/* Kanuri [macrolanguage] */
-  {"kri",	HB_TAG('K','R','I',' ')},	/* Krio */
-  {"krl",	HB_TAG('K','R','L',' ')},	/* Karelian */
-  {"kru",	HB_TAG('K','U','U',' ')},	/* Kurukh */
-  {"ks",	HB_TAG('K','S','H',' ')},	/* Kashmiri */
-  {"ksh",	HB_TAG('K','S','H','0')},	/* Ripuarian, Kölsch */
-/*{"ksw",	HB_TAG('K','R','N',' ')},*/	/* S'gaw Karen (Microsoft fonts?) */
-  {"ksw",	HB_TAG('K','S','W',' ')},	/* S'gaw Karen (OpenType spec and SIL fonts) */
-  {"ktb",	HB_TAG('K','E','B',' ')},	/* Kebena */
-  {"ktu",	HB_TAG('K','O','N',' ')},	/* Kikongo */
-  {"ku",	HB_TAG('K','U','R',' ')},	/* Kurdish [macrolanguage] */
-  {"kum",	HB_TAG('K','U','M',' ')},	/* Kumyk */
-  {"kv",	HB_TAG('K','O','M',' ')},	/* Komi [macrolanguage] */
-  {"kvd",	HB_TAG('K','U','I',' ')},	/* Kui (Indonesia) */
-  {"kw",	HB_TAG('C','O','R',' ')},	/* Cornish */
-  {"kxc",	HB_TAG('K','M','S',' ')},	/* Komso */
-  {"kxu",	HB_TAG('K','U','I',' ')},	/* Kui (India) */
-  {"ky",	HB_TAG('K','I','R',' ')},	/* Kirghiz/Kyrgyz */
-  {"kyu",	HB_TAG('K','Y','U',' ')},	/* Western Kayah */
-  {"la",	HB_TAG('L','A','T',' ')},	/* Latin */
-  {"lad",	HB_TAG('J','U','D',' ')},	/* Ladino */
-  {"lb",	HB_TAG('L','T','Z',' ')},	/* Luxembourgish */
-  {"lbe",	HB_TAG('L','A','K',' ')},	/* Lak */
-  {"lbj",	HB_TAG('L','D','K',' ')},	/* Ladakhi */
-  {"lez",	HB_TAG('L','E','Z',' ')},	/* Lezgi */
-  {"lg",	HB_TAG('L','U','G',' ')},	/* Ganda */
-  {"li",	HB_TAG('L','I','M',' ')},	/* Limburgan/Limburger/Limburgish */
-  {"lif",	HB_TAG('L','M','B',' ')},	/* Limbu */
-  {"lij",	HB_TAG('L','I','J',' ')},	/* Ligurian */
-  {"lis",	HB_TAG('L','I','S',' ')},	/* Lisu */
-  {"ljp",	HB_TAG('L','J','P',' ')},	/* Lampung Api */
-  {"lki",	HB_TAG('L','K','I',' ')},	/* Laki */
-  {"lld",	HB_TAG('L','A','D',' ')},	/* Ladin */
-  {"lmn",	HB_TAG('L','A','M',' ')},	/* Lambani */
-  {"lmo",	HB_TAG('L','M','O',' ')},	/* Lombard */
-  {"ln",	HB_TAG('L','I','N',' ')},	/* Lingala */
-  {"lo",	HB_TAG('L','A','O',' ')},	/* Lao */
-  {"lom",	HB_TAG('L','O','M',' ')},	/* Loma */
-  {"lrc",	HB_TAG('L','R','C',' ')},	/* Northern Luri */
-  {"lt",	HB_TAG('L','T','H',' ')},	/* Lithuanian */
-  {"lu",	HB_TAG('L','U','B',' ')},	/* Luba-Katanga */
-  {"lua",	HB_TAG('L','U','B',' ')},	/* Luba-Kasai */
-  {"luo",	HB_TAG('L','U','O',' ')},	/* Luo (Kenya and Tanzania) */
-  {"lus",	HB_TAG('M','I','Z',' ')},	/* Mizo */
-  {"luy",	HB_TAG('L','U','H',' ')},	/* Luyia/Oluluyia [macrolanguage] */
-  {"luz",	HB_TAG('L','R','C',' ')},	/* Southern Luri */
-  {"lv",	HB_TAG('L','V','I',' ')},	/* Latvian */
-  {"lzz",	HB_TAG('L','A','Z',' ')},	/* Laz */
-  {"mad",	HB_TAG('M','A','D',' ')},	/* Madurese */
-  {"mag",	HB_TAG('M','A','G',' ')},	/* Magahi */
-  {"mai",	HB_TAG('M','T','H',' ')},	/* Maithili */
-  {"mak",	HB_TAG('M','K','R',' ')},	/* Makasar */
-  {"mam",	HB_TAG('M','A','M',' ')},	/* Mam */
-  {"man",	HB_TAG('M','N','K',' ')},	/* Manding/Mandingo [macrolanguage] */
-  {"mdc",	HB_TAG('M','L','E',' ')},	/* Male (Papua New Guinea) */
-  {"mdf",	HB_TAG('M','O','K',' ')},	/* Moksha */
-  {"mdr",	HB_TAG('M','D','R',' ')},	/* Mandar */
-  {"mdy",	HB_TAG('M','L','E',' ')},	/* Male (Ethiopia) */
-  {"men",	HB_TAG('M','D','E',' ')},	/* Mende (Sierra Leone) */
-  {"mer",	HB_TAG('M','E','R',' ')},	/* Meru */
-  {"mfe",	HB_TAG('M','F','E',' ')},	/* Morisyen */
-  {"mg",	HB_TAG('M','L','G',' ')},	/* Malagasy [macrolanguage] */
-  {"mh",	HB_TAG('M','A','H',' ')},	/* Marshallese */
-  {"mhr",	HB_TAG('L','M','A',' ')},	/* Low Mari */
-  {"mi",	HB_TAG('M','R','I',' ')},	/* Maori */
-  {"min",	HB_TAG('M','I','N',' ')},	/* Minangkabau */
-  {"mk",	HB_TAG('M','K','D',' ')},	/* Macedonian */
-  {"mku",	HB_TAG('M','N','K',' ')},	/* Konyanka Maninka */
-  {"mkw",	HB_TAG('M','K','W',' ')},	/* Kituba (Congo) */
-  {"ml",	HB_TAG('M','L','R',' ')},	/* Malayalam */
-  {"mlq",	HB_TAG('M','N','K',' ')},	/* Western Maninkakan */
-  {"mn",	HB_TAG('M','N','G',' ')},	/* Mongolian [macrolanguage] */
-  {"mnc",	HB_TAG('M','C','H',' ')},	/* Manchu */
-  {"mni",	HB_TAG('M','N','I',' ')},	/* Manipuri */
-  {"mnk",	HB_TAG('M','N','D',' ')},	/* Mandinka */
-  {"mns",	HB_TAG('M','A','N',' ')},	/* Mansi */
-  {"mnw",	HB_TAG('M','O','N',' ')},	/* Mon */
-  {"mo",	HB_TAG('M','O','L',' ')},	/* Moldavian */
-  {"moh",	HB_TAG('M','O','H',' ')},	/* Mohawk */
-  {"mos",	HB_TAG('M','O','S',' ')},	/* Mossi */
-  {"mpe",	HB_TAG('M','A','J',' ')},	/* Majang */
-  {"mr",	HB_TAG('M','A','R',' ')},	/* Marathi */
-  {"mrh",	HB_TAG('Q','I','N',' ')},	/* Mara Chin */
-  {"mrj",	HB_TAG('H','M','A',' ')},	/* High Mari */
-  {"ms",	HB_TAG('M','L','Y',' ')},	/* Malay [macrolanguage] */
-  {"msc",	HB_TAG('M','N','K',' ')},	/* Sankaran Maninka */
-  {"mt",	HB_TAG('M','T','S',' ')},	/* Maltese */
-  {"mtr",	HB_TAG('M','A','W',' ')},	/* Mewari */
-  {"mus",	HB_TAG('M','U','S',' ')},	/* Creek */
-  {"mve",	HB_TAG('M','A','W',' ')},	/* Marwari (Pakistan) */
-  {"mwk",	HB_TAG('M','N','K',' ')},	/* Kita Maninkakan */
-  {"mwl",	HB_TAG('M','W','L',' ')},	/* Mirandese */
-  {"mwr",	HB_TAG('M','A','W',' ')},	/* Marwari [macrolanguage] */
-  {"mww",	HB_TAG('M','W','W',' ')},	/* Hmong Daw */
-  {"my",	HB_TAG('B','R','M',' ')},	/* Burmese */
-  {"mym",	HB_TAG('M','E','N',' ')},	/* Me'en */
-  {"myn",	HB_TAG('M','Y','N',' ')},	/* Mayan */
-  {"myq",	HB_TAG('M','N','K',' ')},	/* Forest Maninka (retired code) */
-  {"myv",	HB_TAG('E','R','Z',' ')},	/* Erzya */
-  {"mzn",	HB_TAG('M','Z','N',' ')},	/* Mazanderani */
-  {"na",	HB_TAG('N','A','U',' ')},	/* Nauru */
-  {"nag",	HB_TAG('N','A','G',' ')},	/* Naga-Assamese */
-  {"nah",	HB_TAG('N','A','H',' ')},	/* Nahuatl [family] */
-  {"nap",	HB_TAG('N','A','P',' ')},	/* Neapolitan */
-  {"nb",	HB_TAG('N','O','R',' ')},	/* Norwegian Bokmål */
-  {"nco",	HB_TAG('S','I','B',' ')},	/* Sibe */
-  {"nd",	HB_TAG('N','D','B',' ')},	/* [North] Ndebele */
-  {"ndc",	HB_TAG('N','D','C',' ')},	/* Ndau */
-  {"nds",	HB_TAG('N','D','S',' ')},	/* Low German/Low Saxon */
-  {"ne",	HB_TAG('N','E','P',' ')},	/* Nepali */
-  {"new",	HB_TAG('N','E','W',' ')},	/* Newari */
-  {"ng",	HB_TAG('N','D','G',' ')},	/* Ndonga */
-  {"nga",	HB_TAG('N','G','A',' ')},	/* Ngabaka */
-  {"ngl",	HB_TAG('L','M','W',' ')},	/* Lomwe */
-  {"ngo",	HB_TAG('S','X','T',' ')},	/* Sutu */
-  {"niu",	HB_TAG('N','I','U',' ')},	/* Niuean */
-  {"niv",	HB_TAG('G','I','L',' ')},	/* Gilyak */
-  {"nl",	HB_TAG('N','L','D',' ')},	/* Dutch */
-  {"nn",	HB_TAG('N','Y','N',' ')},	/* Norwegian Nynorsk */
-  {"no",	HB_TAG('N','O','R',' ')},	/* Norwegian [macrolanguage] */
-  {"nod",	HB_TAG('N','T','A',' ')},	/* Northern Thai */
-  {"noe",	HB_TAG('N','O','E',' ')},	/* Nimadi */
-  {"nog",	HB_TAG('N','O','G',' ')},	/* Nogai */
-  {"nov",	HB_TAG('N','O','V',' ')},	/* Novial */
-  {"nqo",	HB_TAG('N','K','O',' ')},	/* N'Ko */
-  {"nr",	HB_TAG('N','D','B',' ')},	/* [South] Ndebele */
-  {"nsk",	HB_TAG('N','A','S',' ')},	/* Naskapi */
-  {"nso",	HB_TAG('S','O','T',' ')},	/* [Northern] Sotho */
-  {"nv",	HB_TAG('N','A','V',' ')},	/* Navajo */
-  {"ny",	HB_TAG('C','H','I',' ')},	/* Chewa/Chichwa/Nyanja */
-  {"nym",	HB_TAG('N','Y','M',' ')},	/* Nyamwezi */
-  {"nyn",	HB_TAG('N','K','L',' ')},	/* Nyankole */
-  {"oc",	HB_TAG('O','C','I',' ')},	/* Occitan (post 1500) */
-  {"oj",	HB_TAG('O','J','B',' ')},	/* Ojibwa [macrolanguage] */
-  {"ojs",	HB_TAG('O','C','R',' ')},	/* Oji-Cree */
-  {"okm",	HB_TAG('K','O','H',' ')},	/* Korean Old Hangul */
-  {"om",	HB_TAG('O','R','O',' ')},	/* Oromo [macrolanguage] */
-  {"or",	HB_TAG('O','R','I',' ')},	/* Oriya */
-  {"os",	HB_TAG('O','S','S',' ')},	/* Ossetian */
-  {"pa",	HB_TAG('P','A','N',' ')},	/* Panjabi */
-  {"pag",	HB_TAG('P','A','G',' ')},	/* Pangasinan */
-  {"pam",	HB_TAG('P','A','M',' ')},	/* Kapampangan/Pampanga */
-  {"pap",	HB_TAG('P','A','P','0')},	/* Papiamento */
-  {"pau",	HB_TAG('P','A','U',' ')},	/* Palauan */
-  {"pcc",	HB_TAG('P','C','C',' ')},	/* Bouyei */
-  {"pcd",	HB_TAG('P','C','D',' ')},	/* Picard */
-  {"pce",	HB_TAG('P','L','G',' ')},	/* [Ruching] Palaung */
-  {"pck",	HB_TAG('Q','I','N',' ')},	/* Paite Chin */
-  {"pdc",	HB_TAG('P','D','C',' ')},	/* Pennsylvania German */
-  {"pes",	HB_TAG('F','A','R',' ')},	/* Iranian Persian */
-  {"phk",	HB_TAG('P','H','K',' ')},	/* Phake */
-  {"pi",	HB_TAG('P','A','L',' ')},	/* Pali */
-  {"pih",	HB_TAG('P','I','H',' ')},	/* Pitcairn-Norfolk */
-  {"pl",	HB_TAG('P','L','K',' ')},	/* Polish */
-  {"pll",	HB_TAG('P','L','G',' ')},	/* [Shwe] Palaung */
-  {"plp",	HB_TAG('P','A','P',' ')},	/* Palpa */
-  {"pms",	HB_TAG('P','M','S',' ')},	/* Piemontese */
-  {"pnb",	HB_TAG('P','N','B',' ')},	/* Western Panjabi */
-  {"poh",	HB_TAG('P','O','H',' ')},	/* Pocomchi */
-  {"pon",	HB_TAG('P','O','N',' ')},	/* Pohnpeian */
-  {"prs",	HB_TAG('D','R','I',' ')},	/* Afghan Persian/Dari */
-  {"ps",	HB_TAG('P','A','S',' ')},	/* Pashto/Pushto [macrolanguage] */
-  {"pt",	HB_TAG('P','T','G',' ')},	/* Portuguese */
-  {"pwo",	HB_TAG('P','W','O',' ')},	/* Pwo Western Karen */
-  {"qu",	HB_TAG('Q','U','Z',' ')},	/* Quechua [macrolanguage] */
-  {"quc",	HB_TAG('Q','U','C',' ')},	/* K'iche'/Quiché */
-  {"quh",	HB_TAG('Q','U','H',' ')},	/* Quechua (Bolivia) */
-  {"quz",	HB_TAG('Q','U','Z',' ')},	/* Cusco Quechua */
-  {"qvi",	HB_TAG('Q','V','I',' ')},	/* Quechua (Ecuador) */
-  {"qwh",	HB_TAG('Q','W','H',' ')},	/* Quechua (Peru) */
-  {"raj",	HB_TAG('R','A','J',' ')},	/* Rajasthani [macrolanguage] */
-  {"rar",	HB_TAG('R','A','R',' ')},	/* Rarotongan */
-  {"rbb",	HB_TAG('P','L','G',' ')},	/* Rumai Palaung */
-  {"rej",	HB_TAG('R','E','J',' ')},	/* Rejang */
-  {"ria",	HB_TAG('R','I','A',' ')},	/* Riang (India) */
-  {"rif",	HB_TAG('R','I','F',' ')},	/* Tarifit */
-  {"ril",	HB_TAG('R','I','A',' ')},	/* Riang (Myanmar) */
-  {"rit",	HB_TAG('R','I','T',' ')},	/* Ritarungo */
-  {"rki",	HB_TAG('A','R','K',' ')},	/* Rakhine */
-  {"rkw",	HB_TAG('R','K','W',' ')},	/* Arakwal */
-  {"rm",	HB_TAG('R','M','S',' ')},	/* Romansh */
-  {"rmy",	HB_TAG('R','M','Y',' ')},	/* Vlax Romani */
-  {"rn",	HB_TAG('R','U','N',' ')},	/* Rundi */
-  {"ro",	HB_TAG('R','O','M',' ')},	/* Romanian */
-  {"rom",	HB_TAG('R','O','Y',' ')},	/* Romany [macrolanguage] */
-  {"rtm",	HB_TAG('R','T','M',' ')},	/* Rotuman */
-  {"ru",	HB_TAG('R','U','S',' ')},	/* Russian */
-  {"rue",	HB_TAG('R','S','Y',' ')},	/* Rusyn */
-  {"rup",	HB_TAG('R','U','P',' ')},	/* Aromanian/Arumanian/Macedo-Romanian */
-  {"rw",	HB_TAG('R','U','A',' ')},	/* Kinyarwanda */
-  {"rwr",	HB_TAG('M','A','W',' ')},	/* Marwari (India) */
-  {"sa",	HB_TAG('S','A','N',' ')},	/* Sanskrit */
-  {"sah",	HB_TAG('Y','A','K',' ')},	/* Yakut */
-  {"sam",	HB_TAG('P','A','A',' ')},	/* Palestinian Aramaic */
-  {"sas",	HB_TAG('S','A','S',' ')},	/* Sasak */
-  {"sat",	HB_TAG('S','A','T',' ')},	/* Santali */
-  {"sc",	HB_TAG('S','R','D',' ')},	/* Sardinian [macrolanguage] */
-  {"sck",	HB_TAG('S','A','D',' ')},	/* Sadri */
-  {"scn",	HB_TAG('S','C','N',' ')},	/* Sicilian */
-  {"sco",	HB_TAG('S','C','O',' ')},	/* Scots */
-  {"scs",	HB_TAG('S','L','A',' ')},	/* [North] Slavey */
-  {"sd",	HB_TAG('S','N','D',' ')},	/* Sindhi */
-  {"se",	HB_TAG('N','S','M',' ')},	/* Northern Sami */
-  {"seh",	HB_TAG('S','N','A',' ')},	/* Sena */
-  {"sel",	HB_TAG('S','E','L',' ')},	/* Selkup */
-  {"sez",	HB_TAG('Q','I','N',' ')},	/* Senthang Chin */
-  {"sg",	HB_TAG('S','G','O',' ')},	/* Sango */
-  {"sga",	HB_TAG('S','G','A',' ')},	/* Old Irish (to 900) */
-  {"sgs",	HB_TAG('S','G','S',' ')},	/* Samogitian */
-  {"sgw",	HB_TAG('C','H','G',' ')},	/* Sebat Bet Gurage */
-/*{"sgw",	HB_TAG('S','G','W',' ')},*/	/* Sebat Bet Gurage (in SIL fonts) */
-  {"shi",	HB_TAG('S','H','I',' ')},	/* Tachelhit */
-  {"shn",	HB_TAG('S','H','N',' ')},	/* Shan */
-  {"si",	HB_TAG('S','N','H',' ')},	/* Sinhala */
-  {"sid",	HB_TAG('S','I','D',' ')},	/* Sidamo */
-  {"sjd",	HB_TAG('K','S','M',' ')},	/* Kildin Sami */
-  {"sk",	HB_TAG('S','K','Y',' ')},	/* Slovak */
-  {"skr",	HB_TAG('S','R','K',' ')},	/* Seraiki */
-  {"sl",	HB_TAG('S','L','V',' ')},	/* Slovenian */
-  {"sm",	HB_TAG('S','M','O',' ')},	/* Samoan */
-  {"sma",	HB_TAG('S','S','M',' ')},	/* Southern Sami */
-  {"smj",	HB_TAG('L','S','M',' ')},	/* Lule Sami */
-  {"smn",	HB_TAG('I','S','M',' ')},	/* Inari Sami */
-  {"sms",	HB_TAG('S','K','S',' ')},	/* Skolt Sami */
-  {"sn",	HB_TAG('S','N','A','0')},	/* Shona */
-  {"snk",	HB_TAG('S','N','K',' ')},	/* Soninke */
-  {"so",	HB_TAG('S','M','L',' ')},	/* Somali */
-  {"sop",	HB_TAG('S','O','P',' ')},	/* Songe */
-  {"sq",	HB_TAG('S','Q','I',' ')},	/* Albanian [macrolanguage] */
-  {"sr",	HB_TAG('S','R','B',' ')},	/* Serbian */
-  {"srr",	HB_TAG('S','R','R',' ')},	/* Serer */
-  {"ss",	HB_TAG('S','W','Z',' ')},	/* Swati */
-  {"st",	HB_TAG('S','O','T',' ')},	/* [Southern] Sotho */
-  {"stq",	HB_TAG('S','T','Q',' ')},	/* Saterfriesisch */
-  {"stv",	HB_TAG('S','I','G',' ')},	/* Silt'e */
-  {"su",	HB_TAG('S','U','N',' ')},	/* Sundanese */
-  {"suk",	HB_TAG('S','U','K',' ')},	/* Sukama */
-  {"suq",	HB_TAG('S','U','R',' ')},	/* Suri */
-  {"sv",	HB_TAG('S','V','E',' ')},	/* Swedish */
-  {"sva",	HB_TAG('S','V','A',' ')},	/* Svan */
-  {"sw",	HB_TAG('S','W','K',' ')},	/* Swahili [macrolanguage] */
-  {"swb",	HB_TAG('C','M','R',' ')},	/* Comorian */
-  {"swh",	HB_TAG('S','W','K',' ')},	/* Kiswahili/Swahili */
-  {"swv",	HB_TAG('M','A','W',' ')},	/* Shekhawati */
-  {"sxu",	HB_TAG('S','X','U',' ')},	/* Upper Saxon */
-  {"syc",	HB_TAG('S','Y','R',' ')},	/* Classical Syriac */
-  {"syl",	HB_TAG('S','Y','L',' ')},	/* Sylheti */
-  {"syr",	HB_TAG('S','Y','R',' ')},	/* Syriac [macrolanguage] */
-  {"szl",	HB_TAG('S','Z','L',' ')},	/* Silesian */
-  {"ta",	HB_TAG('T','A','M',' ')},	/* Tamil */
-  {"tab",	HB_TAG('T','A','B',' ')},	/* Tabasaran */
-  {"tcp",	HB_TAG('Q','I','N',' ')},	/* Tawr Chin */
-  {"tcy",	HB_TAG('T','U','L',' ')},	/* Tulu */
-  {"tcz",	HB_TAG('Q','I','N',' ')},	/* Thado Chin */
-  {"tdd",	HB_TAG('T','D','D',' ')},	/* Tai Nüa */
-  {"te",	HB_TAG('T','E','L',' ')},	/* Telugu */
-  {"tem",	HB_TAG('T','M','N',' ')},	/* Temne */
-  {"tet",	HB_TAG('T','E','T',' ')},	/* Tetum */
-  {"tg",	HB_TAG('T','A','J',' ')},	/* Tajik */
-  {"th",	HB_TAG('T','H','A',' ')},	/* Thai */
-  {"ti",	HB_TAG('T','G','Y',' ')},	/* Tigrinya */
-  {"tig",	HB_TAG('T','G','R',' ')},	/* Tigre */
-  {"tiv",	HB_TAG('T','I','V',' ')},	/* Tiv */
-  {"tk",	HB_TAG('T','K','M',' ')},	/* Turkmen */
-  {"tl",	HB_TAG('T','G','L',' ')},	/* Tagalog */
-  {"tmh",	HB_TAG('T','M','H',' ')},	/* Tamashek */
-  {"tn",	HB_TAG('T','N','A',' ')},	/* Tswana */
-  {"to",	HB_TAG('T','G','N',' ')},	/* Tonga (Tonga Islands) */
-  {"tod",	HB_TAG('T','O','D','0')},	/* Toma */
-  {"toi",	HB_TAG('T','N','G',' ')},	/* Tonga */
-  {"tpi",	HB_TAG('T','P','I',' ')},	/* Tok Pisin */
-  {"tr",	HB_TAG('T','R','K',' ')},	/* Turkish */
-  {"tru",	HB_TAG('T','U','A',' ')},	/* Turoyo Aramaic */
-  {"ts",	HB_TAG('T','S','G',' ')},	/* Tsonga */
-  {"tt",	HB_TAG('T','A','T',' ')},	/* Tatar */
-  {"tum",	HB_TAG('T','U','M',' ')},	/* Tumbuka */
-  {"tvl",	HB_TAG('T','V','L',' ')},	/* Tuvalu */
-  {"tw",	HB_TAG('T','W','I',' ')},	/* Twi */
-  {"ty",	HB_TAG('T','H','T',' ')},	/* Tahitian */
-  {"tyv",	HB_TAG('T','U','V',' ')},	/* Tuvin */
-  {"tyz",	HB_TAG('T','Y','Z',' ')},	/* Tày */
-  {"tzm",	HB_TAG('T','Z','M',' ')},	/* Central Atlas Tamazight */
-  {"tzo",	HB_TAG('T','Z','O',' ')},	/* Tzotzil */
-  {"udm",	HB_TAG('U','D','M',' ')},	/* Udmurt */
-  {"ug",	HB_TAG('U','Y','G',' ')},	/* Uighur */
-  {"uk",	HB_TAG('U','K','R',' ')},	/* Ukrainian */
-  {"umb",	HB_TAG('U','M','B',' ')},	/* Umbundu */
-  {"unr",	HB_TAG('M','U','N',' ')},	/* Mundari */
-  {"ur",	HB_TAG('U','R','D',' ')},	/* Urdu */
-  {"uz",	HB_TAG('U','Z','B',' ')},	/* Uzbek [macrolanguage] */
-  {"uzn",	HB_TAG('U','Z','B',' ')},	/* Northern Uzbek */
-  {"uzs",	HB_TAG('U','Z','B',' ')},	/* Southern Uzbek */
-  {"ve",	HB_TAG('V','E','N',' ')},	/* Venda */
-  {"vec",	HB_TAG('V','E','C',' ')},	/* Venetian */
-  {"vi",	HB_TAG('V','I','T',' ')},	/* Vietnamese */
-  {"vls",	HB_TAG('F','L','E',' ')},	/* Vlaams */
-  {"vmw",	HB_TAG('M','A','K',' ')},	/* Makhuwa */
-  {"vo",	HB_TAG('V','O','L',' ')},	/* Volapük */
-  {"vro",	HB_TAG('V','R','O',' ')},	/* Võro */
-  {"wa",	HB_TAG('W','L','N',' ')},	/* Walloon */
-  {"war",	HB_TAG('W','A','R',' ')},	/* Waray (Philippines) */
-  {"wbm",	HB_TAG('W','A',' ',' ')},	/* Wa */
-  {"wbr",	HB_TAG('W','A','G',' ')},	/* Wagdi */
-  {"wle",	HB_TAG('S','I','G',' ')},	/* Wolane */
-  {"wo",	HB_TAG('W','L','F',' ')},	/* Wolof */
-  {"wry",	HB_TAG('M','A','W',' ')},	/* Merwari */
-  {"wtm",	HB_TAG('W','T','M',' ')},	/* Mewati */
-  {"xal",	HB_TAG('K','L','M',' ')},	/* Kalmyk */
-  {"xan",	HB_TAG('S','E','K',' ')},	/* Sekota */
-  {"xh",	HB_TAG('X','H','S',' ')},	/* Xhosa */
-  {"xjb",	HB_TAG('X','J','B',' ')},	/* Minjangbal */
-  {"xog",	HB_TAG('X','O','G',' ')},	/* Soga */
-  {"xom",	HB_TAG('K','M','O',' ')},	/* Komo (Sudan) */
-  {"xpe",	HB_TAG('X','P','E',' ')},	/* Kpelle (Liberia) */
-  {"xsl",	HB_TAG('S','S','L',' ')},	/* South Slavey */
-  {"xst",	HB_TAG('S','I','G',' ')},	/* Silt'e (retired code) */
-  {"xwo",	HB_TAG('T','O','D',' ')},	/* Written Oirat (Todo) */
-  {"yao",	HB_TAG('Y','A','O',' ')},	/* Yao */
-  {"yap",	HB_TAG('Y','A','P',' ')},	/* Yapese */
-  {"yi",	HB_TAG('J','I','I',' ')},	/* Yiddish [macrolanguage] */
-  {"yo",	HB_TAG('Y','B','A',' ')},	/* Yoruba */
-  {"yos",	HB_TAG('Q','I','N',' ')},	/* Yos, deprecated by IANA in favor of Zou [zom] */
-  {"yso",	HB_TAG('N','I','S',' ')},	/* Nisi (China) */
-  {"za",	HB_TAG('Z','H','A',' ')},	/* Chuang/Zhuang [macrolanguage] */
-  {"zea",	HB_TAG('Z','E','A',' ')},	/* Zeeuws */
-  {"zgh",	HB_TAG('Z','G','H',' ')},	/* Standard Morrocan Tamazigh */
-  {"zne",	HB_TAG('Z','N','D',' ')},	/* Zande */
-  {"zom",	HB_TAG('Q','I','N',' ')},	/* Zou */
-  {"zu",	HB_TAG('Z','U','L',' ')}, 	/* Zulu */
-  {"zum",	HB_TAG('L','R','C',' ')},	/* Kumzari */
-  {"zza",	HB_TAG('Z','Z','A',' ')},	/* Zazaki */
-
-  /* The corresponding languages IDs for the following IDs are unclear,
-   * overlap, or are architecturally weird. Needs more research. */
-
-/*{"chp",	HB_TAG('S','A','Y',' ')},*/	/* Sayisi */
-/*{"cwd",	HB_TAG('T','C','R',' ')},*/	/* TH-Cree */
-/*{"emk",	HB_TAG('E','M','K',' ')},*/	/* Eastern Maninkakan */
-/*{"krc",	HB_TAG('B','A','L',' ')},*/	/* Balkar */
-/*{"??",	HB_TAG('B','C','R',' ')},*/	/* Bible Cree */
-/*{"zh?",	HB_TAG('C','H','N',' ')},*/	/* Chinese (seen in Microsoft fonts) */
-/*{"ar-Syrc?",	HB_TAG('G','A','R',' ')},*/	/* Garshuni */
-/*{"hy?",	HB_TAG('H','Y','E','0')},*/	/* Armenian East (ISO 639-3 hye according to Microsoft, but that’s equivalent to ISO 639-1 hy) */
-/*{"ga-Latg?/"	HB_TAG('I','R','T',' ')},*/	/* Irish Traditional */
-/*{"krc",	HB_TAG('K','A','R',' ')},*/	/* Karachay */
-/*{"ka-Geok?",	HB_TAG('K','G','E',' ')},*/	/* Khutsuri Georgian */
-/*{"kca",	HB_TAG('K','H','K',' ')},*/	/* Khanty-Kazim */
-/*{"kca",	HB_TAG('K','H','S',' ')},*/	/* Khanty-Shurishkar */
-/*{"kca",	HB_TAG('K','H','V',' ')},*/	/* Khanty-Vakhi */
-/*{"kqs, kss",	HB_TAG('K','I','S',' ')},*/	/* Kisii */
-/*{"lua",	HB_TAG('L','U','A',' ')},*/	/* Luba-Lulua */
-/*{"mlq",	HB_TAG('M','L','N',' ')},*/	/* Malinke */
-/*{"nso",	HB_TAG('N','S','O',' ')},*/	/* Sotho, Northern */
-/*{"??",	HB_TAG('M','A','L',' ')},*/	/* Malayalam Traditional */
-/*{"csw",	HB_TAG('N','C','R',' ')},*/	/* N-Cree */
-/*{"csw",	HB_TAG('N','H','C',' ')},*/	/* Norway House Cree */
-/*{"el-polyton",	HB_TAG('P','G','R',' ')},*/	/* Polytonic Greek */
-/*{"bgr, cnh, cnw, czt, sez, tcp, csy, ctd, flm, pck, tcz, zom, cmr, dao, hlt, cka, cnk, mrh, mwg, cbl, cnb, csh",	HB_TAG('Q','I','N',' ')},*/	/* Chin */
-/*{"??",	HB_TAG('Y','I','C',' ')},*/	/* Yi Classic */
-/*{"zh-Latn-pinyin",	HB_TAG('Z','H','P',' ')},*/	/* Chinese Phonetic */
-};
-
-typedef struct {
-  char language[11];
-  hb_tag_t tag;
-} LangTagLong;
-static const LangTagLong ot_languages_zh[] = {
-  /* Store longest-first, if one is a prefix of another. */
-  {"zh-cn",	HB_TAG('Z','H','S',' ')},	/* Chinese (China) */
-  {"zh-hk",	HB_TAG('Z','H','H',' ')},	/* Chinese (Hong Kong) */
-  {"zh-mo",	HB_TAG('Z','H','H',' ')},	/* Chinese (Macao) */
-  {"zh-sg",	HB_TAG('Z','H','S',' ')},	/* Chinese (Singapore) */
-  {"zh-tw",	HB_TAG('Z','H','T',' ')},	/* Chinese (Taiwan) */
-  {"zh-hans",	HB_TAG('Z','H','S',' ')},	/* Chinese (Simplified) */
-  {"zh-hant-hk",HB_TAG('Z','H','H',' ')},	/* Chinese (Hong Kong) */
-  {"zh-hant-mo",HB_TAG('Z','H','H',' ')},	/* Chinese (Macao) */
-  {"zh-hant",	HB_TAG('Z','H','T',' ')},	/* Chinese (Traditional) */
-};
-
 static int
 lang_compare_first_component (const void *pa,
 			      const void *pb)
@@ -895,6 +189,21 @@
   return strncmp (a, b, MAX (da, db));
 }
 
+static bool
+subtag_matches (const char *lang_str,
+		const char *limit,
+		const char *subtag)
+{
+  do {
+    const char *s = strstr (lang_str, subtag);
+    if (!s || s >= limit)
+      return false;
+    if (!ISALNUM (s[strlen (subtag)]))
+      return true;
+    lang_str = s + strlen (subtag);
+  } while (true);
+}
+
 static hb_bool_t
 lang_matches (const char *lang_str, const char *spec)
 {
@@ -904,106 +213,187 @@
 	 (lang_str[len] == '\0' || lang_str[len] == '-');
 }
 
+typedef struct {
+  char language[4];
+  hb_tag_t tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
+} LangTag;
+
+#include "hb-ot-tag-table.hh"
+
+/* The corresponding languages IDs for the following IDs are unclear,
+ * overlap, or are architecturally weird. Needs more research. */
+
+/*{"??",	{HB_TAG('B','C','R',' ')}},*/	/* Bible Cree */
+/*{"zh?",	{HB_TAG('C','H','N',' ')}},*/	/* Chinese (seen in Microsoft fonts) */
+/*{"ar-Syrc?",	{HB_TAG('G','A','R',' ')}},*/	/* Garshuni */
+/*{"??",	{HB_TAG('N','G','R',' ')}},*/	/* Nagari */
+/*{"??",	{HB_TAG('Y','I','C',' ')}},*/	/* Yi Classic */
+/*{"zh?",	{HB_TAG('Z','H','P',' ')}},*/	/* Chinese Phonetic */
+
 hb_tag_t
 hb_ot_tag_from_language (hb_language_t language)
 {
-  const char *lang_str, *s;
+  unsigned int count = 1;
+  hb_tag_t tags[1];
+  hb_ot_tags_from_script_and_language (HB_SCRIPT_UNKNOWN, language, nullptr, nullptr, &count, tags);
+  return count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_LANGUAGE;
+}
 
-  if (language == HB_LANGUAGE_INVALID)
-    return HB_OT_TAG_DEFAULT_LANGUAGE;
+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)
+{
+  const char *s;
 
-  lang_str = hb_language_to_string (language);
+  /* Check for matches of multiple subtags. */
+  if (hb_ot_tags_from_complex_language (lang_str, limit, count, tags))
+    return;
 
-  s = strstr (lang_str, "x-hbot");
-  if (s) {
-    char tag[4];
-    int i;
-    s += 6;
-    for (i = 0; i < 4 && ISALNUM (s[i]); i++)
-      tag[i] = TOUPPER (s[i]);
-    if (i) {
-      for (; i < 4; i++)
-	tag[i] = ' ';
-      return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
-    }
-  }
-
-  /*
-   * "fonipa" is a variant tag in BCP-47, meaning the International Phonetic Alphabet.
-   * It can be applied to any language.
-   */
-  if (strstr (lang_str, "-fonipa")) {
-    return HB_TAG('I','P','P','H');  /* Phonetic transcription—IPA conventions */
-  }
-
-  /*
-   * "fonnapa" is a variant tag in BCP-47, meaning the North American Phonetic Alphabet
-   * also known as Americanist Phonetic Notation.  It can be applied to any language.
-   */
-  if (strstr (lang_str, "-fonnapa")) {
-    return HB_TAG('A','P','P','H');  /* Phonetic transcription—Americanist conventions */
-  }
-
-  /*
-   * "Syre" is a BCP-47 script tag, meaning the Estrangela variant of the Syriac script.
-   * It can be applied to any language.
-   */
-  if (strstr (lang_str, "-syre")) {
-    return HB_TAG('S','Y','R','E');  /* Estrangela Syriac */
-  }
-
-  /*
-   * "Syrj" is a BCP-47 script tag, meaning the Western variant of the Syriac script.
-   * It can be applied to any language.
-   */
-  if (strstr (lang_str, "-syrj")) {
-    return HB_TAG('S','Y','R','J');  /* Western Syriac */
-  }
-
-  /*
-   * "Syrn" is a BCP-47 script tag, meaning the Eastern variant of the Syriac script.
-   * It can be applied to any language.
-   */
-  if (strstr (lang_str, "-syrn")) {
-    return HB_TAG('S','Y','R','N');  /* Eastern Syriac */
-  }
-
-  /* Find a language matching in the first component */
+  /* Find a language matching in the first component. */
+  s = strchr (lang_str, '-');
   {
     const LangTag *lang_tag;
+    if (s && limit - lang_str >= 6)
+    {
+      const char *extlang_end = strchr (s + 1, '-');
+      /* If there is an extended language tag, use it. */
+      if (3 == (extlang_end ? extlang_end - s - 1 : strlen (s + 1)) &&
+	  ISALPHA (s[1]))
+	lang_str = s + 1;
+    }
     lang_tag = (LangTag *) bsearch (lang_str, ot_languages,
 				    ARRAY_LENGTH (ot_languages), sizeof (LangTag),
 				    lang_compare_first_component);
     if (lang_tag)
-      return lang_tag->tag;
-  }
-
-  /* Otherwise, check the Chinese ones */
-  if (0 == lang_compare_first_component (lang_str, "zh"))
-  {
-    unsigned int i;
-
-    for (i = 0; i < ARRAY_LENGTH (ot_languages_zh); i++)
     {
-      const LangTagLong *lang_tag;
-      lang_tag = &ot_languages_zh[i];
-      if (lang_matches (lang_str, lang_tag->language))
-	return lang_tag->tag;
+      unsigned int i;
+      for (i = 0; i < *count && lang_tag->tags[i] != HB_TAG_NONE; i++)
+	tags[i] = lang_tag->tags[i];
+      *count = i;
+      return;
     }
-
-    /* Otherwise just return 'ZHS ' */
-    return HB_TAG('Z','H','S',' ');
   }
 
-  s = strchr (lang_str, '-');
   if (!s)
     s = lang_str + strlen (lang_str);
   if (s - lang_str == 3) {
     /* Assume it's ISO-639-3 and upper-case and use it. */
-    return hb_tag_from_string (lang_str, s - lang_str) & ~0x20202000u;
+    tags[0] = hb_tag_from_string (lang_str, s - lang_str) & ~0x20202000u;
+    *count = 1;
+    return;
   }
 
-  return HB_OT_TAG_DEFAULT_LANGUAGE;
+  *count = 0;
+}
+
+static bool
+parse_private_use_subtag (const char     *private_use_subtag,
+			  unsigned int   *count,
+			  hb_tag_t       *tags,
+			  const char     *prefix,
+			  unsigned char (*normalize) (unsigned char))
+{
+  if (private_use_subtag && count && tags && *count)
+  {
+    const char *s = strstr (private_use_subtag, prefix);
+    if (s)
+    {
+      char tag[4];
+      int i;
+      s += strlen (prefix);
+      for (i = 0; i < 4 && ISALNUM (s[i]); i++)
+	tag[i] = normalize (s[i]);
+      if (i)
+      {
+	for (; i < 4; i++)
+	  tag[i] = ' ';
+	tags[0] = HB_TAG (tag[0], tag[1], tag[2], tag[3]);
+	if ((tags[0] & 0xDFDFDFDF) == HB_OT_TAG_DEFAULT_SCRIPT)
+	  tags[0] ^= ~0xDFDFDFDF;
+	*count = 1;
+	return false;
+      }
+    }
+  }
+  return true;
+}
+
+/**
+ * hb_ot_tags_from_script_and_language:
+ * @script: an #hb_script_t to convert.
+ * @language: an #hb_language_t to convert.
+ * @script_count: (allow-none): maximum number of script tags to retrieve (IN)
+ * and actual number of script tags retrieved (OUT)
+ * @script_tags: (out) (allow-none): array of size at least @script_count to store the
+ * script tag results
+ * @language_count: (allow-none): maximum number of language tags to retrieve
+ * (IN) and actual number of language tags retrieved (OUT)
+ * @language_tags: (out) (allow-none): array of size at least @language_count to store
+ * the language tag results
+ *
+ * Converts an #hb_script_t and an #hb_language_t to script and language tags.
+ *
+ * Since: 2.0.0
+ **/
+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 */)
+{
+  bool needs_script = true;
+
+  if (language == HB_LANGUAGE_INVALID)
+  {
+    if (language_count && language_tags && *language_count)
+      *language_count = 0;
+  }
+  else
+  {
+    const char *lang_str, *s, *limit, *private_use_subtag;
+    bool needs_language;
+
+    lang_str = hb_language_to_string (language);
+    limit = nullptr;
+    private_use_subtag = nullptr;
+    if (lang_str[0] == 'x' && lang_str[1] == '-')
+    {
+      private_use_subtag = lang_str;
+    } else {
+      for (s = lang_str + 1; *s; s++)
+      {
+	if (s[-1] == '-' && s[1] == '-')
+	{
+	  if (s[0] == 'x')
+	  {
+	    private_use_subtag = s;
+	    if (!limit)
+	      limit = s - 1;
+	    break;
+	  } else if (!limit)
+	  {
+	    limit = s - 1;
+	  }
+	}
+      }
+      if (!limit)
+	limit = s;
+    }
+
+    needs_script = parse_private_use_subtag (private_use_subtag, script_count, script_tags, "-hbsc", TOLOWER);
+    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);
+  }
+
+  if (needs_script && script_count && script_tags && *script_count)
+    hb_ot_all_tags_from_script (script, script_count, script_tags);
 }
 
 /**
@@ -1023,36 +413,16 @@
   if (tag == HB_OT_TAG_DEFAULT_LANGUAGE)
     return nullptr;
 
-  /* struct LangTag has only room for 3-letter language tags. */
-  switch (tag) {
-  case HB_TAG('A','P','P','H'):  /* Phonetic transcription—Americanist conventions */
-    return hb_language_from_string ("und-fonnapa", -1);
-  case HB_TAG('I','P','P','H'):  /* Phonetic transcription—IPA conventions */
-    return hb_language_from_string ("und-fonipa", -1);
-  case HB_TAG('S','Y','R',' '):  /* Syriac [macrolanguage] */
-    return hb_language_from_string ("syr", -1);
-  case HB_TAG('S','Y','R','E'):  /* Estrangela Syriac */
-    return hb_language_from_string ("und-Syre", -1);
-  case HB_TAG('S','Y','R','J'):  /* Western Syriac */
-    return hb_language_from_string ("und-Syrj", -1);
-  case HB_TAG('S','Y','R','N'):  /* Eastern Syriac */
-    return hb_language_from_string ("und-Syrn", -1);
+  {
+    hb_language_t disambiguated_tag = hb_ot_ambiguous_tag_to_language (tag);
+    if (disambiguated_tag != HB_LANGUAGE_INVALID)
+      return disambiguated_tag;
   }
 
   for (i = 0; i < ARRAY_LENGTH (ot_languages); i++)
-    if (ot_languages[i].tag == tag)
+    if (ot_languages[i].tags[0] == tag)
       return hb_language_from_string (ot_languages[i].language, -1);
 
-  /* If tag starts with ZH, it's Chinese */
-  if ((tag & 0xFFFF0000u)  == 0x5A480000u) {
-    switch (tag) {
-      case HB_TAG('Z','H','H',' '): return hb_language_from_string ("zh-hk", -1); /* Hong Kong */
-      case HB_TAG('Z','H','S',' '): return hb_language_from_string ("zh-Hans", -1); /* Simplified */
-      case HB_TAG('Z','H','T',' '): return hb_language_from_string ("zh-Hant", -1); /* Traditional */
-      default: break; /* Fall through */
-    }
-  }
-
   /* Else return a custom language in the form of "x-hbotABCD" */
   {
     unsigned char buf[11] = "x-hbot";
@@ -1067,6 +437,71 @@
   }
 }
 
+/**
+ * hb_ot_tags_to_script_and_language:
+ * @script_tag: a script tag
+ * @language_tag: a language tag
+ * @script: (allow-none): the #hb_script_t corresponding to @script_tag (OUT).
+ * @language: (allow-none): the #hb_language_t corresponding to @script_tag and
+ * @language_tag (OUT).
+ *
+ * Converts a script tag and a language tag to an #hb_script_t and an
+ * #hb_language_t.
+ *
+ * Since: 2.0.0
+ **/
+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_script_t script_out = hb_ot_tag_to_script (script_tag);
+  if (script)
+    *script = script_out;
+  if (language)
+  {
+    unsigned int script_count = 1;
+    hb_tag_t primary_script_tag[1];
+    hb_ot_tags_from_script_and_language (script_out,
+					 HB_LANGUAGE_INVALID,
+					 &script_count,
+					 primary_script_tag,
+					 nullptr, nullptr);
+    *language = hb_ot_tag_to_language (language_tag);
+    if (script_count == 0 || primary_script_tag[0] != script_tag)
+    {
+      unsigned char *buf;
+      const char *lang_str = hb_language_to_string (*language);
+      size_t len = strlen (lang_str);
+      buf = (unsigned char *) malloc (len + 11);
+      if (unlikely (!buf))
+      {
+	*language = nullptr;
+      }
+      else
+      {
+	memcpy (buf, lang_str, len);
+	if (lang_str[0] != 'x' || lang_str[1] != '-') {
+	  buf[len++] = '-';
+	  buf[len++] = 'x';
+	}
+	buf[len++] = '-';
+	buf[len++] = 'h';
+	buf[len++] = 'b';
+	buf[len++] = 's';
+	buf[len++] = 'c';
+	buf[len++] = script_tag >> 24;
+	buf[len++] = (script_tag >> 16) & 0xFF;
+	buf[len++] = (script_tag >> 8) & 0xFF;
+	buf[len++] = script_tag & 0xFF;
+	*language = hb_language_from_string ((char *) buf, len);
+	free (buf);
+      }
+    }
+  }
+}
+
 #ifdef MAIN
 static inline void
 test_langs_sorted (void)
diff --git a/src/hb-ot-tag.h b/src/hb-ot-tag.h
index 54fb747..33a4dbd 100644
--- a/src/hb-ot-tag.h
+++ b/src/hb-ot-tag.h
@@ -39,20 +39,39 @@
 #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 (hb_script_t  script,
-			hb_tag_t    *script_tag_1,
-			hb_tag_t    *script_tag_2);
+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_tag_t
-hb_ot_tag_from_language (hb_language_t language);
-
 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
 
diff --git a/src/hb-ot-var-avar-table.hh b/src/hb-ot-var-avar-table.hh
index ad063d3..d100ca2 100644
--- a/src/hb-ot-var-avar-table.hh
+++ b/src/hb-ot-var-avar-table.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OT_VAR_AVAR_TABLE_HH
 #define HB_OT_VAR_AVAR_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
 /*
  * avar -- Axis Variations
@@ -93,6 +93,7 @@
 	    (value - arrayZ[i-1].fromCoord) + denom/2) / denom;
   }
 
+  public:
   DEFINE_SIZE_ARRAY (2, arrayZ);
 };
 
@@ -108,7 +109,7 @@
 		    c->check_struct (this))))
       return_trace (false);
 
-    const SegmentMaps *map = axisSegmentMapsZ;
+    const SegmentMaps *map = axisSegmentMapsZ.arrayZ;
     unsigned int count = axisCount;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -124,7 +125,7 @@
   {
     unsigned int count = MIN<unsigned int> (coords_length, axisCount);
 
-    const SegmentMaps *map = axisSegmentMapsZ;
+    const SegmentMaps *map = axisSegmentMapsZ.arrayZ;
     for (unsigned int i = 0; i < count; i++)
     {
       coords[i] = map->map (coords[i]);
@@ -139,7 +140,8 @@
   HBUINT16	axisCount;	/* The number of variation axes in the font. This
 				 * must be the same number as axisCount in the
 				 * 'fvar' table. */
-  SegmentMaps	axisSegmentMapsZ[VAR];
+  UnsizedArrayOf<SegmentMaps>
+		axisSegmentMapsZ;
 
   public:
   DEFINE_SIZE_MIN (8);
diff --git a/src/hb-ot-var-fvar-table.hh b/src/hb-ot-var-fvar-table.hh
index 101476e..96c39c1 100644
--- a/src/hb-ot-var-fvar-table.hh
+++ b/src/hb-ot-var-fvar-table.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OT_VAR_FVAR_TABLE_HH
 #define HB_OT_VAR_FVAR_TABLE_HH
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
 /*
  * fvar -- Font Variations
@@ -46,20 +46,21 @@
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  c->check_array (coordinates, coordinates[0].static_size, axis_count));
+		  c->check_array (coordinatesZ.arrayZ, axis_count));
   }
 
   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. */
-  Fixed		coordinates[VAR];/* The coordinates array for this instance. */
+  UnsizedArrayOf<Fixed>
+		coordinatesZ;	/* The coordinates array for this instance. */
   //NameID	postScriptNameIDX;/*Optional. The name ID for entries in the 'name'
   //				  * table that provide PostScript names for this
   //				  * instance. */
 
   public:
-  DEFINE_SIZE_ARRAY (4, coordinates);
+  DEFINE_SIZE_ARRAY (4, coordinatesZ);
 };
 
 struct AxisRecord
@@ -176,7 +177,7 @@
       v = (v - axis.default_value) / (axis.default_value - axis.min_value);
     else
       v = (v - axis.default_value) / (axis.max_value - axis.default_value);
-    return (int) (v * 16384. + (v >= 0. ? .5 : -.5));
+    return (int) (v * 16384.f + (v >= 0.f ? .5f : -.5f));
   }
 
   protected:
diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh
index 2b384db..66e086e 100644
--- a/src/hb-ot-var-hvar-table.hh
+++ b/src/hb-ot-var-hvar-table.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OT_VAR_HVAR_TABLE_HH
 #define HB_OT_VAR_HVAR_TABLE_HH
 
-#include "hb-ot-layout-common-private.hh"
+#include "hb-ot-layout-common.hh"
 
 
 namespace OT {
@@ -39,7 +39,7 @@
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
-		  c->check_array (mapData, get_width (), mapCount));
+		  c->check_array (mapDataZ.arrayZ, mapCount, get_width ()));
   }
 
   unsigned int map (unsigned int v) const /* Returns 16.16 outer.inner. */
@@ -55,7 +55,7 @@
     unsigned int u = 0;
     { /* Fetch it. */
       unsigned int w = get_width ();
-      const HBUINT8 *p = mapData + w * v;
+      const HBUINT8 *p = mapDataZ.arrayZ + w * v;
       for (; w; w--)
 	u = (u << 8) + *p++;
     }
@@ -81,10 +81,11 @@
   HBUINT16	format;		/* A packed field that describes the compressed
 				 * representation of delta-set indices. */
   HBUINT16	mapCount;	/* The number of mapping entries. */
-  HBUINT8		mapData[VAR];	/* The delta-set index mapping data. */
+  UnsizedArrayOf<HBUINT8>
+ 		mapDataZ;	/* The delta-set index mapping data. */
 
   public:
-  DEFINE_SIZE_ARRAY (4, mapData);
+  DEFINE_SIZE_ARRAY (4, mapDataZ);
 };
 
 
@@ -114,7 +115,7 @@
   }
 
   inline float get_advance_var (hb_codepoint_t glyph,
-				int *coords, unsigned int coord_count) const
+				const int *coords, unsigned int coord_count) const
   {
     unsigned int varidx = (this+advMap).map (glyph);
     return (this+varStore).get_delta (varidx, coords, coord_count);
diff --git a/src/hb-ot-var-mvar-table.hh b/src/hb-ot-var-mvar-table.hh
index dfde782..5d6b559 100644
--- a/src/hb-ot-var-mvar-table.hh
+++ b/src/hb-ot-var-mvar-table.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OT_VAR_MVAR_TABLE_HH
 #define HB_OT_VAR_MVAR_TABLE_HH
 
-#include "hb-ot-layout-common-private.hh"
+#include "hb-ot-layout-common.hh"
 
 
 namespace OT {
@@ -68,14 +68,14 @@
 		  c->check_struct (this) &&
 		  valueRecordSize >= VariationValueRecord::static_size &&
 		  varStore.sanitize (c, this) &&
-		  c->check_array (values, valueRecordSize, valueRecordCount));
+		  c->check_array (valuesZ.arrayZ, valueRecordCount, valueRecordSize));
   }
 
   inline float get_var (hb_tag_t tag,
-			int *coords, unsigned int coord_count) const
+			const int *coords, unsigned int coord_count) const
   {
     const VariationValueRecord *record;
-    record = (VariationValueRecord *) bsearch (&tag, values,
+    record = (VariationValueRecord *) bsearch (&tag, valuesZ.arrayZ,
 					       valueRecordCount, valueRecordSize,
 					       tag_compare);
     if (!record)
@@ -101,11 +101,12 @@
   HBUINT16	valueRecordCount;/* The number of value records — may be zero. */
   OffsetTo<VariationStore>
 		varStore;	/* Offset to item variation store table. */
-  HBUINT8		values[VAR];	/* Array of value records. The records must be
+  UnsizedArrayOf<HBUINT8>
+		valuesZ;	/* Array of value records. The records must be
 				 * in binary order of their valueTag field. */
 
   public:
-  DEFINE_SIZE_ARRAY (12, values);
+  DEFINE_SIZE_ARRAY (12, valuesZ);
 };
 
 } /* namespace OT */
diff --git a/src/hb-ot-var.cc b/src/hb-ot-var.cc
index 6081ddf..472ee84 100644
--- a/src/hb-ot-var.cc
+++ b/src/hb-ot-var.cc
@@ -24,9 +24,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 
-#include "hb-ot-layout-private.hh"
+#include "hb-ot-face.hh"
 #include "hb-ot-var-avar-table.hh"
 #include "hb-ot-var-fvar-table.hh"
 #include "hb-ot-var-mvar-table.hh"
@@ -40,15 +40,15 @@
 _get_fvar (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(OT::fvar);
-  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-  return *(layout->table.fvar.get ());
+  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_layout_t * layout = hb_ot_layout_from_face (face);
-  return *(layout->table.avar.get ());
+  hb_ot_face_data_t *layout = hb_ot_face_data (face);
+  return *(layout->avar.get ());
 }
 
 /**
diff --git a/src/hb-ot.h b/src/hb-ot.h
index 2120a3e..4b6e3cf 100644
--- a/src/hb-ot.h
+++ b/src/hb-ot.h
@@ -33,6 +33,7 @@
 #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-private.hh b/src/hb-set-digest.hh
similarity index 94%
rename from src/hb-set-digest-private.hh
rename to src/hb-set-digest.hh
index e099a82..0f9329f 100644
--- a/src/hb-set-digest-private.hh
+++ b/src/hb-set-digest.hh
@@ -24,10 +24,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_SET_DIGEST_PRIVATE_HH
-#define HB_SET_DIGEST_PRIVATE_HH
+#ifndef HB_SET_DIGEST_HH
+#define HB_SET_DIGEST_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 /*
  * The set digests here implement various "filters" that support
@@ -50,8 +50,8 @@
 {
   ASSERT_POD ();
 
-  static const unsigned int mask_bytes = sizeof (mask_t);
-  static const unsigned int mask_bits = sizeof (mask_t) * 8;
+  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)
@@ -176,4 +176,4 @@
 > hb_set_digest_t;
 
 
-#endif /* HB_SET_DIGEST_PRIVATE_HH */
+#endif /* HB_SET_DIGEST_HH */
diff --git a/src/hb-set.cc b/src/hb-set.cc
index 25027e6..09dc4b4 100644
--- a/src/hb-set.cc
+++ b/src/hb-set.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-set-private.hh"
+#include "hb-set.hh"
 
 
 /* Public API */
diff --git a/src/hb-set-private.hh b/src/hb-set.hh
similarity index 96%
rename from src/hb-set-private.hh
rename to src/hb-set.hh
index 032ddb1..7ca3297 100644
--- a/src/hb-set-private.hh
+++ b/src/hb-set.hh
@@ -24,10 +24,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_SET_PRIVATE_HH
-#define HB_SET_PRIVATE_HH
+#ifndef HB_SET_HH
+#define HB_SET_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 /*
@@ -157,7 +157,7 @@
     }
 
     typedef unsigned long long elt_t;
-    static const unsigned int PAGE_BITS = 512;
+    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); }
@@ -165,11 +165,11 @@
 
     typedef hb_vector_size_t<elt_t, PAGE_BITS / 8> vector_t;
 
-    static const unsigned int ELT_BITS = sizeof (elt_t) * 8;
-    static const unsigned int ELT_MASK = ELT_BITS - 1;
-    static const unsigned int BITS = sizeof (vector_t) * 8;
-    static const unsigned int MASK = BITS - 1;
-    static_assert (PAGE_BITS == BITS, "");
+    enum { ELT_BITS = sizeof (elt_t) * 8 };
+    enum { ELT_MASK = ELT_BITS - 1 };
+    enum { BITS = sizeof (vector_t) * 8 };
+    enum { MASK = BITS - 1 };
+    static_assert ((unsigned) PAGE_BITS == (unsigned) BITS, "");
 
     elt_t &elt (hb_codepoint_t g) { return v[(g & MASK) / ELT_BITS]; }
     elt_t const &elt (hb_codepoint_t g) const { return v[(g & MASK) / ELT_BITS]; }
@@ -368,8 +368,8 @@
     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 (pages.arrayZ(), other->pages.arrayZ(), count * sizeof (pages.arrayZ()[0]));
+    memcpy (page_map.arrayZ(), other->page_map.arrayZ(), count * sizeof (page_map.arrayZ()[0]));
   }
 
   inline bool is_equal (const hb_set_t *other) const
@@ -697,4 +697,4 @@
 };
 
 
-#endif /* HB_SET_PRIVATE_HH */
+#endif /* HB_SET_HH */
diff --git a/src/hb-shape-plan.cc b/src/hb-shape-plan.cc
index 0d61d9e..b2289f8 100644
--- a/src/hb-shape-plan.cc
+++ b/src/hb-shape-plan.cc
@@ -24,11 +24,11 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
-#include "hb-shape-plan-private.hh"
-#include "hb-shaper-private.hh"
-#include "hb-font-private.hh"
-#include "hb-buffer-private.hh"
+#include "hb.hh"
+#include "hb-shape-plan.hh"
+#include "hb-shaper.hh"
+#include "hb-font.hh"
+#include "hb-buffer.hh"
 
 
 static void
@@ -64,7 +64,7 @@
 
   if (likely (!shaper_list)) {
     for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
-      if (0)
+      if (false)
 	;
 #define HB_SHAPER_IMPLEMENT(shaper) \
       else if (shapers[i].func == _hb_##shaper##_shape) \
@@ -73,7 +73,7 @@
 #undef HB_SHAPER_IMPLEMENT
   } else {
     for (; *shaper_list; shaper_list++)
-      if (0)
+      if (false)
 	;
 #define HB_SHAPER_IMPLEMENT(shaper) \
       else if (0 == strcmp (*shaper_list, #shaper)) \
@@ -346,7 +346,7 @@
 		 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
 	} HB_STMT_END
 
-  if (0)
+  if (false)
     ;
 #define HB_SHAPER_IMPLEMENT(shaper) \
   else if (shape_plan->shaper_func == _hb_##shaper##_shape) \
@@ -501,7 +501,7 @@
     /* 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 (0)
+      if (false)
 	;
 #define HB_SHAPER_IMPLEMENT(shaper) \
       else if (0 == strcmp (*shaper_item, #shaper) && \
diff --git a/src/hb-shape-plan-private.hh b/src/hb-shape-plan.hh
similarity index 92%
rename from src/hb-shape-plan-private.hh
rename to src/hb-shape-plan.hh
index 7d020ff..bf82b91 100644
--- a/src/hb-shape-plan-private.hh
+++ b/src/hb-shape-plan.hh
@@ -24,11 +24,11 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_SHAPE_PLAN_PRIVATE_HH
-#define HB_SHAPE_PLAN_PRIVATE_HH
+#ifndef HB_SHAPE_PLAN_HH
+#define HB_SHAPE_PLAN_HH
 
-#include "hb-private.hh"
-#include "hb-shaper-private.hh"
+#include "hb.hh"
+#include "hb-shaper.hh"
 
 
 struct hb_shape_plan_t
@@ -64,4 +64,4 @@
 #undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 
 
-#endif /* HB_SHAPE_PLAN_PRIVATE_HH */
+#endif /* HB_SHAPE_PLAN_HH */
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 3c2e6c4..e8eeff5 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -26,13 +26,13 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-shaper-private.hh"
-#include "hb-shape-plan-private.hh"
-#include "hb-buffer-private.hh"
-#include "hb-font-private.hh"
-#include "hb-machinery-private.hh"
+#include "hb-shaper.hh"
+#include "hb-shape-plan.hh"
+#include "hb-buffer.hh"
+#include "hb-font.hh"
+#include "hb-machinery.hh"
 
 /**
  * SECTION:hb-shape
@@ -46,8 +46,10 @@
  * contains the output glyphs and their positions.
  **/
 
-
+#ifdef HB_USE_ATEXIT
 static void free_static_shaper_list (void);
+#endif
+
 static const char *nil_shaper_list[] = {nullptr};
 
 static struct hb_shaper_list_lazy_loader_t : hb_lazy_loader_t<const char *,
diff --git a/src/hb-shaper-impl-private.hh b/src/hb-shaper-impl.hh
similarity index 82%
rename from src/hb-shaper-impl-private.hh
rename to src/hb-shaper-impl.hh
index 4a10279..d40cb08 100644
--- a/src/hb-shaper-impl-private.hh
+++ b/src/hb-shaper-impl.hh
@@ -24,15 +24,15 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_SHAPER_IMPL_PRIVATE_HH
-#define HB_SHAPER_IMPL_PRIVATE_HH
+#ifndef HB_SHAPER_IMPL_HH
+#define HB_SHAPER_IMPL_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-shaper-private.hh"
-#include "hb-shape-plan-private.hh"
-#include "hb-font-private.hh"
-#include "hb-buffer-private.hh"
+#include "hb-shaper.hh"
+#include "hb-shape-plan.hh"
+#include "hb-font.hh"
+#include "hb-buffer.hh"
 
 
 #ifdef HB_SHAPER
@@ -40,4 +40,4 @@
 #endif
 
 
-#endif /* HB_SHAPER_IMPL_PRIVATE_HH */
+#endif /* HB_SHAPER_IMPL_HH */
diff --git a/src/hb-shaper-list.hh b/src/hb-shaper-list.hh
index b0835d3..1fdb648 100644
--- a/src/hb-shaper-list.hh
+++ b/src/hb-shaper-list.hh
@@ -39,9 +39,7 @@
 HB_SHAPER_IMPLEMENT (coretext_aat)
 #endif
 
-#ifdef HAVE_OT
 HB_SHAPER_IMPLEMENT (ot) /* <--- This is our main OpenType shaper. */
-#endif
 
 #ifdef HAVE_UNISCRIBE
 HB_SHAPER_IMPLEMENT (uniscribe)
diff --git a/src/hb-shaper.cc b/src/hb-shaper.cc
index e423f25..52418c0 100644
--- a/src/hb-shaper.cc
+++ b/src/hb-shaper.cc
@@ -24,9 +24,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
-#include "hb-shaper-private.hh"
-#include "hb-machinery-private.hh"
+#include "hb.hh"
+#include "hb-shaper.hh"
+#include "hb-machinery.hh"
 
 
 static const hb_shaper_pair_t all_shapers[] = {
@@ -35,8 +35,9 @@
 #undef HB_SHAPER_IMPLEMENT
 };
 
-
+#ifdef HB_USE_ATEXIT
 static void free_static_shapers (void);
+#endif
 
 static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<const hb_shaper_pair_t,
 							  hb_shapers_lazy_loader_t>
diff --git a/src/hb-shaper-private.hh b/src/hb-shaper.hh
similarity index 97%
rename from src/hb-shaper-private.hh
rename to src/hb-shaper.hh
index fb04bbc..361165e 100644
--- a/src/hb-shaper-private.hh
+++ b/src/hb-shaper.hh
@@ -24,10 +24,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_SHAPER_PRIVATE_HH
-#define HB_SHAPER_PRIVATE_HH
+#ifndef HB_SHAPER_HH
+#define HB_SHAPER_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 typedef hb_bool_t hb_shape_func_t (hb_shape_plan_t    *shape_plan,
 				   hb_font_t          *font,
@@ -131,4 +131,4 @@
 #define HB_SHAPERS_COUNT (sizeof (hb_shaper_data_t) / sizeof (void *))
 
 
-#endif /* HB_SHAPER_PRIVATE_HH */
+#endif /* HB_SHAPER_HH */
diff --git a/src/hb-static.cc b/src/hb-static.cc
index ddecbba..e550796 100644
--- a/src/hb-static.cc
+++ b/src/hb-static.cc
@@ -24,12 +24,14 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-open-type-private.hh"
-#include "hb-ot-layout-common-private.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-private.hh"
+#include "hb-face.hh"
 #include "hb-ot-head-table.hh"
 #include "hb-ot-maxp-table.hh"
 
@@ -41,6 +43,8 @@
 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};
+/* Hand-coded because Lookup is a template.  Sad. */
+const unsigned char _hb_Null_AAT_Lookup[2] = {0xFF, 0xFF};
 
 
 void
diff --git a/src/hb-string-array.hh b/src/hb-string-array.hh
index 679841c..c4cf666 100644
--- a/src/hb-string-array.hh
+++ b/src/hb-string-array.hh
@@ -29,7 +29,7 @@
 #define HB_STRING_ARRAY_HH
 #endif
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 /* Based on Bruno Haible's code in Appendix B of Ulrich Drepper's dsohowto.pdf:
  * https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf */
diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc
index 36af3be..499380a 100644
--- a/src/hb-subset-glyf.cc
+++ b/src/hb-subset-glyf.cc
@@ -24,11 +24,10 @@
  * Google Author(s): Garret Rieger, Roderick Sheeter
  */
 
-#include "hb-open-type-private.hh"
+#include "hb-open-type.hh"
 #include "hb-ot-glyf-table.hh"
 #include "hb-set.h"
 #include "hb-subset-glyf.hh"
-#include "hb-subset-plan.hh"
 
 static bool
 _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf,
diff --git a/src/hb-subset-glyf.hh b/src/hb-subset-glyf.hh
index 99b76db..3109ecb 100644
--- a/src/hb-subset-glyf.hh
+++ b/src/hb-subset-glyf.hh
@@ -27,9 +27,9 @@
 #ifndef HB_SUBSET_GLYF_HH
 #define HB_SUBSET_GLYF_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-subset-plan.hh"
+#include "hb-subset.hh"
 
 HB_INTERNAL bool
 hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
diff --git a/src/hb-subset-input.cc b/src/hb-subset-input.cc
index 74470fd..d59b5ba 100644
--- a/src/hb-subset-input.cc
+++ b/src/hb-subset-input.cc
@@ -24,8 +24,8 @@
  * Google Author(s): Garret Rieger, Rod Sheeter, Behdad Esfahbod
  */
 
-#include "hb-subset-private.hh"
-#include "hb-set-private.hh"
+#include "hb-subset.hh"
+#include "hb-set.hh"
 
 /**
  * hb_subset_input_create_or_fail:
@@ -44,7 +44,7 @@
 
   input->unicodes = hb_set_create ();
   input->glyphs = hb_set_create ();
-  input->drop_ot_layout = true;
+  input->drop_layout = true;
 
   return input;
 }
@@ -106,30 +106,28 @@
   return subset_input->glyphs;
 }
 
-/**
- * hb_subset_input_drop_hints:
- * @subset_input: a subset_input.
- *
- * Since: 1.8.0
- **/
-HB_EXTERN hb_bool_t *
-hb_subset_input_drop_hints (hb_subset_input_t *subset_input)
+HB_EXTERN void
+hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input,
+				hb_bool_t drop_hints)
 {
-  return &subset_input->drop_hints;
+  subset_input->drop_hints = drop_hints;
 }
 
-/**
- * hb_subset_input_drop_ot_layout:
- * @subset_input: a subset_input.
- *
- * If enabled ot layout tables will be dropped as part of
- * the subsetting operation. Currently this defaults to
- * true.
- *
- * Since: REPLACEME
- **/
-HB_EXTERN hb_bool_t *
-hb_subset_input_drop_ot_layout (hb_subset_input_t *subset_input)
+HB_EXTERN hb_bool_t
+hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input)
 {
-  return &subset_input->drop_ot_layout;
+  return subset_input->drop_hints;
+}
+
+HB_EXTERN void
+hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input,
+				hb_bool_t drop_layout)
+{
+  subset_input->drop_layout = drop_layout;
+}
+
+HB_EXTERN hb_bool_t
+hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input)
+{
+  return subset_input->drop_layout;
 }
diff --git a/src/hb-subset-private.hh b/src/hb-subset-input.hh
similarity index 74%
rename from src/hb-subset-private.hh
rename to src/hb-subset-input.hh
index 6b2b207..9fc8615 100644
--- a/src/hb-subset-private.hh
+++ b/src/hb-subset-input.hh
@@ -24,27 +24,26 @@
  * Google Author(s): Garret Rieger, Roderick Sheeter
  */
 
-#ifndef HB_SUBSET_PRIVATE_HH
-#define HB_SUBSET_PRIVATE_HH
+#ifndef HB_SUBSET_INPUT_HH
+#define HB_SUBSET_INPUT_HH
 
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb-subset.h"
 
-#include "hb-font-private.hh"
+#include "hb-font.hh"
 
-typedef struct hb_subset_face_data_t hb_subset_face_data_t;
-
-struct hb_subset_input_t {
+struct hb_subset_input_t
+{
   hb_object_header_t header;
   ASSERT_POD ();
 
   hb_set_t *unicodes;
   hb_set_t *glyphs;
 
-  hb_bool_t drop_hints;
-  hb_bool_t drop_ot_layout;
+  bool drop_hints : 1;
+  bool drop_layout : 1;
   /* TODO
    *
    * features
@@ -54,10 +53,5 @@
    */
 };
 
-HB_INTERNAL hb_face_t *
-hb_subset_face_create (void);
 
-HB_INTERNAL hb_bool_t
-hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob);
-
-#endif /* HB_SUBSET_PRIVATE_HH */
+#endif /* HB_SUBSET_INPUT_HH */
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 1256682..0570060 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -24,11 +24,10 @@
  * Google Author(s): Garret Rieger, Roderick Sheeter
  */
 
-#include "hb-map-private.hh"
-#include "hb-subset-private.hh"
-#include "hb-set-private.hh"
-
 #include "hb-subset-plan.hh"
+#include "hb-map.hh"
+#include "hb-set.hh"
+
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-glyf-table.hh"
 
@@ -69,7 +68,7 @@
 }
 
 
-static void
+static hb_set_t *
 _populate_gids_to_retain (hb_face_t *face,
                           const hb_set_t *unicodes,
                           bool close_over_gsub,
@@ -118,9 +117,10 @@
   while (all_gids_to_retain->next (&gid))
     glyphs->push (gid);
 
-  hb_set_destroy (all_gids_to_retain);
   glyf.fini ();
   cmap.fini ();
+
+  return all_gids_to_retain;
 }
 
 static void
@@ -135,7 +135,7 @@
 /**
  * hb_subset_plan_create:
  * Computes a plan for subsetting the supplied face according
- * to a provide profile and input. The plan describes
+ * to a provided input. The plan describes
  * which tables and glyphs should be retained.
  *
  * Return value: New subset plan.
@@ -144,26 +144,24 @@
  **/
 hb_subset_plan_t *
 hb_subset_plan_create (hb_face_t           *face,
-                       hb_subset_profile_t *profile,
                        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_ot_layout = input->drop_ot_layout;
+  plan->drop_layout = input->drop_layout;
   plan->unicodes = hb_set_create();
   plan->glyphs.init();
   plan->source = hb_face_reference (face);
-  plan->dest = hb_subset_face_create ();
+  plan->dest = hb_face_builder_create ();
   plan->codepoint_to_glyph = hb_map_create();
   plan->glyph_map = hb_map_create();
-
-  _populate_gids_to_retain (face,
-                            input->unicodes,
-                            !plan->drop_ot_layout,
-                            plan->unicodes,
-                            plan->codepoint_to_glyph,
-                            &plan->glyphs);
+  plan->glyphset = _populate_gids_to_retain (face,
+					     input->unicodes,
+					     !plan->drop_layout,
+					     plan->unicodes,
+					     plan->codepoint_to_glyph,
+					     &plan->glyphs);
   _create_old_gid_to_new_gid_map (plan->glyphs,
                                   plan->glyph_map);
 
@@ -186,6 +184,7 @@
   hb_face_destroy (plan->dest);
   hb_map_destroy (plan->codepoint_to_glyph);
   hb_map_destroy (plan->glyph_map);
+  hb_set_destroy (plan->glyphset);
 
   free (plan);
 }
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh
index 7501294..c2c484a 100644
--- a/src/hb-subset-plan.hh
+++ b/src/hb-subset-plan.hh
@@ -27,27 +27,26 @@
 #ifndef HB_SUBSET_PLAN_HH
 #define HB_SUBSET_PLAN_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb-subset.h"
-#include "hb-subset-private.hh"
+#include "hb-subset-input.hh"
 
-#include "hb-map-private.hh"
+#include "hb-map.hh"
 
 struct hb_subset_plan_t
 {
   hb_object_header_t header;
   ASSERT_POD ();
 
-  hb_bool_t drop_hints;
-  hb_bool_t drop_ot_layout;
+  bool drop_hints : 1;
+  bool drop_layout : 1;
 
   // For each cp that we'd like to retain maps to the corresponding gid.
   hb_set_t *unicodes;
 
-  // This list contains the complete set of glyphs to retain and may contain
-  // more glyphs then the lists above.
   hb_vector_t<hb_codepoint_t> glyphs;
+  hb_set_t *glyphset;
 
   hb_map_t *codepoint_to_glyph;
   hb_map_t *glyph_map;
@@ -56,7 +55,7 @@
   hb_face_t *source;
   hb_face_t *dest;
 
-  inline hb_bool_t
+  inline bool
   new_gid_for_codepoint (hb_codepoint_t codepoint,
                          hb_codepoint_t *new_gid) const
   {
@@ -67,7 +66,7 @@
     return new_gid_for_old_gid (old_gid, new_gid);
   }
 
-  inline hb_bool_t
+  inline bool
   new_gid_for_old_gid (hb_codepoint_t old_gid,
                       hb_codepoint_t *new_gid) const
   {
@@ -79,7 +78,7 @@
     return true;
   }
 
-  inline hb_bool_t
+  inline bool
   add_table (hb_tag_t tag,
              hb_blob_t *contents)
   {
@@ -89,7 +88,7 @@
               hb_blob_get_length (contents),
               hb_blob_get_length (source_blob));
     hb_blob_destroy (source_blob);
-    return hb_subset_face_add_table(dest, tag, contents);
+    return hb_face_builder_add_table (dest, tag, contents);
   }
 };
 
@@ -97,7 +96,6 @@
 
 HB_INTERNAL hb_subset_plan_t *
 hb_subset_plan_create (hb_face_t           *face,
-                       hb_subset_profile_t *profile,
                        hb_subset_input_t   *input);
 
 HB_INTERNAL void
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 411c6b8..9f14b89 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -24,14 +24,13 @@
  * Google Author(s): Garret Rieger, Rod Sheeter, Behdad Esfahbod
  */
 
-#include "hb-private.hh"
-#include "hb-open-type-private.hh"
+#include "hb.hh"
+#include "hb-open-type.hh"
 
+#include "hb-subset.hh"
 #include "hb-subset-glyf.hh"
-#include "hb-subset-private.hh"
-#include "hb-subset-plan.hh"
 
-#include "hb-open-file-private.hh"
+#include "hb-open-file.hh"
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-glyf-table.hh"
 #include "hb-ot-hdmx-table.hh"
@@ -41,37 +40,76 @@
 #include "hb-ot-maxp-table.hh"
 #include "hb-ot-os2-table.hh"
 #include "hb-ot-post-table.hh"
+#include "hb-ot-layout-gsub-table.hh"
+#include "hb-ot-layout-gpos-table.hh"
 
 
-struct hb_subset_profile_t {
-  hb_object_header_t header;
-  ASSERT_POD ();
-};
-
-/**
- * hb_subset_profile_create:
- *
- * Return value: New profile with default settings.
- *
- * Since: 1.8.0
- **/
-hb_subset_profile_t *
-hb_subset_profile_create ()
+static unsigned int
+_plan_estimate_subset_table_size (hb_subset_plan_t *plan,
+				  unsigned int table_len)
 {
-  return hb_object_create<hb_subset_profile_t>();
+  unsigned int src_glyphs = plan->source->get_num_glyphs ();
+  unsigned int dst_glyphs = plan->glyphset->get_population ();
+
+  if (unlikely (!src_glyphs))
+    return 512 + table_len;
+
+  return 512 + (unsigned int) (table_len * sqrt ((double) dst_glyphs / src_glyphs));
 }
 
-/**
- * hb_subset_profile_destroy:
- *
- * Since: 1.8.0
- **/
-void
-hb_subset_profile_destroy (hb_subset_profile_t *profile)
+template<typename TableType>
+static bool
+_subset2 (hb_subset_plan_t *plan)
 {
-  if (!hb_object_destroy (profile)) return;
+  hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
+  const TableType *table = source_blob->as<TableType> ();
 
-  free (profile);
+  hb_tag_t tag = TableType::tableTag;
+  hb_bool_t result = false;
+  if (source_blob->data)
+  {
+    hb_auto_t<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);
+    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);
+      return false;
+    }
+  retry:
+    hb_serialize_context_t serializer (buf.arrayZ(), buf_size);
+    hb_subset_context_t c (plan, &serializer);
+    result = table->subset (&c);
+    if (serializer.ran_out_of_room)
+    {
+      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);
+      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);
+	return false;
+      }
+      goto retry;
+    }
+    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);
+      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));
+      result = true;
+    }
+  }
+  else
+    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!");
+  return result;
 }
 
 template<typename TableType>
@@ -84,11 +122,9 @@
   hb_tag_t tag = TableType::tableTag;
   hb_bool_t result = false;
   if (source_blob->data)
-  {
-    result = table->subset(plan);
-  } else {
+    result = table->subset (plan);
+  else
     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!");
@@ -96,135 +132,6 @@
 }
 
 
-/*
- * A face that has add_table().
- */
-
-struct hb_subset_face_data_t
-{
-  struct table_entry_t
-  {
-    inline int cmp (const hb_tag_t *t) const
-    {
-      if (*t < tag) return -1;
-      if (*t > tag) return -1;
-      return 0;
-    }
-
-    hb_tag_t   tag;
-    hb_blob_t *blob;
-  };
-
-  hb_vector_t<table_entry_t, 32> tables;
-};
-
-static hb_subset_face_data_t *
-_hb_subset_face_data_create (void)
-{
-  hb_subset_face_data_t *data = (hb_subset_face_data_t *) calloc (1, sizeof (hb_subset_face_data_t));
-  if (unlikely (!data))
-    return nullptr;
-
-  data->tables.init ();
-
-  return data;
-}
-
-static void
-_hb_subset_face_data_destroy (void *user_data)
-{
-  hb_subset_face_data_t *data = (hb_subset_face_data_t *) user_data;
-
-  for (unsigned int i = 0; i < data->tables.len; i++)
-    hb_blob_destroy (data->tables[i].blob);
-
-  data->tables.fini ();
-
-  free (data);
-}
-
-static hb_blob_t *
-_hb_subset_face_data_reference_blob (hb_subset_face_data_t *data)
-{
-
-  unsigned int table_count = data->tables.len;
-  unsigned int face_length = table_count * 16 + 12;
-
-  for (unsigned int i = 0; i < table_count; i++)
-    face_length += hb_ceil_to_4 (hb_blob_get_length (data->tables.arrayZ[i].blob));
-
-  char *buf = (char *) malloc (face_length);
-  if (unlikely (!buf))
-    return nullptr;
-
-  hb_serialize_context_t c (buf, face_length);
-  OT::OpenTypeFontFile *f = c.start_serialize<OT::OpenTypeFontFile> ();
-
-  bool is_cff = data->tables.lsearch (HB_TAG ('C','F','F',' ')) || data->tables.lsearch (HB_TAG ('C','F','F','2'));
-  hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
-
-  Supplier<hb_tag_t>    tags_supplier  (&data->tables[0].tag, table_count, sizeof (data->tables[0]));
-  Supplier<hb_blob_t *> blobs_supplier (&data->tables[0].blob, table_count, sizeof (data->tables[0]));
-  bool ret = f->serialize_single (&c,
-				  sfnt_tag,
-				  tags_supplier,
-				  blobs_supplier,
-				  table_count);
-
-  c.end_serialize ();
-
-  if (unlikely (!ret))
-  {
-    free (buf);
-    return nullptr;
-  }
-
-  return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, free);
-}
-
-static hb_blob_t *
-_hb_subset_face_reference_table (hb_face_t *face, hb_tag_t tag, void *user_data)
-{
-  hb_subset_face_data_t *data = (hb_subset_face_data_t *) user_data;
-
-  if (!tag)
-    return _hb_subset_face_data_reference_blob (data);
-
-  hb_subset_face_data_t::table_entry_t *entry = data->tables.lsearch (tag);
-  if (entry)
-    return hb_blob_reference (entry->blob);
-
-  return nullptr;
-}
-
-/* TODO: Move this to hb-face.h and rename to hb_face_builder_create()
- * with hb_face_builder_add_table(). */
-hb_face_t *
-hb_subset_face_create (void)
-{
-  hb_subset_face_data_t *data = _hb_subset_face_data_create ();
-  if (unlikely (!data)) return hb_face_get_empty ();
-
-  return hb_face_create_for_tables (_hb_subset_face_reference_table,
-				    data,
-				    _hb_subset_face_data_destroy);
-}
-
-hb_bool_t
-hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
-{
-  if (unlikely (face->destroy != (hb_destroy_func_t) _hb_subset_face_data_destroy))
-    return false;
-
-  hb_subset_face_data_t *data = (hb_subset_face_data_t *) face->user_data;
-  hb_subset_face_data_t::table_entry_t *entry = data->tables.push ();
-
-  entry->tag = tag;
-  entry->blob = hb_blob_reference (blob);
-
-  return true;
-}
-
 static bool
 _subset_table (hb_subset_plan_t *plan,
                hb_tag_t          tag)
@@ -270,6 +177,14 @@
     case HB_OT_TAG_post:
       result = _subset<const OT::post> (plan);
       break;
+
+    case HB_OT_TAG_GSUB:
+      result = _subset2<const OT::GSUB> (plan);
+      break;
+    case HB_OT_TAG_GPOS:
+      result = _subset2<const OT::GPOS> (plan);
+      break;
+
     default:
       hb_blob_t *source_table = hb_face_reference_table(plan->source, tag);
       if (likely (source_table))
@@ -298,7 +213,7 @@
     case HB_TAG ('G', 'D', 'E', 'F'): /* temporary */
     case HB_TAG ('G', 'P', 'O', 'S'): /* temporary */
     case HB_TAG ('G', 'S', 'U', 'B'): /* temporary */
-      return plan->drop_ot_layout;
+      return plan->drop_layout;
     // Drop these tables below by default, list pulled
     // from fontTools:
     case HB_TAG ('B', 'A', 'S', 'E'):
@@ -327,19 +242,17 @@
 /**
  * hb_subset:
  * @source: font face data to be subset.
- * @profile: profile to use for the subsetting.
  * @input: input to use for the subsetting.
  *
- * Subsets a font according to provided profile and input.
+ * Subsets a font according to provided input.
  **/
 hb_face_t *
 hb_subset (hb_face_t *source,
-           hb_subset_profile_t *profile,
            hb_subset_input_t *input)
 {
-  if (unlikely (!profile || !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, profile, input);
+  hb_subset_plan_t *plan = hb_subset_plan_create (source, input);
 
   hb_tag_t table_tags[32];
   unsigned int offset = 0, count;
@@ -358,23 +271,9 @@
       success = success && _subset_table (plan, tag);
     }
     offset += count;
-  } while (count == ARRAY_LENGTH (table_tags));
+  } while (success && count == ARRAY_LENGTH (table_tags));
 
   hb_face_t *result = success ? hb_face_reference(plan->dest) : hb_face_get_empty();
   hb_subset_plan_destroy (plan);
   return result;
 }
-
-/**
- * hb_subset_get_all_codepoints:
- * @source: font face data to load.
- * @out: set to add the all codepoints covered by font face, source.
- */
-void
-hb_subset_get_all_codepoints (hb_face_t *source, hb_set_t *out)
-{
-  OT::cmap::accelerator_t cmap;
-  cmap.init (source);
-  cmap.get_all_codepoints (out);
-  cmap.fini();
-}
diff --git a/src/hb-subset.h b/src/hb-subset.h
index f6d2ae0..8b07a45 100644
--- a/src/hb-subset.h
+++ b/src/hb-subset.h
@@ -32,20 +32,6 @@
 HB_BEGIN_DECLS
 
 /*
- * hb_subset_profile_t
- * Things that change based on target environment, e.g. OS.
- * Threadsafe for multiple concurrent subset operations.
- */
-
-typedef struct hb_subset_profile_t hb_subset_profile_t;
-
-HB_EXTERN hb_subset_profile_t *
-hb_subset_profile_create (void);
-
-HB_EXTERN void
-hb_subset_profile_destroy (hb_subset_profile_t *profile);
-
-/*
  * hb_subset_input_t
  *
  * Things that change based on the input. Characters to keep, etc.
@@ -68,21 +54,24 @@
 HB_EXTERN hb_set_t *
 hb_subset_input_glyph_set (hb_subset_input_t *subset_input);
 
-HB_EXTERN hb_bool_t *
-hb_subset_input_drop_hints (hb_subset_input_t *subset_input);
+HB_EXTERN void
+hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input,
+				hb_bool_t drop_hints);
+HB_EXTERN hb_bool_t
+hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input);
 
-HB_EXTERN hb_bool_t *
-hb_subset_input_drop_ot_layout (hb_subset_input_t *subset_input);
+HB_EXTERN void
+hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input,
+				hb_bool_t drop_layout);
+HB_EXTERN hb_bool_t
+hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input);
+
 
 /* hb_subset() */
 HB_EXTERN hb_face_t *
 hb_subset (hb_face_t *source,
-	   hb_subset_profile_t *profile,
            hb_subset_input_t *input);
 
-/* hb_subset_get_all_codepoints */
-HB_EXTERN void
-hb_subset_get_all_codepoints (hb_face_t *source, hb_set_t *out);
 
 HB_END_DECLS
 
diff --git a/src/hb-subset.hh b/src/hb-subset.hh
new file mode 100644
index 0000000..9cdd388
--- /dev/null
+++ b/src/hb-subset.hh
@@ -0,0 +1,60 @@
+/*
+ * 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): Garret Rieger, Roderick Sheeter
+ */
+
+#ifndef HB_SUBSET_HH
+#define HB_SUBSET_HH
+
+
+#include "hb.hh"
+
+#include "hb-subset.h"
+
+#include "hb-machinery.hh"
+#include "hb-subset-input.hh"
+#include "hb-subset-plan.hh"
+
+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"; }
+  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; }
+
+  hb_subset_plan_t *plan;
+  hb_serialize_context_t *serializer;
+  unsigned int debug_depth;
+
+  hb_subset_context_t (hb_subset_plan_t *plan_,
+		       hb_serialize_context_t *serializer_) :
+			plan (plan_),
+			serializer (serializer_),
+			debug_depth (0) {}
+};
+
+
+#endif /* HB_SUBSET_HH */
diff --git a/src/hb-ucdn.cc b/src/hb-ucdn.cc
index 56d13e7..3179683 100644
--- a/src/hb-ucdn.cc
+++ b/src/hb-ucdn.cc
@@ -14,10 +14,9 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-unicode-private.hh"
-#include "hb-machinery-private.hh"
+#include "hb-machinery.hh"
 
 #include "ucdn.h"
 
@@ -182,15 +181,6 @@
     return (hb_unicode_combining_class_t) ucdn_get_combining_class(unicode);
 }
 
-static unsigned int
-hb_ucdn_eastasian_width(hb_unicode_funcs_t *ufuncs HB_UNUSED,
-			hb_codepoint_t unicode,
-			void *user_data HB_UNUSED)
-{
-    int w = ucdn_get_east_asian_width(unicode);
-    return (w == UCDN_EAST_ASIAN_F || w == UCDN_EAST_ASIAN_W) ? 2 : 1;
-}
-
 static hb_unicode_general_category_t
 hb_ucdn_general_category(hb_unicode_funcs_t *ufuncs HB_UNUSED,
 			 hb_codepoint_t unicode,
@@ -231,16 +221,10 @@
     return ucdn_decompose(ab, a, b);
 }
 
-static unsigned int
-hb_ucdn_decompose_compatibility(hb_unicode_funcs_t *ufuncs HB_UNUSED,
-				hb_codepoint_t u, hb_codepoint_t *decomposed,
-				void *user_data HB_UNUSED)
-{
-    return ucdn_compat_decompose(u, decomposed);
-}
 
-
+#ifdef HB_USE_ATEXIT
 static void free_static_ucdn_funcs (void);
+#endif
 
 static struct hb_ucdn_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_ucdn_unicode_funcs_lazy_loader_t>
 {
@@ -248,10 +232,12 @@
   {
     hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr);
 
-#define HB_UNICODE_FUNC_IMPLEMENT(name) \
-    hb_unicode_funcs_set_##name##_func (funcs, hb_ucdn_##name, nullptr, nullptr);
-      HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
-#undef HB_UNICODE_FUNC_IMPLEMENT
+    hb_unicode_funcs_set_combining_class_func (funcs, hb_ucdn_combining_class, nullptr, nullptr);
+    hb_unicode_funcs_set_general_category_func (funcs, hb_ucdn_general_category, nullptr, nullptr);
+    hb_unicode_funcs_set_mirroring_func (funcs, hb_ucdn_mirroring, nullptr, nullptr);
+    hb_unicode_funcs_set_script_func (funcs, hb_ucdn_script, nullptr, nullptr);
+    hb_unicode_funcs_set_compose_func (funcs, hb_ucdn_compose, nullptr, nullptr);
+    hb_unicode_funcs_set_decompose_func (funcs, hb_ucdn_decompose, nullptr, nullptr);
 
     hb_unicode_funcs_make_immutable (funcs);
 
@@ -273,6 +259,9 @@
 
 extern "C" HB_INTERNAL
 hb_unicode_funcs_t *
+hb_ucdn_get_unicode_funcs (void);
+
+hb_unicode_funcs_t *
 hb_ucdn_get_unicode_funcs (void)
 {
   return static_ucdn_funcs.get_unconst ();
diff --git a/src/hb-unicode-emoji-table.hh b/src/hb-unicode-emoji-table.hh
new file mode 100644
index 0000000..41199de
--- /dev/null
+++ b/src/hb-unicode-emoji-table.hh
@@ -0,0 +1,269 @@
+/* == Start of generated table == */
+/*
+ * The following tables are generated by running:
+ *
+ *   ./gen-emoji-table.py emoji-data.txt
+ *
+ * on file with this header:
+ *
+ * # emoji-data.txt
+ * # Date: 2018-02-07, 07:55:18 GMT
+ * # © 2018 Unicode®, Inc.
+ * # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
+ * # For terms of use, see http://www.unicode.org/terms_of_use.html
+ * #
+ * # Emoji Data for UTS #51
+ * # Version: 11.0
+ * #
+ * # For documentation and usage, see http://www.unicode.org/reports/tr51
+ */
+
+#ifndef HB_UNICODE_EMOJI_TABLE_HH
+#define HB_UNICODE_EMOJI_TABLE_HH
+
+#include "hb-unicode.hh"
+
+
+static const struct hb_unicode_range_t _hb_unicode_emoji_Extended_Pictographic_table[] =
+{
+  {0x00A9, 0x00A9},
+  {0x00AE, 0x00AE},
+  {0x203C, 0x203C},
+  {0x2049, 0x2049},
+  {0x2122, 0x2122},
+  {0x2139, 0x2139},
+  {0x2194, 0x2199},
+  {0x21A9, 0x21AA},
+  {0x231A, 0x231B},
+  {0x2328, 0x2328},
+  {0x2388, 0x2388},
+  {0x23CF, 0x23CF},
+  {0x23E9, 0x23F3},
+  {0x23F8, 0x23FA},
+  {0x24C2, 0x24C2},
+  {0x25AA, 0x25AB},
+  {0x25B6, 0x25B6},
+  {0x25C0, 0x25C0},
+  {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},
+  {0x2714, 0x2714},
+  {0x2716, 0x2716},
+  {0x271D, 0x271D},
+  {0x2721, 0x2721},
+  {0x2728, 0x2728},
+  {0x2733, 0x2734},
+  {0x2744, 0x2744},
+  {0x2747, 0x2747},
+  {0x274C, 0x274C},
+  {0x274E, 0x274E},
+  {0x2753, 0x2755},
+  {0x2757, 0x2757},
+  {0x2763, 0x2767},
+  {0x2795, 0x2797},
+  {0x27A1, 0x27A1},
+  {0x27B0, 0x27B0},
+  {0x27BF, 0x27BF},
+  {0x2934, 0x2935},
+  {0x2B05, 0x2B07},
+  {0x2B1B, 0x2B1C},
+  {0x2B50, 0x2B50},
+  {0x2B55, 0x2B55},
+  {0x3030, 0x3030},
+  {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},
+  {0x1F10D, 0x1F10F},
+  {0x1F12F, 0x1F12F},
+  {0x1F16C, 0x1F16F},
+  {0x1F170, 0x1F171},
+  {0x1F17E, 0x1F17E},
+  {0x1F17F, 0x1F17F},
+  {0x1F18E, 0x1F18E},
+  {0x1F191, 0x1F19A},
+  {0x1F1AD, 0x1F1E5},
+  {0x1F201, 0x1F202},
+  {0x1F203, 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},
+  {0x1F774, 0x1F77F},
+  {0x1F7D5, 0x1F7D8},
+  {0x1F7D9, 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},
+};
+
+#endif /* HB_UNICODE_EMOJI_TABLE_HH */
+
+/* == End of generated table == */
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index 8cb4172..7b821b4 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -28,9 +28,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
-#include "hb-unicode-private.hh"
+#include "hb-unicode.hh"
 
 
 
@@ -308,6 +308,8 @@
 {
   if (unlikely (hb_object_is_inert (ufuncs)))
     return;
+  if (ufuncs->immutable)
+    return;
 
   ufuncs->immutable = true;
 }
@@ -450,7 +452,7 @@
 }
 
 
-/* See hb-unicode-private.hh for details. */
+/* See hb-unicode.hh for details. */
 const uint8_t
 _hb_modified_combining_class[256] =
 {
@@ -562,3 +564,19 @@
   241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
   255, /* HB_UNICODE_COMBINING_CLASS_INVALID */
 };
+
+
+/*
+ * Emoji
+ */
+
+#include "hb-unicode-emoji-table.hh"
+
+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);
+}
diff --git a/src/hb-unicode.h b/src/hb-unicode.h
index 2657f48..df0b91f 100644
--- a/src/hb-unicode.h
+++ b/src/hb-unicode.h
@@ -40,6 +40,14 @@
 HB_BEGIN_DECLS
 
 
+/**
+ * HB_UNICODE_MAX
+ *
+ * Since: 1.9.0
+ **/
+#define HB_UNICODE_MAX 0x10FFFFu
+
+
 /* hb_unicode_general_category_t */
 
 /* Unicode Character Database property: General_Category (gc) */
@@ -222,9 +230,6 @@
 typedef hb_unicode_combining_class_t	(*hb_unicode_combining_class_func_t)	(hb_unicode_funcs_t *ufuncs,
 										 hb_codepoint_t      unicode,
 										 void               *user_data);
-typedef unsigned int			(*hb_unicode_eastasian_width_func_t)	(hb_unicode_funcs_t *ufuncs,
-										 hb_codepoint_t      unicode,
-										 void               *user_data);
 typedef hb_unicode_general_category_t	(*hb_unicode_general_category_func_t)	(hb_unicode_funcs_t *ufuncs,
 										 hb_codepoint_t      unicode,
 										 void               *user_data);
@@ -246,32 +251,6 @@
 										 hb_codepoint_t     *b,
 										 void               *user_data);
 
-/**
- * hb_unicode_decompose_compatibility_func_t:
- * @ufuncs: a Unicode function structure
- * @u: codepoint to decompose
- * @decomposed: address of codepoint array (of length %HB_UNICODE_MAX_DECOMPOSITION_LEN) to write decomposition into
- * @user_data: user data pointer as passed to hb_unicode_funcs_set_decompose_compatibility_func()
- *
- * Fully decompose @u to its Unicode compatibility decomposition. The codepoints of the decomposition will be written to @decomposed.
- * The complete length of the decomposition will be returned.
- *
- * If @u has no compatibility decomposition, zero should be returned.
- *
- * The Unicode standard guarantees that a buffer of length %HB_UNICODE_MAX_DECOMPOSITION_LEN codepoints will always be sufficient for any
- * compatibility decomposition plus an terminating value of 0.  Consequently, @decompose must be allocated by the caller to be at least this length.  Implementations
- * of this function type must ensure that they do not write past the provided array.
- *
- * Return value: number of codepoints in the full compatibility decomposition of @u, or 0 if no decomposition available.
- */
-typedef unsigned int			(*hb_unicode_decompose_compatibility_func_t)	(hb_unicode_funcs_t *ufuncs,
-											 hb_codepoint_t      u,
-											 hb_codepoint_t     *decomposed,
-											 void               *user_data);
-
-/* See Unicode 6.1 for details on the maximum decomposition length. */
-#define HB_UNICODE_MAX_DECOMPOSITION_LEN (18+1) /* codepoints */
-
 /* setters */
 
 /**
@@ -291,22 +270,6 @@
 					   void *user_data, hb_destroy_func_t destroy);
 
 /**
- * hb_unicode_funcs_set_eastasian_width_func:
- * @ufuncs: a Unicode function structure
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
- *
- * 
- *
- * Since: 0.9.2
- **/
-HB_EXTERN void
-hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs,
-					   hb_unicode_eastasian_width_func_t func,
-					   void *user_data, hb_destroy_func_t destroy);
-
-/**
  * hb_unicode_funcs_set_general_category_func:
  * @ufuncs: a Unicode function structure
  * @func: (closure user_data) (destroy destroy) (scope notified):
@@ -386,22 +349,6 @@
 				     hb_unicode_decompose_func_t func,
 				     void *user_data, hb_destroy_func_t destroy);
 
-/**
- * hb_unicode_funcs_set_decompose_compatibility_func:
- * @ufuncs: a Unicode function structure
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
- *
- * 
- *
- * Since: 0.9.2
- **/
-HB_EXTERN void
-hb_unicode_funcs_set_decompose_compatibility_func (hb_unicode_funcs_t *ufuncs,
-						   hb_unicode_decompose_compatibility_func_t func,
-						   void *user_data, hb_destroy_func_t destroy);
-
 /* accessors */
 
 /**
@@ -414,15 +361,6 @@
 			    hb_codepoint_t unicode);
 
 /**
- * hb_unicode_eastasian_width:
- *
- * Since: 0.9.2
- **/
-HB_EXTERN unsigned int
-hb_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs,
-			    hb_codepoint_t unicode);
-
-/**
  * hb_unicode_general_category:
  *
  * Since: 0.9.2
@@ -461,11 +399,6 @@
 		      hb_codepoint_t     *a,
 		      hb_codepoint_t     *b);
 
-HB_EXTERN unsigned int
-hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
-				    hb_codepoint_t      u,
-				    hb_codepoint_t     *decomposed);
-
 HB_END_DECLS
 
 #endif /* HB_UNICODE_H */
diff --git a/src/hb-unicode-private.hh b/src/hb-unicode.hh
similarity index 93%
rename from src/hb-unicode-private.hh
rename to src/hb-unicode.hh
index fb16ba4..6d6a4fa 100644
--- a/src/hb-unicode-private.hh
+++ b/src/hb-unicode.hh
@@ -28,10 +28,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_UNICODE_PRIVATE_HH
-#define HB_UNICODE_PRIVATE_HH
+#ifndef HB_UNICODE_HH
+#define HB_UNICODE_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 extern HB_INTERNAL const uint8_t _hb_modified_combining_class[256];
@@ -101,24 +101,23 @@
     return ret;
   }
 
-
   inline unsigned int
-  modified_combining_class (hb_codepoint_t unicode)
+  modified_combining_class (hb_codepoint_t u)
   {
     /* XXX This hack belongs to the Myanmar shaper. */
-    if (unlikely (unicode == 0x1037u)) unicode = 0x103Au;
+    if (unlikely (u == 0x1037u)) u = 0x103Au;
 
     /* XXX This hack belongs to the USE shaper (for Tai Tham):
      * Reorder SAKOT to ensure it comes after any tone marks. */
-    if (unlikely (unicode == 0x1A60u)) return 254;
+    if (unlikely (u == 0x1A60u)) return 254;
 
     /* XXX This hack belongs to the Tibetan shaper:
      * Reorder PADMA to ensure it comes after any vowel marks. */
-    if (unlikely (unicode == 0x0FC6u)) return 254;
+    if (unlikely (u == 0x0FC6u)) return 254;
     /* Reorder TSA -PHRU to reorder before U+0F74 */
-    if (unlikely (unicode == 0x0F39u)) return 127;
+    if (unlikely (u == 0x0F39u)) return 127;
 
-    return _hb_modified_combining_class[combining_class (unicode)];
+    return _hb_modified_combining_class[combining_class (u)];
   }
 
   static inline hb_bool_t
@@ -267,7 +266,9 @@
 DECLARE_NULL_INSTANCE (hb_unicode_funcs_t);
 
 
-/* Modified combining marks */
+/*
+ * Modified combining marks
+ */
 
 /* Hebrew
  *
@@ -360,10 +361,37 @@
 	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \
 	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
 
-#define HB_UNICODE_GENERAL_CATEGORY_IS_NON_ENCLOSING_MARK_OR_MODIFIER_SYMBOL(gen_cat) \
-	(FLAG_UNSAFE (gen_cat) & \
-	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \
-	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | \
-	  FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL)))
 
-#endif /* HB_UNICODE_PRIVATE_HH */
+/*
+ * Ranges, used for bsearch tables.
+ */
+
+struct hb_unicode_range_t
+{
+  static int
+  cmp (const void *_key, const void *_item, void *_arg)
+  {
+    hb_codepoint_t cp = *((hb_codepoint_t *) _key);
+    const hb_unicode_range_t *range = (hb_unicode_range_t *) _item;
+
+    if (cp < range->start)
+      return -1;
+    else if (cp <= range->end)
+      return 0;
+    else
+      return +1;
+  }
+
+  hb_codepoint_t start;
+  hb_codepoint_t end;
+};
+
+/*
+ * Emoji.
+ */
+
+HB_INTERNAL bool
+_hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp);
+
+
+#endif /* HB_UNICODE_HH */
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 11b7384..44a67ae 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -24,9 +24,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 #define HB_SHAPER uniscribe
-#include "hb-shaper-impl-private.hh"
+#include "hb-shaper-impl.hh"
 
 #include <windows.h>
 #include <usp10.h>
@@ -34,7 +34,7 @@
 
 #include "hb-uniscribe.h"
 
-#include "hb-open-file-private.hh"
+#include "hb-open-file.hh"
 #include "hb-ot-name-table.hh"
 #include "hb-ot-tag.h"
 
@@ -392,7 +392,7 @@
   name.stringOffset.set (name.get_size ());
   for (unsigned int i = 0; i < ARRAY_LENGTH (name_IDs); i++)
   {
-    OT::NameRecord &record = name.nameRecord[i];
+    OT::NameRecord &record = name.nameRecordZ[i];
     record.platformID.set (3);
     record.encodingID.set (1);
     record.languageID.set (0x0409u); /* English */
@@ -717,7 +717,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 +728,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.arrayZ() + reinterpret_cast<uintptr_t> (range->props.potfRecords);
     }
   }
 
@@ -902,8 +902,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 +943,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-private.hh b/src/hb-utf.hh
similarity index 97%
rename from src/hb-utf-private.hh
rename to src/hb-utf.hh
index 211eb4d..eccb632 100644
--- a/src/hb-utf-private.hh
+++ b/src/hb-utf.hh
@@ -24,10 +24,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_UTF_PRIVATE_HH
-#define HB_UTF_PRIVATE_HH
+#ifndef HB_UTF_HH
+#define HB_UTF_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 struct hb_utf8_t
@@ -279,4 +279,4 @@
   }
 };
 
-#endif /* HB_UTF_PRIVATE_HH */
+#endif /* HB_UTF_HH */
diff --git a/src/hb-vector-private.hh b/src/hb-vector.hh
similarity index 66%
rename from src/hb-vector-private.hh
rename to src/hb-vector.hh
index 1ef20d4..766e5fb 100644
--- a/src/hb-vector-private.hh
+++ b/src/hb-vector.hh
@@ -25,45 +25,52 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_VECTOR_PRIVATE_HH
-#define HB_VECTOR_PRIVATE_HH
+#ifndef HB_VECTOR_HH
+#define HB_VECTOR_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 
 template <typename Type, unsigned int StaticSize=8>
 struct hb_vector_t
 {
   unsigned int len;
+  private:
   unsigned int allocated; /* == 0 means allocation failed. */
-  Type *arrayZ;
+  Type *arrayZ_;
   Type static_array[StaticSize];
+  public:
 
   void init (void)
   {
     len = 0;
     allocated = ARRAY_LENGTH (static_array);
-    arrayZ = 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)
   {
     if (unlikely (i >= len))
       return Crap (Type);
-    return arrayZ[i];
+    return arrayZ()[i];
   }
   inline const Type& operator [] (unsigned int i) const
   {
     if (unlikely (i >= len))
       return Null(Type);
-    return arrayZ[i];
+    return arrayZ()[i];
   }
 
   inline Type *push (void)
   {
     if (unlikely (!resize (len + 1)))
       return &Crap(Type);
-    return &arrayZ[len - 1];
+    return &arrayZ()[len - 1];
   }
   inline Type *push (const Type& v)
   {
@@ -72,6 +79,8 @@
     return p;
   }
 
+  inline bool in_error (void) const { return allocated == 0; }
+
   /* Allocate for size but don't adjust len. */
   inline bool alloc (unsigned int size)
   {
@@ -89,17 +98,17 @@
 
     Type *new_array = nullptr;
 
-    if (arrayZ == static_array)
+    if (!arrayZ_)
     {
       new_array = (Type *) calloc (new_allocated, sizeof (Type));
       if (new_array)
-        memcpy (new_array, arrayZ, len * sizeof (Type));
+        memcpy (new_array, static_array, len * sizeof (Type));
     }
     else
     {
       bool overflows = (new_allocated < allocated) || hb_unsigned_mul_overflows (new_allocated, sizeof (Type));
       if (likely (!overflows))
-        new_array = (Type *) realloc (arrayZ, new_allocated * sizeof (Type));
+        new_array = (Type *) realloc (arrayZ_, new_allocated * sizeof (Type));
     }
 
     if (unlikely (!new_array))
@@ -108,7 +117,7 @@
       return false;
     }
 
-    arrayZ = new_array;
+    arrayZ_ = new_array;
     allocated = new_allocated;
 
     return true;
@@ -121,7 +130,7 @@
       return false;
 
     if (size > len)
-      memset (arrayZ + len, 0, (size - len) * sizeof (*arrayZ));
+      memset (arrayZ() + len, 0, (size - len) * sizeof (*arrayZ()));
 
     len = size;
     return true;
@@ -135,12 +144,13 @@
 
   inline void remove (unsigned int i)
   {
-     if (unlikely (i >= len))
-       return;
-     memmove (static_cast<void *> (&arrayZ[i]),
-	      static_cast<void *> (&arrayZ[i + 1]),
-	      (len - i - 1) * sizeof (Type));
-     len--;
+    if (unlikely (i >= len))
+      return;
+    Type *array = arrayZ();
+    memmove (static_cast<void *> (&array[i]),
+	     static_cast<void *> (&array[i + 1]),
+	     (len - i - 1) * sizeof (Type));
+    len--;
   }
 
   inline void shrink (int size_)
@@ -151,41 +161,55 @@
   }
 
   template <typename T>
-  inline Type *find (T v) {
+  inline Type *find (T v)
+  {
+    Type *array = arrayZ();
     for (unsigned int i = 0; i < len; i++)
-      if (arrayZ[i] == v)
-	return &arrayZ[i];
+      if (array[i] == v)
+	return &array[i];
     return nullptr;
   }
   template <typename T>
-  inline const Type *find (T v) const {
+  inline const Type *find (T v) const
+  {
+    const Type *array = arrayZ();
     for (unsigned int i = 0; i < len; i++)
-      if (arrayZ[i] == v)
-	return &arrayZ[i];
+      if (array[i] == v)
+	return &array[i];
     return nullptr;
   }
 
   inline void qsort (int (*cmp)(const void*, const void*))
   {
-    ::qsort (arrayZ, len, sizeof (Type), cmp);
+    ::qsort (arrayZ(), len, sizeof (Type), cmp);
   }
 
   inline void qsort (void)
   {
-    ::qsort (arrayZ, len, sizeof (Type), Type::cmp);
+    ::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);
+    ::qsort (arrayZ() + start, end - start, sizeof (Type), Type::cmp);
   }
 
   template <typename T>
   inline Type *lsearch (const T &x)
   {
+    Type *array = arrayZ();
     for (unsigned int i = 0; i < len; i++)
-      if (0 == this->arrayZ[i].cmp (&x))
-	return &arrayZ[i];
+      if (0 == array[i].cmp (&x))
+	return &array[i];
+    return nullptr;
+  }
+  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;
   }
 
@@ -193,22 +217,23 @@
   inline Type *bsearch (const T &x)
   {
     unsigned int i;
-    return bfind (x, &i) ? &arrayZ[i] : nullptr;
+    return bfind (x, &i) ? &arrayZ()[i] : nullptr;
   }
   template <typename T>
   inline const Type *bsearch (const T &x) const
   {
     unsigned int i;
-    return bfind (x, &i) ? &arrayZ[i] : nullptr;
+    return bfind (x, &i) ? &arrayZ()[i] : nullptr;
   }
   template <typename T>
   inline bool bfind (const T &x, unsigned int *i) const
   {
     int min = 0, max = (int) this->len - 1;
+    const Type *array = this->arrayZ();
     while (min <= max)
     {
       int mid = (min + max) / 2;
-      int c = this->arrayZ[mid].cmp (&x);
+      int c = array[mid].cmp (&x);
       if (c < 0)
         max = mid - 1;
       else if (c > 0)
@@ -219,20 +244,29 @@
 	return true;
       }
     }
-    if (max < 0 || (max < (int) this->len && this->arrayZ[max].cmp (&x) > 0))
+    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 != static_array)
-      free (arrayZ);
-    arrayZ = nullptr;
+    if (arrayZ_)
+      free (arrayZ_);
+    arrayZ_ = nullptr;
     allocated = len = 0;
   }
 };
 
 
-#endif /* HB_VECTOR_PRIVATE_HH */
+#endif /* HB_VECTOR_HH */
diff --git a/src/hb-version.h b/src/hb-version.h
index e0816d1..a8db516 100644
--- a/src/hb-version.h
+++ b/src/hb-version.h
@@ -36,11 +36,11 @@
 HB_BEGIN_DECLS
 
 
-#define HB_VERSION_MAJOR 1
-#define HB_VERSION_MINOR 8
-#define HB_VERSION_MICRO 8
+#define HB_VERSION_MAJOR 2
+#define HB_VERSION_MINOR 0
+#define HB_VERSION_MICRO 2
 
-#define HB_VERSION_STRING "1.8.8"
+#define HB_VERSION_STRING "2.0.2"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
diff --git a/src/hb-warning.cc b/src/hb-warning.cc
index f7a87b5..9fb4100 100644
--- a/src/hb-warning.cc
+++ b/src/hb-warning.cc
@@ -24,14 +24,14 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #if defined(HB_ATOMIC_INT_NIL)
 #error "Could not find any system to define atomic_int macros, library WILL NOT be thread-safe"
-#error "Check hb-atomic-private.hh for possible resolutions."
+#error "Check hb-atomic.hh for possible resolutions."
 #endif
 
 #if defined(HB_MUTEX_IMPL_NIL)
 #error "Could not find any system to define mutex macros, library WILL NOT be thread-safe"
-#error "Check hb-mutex-private.hh for possible resolutions."
+#error "Check hb-mutex.hh for possible resolutions."
 #endif
diff --git a/src/hb-private.hh b/src/hb.hh
similarity index 92%
rename from src/hb-private.hh
rename to src/hb.hh
index efe7bda..098b566 100644
--- a/src/hb-private.hh
+++ b/src/hb.hh
@@ -26,8 +26,8 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_PRIVATE_HH
-#define HB_PRIVATE_HH
+#ifndef HB_HH
+#define HB_HH
 
 #define _GNU_SOURCE 1
 
@@ -35,12 +35,18 @@
 #include "config.h"
 #endif
 
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#endif
+
+#if defined (_MSC_VER) && defined (HB_DLL_EXPORT)
+#define HB_EXTERN __declspec (dllexport) extern
+#endif
+
 #include "hb.h"
 #define HB_H_IN
-#ifdef HAVE_OT
 #include "hb-ot.h"
 #define HB_OT_H_IN
-#endif
 
 #include <math.h>
 #include <stdlib.h>
@@ -174,8 +180,10 @@
 # if !defined(HB_NO_VISIBILITY) && !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_MSC_VER) && !defined(__SUNPRO_CC)
 #  define HB_INTERNAL __attribute__((__visibility__("hidden")))
 # elif defined(__MINGW32__)
-   /* We use -export-symbols on mingw32, since it does not support visibility
-    * attribute. */
+   /* We use -export-symbols on mingw32, since it does not support visibility attributes. */
+#  define HB_INTERNAL
+# elif defined (_MSC_VER) && defined (HB_DLL_EXPORT)
+   /* We do not try to export internal symbols on Visual Studio */
 #  define HB_INTERNAL
 #else
 #  define HB_INTERNAL
@@ -229,6 +237,15 @@
 #  define HB_FALLTHROUGH /* FALLTHROUGH */
 #endif
 
+#if defined(__clang__)
+/* Disable certain sanitizer errors. */
+/* https://github.com/harfbuzz/harfbuzz/issues/1247 */
+#define HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW __attribute__((no_sanitize("signed-integer-overflow")))
+#else
+#define HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW
+#endif
+
+
 #if defined(_WIN32) || defined(__CYGWIN__)
    /* We need Windows Vista for both Uniscribe backend and for
     * MemoryBarrier.  We don't support compiling on Windows XP,
@@ -237,7 +254,9 @@
 #    undef _WIN32_WINNT
 #  endif
 #  ifndef _WIN32_WINNT
-#    define _WIN32_WINNT 0x0600
+#    if !defined(WINAPI_FAMILY) || !(WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
+#      define _WIN32_WINNT 0x0600
+#    endif
 #  endif
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN 1
@@ -468,13 +487,21 @@
 #endif
 
 
+/*
+ * For lack of a better place, put Zawgyi script hack here.
+ * https://github.com/harfbuzz/harfbuzz/issues/1162
+ */
+
+#define HB_SCRIPT_MYANMAR_ZAWGYI	((hb_script_t) HB_TAG ('Q','a','a','g'))
+
+
 /* Headers we include for everyone.  Keep sorted.  They express dependency amongst
  * themselves, but no other file should include them.*/
-#include "hb-atomic-private.hh"
+#include "hb-atomic.hh"
 #include "hb-debug.hh"
 #include "hb-dsalgs.hh"
-#include "hb-mutex-private.hh"
+#include "hb-mutex.hh"
 #include "hb-null.hh"
-#include "hb-object-private.hh"
+#include "hb-object.hh"
 
-#endif /* HB_PRIVATE_HH */
+#endif /* HB_HH */
diff --git a/src/main.cc b/src/main.cc
index 98a1320..490b76e 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -25,9 +25,9 @@
  */
 
 #include "hb-static.cc"
-#include "hb-open-file-private.hh"
+#include "hb-open-file.hh"
 #include "hb-ot-layout-gdef-table.hh"
-#include "hb-ot-layout-gsubgpos-private.hh"
+#include "hb-ot-layout-gsubgpos.hh"
 
 #ifdef HAVE_GLIB
 #include <glib.h>
diff --git a/src/sample.py b/src/sample.py
index 8f97195..5d65aa0 100755
--- a/src/sample.py
+++ b/src/sample.py
@@ -54,11 +54,11 @@
 	# buffer:
 	hb.buffer_add_utf8 (buf, text.encode('utf-8'), 0, -1)
 	# Otherwise, then following handles both narrow and wide
-	# Python builds:
+	# Python builds (the first item in the array is BOM, so we skip it):
 elif sys.maxunicode == 0x10FFFF:
-	hb.buffer_add_utf32 (buf, array.array('I', text.encode('utf-32')), 0, -1)
+	hb.buffer_add_utf32 (buf, array.array('I', text.encode('utf-32'))[1:], 0, -1)
 else:
-	hb.buffer_add_utf16 (buf, array.array('H', text.encode('utf-16')), 0, -1)
+	hb.buffer_add_utf16 (buf, array.array('H', text.encode('utf-16'))[1:], 0, -1)
 
 
 hb.buffer_guess_segment_properties (buf)
diff --git a/src/test-buffer-serialize.cc b/src/test-buffer-serialize.cc
index 39eb13d..a91f4f7 100644
--- a/src/test-buffer-serialize.cc
+++ b/src/test-buffer-serialize.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb.h"
 #include "hb-ot.h"
diff --git a/src/test-size-params.cc b/src/test-size-params.cc
index 3c43852..e53a47d 100644
--- a/src/test-size-params.cc
+++ b/src/test-size-params.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb.h"
 #include "hb-ot.h"
diff --git a/src/test-unicode-ranges.cc b/src/test-unicode-ranges.cc
index dbc5fa4..d9342d7 100644
--- a/src/test-unicode-ranges.cc
+++ b/src/test-unicode-ranges.cc
@@ -24,24 +24,24 @@
  * Google Author(s): Garret Rieger
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb-ot-os2-unicode-ranges.hh"
 
-void
+static void
 test (hb_codepoint_t cp, unsigned int bit)
 {
-  if (OT::hb_get_unicode_range_bit (cp) != bit)
+  if (OT::_hb_ot_os2_get_unicode_range_bit (cp) != bit)
   {
     fprintf (stderr, "got incorrect bit (%d) for cp 0x%X. Should have been %d.",
-             OT::hb_get_unicode_range_bit (cp),
+             OT::_hb_ot_os2_get_unicode_range_bit (cp),
              cp,
              bit);
     abort();
   }
 }
 
-void
+static void
 test_get_unicode_range_bit (void)
 {
   test (0x0000, 0);
diff --git a/src/test-would-substitute.cc b/src/test-would-substitute.cc
index 1836d72..268f7db 100644
--- a/src/test-would-substitute.cc
+++ b/src/test-would-substitute.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb.h"
 #include "hb-ot.h"
diff --git a/src/test.cc b/src/test.cc
index cf59e00..f0eace8 100644
--- a/src/test.cc
+++ b/src/test.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include "hb.h"
 
diff --git a/test/api/CMakeLists.txt b/test/api/CMakeLists.txt
index b540eb5..0c7337c 100644
--- a/test/api/CMakeLists.txt
+++ b/test/api/CMakeLists.txt
@@ -3,6 +3,8 @@
   extract_make_variable (TEST_PROGS ${MAKEFILEAM})
 
   list (APPEND TEST_PROGS
+    test-ot-color
+    test-ot-name
     test-ot-tag
     test-c
     test-cplusplus
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index a203344..45a34e6 100644
--- a/test/api/Makefile.am
+++ b/test/api/Makefile.am
@@ -16,6 +16,8 @@
 
 EXTRA_DIST += fonts
 
+LINK = $(CXXLINK)
+
 if HAVE_GLIB
 AM_CPPFLAGS = -DSRCDIR="\"$(srcdir)\"" -I$(top_srcdir)/src/ -I$(top_builddir)/src/ $(GLIB_CFLAGS)
 LDADD = $(top_builddir)/src/libharfbuzz.la $(GLIB_LIBS)
@@ -28,13 +30,14 @@
 TEST_PROGS = \
 	test-blob \
 	test-buffer \
+	test-collect-unicodes \
 	test-common \
 	test-font \
+	test-map \
 	test-object \
 	test-set \
 	test-shape \
 	test-subset \
-	test-subset-codepoints \
 	test-subset-cmap \
 	test-subset-glyf \
 	test-subset-hdmx \
@@ -67,13 +70,24 @@
 endif
 
 
-if HAVE_OT
-
 TEST_PROGS += \
 	test-ot-color \
+	test-ot-name \
 	test-ot-tag \
 	$(NULL)
 
+
+if HAVE_PTHREAD
+if HAVE_FREETYPE
+TEST_PROGS += test-multithread
+test_multithread_CFLAGS = $(CFLAGS) $(PTHREAD_CFLAGS) $(FREETYPE_CFLAGS)
+test_multithread_LDADD = $(LDADD) $(PTHREAD_LIBS) $(FREETYPE_LIBS)
+# The auto-generated link rule somehow includes CFLAGS as well.  Without
+# it, pthread link fails, because, who knows why, $PTHREAD_LIBS is empty.
+test_multithread_LINK = $(LINK) $(PTHREAD_CFLAGS)
+endif
+endif
+
 if HAVE_FREETYPE
 TEST_PROGS += \
 	test-ot-math \
@@ -82,7 +96,6 @@
 test_ot_math_CPPFLAGS = $(AM_CPPFLAGS) $(FREETYPE_CFLAGS)
 endif # HAVE_FREETYPE
 
-endif # HAVE_OT
 
 # Tests for header compilation
 TEST_PROGS += \
@@ -154,13 +167,18 @@
 	$(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
+	$(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 $^ \
 	| grep ' T ' | sed 's/.* T //' | grep -v '^\(_init\|_fini\)$$' \
 	| sort | uniq > $@.tmp && mv $@.tmp $@
-symbols-untested.txt: symbols-tested.txt symbols-exported.txt
+symbols-untested.txt: symbols-tested-or-deprecated.txt symbols-exported.txt
 	$(AM_V_GEN)diff $^ > $@.tmp; mv $@.tmp $@
-CLEANFILES += symbols-tested.txt symbols-exported.txt symbols-untested.txt
+CLEANFILES += symbols-tested.txt \
+	symbols-exported.txt \
+	symbols-untested.txt \
+	symbols-tested-or-deprecated.txt
 check-symbols: symbols-untested.txt
 	@! cat $^ | grep .
 
diff --git a/test/api/fonts/Roboto-Regular.multihdmx.a.ttf b/test/api/fonts/Roboto-Regular.multihdmx.a.ttf
new file mode 100644
index 0000000..dd82178
--- /dev/null
+++ b/test/api/fonts/Roboto-Regular.multihdmx.a.ttf
Binary files differ
diff --git a/test/api/fonts/Roboto-Regular.multihdmx.abc.ttf b/test/api/fonts/Roboto-Regular.multihdmx.abc.ttf
new file mode 100644
index 0000000..03dd8b6
--- /dev/null
+++ b/test/api/fonts/Roboto-Regular.multihdmx.abc.ttf
Binary files differ
diff --git a/test/api/fonts/cv01.otf b/test/api/fonts/cv01.otf
new file mode 100644
index 0000000..01dbf01
--- /dev/null
+++ b/test/api/fonts/cv01.otf
Binary files differ
diff --git a/test/api/hb-subset-test.h b/test/api/hb-subset-test.h
index c34f394..5f5cd8d 100644
--- a/test/api/hb-subset-test.h
+++ b/test/api/hb-subset-test.h
@@ -51,12 +51,21 @@
 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);
+  char *path = g_test_build_filename (G_TEST_DIST, font_path, NULL);
 #else
-  char* path = g_strdup(font_path);
+  char *path = g_strdup (font_path);
 #endif
 
-  return hb_face_create (hb_blob_create_from_file (path), 0);
+  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 *
@@ -72,11 +81,9 @@
 hb_subset_test_create_subset (hb_face_t *source,
                               hb_subset_input_t *input)
 {
-  hb_subset_profile_t *profile = hb_subset_profile_create();
-  hb_face_t *subset = hb_subset (source, profile, input);
+  hb_face_t *subset = hb_subset (source, input);
   g_assert (subset);
 
-  hb_subset_profile_destroy (profile);
   hb_subset_input_destroy (input);
   return subset;
 }
diff --git a/test/api/hb-test.h b/test/api/hb-test.h
index 307845f..39d091b 100644
--- a/test/api/hb-test.h
+++ b/test/api/hb-test.h
@@ -42,6 +42,7 @@
 /* Just in case */
 #undef G_DISABLE_ASSERT
 
+#define HB_UNUSED	G_GNUC_UNUSED
 
 /* Misc */
 
diff --git a/test/api/test-blob.c b/test/api/test-blob.c
index d566f4e..7914a26 100644
--- a/test/api/test-blob.c
+++ b/test/api/test-blob.c
@@ -195,7 +195,7 @@
 }
 
 static void
-fixture_finish (fixture_t *fixture, gconstpointer user_data)
+fixture_finish (fixture_t *fixture, gconstpointer user_data HB_UNUSED)
 {
   hb_blob_destroy (fixture->blob);
   g_assert_cmpint (fixture->freed, ==, 1);
diff --git a/test/api/test-buffer.c b/test/api/test-buffer.c
index 5c98a9a..5fba3b2 100644
--- a/test/api/test-buffer.c
+++ b/test/api/test-buffer.c
@@ -92,14 +92,14 @@
 }
 
 static void
-fixture_finish (fixture_t *fixture, gconstpointer user_data)
+fixture_finish (fixture_t *fixture, gconstpointer user_data HB_UNUSED)
 {
   hb_buffer_destroy (fixture->buffer);
 }
 
 
 static void
-test_buffer_properties (fixture_t *fixture, gconstpointer user_data)
+test_buffer_properties (fixture_t *fixture, gconstpointer user_data HB_UNUSED)
 {
   hb_buffer_t *b = fixture->buffer;
   hb_unicode_funcs_t *ufuncs;
@@ -294,7 +294,7 @@
 }
 
 static void
-test_buffer_positions (fixture_t *fixture, gconstpointer user_data)
+test_buffer_positions (fixture_t *fixture, gconstpointer user_data HB_UNUSED)
 {
   hb_buffer_t *b = fixture->buffer;
   unsigned int i, len, len2;
@@ -319,7 +319,7 @@
 }
 
 static void
-test_buffer_allocation (fixture_t *fixture, gconstpointer user_data)
+test_buffer_allocation (fixture_t *fixture, gconstpointer user_data HB_UNUSED)
 {
   hb_buffer_t *b = fixture->buffer;
 
diff --git a/test/api/test-c.c b/test/api/test-c.c
index 4b43b83..061f35c 100644
--- a/test/api/test-c.c
+++ b/test/api/test-c.c
@@ -32,6 +32,7 @@
 #endif
 
 #include <hb.h>
+#include <hb-ot.h>
 
 #ifdef HAVE_GLIB
 #include <hb-glib.h>
@@ -45,10 +46,6 @@
 #include <hb-ft.h>
 #endif
 
-#ifdef HAVE_OT
-#include <hb-ot.h>
-#endif
-
 #ifdef HAVE_UNISCRIBE
 #include <hb-uniscribe.h>
 #endif
@@ -58,7 +55,7 @@
 #endif
 
 int
-main (int argc, char **argv)
+main (void)
 {
   return !*hb_shape_list_shapers ();
 }
diff --git a/test/api/test-subset-codepoints.c b/test/api/test-collect-unicodes.c
similarity index 83%
rename from test/api/test-subset-codepoints.c
rename to test/api/test-collect-unicodes.c
index 3bd1fe0..f7a7813 100644
--- a/test/api/test-subset-codepoints.c
+++ b/test/api/test-collect-unicodes.c
@@ -28,14 +28,15 @@
 #include "hb-subset-test.h"
 
 static void
-test_get_all_codepoints_format4 (void)
+test_collect_unicodes_format4 (void)
 {
   hb_face_t *face = hb_subset_test_open_font("fonts/Roboto-Regular.abc.format4.ttf");
   hb_set_t *codepoints = hb_set_create();
+  hb_codepoint_t cp;
 
-  hb_subset_get_all_codepoints (face, codepoints);
+  hb_face_collect_unicodes (face, codepoints);
 
-  hb_codepoint_t cp = HB_SET_VALUE_INVALID;
+  cp = HB_SET_VALUE_INVALID;
   g_assert (hb_set_next (codepoints, &cp));
   g_assert_cmpuint (0x61, ==, cp);
   g_assert (hb_set_next (codepoints, &cp));
@@ -49,14 +50,15 @@
 }
 
 static void
-test_get_all_codepoints_format12 (void)
+test_collect_unicodes_format12 (void)
 {
   hb_face_t *face = hb_subset_test_open_font("fonts/Roboto-Regular.abc.format12.ttf");
   hb_set_t *codepoints = hb_set_create();
+  hb_codepoint_t cp;
 
-  hb_subset_get_all_codepoints (face, codepoints);
+  hb_face_collect_unicodes (face, codepoints);
 
-  hb_codepoint_t cp = HB_SET_VALUE_INVALID;
+  cp = HB_SET_VALUE_INVALID;
   g_assert (hb_set_next (codepoints, &cp));
   g_assert_cmpuint (0x61, ==, cp);
   g_assert (hb_set_next (codepoints, &cp));
@@ -70,14 +72,15 @@
 }
 
 static void
-test_get_all_codepoints (void)
+test_collect_unicodes (void)
 {
   hb_face_t *face = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf");
   hb_set_t *codepoints = hb_set_create();
+  hb_codepoint_t cp;
 
-  hb_subset_get_all_codepoints (face, codepoints);
+  hb_face_collect_unicodes (face, codepoints);
 
-  hb_codepoint_t cp = HB_SET_VALUE_INVALID;
+  cp = HB_SET_VALUE_INVALID;
   g_assert (hb_set_next (codepoints, &cp));
   g_assert_cmpuint (0x61, ==, cp);
   g_assert (hb_set_next (codepoints, &cp));
@@ -95,9 +98,9 @@
 {
   hb_test_init (&argc, &argv);
 
-  hb_test_add (test_get_all_codepoints);
-  hb_test_add (test_get_all_codepoints_format4);
-  hb_test_add (test_get_all_codepoints_format12);
+  hb_test_add (test_collect_unicodes);
+  hb_test_add (test_collect_unicodes_format4);
+  hb_test_add (test_collect_unicodes_format12);
 
   return hb_test_run();
 }
diff --git a/test/api/test-common.c b/test/api/test-common.c
index f6f0d48..e9fae13 100644
--- a/test/api/test-common.c
+++ b/test/api/test-common.c
@@ -32,7 +32,6 @@
 static void
 test_types_int (void)
 {
-  /* We already ASSERT_STATIC these in hb-private.h, but anyway */
   g_assert_cmpint (sizeof (int8_t), ==, 1);
   g_assert_cmpint (sizeof (uint8_t), ==, 1);
   g_assert_cmpint (sizeof (int16_t), ==, 2);
diff --git a/test/api/test-font.c b/test/api/test-font.c
index 527dfcd..3d81cf9 100644
--- a/test/api/test-font.c
+++ b/test/api/test-font.c
@@ -83,7 +83,7 @@
 }
 
 static hb_blob_t *
-get_table (hb_face_t *face, hb_tag_t tag, void *user_data)
+get_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data HB_UNUSED)
 {
   if (tag == HB_TAG ('a','b','c','d'))
     return hb_blob_create (test_data, sizeof (test_data), HB_MEMORY_MODE_READONLY, NULL, NULL);
@@ -210,10 +210,10 @@
 }
 
 static hb_bool_t
-contour_point_func1 (hb_font_t *font, void *font_data,
-		     hb_codepoint_t glyph, unsigned int point_index,
+contour_point_func1 (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED,
+		     hb_codepoint_t glyph, unsigned int point_index HB_UNUSED,
 		     hb_position_t *x, hb_position_t *y,
-		     void *user_data)
+		     void *user_data HB_UNUSED)
 {
   if (glyph == 1) {
     *x = 2;
@@ -230,10 +230,10 @@
 }
 
 static hb_bool_t
-contour_point_func2 (hb_font_t *font, void *font_data,
+contour_point_func2 (hb_font_t *font, void *font_data HB_UNUSED,
 		     hb_codepoint_t glyph, unsigned int point_index,
 		     hb_position_t *x, hb_position_t *y,
-		     void *user_data)
+		     void *user_data HB_UNUSED)
 {
   if (glyph == 1) {
     *x = 6;
@@ -246,9 +246,9 @@
 }
 
 static hb_position_t
-glyph_h_advance_func1 (hb_font_t *font, void *font_data,
+glyph_h_advance_func1 (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED,
 		       hb_codepoint_t glyph,
-		       void *user_data)
+		       void *user_data HB_UNUSED)
 {
   if (glyph == 1)
     return 8;
@@ -361,8 +361,74 @@
 
 
   hb_font_destroy (font3);
+  hb_font_destroy (font2);
 }
 
+static hb_bool_t
+nominal_glyph_func (hb_font_t *font HB_UNUSED,
+		    void *font_data HB_UNUSED,
+		    hb_codepoint_t unicode HB_UNUSED,
+		    hb_codepoint_t *glyph,
+		    void *user_data HB_UNUSED)
+{
+  *glyph = 0;
+  return FALSE;
+}
+
+static unsigned int
+nominal_glyphs_func (hb_font_t *font HB_UNUSED,
+		     void *font_data HB_UNUSED,
+		     unsigned int count HB_UNUSED,
+		     const hb_codepoint_t *first_unicode HB_UNUSED,
+		     unsigned int unicode_stride HB_UNUSED,
+		     hb_codepoint_t *first_glyph HB_UNUSED,
+		     unsigned int glyph_stride HB_UNUSED,
+		     void *user_data HB_UNUSED)
+{
+  return 0;
+}
+
+static void
+test_fontfuncs_parallels (void)
+{
+  hb_blob_t *blob;
+  hb_face_t *face;
+
+  hb_font_funcs_t *ffuncs1;
+  hb_font_funcs_t *ffuncs2;
+
+  hb_font_t *font0;
+  hb_font_t *font1;
+  hb_font_t *font2;
+
+  blob = hb_blob_create (test_data, sizeof (test_data), HB_MEMORY_MODE_READONLY, NULL, NULL);
+  face = hb_face_create (blob, 0);
+  hb_blob_destroy (blob);
+  font0 = hb_font_create (face);
+  hb_face_destroy (face);
+
+  /* setup sub-font1 */
+  font1 = hb_font_create_sub_font (font0);
+  hb_font_destroy (font0);
+  ffuncs1 = hb_font_funcs_create ();
+  hb_font_funcs_set_nominal_glyph_func (ffuncs1, nominal_glyph_func, NULL, NULL);
+  hb_font_set_funcs (font1, ffuncs1, NULL, NULL);
+  hb_font_funcs_destroy (ffuncs1);
+
+  /* setup sub-font2 */
+  font2 = hb_font_create_sub_font (font1);
+  hb_font_destroy (font1);
+  ffuncs2 = hb_font_funcs_create ();
+  hb_font_funcs_set_nominal_glyphs_func (ffuncs1, nominal_glyphs_func, NULL, NULL);
+  hb_font_set_funcs (font2, ffuncs2, NULL, NULL);
+  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);
+}
 
 static void
 test_font_empty (void)
@@ -542,6 +608,7 @@
   hb_test_add (test_fontfuncs_empty);
   hb_test_add (test_fontfuncs_nil);
   hb_test_add (test_fontfuncs_subclassing);
+  hb_test_add (test_fontfuncs_parallels);
 
   hb_test_add (test_font_empty);
   hb_test_add (test_font_properties);
diff --git a/test/api/test-map.c b/test/api/test-map.c
new file mode 100644
index 0000000..0d8be0b
--- /dev/null
+++ b/test/api/test-map.c
@@ -0,0 +1,114 @@
+/*
+ * 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"
+
+/* Unit tests for hb-map.h */
+
+
+static void
+test_map_basic (void)
+{
+  hb_map_t *empty = hb_map_get_empty ();
+  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 ();
+  g_assert (hb_map_allocation_successful (m));
+  g_assert (hb_map_is_empty (m));
+
+  hb_map_set (m, 213, 223);
+  hb_map_set (m, 643, 675);
+  g_assert_cmpint (hb_map_get_population (m), ==, 2);
+
+  g_assert_cmpint (hb_map_get (m, 213), ==, 223);
+  g_assert (!hb_map_has (m, 123));
+  g_assert (hb_map_has (m, 213));
+
+  hb_map_del (m, 213);
+  g_assert (!hb_map_has (m, 213));
+
+  g_assert_cmpint (hb_map_get (m, 643), ==, 675);
+  hb_map_set (m, 237, 673);
+  g_assert (hb_map_has (m, 237));
+  hb_map_clear (m);
+  g_assert (!hb_map_has (m, 237));
+  g_assert (!hb_map_has (m, 643));
+  g_assert_cmpint (hb_map_get_population (m), ==, 0);
+
+  hb_map_destroy (m);
+}
+
+static void
+test_map_userdata (void)
+{
+  hb_map_t *m = hb_map_create ();
+
+  hb_user_data_key_t key[2];
+  int *data = (int *) malloc (sizeof (int));
+  *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 = 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);
+  hb_map_set_user_data (m, &key[0], data2, free, TRUE);
+  g_assert_cmpint (*((int *) hb_map_get_user_data (m, &key[0])), ==, 6343);
+
+  hb_map_destroy (m);
+}
+
+static void
+test_map_refcount (void)
+{
+  hb_map_t *m = hb_map_create ();
+  hb_map_set (m, 213, 223);
+  g_assert_cmpint (hb_map_get (m, 213), ==, 223);
+
+  hb_map_t *m2 = hb_map_reference (m);
+  hb_map_destroy (m);
+
+  /* We copied its reference so it is still usable after one destroy */
+  g_assert (hb_map_has (m, 213));
+  g_assert (hb_map_has (m2, 213));
+
+  hb_map_destroy (m2);
+
+  /* Now you can't access them anymore */
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_map_basic);
+  hb_test_add (test_map_userdata);
+  hb_test_add (test_map_refcount);
+
+  return hb_test_run();
+}
diff --git a/test/api/test-multithread.c b/test/api/test-multithread.c
new file mode 100644
index 0000000..7b62a02
--- /dev/null
+++ b/test/api/test-multithread.c
@@ -0,0 +1,178 @@
+/*
+ * 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 <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>
+
+static const char *font_path = "fonts/Inconsolata-Regular.abc.ttf";
+static const char *text = "abc";
+
+static int num_threads = 30;
+static int num_iters = 200;
+
+static hb_font_t *font;
+static hb_buffer_t *ref_buffer;
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void
+fill_the_buffer (hb_buffer_t *buffer)
+{
+  hb_buffer_add_utf8 (buffer, text, -1, 0, -1);
+  hb_buffer_guess_segment_properties (buffer);
+  hb_shape (font, buffer, NULL, 0);
+}
+
+static void
+validity_check (hb_buffer_t *buffer) {
+  if (hb_buffer_diff (ref_buffer, buffer, (hb_codepoint_t) -1, 0))
+  {
+    fprintf (stderr, "One of the buffers was different from the reference.\n");
+    char out[255];
+
+    hb_buffer_serialize_glyphs (buffer, 0, hb_buffer_get_length (ref_buffer),
+				out, sizeof (out), NULL,
+				font, HB_BUFFER_SERIALIZE_FORMAT_TEXT,
+				HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES);
+    fprintf (stderr, "Actual:   %s\n", out);
+
+    hb_buffer_serialize_glyphs (ref_buffer, 0, hb_buffer_get_length (ref_buffer),
+				out, sizeof (out), NULL,
+				font, HB_BUFFER_SERIALIZE_FORMAT_TEXT,
+				HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES);
+    fprintf (stderr, "Expected: %s\n", out);
+
+    exit (1);
+  }
+}
+
+static void *
+thread_func (void *data)
+{
+  hb_buffer_t *buffer = (hb_buffer_t *) data;
+
+  pthread_mutex_lock (&mutex);
+  pthread_mutex_unlock (&mutex);
+
+  int i;
+  for (i = 0; i < num_iters; i++)
+  {
+    hb_buffer_clear_contents (buffer);
+    fill_the_buffer (buffer);
+    validity_check (buffer);
+  }
+
+  return 0;
+}
+
+static void
+test_body (void)
+{
+  int i;
+  pthread_t *threads = calloc (num_threads, sizeof (pthread_t));
+  hb_buffer_t **buffers = calloc (num_threads, sizeof (hb_buffer_t *));
+
+  pthread_mutex_lock (&mutex);
+
+  for (i = 0; i < num_threads; i++)
+  {
+    hb_buffer_t *buffer = hb_buffer_create ();
+    buffers[i] = buffer;
+    pthread_create (&threads[i], NULL, thread_func, buffer);
+  }
+
+  /* Let them loose! */
+  pthread_mutex_unlock (&mutex);
+
+  for (i = 0; i < num_threads; i++)
+  {
+    pthread_join (threads[i], NULL);
+    hb_buffer_destroy (buffers[i]);
+  }
+
+  free (buffers);
+  free (threads);
+}
+
+int
+main (int argc, char **argv)
+{
+  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
+
+  char *path = argc > 1 && *argv[1] ? argv[1] : (char *) default_path;
+  if (argc > 2)
+    num_threads = atoi (argv[2]);
+  if (argc > 3)
+    num_iters = atoi (argv[3]);
+  if (argc > 4)
+    text = argv[4];
+
+  /* Dummy call to alleviate _guess_segment_properties thread safety-ness
+   * 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);
+  font = hb_font_create (face);
+
+  /* Fill the reference */
+  ref_buffer = hb_buffer_create ();
+  fill_the_buffer (ref_buffer);
+
+  /* Unnecessary, since version 2 it is ot-font by default */
+  hb_ot_font_set_funcs (font);
+  test_body ();
+
+  /* Test hb-ft in multithread */
+  hb_ft_font_set_funcs (font);
+  test_body ();
+
+  hb_buffer_destroy (ref_buffer);
+
+  hb_font_destroy (font);
+  hb_face_destroy (face);
+  hb_blob_destroy (blob);
+
+  g_free (default_path);
+
+  return 0;
+}
diff --git a/test/api/test-ot-color.c b/test/api/test-ot-color.c
index 22584d2..254f015 100644
--- a/test/api/test-ot-color.c
+++ b/test/api/test-ot-color.c
@@ -99,6 +99,7 @@
 static hb_face_t *cpal_v1 = NULL;
 
 
+#if 0
 #define assert_color_rgba(colors, i, r, g, b, a) G_STMT_START {	\
   const hb_ot_color_t *_colors = (colors); \
   const size_t _i = (i); \
@@ -122,7 +123,6 @@
 } G_STMT_END
 
 
-#if 0
 static void
 test_hb_ot_color_get_palette_count (void)
 {
diff --git a/test/api/test-ot-math.c b/test/api/test-ot-math.c
index d071c88..7f50015 100644
--- a/test/api/test-ot-math.c
+++ b/test/api/test-ot-math.c
@@ -100,10 +100,14 @@
   hb_face = hb_face_get_empty ();
   hb_font = hb_font_create (hb_face);
   g_assert(!hb_ot_math_has_data (hb_face)); // MATH table not available
+  hb_font_destroy (hb_font);
+  hb_face_destroy (hb_face);
 
   hb_font = hb_font_get_empty ();
   hb_face = hb_font_get_face (hb_font);
   g_assert(!hb_ot_math_has_data (hb_face)); // MATH table not available
+  hb_font_destroy (hb_font);
+  hb_face_destroy (hb_face);
 
   cleanupFreeType();
 }
diff --git a/test/api/test-ot-name.c b/test/api/test-ot-name.c
new file mode 100644
index 0000000..2da504a
--- /dev/null
+++ b/test/api/test-ot-name.c
@@ -0,0 +1,102 @@
+/*
+ * 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 const char *font_path = "fonts/cv01.otf";
+static hb_face_t *face;
+
+static void
+test_ot_layout_feature_get_name_ids_and_characters (void)
+{
+  hb_tag_t cv01 = HB_TAG ('c','v','0','1');
+  unsigned int feature_index;
+  if (!hb_ot_layout_language_find_feature (face,
+					   HB_OT_TAG_GSUB,
+					   0,
+					   HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
+					   cv01,
+					   &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))
+    g_error ("Failed to get name ids");
+
+  g_assert_cmpint (label_id, ==, 256);
+  g_assert_cmpint (tooltip_id, ==, 257);
+  g_assert_cmpint (sample_id, ==, 258);
+  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);
+
+  g_assert_cmpint (all_chars, ==, 2);
+  g_assert_cmpint (char_count, ==, 2);
+  g_assert_cmpint (characters[0], ==, 10);
+  g_assert_cmpint (characters[1], ==, 24030);
+}
+
+int
+main (int argc, char **argv)
+{
+  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);
+
+  unsigned int result = hb_test_run ();
+  hb_face_destroy (face);
+  hb_blob_destroy (blob);
+  g_free (default_path);
+  return result;
+}
diff --git a/test/api/test-ot-tag.c b/test/api/test-ot-tag.c
index e821b36..f89c25d 100644
--- a/test/api/test-ot-tag.c
+++ b/test/api/test-ot-tag.c
@@ -37,50 +37,86 @@
 test_simple_tags (const char *s, hb_script_t script)
 {
   hb_script_t tag;
-  hb_tag_t t1, t2;
+  unsigned int count = 2;
+  hb_tag_t t[2];
 
   g_test_message ("Testing script %c%c%c%c: tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s);
   tag = hb_tag_from_string (s, -1);
 
-  hb_ot_tags_from_script (script, &t1, &t2);
+  hb_ot_tags_from_script_and_language (script,
+				       HB_LANGUAGE_INVALID,
+				       &count, t, NULL, NULL);
 
-  g_assert_cmphex (t1, ==, tag);
-  g_assert_cmphex (t2, ==, HB_OT_TAG_DEFAULT_SCRIPT);
+  if (count)
+    g_assert_cmphex (t[0], ==, tag);
+  else
+    g_assert_cmphex (HB_TAG_CHAR4 ("DFLT"), ==, tag);
 
   g_assert_cmphex (hb_ot_tag_to_script (tag), ==, script);
 }
 
 static void
-test_indic_tags (const char *s1, const char *s2, hb_script_t script)
+test_script_tags_from_language (const char *s, const char *lang_s, hb_script_t script)
 {
-  hb_script_t tag1, tag2;
-  hb_script_t t1, t2;
+  hb_script_t tag;
+  unsigned int count = 1;
+  hb_script_t t;
 
-  g_test_message ("Testing script %c%c%c%c: new tag %s, old tag %s", HB_UNTAG (hb_script_to_iso15924_tag (script)), s1, s2);
+  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);
+
+  hb_ot_tags_from_script_and_language (script, hb_language_from_string (lang_s, -1), &count, &t, NULL, NULL);
+
+  if (count != 0)
+  {
+    g_assert_cmpuint (count, ==, 1);
+    g_assert_cmphex (t, ==, tag);
+  }
+}
+
+static void
+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];
+  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);
   tag1 = hb_tag_from_string (s1, -1);
   tag2 = hb_tag_from_string (s2, -1);
+  tag3 = hb_tag_from_string (s3, -1);
 
-  hb_ot_tags_from_script (script, &t1, &t2);
+  hb_ot_tags_from_script_and_language (script,
+				       HB_LANGUAGE_INVALID,
+				       &count, t, NULL, NULL);
 
-  g_assert_cmphex (t1, ==, tag1);
-  g_assert_cmphex (t2, ==, tag2);
+  g_assert_cmpuint (count, ==, 3);
+  g_assert_cmphex (t[0], ==, tag1);
+  g_assert_cmphex (t[1], ==, tag2);
+  g_assert_cmphex (t[2], ==, tag3);
 
   g_assert_cmphex (hb_ot_tag_to_script (tag1), ==, script);
   g_assert_cmphex (hb_ot_tag_to_script (tag2), ==, script);
+  g_assert_cmphex (hb_ot_tag_to_script (tag3), ==, script);
 }
 
 static void
 test_ot_tag_script_degenerate (void)
 {
-  hb_tag_t t1, t2;
+  hb_script_t t[2];
+  unsigned int count = 2;
 
   g_assert_cmphex (HB_TAG_CHAR4 ("DFLT"), ==, HB_OT_TAG_DEFAULT_SCRIPT);
 
   /* HIRAGANA and KATAKANA both map to 'kana' */
   test_simple_tags ("kana", HB_SCRIPT_KATAKANA);
-  hb_ot_tags_from_script (HB_SCRIPT_HIRAGANA, &t1, &t2);
-  g_assert_cmphex (t1, ==, HB_TAG_CHAR4 ("kana"));
-  g_assert_cmphex (t2, ==, HB_OT_TAG_DEFAULT_SCRIPT);
+
+  hb_ot_tags_from_script_and_language (HB_SCRIPT_HIRAGANA,
+				       HB_LANGUAGE_INVALID,
+				       &count, t, NULL, NULL);
+
+  g_assert_cmpuint (count, ==, 1);
+  g_assert_cmphex (t[0], ==, HB_TAG_CHAR4 ("kana"));
 
   test_simple_tags ("DFLT", HB_SCRIPT_INVALID);
 
@@ -121,18 +157,40 @@
 }
 
 static void
+test_ot_tag_script_from_language (void)
+{
+  test_script_tags_from_language (NULL, NULL, HB_SCRIPT_INVALID);
+  test_script_tags_from_language (NULL, "en", HB_SCRIPT_INVALID);
+  test_script_tags_from_language ("copt", "en", HB_SCRIPT_COPTIC);
+  test_script_tags_from_language (NULL, "x-hbsc", HB_SCRIPT_INVALID);
+  test_script_tags_from_language ("copt", "x-hbsc", HB_SCRIPT_COPTIC);
+  test_script_tags_from_language ("abc ", "x-hbscabc", HB_SCRIPT_INVALID);
+  test_script_tags_from_language ("deva", "x-hbscdeva", HB_SCRIPT_INVALID);
+  test_script_tags_from_language ("dev2", "x-hbscdev2", HB_SCRIPT_INVALID);
+  test_script_tags_from_language ("dev3", "x-hbscdev3", HB_SCRIPT_INVALID);
+  test_script_tags_from_language ("copt", "x-hbotpap0-hbsccopt", HB_SCRIPT_INVALID);
+  test_script_tags_from_language (NULL, "en-x-hbsc", HB_SCRIPT_INVALID);
+  test_script_tags_from_language ("copt", "en-x-hbsc", HB_SCRIPT_COPTIC);
+  test_script_tags_from_language ("abc ", "en-x-hbscabc", HB_SCRIPT_INVALID);
+  test_script_tags_from_language ("deva", "en-x-hbscdeva", HB_SCRIPT_INVALID);
+  test_script_tags_from_language ("dev2", "en-x-hbscdev2", HB_SCRIPT_INVALID);
+  test_script_tags_from_language ("dev3", "en-x-hbscdev3", HB_SCRIPT_INVALID);
+  test_script_tags_from_language ("copt", "en-x-hbotpap0-hbsccopt", HB_SCRIPT_INVALID);
+}
+
+static void
 test_ot_tag_script_indic (void)
 {
-  test_indic_tags ("bng2", "beng", HB_SCRIPT_BENGALI);
-  test_indic_tags ("dev2", "deva", HB_SCRIPT_DEVANAGARI);
-  test_indic_tags ("gjr2", "gujr", HB_SCRIPT_GUJARATI);
-  test_indic_tags ("gur2", "guru", HB_SCRIPT_GURMUKHI);
-  test_indic_tags ("knd2", "knda", HB_SCRIPT_KANNADA);
-  test_indic_tags ("mlm2", "mlym", HB_SCRIPT_MALAYALAM);
-  test_indic_tags ("ory2", "orya", HB_SCRIPT_ORIYA);
-  test_indic_tags ("tml2", "taml", HB_SCRIPT_TAMIL);
-  test_indic_tags ("tel2", "telu", HB_SCRIPT_TELUGU);
-  test_indic_tags ("mym2", "mymr", HB_SCRIPT_MYANMAR);
+  test_indic_tags ("bng3", "bng2", "beng", HB_SCRIPT_BENGALI);
+  test_indic_tags ("dev3", "dev2", "deva", HB_SCRIPT_DEVANAGARI);
+  test_indic_tags ("gjr3", "gjr2", "gujr", HB_SCRIPT_GUJARATI);
+  test_indic_tags ("gur3", "gur2", "guru", HB_SCRIPT_GURMUKHI);
+  test_indic_tags ("knd3", "knd2", "knda", HB_SCRIPT_KANNADA);
+  test_indic_tags ("mlm3", "mlm2", "mlym", HB_SCRIPT_MALAYALAM);
+  test_indic_tags ("ory3", "ory2", "orya", HB_SCRIPT_ORIYA);
+  test_indic_tags ("tml3", "tml2", "taml", HB_SCRIPT_TAMIL);
+  test_indic_tags ("tel3", "tel2", "telu", HB_SCRIPT_TELUGU);
+  test_indic_tags ("mym3", "mym2", "mymr", HB_SCRIPT_MYANMAR);
 }
 
 
@@ -147,7 +205,16 @@
 
   g_test_message ("Testing language %s <-> tag %s", lang_s, tag_s);
 
-  g_assert_cmphex (tag, ==, hb_ot_tag_from_language (lang));
+  hb_tag_t tag2;
+  unsigned int count = 1;
+  hb_ot_tags_from_script_and_language (HB_SCRIPT_INVALID,
+				       lang,
+				       NULL, NULL, &count, &tag2);
+
+  if (count)
+    g_assert_cmphex (tag, ==, tag2);
+  else
+    g_assert_cmphex (tag, ==, HB_TAG_CHAR4 ("dflt"));
   g_assert (lang == hb_ot_tag_to_language (tag));
 }
 
@@ -159,7 +226,16 @@
 
   g_test_message ("Testing language %s -> tag %s", lang_s, tag_s);
 
-  g_assert_cmphex (tag, ==, hb_ot_tag_from_language (lang));
+  hb_tag_t tag2;
+  unsigned int count = 1;
+  hb_ot_tags_from_script_and_language (HB_SCRIPT_INVALID,
+				       lang,
+				       NULL, NULL, &count, &tag2);
+
+  if (count)
+    g_assert_cmphex (tag, ==, tag2);
+  else
+    g_assert_cmphex (tag, ==, HB_TAG_CHAR4 ("dflt"));
 }
 
 static void
@@ -174,6 +250,32 @@
 }
 
 static void
+test_tags_to_script_and_language (const char *script_tag_s,
+				  const char *lang_tag_s,
+				  const char *script_s,
+				  const char *lang_s)
+{
+  hb_script_t actual_script[1];
+  hb_language_t actual_lang[1];
+  hb_tag_t script_tag = hb_tag_from_string (script_tag_s, -1);
+  hb_tag_t lang_tag = hb_tag_from_string (lang_tag_s, -1);
+  hb_ot_tags_to_script_and_language (script_tag, lang_tag, actual_script, actual_lang);
+  g_assert_cmphex (*actual_script, ==, hb_tag_from_string (script_s, -1));
+  g_assert_cmpstr (hb_language_to_string (*actual_lang), ==, lang_s);
+}
+
+static void
+test_ot_tags_to_script_and_language (void)
+{
+  test_tags_to_script_and_language ("DFLT", "ENG", "", "en-x-hbscdflt");
+  test_tags_to_script_and_language ("latn", "ENG", "Latn", "en");
+  test_tags_to_script_and_language ("deva", "MAR", "Deva", "mr-x-hbscdeva");
+  test_tags_to_script_and_language ("dev2", "MAR", "Deva", "mr-x-hbscdev2");
+  test_tags_to_script_and_language ("dev3", "MAR", "Deva", "mr");
+  test_tags_to_script_and_language ("qaa", "QTZ0", "Qaaa", "x-hbotqtz0-hbscqaa");
+}
+
+static void
 test_ot_tag_language (void)
 {
   g_assert_cmphex (HB_TAG_CHAR4 ("dflt"), ==, HB_OT_TAG_DEFAULT_LANGUAGE);
@@ -230,27 +332,27 @@
 
   test_language_two_way ("TUA", "tru"); /* Turoyo Aramaic */
 
-  test_language_two_way ("ZHH", "zh-hk"); /* Chinese (Hong Kong) */
-
   test_tag_from_language ("ZHS", "zh"); /* Chinese */
   test_tag_from_language ("ZHS", "zh-cn"); /* Chinese (China) */
   test_tag_from_language ("ZHS", "zh-sg"); /* Chinese (Singapore) */
   test_tag_from_language ("ZHH", "zh-mo"); /* Chinese (Macao) */
   test_tag_from_language ("ZHH", "zh-hant-mo"); /* Chinese (Macao) */
-  test_tag_from_language ("ZHH", "zh-hk"); /* Chinese (Hong Kong) */
+  test_language_two_way ("ZHH", "zh-HK"); /* Chinese (Hong Kong) */
   test_tag_from_language ("ZHH", "zH-HanT-hK"); /* Chinese (Hong Kong) */
   test_tag_from_language ("ZHT", "zh-tw"); /* Chinese (Taiwan) */
-  test_tag_from_language ("ZHS", "zh-Hans"); /* Chinese (Simplified) */
-  test_tag_from_language ("ZHT", "zh-Hant"); /* Chinese (Traditional) */
+  test_language_two_way ("ZHS", "zh-Hans"); /* Chinese (Simplified) */
+  test_language_two_way ("ZHT", "zh-Hant"); /* Chinese (Traditional) */
   test_tag_from_language ("ZHS", "zh-xx"); /* Chinese (Other) */
 
+  test_tag_from_language ("ZHS", "zh-Hans-TW");
+
+  test_tag_from_language ("ZHH", "yue");
+  test_tag_from_language ("ZHH", "yue-Hant");
+  test_tag_from_language ("ZHS", "yue-Hans");
+
   test_tag_from_language ("ZHS", "zh"); /* Chinese */
   test_tag_from_language ("ZHS", "zh-xx");
 
-  test_tag_to_language ("ZHS", "zh-Hans");
-  test_tag_to_language ("ZHT", "zh-Hant");
-  test_tag_to_language ("ZHP", "x-hbotzhp");
-
   test_language_two_way ("ABC", "x-hbotabc");
   test_tag_from_language ("ABC", "asdf-asdf-wer-x-hbotabc-zxc");
   test_tag_from_language ("ABC", "asdf-asdf-wer-x-hbotabc");
@@ -262,39 +364,64 @@
   test_tag_from_language ("XYZ", "xyz"); /* Unknown ISO 639-3 */
   test_tag_from_language ("XYZ", "xyz-qw"); /* Unknown ISO 639-3 */
 
+  /*
+   * Invalid input. The precise answer does not matter, as long as it
+   * does not crash or get into an infinite loop.
+   */
+  test_tag_from_language ("IPPH", "-fonipa");
+
+  /*
+   * Tags that contain "-fonipa" as a substring but which do not contain
+   * the subtag "fonipa".
+   */
+  test_tag_from_language ("ENG", "en-fonipax");
+  test_tag_from_language ("ENG", "en-x-fonipa");
+  test_tag_from_language ("ENG", "en-a-fonipa");
+  test_tag_from_language ("ENG", "en-a-qwe-b-fonipa");
+
   /* International Phonetic Alphabet */
   test_tag_from_language ("IPPH", "en-fonipa");
+  test_tag_from_language ("IPPH", "en-fonipax-fonipa");
   test_tag_from_language ("IPPH", "rm-CH-fonipa-sursilv-x-foobar");
-  test_tag_from_language ("IPPH", "und-fonipa");
+  test_language_two_way ("IPPH", "und-fonipa");
   test_tag_from_language ("IPPH", "zh-fonipa");
-  test_tag_to_language ("IPPH", "und-fonipa");
 
   /* North American Phonetic Alphabet (Americanist Phonetic Notation) */
   test_tag_from_language ("APPH", "en-fonnapa");
   test_tag_from_language ("APPH", "chr-fonnapa");
-  test_tag_from_language ("APPH", "und-fonnapa");
-  test_tag_to_language ("APPH", "und-fonnapa");
+  test_language_two_way ("APPH", "und-fonnapa");
+
+  /* Khutsuri Georgian */
+  test_tag_from_language ("KGE", "ka-Geok");
+  test_language_two_way ("KGE", "und-Geok");
+
+  /* Irish Traditional */
+  test_language_two_way ("IRT", "ga-Latg");
+
+  /* Moldavian */
+  test_language_two_way ("MOL", "ro-MD");
+
+  /* Polytonic Greek */
+  test_language_two_way ("PGR", "el-polyton");
+  test_tag_from_language ("PGR", "el-CY-polyton");
 
   /* Estrangela Syriac */
   test_tag_from_language ("SYRE", "aii-Syre");
   test_tag_from_language ("SYRE", "de-Syre");
   test_tag_from_language ("SYRE", "syr-Syre");
-  test_tag_from_language ("SYRE", "und-Syre");
-  test_tag_to_language ("SYRE", "und-Syre");
+  test_language_two_way ("SYRE", "und-Syre");
 
   /* Western Syriac */
   test_tag_from_language ("SYRJ", "aii-Syrj");
   test_tag_from_language ("SYRJ", "de-Syrj");
   test_tag_from_language ("SYRJ", "syr-Syrj");
-  test_tag_from_language ("SYRJ", "und-Syrj");
-  test_tag_to_language ("SYRJ", "und-Syrj");
+  test_language_two_way ("SYRJ", "und-Syrj");
 
   /* Eastern Syriac */
   test_tag_from_language ("SYRN", "aii-Syrn");
   test_tag_from_language ("SYRN", "de-Syrn");
   test_tag_from_language ("SYRN", "syr-Syrn");
-  test_tag_from_language ("SYRN", "und-Syrn");
-  test_tag_to_language ("SYRN", "und-Syrn");
+  test_language_two_way ("SYRN", "und-Syrn");
 
   /* Test that x-hbot overrides the base language */
   test_tag_from_language ("ABC", "fa-x-hbotabc-zxc");
@@ -303,6 +430,77 @@
   test_tag_from_language ("ABC", "zh-cn-x-hbotabc-zxc");
   test_tag_from_language ("ABC", "zh-xy-x-hbotabc-zxc");
   test_tag_from_language ("ABC", "xyz-xy-x-hbotabc-zxc");
+
+  /* Unnormalized BCP 47 tags */
+  test_tag_from_language ("ARA", "ar-aao");
+  test_tag_from_language ("JBO", "art-lojban");
+  test_tag_from_language ("KOK", "kok-gom");
+  test_tag_from_language ("LTZ", "i-lux");
+  test_tag_from_language ("MNG", "drh");
+  test_tag_from_language ("MOR", "ar-ary");
+  test_tag_from_language ("MOR", "ar-ary-DZ");
+  test_tag_from_language ("NOR", "no-bok");
+  test_tag_from_language ("NYN", "no-nyn");
+  test_tag_from_language ("ZHS", "i-hak");
+  test_tag_from_language ("ZHS", "zh-guoyu");
+  test_tag_from_language ("ZHS", "zh-min");
+  test_tag_from_language ("ZHS", "zh-min-nan");
+  test_tag_from_language ("ZHS", "zh-xiang");
+
+  /* A UN M.49 region code, not an extended language subtag */
+  test_tag_from_language ("ARA", "ar-001");
+}
+
+static void
+test_tags (hb_script_t  script,
+	   const char  *lang_s,
+	   unsigned int script_count,
+	   unsigned int language_count,
+	   unsigned int expected_script_count,
+	   unsigned int expected_language_count,
+	   ...)
+{
+  va_list expected_tags;
+  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));
+  g_assert (script_tags);
+  g_assert (language_tags);
+  hb_language_t 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);
+
+  g_assert_cmpuint (script_count, ==, expected_script_count);
+  g_assert_cmpuint (language_count, ==, expected_language_count);
+
+  for (i = 0; i < script_count + language_count; i++)
+  {
+    hb_tag_t expected_tag = hb_tag_from_string (va_arg (expected_tags, const char *), -1);
+    hb_tag_t actual_tag = i < script_count ? script_tags[i] : language_tags[i - script_count];
+    g_assert_cmphex (actual_tag, ==, expected_tag);
+  }
+
+  free (script_tags);
+  free (language_tags);
+  va_end (expected_tags);
+}
+
+static void
+test_ot_tag_full (void)
+{
+  test_tags (HB_SCRIPT_INVALID, "en", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "ENG");
+  test_tags (HB_SCRIPT_INVALID, "en-x-hbscdflt", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "DFLT", "ENG");
+  test_tags (HB_SCRIPT_LATIN, "en", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "latn", "ENG");
+  test_tags (HB_SCRIPT_LATIN, "en", 0, 0, 0, 0);
+  test_tags (HB_SCRIPT_INVALID, "und-fonnapa", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "APPH");
+  test_tags (HB_SCRIPT_INVALID, "en-fonnapa", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "APPH");
+  test_tags (HB_SCRIPT_INVALID, "x-hbot1234-hbsc5678", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "5678", "1234");
+  test_tags (HB_SCRIPT_INVALID, "x-hbsc5678-hbot1234", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 1, 1, "5678", "1234");
+  test_tags (HB_SCRIPT_MALAYALAM, "ml", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 3, 2, "mlm3", "mlm2", "mlym", "MAL", "MLR");
+  test_tags (HB_SCRIPT_MALAYALAM, "ml", 1, 1, 1, 1, "mlm3", "MAL");
+  test_tags (HB_SCRIPT_INVALID, "xyz", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 1, "XYZ");
+  test_tags (HB_SCRIPT_INVALID, "xy", HB_OT_MAX_TAGS_PER_SCRIPT, HB_OT_MAX_TAGS_PER_LANGUAGE, 0, 0);
 }
 
 int
@@ -312,9 +510,14 @@
 
   hb_test_add (test_ot_tag_script_degenerate);
   hb_test_add (test_ot_tag_script_simple);
+  hb_test_add (test_ot_tag_script_from_language);
   hb_test_add (test_ot_tag_script_indic);
 
+  hb_test_add (test_ot_tags_to_script_and_language);
+
   hb_test_add (test_ot_tag_language);
 
+  hb_test_add (test_ot_tag_full);
+
   return hb_test_run();
 }
diff --git a/test/api/test-set.c b/test/api/test-set.c
index 338a610..aa2b388 100644
--- a/test/api/test-set.c
+++ b/test/api/test-set.c
@@ -118,6 +118,9 @@
   g_assert (!hb_set_has (s, 801));
   g_assert (!hb_set_has (s, 802));
 
+  hb_set_del (s, 800);
+  g_assert (!hb_set_has (s, 800));
+
   hb_set_destroy (s);
 }
 
@@ -262,6 +265,7 @@
 
   hb_set_destroy (s);
   hb_set_destroy (o);
+  hb_set_destroy (o2);
 }
 
 static void
@@ -380,10 +384,6 @@
 
   test_empty (b);
 
-  hb_set_invert (b);
-
-  test_empty (b);
-
   g_assert (!hb_set_allocation_successful (b));
 
   hb_set_clear (b);
diff --git a/test/api/test-shape.c b/test/api/test-shape.c
index 6232e73..146cf0f 100644
--- a/test/api/test-shape.c
+++ b/test/api/test-shape.c
@@ -41,9 +41,9 @@
 static const char test_data[] = "test\0data";
 
 static hb_position_t
-glyph_h_advance_func (hb_font_t *font, void *font_data,
+glyph_h_advance_func (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED,
 		      hb_codepoint_t glyph,
-		      void *user_data)
+		      void *user_data HB_UNUSED)
 {
   switch (glyph) {
   case 1: return 10;
@@ -54,10 +54,10 @@
 }
 
 static hb_bool_t
-glyph_func (hb_font_t *font, void *font_data,
-	    hb_codepoint_t unicode, hb_codepoint_t variant_selector,
+glyph_func (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED,
+	    hb_codepoint_t unicode,
 	    hb_codepoint_t *glyph,
-	    void *user_data)
+	    void *user_data HB_UNUSED)
 {
   switch (unicode) {
   case 'T': *glyph = 1; return TRUE;
@@ -68,9 +68,9 @@
 }
 
 static hb_position_t
-glyph_h_kerning_func (hb_font_t *font, void *font_data,
+glyph_h_kerning_func (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED,
 		      hb_codepoint_t left, hb_codepoint_t right,
-		      void *user_data)
+		      void *user_data HB_UNUSED)
 {
   if (left == 1 && right == 2)
     return -2;
@@ -101,7 +101,7 @@
 
   ffuncs = hb_font_funcs_create ();
   hb_font_funcs_set_glyph_h_advance_func (ffuncs, glyph_h_advance_func, NULL, NULL);
-  hb_font_funcs_set_glyph_func (ffuncs, glyph_func, malloc (10), free);
+  hb_font_funcs_set_nominal_glyph_func (ffuncs, glyph_func, malloc (10), free);
   hb_font_funcs_set_glyph_h_kerning_func (ffuncs, glyph_h_kerning_func, NULL, NULL);
   hb_font_set_funcs (font, ffuncs, NULL, NULL);
   hb_font_funcs_destroy (ffuncs);
diff --git a/test/api/test-subset-glyf.c b/test/api/test-subset-glyf.c
index e4440e0..05c7f8c 100644
--- a/test/api/test-subset-glyf.c
+++ b/test/api/test-subset-glyf.c
@@ -105,16 +105,18 @@
 {
   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_subset_input_t *input;
+  hb_face_t *face_subset;
 
   hb_set_t *codepoints = hb_set_create();
   hb_set_add (codepoints, 102); // f
   hb_set_add (codepoints, 105); // i
 
-  hb_subset_input_t *input = hb_subset_test_create_input (codepoints);
+  input = hb_subset_test_create_input (codepoints);
   hb_set_destroy (codepoints);
-  *hb_subset_input_drop_ot_layout (input) = false;
+  hb_subset_input_set_drop_layout (input, false);
 
-  hb_face_t *face_subset = hb_subset_test_create_subset (face_fil, input);
+  face_subset = hb_subset_test_create_subset (face_fil, input);
 
   hb_subset_test_check (face_fi, face_subset, HB_TAG ('g','l','y','f'));
   hb_subset_test_check (face_fi, face_subset, HB_TAG ('l','o','c', 'a'));
@@ -130,16 +132,18 @@
 {
   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_subset_input_t *input;
+  hb_face_t *face_subset;
 
   hb_set_t *codepoints = hb_set_create();
   hb_set_add (codepoints, 102); // f
   hb_set_add (codepoints, 105); // i
 
-  hb_subset_input_t *input = hb_subset_test_create_input (codepoints);
+  input = hb_subset_test_create_input (codepoints);
   hb_set_destroy (codepoints);
-  *hb_subset_input_drop_ot_layout (input) = true;
+  hb_subset_input_set_drop_layout (input, true);
 
-  hb_face_t *face_subset = hb_subset_test_create_subset (face_fil, input);
+  face_subset = hb_subset_test_create_subset (face_fil, input);
 
   hb_subset_test_check (face_fi, face_subset, HB_TAG ('g','l','y','f'));
   hb_subset_test_check (face_fi, face_subset, HB_TAG ('l','o','c', 'a'));
@@ -183,7 +187,7 @@
   hb_set_add (codepoints, 'a');
   hb_set_add (codepoints, 'c');
   input = hb_subset_test_create_input (codepoints);
-  *hb_subset_input_drop_hints(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);
 
@@ -207,7 +211,7 @@
   hb_face_t *face_generated_subset;
   hb_set_add (codepoints, 0x1fc);
   input = hb_subset_test_create_input (codepoints);
-  *hb_subset_input_drop_hints(input) = true;
+  hb_subset_input_set_drop_hints (input, true);
 
   face_generated_subset = hb_subset_test_create_subset (face_components, input);
   hb_set_destroy (codepoints);
@@ -224,7 +228,7 @@
 static void
 test_subset_glyf_strip_hints_invalid (void)
 {
-  hb_face_t *face = hb_subset_test_open_font ("fonts/oom-ccc61c92d589f895174cdef6ff2e3b20e9999a1a");
+  hb_face_t *face = hb_subset_test_open_font ("../fuzzing/fonts/oom-ccc61c92d589f895174cdef6ff2e3b20e9999a1a");
 
   hb_set_t *codepoints = hb_set_create();
   const hb_codepoint_t text[] =
@@ -233,16 +237,19 @@
     '3', '@', '_', '%', '&', ')', '*', '$', '!'
   };
   unsigned int i;
+  hb_subset_input_t *input;
+  hb_face_t *face_subset;
+
   for (i = 0; i < sizeof (text) / sizeof (hb_codepoint_t); i++)
   {
     hb_set_add (codepoints, text[i]);
   }
 
-  hb_subset_input_t *input = hb_subset_test_create_input (codepoints);
-  *hb_subset_input_drop_hints(input) = true;
+  input = hb_subset_test_create_input (codepoints);
+  hb_subset_input_set_drop_hints (input, true);
   hb_set_destroy (codepoints);
 
-  hb_face_t *face_subset = hb_subset_test_create_subset (face, input);
+  face_subset = hb_subset_test_create_subset (face, input);
   g_assert (face_subset);
   g_assert (face_subset == hb_face_get_empty ());
 
diff --git a/test/api/test-subset-hdmx.c b/test/api/test-subset-hdmx.c
index c78009b..8496f9e 100644
--- a/test/api/test-subset-hdmx.c
+++ b/test/api/test-subset-hdmx.c
@@ -51,23 +51,42 @@
 }
 
 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_set_t *codepoints = hb_set_create ();
+  hb_face_t *face_abc_subset;
+  hb_set_add (codepoints, 'a');
+  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_a, face_abc_subset, HB_TAG ('h','d','m','x'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_a);
+}
+
+static void
 test_subset_hdmx_invalid (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("fonts/crash-ccc61c92d589f895174cdef6ff2e3b20e9999a1a");
+  hb_face_t *face = hb_subset_test_open_font("../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);
+  hb_face_t *subset;
+
   hb_set_add (codepoints, 'a');
   hb_set_add (codepoints, 'b');
   hb_set_add (codepoints, 'c');
 
-  hb_subset_profile_t *profile = hb_subset_profile_create();
-  hb_face_t *subset = hb_subset (face, profile, input);
+  subset = hb_subset (face, input);
   g_assert (subset);
   g_assert (subset == hb_face_get_empty ());
 
   hb_subset_input_destroy (input);
-  hb_subset_profile_destroy (profile);
   hb_face_destroy (subset);
   hb_face_destroy (face);
 }
@@ -75,21 +94,21 @@
 static void
 test_subset_hdmx_fails_sanitize (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5609911946838016");
+  hb_face_t *face = hb_subset_test_open_font("../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);
+  hb_face_t *subset;
+
   hb_set_add (codepoints, 'a');
   hb_set_add (codepoints, 'b');
   hb_set_add (codepoints, 'c');
 
-  hb_subset_profile_t *profile = hb_subset_profile_create();
-  hb_face_t *subset = hb_subset (face, profile, input);
+  subset = hb_subset (face, input);
   g_assert (subset);
   g_assert (subset == hb_face_get_empty ());
 
   hb_subset_input_destroy (input);
-  hb_subset_profile_destroy (profile);
   hb_face_destroy (subset);
   hb_face_destroy (face);
 }
@@ -119,6 +138,7 @@
   hb_test_init (&argc, &argv);
 
   hb_test_add (test_subset_hdmx_simple_subset);
+  hb_test_add (test_subset_hdmx_multiple_device_records);
   hb_test_add (test_subset_hdmx_invalid);
   hb_test_add (test_subset_hdmx_fails_sanitize);
   hb_test_add (test_subset_hdmx_noop);
diff --git a/test/api/test-subset-hmtx.c b/test/api/test-subset-hmtx.c
index 0ed6256..1a5a44d 100644
--- a/test/api/test-subset-hmtx.c
+++ b/test/api/test-subset-hmtx.c
@@ -153,7 +153,8 @@
 static void
 test_subset_invalid_hmtx (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("fonts/crash-e4e0bb1458a91b692eba492c907ae1f94e635480");
+  hb_face_t *face = hb_subset_test_open_font("../fuzzing/fonts/crash-e4e0bb1458a91b692eba492c907ae1f94e635480");
+  hb_face_t *subset;
 
   hb_subset_input_t *input = hb_subset_input_create_or_fail ();
   hb_set_t *codepoints = hb_subset_input_unicode_set (input);
@@ -161,13 +162,11 @@
   hb_set_add (codepoints, 'b');
   hb_set_add (codepoints, 'c');
 
-  hb_subset_profile_t *profile = hb_subset_profile_create();
-  hb_face_t *subset = hb_subset (face, profile, input);
+  subset = hb_subset (face, input);
   g_assert (subset);
   g_assert (subset == hb_face_get_empty ());
 
   hb_subset_input_destroy (input);
-  hb_subset_profile_destroy (profile);
   hb_face_destroy (subset);
   hb_face_destroy (face);
 }
diff --git a/test/api/test-subset-post.c b/test/api/test-subset-post.c
index 948b18a..c14741e 100644
--- a/test/api/test-subset-post.c
+++ b/test/api/test-subset-post.c
@@ -34,11 +34,12 @@
 {
   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_subset;
 
   hb_set_t *codepoints = hb_set_create ();
   hb_set_add (codepoints, 0x660E);
 
-  hb_face_t *face_full_subset = hb_subset_test_create_subset (face_full, hb_subset_test_create_input (codepoints));
+  face_full_subset = hb_subset_test_create_subset (face_full, hb_subset_test_create_input (codepoints));
   hb_set_destroy (codepoints);
 
   hb_subset_test_check (face_subset, face_full_subset, HB_TAG ('p','o','s','t'));
diff --git a/test/api/test-subset-vmtx.c b/test/api/test-subset-vmtx.c
index 437f0c2..40ea8f8 100644
--- a/test/api/test-subset-vmtx.c
+++ b/test/api/test-subset-vmtx.c
@@ -48,11 +48,12 @@
 {
   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_subset;
 
   hb_set_t *codepoints = hb_set_create ();
   hb_set_add (codepoints, 0x660E);
 
-  hb_face_t *face_full_subset = hb_subset_test_create_subset (face_full, hb_subset_test_create_input (codepoints));
+  face_full_subset = hb_subset_test_create_subset (face_full, hb_subset_test_create_input (codepoints));
   hb_set_destroy (codepoints);
 
   check_num_vmetrics(face_full_subset, 1); /* nothing has same width */
@@ -67,6 +68,7 @@
 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_subset;
 
   hb_set_t *codepoints = hb_set_create();
   hb_set_add (codepoints, 0x660E);
@@ -75,7 +77,7 @@
   hb_set_add (codepoints, 0x5EA6);
   hb_set_add (codepoints, 0x8F38);
   hb_set_add (codepoints, 0x6E05);
-  hb_face_t *face_full_subset = hb_subset_test_create_subset (face_full, hb_subset_test_create_input (codepoints));
+  face_full_subset = hb_subset_test_create_subset (face_full, hb_subset_test_create_input (codepoints));
   hb_set_destroy (codepoints);
 
   check_num_vmetrics(face_full_subset, 1); /* all have the same width */
diff --git a/test/api/test-subset.c b/test/api/test-subset.c
index 6d2bf06..aaed031 100644
--- a/test/api/test-subset.c
+++ b/test/api/test-subset.c
@@ -32,21 +32,21 @@
 static void
 test_subset_32_tables (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("fonts/oom-6ef8c96d3710262511bcc730dce9c00e722cb653");
+  hb_face_t *face = hb_subset_test_open_font("../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);
+  hb_face_t *subset;
+
   hb_set_add (codepoints, 'a');
   hb_set_add (codepoints, 'b');
   hb_set_add (codepoints, 'c');
 
-  hb_subset_profile_t *profile = hb_subset_profile_create();
-  hb_face_t *subset = hb_subset (face, profile, input);
+  subset = hb_subset (face, input);
   g_assert (subset);
   g_assert (subset != hb_face_get_empty ());
 
   hb_subset_input_destroy (input);
-  hb_subset_profile_destroy (profile);
   hb_face_destroy (subset);
   hb_face_destroy (face);
 }
@@ -54,21 +54,21 @@
 static void
 test_subset_no_inf_loop (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5521982557782016");
+  hb_face_t *face = hb_subset_test_open_font("../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);
+  hb_face_t *subset;
+
   hb_set_add (codepoints, 'a');
   hb_set_add (codepoints, 'b');
   hb_set_add (codepoints, 'c');
 
-  hb_subset_profile_t *profile = hb_subset_profile_create();
-  hb_face_t *subset = hb_subset (face, profile, input);
+  subset = hb_subset (face, input);
   g_assert (subset);
   g_assert (subset == hb_face_get_empty ());
 
   hb_subset_input_destroy (input);
-  hb_subset_profile_destroy (profile);
   hb_face_destroy (subset);
   hb_face_destroy (face);
 }
@@ -76,21 +76,21 @@
 static void
 test_subset_crash (void)
 {
-  hb_face_t *face = hb_subset_test_open_font("fonts/crash-4b60576767ee4d9fe1cc10959d89baf73d4e8249");
+  hb_face_t *face = hb_subset_test_open_font("../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);
+  hb_face_t *subset;
+
   hb_set_add (codepoints, 'a');
   hb_set_add (codepoints, 'b');
   hb_set_add (codepoints, 'c');
 
-  hb_subset_profile_t *profile = hb_subset_profile_create();
-  hb_face_t *subset = hb_subset (face, profile, input);
+  subset = hb_subset (face, input);
   g_assert (subset);
   g_assert (subset == hb_face_get_empty ());
 
   hb_subset_input_destroy (input);
-  hb_subset_profile_destroy (profile);
   hb_face_destroy (subset);
   hb_face_destroy (face);
 }
diff --git a/test/api/test-unicode.c b/test/api/test-unicode.c
index 88f12e7..6195bb2 100644
--- a/test/api/test-unicode.c
+++ b/test/api/test-unicode.c
@@ -160,69 +160,6 @@
   { 0x111111, 0 }
 };
 
-static const test_pair_t eastasian_width_tests[] =
-{
-  /* Neutral */
-  {   0x0000, 1 },
-  {   0x0483, 1 },
-  {   0x0641, 1 },
-  {   0xFFFC, 1 },
-  {  0x10000, 1 },
-  {  0xE0001, 1 },
-
-  /* Narrow */
-  {   0x0020, 1 },
-  {   0x0041, 1 },
-  {   0x27E6, 1 },
-
-  /* Halfwidth */
-  {   0x20A9, 1 },
-  {   0xFF61, 1 },
-  {   0xFF69, 1 },
-  {   0xFFEE, 1 },
-
-  /* Ambiguous */
-  {   0x00A1, 1 },
-  {   0x00D8, 1 },
-  {   0x02DD, 1 },
-  {  0xE0100, 1 },
-  { 0x100000, 1 },
-
-  /* Fullwidth */
-  {   0x3000, 2 },
-  {   0xFF60, 2 },
-
-  /* Wide */
-  {   0x2329, 2 },
-  {   0x3001, 2 },
-  {   0xFE69, 2 },
-  {  0x30000, 2 },
-  {  0x3FFFD, 2 },
-
-  { 0x111111, 1 }
-};
-static const test_pair_t eastasian_width_tests_more[] =
-{
-  /* Default Wide blocks */
-  {   0x4DBF, 2 },
-  {   0x9FFF, 2 },
-  {   0xFAFF, 2 },
-  {  0x2A6DF, 2 },
-  {  0x2B73F, 2 },
-  {  0x2B81F, 2 },
-  {  0x2FA1F, 2 },
-
-  /* Uniode-5.2 character additions */
-  /* Wide */
-  {   0x115F, 2 },
-
-  /* Uniode-6.0 character additions */
-  /* Wide */
-  {  0x2B740, 2 },
-  {  0x1B000, 2 },
-
-  { 0x111111, 1 }
-};
 
 static const test_pair_t general_category_tests[] =
 {
@@ -469,7 +406,6 @@
 static const property_t properties[] =
 {
   PROPERTY (combining_class, 0),
-  PROPERTY (eastasian_width, 1),
   PROPERTY (general_category, (unsigned int) HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER),
   PROPERTY (mirroring, RETURNS_UNICODE_ITSELF),
   PROPERTY (script, (unsigned int) HB_SCRIPT_UNKNOWN)
@@ -645,18 +581,18 @@
 } data_fixture_t;
 
 static void
-data_fixture_init (data_fixture_t *f, gconstpointer user_data)
+data_fixture_init (data_fixture_t *f, gconstpointer user_data HB_UNUSED)
 {
   f->data[0].value = MAGIC0;
   f->data[1].value = MAGIC1;
 }
 static void
-data_fixture_finish (data_fixture_t *f, gconstpointer user_data)
+data_fixture_finish (data_fixture_t *f HB_UNUSED, gconstpointer user_data HB_UNUSED)
 {
 }
 
 static void
-test_unicode_subclassing_nil (data_fixture_t *f, gconstpointer user_data)
+test_unicode_subclassing_nil (data_fixture_t *f, gconstpointer user_data HB_UNUSED)
 {
   hb_unicode_funcs_t *uf, *aa;
 
@@ -678,7 +614,7 @@
 }
 
 static void
-test_unicode_subclassing_default (data_fixture_t *f, gconstpointer user_data)
+test_unicode_subclassing_default (data_fixture_t *f, gconstpointer user_data HB_UNUSED)
 {
   hb_unicode_funcs_t *uf, *aa;
 
@@ -697,7 +633,7 @@
 }
 
 static void
-test_unicode_subclassing_deep (data_fixture_t *f, gconstpointer user_data)
+test_unicode_subclassing_deep (data_fixture_t *f, gconstpointer user_data HB_UNUSED)
 {
   hb_unicode_funcs_t *uf, *aa;
 
@@ -786,7 +722,6 @@
 {
   hb_unicode_funcs_t *uf = (hb_unicode_funcs_t *) user_data;
   gunichar a, b, ab;
-  hb_codepoint_t decomposed[HB_UNICODE_MAX_DECOMPOSITION_LEN];
 
 
   /* Test compose() */
@@ -849,56 +784,6 @@
   g_assert (hb_unicode_decompose (uf, 0xD4CC, &a, &b) && a == 0x1111 && b == 0x1171);
   g_assert (hb_unicode_decompose (uf, 0xCE31, &a, &b) && a == 0xCE20 && b == 0x11B8);
   g_assert (hb_unicode_decompose (uf, 0xCE20, &a, &b) && a == 0x110E && b == 0x1173);
-
-
-  /* Test decompose_compatibility() */
-
-  /* Not decomposable */
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x0041, decomposed) == 0);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x1F632, decomposed) == 0);
-
-  /* Singletons */
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x00B5, decomposed) == 1 && decomposed[0] == 0x03BC);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x03D6, decomposed) == 1 && decomposed[0] == 0x03C0);
-
-  /* Arabic compatibility */
-  g_assert (hb_unicode_decompose_compatibility (uf, 0xFB54, decomposed) == 1 && decomposed[0] == 0x067B);
-
-  /* Longest decomposition ever */
-  g_assert (18 <= HB_UNICODE_MAX_DECOMPOSITION_LEN);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0xFDFA, decomposed) == 18 && decomposed[17] == 0x0645);
-
-  /* Note: we deliberately don't test characters that have canonical decompositions but no
-   * compatibility decomposition against the decompose_compatibility() function as that we
-   * leave up to implementations (for now). */
-
-  /* Spaces */
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2002, decomposed) == 1 && decomposed[0] == 0x0020);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2003, decomposed) == 1 && decomposed[0] == 0x0020);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2004, decomposed) == 1 && decomposed[0] == 0x0020);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2005, decomposed) == 1 && decomposed[0] == 0x0020);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2006, decomposed) == 1 && decomposed[0] == 0x0020);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2008, decomposed) == 1 && decomposed[0] == 0x0020);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2009, decomposed) == 1 && decomposed[0] == 0x0020);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x200A, decomposed) == 1 && decomposed[0] == 0x0020);
-
-  /* Pairs */
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x0587, decomposed) == 2 &&
-            decomposed[0] == 0x0565 && decomposed[1] == 0x0582);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2017, decomposed) == 2 &&
-            decomposed[0] == 0x0020 && decomposed[1] == 0x0333);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2025, decomposed) == 2 &&
-            decomposed[0] == 0x002E && decomposed[1] == 0x002E);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2033, decomposed) == 2 &&
-            decomposed[0] == 0x2032 && decomposed[1] == 0x2032);
-
-  /* Triples */
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2026, decomposed) == 3 &&
-            decomposed[0] == 0x002E && decomposed[1] == 0x002E && decomposed[2] == 0x002E);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x2034, decomposed) == 3 &&
-            decomposed[0] == 0x2032 && decomposed[1] == 0x2032 && decomposed[2] == 0x2032);
-  g_assert (hb_unicode_decompose_compatibility (uf, 0x213B, decomposed) == 3 &&
-            decomposed[0] == 0x0046 && decomposed[1] == 0x0041 && decomposed[2] == 0x0058);
 }
 
 
diff --git a/test/fuzzing/CMakeLists.txt b/test/fuzzing/CMakeLists.txt
index 2a45ef6..577d13c 100644
--- a/test/fuzzing/CMakeLists.txt
+++ b/test/fuzzing/CMakeLists.txt
@@ -2,7 +2,6 @@
   file (READ "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.am" MAKEFILEAM)
   extract_make_variable (hb_shape_fuzzer_SOURCES ${MAKEFILEAM})
   extract_make_variable (hb_subset_fuzzer_SOURCES ${MAKEFILEAM})
-  extract_make_variable (hb_subset_get_codepoints_fuzzer_SOURCES ${MAKEFILEAM})
 
   # TODO: enable these two
   #extract_make_variable (FUZZING_CPPFLAGS ${MAKEFILEAM}) # extracting regex fail
@@ -16,17 +15,13 @@
   add_executable (hb-subset-fuzzer ${hb_subset_fuzzer_SOURCES})
   target_link_libraries (hb-subset-fuzzer harfbuzz-subset)
 
-  add_executable (hb-subset-get-codepoints-fuzzer ${hb_subset_get_codepoints_fuzzer_SOURCES})
-  target_link_libraries (hb-subset-get-codepoints-fuzzer harfbuzz-subset)
-
   target_compile_definitions(hb-shape-fuzzer PUBLIC ${FUZZING_CPPFLAGS})
   target_compile_definitions(hb-subset-fuzzer PUBLIC ${FUZZING_CPPFLAGS})
-  target_compile_definitions(hb-subset-get-codepoints-fuzzer PUBLIC ${FUZZING_CPPFLAGS})
 
   add_test (NAME hb-shape-fuzzer
     COMMAND "${PYTHON_EXECUTABLE}" run-shape-fuzzer-tests.py $<TARGET_FILE:hb-shape-fuzzer>
     WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
   add_test (NAME hb-subset-fuzzer
-    COMMAND "${PYTHON_EXECUTABLE}" run-subset-fuzzer-tests.py $<TARGET_FILE:hb-subset-fuzzer> $<TARGET_FILE:hb-subset-get-codepoints-fuzzer>
+    COMMAND "${PYTHON_EXECUTABLE}" run-subset-fuzzer-tests.py $<TARGET_FILE:hb-subset-fuzzer>
     WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
 endif ()
diff --git a/test/fuzzing/Makefile.am b/test/fuzzing/Makefile.am
index 5af5a73..2506088 100644
--- a/test/fuzzing/Makefile.am
+++ b/test/fuzzing/Makefile.am
@@ -18,12 +18,12 @@
 	run-shape-fuzzer-tests.py \
 	run-subset-fuzzer-tests.py \
 	CMakeLists.txt \
+	fonts \
 	$(NULL)
 
 check_PROGRAMS = \
 	hb-shape-fuzzer \
 	hb-subset-fuzzer \
-	hb-subset-get-codepoints-fuzzer \
 	$(NULL)
 
 AM_CPPFLAGS = \
@@ -65,21 +65,6 @@
 	lib \
 	$(NULL)
 
-hb_subset_get_codepoints_fuzzer_SOURCES = \
-	hb-fuzzer.hh \
-	hb-subset-get-codepoints-fuzzer.cc \
-	main.cc \
-	$(NULL)
-hb_subset_get_codepoints_fuzzer_LDADD = \
-	$(top_builddir)/src/libharfbuzz-subset-fuzzing.la \
-	$(NULL)
-hb_subset_get_codepoints_fuzzer_CPPFLAGS = \
-	$(AM_CPPFLAGS) \
-	$(NULL)
-hb_subset_get_codepoints_fuzzer_DEPENDENCIES = \
-	lib \
-	$(NULL)
-
 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
diff --git a/test/shaping/data/in-house/fonts/0509e80afb379d16560e9e47bdd7d888bebdebc6.ttf b/test/fuzzing/fonts/0509e80afb379d16560e9e47bdd7d888bebdebc6
similarity index 100%
rename from test/shaping/data/in-house/fonts/0509e80afb379d16560e9e47bdd7d888bebdebc6.ttf
rename to test/fuzzing/fonts/0509e80afb379d16560e9e47bdd7d888bebdebc6
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/1a6f1687b7a221f9f2c834b0b360d3c8463b6daf.ttf b/test/fuzzing/fonts/1a6f1687b7a221f9f2c834b0b360d3c8463b6daf
similarity index 100%
rename from test/shaping/data/in-house/fonts/1a6f1687b7a221f9f2c834b0b360d3c8463b6daf.ttf
rename to test/fuzzing/fonts/1a6f1687b7a221f9f2c834b0b360d3c8463b6daf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/205edd09bd3d141cc9580f650109556cc28b22cb.ttf b/test/fuzzing/fonts/205edd09bd3d141cc9580f650109556cc28b22cb
similarity index 100%
rename from test/shaping/data/in-house/fonts/205edd09bd3d141cc9580f650109556cc28b22cb.ttf
rename to test/fuzzing/fonts/205edd09bd3d141cc9580f650109556cc28b22cb
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/217a934cfe15c548b572c203dceb2befdf026462.ttf b/test/fuzzing/fonts/217a934cfe15c548b572c203dceb2befdf026462
similarity index 100%
rename from test/shaping/data/in-house/fonts/217a934cfe15c548b572c203dceb2befdf026462.ttf
rename to test/fuzzing/fonts/217a934cfe15c548b572c203dceb2befdf026462
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/3511ff5c1647150595846ac414c595cccac34f18.ttf b/test/fuzzing/fonts/3511ff5c1647150595846ac414c595cccac34f18
similarity index 100%
rename from test/shaping/data/in-house/fonts/3511ff5c1647150595846ac414c595cccac34f18.ttf
rename to test/fuzzing/fonts/3511ff5c1647150595846ac414c595cccac34f18
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/375d6ae32a3cbe52fbf81a4e5777e3377675d5a3.ttf b/test/fuzzing/fonts/375d6ae32a3cbe52fbf81a4e5777e3377675d5a3
similarity index 100%
rename from test/shaping/data/in-house/fonts/375d6ae32a3cbe52fbf81a4e5777e3377675d5a3.ttf
rename to test/fuzzing/fonts/375d6ae32a3cbe52fbf81a4e5777e3377675d5a3
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/43979b90b2dd929723cf4fe1715990bcb9c9a56b.ttf b/test/fuzzing/fonts/43979b90b2dd929723cf4fe1715990bcb9c9a56b
similarity index 100%
rename from test/shaping/data/in-house/fonts/43979b90b2dd929723cf4fe1715990bcb9c9a56b.ttf
rename to test/fuzzing/fonts/43979b90b2dd929723cf4fe1715990bcb9c9a56b
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/558661aa659912f4d30ecd27bd09835171a8e2b0.ttf b/test/fuzzing/fonts/558661aa659912f4d30ecd27bd09835171a8e2b0
similarity index 100%
rename from test/shaping/data/in-house/fonts/558661aa659912f4d30ecd27bd09835171a8e2b0.ttf
rename to test/fuzzing/fonts/558661aa659912f4d30ecd27bd09835171a8e2b0
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/5a5daf5eb5a4db77a2baa3ad9c7a6ed6e0655fa8.ttf b/test/fuzzing/fonts/5a5daf5eb5a4db77a2baa3ad9c7a6ed6e0655fa8
similarity index 100%
rename from test/shaping/data/in-house/fonts/5a5daf5eb5a4db77a2baa3ad9c7a6ed6e0655fa8.ttf
rename to test/fuzzing/fonts/5a5daf5eb5a4db77a2baa3ad9c7a6ed6e0655fa8
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/641bd9db850193064d17575053ae2bf8ec149ddc.ttf b/test/fuzzing/fonts/641bd9db850193064d17575053ae2bf8ec149ddc
similarity index 100%
rename from test/shaping/data/in-house/fonts/641bd9db850193064d17575053ae2bf8ec149ddc.ttf
rename to test/fuzzing/fonts/641bd9db850193064d17575053ae2bf8ec149ddc
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/8240789f6d12d4cfc4b5e8e6f246c3701bcf861f.ttf b/test/fuzzing/fonts/8240789f6d12d4cfc4b5e8e6f246c3701bcf861f
similarity index 100%
rename from test/shaping/data/in-house/fonts/8240789f6d12d4cfc4b5e8e6f246c3701bcf861f.ttf
rename to test/fuzzing/fonts/8240789f6d12d4cfc4b5e8e6f246c3701bcf861f
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/a34a9191d9376bda419836effeef7e75c1386016.ttf b/test/fuzzing/fonts/a34a9191d9376bda419836effeef7e75c1386016
similarity index 100%
rename from test/shaping/data/in-house/fonts/a34a9191d9376bda419836effeef7e75c1386016.ttf
rename to test/fuzzing/fonts/a34a9191d9376bda419836effeef7e75c1386016
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/a69118c2c2ada48ff803d9149daa54c9ebdae30e.ttf b/test/fuzzing/fonts/a69118c2c2ada48ff803d9149daa54c9ebdae30e
similarity index 100%
rename from test/shaping/data/in-house/fonts/a69118c2c2ada48ff803d9149daa54c9ebdae30e.ttf
rename to test/fuzzing/fonts/a69118c2c2ada48ff803d9149daa54c9ebdae30e
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/b9e2aaa0d75fcef6971ec3a96d806ba4a6b31fe2.ttf b/test/fuzzing/fonts/b9e2aaa0d75fcef6971ec3a96d806ba4a6b31fe2
similarity index 100%
rename from test/shaping/data/in-house/fonts/b9e2aaa0d75fcef6971ec3a96d806ba4a6b31fe2.ttf
rename to test/fuzzing/fonts/b9e2aaa0d75fcef6971ec3a96d806ba4a6b31fe2
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-5517117891805184 b/test/fuzzing/fonts/clusterfuzz-testcase-5517117891805184
new file mode 100644
index 0000000..0c7b518
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-5517117891805184
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-6107935408390144 b/test/fuzzing/fonts/clusterfuzz-testcase-6107935408390144
new file mode 100644
index 0000000..4c81a86
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-6107935408390144
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/ef2511f215aa3ca847cbfffbf861793b42170875.ttf b/test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-4666056377368576
similarity index 100%
rename from test/shaping/data/in-house/fonts/ef2511f215aa3ca847cbfffbf861793b42170875.ttf
rename to test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-4666056377368576
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-5662671558934528 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-5662671558934528
new file mode 100644
index 0000000..cbb81ac
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-5662671558934528
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-6243458541944832 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-6243458541944832
new file mode 100644
index 0000000..64864aa
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-6243458541944832
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/9d8a94a67932a3ab75a596fc8b5c6d0392ca9e49.ttf b/test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-6303297511096320
similarity index 100%
rename from test/shaping/data/in-house/fonts/9d8a94a67932a3ab75a596fc8b5c6d0392ca9e49.ttf
rename to test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-6303297511096320
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-6696647723581440 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-6696647723581440
new file mode 100644
index 0000000..8b1c293
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-fuzzer-6696647723581440
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5634395566768128 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5634395566768128
new file mode 100644
index 0000000..cd1a293
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5634395566768128
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5688420752424960 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5688420752424960
new file mode 100644
index 0000000..1fe962b
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5688420752424960
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5728971283496960 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5728971283496960
new file mode 100644
index 0000000..25d7bf1
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5728971283496960
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5746142327865344 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5746142327865344
new file mode 100644
index 0000000..1c1607b
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5746142327865344
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5750379279548416 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5750379279548416
new file mode 100644
index 0000000..f014d4b
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-shape-fuzzer-5750379279548416
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-4884742786777088 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-4884742786777088
new file mode 100644
index 0000000..ac7da9f
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-4884742786777088
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-5255344882188288 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-5255344882188288
new file mode 100644
index 0000000..ab1aede
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-5255344882188288
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/233c1e252e737ca79e03a9fd56b71aaa4a230f2b.ttf b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-5720051798769664
similarity index 100%
rename from test/shaping/data/in-house/fonts/233c1e252e737ca79e03a9fd56b71aaa4a230f2b.ttf
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-5720051798769664
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-5924299061854208 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-5924299061854208
new file mode 100644
index 0000000..a8ea332
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-5924299061854208
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-6460279560863744 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-6460279560863744
new file mode 100644
index 0000000..1cbe75a
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-6460279560863744
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5973566991106048 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5973566991106048
new file mode 100644
index 0000000..984bb4b
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5973566991106048
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4523479581851648 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4523479581851648
new file mode 100644
index 0000000..1d16d70
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4523479581851648
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/dd9f0c7c7c36f75a18be0cab1cddf8f3ab0f366b.ttf b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4535496598355968
similarity index 100%
rename from test/shaping/data/in-house/fonts/dd9f0c7c7c36f75a18be0cab1cddf8f3ab0f366b.ttf
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4535496598355968
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4548492505645056 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4548492505645056
new file mode 100644
index 0000000..065080f
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4548492505645056
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/243798dd281c1c77c065958e1ff467420faa9bde.ttf b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4595692015190016
similarity index 100%
rename from test/shaping/data/in-house/fonts/243798dd281c1c77c065958e1ff467420faa9bde.ttf
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4595692015190016
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4687441845813248 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4687441845813248
new file mode 100644
index 0000000..d3e9f46
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4687441845813248
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4706238090706944 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4706238090706944
new file mode 100644
index 0000000..269813a
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4706238090706944
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4769173588672512 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4769173588672512
new file mode 100644
index 0000000..5426914
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4769173588672512
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4827735151083520 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4827735151083520
new file mode 100644
index 0000000..f5f1255
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4827735151083520
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4841745322868736 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4841745322868736
new file mode 100644
index 0000000..5e0f74d
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4841745322868736
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4884742786777088 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4884742786777088
new file mode 100644
index 0000000..ac7da9f
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-4884742786777088
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5216838347653120 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5216838347653120
new file mode 100644
index 0000000..23cc590
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5216838347653120
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5255344882188288 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5255344882188288
new file mode 100644
index 0000000..ab1aede
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5255344882188288
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5294584596791296 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5294584596791296
new file mode 100644
index 0000000..bc16579
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5294584596791296
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5303930168803328 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5303930168803328
new file mode 100644
index 0000000..805fe4d
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5303930168803328
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/b6acef662e0beb8d5fcf5b61c6b0ca69537b7402.ttf b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5331901587914752
similarity index 100%
rename from test/shaping/data/in-house/fonts/b6acef662e0beb8d5fcf5b61c6b0ca69537b7402.ttf
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5331901587914752
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5388906574905344 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5388906574905344
new file mode 100644
index 0000000..5f7ff7c
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5388906574905344
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5517117891805184 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5517117891805184
new file mode 100644
index 0000000..0c7b518
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5517117891805184
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5617496443846656 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5617496443846656
new file mode 100644
index 0000000..aeffab5
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5617496443846656
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5672141338968064 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5672141338968064
new file mode 100644
index 0000000..3c8303f
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5672141338968064
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5700697074958336 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5700697074958336
new file mode 100644
index 0000000..2664e30
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5700697074958336
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5713868010553344 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5713868010553344
new file mode 100644
index 0000000..ee0a721
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5713868010553344
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/233c1e252e737ca79e03a9fd56b71aaa4a230f2b.ttf b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5720051798769664
similarity index 100%
copy from test/shaping/data/in-house/fonts/233c1e252e737ca79e03a9fd56b71aaa4a230f2b.ttf
copy to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5720051798769664
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5924299061854208 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5924299061854208
new file mode 100644
index 0000000..a8ea332
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-5924299061854208
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6023178755244032 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6023178755244032
new file mode 100644
index 0000000..b0da152
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6023178755244032
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6111685556305920 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6111685556305920
new file mode 100644
index 0000000..8c1940d
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6111685556305920
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/bbf4a308c402f0678c3e82844892a4da2ebe598f.ttf b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6160439919509504
similarity index 100%
rename from test/shaping/data/in-house/fonts/bbf4a308c402f0678c3e82844892a4da2ebe598f.ttf
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6160439919509504
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6210176798425088 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6210176798425088
new file mode 100644
index 0000000..1c62961
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6210176798425088
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6260579246276608 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6260579246276608
new file mode 100644
index 0000000..036a206
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6260579246276608
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6264625609834496 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6264625609834496
new file mode 100644
index 0000000..035dd72
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6264625609834496
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6278851874258944 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6278851874258944
new file mode 100644
index 0000000..775c918
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6278851874258944
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6424351550210048 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6424351550210048
new file mode 100644
index 0000000..afb08c5
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6424351550210048
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6460279560863744 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6460279560863744
new file mode 100644
index 0000000..1cbe75a
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6460279560863744
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6576177596596224 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6576177596596224
new file mode 100644
index 0000000..35171ee
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6576177596596224
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6595199411159040 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6595199411159040
new file mode 100644
index 0000000..a4d503d
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6595199411159040
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6624904746106880 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6624904746106880
new file mode 100644
index 0000000..fcc1b64
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6624904746106880
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6723367514144768 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6723367514144768
new file mode 100644
index 0000000..55b1ef8
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-fuzzer-6723367514144768
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5630246225707008 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5630246225707008
new file mode 100644
index 0000000..ab534e3
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5630246225707008
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5633985665826816 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5633985665826816
new file mode 100644
index 0000000..387d7fd
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5633985665826816
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5635082459545600 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5635082459545600
new file mode 100644
index 0000000..6d0feff
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5635082459545600
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5649959857160192 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5649959857160192
new file mode 100644
index 0000000..72e702e
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5649959857160192
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5650286710882304 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5650286710882304
new file mode 100644
index 0000000..95322e1
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5650286710882304
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5652019562414080 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5652019562414080
new file mode 100644
index 0000000..8e85980
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5652019562414080
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5656511058018304 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5656511058018304
new file mode 100644
index 0000000..bb68572
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5656511058018304
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5659641787187200 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5659641787187200
new file mode 100644
index 0000000..8a63dc4
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5659641787187200
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5664873493561344 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5664873493561344
new file mode 100644
index 0000000..dfc36d8
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5664873493561344
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5668791174823936 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5668791174823936
new file mode 100644
index 0000000..1f750c1
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5668791174823936
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5672261407735808 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5672261407735808
new file mode 100644
index 0000000..6297549
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5672261407735808
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5674361600606208 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5674361600606208
new file mode 100644
index 0000000..1d4f84a
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5674361600606208
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5677421274071040 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5677421274071040
new file mode 100644
index 0000000..3be3b91
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5677421274071040
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5679244475105280 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5679244475105280
new file mode 100644
index 0000000..2894bdc
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5679244475105280
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5685596677210112 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5685596677210112
new file mode 100644
index 0000000..d9937a3
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5685596677210112
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5688420752424960 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5688420752424960
new file mode 100644
index 0000000..e9f01a2
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5688420752424960
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5695615258853376 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5695615258853376
new file mode 100644
index 0000000..af43a44
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5695615258853376
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5696686572175360 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5696686572175360
new file mode 100644
index 0000000..c6e8bfd
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5696686572175360
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5718464350650368 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5718464350650368
new file mode 100644
index 0000000..d511e9d
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5718464350650368
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5718889451749376 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5718889451749376
new file mode 100644
index 0000000..6360579
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5718889451749376
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5719982789361664 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5719982789361664
new file mode 100644
index 0000000..0515086
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5719982789361664
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5725129603022848 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5725129603022848
new file mode 100644
index 0000000..8ba0f9d
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5725129603022848
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5726089628876800 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5726089628876800
new file mode 100644
index 0000000..61750d4
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5726089628876800
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5729361857085440 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5729361857085440
new file mode 100644
index 0000000..a6ecc61
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5729361857085440
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5733166795456512 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5733166795456512
new file mode 100644
index 0000000..dfaf6d9
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5733166795456512
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5734736291430400 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5734736291430400
new file mode 100644
index 0000000..31c805c
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5734736291430400
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5738888765636608 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5738888765636608
new file mode 100644
index 0000000..28e72df
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5738888765636608
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5740171484463104 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5740171484463104
new file mode 100644
index 0000000..aef0d92
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5740171484463104
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5750379279548416 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5750379279548416
new file mode 100644
index 0000000..b4551bf
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5750379279548416
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5762490181353472 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5762490181353472
new file mode 100644
index 0000000..db06a1c
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5762490181353472
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5762953198960640 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5762953198960640
new file mode 100644
index 0000000..9d64eaf
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5762953198960640
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5764636557705216 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5764636557705216
new file mode 100644
index 0000000..b07416b
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5764636557705216
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5359635656605696 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5359635656605696
new file mode 100644
index 0000000..8a659c6
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5359635656605696
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5521982557782016 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5521982557782016
similarity index 100%
rename from test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5521982557782016
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5521982557782016
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5542653037903872 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5542653037903872
similarity index 100%
rename from test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5542653037903872
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5542653037903872
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5609911946838016 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5609911946838016
similarity index 100%
rename from test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5609911946838016
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5609911946838016
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5629878397829120 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5629878397829120
new file mode 100644
index 0000000..a055cdb
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5629878397829120
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5651059347816448 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5651059347816448
new file mode 100644
index 0000000..b2e4034
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5651059347816448
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5669437462544384 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5669437462544384
new file mode 100644
index 0000000..c4eb909
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5669437462544384
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5670861909524480 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5670861909524480
similarity index 100%
rename from test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5670861909524480
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5670861909524480
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5690658895953920 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5690658895953920
new file mode 100644
index 0000000..abafa4b
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5690658895953920
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5695279609675776 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5695279609675776
new file mode 100644
index 0000000..f016262
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5695279609675776
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5696607199166464 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5696607199166464
new file mode 100644
index 0000000..ca4fcd7
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5696607199166464
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5711951464759296 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5711951464759296
new file mode 100644
index 0000000..b20e849
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5711951464759296
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5718215406125056 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5718215406125056
new file mode 100644
index 0000000..a6d87da
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5718215406125056
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5743250149736448 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5743250149736448
new file mode 100644
index 0000000..b17c949
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5743250149736448
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5747265633779712 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5747265633779712
new file mode 100644
index 0000000..463e0c1
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5747265633779712
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5750092395970560 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5750092395970560
similarity index 100%
rename from test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5750092395970560
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5750092395970560
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5758598970343424 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5758598970343424
new file mode 100644
index 0000000..7087f63
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5758598970343424
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5765071062958080 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5765071062958080
new file mode 100644
index 0000000..1f9be04
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5765071062958080
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6543700493598720 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6543700493598720
new file mode 100644
index 0000000..c0e9811
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6543700493598720
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6651660668502016 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6651660668502016
similarity index 100%
rename from test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6651660668502016
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6651660668502016
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5203067375976448 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5203067375976448
new file mode 100644
index 0000000..cf92d88
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5203067375976448
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5630904853069824 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5630904853069824
new file mode 100644
index 0000000..7c242ee
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5630904853069824
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5687638085337088 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5687638085337088
new file mode 100644
index 0000000..e8706f3
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5687638085337088
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5736539338833920 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5736539338833920
new file mode 100644
index 0000000..f572621
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5736539338833920
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5930139383758848 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5930139383758848
new file mode 100644
index 0000000..940fbd5
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5930139383758848
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5973295416475648 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5973295416475648
similarity index 100%
rename from test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5973295416475648
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-5973295416475648
Binary files differ
diff --git a/test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-6136125075750912 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-6136125075750912
similarity index 100%
rename from test/api/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-6136125075750912
rename to test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-6136125075750912
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-6394290358976512 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-6394290358976512
new file mode 100644
index 0000000..8ee7752
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-get-codepoints-fuzzer-6394290358976512
Binary files differ
diff --git a/test/api/fonts/crash-4b60576767ee4d9fe1cc10959d89baf73d4e8249 b/test/fuzzing/fonts/crash-4b60576767ee4d9fe1cc10959d89baf73d4e8249
similarity index 100%
rename from test/api/fonts/crash-4b60576767ee4d9fe1cc10959d89baf73d4e8249
rename to test/fuzzing/fonts/crash-4b60576767ee4d9fe1cc10959d89baf73d4e8249
Binary files differ
diff --git a/test/api/fonts/crash-b577db318b30f2851828a4c9ef97cb30678b1b54 b/test/fuzzing/fonts/crash-b577db318b30f2851828a4c9ef97cb30678b1b54
similarity index 100%
rename from test/api/fonts/crash-b577db318b30f2851828a4c9ef97cb30678b1b54
rename to test/fuzzing/fonts/crash-b577db318b30f2851828a4c9ef97cb30678b1b54
Binary files differ
diff --git a/test/api/fonts/crash-ccc61c92d589f895174cdef6ff2e3b20e9999a1a b/test/fuzzing/fonts/crash-ccc61c92d589f895174cdef6ff2e3b20e9999a1a
similarity index 100%
rename from test/api/fonts/crash-ccc61c92d589f895174cdef6ff2e3b20e9999a1a
rename to test/fuzzing/fonts/crash-ccc61c92d589f895174cdef6ff2e3b20e9999a1a
Binary files differ
diff --git a/test/api/fonts/crash-e4e0bb1458a91b692eba492c907ae1f94e635480 b/test/fuzzing/fonts/crash-e4e0bb1458a91b692eba492c907ae1f94e635480
similarity index 100%
rename from test/api/fonts/crash-e4e0bb1458a91b692eba492c907ae1f94e635480
rename to test/fuzzing/fonts/crash-e4e0bb1458a91b692eba492c907ae1f94e635480
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/e88c339237f52d21e01c55f01b9c1b4cc14a0467.ttf b/test/fuzzing/fonts/e88c339237f52d21e01c55f01b9c1b4cc14a0467
similarity index 100%
rename from test/shaping/data/in-house/fonts/e88c339237f52d21e01c55f01b9c1b4cc14a0467.ttf
rename to test/fuzzing/fonts/e88c339237f52d21e01c55f01b9c1b4cc14a0467
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/fab39d60d758cb586db5a504f218442cd1395725.ttf b/test/fuzzing/fonts/fab39d60d758cb586db5a504f218442cd1395725
similarity index 100%
rename from test/shaping/data/in-house/fonts/fab39d60d758cb586db5a504f218442cd1395725.ttf
rename to test/fuzzing/fonts/fab39d60d758cb586db5a504f218442cd1395725
Binary files differ
diff --git a/test/api/fonts/oom-6ef8c96d3710262511bcc730dce9c00e722cb653 b/test/fuzzing/fonts/oom-6ef8c96d3710262511bcc730dce9c00e722cb653
similarity index 100%
rename from test/api/fonts/oom-6ef8c96d3710262511bcc730dce9c00e722cb653
rename to test/fuzzing/fonts/oom-6ef8c96d3710262511bcc730dce9c00e722cb653
Binary files differ
diff --git a/test/api/fonts/oom-ccc61c92d589f895174cdef6ff2e3b20e9999a1a b/test/fuzzing/fonts/oom-ccc61c92d589f895174cdef6ff2e3b20e9999a1a
similarity index 100%
rename from test/api/fonts/oom-ccc61c92d589f895174cdef6ff2e3b20e9999a1a
rename to test/fuzzing/fonts/oom-ccc61c92d589f895174cdef6ff2e3b20e9999a1a
Binary files differ
diff --git a/test/fuzzing/hb-shape-fuzzer.cc b/test/fuzzing/hb-shape-fuzzer.cc
index 79f3222..b5a6c12 100644
--- a/test/fuzzing/hb-shape-fuzzer.cc
+++ b/test/fuzzing/hb-shape-fuzzer.cc
@@ -5,29 +5,29 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
 {
-  hb_blob_t *blob = hb_blob_create((const char *)data, size,
-                                   HB_MEMORY_MODE_READONLY, NULL, NULL);
-  hb_face_t *face = hb_face_create(blob, 0);
-  hb_font_t *font = hb_font_create(face);
-  hb_ot_font_set_funcs(font);
-  hb_font_set_scale(font, 12, 12);
+  hb_blob_t *blob = hb_blob_create ((const char *)data, size,
+				    HB_MEMORY_MODE_READONLY, NULL, NULL);
+  hb_face_t *face = hb_face_create (blob, 0);
+  hb_font_t *font = hb_font_create (face);
+  hb_ot_font_set_funcs (font);
+  hb_font_set_scale (font, 12, 12);
 
   {
     const char text[] = "ABCDEXYZ123@_%&)*$!";
-    hb_buffer_t *buffer = hb_buffer_create();
-    hb_buffer_add_utf8(buffer, text, -1, 0, -1);
-    hb_buffer_guess_segment_properties(buffer);
-    hb_shape(font, buffer, NULL, 0);
-    hb_buffer_destroy(buffer);
+    hb_buffer_t *buffer = hb_buffer_create ();
+    hb_buffer_add_utf8 (buffer, text, -1, 0, -1);
+    hb_buffer_guess_segment_properties (buffer);
+    hb_shape (font, buffer, NULL, 0);
+    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);
+  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);
 
     unsigned int len = hb_buffer_get_length (buffer);
     hb_glyph_info_t *infos = hb_buffer_get_glyph_infos (buffer, NULL);
@@ -41,12 +41,12 @@
       hb_font_get_glyph_extents (font, info.codepoint, &extents);
     }
 
-    hb_buffer_destroy(buffer);
+    hb_buffer_destroy (buffer);
   }
 
 
-  hb_font_destroy(font);
-  hb_face_destroy(face);
-  hb_blob_destroy(blob);
+  hb_font_destroy (font);
+  hb_face_destroy (face);
+  hb_blob_destroy (blob);
   return 0;
 }
diff --git a/test/fuzzing/hb-subset-fuzzer.cc b/test/fuzzing/hb-subset-fuzzer.cc
index 28ce921..3a71f22 100644
--- a/test/fuzzing/hb-subset-fuzzer.cc
+++ b/test/fuzzing/hb-subset-fuzzer.cc
@@ -6,17 +6,16 @@
 
 #include "hb-subset.h"
 
-void trySubset (hb_face_t *face,
-                const hb_codepoint_t text[],
-                int text_length,
-                bool drop_hints,
-                bool drop_ot_layout)
+static void
+trySubset (hb_face_t *face,
+	   const hb_codepoint_t text[],
+	   int text_length,
+	   bool drop_hints,
+	   bool drop_layout)
 {
-  hb_subset_profile_t *profile = hb_subset_profile_create ();
-
   hb_subset_input_t *input = hb_subset_input_create_or_fail ();
-  *hb_subset_input_drop_hints (input) = drop_hints;
-  *hb_subset_input_drop_ot_layout (input) = drop_ot_layout;
+  hb_subset_input_set_drop_hints (input, drop_hints);
+  hb_subset_input_set_drop_layout (input, drop_layout);
   hb_set_t *codepoints = hb_subset_input_unicode_set (input);
 
   for (int i = 0; i < text_length; i++)
@@ -24,23 +23,23 @@
     hb_set_add (codepoints, text[i]);
   }
 
-  hb_face_t *result = hb_subset (face, profile, input);
+  hb_face_t *result = hb_subset (face, input);
   hb_face_destroy (result);
 
   hb_subset_input_destroy (input);
-  hb_subset_profile_destroy (profile);
 }
 
-void trySubset (hb_face_t *face,
-                const hb_codepoint_t text[],
-                int text_length)
+static void
+trySubset (hb_face_t *face,
+	   const hb_codepoint_t text[],
+	   int text_length)
 {
   for (unsigned int drop_hints = 0; drop_hints < 2; drop_hints++)
   {
-    for (unsigned int drop_ot_layout = 0; drop_ot_layout < 2; drop_ot_layout++)
+    for (unsigned int drop_layout = 0; drop_layout < 2; drop_layout++)
     {
       trySubset (face, text, text_length,
-                 (bool) drop_hints, (bool) drop_ot_layout);
+		 (bool) drop_hints, (bool) drop_layout);
     }
   }
 }
@@ -48,22 +47,27 @@
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
 {
   hb_blob_t *blob = hb_blob_create ((const char *)data, size,
-                                    HB_MEMORY_MODE_READONLY, NULL, NULL);
+				    HB_MEMORY_MODE_READONLY, NULL, NULL);
   hb_face_t *face = hb_face_create (blob, 0);
 
+  /* Just test this API here quickly. */
+  hb_set_t *output = hb_set_create();
+  hb_face_collect_unicodes (face, output);
+  hb_set_destroy (output);
+
   const hb_codepoint_t text[] =
       {
-        'A', 'B', 'C', 'D', 'E', 'X', 'Y', 'Z', '1', '2',
-        '3', '@', '_', '%', '&', ')', '*', '$', '!'
+	'A', 'B', 'C', 'D', 'E', 'X', 'Y', 'Z', '1', '2',
+	'3', '@', '_', '%', '&', ')', '*', '$', '!'
       };
 
   trySubset (face, text, sizeof (text) / sizeof (hb_codepoint_t));
 
   hb_codepoint_t text_from_data[16];
   if (size > sizeof(text_from_data)) {
-    memcpy(text_from_data,
-           data + size - sizeof(text_from_data),
-           sizeof(text_from_data));
+    memcpy (text_from_data,
+	    data + size - sizeof(text_from_data),
+	    sizeof(text_from_data));
     unsigned int text_size = sizeof (text_from_data) / sizeof (hb_codepoint_t);
     trySubset (face, text_from_data, text_size);
   }
diff --git a/test/fuzzing/hb-subset-get-codepoints-fuzzer.cc b/test/fuzzing/hb-subset-get-codepoints-fuzzer.cc
deleted file mode 100644
index 38f338b..0000000
--- a/test/fuzzing/hb-subset-get-codepoints-fuzzer.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "hb-fuzzer.hh"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "hb-subset.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
-{
-  hb_blob_t *blob = hb_blob_create ((const char *)data, size,
-                                    HB_MEMORY_MODE_READONLY, NULL, NULL);
-  hb_face_t *face = hb_face_create (blob, 0);
-
-  hb_set_t *output = hb_set_create();
-  hb_subset_get_all_codepoints (face, output);
-
-  hb_set_destroy (output);
-  hb_face_destroy (face);
-  hb_blob_destroy (blob);
-
-  return 0;
-}
diff --git a/test/fuzzing/main.cc b/test/fuzzing/main.cc
index 4692f7b..f15247c 100644
--- a/test/fuzzing/main.cc
+++ b/test/fuzzing/main.cc
@@ -1,21 +1,23 @@
 #include "hb-fuzzer.hh"
 
-#include <iostream>
-#include <iterator>
-#include <fstream>
+#include <stdio.h>
+#include <stdlib.h>
 #include <assert.h>
 
-std::string FileToString(const std::string &Path) {
-  /* TODO This silently passes if file does not exist.  Fix it! */
-  std::ifstream T(Path.c_str());
-  return std::string((std::istreambuf_iterator<char>(T)),
-                     std::istreambuf_iterator<char>());
-}
-
 int main(int argc, char **argv) {
-  for (int i = 1; i < argc; i++) {
-    std::string s = FileToString(argv[i]);
-    std::cout << argv[i] << std::endl;
-    LLVMFuzzerTestOneInput((const unsigned char*)s.data(), s.size());
+  hb_blob_t *blob = hb_blob_create_from_file (argv[1]);
+  unsigned int len;
+  const char *font_data = hb_blob_get_data (blob, &len);
+  if (len == 0)
+  {
+    printf ("Font not found.\n");
+    exit (1);
   }
+
+  for (int i = 1; i < argc; i++) {
+    printf ("%s\n", argv[i]);
+    LLVMFuzzerTestOneInput((const uint8_t *) font_data, len);
+  }
+
+  hb_blob_destroy (blob);
 }
diff --git a/test/fuzzing/run-shape-fuzzer-tests.py b/test/fuzzing/run-shape-fuzzer-tests.py
index fea0b01..53c4f50 100755
--- a/test/fuzzing/run-shape-fuzzer-tests.py
+++ b/test/fuzzing/run-shape-fuzzer-tests.py
@@ -2,7 +2,54 @@
 
 from __future__ import print_function, division, absolute_import
 
-import sys, os, subprocess
+import sys, os, subprocess, tempfile, threading
+
+
+def which(program):
+	# https://stackoverflow.com/a/377028
+	def is_exe(fpath):
+		return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+	fpath, _ = os.path.split(program)
+	if fpath:
+		if is_exe(program):
+			return program
+	else:
+		for path in os.environ["PATH"].split(os.pathsep):
+			exe_file = os.path.join(path, program)
+			if is_exe(exe_file):
+				return exe_file
+
+	return None
+
+
+def cmd(command):
+	# https://stackoverflow.com/a/4408409
+	# https://stackoverflow.com/a/10012262
+	with tempfile.TemporaryFile() as tempf:
+		p = subprocess.Popen (command, stderr=tempf)
+		is_killed = {'value': False}
+
+		def timeout(p, is_killed):
+			is_killed['value'] = True
+			p.kill()
+		timer = threading.Timer (2, timeout, [p, is_killed])
+
+		try:
+			timer.start()
+			p.wait ()
+			tempf.seek (0)
+			text = tempf.read().decode ("utf-8").strip ()
+			returncode = p.returncode
+		finally:
+			timer.cancel()
+
+		if is_killed['value']:
+			text = 'error: timeout, ' + text
+			returncode = 1
+
+		return text, returncode
+
 
 srcdir = os.environ.get ("srcdir", ".")
 EXEEXT = os.environ.get ("EXEEXT", "")
@@ -20,14 +67,30 @@
 print ('hb_shape_fuzzer:', hb_shape_fuzzer)
 fails = 0
 
-parent_path = os.path.join (srcdir, "..", "shaping", "data", "in-house", "tests")
-for line in open (os.path.join (parent_path, "fuzzed.tests")):
-	font = line.split (":")[0]
-	font_path = os.path.join (parent_path, font)
+valgrind = None
+if os.environ.get('RUN_VALGRIND', ''):
+	valgrind = which ('valgrind')
 
-	p = subprocess.Popen ([hb_shape_fuzzer, font_path])
+parent_path = os.path.join (srcdir, "fonts")
+for file in os.listdir (parent_path):
+	path = os.path.join(parent_path, file)
 
-	if p.wait () != 0:
+	text, returncode = cmd ([hb_shape_fuzzer, path])
+	print (text)
+
+	failed = False
+	if returncode != 0 or 'error' in text:
+		print ('failure on %s' % file)
+		failed = True
+
+	if valgrind:
+		text, returncode = cmd ([valgrind, '--error-exitcode=1', hb_shape_fuzzer, path])
+		if returncode:
+			print (text)
+			print ('failure on %s' % file)
+			failed = True
+
+	if failed:
 		fails = fails + 1
 
 if fails:
diff --git a/test/fuzzing/run-subset-fuzzer-tests.py b/test/fuzzing/run-subset-fuzzer-tests.py
index 0136288..d4e3487 100755
--- a/test/fuzzing/run-subset-fuzzer-tests.py
+++ b/test/fuzzing/run-subset-fuzzer-tests.py
@@ -8,7 +8,6 @@
 EXEEXT = os.environ.get ("EXEEXT", "")
 top_builddir = os.environ.get ("top_builddir", ".")
 hb_subset_fuzzer = os.path.join (top_builddir, "hb-subset-fuzzer" + EXEEXT)
-hb_subset_get_codepoints_fuzzer = os.path.join (top_builddir, "hb-subset-get-codepoints-fuzzer" + EXEEXT)
 
 if not os.path.exists (hb_subset_fuzzer):
         if len (sys.argv) < 2 or not os.path.exists (sys.argv[1]):
@@ -18,35 +17,29 @@
 
         hb_subset_fuzzer = sys.argv[1]
 
-if not os.path.exists (hb_subset_get_codepoints_fuzzer):
-        if len (sys.argv) < 3 or not os.path.exists (sys.argv[2]):
-                print ("""Failed to find hb-subset-get-codepoints-fuzzer binary automatically,
-please provide it as the second argument to the tool""")
-                sys.exit (1)
-
-        hb_subset_get_codepoints_fuzzer = sys.argv[2]
-
 print ('hb_subset_fuzzer:', hb_subset_fuzzer)
 fails = 0
 
-parent_path = os.path.join (srcdir, "..", "subset", "data", "fonts")
-print ("running subset fuzzer against fonts in %s" % parent_path)
-for file in os.listdir (parent_path):
-        path = os.path.join(parent_path, file)
+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)
 
-        print ("running subset fuzzer against %s" % path)
-        p = subprocess.Popen ([hb_subset_fuzzer, path])
+		print ("running subset fuzzer against %s" % path)
+		p = subprocess.Popen ([hb_subset_fuzzer, path])
 
-        if p.wait () != 0:
-                print ("failed for %s" % path)
-                fails = fails + 1
+		if p.wait () != 0:
+			print ("failed for %s" % path)
+			fails = fails + 1
 
-        print ("running subset get codepoints fuzzer against %s" % path)
-        p = subprocess.Popen ([hb_subset_get_codepoints_fuzzer, path])
+		if p.wait () != 0:
+			print ("failed for %s" % path)
+			fails = fails + 1
 
-        if p.wait () != 0:
-                print ("failed for %s" % path)
-                fails = fails + 1
+run_dir (os.path.join (srcdir, "..", "subset", "data", "fonts"))
+# TODO running these tests very slow tests.  Fix and re-enable
+#run_dir (os.path.join (srcdir, "fonts"))
 
 if fails:
         print ("%i subset fuzzer related tests failed." % fails)
diff --git a/test/shaping/Makefile.am b/test/shaping/Makefile.am
index 7320336..5ca9bc1 100644
--- a/test/shaping/Makefile.am
+++ b/test/shaping/Makefile.am
@@ -8,6 +8,8 @@
 # Convenience targets:
 lib:
 	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
+libs:
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src libs
 
 EXTRA_DIST += \
 	README.md \
diff --git a/test/shaping/data/in-house/Makefile.sources b/test/shaping/data/in-house/Makefile.sources
index ef16fae..6e21ddd 100644
--- a/test/shaping/data/in-house/Makefile.sources
+++ b/test/shaping/data/in-house/Makefile.sources
@@ -1,18 +1,20 @@
 TESTS = \
+	tests/aat-trak.tests \
 	tests/arabic-fallback-shaping.tests \
 	tests/arabic-feature-order.tests \
 	tests/arabic-like-joining.tests \
+	tests/arabic-mark-attach.tests \
 	tests/arabic-mark-order.tests \
 	tests/arabic-stch.tests \
 	tests/automatic-fractions.tests \
 	tests/cluster.tests \
+	tests/collections.tests \
 	tests/color-fonts.tests \
 	tests/context-matching.tests \
 	tests/cursive-positioning.tests \
 	tests/default-ignorables.tests \
-	tests/emoji-flag-tags.tests \
+	tests/emoji.tests \
 	tests/fallback-positioning.tests \
-	tests/fuzzed.tests \
 	tests/hangul-jamo.tests \
 	tests/hyphens.tests \
 	tests/indic-consonant-with-stacker.tests \
@@ -25,13 +27,18 @@
 	tests/indic-script-extensions.tests \
 	tests/indic-special-cases.tests \
 	tests/indic-syllable.tests \
+	tests/indic-vowel-letter-spoofing.tests \
+	tests/khmer-mark-order.tests \
+	tests/khmer-misc.tests \
 	tests/language-tags.tests \
 	tests/ligature-id.tests \
 	tests/mark-attachment.tests \
 	tests/mark-filtering-sets.tests \
 	tests/mongolian-variation-selector.tests \
 	tests/myanmar-syllable.tests \
+	tests/myanmar-zawgyi.tests \
 	tests/none-directional.tests \
+	tests/rand.tests \
 	tests/spaces.tests \
 	tests/simple.tests \
 	tests/sinhala.tests \
@@ -39,6 +46,7 @@
 	tests/tibetan-contractions-2.tests \
 	tests/tibetan-vowels.tests \
 	tests/use.tests \
+	tests/use-indic3.tests \
 	tests/use-marchen.tests \
 	tests/use-syllable.tests \
 	tests/variations-rvrn.tests \
diff --git a/test/shaping/data/in-house/fonts/03e3f463c3a985bc42096620cc415342818454fb.ttf b/test/shaping/data/in-house/fonts/03e3f463c3a985bc42096620cc415342818454fb.ttf
new file mode 100644
index 0000000..ee540f3
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/03e3f463c3a985bc42096620cc415342818454fb.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf b/test/shaping/data/in-house/fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf
new file mode 100644
index 0000000..383aee6
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/28f497629c04ceb15546c9a70e0730125ed6698d.ttf b/test/shaping/data/in-house/fonts/28f497629c04ceb15546c9a70e0730125ed6698d.ttf
new file mode 100644
index 0000000..13c4d8a
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/28f497629c04ceb15546c9a70e0730125ed6698d.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/2c25beb56d9c556622d56b0b5d02b4670c034f89.ttf b/test/shaping/data/in-house/fonts/2c25beb56d9c556622d56b0b5d02b4670c034f89.ttf
new file mode 100644
index 0000000..ef94d3f
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/2c25beb56d9c556622d56b0b5d02b4670c034f89.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/3c96e7a303c58475a8c750bf4289bbe73784f37d.ttf b/test/shaping/data/in-house/fonts/3c96e7a303c58475a8c750bf4289bbe73784f37d.ttf
new file mode 100644
index 0000000..cebd375
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/3c96e7a303c58475a8c750bf4289bbe73784f37d.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/3cf6f8ac6d647473a43a3100e7494b202b2cfafe.ttf b/test/shaping/data/in-house/fonts/3cf6f8ac6d647473a43a3100e7494b202b2cfafe.ttf
new file mode 100644
index 0000000..63c0c71
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/3cf6f8ac6d647473a43a3100e7494b202b2cfafe.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf b/test/shaping/data/in-house/fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf
new file mode 100644
index 0000000..14de6a1
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/5bb74492f5e0ffa1fbb72e4c881be035120b6513.ttf b/test/shaping/data/in-house/fonts/5bb74492f5e0ffa1fbb72e4c881be035120b6513.ttf
new file mode 100644
index 0000000..588ce3b
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/5bb74492f5e0ffa1fbb72e4c881be035120b6513.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf b/test/shaping/data/in-house/fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf
new file mode 100644
index 0000000..a6f1c9d
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/641ca9d7808b01cafa9a666c13811c9b56eb9c52.ttf b/test/shaping/data/in-house/fonts/641ca9d7808b01cafa9a666c13811c9b56eb9c52.ttf
new file mode 100644
index 0000000..1328e13
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/641ca9d7808b01cafa9a666c13811c9b56eb9c52.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf b/test/shaping/data/in-house/fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf
new file mode 100644
index 0000000..c3e4167
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/7d18685e1529e4ceaad5b6095dfab2f9789e5bce.ttf b/test/shaping/data/in-house/fonts/7d18685e1529e4ceaad5b6095dfab2f9789e5bce.ttf
new file mode 100644
index 0000000..ffdddf3
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/7d18685e1529e4ceaad5b6095dfab2f9789e5bce.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/881642af1667ae30a54e58de8be904566d00508f.ttf b/test/shaping/data/in-house/fonts/881642af1667ae30a54e58de8be904566d00508f.ttf
new file mode 100644
index 0000000..a749cdf
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/881642af1667ae30a54e58de8be904566d00508f.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/9fc3e6960b3520e5304033ef5fd540285f72f14d.ttf b/test/shaping/data/in-house/fonts/9fc3e6960b3520e5304033ef5fd540285f72f14d.ttf
new file mode 100644
index 0000000..1841cf3
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/9fc3e6960b3520e5304033ef5fd540285f72f14d.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/TestDFONT.dfont b/test/shaping/data/in-house/fonts/TestDFONT.dfont
new file mode 100644
index 0000000..a6ea700
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/TestDFONT.dfont
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/TestTRAK.ttf b/test/shaping/data/in-house/fonts/TestTRAK.ttf
new file mode 100644
index 0000000..07ae3af
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/TestTRAK.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/TestTTC.ttc b/test/shaping/data/in-house/fonts/TestTTC.ttc
new file mode 100644
index 0000000..a21fe89
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/TestTTC.ttc
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/ab14b4eb9d7a67e293f51d30d719add06c9d6e06.ttf b/test/shaping/data/in-house/fonts/ab14b4eb9d7a67e293f51d30d719add06c9d6e06.ttf
new file mode 100644
index 0000000..a64ecea
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/ab14b4eb9d7a67e293f51d30d719add06c9d6e06.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/af85624080af5627fb050f570d148a62f04fda74.ttf b/test/shaping/data/in-house/fonts/af85624080af5627fb050f570d148a62f04fda74.ttf
new file mode 100644
index 0000000..9cd40d4
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/af85624080af5627fb050f570d148a62f04fda74.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf b/test/shaping/data/in-house/fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf
new file mode 100644
index 0000000..a9dc202
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/bb29ce50df2bdba2d10726427c6b7609bf460e04.ttf b/test/shaping/data/in-house/fonts/bb29ce50df2bdba2d10726427c6b7609bf460e04.ttf
deleted file mode 100644
index fba200f..0000000
--- a/test/shaping/data/in-house/fonts/bb29ce50df2bdba2d10726427c6b7609bf460e04.ttf
+++ /dev/null
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/d3129450fafe5e5c98cfc25a4e71809b1b4d2855.ttf b/test/shaping/data/in-house/fonts/d3129450fafe5e5c98cfc25a4e71809b1b4d2855.ttf
new file mode 100644
index 0000000..dbd928a
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/d3129450fafe5e5c98cfc25a4e71809b1b4d2855.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/tests/aat-trak.tests b/test/shaping/data/in-house/tests/aat-trak.tests
new file mode 100644
index 0000000..48b224f
--- /dev/null
+++ b/test/shaping/data/in-house/tests/aat-trak.tests
@@ -0,0 +1,8 @@
+../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]
diff --git a/test/shaping/data/in-house/tests/arabic-mark-attach.tests b/test/shaping/data/in-house/tests/arabic-mark-attach.tests
new file mode 100644
index 0000000..a577e51
--- /dev/null
+++ b/test/shaping/data/in-house/tests/arabic-mark-attach.tests
@@ -0,0 +1 @@
+../fonts/641ca9d7808b01cafa9a666c13811c9b56eb9c52.ttf::U+064A,U+0633,U+06E1,U+200D,U+0654,U+064E,U+0644:[afii57444.zz04=6+1091|afii57454=1@75,925+0|uni0654=1+0|space=1+0|uni06E1=1@950,1115+0|afii57427.zz03_calt=1+1847|afii57450.zz21=0+345]
diff --git a/test/shaping/data/in-house/tests/collections.tests b/test/shaping/data/in-house/tests/collections.tests
new file mode 100644
index 0000000..85653c5
--- /dev/null
+++ b/test/shaping/data/in-house/tests/collections.tests
@@ -0,0 +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]
diff --git a/test/shaping/data/in-house/tests/cursive-positioning.tests b/test/shaping/data/in-house/tests/cursive-positioning.tests
index 74d283d..15a1ffc 100644
--- a/test/shaping/data/in-house/tests/cursive-positioning.tests
+++ b/test/shaping/data/in-house/tests/cursive-positioning.tests
@@ -2,3 +2,4 @@
 ../fonts/298c9e1d955f10f6f72c6915c3c6ff9bf9695cec.ttf::U+0643,U+0645,U+0645,U+062B,U+0644:[gid8=4+738|gid5=3@441,1197+0|gid6=3@0,432+405|gid9=2@0,477+500|gid9=1@0,577+452|gid10=0@20,1177+207]
 #../fonts/706c5d7b625f207bc0d874c67237aad6f1e9cd6f.ttf::U+0B1F,U+0B4D,U+0B1A,U+0B4D,U+0B1A:[ttaorya=0+1307|casubscriptorya=0@-242,104+-231|casubscriptnarroworya=0@20,104+507]
 ../fonts/07f054357ff8638bac3711b422a1e31180bba863.ttf:--font-funcs=ot --no-glyph-names:U+0606,U+06E1:[2=0@40,502+0|1=0+1000]
+../fonts/9fc3e6960b3520e5304033ef5fd540285f72f14d.ttf::U+16F0A,U+16F57,U+16F8F:[u16F0A=0+422|u16F57=0@0,209+338|u16F8F=0+0]
diff --git a/test/shaping/data/in-house/tests/emoji-flag-tags.tests b/test/shaping/data/in-house/tests/emoji-flag-tags.tests
deleted file mode 100644
index 189de55..0000000
--- a/test/shaping/data/in-house/tests/emoji-flag-tags.tests
+++ /dev/null
@@ -1,2 +0,0 @@
-../fonts/53374c7ca3657be37efde7ed02ae34229a56ae1f.ttf::U+1F3F4,U+E0055,U+E0053,U+E0064,U+E0065,U+E007F:[u1F3F4=0+2126|space=1+0|space=2+0|space=3+0|space=4+0|space=5+0]
-../fonts/53374c7ca3657be37efde7ed02ae34229a56ae1f.ttf::U+1F3F4,U+E0064,U+E0065,U+E007F:[de=0+3200]
diff --git a/test/shaping/data/in-house/tests/emoji.tests b/test/shaping/data/in-house/tests/emoji.tests
new file mode 100644
index 0000000..8d9b254
--- /dev/null
+++ b/test/shaping/data/in-house/tests/emoji.tests
@@ -0,0 +1,4 @@
+../fonts/53374c7ca3657be37efde7ed02ae34229a56ae1f.ttf::U+1F3F4,U+E0055,U+E0053,U+E0064,U+E0065,U+E007F:[u1F3F4=0+2126|space=1+0|space=2+0|space=3+0|space=4+0|space=5+0]
+../fonts/53374c7ca3657be37efde7ed02ae34229a56ae1f.ttf::U+1F3F4,U+E0064,U+E0065,U+E007F:[de=0+3200]
+../fonts/3cf6f8ac6d647473a43a3100e7494b202b2cfafe.ttf:--font-funcs=ot --direction=l:U+1F481,U+1F3FB,U+200D,U+2642,U+FE0F:[gid7=0+2550]
+../fonts/3cf6f8ac6d647473a43a3100e7494b202b2cfafe.ttf:--font-funcs=ot --direction=r:U+1F481,U+1F3FB,U+200D,U+2642,U+FE0F:[gid7=0+2550]
diff --git a/test/shaping/data/in-house/tests/fuzzed.tests b/test/shaping/data/in-house/tests/fuzzed.tests
deleted file mode 100644
index 43a1933..0000000
--- a/test/shaping/data/in-house/tests/fuzzed.tests
+++ /dev/null
@@ -1,23 +0,0 @@
-../fonts/1a6f1687b7a221f9f2c834b0b360d3c8463b6daf.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/5a5daf5eb5a4db77a2baa3ad9c7a6ed6e0655fa8.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/0509e80afb379d16560e9e47bdd7d888bebdebc6.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/641bd9db850193064d17575053ae2bf8ec149ddc.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/375d6ae32a3cbe52fbf81a4e5777e3377675d5a3.ttf:--font-funcs=ot:U+0041:[gid0=0+4352]
-../fonts/8240789f6d12d4cfc4b5e8e6f246c3701bcf861f.ttf:--font-funcs=ot:U+0041:[gid0=0+1024]
-../fonts/b9e2aaa0d75fcef6971ec3a96d806ba4a6b31fe2.ttf:--font-funcs=ot:U+0041:[gid0=0+1000|gid1=0+1000|gid8=0+1000|gid3=0+1000|gid0=0+1000|gid1=0+1000|gid1=0+1000|gid8=0+1000|gid3=0+1000|gid0=0+1000|gid1=0+1000|gid8=0+1000|gid3=0+1000|gid0=0+1000|gid1=0+1000|gid1=0+1000]
-../fonts/43979b90b2dd929723cf4fe1715990bcb9c9a56b.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/3511ff5c1647150595846ac414c595cccac34f18.ttf:--font-funcs=ot --no-positions --no-clusters --no-glyph-names:U+0041:[0|512|15104|11004|3408|18244|17872|17961|0|992|15616|0|14151|20559|20992|5440|256|0|10|8960|256|1024|1490|0|768|4096|256|2216|0|256|256|0|768|10752|11004|3408|18244|17734|53248|256|0|512|14848|10793|57344|768|18227|20285|20480|0|256|0|810|0|11004|3408|18244|17734|53289|57344|768|15667|71|0|20559|21248|256|0|2816|2776|0|51516|0|32|26209|28005|65249|29690|0|51548|0|2454|28783|29556|1291|3458|80|0|2804|210|28786|25968|45763|50546|0|59136|0|38144|256|0|2560|30208|52224|580|17996|21504|6734|108|116|24846|1024|0|255|65280|256|0|8704|1345|23109|8192|10823|21076|8192|12877|20300|8192|6738|20301|8192|16980|21067|8251|18944|255|65280|15360|256|255|65280|256|768|255|65280|256|768|255|65280|256|1024|12|65280|256|1280|255|65280|256|1536|1899|25970|110|11264|27502|29285|12907|25974|28160|14443|25970|28288|3|118|18259|21826|45716|46369|0|0|1|16|17|256|4|16|18244|17734|28|12|0|284|0|28|18256|20307|45114|47616|226|10296|0|57927|1|0|0|21248|5440|256|0|10|768|256|1024|512|0|297|16|24833|28774|10794|2304|29|32|42|64515|42|42|64525|20551|17477|18128|10720|3|61|3408|18244|17734|53289|57344|768|15616|512|55|10576|20307|0|255|56063|53504|42|42|64525|12288|18176|80|20307|1|0|62]
-../fonts/fab39d60d758cb586db5a504f218442cd1395725.ttf:--font-funcs=ot:U+0041,U+0041:[gid0=0+1000|gid0=1+1000]
-../fonts/205edd09bd3d141cc9580f650109556cc28b22cb.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/217a934cfe15c548b572c203dceb2befdf026462.ttf:--font-funcs=ot:U+0061,U+0061,U+0061:[]
-../fonts/558661aa659912f4d30ecd27bd09835171a8e2b0.ttf:--font-funcs=ot:U+FFFD,U+E0100,U+FFFD,U+E0010:[]
-../fonts/a34a9191d9376bda419836effeef7e75c1386016.ttf:--font-funcs=ot:U+0041:[]
-../fonts/a69118c2c2ada48ff803d9149daa54c9ebdae30e.ttf:--font-funcs=ot:U+0041:[gid0=0+1229]
-../fonts/b6acef662e0beb8d5fcf5b61c6b0ca69537b7402.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/e88c339237f52d21e01c55f01b9c1b4cc14a0467.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/243798dd281c1c77c065958e1ff467420faa9bde.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/dd9f0c7c7c36f75a18be0cab1cddf8f3ab0f366b.ttf:--font-funcs=ot --no-positions --no-clusters --no-glyph-names:U+0041:[0|0|2|0|0|2|0|0|2|0|0|2|0|0|2|0|0|2|0|0|0|2|0|0|0|2|0|0|2|0|0|2|0|0|2|0|0|2|0|0|0|2|0|0|2|0|0|2|0|0|2|0]
-../fonts/ef2511f215aa3ca847cbfffbf861793b42170875.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/9d8a94a67932a3ab75a596fc8b5c6d0392ca9e49.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/bbf4a308c402f0678c3e82844892a4da2ebe598f.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-../fonts/233c1e252e737ca79e03a9fd56b71aaa4a230f2b.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
diff --git a/test/shaping/data/in-house/tests/indic-joiner-candrabindu.tests b/test/shaping/data/in-house/tests/indic-joiner-candrabindu.tests
index 87b3603..6b75137 100644
--- a/test/shaping/data/in-house/tests/indic-joiner-candrabindu.tests
+++ b/test/shaping/data/in-house/tests/indic-joiner-candrabindu.tests
@@ -1,2 +1,2 @@
 ../fonts/5028afb650b1bb718ed2131e872fbcce57828fff.ttf::U+0B13,U+200D,U+0B01:[omorya=0+1450]
-../fonts/5028afb650b1bb718ed2131e872fbcce57828fff.ttf::U+0B13,U+200C,U+0B01:[oorya=0+1309|space=0+0|candrabinduorya=0+0]
+../fonts/5028afb650b1bb718ed2131e872fbcce57828fff.ttf::U+0B13,U+200C,U+0B01:[oorya=0+1309|space=1+0|candrabinduorya=1+0]
diff --git a/test/shaping/data/in-house/tests/indic-joiners.tests b/test/shaping/data/in-house/tests/indic-joiners.tests
index dca83f8..80e392c 100644
--- a/test/shaping/data/in-house/tests/indic-joiners.tests
+++ b/test/shaping/data/in-house/tests/indic-joiners.tests
@@ -1,6 +1,6 @@
-../fonts/f443753e8ffe8e8aae606cfba158e00334b6efb1.ttf::U+179A,U+1784,U+17D2,U+179F,U+200C,U+17CA,U+17B8,U+0020:[uni179a=0+775|uni1784=1+1550|uni179f.sub=1+775|space=1+0|uni17ca=1+0|uni17b8=1@0,300+0|space=7+600]
+../fonts/f443753e8ffe8e8aae606cfba158e00334b6efb1.ttf::U+179A,U+1784,U+17D2,U+179F,U+200C,U+17CA,U+17B8,U+0020:[uni179a=0+775|uni1784=1+1550|uni179f.sub=1+775|space=4+0|uni17ca=4+0|uni17b8=4@0,300+0|space=7+600]
 ../fonts/f443753e8ffe8e8aae606cfba158e00334b6efb1.ttf::U+179A,U+1784,U+17D2,U+179F,U+17CA,U+17B8:[uni179a=0+775|uni1784=1+1550|uni179f.sub=1+775|uni17bb=1@-75,-700+0|uni17b8=1+0]
-../fonts/8116e5d8fedfbec74e45dc350d2416d810bed8c4.ttf:--font-funcs=ft:U+091F,U+094D,U+200C,U+092F,U+093F:[uni091F=0+876|uni094D=0@4,0+0|space=0+0|uni093F.750=3+397|uni092F=3+924]
+../fonts/8116e5d8fedfbec74e45dc350d2416d810bed8c4.ttf:--font-funcs=ft:U+091F,U+094D,U+200C,U+092F,U+093F:[uni091F=0+876|uni094D=0@4,0+0|space=2+0|uni093F.750=3+397|uni092F=3+924]
 ../fonts/8116e5d8fedfbec74e45dc350d2416d810bed8c4.ttf:--font-funcs=ft:U+091F,U+094D,U+200D,U+092F,U+093F:[uni093F=0+398|uni091F=0+876|uni094D=0@4,0+0|space=0+0|uni092F=0+924]
-../fonts/8116e5d8fedfbec74e45dc350d2416d810bed8c4.ttf:--font-funcs=ft:U+091F,U+094D,U+200D,U+091F,U+094D,U+200C,U+091F,U+094D,U+200D,U+092F,U+093F:[uni091F=0+876|uni094D=0@4,0+0|space=0+0|uni091F=3+876|uni094D=3@4,0+0|space=3+0|uni093F=6+398|uni091F=6+876|uni094D=6@4,0+0|space=6+0|uni092F=6+924]
+../fonts/8116e5d8fedfbec74e45dc350d2416d810bed8c4.ttf:--font-funcs=ft:U+091F,U+094D,U+200D,U+091F,U+094D,U+200C,U+091F,U+094D,U+200D,U+092F,U+093F:[uni091F=0+876|uni094D=0@4,0+0|space=0+0|uni091F=3+876|uni094D=3@4,0+0|space=5+0|uni093F=6+398|uni091F=6+876|uni094D=6@4,0+0|space=6+0|uni092F=6+924]
 ../fonts/8116e5d8fedfbec74e45dc350d2416d810bed8c4.ttf:--font-funcs=ft:U+091F,U+094D,U+200D,U+091F,U+094D,U+200D,U+091F,U+094D,U+200D,U+092F,U+093F:[uni093F=0+398|uni091F=0+876|uni094D=0@4,0+0|space=0+0|uni091F=0+876|uni094D=0@4,0+0|space=0+0|uni091F=0+876|uni094D=0@4,0+0|space=0+0|uni092F=0+924]
diff --git a/test/shaping/data/in-house/tests/indic-vowel-letter-spoofing.tests b/test/shaping/data/in-house/tests/indic-vowel-letter-spoofing.tests
new file mode 100644
index 0000000..f8305a3
--- /dev/null
+++ b/test/shaping/data/in-house/tests/indic-vowel-letter-spoofing.tests
@@ -0,0 +1,53 @@
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0904,U+0020,U+0905,U+0946:[ashortdeva=0+764|space=1+260|adeva=2+764|uni25CC=2+510|eshortvowelsigndeva=2+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0906,U+0020,U+0905,U+093E:[aadeva=0+1023|space=1+260|adeva=2+764|uni25CC=2+510|aavowelsigndeva=2+259]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0908,U+0020,U+0930,U+094D,U+0907:[iideva=0+491|space=1+260|uni25CC=2+510|rephdeva=2+0|ideva=2+491]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+090A,U+0020,U+0909,U+0941:[uudeva=0+765|space=1+260|udeva=2+548|uni25CC=2+510|uvowelsigndeva=2+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+090D,U+0020,U+090F,U+0945:[ecandradeva=0+553|space=1+260|edeva=2+553|uni25CC=2+510|ecandravowelsigndeva=2+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+090E,U+0020,U+090F,U+0946:[eshortdeva=0+553|space=1+260|edeva=2+553|uni25CC=2+510|eshortvowelsigndeva=2+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0910,U+0020,U+090F,U+0947:[aideva=0+553|space=1+260|edeva=2+553|uni25CC=2+510|evowelsigndeva=2+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0911,U+0020,U+0905,U+0949,U+0020,U+0906,U+0945:[ocandradeva=0+1023|space=1+260|adeva=2+764|uni25CC=2+510|ocandravowelsigndeva=2+259|space=4+260|aadeva=5+1023|uni25CC=5+510|ecandravowelsigndeva=5+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0912,U+0020,U+0905,U+094A,U+0020,U+0906,U+0946:[oshortdeva=0+1023|space=1+260|adeva=2+764|uni25CC=2+510|oshortvowelsigndeva=2+259|space=4+260|aadeva=5+1023|uni25CC=5+510|eshortvowelsigndeva=5+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0913,U+0020,U+0905,U+094B,U+0020,U+0906,U+0947:[odeva=0+1023|space=1+260|adeva=2+764|uni25CC=2+510|ovowelsigndeva=2+259|space=4+260|aadeva=5+1023|uni25CC=5+510|evowelsigndeva=5+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0914,U+0020,U+0905,U+094C,U+0020,U+0906,U+0948:[audeva=0+1023|space=1+260|adeva=2+764|uni25CC=2+510|auvowelsigndeva=2+259|space=4+260|aadeva=5+1023|uni25CC=5+510|aivowelsigndeva=5+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0972,U+0020,U+0905,U+0945:[acandradeva=0+764|space=1+260|adeva=2+764|uni25CC=2+510|ecandravowelsigndeva=2+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0973,U+0020,U+0905,U+093A:[oedeva=0+764|space=1+260|adeva=2+764|uni25CC=2+510|oevowelsigndeva=2+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0974,U+0020,U+0905,U+093B,U+0020,U+0906,U+093A:[ooedeva=0+1023|space=1+260|adeva=2+764|uni25CC=2+510|ooevowelsigndeva=2+259|space=4+260|aadeva=5+1023|uni25CC=5+510|oevowelsigndeva=5+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0975,U+0020,U+0905,U+094F:[awdeva=0+1023|space=1+260|adeva=2+764|uni25CC=2+510|awvowelsigndeva=2+259]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0976,U+0020,U+0905,U+0956:[uedeva=0+764|space=1+260|adeva=2+764|uni25CC=2+510|uevowelsigndeva=2@50,0+0]
+../fonts/1a5face3fcbd929d228235c2f72bbd6f8eb37424.ttf::U+0977,U+0020,U+0905,U+0957:[uuedeva=0+764|space=1+260|adeva=2+764|uni25CC=2+510|uuevowelsigndeva=2@50,0+0]
+../fonts/881642af1667ae30a54e58de8be904566d00508f.ttf::U+0986,U+0020,U+0985,U+09BE:[aabeng=0+1158|space=1+260|abeng=2+893|uni25CC=2+510|aavowelsignbeng=2+266]
+../fonts/881642af1667ae30a54e58de8be904566d00508f.ttf::U+09E0,U+0020,U+098B,U+09C3:[rrvocalicbeng=0+853|space=1+260|rvocalicbeng=2+853|uni25CC=2+510|rvocalicvowelsignbeng=2+0]
+../fonts/881642af1667ae30a54e58de8be904566d00508f.ttf::U+09E1,U+0020,U+098C,U+09E2:[llvocalicbeng=0+639|space=1+260|lvocalicbeng=2+639|uni25CC=2+510|lvocalicvowelsignbeng=2+0]
+../fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf::U+0A06,U+0020,U+0A05,U+0A3E:[aaguru=0+2001|space=1+532|aguru=2+1520|uni25CC=2+1044|aamatraguru=2+481]
+../fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf::U+0A07,U+0020,U+0A72,U+0A3F:[iguru=0+1671|space=1+532|iriguru=2+1141|imatraguru=2+530|uni25CC=2+1044]
+../fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf::U+0A08,U+0020,U+0A72,U+0A40:[iiguru=0+1671|space=1+532|iriguru=2+1141|uni25CC=2+1044|iimatraguru=2+530]
+../fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf::U+0A09,U+0020,U+0A73,U+0A41:[uguru=0+1356|space=1+532|uraguru=2+1356|uni25CC=2+1044|umatraguru=2@102,0+0]
+../fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf::U+0A0A,U+0020,U+0A73,U+0A42:[uuguru=0+1356|space=1+532|uraguru=2+1356|uni25CC=2+1044|uumatraguru=2@102,0+0]
+../fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf::U+0A0F,U+0020,U+0A72,U+0A47:[eeguru=0+1141|space=1+532|iriguru=2+1141|uni25CC=2+1044|eematraguru=2+0]
+../fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf::U+0A10,U+0020,U+0A05,U+0A48:[aiguru=0+1520|space=1+532|aguru=2+1520|uni25CC=2+1044|aimatraguru=2+0]
+../fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf::U+0A13,U+0020,U+0A73,U+0A4B:[ooguru=0+1356|space=1+532|uraguru=2+1356|uni25CC=2+1044|oomatraguru=2+0]
+../fonts/604026ae5aaca83c49cd8416909d71ba3e1c1194.ttf::U+0A14,U+0020,U+0A05,U+0A4C:[auguru=0+1520|space=1+532|aguru=2+1520|uni25CC=2+1044|aumatraguru=2+0]
+../fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf::U+0A86,U+0020,U+0A85,U+0ABE:[gid3=0+2351|gid1=1+612|gid2=2+1808|gid17=2+1044|gid10=2+543]
+../fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf::U+0A8D,U+0020,U+0A85,U+0AC5:[gid4=0+1808|gid1=1+612|gid2=2+1808|gid17=2+1044|gid11=2+0]
+../fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf::U+0A8F,U+0020,U+0A85,U+0AC7:[gid5=0+1808|gid1=1+612|gid2=2+1808|gid17=2+1044|gid12=2+0]
+../fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf::U+0A90,U+0020,U+0A85,U+0AC8:[gid6=0+1808|gid1=1+612|gid2=2+1808|gid17=2+1044|gid13=2+0]
+../fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf::U+0A91,U+0020,U+0A85,U+0AC9:[gid7=0+2351|gid1=1+612|gid2=2+1808|gid17=2+1044|gid14=2+543]
+../fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf::U+0A93,U+0020,U+0A85,U+0ACB,U+0020,U+0A85,U+0ABE,U+0AC5:[gid8=0+2351|gid1=1+612|gid2=2+1808|gid17=2+1044|gid15=2+543|gid1=4+612|gid2=5+1808|gid17=5+1044|gid11=5+0|gid10=5+543]
+../fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf::U+0A94,U+0020,U+0A85,U+0ACC,U+0020,U+0A85,U+0ABE,U+0AC8:[gid9=0+2351|gid1=1+612|gid2=2+1808|gid17=2+1044|gid16=2+543|gid1=4+612|gid2=5+1808|gid17=5+1044|gid13=5+0|gid10=5+543]
+../fonts/738d9f3b8c2dfd03875bf35a61d28fd78faf17c8.ttf::U+0AC9,U+0020,U+0AC5,U+0ABE:[gid17=0+1044|gid14=0+543|gid1=1+612|gid17=1+1044|gid11=1+0|gid17=1+1044|gid10=1+543]
+../fonts/2c25beb56d9c556622d56b0b5d02b4670c034f89.ttf::U+0B06,U+0020,U+0B05,U+0B3E:[aaorya=0+1681|space=1+881|aorya=2+1284|uni25CC=2+1044|aavowelsignorya=2+387]
+../fonts/2c25beb56d9c556622d56b0b5d02b4670c034f89.ttf::U+0B10,U+0020,U+0B0F,U+0B57:[aiorya=0+1681|space=1+881|eorya=2+1315|uni25CC=2+1044|aulengthmarkorya=2+387]
+../fonts/2c25beb56d9c556622d56b0b5d02b4670c034f89.ttf::U+0B14,U+0020,U+0B13,U+0B57:[auorya=0+1679|space=1+881|oorya=2+1309|uni25CC=2+1044|aulengthmarkorya=2+387]
+../fonts/03e3f463c3a985bc42096620cc415342818454fb.ttf::U+0C13,U+0020,U+0C12,U+0C55:[gid3=0+1497|gid1=1+580|gid2=2+1497|gid13=2+1184|gid12=2+0]
+../fonts/03e3f463c3a985bc42096620cc415342818454fb.ttf::U+0C14,U+0020,U+0C12,U+0C4C:[gid4=0+1497|gid1=1+580|gid2=2+1497|gid13=2+1184|gid11=2+634]
+../fonts/03e3f463c3a985bc42096620cc415342818454fb.ttf::U+0C40,U+0020,U+0C3F,U+0C55:[gid13=0+1184|gid6=0+0|gid1=1+580|gid13=1+1184|gid5=1+0|gid13=1+1184|gid12=1+0]
+../fonts/03e3f463c3a985bc42096620cc415342818454fb.ttf::U+0C47,U+0020,U+0C46,U+0C55:[gid13=0+1184|gid8=0+0|gid1=1+580|gid13=1+1184|gid7=1+0|gid13=1+1184|gid12=1+0]
+../fonts/03e3f463c3a985bc42096620cc415342818454fb.ttf::U+0C4B,U+0020,U+0C4A,U+0C55:[gid13=0+1184|gid10=0+634|gid1=1+580|gid13=1+1184|gid9=1+634|gid13=1+1184|gid12=1+0]
+../fonts/7d18685e1529e4ceaad5b6095dfab2f9789e5bce.ttf::U+0C8A,U+0020,U+0C89,U+0CBE:[gid3=0+3269|gid1=1+590|gid2=2+2502|gid10=2+1184|gid7=2+919]
+../fonts/7d18685e1529e4ceaad5b6095dfab2f9789e5bce.ttf::U+0C94,U+0020,U+0C92,U+0CCC:[gid6=0+1596|gid1=1+590|gid5=2+1590|gid10=2+1184|gid8=2+880]
+../fonts/7d18685e1529e4ceaad5b6095dfab2f9789e5bce.ttf::U+0CE0,U+0020,U+0C8B,U+0CBE:[gid9=0+3214|gid1=1+590|gid4=2+2440|gid10=2+1184|gid7=2+919]
+../fonts/af85624080af5627fb050f570d148a62f04fda74.ttf::U+0D08,U+0020,U+0D07,U+0D57:[gid3=0+3574|gid1=1+632|gid2=2+2019|gid14=2+1184|gid13=2+1555]
+../fonts/af85624080af5627fb050f570d148a62f04fda74.ttf::U+0D0A,U+0020,U+0D09,U+0D57:[gid5=0+2972|gid1=1+632|gid4=2+1417|gid14=2+1184|gid13=2+1555]
+../fonts/af85624080af5627fb050f570d148a62f04fda74.ttf::U+0D10,U+0020,U+0D0E,U+0D46:[gid7=0+4073|gid1=1+632|gid6=2+2608|gid12=2+1465|gid14=2+1184]
+../fonts/af85624080af5627fb050f570d148a62f04fda74.ttf::U+0D13,U+0020,U+0D12,U+0D3E:[gid9=0+2557|gid1=1+632|gid8=2+1524|gid14=2+1184|gid11=2+1033]
+../fonts/af85624080af5627fb050f570d148a62f04fda74.ttf::U+0D14,U+0020,U+0D12,U+0D57:[gid10=0+3073|gid1=1+632|gid8=2+1524|gid14=2+1184|gid13=2+1555]
diff --git a/test/shaping/data/in-house/tests/khmer-mark-order.tests b/test/shaping/data/in-house/tests/khmer-mark-order.tests
new file mode 100644
index 0000000..d581dd1
--- /dev/null
+++ b/test/shaping/data/in-house/tests/khmer-mark-order.tests
@@ -0,0 +1,25 @@
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17CA,U+17BE,U+1794:[uni17C1=0+288|uni179F=0+928|uni17BB=0@-6,-26+0|uni17B8=0@-32,-29+0|uni1794=3+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17C9,U+17BE,U+17BB,U+1794:[uni17C1=0+288|uni179F=0+928|uni17C9=0@-32,-29+0|uni17B8=0@-32,237+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=4+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17C9,U+17C1,U+17B8,U+17BB,U+1794:[uni17C1=0+288|uni179F=0+928|uni17C9=0@-32,-29+0|uni17B8=0@-32,237+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=5+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17C9,U+17B8,U+17C1,U+17BB,U+1794:[uni179F=0+928|uni17C9=0@-32,-29+0|uni17B8=0@-32,237+0|uni17C1=0+288|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=5+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17C9,U+17BE,U+17BB,U+17BB,U+1794:[uni17C1=0+288|uni179F=0+928|uni17C9=0@-32,-29+0|uni17B8=0@-32,237+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=5+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17C9,U+17C1,U+17B8,U+17BB,U+17BB,U+1794:[uni17C1=0+288|uni179F=0+928|uni17C9=0@-32,-29+0|uni17B8=0@-32,237+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=6+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17C9,U+17B8,U+17C1,U+17BB,U+17BB,U+1794:[uni179F=0+928|uni17C9=0@-32,-29+0|uni17B8=0@-32,237+0|uni17C1=0+288|uni25CC=0+635|uni17BB=0@-20,-26+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=6+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17CA,U+17BE,U+17BB,U+1794:[uni17C1=0+288|uni179F=0+928|uni17BB=0@-6,-26+0|uni17B8=0@-32,-29+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=4+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17CA,U+17C1,U+17B8,U+17BB,U+1794:[uni17C1=0+288|uni179F=0+928|uni17BB=0@-6,-26+0|uni17B8=0@-32,-29+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=5+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17CA,U+17B8,U+17C1,U+17BB,U+1794:[uni179F=0+928|uni17BB=0@-6,-26+0|uni17B8=0@-32,-29+0|uni17C1=0+288|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=5+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17CA,U+17BE,U+17BB,U+17BB,U+1794:[uni17C1=0+288|uni179F=0+928|uni17BB=0@-6,-26+0|uni17B8=0@-32,-29+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=5+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17CA,U+17C1,U+17B8,U+17BB,U+17BB,U+1794:[uni17C1=0+288|uni179F=0+928|uni17BB=0@-6,-26+0|uni17B8=0@-32,-29+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=6+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17CA,U+17B8,U+17C1,U+17BB,U+17BB,U+1794:[uni179F=0+928|uni17BB=0@-6,-26+0|uni17B8=0@-32,-29+0|uni17C1=0+288|uni25CC=0+635|uni17BB=0@-20,-26+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=6+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17C9,U+17BE,U+17B8,U+1794:[uni17C1=0+288|uni179F=0+928|uni17C9=0@-32,-29+0|uni17B8=0@-32,237+0|uni25CC=0+635|uni17B8=0@-20,-84+0|uni1794=4+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17C9,U+17B8,U+17BE,U+1794:[uni179F=0+928|uni17C9=0@-32,-29+0|uni17B8=0@-32,237+0|uni17C1=0+288|uni25CC=0+635|uni17B8=0@-20,-84+0|uni1794=4+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17C9,U+17BE,U+17B8,U+17BB,U+1794:[uni17C1=0+288|uni179F=0+928|uni17C9=0@-32,-29+0|uni17B8=0@-32,237+0|uni25CC=0+635|uni17B8=0@-20,-84+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=5+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17C9,U+17B8,U+17BE,U+17BB,U+1794:[uni179F=0+928|uni17C9=0@-32,-29+0|uni17B8=0@-32,237+0|uni17C1=0+288|uni25CC=0+635|uni17B8=0@-20,-84+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=5+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17CA,U+17BE,U+17B8,U+1794:[uni17C1=0+288|uni179F=0+928|uni17BB=0@-6,-26+0|uni17B8=0@-32,-29+0|uni25CC=0+635|uni17B8=0@-20,-84+0|uni1794=4+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17CA,U+17B8,U+17BE,U+1794:[uni179F=0+928|uni17BB=0@-6,-26+0|uni17B8=0@-32,-29+0|uni17C1=0+288|uni25CC=0+635|uni17B8=0@-20,-84+0|uni1794=4+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17CA,U+17BE,U+17B8,U+17BB,U+1794:[uni17C1=0+288|uni179F=0+928|uni17BB=0@-6,-26+0|uni17B8=0@-32,-29+0|uni25CC=0+635|uni17B8=0@-20,-84+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=5+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17CA,U+17B8,U+17BE,U+17BB,U+1794:[uni179F=0+928|uni17BB=0@-6,-26+0|uni17B8=0@-32,-29+0|uni17C1=0+288|uni25CC=0+635|uni17B8=0@-20,-84+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=5+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17BE,U+17B8,U+17BB,U+1794:[uni17C1=0+288|uni179F=0+928|uni17B8=0@-32,-29+0|uni25CC=0+635|uni17B8=0@-20,-84+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=4+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17BE,U+17BB,U+17B8,U+1794:[uni17C1=0+288|uni179F=0+928|uni17B8=0@-32,-29+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni17B8=0@-20,-84+0|uni1794=4+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17B8,U+17BE,U+17BB,U+1794:[uni179F=0+928|uni17B8=0@-32,-29+0|uni17C1=0+288|uni25CC=0+635|uni17B8=0@-20,-84+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni1794=4+635]
+../fonts/b6031119874ae9ff1dd65383a335e361c0962220.ttf::U+179F,U+17B8,U+17BB,U+17BE,U+1794:[uni179F=0+928|uni17B8=0@-32,-29+0|uni25CC=0+635|uni17BB=0@-20,-26+0|uni17C1=0+288|uni25CC=0+635|uni17B8=0@-20,-84+0|uni1794=4+635]
diff --git a/test/shaping/data/in-house/tests/language-tags.tests b/test/shaping/data/in-house/tests/language-tags.tests
index 4c62113..c7be180 100644
--- a/test/shaping/data/in-house/tests/language-tags.tests
+++ b/test/shaping/data/in-house/tests/language-tags.tests
@@ -10,3 +10,4 @@
 ../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-HK:U+004A:[gid6=0+1000]
 ../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-mo:U+004A:[gid6=0+1000]
 ../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-Hant-mo:U+004A:[gid6=0+1000]
+../fonts/d3129450fafe5e5c98cfc25a4e71809b1b4d2855.ttf:--language=dv --no-glyph-names:U+007C:[2=0+156]
diff --git a/test/shaping/data/in-house/tests/mongolian-variation-selector.tests b/test/shaping/data/in-house/tests/mongolian-variation-selector.tests
index efb4cf4..c5e35c8 100644
--- a/test/shaping/data/in-house/tests/mongolian-variation-selector.tests
+++ b/test/shaping/data/in-house/tests/mongolian-variation-selector.tests
@@ -1,4 +1,19 @@
 ../fonts/37033cc5cf37bb223d7355153016b6ccece93b28.ttf::U+1826,U+180B,U+1826:[uni1826.E85E_ue.init1=0+599|uni1826.E856_ue.fina=2+750]
 ../fonts/ef86fe710cfea877bbe0dbb6946a1f88d0661031.ttf::U+1820,U+180B:[uni1820.E821_a.isol1=0+1199]
-../fonts/bb29ce50df2bdba2d10726427c6b7609bf460e04.ttf::U+183A,U+1823,U+182E,U+182B,U+1822,U+1826,U+180B,U+1832,U+180B,U+1827,U+1837,U+0020,U+182D,U+182D,U+180B,U+0020,U+182D,U+180C,U+0020,U+182D,U+180D,U+200D,U+0020,U+182D,U+200D,U+182D,U+180B,U+200D,U+0020,U+182D,U+180C,U+200D,U+0020,U+182D,U+180D,U+200D,U+0020,U+200D,U+182D,U+200D,U+200D,U+182D,U+180B,U+200D,U+0020,U+200D,U+182D,U+180C,U+200D,U+0020,U+200D,U+182D,U+180D,U+200D,U+0020,U+200D,U+182D,U+200D,U+182D,U+180B,U+0020,U+200D,U+182D,U+180C,U+0020,U+1820,U+200C,U+182D,U+1820,U+1837,U+0020,U+1830,U+1824,U+1837,U+200D,U+200D,U+182D,U+1820,U+200D,U+0020,U+200D,U+182D,U+1824,U+182F,U+1822,U+0020,U+182A,U+1820,U+1822,U+182D,U+180E,U+1820,U+202F,U+1836,U+1822,U+1828:[uni183A1823.E971_ko.init=0+950|uni182E.E904_m.medi=2+400|uni182B1822.E8A6_pi.medi=3+1150|uni1826.E854_ue.medi1=5+1100|uni1832.E916_t.medi1=7+1000|uni1827.E85C_ee.medi=9+750|uni1837.E931_r.fina=10+750|space=11+500|uni182D.E8E2_g.init=12+1000|uni182D.E8E8_g.fina1=13+1250|space=15+500|uni182D.EA1B_g.isol2=16+1000|space=18+500|uni182D.EA1E_g.init3=19+650|space=19+0|space=22+500|uni182D.E8E2_g.init=23+1000|space=23+0|uni182D.E8E5_g.medi1=25+800|space=25+0|space=28+500|uni182D.EA1D_g.init2=29+950|space=29+0|space=32+500|uni182D.EA1E_g.init3=33+650|space=33+0|space=36+500|space=36+0|uni182D.E8E4_g.medi=38+800|space=38+0|space=38+0|uni182D.E8E5_g.medi1=41+800|space=41+0|space=44+500|space=44+0|uni182D.E8E6_g.medi2=46+650|space=46+0|space=49+500|space=49+0|uni182D.E8E6_g.medi2=51+650|space=51+0|space=54+500|space=54+0|uni182D.E8E4_g.medi=56+800|space=56+0|uni182D.E8E8_g.fina1=58+1250|space=60+500|space=60+0|uni182D.E8E9_g.fina2=62+1050|space=64+500|uni1820.E820_a.isol=65+1550|space=65+0|uni182D.E8E2_g.init=67+1000|uni1820.E823_a.medi=68+400|uni1837.E931_r.fina=69+750|space=70+500|uni1830.E90B_s.init=71+850|uni1824.E844_u.medi=72+600|uni1837.E930_r.medi=73+600|space=73+0|space=73+0|uni182D.E8E5_g.medi1=76+800|uni1820.E823_a.medi=77+400|space=77+0|space=79+500|space=79+0|uni182D.E8E5_g.medi1=81+800|uni1824.E844_u.medi=82+600|uni182F.E908_l.medi=83+400|uni1822.E837_i.fina=84+600|space=85+500|uni182A1820.E875_ba.init=86+1000|uni1822.E836_i.medi2=88+1000|uni182D.E8E8_g.fina1=89+1250|space=90+0|uni1820.E827_a.fina2=91+600|uni202F.nobreak=92+500|uni1836.E92B_y.init1=93+500|uni1822.E834_i.medi=94+500|uni1828.E866_n.fina=95+850]
 ../fonts/a34a7b00f22ffb5fd7eef6933b81c7e71bc2cdfb.ttf::U+180A,U+1868,U+180A,U+1868,U+180B,U+180A,U+1868,U+180C,U+180A,U+1868,U+180D,U+180A:[gid1=0+268|gid10=1+778|gid1=2+268|gid9=3+575|gid1=5+268|gid10=6+778|gid1=8+268|gid8=9+575|gid1=11+268]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+183A,U+1823,U+182E,U+182B,U+1822,U+1826,U+180B,U+1832,U+180B,U+1827,U+1837:[uni183A1823.E971_ko.init=0+950|uni182E.E904_m.medi=2+400|uni182B1822.E8A6_pi.medi=3+1150|uni1826.E854_ue.medi1=5+1100|uni1832.E916_t.medi1=7+1000|uni1827.E85C_ee.medi=9+750|uni1837.E931_r.fina=10+750]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+182D,U+182D,U+180B:[uni182D.E8E2_g.init=0+1000|uni182D.E8E8_g.fina1=1+1250]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+182D,U+180C:[uni182D.EA1B_g.isol2=0+1000]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+182D,U+180D,U+200D:[uni182D.EA1E_g.init3=0+650|space=0+0]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+182D,U+200D,U+182D,U+180B,U+200D:[uni182D.E8E2_g.init=0+1000|space=0+0|uni182D.E8E5_g.medi1=2+800|space=2+0]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+182D,U+180C,U+200D:[uni182D.EA1D_g.init2=0+950|space=0+0]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+182D,U+180D,U+200D:[uni182D.EA1E_g.init3=0+650|space=0+0]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+200D,U+182D,U+200D,U+200D,U+182D,U+180B,U+200D:[space=0+0|uni182D.E8E4_g.medi=1+800|space=1+0|space=1+0|uni182D.E8E5_g.medi1=4+800|space=4+0]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+200D,U+182D,U+180C,U+200D:[space=0+0|uni182D.E8E6_g.medi2=1+650|space=1+0]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+200D,U+182D,U+180D,U+200D:[space=0+0|uni182D.E8E6_g.medi2=1+650|space=1+0]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+200D,U+182D,U+200D,U+182D,U+180B:[space=0+0|uni182D.E8E4_g.medi=1+800|space=1+0|uni182D.E8E8_g.fina1=3+1250]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+200D,U+182D,U+180C:[space=0+0|uni182D.E8E9_g.fina2=1+1050]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+1820,U+200C,U+182D,U+1820,U+1837:[uni1820.E820_a.isol=0+1550|space=1+0|uni182D.E8E2_g.init=2+1000|uni1820.E823_a.medi=3+400|uni1837.E931_r.fina=4+750]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+1830,U+1824,U+1837,U+200D,U+200D,U+182D,U+1820,U+200D:[uni1830.E90B_s.init=0+850|uni1824.E844_u.medi=1+600|uni1837.E930_r.medi=2+600|space=2+0|space=2+0|uni182D.E8E5_g.medi1=5+800|uni1820.E823_a.medi=6+400|space=6+0]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+200D,U+182D,U+1824,U+182F,U+1822:[space=0+0|uni182D.E8E5_g.medi1=1+800|uni1824.E844_u.medi=2+600|uni182F.E908_l.medi=3+400|uni1822.E837_i.fina=4+600]
+../fonts/4d4206e30b2dbf1c1ef492a8eae1c9e7829ebad8.ttf::U+182A,U+1820,U+1822,U+182D,U+180E,U+1820,U+202F,U+1836,U+1822,U+1828:[uni182A1820.E875_ba.init=0+1000|uni1822.E836_i.medi2=2+1000|uni182D.E8E8_g.fina1=3+1250|space=4+0|uni1820.E827_a.fina2=5+600|uni202F.nobreak=6+500|uni1836.E92B_y.init1=7+500|uni1822.E834_i.medi=8+500|uni1828.E866_n.fina=9+850]
diff --git a/test/shaping/data/in-house/tests/myanmar-zawgyi.tests b/test/shaping/data/in-house/tests/myanmar-zawgyi.tests
new file mode 100644
index 0000000..b79d4fb
--- /dev/null
+++ b/test/shaping/data/in-house/tests/myanmar-zawgyi.tests
@@ -0,0 +1 @@
+../fonts/ab14b4eb9d7a67e293f51d30d719add06c9d6e06.ttf:--script=Qaag:U+1000,U+103A,U+1004,U+1037,U+1039,U+1041:[Ka=0+2217|Ya-Semivowel=0+286|Nga=2+1247|Dot Below=2+0|Virama-Killer=2+0|One-Myanmar=5+1247]
diff --git a/test/shaping/data/in-house/tests/rand.tests b/test/shaping/data/in-house/tests/rand.tests
new file mode 100644
index 0000000..df324b9
--- /dev/null
+++ b/test/shaping/data/in-house/tests/rand.tests
@@ -0,0 +1,3 @@
+../fonts/5bb74492f5e0ffa1fbb72e4c881be035120b6513.ttf:--no-glyph-names --features=-rand:U+0054,U+0055,U+0056:[1=0+560|2=1+602|3=2+602]
+../fonts/5bb74492f5e0ffa1fbb72e4c881be035120b6513.ttf:--no-glyph-names --features=rand=2:U+0054,U+0055,U+0056:[5=0+560|8=1+602|11=2+602]
+../fonts/5bb74492f5e0ffa1fbb72e4c881be035120b6513.ttf:--no-glyph-names:U+0054,U+0055,U+0056,U+0054,U+0055,U+0056,U+0054,U+0055,U+0056,U+0054,U+0055,U+0056:[5=0+560|7=1+602|10=2+602|4=3+560|7=4+602|10=5+602|6=6+560|9=7+602|10=8+602|5=9+560|8=10+602|12=11+602]
diff --git a/test/shaping/data/in-house/tests/use-indic3.tests b/test/shaping/data/in-house/tests/use-indic3.tests
new file mode 100644
index 0000000..8c3ae13
--- /dev/null
+++ b/test/shaping/data/in-house/tests/use-indic3.tests
@@ -0,0 +1 @@
+../fonts/3c96e7a303c58475a8c750bf4289bbe73784f37d.ttf::U+0C95,U+0CCD,U+0CB0:[uni0C95=0+1176|uni0CB0_uni0CCD.blwf=0+275]
diff --git a/test/shaping/data/in-house/tests/use-syllable.tests b/test/shaping/data/in-house/tests/use-syllable.tests
index 5d2fab3..d8f1dff 100644
--- a/test/shaping/data/in-house/tests/use-syllable.tests
+++ b/test/shaping/data/in-house/tests/use-syllable.tests
@@ -6,3 +6,6 @@
 ../fonts/373e67bf41ca264e260a9716162b71a23549e885.ttf:--no-glyph-names:U+A8AC,U+A8B4,U+A8B5:[2=0+377|3=0+242|4=0+210]
 ../fonts/59a585a63b3df608fbeef00956c8c108deec7de6.ttf:--no-glyph-names:U+1BC7,U+1BEA,U+1BF3:[1=0+749|2=0+402|4=0+535|3=0+401]
 ../fonts/1ed7e9064f008f62de6ff0207bb4dd29409597a5.ttf::U+11064,U+1107F,U+11052,U+11065,U+1107F,U+11053:[brm_num100.1=0+2224|brm_num1000.2=3+1834]
+../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]
diff --git a/test/shaping/data/in-house/tests/vertical.tests b/test/shaping/data/in-house/tests/vertical.tests
index 17df28e..b181192 100644
--- a/test/shaping/data/in-house/tests/vertical.tests
+++ b/test/shaping/data/in-house/tests/vertical.tests
@@ -1,3 +1,3 @@
 ../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,-2189+0,-2789|gid2=1@-665,-2189+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]
diff --git a/test/shaping/data/text-rendering-tests/DISABLED b/test/shaping/data/text-rendering-tests/DISABLED
index 58de258..ef987a4 100644
--- a/test/shaping/data/text-rendering-tests/DISABLED
+++ b/test/shaping/data/text-rendering-tests/DISABLED
@@ -1,34 +1,8 @@
+tests/MORX-41.tests
+
 # Non-Unicode cmap
 tests/CMAP-3.tests
 
-# Not hooked up
-tests/MORX-1.tests
-tests/MORX-2.tests
-tests/MORX-3.tests
-tests/MORX-4.tests
-tests/MORX-5.tests
-tests/MORX-6.tests
-tests/MORX-7.tests
-tests/MORX-8.tests
-tests/MORX-9.tests
-tests/MORX-10.tests
-tests/MORX-11.tests
-tests/MORX-12.tests
-tests/MORX-13.tests
-tests/MORX-14.tests
-tests/MORX-16.tests
-tests/MORX-17.tests
-tests/MORX-18.tests
-tests/MORX-19.tests
-tests/MORX-20.tests
-tests/MORX-21.tests
-tests/MORX-22.tests
-tests/MORX-23.tests
-tests/MORX-25.tests
-tests/MORX-26.tests
-tests/MORX-27.tests
-tests/MORX-28.tests
-
 # Rounding differences
 tests/SHARAN-1.tests
 tests/SHBALI-1.tests
diff --git a/test/shaping/data/text-rendering-tests/Makefile.sources b/test/shaping/data/text-rendering-tests/Makefile.sources
index 4be9d05..5e0db6b 100644
--- a/test/shaping/data/text-rendering-tests/Makefile.sources
+++ b/test/shaping/data/text-rendering-tests/Makefile.sources
@@ -15,6 +15,7 @@
 	tests/GPOS-5.tests \
 	tests/GSUB-1.tests \
 	tests/GSUB-2.tests \
+	tests/GSUB-3.tests \
 	tests/GVAR-1.tests \
 	tests/GVAR-2.tests \
 	tests/GVAR-3.tests \
@@ -28,12 +29,6 @@
 	tests/HVAR-2.tests \
 	tests/KERN-1.tests \
 	tests/KERN-2.tests \
-	tests/SHBALI-3.tests \
-	tests/SHKNDA-1.tests \
-	$(NULL)
-
-DISBALED_TESTS = \
-	tests/CMAP-3.tests \
 	tests/MORX-10.tests \
 	tests/MORX-11.tests \
 	tests/MORX-12.tests \
@@ -48,18 +43,38 @@
 	tests/MORX-21.tests \
 	tests/MORX-22.tests \
 	tests/MORX-23.tests \
+	tests/MORX-24.tests \
 	tests/MORX-25.tests \
 	tests/MORX-26.tests \
 	tests/MORX-27.tests \
 	tests/MORX-28.tests \
+	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 \
+	tests/MORX-35.tests \
+	tests/MORX-36.tests \
+	tests/MORX-37.tests \
+	tests/MORX-38.tests \
+	tests/MORX-39.tests \
 	tests/MORX-3.tests \
+	tests/MORX-40.tests \
 	tests/MORX-4.tests \
 	tests/MORX-5.tests \
 	tests/MORX-6.tests \
 	tests/MORX-7.tests \
 	tests/MORX-8.tests \
 	tests/MORX-9.tests \
+	tests/SHBALI-3.tests \
+	tests/SHKNDA-1.tests \
+	$(NULL)
+
+DISBALED_TESTS = \
+	tests/MORX-41.tests \
+	tests/CMAP-3.tests \
 	tests/SHARAN-1.tests \
 	tests/SHBALI-1.tests \
 	tests/SHBALI-2.tests \
diff --git a/test/shaping/data/text-rendering-tests/extract-tests.py b/test/shaping/data/text-rendering-tests/extract-tests.py
index 36963e5..27d5686 100755
--- a/test/shaping/data/text-rendering-tests/extract-tests.py
+++ b/test/shaping/data/text-rendering-tests/extract-tests.py
@@ -28,12 +28,13 @@
 
 html = ET.fromstring(sys.stdin.read())
 found = False
+
 for elt in html.findall(".//*[@class='expected'][@ft:id]", namespaces):
 	found = True
 	name = elt.get(ns('ft:id'))
 	text = elt.get(ns('ft:render'))
 	font = elt.get(ns('ft:font'))
-	vars = elt.get(ns('ft:var'), '').replace(':', '=').replace(';', ',')
+	variations = elt.get(ns('ft:var'), '').replace(':', '=').replace(';', ',')
 	glyphs = []
 	for use in elt.findall(".//use"):
 		x = int(use.get('x'))
@@ -43,8 +44,19 @@
 		glyphname = '.'.join(href[1:].split('/')[1].split('.')[1:])
 		glyphs.append((glyphname, x, y))
 	opts = '--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft'
-	if vars:
-		opts = opts + ' --variations=%s' % vars
+	if variations:
+		opts = opts + ' --variations=%s' % variations
 	print ("../fonts/%s:%s:%s:%s" % (font, opts, unistr(text), glyphstr(glyphs)))
 
+for elt in html.findall(".//*[@class='should-not-crash'][@ft:id]", namespaces):
+	found = True
+	name = elt.get(ns('ft:id'))
+	text = elt.get(ns('ft:render'))
+	font = elt.get(ns('ft:font'))
+	variations = elt.get(ns('ft:var'), '').replace(':', '=').replace(';', ',')
+	opts = ''
+	if variations:
+		opts = '--variations=%s' % variations
+	print ("../fonts/%s:%s:%s:*" % (font, opts, unistr(text)))
+
 sys.exit(0 if found else 1)
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGSUBThree.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGSUBThree.ttf
new file mode 100644
index 0000000..8fce4ac
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGSUBThree.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXForty.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXForty.ttf
new file mode 100644
index 0000000..37d0b63
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXForty.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXFourtyone.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXFourtyone.ttf
new file mode 100644
index 0000000..98ebe33
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXFourtyone.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyeight.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyeight.ttf
new file mode 100644
index 0000000..29a41d0
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyeight.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyfive.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyfive.ttf
new file mode 100644
index 0000000..f157063
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyfive.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyfour.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyfour.ttf
new file mode 100644
index 0000000..a70dadc
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyfour.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtynine.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtynine.ttf
new file mode 100644
index 0000000..c106ae9
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtynine.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyone.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyone.ttf
new file mode 100644
index 0000000..c64c12c
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyone.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyseven.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyseven.ttf
new file mode 100644
index 0000000..22057f1
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtyseven.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtysix.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtysix.ttf
new file mode 100644
index 0000000..6676e52
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtysix.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtythree.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtythree.ttf
new file mode 100644
index 0000000..5cab73e
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtythree.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtytwo.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtytwo.ttf
new file mode 100644
index 0000000..07ed76c
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirtytwo.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentyfour.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentyfour.ttf
new file mode 100644
index 0000000..271dddb
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentyfour.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentynine.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentynine.ttf
new file mode 100644
index 0000000..9f015ca
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentynine.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/tests/GSUB-3.tests b/test/shaping/data/text-rendering-tests/tests/GSUB-3.tests
new file mode 100644
index 0000000..c2f7e6e
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GSUB-3.tests
@@ -0,0 +1 @@
+../fonts/TestGSUBThree.ttf::U+006C,U+006F,U+006C:*
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-24.tests b/test/shaping/data/text-rendering-tests/tests/MORX-24.tests
new file mode 100644
index 0000000..79a3d7b
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-24.tests
@@ -0,0 +1 @@
+../fonts/TestMORXTwentyfour.ttf::U+0041,U+0042,U+0043,U+0044,U+0045:*
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-29.tests b/test/shaping/data/text-rendering-tests/tests/MORX-29.tests
new file mode 100644
index 0000000..82fd963
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-29.tests
@@ -0,0 +1,4 @@
+../fonts/TestMORXTwentynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+004D,U+004D,U+0058,U+0058,U+004D,U+004D,U+0059,U+0059,U+0041,U+005A,U+005A:[P|Q@333,0|R@699,0|M@1050,0|M@1880,0|X@2710,0|X@3074,0|M@3438,0|I@4268,0|N@5098,0|S@5928,0|M@6758,0|Y@7588,0|Y@7920,0|A@8252,0|Z@9082,0|Z@9404,0]
+../fonts/TestMORXTwentynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+004D,U+004D,U+0058,U+0058,U+004D,U+004D,U+0059,U+0059,U+0042,U+005A,U+005A:[P|Q@333,0|R@699,0|M@1050,0|M@1880,0|X@2710,0|X@3074,0|M@3438,0|M@4268,0|I@5098,0|N@5928,0|S@6758,0|Y@7588,0|Y@7920,0|B@8252,0|Z@9082,0|Z@9404,0]
+../fonts/TestMORXTwentynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+004D,U+004D,U+0058,U+0058,U+004D,U+004D,U+0059,U+0059,U+0043,U+005A,U+005A:[P|Q@333,0|R@699,0|M@1050,0|M@1880,0|X@2710,0|X@3074,0|M@3438,0|M@4268,0|Y@5098,0|Y@5430,0|I@5762,0|N@6592,0|S@7422,0|C@8252,0|Z@9082,0|Z@9404,0]
+../fonts/TestMORXTwentynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+004D,U+004D,U+0058,U+0058,U+004D,U+004D,U+0059,U+0059,U+0044,U+005A,U+005A:[P|Q@333,0|R@699,0|M@1050,0|M@1880,0|X@2710,0|X@3074,0|M@3438,0|M@4268,0|Y@5098,0|Y@5430,0|D@5762,0|I@6592,0|N@7422,0|S@8252,0|Z@9082,0|Z@9404,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-30.tests b/test/shaping/data/text-rendering-tests/tests/MORX-30.tests
new file mode 100644
index 0000000..ad4ab21
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-30.tests
@@ -0,0 +1,4 @@
+../fonts/TestMORXTwentynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+004D,U+004D,U+0058,U+0058,U+0058,U+0041,U+0059,U+0059,U+0041,U+005A,U+005A:[P|Q@333,0|R@699,0|M@1050,0|I@1880,0|N@2710,0|S@3540,0|I@4370,0|N@5200,0|S@6030,0|M@6860,0|X@7690,0|X@8054,0|X@8418,0|A@8782,0|Y@9612,0|Y@9944,0|A@10276,0|Z@11106,0|Z@11428,0]
+../fonts/TestMORXTwentynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+004D,U+004D,U+0058,U+0058,U+0058,U+0041,U+0059,U+0059,U+0042,U+005A,U+005A:[P|Q@333,0|R@699,0|M@1050,0|I@1880,0|I@2710,0|N@3540,0|S@4370,0|N@5200,0|S@6030,0|M@6860,0|X@7690,0|X@8054,0|X@8418,0|A@8782,0|Y@9612,0|Y@9944,0|B@10276,0|Z@11106,0|Z@11428,0]
+../fonts/TestMORXTwentynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+004D,U+004D,U+0058,U+0058,U+0058,U+0042,U+0059,U+0059,U+0041,U+005A,U+005A:[P|Q@333,0|R@699,0|M@1050,0|I@1880,0|N@2710,0|S@3540,0|M@4370,0|I@5200,0|N@6030,0|S@6860,0|X@7690,0|X@8054,0|X@8418,0|B@8782,0|Y@9612,0|Y@9944,0|A@10276,0|Z@11106,0|Z@11428,0]
+../fonts/TestMORXTwentynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+004D,U+004D,U+0058,U+0058,U+0058,U+0042,U+0059,U+0059,U+0042,U+005A,U+005A:[P|Q@333,0|R@699,0|M@1050,0|M@1880,0|I@2710,0|N@3540,0|S@4370,0|I@5200,0|N@6030,0|S@6860,0|X@7690,0|X@8054,0|X@8418,0|B@8782,0|Y@9612,0|Y@9944,0|B@10276,0|Z@11106,0|Z@11428,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-31.tests b/test/shaping/data/text-rendering-tests/tests/MORX-31.tests
new file mode 100644
index 0000000..6cc40b6
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-31.tests
@@ -0,0 +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]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-32.tests b/test/shaping/data/text-rendering-tests/tests/MORX-32.tests
new file mode 100644
index 0000000..87c1152
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-32.tests
@@ -0,0 +1,4 @@
+../fonts/TestMORXThirtytwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041:[A]
+../fonts/TestMORXThirtytwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0041,U+0059:[X|A@364,0|Y@1194,0]
+../fonts/TestMORXThirtytwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0042:[B]
+../fonts/TestMORXThirtytwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0042,U+0059:[X|B@364,0|Y@1194,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-33.tests b/test/shaping/data/text-rendering-tests/tests/MORX-33.tests
new file mode 100644
index 0000000..17d080a
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-33.tests
@@ -0,0 +1,3 @@
+../fonts/TestMORXThirtythree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0068,U+0061:[h|a@618,0|h@1179,0|a@1797,0]
+../fonts/TestMORXThirtythree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0068,U+0061,U+0068,U+0061:[h|a@618,0|h@1179,0|a@1797,0|h@2358,0|a@2976,0|h@3537,0|a@4155,0]
+../fonts/TestMORXThirtythree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0061,U+0068:[a|h@561,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-34.tests b/test/shaping/data/text-rendering-tests/tests/MORX-34.tests
new file mode 100644
index 0000000..8c309df
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-34.tests
@@ -0,0 +1 @@
+../fonts/TestMORXThirtyfour.ttf::U+0068,U+0061:*
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-35.tests b/test/shaping/data/text-rendering-tests/tests/MORX-35.tests
new file mode 100644
index 0000000..a033185
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-35.tests
@@ -0,0 +1,2 @@
+../fonts/TestMORXThirtyfive.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041:[A|B@639,0|C@1265,0|E@1861,0]
+../fonts/TestMORXThirtyfive.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0041,U+0059:[X|A@586,0|B@1225,0|C@1851,0|E@2447,0|Y@3003,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-36.tests b/test/shaping/data/text-rendering-tests/tests/MORX-36.tests
new file mode 100644
index 0000000..6b2340e
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-36.tests
@@ -0,0 +1 @@
+../fonts/TestMORXThirtysix.ttf::U+0041:*
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-37.tests b/test/shaping/data/text-rendering-tests/tests/MORX-37.tests
new file mode 100644
index 0000000..f28c5e2
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-37.tests
@@ -0,0 +1,4 @@
+../fonts/TestMORXThirtyseven.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042:[A.alt|B.alt@1000,0]
+../fonts/TestMORXThirtyseven.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0042,U+0041:[B|A@650,0]
+../fonts/TestMORXThirtyseven.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+05D0,U+05D1:[uni05D1|uni05D0@542,0]
+../fonts/TestMORXThirtyseven.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+05D1,U+05D0:[uni05D0.alt|uni05D1.alt@1000,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-38.tests b/test/shaping/data/text-rendering-tests/tests/MORX-38.tests
new file mode 100644
index 0000000..abefe29
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-38.tests
@@ -0,0 +1,4 @@
+../fonts/TestMORXThirtyeight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042:[A.alt|B.alt@1000,0]
+../fonts/TestMORXThirtyeight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0042,U+0041:[B|A@650,0]
+../fonts/TestMORXThirtyeight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+05D0,U+05D1:[uni05D1.alt|uni05D0.alt@1000,0]
+../fonts/TestMORXThirtyeight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+05D1,U+05D0:[uni05D0|uni05D1@606,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-39.tests b/test/shaping/data/text-rendering-tests/tests/MORX-39.tests
new file mode 100644
index 0000000..83bfa52
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-39.tests
@@ -0,0 +1,4 @@
+../fonts/TestMORXThirtynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042:[A|B@639,0]
+../fonts/TestMORXThirtynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0042,U+0041:[B.alt|A.alt@1000,0]
+../fonts/TestMORXThirtynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+05D0,U+05D1:[uni05D1.alt|uni05D0.alt@1000,0]
+../fonts/TestMORXThirtynine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+05D1,U+05D0:[uni05D0|uni05D1@606,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-40.tests b/test/shaping/data/text-rendering-tests/tests/MORX-40.tests
new file mode 100644
index 0000000..c99155e
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-40.tests
@@ -0,0 +1,4 @@
+../fonts/TestMORXForty.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042:[A|B@639,0]
+../fonts/TestMORXForty.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0042,U+0041:[B.alt|A.alt@1000,0]
+../fonts/TestMORXForty.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+05D0,U+05D1:[uni05D1|uni05D0@542,0]
+../fonts/TestMORXForty.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+05D1,U+05D0:[uni05D0.alt|uni05D1.alt@1000,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-41.tests b/test/shaping/data/text-rendering-tests/tests/MORX-41.tests
new file mode 100644
index 0000000..84dca89
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-41.tests
@@ -0,0 +1,4 @@
+../fonts/TestMORXFourtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0061,U+0063:[a_c]
+../fonts/TestMORXFourtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0062,U+0063:[b_c]
+../fonts/TestMORXFourtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0063,U+0063:[c]
+../fonts/TestMORXFourtyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0061,U+0062,U+0063,U+0063:[a|b_c@561,0|c@1631,0]
diff --git a/test/shaping/hb_test_tools.py b/test/shaping/hb_test_tools.py
index 8348dc2..c9a4403 100644
--- a/test/shaping/hb_test_tools.py
+++ b/test/shaping/hb_test_tools.py
@@ -4,6 +4,10 @@
 
 import sys, os, re, difflib, unicodedata, errno, cgi
 from itertools import *
+try:
+	import unicodedata2 as unicodedata
+except Exception:
+	pass
 
 diff_symbols = "-+=*&^%$#@!~/"
 diff_colors = ['red', 'green', 'blue']
diff --git a/test/shaping/record-test.sh b/test/shaping/record-test.sh
index 93ebcfc..4ab74f0 100755
--- a/test/shaping/record-test.sh
+++ b/test/shaping/record-test.sh
@@ -3,8 +3,9 @@
 dir=`mktemp -d`
 
 out=/dev/stdout
-if test "x${1:0:3}" == 'x-o='; then
-	out=${1:3}
+if test "x$1" == 'x-o'; then
+	shift
+	out=$1
 	shift
 fi
 hb_shape=$1
diff --git a/test/shaping/run-tests.py b/test/shaping/run-tests.py
index 73b61c2..f77a17c 100755
--- a/test/shaping/run-tests.py
+++ b/test/shaping/run-tests.py
@@ -2,16 +2,16 @@
 
 from __future__ import print_function, division, absolute_import
 
-import sys, os, subprocess
+import sys, os, subprocess, tempfile
 
 
 def cmd(command):
-	p = subprocess.Popen (
-		command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-	p.wait ()
-	print (p.stderr.read (), end="") # file=sys.stderr
-	return p.stdout.read ().decode ("utf-8").strip (), p.returncode
-
+	# 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
 
 args = sys.argv[1:]
 if not args or sys.argv[1].find('hb-shape') == -1 or not os.path.exists (sys.argv[1]):
@@ -19,8 +19,6 @@
 	sys.exit (1)
 hb_shape, args = args[0], args[1:]
 
-extra_options = "--verify"
-
 fails = 0
 
 reference = False
@@ -48,6 +46,11 @@
 		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 not reference:
 				print ("# %s %s --unicodes %s" % (hb_shape, fontfile, unicodes))
@@ -55,19 +58,19 @@
 
 		if not reference:
 			print ("%s %s %s %s --unicodes %s" %
-					 (hb_shape, fontfile, extra_options, options, unicodes))
+					 (hb_shape, fontfile, ' '.join(extra_options), options, unicodes))
 
 		glyphs1, returncode = cmd ([hb_shape, "--font-funcs=ft",
-			fontfile, extra_options, "--unicodes",
+			fontfile] + extra_options + ["--unicodes",
 			unicodes] + (options.split (' ') if options else []))
 
 		if returncode:
-			print ("hb-shape --font-funcs=ft failed.") # file=sys.stderr
+			print ("ERROR: hb-shape --font-funcs=ft failed.") # file=sys.stderr
 			fails = fails + 1
 			#continue
 
 		glyphs2, returncode = cmd ([hb_shape, "--font-funcs=ot",
-			fontfile, extra_options, "--unicodes",
+			fontfile] + extra_options + ["--unicodes",
 			unicodes] + (options.split (' ') if options else []))
 
 		if returncode:
@@ -75,7 +78,7 @@
 			fails = fails + 1
 			#continue
 
-		if glyphs1 != glyphs2:
+		if glyphs1 != glyphs2 and glyphs_expected != '*':
 			print ("FT funcs: " + glyphs1) # file=sys.stderr
 			print ("OT funcs: " + glyphs2) # file=sys.stderr
 			fails = fails + 1
@@ -84,7 +87,7 @@
 			print (":".join ([fontfile, options, unicodes, glyphs1]))
 			continue
 
-		if glyphs1.strip() != glyphs_expected.strip():
+		if glyphs1.strip() != glyphs_expected and glyphs_expected != '*':
 			print ("Actual:   " + glyphs1) # file=sys.stderr
 			print ("Expected: " + glyphs_expected) # file=sys.stderr
 			fails = fails + 1
diff --git a/test/shaping/texts/in-house/shaper-indic/script-bengali/bengali-vowel-letters.txt b/test/shaping/texts/in-house/shaper-indic/script-bengali/bengali-vowel-letters.txt
new file mode 100644
index 0000000..f09dbc8
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-indic/script-bengali/bengali-vowel-letters.txt
@@ -0,0 +1,3 @@
+আ অা
+ৠ ঋৃ
+ৡ ঌৢ
diff --git a/test/shaping/texts/in-house/shaper-indic/script-devanagari/devanagari-atomic-consonants.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/devanagari-atomic-consonants.txt
new file mode 100644
index 0000000..4265436
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-indic/script-devanagari/devanagari-atomic-consonants.txt
@@ -0,0 +1,33 @@
+ख ख्ा ख्‍ा
+ग ग्ा ग्‍ा
+घ घ्ा घ्‍ा
+च च्ा च्‍ा
+ज ज्ा ज्‍ा
+झ झ्ा झ्‍ा
+ञ ञ्ा ञ्‍ा
+ण ण्ा ण्‍ा
+त त्ा त्‍ा
+थ थ्ा थ्‍ा
+ध ध्ा ध्‍ा
+न न्ा न्‍ा
+ऩ ऩ्ा ऩ्‍ा ऩ्ा ऩ्‍ा
+प प्ा प्‍ा
+ब ब्ा ब्‍ा
+भ भ्ा भ्‍ा
+म म्ा म्‍ा
+य य्ा य्‍ा
+ल ल्ा ल्‍ा
+व व्ा व्‍ा
+श श्ा श्‍ा
+ष ष्ा ष्‍ा
+स स्ा स्‍ा
+ख़ ख़्ा ख़्‍ा ख़्ा ख़्‍ा
+ग़ ग़्ा ग़्‍ा ग़्ा ग़्‍ा
+ज़ ज़्ा ज़्‍ा ज़्ा ज़्‍ा
+य़ य़्ा य़्‍ा य़्ा य़्‍ा
+ॹ ॹ्ा ॹ्‍ा
+ॺ ॺ्ा ॺ्‍ा
+ज़ ॻ्ा ॻ्‍ा
+ॼ ॼ्ा ॼ्‍ा
+ॾ ॾ्ा ॾ्‍ा
+ॿ ॿ्ा ॿ्‍ा
diff --git a/test/shaping/texts/in-house/shaper-indic/script-devanagari/devanagari-vowel-letters.txt b/test/shaping/texts/in-house/shaper-indic/script-devanagari/devanagari-vowel-letters.txt
new file mode 100644
index 0000000..5a41252
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-indic/script-devanagari/devanagari-vowel-letters.txt
@@ -0,0 +1,17 @@
+ऄ अॆ
+आ अा
+ई र्इ
+ऊ उु
+ऍ एॅ
+ऎ एॆ
+ऐ एे
+ऑ अॉ आॅ
+ऒ अॊ आॆ
+ओ अो आे
+औ अौ आै
+ॲ अॅ
+ॳ अऺ
+ॴ अऻ आऺ
+ॵ अॏ
+ॶ अॖ
+ॷ अॗ
diff --git a/test/shaping/texts/in-house/shaper-indic/script-gujarati/gujarati-vowel-letters.txt b/test/shaping/texts/in-house/shaper-indic/script-gujarati/gujarati-vowel-letters.txt
new file mode 100644
index 0000000..add4332
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-indic/script-gujarati/gujarati-vowel-letters.txt
@@ -0,0 +1,8 @@
+આ અા
+ઍ અૅ
+એ અે
+ઐ અૈ
+ઑ અૉ
+ઓ અો અાૅ
+ઔ અૌ અાૈ
+ૉ ૅા
diff --git a/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/gurmukhi-vowel-letters.txt b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/gurmukhi-vowel-letters.txt
new file mode 100644
index 0000000..b2adaab
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-indic/script-gurmukhi/gurmukhi-vowel-letters.txt
@@ -0,0 +1,9 @@
+ਆ ਅਾ
+ਇ ੲਿ
+ਈ ੲੀ
+ਉ ੳੁ
+ਊ ੳੂ
+ਏ ੲੇ
+ਐ ਅੈ
+ਓ ੳੋ
+ਔ ਅੌ
diff --git a/test/shaping/texts/in-house/shaper-indic/script-kannada/kannada-vowel-letters.txt b/test/shaping/texts/in-house/shaper-indic/script-kannada/kannada-vowel-letters.txt
new file mode 100644
index 0000000..cc05db9
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-indic/script-kannada/kannada-vowel-letters.txt
@@ -0,0 +1,3 @@
+ಊ ಉಾ
+ಔ ಒೌ
+ೠ ಋಾ
diff --git a/test/shaping/texts/in-house/shaper-indic/script-malayalam/malayalam-vowel-letters.txt b/test/shaping/texts/in-house/shaper-indic/script-malayalam/malayalam-vowel-letters.txt
new file mode 100644
index 0000000..061c642
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-indic/script-malayalam/malayalam-vowel-letters.txt
@@ -0,0 +1,5 @@
+ഈ ഇൗ
+ഊ ഉൗ
+ഐ എെ
+ഓ ഒാ
+ഔ ഒൗ
diff --git a/test/shaping/texts/in-house/shaper-indic/script-oriya/oriya-vowel-letters.txt b/test/shaping/texts/in-house/shaper-indic/script-oriya/oriya-vowel-letters.txt
new file mode 100644
index 0000000..e8d24cb
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-indic/script-oriya/oriya-vowel-letters.txt
@@ -0,0 +1,3 @@
+ଆ ଅା
+ଐ ଏୗ
+ଔ ଓୗ
diff --git a/test/shaping/texts/in-house/shaper-indic/script-telugu/telugu-vowel-letters.txt b/test/shaping/texts/in-house/shaper-indic/script-telugu/telugu-vowel-letters.txt
new file mode 100644
index 0000000..c3cfc84
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-indic/script-telugu/telugu-vowel-letters.txt
@@ -0,0 +1,5 @@
+ఓ ఒౕ
+ఔ ఒౌ
+ీ ిౕ
+ే ెౕ
+ో ొౕ
diff --git a/test/subset/Makefile.am b/test/subset/Makefile.am
index 336d33d..1673cfb 100644
--- a/test/subset/Makefile.am
+++ b/test/subset/Makefile.am
@@ -7,7 +7,7 @@
 
 # Convenience targets:
 lib:
-	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src libs
 
 EXTRA_DIST += \
 	CMakeLists.txt \
diff --git a/util/Makefile.am b/util/Makefile.am
index d4ab9cd..85f9eda 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -47,17 +47,16 @@
 bin_PROGRAMS += hb-shape
 
 hb_subset_SOURCES = $(HB_SUBSET_CLI_sources)
-hb_subset_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
+hb_subset_LDADD = \
+	$(LDADD) \
+	$(top_builddir)/src/libharfbuzz-subset.la
 bin_PROGRAMS += hb-subset
 
-if HAVE_OT
 hb_ot_shape_closure_SOURCES = $(HB_OT_SHAPE_CLOSURE_sources)
 bin_PROGRAMS += hb-ot-shape-closure
-endif # HAVE_OT
 
 endif # HAVE_GLIB
 
-#if HAVE_OT
 #if HAVE_FONTCONFIG
 #hb_fc_list_SOURCES = \
 #	hb-fc.cc \
@@ -70,6 +69,5 @@
 #	$(NULL)
 #bin_PROGRAMS += hb-fc-list
 #endif # HAVE_FONTCONFIG
-#endif # HAVE_OT
 
 -include $(top_srcdir)/git.mk
diff --git a/util/ansi-print.cc b/util/ansi-print.cc
index 0daee1f..5e2845c 100644
--- a/util/ansi-print.cc
+++ b/util/ansi-print.cc
@@ -71,7 +71,7 @@
 {
   static color_t from_ansi (unsigned int x)
   {
-    color_t c = {(0xFF<<24) | ((0xFF*(x&1))<<16) | ((0xFF*((x >> 1)&1))<<8) | (0xFF*((x >> 2)&1))};
+    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)
@@ -223,7 +223,7 @@
   uint8_t * const data;
 };
 
-const char *
+static const char *
 block_best (const biimage_t &bi, bool *inverse)
 {
   assert (bi.width  <= CELL_W);
diff --git a/util/ansi-print.hh b/util/ansi-print.hh
index 1ea5b37..9640d89 100644
--- a/util/ansi-print.hh
+++ b/util/ansi-print.hh
@@ -27,8 +27,7 @@
 #ifndef ANSI_PRINT_HH
 #define ANSI_PRINT_HH
 
-#include "hb-private.hh"
-#include <hb.h> /* for int types */
+#include "hb.hh"
 
 void
 ansi_print_image_rgb24 (const uint32_t *data,
diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index 2061755..3f0963c 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -29,7 +29,6 @@
 
 #include "main-font-text.hh"
 #include "hb-subset.h"
-#include "hb-subset-private.hh"
 
 /*
  * Command line interface to the harfbuzz font subsetter.
@@ -90,19 +89,17 @@
 
   void finish (const font_options_t *font_opts)
   {
-    input->drop_hints = subset_options.drop_hints;
+    hb_subset_input_set_drop_hints (input, subset_options.drop_hints);
 
-    hb_subset_profile_t *subset_profile = hb_subset_profile_create();
     hb_face_t *face = hb_font_get_face (font);
 
-    hb_face_t *new_face = hb_subset(face, subset_profile, 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);
     if (!failed)
       write_file (options.output_file, result);
 
-    hb_subset_profile_destroy (subset_profile);
     hb_subset_input_destroy (input);
     hb_blob_destroy (result);
     hb_face_destroy (new_face);
diff --git a/util/hb-view.cc b/util/hb-view.cc
index ef75e6d..69a4c95 100644
--- a/util/hb-view.cc
+++ b/util/hb-view.cc
@@ -30,7 +30,7 @@
 #include "view-cairo.hh"
 
 #define DEFAULT_FONT_SIZE 256
-#define SUBPIXEL_BITS 8
+#define SUBPIXEL_BITS 6
 
 int
 main (int argc, char **argv)
diff --git a/util/helper-cairo-ansi.hh b/util/helper-cairo-ansi.hh
index cf18ea4..bc23132 100644
--- a/util/helper-cairo-ansi.hh
+++ b/util/helper-cairo-ansi.hh
@@ -27,7 +27,7 @@
 #ifndef HELPER_CAIRO_ANSI_HH
 #define HELPER_CAIRO_ANSI_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include <cairo.h>
 
diff --git a/util/helper-cairo.cc b/util/helper-cairo.cc
index 3eaf90a..7a698f3 100644
--- a/util/helper-cairo.cc
+++ b/util/helper-cairo.cc
@@ -64,11 +64,13 @@
 
 static FT_Library ft_library;
 
+#ifdef HAVE_ATEXIT
 static inline
 void free_ft_library (void)
 {
   FT_Done_FreeType (ft_library);
 }
+#endif
 
 cairo_scaled_font_t *
 helper_cairo_create_scaled_font (const font_options_t *font_opts)
@@ -125,7 +127,7 @@
     }
 #endif
 
-    cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
+    cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, font_opts->ft_load_flags);
   }
   cairo_matrix_t ctm, font_matrix;
   cairo_font_options_t *font_options;
diff --git a/util/helper-cairo.hh b/util/helper-cairo.hh
index 50bc0af..1613ce4 100644
--- a/util/helper-cairo.hh
+++ b/util/helper-cairo.hh
@@ -27,7 +27,7 @@
 #ifndef HELPER_CAIRO_HH
 #define HELPER_CAIRO_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 #include "options.hh"
 
 #include <cairo.h>
diff --git a/util/main-font-text.hh b/util/main-font-text.hh
index 3390371..01bd2d4 100644
--- a/util/main-font-text.hh
+++ b/util/main-font-text.hh
@@ -27,7 +27,7 @@
 #ifndef HB_MAIN_FONT_TEXT_HH
 #define HB_MAIN_FONT_TEXT_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 #include "options.hh"
 
 /* main() body for utilities taking font and processing text.*/
diff --git a/util/options.cc b/util/options.cc
index 57cc4aa..5661cd0 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -29,11 +29,9 @@
 #ifdef HAVE_FREETYPE
 #include <hb-ft.h>
 #endif
-#ifdef HAVE_OT
 #include <hb-ot.h>
-#endif
 
-struct supported_font_funcs_t {
+static struct supported_font_funcs_t {
 	char name[4];
 	void (*func) (hb_font_t *);
 } supported_font_funcs[] =
@@ -41,9 +39,7 @@
 #ifdef HAVE_FREETYPE
   {"ft",	hb_ft_font_set_funcs},
 #endif
-#ifdef HAVE_OT
   {"ot",	hb_ot_font_set_funcs},
-#endif
 };
 
 
@@ -172,9 +168,9 @@
   view_options_t *view_opts = (view_options_t *) data;
   view_options_t::margin_t &m = view_opts->margin;
   switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf", &m.t, &m.r, &m.b, &m.l)) {
-    case 1: m.r = m.t;
-    case 2: m.b = m.t;
-    case 3: m.l = m.r;
+    case 1: m.r = m.t; HB_FALLTHROUGH;
+    case 2: m.b = m.t; HB_FALLTHROUGH;
+    case 3: m.l = m.r; HB_FALLTHROUGH;
     case 4: return true;
     default:
       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
@@ -415,11 +411,12 @@
     {"eot",		0, 0, G_OPTION_ARG_NONE,	&this->eot,			"Treat text as end-of-paragraph",	nullptr},
     {"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE,	&this->preserve_default_ignorables,	"Preserve Default-Ignorable characters",	nullptr},
     {"remove-default-ignorables",0, 0, G_OPTION_ARG_NONE,	&this->remove_default_ignorables,	"Remove Default-Ignorable characters",	nullptr},
+    {"invisible-glyph",	0, 0, G_OPTION_ARG_INT,		&this->invisible_glyph,		"Glyph value to replace Default-Ignorables with",	nullptr},
     {"utf8-clusters",	0, 0, G_OPTION_ARG_NONE,	&this->utf8_clusters,		"Use UTF8 byte indices, not char indices",	nullptr},
     {"cluster-level",	0, 0, G_OPTION_ARG_INT,		&this->cluster_level,		"Cluster merging level (default: 0)",	"0/1/2"},
     {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE,	&this->normalize_glyphs,	"Rearrange glyph clusters in nominal order",	nullptr},
     {"verify",		0, 0, G_OPTION_ARG_NONE,	&this->verify,			"Perform sanity checks on shaping results",	nullptr},
-    {"num-iterations",	0, 0, G_OPTION_ARG_INT,		&this->num_iterations,		"Run shaper N times (default: 1)",	"N"},
+    {"num-iterations", 'n', 0, G_OPTION_ARG_INT,		&this->num_iterations,		"Run shaper N times (default: 1)",	"N"},
     {nullptr}
   };
   parser->add_group (entries,
@@ -489,7 +486,7 @@
     return true;
   }
   switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->font_size_x, &font_opts->font_size_y)) {
-    case 1: font_opts->font_size_y = font_opts->font_size_x;
+    case 1: font_opts->font_size_y = font_opts->font_size_x; HB_FALLTHROUGH;
     case 2: return true;
     default:
       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
@@ -507,7 +504,7 @@
 {
   font_options_t *font_opts = (font_options_t *) data;
   switch (sscanf (arg, "%d%*[ ,]%d", &font_opts->x_ppem, &font_opts->y_ppem)) {
-    case 1: font_opts->y_ppem = font_opts->x_ppem;
+    case 1: font_opts->y_ppem = font_opts->x_ppem; HB_FALLTHROUGH;
     case 2: return true;
     default:
       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
@@ -556,6 +553,7 @@
     {"font-ppem",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_font_ppem,	"Set x,y pixels per EM (default: 0; disabled)",	"1/2 integers"},
     {"font-ptem",	0, 0, G_OPTION_ARG_DOUBLE,	&this->ptem,			"Set font point-size (default: 0; disabled)",	"point-size"},
     {"font-funcs",	0, 0, G_OPTION_ARG_STRING,	&this->font_funcs,		text,						"impl"},
+    {"ft-load-flags",	0, 0, G_OPTION_ARG_INT,		&this->ft_load_flags,		"Set FreeType load-flags (default: 2)",		"integer"},
     {nullptr}
   };
   parser->add_group (entries,
@@ -662,7 +660,7 @@
   blob = hb_blob_create_from_file (font_path);
 
   if (blob == hb_blob_get_empty ())
-    fail (false, "No such file or directory");
+    fail (false, "Couldn't read or find %s, or it was empty.", font_path);
 
   /* Create the face */
   hb_face_t *face = hb_face_create (blob, face_index);
@@ -717,6 +715,9 @@
     }
   }
   set_font_funcs (font);
+#ifdef HAVE_FREETYPE
+  hb_ft_font_set_load_flags (font, ft_load_flags);
+#endif
 
   return font;
 }
diff --git a/util/options.hh b/util/options.hh
index 2160d5e..dd62859 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -27,7 +27,7 @@
 #ifndef OPTIONS_HH
 #define OPTIONS_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 
 #include <stdlib.h>
 #include <stddef.h>
@@ -46,9 +46,7 @@
 #endif
 
 #include <hb.h>
-#ifdef HAVE_OT
 #include <hb-ot.h>
-#endif
 #include <glib.h>
 #include <glib/gprintf.h>
 
@@ -56,16 +54,19 @@
 
 struct option_group_t
 {
+  virtual ~option_group_t (void) {}
+
   virtual void add_options (struct option_parser_t *parser) = 0;
 
-  virtual void pre_parse (GError **error G_GNUC_UNUSED) {};
-  virtual void post_parse (GError **error G_GNUC_UNUSED) {};
+  virtual void pre_parse (GError **error G_GNUC_UNUSED) {}
+  virtual void post_parse (GError **error G_GNUC_UNUSED) {}
 };
 
 
 struct option_parser_t
 {
-  option_parser_t (const char *usage) {
+  option_parser_t (const char *usage)
+  {
     memset (this, 0, sizeof (*this));
     usage_str = usage;
     context = g_option_context_new (usage);
@@ -73,7 +74,8 @@
 
     add_main_options ();
   }
-  ~option_parser_t (void) {
+  ~option_parser_t (void)
+  {
     g_option_context_free (context);
     g_ptr_array_foreach (to_free, (GFunc) g_free, nullptr);
     g_ptr_array_free (to_free, TRUE);
@@ -113,7 +115,8 @@
 
 struct view_options_t : option_group_t
 {
-  view_options_t (option_parser_t *parser) {
+  view_options_t (option_parser_t *parser)
+  {
     annotate = false;
     fore = nullptr;
     back = nullptr;
@@ -122,7 +125,7 @@
 
     add_options (parser);
   }
-  ~view_options_t (void)
+  virtual ~view_options_t (void)
   {
     g_free (fore);
     g_free (back);
@@ -150,6 +153,7 @@
     num_features = 0;
     shapers = nullptr;
     utf8_clusters = false;
+    invisible_glyph = 0;
     cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT;
     normalize_glyphs = false;
     verify = false;
@@ -157,7 +161,7 @@
 
     add_options (parser);
   }
-  ~shape_options_t (void)
+  virtual ~shape_options_t (void)
   {
     g_free (direction);
     g_free (language);
@@ -180,6 +184,7 @@
 				  (preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0) |
 				  (remove_default_ignorables ? HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES : 0) |
 				  0));
+    hb_buffer_set_invisible_glyph (buffer, invisible_glyph);
     hb_buffer_set_cluster_level (buffer, cluster_level);
     hb_buffer_guess_segment_properties (buffer);
   }
@@ -430,6 +435,7 @@
   unsigned int num_features;
   char **shapers;
   hb_bool_t utf8_clusters;
+  hb_codepoint_t invisible_glyph;
   hb_buffer_cluster_level_t cluster_level;
   hb_bool_t normalize_glyphs;
   hb_bool_t verify;
@@ -454,13 +460,15 @@
     face_index = 0;
     font_size_x = font_size_y = default_font_size;
     font_funcs = nullptr;
+    ft_load_flags = 2;
 
     blob = nullptr;
     font = nullptr;
 
     add_options (parser);
   }
-  ~font_options_t (void) {
+  virtual ~font_options_t (void)
+  {
     g_free (font_file);
     free (variations);
     g_free (font_funcs);
@@ -484,6 +492,7 @@
   mutable double font_size_x;
   mutable double font_size_y;
   char *font_funcs;
+  int ft_load_flags;
 
   private:
   mutable hb_font_t *font;
@@ -492,7 +501,8 @@
 
 struct text_options_t : option_group_t
 {
-  text_options_t (option_parser_t *parser) {
+  text_options_t (option_parser_t *parser)
+  {
     text_before = nullptr;
     text_after = nullptr;
 
@@ -506,7 +516,8 @@
 
     add_options (parser);
   }
-  ~text_options_t (void) {
+  virtual ~text_options_t (void)
+  {
     g_free (text_before);
     g_free (text_after);
     g_free (text);
@@ -544,7 +555,8 @@
 struct output_options_t : option_group_t
 {
   output_options_t (option_parser_t *parser,
-		    const char **supported_formats_ = nullptr) {
+		    const char **supported_formats_ = nullptr)
+  {
     output_file = nullptr;
     output_format = nullptr;
     supported_formats = supported_formats_;
@@ -554,7 +566,8 @@
 
     add_options (parser);
   }
-  ~output_options_t (void) {
+  virtual ~output_options_t (void)
+  {
     g_free (output_file);
     g_free (output_format);
     if (fp)
@@ -573,7 +586,7 @@
       if (output_format)
       {
 	  output_format++; /* skip the dot */
-	  output_format = strdup (output_format);
+	  output_format = g_strdup (output_format);
       }
     }
 
diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh
index fa419f1..da0d880 100644
--- a/util/shape-consumer.hh
+++ b/util/shape-consumer.hh
@@ -27,7 +27,7 @@
 #ifndef HB_SHAPE_CONSUMER_HH
 #define HB_SHAPE_CONSUMER_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 #include "options.hh"
 
 
diff --git a/util/view-cairo.hh b/util/view-cairo.hh
index 00df68c..5be3523 100644
--- a/util/view-cairo.hh
+++ b/util/view-cairo.hh
@@ -27,7 +27,7 @@
 #ifndef VIEW_CAIRO_HH
 #define VIEW_CAIRO_HH
 
-#include "hb-private.hh"
+#include "hb.hh"
 #include "options.hh"
 #include "helper-cairo.hh"
 
@@ -46,7 +46,7 @@
   void init (hb_buffer_t *buffer, const font_options_t *font_opts)
   {
     lines = g_array_new (false, false, sizeof (helper_cairo_line_t));
-    scale_bits = -font_opts->subpixel_bits;
+    scale_bits = - (int) font_opts->subpixel_bits;
   }
   void new_line (void)
   {