Merge remote-tracking branch 'eroux/add-base'
diff --git a/.ci/deploy-docs.sh b/.ci/deploy-docs.sh
index e4dbad4..a8a8523 100755
--- a/.ci/deploy-docs.sh
+++ b/.ci/deploy-docs.sh
@@ -3,6 +3,8 @@
 set -x
 set -o errexit -o nounset
 
+if test "x$TRAVIS_SECURE_ENV_VARS" != xtrue; then exit; fi
+
 BRANCH="$TRAVIS_BRANCH"
 if test "x$BRANCH" != xmaster; then exit; fi
 
@@ -16,18 +18,19 @@
 cd $DOCSDIR
 
 cp ../docs/html/* .
+#cp ../docs/CNAME .
 
 git init
 git config user.name "Travis CI"
 git config user.email "travis@harfbuzz.org"
 set +x
-echo "git remote add upstream \"https://\$GH_TOKEN@github.com/$TRAVIS_REPO_SLUG.git\""
-git remote add upstream "https://$GH_TOKEN@github.com/$TRAVIS_REPO_SLUG.git"
+echo "git remote add upstream \"https://\$GH_TOKEN@github.com/harfbuzz/harfbuzz.github.io.git\""
+git remote add upstream "https://$GH_TOKEN@github.com/harfbuzz/harfbuzz.github.io.git"
 set -x
 git fetch upstream
-git reset upstream/gh-pages
+git reset upstream/master
 
 touch .
 git add -A .
-git commit -m "Rebuild docs for $REVISION"
-git push -q upstream HEAD:gh-pages
+git commit -m "Rebuild docs for https://github.com/harfbuzz/harfbuzz/commit/$REVISION"
+git push -q upstream HEAD:master
diff --git a/.ci/fail.sh b/.ci/fail.sh
new file mode 100755
index 0000000..91701d3
--- /dev/null
+++ b/.ci/fail.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+for f in $(find . -name '*.log' -not -name 'config.log'); do
+    last=$(tail -1 $f)
+    if [[ $last = FAIL* || $last = *failed* ]]; then
+        echo '====' $f '===='
+        cat $f
+    fi
+done
+
+# Intentionally exiting with non-zero.
+exit 1
diff --git a/.ci/run-coveralls.sh b/.ci/run-coveralls.sh
new file mode 100755
index 0000000..58b8311
--- /dev/null
+++ b/.ci/run-coveralls.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+set -x
+set -o errexit -o nounset
+
+if test "x$TRAVIS_SLUG" != x"harfbuzz/harfbuzz"; then exit; fi
+
+pip install --user nose
+pip install --user cpp-coveralls
+export PATH=$HOME/.local/bin:$PATH
+
+rm -f src/.libs/NONE.gcov
+touch src/NONE
+coveralls -e docs
diff --git a/.ci/trigger-coverity.sh b/.ci/trigger-coverity.sh
new file mode 100644
index 0000000..e241692
--- /dev/null
+++ b/.ci/trigger-coverity.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -x
+set -o errexit -o nounset
+
+if test "x$TRAVIS_EVENT_TYPE" != x"cron"; then exit; fi
+
+BRANCH="$TRAVIS_BRANCH"
+if test "x$BRANCH" != xmaster; then exit; fi
+
+git fetch --unshallow
+git remote add upstream "https://$GH_TOKEN@github.com/harfbuzz/harfbuzz.git"
+git push -q upstream master:coverity_scan
diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000..e30b5b4
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,174 @@
+version: 2
+
+jobs:
+
+  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: pip install fonttools
+      - run: ./autogen.sh
+      - run: make
+      - 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
+
+  alpine-O3:
+    docker:
+      - image: alpine
+    steps:
+      - checkout
+      - 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" ./autogen.sh
+      - run: make
+      - run: make check || .ci/fail.sh
+
+  archlinux-debug-O0:
+    docker:
+      - image: base/devel
+    steps:
+      - checkout
+      - run: pacman --noconfirm -Syu freetype2 cairo icu gettext gobject-introspection gcc gcc-libs glib2 graphite pkg-config ragel python
+      # 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 check || .ci/fail.sh
+
+  fedora-outoftreebuild:
+    docker:
+      - image: fedora
+    steps:
+      - checkout
+      - run: dnf install -y pkg-config ragel gcc gcc-c++ automake autoconf libtool make which glib2-devel freetype-devel cairo-devel libicu-devel gobject-introspection-devel graphite2-devel redhat-rpm-config python || true
+      - run: NOCONFIGURE=1 ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2
+      - run: mkdir build && cd build && ../configure && make && (make check || ../.ci/fail.sh)
+
+  cmake-gcc:
+    docker:
+      - image: ubuntu:17.10
+    steps:
+      - checkout
+      - run: apt update && apt install -y ninja-build binutils cmake gcc g++ pkg-config ragel gtk-doc-tools libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
+      - run: pip install fonttools
+      - run: cmake -DHB_CHECK=ON -Bbuild -H. -GNinja
+      - run: ninja -Cbuild
+      - run: CTEST_OUTPUT_ON_FAILURE=1 ninja -Cbuild test
+      - run: ninja -Cbuild install
+
+  cmake-oracledeveloperstudio:
+    docker:
+      - image: fedora
+    steps:
+      - checkout
+      - run: dnf install -y gcc ragel cmake make which glib2-devel freetype-devel cairo-devel libicu-devel graphite2-devel wget tar bzip2 python || 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_ICU=ON -DHB_HAVE_FREETYPE=ON -Bbuild -H.
+      - run: make -Cbuild
+      - run: CTEST_OUTPUT_ON_FAILURE=1 make -Cbuild test
+      - run: make -Cbuild install
+
+  crosscompile-notest-djgpp:
+    docker:
+      - image: quay.io/ebraminio/djgpp
+    steps:
+      - 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
+
+  crosscompile-notest-freebsd9:
+    docker:
+      - image: donbowman/freebsd-cross-build
+    steps:
+      - checkout
+      - run: apt update && apt install -y pkg-config ragel
+      - run: ./autogen.sh --prefix=/freebsd --host=x86_64-pc-freebsd9
+      - run: make
+
+  crosscompile-notest-psvita:
+    docker:
+      - image: dockcross/base
+    steps:
+      - checkout
+      - 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
+
+  crosscompile-cmake-notest-android-arm:
+    docker:
+      - image: dockcross/android-arm
+    steps:
+      - checkout
+      - run: apt update && apt install ragel
+      - run: cmake -Bbuild -H. -GNinja
+      - run: ninja -Cbuild
+
+  crosscompile-cmake-notest-browser-asmjs:
+    docker:
+      - image: dockcross/browser-asmjs
+    steps:
+      - checkout
+      - run: apt update && apt install ragel
+      - run: cmake -Bbuild -H. -GNinja
+      - run: ninja -Cbuild
+
+  crosscompile-cmake-notest-linux-arm64:
+    docker:
+      - image: dockcross/linux-arm64
+    steps:
+      - checkout
+      - run: apt update && apt install ragel
+      - run: cmake -Bbuild -H. -GNinja
+      - run: ninja -Cbuild
+
+  crosscompile-cmake-notest-linux-mips:
+    docker:
+      - image: dockcross/linux-mips
+    steps:
+      - checkout
+      - run: apt update && apt install ragel
+      - run: cmake -Bbuild -H. -GNinja
+      - run: ninja -Cbuild
+
+  crosscompile-cmake-notest-windows-x64:
+    docker:
+      - image: dockcross/windows-x64
+    steps:
+      - checkout
+      - run: apt update && apt install ragel
+      - run: cmake -Bbuild -H. -GNinja
+      - run: ninja -Cbuild
+
+workflows:
+  version: 2
+  build:
+    jobs:
+      # both autotools and cmake
+      - distcheck
+
+      # autotools based builds
+      - alpine-O3
+      - archlinux-debug-O0
+      - fedora-outoftreebuild
+
+      # cmake based builds
+      - cmake-gcc
+      - cmake-oracledeveloperstudio
+
+      # crosscompiles
+      # they can't be test thus are without tests
+      ## autotools
+      - crosscompile-notest-djgpp
+      - crosscompile-notest-freebsd9
+      - crosscompile-notest-psvita
+
+      ## cmake
+      - crosscompile-cmake-notest-android-arm
+      - crosscompile-cmake-notest-browser-asmjs
+      - crosscompile-cmake-notest-linux-arm64
+      - crosscompile-cmake-notest-linux-mips
+      - crosscompile-cmake-notest-windows-x64
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..708188a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,18 @@
+root = true
+
+[*]
+charset = utf-8
+trim_trailing_whitespace = true
+end_of_line = lf
+insert_final_newline = true
+
+[*.{c,cc,h,hh}]
+indent_size = 2
+indent_style = space
+tab_width = 8
+
+[*.{py,sh}]
+indent_style = tab
+
+[{CMakeLists.txt,*.cmake}]
+indent_size = 2
diff --git a/.travis.yml b/.travis.yml
index 1b15058..662fab5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,41 +1,68 @@
 # Build Configuration for Travis
-sudo: required # For Trusty beta
-os:
-  - linux
-  - osx
 dist: trusty
+
 language: cpp
-compiler:
-  - clang
-  - gcc
+
 env:
   global:
     - CPPFLAGS=""
     - CFLAGS="-Werror --coverage"
     - CXXFLAGS="-Werror -Wno-deprecated-register --coverage" # glib uses register and clang raises a warning
     - LDFLAGS="--coverage"
-install:
-  - if [ "$TRAVIS_OS_NAME" == "linux" ]; then pip install --user nose; fi
-  - if [ "$TRAVIS_OS_NAME" == "linux" ]; then pip install --user cpp-coveralls; fi # for coveralls.io code coverage tracking
-  - if [ "$TRAVIS_OS_NAME" == "linux" ]; then export PATH=$HOME/.local/bin:$PATH; fi # Make sure we can find the above Python packages
-  - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update; fi;
-  - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew uninstall libtool && brew install libtool; fi # Workaround Travis/brew bug
-  - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install ragel freetype glib gobject-introspection cairo icu4c graphite2; fi
-  - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew link --force icu4c; fi # icu4c is keg-only
-script:
-  - NOCONFIGURE=1 ./autogen.sh
-  - export CONFIGURE_OPTS="--with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2"
-  - if [ "$TRAVIS_OS_NAME" == "linux" -a "$CC" == "gcc" ]; then export CONFIGURE_OPTS="$CONFIGURE_OPTS  --enable-gtk-doc"; fi
-  - if [ "$TRAVIS_OS_NAME" == "osx" ]; then export CONFIGURE_OPTS="$CONFIGURE_OPTS --with-coretext"; fi
-  - ./configure $CONFIGURE_OPTS
-  - make
-  - make check
-  - if [ "$TRAVIS_OS_NAME" == "linux" -a "$CC" == "gcc" -a "$TRAVIS_SLUG" == "behdad/harfbuzz" ]; then rm -f src/.libs/NONE.gcov; touch src/NONE; coveralls -e docs; fi
-after_success:
-  - if [ "$TRAVIS_OS_NAME" == "linux" -a "$CC" == "gcc" -a "$TRAVIS_SECURE_ENV_VARS" == "true" ]; then bash .ci/deploy-docs.sh; fi
+    - CONFIGURE_OPTS="--with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2"
+    - NOCONFIGURE=1
+    # COVERITY_SCAN_TOKEN
+    - secure: "Vw1UUHsAr4t3xuvOqqBsFAORptmNcQRrcGJnzSX7jDODBIRNfnU2LIBTagrPNKfSUhyQgCHqt1gX9iWNWvVfy3sDOXr2MXZeoqmF6Y1mS35J0rA/EPJgRHsdkxygkmFnXVeQkEuI55BINkaSoOpAeunmXKJNw1p9JVw368Fm/tU="
+
+matrix:
+  include:
+    - os: linux
+      compiler: gcc
+      script:
+        # Remove these two lines when Travis updated its distro
+        - 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 ..
+        - export LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs"
+
+        - ./autogen.sh
+        - ./configure $CONFIGURE_OPTS --enable-gtk-doc
+        - make
+        - make check || .ci/fail.sh
+      after_success:
+        - bash .ci/run-coveralls.sh # for coveralls.io code coverage tracking
+        - bash .ci/deploy-docs.sh
+        - bash .ci/trigger-coverity.sh
+
+    - os: linux
+      compiler: clang
+      script:
+        # Remove these two lines when Travis updated its distro
+        - 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 ..
+        - export LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs"
+
+        - ./autogen.sh
+        - ./configure $CONFIGURE_OPTS
+        - make
+        - make check || .ci/fail.sh
+
+    - 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
+        - brew install ragel freetype glib gobject-introspection cairo icu4c graphite2
+        - brew link --force icu4c # icu4c is keg-only
+      script:
+        - ./autogen.sh
+        - ./configure $CONFIGURE_OPTS --with-coretext
+        - make
+        - make check || .ci/fail.sh
+
 notifications:
   irc: "irc.freenode.org#harfbuzz"
-  email: harfbuzz@lists.freedesktop.org
+  email: harfbuzz-bots-chatter@googlegroups.com
 
 addons:
   apt:
@@ -48,4 +75,14 @@
       - libcairo2-dev # for utils
       - libicu-dev # for extra unicode functions
       - libgraphite2-dev # for extra shapers
-      - # libgirepository1.0-dev # for gobject-introspection
+      #- libgirepository1.0-dev # for gobject-introspection
+
+  coverity_scan:
+    project:
+      name: HarfBuzz
+      version: 1.0
+      description: HarfBuzz OpenType text shaping engine
+    notification_email: harfbuzz-bots-chatter@googlegroups.com
+    build_command_prepend: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2
+    build_command: make
+    branch_pattern: coverity_scan
diff --git a/BUILD.md b/BUILD.md
index 7518c2e..8a6b569 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -1,36 +1,50 @@
 On Linux, install the development packages for FreeType,
 Cairo, and GLib. For example, on Ubuntu / Debian, you would do:
-* sudo apt-get install gcc g++ libfreetype6-dev libglib2.0-dev libcairo2-dev
+
+    sudo apt-get install gcc g++ libfreetype6-dev libglib2.0-dev libcairo2-dev
 
 whereas on Fedora, RHEL, CentOS, and other Red Hat based systems you would do:
-* sudo yum install gcc gcc-c++ freetype-devel glib2-devel cairo-devel
 
-on the Mac, using MacPorts:
-* sudo port install freetype glib2 cairo
+    sudo yum install gcc gcc-c++ freetype-devel glib2-devel cairo-devel
+
+on Windows, consider using [vcpkg](https://github.com/Microsoft/vcpkg),
+provided by Microsoft, for building HarfBuzz and other open-source libraries
+but if you need to build harfbuzz from source, put ragel binary on your
+PATH and follow appveyor CI's cmake
+[build steps](https://github.com/harfbuzz/harfbuzz/blob/master/appveyor.yml).
+
+on macOS, using MacPorts:
+
+    sudo port install freetype glib2 cairo
 
 or using Homebrew:
-* brew install freetype glib cairo
+
+    brew install freetype glib cairo
 
 If you are using a tarball, you can now proceed to running configure and make
 as with any other standard package. That should leave you with a shared
-library in src/, and a few utility programs including hb-view and hb-shape
-under util/.  From the tarball, NMake Makefiles are also provided in win32/,
-which supports building HarfBuzz using Visual Studio, with a README.txt that
-gives instructions on building using NMake.
+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
-run autogen.sh for the first time. Namely, pkg-config and ragel. Again,
-on Ubuntu / Debian:
-* sudo apt-get install autoconf automake libtool pkg-config ragel gtk-doc-tools
+run `autogen.sh` for the first time. Namely, `pkg-config` and `ragel`.
+
+Again, on Ubuntu / Debian:
+
+    sudo apt-get install autoconf automake libtool pkg-config ragel gtk-doc-tools
 
 and on Fedora, RHEL, CentOS:
-* sudo yum install autoconf automake libtool pkgconfig ragel gtk-doc
+
+    sudo yum install autoconf automake libtool pkgconfig ragel gtk-doc
 
 on the Mac, using MacPorts:
-* sudo port install autoconf automake libtool pkgconfig ragel gtk-doc
+
+    sudo port install autoconf automake libtool pkgconfig ragel gtk-doc
 
 or using Homebrew:
-* brew install autoconf automake libtool pkgconfig ragel gtk-doc
+
+    brew install autoconf automake libtool pkgconfig ragel gtk-doc
 
 To build the Python bindings, you also need:
 
-* brew install pygobject3
+    brew install pygobject3
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..6b4e108
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,860 @@
+cmake_minimum_required(VERSION 2.8.0)
+project(harfbuzz)
+
+enable_testing()
+
+## Limit framework build to Xcode generator
+if (BUILD_FRAMEWORK)
+  # for a framework build on macOS, use:
+  # cmake -DBUILD_FRAMEWORK=ON -Bbuild -H. -GXcode && cmake --build build
+  if (NOT "${CMAKE_GENERATOR}" STREQUAL "Xcode")
+    message(FATAL_ERROR
+      "You should use Xcode generator with BUILD_FRAMEWORK enabled")
+  endif ()
+  set (CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD_32_64_BIT)")
+  set (CMAKE_MACOSX_RPATH ON)
+  set (BUILD_SHARED_LIBS ON)
+endif ()
+
+
+## Disallow in-source builds, as CMake generated make files can collide with autotools ones
+if (NOT MSVC AND "${PROJECT_BINARY_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}")
+  message(FATAL_ERROR
+    "
+In-source builds are not permitted!  Make a separate folder for"
+    " building, e.g.,"
+    "
+  mkdir build; cd build; cmake .."
+    "
+Before that, remove the files created by this failed run with"
+    "
+  rm -rf CMakeCache.txt CMakeFiles")
+endif ()
+
+
+## HarfBuzz build configurations
+option(HB_HAVE_FREETYPE "Enable freetype interop helpers" OFF)
+option(HB_HAVE_GRAPHITE2 "Enable Graphite2 complementary shaper" OFF)
+option(HB_BUILTIN_UCDN "Use HarfBuzz provided UCDN" ON)
+option(HB_HAVE_GLIB "Enable glib unicode functions" OFF)
+option(HB_HAVE_ICU "Enable icu unicode functions" OFF)
+if (APPLE)
+  option(HB_HAVE_CORETEXT "Enable CoreText shaper backend on macOS" ON)
+  set (CMAKE_MACOSX_RPATH ON)
+endif ()
+if (WIN32)
+  option(HB_HAVE_UNISCRIBE "Enable Uniscribe shaper backend on Windows" OFF)
+  option(HB_HAVE_DIRECTWRITE "Enable DirectWrite shaper backend on Windows" OFF)
+endif ()
+option(HB_BUILD_UTILS "Build harfbuzz utils, needs cairo, freetype, and glib properly be installed" OFF)
+if (HB_BUILD_UTILS)
+  set (HB_HAVE_GLIB ON)
+  set (HB_HAVE_FREETYPE ON)
+endif ()
+
+option(HB_HAVE_GOBJECT "Enable GObject Bindings" OFF)
+if (HB_HAVE_GOBJECT)
+  set (HB_HAVE_GLIB ON)
+endif ()
+
+option(HB_HAVE_INTROSPECTION "Enable building introspection (.gir/.typelib) files" OFF)
+if (HB_HAVE_INTROSPECTION)
+  set (HB_HAVE_GOBJECT ON)
+  set (HB_HAVE_GLIB ON)
+endif ()
+
+option(HB_DISABLE_TEST_PROGS OFF "Do not build some of the test programs, useful for continuous builds")
+option(HB_CHECK OFF "Do a configuration suitable for testing (shared library and enable all options)")
+if (HB_CHECK)
+  set (BUILD_SHARED_LIBS ON)
+  set (HB_BUILD_UTILS ON)
+  set (HB_BUILTIN_UCDN ON)
+  set (HB_HAVE_ICU)
+  set (HB_HAVE_GLIB ON)
+  #set (HB_HAVE_GOBJECT ON)
+  #set (HB_HAVE_INTROSPECTION ON)
+  set (HB_HAVE_FREETYPE ON)
+  set (HB_HAVE_GRAPHITE2 ON)
+  if (WIN32)
+    set (HB_HAVE_UNISCRIBE ON)
+    set (HB_HAVE_DIRECTWRITE ON)
+  elseif (APPLE)
+    set (HB_HAVE_CORETEXT ON)
+  endif ()
+endif ()
+
+include_directories(AFTER
+  ${PROJECT_SOURCE_DIR}/src
+  ${PROJECT_BINARY_DIR}/src
+)
+
+add_definitions(-DHAVE_OT)
+add_definitions(-DHAVE_FALLBACK)
+
+
+## Functions and headers
+include (CheckFunctionExists)
+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})
+    endif ()
+  endforeach ()
+endmacro ()
+check_funcs(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l)
+check_include_file(unistd.h HAVE_UNISTD_H)
+if (${HAVE_UNISTD_H})
+  add_definitions(-DHAVE_UNISTD_H)
+endif ()
+check_include_file(sys/mman.h HAVE_SYS_MMAN_H)
+if (${HAVE_SYS_MMAN_H})
+  add_definitions(-DHAVE_SYS_MMAN_H)
+endif ()
+check_include_file(xlocale.h HAVE_XLOCALE_H)
+if (${HAVE_XLOCALE_H})
+  add_definitions(-DHAVE_XLOCALE_H)
+endif ()
+
+
+if (MSVC)
+  add_definitions(-wd4244 -wd4267 -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS)
+endif ()
+
+if (BUILD_SHARED_LIBS)
+  if (WIN32 AND NOT MINGW)
+    add_definitions("-DHB_EXTERN=__declspec(dllexport) extern")
+  else ()
+    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden")
+  endif ()
+endif ()
+
+
+## Detect if we are running inside a distribution or regular repository folder
+set (IN_HB_DIST FALSE)
+if (EXISTS "${PROJECT_SOURCE_DIR}/ChangeLog")
+  # perhaps we are on dist directory
+  set (IN_HB_DIST TRUE)
+  set (HB_VERSION_H "${PROJECT_SOURCE_DIR}/src/hb-version.h")
+endif ()
+
+
+## Extract variables from Makefile files
+function (extract_make_variable variable makefile_source)
+  string(REGEX MATCH "${variable} = ([^$]+)\\$" temp ${makefile_source})
+  string(REGEX MATCHALL "[^ \n\t\\]+" listVar ${CMAKE_MATCH_1})
+  set (${variable} ${listVar} PARENT_SCOPE)
+endfunction ()
+
+# http://stackoverflow.com/a/27630120
+function (add_prefix_to_list var prefix)
+  set (listVar "")
+  foreach (f ${${var}})
+    list(APPEND listVar "${prefix}${f}")
+  endforeach ()
+  set (${var} "${listVar}" PARENT_SCOPE)
+endfunction ()
+
+file(READ ${PROJECT_SOURCE_DIR}/src/Makefile.sources SRCSOURCES)
+file(READ ${PROJECT_SOURCE_DIR}/util/Makefile.sources UTILSOURCES)
+file(READ ${PROJECT_SOURCE_DIR}/src/hb-ucdn/Makefile.sources UCDNSOURCES)
+
+extract_make_variable(HB_BASE_sources ${SRCSOURCES})
+add_prefix_to_list(HB_BASE_sources "${PROJECT_SOURCE_DIR}/src/")
+extract_make_variable(HB_BASE_headers ${SRCSOURCES})
+add_prefix_to_list(HB_BASE_headers "${PROJECT_SOURCE_DIR}/src/")
+extract_make_variable(HB_FALLBACK_sources ${SRCSOURCES})
+add_prefix_to_list(HB_FALLBACK_sources "${PROJECT_SOURCE_DIR}/src/")
+extract_make_variable(HB_OT_sources ${SRCSOURCES})
+add_prefix_to_list(HB_OT_sources "${PROJECT_SOURCE_DIR}/src/")
+extract_make_variable(HB_OT_headers ${SRCSOURCES})
+add_prefix_to_list(HB_OT_headers "${PROJECT_SOURCE_DIR}/src/")
+
+extract_make_variable(HB_SUBSET_sources ${SRCSOURCES})
+add_prefix_to_list(HB_SUBSET_sources "${PROJECT_SOURCE_DIR}/src/")
+
+extract_make_variable(HB_SUBSET_headers ${SRCSOURCES})
+add_prefix_to_list(HB_SUBSET_headers "${PROJECT_SOURCE_DIR}/src/")
+
+extract_make_variable(HB_BASE_RAGEL_GENERATED_sources ${SRCSOURCES})
+extract_make_variable(HB_OT_RAGEL_GENERATED_sources ${SRCSOURCES})
+if (IN_HB_DIST)
+  add_prefix_to_list(HB_BASE_RAGEL_GENERATED_sources "${PROJECT_SOURCE_DIR}/src/")
+  add_prefix_to_list(HB_OT_RAGEL_GENERATED_sources "${PROJECT_SOURCE_DIR}/src/")
+else ()
+  add_prefix_to_list(HB_BASE_RAGEL_GENERATED_sources "${PROJECT_BINARY_DIR}/src/")
+  add_prefix_to_list(HB_OT_RAGEL_GENERATED_sources "${PROJECT_BINARY_DIR}/src/")
+endif ()
+
+extract_make_variable(HB_VIEW_sources ${UTILSOURCES})
+add_prefix_to_list(HB_VIEW_sources "${PROJECT_SOURCE_DIR}/util/")
+extract_make_variable(HB_SHAPE_sources ${UTILSOURCES})
+add_prefix_to_list(HB_SHAPE_sources "${PROJECT_SOURCE_DIR}/util/")
+extract_make_variable(HB_SUBSET_CLI_sources ${UTILSOURCES})
+add_prefix_to_list(HB_SUBSET_CLI_sources "${PROJECT_SOURCE_DIR}/util/")
+extract_make_variable(HB_OT_SHAPE_CLOSURE_sources ${UTILSOURCES})
+add_prefix_to_list(HB_OT_SHAPE_CLOSURE_sources "${PROJECT_SOURCE_DIR}/util/")
+
+extract_make_variable(LIBHB_UCDN_sources ${UCDNSOURCES})
+add_prefix_to_list(LIBHB_UCDN_sources "${PROJECT_SOURCE_DIR}/src/hb-ucdn/")
+
+
+file(READ configure.ac CONFIGUREAC)
+string(REGEX MATCH "\\[(([0-9]+)\\.([0-9]+)\\.([0-9]+))\\]" HB_VERSION_MATCH ${CONFIGUREAC})
+set (HB_VERSION ${CMAKE_MATCH_1})
+set (HB_VERSION_MAJOR ${CMAKE_MATCH_2})
+set (HB_VERSION_MINOR ${CMAKE_MATCH_3})
+set (HB_VERSION_MICRO ${CMAKE_MATCH_4})
+
+
+## Define ragel tasks
+if (NOT IN_HB_DIST)
+  find_program(RAGEL "ragel" CMAKE_FIND_ROOT_PATH_BOTH)
+
+  if (RAGEL)
+    message(STATUS "ragel found at: ${RAGEL}")
+  else ()
+    message(FATAL_ERROR "ragel not found, get it here -- http://www.complang.org/ragel/ or, use harfbuzz releases https://github.com/harfbuzz/harfbuzz/releases")
+  endif ()
+
+  foreach (ragel_output IN ITEMS ${HB_BASE_RAGEL_GENERATED_sources} ${HB_OT_RAGEL_GENERATED_sources})
+    string(REGEX MATCH "([^/]+)\\.hh" temp ${ragel_output})
+    set (target_name ${CMAKE_MATCH_1})
+    add_custom_command(OUTPUT ${ragel_output}
+      COMMAND ${RAGEL} -G2 -o ${ragel_output} ${PROJECT_SOURCE_DIR}/src/${target_name}.rl -I ${PROJECT_SOURCE_DIR} ${ARGN}
+      DEPENDS ${PROJECT_SOURCE_DIR}/src/${target_name}.rl
+    )
+    add_custom_target(harfbuzz_${target_name} DEPENDS ${PROJECT_BINARY_DIR}/src/${target_name})
+  endforeach ()
+
+  mark_as_advanced(RAGEL)
+endif ()
+
+
+## Generate hb-version.h
+if (NOT IN_HB_DIST)
+  set (HB_VERSION_H_IN "${PROJECT_SOURCE_DIR}/src/hb-version.h.in")
+  set (HB_VERSION_H "${PROJECT_BINARY_DIR}/src/hb-version.h")
+  set_source_files_properties("${HB_VERSION_H}" PROPERTIES GENERATED true)
+  configure_file("${HB_VERSION_H_IN}" "${HB_VERSION_H}.tmp" @ONLY)
+  execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different
+    "${HB_VERSION_H}.tmp"
+    "${HB_VERSION_H}"
+  )
+  file(REMOVE "${HB_VERSION_H}.tmp")
+endif ()
+
+
+## Define sources and headers of the project
+set (project_sources
+  ${HB_BASE_sources}
+  ${HB_BASE_RAGEL_GENERATED_sources}
+
+  ${HB_FALLBACK_sources}
+  ${HB_OT_sources}
+  ${HB_OT_RAGEL_GENERATED_sources}
+)
+
+set (subset_project_sources
+  ${HB_SUBSET_sources}
+)
+
+set (project_extra_sources)
+
+set (project_headers
+  ${HB_VERSION_H}
+
+  ${HB_BASE_headers}
+  ${HB_OT_headers}
+)
+
+set (subset_project_headers
+  ${HB_SUBSET_headers}
+)
+
+
+## Find and include needed header folders and libraries
+if (HB_HAVE_FREETYPE)
+  include (FindFreetype)
+  if (NOT FREETYPE_FOUND)
+    message(FATAL_ERROR "HB_HAVE_FREETYPE was set, but we failed to find it. Maybe add a CMAKE_PREFIX_PATH= to your Freetype2 install prefix")
+  endif ()
+
+  list(APPEND THIRD_PARTY_LIBS ${FREETYPE_LIBRARIES})
+  include_directories(AFTER ${FREETYPE_INCLUDE_DIRS})
+  add_definitions(-DHAVE_FREETYPE=1)
+
+  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-ft.cc)
+  list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-ft.h)
+
+  # So check_funcs can find its headers
+  set (CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${FREETYPE_INCLUDE_DIRS})
+  set (CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${FREETYPE_LIBRARIES})
+
+  check_funcs(FT_Get_Var_Blend_Coordinates FT_Set_Var_Blend_Coordinates FT_Done_MM_Var)
+endif ()
+
+if (HB_HAVE_GRAPHITE2)
+  add_definitions(-DHAVE_GRAPHITE2)
+
+  find_path(GRAPHITE2_INCLUDE_DIR graphite2/Font.h)
+  find_library(GRAPHITE2_LIBRARY graphite2)
+
+  include_directories(${GRAPHITE2_INCLUDE_DIR})
+
+  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-graphite2.cc)
+  list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-graphite2.h)
+
+  list(APPEND THIRD_PARTY_LIBS ${GRAPHITE2_LIBRARY})
+
+  mark_as_advanced(GRAPHITE2_INCLUDE_DIR GRAPHITE2_LIBRARY)
+endif ()
+
+if (HB_BUILTIN_UCDN)
+  include_directories(src/hb-ucdn)
+  add_definitions(-DHAVE_UCDN)
+
+  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-ucdn.cc)
+  list(APPEND project_extra_sources ${LIBHB_UCDN_sources})
+endif ()
+
+if (HB_HAVE_GLIB)
+  add_definitions(-DHAVE_GLIB)
+
+  # https://github.com/WebKit/webkit/blob/master/Source/cmake/FindGLIB.cmake
+  find_package(PkgConfig)
+  pkg_check_modules(PC_GLIB QUIET glib-2.0)
+
+  find_library(GLIB_LIBRARIES NAMES glib-2.0 HINTS ${PC_GLIB_LIBDIR} ${PC_GLIB_LIBRARY_DIRS})
+  find_path(GLIBCONFIG_INCLUDE_DIR NAMES glibconfig.h HINTS ${PC_LIBDIR} ${PC_LIBRARY_DIRS} ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS} PATH_SUFFIXES glib-2.0/include)
+  find_path(GLIB_INCLUDE_DIR NAMES glib.h HINTS ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS} PATH_SUFFIXES glib-2.0)
+
+  include_directories(${GLIBCONFIG_INCLUDE_DIR} ${GLIB_INCLUDE_DIR})
+
+  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-glib.cc)
+  list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-glib.h)
+
+  list(APPEND THIRD_PARTY_LIBS ${GLIB_LIBRARIES})
+
+  mark_as_advanced(GLIB_LIBRARIES GLIBCONFIG_INCLUDE_DIR GLIB_INCLUDE_DIR)
+endif ()
+
+if (HB_HAVE_ICU)
+  add_definitions(-DHAVE_ICU)
+
+  # https://github.com/WebKit/webkit/blob/master/Source/cmake/FindICU.cmake
+  find_package(PkgConfig)
+  pkg_check_modules(PC_ICU QUIET icu-uc)
+
+  find_path(ICU_INCLUDE_DIR NAMES unicode/utypes.h HINTS ${PC_ICU_INCLUDE_DIRS} ${PC_ICU_INCLUDEDIR})
+  find_library(ICU_LIBRARY NAMES libicuuc cygicuuc cygicuuc32 icuuc HINTS ${PC_ICU_LIBRARY_DIRS} ${PC_ICU_LIBDIR})
+
+  include_directories(${ICU_INCLUDE_DIR})
+
+  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-icu.cc)
+  list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-icu.h)
+
+  list(APPEND THIRD_PARTY_LIBS ${ICU_LIBRARY})
+
+  mark_as_advanced(ICU_INCLUDE_DIR ICU_LIBRARY)
+endif ()
+
+if (APPLE AND HB_HAVE_CORETEXT)
+  # Apple Advanced Typography
+  add_definitions(-DHAVE_CORETEXT)
+
+  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-coretext.cc)
+  list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-coretext.h)
+
+  find_library(APPLICATION_SERVICES_FRAMEWORK ApplicationServices)
+  if (APPLICATION_SERVICES_FRAMEWORK)
+    list(APPEND THIRD_PARTY_LIBS ${APPLICATION_SERVICES_FRAMEWORK})
+  endif (APPLICATION_SERVICES_FRAMEWORK)
+
+  mark_as_advanced(APPLICATION_SERVICES_FRAMEWORK)
+endif ()
+
+if (WIN32 AND HB_HAVE_UNISCRIBE)
+  add_definitions(-DHAVE_UNISCRIBE)
+
+  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-uniscribe.cc)
+  list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-uniscribe.h)
+
+  list(APPEND THIRD_PARTY_LIBS usp10 gdi32 rpcrt4)
+endif ()
+
+if (WIN32 AND HB_HAVE_DIRECTWRITE)
+  add_definitions(-DHAVE_DIRECTWRITE)
+
+  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-directwrite.cc)
+  list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-directwrite.h)
+
+  list(APPEND THIRD_PARTY_LIBS dwrite rpcrt4)
+endif ()
+
+if (HB_HAVE_GOBJECT)
+  include (FindPythonInterp)
+  include (FindPerl)
+
+  # Use the hints from glib-2.0.pc to find glib-mkenums
+  find_package(PkgConfig)
+  pkg_check_modules(PC_GLIB QUIET glib-2.0)
+  find_program(GLIB_MKENUMS glib-mkenums
+    HINTS ${PC_glib_mkenums}
+  )
+  set (GLIB_MKENUMS_CMD)
+
+  if (WIN32 AND NOT MINGW)
+    # In Visual Studio builds, shebang lines are not supported
+    # in the standard cmd.exe shell that we use, so we need to
+    # first determine whether glib-mkenums is a Python or PERL
+    # script
+    execute_process(COMMAND "${PYTHON_EXECUTABLE}" "${GLIB_MKENUMS}" --version
+      RESULT_VARIABLE GLIB_MKENUMS_PYTHON
+      OUTPUT_QUIET ERROR_QUIET
+    )
+    if (GLIB_MKENUMS_PYTHON EQUAL 0)
+      message("${GLIB_MKENUMS} is a Python script.")
+      set (GLIB_MKENUMS_CMD "${PYTHON_EXECUTABLE}" "${GLIB_MKENUMS}")
+    else ()
+      execute_process(COMMAND "${PERL_EXECUTABLE}" "${GLIB_MKENUMS}" --version
+        RESULT_VARIABLE GLIB_MKENUMS_PERL
+        OUTPUT_QUIET ERROR_QUIET
+      )
+      if (GLIB_MKENUMS_PERL EQUAL 0)
+        message("${GLIB_MKENUMS} is a PERL script.")
+        set (GLIB_MKENUMS_CMD "${PERL_EXECUTABLE}" "${GLIB_MKENUMS}")
+      endif ()
+      if (NOT GLIB_MKENUMS_PERL EQUAL 0 AND NOT GLIB_MKENUMS_PYTHON EQUAL 0)
+        message(FATAL_ERROR "Unable to determine type of glib-mkenums script")
+      endif ()
+    endif ()
+  else ()
+    set (GLIB_MKENUMS_CMD "${GLIB_MKENUMS}")
+  endif ()
+  if (NOT GLIB_MKENUMS_CMD)
+    message(FATAL_ERROR "HB_HAVE_GOBJECT was set, but we failed to find glib-mkenums, which is required")
+  endif ()
+
+  pkg_check_modules(PC_GOBJECT QUIET gobject-2.0)
+
+  find_library(GOBJECT_LIBRARIES NAMES gobject-2.0 HINTS ${PC_GLIB_LIBDIR} ${PC_GLIB_LIBRARY_DIRS})
+  find_path(GOBJECT_INCLUDE_DIR NAMES glib-object.h HINTS ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS} PATH_SUFFIXES glib-2.0)
+
+  include_directories(${GOBJECTCONFIG_INCLUDE_DIR} ${GOBJECT_INCLUDE_DIR})
+  mark_as_advanced(GOBJECT_LIBRARIES GOBJECT_INCLUDE_DIR)
+
+  list(APPEND hb_gobject_sources ${PROJECT_SOURCE_DIR}/src/hb-gobject-structs.cc)
+  list(APPEND hb_gobject_gen_sources
+    ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.cc
+  )
+  list(APPEND hb_gobject_structs_headers
+    ${PROJECT_SOURCE_DIR}/src/hb-gobject-structs.h
+  )
+  list(APPEND hb_gobject_headers
+    ${PROJECT_SOURCE_DIR}/src/hb-gobject.h
+    ${hb_gobject_structs_headers}
+  )
+  list(APPEND hb_gobject_gen_headers
+    ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h
+  )
+
+  add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h
+    COMMAND ${GLIB_MKENUMS_CMD}
+      --template=${PROJECT_SOURCE_DIR}/src/hb-gobject-enums.h.tmpl
+      --identifier-prefix hb_
+      --symbol-prefix hb_gobject
+      ${hb_gobject_structs_headers}
+      ${project_headers}
+      > ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h.tmp
+    COMMAND "${CMAKE_COMMAND}"
+      "-DENUM_INPUT_SRC=${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h.tmp"
+      "-DENUM_OUTPUT_SRC=${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h"
+      -P ${PROJECT_SOURCE_DIR}/replace-enum-strings.cmake
+    DEPENDS ${PROJECT_SOURCE_DIR}/src/hb-gobject-enums.h.tmpl
+      ${hb_gobject_header}
+      ${project_headers}
+  )
+
+  add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.cc
+    COMMAND ${GLIB_MKENUMS_CMD}
+      --template=${PROJECT_SOURCE_DIR}/src/hb-gobject-enums.cc.tmpl
+      --identifier-prefix hb_
+      --symbol-prefix hb_gobject
+      ${hb_gobject_header}
+      ${project_headers}
+      > ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.cc.tmp
+    COMMAND "${CMAKE_COMMAND}"
+      "-DENUM_INPUT_SRC=${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.cc.tmp"
+      "-DENUM_OUTPUT_SRC=${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.cc"
+      -P ${PROJECT_SOURCE_DIR}/replace-enum-strings.cmake
+    DEPENDS ${PROJECT_SOURCE_DIR}/src/hb-gobject-enums.cc.tmpl
+      ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h
+      ${hb_gobject_header}
+      ${project_headers}
+  )
+endif ()
+
+
+## Atomic ops availability detection
+file(WRITE "${PROJECT_BINARY_DIR}/try_compile_intel_atomic_primitives.c"
+"		void memory_barrier (void) { __sync_synchronize (); }
+		int atomic_add (int *i) { return __sync_fetch_and_add (i, 1); }
+		int mutex_trylock (int *m) { return __sync_lock_test_and_set (m, 1); }
+		void mutex_unlock (int *m) { __sync_lock_release (m); }
+		int main () { return 0; }
+")
+try_compile(HB_HAVE_INTEL_ATOMIC_PRIMITIVES
+  ${PROJECT_BINARY_DIR}/try_compile_intel_atomic_primitives
+  ${PROJECT_BINARY_DIR}/try_compile_intel_atomic_primitives.c)
+if (HB_HAVE_INTEL_ATOMIC_PRIMITIVES)
+  add_definitions(-DHAVE_INTEL_ATOMIC_PRIMITIVES)
+endif ()
+
+file(WRITE "${PROJECT_BINARY_DIR}/try_compile_solaris_atomic_ops.c"
+"		#include <atomic.h>
+		/* This requires Solaris Studio 12.2 or newer: */
+		#include <mbarrier.h>
+		void memory_barrier (void) { __machine_rw_barrier (); }
+		int atomic_add (volatile unsigned *i) { return atomic_add_int_nv (i, 1); }
+		void *atomic_ptr_cmpxchg (volatile void **target, void *cmp, void *newval) { return atomic_cas_ptr (target, cmp, newval); }
+		int main () { return 0; }
+")
+try_compile(HB_HAVE_SOLARIS_ATOMIC_OPS
+  ${PROJECT_BINARY_DIR}/try_compile_solaris_atomic_ops
+  ${PROJECT_BINARY_DIR}/try_compile_solaris_atomic_ops.c)
+if (HB_HAVE_SOLARIS_ATOMIC_OPS)
+  add_definitions(-DHAVE_SOLARIS_ATOMIC_OPS)
+endif ()
+
+
+## Define harfbuzz library
+add_library(harfbuzz ${project_sources} ${project_extra_sources} ${project_headers})
+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 (UNIX OR MINGW)
+  # Make symbols link locally
+  link_libraries(-Bsymbolic-functions)
+
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    # Make sure we don't link to libstdc++
+    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions")
+    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)
+
+    # No threadsafe statics as we do it ourselves
+    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics")
+  endif ()
+endif ()
+
+## Define harfbuzz-gobject library
+if (HB_HAVE_GOBJECT)
+  add_library(harfbuzz-gobject
+    ${hb_gobject_sources}
+    ${hb_gobject_gen_sources}
+    ${hb_gobject_headers}
+    ${hb_gobject_gen_headers}
+  )
+  include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/src)
+  add_dependencies(harfbuzz-gobject harfbuzz)
+  target_link_libraries(harfbuzz-gobject harfbuzz ${GOBJECT_LIBRARIES} ${THIRD_PARTY_LIBS})
+endif ()
+
+# On Windows, g-ir-scanner requires a DLL build in order for it to work
+if (WIN32)
+  if (NOT BUILD_SHARED_LIBS)
+    message("Building introspection files on Windows requires BUILD_SHARED_LIBS to be enabled.")
+    set (HB_HAVE_INTROSPECTION OFF)
+  endif ()
+endif ()
+
+if (HB_HAVE_INTROSPECTION)
+
+  find_package(PkgConfig)
+  pkg_check_modules(PC_GI QUIET gobject-introspection-1.0)
+
+  find_program(G_IR_SCANNER g-ir-scanner
+    HINTS ${PC_g_ir_scanner}
+  )
+
+  find_program(G_IR_COMPILER g-ir-compiler
+    HINTS ${PC_g_ir_compiler}
+  )
+
+  if (WIN32 AND NOT MINGW)
+    # Note that since we already enable HB_HAVE_GOBJECT
+    # we would already have PYTHON_EXECUTABLE handy
+    set (G_IR_SCANNER_CMD "${PYTHON_EXECUTABLE}" "${G_IR_SCANNER}")
+  else ()
+    set (G_IR_SCANNER_CMD "${G_IR_SCANNER}")
+  endif ()
+
+  # We need to account for the varying output directories
+  # when we build using Visual Studio projects
+  if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio*")
+    set (hb_libpath "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>")
+  else ()
+    set (hb_libpath "$<TARGET_FILE_DIR:harfbuzz-gobject>")
+  endif ()
+
+  # Get the CFlags that we used to build HarfBuzz/HarfBuzz-GObject
+  set (hb_defines_cflags "")
+  foreach (hb_cflag ${hb_cflags})
+    list(APPEND hb_defines_cflags "-D${hb_cflag}")
+  endforeach (hb_cflag)
+
+  # Get the other dependent libraries we used to build HarfBuzz/HarfBuzz-GObject
+  set (extra_libs "")
+  foreach (extra_lib ${THIRD_PARTY_LIBS})
+    # We don't want the .lib extension here...
+    string(REPLACE ".lib" "" extra_lib_stripped "${extra_lib}")
+    list(APPEND extra_libs "--extra-library=${extra_lib_stripped}")
+  endforeach ()
+
+  set (introspected_sources)
+  foreach (f
+    ${project_headers}
+    ${project_sources}
+    ${hb_gobject_gen_sources}
+    ${hb_gobject_gen_headers}
+    ${hb_gobject_sources}
+    ${hb_gobject_headers}
+  )
+    if (WIN32)
+      # Nasty issue: We need to make drive letters lower case,
+      # otherwise g-ir-scanner won't like it and give us a bunch
+      # of invalid items and unresolved types...
+      STRING(SUBSTRING "${f}" 0 1 drive)
+      STRING(SUBSTRING "${f}" 1 -1 path)
+      if (drive MATCHES "[A-Z]")
+        STRING(TOLOWER ${drive} drive_lower)
+        list(APPEND introspected_sources "${drive_lower}${path}")
+      else ()
+        list(APPEND introspected_sources "${f}")
+      endif ()
+    else ()
+      list(APPEND introspected_sources "${f}")
+    endif ()
+  endforeach ()
+
+  # Finally, build the introspection files...
+  add_custom_command(
+    TARGET harfbuzz-gobject
+    POST_BUILD
+    COMMAND ${G_IR_SCANNER_CMD}
+      --warn-all --no-libtool --verbose
+      -n hb
+      --namespace=HarfBuzz
+      --nsversion=0.0
+      --identifier-prefix=hb_
+      --include GObject-2.0
+      --pkg-export=harfbuzz
+      --cflags-begin
+      -I${PROJECT_SOURCE_DIR}/src
+      -I${PROJECT_BINARY_DIR}/src
+      ${hb_includedir_cflags}
+      ${hb_defines_cflags}
+      -DHB_H
+      -DHB_H_IN
+      -DHB_OT_H
+      -DHB_OT_H_IN
+      -DHB_GOBJECT_H
+      -DHB_GOBJECT_H_IN
+      -DHB_EXTERN=
+      --cflags-end
+      --library=harfbuzz-gobject
+      --library=harfbuzz
+      -L${hb_libpath}
+      ${extra_libs}
+      ${introspected_sources}
+      -o ${hb_libpath}/HarfBuzz-0.0.gir
+    DEPENDS harfbuzz-gobject harfbuzz
+  )
+
+  add_custom_command(
+    TARGET harfbuzz-gobject
+    POST_BUILD
+    COMMAND "${G_IR_COMPILER}"
+      --verbose --debug
+      --includedir ${CMAKE_CURRENT_BINARY_DIR}
+      ${hb_libpath}/HarfBuzz-0.0.gir
+      -o ${hb_libpath}/HarfBuzz-0.0.typelib
+    DEPENDS ${hb_libpath}/HarfBuzz-0.0.gir harfbuzz-gobject
+  )
+endif ()
+
+
+## Additional framework build configs
+if (BUILD_FRAMEWORK)
+  set (CMAKE_MACOSX_RPATH ON)
+  set_target_properties(harfbuzz PROPERTIES
+    FRAMEWORK TRUE
+    PUBLIC_HEADER "${project_headers}"
+    XCODE_ATTRIBUTE_INSTALL_PATH "@rpath"
+  )
+  set (MACOSX_FRAMEWORK_IDENTIFIER "harfbuzz")
+  set (MACOSX_FRAMEWORK_SHORT_VERSION_STRING "${HB_VERSION}")
+  set (MACOSX_FRAMEWORK_BUNDLE_VERSION "${HB_VERSION}")
+endif ()
+
+
+## Additional harfbuzz build artifacts
+if (HB_BUILD_UTILS)
+  # https://github.com/WebKit/webkit/blob/master/Source/cmake/FindCairo.cmake
+  find_package(PkgConfig)
+  pkg_check_modules(PC_CAIRO QUIET cairo)
+
+  find_path(CAIRO_INCLUDE_DIRS NAMES cairo.h HINTS ${PC_CAIRO_INCLUDEDIR} ${PC_CAIRO_INCLUDE_DIRS} PATH_SUFFIXES cairo)
+  find_library(CAIRO_LIBRARIESNAMES cairo HINTS ${PC_CAIRO_LIBDIR} ${PC_CAIRO_LIBRARY_DIRS})
+
+  add_definitions("-DPACKAGE_NAME=\"HarfBuzz\"")
+  add_definitions("-DPACKAGE_VERSION=\"${HB_VERSION}\"")
+  include_directories(${CAIRO_INCLUDE_DIRS})
+
+  add_executable(hb-view ${HB_VIEW_sources})
+  target_link_libraries(hb-view harfbuzz ${CAIRO_LIBRARIESNAMES})
+
+  add_executable(hb-shape ${HB_SHAPE_sources})
+  target_link_libraries(hb-shape harfbuzz)
+
+  add_executable(hb-subset ${HB_SUBSET_CLI_sources})
+  target_link_libraries(hb-subset harfbuzz harfbuzz-subset)
+
+  add_executable(hb-ot-shape-closure ${HB_OT_SHAPE_CLOSURE_sources})
+  target_link_libraries(hb-ot-shape-closure harfbuzz)
+
+  mark_as_advanced(CAIRO_INCLUDE_DIRS CAIRO_LIBRARIESNAMES)
+endif ()
+
+
+## Install
+include (GNUInstallDirs)
+
+if (NOT SKIP_INSTALL_HEADERS AND NOT SKIP_INSTALL_ALL)
+  install(FILES ${project_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/harfbuzz)
+  if (HB_HAVE_GOBJECT)
+    install(FILES ${hb_gobject_headers} ${hb_gobject_gen_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/harfbuzz)
+  endif ()
+endif ()
+
+if (NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL)
+  install(TARGETS harfbuzz
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    FRAMEWORK DESTINATION Library/Frameworks
+  )
+  if (HB_BUILD_UTILS)
+    install(TARGETS hb-view
+      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    )
+    install(TARGETS hb-view
+      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    )
+
+    install(TARGETS hb-shape
+      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    )
+
+    install(TARGETS hb-ot-shape-closure
+      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    )
+  endif ()
+  if (HB_HAVE_GOBJECT)
+    install(TARGETS harfbuzz-gobject
+      ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+      LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    )
+    if (HB_HAVE_INTROSPECTION)
+      if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio*")
+        set (hb_libpath "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>")
+      else ()
+        set (hb_libpath "$<TARGET_FILE_DIR:harfbuzz-gobject>")
+      endif ()
+
+      install(FILES "${hb_libpath}/HarfBuzz-0.0.gir"
+        DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gir-1.0
+      )
+
+      install(FILES "${hb_libpath}/HarfBuzz-0.0.typelib"
+        DESTINATION ${CMAKE_INSTALL_LIBDIR}/girepository-1.0
+      )
+    endif ()
+  endif ()
+endif ()
+
+if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja")
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics")
+    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics")
+  endif ()
+  if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color")
+    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color")
+  endif ()
+endif ()
+
+
+## src/ executables
+if (NOT HB_DISABLE_TEST_PROGS)
+  foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag)
+    set (prog_name ${prog})
+    if (${prog_name} STREQUAL "test")
+      # test can not be used as a valid executable name on cmake, lets special case it
+      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")
+endif ()
+
+## Tests
+if (UNIX OR MINGW)
+  if (BUILD_SHARED_LIBS)
+    # generate harfbuzz.def after build completion
+    string(REPLACE ";" " " space_separated_headers "${project_headers}")
+    add_custom_command(TARGET harfbuzz POST_BUILD
+      COMMAND ${CMAKE_COMMAND} -E env "headers=${space_separated_headers}" python ${PROJECT_SOURCE_DIR}/src/gen-def.py ${PROJECT_BINARY_DIR}/harfbuzz.def
+      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)
+
+    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-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)
+endif ()
+
+# Needs to come last so that variables defined above are passed to
+# subdirectories.
+add_subdirectory(test)
diff --git a/Makefile.am b/Makefile.am
index 8dc8a4b..fde5256 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,13 +4,16 @@
 
 ACLOCAL_AMFLAGS = -I m4
 
-SUBDIRS = src util test docs win32
+SUBDIRS = src util test docs
 
 EXTRA_DIST = \
 	autogen.sh \
 	harfbuzz.doap \
 	README.python \
 	BUILD.md \
+	RELEASING.md \
+	CMakeLists.txt \
+	replace-enum-strings.cmake \
 	$(NULL)
 
 MAINTAINERCLEANFILES = \
diff --git a/NEWS b/NEWS
index 43a3bac..9015c4a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,239 @@
+Overview of changes leading to 1.7.5
+Tuesday, January 30, 2018
+====================================
+
+- Separate Khmer shaper from Indic.
+- First stab at AAT morx. Not hooked up.
+- Misc bug fixes.
+
+
+Overview of changes leading to 1.7.4
+Wednesday, December 20, 2017
+====================================
+
+- Fix collect_glyphs() regression caused by hb_set_t changes.
+
+
+Overview of changes leading to 1.7.3
+Monday, December 18, 2017
+====================================
+
+- hb_set_t performance tuning and optimizations.
+- Speed up collect_glyphs() and reject garbage data.
+- In hb_coretext_font_create() set font point-size (ptem).
+- Misc fixes.
+
+
+Overview of changes leading to 1.7.2
+Monday, December 4, 2017
+====================================
+
+- Optimize hb_set_add_range().
+- Misc fixes.
+- New API:
+hb_coretext_font_create()
+
+
+Overview of changes leading to 1.7.1
+Tuesday, November 14, 2017
+====================================
+
+- Fix atexit object destruction regression.
+- Fix minor integer-overflow.
+
+
+Overview of changes leading to 1.7.0
+Monday, November 13, 2017
+====================================
+
+- Minor Indic fixes.
+- Implement kerning and glyph names in hb-ot-font.
+- Various DSO optimization re .data and .bss sizes.
+- Make C++11 optional; build fixes.
+- Mark all other backends "unsafe-to-break".
+- Graphite fix.
+
+
+Overview of changes leading to 1.6.3
+Thursday, October 26th, 2017
+====================================
+
+- Fix hb_set_t some more.  Should be solid now.
+- Implement get_glyph_name() for hb-ot-font.
+- Misc fixes.
+
+
+Overview of changes leading to 1.6.2
+Monday, October 23nd, 2017
+====================================
+
+- Yesterday's release had a bad crasher; don't use it.  That's what
+  happens when one works on Sunday...
+  https://github.com/harfbuzz/harfbuzz/issues/578
+- Build fixes for FreeBSD and Chrome Android.
+
+
+Overview of changes leading to 1.6.1
+Sunday, October 22nd, 2017
+====================================
+
+- Don't skip over COMBINING GRAPHEME JOINER when ligating, etc.
+  To be refined: https://github.com/harfbuzz/harfbuzz/issues/554
+- Faster hb_set_t implementation.
+- Don't use deprecated ICU API.
+- Fix undefined-behavior in Myanmar shaper, introduced in 1.6.0
+- Deprecated API:
+  hb_set_invert()
+
+
+Overview of changes leading to 1.6.0
+Friday, October the 13th, 2017
+====================================
+
+- Update to Unicode 10.
+
+- Various Indic and Universal Shaping Engine fixes as a result of
+  HarfBuzz Hackfest with Jonathan Kew at Web Engines Hackfest at
+  the Igalia offices in A Coruña, Spain.  Thanks Igalia for having
+  us!
+
+- Implement Unicode Arabic Mark Ordering Algorithm UTR#53.
+
+- Implement optical sizing / tracking in CoreText backend, using
+  new API hb_font_set_ptem().
+
+- Allow notifying hb_font_t that underlying FT_Face changed sizing,
+  using new API hb_ft_font_changed().
+
+- More Graphite backend RTL fixes.
+
+- Fix caching of variable font shaping plans.
+
+- hb-view / hb-shape now accept following new arguments:
+
+  o --unicodes: takes a list of hex numbers that represent Unicode
+    codepoints.
+
+New API:
++hb_face_get_table_tags()
++hb_font_set_ptem()
++hb_font_get_ptem()
++hb_ft_font_changed()
+
+
+Overview of changes leading to 1.5.1
+Tuesday, September 5, 2017
+====================================
+
+- Fix "unsafe-to-break" in fallback shaping and other corner cases.
+  All our tests pass with --verify now, meaning unsafe-to-break API
+  works as expected.
+- Add --unicodes to hb-view / hb-shape.
+- [indic] Treat Consonant_With_Stacker as consonant.  This will need
+  further tweaking.
+- hb_buffer_diff() tweaks.
+
+
+Overview of changes leading to 1.5.0
+Wednesday, August 23, 2017
+====================================
+
+- Misc new API, for appending a buffer to another, and for comparing
+  contents of two buffers for types of differences.
+
+- New "unsafe-to-break" API.  Can be used to speed up reshaping
+  in line-breaking situations.  Essentially, after shaping, it returns
+  positions in the input string (some of the cluster boundaries) that
+  are "safe to break" in that if the text is segmented at that position
+  and two sides reshaped and concatenated, the shaping result is
+  exactly the same as shaping the text in one piece.
+
+  hb-view and hb-shape and hb-shape now take --verify, which verifies
+  the above property.
+
+  Some corner cases of the implementation are still not quite working.
+  Those will be fixed in subsequent releases.
+
+- New API:
+
+hb_buffer_append()
+
+hb_glyph_flags_t
+HB_GLYPH_FLAG_UNSAFE_TO_BREAK
+HB_GLYPH_FLAG_DEFINED
+hb_glyph_info_get_glyph_flags()
+
+HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS
+
+hb_buffer_diff_flags_t
+HB_BUFFER_DIFF_FLAG_EQUAL
+HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH
+HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH
+HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT
+HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
+HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH
+HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH
+HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH
+HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH
+hb_buffer_diff
+
+
+Overview of changes leading to 1.4.8
+Tuesday, August 8, 2017
+====================================
+
+- Major fix to avar table handling.
+- Rename hb-shape --show-message to --trace.
+- Build fixes.
+
+
+Overview of changes leading to 1.4.7
+Tuesday, July 18, 2017
+====================================
+
+- Multiple Indic, Tibetan, and Cham fixes.
+- CoreText: Allow disabling kerning.
+- Adjust Arabic feature order again.
+- Misc build fixes.
+
+
+Overview of changes leading to 1.4.6
+Sunday, April 23, 2017
+====================================
+
+- Graphite2: Fix RTL positioning issue.
+- Backlist GDEF of more versions of Padauk and Tahoma.
+- New, experimental, cmake alternative build system.
+
+
+Overview of changes leading to 1.4.5
+Friday, March 10, 2017
+====================================
+
+- Revert "Fix Context lookup application when moving back after a glyph..."
+  This introduced memory access problems.  To be fixed properly soon.
+
+
+Overview of changes leading to 1.4.4
+Sunday, March 5, 2017
+====================================
+
+- Fix Context lookup application when moving back after a glyph deletion.
+- Fix buffer-overrun in Bengali.
+
+
+Overview of changes leading to 1.4.3
+Saturday, February 25, 2017
+====================================
+
+- Route Adlam script to Arabic shaper.
+- Misc fixes.
+- New API:
+  hb_font_set_face()
+- Deprecate API:
+  hb_graphite2_font_get_gr_font()
+
+
 Overview of changes leading to 1.4.2
 Monday, January 23, 2017
 ====================================
@@ -243,7 +479,7 @@
 - CoreText: Drastically speed up font initialization.
 - CoreText: Fix tiny leak.
 - Group ZWJ/ZWNJ with previous syllable under cluster-level=0.
-  https://github.com/behdad/harfbuzz/issues/217
+  https://github.com/harfbuzz/harfbuzz/issues/217
 - Add test/shaping/README.md about how to add tests to the suite.
 
 
@@ -259,8 +495,8 @@
 - Allow GPOS cursive connection on marks, and fix the interaction with
   mark attachment.  This work resulted in some changes to how mark
   attachments work.  See:
-  https://github.com/behdad/harfbuzz/issues/211
-  https://github.com/behdad/harfbuzz/commit/86c68c7a2c971efe8e35b1f1bd99401dc8b688d2
+  https://github.com/harfbuzz/harfbuzz/issues/211
+  https://github.com/harfbuzz/harfbuzz/commit/86c68c7a2c971efe8e35b1f1bd99401dc8b688d2
 - Graphite2 shaper: improved negative advance handling (eg. Nastaliq).
 - Add nmake-based build system for Windows.
 - Minor speedup.
@@ -301,7 +537,7 @@
 ====================================
 
 - Fix badly-broken fallback shaper that affected terminology.
-  https://github.com/behdad/harfbuzz/issues/187
+  https://github.com/harfbuzz/harfbuzz/issues/187
 - Fix y_scaling in Graphite shaper.
 - API changes:
   * An unset glyph_h_origin() function in font-funcs now (sensibly)
@@ -323,11 +559,11 @@
 ====================================
 
 - Implement 'stch' stretch feature for Syriac Abbreviation Mark.
-  https://github.com/behdad/harfbuzz/issues/141
+  https://github.com/harfbuzz/harfbuzz/issues/141
 - Disable use of decompose_compatibility() callback.
 - Implement "shaping" of various Unicode space characters, even
   if the font does not support them.
-  https://github.com/behdad/harfbuzz/issues/153
+  https://github.com/harfbuzz/harfbuzz/issues/153
 - If font does not support U+2011 NO-BREAK HYPHEN, fallback to
   U+2010 HYPHEN.
 - Changes resulting from libFuzzer continuous fuzzing:
@@ -350,7 +586,7 @@
 - Revert default load-flags of fonts created using hb_ft_font_create()
   back to FT_LOAD_DEFAULT|FT_LOAD_NO_HINTING.  This was changed in
   last release (1.0.5), but caused major issues, so revert.
-  https://github.com/behdad/harfbuzz/issues/143
+  https://github.com/harfbuzz/harfbuzz/issues/143
 
 
 Overview of changes leading to 1.0.5
@@ -358,7 +594,7 @@
 ====================================
 
 - Fix multiple memory access bugs discovered using libFuzzer.
-  https://github.com/behdad/harfbuzz/issues/139
+  https://github.com/harfbuzz/harfbuzz/issues/139
   Everyone should upgrade to this version as soon as possible.
   We now have continuous fuzzing set up, to avoid issues like
   these creeping in again.
@@ -629,7 +865,7 @@
   U+FFFD REPLACEMENT CHARACTER now.
 - With all changes in this release, the buffer will contain fully
   valid Unicode after hb_buffer_add_utf8/16/32 no matter how
-  broken the input is.  This can be overriden though.  See below.
+  broken the input is.  This can be overridden though.  See below.
 - Fix Mongolian Variation Selectors for fonts without GDEF.
 - Fix minor invalid buffer access.
 - Accept zh-Hant and zh-Hans language tags.  hb_ot_tag_to_language()
diff --git a/README b/README
index 69a1bdd..6e21322 100644
--- a/README
+++ b/README
@@ -1,6 +1,8 @@
-[![Build Status](https://travis-ci.org/behdad/harfbuzz.svg)](https://travis-ci.org/behdad/harfbuzz)
-[![Build Status](https://ci.appveyor.com/api/projects/status/4oaq58ns2h0m2soa?svg=true)](https://ci.appveyor.com/project/behdad/harfbuzz)
-[![Coverage Status](https://img.shields.io/coveralls/behdad/harfbuzz.svg)](https://coveralls.io/r/behdad/harfbuzz)
+[![Build Status](https://travis-ci.org/harfbuzz/harfbuzz.svg)](https://travis-ci.org/harfbuzz/harfbuzz)
+[![Build status](https://ci.appveyor.com/api/projects/status/0t0flrxpstj9lb9w?svg=true)](https://ci.appveyor.com/project/harfbuzz/harfbuzz)
+[![CircleCI](https://circleci.com/gh/harfbuzz/harfbuzz.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz)
+[![Coverity](https://img.shields.io/coverity/scan/5450.svg)](https://scan.coverity.com/projects/behdad-harfbuzz)
+[![Coverage Status](https://img.shields.io/coveralls/harfbuzz/harfbuzz.svg)](https://coveralls.io/r/harfbuzz/harfbuzz)
 [ABI Tracker](http://abi-laboratory.pro/tracker/timeline/harfbuzz/)
 
 This is HarfBuzz, a text shaping library.
@@ -10,3 +12,5 @@
   http://harfbuzz.org/
 
 For license information, see the file COPYING.
+
+Documentation: https://harfbuzz.github.io
diff --git a/RELEASING.md b/RELEASING.md
new file mode 100644
index 0000000..dedcca8
--- /dev/null
+++ b/RELEASING.md
@@ -0,0 +1,99 @@
+HarfBuzz release walk-through checklist:
+
+1. Open gitk and review changes since last release.
+
+   * `git diff $(git describe | sed 's/-.*//').. src/*.h` prints all public API
+     changes.
+
+     Document them in NEWS.  All API and API semantic changes should be clearly
+     marked as API additions, API changes, or API deletions.  Document
+     deprecations.
+
+     If there's a backward-incompatible API change (including deletions for API
+     used anywhere), that's a release blocker.  Do NOT release.
+
+2. Based on severity of changes, decide whether it's a minor or micro release
+   number bump,
+
+3. Make sure you have correct date and new version at the top of NEWS file,
+
+4. Bump version in configure.ac line 3,
+
+5. Do "make distcheck", if it passes, you get a tarball.
+   Otherwise, fix things and commit them separately before making release,
+
+6. "make release-files".  Enter your GPG password.  This creates a sha256 hash
+   and signs it.
+
+7. Now that you have release files built, commit NEWS and configure.ac changes.
+   The commit message is simply the release number.  Eg. "1.4.7"
+
+8. Tag the release and sign it: Eg. "git tag -s 1.4.7 -m 1.4.7".  Enter your
+   GPG password again.
+
+9. Build win32 bundle.
+
+   a. Put contents of [this](https://drive.google.com/open?id=0B3_fQkxDZZXXbWltRGd5bjVrUDQ) on your `~/.local/i686-w64-mingw32`,
+
+   b. Run `../mingw32.sh --with-uniscribe` script (available below) to configure harfbuzz with mingw in a subdirector (eg. winbuild/),
+
+   c. make
+
+   d. Back in the parent directory, run `./UPDATE.sh` (available below) to build win32 bundle.
+
+10. Copy all artefacts to users.freedesktop.org and move them into
+    `/srv/www.freedesktop.org/www/software/harfbuzz/release` There should be four
+    files.  Eg.:
+ ```
+-rw-r--r--  1 behdad eng 1592693 Jul 18 11:25 harfbuzz-1.4.7.tar.bz2
+-rw-r--r--  1 behdad eng      89 Jul 18 11:34 harfbuzz-1.4.7.tar.bz2.sha256
+-rw-r--r--  1 behdad eng     339 Jul 18 11:34 harfbuzz-1.4.7.tar.bz2.sha256.asc
+-rw-r--r--  1 behdad eng 2895619 Jul 18 11:34 harfbuzz-1.4.7-win32.zip
+```
+
+11. While doing that, quickly double-check the size of the .tar.bz2 and .zip
+    files against their previous releases to make sure nothing bad happened.
+    They should be in the ballpark, perhaps slightly larger.  Sometimes they
+    do shrink, that's not by itself a stopper.
+
+12. Push the commit and tag out: "git push --follow-tags".  Make sure it's
+    pushed both to freedesktop repo and github.
+
+13. Go to GitHub release page [here](https://github.com/harfbuzz/harfbuzz/releases),
+    edit the tag, upload artefacts and NEWS entry and save.
+
+
+## UPDATE.sh
+```bash
+#!/bin/bash
+
+v=$1
+
+if test "x$v" = x; then
+	echo "usage: UPDATE.sh micro-version"
+	exit 1
+fi
+
+dir_prefix=harfbuzz-1.4.
+dir_suffix=-win32
+dir=$dir_prefix$v$dir_suffix
+dir_old=$dir_prefix$((v-1))$dir_suffix
+if test -d "$dir"; then
+	echo "New dir $dir exists; not overwriting"
+	exit 1
+fi
+if ! test -d "$dir_old"; then
+	echo "Old dir $dir_old does NOT exist; aborting"
+	exit 1
+fi
+set -ex
+cp -a "$dir_old" "$dir.tmp"
+rm -f "$dir.tmp"/GDX32.dll
+rm -f "$dir.tmp"/usp10.dll
+cp ../winbuild/src/.libs/libharfbuzz-0.dll{,.def} $dir.tmp/
+cp ../winbuild/util/.libs/hb-{shape,view}.exe $dir.tmp/
+i686-w64-mingw32-strip $dir.tmp/{hb-shape.exe,hb-view.exe,libharfbuzz-0.dll}
+mv $dir.tmp $dir
+zip -r $dir.zip $dir
+echo Bundle $dir.zip ready
+```
diff --git a/TODO b/TODO
index 4f37f60..53ffbe9 100644
--- a/TODO
+++ b/TODO
@@ -1,24 +1,14 @@
 General fixes:
 =============
 
-- AAT 'morx' implementation.
-
-- Return "safe-to-break" bit from shaping.
-
 - Implement 'rand' feature.
 
-- mask propagation? (when ligation, "or" the masks).
-
 
 API issues:
 ===========
 
 - API to accept a list of languages?
 
-- Add init_func to font_funcs.  Adjust ft.
-
-- 'const' for getter APIs? (use mutable internally)
-
 - Remove hb_ot_shape_glyphs_closure()?
 
 
@@ -39,7 +29,7 @@
 
 - Add query / enumeration API for aalt-like features?
 
-- SFNT api? get_num_faces? get_table_tags? (there's something in stash)
+- SFNT api? get_num_faces?
 
 - Add segmentation API
 
@@ -50,20 +40,3 @@
 ===============================
 
 - Add --width, --height, --auto-size, --ink-box, --align, etc?
-
-
-Tests to write:
-==============
-
-- ot-layout enumeration API (needs font)
-
-- Finish test-shape.c, grep for TODO
-
-- Finish test-unicode.c, grep for TODO
-
-- GObject, FreeType, etc
-
-- hb_cache_t and relatives
-
-- hb_feature_to/from_string
-- hb_buffer_[sg]et_contents
diff --git a/appveyor.yml b/appveyor.yml
index 67f6114..5971337 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -3,21 +3,21 @@
 environment:
   matrix:
     - compiler: msvc
-      ARCH: amd64
-      VCPKG_ARCH: x64-windows
-      CFG: release
+      generator: Visual Studio 14
+      platform: Win32
+      configuration: Debug
+      triplet: x86-windows
     - compiler: msvc
-      ARCH: x86
-      VCPKG_ARCH: x86-windows
-      CFG: release
+      generator: Visual Studio 14 Win64
+      platform: x64
+      configuration: Debug
+      triplet: x64-windows
+
     - compiler: msvc
-      ARCH: amd64
-      VCPKG_ARCH: x64-windows
-      CFG: debug
-    - compiler: msvc
-      ARCH: x86
-      VCPKG_ARCH: x86-windows
-      CFG: debug
+      generator: Visual Studio 14 ARM
+      platform: ARM
+      configuration: Debug
+
 
     - compiler: msys2
       MINGW_PREFIX: /c/msys2/mingw64/
@@ -31,22 +31,31 @@
 install:
   - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-ragel"
 
-  - 'if "%compiler%"=="msvc" call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %ARCH%'
-#  - 'if "%compiler%"=="msvc" git clone https://github.com/Microsoft/vcpkg'
-#  - 'if "%compiler%"=="msvc" cd vcpkg'
-#  - 'if "%compiler%"=="msvc" powershell -exec bypass scripts\bootstrap.ps1'
-#  - 'if "%compiler%"=="msvc" vcpkg install freetype:%VCPKG_ARCH%'
-#  - 'if "%compiler%"=="msvc" copy installed\%VCPKG_ARCH%\debug\lib\freetyped.lib installed\%VCPKG_ARCH%\lib'
-#  - 'if "%compiler%"=="msvc" cd ..'
-
 build_script:
-  - 'if "%compiler%"=="msvc" C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER; PATH=$PATH:/mingw64/bin:/mingw32/bin; ./autogen.sh; make distdir"'
-  - 'if "%compiler%"=="msvc" cd harfbuzz-*\win32'
-  - 'if "%compiler%"=="msvc" nmake /f Makefile.vc CFG=%CFG% UNISCRIBE=1 DIRECTWRITE=1' # FREETYPE=1 FREETYPE_DIR=..\..\vcpkg\installed\%VCPKG_ARCH%\include ADDITIONAL_LIB_DIR=..\..\vcpkg\installed\%VCPKG_ARCH%\lib'
-  - 'if "%compiler%"=="msvc" nmake /f Makefile.vc CFG=%CFG% UNISCRIBE=1 DIRECTWRITE=1 install' # FREETYPE=1 install'
+  - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" vcpkg install glib:%triplet% freetype:%triplet% cairo:%triplet%'
+  - 'if "%compiler%"=="msvc" md build'
+  - 'if "%compiler%"=="msvc" cd build'
+  - 'if "%compiler%"=="msvc" set PATH=%PATH%;C:\Program Files (x86)\MSBuild\14.0\Bin;c:\msys64\mingw64\bin' # msys2 is added just for having "ragel" on PATH
 
-  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-$MSYS2_ARCH-{freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config}"'
-  - '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 --build=$MINGW_CHOST --host=$MINGW_CHOST --prefix=$MINGW_PREFIX; make; make check"'
+  - 'if "%compiler%"=="msvc" if "%platform%"=="ARM" cmake -DHB_HAVE_UNISCRIBE=ON -DHB_HAVE_DIRECTWRITE=ON -G "%generator%" ../'
+  - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" cmake -DHB_HAVE_UNISCRIBE=ON -DHB_HAVE_DIRECTWRITE=ON -DHB_HAVE_GLIB=ON -DHB_HAVE_FREETYPE=ON -DHB_BUILD_UTILS=ON -G "%generator%" -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake ../'
+
+  - 'if "%compiler%"=="msvc" msbuild harfbuzz.sln /p:Configuration=%configuration% /p:Platform=%platform%'
+  - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" ctest --output-on-failure -C %configuration%'
+
+  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S 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 "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 --build=%MINGW_CHOST% --host=%MINGW_CHOST% --prefix=%MINGW_PREFIX%; make; make check || .ci/fail.sh"'
+
+cache:
+  - c:\tools\vcpkg\installed\
+
+notifications:
+  - provider: Email
+    to:
+      - harfbuzz-bots-chatter@googlegroups.com
+    on_build_success: false
+    on_build_failure: true
+    on_build_status_changed: true
 
 # disable automatic tests
 test: off
diff --git a/autogen.sh b/autogen.sh
index ff1b0c0..fd5c198 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -7,11 +7,11 @@
 olddir=`pwd`
 cd $srcdir
 
-echo -n "checking for ragel... "
-which ragel || {
-	echo "You need to install ragel... See http://www.complang.org/ragel/"
-	exit 1
-}
+#echo -n "checking for ragel... "
+#which ragel || {
+#	echo "You need to install ragel... See http://www.complang.org/ragel/"
+#	exit 1
+#}
 
 echo -n "checking for pkg-config... "
 which pkg-config || {
@@ -42,5 +42,7 @@
 autoreconf --force --install --verbose || exit $?
 
 cd $olddir
-echo "running configure $@"
-test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
+test -n "$NOCONFIGURE" || {
+	echo "running configure $@"
+	"$srcdir/configure" "$@"
+}
diff --git a/configure.ac b/configure.ac
index 31fa97d..eee2568 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.4.2],
-        [https://github.com/behdad/harfbuzz/issues/new],
+        [1.7.5],
+        [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
 
@@ -9,8 +9,7 @@
 AC_CONFIG_SRCDIR([src/harfbuzz.pc.in])
 AC_CONFIG_HEADERS([config.h])
 
-AM_INIT_AUTOMAKE([1.11.1 gnits tar-ustar dist-bzip2 no-dist-gzip -Wall no-define color-tests -Wno-portability])
-AM_CONDITIONAL(AUTOMAKE_OLDER_THAN_1_13, test $am__api_version = 1.11 -o $am__api_version = 1.12)
+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])
 
 # Initialize libtool
@@ -21,8 +20,10 @@
 # Check for programs
 AC_USE_SYSTEM_EXTENSIONS
 AC_PROG_CC
+AC_PROG_CC_C99
 AM_PROG_CC_C_O
 AC_PROG_CXX
+dnl AX_CXX_COMPILE_STDCXX(11, noext, optional)
 AC_SYS_LARGEFILE
 PKG_PROG_PKG_CONFIG([0.20])
 AM_MISSING_PROG([RAGEL], [ragel])
@@ -57,6 +58,13 @@
 HB_LIBTOOL_VERSION_INFO=hb_libtool_current:hb_libtool_revision:hb_libtool_age
 AC_SUBST(HB_LIBTOOL_VERSION_INFO)
 
+AC_ARG_WITH([libstdc++],
+	[AS_HELP_STRING([--with-libstdc++=@<:@yes/no@:>@],
+			[Allow linking with libstdc++ @<:@default=no@:>@])],
+	[with_libstdcxx=$withval],
+	[with_libstdcxx=no])
+AM_CONDITIONAL(WITH_LIBSTDCXX, [test "x$with_libstdcxx" = "xyes"])
+
 # Documentation
 have_gtk_doc=false
 m4_ifdef([GTK_DOC_CHECK], [
@@ -68,9 +76,9 @@
 	AM_CONDITIONAL([ENABLE_GTK_DOC], false)
 ])
 
-# Functions and headers
-AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty)
-AC_CHECK_HEADERS(unistd.h sys/mman.h)
+# Functions, and headers
+AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l setlinebuf)
+AC_CHECK_HEADERS(unistd.h sys/mman.h xlocale.h)
 
 # Compiler flags
 AC_CANONICAL_HOST
@@ -80,9 +88,6 @@
 	# Make symbols link locally
 	LDFLAGS="$LDFLAGS -Bsymbolic-functions"
 
-	# Make sure we don't link to libstdc++
-	CXXFLAGS="$CXXFLAGS -fno-rtti -fno-exceptions"
-
 	# Assorted warnings
 	CXXFLAGS="$CXXFLAGS -Wcast-align"
 
@@ -164,7 +169,7 @@
 
 AC_ARG_WITH(gobject,
 	[AS_HELP_STRING([--with-gobject=@<:@yes/no/auto@:>@],
-			[Use gobject @<:@default=auto@:>@])],,
+			[Use gobject @<:@default=no@:>@])],,
 	[with_gobject=no])
 have_gobject=false
 if test "x$with_gobject" = "xyes" -o "x$with_gobject" = "xauto"; then
@@ -179,6 +184,7 @@
 	AC_SUBST(GLIB_MKENUMS)
 fi
 AM_CONDITIONAL(HAVE_GOBJECT, $have_gobject)
+AC_SUBST(have_gobject)
 
 dnl ===========================================================================
 
@@ -352,7 +358,7 @@
 	AC_DEFINE(HAVE_FREETYPE, 1, [Have FreeType 2 library])
 	save_libs=$LIBS
 	LIBS="$LIBS $FREETYPE_LIBS"
-	AC_CHECK_FUNCS(FT_Get_Var_Blend_Coordinates)
+	AC_CHECK_FUNCS(FT_Get_Var_Blend_Coordinates FT_Set_Var_Blend_Coordinates FT_Done_MM_Var)
 	LIBS=$save_libs
 fi
 AM_CONDITIONAL(HAVE_FREETYPE, $have_freetype)
@@ -427,7 +433,7 @@
 
 		if $have_coretext; then
 			CORETEXT_CFLAGS=
-			CORETEXT_LIBS="-framework CoreText -framework CoreGraphics"
+			CORETEXT_LIBS="-framework CoreText -framework CoreGraphics -framework CoreFoundation"
 			AC_SUBST(CORETEXT_CFLAGS)
 			AC_SUBST(CORETEXT_LIBS)
 		fi
@@ -486,16 +492,20 @@
 Makefile
 src/Makefile
 src/hb-version.h
+src/harfbuzz-config.cmake
 src/hb-ucdn/Makefile
 util/Makefile
 test/Makefile
 test/api/Makefile
 test/fuzzing/Makefile
 test/shaping/Makefile
+test/shaping/data/Makefile
+test/shaping/data/in-house/Makefile
+test/shaping/data/text-rendering-tests/Makefile
+test/subset/Makefile
+test/subset/data/Makefile
 docs/Makefile
 docs/version.xml
-win32/Makefile
-win32/config.h.win32
 ])
 
 AC_OUTPUT
@@ -509,14 +519,14 @@
 	Glib:			${have_glib}
 	ICU:			${have_icu}
 
-Font callbacks (the more the better):
+Font callbacks (the more the merrier):
 	FreeType:		${have_freetype}
 
 Tools used for command-line utilities:
 	Cairo:			${have_cairo}
 	Fontconfig:		${have_fontconfig}
 
-Additional shapers (the more the better):
+Additional shapers (the more the merrier):
 	Graphite2:		${have_graphite2}
 
 Platform shapers (not normally needed):
@@ -525,7 +535,7 @@
 	DirectWrite:		${have_directwrite}
 
 Other features:
-	Documentation:		${have_gtk_doc}
+	Documentation:		${enable_gtk_doc}
 	GObject bindings:	${have_gobject}
 	Introspection:		${have_introspection}
 ])
diff --git a/docs/HarfBuzz.png b/docs/HarfBuzz.png
index d58d9fc..771d955 100644
--- a/docs/HarfBuzz.png
+++ b/docs/HarfBuzz.png
Binary files differ
diff --git a/docs/HarfBuzz.svg b/docs/HarfBuzz.svg
new file mode 100644
index 0000000..4e2df25
--- /dev/null
+++ b/docs/HarfBuzz.svg
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="svg2"
+   width="682.66669"
+   height="682.66669"
+   viewBox="0 0 682.66669 682.66669"
+   sodipodi:docname="harfbuzz2.svg"
+   inkscape:version="0.92.2 5c3e80d, 2017-08-06"
+   inkscape:export-filename="harfbuzz2.png"
+   inkscape:export-xdpi="72"
+   inkscape:export-ydpi="72">
+  <metadata
+     id="metadata8">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs6">
+    <g
+       id="g50">
+      <symbol
+         id="glyph0-0"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path26"
+           d="M 32,0 V -192 H 224 V 0 Z M 48,-16 H 208 V -176 H 48 Z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-1"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path29"
+           d="m 52.5,-64.875 c -0.08594,2.25 -0.9375,6.335938 -2.5625,12.25 -1.625,5.917969 -3.75,12.375 -6.375,19.375 -2.625,7 -4.9375,12.292969 -6.9375,15.875 -2,3.585938 -4.1875,6.3125 -6.5625,8.1875 -2.375,1.875 -6.210938,3.585938 -11.5,5.125 -5.292969,1.542969 -9.542969,2.585938 -12.75,3.125 -3.210938,0.542969 -5.230469,0.8125 -6.0625,0.8125 -0.832031,-0.082031 -1.332031,-0.414062 -1.5,-1 -0.164062,-0.582031 0.085938,-1.332031 0.75,-2.25 0.9179688,-1.414062 3.226562,-3.269531 6.9375,-5.5625 3.707031,-2.289062 8.019531,-5.164062 12.9375,-8.625 4.914062,-3.457031 8.414062,-6.476562 10.5,-9.0625 2.082031,-2.582031 4.4375,-6.457031 7.0625,-11.625 2.625,-5.164062 5,-10.375 7.125,-15.625 2.125,-5.25 3.644531,-8.832031 4.5625,-10.75 0.914062,-1.914062 1.914062,-2.832031 3,-2.75 0.914062,0.167969 1.375,1 1.375,2.5 z M 41.75,-117.75 c 0,-1.83203 1.5625,-5.8125 4.6875,-11.9375 3.125,-6.125 5.144531,-9.1875 6.0625,-9.1875 1.832031,-0.75 4.5,0.64844 8,4.1875 3.5,3.54297 5.375,6.3125 5.625,8.3125 0,2.66797 -1.460938,6.5 -4.375,11.5 -2.917969,5 -5.042969,7.5 -6.375,7.5 -0.5,-0.25 -2.230469,-1.35156 -5.1875,-3.3125 -2.960938,-1.95703 -5.148438,-3.4375 -6.5625,-4.4375 -1.25,-0.83203 -1.875,-1.70703 -1.875,-2.625 z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-2"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path32"
+           d="m 19.75,-47.125 c -0.167969,3.167969 -0.5,6.023438 -1,8.5625 -0.5,2.542969 -1.167969,4.710938 -2,6.5 -0.835938,1.792969 -1.898438,3.125 -3.1875,4 -1.292969,0.875 -2.855469,1.1875 -4.6875,0.9375 0.082031,-6.5 0.769531,-15.5625 2.0625,-27.1875 1.289062,-11.625 3.226562,-24.476562 5.8125,-38.5625 1.332031,-4.082031 4.039062,-11.28906 8.125,-21.625 0.414062,-1 0.851562,-1.41406 1.3125,-1.25 0.457031,0.16797 0.6875,0.58594 0.6875,1.25 -0.25,5.08594 -1.292969,14.792969 -3.125,29.125 -1.835938,14.335938 -2.960938,24.167969 -3.375,29.5 -0.417969,5.335938 -0.625,8.25 -0.625,8.75 z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-3"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path35"
+           d="m -10.875,-27.25 c 1.667969,-7.164062 3.5625,-12.457031 5.6875,-15.875 2.125,-3.414062 3.855469,-5.289062 5.1875,-5.625 v 5.5 c 0,7 1.875,11.875 5.625,14.625 2.25,1.5 4.5,2 6.75,1.5 2.25,-0.5 3.976562,-1.664062 5.1875,-3.5 1.207031,-1.832031 2.226562,-3.976562 3.0625,-6.4375 0.832031,-2.457031 1.625,-3.6875 2.375,-3.6875 0.914062,0 1.289062,0.875 1.125,2.625 -0.08594,3 -1.023438,7.875 -2.8125,14.625 C 19.519531,-16.75 17.082031,-11.207031 14,-6.875 10.914062,-2.539062 7.164062,-0.25 2.75,0 c -4.5,-0.164062 -8,-2.707031 -10.5,-7.625 -2.25,-4.832031 -3.289062,-11.082031 -3.125,-18.75 z m 6.625,111.5 c 0,-1.835938 1.5625,-5.8125 4.6875,-11.9375 3.125,-6.125 5.144531,-9.1875 6.0625,-9.1875 1.832031,-0.75 4.5,0.644531 8,4.1875 3.5,3.539062 5.375,6.3125 5.625,8.3125 0,2.664062 -1.460938,6.5 -4.375,11.5 -2.917969,5 -5.042969,7.5 -6.375,7.5 C 8.875,94.375 7.144531,93.269531 4.1875,91.3125 1.226562,89.351562 -0.957031,87.875 -2.375,86.875 -3.625,86.039062 -4.25,85.164062 -4.25,84.25 Z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-4"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path38"
+           d=""
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-5"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path41"
+           d="m 118.5,-33 c 15.33203,-3.914062 27.375,-8.289062 36.125,-13.125 7.5,-4.082031 11.25,-7.5 11.25,-10.25 0,-0.832031 -0.5625,-1.894531 -1.6875,-3.1875 -1.125,-1.289062 -1.9375,-1.9375 -2.4375,-1.9375 -0.83594,0 -2.02344,0.148438 -3.5625,0.4375 -1.54297,0.292969 -2.64844,0.4375 -3.3125,0.4375 -2.91797,0 -4.98047,-0.769531 -6.1875,-2.3125 -1.21094,-1.539062 -1.8125,-3.601562 -1.8125,-6.1875 0,-1.5 2.16406,-6.539062 6.5,-15.125 1.66406,-3.414062 3.0625,-5.8125 4.1875,-7.1875 1.125,-1.375 2.76953,-2.0625 4.9375,-2.0625 3.58203,0 6.91406,1.9375 10,5.8125 3.08203,3.875 4.625,8.855469 4.625,14.9375 0,6 -0.8125,11.4375 -2.4375,16.3125 -1.625,4.875 -4.14844,9.605469 -7.5625,14.1875 -3.41797,4.585938 -7.83594,9.042969 -13.25,13.375 -8.08594,6.085938 -17.625,11.230469 -28.625,15.4375 -11,4.210938 -23.83594,7.5 -38.5,9.875 C 72.082031,-1.1875 58.082031,0 44.75,0 37.082031,0 30.082031,-0.375 23.75,-1.125 17.414062,-1.875 12.582031,-3.289062 9.25,-5.375 3.082031,-8.789062 0.25,-14.5 0.75,-22.5 c 0.332031,-4.082031 0.832031,-8.4375 1.5,-13.0625 0.664062,-4.625 1.351562,-8.3125 2.0625,-11.0625 0.707031,-2.75 1.5625,-4.125 2.5625,-4.125 0.832031,0 1.332031,1.75 1.5,5.25 0.164062,3.5 0.6875,6.0625 1.5625,7.6875 0.875,1.625 2.207031,3 4,4.125 1.789062,1.125 4.6875,2.210938 8.6875,3.25 4,1.042969 8.6875,1.855469 14.0625,2.4375 5.375,0.585938 12.519531,0.875 21.4375,0.875 12.414062,0 23.019531,-0.4375 31.8125,-1.3125 C 98.726562,-29.3125 108.25,-30.832031 118.5,-33 Z m -38.75,-86.75 c 0,-1.83203 1.5625,-5.8125 4.6875,-11.9375 3.125,-6.125 5.144531,-9.1875 6.0625,-9.1875 1.832031,-0.75 4.5,0.64844 8,4.1875 3.5,3.54297 5.375,6.3125 5.625,8.3125 0,2.66797 -1.46094,6.5 -4.375,11.5 -2.917969,5 -5.042969,7.5 -6.375,7.5 -0.5,-0.25 -2.230469,-1.35156 -5.1875,-3.3125 -2.960938,-1.95703 -5.148438,-3.4375 -6.5625,-4.4375 -1.25,-0.83203 -1.875,-1.70703 -1.875,-2.625 z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-6"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path44"
+           d="m 78,-56.875 c -1.335938,2.25 -2.25,3.875 -2.75,4.875 -1.417969,2.335938 -2.875,4.210938 -4.375,5.625 -1.75,1.75 -4.042969,3.292969 -6.875,4.625 -6.085938,2.917969 -10.460938,5.75 -13.125,8.5 -1.75,1.75 -3.792969,4.960938 -6.125,9.625 -4.25,8.585938 -8.417969,14.292969 -12.5,17.125 -4.085938,2.917969 -11.417969,5 -22,6.25 C 9.414062,-0.0820312 8.414062,0 7.25,0 H 4.375 c -1.5,0 -2.25,-0.164062 -2.25,-0.5 0,-1.914062 2.539062,-4.5 7.625,-7.75 4,-2.414062 7,-4.207031 9,-5.375 6,-3.414062 10.414062,-6.125 13.25,-8.125 4,-2.914062 6.625,-5.75 7.875,-8.5 4.164062,-8.5 7.625,-14.414062 10.375,-17.75 3.082031,-3.664062 6.625,-6.5 10.625,-8.5 C 67.289062,-60.082031 71,-62.207031 72,-62.875 l 10,2.375 z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-7"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path47"
+           d="m 13,-17.625 c -1,1.75 -2.0625,3.480469 -3.1875,5.1875 C 8.6875,-10.726562 7.539062,-9.082031 6.375,-7.5 5.207031,-5.914062 4.0625,-4.476562 2.9375,-3.1875 1.8125,-1.894531 0.832031,-0.832031 0,0 c -0.5,-2.582031 -0.832031,-5.125 -1,-7.625 -0.164062,-2.5 0.167969,-5.039062 1,-7.625 1.25,-0.75 2.207031,-1.414062 2.875,-2 1.664062,-1.5 5.375,-6.582031 11.125,-15.25 -0.667969,-0.664062 -2.585938,-1.601562 -5.75,-2.8125 -3.167969,-1.207031 -6.167969,-1.894531 -9,-2.0625 -2.582031,-0.164062 -4.726562,0.3125 -6.4375,1.4375 -1.707031,1.125 -3.25,2.710938 -4.625,4.75 -1.375,2.042969 -2.5625,2.980469 -3.5625,2.8125 -0.332031,-0.164062 -0.375,-0.851562 -0.125,-2.0625 0.25,-1.207031 0.625,-2.394531 1.125,-3.5625 2.085938,-4.832031 4.480469,-8.4375 7.1875,-10.8125 2.710938,-2.375 5.9375,-3.5625 9.6875,-3.5625 2.75,0 6.164062,0.667969 10.25,2 2.582031,0.917969 4.789062,1.375 6.625,1.375 3.832031,0 7.625,-1.375 11.375,-4.125 0.832031,0 1.125,0.542969 0.875,1.625 -0.25,1.085938 -0.605469,2.355469 -1.0625,3.8125 -0.460938,1.460938 -0.773438,2.4375 -0.9375,2.9375 -0.835938,2.585938 -1.5,4.085938 -2,4.5 -0.5,0.417969 -1.960938,1.5 -4.375,3.25 -1.335938,1 -2.480469,2.148438 -3.4375,3.4375 -0.960938,1.292969 -1.9375,2.835938 -2.9375,4.625 -1,1.792969 -2.292969,4.230469 -3.875,7.3125 z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+    </g>
+    <g
+       id="g184">
+      <symbol
+         id="glyph0-0-3"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path163"
+           d=""
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-1-6"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path166"
+           d="m 79.625,-103.25 c -3.585938,22 -6.8125,38.417969 -9.6875,49.25 -2.875,10.835938 -5.480469,19.105469 -7.8125,24.8125 -2.335938,5.710938 -4.898438,10.0625 -7.6875,13.0625 -2.792969,3 -7.917969,5.855469 -15.375,8.5625 -7.460938,2.710938 -13.6875,4.605469 -18.6875,5.6875 -5,1.085938 -8.125,1.667969 -9.375,1.75 C 9.75,-0.207031 8.957031,-0.4375 8.625,-0.8125 8.289062,-1.1875 8.582031,-2.082031 9.5,-3.5 c 1.582031,-2.25 4.894531,-5.332031 9.9375,-9.25 5.039062,-3.914062 11,-8.789062 17.875,-14.625 6.875,-5.832031 12.019531,-10.976562 15.4375,-15.4375 3.414062,-4.457031 6.4375,-10.539062 9.0625,-18.25 C 64.4375,-68.769531 67,-76.832031 69.5,-85.25 c 2.5,-8.414062 4.3125,-14.125 5.4375,-17.125 1.125,-3 2.269531,-4.45703 3.4375,-4.375 1.332031,0.25 1.75,1.41797 1.25,3.5 z m 8.875,-68.625 c 0.664062,2.41797 0.644531,4.91797 -0.0625,7.5 -0.710938,2.58594 -2.023438,5.29297 -3.9375,8.125 -1.167969,1.91797 -2.398438,3.64844 -3.6875,5.1875 -1.292969,1.54297 -2.648438,3.02344 -4.0625,4.4375 -0.417969,0.5 -1.085938,1.125 -2,1.875 -0.917969,0.75 -1.875,1.46094 -2.875,2.125 -1,0.66797 -1.960938,1.14844 -2.875,1.4375 -0.917969,0.29297 -1.585938,0.1875 -2,-0.3125 -3,-2.41406 -5.960938,-4.875 -8.875,-7.375 -2.917969,-2.5 -6.125,-4.83203 -9.625,-7 -0.585938,-0.41406 -0.855469,-0.8125 -0.8125,-1.1875 0.03906,-0.375 0.269531,-0.9375 0.6875,-1.6875 l 20,-30.5 c 0.582031,-0.83203 1.164062,-1.22656 1.75,-1.1875 0.582031,0.043 1.207031,0.27344 1.875,0.6875 1.75,1 3.5,2.125 5.25,3.375 1.75,1.25 3.375,2.60547 4.875,4.0625 1.5,1.46094 2.8125,3.04297 3.9375,4.75 1.125,1.71094 1.9375,3.60547 2.4375,5.6875 z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-2-7"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path169"
+           d="m 29.625,-70.625 c -0.667969,8.335938 -2.542969,15.417969 -5.625,21.25 -3.085938,5.835938 -6.585938,8.75 -10.5,8.75 0,-3.164062 1.019531,-13 3.0625,-29.5 2.039062,-16.5 3.582031,-28.3125 4.625,-35.4375 1.039062,-7.125 2.226562,-14.6875 3.5625,-22.6875 1.332031,-8 2.625,-14.45703 3.875,-19.375 1.414062,-4.08203 2.9375,-8.20703 4.5625,-12.375 1.625,-4.16406 3.3125,-8.28906 5.0625,-12.375 1.164062,-1.83203 1.914062,-1.53906 2.25,0.875 -2.085938,13.83594 -4,28.1875 -5.75,43.0625 -1.75,14.875 -2.9375,25.52344 -3.5625,31.9375 -0.625,6.417969 -0.9375,10.167969 -0.9375,11.25 -0.417969,6.835938 -0.625,11.710938 -0.625,14.625 z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-3-5"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path172"
+           d="m -16.125,-40.625 c 1.25,-5.332031 2.625,-10 4.125,-14 1.5,-4 2.960938,-7.3125 4.375,-9.9375 1.417969,-2.625 2.792969,-4.644531 4.125,-6.0625 1.335938,-1.414062 2.5,-2.25 3.5,-2.5 v 8.25 c 0,10.5 2.789062,17.835938 8.375,22 3.414062,2.25 6.8125,3 10.1875,2.25 3.375,-0.75 5.957031,-2.5 7.75,-5.25 1.789062,-2.75 3.3125,-5.976562 4.5625,-9.6875 1.25,-3.707031 2.457031,-5.5625 3.625,-5.5625 1.332031,0 1.875,1.335938 1.625,4 -0.08594,4.5 -1.480469,11.8125 -4.1875,21.9375 -2.710938,10.125 -6.375,18.4375 -11,24.9375 -4.625,6.5 -10.230469,9.917969 -16.8125,10.25 -6.75,-0.25 -12,-4.039062 -15.75,-11.375 -3.414062,-7.25 -4.914062,-16.625 -4.5,-28.125 z M 28.5,82.125 c 0.664062,2.414062 0.644531,4.914062 -0.0625,7.5 -0.710938,2.582031 -2.023438,5.289062 -3.9375,8.125 -1.167969,1.914062 -2.398438,3.64453 -3.6875,5.1875 -1.292969,1.53906 -2.648438,3.01953 -4.0625,4.4375 -0.417969,0.5 -1.085938,1.125 -2,1.875 -0.917969,0.75 -1.875,1.45703 -2.875,2.125 -1,0.66406 -1.960938,1.14453 -2.875,1.4375 C 8.082031,113.10156 7.414062,113 7,112.5 c -3,-2.41797 -5.960938,-4.875 -8.875,-7.375 -2.914062,-2.5 -6.125,-4.83594 -9.625,-7 -0.582031,-0.417969 -0.851562,-0.8125 -0.8125,-1.1875 0.04297,-0.375 0.273438,-0.9375 0.6875,-1.6875 l 20,-30.5 c 0.582031,-0.835938 1.164062,-1.230469 1.75,-1.1875 0.582031,0.03906 1.207031,0.269531 1.875,0.6875 1.75,1 3.5,2.125 5.25,3.375 1.75,1.25 3.375,2.601562 4.875,4.0625 1.5,1.457031 2.8125,3.039062 3.9375,4.75 1.125,1.707031 1.9375,3.601562 2.4375,5.6875 z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-4-3"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path175"
+           d=""
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-5-5"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path178"
+           d="M 72.125,0.125 C 58.039062,0.0390625 47.164062,-0.582031 39.5,-1.75 26.164062,-3.832031 16.664062,-7.832031 11,-13.75 5.914062,-18.332031 3.375,-24.582031 3.375,-32.5 c 0,-3.082031 0.289062,-7.5 0.875,-13.25 0.582031,-5.75 1.375,-11.5 2.375,-17.25 0.414062,-2.414062 0.789062,-4.582031 1.125,-6.5 0.25,-1.332031 0.582031,-2.414062 1,-3.25 0.414062,-0.832031 0.875,-1.3125 1.375,-1.4375 0.5,-0.125 0.976562,0.210938 1.4375,1 0.457031,0.792969 0.894531,2.148438 1.3125,4.0625 0.332031,2.085938 0.789062,4.25 1.375,6.5 1.332031,5.417969 4.957031,9.460938 10.875,12.125 15,6.417969 37.914062,9.75 68.75,10 12.75,-0.164062 24.58203,-0.625 35.5,-1.375 11.16406,-0.832031 23.75,-2.789062 37.75,-5.875 14,-3.082031 25.14453,-6.457031 33.4375,-10.125 8.28906,-3.664062 15.35156,-7.625 21.1875,-11.875 4.58203,-3.332031 6.875,-6.539062 6.875,-9.625 0,-1.25 -1.1875,-2.726562 -3.5625,-4.4375 -2.375,-1.707031 -3.9375,-2.5625 -4.6875,-2.5625 -1.41797,0.667969 -3.08594,1.1875 -5,1.5625 -1.91797,0.375 -3.66797,0.480469 -5.25,0.3125 -4.41797,-0.5 -7.71094,-1.875 -9.875,-4.125 -2.16797,-2.25 -3.25,-5.289062 -3.25,-9.125 0,-2.41406 3.20703,-10 9.625,-22.75 2.58203,-5.16406 4.85156,-8.76953 6.8125,-10.8125 1.95703,-2.03906 4.64453,-3.0625 8.0625,-3.0625 5.41406,0 10.4375,2.91797 15.0625,8.75 4.625,5.83594 6.9375,13.29297 6.9375,22.375 0,9 -1.21094,16.792969 -3.625,23.375 -3,9 -7.08594,17.335938 -12.25,25 -6.5,9.25 -13.625,16.5625 -21.375,21.9375 -7.75,5.375 -15.58594,9.648438 -23.5,12.8125 -7.08594,3.5 -16.23047,6.773438 -27.4375,9.8125 -11.21094,3.042969 -24.8125,5.523438 -40.8125,7.4375 -15.835938,1.917969 -29.960938,2.9140625 -42.375,3 z m 82.375,-172 c 0.66406,2.41797 0.64453,4.91797 -0.0625,7.5 -0.71094,2.58594 -2.02344,5.29297 -3.9375,8.125 -1.16797,1.91797 -2.39844,3.64844 -3.6875,5.1875 -1.29297,1.54297 -2.64844,3.02344 -4.0625,4.4375 -0.41797,0.5 -1.08594,1.125 -2,1.875 -0.91797,0.75 -1.875,1.46094 -2.875,2.125 -1,0.66797 -1.96094,1.14844 -2.875,1.4375 -0.91797,0.29297 -1.58594,0.1875 -2,-0.3125 -3,-2.41406 -5.96094,-4.875 -8.875,-7.375 -2.91797,-2.5 -6.125,-4.83203 -9.625,-7 -0.58594,-0.41406 -0.85547,-0.8125 -0.8125,-1.1875 0.0391,-0.375 0.26953,-0.9375 0.6875,-1.6875 l 20,-30.5 c 0.58203,-0.83203 1.16406,-1.22656 1.75,-1.1875 0.58203,0.043 1.20703,0.27344 1.875,0.6875 1.75,1 3.5,2.125 5.25,3.375 1.75,1.25 3.375,2.60547 4.875,4.0625 1.5,1.46094 2.8125,3.04297 3.9375,4.75 1.125,1.71094 1.9375,3.60547 2.4375,5.6875 z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+      <symbol
+         id="glyph0-6-6"
+         overflow="visible"
+         style="overflow:visible">
+        <path
+           id="path181"
+           d="m 115,-102.5 c -1.83594,3.25 -3.75,6.523438 -5.75,9.8125 -2,3.292969 -4.23047,6.398438 -6.6875,9.3125 -2.46094,2.917969 -5.167969,5.5625 -8.125,7.9375 -2.960938,2.375 -6.3125,4.273438 -10.0625,5.6875 -3.085938,1.25 -5.710938,2.648438 -7.875,4.1875 -2.167969,1.542969 -3.960938,3.1875 -5.375,4.9375 -2.167969,2.335938 -4.042969,5.5625 -5.625,9.6875 -1.585938,4.125 -3.230469,8.480469 -4.9375,13.0625 -1.710938,4.585938 -3.585938,9.0625 -5.625,13.4375 -2.042969,4.375 -4.605469,7.980469 -7.6875,10.8125 C 43,-9.125 36.707031,-6.082031 28.375,-4.5 L 5.5,-0.625 C 4.832031,-0.539062 4.019531,-0.476562 3.0625,-0.4375 2.101562,-0.394531 1.414062,-0.414062 1,-0.5 -0.914062,-1.082031 -1.5,-2.351562 -0.75,-4.3125 0,-6.269531 1.957031,-8.25 5.125,-10.25 c 6.414062,-4.25 11.539062,-7.6875 15.375,-10.3125 3.832031,-2.625 6.957031,-4.769531 9.375,-6.4375 2.414062,-1.664062 4.351562,-3.039062 5.8125,-4.125 1.457031,-1.082031 3.019531,-2.289062 4.6875,-3.625 6.75,-5.082031 11.414062,-10.414062 14,-16 1.5,-2.914062 2.875,-5.75 4.125,-8.5 1.25,-2.75 2.5625,-5.457031 3.9375,-8.125 1.375,-2.664062 2.851562,-5.3125 4.4375,-7.9375 1.582031,-2.625 3.414062,-5.269531 5.5,-7.9375 2.164062,-2.664062 5.082031,-5.269531 8.75,-7.8125 3.664062,-2.539062 8.082031,-5.0625 13.25,-7.5625 0.582031,-0.25 1.539062,-0.8125 2.875,-1.6875 1.332031,-0.875 2.95703,-2.26953 4.875,-4.1875 1.91406,-1.91406 4.10156,-4.51953 6.5625,-7.8125 2.45703,-3.28906 5.0625,-7.47656 7.8125,-12.5625 -1,-1 -3.875,-2.375 -8.625,-4.125 -4.75,-1.83203 -9.25,-2.875 -13.5,-3.125 -3.917969,-0.25 -7.148438,0.46094 -9.6875,2.125 -2.542969,1.66797 -4.855469,4.04297 -6.9375,7.125 -2.085938,3.08594 -3.875,4.5 -5.375,4.25 -0.5,-0.25 -0.585938,-1.28906 -0.25,-3.125 0.414062,-1.83203 1,-3.625 1.75,-5.375 3.164062,-7.25 6.75,-12.625 10.75,-16.125 4.082031,-3.58203 8.957031,-5.375 14.625,-5.375 4.08203,0 9.20703,1 15.375,3 3.83203,1.33594 7.125,2 9.875,2 5.75,0 11.45703,-2.03906 17.125,-6.125 1.25,0 1.6875,0.8125 1.3125,2.4375 -0.375,1.625 -0.9375,3.52344 -1.6875,5.6875 -0.66797,2.16797 -1.125,3.625 -1.375,4.375 -1.25,3.91797 -2.25,6.21094 -3,6.875 -0.75,0.58594 -2.91797,2.16797 -6.5,4.75 -2,1.5 -3.73047,3.23047 -5.1875,5.1875 -1.46094,1.96094 -2.9375,4.27344 -4.4375,6.9375 -1.5,2.66797 -3.41797,6.33594 -5.75,11 z m 0,0"
+           style="stroke:none"
+           inkscape:connector-curvature="0" />
+      </symbol>
+    </g>
+  </defs>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="2560"
+     inkscape:window-height="1471"
+     id="namedview4"
+     showgrid="false"
+     inkscape:zoom="0.59454973"
+     inkscape:cx="-165.7731"
+     inkscape:cy="361.75575"
+     inkscape:window-x="0"
+     inkscape:window-y="55"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2" />
+  <path
+     id="path871"
+     d="m 205.59223,196.11402 c -5.97657,36.66667 -11.35417,64.02995 -16.14583,82.08333 -4.79167,18.0599 -9.13412,31.84245 -13.02084,41.35417 -3.89323,9.51823 -8.16406,16.77083 -12.8125,21.77083 -4.65495,5 -13.19661,9.75912 -25.625,14.27083 -12.43489,4.51823 -22.8125,7.67579 -31.14583,9.47917 -8.333331,1.8099 -13.541664,2.77995 -15.624997,2.91667 -2.083334,-0.13672 -3.404949,-0.52084 -3.958334,-1.14584 -0.559896,-0.625 -0.07161,-2.11588 1.458334,-4.47916 2.636718,-3.75 8.157551,-8.88672 16.562497,-15.41667 8.39844,-6.52344 18.33333,-14.64844 29.79167,-24.375 11.45833,-9.72005 20.03255,-18.29427 25.72916,-25.72916 5.69011,-7.42839 10.72917,-17.56511 15.10417,-30.41667 4.375,-12.84505 8.64583,-26.28255 12.8125,-40.3125 4.16667,-14.02343 7.1875,-23.54166 9.0625,-28.54166 1.875,-5 3.78255,-7.42839 5.72916,-7.29167 2.22006,0.41667 2.91667,2.36328 2.08334,5.83333 z M 220.38389,81.739028 c 1.10677,4.02995 1.07422,8.196616 -0.10416,12.5 -1.1849,4.309899 -3.3724,8.821612 -6.5625,13.541662 -1.94662,3.19662 -3.9974,6.08074 -6.14584,8.64584 -2.15494,2.57161 -4.41406,5.03906 -6.77083,7.39583 -0.69661,0.83333 -1.8099,1.875 -3.33333,3.125 -1.52995,1.25 -3.125,2.4349 -4.79167,3.54167 -1.66666,1.11328 -3.26823,1.91406 -4.79166,2.39583 -1.52995,0.48828 -2.64323,0.3125 -3.33334,-0.52083 -5,-4.02344 -9.93489,-8.125 -14.79166,-12.29167 -4.86329,-4.16667 -10.20834,-8.05338 -16.04167,-11.66667 -0.97656,-0.6901 -1.42578,-1.35416 -1.35417,-1.97916 0.0651,-0.625 0.44922,-1.5625 1.14584,-2.8125 l 33.33333,-50.833334 c 0.97005,-1.386717 1.9401,-2.044267 2.91667,-1.979167 0.97005,0.07167 2.01171,0.455734 3.125,1.145834 2.91666,1.666666 5.83333,3.541666 8.74999,5.624999 2.91667,2.083334 5.625,4.34245 8.125,6.770833 2.5,2.4349 4.6875,5.071617 6.5625,7.916667 1.875,2.851566 3.22917,6.009116 4.0625,9.479166 z m 0,0"
+     style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66666663"
+     inkscape:connector-curvature="0" />
+  <path
+     inkscape:connector-curvature="0"
+     style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66666663"
+     d="m 256.21722,250.48902 c -1.11328,13.89323 -4.23828,25.69662 -9.375,35.41667 -5.14323,9.72656 -10.97656,14.58333 -17.5,14.58333 0,-5.27344 1.69922,-21.66667 5.10417,-49.16667 3.39843,-27.49999 5.97005,-47.18749 7.70833,-59.06249 1.73177,-11.875 3.71094,-24.47917 5.9375,-37.8125 2.22005,-13.33333 4.375,-24.09505 6.45833,-32.29167 2.35677,-6.80338 4.89583,-13.67838 7.60417,-20.625 2.70833,-6.940096 5.52083,-13.815095 8.4375,-20.624995 1.9401,-3.053383 3.1901,-2.5651 3.75,1.458333 -3.47657,23.059902 -6.66667,46.979162 -9.58334,71.770832 -2.91666,24.79166 -4.89583,42.53906 -5.9375,53.22916 -1.04166,10.69662 -1.5625,16.94662 -1.5625,18.75 -0.69661,11.39323 -1.04166,19.51823 -1.04166,24.375 z m 0,0"
+     id="path875" />
+  <path
+     id="path945"
+     d="m 229.34222,300.48902 c 2.08333,-8.88672 4.375,-16.66667 6.875,-23.33333 2.5,-6.66667 4.9349,-12.1875 7.29167,-16.5625 2.36328,-4.375 4.65495,-7.74089 6.875,-10.10417 2.22656,-2.35677 4.16666,-3.75 5.83333,-4.16667 v 13.75 c 0,17.5 4.64844,29.72657 13.95833,36.66667 5.69011,3.75 11.35417,5 16.97917,3.75 5.625,-1.25 9.92838,-4.16667 12.91666,-8.75 2.98177,-4.58333 5.52084,-9.96094 7.60417,-16.14583 2.08333,-6.17839 4.09505,-9.27084 6.04167,-9.27084 2.22005,0 3.125,2.22657 2.70833,6.66667 -0.14323,7.5 -2.46745,19.6875 -6.97917,36.5625 -4.51823,16.875 -10.625,30.72916 -18.33333,41.5625 -7.70833,10.83333 -17.05078,16.52995 -28.02083,17.08333 -11.25,-0.41667 -20,-6.73177 -26.25,-18.95833 -5.6901,-12.08334 -8.1901,-27.70833 -7.5,-46.875 z"
+     style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66666663"
+     inkscape:connector-curvature="0" />
+  <path
+     id="path943"
+     d="m 286.78334,583.03989 c 1.10677,4.02343 1.07422,8.1901 -0.10416,12.5 -1.1849,4.30338 -3.3724,8.8151 -6.5625,13.54167 -1.94662,3.1901 -3.9974,6.07421 -6.14584,8.64583 -2.15495,2.5651 -4.41406,5.03255 -6.77083,7.39583 -0.69662,0.83334 -1.8099,1.875 -3.33333,3.125 -1.52995,1.25 -3.125,2.42839 -4.79167,3.54167 -1.66667,1.10677 -3.26823,1.90755 -4.79167,2.39583 -1.52994,0.48177 -2.64323,0.3125 -3.33333,-0.52083 -5,-4.02995 -9.9349,-8.125 -14.79167,-12.29167 -4.85677,-4.16666 -10.20833,-8.0599 -16.04166,-11.66666 -0.97005,-0.69662 -1.41927,-1.35417 -1.35417,-1.97917 0.0716,-0.625 0.45573,-1.5625 1.14583,-2.8125 l 33.33334,-50.83333 c 0.97005,-1.39323 1.9401,-2.05078 2.91666,-1.97917 0.97006,0.0652 2.01172,0.44922 3.125,1.14584 2.91667,1.66666 5.83334,3.54166 8.75,5.625 2.91667,2.08333 5.625,4.33593 8.125,6.77083 2.5,2.42838 4.6875,5.0651 6.5625,7.91667 1.875,2.84505 3.22917,6.00259 4.0625,9.47916 z"
+     style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66666663"
+     inkscape:connector-curvature="0" />
+  <path
+     id="path879"
+     d="M 303.71722,505.07234"
+     style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66666663"
+     inkscape:connector-curvature="0" />
+  <path
+     inkscape:connector-curvature="0"
+     id="path925"
+     d="m 168.42167,500.82557 c -23.47656,-0.14323 -41.60156,-1.17838 -54.375,-3.125 -22.226562,-3.47005 -38.059894,-10.13671 -47.499997,-20 -8.476563,-7.63671 -12.708333,-18.05338 -12.708333,-31.24999 0,-5.13672 0.48177,-12.5 1.458333,-22.08334 0.970052,-9.58333 2.291667,-19.16666 3.958333,-28.75 0.690104,-4.02343 1.315104,-7.63671 1.875,-10.83333 0.416667,-2.22005 0.970052,-4.02344 1.666667,-5.41667 0.690103,-1.38671 1.458333,-2.1875 2.291666,-2.39583 0.833334,-0.20833 1.627604,0.35156 2.395834,1.66667 0.761718,1.32161 1.490885,3.58073 2.187499,6.77083 0.553385,3.47656 1.315104,7.08333 2.291667,10.83333 2.220052,9.02995 8.261718,15.76823 18.124999,20.20834 25.000002,10.69661 63.190102,16.25 114.583332,16.66666 21.25,-0.27343 40.97005,-1.04166 59.16666,-2.29166 18.60677,-1.38672 39.58333,-4.64844 62.91667,-9.79167 23.33333,-5.13672 41.90754,-10.76172 55.72916,-16.875 13.8151,-6.10677 25.58593,-12.70833 35.3125,-19.79167 7.63671,-5.55338 11.45833,-10.89843 11.45833,-16.04166 0,-2.08333 -1.97917,-4.54427 -5.9375,-7.39583 -3.95833,-2.84506 -6.5625,-4.27084 -7.8125,-4.27084 -2.36328,1.11328 -5.14323,1.97917 -8.33333,2.60417 -3.19662,0.625 -6.11328,0.80078 -8.75,0.52083 -7.36328,-0.83333 -12.85157,-3.125 -16.45833,-6.875 -3.61329,-3.75 -5.41667,-8.8151 -5.41667,-15.20833 0,-4.02343 5.34505,-16.66667 16.04167,-37.91667 4.30338,-8.60676 8.08593,-14.61588 11.35416,-18.02083 3.26172,-3.39843 7.74089,-5.10416 13.4375,-5.10416 9.02343,0 17.39583,4.86328 25.10417,14.58333 7.70833,9.72656 11.5625,22.15495 11.5625,37.29166 0,15 -2.01824,27.98828 -6.04167,38.95834 -5,14.99999 -11.8099,28.89322 -20.41667,41.66666 -10.83333,15.41667 -22.70833,27.60417 -35.62499,36.5625 -12.91667,8.95833 -25.97657,16.08073 -39.16667,21.35416 -11.8099,5.83334 -27.05078,11.28907 -45.72916,16.35417 -18.6849,5.07162 -41.35417,9.20573 -68.02083,12.39583 -26.39323,3.19662 -49.9349,4.85677 -70.625,5 z"
+     style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66666663" />
+  <path
+     inkscape:connector-curvature="0"
+     id="path923"
+     d="m 430.22191,133.16177 c 1.10676,4.02995 1.07421,8.19662 -0.10417,12.5 -1.1849,4.3099 -3.3724,8.82162 -6.5625,13.54167 -1.94661,3.19661 -3.9974,6.08073 -6.14583,8.64583 -2.15495,2.57162 -4.41407,5.03907 -6.77083,7.39583 -0.69662,0.83334 -1.8099,1.875 -3.33334,3.125 -1.52995,1.25 -3.125,2.4349 -4.79166,3.54167 -1.66667,1.11328 -3.26824,1.91407 -4.79167,2.39583 -1.52995,0.48829 -2.64323,0.3125 -3.33333,-0.52083 -5,-4.02343 -9.9349,-8.125 -14.79167,-12.29167 -4.86328,-4.16666 -10.20833,-8.05338 -16.04167,-11.66666 -0.97656,-0.6901 -1.42578,-1.35417 -1.35416,-1.97917 0.0652,-0.625 0.44921,-1.5625 1.14583,-2.8125 l 33.33333,-50.83333 c 0.97005,-1.38672 1.9401,-2.04427 2.91667,-1.97917 0.97005,0.0717 2.01172,0.45574 3.125,1.14584 2.91667,1.66666 5.83333,3.54166 8.75,5.625 2.91667,2.08333 5.625,4.34245 8.125,6.77083 2.5,2.4349 4.6875,5.07162 6.5625,7.91667 1.875,2.85156 3.22916,6.00911 4.0625,9.47916 z"
+     style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66666663" />
+  <path
+     inkscape:connector-curvature="0"
+     id="path883"
+     d="M 305.71333,214.15892"
+     style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66666663" />
+  <path
+     id="path887"
+     d="m 599.12998,319.78391 c -3.0599,5.41667 -6.25,10.8724 -9.58333,16.35417 -3.33334,5.48828 -7.05079,10.66406 -11.14584,15.52083 -4.10156,4.86328 -8.61328,9.27084 -13.54166,13.22917 -4.9349,3.95833 -10.52084,7.12239 -16.77084,9.47916 -5.14323,2.08334 -9.51823,4.41407 -13.125,6.97917 -3.61328,2.57162 -6.60156,5.3125 -8.95833,8.22917 -3.61328,3.89323 -6.73828,9.27083 -9.375,16.14583 -2.64323,6.875 -5.38411,14.13411 -8.22916,21.77083 -2.85157,7.64323 -5.97657,15.10417 -9.375,22.39583 -3.40495,7.29167 -7.67579,13.30079 -12.8125,18.02084 -7.08334,7.5 -17.57162,12.57161 -31.45834,15.20833 l -38.12499,6.45833 c -1.11329,0.14323 -2.46745,0.2474 -4.0625,0.3125 -1.60157,0.0716 -2.7474,0.0391 -3.4375,-0.10416 -3.19011,-0.97005 -4.16667,-3.08594 -2.91667,-6.35417 1.25,-3.26172 4.51172,-6.5625 9.79167,-9.89583 10.6901,-7.08334 19.23177,-12.8125 25.625,-17.1875 6.38671,-4.375 11.59505,-7.94922 15.62499,-10.72917 4.02344,-2.77343 7.25261,-5.0651 9.6875,-6.875 2.42839,-1.80338 5.03256,-3.8151 7.8125,-6.04166 11.25,-8.47006 19.02344,-17.35677 23.33334,-26.66667 2.5,-4.85677 4.79166,-9.58333 6.875,-14.16667 2.08333,-4.58333 4.27083,-9.09505 6.5625,-13.54166 2.29166,-4.44011 4.7526,-8.85417 7.39583,-13.22917 2.63672,-4.375 5.6901,-8.78255 9.16667,-13.22916 3.60677,-4.44011 8.47005,-8.78256 14.58333,-13.02084 6.10677,-4.23177 13.47005,-8.4375 22.08333,-12.60416 0.97005,-0.41667 2.5651,-1.35417 4.79167,-2.8125 2.22005,-1.45834 4.92838,-3.78255 8.125,-6.97917 3.1901,-3.1901 6.83593,-7.53255 10.9375,-13.02083 4.09505,-5.48177 8.4375,-12.46094 13.02083,-20.9375 -1.66667,-1.66667 -6.45833,-3.95833 -14.375,-6.875 -7.91667,-3.05338 -15.41667,-4.79167 -22.5,-5.20833 -6.52995,-0.41667 -11.91406,0.76823 -16.14583,3.54166 -4.23828,2.77995 -8.09245,6.73829 -11.5625,11.875 -3.47657,5.14323 -6.45833,7.5 -8.95833,7.08333 -0.83334,-0.41666 -0.97657,-2.14843 -0.41667,-5.20833 0.6901,-3.05338 1.66667,-6.04166 2.91667,-8.95833 5.27343,-12.08333 11.24999,-21.04167 17.91666,-26.875 6.80339,-5.97005 14.92839,-8.95833 24.375,-8.95833 6.80338,0 15.34505,1.66666 25.625,5 6.38672,2.22656 11.875,3.33333 16.45833,3.33333 9.58333,0 19.09505,-3.39843 28.54167,-10.20833 2.08333,0 2.8125,1.35416 2.1875,4.0625 -0.625,2.70833 -1.5625,5.8724 -2.8125,9.47916 -1.11329,3.61329 -1.875,6.04167 -2.29167,7.29167 -2.08333,6.52995 -3.75,10.35157 -5,11.45833 -1.25,0.97657 -4.86328,3.61329 -10.83333,7.91667 -3.33334,2.5 -6.21745,5.38411 -8.64584,8.64583 -2.43489,3.26823 -4.89583,7.1224 -7.39583,11.5625 -2.5,4.44662 -5.69661,10.5599 -9.58333,18.33333 z m 0,0"
+     style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66666663"
+     inkscape:connector-curvature="0" />
+  <use
+     style="fill:#000000;fill-opacity:1"
+     id="use62"
+     y="291"
+     x="119.25"
+     xlink:href="#glyph0-4"
+     width="100%"
+     height="100%"
+     transform="matrix(1.3333333,0,0,1.3333333,72.589732,-189.32751)" />
+  <use
+     style="fill:#000000;fill-opacity:1"
+     id="use196"
+     y="391"
+     x="168.375"
+     xlink:href="#glyph0-4-3"
+     width="100%"
+     height="100%"
+     transform="matrix(1.3333333,0,0,1.3333333,-861.41828,-631.73483)" />
+</svg>
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 3916801..a993538 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -67,7 +67,8 @@
 # Images to copy into HTML directory.
 # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
 HTML_IMAGES=  \
-	HarfBuzz.png
+	HarfBuzz.png \
+	HarfBuzz.svg
 
 # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
 # e.g. content_files=running.sgml building.sgml changes-2.0.sgml
@@ -110,7 +111,7 @@
 # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
 #DISTCLEANFILES +=
 
-# Comment this out if you want 'make check' to test you doc status
+# Comment this out if you don't want 'make check' to test you doc status
 # and run some sanity checks
 if ENABLE_GTK_DOC
 TESTS_ENVIRONMENT = cd $(srcdir) && \
diff --git a/docs/harfbuzz-docs.xml b/docs/harfbuzz-docs.xml
index 00113e9..9452a92 100644
--- a/docs/harfbuzz-docs.xml
+++ b/docs/harfbuzz-docs.xml
@@ -22,7 +22,7 @@
         source tree is available
         <ulink url="http://cgit.freedesktop.org/harfbuzz/">here</ulink>.
         Also available on
-        <ulink url="https://github.com/behdad/harfbuzz">github</ulink>.
+        <ulink url="https://github.com/harfbuzz/harfbuzz">github</ulink>.
         See <xref linkend="download" endterm="download.title"/> for release tarballs.
       </para>
       <para>
@@ -60,7 +60,7 @@
     </partinfo>
     <title>Reference manual</title>
       <chapter>
-        <title>Harfbuzz API</title>
+        <title>HarfBuzz API</title>
         <xi:include href="xml/hb.xml"/>
         <xi:include href="xml/hb-common.xml"/>
         <xi:include href="xml/hb-unicode.xml"/>
@@ -184,6 +184,22 @@
         <title>Index of new symbols in 1.3.3</title>
         <xi:include href="xml/api-index-1.3.3.xml"><xi:fallback /></xi:include>
       </index>
+      <index id="api-index-1-4-2" role="1.4.2">
+        <title>Index of new symbols in 1.4.2</title>
+        <xi:include href="xml/api-index-1.4.2.xml"><xi:fallback /></xi:include>
+      </index>
+      <index id="api-index-1-4-3" role="1.4.3">
+        <title>Index of new symbols in 1.4.3</title>
+        <xi:include href="xml/api-index-1.4.3.xml"><xi:fallback /></xi:include>
+      </index>
+      <index id="api-index-1-5-0" role="1.5.0">
+        <title>Index of new symbols in 1.5.0</title>
+        <xi:include href="xml/api-index-1.5.0.xml"><xi:fallback /></xi:include>
+      </index>
+      <index id="api-index-1-6-0" role="1.6.0">
+        <title>Index of new symbols in 1.6.0</title>
+        <xi:include href="xml/api-index-1.6.0.xml"><xi:fallback /></xi:include>
+      </index>
       <index id="deprecated-api-index" role="deprecated">
         <title>Index of deprecated API</title>
         <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index a91eb4c..91faa0b 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -9,6 +9,7 @@
 <FILE>hb-blob</FILE>
 hb_blob_create
 hb_blob_create_sub_blob
+hb_blob_copy_writable_or_fail
 hb_blob_destroy
 hb_blob_get_data
 hb_blob_get_data_writable
@@ -41,6 +42,7 @@
 hb_buffer_add_utf16
 hb_buffer_add_utf8
 hb_buffer_add_latin1
+hb_buffer_append
 hb_buffer_set_content_type
 hb_buffer_get_content_type
 hb_buffer_set_direction
@@ -77,9 +79,12 @@
 hb_buffer_serialize_list_formats
 hb_segment_properties_equal
 hb_segment_properties_hash
+hb_buffer_diff
 hb_buffer_set_message_func
 hb_buffer_t
+hb_glyph_info_get_glyph_flags
 hb_glyph_info_t
+hb_glyph_flags_t
 hb_glyph_position_t
 hb_buffer_content_type_t
 hb_buffer_flags_t
@@ -87,6 +92,7 @@
 hb_segment_properties_t
 hb_buffer_serialize_format_t
 hb_buffer_serialize_flags_t
+hb_buffer_diff_flags_t
 hb_buffer_message_func_t
 </SECTION>
 
@@ -146,13 +152,16 @@
 HB_SCRIPT_CANADIAN_ABORIGINAL
 hb_font_funcs_set_glyph_func
 hb_font_get_glyph_func_t
+hb_set_invert
 </SECTION>
 
 <SECTION>
 <FILE>hb-coretext</FILE>
+HB_CORETEXT_TAG_KERX
 HB_CORETEXT_TAG_MORT
 HB_CORETEXT_TAG_MORX
 hb_coretext_face_create
+hb_coretext_font_create
 hb_coretext_face_get_cg_font
 hb_coretext_font_get_ct_font
 </SECTION>
@@ -163,6 +172,7 @@
 hb_face_create_for_tables
 hb_face_destroy
 hb_face_get_empty
+hb_face_get_table_tags
 hb_face_get_glyph_count
 hb_face_get_index
 hb_face_get_upem
@@ -241,6 +251,7 @@
 hb_font_get_nominal_glyph_func_t
 hb_font_get_parent
 hb_font_get_ppem
+hb_font_get_ptem
 hb_font_get_scale
 hb_font_get_user_data
 hb_font_get_variation_glyph
@@ -251,10 +262,12 @@
 hb_font_is_immutable
 hb_font_make_immutable
 hb_font_reference
+hb_font_set_face
 hb_font_set_funcs
 hb_font_set_funcs_data
 hb_font_set_parent
 hb_font_set_ppem
+hb_font_set_ptem
 hb_font_set_scale
 hb_font_set_user_data
 hb_variation_t
@@ -283,6 +296,7 @@
 hb_ft_face_create_referenced
 hb_ft_font_create
 hb_ft_font_create_referenced
+hb_ft_font_changed
 hb_ft_font_get_face
 hb_ft_font_set_load_flags
 hb_ft_font_get_load_flags
@@ -302,6 +316,7 @@
 HB_GOBJECT_TYPE_BLOB
 HB_GOBJECT_TYPE_BUFFER
 HB_GOBJECT_TYPE_BUFFER_CONTENT_TYPE
+HB_GOBJECT_TYPE_BUFFER_DIFF_FLAGS
 HB_GOBJECT_TYPE_BUFFER_FLAGS
 HB_GOBJECT_TYPE_BUFFER_SERIALIZE_FLAGS
 HB_GOBJECT_TYPE_BUFFER_SERIALIZE_FORMAT
@@ -309,10 +324,13 @@
 HB_GOBJECT_TYPE_FACE
 HB_GOBJECT_TYPE_FONT
 HB_GOBJECT_TYPE_FONT_FUNCS
+HB_GOBJECT_TYPE_GLYPH_FLAGS
 HB_GOBJECT_TYPE_MEMORY_MODE
 HB_GOBJECT_TYPE_OT_LAYOUT_GLYPH_CLASS
 HB_GOBJECT_TYPE_OT_MATH_CONSTANT
+HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART
 HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART_FLAGS
+HB_GOBJECT_TYPE_OT_MATH_GLYPH_VARIANT
 HB_GOBJECT_TYPE_OT_MATH_KERN
 HB_GOBJECT_TYPE_SCRIPT
 HB_GOBJECT_TYPE_SHAPE_PLAN
@@ -328,6 +346,7 @@
 HB_GOBJECT_TYPE_USER_DATA_KEY
 hb_gobject_blob_get_type
 hb_gobject_buffer_content_type_get_type
+hb_gobject_buffer_diff_flags_get_type
 hb_gobject_buffer_flags_get_type
 hb_gobject_buffer_get_type
 hb_gobject_buffer_serialize_flags_get_type
@@ -336,10 +355,13 @@
 hb_gobject_face_get_type
 hb_gobject_font_funcs_get_type
 hb_gobject_font_get_type
+hb_gobject_glyph_flags_get_type
 hb_gobject_memory_mode_get_type
 hb_gobject_ot_layout_glyph_class_get_type
 hb_gobject_ot_math_constant_get_type
+hb_gobject_ot_math_glyph_part_get_type
 hb_gobject_ot_math_glyph_part_flags_get_type
+hb_gobject_ot_math_glyph_variant_get_type
 hb_gobject_ot_math_kern_get_type
 hb_gobject_script_get_type
 hb_gobject_shape_plan_get_type
@@ -503,11 +525,12 @@
 hb_set_get_user_data
 hb_set_has
 hb_set_intersect
-hb_set_invert
 hb_set_is_empty
 hb_set_is_equal
 hb_set_next
+hb_set_previous
 hb_set_next_range
+hb_set_previous_range
 hb_set_reference
 hb_set_set
 hb_set_set_user_data
diff --git a/docs/usermanual-buffers-language-script-and-direction.xml b/docs/usermanual-buffers-language-script-and-direction.xml
index 3a26c55..9eddb71 100644
--- a/docs/usermanual-buffers-language-script-and-direction.xml
+++ b/docs/usermanual-buffers-language-script-and-direction.xml
@@ -1,7 +1,7 @@
 <chapter id="buffers-language-script-and-direction">
   <title>Buffers, language, script and direction</title>
   <para>
-    The input to Harfbuzz is a series of Unicode characters, stored in a
+    The input to HarfBuzz is a series of Unicode characters, stored in a
     buffer. In this chapter, we'll look at how to set up a buffer with
     the text that we want and then customize the properties of the
     buffer.
@@ -15,7 +15,7 @@
       default values and ready to accept your Unicode strings.
     </para>
     <para>
-      Harfbuzz manages the memory of objects that it creates (such as
+      HarfBuzz manages the memory of objects that it creates (such as
       buffers), so you don't have to. When you have finished working on
       a buffer, you can call <literal>hb_buffer_destroy()</literal>:
     </para>
@@ -27,7 +27,7 @@
     <para>
       This will destroy the object and free its associated memory -
       unless some other part of the program holds a reference to this
-      buffer. If you acquire a Harfbuzz buffer from another subsystem
+      buffer. If you acquire a HarfBuzz buffer from another subsystem
       and want to ensure that it is not garbage collected by someone
       else destroying it, you should increase its reference count:
     </para>
@@ -53,8 +53,8 @@
   <section id="adding-text-to-the-buffer">
     <title>Adding text to the buffer</title>
     <para>
-      Now we have a brand new Harfbuzz buffer. Let's start filling it
-      with text! From Harfbuzz's perspective, a buffer is just a stream
+      Now we have a brand new HarfBuzz buffer. Let's start filling it
+      with text! From HarfBuzz's perspective, a buffer is just a stream
       of Unicode codepoints, but your input string is probably in one of
       the standard Unicode character encodings (UTF-8, UTF-16, UTF-32)
     </para>
diff --git a/docs/usermanual-clusters.xml b/docs/usermanual-clusters.xml
index 8b64bde..608371b 100644
--- a/docs/usermanual-clusters.xml
+++ b/docs/usermanual-clusters.xml
@@ -290,11 +290,11 @@
   0 ,3,2,4   
 </programlisting>
     <para>
-      There's no way to differentitate between these two scenarios based
+      There's no way to differentiate between these two scenarios based
       on the cluster numbers alone.
     </para>
     <para>
-      Another problem appens with ligatures under level 2 if the
+      Another problem happens with ligatures under level 2 if the
       direction of the text is forced to opposite of its natural
       direction (e.g. left-to-right Arabic). But that's too much of a
       corner case to worry about.
diff --git a/docs/usermanual-fonts-and-faces.xml b/docs/usermanual-fonts-and-faces.xml
index 01fcdc9..7de0f05 100644
--- a/docs/usermanual-fonts-and-faces.xml
+++ b/docs/usermanual-fonts-and-faces.xml
@@ -6,7 +6,7 @@
     </para>
   </section>
   <section id="using-harfbuzzs-native-opentype-implementation">
-    <title>Using Harfbuzz's native OpenType implementation</title>
+    <title>Using HarfBuzz's native OpenType implementation</title>
     <para>
     </para>
   </section>
diff --git a/docs/usermanual-hello-harfbuzz.xml b/docs/usermanual-hello-harfbuzz.xml
index 34db017..716b2f2 100644
--- a/docs/usermanual-hello-harfbuzz.xml
+++ b/docs/usermanual-hello-harfbuzz.xml
@@ -1,7 +1,7 @@
 <chapter id="hello-harfbuzz">
-  <title>Hello, Harfbuzz</title>
+  <title>Hello, HarfBuzz</title>
   <para>
-    Here's the simplest Harfbuzz that can possibly work. We will improve
+    Here's the simplest HarfBuzz that can possibly work. We will improve
     it later.
   </para>
   <orderedlist numeration="arabic">
@@ -91,23 +91,23 @@
   hb_font_destroy(hb_ft_font);
 </programlisting>
   <section id="what-harfbuzz-doesnt-do">
-    <title>What Harfbuzz doesn't do</title>
+    <title>What HarfBuzz doesn't do</title>
     <para>
       The code above will take a UTF8 string, shape it, and give you the
       information required to lay it out correctly on a single
       horizontal (or vertical) line using the font provided. That is the
-      extent of Harfbuzz's responsibility.
+      extent of HarfBuzz's responsibility.
     </para>
     <para>
       If you are implementing a text layout engine you may have other
-      responsibilities, that Harfbuzz will not help you with:
+      responsibilities, that HarfBuzz will not help you with:
     </para>
     <itemizedlist>
       <listitem>
         <para>
-          Harfbuzz won't help you with bidirectionality. If you want to
+          HarfBuzz won't help you with bidirectionality. If you want to
           lay out text with mixed Hebrew and English, you will need to
-          ensure that the buffer provided to Harfbuzz has those
+          ensure that the buffer provided to HarfBuzz has those
           characters in the correct layout order. This will be different
           from the logical order in which the Unicode text is stored. In
           other words, the user will hit the keys in the following
@@ -127,30 +127,30 @@
           (&quot;bidi&quot; is short for bidirectional), and there's an
           algorithm as an annex to the Unicode Standard which tells you how
           to reorder a string from logical order into presentation order.
-          Before sending your string to Harfbuzz, you may need to apply the
+          Before sending your string to HarfBuzz, you may need to apply the
           bidi algorithm to it. Libraries such as ICU and fribidi can do
           this for you.
         </para>
       </listitem>
       <listitem>
         <para>
-          Harfbuzz won't help you with text that contains different font
+          HarfBuzz won't help you with text that contains different font
           properties. For instance, if you have the string &quot;a
           <emphasis>huge</emphasis> breakfast&quot;, and you expect
           &quot;huge&quot; to be italic, you will need to send three
-          strings to Harfbuzz: <literal>a</literal>, in your Roman font;
+          strings to HarfBuzz: <literal>a</literal>, in your Roman font;
           <literal>huge</literal> using your italic font; and
           <literal>breakfast</literal> using your Roman font again.
           Similarly if you change font, font size, script, language or
           direction within your string, you will need to shape each run
-          independently and then output them independently. Harfbuzz
+          independently and then output them independently. HarfBuzz
           expects to shape a run of characters sharing the same
           properties.
         </para>
       </listitem>
       <listitem>
         <para>
-          Harfbuzz won't help you with line breaking, hyphenation or
+          HarfBuzz won't help you with line breaking, hyphenation or
           justification. As mentioned above, it lays out the string
           along a <emphasis>single line</emphasis> of, notionally,
           infinite length. If you want to find out where the potential
@@ -158,12 +158,12 @@
           could use the ICU library's break iterator functions.
         </para>
         <para>
-          Harfbuzz can tell you how wide a shaped piece of text is, which is
+          HarfBuzz can tell you how wide a shaped piece of text is, which is
           useful input to a justification algorithm, but it knows nothing
           about paragraphs, lines or line lengths. Nor will it adjust the
           space between words to fit them proportionally into a line. If you
           want to layout text in paragraphs, you will probably want to send
-          each word of your text to Harfbuzz to determine its shaped width
+          each word of your text to HarfBuzz to determine its shaped width
           after glyph substitutions, then work out how many words will fit
           on a line, and then finally output each word of the line separated
           by a space of the correct size to fully justify the paragraph.
@@ -171,12 +171,12 @@
       </listitem>
     </itemizedlist>
     <para>
-      As a layout engine implementor, Harfbuzz will help you with the
+      As a layout engine implementor, HarfBuzz will help you with the
       interface between your text and your font, and that's something
       that you'll need - what you then do with the glyphs that your font
       returns is up to you. The example we saw above enough to get us
-      started using Harfbuzz. Now we are going to use the remainder of
-      Harfbuzz's API to refine that example and improve our text shaping
+      started using HarfBuzz. Now we are going to use the remainder of
+      HarfBuzz's API to refine that example and improve our text shaping
       capabilities.
     </para>
   </section>
diff --git a/docs/usermanual-install-harfbuzz.xml b/docs/usermanual-install-harfbuzz.xml
index be8ac8d..899cc5b 100644
--- a/docs/usermanual-install-harfbuzz.xml
+++ b/docs/usermanual-install-harfbuzz.xml
@@ -1,5 +1,5 @@
 <chapter id="install-harfbuzz">
-  <title>Install Harfbuzz</title>
+  <title>Install HarfBuzz</title>
   <section id="download">
     <title id="download.title">Download</title>
     <para>
@@ -12,7 +12,7 @@
     <para>
       The canonical source tree is available
       <ulink url="http://cgit.freedesktop.org/harfbuzz/">here</ulink>.
-      Also available on <ulink url="https://github.com/behdad/harfbuzz">github</ulink>.
+      Also available on <ulink url="https://github.com/harfbuzz/harfbuzz">github</ulink>.
     </para>
     <para>
       The API that comes with <filename class='headerfile'>hb.h</filename> will
@@ -50,7 +50,7 @@
       and hb-shape under <filename>util/</filename>.
     </para>
     <para>
-      If you are bootstraping from git, you need a few more tools before you
+      If you are bootstrapping from git, you need a few more tools before you
       can run <filename>autogen.sh</filename> for the first time. Namely,
       pkg-config and <ulink url="http://www.complang.org/ragel/">ragel</ulink>.
       Again, on Ubuntu / Debian:
diff --git a/docs/usermanual-what-is-harfbuzz.xml b/docs/usermanual-what-is-harfbuzz.xml
index 3574d75..38f40cf 100644
--- a/docs/usermanual-what-is-harfbuzz.xml
+++ b/docs/usermanual-what-is-harfbuzz.xml
@@ -1,7 +1,7 @@
 <chapter id="what-is-harfbuzz">
-  <title>What is Harfbuzz?</title>
+  <title>What is HarfBuzz?</title>
   <para>
-    Harfbuzz is a <emphasis>text shaping engine</emphasis>. It solves
+    HarfBuzz is a <emphasis>text shaping engine</emphasis>. It solves
     the problem of selecting and positioning glyphs from a font given a
     Unicode string.
   </para>
@@ -9,17 +9,17 @@
     <title>Why do I need it?</title>
     <para>
       Text shaping is an integral part of preparing text for display. It
-      is a fairly low level operation; Harfbuzz is used directly by
+      is a fairly low level operation; HarfBuzz is used directly by
       graphic rendering libraries such as Pango, and the layout engines
       in Firefox, LibreOffice and Chromium. Unless you are
       <emphasis>writing</emphasis> one of these layout engines yourself,
-      you will probably not need to use Harfbuzz - normally higher level
+      you will probably not need to use HarfBuzz - normally higher level
       libraries will turn text into glyphs for you.
     </para>
     <para>
       However, if you <emphasis>are</emphasis> writing a layout engine
       or graphics library yourself, you will need to perform text
-      shaping, and this is where Harfbuzz can help you. Here are some
+      shaping, and this is where HarfBuzz can help you. Here are some
       reasons why you need it:
     </para>
     <itemizedlist>
@@ -95,20 +95,20 @@
     <para>
       If this is something that you need to do, then you need a text
       shaping engine: you could use Uniscribe if you are using Windows;
-      you could use CoreText on OS X; or you could use Harfbuzz. In the
+      you could use CoreText on OS X; or you could use HarfBuzz. In the
       rest of this manual, we are going to assume that you are the
       implementor of a text layout engine.
     </para>
   </section>
   <section id="why-is-it-called-harfbuzz">
-    <title>Why is it called Harfbuzz?</title>
+    <title>Why is it called HarfBuzz?</title>
     <para>
-      Harfbuzz began its life as text shaping code within the FreeType
+      HarfBuzz began its life as text shaping code within the FreeType
       project, (and you will see references to the FreeType authors
       within the source code copyright declarations) but was then
       abstracted out to its own project. This project is maintained by
-      Behdad Esfahbod, and named Harfbuzz. Originally, it was a shaping
-      engine for OpenType fonts - &quot;Harfbuzz&quot; is the Persian
+      Behdad Esfahbod, and named HarfBuzz. Originally, it was a shaping
+      engine for OpenType fonts - &quot;HarfBuzz&quot; is the Persian
       for &quot;open type&quot;.
     </para>
   </section>
diff --git a/git.mk b/git.mk
index bd39ae1..6e2708f 100644
--- a/git.mk
+++ b/git.mk
@@ -48,7 +48,7 @@
 #
 # This file knows how to handle autoconf, automake, libtool, gtk-doc,
 # gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu, appdata,
-# appstream.
+# appstream, hotdoc.
 #
 # This makefile provides the following targets:
 #
@@ -86,6 +86,7 @@
 		ar-lib \
 		compile \
 		config.guess \
+		config.rpath \
 		config.sub \
 		depcomp \
 		install-sh \
@@ -120,6 +121,47 @@
 			lt~obsolete.m4 \
 		; do echo "$$MACRO_DIR/$$x"; done; \
 	 fi`
+#
+# Modules that use gettext and use  AC_CONFIG_MACRO_DIR() may also include this,
+# though it's harmless to include regardless.
+GITIGNORE_MAINTAINERCLEANFILES_M4_GETTEXT = \
+	`MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \
+	if test "x$$MACRO_DIR" != "x$(srcdir)/"; then	\
+		for x in				\
+			codeset.m4			\
+			extern-inline.m4		\
+			fcntl-o.m4			\
+			gettext.m4			\
+			glibc2.m4			\
+			glibc21.m4			\
+			iconv.m4			\
+			intdiv0.m4			\
+			intl.m4				\
+			intldir.m4			\
+			intlmacosx.m4			\
+			intmax.m4			\
+			inttypes-pri.m4			\
+			inttypes_h.m4			\
+			lcmessage.m4			\
+			lib-ld.m4			\
+			lib-link.m4			\
+			lib-prefix.m4			\
+			lock.m4				\
+			longlong.m4			\
+			nls.m4				\
+			po.m4				\
+			printf-posix.m4			\
+			progtest.m4			\
+			size_max.m4			\
+			stdint_h.m4			\
+			threadlib.m4			\
+			uintmax_t.m4			\
+			visibility.m4			\
+			wchar_t.m4			\
+			wint_t.m4			\
+			xsize.m4			\
+		; do echo "$$MACRO_DIR/$$x"; done; \
+	fi`
 
 
 
@@ -208,6 +250,15 @@
 				"*/*.omf.out" \
 			; do echo /$$x; done; \
 		fi; \
+		if test "x$(HOTDOC)" = x; then :; else \
+			$(foreach project, $(HOTDOC_PROJECTS),echo "/$(call HOTDOC_TARGET,$(project))"; \
+				echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-conf-path output)" ; \
+				echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-private-folder)" ; \
+			) \
+			for x in \
+				.hotdoc.d \
+			; do echo "/$$x"; done; \
+		fi; \
 		if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \
 			for lc in $(HELP_LINGUAS); do \
 				for x in \
@@ -235,6 +286,7 @@
 		fi; \
 		if test -f $(srcdir)/po/Makefile.in.in; then \
 			for x in \
+				ABOUT-NLS \
 				po/Makefile.in.in \
 				po/Makefile.in.in~ \
 				po/Makefile.in \
@@ -243,6 +295,7 @@
 				po/POTFILES \
 				po/Rules-quot \
 				po/stamp-it \
+				po/stamp-po \
 				po/.intltool-merge-cache \
 				"po/*.gmo" \
 				"po/*.header" \
@@ -274,7 +327,7 @@
 		if test "x$(am__dirstamp)" = x; then :; else \
 			echo "$(am__dirstamp)"; \
 		fi; \
-		if test "x$(LTCOMPILE)" = x -a "x$(LTCXXCOMPILE)" = x -a "x$(GTKDOC_RUN)" = x; then :; else \
+		if test "x$(findstring libtool,$(LTCOMPILE))" = x -a "x$(findstring libtool,$(LTCXXCOMPILE))" = x -a "x$(GTKDOC_RUN)" = x; then :; else \
 			for x in \
 				"*.lo" \
 				".libs" "_libs" \
diff --git a/harfbuzz.doap b/harfbuzz.doap
index d2896eb..0769969 100644
--- a/harfbuzz.doap
+++ b/harfbuzz.doap
@@ -13,7 +13,7 @@
   <!--download-page
   rdf:resource=""/-->
   <bug-database
-  rdf:resource="http://bugs.freedesktop.org/enter_bug.cgi?product=harfbuzz"/>
+  rdf:resource="https://github.com/harfbuzz/harfbuzz/issues" />
 
   <maintainer>
     <foaf:Person>
diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4
new file mode 100644
index 0000000..5032bba
--- /dev/null
+++ b/m4/ax_cxx_compile_stdcxx.m4
@@ -0,0 +1,982 @@
+# ===========================================================================
+#  https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+#   Check for baseline language coverage in the compiler for the specified
+#   version of the C++ standard.  If necessary, add switches to CXX and
+#   CXXCPP to enable support.  VERSION may be '11' (for the C++11 standard)
+#   or '14' (for the C++14 standard).
+#
+#   The second argument, if specified, indicates whether you insist on an
+#   extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+#   -std=c++11).  If neither is specified, you get whatever works, with
+#   preference for an extended mode.
+#
+#   The third argument, if specified 'mandatory' or if left unspecified,
+#   indicates that baseline support for the specified C++ standard is
+#   required and that the macro should error out if no mode with that
+#   support is found.  If specified 'optional', then configuration proceeds
+#   regardless, after defining HAVE_CXX${VERSION} if and only if a
+#   supporting mode is found.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+#   Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+#   Copyright (c) 2015 Paul Norman <penorman@mac.com>
+#   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+#   Copyright (c) 2016 Krzesimir Nowak <qdlacz@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 7
+
+dnl  This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
+dnl  (serial version number 13).
+
+AX_REQUIRE_DEFINED([AC_MSG_WARN])
+AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
+  m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
+        [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
+        [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
+        [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
+  m4_if([$2], [], [],
+        [$2], [ext], [],
+        [$2], [noext], [],
+        [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
+  m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
+        [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
+        [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
+        [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
+  AC_LANG_PUSH([C++])dnl
+  ac_success=no
+  AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
+  ax_cv_cxx_compile_cxx$1,
+  [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+    [ax_cv_cxx_compile_cxx$1=yes],
+    [ax_cv_cxx_compile_cxx$1=no])])
+  if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
+    ac_success=yes
+  fi
+
+  m4_if([$2], [noext], [], [dnl
+  if test x$ac_success = xno; then
+    for alternative in ${ax_cxx_compile_alternatives}; do
+      switch="-std=gnu++${alternative}"
+      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+      AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+                     $cachevar,
+        [ac_save_CXX="$CXX"
+         CXX="$CXX $switch"
+         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+          [eval $cachevar=yes],
+          [eval $cachevar=no])
+         CXX="$ac_save_CXX"])
+      if eval test x\$$cachevar = xyes; then
+        CXX="$CXX $switch"
+        if test -n "$CXXCPP" ; then
+          CXXCPP="$CXXCPP $switch"
+        fi
+        ac_success=yes
+        break
+      fi
+    done
+  fi])
+
+  m4_if([$2], [ext], [], [dnl
+  if test x$ac_success = xno; then
+    dnl HP's aCC needs +std=c++11 according to:
+    dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
+    dnl Cray's crayCC needs "-h std=c++11"
+    for alternative in ${ax_cxx_compile_alternatives}; do
+      for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
+        cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+        AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+                       $cachevar,
+          [ac_save_CXX="$CXX"
+           CXX="$CXX $switch"
+           AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+            [eval $cachevar=yes],
+            [eval $cachevar=no])
+           CXX="$ac_save_CXX"])
+        if eval test x\$$cachevar = xyes; then
+          CXX="$CXX $switch"
+          if test -n "$CXXCPP" ; then
+            CXXCPP="$CXXCPP $switch"
+          fi
+          ac_success=yes
+          break
+        fi
+      done
+      if test x$ac_success = xyes; then
+        break
+      fi
+    done
+  fi])
+  AC_LANG_POP([C++])
+  if test x$ax_cxx_compile_cxx$1_required = xtrue; then
+    if test x$ac_success = xno; then
+      AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
+    fi
+  fi
+  if test x$ac_success = xno; then
+    HAVE_CXX$1=0
+    AC_MSG_NOTICE([No compiler with C++$1 support was found])
+  else
+    HAVE_CXX$1=1
+    AC_DEFINE(HAVE_CXX$1,1,
+              [define if the compiler supports basic C++$1 syntax])
+  fi
+  AC_SUBST(HAVE_CXX$1)
+  m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])])
+])
+
+
+dnl  Test body for checking C++11 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+)
+
+
+dnl  Test body for checking C++14 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+)
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
+)
+
+dnl  Tests for new features in C++11
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201103L
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+  namespace test_static_assert
+  {
+
+    template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+  }
+
+  namespace test_final_override
+  {
+
+    struct Base
+    {
+      virtual void f() {}
+    };
+
+    struct Derived : public Base
+    {
+      virtual void f() override {}
+    };
+
+  }
+
+  namespace test_double_right_angle_brackets
+  {
+
+    template < typename T >
+    struct check {};
+
+    typedef check<void> single_type;
+    typedef check<check<void>> double_type;
+    typedef check<check<check<void>>> triple_type;
+    typedef check<check<check<check<void>>>> quadruple_type;
+
+  }
+
+  namespace test_decltype
+  {
+
+    int
+    f()
+    {
+      int a = 1;
+      decltype(a) b = 2;
+      return a + b;
+    }
+
+  }
+
+  namespace test_type_deduction
+  {
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static const bool value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static const bool value = true;
+    };
+
+    template < typename T1, typename T2 >
+    auto
+    add(T1 a1, T2 a2) -> decltype(a1 + a2)
+    {
+      return a1 + a2;
+    }
+
+    int
+    test(const int c, volatile int v)
+    {
+      static_assert(is_same<int, decltype(0)>::value == true, "");
+      static_assert(is_same<int, decltype(c)>::value == false, "");
+      static_assert(is_same<int, decltype(v)>::value == false, "");
+      auto ac = c;
+      auto av = v;
+      auto sumi = ac + av + 'x';
+      auto sumf = ac + av + 1.0;
+      static_assert(is_same<int, decltype(ac)>::value == true, "");
+      static_assert(is_same<int, decltype(av)>::value == true, "");
+      static_assert(is_same<int, decltype(sumi)>::value == true, "");
+      static_assert(is_same<int, decltype(sumf)>::value == false, "");
+      static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+      return (sumf > 0.0) ? sumi : add(c, v);
+    }
+
+  }
+
+  namespace test_noexcept
+  {
+
+    int f() { return 0; }
+    int g() noexcept { return 0; }
+
+    static_assert(noexcept(f()) == false, "");
+    static_assert(noexcept(g()) == true, "");
+
+  }
+
+  namespace test_constexpr
+  {
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+    {
+      return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+    }
+
+    template < typename CharT >
+    unsigned long constexpr
+    strlen_c(const CharT *const s) noexcept
+    {
+      return strlen_c_r(s, 0UL);
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("1") == 1UL, "");
+    static_assert(strlen_c("example") == 7UL, "");
+    static_assert(strlen_c("another\0example") == 7UL, "");
+
+  }
+
+  namespace test_rvalue_references
+  {
+
+    template < int N >
+    struct answer
+    {
+      static constexpr int value = N;
+    };
+
+    answer<1> f(int&)       { return answer<1>(); }
+    answer<2> f(const int&) { return answer<2>(); }
+    answer<3> f(int&&)      { return answer<3>(); }
+
+    void
+    test()
+    {
+      int i = 0;
+      const int c = 0;
+      static_assert(decltype(f(i))::value == 1, "");
+      static_assert(decltype(f(c))::value == 2, "");
+      static_assert(decltype(f(0))::value == 3, "");
+    }
+
+  }
+
+  namespace test_uniform_initialization
+  {
+
+    struct test
+    {
+      static const int zero {};
+      static const int one {1};
+    };
+
+    static_assert(test::zero == 0, "");
+    static_assert(test::one == 1, "");
+
+  }
+
+  namespace test_lambdas
+  {
+
+    void
+    test1()
+    {
+      auto lambda1 = [](){};
+      auto lambda2 = lambda1;
+      lambda1();
+      lambda2();
+    }
+
+    int
+    test2()
+    {
+      auto a = [](int i, int j){ return i + j; }(1, 2);
+      auto b = []() -> int { return '0'; }();
+      auto c = [=](){ return a + b; }();
+      auto d = [&](){ return c; }();
+      auto e = [a, &b](int x) mutable {
+        const auto identity = [](int y){ return y; };
+        for (auto i = 0; i < a; ++i)
+          a += b--;
+        return x + identity(a + b);
+      }(0);
+      return a + b + c + d + e;
+    }
+
+    int
+    test3()
+    {
+      const auto nullary = [](){ return 0; };
+      const auto unary = [](int x){ return x; };
+      using nullary_t = decltype(nullary);
+      using unary_t = decltype(unary);
+      const auto higher1st = [](nullary_t f){ return f(); };
+      const auto higher2nd = [unary](nullary_t f1){
+        return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+      };
+      return higher1st(nullary) + higher2nd(nullary)(unary);
+    }
+
+  }
+
+  namespace test_variadic_templates
+  {
+
+    template <int...>
+    struct sum;
+
+    template <int N0, int... N1toN>
+    struct sum<N0, N1toN...>
+    {
+      static constexpr auto value = N0 + sum<N1toN...>::value;
+    };
+
+    template <>
+    struct sum<>
+    {
+      static constexpr auto value = 0;
+    };
+
+    static_assert(sum<>::value == 0, "");
+    static_assert(sum<1>::value == 1, "");
+    static_assert(sum<23>::value == 23, "");
+    static_assert(sum<1, 2>::value == 3, "");
+    static_assert(sum<5, 5, 11>::value == 21, "");
+    static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+  }
+
+  // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+  // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+  // because of this.
+  namespace test_template_alias_sfinae
+  {
+
+    struct foo {};
+
+    template<typename T>
+    using member = typename T::member_type;
+
+    template<typename T>
+    void func(...) {}
+
+    template<typename T>
+    void func(member<T>*) {}
+
+    void test();
+
+    void test() { func<foo>(0); }
+
+  }
+
+}  // namespace cxx11
+
+#endif  // __cplusplus >= 201103L
+
+]])
+
+
+dnl  Tests for new features in C++14
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
+
+// If the compiler admits that it is not ready for C++14, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201402L
+
+#error "This is not a C++14 compiler"
+
+#else
+
+namespace cxx14
+{
+
+  namespace test_polymorphic_lambdas
+  {
+
+    int
+    test()
+    {
+      const auto lambda = [](auto&&... args){
+        const auto istiny = [](auto x){
+          return (sizeof(x) == 1UL) ? 1 : 0;
+        };
+        const int aretiny[] = { istiny(args)... };
+        return aretiny[0];
+      };
+      return lambda(1, 1L, 1.0f, '1');
+    }
+
+  }
+
+  namespace test_binary_literals
+  {
+
+    constexpr auto ivii = 0b0000000000101010;
+    static_assert(ivii == 42, "wrong value");
+
+  }
+
+  namespace test_generalized_constexpr
+  {
+
+    template < typename CharT >
+    constexpr unsigned long
+    strlen_c(const CharT *const s) noexcept
+    {
+      auto length = 0UL;
+      for (auto p = s; *p; ++p)
+        ++length;
+      return length;
+    }
+
+    static_assert(strlen_c("") == 0UL, "");
+    static_assert(strlen_c("x") == 1UL, "");
+    static_assert(strlen_c("test") == 4UL, "");
+    static_assert(strlen_c("another\0test") == 7UL, "");
+
+  }
+
+  namespace test_lambda_init_capture
+  {
+
+    int
+    test()
+    {
+      auto x = 0;
+      const auto lambda1 = [a = x](int b){ return a + b; };
+      const auto lambda2 = [a = lambda1(x)](){ return a; };
+      return lambda2();
+    }
+
+  }
+
+  namespace test_digit_separators
+  {
+
+    constexpr auto ten_million = 100'000'000;
+    static_assert(ten_million == 100000000, "");
+
+  }
+
+  namespace test_return_type_deduction
+  {
+
+    auto f(int& x) { return x; }
+    decltype(auto) g(int& x) { return x; }
+
+    template < typename T1, typename T2 >
+    struct is_same
+    {
+      static constexpr auto value = false;
+    };
+
+    template < typename T >
+    struct is_same<T, T>
+    {
+      static constexpr auto value = true;
+    };
+
+    int
+    test()
+    {
+      auto x = 0;
+      static_assert(is_same<int, decltype(f(x))>::value, "");
+      static_assert(is_same<int&, decltype(g(x))>::value, "");
+      return x;
+    }
+
+  }
+
+}  // namespace cxx14
+
+#endif  // __cplusplus >= 201402L
+
+]])
+
+
+dnl  Tests for new features in C++17
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
+
+// If the compiler admits that it is not ready for C++17, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus <= 201402L
+
+#error "This is not a C++17 compiler"
+
+#else
+
+#if defined(__clang__)
+  #define REALLY_CLANG
+#else
+  #if defined(__GNUC__)
+    #define REALLY_GCC
+  #endif
+#endif
+
+#include <initializer_list>
+#include <utility>
+#include <type_traits>
+
+namespace cxx17
+{
+
+#if !defined(REALLY_CLANG)
+  namespace test_constexpr_lambdas
+  {
+
+    // TODO: test it with clang++ from git
+
+    constexpr int foo = [](){return 42;}();
+
+  }
+#endif // !defined(REALLY_CLANG)
+
+  namespace test::nested_namespace::definitions
+  {
+
+  }
+
+  namespace test_fold_expression
+  {
+
+    template<typename... Args>
+    int multiply(Args... args)
+    {
+      return (args * ... * 1);
+    }
+
+    template<typename... Args>
+    bool all(Args... args)
+    {
+      return (args && ...);
+    }
+
+  }
+
+  namespace test_extended_static_assert
+  {
+
+    static_assert (true);
+
+  }
+
+  namespace test_auto_brace_init_list
+  {
+
+    auto foo = {5};
+    auto bar {5};
+
+    static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
+    static_assert(std::is_same<int, decltype(bar)>::value);
+  }
+
+  namespace test_typename_in_template_template_parameter
+  {
+
+    template<template<typename> typename X> struct D;
+
+  }
+
+  namespace test_fallthrough_nodiscard_maybe_unused_attributes
+  {
+
+    int f1()
+    {
+      return 42;
+    }
+
+    [[nodiscard]] int f2()
+    {
+      [[maybe_unused]] auto unused = f1();
+
+      switch (f1())
+      {
+      case 17:
+        f1();
+        [[fallthrough]];
+      case 42:
+        f1();
+      }
+      return f1();
+    }
+
+  }
+
+  namespace test_extended_aggregate_initialization
+  {
+
+    struct base1
+    {
+      int b1, b2 = 42;
+    };
+
+    struct base2
+    {
+      base2() {
+        b3 = 42;
+      }
+      int b3;
+    };
+
+    struct derived : base1, base2
+    {
+        int d;
+    };
+
+    derived d1 {{1, 2}, {}, 4};  // full initialization
+    derived d2 {{}, {}, 4};      // value-initialized bases
+
+  }
+
+  namespace test_general_range_based_for_loop
+  {
+
+    struct iter
+    {
+      int i;
+
+      int& operator* ()
+      {
+        return i;
+      }
+
+      const int& operator* () const
+      {
+        return i;
+      }
+
+      iter& operator++()
+      {
+        ++i;
+        return *this;
+      }
+    };
+
+    struct sentinel
+    {
+      int i;
+    };
+
+    bool operator== (const iter& i, const sentinel& s)
+    {
+      return i.i == s.i;
+    }
+
+    bool operator!= (const iter& i, const sentinel& s)
+    {
+      return !(i == s);
+    }
+
+    struct range
+    {
+      iter begin() const
+      {
+        return {0};
+      }
+
+      sentinel end() const
+      {
+        return {5};
+      }
+    };
+
+    void f()
+    {
+      range r {};
+
+      for (auto i : r)
+      {
+        [[maybe_unused]] auto v = i;
+      }
+    }
+
+  }
+
+  namespace test_lambda_capture_asterisk_this_by_value
+  {
+
+    struct t
+    {
+      int i;
+      int foo()
+      {
+        return [*this]()
+        {
+          return i;
+        }();
+      }
+    };
+
+  }
+
+  namespace test_enum_class_construction
+  {
+
+    enum class byte : unsigned char
+    {};
+
+    byte foo {42};
+
+  }
+
+  namespace test_constexpr_if
+  {
+
+    template <bool cond>
+    int f ()
+    {
+      if constexpr(cond)
+      {
+        return 13;
+      }
+      else
+      {
+        return 42;
+      }
+    }
+
+  }
+
+  namespace test_selection_statement_with_initializer
+  {
+
+    int f()
+    {
+      return 13;
+    }
+
+    int f2()
+    {
+      if (auto i = f(); i > 0)
+      {
+        return 3;
+      }
+
+      switch (auto i = f(); i + 4)
+      {
+      case 17:
+        return 2;
+
+      default:
+        return 1;
+      }
+    }
+
+  }
+
+#if !defined(REALLY_CLANG)
+  namespace test_template_argument_deduction_for_class_templates
+  {
+
+    // TODO: test it with clang++ from git
+
+    template <typename T1, typename T2>
+    struct pair
+    {
+      pair (T1 p1, T2 p2)
+        : m1 {p1},
+          m2 {p2}
+      {}
+
+      T1 m1;
+      T2 m2;
+    };
+
+    void f()
+    {
+      [[maybe_unused]] auto p = pair{13, 42u};
+    }
+
+  }
+#endif // !defined(REALLY_CLANG)
+
+  namespace test_non_type_auto_template_parameters
+  {
+
+    template <auto n>
+    struct B
+    {};
+
+    B<5> b1;
+    B<'a'> b2;
+
+  }
+
+#if !defined(REALLY_CLANG)
+  namespace test_structured_bindings
+  {
+
+    // TODO: test it with clang++ from git
+
+    int arr[2] = { 1, 2 };
+    std::pair<int, int> pr = { 1, 2 };
+
+    auto f1() -> int(&)[2]
+    {
+      return arr;
+    }
+
+    auto f2() -> std::pair<int, int>&
+    {
+      return pr;
+    }
+
+    struct S
+    {
+      int x1 : 2;
+      volatile double y1;
+    };
+
+    S f3()
+    {
+      return {};
+    }
+
+    auto [ x1, y1 ] = f1();
+    auto& [ xr1, yr1 ] = f1();
+    auto [ x2, y2 ] = f2();
+    auto& [ xr2, yr2 ] = f2();
+    const auto [ x3, y3 ] = f3();
+
+  }
+#endif // !defined(REALLY_CLANG)
+
+#if !defined(REALLY_CLANG)
+  namespace test_exception_spec_type_system
+  {
+
+    // TODO: test it with clang++ from git
+
+    struct Good {};
+    struct Bad {};
+
+    void g1() noexcept;
+    void g2();
+
+    template<typename T>
+    Bad
+    f(T*, T*);
+
+    template<typename T1, typename T2>
+    Good
+    f(T1*, T2*);
+
+    static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
+
+  }
+#endif // !defined(REALLY_CLANG)
+
+  namespace test_inline_variables
+  {
+
+    template<class T> void f(T)
+    {}
+
+    template<class T> inline T g(T)
+    {
+      return T{};
+    }
+
+    template<> inline void f<>(int)
+    {}
+
+    template<> int g<>(int)
+    {
+      return 5;
+    }
+
+  }
+
+}  // namespace cxx17
+
+#endif  // __cplusplus <= 201402L
+
+]])
diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4
index 4c4051e..5fbf9fe 100644
--- a/m4/ax_pthread.m4
+++ b/m4/ax_pthread.m4
@@ -1,5 +1,5 @@
 # ===========================================================================
-#        http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+#        https://www.gnu.org/software/autoconf-archive/ax_pthread.html
 # ===========================================================================
 #
 # SYNOPSIS
@@ -67,7 +67,7 @@
 #   Public License for more details.
 #
 #   You should have received a copy of the GNU General Public License along
-#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#   with this program. If not, see <https://www.gnu.org/licenses/>.
 #
 #   As a special exception, the respective Autoconf Macro's copyright owner
 #   gives unlimited permission to copy, distribute and modify the configure
@@ -82,7 +82,7 @@
 #   modified version of the Autoconf Macro, you may extend this special
 #   exception to the GPL to apply to your modified version as well.
 
-#serial 23
+#serial 24
 
 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
 AC_DEFUN([AX_PTHREAD], [
diff --git a/mingw32.sh b/mingw32.sh
new file mode 100755
index 0000000..6774405
--- /dev/null
+++ b/mingw32.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+target=i686-w64-mingw32
+
+unset CC
+unset CXX
+unset CPP
+unset LD
+unset LDFLAGS
+unset CFLAGS
+unset CXXFLAGS
+unset PKG_CONFIG_PATH
+
+# Removed -static from the following
+export CFLAGS="-static-libgcc"
+export CXXFLAGS="-static-libgcc -static-libstdc++"
+export CPPFLAGS="-I$HOME/.local/$target/include -O2"
+export LDFLAGS=-L$HOME/.local/$target/lib
+export PKG_CONFIG_LIBDIR=$HOME/.local/$target/lib/pkgconfig
+export PATH=$HOME/.local/$target/bin:$PATH
+
+../configure --build=`../config.guess` --host=$target --prefix=$HOME/.local/$target "$@"
diff --git a/mingw64.sh b/mingw64.sh
new file mode 100755
index 0000000..49a1431
--- /dev/null
+++ b/mingw64.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+target=x86_64-w64-mingw32
+
+unset CC
+unset CXX
+unset CPP
+unset LD
+unset LDFLAGS
+unset CFLAGS
+unset CXXFLAGS
+unset PKG_CONFIG_PATH
+
+# Removed -static from the following
+export CFLAGS="-static-libgcc"
+export CXXFLAGS="-static-libgcc -static-libstdc++"
+export CPPFLAGS="-I$HOME/.local/$target/include -O2"
+export LDFLAGS=-L$HOME/.local/$target/lib
+export PKG_CONFIG_LIBDIR=$HOME/.local/$target/lib/pkgconfig
+export PATH=$HOME/.local/$target/bin:$PATH
+
+../configure --build=`../config.guess` --host=$target --prefix=$HOME/.local/$target "$@"
diff --git a/replace-enum-strings.cmake b/replace-enum-strings.cmake
new file mode 100644
index 0000000..42fdbff
--- /dev/null
+++ b/replace-enum-strings.cmake
@@ -0,0 +1,21 @@
+# CMake script to replace items

+# in sources generated by glib-mkenums

+

+FILE(READ ${ENUM_INPUT_SRC} enum_in)

+

+STRING(REPLACE

+  "_t_get_type"

+  "_get_type"

+  enum_out_tmp

+  "${enum_in}"

+  )

+

+STRING(REPLACE

+  "_T ("

+  " ("

+  enum_out

+  "${enum_out_tmp}"

+  )

+

+FILE(WRITE ${ENUM_OUTPUT_SRC} "${enum_out}")

+FILE(REMOVE ${ENUM_INPUT_SRC})
\ No newline at end of file
diff --git a/src/Makefile.am b/src/Makefile.am
index e322d56..c4d5fb8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,6 @@
 # Process this file with automake to produce Makefile.in
 
+NULL =
 SUBDIRS =
 DIST_SUBDIRS =
 BUILT_SOURCES =
@@ -8,12 +9,14 @@
 DISTCLEANFILES =
 MAINTAINERCLEANFILES =
 DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
+TESTS =
+check_PROGRAMS =
 
 # The following warning options are useful for debugging: -Wpadded
 #AM_CXXFLAGS =
 
 # Convenience targets:
-lib: $(BUILT_SOURCES) libharfbuzz.la
+lib: $(BUILT_SOURCES) libharfbuzz.la libharfbuzz-subset.la
 fuzzing: $(BUILT_SOURCES) libharfbuzz-fuzzing.la
 
 lib_LTLIBRARIES = libharfbuzz.la
@@ -25,11 +28,22 @@
 HBNONPCLIBS =
 HBDEPS =
 HBSOURCES =  $(HB_BASE_sources)
+HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources)
 HBHEADERS = $(HB_BASE_headers)
 HBNODISTHEADERS = $(HB_NODIST_headers)
 
+if WITH_LIBSTDCXX
+HBNOLIBCXXCFLAGS =
+else
+# Make sure we don't link to libstdc++
+# No threadsafe statics in C++ as we do it ourselves
+HBCFLAGS += -fno-exceptions
+HBNOLIBCXXFLAGS = -fno-threadsafe-statics -fno-rtti
+endif
+
 if HAVE_OT
 HBSOURCES += $(HB_OT_sources)
+HBSOURCES += $(HB_OT_RAGEL_GENERATED_sources)
 HBHEADERS += $(HB_OT_headers)
 endif
 
@@ -97,6 +111,9 @@
 HBCFLAGS += -I$(srcdir)/hb-ucdn
 HBLIBS   += hb-ucdn/libhb-ucdn.la
 HBSOURCES += $(HB_UCDN_sources)
+hb-ucdn/libhb-ucdn.la: ucdn
+ucdn:
+	@$(MAKE) $(AM_MAKEFLAGS) -C hb-ucdn
 endif
 DIST_SUBDIRS += hb-ucdn
 
@@ -108,40 +125,69 @@
 if OS_WIN32
 export_symbols = -export-symbols harfbuzz.def
 harfbuzz_def_dependency = harfbuzz.def
-libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
+export_symbols_subset = -export-symbols harfbuzz-subset.def
+harfbuzz_subset_def_dependency = harfbuzz-subset.def
+export_symbols_icu = -export-symbols harfbuzz-icu.def
+harfbuzz_icu_def_dependency = harfbuzz-icu.def
+export_symbols_gobject = -export-symbols harfbuzz-gobject.def
+harfbuzz_gobject_def_dependency = harfbuzz-gobject.def
+chosen_linker = $(CXXLINK)
 else
-# Use a C linker for GCC, not C++; Don't link to libstdc++
+if WITH_LIBSTDCXX
+chosen_linker = $(CXXLINK)
+else
 if HAVE_GCC
-libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS)
+# Use a C linker for GCC, not C++; Don't link to libstdc++
+chosen_linker = $(LINK)
 else
-libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
+chosen_linker = $(CXXLINK)
+endif
 endif
 endif
 
+base_link_flags = $(AM_LDFLAGS) -lm -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
+libharfbuzz_la_LINK = $(chosen_linker) $(libharfbuzz_la_LDFLAGS)
 libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) $(HBNODISTHEADERS)
-libharfbuzz_la_CPPFLAGS = $(HBCFLAGS)
-libharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) $(export_symbols) -no-undefined
+libharfbuzz_la_CPPFLAGS = $(HBCFLAGS) $(HBNOLIBCXXFLAGS)
+libharfbuzz_la_LDFLAGS = $(base_link_flags) $(export_symbols)
 libharfbuzz_la_LIBADD = $(HBLIBS)
 EXTRA_libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency)
 pkginclude_HEADERS = $(HBHEADERS)
 nodist_pkginclude_HEADERS = $(HBNODISTHEADERS)
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = harfbuzz.pc
-EXTRA_DIST += harfbuzz.pc.in
+cmakedir = $(libdir)/cmake/harfbuzz
+cmake_DATA = harfbuzz-config.cmake
+EXTRA_DIST += harfbuzz.pc.in harfbuzz-config.cmake.in
 
-FUZZING_CPPFLAGS= \
+lib_LTLIBRARIES += libharfbuzz-subset.la
+libharfbuzz_subset_la_SOURCES = $(HB_SUBSET_sources)
+libharfbuzz_subset_la_CPPFLAGS = $(HBCFLAGS)
+libharfbuzz_subset_la_LDFLAGS = $(base_link_flags) $(export_symbols_subset)
+libharfbuzz_subset_la_LIBADD = libharfbuzz.la
+EXTRA_libharfbuzz_subset_la_DEPENDENCIES = $(harfbuzz_subset_def_dependency)
+pkginclude_HEADERS += $(HB_SUBSET_headers)
+pkgconfig_DATA += harfbuzz-subset.pc
+EXTRA_DIST += harfbuzz-subset.pc.in
+
+FUZZING_CPPFLAGS = \
 	-DHB_NDEBUG \
 	-DHB_MAX_NESTING_LEVEL=3 \
 	-DHB_SANITIZE_MAX_EDITS=3 \
-	-DHB_BUFFER_MAX_EXPANSION_FACTOR=3 \
+	-DHB_SANITIZE_MAX_OPS_FACTOR=3 \
+	-DHB_SANITIZE_MAX_OPS_MIN=128 \
+	-DHB_BUFFER_MAX_LEN_FACTOR=3 \
 	-DHB_BUFFER_MAX_LEN_MIN=8 \
 	-DHB_BUFFER_MAX_LEN_DEFAULT=128 \
+	-DHB_BUFFER_MAX_OPS_FACTOR=8 \
+	-DHB_BUFFER_MAX_OPS_MIN=64 \
+	-DHB_BUFFER_MAX_OPS_DEFAULT=1024 \
 	$(NULL)
 EXTRA_LTLIBRARIES = libharfbuzz-fuzzing.la
-libharfbuzz_fuzzing_la_LINK = $(libharfbuzz_la_LINK)
+libharfbuzz_fuzzing_la_LINK = $(chosen_linker) $(libharfbuzz_fuzzing_la_LDFLAGS)
 libharfbuzz_fuzzing_la_SOURCES = $(libharfbuzz_la_SOURCES)
-libharfbuzz_fuzzing_la_CPPFLAGS = $(libharfbuzz_la_CPPFLAGS) $(FUZZING_CPPFLAGS)
-libharfbuzz_fuzzing_la_LDFLAGS = $(libharfbuzz_la_LDFLAGS)
+libharfbuzz_fuzzing_la_CPPFLAGS = $(HBCFLAGS) $(FUZZING_CPPFLAGS)
+libharfbuzz_fuzzing_la_LDFLAGS = $(AM_LDFLAGS)
 libharfbuzz_fuzzing_la_LIBADD = $(libharfbuzz_la_LIBADD)
 EXTRA_libharfbuzz_fuzzing_la_DEPENDENCIES = $(EXTRA_libharfbuzz_la_DEPENDENCIES)
 CLEANFILES += libharfbuzz-fuzzing.la
@@ -155,9 +201,10 @@
 else
 lib_LTLIBRARIES += libharfbuzz-icu.la
 libharfbuzz_icu_la_SOURCES = $(HB_ICU_sources)
-libharfbuzz_icu_la_CPPFLAGS = $(ICU_CFLAGS)
-libharfbuzz_icu_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
+libharfbuzz_icu_la_CPPFLAGS = $(HBCFLAGS) $(ICU_CFLAGS)
+libharfbuzz_icu_la_LDFLAGS = $(base_link_flags) $(export_symbols_icu)
 libharfbuzz_icu_la_LIBADD = $(ICU_LIBS) libharfbuzz.la
+EXTRA_libharfbuzz_icu_la_DEPENDENCIES = $(harfbuzz_icu_def_dependency)
 pkginclude_HEADERS += $(HB_ICU_headers)
 pkgconfig_DATA += harfbuzz-icu.pc
 endif
@@ -166,13 +213,15 @@
 
 if HAVE_GOBJECT
 lib_LTLIBRARIES += libharfbuzz-gobject.la
-libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_sources)
-nodist_libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_ENUM_sources)
-libharfbuzz_gobject_la_CPPFLAGS = $(GOBJECT_CFLAGS)
-libharfbuzz_gobject_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
+libharfbuzz_gobject_la_LINK = $(chosen_linker) $(libharfbuzz_gobject_la_LDFLAGS)
+libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_DIST_sources)
+nodist_libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_NODIST_sources)
+libharfbuzz_gobject_la_CPPFLAGS = $(HBCFLAGS) $(HBNOLIBCXXFLAGS) $(GOBJECT_CFLAGS)
+libharfbuzz_gobject_la_LDFLAGS = $(base_link_flags)
 libharfbuzz_gobject_la_LIBADD = $(GOBJECT_LIBS) libharfbuzz.la
-pkginclude_HEADERS += $(HB_GOBJECT_headers)
-nodist_pkginclude_HEADERS += $(HB_GOBJECT_ENUM_headers)
+EXTRA_libharfbuzz_gobject_la_DEPENDENCIES = $(harfbuzz_gobject_def_dependency)
+pkginclude_HEADERS += $(HB_GOBJECT_DIST_headers)
+nodist_pkginclude_HEADERS += $(HB_GOBJECT_NODIST_headers)
 pkgconfig_DATA += harfbuzz-gobject.pc
 
 BUILT_SOURCES += \
@@ -212,23 +261,27 @@
 CLEANFILES += $(pkgconfig_DATA)
 
 
-CLEANFILES += harfbuzz.def
+DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def
+if HAVE_GOBJECT
+DEF_FILES += harfbuzz-gobject.def
+endif
+check: $(DEF_FILES) # For check-symbols.sh
+CLEANFILES += $(DEF_FILES)
 harfbuzz.def: $(HBHEADERS) $(HBNODISTHEADERS)
-	$(AM_V_GEN) (echo EXPORTS; \
-	(cat $^ || echo 'hb_ERROR ()' ) | \
-	$(EGREP) '^hb_.* \(' | \
-	sed -e 's/ (.*//' | \
-	LC_ALL=C sort; \
-	echo LIBRARY libharfbuzz-0.dll; \
-	) >"$@"
-	@ ! grep -q hb_ERROR "$@" \
-	|| ($(RM) "$@"; false)
+	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@
+harfbuzz-subset.def: $(HB_SUBSET_headers)
+	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@
+harfbuzz-icu.def: $(HB_ICU_headers)
+	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@
+harfbuzz-gobject.def: $(HB_GOBJECT_headers)
+	$(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@
 
 
 GENERATORS = \
 	gen-arabic-table.py \
 	gen-indic-table.py \
 	gen-use-table.py \
+	gen-def.py \
 	$(NULL)
 EXTRA_DIST += $(GENERATORS)
 
@@ -251,21 +304,16 @@
 .PHONY: unicode-tables arabic-table indic-table use-table built-sources
 
 RAGEL_GENERATED = \
-	$(srcdir)/hb-buffer-deserialize-json.hh \
-	$(srcdir)/hb-buffer-deserialize-text.hh \
-	$(srcdir)/hb-ot-shape-complex-indic-machine.hh \
-	$(srcdir)/hb-ot-shape-complex-myanmar-machine.hh \
-	$(srcdir)/hb-ot-shape-complex-use-machine.hh \
+	$(patsubst %,$(srcdir)/%,$(HB_BASE_RAGEL_GENERATED_sources)) \
+	$(patsubst %,$(srcdir)/%,$(HB_OT_RAGEL_GENERATED_sources)) \
 	$(NULL)
 BUILT_SOURCES += $(RAGEL_GENERATED)
 EXTRA_DIST += \
-	hb-buffer-deserialize-json.rl \
-	hb-buffer-deserialize-text.rl \
-	hb-ot-shape-complex-indic-machine.rl \
-	hb-ot-shape-complex-myanmar-machine.rl \
-	hb-ot-shape-complex-use-machine.rl \
+	$(HB_BASE_RAGEL_sources) \
+	$(HB_OT_RAGEL_sources) \
 	$(NULL)
-MAINTAINERCLEANFILES += $(RAGEL_GENERATED)
+# We decided to add ragel-generated files to git...
+#MAINTAINERCLEANFILES += $(RAGEL_GENERATED)
 $(srcdir)/%.hh: $(srcdir)/%.rl
 	$(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \
 	|| ($(RM) "$@"; false)
@@ -299,26 +347,47 @@
 test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
 test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
 
-check: harfbuzz.def # For check-defs.sh
-
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
-	check-defs.sh \
+	check-externs.sh \
 	check-header-guards.sh \
 	check-includes.sh \
-	check-libstdc++.sh \
 	check-static-inits.sh \
 	check-symbols.sh \
 	$(NULL)
+TESTS += $(dist_check_SCRIPTS)
 
-check_PROGRAMS = \
-	test-ot-tag \
+if !WITH_LIBSTDCXX
+dist_check_SCRIPTS += \
+	check-libstdc++.sh \
 	$(NULL)
+endif
+
+check_PROGRAMS += \
+	dump-indic-data \
+	dump-khmer-data \
+	dump-myanmar-data \
+	dump-use-data \
+	$(NULL)
+dump_indic_data_SOURCES = dump-indic-data.cc hb-ot-shape-complex-indic-table.cc
+dump_indic_data_CPPFLAGS = $(HBCFLAGS)
+dump_indic_data_LDADD = libharfbuzz.la $(HBLIBS)
+dump_khmer_data_SOURCES = dump-khmer-data.cc hb-ot-shape-complex-indic-table.cc
+dump_khmer_data_CPPFLAGS = $(HBCFLAGS)
+dump_khmer_data_LDADD = libharfbuzz.la $(HBLIBS)
+dump_myanmar_data_SOURCES = dump-myanmar-data.cc hb-ot-shape-complex-indic-table.cc
+dump_myanmar_data_CPPFLAGS = $(HBCFLAGS)
+dump_myanmar_data_LDADD = libharfbuzz.la $(HBLIBS)
+dump_use_data_SOURCES = dump-use-data.cc hb-ot-shape-complex-use-table.cc
+dump_use_data_CPPFLAGS = $(HBCFLAGS)
+dump_use_data_LDADD = libharfbuzz.la $(HBLIBS)
+
+check_PROGRAMS += test-ot-tag
+TESTS += test-ot-tag
 test_ot_tag_SOURCES = hb-ot-tag.cc
 test_ot_tag_CPPFLAGS = $(HBCFLAGS) -DMAIN
 test_ot_tag_LDADD = libharfbuzz.la $(HBLIBS)
 
-TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS)
 TESTS_ENVIRONMENT = \
 	srcdir="$(srcdir)" \
 	MAKE="$(MAKE) $(AM_MAKEFLAGS)" \
@@ -355,10 +424,8 @@
 	$(HBHEADERS) \
 	$(HBNODISTHEADERS) \
 	$(HBSOURCES) \
-	$(HB_GOBJECT_ENUM_sources) \
-	$(HB_GOBJECT_ENUM_headers) \
 	$(HB_GOBJECT_sources) \
-	$(HB_GOBJECT_STRUCTS_headers) \
+	$(HB_GOBJECT_headers) \
 	$(NULL)
 
 girdir = $(datadir)/gir-1.0
diff --git a/src/Makefile.sources b/src/Makefile.sources
index db73056..cecd644 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -1,17 +1,14 @@
-NULL =
-
 # Base and default-included sources and headers
 
 HB_BASE_sources = \
 	hb-atomic-private.hh \
 	hb-blob.cc \
-	hb-buffer-deserialize-json.hh \
-	hb-buffer-deserialize-text.hh \
 	hb-buffer-private.hh \
 	hb-buffer-serialize.cc \
 	hb-buffer.cc \
-	hb-cache-private.hh \
 	hb-common.cc \
+	hb-debug.hh \
+	hb-dsalgs.hh \
 	hb-face-private.hh \
 	hb-face.cc \
 	hb-font-private.hh \
@@ -23,15 +20,19 @@
 	hb-ot-cbdt-table.hh \
 	hb-ot-cmap-table.hh \
 	hb-ot-glyf-table.hh \
+	hb-ot-hdmx-table.hh \
 	hb-ot-head-table.hh \
 	hb-ot-hhea-table.hh \
 	hb-ot-hmtx-table.hh \
+	hb-ot-kern-table.hh \
 	hb-ot-maxp-table.hh \
 	hb-ot-name-table.hh \
 	hb-ot-os2-table.hh \
+	hb-ot-post-macroman.hh \
 	hb-ot-post-table.hh \
 	hb-ot-tag.cc \
 	hb-private.hh \
+	hb-set-digest-private.hh \
 	hb-set-private.hh \
 	hb-set.cc \
 	hb-shape.cc \
@@ -41,12 +42,22 @@
 	hb-shaper-impl-private.hh \
 	hb-shaper-private.hh \
 	hb-shaper.cc \
+	hb-string-array.hh \
 	hb-unicode-private.hh \
 	hb-unicode.cc \
 	hb-utf-private.hh \
 	hb-warning.cc \
 	$(NULL)
 
+HB_BASE_RAGEL_GENERATED_sources = \
+	hb-buffer-deserialize-json.hh \
+	hb-buffer-deserialize-text.hh \
+	$(NULL)
+HB_BASE_RAGEL_sources = \
+	hb-buffer-deserialize-json.rl \
+	hb-buffer-deserialize-text.rl \
+	$(NULL)
+
 HB_BASE_headers = \
 	hb.h \
 	hb-blob.h \
@@ -65,9 +76,17 @@
 	hb-version.h \
 	$(NULL)
 
-HB_FALLBACK_sources = hb-fallback-shape.cc
+HB_FALLBACK_sources = \
+	hb-fallback-shape.cc	\
+	$(NULL)
 
 HB_OT_sources = \
+	hb-aat-layout.cc \
+	hb-aat-layout-common-private.hh \
+	hb-aat-layout-morx-table.hh \
+	hb-aat-layout-kerx-table.hh \
+	hb-aat-layout-trak-table.hh \
+	hb-aat-layout-private.hh \
 	hb-ot-base.cc \
 	hb-ot-font.cc \
 	hb-ot-layout.cc \
@@ -93,15 +112,15 @@
 	hb-ot-shape-complex-hangul.cc \
 	hb-ot-shape-complex-hebrew.cc \
 	hb-ot-shape-complex-indic.cc \
-	hb-ot-shape-complex-indic-machine.hh \
 	hb-ot-shape-complex-indic-private.hh \
 	hb-ot-shape-complex-indic-table.cc \
+	hb-ot-shape-complex-khmer-private.hh \
+	hb-ot-shape-complex-khmer.cc \
+	hb-ot-shape-complex-myanmar-private.hh \
 	hb-ot-shape-complex-myanmar.cc \
-	hb-ot-shape-complex-myanmar-machine.hh \
 	hb-ot-shape-complex-thai.cc \
 	hb-ot-shape-complex-tibetan.cc \
 	hb-ot-shape-complex-use.cc \
-	hb-ot-shape-complex-use-machine.hh \
 	hb-ot-shape-complex-use-private.hh \
 	hb-ot-shape-complex-use-table.cc \
 	hb-ot-shape-complex-private.hh \
@@ -117,6 +136,19 @@
 	hb-ot-var-mvar-table.hh \
 	$(NULL)
 
+HB_OT_RAGEL_GENERATED_sources = \
+	hb-ot-shape-complex-indic-machine.hh \
+	hb-ot-shape-complex-khmer-machine.hh \
+	hb-ot-shape-complex-myanmar-machine.hh \
+	hb-ot-shape-complex-use-machine.hh \
+	$(NULL)
+HB_OT_RAGEL_sources = \
+	hb-ot-shape-complex-indic-machine.rl \
+	hb-ot-shape-complex-khmer-machine.rl \
+	hb-ot-shape-complex-myanmar-machine.rl \
+	hb-ot-shape-complex-use-machine.rl \
+	$(NULL)
+
 HB_OT_headers = \
 	hb-ot.h \
 	hb-ot-font.h \
@@ -157,8 +189,26 @@
 HB_ICU_sources = hb-icu.cc
 HB_ICU_headers = hb-icu.h
 
-HB_GOBJECT_sources = hb-gobject-structs.cc
-HB_GOBJECT_STRUCTS_headers = hb-gobject-structs.h
-HB_GOBJECT_headers = hb-gobject.h $(HB_GOBJECT_STRUCTS_headers)
+# Sources for libharfbuzz-subset
+HB_SUBSET_sources = \
+	hb-subset.cc \
+	hb-subset-glyf.cc \
+	hb-subset-input.cc \
+	hb-subset-plan.cc \
+	$(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
+HB_GOBJECT_DIST_headers = hb-gobject.h hb-gobject-structs.h
 HB_GOBJECT_ENUM_sources = hb-gobject-enums.cc
 HB_GOBJECT_ENUM_headers = hb-gobject-enums.h
+HB_GOBJECT_NODIST_sources = $(HB_GOBJECT_ENUM_sources)
+HB_GOBJECT_NODIST_headers = $(HB_GOBJECT_ENUM_headers)
+HB_GOBJECT_sources = $(HB_GOBJECT_DIST_sources) $(HB_GOBJECT_NODIST_sources)
+HB_GOBJECT_headers = $(HB_GOBJECT_DIST_headers) $(HB_GOBJECT_NODIST_headers)
diff --git a/src/check-c-linkage-decls.sh b/src/check-c-linkage-decls.sh
index b10310f..8234abc 100755
--- a/src/check-c-linkage-decls.sh
+++ b/src/check-c-linkage-decls.sh
@@ -7,18 +7,17 @@
 stat=0
 
 test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
-test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
-
+test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.cc'`
 
 for x in $HBHEADERS; do
-	test -f $srcdir/$x && x=$srcdir/$x
+	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
 	if ! grep -q HB_BEGIN_DECLS "$x" || ! grep -q HB_END_DECLS "$x"; then
 		echo "Ouch, file $x does not have HB_BEGIN_DECLS / HB_END_DECLS, but it should"
 		stat=1
 	fi
 done
 for x in $HBSOURCES; do
-	test -f $srcdir/$x && x=$srcdir/$x
+	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
 	if grep -q HB_BEGIN_DECLS "$x" || grep -q HB_END_DECLS "$x"; then
 		echo "Ouch, file $x has HB_BEGIN_DECLS / HB_END_DECLS, but it shouldn't"
 		stat=1
diff --git a/src/check-defs.sh b/src/check-defs.sh
deleted file mode 100755
index 65a2467..0000000
--- a/src/check-defs.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh
-
-LC_ALL=C
-export LC_ALL
-
-test -z "$srcdir" && srcdir=.
-test -z "$MAKE" && MAKE=make
-stat=0
-
-if which nm 2>/dev/null >/dev/null; then
-	:
-else
-	echo "check-defs.sh: 'nm' not found; skipping test"
-	exit 77
-fi
-
-defs="harfbuzz.def"
-$MAKE $defs > /dev/null
-tested=false
-for def in $defs; do
-	lib=`echo "$def" | sed 's/[.]def$//;s@.*/@@'`
-	so=.libs/lib${lib}.so
-
-	EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| llvm_' | cut -d' ' -f3`"
-
-	if test -f "$so"; then
-
-		echo "Checking that $so has the same symbol list as $def"
-		{
-			echo EXPORTS
-			echo "$EXPORTED_SYMBOLS"
-			# cheat: copy the last line from the def file!
-			tail -n1 "$def"
-		} | diff "$def" - >&2 || stat=1
-
-		tested=true
-	fi
-done
-if ! $tested; then
-	echo "check-defs.sh: libharfbuzz shared library not found; skipping test"
-	exit 77
-fi
-
-exit $stat
diff --git a/src/check-externs.sh b/src/check-externs.sh
new file mode 100755
index 0000000..a6de375
--- /dev/null
+++ b/src/check-externs.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+test -z "$srcdir" && srcdir=.
+stat=0
+
+test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
+test "x$EGREP" = x && EGREP='grep -E'
+
+
+echo 'Checking that all public symbols are exported with HB_EXTERN'
+
+for x in $HBHEADERS; do
+	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
+	$EGREP -B1 -n '^hb_' /dev/null "$x" |
+	$EGREP -v '(^--|:hb_|-HB_EXTERN )' -A1
+done |
+grep . >&2 && stat=1
+
+exit $stat
diff --git a/src/check-header-guards.sh b/src/check-header-guards.sh
index 09c5ea8..b67640f 100755
--- a/src/check-header-guards.sh
+++ b/src/check-header-guards.sh
@@ -6,11 +6,11 @@
 test -z "$srcdir" && srcdir=.
 stat=0
 
-test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
+test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h' ! -name 'hb-gobject-structs.h'`
 test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'`
 
 for x in $HBHEADERS $HBSOURCES; do
-	test -f "$srcdir/$x" && x="$srcdir/$x"
+	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
 	echo "$x" | grep -q '[^h]$' && continue;
 	xx=`echo "$x" | sed 's@.*/@@'`
 	tag=`echo "$xx" | tr 'a-z.-' 'A-Z_'`
diff --git a/src/check-includes.sh b/src/check-includes.sh
index 902f235..fd565da 100755
--- a/src/check-includes.sh
+++ b/src/check-includes.sh
@@ -13,7 +13,7 @@
 echo 'Checking that public header files #include "hb-common.h" or "hb.h" first (or none)'
 
 for x in $HBHEADERS; do
-	test -f "$srcdir/$x" && x="$srcdir/$x"
+	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
 	grep '#.*\<include\>' "$x" /dev/null | head -n 1
 done |
 grep -v '"hb-common[.]h"' |
@@ -26,7 +26,7 @@
 echo 'Checking that source files #include "hb-*private.hh" first (or none)'
 
 for x in $HBSOURCES; do
-	test -f "$srcdir/$x" && x="$srcdir/$x"
+	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
 	grep '#.*\<include\>' "$x" /dev/null | grep -v 'include _' | head -n 1
 done |
 grep -v '"hb-.*private[.]hh"' |
@@ -34,7 +34,7 @@
 grep . >&2 && stat=1
 
 
-echo 'Checking that there is no #include <hb.*.h>'
+echo 'Checking that there is no #include <hb-*.h>'
 for x in $HBHEADERS $HBSOURCES; do
 	test -f "$srcdir/$x" && x="$srcdir/$x"
 	grep '#.*\<include\>.*<.*hb' "$x" /dev/null >&2 && stat=1
diff --git a/src/check-libstdc++.sh b/src/check-libstdc++.sh
index b541828..ce0bdab 100755
--- a/src/check-libstdc++.sh
+++ b/src/check-libstdc++.sh
@@ -4,27 +4,37 @@
 export LC_ALL
 
 test -z "$srcdir" && srcdir=.
+test -z "$libs" && libs=.libs
 stat=0
 
 
 if which ldd 2>/dev/null >/dev/null; then
-	:
+	LDD=ldd
 else
-	echo "check-libstdc++.sh: 'ldd' not found; skipping test"
-	exit 77
+	# macOS specific tool
+	if which otool 2>/dev/null >/dev/null; then
+		LDD="otool -L"
+	else
+		echo "check-libstdc++.sh: 'ldd' not found; skipping test"
+		exit 77
+	fi
 fi
 
 tested=false
-for suffix in so dylib; do
-	so=.libs/libharfbuzz.$suffix
-	if ! test -f "$so"; then continue; fi
+# harfbuzz-icu links to libstdc++ because icu does.
+# harfbuzz-subset uses libstdc++.
+for soname in harfbuzz harfbuzz-gobject; do
+	for suffix in so dylib; do
+		so=$libs/lib$soname.$suffix
+		if ! test -f "$so"; then continue; fi
 
-	echo "Checking that we are not linking to libstdc++ or libc++"
-	if ldd $so | grep 'libstdc[+][+]\|libc[+][+]'; then
-		echo "Ouch, linked to libstdc++ or libc++"
-		stat=1
-	fi
-	tested=true
+		echo "Checking that we are not linking to libstdc++ or libc++ in $so"
+		if $LDD $so | grep 'libstdc[+][+]\|libc[+][+]'; then
+			echo "Ouch, linked to libstdc++ or libc++"
+			stat=1
+		fi
+		tested=true
+	done
 done
 if ! $tested; then
 	echo "check-libstdc++.sh: libharfbuzz shared library not found; skipping test"
diff --git a/src/check-static-inits.sh b/src/check-static-inits.sh
index 1446fa7..71551cb 100755
--- a/src/check-static-inits.sh
+++ b/src/check-static-inits.sh
@@ -4,6 +4,7 @@
 export LC_ALL
 
 test -z "$srcdir" && srcdir=.
+test -z "$libs" && libs=.libs
 stat=0
 
 
@@ -14,7 +15,7 @@
 	exit 77
 fi
 
-OBJS=.libs/*.o
+OBJS=$libs/*.o
 if test "x`echo $OBJS`" = "x$OBJS" 2>/dev/null >/dev/null; then
 	echo "check-static-inits.sh: object files not found; skipping test"
 	exit 77
diff --git a/src/check-symbols.sh b/src/check-symbols.sh
index ba09ba1..bfc93b3 100755
--- a/src/check-symbols.sh
+++ b/src/check-symbols.sh
@@ -4,8 +4,10 @@
 export LC_ALL
 
 test -z "$srcdir" && srcdir=.
+test -z "$libs" && libs=.libs
 stat=0
 
+IGNORED_SYMBOLS='_fini\|_init\|_fdata\|_ftext\|_fbss\|__bss_start\|__bss_start__\|__bss_end__\|_edata\|_end\|_bss_end__\|__end__\|__gcov_flush\|llvm_.*'
 
 if which nm 2>/dev/null >/dev/null; then
 	:
@@ -14,29 +16,46 @@
 	exit 77
 fi
 
-echo "Checking that we are not exposing internal symbols"
 tested=false
-for suffix in so dylib; do
-	so=.libs/libharfbuzz.$suffix
-	if ! test -f "$so"; then continue; fi
+for soname in harfbuzz harfbuzz-subset harfbuzz-icu harfbuzz-gobject; do
+	for suffix in so dylib; do
+		so=$libs/lib$soname.$suffix
+		if ! test -f "$so"; then continue; fi
 
-	EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| ___gcov_flush\>\| llvm_\| _llvm_' | cut -d' ' -f3`"
+		# On macOS, C symbols are prefixed with _
+		symprefix=
+		if test $suffix = dylib; then symprefix=_; fi
 
-	prefix=`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
+		EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`"
 
-	# On mac, C symbols are prefixed with _
-	if test $suffix = dylib; then prefix="_$prefix"; fi
+		prefix=$symprefix`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
 
-	echo "Processing $so"
-	if echo "$EXPORTED_SYMBOLS" | grep -v "^${prefix}_"; then
-		echo "Ouch, internal symbols exposed"
-		stat=1
-	fi
+		echo
+		echo "Checking that $so does not expose internal symbols"
+		if echo "$EXPORTED_SYMBOLS" | grep -v "^${prefix}\(_\|$\)"; then
+			echo "Ouch, internal symbols exposed"
+			stat=1
+		fi
 
-	tested=true
+		def=$soname.def
+		if ! test -f "$def"; then
+			echo "'$def' not found; skipping"
+		else
+			echo
+			echo "Checking that $so has the same symbol list as $def"
+			{
+				echo EXPORTS
+				echo "$EXPORTED_SYMBOLS" | sed -e "s/^${symprefix}hb/hb/g"
+				# cheat: copy the last line from the def file!
+				tail -n1 "$def"
+			} | c++filt | diff "$def" - >&2 || stat=1
+		fi
+
+		tested=true
+	done
 done
 if ! $tested; then
-	echo "check-symbols.sh: no shared library found; skipping test"
+	echo "check-symbols.sh: no shared libraries found; skipping test"
 	exit 77
 fi
 
diff --git a/src/dev-run.sh b/src/dev-run.sh
new file mode 100755
index 0000000..3b2257b
--- /dev/null
+++ b/src/dev-run.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+# Suggested setup to use the script:
+#  (on the root of the project)
+#  $ NOCONFIGURE=1 ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
+#  $ mkdir build && cd build && ../configure && make -j5 && cd ..
+#  $ src/dev-run.sh [FONT-FILE] [TEXT]
+#
+# Or, using cmake:
+#  $ cmake -DHB_CHECK=ON -Bbuild -H. -GNinja && ninja -Cbuild
+#  $ src/dev-run.sh [FONT-FILE] [TEXT]
+#
+
+[ $# = 0 ] && echo Usage: "src/dev-run.sh [FONT-FILE] [TEXT]" && exit
+command -v entr >/dev/null 2>&1 || { echo >&2 "This script needs `entr` be installed"; exit 1; }
+
+GDB=gdb
+# if gdb doesn't exist, hopefully lldb exist
+command -v $GDB >/dev/null 2>&1 || export GDB="lldb"
+
+[ -f 'build/build.ninja' ] && CMAKENINJA=TRUE
+# or "fswatch -0 . -e build/ -e .git"
+find src/ | entr printf '\0' | while read -d ""; do
+	clear
+	echo '===================================================='
+	if [[ $CMAKENINJA ]]; then
+		ninja -Cbuild hb-shape hb-view && {
+			build/hb-shape $@
+			build/hb-view $@
+		}
+	else
+		make -Cbuild/src -j5 -s lib && {
+			build/util/hb-shape $@
+			build/util/hb-view $@
+		}
+	fi
+done
+
+read -n 1 -p "[T]est, [D]ebug, [R]estart, [Q]uit?" answer
+case "$answer" in
+t|T )
+	if [[ $CMAKENINJA ]]; then
+		CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=5 ninja -Cbuild test
+	else
+		make -Cbuild -j5 check && .ci/fail.sh
+	fi
+;;
+d|D )
+	if [[ $CMAKENINJA ]]; then
+		echo "Not supported on cmake builds yet"
+	else
+		build/libtool --mode=execute $GDB build/util/hb-shape $@
+	fi
+;;
+r|R )
+	src/dev-run.sh $@
+;;
+* )
+	exit
+;;
+esac
diff --git a/src/dump-indic-data.cc b/src/dump-indic-data.cc
new file mode 100644
index 0000000..d574138
--- /dev/null
+++ b/src/dump-indic-data.cc
@@ -0,0 +1,43 @@
+/*
+ * 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-shape-complex-indic-private.hh"
+
+int
+main (void)
+{
+  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
+  {
+    hb_glyph_info_t info;
+    info.codepoint = u;
+    set_indic_properties (info);
+    if (info.indic_category() != INDIC_SYLLABIC_CATEGORY_OTHER ||
+	info.indic_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE)
+      printf("U+%04X	%u	%u\n", u,
+	     info.indic_category(),
+	     info.indic_position());
+  }
+}
diff --git a/src/dump-khmer-data.cc b/src/dump-khmer-data.cc
new file mode 100644
index 0000000..7dd09b2
--- /dev/null
+++ b/src/dump-khmer-data.cc
@@ -0,0 +1,43 @@
+/*
+ * 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-shape-complex-khmer-private.hh"
+
+int
+main (void)
+{
+  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
+  {
+    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());
+  }
+}
diff --git a/src/dump-myanmar-data.cc b/src/dump-myanmar-data.cc
new file mode 100644
index 0000000..2df9cd9
--- /dev/null
+++ b/src/dump-myanmar-data.cc
@@ -0,0 +1,43 @@
+/*
+ * 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-shape-complex-myanmar-private.hh"
+
+int
+main (void)
+{
+  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
+  {
+    hb_glyph_info_t info;
+    info.codepoint = u;
+    set_myanmar_properties (info);
+    if (info.myanmar_category() != INDIC_SYLLABIC_CATEGORY_OTHER ||
+	info.myanmar_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE)
+      printf("U+%04X	%u	%u\n", u,
+	     info.myanmar_category(),
+	     info.myanmar_position());
+  }
+}
diff --git a/src/dump-use-data.cc b/src/dump-use-data.cc
new file mode 100644
index 0000000..0e64688
--- /dev/null
+++ b/src/dump-use-data.cc
@@ -0,0 +1,38 @@
+/*
+ * 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-shape-complex-use-private.hh"
+
+int
+main (void)
+{
+  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
+  {
+    unsigned int category = hb_use_get_category (u);
+    if (category != USE_O)
+      printf("U+%04X	%u\n", u, category);
+  }
+}
diff --git a/src/gen-arabic-table.py b/src/gen-arabic-table.py
index 308435f..59bd760 100755
--- a/src/gen-arabic-table.py
+++ b/src/gen-arabic-table.py
@@ -134,7 +134,7 @@
 		for (start,end) in ranges:
 			if p not in [start>>page_bits, end>>page_bits]: continue
 			offset = "joining_offset_0x%04xu" % start
-			print "      if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return joining_table[u - 0x%04Xu + %s];" % (start, end, start, offset)
+			print "      if (hb_in_range<hb_codepoint_t> (u, 0x%04Xu, 0x%04Xu)) return joining_table[u - 0x%04Xu + %s];" % (start, end, start, offset)
 		print "      break;"
 		print ""
 	print "    default:"
diff --git a/src/gen-def.py b/src/gen-def.py
new file mode 100755
index 0000000..1673537
--- /dev/null
+++ b/src/gen-def.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+
+import io, os, re, sys
+
+headers_content = []
+for h in os.environ["headers"].split (' '):
+	if h.endswith (".h"):
+		with io.open(h, encoding='utf8') as f: headers_content.append (f.read ())
+
+result = """EXPORTS
+%s
+LIBRARY lib%s-0.dll""" % (
+	"\n".join (sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M))),
+	sys.argv[1].replace ('.def', '')
+)
+
+with open (sys.argv[1], "w") as f: f.write (result)
diff --git a/src/gen-indic-table.py b/src/gen-indic-table.py
index 3016cd0..735b901 100755
--- a/src/gen-indic-table.py
+++ b/src/gen-indic-table.py
@@ -229,13 +229,13 @@
 pages = set([u>>page_bits for u in starts+ends+singles.keys()])
 for p in sorted(pages):
 	print "    case 0x%0Xu:" % p
-	for (start,end) in zip (starts, ends):
-		if p not in [start>>page_bits, end>>page_bits]: continue
-		offset = "indic_offset_0x%04xu" % start
-		print "      if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return indic_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
 	for u,d in singles.items ():
 		if p != u>>page_bits: continue
 		print "      if (unlikely (u == 0x%04Xu)) return _(%s,%s);" % (u, short[0][d[0]], short[1][d[1]])
+	for (start,end) in zip (starts, ends):
+		if p not in [start>>page_bits, end>>page_bits]: continue
+		offset = "indic_offset_0x%04xu" % start
+		print "      if (hb_in_range<hb_codepoint_t> (u, 0x%04Xu, 0x%04Xu)) return indic_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
 	print "      break;"
 	print ""
 print "    default:"
diff --git a/src/gen-use-table.py b/src/gen-use-table.py
index a922c92..0681725 100755
--- a/src/gen-use-table.py
+++ b/src/gen-use-table.py
@@ -44,6 +44,7 @@
 # TODO Characters that are not in Unicode Indic files, but used in USE
 data[0][0x034F] = defaults[0]
 data[0][0x2060] = defaults[0]
+data[0][0x20F0] = defaults[0]
 for u in range (0xFE00, 0xFE0F + 1):
 	data[0][u] = defaults[0]
 
@@ -117,6 +118,7 @@
 	'Top_And_Right',
 	'Top_And_Left',
 	'Top_And_Left_And_Right',
+	'Bottom_And_Left',
 	'Bottom_And_Right',
 	'Top_And_Bottom_And_Right',
 	'Overstruck',
@@ -153,7 +155,7 @@
 def is_BASE_IND(U, UISC, UGC):
 	#SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
 	return (UISC in [Consonant_Dead, Modifying_Letter] or
-		(UGC == Po and not U in [0x104E, 0x2022]) or
+		(UGC == Po and not U in [0x104E, 0x2022, 0x11A3F, 0x11A45]) or
 		False # SPEC-DRAFT-OUTDATED! U == 0x002D
 		)
 def is_BASE_NUM(U, UISC, UGC):
@@ -177,6 +179,8 @@
 def is_CONS_SUB(U, UISC, UGC):
 	#SPEC-DRAFT return UISC == Consonant_Subjoined
 	return UISC == Consonant_Subjoined and UGC != Lo
+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]
 def is_HALANT_NUM(U, UISC, UGC):
@@ -198,9 +202,7 @@
 def is_Reserved(U, UISC, UGC):
 	return UGC == 'Cn'
 def is_REPHA(U, UISC, UGC):
-	#return UISC == Consonant_Preceding_Repha
-	#SPEC-OUTDATED hack to categorize Consonant_With_Stacker and Consonant_Prefixed
-	return UISC in [Consonant_Preceding_Repha, Consonant_With_Stacker, Consonant_Prefixed]
+	return UISC in [Consonant_Preceding_Repha, Consonant_Prefixed]
 def is_SYM(U, UISC, UGC):
 	if U == 0x25CC: return False #SPEC-DRAFT
 	#SPEC-DRAFT return UGC in [So, Sc] or UISC == Symbol_Letter
@@ -210,11 +212,13 @@
 def is_VARIATION_SELECTOR(U, UISC, UGC):
 	return 0xFE00 <= U <= 0xFE0F
 def is_VOWEL(U, UISC, UGC):
+	# https://github.com/roozbehp/unicode-data/issues/6
 	return (UISC == Pure_Killer or
-		(UGC != Lo and UISC in [Vowel, Vowel_Dependent]))
+		(UGC != Lo and UISC in [Vowel, Vowel_Dependent] and U not in [0xAA29]))
 def is_VOWEL_MOD(U, UISC, UGC):
+	# https://github.com/roozbehp/unicode-data/issues/6
 	return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or
-		(UGC != Lo and UISC == Bindu))
+		(UGC != Lo and (UISC == Bindu or U in [0xAA29])))
 
 use_mapping = {
 	'B':	is_BASE,
@@ -227,6 +231,7 @@
 	'M':	is_CONS_MED,
 	'CM':	is_CONS_MOD,
 	'SUB':	is_CONS_SUB,
+	'CS':	is_CONS_WITH_STACKER,
 	'H':	is_HALANT,
 	'HN':	is_HALANT_NUM,
 	'ZWNJ':	is_ZWNJ,
@@ -250,7 +255,7 @@
 	},
 	'M': {
 		'Abv': [Top],
-		'Blw': [Bottom],
+		'Blw': [Bottom, Bottom_And_Left],
 		'Pst': [Right],
 		'Pre': [Left],
 	},
@@ -292,12 +297,23 @@
 		if U == 0x17DD: UISC = Vowel_Dependent
 		if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark
 
+		# TODO: https://github.com/harfbuzz/harfbuzz/pull/627
+		if 0x1BF2 <= U <= 0x1BF3: UISC = Nukta; UIPC = Bottom
+
 		# TODO: U+1CED should only be allowed after some of
 		# the nasalization marks, maybe only for U+1CE9..U+1CF1.
 		if U == 0x1CED: UISC = Tone_Mark
 
-		evals = [(k, v(U,UISC,UGC)) for k,v in items]
-		values = [k for k,v in evals if v]
+		# TODO: https://github.com/harfbuzz/harfbuzz/issues/525
+		if U == 0x1A7F: UISC = Consonant_Final; UIPC = Bottom
+
+		# TODO: https://github.com/harfbuzz/harfbuzz/pull/609
+		if U == 0x20F0: UISC = Cantillation_Mark; UIPC = Top
+
+		# TODO: https://github.com/harfbuzz/harfbuzz/pull/626
+		if U == 0xA8B4: UISC = Consonant_Medial
+
+		values = [k for k,v in items if v(U,UISC,UGC)]
 		assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values)
 		USE = values[0]
 
@@ -336,12 +352,6 @@
 defaults = ('O', 'No_Block')
 data = map_to_use(data)
 
-# Remove the outliers
-singles = {}
-for u in [0x034F, 0x25CC, 0x1107F]:
-	singles[u] = data[u]
-	del data[u]
-
 print "/* == Start of generated table == */"
 print "/*"
 print " * The following table is generated by running:"
@@ -439,20 +449,17 @@
 print "}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy)
 print
 print "USE_TABLE_ELEMENT_TYPE"
-print "hb_use_get_categories (hb_codepoint_t u)"
+print "hb_use_get_category (hb_codepoint_t u)"
 print "{"
 print "  switch (u >> %d)" % page_bits
 print "  {"
-pages = set([u>>page_bits for u in starts+ends+singles.keys()])
+pages = set([u>>page_bits for u in starts+ends])
 for p in sorted(pages):
 	print "    case 0x%0Xu:" % p
 	for (start,end) in zip (starts, ends):
 		if p not in [start>>page_bits, end>>page_bits]: continue
 		offset = "use_offset_0x%04xu" % start
-		print "      if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return use_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
-	for u,d in singles.items ():
-		if p != u>>page_bits: continue
-		print "      if (unlikely (u == 0x%04Xu)) return %s;" % (u, d[0])
+		print "      if (hb_in_range<hb_codepoint_t> (u, 0x%04Xu, 0x%04Xu)) return use_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
 	print "      break;"
 	print ""
 print "    default:"
diff --git a/src/harfbuzz-config.cmake.in b/src/harfbuzz-config.cmake.in
new file mode 100644
index 0000000..87b1572
--- /dev/null
+++ b/src/harfbuzz-config.cmake.in
@@ -0,0 +1,82 @@
+# Set these variables so that the `${prefix}/lib` expands to something we can
+# remove.
+set(_harfbuzz_remove_string "REMOVE_ME")
+set(exec_prefix "${_harfbuzz_remove_string}")
+set(prefix "${_harfbuzz_remove_string}")
+
+# Compute the installation prefix by stripping components from our current
+# location.
+get_filename_component(_harfbuzz_prefix "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY)
+get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY)
+set(_harfbuzz_libdir "@libdir@")
+string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_libdir "${_harfbuzz_libdir}")
+set(_harfbuzz_libdir_iter "${_harfbuzz_libdir}")
+while (_harfbuzz_libdir_iter)
+  get_filename_component(_harfbuzz_libdir_iter "${_harfbuzz_libdir_iter}" DIRECTORY)
+  get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY)
+endwhile ()
+unset(_harfbuzz_libdir_iter)
+
+# Get the include subdir.
+set(_harfbuzz_includedir "@includedir@")
+string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_includedir "${_harfbuzz_includedir}")
+
+# Extract version information from libtool.
+set(_harfbuzz_version_info "@HB_LIBTOOL_VERSION_INFO@")
+string(REPLACE ":" ";" _harfbuzz_version_info "${_harfbuzz_version_info}")
+list(GET _harfbuzz_version_info 0
+  _harfbuzz_current)
+list(GET _harfbuzz_version_info 1
+  _harfbuzz_revision)
+list(GET _harfbuzz_version_info 2
+  _harfbuzz_age)
+unset(_harfbuzz_version_info)
+
+if (APPLE)
+  set(_harfbuzz_lib_suffix ".0${CMAKE_SHARED_LIBRARY_SUFFIX}")
+elseif (UNIX)
+  set(_harfbuzz_lib_suffix "${CMAKE_SHARED_LIBRARY_SUFFIX}.0.${_harfbuzz_current}.${_harfbuzz_revision}")
+else ()
+  # Unsupported.
+  set(harfbuzz_FOUND 0)
+endif ()
+
+# Add the libraries.
+add_library(harfbuzz::harfbuzz SHARED IMPORTED)
+set_target_properties(harfbuzz::harfbuzz PROPERTIES
+  INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
+  IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz${_harfbuzz_lib_suffix}")
+
+add_library(harfbuzz::icu SHARED IMPORTED)
+set_target_properties(harfbuzz::icu PROPERTIES
+  INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
+  INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
+  IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-icu${_harfbuzz_lib_suffix}")
+
+add_library(harfbuzz::subset SHARED IMPORTED)
+set_target_properties(harfbuzz::subset PROPERTIES
+  INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
+  INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
+  IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-subset${_harfbuzz_lib_suffix}")
+
+# Only add the gobject library if it was built.
+set(_harfbuzz_have_gobject "@have_gobject@")
+if (_harfbuzz_have_gobject)
+  add_library(harfbuzz::gobject SHARED IMPORTED)
+  set_target_properties(harfbuzz::gobject PROPERTIES
+    INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
+    INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
+    IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-gobject${_harfbuzz_lib_suffix}")
+endif ()
+
+# Clean out variables we used in our scope.
+unset(_harfbuzz_lib_suffix)
+unset(_harfbuzz_current)
+unset(_harfbuzz_revision)
+unset(_harfbuzz_age)
+unset(_harfbuzz_includedir)
+unset(_harfbuzz_libdir)
+unset(_harfbuzz_prefix)
+unset(exec_prefix)
+unset(prefix)
+unset(_harfbuzz_remove_string)
diff --git a/src/harfbuzz-subset.pc.in b/src/harfbuzz-subset.pc.in
new file mode 100644
index 0000000..5da64b3
--- /dev/null
+++ b/src/harfbuzz-subset.pc.in
@@ -0,0 +1,12 @@
+prefix=%prefix%
+exec_prefix=%exec_prefix%
+libdir=%libdir%
+includedir=%includedir%
+
+Name: harfbuzz
+Description: HarfBuzz font subsetter
+Version: %VERSION%
+
+Requires: harfbuzz
+Libs: -L${libdir} -lharfbuzz-subset
+Cflags: -I${includedir}/harfbuzz
diff --git a/src/harfbuzz.pc.in b/src/harfbuzz.pc.in
index b3e124a..661251c 100644
--- a/src/harfbuzz.pc.in
+++ b/src/harfbuzz.pc.in
@@ -8,6 +8,6 @@
 Version: %VERSION%
 
 Libs: -L${libdir} -lharfbuzz
-Libs.private: %libs_private%
+Libs.private: -lm %libs_private%
 Requires.private: %requires_private%
 Cflags: -I${includedir}/harfbuzz
diff --git a/src/hb-aat-layout-common-private.hh b/src/hb-aat-layout-common-private.hh
new file mode 100644
index 0000000..7c0dfa8
--- /dev/null
+++ b/src/hb-aat-layout-common-private.hh
@@ -0,0 +1,728 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_AAT_LAYOUT_COMMON_PRIVATE_HH
+#define HB_AAT_LAYOUT_COMMON_PRIVATE_HH
+
+#include "hb-aat-layout-private.hh"
+
+
+namespace AAT {
+
+using namespace OT;
+
+
+/*
+ * 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> (bytes, i * header.unitSize);
+  }
+  inline Type& operator [] (unsigned int i)
+  {
+    return StructAtOffset<Type> (bytes, 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> (bytes, 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 *) bytes) + (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 (bytes, header.unitSize, header.nUnits));
+  }
+
+  protected:
+  BinSearchHeader	header;
+  HBUINT8			bytes[VAR];
+  public:
+  DEFINE_SIZE_ARRAY (10, bytes);
+};
+
+
+/* TODO Move this to hb-open-type-private.hh and use it in ArrayOf, HeadlessArrayOf,
+ * and other places around the code base?? */
+template <typename Type>
+struct UnsizedArrayOf
+{
+  inline const Type& operator [] (unsigned int i) const { return arrayZ[i]; }
+  inline Type& operator [] (unsigned int i) { return arrayZ[i]; }
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!sanitize_shallow (c, count))) 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 && arrayZ[0].sanitize (c));
+
+    return_trace (true);
+  }
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
+    for (unsigned int i = 0; i < count; i++)
+      if (unlikely (!arrayZ[i].sanitize (c, base)))
+        return_trace (false);
+    return_trace (true);
+  }
+  template <typename T>
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base, T user_data) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
+    for (unsigned int i = 0; i < count; i++)
+      if (unlikely (!arrayZ[i].sanitize (c, base, user_data)))
+        return_trace (false);
+    return_trace (true);
+  }
+
+  private:
+  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));
+  }
+
+  public:
+  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> > {};
+
+/* Unsized array of offsets relative to the beginning of the array itself. */
+template <typename Type, typename OffsetType>
+struct UnsizedOffsetListOf : UnsizedOffsetArrayOf<Type, OffsetType>
+{
+  inline const Type& operator [] (unsigned int i) const
+  {
+    return this+this->arrayZ[i];
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace ((UnsizedOffsetArrayOf<Type, OffsetType>::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)));
+  }
+};
+
+
+/*
+ * Lookup Table
+ */
+
+template <typename T> struct Lookup;
+
+template <typename T>
+struct LookupFormat0
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  {
+    if (unlikely (glyph_id >= num_glyphs)) return nullptr;
+    return &arrayZ[glyph_id];
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (arrayZ.sanitize (c, c->num_glyphs));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 0 */
+  UnsizedArrayOf<T>
+		arrayZ;		/* Array of lookup values, indexed by glyph index. */
+  public:
+  DEFINE_SIZE_ARRAY (2, arrayZ);
+};
+
+
+template <typename T>
+struct LookupSegmentSingle
+{
+  inline int cmp (hb_codepoint_t g) const {
+    return g < first ? -1 : g <= last ? 0 : +1 ;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && value.sanitize (c));
+  }
+
+  GlyphID	last;		/* Last GlyphID in this segment */
+  GlyphID	first;		/* First GlyphID in this segment */
+  T		value;		/* The lookup value (only one) */
+  public:
+  DEFINE_SIZE_STATIC (4 + T::static_size);
+};
+
+template <typename T>
+struct LookupFormat2
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const T* get_value (hb_codepoint_t glyph_id) const
+  {
+    const LookupSegmentSingle<T> *v = segments.bsearch (glyph_id);
+    return v ? &v->value : nullptr;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (segments.sanitize (c));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 2 */
+  BinSearchArrayOf<LookupSegmentSingle<T> >
+		segments;	/* The actual segments. These must already be sorted,
+				 * according to the first word in each one (the last
+				 * glyph in each segment). */
+  public:
+  DEFINE_SIZE_ARRAY (8, segments);
+};
+
+template <typename T>
+struct LookupSegmentArray
+{
+  inline const T* get_value (hb_codepoint_t glyph_id, const void *base) const
+  {
+    return first <= glyph_id && glyph_id <= last ? &(base+valuesZ)[glyph_id - first] : nullptr;
+  }
+
+  inline int cmp (hb_codepoint_t g) const {
+    return g < first ? -1 : g <= last ? 0 : +1 ;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  first <= last &&
+		  valuesZ.sanitize (c, base, last - first + 1));
+  }
+
+  GlyphID	last;		/* Last GlyphID in this segment */
+  GlyphID	first;		/* First GlyphID in this segment */
+  OffsetTo<UnsizedArrayOf<T> >
+		valuesZ;	/* A 16-bit offset from the start of
+				 * the table to the data. */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+template <typename T>
+struct LookupFormat4
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const T* get_value (hb_codepoint_t glyph_id) const
+  {
+    const LookupSegmentArray<T> *v = segments.bsearch (glyph_id);
+    return v ? v->get_value (glyph_id, this) : nullptr;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (segments.sanitize (c, this));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 2 */
+  BinSearchArrayOf<LookupSegmentArray<T> >
+		segments;	/* The actual segments. These must already be sorted,
+				 * according to the first word in each one (the last
+				 * glyph in each segment). */
+  public:
+  DEFINE_SIZE_ARRAY (8, segments);
+};
+
+template <typename T>
+struct LookupSingle
+{
+  inline int cmp (hb_codepoint_t g) const { return glyph.cmp (g); }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && value.sanitize (c));
+  }
+
+  GlyphID	glyph;		/* Last GlyphID */
+  T		value;		/* The lookup value (only one) */
+  public:
+  DEFINE_SIZE_STATIC (4 + T::static_size);
+};
+
+template <typename T>
+struct LookupFormat6
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const T* get_value (hb_codepoint_t glyph_id) const
+  {
+    const LookupSingle<T> *v = entries.bsearch (glyph_id);
+    return v ? &v->value : nullptr;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (entries.sanitize (c));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 6 */
+  BinSearchArrayOf<LookupSingle<T> >
+		entries;	/* The actual entries, sorted by glyph index. */
+  public:
+  DEFINE_SIZE_ARRAY (8, entries);
+};
+
+template <typename T>
+struct LookupFormat8
+{
+  friend struct Lookup<T>;
+
+  private:
+  inline const T* get_value (hb_codepoint_t glyph_id) const
+  {
+    return firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount ? &valueArrayZ[glyph_id - firstGlyph] : nullptr;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount));
+  }
+
+  protected:
+  HBUINT16	format;		/* Format identifier--format = 6 */
+  GlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
+  HBUINT16	glyphCount;	/* Total number of glyphs (equivalent to the last
+				 * glyph minus the value of firstGlyph plus 1). */
+  UnsizedArrayOf<T>
+		valueArrayZ;	/* The lookup values (indexed by the glyph index
+				 * minus the value of firstGlyph). */
+  public:
+  DEFINE_SIZE_ARRAY (6, valueArrayZ);
+};
+
+template <typename T>
+struct Lookup
+{
+  inline const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+  {
+    switch (u.format) {
+    case 0: return u.format0.get_value (glyph_id, num_glyphs);
+    case 2: return u.format2.get_value (glyph_id);
+    case 4: return u.format4.get_value (glyph_id);
+    case 6: return u.format6.get_value (glyph_id);
+    case 8: return u.format8.get_value (glyph_id);
+    default:return nullptr;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format) {
+    case 0: return_trace (u.format0.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    case 4: return_trace (u.format4.sanitize (c));
+    case 6: return_trace (u.format6.sanitize (c));
+    case 8: return_trace (u.format8.sanitize (c));
+    default:return_trace (true);
+    }
+  }
+
+  protected:
+  union {
+  HBUINT16		format;		/* Format identifier */
+  LookupFormat0<T>	format0;
+  LookupFormat2<T>	format2;
+  LookupFormat4<T>	format4;
+  LookupFormat6<T>	format6;
+  LookupFormat8<T>	format8;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+
+/*
+ * Extended State Table
+ */
+
+template <typename T>
+struct Entry
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  {
+    TRACE_SANITIZE (this);
+    /* Note, we don't recurse-sanitize data because we don't access it.
+     * That said, in our DEFINE_SIZE_STATIC we access T::static_size,
+     * which ensures that data has a simple sanitize(). To be determined
+     * if I need to remove that as well. */
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  HBUINT16	newState;	/* Byte offset from beginning of state table
+				 * to the new state. Really?!?! Or just state
+				 * number?  The latter in morx for sure. */
+  HBUINT16	flags;		/* Table specific. */
+  T		data;		/* Optional offsets to per-glyph tables. */
+  public:
+  DEFINE_SIZE_STATIC (4 + T::static_size);
+};
+
+template <>
+struct Entry<void>
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  HBUINT16	newState;	/* Byte offset from beginning of state table to the new state. */
+  HBUINT16	flags;		/* Table specific. */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+template <typename Extra>
+struct StateTable
+{
+  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;
+  }
+
+  inline const Entry<Extra> *get_entries () const
+  {
+    return (this+entryTable).arrayZ;
+  }
+
+  inline const Entry<Extra> *get_entryZ (unsigned int state, unsigned int klass) const
+  {
+    if (unlikely (klass >= nClasses)) return nullptr;
+
+    const HBUINT16 *states = (this+stateArrayTable).arrayZ;
+    const Entry<Extra> *entries = (this+entryTable).arrayZ;
+
+    unsigned int entry = states[state * nClasses + klass];
+
+    return &entries[entry];
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c,
+			unsigned int *num_entries_out = nullptr) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!(c->check_struct (this) &&
+		    classTable.sanitize (c, this)))) return_trace (false);
+
+    const HBUINT16 *states = (this+stateArrayTable).arrayZ;
+    const Entry<Extra> *entries = (this+entryTable).arrayZ;
+
+    unsigned int num_states = 1;
+    unsigned int num_entries = 0;
+
+    unsigned int state = 0;
+    unsigned int entry = 0;
+    while (state < num_states)
+    {
+      if (unlikely (!c->check_array (states,
+				     states[0].static_size * nClasses,
+				     num_states)))
+	return_trace (false);
+      { /* Sweep new states. */
+	const HBUINT16 *stop = &states[num_states * nClasses];
+	for (const HBUINT16 *p = &states[state * nClasses]; 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)))
+	return_trace (false);
+      { /* Sweep new entries. */
+	const Entry<Extra> *stop = &entries[num_entries];
+	for (const Entry<Extra> *p = &entries[entry]; p < stop; p++)
+	  num_states = MAX<unsigned int> (num_states, p->newState + 1);
+	entry = num_entries;
+      }
+    }
+
+    if (num_entries_out)
+      *num_entries_out = num_entries;
+
+    return_trace (true);
+  }
+
+  protected:
+  HBUINT32	nClasses;	/* Number of classes, which is the number of indices
+				 * in a single line in the state array. */
+  OffsetTo<Lookup<HBUINT16>, HBUINT32>
+		classTable;	/* Offset to the class table. */
+  OffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT32>
+		stateArrayTable;/* Offset to the state array. */
+  OffsetTo<UnsizedArrayOf<Entry<Extra> >, HBUINT32>
+		entryTable;	/* Offset to the entry array. */
+
+  public:
+  DEFINE_SIZE_STATIC (16);
+};
+
+template <typename EntryData>
+struct StateTableDriver
+{
+  inline StateTableDriver (const StateTable<EntryData> &machine_,
+			   hb_buffer_t *buffer_,
+			   hb_face_t *face_) :
+	      machine (machine_),
+	      buffer (buffer_),
+	      num_glyphs (face_->get_num_glyphs ()) {}
+
+  template <typename context_t>
+  inline void drive (context_t *c)
+  {
+    hb_glyph_info_t *info = buffer->info;
+
+    if (!c->in_place)
+      buffer->clear_output ();
+
+    unsigned int state = 0;
+    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 */;
+      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)
+      {
+	/* 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);
+      }
+
+      /* Unsafe-to-break if end-of-text would kick in here. */
+      if (buffer->idx + 2 <= buffer->len)
+      {
+	const Entry<EntryData> *end_entry = machine.get_entryZ (state, 0);
+	if (c->is_actionable (this, end_entry))
+	  buffer->unsafe_to_break (buffer->idx, buffer->idx + 2);
+      }
+
+      if (unlikely (!c->transition (this, entry)))
+        break;
+
+      last_was_dont_advance = (entry->flags & context_t::DontAdvance) && buffer->max_ops-- > 0;
+
+      state = entry->newState;
+
+      if (buffer->idx == buffer->len)
+        break;
+
+      if (!last_was_dont_advance)
+        buffer->next_glyph ();
+    }
+
+    if (!c->in_place)
+    {
+      for (; buffer->idx < buffer->len;)
+        buffer->next_glyph ();
+      buffer->swap_buffers ();
+    }
+  }
+
+  public:
+  const StateTable<EntryData> &machine;
+  hb_buffer_t *buffer;
+  unsigned int num_glyphs;
+};
+
+
+
+struct hb_aat_apply_context_t :
+       hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
+{
+  inline const char *get_name (void) { return "APPLY"; }
+  template <typename T>
+  inline return_t dispatch (const T &obj) { return obj.apply (this); }
+  static return_t default_return_value (void) { return false; }
+  bool stop_sublookup_iteration (return_t r) const { return r; }
+
+  hb_font_t *font;
+  hb_face_t *face;
+  hb_buffer_t *buffer;
+  hb_sanitize_context_t sanitizer;
+
+  /* Unused. For debug tracing only. */
+  unsigned int lookup_index;
+  unsigned int debug_depth;
+
+  inline hb_aat_apply_context_t (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)
+  {
+    sanitizer.init (table);
+    sanitizer.num_glyphs = face->get_num_glyphs ();
+    sanitizer.start_processing ();
+  }
+
+  inline void set_lookup_index (unsigned int i) { lookup_index = i; }
+
+  inline ~hb_aat_apply_context_t (void)
+  {
+    sanitizer.end_processing ();
+  }
+};
+
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_COMMON_PRIVATE_HH */
diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
new file mode 100644
index 0000000..b061f11
--- /dev/null
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -0,0 +1,377 @@
+/*
+ * Copyright © 2018  Google, Inc.
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_AAT_LAYOUT_KERX_TABLE_HH
+#define HB_AAT_LAYOUT_KERX_TABLE_HH
+
+#include "hb-aat-layout-common-private.hh"
+
+namespace AAT {
+
+
+/*
+ * kerx -- Kerning
+ */
+
+#define HB_AAT_TAG_kerx HB_TAG('k','e','r','x')
+
+struct hb_glyph_pair_t
+{
+  hb_codepoint_t left;
+  hb_codepoint_t right;
+};
+
+struct KerxPair
+{
+  inline int get_kerning (void) const
+  { return value; }
+
+  inline int cmp (const hb_glyph_pair_t &o) const
+  {
+    int ret = left.cmp (o.left);
+    if (ret) return ret;
+    return right.cmp (o.right);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  GlyphID	left;
+  GlyphID	right;
+  FWORD		value;
+  HBUINT16 pad;
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct KerxSubTableFormat0
+{
+  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 bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (pairs.sanitize (c));
+  }
+
+  protected:
+  BinSearchArrayOf<KerxPair> pairs;	/* Array of kerning pairs. */
+  //FIXME: BinSearchArrayOf and its BinSearchHeader should be
+  //modified in a way to accept uint32s
+  public:
+  //DEFINE_SIZE_ARRAY (16, pairs);
+};
+
+struct KerxAction
+{
+  HBUINT16 index;
+};
+
+struct KerxSubTableFormat1
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    //TRACE_SANITIZE (this);
+    //return_trace (stateHeader.sanitize (c));
+    return false;
+  }
+
+  protected:
+  StateTable<KerxAction> stateHeader;
+  OffsetTo<ArrayOf<HBUINT16>, HBUINT32> valueTable;
+
+  public:
+  //DEFINE_SIZE_MIN (4);
+};
+
+//FIXME: 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 (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);
+};
+
+struct KerxSubTableFormat2
+{
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) 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;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (rowWidth.sanitize (c) &&
+		  leftClassTable.sanitize (c, this) &&
+		  rightClassTable.sanitize (c, this) &&
+		  array.sanitize (c, this));
+  }
+
+  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. */
+  public:
+  DEFINE_SIZE_MIN (16);
+};
+
+struct KerxSubTableFormat4
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (rowWidth.sanitize (c) &&
+		  leftClassTable.sanitize (c, this) &&
+		  rightClassTable.sanitize (c, this) &&
+		  array.sanitize (c, this));
+  }
+
+  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. */
+  public:
+  DEFINE_SIZE_MIN (16);
+};
+
+struct KerxSubTableFormat6
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    //TRACE_SANITIZE (this);
+    //return_trace ;
+    return false;
+  }
+
+  protected:
+  HBUINT32 flags;
+  HBUINT16 rowCount;
+  HBUINT16 columnCount;
+  HBUINT32 rowIndexTableOffset;
+  HBUINT32 columnIndexTableOffset;
+  HBUINT32 kerningArrayOffset;
+  HBUINT32 kerningVectorOffset;
+
+  public:
+  DEFINE_SIZE_MIN (24);
+};
+
+struct KerxSubTable
+{
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end, unsigned int format) const
+  {
+    switch (format) {
+    case 0: return u.format0.get_kerning (left, right);
+    case 2: return u.format2.get_kerning (left, right, end);
+    default:return 0;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int format) const
+  {
+    TRACE_SANITIZE (this);
+    switch (format) {
+    case 0: return_trace (u.format0.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    default:return_trace (true);
+    }
+  }
+
+  protected:
+  union {
+  KerxSubTableFormat0	format0;
+  KerxSubTableFormat2	format2;
+  KerxSubTableFormat4	format4;
+  KerxSubTableFormat6	format6;
+  } u;
+  public:
+  DEFINE_SIZE_MIN (0);
+};
+
+
+
+struct kerx
+{
+  static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
+
+  struct SubTableWrapper
+  {
+    enum coverage_flags_t {
+      COVERAGE_VERTICAL_FLAG	= 0x8000u,
+      COVERAGE_CROSSSTREAM_FLAG	= 0x4000u,
+      COVERAGE_VARIATION_FLAG	= 0x2000u,
+
+      COVERAGE_OVERRIDE_FLAG	= 0x0000u, /* Not supported. */
+
+      COVERAGE_CHECK_FLAGS	= 0x0700u, //FIXME: Where these two come from?
+      COVERAGE_CHECK_HORIZONTAL	= 0x0100u
+    };
+
+    protected:
+    HBUINT32	length;		/* Length of the subtable (including this header). */
+    HBUINT16	coverage;	/* Coverage bits. */
+    HBUINT16	format;		/* Subtable format. */
+    HBUINT32	tupleIndex;	/* The tuple index (used for variations fonts).
+				 * This value specifies which tuple this subtable covers. */
+    KerxSubTable subtable;	/* Subtable data. */
+    public:
+    inline bool is_horizontal (void) const
+    { return (coverage & COVERAGE_CHECK_FLAGS) == COVERAGE_CHECK_HORIZONTAL; }
+
+    inline bool is_override (void) const
+    { return bool (coverage & COVERAGE_OVERRIDE_FLAG); }
+
+    inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
+    { return subtable.get_kerning (left, right, end, format); }
+
+    inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
+    { return is_horizontal () ? get_kerning (left, right, end) : 0; }
+
+    inline unsigned int get_size (void) const { return length; }
+
+    inline bool sanitize (hb_sanitize_context_t *c) const
+    {
+      TRACE_SANITIZE (this);
+      return_trace (c->check_struct (this) &&
+        length >= min_size &&
+        c->check_array (this, 1, length) &&
+        subtable.sanitize (c, format));
+    }
+    DEFINE_SIZE_MIN (12);
+  };
+
+  inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const
+  {
+    int v = 0;
+    const SubTableWrapper *st = (SubTableWrapper *) data;
+    unsigned int count = 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);
+      st = (SubTableWrapper *) st;
+    }
+    return v;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    const SubTableWrapper *st = (SubTableWrapper *) data;
+    unsigned int count = nTables;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (unlikely (!st->sanitize (c)))
+	return_trace (false);
+      st = (SubTableWrapper *) st;
+    }
+
+    return_trace (true);
+  }
+
+  struct accelerator_t
+  {
+    inline void init (hb_face_t *face)
+    {
+      blob = Sanitizer<kerx>().sanitize (face->reference_table (HB_AAT_TAG_kerx));
+      table = Sanitizer<kerx>::lock_instance (blob);
+      table_length = hb_blob_get_length (blob);
+    }
+    inline void fini (void)
+    {
+      hb_blob_destroy (blob);
+    }
+
+    inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+    { return table->get_h_kerning (left, right, table_length); }
+
+    private:
+    hb_blob_t *blob;
+    const kerx *table;
+    unsigned int table_length;
+  };
+
+  protected:
+  HBUINT16		version;
+  HBUINT16		padding;
+  HBUINT32		nTables;	/* Number of subtables in the kerning table. */
+  HBUINT8		data[VAR];
+  //ArrayOf<GlyphCoverageArray> subtableGlyphCoverageArray;
+  public:
+  DEFINE_SIZE_ARRAY (8, data);
+};
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */
diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
new file mode 100644
index 0000000..4cc2824
--- /dev/null
+++ b/src/hb-aat-layout-morx-table.hh
@@ -0,0 +1,728 @@
+/*
+ * 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_MORX_TABLE_HH
+#define HB_AAT_LAYOUT_MORX_TABLE_HH
+
+#include "hb-open-type-private.hh"
+#include "hb-aat-layout-common-private.hh"
+
+#define HB_AAT_TAG_MORX HB_TAG('m','o','r','x')
+
+
+namespace AAT {
+
+using namespace OT;
+
+
+struct RearrangementSubtable
+{
+  typedef void EntryData;
+
+  struct driver_context_t
+  {
+    static const bool in_place = true;
+    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
+					 * before going to the new state. This means
+					 * that the glyph index doesn't change, even
+					 * if the glyph at that index has changed. */
+      MarkLast		= 0x2000,	/* If set, make the current glyph the last
+					 * glyph to be rearranged. */
+      Reserved		= 0x1FF0,	/* These bits are reserved and should be set to 0. */
+      Verb		= 0x000F,	/* The type of rearrangement specified. */
+    };
+
+    inline driver_context_t (const RearrangementSubtable *table) :
+	ret (false),
+	start (0), end (0) {}
+
+    inline bool is_actionable (StateTableDriver<EntryData> *driver,
+			       const Entry<EntryData> *entry)
+    {
+      return (entry->flags & Verb) && start < end;
+    }
+    inline bool transition (StateTableDriver<EntryData> *driver,
+			    const Entry<EntryData> *entry)
+    {
+      hb_buffer_t *buffer = driver->buffer;
+      unsigned int flags = entry->flags;
+
+      if (flags & MarkFirst)
+	start = buffer->idx;
+
+      if (flags & MarkLast)
+	end = MIN (buffer->idx + 1, buffer->len);
+
+      if ((flags & Verb) && start < end)
+      {
+	/* The following map has two nibbles, for start-side
+	 * and end-side. Values of 0,1,2 mean move that many
+	 * to the other side. Value of 3 means move 2 and
+	 * flip them. */
+	const unsigned char map[16] =
+	{
+	  0x00,	/* 0	no change */
+	  0x10,	/* 1	Ax => xA */
+	  0x01,	/* 2	xD => Dx */
+	  0x11,	/* 3	AxD => DxA */
+	  0x20,	/* 4	ABx => xAB */
+	  0x30,	/* 5	ABx => xBA */
+	  0x02,	/* 6	xCD => CDx */
+	  0x03,	/* 7	xCD => DCx */
+	  0x12,	/* 8	AxCD => CDxA */
+	  0x13,	/* 9	AxCD => DCxA */
+	  0x21,	/* 10	ABxD => DxAB */
+	  0x31,	/* 11	ABxD => DxBA */
+	  0x22,	/* 12	ABxCD => CDxAB */
+	  0x32,	/* 13	ABxCD => CDxBA */
+	  0x23,	/* 14	ABxCD => DCxAB */
+	  0x33,	/* 15	ABxCD => DCxBA */
+	};
+
+	unsigned int m = map[flags & Verb];
+	unsigned int l = MIN<unsigned int> (2, m >> 4);
+	unsigned int r = MIN<unsigned int> (2, m & 0x0F);
+	bool reverse_l = 3 == (m >> 4);
+	bool reverse_r = 3 == (m & 0x0F);
+
+	if (end - start >= l + r)
+	{
+	  buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len));
+	  buffer->merge_clusters (start, end);
+
+	  hb_glyph_info_t *info = buffer->info;
+	  hb_glyph_info_t buf[4];
+
+	  memcpy (buf, info + start, l * sizeof (buf[0]));
+	  memcpy (buf + 2, info + end - r, r * sizeof (buf[0]));
+
+	  if (l != r)
+	    memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0]));
+
+	  memcpy (info + start, buf + 2, r * sizeof (buf[0]));
+	  memcpy (info + end - l, buf, l * sizeof (buf[0]));
+	  if (reverse_l)
+	  {
+	    buf[0] = info[end - 1];
+	    info[end - 1] = info[end - 2];
+	    info[end - 2] = buf[0];
+	  }
+	  if (reverse_r)
+	  {
+	    buf[0] = info[start];
+	    info[start] = info[start + 1];
+	    info[start + 1] = buf[0];
+	  }
+	}
+      }
+
+      return true;
+    }
+
+    public:
+    bool ret;
+    private:
+    unsigned int start;
+    unsigned int end;
+  };
+
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    driver_context_t dc (this);
+
+    StateTableDriver<void> 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);
+    return_trace (machine.sanitize (c));
+  }
+
+  protected:
+  StateTable<EntryData>	machine;
+  public:
+  DEFINE_SIZE_STATIC (16);
+};
+
+struct ContextualSubtable
+{
+  struct EntryData
+  {
+    HBUINT16	markIndex;	/* Index of the substitution table for the
+				 * marked glyph (use 0xFFFF for none). */
+    HBUINT16	currentIndex;	/* Index of the substitution table for the
+				 * current glyph (use 0xFFFF for none). */
+    public:
+    DEFINE_SIZE_STATIC (4);
+  };
+
+  struct driver_context_t
+  {
+    static const bool in_place = true;
+    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. */
+      Reserved		= 0x3FFF,	/* These bits are reserved and should be set to 0. */
+    };
+
+    inline driver_context_t (const ContextualSubtable *table) :
+	ret (false),
+	mark_set (false),
+	mark (0),
+	subs (table+table->substitutionTables) {}
+
+    inline bool is_actionable (StateTableDriver<EntryData> *driver,
+			       const Entry<EntryData> *entry)
+    {
+      hb_buffer_t *buffer = driver->buffer;
+
+      if (buffer->idx == buffer->len && !mark_set)
+        return false;
+
+      return entry->data.markIndex != 0xFFFF || entry->data.currentIndex != 0xFFFF;
+    }
+    inline bool transition (StateTableDriver<EntryData> *driver,
+			    const Entry<EntryData> *entry)
+    {
+      hb_buffer_t *buffer = driver->buffer;
+
+      /* Looks like CoreText applies neither mark nor current substitution for
+       * end-of-text if mark was not explicitly set. */
+      if (buffer->idx == buffer->len && !mark_set)
+        return true;
+
+      if (entry->data.markIndex != 0xFFFF)
+      {
+	const Lookup<GlyphID> &lookup = subs[entry->data.markIndex];
+	hb_glyph_info_t *info = buffer->info;
+	const GlyphID *replacement = lookup.get_value (info[mark].codepoint, driver->num_glyphs);
+	if (replacement)
+	{
+	  buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
+	  info[mark].codepoint = *replacement;
+	  ret = true;
+	}
+      }
+      if (entry->data.currentIndex != 0xFFFF)
+      {
+        unsigned int idx = MIN (buffer->idx, buffer->len - 1);
+	const Lookup<GlyphID> &lookup = subs[entry->data.currentIndex];
+	hb_glyph_info_t *info = buffer->info;
+	const GlyphID *replacement = lookup.get_value (info[idx].codepoint, driver->num_glyphs);
+	if (replacement)
+	{
+	  info[idx].codepoint = *replacement;
+	  ret = true;
+	}
+      }
+
+      if (entry->flags & SetMark)
+      {
+	mark_set = true;
+	mark = buffer->idx;
+      }
+
+      return true;
+    }
+
+    public:
+    bool ret;
+    private:
+    bool mark_set;
+    unsigned int mark;
+    const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32> &subs;
+  };
+
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    driver_context_t dc (this);
+
+    StateTableDriver<EntryData> driver (machine, c->buffer, c->face);
+    driver.drive (&dc);
+
+    return_trace (dc.ret);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+
+    unsigned int num_entries = 0;
+    if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false);
+
+    unsigned int num_lookups = 0;
+
+    const Entry<EntryData> *entries = machine.get_entries ();
+    for (unsigned int i = 0; i < num_entries; i++)
+    {
+      const EntryData &data = entries[i].data;
+
+      if (data.markIndex != 0xFFFF)
+	num_lookups = MAX<unsigned int> (num_lookups, 1 + data.markIndex);
+      if (data.currentIndex != 0xFFFF)
+	num_lookups = MAX<unsigned int> (num_lookups, 1 + data.currentIndex);
+    }
+
+    return_trace (substitutionTables.sanitize (c, this, num_lookups));
+  }
+
+  protected:
+  StateTable<EntryData>	machine;
+  OffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT32>, HBUINT32>
+			substitutionTables;
+  public:
+  DEFINE_SIZE_STATIC (20);
+};
+
+struct LigatureSubtable
+{
+  struct EntryData
+  {
+    HBUINT16	ligActionIndex;	/* Index to the first ligActionTable entry
+				 * for processing this group, if indicated
+				 * by the flags. */
+    public:
+    DEFINE_SIZE_STATIC (2);
+  };
+
+  struct driver_context_t
+  {
+    static const bool in_place = false;
+    enum Flags {
+      SetComponent	= 0x8000,	/* Push this glyph onto the component stack for
+					 * eventual processing. */
+      DontAdvance	= 0x4000,	/* Leave the glyph pointer at this glyph for the
+					   next iteration. */
+      PerformAction	= 0x2000,	/* Use the ligActionIndex to process a ligature
+					 * group. */
+      Reserved		= 0x1FFF,	/* These bits are reserved and should be set to 0. */
+    };
+    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
+					 * in the ligature table in place of the marked
+					 * (i.e. currently-popped) glyph. */
+      LigActionOffset	= 0x3FFFFFFF,	/* A 30-bit value which is sign-extended to 32-bits
+					 * and added to the glyph ID, resulting in an index
+					 * into the component table. */
+    };
+
+    inline driver_context_t (const LigatureSubtable *table,
+			     hb_aat_apply_context_t *c_) :
+	ret (false),
+	c (c_),
+	ligAction (table+table->ligAction),
+	component (table+table->component),
+	ligature (table+table->ligature),
+	match_length (0) {}
+
+    inline bool is_actionable (StateTableDriver<EntryData> *driver,
+			       const Entry<EntryData> *entry)
+    {
+      return !!(entry->flags & PerformAction);
+    }
+    inline bool transition (StateTableDriver<EntryData> *driver,
+			    const Entry<EntryData> *entry)
+    {
+      hb_buffer_t *buffer = driver->buffer;
+      unsigned int flags = entry->flags;
+
+      if (flags & SetComponent)
+      {
+        if (unlikely (match_length >= ARRAY_LENGTH (match_positions)))
+	  return false;
+
+	/* Never mark same index twice, in case DontAdvance was used... */
+	if (match_length && match_positions[match_length - 1] == buffer->out_len)
+	  match_length--;
+
+	match_positions[match_length++] = buffer->out_len;
+      }
+
+      if (flags & PerformAction)
+      {
+	unsigned int end = buffer->out_len;
+	unsigned int action_idx = entry->data.ligActionIndex;
+	unsigned int action;
+	unsigned int ligature_idx = 0;
+        do
+	{
+	  if (unlikely (!match_length))
+	    return false;
+
+	  buffer->move_to (match_positions[--match_length]);
+
+	  const HBUINT32 &actionData = ligAction[action_idx];
+	  if (unlikely (!actionData.sanitize (&c->sanitizer))) return false;
+	  action = actionData;
+
+	  uint32_t uoffset = action & LigActionOffset;
+	  if (uoffset & 0x20000000)
+	    uoffset += 0xC0000000;
+	  int32_t offset = (int32_t) uoffset;
+	  unsigned int component_idx = buffer->cur().codepoint + offset;
+
+	  const HBUINT16 &componentData = component[component_idx];
+	  if (unlikely (!componentData.sanitize (&c->sanitizer))) return false;
+	  ligature_idx += componentData;
+
+	  if (action & (LigActionStore | LigActionLast))
+	  {
+	    const GlyphID &ligatureData = ligature[ligature_idx];
+	    if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false;
+	    hb_codepoint_t lig = ligatureData;
+
+	    match_positions[match_length++] = buffer->out_len;
+	    buffer->replace_glyph (lig);
+
+	    //ligature_idx = 0; // XXX Yes or no?
+	  }
+	  else
+	  {
+	    buffer->skip_glyph ();
+	    end--;
+	  }
+	  /* TODO merge_clusters / unsafe_to_break */
+
+	  action_idx++;
+	}
+	while (!(action & LigActionLast));
+	buffer->move_to (end);
+      }
+
+      return true;
+    }
+
+    public:
+    bool ret;
+    private:
+    hb_aat_apply_context_t *c;
+    const UnsizedArrayOf<HBUINT32> &ligAction;
+    const UnsizedArrayOf<HBUINT16> &component;
+    const UnsizedArrayOf<GlyphID> &ligature;
+    unsigned int match_length;
+    unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
+  };
+
+  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->face);
+    driver.drive (&dc);
+
+    return_trace (dc.ret);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    /* The rest of array sanitizations are done at run-time. */
+    return_trace (c->check_struct (this) && machine.sanitize (c) &&
+		  ligAction && component && ligature);
+  }
+
+  protected:
+  StateTable<EntryData>	machine;
+  OffsetTo<UnsizedArrayOf<HBUINT32>, HBUINT32>
+		ligAction;	/* Offset to the ligature action table. */
+  OffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT32>
+		component;	/* Offset to the component table. */
+  OffsetTo<UnsizedArrayOf<GlyphID>, HBUINT32>
+		ligature;	/* Offset to the actual ligature lists. */
+  public:
+  DEFINE_SIZE_STATIC (28);
+};
+
+struct NoncontextualSubtable
+{
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+
+    bool ret = false;
+    unsigned int num_glyphs = c->face->get_num_glyphs ();
+
+    hb_glyph_info_t *info = c->buffer->info;
+    unsigned int count = c->buffer->len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      const GlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
+      if (replacement)
+      {
+	info[i].codepoint = *replacement;
+	ret = true;
+      }
+    }
+
+    return_trace (ret);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (substitute.sanitize (c));
+  }
+
+  protected:
+  Lookup<GlyphID>	substitute;
+  public:
+  DEFINE_SIZE_MIN (2);
+};
+
+struct InsertionSubtable
+{
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    /* TODO */
+    return_trace (false);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    /* TODO */
+    return_trace (true);
+  }
+};
+
+
+struct Feature
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  HBUINT16	featureType;	/* The type of feature. */
+  HBUINT16	featureSetting;	/* The feature's setting (aka selector). */
+  HBUINT32	enableFlags;	/* Flags for the settings that this feature
+				 * and setting enables. */
+  HBUINT32	disableFlags;	/* Complement of flags for the settings that this
+				 * feature and setting disable. */
+
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+
+struct ChainSubtable
+{
+  friend struct Chain;
+
+  inline unsigned int get_size (void) const { return length; }
+  inline unsigned int get_type (void) const { return coverage & 0xFF; }
+
+  enum Type {
+    Rearrangement	= 0,
+    Contextual		= 1,
+    Ligature		= 2,
+    Noncontextual	= 4,
+    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
+  {
+    unsigned int subtable_type = get_type ();
+    TRACE_DISPATCH (this, subtable_type);
+    switch (subtable_type) {
+    case Rearrangement:		return_trace (c->dispatch (u.rearrangement));
+    case Contextual:		return_trace (c->dispatch (u.contextual));
+    case Ligature:		return_trace (c->dispatch (u.ligature));
+    case Noncontextual:		return_trace (c->dispatch (u.noncontextual));
+    case Insertion:		return_trace (c->dispatch (u.insertion));
+    default:			return_trace (c->default_return_value ());
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!length.sanitize (c) ||
+	length < min_size ||
+	!c->check_range (this, length))
+      return_trace (false);
+
+    return_trace (dispatch (c));
+  }
+
+  protected:
+  HBUINT32	length;		/* Total subtable length, including this header. */
+  HBUINT32	coverage;	/* Coverage flags and subtable type. */
+  HBUINT32	subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */
+  union {
+  RearrangementSubtable		rearrangement;
+  ContextualSubtable		contextual;
+  LigatureSubtable		ligature;
+  NoncontextualSubtable		noncontextual;
+  InsertionSubtable		insertion;
+  } u;
+  public:
+  DEFINE_SIZE_MIN (12);
+};
+
+struct Chain
+{
+  inline void apply (hb_aat_apply_context_t *c) const
+  {
+    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
+    unsigned int count = subtableCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
+      {
+	c->set_lookup_index (c->lookup_index + 1);
+	continue;
+      }
+
+      subtable->apply (c);
+      subtable = &StructAfter<ChainSubtable> (*subtable);
+
+      (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);
+
+      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
+  {
+    TRACE_SANITIZE (this);
+    if (!length.sanitize (c) ||
+	length < min_size ||
+	!c->check_range (this, length))
+      return_trace (false);
+
+    if (!c->check_array (featureZ, featureZ[0].static_size, featureCount))
+      return_trace (false);
+
+    const ChainSubtable *subtable = &StructAtOffset<ChainSubtable> (featureZ, featureZ[0].static_size * featureCount);
+    unsigned int count = subtableCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (!subtable->sanitize (c))
+	return_trace (false);
+      subtable = &StructAfter<ChainSubtable> (*subtable);
+    }
+
+    return_trace (true);
+  }
+
+  protected:
+  HBUINT32	defaultFlags;	/* The default specification for subtables. */
+  HBUINT32	length;		/* Total byte count, including this header. */
+  HBUINT32	featureCount;	/* Number of feature subtable entries. */
+  HBUINT32	subtableCount;	/* The number of subtables in the chain. */
+
+  Feature	featureZ[VAR];	/* Features. */
+  ChainSubtable	subtableX[VAR];	/* Subtables. */
+  // subtableGlyphCoverageArray if major == 3
+
+  public:
+  DEFINE_SIZE_MIN (16);
+};
+
+
+/*
+ * The 'mort'/'morx' Tables
+ */
+
+struct morx
+{
+  static const hb_tag_t tableTag = HB_AAT_TAG_MORX;
+
+  inline void apply (hb_aat_apply_context_t *c) const
+  {
+    c->set_lookup_index (0);
+    const Chain *chain = chains;
+    unsigned int count = chainCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      chain->apply (c);
+      chain = &StructAfter<Chain> (*chain);
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!version.sanitize (c) ||
+	(version.major >> (sizeof (HBUINT32) == 4 ? 1 : 0)) != 1 ||
+	!chainCount.sanitize (c))
+      return_trace (false);
+
+    const Chain *chain = chains;
+    unsigned int count = chainCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (!chain->sanitize (c, version.major))
+	return_trace (false);
+      chain = &StructAfter<Chain> (*chain);
+    }
+
+    return_trace (true);
+  }
+
+  protected:
+  FixedVersion<>version;	/* Version number of the glyph metamorphosis table.
+				 * 1 for mort, 2 or 3 for morx. */
+  HBUINT32	chainCount;	/* Number of metamorphosis chains contained in this
+				 * table. */
+  Chain		chains[VAR];	/* Chains. */
+
+  public:
+  DEFINE_SIZE_MIN (8);
+};
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */
diff --git a/src/hb-aat-layout-private.hh b/src/hb-aat-layout-private.hh
new file mode 100644
index 0000000..c1c607a
--- /dev/null
+++ b/src/hb-aat-layout-private.hh
@@ -0,0 +1,40 @@
+/*
+ * 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);
+
+#endif /* HB_AAT_LAYOUT_PRIVATE_HH */
diff --git a/src/hb-aat-layout-trak-table.hh b/src/hb-aat-layout-trak-table.hh
new file mode 100644
index 0000000..6dbd05a
--- /dev/null
+++ b/src/hb-aat-layout-trak-table.hh
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2018  Google, Inc.
+ * Copyright © 2018  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_AAT_LAYOUT_TRAK_TABLE_HH
+#define HB_AAT_LAYOUT_TRAK_TABLE_HH
+
+#include "hb-aat-layout-common-private.hh"
+#include "hb-open-type-private.hh"
+
+#define HB_AAT_TAG_trak HB_TAG('t','r','a','k')
+
+
+namespace AAT {
+
+
+struct TrackTableEntry
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  Fixed			track;		/* Track value for this record. */
+  HBUINT16		trackNameID;	/* The 'name' table index for this track */
+  OffsetTo<UnsizedArrayOf<Fixed> >
+			values;		/* Offset from start of tracking table to
+					 * per-size tracking values for this track. */
+
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct TrackData
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  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;
+  TrackTableEntry	trackTable[VAR];/* Array[nSizes] of size values. */
+
+  public:
+  DEFINE_SIZE_ARRAY (8, trackTable);
+};
+
+struct trak
+{
+  static const hb_tag_t tableTag = HB_AAT_TAG_trak;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  FixedVersion<>	version;	/* Version of the tracking table--currently
+					 * 0x00010000u for version 1.0. */
+  HBUINT16		format; 	/* Format of the tracking table */
+  OffsetTo<TrackData>	horizOffset;	/* TrackData for horizontal text */
+  OffsetTo<TrackData>	vertOffset;	/* TrackData for vertical text */
+  HBUINT16		reserved;	/* Reserved. Set to 0. */
+
+  public:
+  DEFINE_SIZE_MIN (12);
+};
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_TRAK_TABLE_HH */
diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc
new file mode 100644
index 0000000..3b967c6
--- /dev/null
+++ b/src/hb-aat-layout.cc
@@ -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
+ */
+
+#include "hb-open-type-private.hh"
+
+#include "hb-ot-layout-private.hh"
+#include "hb-ot-layout-gsubgpos-private.hh"
+
+#include "hb-aat-layout-private.hh"
+#include "hb-aat-layout-morx-table.hh"
+#include "hb-aat-layout-kerx-table.hh"
+#include "hb-aat-layout-trak-table.hh"
+
+/*
+ * mort/morx
+ */
+
+static inline const AAT::morx&
+_get_morx (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 OT::Null(AAT::morx);
+  }
+  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
+  /* XXX this doesn't call set_num_glyphs on sanitizer. */
+  const AAT::morx& morx = *(layout->morx.get ());
+  if (blob)
+    *blob = layout->morx.blob;
+  return morx;
+}
+
+static inline void
+_hb_aat_layout_create (hb_face_t *face)
+{
+  OT::Sanitizer<AAT::morx> sanitizer;
+  sanitizer.set_num_glyphs (face->get_num_glyphs ());
+  hb_blob_t *morx_blob = sanitizer.sanitize (face->reference_table (HB_AAT_TAG_MORX));
+  OT::Sanitizer<AAT::morx>::lock_instance (morx_blob);
+
+  if (0)
+  {
+    OT::Sanitizer<AAT::Lookup<OT::GlyphID> >::lock_instance (morx_blob)->get_value (1, face->get_num_glyphs ());
+  }
+}
+
+void
+hb_aat_layout_substitute (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);
+  morx.apply (&c);
+}
diff --git a/src/hb-atomic-private.hh b/src/hb-atomic-private.hh
index 100ba53..a7e9b11 100644
--- a/src/hb-atomic-private.hh
+++ b/src/hb-atomic-private.hh
@@ -70,32 +70,6 @@
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	(InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O))
 
 
-#elif !defined(HB_NO_MT) && defined(__APPLE__)
-
-#include <libkern/OSAtomic.h>
-#ifdef __MAC_OS_X_MIN_REQUIRED
-#include <AvailabilityMacros.h>
-#elif defined(__IPHONE_OS_MIN_REQUIRED)
-#include <Availability.h>
-#endif
-
-
-typedef int32_t hb_atomic_int_impl_t;
-#define HB_ATOMIC_INT_IMPL_INIT(V) (V)
-#define hb_atomic_int_impl_add(AI, V)		(OSAtomicAdd32Barrier ((V), &(AI)) - (V))
-
-#define hb_atomic_ptr_impl_get(P)		(OSMemoryBarrier (), (void *) *(P))
-#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))
-#else
-#if __ppc64__ || __x86_64__ || __aarch64__
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P))
-#else
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P))
-#endif
-#endif
-
-
 #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
 
 typedef int hb_atomic_int_impl_t;
@@ -119,18 +93,44 @@
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	( ({__machine_rw_barrier ();}), atomic_cas_ptr ((void **) (P), (void *) (O), (void *) (N)) == (void *) (O) ? true : false)
 
 
+#elif !defined(HB_NO_MT) && defined(__APPLE__)
+
+#include <libkern/OSAtomic.h>
+#ifdef __MAC_OS_X_MIN_REQUIRED
+#include <AvailabilityMacros.h>
+#elif defined(__IPHONE_OS_MIN_REQUIRED)
+#include <Availability.h>
+#endif
+
+
+typedef int32_t hb_atomic_int_impl_t;
+#define HB_ATOMIC_INT_IMPL_INIT(V) (V)
+#define hb_atomic_int_impl_add(AI, V)		(OSAtomicAdd32Barrier ((V), &(AI)) - (V))
+
+#define hb_atomic_ptr_impl_get(P)		(OSMemoryBarrier (), (void *) *(P))
+#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))
+#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))
+#else
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)	OSAtomicCompareAndSwap32Barrier ((int32_t) (void *) (O), (int32_t) (void *) (N), (int32_t*) (P))
+#endif
+#endif
+
+
 #elif !defined(HB_NO_MT) && defined(_AIX) && defined(__IBMCPP__)
 
 #include <builtins.h>
 
 
-static inline int hb_fetch_and_add(volatile int* AI, unsigned int V) {
+static inline int _hb_fetch_and_add(volatile int* AI, unsigned int V) {
   __lwsync();
   int result = __fetch_and_add(AI, V);
   __isync();
   return result;
 }
-static inline int hb_compare_and_swaplp(volatile long* P, long O, long N) {
+static inline int _hb_compare_and_swaplp(volatile long* P, long O, long N) {
   __sync();
   int result = __compare_and_swaplp (P, &O, N);
   __sync();
@@ -139,10 +139,10 @@
 
 typedef int hb_atomic_int_impl_t;
 #define HB_ATOMIC_INT_IMPL_INIT(V) (V)
-#define hb_atomic_int_impl_add(AI, V)           hb_fetch_and_add (&(AI), (V))
+#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add (&(AI), (V))
 
 #define hb_atomic_ptr_impl_get(P)               (__sync(), (void *) *(P))
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)       hb_compare_and_swaplp ((long*)(P), (long)(O), (long)(N))
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swaplp ((long*)(P), (long)(O), (long)(N))
 
 #elif !defined(HB_NO_MT)
 
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index fb48f03..b5291f6 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -26,10 +26,11 @@
 
 /* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */
 #ifndef _POSIX_C_SOURCE
-#define _POSIX_C_SOURCE 199309L
+#define _POSIX_C_SOURCE 200809L
 #endif
 
 #include "hb-private.hh"
+#include "hb-debug.hh"
 
 #include "hb-object-private.hh"
 
@@ -44,12 +45,6 @@
 #include <errno.h>
 
 
-
-#ifndef HB_DEBUG_BLOB
-#define HB_DEBUG_BLOB (HB_DEBUG+0)
-#endif
-
-
 struct hb_blob_t {
   hb_object_header_t header;
   ASSERT_POD ();
@@ -72,8 +67,8 @@
 {
   if (blob->destroy) {
     blob->destroy (blob->user_data);
-    blob->user_data = NULL;
-    blob->destroy = NULL;
+    blob->user_data = nullptr;
+    blob->destroy = nullptr;
   }
 }
 
@@ -128,6 +123,12 @@
   return blob;
 }
 
+static void
+_hb_blob_destroy (void *data)
+{
+  hb_blob_destroy ((hb_blob_t *) data);
+}
+
 /**
  * hb_blob_create_sub_blob:
  * @parent: Parent blob.
@@ -164,7 +165,32 @@
 			 MIN (length, parent->length - offset),
 			 HB_MEMORY_MODE_READONLY,
 			 hb_blob_reference (parent),
-			 (hb_destroy_func_t) hb_blob_destroy);
+			 _hb_blob_destroy);
+
+  return blob;
+}
+
+/**
+ * hb_blob_copy_writable_or_fail:
+ * @blob: A blob.
+ *
+ * Makes a writable copy of @blob.
+ *
+ * Return value: New blob, or nullptr if allocation failed.
+ *
+ * Since: 1.8.0
+ **/
+hb_blob_t *
+hb_blob_copy_writable_or_fail (hb_blob_t *blob)
+{
+  blob = hb_blob_create (blob->data,
+			 blob->length,
+			 HB_MEMORY_MODE_DUPLICATE,
+			 nullptr,
+			 nullptr);
+
+  if (unlikely (blob == hb_blob_get_empty ()))
+    blob = nullptr;
 
   return blob;
 }
@@ -188,12 +214,12 @@
 
     true, /* immutable */
 
-    NULL, /* data */
+    nullptr, /* data */
     0, /* length */
     HB_MEMORY_MODE_READONLY, /* mode */
 
-    NULL, /* user_data */
-    NULL  /* destroy */
+    nullptr, /* user_data */
+    nullptr  /* destroy */
   };
 
   return const_cast<hb_blob_t *> (&_hb_blob_nil);
@@ -221,7 +247,7 @@
  * hb_blob_destroy: (skip)
  * @blob: a blob.
  *
- * Descreases the reference count on @blob, and if it reaches zero, destroys
+ * Decreases the reference count on @blob, and if it reaches zero, destroys
  * @blob, freeing all memory, possibly calling the destroy-callback the blob
  * was created for if it has not been called already.
  *
@@ -373,7 +399,7 @@
     if (length)
       *length = 0;
 
-    return NULL;
+    return nullptr;
   }
 
   if (length)
diff --git a/src/hb-blob.h b/src/hb-blob.h
index ef3fc98..fd561f7 100644
--- a/src/hb-blob.h
+++ b/src/hb-blob.h
@@ -44,7 +44,7 @@
  *   any such possibility, MODE_DUPLICATE should be used
  *   such that HarfBuzz makes a copy immediately,
  *
- * - Use MODE_READONLY otherse, unless you really really
+ * - Use MODE_READONLY otherwise, unless you really really
  *   really know what you are doing,
  *
  * - MODE_WRITABLE is appropriate if you really made a
@@ -83,6 +83,9 @@
 			 unsigned int  length);
 
 HB_EXTERN hb_blob_t *
+hb_blob_copy_writable_or_fail (hb_blob_t *blob);
+
+HB_EXTERN hb_blob_t *
 hb_blob_get_empty (void);
 
 HB_EXTERN hb_blob_t *
diff --git a/src/hb-buffer-deserialize-json.hh b/src/hb-buffer-deserialize-json.hh
new file mode 100644
index 0000000..be374c7
--- /dev/null
+++ b/src/hb-buffer-deserialize-json.hh
@@ -0,0 +1,643 @@
+
+#line 1 "hb-buffer-deserialize-json.rl"
+/*
+ * Copyright © 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.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_BUFFER_DESERIALIZE_JSON_HH
+#define HB_BUFFER_DESERIALIZE_JSON_HH
+
+#include "hb-private.hh"
+
+
+#line 36 "hb-buffer-deserialize-json.hh"
+static const unsigned char _deserialize_json_trans_keys[] = {
+	0u, 0u, 9u, 123u, 9u, 34u, 97u, 103u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u,
+	48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u,
+	9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u,
+	120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u,
+	9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u,
+	65u, 122u, 34u, 122u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0
+};
+
+static const char _deserialize_json_key_spans[] = {
+	0, 115, 26, 7, 2, 1, 50, 49,
+	10, 117, 117, 117, 1, 50, 49, 10,
+	117, 117, 1, 1, 50, 49, 117, 117,
+	2, 1, 50, 49, 10, 117, 117, 1,
+	50, 49, 10, 117, 117, 1, 50, 49,
+	58, 89, 117, 117, 85, 115, 0
+};
+
+static const short _deserialize_json_index_offsets[] = {
+	0, 0, 116, 143, 151, 154, 156, 207,
+	257, 268, 386, 504, 622, 624, 675, 725,
+	736, 854, 972, 974, 976, 1027, 1077, 1195,
+	1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666,
+	1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069,
+	2119, 2178, 2268, 2386, 2504, 2590, 2706
+};
+
+static const char _deserialize_json_indicies[] = {
+	0, 0, 0, 0, 0, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	0, 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, 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, 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, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 2, 1, 3, 3, 3,
+	3, 3, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 3, 1, 4, 1,
+	5, 1, 6, 7, 1, 1, 8, 1,
+	9, 10, 1, 11, 1, 11, 11, 11,
+	11, 11, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 11, 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, 12, 1,
+	12, 12, 12, 12, 12, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 12,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 13, 1, 1, 14,
+	15, 15, 15, 15, 15, 15, 15, 15,
+	15, 1, 16, 17, 17, 17, 17, 17,
+	17, 17, 17, 17, 1, 18, 18, 18,
+	18, 18, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 18, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	19, 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, 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, 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, 20, 1, 21, 21, 21, 21, 21,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 21, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 3, 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, 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, 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, 22,
+	1, 18, 18, 18, 18, 18, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	18, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 19, 1, 1, 1,
+	17, 17, 17, 17, 17, 17, 17, 17,
+	17, 17, 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, 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,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 20, 1, 23,
+	1, 23, 23, 23, 23, 23, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	23, 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, 24, 1, 24, 24, 24, 24,
+	24, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 24, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	25, 1, 1, 26, 27, 27, 27, 27,
+	27, 27, 27, 27, 27, 1, 28, 29,
+	29, 29, 29, 29, 29, 29, 29, 29,
+	1, 30, 30, 30, 30, 30, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	30, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 31, 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,
+	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, 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, 32, 1, 30,
+	30, 30, 30, 30, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 30, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 31, 1, 1, 1, 29, 29,
+	29, 29, 29, 29, 29, 29, 29, 29,
+	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, 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, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 32, 1, 33, 1, 34,
+	1, 34, 34, 34, 34, 34, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	34, 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, 35, 1, 35, 35, 35, 35,
+	35, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 35, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 36, 37, 37, 37, 37,
+	37, 37, 37, 37, 37, 1, 38, 38,
+	38, 38, 38, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 38, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 39, 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, 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,
+	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, 40, 1, 38, 38, 38, 38,
+	38, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 38, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 39,
+	1, 1, 1, 41, 41, 41, 41, 41,
+	41, 41, 41, 41, 41, 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,
+	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, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	40, 1, 42, 43, 1, 44, 1, 44,
+	44, 44, 44, 44, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 44, 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,
+	45, 1, 45, 45, 45, 45, 45, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 45, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 46, 1,
+	1, 47, 48, 48, 48, 48, 48, 48,
+	48, 48, 48, 1, 49, 50, 50, 50,
+	50, 50, 50, 50, 50, 50, 1, 51,
+	51, 51, 51, 51, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 51, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 52, 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, 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, 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, 53, 1, 51, 51, 51,
+	51, 51, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 51, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	52, 1, 1, 1, 50, 50, 50, 50,
+	50, 50, 50, 50, 50, 50, 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, 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, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 53, 1, 54, 1, 54, 54, 54,
+	54, 54, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 54, 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, 55, 1,
+	55, 55, 55, 55, 55, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 55,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 56, 1, 1, 57,
+	58, 58, 58, 58, 58, 58, 58, 58,
+	58, 1, 59, 60, 60, 60, 60, 60,
+	60, 60, 60, 60, 1, 61, 61, 61,
+	61, 61, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 61, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	62, 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, 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, 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, 63, 1, 61, 61, 61, 61, 61,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 61, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 62, 1,
+	1, 1, 60, 60, 60, 60, 60, 60,
+	60, 60, 60, 60, 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, 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, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 63,
+	1, 64, 1, 64, 64, 64, 64, 64,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 64, 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, 65, 1, 65, 65,
+	65, 65, 65, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 65, 1, 66,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 67, 68, 68,
+	68, 68, 68, 68, 68, 68, 68, 1,
+	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, 1, 1, 1, 1, 1, 1,
+	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, 1, 70, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 71, 71,
+	1, 71, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 1, 1, 1, 1, 1,
+	1, 1, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 1, 1, 1, 1,
+	71, 1, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 71, 71, 71, 71,
+	71, 71, 71, 71, 1, 72, 72, 72,
+	72, 72, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 72, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	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, 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, 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,
+	1, 74, 1, 72, 72, 72, 72, 72,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 72, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 73, 1,
+	1, 1, 75, 75, 75, 75, 75, 75,
+	75, 75, 75, 75, 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, 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, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 74,
+	1, 76, 76, 76, 76, 76, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	76, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 77, 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,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 78, 1, 0,
+	0, 0, 0, 0, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 0, 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, 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, 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,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 2, 1, 1, 0
+};
+
+static const char _deserialize_json_trans_targs[] = {
+	1, 0, 2, 2, 3, 4, 18, 24,
+	37, 5, 12, 6, 7, 8, 9, 11,
+	9, 11, 10, 2, 44, 10, 44, 13,
+	14, 15, 16, 17, 16, 17, 10, 2,
+	44, 19, 20, 21, 22, 23, 10, 2,
+	44, 23, 25, 31, 26, 27, 28, 29,
+	30, 29, 30, 10, 2, 44, 32, 33,
+	34, 35, 36, 35, 36, 10, 2, 44,
+	38, 39, 40, 42, 43, 41, 10, 41,
+	10, 2, 44, 43, 44, 45, 46
+};
+
+static const char _deserialize_json_trans_actions[] = {
+	0, 0, 1, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 2, 2, 2,
+	0, 0, 3, 3, 4, 0, 5, 0,
+	0, 2, 2, 2, 0, 0, 6, 6,
+	7, 0, 0, 0, 2, 2, 8, 8,
+	9, 0, 0, 0, 0, 0, 2, 2,
+	2, 0, 0, 10, 10, 11, 0, 0,
+	2, 2, 2, 0, 0, 12, 12, 13,
+	0, 0, 0, 2, 2, 2, 14, 0,
+	15, 15, 16, 0, 0, 0, 0
+};
+
+static const int deserialize_json_start = 1;
+static const int deserialize_json_first_final = 44;
+static const int deserialize_json_error = 0;
+
+static const int deserialize_json_en_main = 1;
+
+
+#line 97 "hb-buffer-deserialize-json.rl"
+
+
+static hb_bool_t
+_hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer,
+				    const char *buf,
+				    unsigned int buf_len,
+				    const char **end_ptr,
+				    hb_font_t *font)
+{
+  const char *p = buf, *pe = buf + buf_len;
+
+  /* Ensure we have positions. */
+  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
+
+  while (p < pe && ISSPACE (*p))
+    p++;
+  if (p < pe && *p == (buffer->len ? ',' : '['))
+  {
+    *end_ptr = ++p;
+  }
+
+  const char *tok = nullptr;
+  int cs;
+  hb_glyph_info_t info = {0};
+  hb_glyph_position_t pos = {0};
+
+#line 466 "hb-buffer-deserialize-json.hh"
+	{
+	cs = deserialize_json_start;
+	}
+
+#line 471 "hb-buffer-deserialize-json.hh"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+	if ( cs == 0 )
+		goto _out;
+_resume:
+	_keys = _deserialize_json_trans_keys + (cs<<1);
+	_inds = _deserialize_json_indicies + _deserialize_json_index_offsets[cs];
+
+	_slen = _deserialize_json_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
+		(*p) <= _keys[1] ?
+		(*p) - _keys[0] : _slen ];
+
+	cs = _deserialize_json_trans_targs[_trans];
+
+	if ( _deserialize_json_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _deserialize_json_trans_actions[_trans] ) {
+	case 1:
+#line 38 "hb-buffer-deserialize-json.rl"
+	{
+	memset (&info, 0, sizeof (info));
+	memset (&pos , 0, sizeof (pos ));
+}
+	break;
+	case 5:
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 2:
+#line 51 "hb-buffer-deserialize-json.rl"
+	{
+	tok = p;
+}
+	break;
+	case 14:
+#line 55 "hb-buffer-deserialize-json.rl"
+	{
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+	break;
+	case 15:
+#line 62 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_uint (tok, p, &info.codepoint)) return false; }
+	break;
+	case 8:
+#line 63 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+	break;
+	case 10:
+#line 64 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+	break;
+	case 12:
+#line 65 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+	break;
+	case 3:
+#line 66 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+	break;
+	case 6:
+#line 67 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+	break;
+	case 16:
+#line 62 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_uint (tok, p, &info.codepoint)) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 9:
+#line 63 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 11:
+#line 64 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 13:
+#line 65 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 4:
+#line 66 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 7:
+#line 67 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+#line 624 "hb-buffer-deserialize-json.hh"
+	}
+
+_again:
+	if ( cs == 0 )
+		goto _out;
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	_out: {}
+	}
+
+#line 125 "hb-buffer-deserialize-json.rl"
+
+
+  *end_ptr = p;
+
+  return p == pe && *(p-1) != ']';
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */
diff --git a/src/hb-buffer-deserialize-json.rl b/src/hb-buffer-deserialize-json.rl
index 91b350f..0f7d48e 100644
--- a/src/hb-buffer-deserialize-json.rl
+++ b/src/hb-buffer-deserialize-json.rl
@@ -106,7 +106,7 @@
   const char *p = buf, *pe = buf + buf_len;
 
   /* Ensure we have positions. */
-  (void) hb_buffer_get_glyph_positions (buffer, NULL);
+  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
 
   while (p < pe && ISSPACE (*p))
     p++;
@@ -115,7 +115,7 @@
     *end_ptr = ++p;
   }
 
-  const char *tok = NULL;
+  const char *tok = nullptr;
   int cs;
   hb_glyph_info_t info = {0};
   hb_glyph_position_t pos = {0};
diff --git a/src/hb-buffer-deserialize-text.hh b/src/hb-buffer-deserialize-text.hh
new file mode 100644
index 0000000..a6ab0bb
--- /dev/null
+++ b/src/hb-buffer-deserialize-text.hh
@@ -0,0 +1,571 @@
+
+#line 1 "hb-buffer-deserialize-text.rl"
+/*
+ * Copyright © 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.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_BUFFER_DESERIALIZE_TEXT_HH
+#define HB_BUFFER_DESERIALIZE_TEXT_HH
+
+#include "hb-private.hh"
+
+
+#line 36 "hb-buffer-deserialize-text.hh"
+static const unsigned char _deserialize_text_trans_keys[] = {
+	0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u,
+	48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u,
+	9u, 122u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u,
+	9u, 124u, 9u, 124u, 9u, 124u, 0
+};
+
+static const char _deserialize_text_key_spans[] = {
+	0, 114, 13, 10, 13, 10, 10, 13,
+	10, 1, 13, 10, 14, 116, 116, 0,
+	114, 116, 116, 116, 116, 116, 116, 116,
+	116, 116, 116
+};
+
+static const short _deserialize_text_index_offsets[] = {
+	0, 0, 115, 129, 140, 154, 165, 176,
+	190, 201, 203, 217, 228, 243, 360, 477,
+	478, 593, 710, 827, 944, 1061, 1178, 1295,
+	1412, 1529, 1646
+};
+
+static const char _deserialize_text_indicies[] = {
+	0, 0, 0, 0, 0, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	0, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	2, 3, 3, 3, 3, 3, 3, 3,
+	3, 3, 1, 1, 1, 1, 1, 1,
+	1, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 1, 1, 1, 1, 1,
+	1, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 1, 5, 1, 1, 6,
+	7, 7, 7, 7, 7, 7, 7, 7,
+	7, 1, 8, 9, 9, 9, 9, 9,
+	9, 9, 9, 9, 1, 10, 1, 1,
+	11, 12, 12, 12, 12, 12, 12, 12,
+	12, 12, 1, 13, 14, 14, 14, 14,
+	14, 14, 14, 14, 14, 1, 15, 16,
+	16, 16, 16, 16, 16, 16, 16, 16,
+	1, 17, 1, 1, 18, 19, 19, 19,
+	19, 19, 19, 19, 19, 19, 1, 20,
+	21, 21, 21, 21, 21, 21, 21, 21,
+	21, 1, 22, 1, 23, 1, 1, 24,
+	25, 25, 25, 25, 25, 25, 25, 25,
+	25, 1, 26, 27, 27, 27, 27, 27,
+	27, 27, 27, 27, 1, 22, 1, 1,
+	1, 21, 21, 21, 21, 21, 21, 21,
+	21, 21, 21, 1, 28, 28, 28, 28,
+	28, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 28, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 29, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	30, 1, 1, 31, 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, 1,
+	32, 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, 1, 1, 1, 33,
+	1, 34, 34, 34, 34, 34, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	34, 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, 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, 1,
+	1, 1, 1, 1, 1, 35, 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, 1, 1, 1, 36, 1, 1, 0,
+	0, 0, 0, 0, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 0, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 2, 3,
+	3, 3, 3, 3, 3, 3, 3, 3,
+	1, 1, 1, 1, 1, 1, 1, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 1, 1, 1, 1, 1, 1, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 4, 4, 4, 4,
+	4, 1, 28, 28, 28, 28, 28, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 28, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 29, 1, 1, 1,
+	1, 37, 37, 37, 37, 37, 37, 37,
+	37, 37, 37, 1, 1, 1, 30, 1,
+	1, 31, 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, 1, 32, 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, 1, 1, 1, 33, 1, 38,
+	38, 38, 38, 38, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 38, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 39, 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, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 40, 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, 1,
+	1, 1, 41, 1, 42, 42, 42, 42,
+	42, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 42, 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, 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, 1, 1, 1, 1, 1, 1,
+	43, 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, 1, 1, 1, 44,
+	1, 42, 42, 42, 42, 42, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	42, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	14, 14, 14, 14, 14, 14, 14, 14,
+	14, 14, 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, 1, 1, 1,
+	1, 1, 1, 1, 1, 43, 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, 1, 1, 1, 44, 1, 38, 38,
+	38, 38, 38, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 38, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 39, 1, 1, 1, 9, 9, 9,
+	9, 9, 9, 9, 9, 9, 9, 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, 1, 1, 1, 1, 1, 1,
+	1, 1, 40, 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, 1, 1,
+	1, 41, 1, 45, 45, 45, 45, 45,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 45, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 46, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 47, 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, 1, 48,
+	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, 1, 1, 1, 49, 1,
+	50, 50, 50, 50, 50, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 50,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 51, 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, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 52, 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,
+	1, 1, 1, 53, 1, 50, 50, 50,
+	50, 50, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 50, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 51,
+	1, 1, 1, 1, 27, 27, 27, 27,
+	27, 27, 27, 27, 27, 27, 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, 1, 1, 1, 1, 1, 1, 1,
+	1, 52, 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, 1, 1, 1,
+	53, 1, 45, 45, 45, 45, 45, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 45, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 46, 1, 1, 1,
+	1, 54, 54, 54, 54, 54, 54, 54,
+	54, 54, 54, 1, 1, 1, 1, 1,
+	1, 47, 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, 1, 48, 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, 1, 1, 1, 49, 1, 28,
+	28, 28, 28, 28, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 28, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 29, 1, 55, 55, 1, 55, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	1, 1, 1, 30, 1, 1, 31, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	55, 1, 1, 32, 1, 55, 1, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	55, 55, 55, 55, 55, 55, 55, 55,
+	55, 1, 33, 1, 0
+};
+
+static const char _deserialize_text_trans_targs[] = {
+	1, 0, 13, 17, 26, 3, 18, 21,
+	18, 21, 5, 19, 20, 19, 20, 22,
+	25, 8, 9, 12, 9, 12, 10, 11,
+	23, 24, 23, 24, 14, 2, 6, 7,
+	15, 16, 14, 15, 16, 17, 14, 4,
+	15, 16, 14, 15, 16, 14, 2, 7,
+	15, 16, 14, 2, 15, 16, 25, 26
+};
+
+static const char _deserialize_text_trans_actions[] = {
+	0, 0, 1, 1, 1, 2, 2, 2,
+	0, 0, 2, 2, 2, 0, 0, 2,
+	2, 2, 2, 2, 0, 0, 3, 2,
+	2, 2, 0, 0, 4, 5, 5, 5,
+	4, 4, 0, 0, 0, 0, 6, 7,
+	6, 6, 8, 8, 8, 9, 10, 10,
+	9, 9, 11, 12, 11, 11, 0, 0
+};
+
+static const char _deserialize_text_eof_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 4, 0, 0,
+	0, 4, 6, 8, 8, 6, 9, 11,
+	11, 9, 4
+};
+
+static const int deserialize_text_start = 1;
+static const int deserialize_text_first_final = 13;
+static const int deserialize_text_error = 0;
+
+static const int deserialize_text_en_main = 1;
+
+
+#line 91 "hb-buffer-deserialize-text.rl"
+
+
+static hb_bool_t
+_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer,
+				    const char *buf,
+				    unsigned int buf_len,
+				    const char **end_ptr,
+				    hb_font_t *font)
+{
+  const char *p = buf, *pe = buf + buf_len;
+
+  /* Ensure we have positions. */
+  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
+
+  while (p < pe && ISSPACE (*p))
+    p++;
+  if (p < pe && *p == (buffer->len ? '|' : '['))
+  {
+    *end_ptr = ++p;
+  }
+
+  const char *eof = pe, *tok = nullptr;
+  int cs;
+  hb_glyph_info_t info = {0};
+  hb_glyph_position_t pos = {0};
+
+#line 343 "hb-buffer-deserialize-text.hh"
+	{
+	cs = deserialize_text_start;
+	}
+
+#line 348 "hb-buffer-deserialize-text.hh"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+	if ( cs == 0 )
+		goto _out;
+_resume:
+	_keys = _deserialize_text_trans_keys + (cs<<1);
+	_inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs];
+
+	_slen = _deserialize_text_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
+		(*p) <= _keys[1] ?
+		(*p) - _keys[0] : _slen ];
+
+	cs = _deserialize_text_trans_targs[_trans];
+
+	if ( _deserialize_text_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _deserialize_text_trans_actions[_trans] ) {
+	case 2:
+#line 51 "hb-buffer-deserialize-text.rl"
+	{
+	tok = p;
+}
+	break;
+	case 5:
+#line 55 "hb-buffer-deserialize-text.rl"
+	{
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+	break;
+	case 10:
+#line 62 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+	break;
+	case 3:
+#line 63 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+	break;
+	case 12:
+#line 64 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+	break;
+	case 7:
+#line 65 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+	break;
+	case 1:
+#line 38 "hb-buffer-deserialize-text.rl"
+	{
+	memset (&info, 0, sizeof (info));
+	memset (&pos , 0, sizeof (pos ));
+}
+#line 51 "hb-buffer-deserialize-text.rl"
+	{
+	tok = p;
+}
+	break;
+	case 4:
+#line 55 "hb-buffer-deserialize-text.rl"
+	{
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 9:
+#line 62 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 11:
+#line 64 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 6:
+#line 65 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 8:
+#line 66 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+#line 480 "hb-buffer-deserialize-text.hh"
+	}
+
+_again:
+	if ( cs == 0 )
+		goto _out;
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	switch ( _deserialize_text_eof_actions[cs] ) {
+	case 4:
+#line 55 "hb-buffer-deserialize-text.rl"
+	{
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 9:
+#line 62 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 11:
+#line 64 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 6:
+#line 65 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 8:
+#line 66 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+#line 557 "hb-buffer-deserialize-text.hh"
+	}
+	}
+
+	_out: {}
+	}
+
+#line 119 "hb-buffer-deserialize-text.rl"
+
+
+  *end_ptr = p;
+
+  return p == pe && *(p-1) != ']';
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */
diff --git a/src/hb-buffer-deserialize-text.rl b/src/hb-buffer-deserialize-text.rl
index 8a682f7..fd9be42 100644
--- a/src/hb-buffer-deserialize-text.rl
+++ b/src/hb-buffer-deserialize-text.rl
@@ -100,7 +100,7 @@
   const char *p = buf, *pe = buf + buf_len;
 
   /* Ensure we have positions. */
-  (void) hb_buffer_get_glyph_positions (buffer, NULL);
+  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
 
   while (p < pe && ISSPACE (*p))
     p++;
@@ -109,7 +109,7 @@
     *end_ptr = ++p;
   }
 
-  const char *eof = pe, *tok = NULL;
+  const char *eof = pe, *tok = nullptr;
   int cs;
   hb_glyph_info_t info = {0};
   hb_glyph_position_t pos = {0};
diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index bca308d..af4767f 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -35,8 +35,8 @@
 #include "hb-unicode-private.hh"
 
 
-#ifndef HB_BUFFER_MAX_EXPANSION_FACTOR
-#define HB_BUFFER_MAX_EXPANSION_FACTOR 32
+#ifndef HB_BUFFER_MAX_LEN_FACTOR
+#define HB_BUFFER_MAX_LEN_FACTOR 32
 #endif
 #ifndef HB_BUFFER_MAX_LEN_MIN
 #define HB_BUFFER_MAX_LEN_MIN 8192
@@ -45,11 +45,22 @@
 #define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */
 #endif
 
-ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20);
-ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t));
+#ifndef HB_BUFFER_MAX_OPS_FACTOR
+#define HB_BUFFER_MAX_OPS_FACTOR 64
+#endif
+#ifndef HB_BUFFER_MAX_OPS_MIN
+#define HB_BUFFER_MAX_OPS_MIN 1024
+#endif
+#ifndef HB_BUFFER_MAX_OPS_DEFAULT
+#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */
+#endif
+
+static_assert ((sizeof (hb_glyph_info_t) == 20), "");
+static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), "");
 
 HB_MARK_AS_FLAG_T (hb_buffer_flags_t);
 HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t);
+HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t);
 
 enum hb_buffer_scratch_flags_t {
   HB_BUFFER_SCRATCH_FLAG_DEFAULT			= 0x00000000u,
@@ -57,6 +68,9 @@
   HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES		= 0x00000002u,
   HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK		= 0x00000004u,
   HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT		= 0x00000008u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK		= 0x00000010u,
+  HB_BUFFER_SCRATCH_FLAG_HAS_CGJ			= 0x00000020u,
+
   /* Reserved for complex shapers' internal use. */
   HB_BUFFER_SCRATCH_FLAG_COMPLEX0			= 0x01000000u,
   HB_BUFFER_SCRATCH_FLAG_COMPLEX1			= 0x02000000u,
@@ -79,8 +93,9 @@
   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_buffer_scratch_flags_t scratch_flags; /* Have space-flallback, etc. */
+  hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */
   unsigned int max_len; /* Maximum allowed len. */
+  int max_ops; /* Maximum allowed operations. */
 
   /* Buffer contents */
   hb_buffer_content_type_t content_type;
@@ -99,17 +114,6 @@
   hb_glyph_info_t     *out_info;
   hb_glyph_position_t *pos;
 
-  inline hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
-  inline hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; }
-
-  inline hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
-  inline hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
-
-  inline hb_glyph_info_t &prev (void) { return out_info[out_len ? out_len - 1 : 0]; }
-  inline hb_glyph_info_t prev (void) const { return out_info[out_len ? out_len - 1 : 0]; }
-
-  inline bool has_separate_output (void) const { return info != out_info; }
-
   unsigned int serial;
 
   /* Text before / after the main buffer contents.
@@ -129,6 +133,10 @@
 #ifndef HB_NDEBUG
   uint8_t allocated_var_bits;
 #endif
+
+
+  /* Methods */
+
   inline void allocate_var (unsigned int start, unsigned int count)
   {
 #ifndef HB_NDEBUG
@@ -165,8 +173,17 @@
 #endif
   }
 
+  inline hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
+  inline hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; }
 
-  /* Methods */
+  inline hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
+  inline hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
+
+  inline hb_glyph_info_t &prev (void) { return out_info[out_len ? out_len - 1 : 0]; }
+  inline hb_glyph_info_t prev (void) const { return out_info[out_len ? out_len - 1 : 0]; }
+
+  inline bool has_separate_output (void) const { return info != out_info; }
+
 
   HB_INTERNAL void reset (void);
   HB_INTERNAL void clear (void);
@@ -232,25 +249,31 @@
     for (unsigned int j = 0; j < len; j++)
       info[j].mask |= mask;
   }
-  HB_INTERNAL void set_masks (hb_mask_t value,
-			      hb_mask_t mask,
-			      unsigned int cluster_start,
-			      unsigned int cluster_end);
+  HB_INTERNAL void set_masks (hb_mask_t value, hb_mask_t mask,
+			      unsigned int cluster_start, unsigned int cluster_end);
 
-  HB_INTERNAL void merge_clusters (unsigned int start,
-				   unsigned int end)
+  inline void merge_clusters (unsigned int start, unsigned int end)
   {
     if (end - start < 2)
       return;
     merge_clusters_impl (start, end);
   }
-  HB_INTERNAL void merge_clusters_impl (unsigned int start,
-					unsigned int end);
-  HB_INTERNAL void merge_out_clusters (unsigned int start,
-				       unsigned int end);
+  HB_INTERNAL void merge_clusters_impl (unsigned int start, unsigned int end);
+  HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end);
   /* Merge clusters for deleting current glyph, and skip it. */
   HB_INTERNAL void delete_glyph (void);
 
+  inline void unsafe_to_break (unsigned int start,
+			       unsigned int end)
+  {
+    if (end - start < 2)
+      return;
+    unsafe_to_break_impl (start, end);
+  }
+  HB_INTERNAL void unsafe_to_break_impl (unsigned int start, unsigned int end);
+  HB_INTERNAL void unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end);
+
+
   /* Internal methods */
   HB_INTERNAL bool enlarge (unsigned int size);
 
@@ -282,9 +305,78 @@
     return ret;
   }
   HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0);
+
+  static inline void
+  set_cluster (hb_glyph_info_t &inf, unsigned int cluster, unsigned int mask = 0)
+  {
+    if (inf.cluster != cluster)
+    {
+      if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
+	inf.mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+      else
+	inf.mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+    }
+    inf.cluster = cluster;
+  }
+
+  inline int
+  _unsafe_to_break_find_min_cluster (const hb_glyph_info_t *infos,
+				     unsigned int start, unsigned int end,
+				     unsigned int cluster) const
+  {
+    for (unsigned int i = start; i < end; i++)
+      cluster = MIN<unsigned int> (cluster, infos[i].cluster);
+    return cluster;
+  }
+  inline void
+  _unsafe_to_break_set_mask (hb_glyph_info_t *infos,
+			     unsigned int start, unsigned int end,
+			     unsigned int cluster)
+  {
+    for (unsigned int i = start; i < end; i++)
+      if (cluster != infos[i].cluster)
+      {
+	scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK;
+	infos[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+      }
+  }
+
+  inline void
+  unsafe_to_break_all (void)
+  {
+    unsafe_to_break_impl (0, len);
+  }
+  inline void
+  safe_to_break_all (void)
+  {
+    for (unsigned int i = 0; i < len; i++)
+      info[i].mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+  }
 };
 
 
+/* Loop over clusters. Duplicated in foreach_syllable(). */
+#define foreach_cluster(buffer, start, end) \
+  for (unsigned int \
+       _count = buffer->len, \
+       start = 0, end = _count ? _next_cluster (buffer, 0) : 0; \
+       start < _count; \
+       start = end, end = _next_cluster (buffer, start))
+
+static inline unsigned int
+_next_cluster (hb_buffer_t *buffer, unsigned int start)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+
+  unsigned int cluster = info[start].cluster;
+  while (++start < count && cluster == info[start].cluster)
+    ;
+
+  return start;
+}
+
+
 #define HB_BUFFER_XALLOCATE_VAR(b, func, var) \
   b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \
 	   sizeof (b->info[0].var))
diff --git a/src/hb-buffer-serialize.cc b/src/hb-buffer-serialize.cc
index 63a0f34..1147194 100644
--- a/src/hb-buffer-serialize.cc
+++ b/src/hb-buffer-serialize.cc
@@ -30,7 +30,7 @@
 static const char *serialize_formats[] = {
   "text",
   "json",
-  NULL
+  nullptr
 };
 
 /**
@@ -90,7 +90,7 @@
     case HB_BUFFER_SERIALIZE_FORMAT_TEXT:	return serialize_formats[0];
     case HB_BUFFER_SERIALIZE_FORMAT_JSON:	return serialize_formats[1];
     default:
-    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:	return NULL;
+    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:	return nullptr;
   }
 }
 
@@ -104,11 +104,12 @@
 				  hb_font_t *font,
 				  hb_buffer_serialize_flags_t flags)
 {
-  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
   hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ?
-			     NULL : hb_buffer_get_glyph_positions (buffer, NULL);
+			     nullptr : hb_buffer_get_glyph_positions (buffer, nullptr);
 
   *buf_consumed = 0;
+  hb_position_t x = 0, y = 0;
   for (unsigned int i = start; i < end; i++)
   {
     char b[1024];
@@ -145,10 +146,17 @@
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
-		     pos[i].x_offset, pos[i].y_offset);
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
-		     pos[i].x_advance, pos[i].y_advance);
+      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
+			     x+pos[i].x_offset, y+pos[i].y_offset));
+      if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
+	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
+			       pos[i].x_advance, pos[i].y_advance));
+    }
+
+    if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS)
+    {
+      if (info[i].mask & HB_GLYPH_FLAG_DEFINED)
+	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED));
     }
 
     if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS)
@@ -156,9 +164,9 @@
       hb_glyph_extents_t extents;
       hb_font_get_glyph_extents(font, info[i].codepoint, &extents);
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d",
-        extents.x_bearing, extents.y_bearing));
+		extents.x_bearing, extents.y_bearing));
       p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d",
-        extents.width, extents.height));
+		extents.width, extents.height));
     }
 
     *p++ = '}';
@@ -173,6 +181,12 @@
       *buf = '\0';
     } else
       return i - start;
+
+    if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
+    {
+      x += pos[i].x_advance;
+      y += pos[i].y_advance;
+    }
   }
 
   return end - start;
@@ -188,11 +202,12 @@
 				  hb_font_t *font,
 				  hb_buffer_serialize_flags_t flags)
 {
-  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
   hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ?
-			     NULL : hb_buffer_get_glyph_positions (buffer, NULL);
+			     nullptr : hb_buffer_get_glyph_positions (buffer, nullptr);
 
   *buf_consumed = 0;
+  hb_position_t x = 0, y = 0;
   for (unsigned int i = start; i < end; i++)
   {
     char b[1024];
@@ -217,13 +232,22 @@
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
-      if (pos[i].x_offset || pos[i].y_offset)
-	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset));
+      if (x+pos[i].x_offset || y+pos[i].y_offset)
+	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset));
 
-      *p++ = '+';
-      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
-      if (pos[i].y_advance)
-	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance));
+      if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
+      {
+	*p++ = '+';
+	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
+	if (pos[i].y_advance)
+	  p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance));
+      }
+    }
+
+    if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS)
+    {
+      if (info[i].mask &HB_GLYPH_FLAG_DEFINED)
+	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED));
     }
 
     if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS)
@@ -243,6 +267,12 @@
       *buf = '\0';
     } else
       return i - start;
+
+    if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
+    {
+      x += pos[i].x_advance;
+      y += pos[i].y_advance;
+    }
   }
 
   return end - start;
@@ -311,6 +341,8 @@
   if (!buf_consumed)
     buf_consumed = &sconsumed;
   *buf_consumed = 0;
+  if (buf_size)
+    *buf = '\0';
 
   assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) ||
 	  buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
@@ -408,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 NULL */
-			      hb_font_t *font, /* May be NULL */
+			      const char **end_ptr, /* May be nullptr */
+			      hb_font_t *font, /* May be nullptr */
 			      hb_buffer_serialize_format_t format)
 {
   const char *end;
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index 3940a3d..dc0639f 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -31,10 +31,6 @@
 #include "hb-utf-private.hh"
 
 
-#ifndef HB_DEBUG_BUFFER
-#define HB_DEBUG_BUFFER (HB_DEBUG+0)
-#endif
-
 /**
  * SECTION: hb-buffer
  * @title: Buffers
@@ -124,8 +120,8 @@
   }
 
   unsigned int new_allocated = allocated;
-  hb_glyph_position_t *new_pos = NULL;
-  hb_glyph_info_t *new_info = NULL;
+  hb_glyph_position_t *new_pos = nullptr;
+  hb_glyph_info_t *new_info = nullptr;
   bool separate_out = out_info != info;
 
   if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0]))))
@@ -134,7 +130,7 @@
   while (size >= new_allocated)
     new_allocated += (new_allocated >> 1) + 32;
 
-  ASSERT_STATIC (sizeof (info[0]) == sizeof (pos[0]));
+  static_assert ((sizeof (info[0]) == sizeof (pos[0])), "");
   if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0]))))
     goto done;
 
@@ -267,7 +263,7 @@
 
   memset (glyph, 0, sizeof (*glyph));
   glyph->codepoint = codepoint;
-  glyph->mask = 1;
+  glyph->mask = 0;
   glyph->cluster = cluster;
 
   len++;
@@ -550,12 +546,15 @@
 				  unsigned int end)
 {
   if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
+  {
+    unsafe_to_break (start, end);
     return;
+  }
 
   unsigned int cluster = info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
-    cluster = MIN (cluster, info[i].cluster);
+    cluster = MIN<unsigned int> (cluster, info[i].cluster);
 
   /* Extend end */
   while (end < len && info[end - 1].cluster == info[end].cluster)
@@ -568,10 +567,10 @@
   /* If we hit the start of buffer, continue in out-buffer. */
   if (idx == start)
     for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
-      out_info[i - 1].cluster = cluster;
+      set_cluster (out_info[i - 1], cluster);
 
   for (unsigned int i = start; i < end; i++)
-    info[i].cluster = cluster;
+    set_cluster (info[i], cluster);
 }
 void
 hb_buffer_t::merge_out_clusters (unsigned int start,
@@ -586,7 +585,7 @@
   unsigned int cluster = out_info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
-    cluster = MIN (cluster, out_info[i].cluster);
+    cluster = MIN<unsigned int> (cluster, out_info[i].cluster);
 
   /* Extend start */
   while (start && out_info[start - 1].cluster == out_info[start].cluster)
@@ -599,14 +598,16 @@
   /* If we hit the end of out-buffer, continue in buffer. */
   if (end == out_len)
     for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
-      info[i].cluster = cluster;
+      set_cluster (info[i], cluster);
 
   for (unsigned int i = start; i < end; i++)
-    out_info[i].cluster = cluster;
+    set_cluster (out_info[i], cluster);
 }
 void
 hb_buffer_t::delete_glyph ()
 {
+  /* The logic here is duplicated in hb_ot_hide_default_ignorables(). */
+
   unsigned int cluster = info[idx].cluster;
   if (idx + 1 < len && cluster == info[idx + 1].cluster)
   {
@@ -619,9 +620,10 @@
     /* Merge cluster backward. */
     if (cluster < out_info[out_len - 1].cluster)
     {
+      unsigned int mask = info[idx].mask;
       unsigned int old_cluster = out_info[out_len - 1].cluster;
       for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--)
-	out_info[i - 1].cluster = cluster;
+	set_cluster (out_info[i - 1], cluster, mask);
     }
     goto done;
   }
@@ -638,6 +640,32 @@
 }
 
 void
+hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end)
+{
+  unsigned int cluster = (unsigned int) -1;
+  cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster);
+  _unsafe_to_break_set_mask (info, start, end, cluster);
+}
+void
+hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end)
+{
+  if (!have_output)
+  {
+    unsafe_to_break_impl (start, end);
+    return;
+  }
+
+  assert (start <= out_len);
+  assert (idx <= end);
+
+  unsigned int cluster = (unsigned int) -1;
+  cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster);
+  cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster);
+  _unsafe_to_break_set_mask (out_info, start, out_len, cluster);
+  _unsafe_to_break_set_mask (info, idx, end, cluster);
+}
+
+void
 hb_buffer_t::guess_segment_properties (void)
 {
   assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
@@ -694,6 +722,7 @@
     return hb_buffer_get_empty ();
 
   buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT;
+  buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT;
 
   buffer->reset ();
 
@@ -721,6 +750,7 @@
     HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
     HB_BUFFER_SCRATCH_FLAG_DEFAULT,
     HB_BUFFER_MAX_LEN_DEFAULT,
+    HB_BUFFER_MAX_OPS_DEFAULT,
 
     HB_BUFFER_CONTENT_TYPE_INVALID,
     HB_SEGMENT_PROPERTIES_DEFAULT,
@@ -1380,6 +1410,23 @@
 }
 
 /**
+ * hb_glyph_info_get_glyph_flags:
+ * @info: a #hb_glyph_info_t.
+ *
+ * Returns glyph flags encoded within a #hb_glyph_info_t.
+ *
+ * Return value:
+ * The #hb_glyph_flags_t encoded within @info.
+ *
+ * Since: 1.5.0
+ **/
+hb_glyph_flags_t
+(hb_glyph_info_get_glyph_flags) (const hb_glyph_info_t *info)
+{
+  return hb_glyph_info_get_glyph_flags (info);
+}
+
+/**
  * hb_buffer_reverse:
  * @buffer: an #hb_buffer_t.
  *
@@ -1666,6 +1713,58 @@
 }
 
 
+/**
+ * hb_buffer_append:
+ * @buffer: an #hb_buffer_t.
+ * @source: source #hb_buffer_t.
+ * @start: start index into source buffer to copy.  Use 0 to copy from start of buffer.
+ * @end: end index into source buffer to copy.  Use (unsigned int) -1 to copy to end of buffer.
+ *
+ * Append (part of) contents of another buffer to this buffer.
+ *
+ * Since: 1.5.0
+ **/
+HB_EXTERN void
+hb_buffer_append (hb_buffer_t *buffer,
+		  hb_buffer_t *source,
+		  unsigned int start,
+		  unsigned int end)
+{
+  assert (!buffer->have_output && !source->have_output);
+  assert (buffer->have_positions == source->have_positions ||
+	  !buffer->len || !source->len);
+  assert (buffer->content_type == source->content_type ||
+	  !buffer->len || !source->len);
+
+  if (end > source->len)
+    end = source->len;
+  if (start > end)
+    start = end;
+  if (start == end)
+    return;
+
+  if (!buffer->len)
+    buffer->content_type = source->content_type;
+  if (!buffer->have_positions && source->have_positions)
+    buffer->clear_positions ();
+
+  if (buffer->len + (end - start) < buffer->len) /* Overflows. */
+  {
+    buffer->in_error = true;
+    return;
+  }
+
+  unsigned int orig_len = buffer->len;
+  hb_buffer_set_length (buffer, buffer->len + (end - start));
+  if (buffer->in_error)
+    return;
+
+  memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0]));
+  if (buffer->have_positions)
+    memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0]));
+}
+
+
 static int
 compare_info_codepoint (const hb_glyph_info_t *pa,
 			const hb_glyph_info_t *pb)
@@ -1736,7 +1835,8 @@
 hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
 {
   assert (buffer->have_positions);
-  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
+  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS ||
+	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
 
   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
 
@@ -1775,6 +1875,98 @@
   }
 }
 
+
+/*
+ * Comparing buffers.
+ */
+
+/**
+ * hb_buffer_diff:
+ *
+ * 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
+ * callers if just comparing two buffers is needed.
+ *
+ * Since: 1.5.0
+ **/
+hb_buffer_diff_flags_t
+hb_buffer_diff (hb_buffer_t *buffer,
+		hb_buffer_t *reference,
+		hb_codepoint_t dottedcircle_glyph,
+		unsigned int position_fuzz)
+{
+  if (buffer->content_type != reference->content_type && buffer->len && reference->len)
+    return HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH;
+
+  hb_buffer_diff_flags_t result = HB_BUFFER_DIFF_FLAG_EQUAL;
+  bool contains = dottedcircle_glyph != (hb_codepoint_t) -1;
+
+  unsigned int count = reference->len;
+
+  if (buffer->len != count)
+  {
+    /*
+     * we can't compare glyph-by-glyph, but we do want to know if there
+     * are .notdef or dottedcircle glyphs present in the reference buffer
+     */
+    const hb_glyph_info_t *info = reference->info;
+    unsigned int i;
+    for (i = 0; i < count; i++)
+    {
+      if (contains && info[i].codepoint == dottedcircle_glyph)
+        result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
+      if (contains && info[i].codepoint == 0)
+        result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT;
+    }
+    result |= HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH;
+    return hb_buffer_diff_flags_t (result);
+  }
+
+  if (!count)
+    return hb_buffer_diff_flags_t (result);
+
+  const hb_glyph_info_t *buf_info = buffer->info;
+  const hb_glyph_info_t *ref_info = reference->info;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    if (buf_info->codepoint != ref_info->codepoint)
+      result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH;
+    if (buf_info->cluster != ref_info->cluster)
+      result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH;
+    if ((buf_info->mask & ~ref_info->mask & HB_GLYPH_FLAG_DEFINED))
+      result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH;
+    if (contains && ref_info->codepoint == dottedcircle_glyph)
+      result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
+    if (contains && ref_info->codepoint == 0)
+      result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT;
+    buf_info++;
+    ref_info++;
+  }
+
+  if (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS)
+  {
+    assert (buffer->have_positions);
+    const hb_glyph_position_t *buf_pos = buffer->pos;
+    const hb_glyph_position_t *ref_pos = reference->pos;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if ((unsigned int) abs (buf_pos->x_advance - ref_pos->x_advance) > position_fuzz ||
+          (unsigned int) abs (buf_pos->y_advance - ref_pos->y_advance) > position_fuzz ||
+          (unsigned int) abs (buf_pos->x_offset - ref_pos->x_offset) > position_fuzz ||
+          (unsigned int) abs (buf_pos->y_offset - ref_pos->y_offset) > position_fuzz)
+      {
+        result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH;
+        break;
+      }
+      buf_pos++;
+      ref_pos++;
+    }
+  }
+
+  return result;
+}
+
+
 /*
  * Debugging.
  */
@@ -1803,9 +1995,9 @@
     buffer->message_data = user_data;
     buffer->message_destroy = destroy;
   } else {
-    buffer->message_func = NULL;
-    buffer->message_data = NULL;
-    buffer->message_destroy = NULL;
+    buffer->message_func = nullptr;
+    buffer->message_data = nullptr;
+    buffer->message_destroy = nullptr;
   }
 }
 
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index bf289c1..8a2d3e8 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -63,7 +63,7 @@
  */
 typedef struct hb_glyph_info_t {
   hb_codepoint_t codepoint;
-  hb_mask_t      mask;
+  hb_mask_t      mask; /* Holds hb_glyph_flags_t after hb_shape(), plus other things. */
   uint32_t       cluster;
 
   /*< private >*/
@@ -72,6 +72,37 @@
 } hb_glyph_info_t;
 
 /**
+ * hb_glyph_flags_t:
+ * @HB_GLYPH_FLAG_UNSAFE_TO_BREAK: Indicates that if input text is broken at the
+ * 				   beginning of the cluster this glyph is part of,
+ * 				   then both sides need to be re-shaped, as the
+ * 				   result might be different.  On the flip side,
+ * 				   it means that when this flag is not present,
+ * 				   then it's safe to break the glyph-run at the
+ * 				   beginning of this cluster, and the two sides
+ * 				   represent the exact same result one would get
+ * 				   if breaking input text at the beginning of
+ * 				   this cluster and shaping the two sides
+ * 				   separately.  This can be used to optimize
+ * 				   paragraph layout, by avoiding re-shaping
+ * 				   of each line after line-breaking, or limiting
+ * 				   the reshaping to a small piece around the
+ * 				   breaking point only.
+ */
+typedef enum { /*< flags >*/
+  HB_GLYPH_FLAG_UNSAFE_TO_BREAK		= 0x00000001,
+
+  HB_GLYPH_FLAG_DEFINED			= 0x00000001 /* OR of all defined flags */
+} hb_glyph_flags_t;
+
+HB_EXTERN hb_glyph_flags_t
+hb_glyph_info_get_glyph_flags (const hb_glyph_info_t *info);
+
+#define hb_glyph_info_get_glyph_flags(info) \
+	((hb_glyph_flags_t) ((unsigned int) (info)->mask & HB_GLYPH_FLAG_DEFINED))
+
+
+/**
  * hb_glyph_position_t:
  * @x_advance: how much the line advances after drawing this glyph when setting
  *             text in horizontal direction.
@@ -119,8 +150,8 @@
 #define HB_SEGMENT_PROPERTIES_DEFAULT {HB_DIRECTION_INVALID, \
 				       HB_SCRIPT_INVALID, \
 				       HB_LANGUAGE_INVALID, \
-				       NULL, \
-				       NULL}
+				       (void *) 0, \
+				       (void *) 0}
 
 HB_EXTERN hb_bool_t
 hb_segment_properties_equal (const hb_segment_properties_t *a,
@@ -163,6 +194,7 @@
 hb_buffer_get_user_data (hb_buffer_t        *buffer,
 			 hb_user_data_key_t *key);
 
+
 /**
  * hb_buffer_content_type_t:
  * @HB_BUFFER_CONTENT_TYPE_INVALID: Initial value for new buffer.
@@ -233,13 +265,21 @@
  *                      of the text without the full context.
  * @HB_BUFFER_FLAG_EOT: flag indicating that special handling of the end of text
  *                      paragraph can be applied to this buffer, similar to
- *                      @HB_BUFFER_FLAG_EOT.
+ *                      @HB_BUFFER_FLAG_BOT.
  * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES:
  *                      flag indication that character with Default_Ignorable
  *                      Unicode property should use the corresponding glyph
- *                      from the font, instead of hiding them (currently done
- *                      by replacing them with the space glyph and zeroing the
- *                      advance width.)
+ *                      from the font, instead of hiding them (done by
+ *                      replacing them with the space glyph and zeroing the
+ *                      advance width.)  This flag takes precedence over
+ *                      @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES.
+ * @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES:
+ *                      flag indication that character with Default_Ignorable
+ *                      Unicode property should be removed from glyph string
+ *                      instead of hiding them (done by replacing them with the
+ *                      space glyph and zeroing the advance width.)
+ *                      @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES takes
+ *                      precedence over this flag. Since: 1.8.0
  *
  * Since: 0.9.20
  */
@@ -247,7 +287,8 @@
   HB_BUFFER_FLAG_DEFAULT			= 0x00000000u,
   HB_BUFFER_FLAG_BOT				= 0x00000001u, /* Beginning-of-text */
   HB_BUFFER_FLAG_EOT				= 0x00000002u, /* End-of-text */
-  HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES	= 0x00000004u
+  HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES	= 0x00000004u,
+  HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES	= 0x00000008u
 } hb_buffer_flags_t;
 
 HB_EXTERN void
@@ -359,6 +400,11 @@
 			  unsigned int          item_offset,
 			  int                   item_length);
 
+HB_EXTERN void
+hb_buffer_append (hb_buffer_t *buffer,
+		  hb_buffer_t *source,
+		  unsigned int start,
+		  unsigned int end);
 
 HB_EXTERN hb_bool_t
 hb_buffer_set_length (hb_buffer_t  *buffer,
@@ -393,6 +439,9 @@
  * @HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS: do not serialize glyph position information.
  * @HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES: do no serialize glyph name.
  * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS: serialize glyph extents.
+ * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS: serialize glyph flags. Since: 1.5.0
+ * @HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES: do not serialize glyph advances,
+ *  glyph offsets will reflect absolute glyph positions. Since: 1.8.0
  *
  * Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs().
  *
@@ -403,7 +452,9 @@
   HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS		= 0x00000001u,
   HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS		= 0x00000002u,
   HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES	= 0x00000004u,
-  HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS	= 0x00000008u
+  HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS	= 0x00000008u,
+  HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS		= 0x00000010u,
+  HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES		= 0x00000020u
 } hb_buffer_serialize_flags_t;
 
 /**
@@ -453,6 +504,45 @@
 
 
 /*
+ * Compare buffers
+ */
+
+typedef enum { /*< flags >*/
+  HB_BUFFER_DIFF_FLAG_EQUAL			= 0x0000,
+
+  /* Buffers with different content_type cannot be meaningfully compared
+   * in any further detail. */
+  HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH	= 0x0001,
+
+  /* For buffers with differing length, the per-glyph comparison is not
+   * attempted, though we do still scan reference for dottedcircle / .notdef
+   * glyphs. */
+  HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH		= 0x0002,
+
+  /* We want to know if dottedcircle / .notdef glyphs are present in the
+   * reference, as we may not care so much about other differences in this
+   * case. */
+  HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT		= 0x0004,
+  HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT	= 0x0008,
+
+  /* If the buffers have the same length, we compare them glyph-by-glyph
+   * and report which aspect(s) of the glyph info/position are different. */
+  HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH	= 0x0010,
+  HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH		= 0x0020,
+  HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH	= 0x0040,
+  HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH		= 0x0080
+
+} hb_buffer_diff_flags_t;
+
+/* Compare the contents of two buffers, report types of differences. */
+HB_EXTERN hb_buffer_diff_flags_t
+hb_buffer_diff (hb_buffer_t *buffer,
+		hb_buffer_t *reference,
+		hb_codepoint_t dottedcircle_glyph,
+		unsigned int position_fuzz);
+
+
+/*
  * Debugging.
  */
 
diff --git a/src/hb-cache-private.hh b/src/hb-cache-private.hh
deleted file mode 100644
index 24957e1..0000000
--- a/src/hb-cache-private.hh
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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_PRIVATE_HH
-#define HB_CACHE_PRIVATE_HH
-
-#include "hb-private.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
-{
-  ASSERT_STATIC (key_bits >= cache_bits);
-  ASSERT_STATIC (key_bits + value_bits - cache_bits < 8 * sizeof (unsigned int));
-
-  inline void clear (void)
-  {
-    memset (values, 255, sizeof (values));
-  }
-
-  inline bool get (unsigned int key, unsigned int *value)
-  {
-    unsigned int k = key & ((1u<<cache_bits)-1);
-    unsigned int v = values[k];
-    if ((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] = v;
-    return true;
-  }
-
-  private:
-  unsigned int 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_PRIVATE_HH */
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 64e77d4..d1fcf79 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -32,6 +32,9 @@
 #include "hb-object-private.hh"
 
 #include <locale.h>
+#ifdef HAVE_XLOCALE_H
+#include <xlocale.h>
+#endif
 
 
 /* hb_options_t */
@@ -82,7 +85,7 @@
   for (; i < 4; i++)
     tag[i] = ' ';
 
-  return HB_TAG_CHAR4 (tag);
+  return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
 }
 
 /**
@@ -186,8 +189,10 @@
   const unsigned char *p1 = (const unsigned char *) v1;
   const unsigned char *p2 = (const unsigned char *) v2;
 
-  while (*p1 && *p1 == canon_map[*p2])
-    p1++, p2++;
+  while (*p1 && *p1 == canon_map[*p2]) {
+    p1++;
+    p2++;
+  }
 
   return *p1 == canon_map[*p2];
 }
@@ -219,9 +224,18 @@
   }
 
   inline hb_language_item_t & operator = (const char *s) {
-    lang = (hb_language_t) strdup (s);
-    for (unsigned char *p = (unsigned char *) lang; *p; p++)
-      *p = canon_map[*p];
+    /* If a custom allocated is used calling strdup() pairs
+    badly with a call to the custom free() in finish() below.
+    Therefore don't call strdup(), implement its behavior.
+    */
+    size_t len = strlen(s) + 1;
+    lang = (hb_language_t) malloc(len);
+    if (likely (lang))
+    {
+      memcpy((unsigned char *) lang, s, len);
+      for (unsigned char *p = (unsigned char *) lang; *p; p++)
+	*p = canon_map[*p];
+    }
 
     return *this;
   }
@@ -235,8 +249,8 @@
 static hb_language_item_t *langs;
 
 #ifdef HB_USE_ATEXIT
-static
-void free_langs (void)
+static void
+free_langs (void)
 {
   while (langs) {
     hb_language_item_t *next = langs->next;
@@ -260,9 +274,14 @@
   /* Not found; allocate one. */
   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
   if (unlikely (!lang))
-    return NULL;
+    return nullptr;
   lang->next = first_lang;
   *lang = key;
+  if (unlikely (!lang->lang))
+  {
+    free (lang);
+    return nullptr;
+  }
 
   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
     lang->finish ();
@@ -299,7 +318,7 @@
   if (!str || !len || !*str)
     return HB_LANGUAGE_INVALID;
 
-  hb_language_item_t *item = NULL;
+  hb_language_item_t *item = nullptr;
   if (len >= 0)
   {
     /* NUL-terminate it. */
@@ -330,7 +349,7 @@
 const char *
 hb_language_to_string (hb_language_t language)
 {
-  /* This is actually NULL-safe! */
+  /* This is actually nullptr-safe! */
   return language->s;
 }
 
@@ -350,7 +369,7 @@
 
   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
   if (unlikely (language == HB_LANGUAGE_INVALID)) {
-    language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
+    language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
     (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
   }
 
@@ -505,6 +524,7 @@
     case HB_SCRIPT_PSALTER_PAHLAVI:
 
     /* Unicode-8.0 additions */
+    case HB_SCRIPT_HATRAN:
     case HB_SCRIPT_OLD_HUNGARIAN:
 
     /* Unicode-9.0 additions */
@@ -543,9 +563,9 @@
 void *
 hb_user_data_array_t::get (hb_user_data_key_t *key)
 {
-  hb_user_data_item_t item = {NULL, NULL, NULL};
+  hb_user_data_item_t item = {nullptr, nullptr, nullptr};
 
-  return items.find (key, &item, lock) ? item.data : NULL;
+  return items.find (key, &item, lock) ? item.data : nullptr;
 }
 
 
@@ -655,6 +675,81 @@
 }
 
 static bool
+parse_uint32 (const char **pp, const char *end, uint32_t *pv)
+{
+  char buf[32];
+  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
+  strncpy (buf, *pp, len);
+  buf[len] = '\0';
+
+  char *p = buf;
+  char *pend = p;
+  unsigned int v;
+
+  /* Intentionally use strtol instead of strtoul, such that
+   * -1 turns into "big number"... */
+  errno = 0;
+  v = strtol (p, &pend, 0);
+  if (errno || p == pend)
+    return false;
+
+  *pv = v;
+  *pp += pend - p;
+  return true;
+}
+
+#if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
+#define USE_XLOCALE 1
+#define HB_LOCALE_T locale_t
+#define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
+#define HB_FREE_LOCALE(loc) freelocale (loc)
+#elif defined(_MSC_VER)
+#define USE_XLOCALE 1
+#define HB_LOCALE_T _locale_t
+#define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
+#define HB_FREE_LOCALE(loc) _free_locale (loc)
+#define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
+#endif
+
+#ifdef USE_XLOCALE
+
+static HB_LOCALE_T C_locale;
+
+#ifdef HB_USE_ATEXIT
+static void
+free_C_locale (void)
+{
+  if (C_locale)
+    HB_FREE_LOCALE (C_locale);
+}
+#endif
+
+static HB_LOCALE_T
+get_C_locale (void)
+{
+retry:
+  HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
+
+  if (unlikely (!C))
+  {
+    C = HB_CREATE_LOCALE ("C");
+
+    if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C))
+    {
+      HB_FREE_LOCALE (C_locale);
+      goto retry;
+    }
+
+#ifdef HB_USE_ATEXIT
+    atexit (free_C_locale); /* First person registers atexit() callback. */
+#endif
+  }
+
+  return C;
+}
+#endif
+
+static bool
 parse_float (const char **pp, const char *end, float *pv)
 {
   char buf[32];
@@ -667,7 +762,11 @@
   float v;
 
   errno = 0;
-  v = strtof (p, &pend);
+#ifdef USE_XLOCALE
+  v = strtod_l (p, &pend, get_C_locale ());
+#else
+  v = strtod (p, &pend);
+#endif
   if (errno || p == pend)
     return false;
 
@@ -677,7 +776,7 @@
 }
 
 static bool
-parse_bool (const char **pp, const char *end, unsigned int *pv)
+parse_bool (const char **pp, const char *end, uint32_t *pv)
 {
   parse_space (pp, end);
 
@@ -686,9 +785,9 @@
     (*pp)++;
 
   /* CSS allows on/off as aliases 1/0. */
-  if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
+  if (*pp - p == 2 && 0 == strncmp (p, "on", 2))
     *pv = 1;
-  else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
+  else if (*pp - p == 3 && 0 == strncmp (p, "off", 3))
     *pv = 0;
   else
     return false;
@@ -776,11 +875,11 @@
 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
 {
   bool had_equal = parse_char (pp, end, '=');
-  bool had_value = parse_uint (pp, end, &feature->value) ||
+  bool had_value = parse_uint32 (pp, end, &feature->value) ||
                    parse_bool (pp, end, &feature->value);
   /* CSS doesn't use equal-sign between tag and value.
    * If there was an equal-sign, then there *must* be a value.
-   * A value without an eqaul-sign is ok, but not required. */
+   * A value without an equal-sign is ok, but not required. */
   return !had_equal || had_value;
 }
 
diff --git a/src/hb-common.h b/src/hb-common.h
index 634cb96..26200ce 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -43,30 +43,16 @@
 # endif /* !__cplusplus */
 #endif
 
-#if !defined (HB_DONT_DEFINE_STDINT)
-
 #if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \
     defined (_sgi) || defined (__sun) || defined (sun) || \
     defined (__digital__) || defined (__HP_cc)
 #  include <inttypes.h>
 #elif defined (_AIX)
 #  include <sys/inttypes.h>
-/* VS 2010 (_MSC_VER 1600) has stdint.h */
-#elif defined (_MSC_VER) && _MSC_VER < 1600
-typedef __int8 int8_t;
-typedef unsigned __int8 uint8_t;
-typedef __int16 int16_t;
-typedef unsigned __int16 uint16_t;
-typedef __int32 int32_t;
-typedef unsigned __int32 uint32_t;
-typedef __int64 int64_t;
-typedef unsigned __int64 uint64_t;
 #else
 #  include <stdint.h>
 #endif
 
-#endif
-
 HB_BEGIN_DECLS
 
 
@@ -148,7 +134,7 @@
 HB_EXTERN const char *
 hb_language_to_string (hb_language_t language);
 
-#define HB_LANGUAGE_INVALID ((hb_language_t) NULL)
+#define HB_LANGUAGE_INVALID ((hb_language_t) 0)
 
 HB_EXTERN hb_language_t
 hb_language_get_default (void);
@@ -321,6 +307,14 @@
   /*9.0*/ HB_SCRIPT_TANGUT			= HB_TAG ('T','a','n','g'),
   /*9.0*/ HB_SCRIPT_NEWA			= HB_TAG ('N','e','w','a'),
 
+  /*
+   * Since 1.6.0
+   */
+  /*10.0*/HB_SCRIPT_MASARAM_GONDI		= HB_TAG ('G','o','n','m'),
+  /*10.0*/HB_SCRIPT_NUSHU			= HB_TAG ('N','s','h','u'),
+  /*10.0*/HB_SCRIPT_SOYOMBO			= HB_TAG ('S','o','y','o'),
+  /*10.0*/HB_SCRIPT_ZANABAZAR_SQUARE		= HB_TAG ('Z','a','n','b'),
+
   /* No script set. */
   HB_SCRIPT_INVALID				= HB_TAG_NONE,
 
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc
index 8f2483b..aba7cf4 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -27,15 +27,34 @@
  */
 
 #define HB_SHAPER coretext
+
+#include "hb-private.hh"
+#include "hb-debug.hh"
 #include "hb-shaper-impl-private.hh"
 
 #include "hb-coretext.h"
+#include <math.h>
 
+/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
+#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
 
-#ifndef HB_DEBUG_CORETEXT
-#define HB_DEBUG_CORETEXT (HB_DEBUG+0)
-#endif
-
+static CGFloat
+coretext_font_size_from_ptem (float ptem)
+{
+  /* 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
+   */
+  ptem *= 96.f / 72.f;
+  return ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : ptem;
+}
+static float
+coretext_font_size_to_ptem (CGFloat size)
+{
+  size *= 72.f / 96.f;
+  return size <= 0.f ? 0 : size;
+}
 
 static void
 release_table_data (void *user_data)
@@ -50,32 +69,32 @@
   CGFontRef cg_font = reinterpret_cast<CGFontRef> (user_data);
   CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag);
   if (unlikely (!cf_data))
-    return NULL;
+    return nullptr;
 
   const char *data = reinterpret_cast<const char*> (CFDataGetBytePtr (cf_data));
   const size_t length = CFDataGetLength (cf_data);
   if (!data || !length)
-    return NULL;
+  {
+    CFRelease (cf_data);
+    return nullptr;
+  }
 
   return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY,
 			 reinterpret_cast<void *> (const_cast<__CFData *> (cf_data)),
 			 release_table_data);
 }
 
-hb_face_t *
-hb_coretext_face_create (CGFontRef cg_font)
+static void
+_hb_cg_font_release (void *data)
 {
-  return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), (hb_destroy_func_t) CGFontRelease);
+  CGFontRelease ((CGFontRef) data);
 }
 
 
 HB_SHAPER_DATA_ENSURE_DEFINE(coretext, face)
-HB_SHAPER_DATA_ENSURE_DEFINE(coretext, font)
-
-
-/*
- * shaper face data
- */
+HB_SHAPER_DATA_ENSURE_DEFINE_WITH_CONDITION(coretext, font,
+	fabs (CTFontGetSize((CTFontRef) data) - coretext_font_size_from_ptem (font->ptem)) <= .5
+)
 
 static CTFontDescriptorRef
 get_last_resort_font_desc (void)
@@ -104,7 +123,7 @@
 release_data (void *info, const void *data, size_t size)
 {
   assert (hb_blob_get_length ((hb_blob_t *) info) == size &&
-          hb_blob_get_data ((hb_blob_t *) info, NULL) == data);
+          hb_blob_get_data ((hb_blob_t *) info, nullptr) == data);
 
   hb_blob_destroy ((hb_blob_t *) info);
 }
@@ -112,8 +131,8 @@
 static CGFontRef
 create_cg_font (hb_face_t *face)
 {
-  CGFontRef cg_font = NULL;
-  if (face->destroy == (hb_destroy_func_t) CGFontRelease)
+  CGFontRef cg_font = nullptr;
+  if (face->destroy == _hb_cg_font_release)
   {
     cg_font = CGFontRetain ((CGFontRef) face->user_data);
   }
@@ -140,10 +159,36 @@
 static CTFontRef
 create_ct_font (CGFontRef cg_font, CGFloat font_size)
 {
-  CTFontRef ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, NULL, NULL);
+  CTFontRef ct_font = nullptr;
+
+  /* CoreText does not enable trak table usage / tracking when creating a CTFont
+   * using CTFontCreateWithGraphicsFont. The only way of enabling tracking seems
+   * to be through the CTFontCreateUIFontForLanguage call. */
+  CFStringRef cg_postscript_name = CGFontCopyPostScriptName (cg_font);
+  if (CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSText")) ||
+      CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSDisplay")))
+  {
+    CTFontUIFontType font_type = kCTFontUIFontSystem;
+    if (CFStringHasSuffix (cg_postscript_name, CFSTR ("-Bold")))
+      font_type = kCTFontUIFontEmphasizedSystem;
+
+    ct_font = CTFontCreateUIFontForLanguage (font_type, font_size, nullptr);
+    CFStringRef ct_result_name = CTFontCopyPostScriptName(ct_font);
+    if (CFStringCompare (ct_result_name, cg_postscript_name, 0) != kCFCompareEqualTo)
+    {
+      CFRelease(ct_font);
+      ct_font = nullptr;
+    }
+    CFRelease (ct_result_name);
+  }
+  CFRelease (cg_postscript_name);
+
+  if (!ct_font)
+    ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, nullptr, nullptr);
+
   if (unlikely (!ct_font)) {
     DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed");
-    return NULL;
+    return nullptr;
   }
 
   /* crbug.com/576941 and crbug.com/625902 and the investigation in the latter
@@ -153,7 +198,7 @@
    * reconfiguring the cascade list causes CoreText crashes. For details, see
    * crbug.com/549610 */
   // 0x00070000 stands for "kCTVersionNumber10_10", see CoreText.h
-  if (&CTGetCoreTextVersion != NULL && CTGetCoreTextVersion() < 0x00070000) {
+  if (&CTGetCoreTextVersion != nullptr && CTGetCoreTextVersion() < 0x00070000) {
     CFStringRef fontName = CTFontCopyPostScriptName (ct_font);
     bool isEmojiFont = CFStringCompare (fontName, CFSTR("AppleColorEmoji"), 0) == kCFCompareEqualTo;
     CFRelease (fontName);
@@ -167,7 +212,7 @@
    * font fallback which we don't need anyway. */
   {
     CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc ();
-    CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, NULL, last_resort_font_desc);
+    CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, last_resort_font_desc);
     CFRelease (last_resort_font_desc);
     if (new_ct_font)
     {
@@ -202,51 +247,30 @@
   return ct_font;
 }
 
-struct hb_coretext_shaper_face_data_t {
-  CGFontRef cg_font;
-  CTFontRef ct_font;
-};
-
 hb_coretext_shaper_face_data_t *
 _hb_coretext_shaper_face_data_create (hb_face_t *face)
 {
-  hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t));
-  if (unlikely (!data))
-    return NULL;
+  CGFontRef cg_font = create_cg_font (face);
 
-  data->cg_font = create_cg_font (face);
-  if (unlikely (!data->cg_font))
+  if (unlikely (!cg_font))
   {
     DEBUG_MSG (CORETEXT, face, "CGFont creation failed..");
-    free (data);
-    return NULL;
+    return nullptr;
   }
 
-  /* We use 36pt size instead of UPEM, because CoreText implements the 'trak' table,
-   * which can make the font too tight at large sizes.  36pt should be a good semi-neutral
-   * size.
-   *
-   * Since we always create CTFont at a fixed size, our CTFont lives in face_data
-   * instead of font_data.  Which is good, because when people change scale on
-   * hb_font_t, we won't need to update our CTFont. */
-  data->ct_font = create_ct_font (data->cg_font, 36.);
-  if (unlikely (!data->ct_font))
-  {
-    DEBUG_MSG (CORETEXT, face, "CTFont creation failed.");
-    CFRelease (data->cg_font);
-    free (data);
-    return NULL;
-  }
-
-  return data;
+  return (hb_coretext_shaper_face_data_t *) cg_font;
 }
 
 void
 _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data)
 {
-  CFRelease (data->ct_font);
-  CFRelease (data->cg_font);
-  free (data);
+  CFRelease ((CGFontRef) data);
+}
+
+hb_face_t *
+hb_coretext_face_create (CGFontRef cg_font)
+{
+  return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), _hb_cg_font_release);
 }
 
 /*
@@ -255,29 +279,66 @@
 CGFontRef
 hb_coretext_face_get_cg_font (hb_face_t *face)
 {
-  if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL;
-  hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-  return face_data->cg_font;
+  if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr;
+  return (CGFontRef) HB_SHAPER_DATA_GET (face);
 }
 
 
-/*
- * shaper font data
- */
-
-struct hb_coretext_shaper_font_data_t {};
-
 hb_coretext_shaper_font_data_t *
-_hb_coretext_shaper_font_data_create (hb_font_t *font HB_UNUSED)
+_hb_coretext_shaper_font_data_create (hb_font_t *font)
 {
-  return (hb_coretext_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  hb_face_t *face = font->face;
+  if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr;
+  CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face);
+
+  CTFontRef ct_font = create_ct_font (cg_font, coretext_font_size_from_ptem (font->ptem));
+
+  if (unlikely (!ct_font))
+  {
+    DEBUG_MSG (CORETEXT, font, "CGFont creation failed..");
+    return nullptr;
+  }
+
+  return (hb_coretext_shaper_font_data_t *) ct_font;
 }
 
 void
 _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data)
 {
+  CFRelease ((CTFontRef) data);
 }
 
+/*
+ * Since: 1.7.2
+ */
+hb_font_t *
+hb_coretext_font_create (CTFontRef ct_font)
+{
+  CGFontRef cg_font = CTFontCopyGraphicsFont (ct_font, nullptr);
+  hb_face_t *face = hb_coretext_face_create (cg_font);
+  CFRelease (cg_font);
+  hb_font_t *font = hb_font_create (face);
+  hb_face_destroy (face);
+
+  if (unlikely (hb_object_is_inert (font)))
+    return font;
+
+  hb_font_set_ptem (font, coretext_font_size_to_ptem (CTFontGetSize(ct_font)));
+
+  /* Let there be dragons here... */
+  HB_SHAPER_DATA_GET (font) = (hb_coretext_shaper_font_data_t *) CFRetain (ct_font);
+
+  return font;
+}
+
+CTFontRef
+hb_coretext_font_get_ct_font (hb_font_t *font)
+{
+  if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return nullptr;
+  return (CTFontRef) HB_SHAPER_DATA_GET (font);
+}
+
+
 
 /*
  * shaper shape_plan data
@@ -300,15 +361,6 @@
 {
 }
 
-CTFontRef
-hb_coretext_font_get_ct_font (hb_font_t *font)
-{
-  hb_face_t *face = font->face;
-  if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL;
-  hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-  return face_data->ct_font;
-}
-
 
 /*
  * shaper
@@ -323,7 +375,9 @@
   feature_record_t rec;
   unsigned int order;
 
-  static int cmp (const active_feature_t *a, const active_feature_t *b) {
+  static int cmp (const void *pa, const void *pb) {
+    const active_feature_t *a = (const active_feature_t *) pa;
+    const active_feature_t *b = (const active_feature_t *) pb;
     return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 :
 	   a->order < b->order ? -1 : a->order > b->order ? 1 :
 	   a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 :
@@ -339,7 +393,9 @@
   bool start;
   active_feature_t feature;
 
-  static int cmp (const feature_event_t *a, const feature_event_t *b) {
+  static int cmp (const void *pa, const void *pb) {
+    const feature_event_t *a = (const feature_event_t *) pa;
+    const feature_event_t *b = (const feature_event_t *) pb;
     return a->index < b->index ? -1 : a->index > b->index ? 1 :
 	   a->start < b->start ? -1 : a->start > b->start ? 1 :
 	   active_feature_t::cmp (&a->feature, &b->feature);
@@ -538,9 +594,10 @@
                     unsigned int        num_features)
 {
   hb_face_t *face = font->face;
-  hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
+  CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face);
+  CTFontRef ct_font = (CTFontRef) HB_SHAPER_DATA_GET (font);
 
-  CGFloat ct_font_size = CTFontGetSize (face_data->ct_font);
+  CGFloat ct_font_size = CTFontGetSize (ct_font);
   CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size;
   CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size;
 
@@ -641,22 +698,23 @@
 	  /* active_features.qsort (); */
 	  for (unsigned int j = 0; j < active_features.len; j++)
 	  {
-	    CFStringRef keys[2] = {
+	    CFStringRef keys[] = {
 	      kCTFontFeatureTypeIdentifierKey,
 	      kCTFontFeatureSelectorIdentifierKey
 	    };
-	    CFNumberRef values[2] = {
+	    CFNumberRef values[] = {
 	      CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature),
 	      CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting)
 	    };
+	    static_assert ((ARRAY_LENGTH_CONST (keys) == ARRAY_LENGTH_CONST (values)), "");
 	    CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault,
 						       (const void **) keys,
 						       (const void **) values,
-						       2,
+						       ARRAY_LENGTH (keys),
 						       &kCFTypeDictionaryKeyCallBacks,
 						       &kCFTypeDictionaryValueCallBacks);
-	    CFRelease (values[0]);
-	    CFRelease (values[1]);
+	    for (unsigned int i = 0; i < ARRAY_LENGTH (values); i++)
+	      CFRelease (values[i]);
 
 	    CFArrayAppendValue (features_array, dict);
 	    CFRelease (dict);
@@ -674,12 +732,12 @@
 	  CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes);
 	  CFRelease (attributes);
 
-	  range->font = CTFontCreateCopyWithAttributes (face_data->ct_font, 0.0, NULL, font_desc);
+	  range->font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, font_desc);
 	  CFRelease (font_desc);
 	}
 	else
 	{
-	  range->font = NULL;
+	  range->font = nullptr;
 	}
 
 	range->index_first = last_index;
@@ -699,9 +757,6 @@
 	  active_features.remove (feature - active_features.array);
       }
     }
-
-    if (!range_records.len) /* No active feature found. */
-      goto fail_features;
   }
   else
   {
@@ -752,14 +807,14 @@
 
 #define FAIL(...) \
   HB_STMT_START { \
-    DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
+    DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \
     ret = false; \
     goto fail; \
   } HB_STMT_END;
 
   bool ret = true;
-  CFStringRef string_ref = NULL;
-  CTLineRef line = NULL;
+  CFStringRef string_ref = nullptr;
+  CTLineRef line = nullptr;
 
   if (0)
   {
@@ -771,8 +826,8 @@
     assert (line);
     CFRelease (string_ref);
     CFRelease (line);
-    string_ref = NULL;
-    line = NULL;
+    string_ref = nullptr;
+    line = nullptr;
 
     /* Get previous start-of-scratch-area, that we use later for readjusting
      * our existing scratch arrays. */
@@ -793,7 +848,7 @@
     scratch_size -= old_scratch_used;
   }
   {
-    string_ref = CFStringCreateWithCharactersNoCopy (NULL,
+    string_ref = CFStringCreateWithCharactersNoCopy (nullptr,
 						     pchars, chars_len,
 						     kCFAllocatorNull);
     if (unlikely (!string_ref))
@@ -825,15 +880,18 @@
 							    kCFStringEncodingUTF8,
 							    kCFAllocatorNull);
 	if (unlikely (!lang))
+        {
+	  CFRelease (attr_string);
 	  FAIL ("CFStringCreateWithCStringNoCopy failed");
+        }
 	CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
 					kCTLanguageAttributeName, lang);
 	CFRelease (lang);
       }
       CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
-				      kCTFontAttributeName, face_data->ct_font);
+				      kCTFontAttributeName, ct_font);
 
-      if (num_features)
+      if (num_features && range_records.len)
       {
 	unsigned int start = 0;
 	range_record_t *last_range = &range_records[0];
@@ -859,6 +917,30 @@
 	  CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start),
 					  kCTFontAttributeName, last_range->font);
       }
+      /* Enable/disable kern if requested.
+       *
+       * Note: once kern is disabled, reenabling it doesn't currently seem to work in CoreText.
+       */
+      if (num_features)
+      {
+	unsigned int zeroint = 0;
+	CFNumberRef zero = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &zeroint);
+	for (unsigned int i = 0; i < num_features; i++)
+	{
+	  const hb_feature_t &feature = features[i];
+	  if (feature.tag == HB_TAG('k','e','r','n') &&
+	      feature.start < chars_len && feature.start < feature.end)
+	  {
+	    CFRange feature_range = CFRangeMake (feature.start,
+	                                         MIN (feature.end, chars_len) - feature.start);
+	    if (feature.value)
+	      CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName);
+	    else
+	      CFAttributedStringSetAttribute (attr_string, feature_range, kCTKernAttributeName, zero);
+	  }
+	}
+	CFRelease (zero);
+      }
 
       int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
       CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level);
@@ -868,8 +950,12 @@
 						    1,
 						    &kCFTypeDictionaryKeyCallBacks,
 						    &kCFTypeDictionaryValueCallBacks);
+      CFRelease (level_number);
       if (unlikely (!options))
+      {
+        CFRelease (attr_string);
         FAIL ("CFDictionaryCreate failed");
+      }
 
       CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options);
       CFRelease (options);
@@ -885,7 +971,7 @@
 
     CFArrayRef glyph_runs = CTLineGetGlyphRuns (line);
     unsigned int num_runs = CFArrayGetCount (glyph_runs);
-    DEBUG_MSG (CORETEXT, NULL, "Num runs: %d", num_runs);
+    DEBUG_MSG (CORETEXT, nullptr, "Num runs: %d", num_runs);
 
     buffer->len = 0;
     uint32_t status_and = ~0, status_or = 0;
@@ -911,7 +997,7 @@
       status_or  |= run_status;
       status_and &= run_status;
       DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status);
-      double run_advance = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL);
+      double run_advance = CTRunGetTypographicBounds (run, range_all, nullptr, nullptr, nullptr);
       if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction))
 	  run_advance = -run_advance;
       DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance);
@@ -924,7 +1010,7 @@
        */
       CFDictionaryRef attributes = CTRunGetAttributes (run);
       CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName));
-      if (!CFEqual (run_ct_font, face_data->ct_font))
+      if (!CFEqual (run_ct_font, ct_font))
       {
 	/* The run doesn't use our main font instance.  We have to figure out
 	 * whether font fallback happened, or this is just CoreText giving us
@@ -946,7 +1032,7 @@
 	 * However, even that wouldn't work if we were passed in the CGFont to
 	 * construct a hb_face to begin with.
 	 *
-	 * See: http://github.com/behdad/harfbuzz/pull/36
+	 * See: http://github.com/harfbuzz/harfbuzz/pull/36
 	 *
 	 * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098
 	 */
@@ -959,16 +1045,16 @@
 	  }
 	if (!matched)
 	{
-	  CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0);
+	  CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, nullptr);
 	  if (run_cg_font)
 	  {
-	    matched = CFEqual (run_cg_font, face_data->cg_font);
+	    matched = CFEqual (run_cg_font, cg_font);
 	    CFRelease (run_cg_font);
 	  }
 	}
 	if (!matched)
 	{
-	  CFStringRef font_ps_name = CTFontCopyName (face_data->ct_font, kCTFontPostScriptNameKey);
+	  CFStringRef font_ps_name = CTFontCopyName (ct_font, kCTFontPostScriptNameKey);
 	  CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey);
 	  CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0);
 	  CFRelease (run_ps_name);
@@ -1037,7 +1123,7 @@
 
       /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always
        * succeed, and so copying data to our own buffer will be rare.  Reports
-       * have it that this changed in OS X 10.10 Yosemite, and NULL is returned
+       * have it that this changed in OS X 10.10 Yosemite, and nullptr is returned
        * frequently.  At any rate, we can test that codepath by setting USE_PTR
        * to false. */
 
@@ -1053,13 +1139,13 @@
 
       { /* Setup glyphs */
         SCRATCH_SAVE();
-	const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : NULL;
+	const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : nullptr;
 	if (!glyphs) {
 	  ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry);
 	  CTRunGetGlyphs (run, range_all, glyph_buf);
 	  glyphs = glyph_buf;
 	}
-	const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : NULL;
+	const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : nullptr;
 	if (!string_indices) {
 	  ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry);
 	  CTRunGetStringIndices (run, range_all, index_buf);
@@ -1081,7 +1167,7 @@
 	 * advance (in the advance direction only), and for last glyph we set
 	 * whatever is needed to make the whole run's advance add up. */
         SCRATCH_SAVE();
-	const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : NULL;
+	const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : nullptr;
 	if (!positions) {
 	  ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry);
 	  CTRunGetPositions (run, range_all, position_buf);
@@ -1132,7 +1218,7 @@
     }
 
     /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel,
-     * or if it does, it doesn't resepct it.  So we get runs with wrong
+     * or if it does, it doesn't respect it.  So we get runs with wrong
      * directions.  As such, disable the assert...  It wouldn't crash, but
      * cursoring will be off...
      *
@@ -1157,6 +1243,7 @@
 	pos->x_advance = info->mask;
 	pos->x_offset = info->var1.i32;
 	pos->y_offset = info->var2.i32;
+
 	info++, pos++;
       }
     else
@@ -1165,6 +1252,7 @@
 	pos->y_advance = info->mask;
 	pos->x_offset = info->var1.i32;
 	pos->y_offset = info->var2.i32;
+
 	info++, pos++;
       }
 
@@ -1202,6 +1290,8 @@
     }
   }
 
+  buffer->unsafe_to_break_all ();
+
 #undef FAIL
 
 fail:
@@ -1234,22 +1324,20 @@
 hb_coretext_aat_shaper_face_data_t *
 _hb_coretext_aat_shaper_face_data_create (hb_face_t *face)
 {
-  hb_blob_t *mort_blob = face->reference_table (HB_CORETEXT_TAG_MORT);
-  /* Umm, we just reference the table to check whether it exists.
-   * Maybe add better API for this? */
-  if (!hb_blob_get_length (mort_blob))
-  {
-    hb_blob_destroy (mort_blob);
-    mort_blob = face->reference_table (HB_CORETEXT_TAG_MORX);
-    if (!hb_blob_get_length (mort_blob))
-    {
-      hb_blob_destroy (mort_blob);
-      return NULL;
-    }
-  }
-  hb_blob_destroy (mort_blob);
+  static const hb_tag_t tags[] = {HB_CORETEXT_TAG_MORX, HB_CORETEXT_TAG_MORT, HB_CORETEXT_TAG_KERX};
 
-  return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL;
+  for (unsigned int i = 0; i < ARRAY_LENGTH (tags); i++)
+  {
+    hb_blob_t *blob = face->reference_table (tags[i]);
+    if (hb_blob_get_length (blob))
+    {
+      hb_blob_destroy (blob);
+      return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
+    }
+    hb_blob_destroy (blob);
+  }
+
+  return nullptr;
 }
 
 void
@@ -1267,7 +1355,7 @@
 hb_coretext_aat_shaper_font_data_t *
 _hb_coretext_aat_shaper_font_data_create (hb_font_t *font)
 {
-  return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL;
+  return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
 }
 
 void
diff --git a/src/hb-coretext.h b/src/hb-coretext.h
index 82066e4..4b0a6f0 100644
--- a/src/hb-coretext.h
+++ b/src/hb-coretext.h
@@ -42,11 +42,15 @@
 
 #define HB_CORETEXT_TAG_MORT HB_TAG('m','o','r','t')
 #define HB_CORETEXT_TAG_MORX HB_TAG('m','o','r','x')
+#define HB_CORETEXT_TAG_KERX HB_TAG('k','e','r','x')
 
 
 HB_EXTERN hb_face_t *
 hb_coretext_face_create (CGFontRef cg_font);
 
+HB_EXTERN hb_font_t *
+hb_coretext_font_create (CTFontRef ct_font);
+
 
 HB_EXTERN CGFontRef
 hb_coretext_face_get_cg_font (hb_face_t *face);
diff --git a/src/hb-debug.hh b/src/hb-debug.hh
new file mode 100644
index 0000000..c244347
--- /dev/null
+++ b/src/hb-debug.hh
@@ -0,0 +1,444 @@
+/*
+ * 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_DEBUG_HH
+#define HB_DEBUG_HH
+
+#include "hb-private.hh"
+
+
+#ifndef HB_DEBUG
+#define HB_DEBUG 0
+#endif
+
+static inline bool
+_hb_debug (unsigned int level,
+	   unsigned int max_level)
+{
+  return level < max_level;
+}
+
+#define DEBUG_LEVEL_ENABLED(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT))
+#define DEBUG_ENABLED(WHAT) (DEBUG_LEVEL_ENABLED (WHAT, 0))
+
+static inline void
+_hb_print_func (const char *func)
+{
+  if (func)
+  {
+    unsigned int func_len = strlen (func);
+    /* Skip "static" */
+    if (0 == strncmp (func, "static ", 7))
+      func += 7;
+    /* Skip "typename" */
+    if (0 == strncmp (func, "typename ", 9))
+      func += 9;
+    /* Skip return type */
+    const char *space = strchr (func, ' ');
+    if (space)
+      func = space + 1;
+    /* Skip parameter list */
+    const char *paren = strchr (func, '(');
+    if (paren)
+      func_len = paren - func;
+    fprintf (stderr, "%.*s", func_len, func);
+  }
+}
+
+template <int max_level> static inline void
+_hb_debug_msg_va (const char *what,
+		  const void *obj,
+		  const char *func,
+		  bool indented,
+		  unsigned int level,
+		  int level_dir,
+		  const char *message,
+		  va_list ap) HB_PRINTF_FUNC(7, 0);
+template <int max_level> static inline void
+_hb_debug_msg_va (const char *what,
+		  const void *obj,
+		  const char *func,
+		  bool indented,
+		  unsigned int level,
+		  int level_dir,
+		  const char *message,
+		  va_list ap)
+{
+  if (!_hb_debug (level, max_level))
+    return;
+
+  fprintf (stderr, "%-10s", what ? what : "");
+
+  if (obj)
+    fprintf (stderr, "(%*p) ", (unsigned int) (2 * sizeof (void *)), obj);
+  else
+    fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), "");
+
+  if (indented) {
+#define VBAR	"\342\224\202"	/* U+2502 BOX DRAWINGS LIGHT VERTICAL */
+#define VRBAR	"\342\224\234"	/* U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
+#define DLBAR	"\342\225\256"	/* U+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT */
+#define ULBAR	"\342\225\257"	/* U+256F BOX DRAWINGS LIGHT ARC UP AND LEFT */
+#define LBAR	"\342\225\264"	/* U+2574 BOX DRAWINGS LIGHT LEFT */
+    static const char bars[] =
+      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
+      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
+      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
+      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
+      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR;
+    fprintf (stderr, "%2u %s" VRBAR "%s",
+	     level,
+	     bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level),
+	     level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR);
+  } else
+    fprintf (stderr, "   " VRBAR LBAR);
+
+  _hb_print_func (func);
+
+  if (message)
+  {
+    fprintf (stderr, ": ");
+    vfprintf (stderr, message, ap);
+  }
+
+  fprintf (stderr, "\n");
+}
+template <> inline void
+_hb_debug_msg_va<0> (const char *what HB_UNUSED,
+		     const void *obj HB_UNUSED,
+		     const char *func HB_UNUSED,
+		     bool indented HB_UNUSED,
+		     unsigned int level HB_UNUSED,
+		     int level_dir HB_UNUSED,
+		     const char *message HB_UNUSED,
+		     va_list ap HB_UNUSED) {}
+
+template <int max_level> static inline void
+_hb_debug_msg (const char *what,
+	       const void *obj,
+	       const char *func,
+	       bool indented,
+	       unsigned int level,
+	       int level_dir,
+	       const char *message,
+	       ...) HB_PRINTF_FUNC(7, 8);
+template <int max_level> static inline void
+_hb_debug_msg (const char *what,
+	       const void *obj,
+	       const char *func,
+	       bool indented,
+	       unsigned int level,
+	       int level_dir,
+	       const char *message,
+	       ...)
+{
+  va_list ap;
+  va_start (ap, message);
+  _hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap);
+  va_end (ap);
+}
+template <> inline void
+_hb_debug_msg<0> (const char *what HB_UNUSED,
+		  const void *obj HB_UNUSED,
+		  const char *func HB_UNUSED,
+		  bool indented HB_UNUSED,
+		  unsigned int level HB_UNUSED,
+		  int level_dir HB_UNUSED,
+		  const char *message HB_UNUSED,
+		  ...) HB_PRINTF_FUNC(7, 8);
+template <> inline void
+_hb_debug_msg<0> (const char *what HB_UNUSED,
+		  const void *obj HB_UNUSED,
+		  const char *func HB_UNUSED,
+		  bool indented HB_UNUSED,
+		  unsigned int level HB_UNUSED,
+		  int level_dir HB_UNUSED,
+		  const char *message HB_UNUSED,
+		  ...) {}
+
+#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...)	_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), nullptr,    true, (LEVEL), (LEVEL_DIR), __VA_ARGS__)
+#define DEBUG_MSG(WHAT, OBJ, ...) 				_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), nullptr,    false, 0, 0, __VA_ARGS__)
+#define DEBUG_MSG_FUNC(WHAT, OBJ, ...)				_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__)
+
+
+/*
+ * Printer
+ */
+
+template <typename T>
+struct hb_printer_t {
+  const char *print (const T&) { return "something"; }
+};
+
+template <>
+struct hb_printer_t<bool> {
+  const char *print (bool v) { return v ? "true" : "false"; }
+};
+
+template <>
+struct hb_printer_t<hb_void_t> {
+  const char *print (hb_void_t) { return ""; }
+};
+
+
+/*
+ * Trace
+ */
+
+template <typename T>
+static inline void _hb_warn_no_return (bool returned)
+{
+  if (unlikely (!returned)) {
+    fprintf (stderr, "OUCH, returned with no call to return_trace().  This is a bug, please report.\n");
+  }
+}
+template <>
+/*static*/ inline void _hb_warn_no_return<hb_void_t> (bool returned HB_UNUSED)
+{}
+
+template <int max_level, typename ret_t>
+struct hb_auto_trace_t
+{
+  explicit inline hb_auto_trace_t (unsigned int *plevel_,
+				   const char *what_,
+				   const void *obj_,
+				   const char *func,
+				   const char *message,
+				   ...) HB_PRINTF_FUNC(6, 7)
+				   : plevel (plevel_), what (what_), obj (obj_), returned (false)
+  {
+    if (plevel) ++*plevel;
+
+    va_list ap;
+    va_start (ap, message);
+    _hb_debug_msg_va<max_level> (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap);
+    va_end (ap);
+  }
+  inline ~hb_auto_trace_t (void)
+  {
+    _hb_warn_no_return<ret_t> (returned);
+    if (!returned) {
+      _hb_debug_msg<max_level> (what, obj, nullptr, true, plevel ? *plevel : 1, -1, " ");
+    }
+    if (plevel) --*plevel;
+  }
+
+  inline ret_t ret (ret_t v, unsigned int line = 0)
+  {
+    if (unlikely (returned)) {
+      fprintf (stderr, "OUCH, double calls to return_trace().  This is a bug, please report.\n");
+      return v;
+    }
+
+    _hb_debug_msg<max_level> (what, obj, nullptr, true, plevel ? *plevel : 1, -1,
+			      "return %s (line %d)",
+			      hb_printer_t<ret_t>().print (v), line);
+    if (plevel) --*plevel;
+    plevel = nullptr;
+    returned = true;
+    return v;
+  }
+
+  private:
+  unsigned int *plevel;
+  const char *what;
+  const void *obj;
+  bool returned;
+};
+template <typename ret_t> /* Make sure we don't use hb_auto_trace_t when not tracing. */
+struct hb_auto_trace_t<0, ret_t>
+{
+  explicit inline hb_auto_trace_t (unsigned int *plevel_,
+				   const char *what_,
+				   const void *obj_,
+				   const char *func,
+				   const char *message,
+				   ...) HB_PRINTF_FUNC(6, 7) {}
+
+  inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; }
+};
+
+/* For disabled tracing; optimize out everything.
+ * https://github.com/harfbuzz/harfbuzz/pull/605 */
+template <typename ret_t>
+struct hb_no_trace_t {
+  inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; }
+};
+
+#define return_trace(RET) return trace.ret (RET, __LINE__)
+
+
+/*
+ * Instances.
+ */
+
+#ifndef HB_DEBUG_ARABIC
+#define HB_DEBUG_ARABIC (HB_DEBUG+0)
+#endif
+
+#ifndef HB_DEBUG_BLOB
+#define HB_DEBUG_BLOB (HB_DEBUG+0)
+#endif
+
+#ifndef HB_DEBUG_CORETEXT
+#define HB_DEBUG_CORETEXT (HB_DEBUG+0)
+#endif
+
+#ifndef HB_DEBUG_DIRECTWRITE
+#define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0)
+#endif
+
+#ifndef HB_DEBUG_FT
+#define HB_DEBUG_FT (HB_DEBUG+0)
+#endif
+
+#ifndef HB_DEBUG_GET_COVERAGE
+#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0)
+#endif
+
+#ifndef HB_DEBUG_OBJECT
+#define HB_DEBUG_OBJECT (HB_DEBUG+0)
+#endif
+
+#ifndef HB_DEBUG_SHAPE_PLAN
+#define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0)
+#endif
+
+#ifndef HB_DEBUG_UNISCRIBE
+#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0)
+#endif
+
+/*
+ * With tracing.
+ */
+
+#ifndef HB_DEBUG_APPLY
+#define HB_DEBUG_APPLY (HB_DEBUG+0)
+#endif
+#if HB_DEBUG_APPLY
+#define TRACE_APPLY(this) \
+	hb_auto_trace_t<HB_DEBUG_APPLY, bool> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 "idx %d gid %u lookup %d", \
+	 c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index)
+#else
+#define TRACE_APPLY(this) hb_no_trace_t<bool> trace
+#endif
+
+#ifndef HB_DEBUG_CLOSURE
+#define HB_DEBUG_CLOSURE (HB_DEBUG+0)
+#endif
+#if HB_DEBUG_CLOSURE
+#define TRACE_CLOSURE(this) \
+	hb_auto_trace_t<HB_DEBUG_CLOSURE, hb_void_t> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 " ")
+#else
+#define TRACE_CLOSURE(this) hb_no_trace_t<hb_void_t> trace HB_UNUSED
+#endif
+
+#ifndef HB_DEBUG_COLLECT_GLYPHS
+#define HB_DEBUG_COLLECT_GLYPHS (HB_DEBUG+0)
+#endif
+#if HB_DEBUG_COLLECT_GLYPHS
+#define TRACE_COLLECT_GLYPHS(this) \
+	hb_auto_trace_t<HB_DEBUG_COLLECT_GLYPHS, hb_void_t> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 " ")
+#else
+#define TRACE_COLLECT_GLYPHS(this) hb_no_trace_t<hb_void_t> trace HB_UNUSED
+#endif
+
+#ifndef HB_DEBUG_SANITIZE
+#define HB_DEBUG_SANITIZE (HB_DEBUG+0)
+#endif
+#if HB_DEBUG_SANITIZE
+#define TRACE_SANITIZE(this) \
+	hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 " ");
+#else
+#define TRACE_SANITIZE(this) hb_no_trace_t<bool> trace
+#endif
+
+#ifndef HB_DEBUG_SERIALIZE
+#define HB_DEBUG_SERIALIZE (HB_DEBUG+0)
+#endif
+#if HB_DEBUG_SERIALIZE
+#define TRACE_SERIALIZE(this) \
+	hb_auto_trace_t<HB_DEBUG_SERIALIZE, bool> trace \
+	(&c->debug_depth, "SERIALIZE", c, HB_FUNC, \
+	 " ");
+#else
+#define TRACE_SERIALIZE(this) hb_no_trace_t<bool> trace
+#endif
+
+#ifndef HB_DEBUG_SUBSET
+#define HB_DEBUG_SUBSET (HB_DEBUG+0)
+#endif
+#if HB_DEBUG_SUBSET
+#define TRACE_SUBSET(this) \
+  hb_auto_trace_t<HB_DEBUG_SUBSET, bool> trace \
+  (&c->debug_depth, c->get_name (), this, HB_FUNC, \
+   " ");
+#else
+#define TRACE_SUBSET(this) hb_no_trace_t<bool> trace
+#endif
+
+#ifndef HB_DEBUG_WOULD_APPLY
+#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
+#endif
+#if HB_DEBUG_WOULD_APPLY
+#define TRACE_WOULD_APPLY(this) \
+	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY, bool> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 "%d glyphs", c->len);
+#else
+#define TRACE_WOULD_APPLY(this) hb_no_trace_t<bool> trace
+#endif
+
+#ifndef HB_DEBUG_DISPATCH
+#define HB_DEBUG_DISPATCH ( \
+	HB_DEBUG_APPLY + \
+	HB_DEBUG_CLOSURE + \
+	HB_DEBUG_COLLECT_GLYPHS + \
+	HB_DEBUG_SANITIZE + \
+	HB_DEBUG_SERIALIZE + \
+  HB_DEBUG_SUBSET + \
+	HB_DEBUG_WOULD_APPLY + \
+	0)
+#endif
+#if HB_DEBUG_DISPATCH
+#define TRACE_DISPATCH(this, format) \
+	hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 "format %d", (int) format);
+#else
+#define TRACE_DISPATCH(this, format) hb_no_trace_t<typename context_t::return_t> trace
+#endif
+
+
+#endif /* HB_DEBUG_HH */
diff --git a/src/hb-deprecated.h b/src/hb-deprecated.h
index 0398dfa..eac7efb 100644
--- a/src/hb-deprecated.h
+++ b/src/hb-deprecated.h
@@ -34,6 +34,7 @@
 #include "hb-common.h"
 #include "hb-unicode.h"
 #include "hb-font.h"
+#include "hb-set.h"
 
 HB_BEGIN_DECLS
 
@@ -54,6 +55,9 @@
 			      hb_font_get_glyph_func_t func,
 			      void *user_data, hb_destroy_func_t destroy);
 
+HB_EXTERN void
+hb_set_invert (hb_set_t *set);
+
 #endif
 
 HB_END_DECLS
diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc
index ab07d8a..69a8aa2 100644
--- a/src/hb-directwrite.cc
+++ b/src/hb-directwrite.cc
@@ -22,6 +22,8 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
+#include "hb-private.hh"
+#include "hb-debug.hh"
 #define HB_SHAPER directwrite
 #include "hb-shaper-impl-private.hh"
 
@@ -30,10 +32,6 @@
 #include "hb-directwrite.h"
 
 
-#ifndef HB_DEBUG_DIRECTWRITE
-#define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0)
-#endif
-
 HB_SHAPER_DATA_ENSURE_DEFINE(directwrite, face)
 HB_SHAPER_DATA_ENSURE_DEFINE(directwrite, font)
 
@@ -140,7 +138,7 @@
   hb_directwrite_shaper_face_data_t *data =
     (hb_directwrite_shaper_face_data_t *) malloc (sizeof (hb_directwrite_shaper_face_data_t));
   if (unlikely (!data))
-    return NULL;
+    return nullptr;
 
   // TODO: factory and fontFileLoader should be cached separately
   IDWriteFactory* dwriteFactory;
@@ -153,7 +151,7 @@
   HRESULT hr;
   hb_blob_t *blob = hb_face_reference_blob (face);
   IDWriteFontFileStream *fontFileStream = new DWriteFontFileStream (
-    (uint8_t*) hb_blob_get_data (blob, NULL), hb_blob_get_length (blob));
+    (uint8_t*) hb_blob_get_data (blob, nullptr), hb_blob_get_length (blob));
 
   IDWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream);
   dwriteFactory->RegisterFontFileLoader (fontFileLoader);
@@ -165,7 +163,7 @@
 
 #define FAIL(...) \
   HB_STMT_START { \
-    DEBUG_MSG (DIRECTWRITE, NULL, __VA_ARGS__); \
+    DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
     return false; \
   } HB_STMT_END;
 
@@ -233,12 +231,12 @@
 hb_directwrite_shaper_font_data_t *
 _hb_directwrite_shaper_font_data_create (hb_font_t *font)
 {
-  if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return NULL;
+  if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return nullptr;
 
   hb_directwrite_shaper_font_data_t *data =
     (hb_directwrite_shaper_font_data_t *) malloc (sizeof (hb_directwrite_shaper_font_data_t));
   if (unlikely (!data))
-    return NULL;
+    return nullptr;
 
   return data;
 }
@@ -313,7 +311,7 @@
     , mTextLength(textLength)
     , mLocaleName(localeName)
     , mReadingDirection(readingDirection)
-    , mCurrentRun(NULL) { };
+    , mCurrentRun(nullptr) { };
 
   ~TextAnalysis() {
     // delete runs, except mRunHead which is part of the TextAnalysis object
@@ -337,7 +335,7 @@
     mRunHead.mTextLength = mTextLength;
     mRunHead.mBidiLevel =
       (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
-    mRunHead.nextRun = NULL;
+    mRunHead.nextRun = nullptr;
     mCurrentRun = &mRunHead;
 
     // Call each of the analyzers in sequence, recording their results.
@@ -356,7 +354,7 @@
   {
     if (textPosition >= mTextLength) {
       // No text at this position, valid query though.
-      *textString = NULL;
+      *textString = nullptr;
       *textLength = 0;
     }
     else {
@@ -372,8 +370,8 @@
   {
     if (textPosition == 0 || textPosition > mTextLength) {
       // Either there is no text before here (== 0), or this
-      // is an invalid position. The query is considered valid thouh.
-      *textString = NULL;
+      // is an invalid position. The query is considered valid though.
+      *textString = nullptr;
       *textLength = 0;
     }
     else {
@@ -399,7 +397,7 @@
     OUT IDWriteNumberSubstitution** numberSubstitution)
   {
     // We do not support number substitution.
-    *numberSubstitution = NULL;
+    *numberSubstitution = nullptr;
     *textLength = mTextLength - textPosition;
 
     return S_OK;
@@ -617,14 +615,14 @@
   */
   uint32_t textLength = buffer->len;
 
-  TextAnalysis analysis(textString, textLength, NULL, readingDirection);
+  TextAnalysis analysis(textString, textLength, nullptr, readingDirection);
   TextAnalysis::Run *runHead;
   HRESULT hr;
   hr = analysis.GenerateResults(analyzer, &runHead);
 
 #define FAIL(...) \
   HB_STMT_START { \
-    DEBUG_MSG (DIRECTWRITE, NULL, __VA_ARGS__); \
+    DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
     return false; \
   } HB_STMT_END;
 
@@ -639,7 +637,7 @@
   bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
 
   const wchar_t localeName[20] = {0};
-  if (buffer->props.language != NULL)
+  if (buffer->props.language != nullptr)
   {
     mbstowcs ((wchar_t*) localeName,
       hb_language_to_string (buffer->props.language), 20);
@@ -672,7 +670,7 @@
     malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES));
 
   hr = analyzer->GetGlyphs (textString, textLength, fontFace, false,
-    isRightToLeft, &runHead->mScript, localeName, NULL, &dwFeatures,
+    isRightToLeft, &runHead->mScript, localeName, nullptr, &dwFeatures,
     featureRangeLengths, 1, maxGlyphCount, clusterMap, textProperties, glyphIndices,
     glyphProperties, &glyphCount);
 
@@ -927,8 +925,7 @@
   hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
     features, num_features, width);
 
-  if (res)
-    buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
+  buffer->unsafe_to_break_all ();
 
   return res;
 }
diff --git a/src/hb-dsalgs.hh b/src/hb-dsalgs.hh
new file mode 100644
index 0000000..e413847
--- /dev/null
+++ b/src/hb-dsalgs.hh
@@ -0,0 +1,161 @@
+/*
+ * 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_DSALGS_HH
+#define HB_DSALGS_HH
+
+#include "hb-private.hh"
+
+
+static inline void *
+hb_bsearch_r (const void *key, const void *base,
+	      size_t nmemb, size_t size,
+	      int (*compar)(const void *_key, const void *_item, void *_arg),
+	      void *arg)
+{
+  int min = 0, max = (int) nmemb - 1;
+  while (min <= max)
+  {
+    int mid = (min + max) / 2;
+    const void *p = (const void *) (((const char *) base) + (mid * size));
+    int c = compar (key, p, arg);
+    if (c < 0)
+      max = mid - 1;
+    else if (c > 0)
+      min = mid + 1;
+    else
+      return (void *) p;
+  }
+  return NULL;
+}
+
+
+
+/* From https://github.com/noporpoise/sort_r */
+
+/* Isaac Turner 29 April 2014 Public Domain */
+
+/*
+
+hb_sort_r function to be exported.
+
+Parameters:
+  base is the array to be sorted
+  nel is the number of elements in the array
+  width is the size in bytes of each element of the array
+  compar is the comparison function
+  arg is a pointer to be passed to the comparison function
+
+void hb_sort_r(void *base, size_t nel, size_t width,
+               int (*compar)(const void *_a, const void *_b, void *_arg),
+               void *arg);
+*/
+
+
+/* swap a, b iff a>b */
+/* __restrict is same as restrict but better support on old machines */
+static int sort_r_cmpswap(char *__restrict a, char *__restrict b, size_t w,
+			  int (*compar)(const void *_a, const void *_b,
+					void *_arg),
+			  void *arg)
+{
+  char tmp, *end = a+w;
+  if(compar(a, b, arg) > 0) {
+    for(; a < end; a++, b++) { tmp = *a; *a = *b; *b = tmp; }
+    return 1;
+  }
+  return 0;
+}
+
+/* Note: quicksort is not stable, equivalent values may be swapped */
+static inline void sort_r_simple(void *base, size_t nel, size_t w,
+				 int (*compar)(const void *_a, const void *_b,
+					       void *_arg),
+				 void *arg)
+{
+  char *b = (char *)base, *end = b + nel*w;
+  if(nel < 7) {
+    /* Insertion sort for arbitrarily small inputs */
+    char *pi, *pj;
+    for(pi = b+w; pi < end; pi += w) {
+      for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,arg); pj -= w) {}
+    }
+  }
+  else
+  {
+    /* nel > 6; Quicksort */
+
+    /* Use median of first, middle and last items as pivot */
+    char *x, *y, *xend, ch;
+    char *pl, *pr;
+    char *last = b+w*(nel-1), *tmp;
+    char *l[3];
+    l[0] = b;
+    l[1] = b+w*(nel/2);
+    l[2] = last;
+
+    if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; }
+    if(compar(l[1],l[2],arg) > 0) {
+      tmp=l[1]; l[1]=l[2]; l[2]=tmp; /* swap(l[1],l[2]) */
+      if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; }
+    }
+
+    /* swap l[id], l[2] to put pivot as last element */
+    for(x = l[1], y = last, xend = x+w; x<xend; x++, y++) {
+      ch = *x; *x = *y; *y = ch;
+    }
+
+    pl = b;
+    pr = last;
+
+    while(pl < pr) {
+      for(; pl < pr; pl += w) {
+        if(sort_r_cmpswap(pl, pr, w, compar, arg)) {
+          pr -= w; /* pivot now at pl */
+          break;
+        }
+      }
+      for(; pl < pr; pr -= w) {
+        if(sort_r_cmpswap(pl, pr, w, compar, arg)) {
+          pl += w; /* pivot now at pr */
+          break;
+        }
+      }
+    }
+
+    sort_r_simple(b, (pl-b)/w, w, compar, arg);
+    sort_r_simple(pl+w, (end-(pl+w))/w, w, compar, arg);
+  }
+}
+
+static inline void hb_sort_r(void *base, size_t nel, size_t width,
+			     int (*compar)(const void *_a, const void *_b, void *_arg),
+			     void *arg)
+{
+    sort_r_simple(base, nel, width, compar, arg);
+}
+
+#endif /* HB_DSALGS_HH */
diff --git a/src/hb-face-private.hh b/src/hb-face-private.hh
index eb0e850..43e7b1c 100644
--- a/src/hb-face-private.hh
+++ b/src/hb-face-private.hh
@@ -54,13 +54,6 @@
   mutable unsigned int upem;		/* Units-per-EM. */
   mutable unsigned int num_glyphs;	/* Number of glyphs. */
 
-  enum dirty_t {
-    NOTHING	= 0x0000,
-    INDEX	= 0x0001,
-    UPEM	= 0x0002,
-    NUM_GLYPHS	= 0x0004,
-  } dirty;
-
   struct hb_shaper_data_t shaper_data;	/* Various shaper data. */
 
   /* Various non-shaping data. */
@@ -106,8 +99,6 @@
   HB_INTERNAL void load_num_glyphs (void) const;
 };
 
-HB_MARK_AS_FLAG_T (hb_face_t::dirty_t);
-
 extern HB_INTERNAL const hb_face_t _hb_face_nil;
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
diff --git a/src/hb-face.cc b/src/hb-face.cc
index 1800c99..d8af8c1 100644
--- a/src/hb-face.cc
+++ b/src/hb-face.cc
@@ -43,23 +43,21 @@
 
   true, /* immutable */
 
-  NULL, /* reference_table_func */
-  NULL, /* user_data */
-  NULL, /* destroy */
+  nullptr, /* reference_table_func */
+  nullptr, /* user_data */
+  nullptr, /* destroy */
 
   0,    /* index */
   1000, /* upem */
   0,    /* num_glyphs */
 
-  hb_face_t::NOTHING, /* dirty */
-
   {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
   },
 
-  NULL, /* shape_plans */
+  nullptr, /* shape_plans */
 };
 
 
@@ -111,7 +109,7 @@
 
   closure = (hb_face_for_data_closure_t *) calloc (1, sizeof (hb_face_for_data_closure_t));
   if (unlikely (!closure))
-    return NULL;
+    return nullptr;
 
   closure->blob = blob;
   closure->index = index;
@@ -120,8 +118,10 @@
 }
 
 static void
-_hb_face_for_data_closure_destroy (hb_face_for_data_closure_t *closure)
+_hb_face_for_data_closure_destroy (void *data)
 {
+  hb_face_for_data_closure_t *closure = (hb_face_for_data_closure_t *) data;
+
   hb_blob_destroy (closure->blob);
   free (closure);
 }
@@ -164,14 +164,14 @@
   if (unlikely (!blob))
     blob = hb_blob_get_empty ();
 
-  hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer<OT::OpenTypeFontFile>::sanitize (hb_blob_reference (blob)), index);
+  hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer<OT::OpenTypeFontFile>().sanitize (hb_blob_reference (blob)), index);
 
   if (unlikely (!closure))
     return hb_face_get_empty ();
 
   face = hb_face_create_for_tables (_hb_face_for_data_reference_table,
 				    closure,
-				    (hb_destroy_func_t) _hb_face_for_data_closure_destroy);
+				    _hb_face_for_data_closure_destroy);
 
   face->index = index;
 
@@ -367,11 +367,6 @@
   if (face->immutable)
     return;
 
-  if (face->index == index)
-    return;
-
-  face->dirty |= face->INDEX;
-
   face->index = index;
 }
 
@@ -407,11 +402,6 @@
   if (face->immutable)
     return;
 
-  if (face->upem == upem)
-    return;
-
-  face->dirty |= face->UPEM;
-
   face->upem = upem;
 }
 
@@ -434,7 +424,7 @@
 void
 hb_face_t::load_upem (void) const
 {
-  hb_blob_t *head_blob = OT::Sanitizer<OT::head>::sanitize (reference_table (HB_OT_TAG_head));
+  hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (reference_table (HB_OT_TAG_head));
   const OT::head *head_table = OT::Sanitizer<OT::head>::lock_instance (head_blob);
   upem = head_table->get_upem ();
   hb_blob_destroy (head_blob);
@@ -456,11 +446,6 @@
   if (face->immutable)
     return;
 
-  if (face->num_glyphs == glyph_count)
-    return;
-
-  face->dirty |= face->NUM_GLYPHS;
-
   face->num_glyphs = glyph_count;
 }
 
@@ -483,10 +468,39 @@
 void
 hb_face_t::load_num_glyphs (void) const
 {
-  hb_blob_t *maxp_blob = OT::Sanitizer<OT::maxp>::sanitize (reference_table (HB_OT_TAG_maxp));
+  hb_blob_t *maxp_blob = OT::Sanitizer<OT::maxp>().sanitize (reference_table (HB_OT_TAG_maxp));
   const OT::maxp *maxp_table = OT::Sanitizer<OT::maxp>::lock_instance (maxp_blob);
   num_glyphs = maxp_table->get_num_glyphs ();
   hb_blob_destroy (maxp_blob);
 }
 
+/**
+ * hb_face_get_table_tags:
+ * @face: a face.
+ *
+ * Retrieves table tags for a face, if possible.
+ *
+ * Return value: total number of tables, or 0 if not possible to list.
+ *
+ * Since: 1.6.0
+ **/
+unsigned int
+hb_face_get_table_tags (hb_face_t    *face,
+			unsigned int  start_offset,
+			unsigned int *table_count, /* IN/OUT */
+			hb_tag_t     *table_tags /* OUT */)
+{
+  if (face->destroy != _hb_face_for_data_closure_destroy)
+  {
+    if (table_count)
+      *table_count = 0;
+    return 0;
+  }
 
+  hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) face->user_data;
+
+  const OT::OpenTypeFontFile &ot_file = *OT::Sanitizer<OT::OpenTypeFontFile>::lock_instance (data->blob);
+  const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
+
+  return ot_face.get_table_tags (start_offset, table_count, table_tags);
+}
diff --git a/src/hb-face.h b/src/hb-face.h
index 91237b7..0ce8d04 100644
--- a/src/hb-face.h
+++ b/src/hb-face.h
@@ -71,7 +71,6 @@
 		       hb_destroy_func_t   destroy,
 		       hb_bool_t           replace);
 
-
 HB_EXTERN void *
 hb_face_get_user_data (hb_face_t          *face,
 		       hb_user_data_key_t *key);
@@ -111,6 +110,11 @@
 HB_EXTERN unsigned int
 hb_face_get_glyph_count (hb_face_t *face);
 
+HB_EXTERN unsigned int
+hb_face_get_table_tags (hb_face_t    *face,
+			unsigned int  start_offset,
+			unsigned int *table_count, /* IN/OUT */
+			hb_tag_t     *table_tags /* OUT */);
 
 HB_END_DECLS
 
diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc
index 4b60c6c..3f09c3f 100644
--- a/src/hb-fallback-shape.cc
+++ b/src/hb-fallback-shape.cc
@@ -143,5 +143,7 @@
   if (HB_DIRECTION_IS_BACKWARD (direction))
     hb_buffer_reverse (buffer);
 
+  buffer->safe_to_break_all ();
+
   return true;
 }
diff --git a/src/hb-font-private.hh b/src/hb-font-private.hh
index fbb16a0..992152f 100644
--- a/src/hb-font-private.hh
+++ b/src/hb-font-private.hh
@@ -108,6 +108,8 @@
   unsigned int x_ppem;
   unsigned int y_ppem;
 
+  float ptem;
+
   /* Font variation coordinates. */
   unsigned int num_coords;
   int *coords;
@@ -116,16 +118,6 @@
   void              *user_data;
   hb_destroy_func_t  destroy;
 
-  enum dirty_t {
-    NOTHING	= 0x0000,
-    FACE	= 0x0001,
-    PARENT	= 0x0002,
-    FUNCS	= 0x0004,
-    SCALE	= 0x0008,
-    PPEM	= 0x0010,
-    VARIATIONS	= 0x0020,
-  } dirty;
-
   struct hb_shaper_data_t shaper_data;
 
 
@@ -136,6 +128,8 @@
   inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); }
   inline hb_position_t em_scalef_x (float v) { return em_scalef (v, this->x_scale); }
   inline hb_position_t em_scalef_y (float v) { return em_scalef (v, this->y_scale); }
+  inline float em_fscale_x (int16_t v) { return em_fscale (v, x_scale); }
+  inline float em_fscale_y (int16_t v) { return em_fscale (v, y_scale); }
   inline hb_position_t em_scale_dir (int16_t v, hb_direction_t direction)
   { return em_scale (v, dir_scale (direction)); }
 
@@ -549,12 +543,14 @@
   }
   inline hb_position_t em_scalef (float v, int scale)
   {
-    return (hb_position_t) (v * scale / face->get_upem ());
+    return (hb_position_t) round (v * scale / face->get_upem ());
+  }
+  inline float em_fscale (int16_t v, int scale)
+  {
+    return (float) v * scale / face->get_upem ();
   }
 };
 
-HB_MARK_AS_FLAG_T (hb_font_t::dirty_t);
-
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font);
 #include "hb-shaper-list.hh"
diff --git a/src/hb-font.cc b/src/hb-font.cc
index e900bd3..f3534b6 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -36,7 +36,7 @@
  */
 
 static hb_bool_t
-hb_font_get_font_h_extents_nil (hb_font_t *font,
+hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED,
 				void *font_data HB_UNUSED,
 				hb_font_extents_t *metrics,
 				void *user_data HB_UNUSED)
@@ -60,7 +60,7 @@
 }
 
 static hb_bool_t
-hb_font_get_font_v_extents_nil (hb_font_t *font,
+hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED,
 				void *font_data HB_UNUSED,
 				hb_font_extents_t *metrics,
 				void *user_data HB_UNUSED)
@@ -347,12 +347,12 @@
   true, /* immutable */
 
   {
-#define HB_FONT_FUNC_IMPLEMENT(name) NULL,
+#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
   },
   {
-#define HB_FONT_FUNC_IMPLEMENT(name) NULL,
+#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
   },
@@ -370,12 +370,12 @@
   true, /* immutable */
 
   {
-#define HB_FONT_FUNC_IMPLEMENT(name) NULL,
+#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
   },
   {
-#define HB_FONT_FUNC_IMPLEMENT(name) NULL,
+#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
     HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_FONT_FUNC_IMPLEMENT
   },
@@ -563,8 +563,8 @@
     ffuncs->destroy.name = destroy;                                      \
   } else {                                                               \
     ffuncs->get.f.name = hb_font_get_##name##_parent;                    \
-    ffuncs->user_data.name = NULL;                                       \
-    ffuncs->destroy.name = NULL;                                         \
+    ffuncs->user_data.name = nullptr;                                       \
+    ffuncs->destroy.name = nullptr;                                         \
   }                                                                      \
 }
 
@@ -1157,8 +1157,20 @@
   font->y_scale = parent->y_scale;
   font->x_ppem = parent->x_ppem;
   font->y_ppem = parent->y_ppem;
+  font->ptem = parent->ptem;
 
-  /* TODO: copy variation coordinates. */
+  font->num_coords = parent->num_coords;
+  if (!font->num_coords)
+    font->coords = nullptr;
+  else
+  {
+    unsigned int size = parent->num_coords * sizeof (parent->coords[0]);
+    font->coords = (int *) malloc (size);
+    if (unlikely (!font->coords))
+      font->num_coords = 0;
+    else
+      memcpy (font->coords, parent->coords, size);
+  }
 
   return font;
 }
@@ -1180,7 +1192,7 @@
 
     true, /* immutable */
 
-    NULL, /* parent */
+    nullptr, /* parent */
     const_cast<hb_face_t *> (&_hb_face_nil),
 
     1000, /* x_scale */
@@ -1188,15 +1200,14 @@
 
     0, /* x_ppem */
     0, /* y_ppem */
+    0, /* ptem */
 
     0, /* num_coords */
-    NULL, /* coords */
+    nullptr, /* coords */
 
     const_cast<hb_font_funcs_t *> (&_hb_font_funcs_nil), /* klass */
-    NULL, /* user_data */
-    NULL, /* destroy */
-
-    hb_font_t::NOTHING, /* dirty */
+    nullptr, /* user_data */
+    nullptr, /* destroy */
 
     {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
@@ -1350,11 +1361,6 @@
   if (!parent)
     parent = hb_font_get_empty ();
 
-  if (parent == font->parent)
-    return;
-
-  font->dirty |= font->PARENT;
-
   hb_font_t *old = font->parent;
 
   font->parent = hb_font_reference (parent);
@@ -1397,11 +1403,6 @@
   if (unlikely (!face))
     face = hb_face_get_empty ();
 
-  if (font->face == face)
-    return;
-
-  font->dirty |= font->FACE;
-
   hb_face_t *old = font->face;
 
   font->face = hb_face_reference (face);
@@ -1455,8 +1456,6 @@
   if (!klass)
     klass = hb_font_funcs_get_empty ();
 
-  font->dirty |= font->FUNCS;
-
   hb_font_funcs_reference (klass);
   hb_font_funcs_destroy (font->klass);
   font->klass = klass;
@@ -1512,11 +1511,6 @@
   if (font->immutable)
     return;
 
-  if (font->x_scale == x_scale && font->y_scale == y_scale)
-    return;
-
-  font->dirty |= font->SCALE;
-
   font->x_scale = x_scale;
   font->y_scale = y_scale;
 }
@@ -1558,11 +1552,6 @@
   if (font->immutable)
     return;
 
-  if (font->x_ppem == x_ppem && font->y_ppem == y_ppem)
-    return;
-
-  font->dirty |= font->PPEM;
-
   font->x_ppem = x_ppem;
   font->y_ppem = y_ppem;
 }
@@ -1586,6 +1575,40 @@
   if (y_ppem) *y_ppem = font->y_ppem;
 }
 
+/**
+ * hb_font_set_ptem:
+ * @font: a font.
+ * @ptem: 
+ *
+ * Sets "point size" of the font.
+ *
+ * Since: 1.6.0
+ **/
+void
+hb_font_set_ptem (hb_font_t *font, float ptem)
+{
+  if (font->immutable)
+    return;
+
+  font->ptem = ptem;
+}
+
+/**
+ * hb_font_get_ptem:
+ * @font: a font.
+ *
+ * Gets the "point size" of the font.  A value of 0 means unset.
+ *
+ * Return value: Point size.
+ *
+ * Since: 0.9.2
+ **/
+float
+hb_font_get_ptem (hb_font_t *font)
+{
+  return font->ptem;
+}
+
 /*
  * Variations
  */
@@ -1595,16 +1618,6 @@
 				      int *coords, /* 2.14 normalized */
 				      unsigned int coords_length)
 {
-  if (font->num_coords == coords_length &&
-      (coords_length == 0 ||
-       0 == memcmp (font->coords, coords, coords_length * sizeof (coords[0]))))
-  {
-    free (coords);
-    return;
-  }
-
-  font->dirty |= font->VARIATIONS;
-
   free (font->coords);
 
   font->coords = coords;
@@ -1626,13 +1639,13 @@
 
   if (!variations_length)
   {
-    hb_font_set_var_coords_normalized (font, NULL, 0);
+    hb_font_set_var_coords_normalized (font, nullptr, 0);
     return;
   }
 
   unsigned int coords_length = hb_ot_var_get_axis_count (font->face);
 
-  int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : NULL;
+  int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr;
   if (unlikely (coords_length && !normalized))
     return;
 
@@ -1655,7 +1668,7 @@
   if (font->immutable)
     return;
 
-  int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : NULL;
+  int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr;
   if (unlikely (coords_length && !normalized))
     return;
 
@@ -1676,7 +1689,7 @@
   if (font->immutable)
     return;
 
-  int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : NULL;
+  int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr;
   if (unlikely (coords_length && !copy))
     return;
 
@@ -1736,7 +1749,7 @@
   trampoline_t *trampoline = (trampoline_t *) calloc (1, sizeof (trampoline_t));
 
   if (unlikely (!trampoline))
-    return NULL;
+    return nullptr;
 
   trampoline->closure.user_data = user_data;
   trampoline->closure.destroy = destroy;
diff --git a/src/hb-font.h b/src/hb-font.h
index 85fb56d..c95b61d 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -456,7 +456,7 @@
 /* high-level funcs, with fallback */
 
 /* Calls either hb_font_get_nominal_glyph() if variation_selector is 0,
- * otherwise callse hb_font_get_variation_glyph(). */
+ * otherwise calls hb_font_get_variation_glyph(). */
 HB_EXTERN hb_bool_t
 hb_font_get_glyph (hb_font_t *font,
 		   hb_codepoint_t unicode, hb_codepoint_t variation_selector,
@@ -607,6 +607,16 @@
 		  unsigned int *x_ppem,
 		  unsigned int *y_ppem);
 
+/*
+ * Point size per EM.  Used for optical-sizing in CoreText.
+ * A value of zero means "not set".
+ */
+HB_EXTERN void
+hb_font_set_ptem (hb_font_t *font, float ptem);
+
+HB_EXTERN float
+hb_font_get_ptem (hb_font_t *font);
+
 HB_EXTERN void
 hb_font_set_variations (hb_font_t *font,
 			const hb_variation_t *variations,
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index 48d6a0e..fc4b112 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -28,24 +28,17 @@
  */
 
 #include "hb-private.hh"
+#include "hb-debug.hh"
 
 #include "hb-ft.h"
 
 #include "hb-font-private.hh"
 
-#include "hb-cache-private.hh" // Maybe use in the future?
-
 #include FT_ADVANCES_H
 #include FT_MULTIPLE_MASTERS_H
 #include FT_TRUETYPE_TABLES_H
 
 
-
-#ifndef HB_DEBUG_FT
-#define HB_DEBUG_FT (HB_DEBUG+0)
-#endif
-
-
 /* TODO:
  *
  * In general, this file does a fine job of what it's supposed to do.
@@ -65,7 +58,7 @@
  *
  *   - In the future, we should add constructors to create fonts in font space?
  *
- *   - FT_Load_Glyph() is exteremely costly.  Do something about it?
+ *   - FT_Load_Glyph() is extremely costly.  Do something about it?
  */
 
 
@@ -83,7 +76,7 @@
   hb_ft_font_t *ft_font = (hb_ft_font_t *) calloc (1, sizeof (hb_ft_font_t));
 
   if (unlikely (!ft_font))
-    return NULL;
+    return nullptr;
 
   ft_font->ft_face = ft_face;
   ft_font->symbol = symbol;
@@ -95,14 +88,16 @@
 }
 
 static void
-_hb_ft_face_destroy (FT_Face ft_face)
+_hb_ft_face_destroy (void *data)
 {
-  FT_Done_Face (ft_face);
+  FT_Done_Face ((FT_Face) data);
 }
 
 static void
-_hb_ft_font_destroy (hb_ft_font_t *ft_font)
+_hb_ft_font_destroy (void *data)
 {
+  hb_ft_font_t *ft_font = (hb_ft_font_t *) data;
+
   if (ft_font->unref)
     _hb_ft_face_destroy (ft_font->ft_face);
 
@@ -124,7 +119,7 @@
   if (font->immutable)
     return;
 
-  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
+  if (font->destroy != _hb_ft_font_destroy)
     return;
 
   hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
@@ -144,7 +139,7 @@
 int
 hb_ft_font_get_load_flags (hb_font_t *font)
 {
-  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
+  if (font->destroy != _hb_ft_font_destroy)
     return 0;
 
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
@@ -155,8 +150,8 @@
 FT_Face
 hb_ft_font_get_face (hb_font_t *font)
 {
-  if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)
-    return NULL;
+  if (font->destroy != _hb_ft_font_destroy)
+    return nullptr;
 
   const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data;
 
@@ -422,7 +417,7 @@
   return true;
 }
 
-static hb_font_funcs_t *static_ft_funcs = NULL;
+static hb_font_funcs_t *static_ft_funcs = nullptr;
 
 #ifdef HB_USE_ATEXIT
 static
@@ -442,24 +437,24 @@
   {
     funcs = hb_font_funcs_create ();
 
-    hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, NULL, NULL);
-    //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, NULL, NULL);
-    hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, NULL, NULL);
-    hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, NULL, NULL);
-    hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ft_get_glyph_h_advance, NULL, NULL);
-    hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, NULL, NULL);
-    //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, NULL, NULL);
-    hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, NULL, NULL);
-    hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, NULL, NULL);
-    //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ft_get_glyph_v_kerning, NULL, NULL);
-    hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, NULL, NULL);
-    hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, NULL, NULL);
-    hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, NULL, NULL);
-    hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, NULL, NULL);
+    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_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_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);
+    hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, nullptr, nullptr);
 
     hb_font_funcs_make_immutable (funcs);
 
-    if (!hb_atomic_ptr_cmpexch (&static_ft_funcs, NULL, funcs)) {
+    if (!hb_atomic_ptr_cmpexch (&static_ft_funcs, nullptr, funcs)) {
       hb_font_funcs_destroy (funcs);
       goto retry;
     }
@@ -474,7 +469,7 @@
   hb_font_set_funcs (font,
 		     funcs,
 		     _hb_ft_font_create (ft_face, symbol, unref),
-		     (hb_destroy_func_t) _hb_ft_font_destroy);
+		     _hb_ft_font_destroy);
 }
 
 
@@ -488,17 +483,17 @@
 
   /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */
 
-  error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length);
+  error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length);
   if (error)
-    return NULL;
+    return nullptr;
 
   buffer = (FT_Byte *) malloc (length);
-  if (buffer == NULL)
-    return NULL;
+  if (!buffer)
+    return nullptr;
 
   error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length);
   if (error)
-    return NULL;
+    return nullptr;
 
   return hb_blob_create ((const char *) buffer, length,
 			 HB_MEMORY_MODE_WRITABLE,
@@ -521,7 +516,7 @@
 {
   hb_face_t *face;
 
-  if (ft_face->stream->read == NULL) {
+  if (!ft_face->stream->read) {
     hb_blob_t *blob;
 
     blob = hb_blob_create ((const char *) ft_face->stream->base,
@@ -553,7 +548,7 @@
 hb_ft_face_create_referenced (FT_Face ft_face)
 {
   FT_Reference_Face (ft_face);
-  return hb_ft_face_create (ft_face, (hb_destroy_func_t) _hb_ft_face_destroy);
+  return hb_ft_face_create (ft_face, _hb_ft_face_destroy);
 }
 
 static void
@@ -579,7 +574,7 @@
     if (ft_face->generic.finalizer)
       ft_face->generic.finalizer (ft_face);
 
-    ft_face->generic.data = hb_ft_face_create (ft_face, NULL);
+    ft_face->generic.data = hb_ft_face_create (ft_face, nullptr);
     ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize;
   }
 
@@ -608,6 +603,19 @@
   font = hb_font_create (face);
   hb_face_destroy (face);
   _hb_ft_font_set_funcs (font, ft_face, false);
+  hb_ft_font_changed (font);
+  return font;
+}
+
+void
+hb_ft_font_changed (hb_font_t *font)
+{
+  if (font->destroy != _hb_ft_font_destroy)
+    return;
+
+  hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
+  FT_Face ft_face = ft_font->ft_face;
+
   hb_font_set_scale (font,
 		     (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16),
 		     (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16));
@@ -618,7 +626,7 @@
 #endif
 
 #ifdef HAVE_FT_GET_VAR_BLEND_COORDINATES
-  FT_MM_Var *mm_var = NULL;
+  FT_MM_Var *mm_var = nullptr;
   if (!FT_Get_MM_Var (ft_face, &mm_var))
   {
     FT_Fixed *ft_coords = (FT_Fixed *) calloc (mm_var->num_axis, sizeof (FT_Fixed));
@@ -627,19 +635,29 @@
     {
       if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords))
       {
-	for (unsigned int i = 0; i < mm_var->num_axis; ++i)
-	  coords[i] = ft_coords[i] >>= 2;
+	bool nonzero = false;
 
-	hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis);
+	for (unsigned int i = 0; i < mm_var->num_axis; ++i)
+	 {
+	  coords[i] = ft_coords[i] >>= 2;
+	  nonzero = nonzero || coords[i];
+	 }
+
+	if (nonzero)
+	  hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis);
+	else
+	  hb_font_set_var_coords_normalized (font, nullptr, 0);
       }
-      free (coords);
-      free (ft_coords);
     }
+    free (coords);
+    free (ft_coords);
+#ifdef HAVE_FT_DONE_MM_VAR
+    FT_Done_MM_Var (ft_face->glyph->library, mm_var);
+#else
     free (mm_var);
+#endif
   }
 #endif
-
-  return font;
 }
 
 /**
@@ -655,7 +673,7 @@
 hb_ft_font_create_referenced (FT_Face ft_face)
 {
   FT_Reference_Face (ft_face);
-  return hb_ft_font_create (ft_face, (hb_destroy_func_t) _hb_ft_face_destroy);
+  return hb_ft_font_create (ft_face, _hb_ft_face_destroy);
 }
 
 
@@ -681,9 +699,9 @@
   {
     /* Not found; allocate one. */
     if (FT_Init_FreeType (&library))
-      return NULL;
+      return nullptr;
 
-    if (!hb_atomic_ptr_cmpexch (&ft_library, NULL, library)) {
+    if (!hb_atomic_ptr_cmpexch (&ft_library, nullptr, library)) {
       FT_Done_FreeType (library);
       goto retry;
     }
@@ -711,7 +729,7 @@
   if (unlikely (!blob_length))
     DEBUG_MSG (FT, font, "Font face has empty blob");
 
-  FT_Face ft_face = NULL;
+  FT_Face ft_face = nullptr;
   FT_Error err = FT_New_Memory_Face (get_ft_library (),
 				     (const FT_Byte *) blob_data,
 				     blob_length,
@@ -738,9 +756,10 @@
   {
     FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0,
 			  0, font->y_scale < 0 ? -1 : +1};
-    FT_Set_Transform (ft_face, &matrix, NULL);
+    FT_Set_Transform (ft_face, &matrix, nullptr);
   }
 
+#ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES
   unsigned int num_coords;
   const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
   if (num_coords)
@@ -754,6 +773,7 @@
       free (ft_coords);
     }
   }
+#endif
 
   ft_face->generic.data = blob;
   ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob;
diff --git a/src/hb-ft.h b/src/hb-ft.h
index dc8ef85..94013ee 100644
--- a/src/hb-ft.h
+++ b/src/hb-ft.h
@@ -116,7 +116,13 @@
 HB_EXTERN int
 hb_ft_font_get_load_flags (hb_font_t *font);
 
-/* Makes an hb_font_t use FreeType internally to implement font functions. */
+/* Call when size or variations settings on underlying FT_Face change. */
+HB_EXTERN void
+hb_ft_font_changed (hb_font_t *font);
+
+/* Makes an hb_font_t use FreeType internally to implement font functions.
+ * Note: this internally creates an FT_Face.  Use it when you create your
+ * hb_face_t using hb_face_create(). */
 HB_EXTERN void
 hb_ft_font_set_funcs (hb_font_t *font);
 
diff --git a/src/hb-glib.cc b/src/hb-glib.cc
index 2b91b5b..50c30e9 100644
--- a/src/hb-glib.cc
+++ b/src/hb-glib.cc
@@ -364,25 +364,54 @@
   return utf8_decomposed_len;
 }
 
+static hb_unicode_funcs_t *static_glib_funcs = nullptr;
+
+#ifdef HB_USE_ATEXIT
+static
+void free_static_glib_funcs (void)
+{
+  hb_unicode_funcs_destroy (static_glib_funcs);
+}
+#endif
+
 hb_unicode_funcs_t *
 hb_glib_get_unicode_funcs (void)
 {
-  static const hb_unicode_funcs_t _hb_glib_unicode_funcs = {
-    HB_OBJECT_HEADER_STATIC,
+retry:
+  hb_unicode_funcs_t *funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_glib_funcs);
 
-    NULL, /* parent */
-    true, /* immutable */
-    {
-#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_glib_unicode_##name,
+  if (unlikely (!funcs))
+  {
+    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_make_immutable (funcs);
+
+    if (!hb_atomic_ptr_cmpexch (&static_glib_funcs, nullptr, funcs)) {
+      hb_unicode_funcs_destroy (funcs);
+      goto retry;
     }
+
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_glib_funcs); /* First person registers atexit() callback. */
+#endif
   };
 
-  return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
+  return hb_unicode_funcs_reference (funcs);
 }
 
 #if GLIB_CHECK_VERSION(2,31,10)
+
+static void
+_hb_g_bytes_unref (void *data)
+{
+  g_bytes_unref ((GBytes *) data);
+}
+
 /**
  * hb_glib_blob_create:
  *
@@ -397,6 +426,6 @@
 			 size,
 			 HB_MEMORY_MODE_READONLY,
 			 g_bytes_ref (gbytes),
-			 (hb_destroy_func_t) g_bytes_unref);
+			 _hb_g_bytes_unref);
 }
 #endif
diff --git a/src/hb-gobject-enums.h.tmpl b/src/hb-gobject-enums.h.tmpl
index e28510c..606727c 100644
--- a/src/hb-gobject-enums.h.tmpl
+++ b/src/hb-gobject-enums.h.tmpl
@@ -42,7 +42,8 @@
 /*** END file-header ***/
 
 /*** BEGIN value-header ***/
-HB_EXTERN GType @enum_name@_get_type (void) G_GNUC_CONST;
+HB_EXTERN GType
+@enum_name@_get_type (void) G_GNUC_CONST;
 #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
 
 /*** END value-header ***/
diff --git a/src/hb-gobject-structs.cc b/src/hb-gobject-structs.cc
index fef0024..a96c358 100644
--- a/src/hb-gobject-structs.cc
+++ b/src/hb-gobject-structs.cc
@@ -58,7 +58,7 @@
 	static hb_##name##_t *_hb_##name##_reference (const hb_##name##_t *l) \
 	{ \
 	  hb_##name##_t *c = (hb_##name##_t *) calloc (1, sizeof (hb_##name##_t)); \
-	  if (unlikely (!c)) return NULL; \
+	  if (unlikely (!c)) return nullptr; \
 	  *c = *l; \
 	  return c; \
 	} \
diff --git a/src/hb-gobject-structs.h b/src/hb-gobject-structs.h
index 1c30321..302dc95 100644
--- a/src/hb-gobject-structs.h
+++ b/src/hb-gobject-structs.h
@@ -45,7 +45,8 @@
  *
  * Since: 0.9.2
  **/
-HB_EXTERN GType hb_gobject_blob_get_type (void);
+HB_EXTERN GType
+hb_gobject_blob_get_type (void);
 #define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ())
 
 /**
@@ -53,7 +54,8 @@
  *
  * Since: 0.9.2
  **/
-HB_EXTERN GType hb_gobject_buffer_get_type (void);
+HB_EXTERN GType
+hb_gobject_buffer_get_type (void);
 #define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ())
 
 /**
@@ -61,7 +63,8 @@
  *
  * Since: 0.9.2
  **/
-HB_EXTERN GType hb_gobject_face_get_type (void);
+HB_EXTERN GType
+hb_gobject_face_get_type (void);
 #define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ())
 
 /**
@@ -69,7 +72,8 @@
  *
  * Since: 0.9.2
  **/
-HB_EXTERN GType hb_gobject_font_get_type (void);
+HB_EXTERN GType
+hb_gobject_font_get_type (void);
 #define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ())
 
 /**
@@ -77,13 +81,16 @@
  *
  * Since: 0.9.2
  **/
-HB_EXTERN GType hb_gobject_font_funcs_get_type (void);
+HB_EXTERN GType
+hb_gobject_font_funcs_get_type (void);
 #define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ())
 
-HB_EXTERN GType hb_gobject_set_get_type (void);
+HB_EXTERN GType
+hb_gobject_set_get_type (void);
 #define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ())
 
-HB_EXTERN GType hb_gobject_shape_plan_get_type (void);
+HB_EXTERN GType
+hb_gobject_shape_plan_get_type (void);
 #define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ())
 
 /**
@@ -91,26 +98,40 @@
  *
  * Since: 0.9.2
  **/
-HB_EXTERN GType hb_gobject_unicode_funcs_get_type (void);
+HB_EXTERN GType
+hb_gobject_unicode_funcs_get_type (void);
 #define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ())
 
 /* Value types */
 
-HB_EXTERN GType hb_gobject_feature_get_type (void);
+HB_EXTERN GType
+hb_gobject_feature_get_type (void);
 #define HB_GOBJECT_TYPE_FEATURE (hb_gobject_feature_get_type ())
 
-HB_EXTERN GType hb_gobject_glyph_info_get_type (void);
+HB_EXTERN GType
+hb_gobject_glyph_info_get_type (void);
 #define HB_GOBJECT_TYPE_GLYPH_INFO (hb_gobject_glyph_info_get_type ())
 
-HB_EXTERN GType hb_gobject_glyph_position_get_type (void);
+HB_EXTERN GType
+hb_gobject_glyph_position_get_type (void);
 #define HB_GOBJECT_TYPE_GLYPH_POSITION (hb_gobject_glyph_position_get_type ())
 
-HB_EXTERN GType hb_gobject_segment_properties_get_type (void);
+HB_EXTERN GType
+hb_gobject_segment_properties_get_type (void);
 #define HB_GOBJECT_TYPE_SEGMENT_PROPERTIES (hb_gobject_segment_properties_get_type ())
 
-HB_EXTERN GType hb_gobject_user_data_key_get_type (void);
+HB_EXTERN GType
+hb_gobject_user_data_key_get_type (void);
 #define HB_GOBJECT_TYPE_USER_DATA_KEY (hb_gobject_user_data_key_get_type ())
 
+HB_EXTERN GType
+hb_gobject_ot_math_glyph_variant_get_type (void);
+#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_VARIANT (hb_gobject_ot_math_glyph_variant_get_type ())
+
+HB_EXTERN GType
+hb_gobject_ot_math_glyph_part_get_type (void);
+#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART (hb_gobject_ot_math_glyph_part_get_type ())
+
 
 HB_END_DECLS
 
diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index 5b804b8..46fe139 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -59,7 +59,7 @@
   hb_graphite2_shaper_face_data_t *face_data = (hb_graphite2_shaper_face_data_t *) data;
   hb_graphite2_tablelist_t *tlist = face_data->tlist;
 
-  hb_blob_t *blob = NULL;
+  hb_blob_t *blob = nullptr;
 
   for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next)
     if (p->tag == tag) {
@@ -74,13 +74,13 @@
     hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) calloc (1, sizeof (hb_graphite2_tablelist_t));
     if (unlikely (!p)) {
       hb_blob_destroy (blob);
-      return NULL;
+      return nullptr;
     }
     p->blob = blob;
     p->tag = tag;
 
     /* TODO Not thread-safe, but fairly harmless.
-     * We can do the double-chcked pointer cmpexch thing here. */
+     * We can do the double-checked pointer cmpexch thing here. */
     p->next = face_data->tlist;
     face_data->tlist = p;
   }
@@ -100,20 +100,20 @@
   if (!hb_blob_get_length (silf_blob))
   {
     hb_blob_destroy (silf_blob);
-    return NULL;
+    return nullptr;
   }
   hb_blob_destroy (silf_blob);
 
   hb_graphite2_shaper_face_data_t *data = (hb_graphite2_shaper_face_data_t *) calloc (1, sizeof (hb_graphite2_shaper_face_data_t));
   if (unlikely (!data))
-    return NULL;
+    return nullptr;
 
   data->face = face;
   data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll);
 
   if (unlikely (!data->grface)) {
     free (data);
-    return NULL;
+    return nullptr;
   }
 
   return data;
@@ -143,7 +143,7 @@
 gr_face *
 hb_graphite2_face_get_gr_face (hb_face_t *face)
 {
-  if (unlikely (!hb_graphite2_shaper_face_data_ensure (face))) return NULL;
+  if (unlikely (!hb_graphite2_shaper_face_data_ensure (face))) return nullptr;
   return HB_SHAPER_DATA_GET (face)->grface;
 }
 
@@ -171,7 +171,7 @@
 gr_font *
 hb_graphite2_font_get_gr_font (hb_font_t *font)
 {
-  return NULL;
+  return nullptr;
 }
 
 
@@ -221,7 +221,7 @@
   gr_face *grface = HB_SHAPER_DATA_GET (face)->grface;
 
   const char *lang = hb_language_to_string (hb_buffer_get_language (buffer));
-  const char *lang_end = lang ? strchr (lang, '-') : NULL;
+  const char *lang_end = lang ? strchr (lang, '-') : nullptr;
   int lang_len = lang_end ? lang_end - lang : -1;
   gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0);
 
@@ -232,7 +232,7 @@
       gr_fref_set_feature_value (fref, features[i].value, feats);
   }
 
-  gr_segment *seg = NULL;
+  gr_segment *seg = nullptr;
   const gr_slot *is;
   unsigned int ci = 0, ic = 0;
   float curradvx = 0., curradvy = 0.;
@@ -250,7 +250,7 @@
   hb_tag_t script_tag[2];
   hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]);
 
-  seg = gr_make_seg (NULL, grface,
+  seg = gr_make_seg (nullptr, grface,
 		     script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1],
 		     feats,
 		     gr_utf32, chars, buffer->len,
@@ -301,12 +301,14 @@
 
   hb_codepoint_t *pg = gids;
   clusters[0].cluster = buffer->info[0].cluster;
-  float curradv = HB_DIRECTION_IS_BACKWARD(buffer->props.direction) ? gr_slot_origin_X(gr_seg_first_slot(seg)) : 0.;
+  float curradv = 0.;
   if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
   {
     curradv = gr_slot_origin_X(gr_seg_first_slot(seg));
     clusters[0].advance = gr_seg_advance_X(seg) - curradv;
   }
+  else
+    clusters[0].advance = 0;
   for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
   {
     unsigned int before = gr_slot_before (is);
@@ -330,13 +332,13 @@
       c->base_glyph = ic;
       c->num_glyphs = 0;
       if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+        c->advance = curradv - gr_slot_origin_X(is);
+      else
       {
-        ci++;
-        clusters[ci].advance = curradv - gr_slot_origin_X(is);
-      } else {
-        clusters[ci].advance = gr_slot_origin_X(is) - curradv;
-        ci++;
+        c->advance = 0;
+        clusters[ci].advance += gr_slot_origin_X(is) - curradv;
       }
+      ci++;
       curradv = gr_slot_origin_X(is);
     }
     clusters[ci].num_glyphs++;
@@ -345,8 +347,10 @@
 	clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
   }
 
-  if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
-    clusters[ci].advance = gr_seg_advance_X(seg) - curradv;
+  if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+    clusters[ci].advance += curradv;
+  else
+    clusters[ci].advance += gr_seg_advance_X(seg) - curradv;
   ci++;
 
   for (unsigned int i = 0; i < ci; ++i)
@@ -366,11 +370,11 @@
   float yscale = (float) font->y_scale / upem;
   yscale *= yscale / xscale;
   /* Positioning. */
+  unsigned int currclus = (unsigned int) -1;
+  const hb_glyph_info_t *info = buffer->info;
+  hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, nullptr);
   if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
   {
-    int currclus = -1;
-    const hb_glyph_info_t *info = buffer->info;
-    hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL);
     curradvx = 0;
     for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is))
     {
@@ -383,29 +387,26 @@
       } else
         pPos->x_advance = 0.;
 
-      pPos->y_advance = gr_slot_advance_Y (is, grface, NULL) * yscale;
+      pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale;
       curradvy += pPos->y_advance;
     }
   }
   else
   {
-    int currclus = -1;
-    const hb_glyph_info_t *info = buffer->info;
-    hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL);
     curradvx = gr_seg_advance_X(seg) * xscale;
     for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is))
     {
       if (info->cluster != currclus)
       {
         pPos->x_advance = info->var1.i32 * xscale;
-        if (currclus != -1) curradvx -= info[-1].var1.i32 * xscale;
+        curradvx -= pPos->x_advance;
         currclus = info->cluster;
       } else
-      pPos->x_advance = 0.;
+        pPos->x_advance = 0.;
 
-      pPos->y_advance = gr_slot_advance_Y (is, grface, NULL) * yscale;
+      pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale;
       curradvy -= pPos->y_advance;
-      pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx + pPos->x_advance;
+      pPos->x_offset = (gr_slot_origin_X (is) - info->var1.i32) * xscale - curradvx + pPos->x_advance;
       pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy;
     }
     hb_buffer_reverse_clusters (buffer);
@@ -414,5 +415,7 @@
   if (feats) gr_featureval_destroy (feats);
   gr_seg_destroy (seg);
 
+  buffer->unsafe_to_break_all ();
+
   return true;
 }
diff --git a/src/hb-icu.cc b/src/hb-icu.cc
index ee54721..552eaec 100644
--- a/src/hb-icu.cc
+++ b/src/hb-icu.cc
@@ -34,7 +34,7 @@
 #include "hb-unicode-private.hh"
 
 #include <unicode/uchar.h>
-#include <unicode/unorm.h>
+#include <unicode/unorm2.h>
 #include <unicode/ustring.h>
 #include <unicode/utf16.h>
 #include <unicode/uversion.h>
@@ -200,7 +200,7 @@
   if (err) return false;
 
   icu_err = U_ZERO_ERROR;
-  len = unorm_normalize (utf16, len, UNORM_NFC, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
+  len = unorm2_normalize (unorm2_getNFCInstance (&icu_err), utf16, len, normalized, ARRAY_LENGTH (normalized), &icu_err);
   if (U_FAILURE (icu_err))
     return false;
   if (u_countChar32 (normalized, len) == 1) {
@@ -261,7 +261,7 @@
   if (err) return false;
 
   icu_err = U_ZERO_ERROR;
-  len = unorm_normalize (utf16, len, UNORM_NFD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
+  len = unorm2_normalize (unorm2_getNFDInstance (&icu_err), utf16, len, normalized, ARRAY_LENGTH (normalized), &icu_err);
   if (U_FAILURE (icu_err))
     return false;
 
@@ -281,7 +281,7 @@
      * the second part :-(. */
     UChar recomposed[20];
     icu_err = U_ZERO_ERROR;
-    unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
+    unorm2_normalize (unorm2_getNFCInstance (&icu_err), normalized, len, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
     if (U_FAILURE (icu_err))
       return false;
     hb_codepoint_t c;
@@ -297,7 +297,7 @@
     U16_PREV_UNSAFE (normalized, len, *b); /* Changes len in-place. */
     UChar recomposed[18 * 2];
     icu_err = U_ZERO_ERROR;
-    len = unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
+    len = unorm2_normalize (unorm2_getNFCInstance (&icu_err), normalized, len, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
     if (U_FAILURE (icu_err))
       return false;
     /* We expect that recomposed has exactly one character now. */
@@ -331,41 +331,64 @@
 
   /* Normalise the codepoint using NFKD mode. */
   icu_err = U_ZERO_ERROR;
-  len = unorm_normalize (utf16, len, UNORM_NFKD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
-  if (icu_err)
+  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 (icu_err)
+  if (U_FAILURE (icu_err))
     return 0;
 
   return utf32_len;
 }
 
 
+static hb_unicode_funcs_t *static_icu_funcs = nullptr;
+
+#ifdef HB_USE_ATEXIT
+static
+void free_static_icu_funcs (void)
+{
+  hb_unicode_funcs_destroy (static_icu_funcs);
+}
+#endif
+
 hb_unicode_funcs_t *
 hb_icu_get_unicode_funcs (void)
 {
-  static const hb_unicode_funcs_t _hb_icu_unicode_funcs = {
-    HB_OBJECT_HEADER_STATIC,
+retry:
+  hb_unicode_funcs_t *funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_icu_funcs);
 
-    NULL, /* parent */
-    true, /* immutable */
-    {
-#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_icu_unicode_##name,
+  if (unlikely (!funcs))
+  {
+#if U_ICU_VERSION_MAJOR_NUM >= 49
+    if (!hb_atomic_ptr_get (&normalizer)) {
+      UErrorCode icu_err = U_ZERO_ERROR;
+      /* We ignore failure in getNFCInstace(). */
+      (void) hb_atomic_ptr_cmpexch (&normalizer, nullptr, unorm2_getNFCInstance (&icu_err));
+    }
+#endif
+
+    funcs = hb_unicode_funcs_create (nullptr);
+
+#define HB_UNICODE_FUNC_IMPLEMENT(name) \
+    hb_unicode_funcs_set_##name##_func (funcs, hb_icu_unicode_##name, nullptr, nullptr);
       HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_UNICODE_FUNC_IMPLEMENT
+
+    hb_unicode_funcs_make_immutable (funcs);
+
+    if (!hb_atomic_ptr_cmpexch (&static_icu_funcs, nullptr, funcs)) {
+      hb_unicode_funcs_destroy (funcs);
+      goto retry;
     }
+
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_icu_funcs); /* First person registers atexit() callback. */
+#endif
   };
 
-#if U_ICU_VERSION_MAJOR_NUM >= 49
-  if (!hb_atomic_ptr_get (&normalizer)) {
-    UErrorCode icu_err = U_ZERO_ERROR;
-    /* We ignore failure in getNFCInstace(). */
-    (void) hb_atomic_ptr_cmpexch (&normalizer, NULL, unorm2_getNFCInstance (&icu_err));
-  }
-#endif
-  return const_cast<hb_unicode_funcs_t *> (&_hb_icu_unicode_funcs);
+  return hb_unicode_funcs_reference (funcs);
 }
diff --git a/src/hb-mutex-private.hh b/src/hb-mutex-private.hh
index ed27035..49ed10e 100644
--- a/src/hb-mutex-private.hh
+++ b/src/hb-mutex-private.hh
@@ -68,7 +68,7 @@
 #include <pthread.h>
 typedef pthread_mutex_t hb_mutex_impl_t;
 #define HB_MUTEX_IMPL_INIT	PTHREAD_MUTEX_INITIALIZER
-#define hb_mutex_impl_init(M)	pthread_mutex_init (M, NULL)
+#define hb_mutex_impl_init(M)	pthread_mutex_init (M, nullptr)
 #define hb_mutex_impl_lock(M)	pthread_mutex_lock (M)
 #define hb_mutex_impl_unlock(M)	pthread_mutex_unlock (M)
 #define hb_mutex_impl_finish(M)	pthread_mutex_destroy (M)
diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh
index 6b73ff9..baa1f8f 100644
--- a/src/hb-object-private.hh
+++ b/src/hb-object-private.hh
@@ -33,18 +33,12 @@
 #define HB_OBJECT_PRIVATE_HH
 
 #include "hb-private.hh"
+#include "hb-debug.hh"
 
 #include "hb-atomic-private.hh"
 #include "hb-mutex-private.hh"
 
 
-/* Debug */
-
-#ifndef HB_DEBUG_OBJECT
-#define HB_DEBUG_OBJECT (HB_DEBUG+0)
-#endif
-
-
 /* reference_count */
 
 #define HB_REFERENCE_COUNT_INERT_VALUE -1
@@ -193,7 +187,7 @@
 					     hb_user_data_key_t *key)
 {
   if (unlikely (!obj || hb_object_is_inert (obj)))
-    return NULL;
+    return nullptr;
   assert (hb_object_is_valid (obj));
   return obj->header.user_data.get (key);
 }
diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh
index f208419..e2644ea 100644
--- a/src/hb-open-file-private.hh
+++ b/src/hb-open-file-private.hh
@@ -30,6 +30,7 @@
 #define HB_OPEN_FILE_PRIVATE_HH
 
 #include "hb-open-type-private.hh"
+#include "hb-ot-head-table.hh"
 
 
 namespace OT {
@@ -53,6 +54,16 @@
 
 typedef struct TableRecord
 {
+  int cmp (Tag t) const
+  { return -t.cmp (tag); }
+
+  static int cmp (const void *pa, const void *pb)
+  {
+    const TableRecord *a = (const TableRecord *) pa;
+    const TableRecord *b = (const TableRecord *) pb;
+    return b->cmp (a->tag);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -61,9 +72,9 @@
 
   Tag		tag;		/* 4-byte identifier. */
   CheckSum	checkSum;	/* CheckSum for this table. */
-  ULONG		offset;		/* Offset from beginning of TrueType font
+  Offset32	offset;		/* Offset from beginning of TrueType font
 				 * file. */
-  ULONG		length;		/* Length of this table. */
+  HBUINT32	length;		/* Length of this table. */
   public:
   DEFINE_SIZE_STATIC (16);
 } OpenTypeTable;
@@ -73,27 +84,39 @@
   friend struct OpenTypeFontFile;
 
   inline unsigned int get_table_count (void) const
-  { return numTables; }
+  { return tables.len; }
   inline const TableRecord& get_table (unsigned int i) const
   {
-    if (unlikely (i >= numTables)) return Null(TableRecord);
     return tables[i];
   }
+  inline unsigned int get_table_tags (unsigned int  start_offset,
+				      unsigned int *table_count, /* IN/OUT */
+				      hb_tag_t     *table_tags /* OUT */) const
+  {
+    if (table_count)
+    {
+      if (start_offset >= tables.len)
+        *table_count = 0;
+      else
+        *table_count = MIN<unsigned int> (*table_count, tables.len - start_offset);
+
+      const TableRecord *sub_tables = tables.array + start_offset;
+      unsigned int count = *table_count;
+      for (unsigned int i = 0; i < count; i++)
+	table_tags[i] = sub_tables[i].tag;
+    }
+    return tables.len;
+  }
   inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const
   {
     Tag t;
     t.set (tag);
-    unsigned int count = numTables;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      if (t == tables[i].tag)
-      {
-        if (table_index) *table_index = i;
-        return true;
-      }
-    }
-    if (table_index) *table_index = Index::NOT_FOUND_INDEX;
-    return false;
+    /* Linear-search for small tables to work around fonts with unsorted
+     * table list. */
+    int i = tables.len < 64 ? tables.lsearch (t) : tables.bsearch (t);
+    if (table_index)
+      *table_index = i == -1 ? Index::NOT_FOUND_INDEX : (unsigned int) i;
+    return i != -1;
   }
   inline const TableRecord& get_table_by_tag (hb_tag_t tag) const
   {
@@ -103,19 +126,88 @@
   }
 
   public:
+
+  inline bool serialize (hb_serialize_context_t *c,
+			 hb_tag_t sfnt_tag,
+			 Supplier<hb_tag_t> &tags,
+			 Supplier<hb_blob_t *> &blobs,
+			 unsigned int table_count)
+  {
+    TRACE_SERIALIZE (this);
+    /* Alloc 12 for the OTHeader. */
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    /* Write sfntVersion (bytes 0..3). */
+    sfnt_version.set (sfnt_tag);
+    /* Take space for numTables, searchRange, entrySelector, RangeShift
+     * and the TableRecords themselves.  */
+    if (unlikely (!tables.serialize (c, table_count))) return_trace (false);
+
+    const char *dir_end = (const char *) c->head;
+    HBUINT32 *checksum_adjustment = nullptr;
+
+    /* Write OffsetTables, alloc for and write actual table blobs. */
+    for (unsigned int i = 0; i < table_count; i++)
+    {
+      TableRecord &rec = tables.array[i];
+      hb_blob_t *blob = blobs[i];
+      rec.tag.set (tags[i]);
+      rec.length.set (hb_blob_get_length (blob));
+      rec.offset.serialize (c, this);
+
+      /* Allocate room for the table and copy it. */
+      char *start = (char *) c->allocate_size<void> (rec.length);
+      if (unlikely (!start)) {return false;}
+
+      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);
+      const char *end = (const char *) c->head;
+
+      if (tags[i] == HB_OT_TAG_head && end - start >= head::static_size)
+      {
+	head *h = (head *) start;
+	checksum_adjustment = &h->checkSumAdjustment;
+	checksum_adjustment->set (0);
+      }
+
+      rec.checkSum.set_for_data (start, end - start);
+    }
+    tags += table_count;
+    blobs += table_count;
+
+    tables.qsort ();
+
+    if (checksum_adjustment)
+    {
+      CheckSum checksum;
+
+      /* The following line is a slower version of the following block. */
+      //checksum.set_for_data (this, (const char *) c->head - (const char *) this);
+      checksum.set_for_data (this, dir_end - (const char *) this);
+      for (unsigned int i = 0; i < table_count; i++)
+      {
+	TableRecord &rec = tables.array[i];
+	checksum.set (checksum + rec.checkSum);
+      }
+
+      checksum_adjustment->set (0xB1B0AFBAu - checksum);
+    }
+
+    return_trace (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables));
+    return_trace (c->check_struct (this) && tables.sanitize (c));
   }
 
   protected:
   Tag		sfnt_version;	/* '\0\001\0\00' if TrueType / 'OTTO' if CFF */
-  USHORT	numTables;	/* Number of tables. */
-  USHORT	searchRangeZ;	/* (Maximum power of 2 <= numTables) x 16 */
-  USHORT	entrySelectorZ;	/* Log2(maximum power of 2 <= numTables). */
-  USHORT	rangeShiftZ;	/* NumTables x 16-searchRange. */
-  TableRecord	tables[VAR];	/* TableRecord entries. numTables items */
+  BinSearchArrayOf<TableRecord>
+		tables;
   public:
   DEFINE_SIZE_ARRAY (12, tables);
 } OpenTypeFontFace;
@@ -142,7 +234,7 @@
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
   FixedVersion<>version;	/* Version of the TTC Header (1.0),
 				 * 0x00010000u */
-  ArrayOf<LOffsetTo<OffsetTable>, ULONG>
+  ArrayOf<LOffsetTo<OffsetTable>, HBUINT32>
 		table;		/* Array of offsets to the OffsetTable for each font
 				 * from the beginning of the file */
   public:
@@ -237,6 +329,18 @@
     }
   }
 
+  inline bool serialize_single (hb_serialize_context_t *c,
+				hb_tag_t sfnt_tag,
+			        Supplier<hb_tag_t> &tags,
+			        Supplier<hb_blob_t *> &blobs,
+			        unsigned int table_count)
+  {
+    TRACE_SERIALIZE (this);
+    assert (sfnt_tag != TTCTag);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    return_trace (u.fontFace.serialize (c, sfnt_tag, tags, blobs, table_count));
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index d90d68c..f5c20bc 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -30,6 +30,7 @@
 #define HB_OPEN_TYPE_PRIVATE_HH
 
 #include "hb-private.hh"
+#include "hb-debug.hh"
 #include "hb-face-private.hh"
 
 
@@ -85,7 +86,7 @@
 #define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \
   inline void _instance_assertion_on_line_##_line (void) const \
   { \
-    ASSERT_STATIC (_assertion); \
+    static_assert ((_assertion), ""); \
     ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \
   }
 # define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion)
@@ -130,14 +131,26 @@
  */
 
 /* Global nul-content Null pool.  Enlarge as necessary. */
-/* TODO This really should be a extern HB_INTERNAL and defined somewhere... */
-static const void *_NullPool[(256+8) / sizeof (void *)];
+
+#define HB_NULL_POOL_SIZE 264
+static_assert (HB_NULL_POOL_SIZE % sizeof (void *) == 0, "Align HB_NULL_POOL_SIZE.");
+
+#ifdef HB_NO_VISIBILITY
+static
+#else
+extern HB_INTERNAL
+#endif
+const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)]
+#ifdef HB_NO_VISIBILITY
+= {}
+#endif
+;
 
 /* Generic nul-content Null objects. */
 template <typename Type>
 static inline const Type& Null (void) {
-  ASSERT_STATIC (sizeof (Type) <= sizeof (_NullPool));
-  return *CastP<Type> (_NullPool);
+  static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
+  return *CastP<Type> (_hb_NullPool);
 }
 
 /* Specializaiton for arbitrary-content arbitrary-sized Null objects. */
@@ -147,7 +160,7 @@
 /*static*/ inline const Type& Null<Type> (void) { \
   return *CastP<Type> (_Null##Type); \
 } /* The following line really exists such that we end in a place needing semicolon */ \
-ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type))
+static_assert (Type::min_size + 1 <= sizeof (_Null##Type), "Null pool too small.  Enlarge.")
 
 /* Accessor macro. */
 #define Null(Type) Null<Type>()
@@ -172,29 +185,26 @@
  * Sanitize
  */
 
-#ifndef HB_DEBUG_SANITIZE
-#define HB_DEBUG_SANITIZE (HB_DEBUG+0)
-#endif
-
-
-#define TRACE_SANITIZE(this) \
-	hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace \
-	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
-	 "");
-
 /* This limits sanitizing time on really broken fonts. */
 #ifndef HB_SANITIZE_MAX_EDITS
 #define HB_SANITIZE_MAX_EDITS 32
 #endif
+#ifndef HB_SANITIZE_MAX_OPS_FACTOR
+#define HB_SANITIZE_MAX_OPS_FACTOR 8
+#endif
+#ifndef HB_SANITIZE_MAX_OPS_MIN
+#define HB_SANITIZE_MAX_OPS_MIN 16384
+#endif
 
 struct hb_sanitize_context_t :
        hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
 {
   inline hb_sanitize_context_t (void) :
 	debug_depth (0),
-	start (NULL), end (NULL),
-	writable (false), edit_count (0),
-	blob (NULL) {}
+	start (nullptr), end (nullptr),
+	writable (false), edit_count (0), max_ops (0),
+	blob (nullptr),
+	num_glyphs (0) {}
 
   inline const char *get_name (void) { return "SANITIZE"; }
   template <typename T, typename F>
@@ -214,9 +224,11 @@
 
   inline void start_processing (void)
   {
-    this->start = hb_blob_get_data (this->blob, NULL);
+    this->start = hb_blob_get_data (this->blob, nullptr);
     this->end = this->start + hb_blob_get_length (this->blob);
     assert (this->start <= this->end); /* Must not overflow. */
+    this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR,
+			 (unsigned) HB_SANITIZE_MAX_OPS_MIN);
     this->edit_count = 0;
     this->debug_depth = 0;
 
@@ -233,14 +245,17 @@
 		     this->start, this->end, this->edit_count);
 
     hb_blob_destroy (this->blob);
-    this->blob = NULL;
-    this->start = this->end = NULL;
+    this->blob = nullptr;
+    this->start = this->end = nullptr;
   }
 
   inline bool check_range (const void *base, unsigned int len) const
   {
     const char *p = (const char *) base;
-    bool ok = this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len;
+    bool ok = this->max_ops-- > 0 &&
+	      this->start <= p &&
+	      p <= this->end &&
+	      (unsigned int) (this->end - p) >= len;
 
     DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
        "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s",
@@ -304,7 +319,9 @@
   const char *start, *end;
   bool writable;
   unsigned int edit_count;
+  mutable int max_ops;
   hb_blob_t *blob;
+  unsigned int num_glyphs;
 };
 
 
@@ -313,8 +330,9 @@
 template <typename Type>
 struct Sanitizer
 {
-  static hb_blob_t *sanitize (hb_blob_t *blob) {
-    hb_sanitize_context_t c[1];
+  inline Sanitizer (void) {}
+
+  inline hb_blob_t *sanitize (hb_blob_t *blob) {
     bool sane;
 
     /* TODO is_sane() stuff */
@@ -349,7 +367,7 @@
     } else {
       unsigned int edit_count = c->edit_count;
       if (edit_count && !c->writable) {
-        c->start = hb_blob_get_data_writable (blob, NULL);
+        c->start = hb_blob_get_data_writable (blob, nullptr);
 	c->end = c->start + hb_blob_get_length (blob);
 
 	if (c->start) {
@@ -374,9 +392,14 @@
 
   static const Type* lock_instance (hb_blob_t *blob) {
     hb_blob_make_immutable (blob);
-    const char *base = hb_blob_get_data (blob, NULL);
+    const char *base = hb_blob_get_data (blob, nullptr);
     return unlikely (!base) ? &Null(Type) : CastP<Type> (base);
   }
+
+  inline void set_num_glyphs (unsigned int num_glyphs) { c->num_glyphs = num_glyphs; }
+
+  private:
+  hb_sanitize_context_t c[1];
 };
 
 
@@ -385,16 +408,6 @@
  * Serialize
  */
 
-#ifndef HB_DEBUG_SERIALIZE
-#define HB_DEBUG_SERIALIZE (HB_DEBUG+0)
-#endif
-
-
-#define TRACE_SERIALIZE(this) \
-	hb_auto_trace_t<HB_DEBUG_SERIALIZE, bool> trace \
-	(&c->debug_depth, "SERIALIZE", c, HB_FUNC, \
-	 "");
-
 
 struct hb_serialize_context_t
 {
@@ -445,7 +458,7 @@
   {
     if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) {
       this->ran_out_of_room = true;
-      return NULL;
+      return nullptr;
     }
     memset (this->head, 0, size);
     char *ret = this->head;
@@ -471,7 +484,7 @@
   {
     unsigned int size = obj.get_size ();
     Type *ret = this->allocate_size<Type> (size);
-    if (unlikely (!ret)) return NULL;
+    if (unlikely (!ret)) return nullptr;
     memcpy (ret, obj, size);
     return ret;
   }
@@ -481,7 +494,7 @@
   {
     unsigned int size = obj.min_size;
     assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
-    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return NULL;
+    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
     return reinterpret_cast<Type *> (&obj);
   }
 
@@ -490,7 +503,7 @@
   {
     unsigned int size = obj.get_size ();
     assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
-    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return NULL;
+    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr;
     return reinterpret_cast<Type *> (&obj);
   }
 
@@ -508,23 +521,25 @@
 template <typename Type>
 struct Supplier
 {
-  inline Supplier (const Type *array, unsigned int len_)
+  inline Supplier (const Type *array, unsigned int len_, unsigned int stride_=sizeof(Type))
   {
     head = array;
     len = len_;
+    stride = stride_;
   }
   inline const Type operator [] (unsigned int i) const
   {
     if (unlikely (i >= len)) return Type ();
-    return head[i];
+    return * (const Type *) (const void *) ((const char *) head + stride * i);
   }
 
-  inline void advance (unsigned int count)
+  inline Supplier<Type> & operator += (unsigned int count)
   {
     if (unlikely (count > len))
       count = len;
     len -= count;
-    head += count;
+    head = (const Type *) (const void *) ((const char *) head + stride * count);
+    return *this;
   }
 
   private:
@@ -532,12 +547,11 @@
   inline Supplier<Type>& operator= (const Supplier<Type> &); /* Disallow copy */
 
   unsigned int len;
+  unsigned int stride;
   const Type *head;
 };
 
 
-
-
 /*
  *
  * The OpenType Font File: Data Types
@@ -632,10 +646,11 @@
   inline bool operator == (const IntType<Type,Size> &o) const { return (Type) v == (Type) o.v; }
   inline bool operator != (const IntType<Type,Size> &o) const { return !(*this == o); }
   static inline int cmp (const IntType<Type,Size> *a, const IntType<Type,Size> *b) { return b->cmp (*a); }
-  inline int cmp (Type a) const
+  template <typename Type2>
+  inline int cmp (Type2 a) const
   {
     Type b = v;
-    if (sizeof (Type) < sizeof (int))
+    if (sizeof (Type) < sizeof (int) && sizeof (Type2) < sizeof (int))
       return (int) a - (int) b;
     else
       return a < b ? -1 : a == b ? 0 : +1;
@@ -651,23 +666,22 @@
   DEFINE_SIZE_STATIC (Size);
 };
 
-typedef	IntType<int8_t	, 1> CHAR;	/* 8-bit signed integer. */
-typedef	IntType<uint8_t	, 1> BYTE;	/* 8-bit unsigned integer. */
-typedef	IntType<int8_t	, 1> INT8;	/* 8-bit signed integer. */
-typedef IntType<uint16_t, 2> USHORT;	/* 16-bit unsigned integer. */
-typedef IntType<int16_t,  2> SHORT;	/* 16-bit signed integer. */
-typedef IntType<uint32_t, 4> ULONG;	/* 32-bit unsigned integer. */
-typedef IntType<int32_t,  4> LONG;	/* 32-bit signed integer. */
+typedef IntType<uint8_t,  1> HBUINT8;	/* 8-bit unsigned integer. */
+typedef IntType<int8_t,   1> HBINT8;	/* 8-bit signed integer. */
+typedef IntType<uint16_t, 2> HBUINT16;	/* 16-bit unsigned integer. */
+typedef IntType<int16_t,  2> HBINT16;	/* 16-bit signed integer. */
+typedef IntType<uint32_t, 4> HBUINT32;	/* 32-bit unsigned integer. */
+typedef IntType<int32_t,  4> HBINT32;	/* 32-bit signed integer. */
 typedef IntType<uint32_t, 3> UINT24;	/* 24-bit unsigned integer. */
 
-/* 16-bit signed integer (SHORT) that describes a quantity in FUnits. */
-typedef SHORT FWORD;
+/* 16-bit signed integer (HBINT16) that describes a quantity in FUnits. */
+typedef HBINT16 FWORD;
 
-/* 16-bit unsigned integer (USHORT) that describes a quantity in FUnits. */
-typedef USHORT UFWORD;
+/* 16-bit unsigned integer (HBUINT16) that describes a quantity in FUnits. */
+typedef HBUINT16 UFWORD;
 
 /* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */
-struct F2DOT14 : SHORT
+struct F2DOT14 : HBINT16
 {
   //inline float to_float (void) const { return ???; }
   //inline void set_float (float f) { v.set (f * ???); }
@@ -676,7 +690,7 @@
 };
 
 /* 32-bit signed fixed-point number (16.16). */
-struct Fixed: LONG
+struct Fixed: HBINT32
 {
   //inline float to_float (void) const { return ???; }
   //inline void set_float (float f) { v.set (f * ???); }
@@ -694,15 +708,15 @@
     return_trace (likely (c->check_struct (this)));
   }
   protected:
-  LONG major;
-  ULONG minor;
+  HBINT32 major;
+  HBUINT32 minor;
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
 /* Array of four uint8s (length = 32 bits) used to identify a script, language
  * system, feature, or baseline */
-struct Tag : ULONG
+struct Tag : HBUINT32
 {
   /* What the char* converters return is NOT nul-terminated.  Print using "%.4s" */
   inline operator const char* (void) const { return reinterpret_cast<const char *> (&this->v); }
@@ -713,35 +727,43 @@
 DEFINE_NULL_DATA (Tag, "    ");
 
 /* Glyph index number, same as uint16 (length = 16 bits) */
-struct GlyphID : USHORT {
-  static inline int cmp (const GlyphID *a, const GlyphID *b) { return b->USHORT::cmp (*a); }
-  inline int cmp (hb_codepoint_t a) const { return (int) a - (int) *this; }
-};
+typedef HBUINT16 GlyphID;
 
 /* Script/language-system/feature index */
-struct Index : USHORT {
+struct Index : HBUINT16 {
   static const unsigned int NOT_FOUND_INDEX = 0xFFFFu;
 };
 DEFINE_NULL_DATA (Index, "\xff\xff");
 
 /* Offset, Null offset = 0 */
-template <typename Type=USHORT>
+template <typename Type>
 struct Offset : Type
 {
   inline bool is_null (void) const { return 0 == *this; }
   public:
   DEFINE_SIZE_STATIC (sizeof(Type));
+
+  inline void *serialize (hb_serialize_context_t *c, const void *base)
+  {
+    void *t = c->start_embed<void> ();
+    this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */
+    return t;
+  }
 };
 
+typedef Offset<HBUINT16> Offset16;
+typedef Offset<HBUINT32> Offset32;
+
 
 /* CheckSum */
-struct CheckSum : ULONG
+struct CheckSum : HBUINT32
 {
   /* This is reference implementation from the spec. */
-  static inline uint32_t CalcTableChecksum (const ULONG *Table, uint32_t Length)
+  static inline uint32_t CalcTableChecksum (const HBUINT32 *Table, uint32_t Length)
   {
     uint32_t Sum = 0L;
-    const ULONG *EndPtr = Table+((Length+3) & ~3) / ULONG::static_size;
+    assert (0 == (Length & 3));
+    const HBUINT32 *EndPtr = Table + Length / HBUINT32::static_size;
 
     while (Table < EndPtr)
       Sum += *Table++;
@@ -750,7 +772,7 @@
 
   /* Note: data should be 4byte aligned and have 4byte padding at the end. */
   inline void set_for_data (const void *data, unsigned int length)
-  { set (CalcTableChecksum ((const ULONG *) data, length)); }
+  { set (CalcTableChecksum ((const HBUINT32 *) data, length)); }
 
   public:
   DEFINE_SIZE_STATIC (4);
@@ -761,7 +783,7 @@
  * Version Numbers
  */
 
-template <typename FixedType=USHORT>
+template <typename FixedType=HBUINT16>
 struct FixedVersion
 {
   inline uint32_t to_int (void) const { return (major << (sizeof(FixedType) * 8)) + minor; }
@@ -785,7 +807,7 @@
  * Use: (base+offset)
  */
 
-template <typename Type, typename OffsetType=USHORT>
+template <typename Type, typename OffsetType=HBUINT16>
 struct OffsetTo : Offset<OffsetType>
 {
   inline const Type& operator () (const void *base) const
@@ -797,9 +819,7 @@
 
   inline Type& serialize (hb_serialize_context_t *c, const void *base)
   {
-    Type *t = c->start_embed<Type> ();
-    this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */
-    return *t;
+    return * (Type *) Offset<OffsetType>::serialize (c, base);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
@@ -830,7 +850,7 @@
   }
   DEFINE_SIZE_STATIC (sizeof(OffsetType));
 };
-template <typename Type> struct LOffsetTo : OffsetTo<Type, ULONG> {};
+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>
@@ -842,7 +862,7 @@
  */
 
 /* An array with a number of elements. */
-template <typename Type, typename LenType=USHORT>
+template <typename Type, typename LenType=HBUINT16>
 struct ArrayOf
 {
   const Type *sub_array (unsigned int start_offset, unsigned int *pcount /* IN/OUT */) const
@@ -887,7 +907,7 @@
     if (unlikely (!serialize (c, items_len))) return_trace (false);
     for (unsigned int i = 0; i < items_len; i++)
       array[i] = items[i];
-    items.advance (items_len);
+    items += items_len;
     return_trace (true);
   }
 
@@ -939,11 +959,16 @@
     return -1;
   }
 
+  inline void qsort (void)
+  {
+    ::qsort (array, len, sizeof (Type), Type::cmp);
+  }
+
   private:
   inline bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && c->check_array (array, Type::static_size, len));
+    return_trace (len.sanitize (c) && c->check_array (array, Type::static_size, len));
   }
 
   public:
@@ -952,10 +977,10 @@
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), array);
 };
-template <typename Type> struct LArrayOf : ArrayOf<Type, ULONG> {};
+template <typename Type> struct LArrayOf : ArrayOf<Type, HBUINT32> {};
 
 /* Array of Offset's */
-template <typename Type, typename OffsetType=USHORT>
+template <typename Type, typename OffsetType=HBUINT16>
 struct OffsetArrayOf : ArrayOf<OffsetTo<Type, OffsetType> > {};
 
 /* Array of offsets relative to the beginning of the array itself. */
@@ -983,7 +1008,7 @@
 
 
 /* An array starting at second element. */
-template <typename Type, typename LenType=USHORT>
+template <typename Type, typename LenType=HBUINT16>
 struct HeadlessArrayOf
 {
   inline const Type& operator [] (unsigned int i) const
@@ -1005,16 +1030,10 @@
     if (unlikely (!c->extend (*this))) return_trace (false);
     for (unsigned int i = 0; i < items_len - 1; i++)
       array[i] = items[i];
-    items.advance (items_len - 1);
+    items += items_len - 1;
     return_trace (true);
   }
 
-  inline bool sanitize_shallow (hb_sanitize_context_t *c) const
-  {
-    return c->check_struct (this)
-	&& c->check_array (this, Type::static_size, len);
-  }
-
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -1032,6 +1051,15 @@
     return_trace (true);
   }
 
+  private:
+  inline bool sanitize_shallow (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (len.sanitize (c) &&
+		  (!len || c->check_array (array, Type::static_size, len - 1)));
+  }
+
+  public:
   LenType len;
   Type array[VAR];
   public:
@@ -1039,19 +1067,22 @@
 };
 
 
-/* An array with sorted elements.  Supports binary searching. */
-template <typename Type, typename LenType=USHORT>
+/*
+ * An array with sorted elements.  Supports binary searching.
+ */
+template <typename Type, typename LenType=HBUINT16>
 struct SortedArrayOf : ArrayOf<Type, LenType>
 {
   template <typename SearchType>
   inline int bsearch (const SearchType &x) const
   {
     /* Hand-coded bsearch here since this is in the hot inner loop. */
+    const Type *arr = this->array;
     int min = 0, max = (int) this->len - 1;
     while (min <= max)
     {
       int mid = (min + max) / 2;
-      int c = this->array[mid].cmp (x);
+      int c = arr[mid].cmp (x);
       if (c < 0)
         max = mid - 1;
       else if (c > 0)
@@ -1063,6 +1094,44 @@
   }
 };
 
+/*
+ * Binary-search arrays
+ */
+
+struct BinSearchHeader
+{
+  inline operator uint32_t (void) const { return len; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  inline void set (unsigned int v)
+  {
+    len.set (v);
+    assert (len == v);
+    entrySelectorZ.set (MAX (1u, _hb_bit_storage (v)) - 1);
+    searchRangeZ.set (16 * (1u << entrySelectorZ));
+    rangeShiftZ.set (v * 16 > searchRangeZ
+                     ? 16 * v - searchRangeZ
+                     : 0);
+  }
+
+  protected:
+  HBUINT16	len;
+  HBUINT16	searchRangeZ;
+  HBUINT16	entrySelectorZ;
+  HBUINT16	rangeShiftZ;
+
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+template <typename Type>
+struct BinSearchArrayOf : SortedArrayOf<Type, BinSearchHeader> {};
+
 
 /* Lazy struct and blob loaders. */
 
@@ -1073,7 +1142,7 @@
   inline void init (hb_face_t *face_)
   {
     face = face_;
-    instance = NULL;
+    instance = nullptr;
   }
 
   inline void fini (void)
@@ -1096,7 +1165,7 @@
         p = const_cast<T *> (&OT::Null(T));
       else
 	p->init (face);
-      if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), NULL, p)))
+      if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), nullptr, p)))
       {
 	if (p != &OT::Null(T))
 	  p->fini ();
@@ -1123,8 +1192,8 @@
   inline void init (hb_face_t *face_)
   {
     face = face_;
-    instance = NULL;
-    blob = NULL;
+    blob = nullptr;
+    instance = nullptr;
   }
 
   inline void fini (void)
@@ -1138,9 +1207,9 @@
     T *p = (T *) hb_atomic_ptr_get (&instance);
     if (unlikely (!p))
     {
-      hb_blob_t *blob_ = OT::Sanitizer<T>::sanitize (face->reference_table (T::tableTag));
+      hb_blob_t *blob_ = OT::Sanitizer<T>().sanitize (face->reference_table (T::tableTag));
       p = const_cast<T *>(OT::Sanitizer<T>::lock_instance (blob_));
-      if (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), NULL, p))
+      if (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), nullptr, p))
       {
 	hb_blob_destroy (blob_);
 	goto retry;
@@ -1155,10 +1224,10 @@
     return get();
   }
 
-  private:
   hb_face_t *face;
-  T *instance;
   mutable hb_blob_t *blob;
+  private:
+  mutable T *instance;
 };
 
 
diff --git a/src/hb-ot-cbdt-table.hh b/src/hb-ot-cbdt-table.hh
index 0a7fbf5..e451952 100644
--- a/src/hb-ot-cbdt-table.hh
+++ b/src/hb-ot-cbdt-table.hh
@@ -47,20 +47,20 @@
     extents->height = -height;
   }
 
-  BYTE height;
-  BYTE width;
-  CHAR bearingX;
-  CHAR bearingY;
-  BYTE advance;
+  HBUINT8 height;
+  HBUINT8 width;
+  HBINT8 bearingX;
+  HBINT8 bearingY;
+  HBUINT8 advance;
 
   DEFINE_SIZE_STATIC(5);
 };
 
 struct BigGlyphMetrics : SmallGlyphMetrics
 {
-  CHAR vertBearingX;
-  CHAR vertBearingY;
-  BYTE vertAdvance;
+  HBINT8 vertBearingX;
+  HBINT8 vertBearingY;
+  HBUINT8 vertAdvance;
 
   DEFINE_SIZE_STATIC(8);
 };
@@ -73,18 +73,18 @@
     return_trace (c->check_struct (this));
   }
 
-  CHAR ascender;
-  CHAR decender;
-  BYTE widthMax;
-  CHAR caretSlopeNumerator;
-  CHAR caretSlopeDenominator;
-  CHAR caretOffset;
-  CHAR minOriginSB;
-  CHAR minAdvanceSB;
-  CHAR maxBeforeBL;
-  CHAR minAfterBL;
-  CHAR padding1;
-  CHAR padding2;
+  HBINT8 ascender;
+  HBINT8 decender;
+  HBUINT8 widthMax;
+  HBINT8 caretSlopeNumerator;
+  HBINT8 caretSlopeDenominator;
+  HBINT8 caretOffset;
+  HBINT8 minOriginSB;
+  HBINT8 minAdvanceSB;
+  HBINT8 maxBeforeBL;
+  HBINT8 minAfterBL;
+  HBINT8 padding1;
+  HBINT8 padding2;
 
   DEFINE_SIZE_STATIC(12);
 };
@@ -102,9 +102,9 @@
     return_trace (c->check_struct (this));
   }
 
-  USHORT indexFormat;
-  USHORT imageFormat;
-  ULONG imageDataOffset;
+  HBUINT16 indexFormat;
+  HBUINT16 imageFormat;
+  HBUINT32 imageDataOffset;
 
   DEFINE_SIZE_STATIC(8);
 };
@@ -137,8 +137,8 @@
   DEFINE_SIZE_ARRAY(8, offsetArrayZ);
 };
 
-struct IndexSubtableFormat1 : IndexSubtableFormat1Or3<ULONG> {};
-struct IndexSubtableFormat3 : IndexSubtableFormat1Or3<USHORT> {};
+struct IndexSubtableFormat1 : IndexSubtableFormat1Or3<HBUINT32> {};
+struct IndexSubtableFormat3 : IndexSubtableFormat1Or3<HBUINT16> {};
 
 struct IndexSubtable
 {
@@ -214,8 +214,8 @@
 						   offset, length, format);
   }
 
-  USHORT firstGlyphIndex;
-  USHORT lastGlyphIndex;
+  HBUINT16 firstGlyphIndex;
+  HBUINT16 lastGlyphIndex;
   LOffsetTo<IndexSubtable> offsetToSubtable;
 
   DEFINE_SIZE_STATIC(8);
@@ -245,7 +245,7 @@
         return &indexSubtablesZ[i];
       }
     }
-    return NULL;
+    return nullptr;
   }
 
   protected:
@@ -276,19 +276,19 @@
 
   protected:
   LOffsetTo<IndexSubtableArray> indexSubtableArrayOffset;
-  ULONG indexTablesSize;
-  ULONG numberOfIndexSubtables;
-  ULONG colorRef;
+  HBUINT32 indexTablesSize;
+  HBUINT32 numberOfIndexSubtables;
+  HBUINT32 colorRef;
   SBitLineMetrics horizontal;
   SBitLineMetrics vertical;
-  USHORT startGlyphIndex;
-  USHORT endGlyphIndex;
-  BYTE ppemX;
-  BYTE ppemY;
-  BYTE bitDepth;
-  CHAR flags;
+  HBUINT16 startGlyphIndex;
+  HBUINT16 endGlyphIndex;
+  HBUINT8 ppemX;
+  HBUINT8 ppemY;
+  HBUINT8 bitDepth;
+  HBINT8 flags;
 
-public:
+  public:
   DEFINE_SIZE_STATIC(48);
 };
 
@@ -300,8 +300,8 @@
 struct GlyphBitmapDataFormat17
 {
   SmallGlyphMetrics glyphMetrics;
-  ULONG dataLen;
-  BYTE dataZ[VAR];
+  HBUINT32 dataLen;
+  HBUINT8 dataZ[VAR];
 
   DEFINE_SIZE_ARRAY(9, dataZ);
 };
@@ -315,6 +315,8 @@
 
 struct CBLC
 {
+  friend struct CBDT;
+
   static const hb_tag_t tableTag = HB_OT_TAG_CBLC;
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -325,7 +327,7 @@
 		  sizeTables.sanitize (c, this));
   }
 
-  public:
+  protected:
   const IndexSubtableRecord *find_table (hb_codepoint_t glyph,
 					 unsigned int *x_ppem, unsigned int *y_ppem) const
   {
@@ -344,7 +346,7 @@
       }
     }
 
-    return NULL;
+    return nullptr;
   }
 
   protected:
@@ -371,9 +373,94 @@
 		  likely (version.major == 2 || version.major == 3));
   }
 
+  struct accelerator_t
+  {
+    inline void init (hb_face_t *face)
+    {
+      upem = hb_face_get_upem (face);
+
+      cblc_blob = Sanitizer<CBLC>().sanitize (face->reference_table (HB_OT_TAG_CBLC));
+      cbdt_blob = Sanitizer<CBDT>().sanitize (face->reference_table (HB_OT_TAG_CBDT));
+      cbdt_len = hb_blob_get_length (cbdt_blob);
+
+      if (hb_blob_get_length (cblc_blob) == 0) {
+	cblc = nullptr;
+	cbdt = nullptr;
+	return;  /* Not a bitmap font. */
+      }
+      cblc = Sanitizer<CBLC>::lock_instance (cblc_blob);
+      cbdt = Sanitizer<CBDT>::lock_instance (cbdt_blob);
+
+    }
+
+    inline void fini (void)
+    {
+      hb_blob_destroy (this->cblc_blob);
+      hb_blob_destroy (this->cbdt_blob);
+    }
+
+    inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
+    {
+      unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */
+
+      if (!cblc)
+	return false;  // Not a color bitmap font.
+
+      const IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem);
+      if (!subtable_record || !x_ppem || !y_ppem)
+	return false;
+
+      if (subtable_record->get_extents (extents))
+	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))
+	return false;
+
+      {
+	if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length))
+	  return false;
+
+	switch (image_format)
+	{
+	  case 17: {
+	    if (unlikely (image_length < GlyphBitmapDataFormat17::min_size))
+	      return false;
+
+	    const GlyphBitmapDataFormat17& glyphFormat17 =
+		StructAtOffset<GlyphBitmapDataFormat17> (this->cbdt, image_offset);
+	    glyphFormat17.glyphMetrics.get_extents (extents);
+	  }
+	  break;
+	  default:
+	    // TODO: Support other image formats.
+	    return false;
+	}
+      }
+
+      /* Convert to the font units. */
+      extents->x_bearing *= upem / (float) x_ppem;
+      extents->y_bearing *= upem / (float) y_ppem;
+      extents->width *= upem / (float) x_ppem;
+      extents->height *= upem / (float) y_ppem;
+
+      return true;
+    }
+
+    private:
+    hb_blob_t *cblc_blob;
+    hb_blob_t *cbdt_blob;
+    const CBLC *cblc;
+    const CBDT *cbdt;
+
+    unsigned int cbdt_len;
+    unsigned int upem;
+  };
+
+
   protected:
   FixedVersion<>version;
-  BYTE dataZ[VAR];
+  HBUINT8 dataZ[VAR];
 
   public:
   DEFINE_SIZE_ARRAY(4, dataZ);
diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index 3a53a1c..0207989 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -28,7 +28,7 @@
 #define HB_OT_CMAP_TABLE_HH
 
 #include "hb-open-type-private.hh"
-
+#include "hb-subset-plan.hh"
 
 namespace OT {
 
@@ -58,10 +58,10 @@
   }
 
   protected:
-  USHORT	format;		/* Format number is set to 0. */
-  USHORT	lengthZ;	/* Byte length of this subtable. */
-  USHORT	languageZ;	/* Ignore. */
-  BYTE		glyphIdArray[256];/* An array that maps character
+  HBUINT16	format;		/* Format number is set to 0. */
+  HBUINT16	lengthZ;	/* Byte length of this subtable. */
+  HBUINT16	languageZ;	/* Ignore. */
+  HBUINT8	glyphIdArray[256];/* An array that maps character
 				 * code to glyph index values. */
   public:
   DEFINE_SIZE_STATIC (6 + 256);
@@ -88,8 +88,8 @@
 
       /* Custom two-array bsearch. */
       int min = 0, max = (int) thiz->segCount - 1;
-      const USHORT *startCount = thiz->startCount;
-      const USHORT *endCount = thiz->endCount;
+      const HBUINT16 *startCount = thiz->startCount;
+      const HBUINT16 *endCount = thiz->endCount;
       unsigned int i;
       while (min <= max)
       {
@@ -127,11 +127,11 @@
       return true;
     }
 
-    const USHORT *endCount;
-    const USHORT *startCount;
-    const USHORT *idDelta;
-    const USHORT *idRangeOffset;
-    const USHORT *glyphIdArray;
+    const HBUINT16 *endCount;
+    const HBUINT16 *startCount;
+    const HBUINT16 *idDelta;
+    const HBUINT16 *idRangeOffset;
+    const HBUINT16 *glyphIdArray;
     unsigned int segCount;
     unsigned int glyphIdArrayLength;
   };
@@ -165,24 +165,24 @@
   }
 
   protected:
-  USHORT	format;		/* Format number is set to 4. */
-  USHORT	length;		/* This is the length in bytes of the
+  HBUINT16	format;		/* Format number is set to 4. */
+  HBUINT16	length;		/* This is the length in bytes of the
 				 * subtable. */
-  USHORT	languageZ;	/* Ignore. */
-  USHORT	segCountX2;	/* 2 x segCount. */
-  USHORT	searchRangeZ;	/* 2 * (2**floor(log2(segCount))) */
-  USHORT	entrySelectorZ;	/* log2(searchRange/2) */
-  USHORT	rangeShiftZ;	/* 2 x segCount - searchRange */
+  HBUINT16	languageZ;	/* Ignore. */
+  HBUINT16	segCountX2;	/* 2 x segCount. */
+  HBUINT16	searchRangeZ;	/* 2 * (2**floor(log2(segCount))) */
+  HBUINT16	entrySelectorZ;	/* log2(searchRange/2) */
+  HBUINT16	rangeShiftZ;	/* 2 x segCount - searchRange */
 
-  USHORT	values[VAR];
+  HBUINT16	values[VAR];
 #if 0
-  USHORT	endCount[segCount];	/* End characterCode for each segment,
+  HBUINT16	endCount[segCount];	/* End characterCode for each segment,
 					 * last=0xFFFFu. */
-  USHORT	reservedPad;		/* Set to 0. */
-  USHORT	startCount[segCount];	/* Start character code for each segment. */
-  SHORT		idDelta[segCount];	/* Delta for all character codes in segment. */
-  USHORT	idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */
-  USHORT	glyphIdArray[VAR];	/* Glyph index array (arbitrary length) */
+  HBUINT16	reservedPad;		/* Set to 0. */
+  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) */
 #endif
 
   public:
@@ -193,6 +193,7 @@
 {
   friend struct CmapSubtableFormat12;
   friend struct CmapSubtableFormat13;
+  friend struct cmap;
 
   int cmp (hb_codepoint_t codepoint) const
   {
@@ -208,9 +209,9 @@
   }
 
   private:
-  ULONG		startCharCode;	/* First character code in this group. */
-  ULONG		endCharCode;	/* Last character code in this group. */
-  ULONG		glyphID;	/* Glyph index; interpretation depends on
+  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. */
   public:
   DEFINE_SIZE_STATIC (12);
@@ -247,12 +248,14 @@
   DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray);
 };
 
-struct CmapSubtableFormat6  : CmapSubtableTrimmed<USHORT> {};
-struct CmapSubtableFormat10 : CmapSubtableTrimmed<ULONG > {};
+struct CmapSubtableFormat6  : CmapSubtableTrimmed<HBUINT16> {};
+struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32 > {};
 
 template <typename T>
 struct CmapSubtableLongSegmented
 {
+  friend struct cmap;
+
   inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
   {
     int i = groups.bsearch (codepoint);
@@ -268,12 +271,22 @@
     return_trace (c->check_struct (this) && groups.sanitize (c));
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+                         hb_prealloced_array_t<CmapSubtableLongGroup> &group_data)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    Supplier<CmapSubtableLongGroup> supplier (group_data.array, group_data.len);
+    if (unlikely (!groups.serialize (c, supplier, group_data.len))) return_trace (false);
+    return true;
+  }
+
   protected:
-  USHORT	format;		/* Subtable format; set to 12. */
-  USHORT	reservedZ;	/* Reserved; set to 0. */
-  ULONG		lengthZ;	/* Byte length of this subtable. */
-  ULONG		languageZ;	/* Ignore. */
-  SortedArrayOf<CmapSubtableLongGroup, ULONG>
+  HBUINT16	format;		/* Subtable format; set to 12. */
+  HBUINT16	reservedZ;	/* Reserved; set to 0. */
+  HBUINT32		lengthZ;	/* Byte length of this subtable. */
+  HBUINT32		languageZ;	/* Ignore. */
+  SortedArrayOf<CmapSubtableLongGroup, HBUINT32>
 		groups;		/* Groupings. */
   public:
   DEFINE_SIZE_ARRAY (16, groups);
@@ -316,13 +329,13 @@
   }
 
   UINT24	startUnicodeValue;	/* First value in this range. */
-  BYTE		additionalCount;	/* Number of additional values in this
+  HBUINT8		additionalCount;	/* Number of additional values in this
 					 * range. */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
-typedef SortedArrayOf<UnicodeValueRange, ULONG> DefaultUVS;
+typedef SortedArrayOf<UnicodeValueRange, HBUINT32> DefaultUVS;
 
 struct UVSMapping
 {
@@ -343,7 +356,7 @@
   DEFINE_SIZE_STATIC (5);
 };
 
-typedef SortedArrayOf<UVSMapping, ULONG> NonDefaultUVS;
+typedef SortedArrayOf<UVSMapping, HBUINT32> NonDefaultUVS;
 
 struct VariationSelectorRecord
 {
@@ -405,9 +418,9 @@
   }
 
   protected:
-  USHORT	format;		/* Format number is set to 14. */
-  ULONG		lengthZ;	/* Byte length of this subtable. */
-  SortedArrayOf<VariationSelectorRecord, ULONG>
+  HBUINT16	format;		/* Format number is set to 14. */
+  HBUINT32		lengthZ;	/* Byte length of this subtable. */
+  SortedArrayOf<VariationSelectorRecord, HBUINT32>
 		record;		/* Variation selector records; sorted
 				 * in increasing order of `varSelector'. */
   public:
@@ -422,12 +435,12 @@
 			 hb_codepoint_t *glyph) const
   {
     switch (u.format) {
-    case  0: return u.format0 .get_glyph(codepoint, glyph);
-    case  4: return u.format4 .get_glyph(codepoint, glyph);
-    case  6: return u.format6 .get_glyph(codepoint, glyph);
-    case 10: return u.format10.get_glyph(codepoint, glyph);
-    case 12: return u.format12.get_glyph(codepoint, glyph);
-    case 13: return u.format13.get_glyph(codepoint, glyph);
+    case  0: return u.format0 .get_glyph (codepoint, glyph);
+    case  4: return u.format4 .get_glyph (codepoint, glyph);
+    case  6: return u.format6 .get_glyph (codepoint, glyph);
+    case 10: return u.format10.get_glyph (codepoint, glyph);
+    case 12: return u.format12.get_glyph (codepoint, glyph);
+    case 13: return u.format13.get_glyph (codepoint, glyph);
     case 14:
     default: return false;
     }
@@ -451,7 +464,7 @@
 
   public:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   CmapSubtableFormat0	format0;
   CmapSubtableFormat4	format4;
   CmapSubtableFormat6	format6;
@@ -484,8 +497,8 @@
 		  subtable.sanitize (c, base));
   }
 
-  USHORT	platformID;	/* Platform ID. */
-  USHORT	encodingID;	/* Platform-specific encoding ID. */
+  HBUINT16	platformID;	/* Platform ID. */
+  HBUINT16	encodingID;	/* Platform-specific encoding ID. */
   LOffsetTo<CmapSubtable>
 		subtable;	/* Byte offset from beginning of table to the subtable for this encoding. */
   public:
@@ -496,6 +509,254 @@
 {
   static const hb_tag_t tableTag	= HB_OT_TAG_cmap;
 
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  likely (version == 0) &&
+		  encodingRecord.sanitize (c, this));
+  }
+
+  inline bool populate_groups (hb_subset_plan_t *plan,
+			       hb_prealloced_array_t<CmapSubtableLongGroup> *groups) const
+  {
+    CmapSubtableLongGroup *group = nullptr;
+    for (unsigned int i = 0; i < plan->codepoints.len; i++) {
+
+      hb_codepoint_t cp = plan->codepoints[i];
+      if (!group || cp - 1 != group->endCharCode)
+      {
+        group = groups->push ();
+        group->startCharCode.set (cp);
+        group->endCharCode.set (cp);
+        hb_codepoint_t new_gid;
+        if (unlikely (!hb_subset_plan_new_gid_for_codepoint (plan, cp, &new_gid)))
+        {
+          DEBUG_MSG(SUBSET, nullptr, "Unable to find new gid for %04x", cp);
+          return false;
+        }
+        group->glyphID.set (new_gid);
+      } else
+      {
+        group->endCharCode.set (cp);
+      }
+    }
+
+    DEBUG_MSG(SUBSET, nullptr, "cmap");
+    for (unsigned int i = 0; i < groups->len; i++) {
+      CmapSubtableLongGroup& group = (*groups)[i];
+      DEBUG_MSG(SUBSET, nullptr, "  %d: U+%04X-U+%04X, gid %d-%d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID, (uint32_t) group.glyphID + ((uint32_t) group.endCharCode - (uint32_t) group.startCharCode));
+    }
+
+    return true;
+  }
+
+  inline bool _subset (hb_prealloced_array_t<CmapSubtableLongGroup> &groups,
+		       size_t dest_sz,
+		       void *dest) const
+  {
+    hb_serialize_context_t c (dest, dest_sz);
+
+    OT::cmap *cmap = c.start_serialize<OT::cmap> ();
+    if (unlikely (!c.extend_min (*cmap)))
+    {
+      return false;
+    }
+
+    cmap->version.set (0);
+
+    if (unlikely (!cmap->encodingRecord.serialize (&c, /* numTables */ 1))) return false;
+
+    EncodingRecord &rec = cmap->encodingRecord[0];
+    rec.platformID.set (3); // Windows
+    rec.encodingID.set (10); // Unicode UCS-4
+
+    /* capture offset to subtable */
+    CmapSubtable &subtable = rec.subtable.serialize (&c, cmap);
+
+    subtable.u.format.set (12);
+
+    CmapSubtableFormat12 &format12 = subtable.u.format12;
+    if (unlikely (!c.extend_min (format12))) return false;
+
+    format12.format.set (12);
+    format12.reservedZ.set (0);
+    format12.lengthZ.set (16 + 12 * groups.len);
+
+    if (unlikely (!format12.serialize (&c, groups))) return false;
+
+    c.end_serialize ();
+
+    return true;
+  }
+
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_auto_array_t<CmapSubtableLongGroup> groups;
+
+    if (unlikely (!populate_groups (plan, &groups))) return false;
+
+    // We now know how big our blob needs to be
+    // TODO use APIs from the structs to get size?
+    size_t dest_sz = 4 // header
+                   + 8 // 1 EncodingRecord
+                   + 16 // Format 12 header
+                   + 12 * groups.len; // SequentialMapGroup records
+    void *dest = malloc (dest_sz);
+    if (unlikely (!dest)) {
+      DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for cmap subset output", (unsigned long) dest_sz);
+      return false;
+    }
+
+    if (unlikely (!_subset (groups, dest_sz, dest)))
+    {
+      free (dest);
+      return false;
+    }
+
+    // all done, write the blob into dest
+    hb_blob_t *cmap_prime = hb_blob_create ((const char *)dest,
+                                            dest_sz,
+                                            HB_MEMORY_MODE_READONLY,
+                                            dest,
+                                            free);
+    bool result =  hb_subset_plan_add_table (plan, HB_OT_TAG_cmap, cmap_prime);
+    hb_blob_destroy (cmap_prime);
+    return result;
+  }
+
+  struct accelerator_t
+  {
+    inline void init (hb_face_t *face)
+    {
+      this->blob = OT::Sanitizer<OT::cmap>().sanitize (face->reference_table (HB_OT_TAG_cmap));
+      const OT::cmap *cmap = OT::Sanitizer<OT::cmap>::lock_instance (this->blob);
+      const OT::CmapSubtable *subtable = nullptr;
+      const OT::CmapSubtableFormat14 *subtable_uvs = nullptr;
+
+      bool symbol = false;
+      /* 32-bit subtables. */
+      if (!subtable) subtable = cmap->find_subtable (3, 10);
+      if (!subtable) subtable = cmap->find_subtable (0, 6);
+      if (!subtable) subtable = cmap->find_subtable (0, 4);
+      /* 16-bit subtables. */
+      if (!subtable) subtable = cmap->find_subtable (3, 1);
+      if (!subtable) subtable = cmap->find_subtable (0, 3);
+      if (!subtable) subtable = cmap->find_subtable (0, 2);
+      if (!subtable) subtable = cmap->find_subtable (0, 1);
+      if (!subtable) subtable = cmap->find_subtable (0, 0);
+      if (!subtable)
+      {
+	subtable = cmap->find_subtable (3, 0);
+	if (subtable) symbol = true;
+      }
+      /* Meh. */
+      if (!subtable) subtable = &OT::Null(OT::CmapSubtable);
+
+      /* UVS subtable. */
+      if (!subtable_uvs)
+      {
+	const OT::CmapSubtable *st = cmap->find_subtable (0, 5);
+	if (st && st->u.format == 14)
+	  subtable_uvs = &st->u.format14;
+      }
+      /* Meh. */
+      if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14);
+
+      this->uvs_table = subtable_uvs;
+
+      this->get_glyph_data = subtable;
+      if (unlikely (symbol))
+	this->get_glyph_func = get_glyph_from_symbol<OT::CmapSubtable>;
+      else
+	switch (subtable->u.format) {
+	/* Accelerate format 4 and format 12. */
+	default: this->get_glyph_func = get_glyph_from<OT::CmapSubtable>;		break;
+	case 12: this->get_glyph_func = get_glyph_from<OT::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;
+	  }
+	  break;
+	}
+    }
+
+    inline void fini (void)
+    {
+      hb_blob_destroy (this->blob);
+    }
+
+    inline bool get_nominal_glyph (hb_codepoint_t  unicode,
+				   hb_codepoint_t *glyph) const
+    {
+      return this->get_glyph_func (this->get_glyph_data, unicode, glyph);
+    }
+
+    inline bool get_variation_glyph (hb_codepoint_t  unicode,
+				     hb_codepoint_t  variation_selector,
+				     hb_codepoint_t *glyph) const
+    {
+      switch (this->uvs_table->get_glyph_variant (unicode,
+						  variation_selector,
+						  glyph))
+      {
+	case OT::GLYPH_VARIANT_NOT_FOUND:		return false;
+	case OT::GLYPH_VARIANT_FOUND:		return true;
+	case OT::GLYPH_VARIANT_USE_DEFAULT:	break;
+      }
+
+      return get_nominal_glyph (unicode, glyph);
+    }
+
+    protected:
+    typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
+					      hb_codepoint_t codepoint,
+					      hb_codepoint_t *glyph);
+
+    template <typename Type>
+    static inline bool get_glyph_from (const void *obj,
+				       hb_codepoint_t codepoint,
+				       hb_codepoint_t *glyph)
+    {
+      const Type *typed_obj = (const Type *) obj;
+      return typed_obj->get_glyph (codepoint, glyph);
+    }
+
+    template <typename Type>
+    static inline bool get_glyph_from_symbol (const void *obj,
+					      hb_codepoint_t codepoint,
+					      hb_codepoint_t *glyph)
+    {
+      const Type *typed_obj = (const Type *) obj;
+      if (likely (typed_obj->get_glyph (codepoint, glyph)))
+	return true;
+
+      if (codepoint <= 0x00FFu)
+      {
+	/* For symbol-encoded OpenType fonts, we duplicate the
+	 * U+F000..F0FF range at U+0000..U+00FF.  That's what
+	 * Windows seems to do, and that's hinted about at:
+	 * http://www.microsoft.com/typography/otspec/recom.htm
+	 * under "Non-Standard (Symbol) Fonts". */
+	return typed_obj->get_glyph (0xF000u + codepoint, glyph);
+      }
+
+      return false;
+    }
+
+    private:
+    hb_cmap_get_glyph_func_t get_glyph_func;
+    const void *get_glyph_data;
+    OT::CmapSubtableFormat4::accelerator_t format4_accel;
+
+    const OT::CmapSubtableFormat14 *uvs_table;
+    hb_blob_t *blob;
+  };
+
+  protected:
+
   inline const CmapSubtable *find_subtable (unsigned int platform_id,
 					    unsigned int encoding_id) const
   {
@@ -508,20 +769,13 @@
      * unsorted subtable list. */
     int result = encodingRecord./*bsearch*/lsearch (key);
     if (result == -1 || !encodingRecord[result].subtable)
-      return NULL;
+      return nullptr;
 
     return &(this+encodingRecord[result].subtable);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  likely (version == 0) &&
-		  encodingRecord.sanitize (c, this));
-  }
-
-  USHORT		version;	/* Table version number (0). */
+  protected:
+  HBUINT16		version;	/* Table version number (0). */
   SortedArrayOf<EncodingRecord>
 			encodingRecord;	/* Encoding tables. */
   public:
diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc
index 009db20..9864064 100644
--- a/src/hb-ot-font.cc
+++ b/src/hb-ot-font.cc
@@ -33,409 +33,20 @@
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-cbdt-table.hh"
 #include "hb-ot-glyf-table.hh"
-#include "hb-ot-head-table.hh"
-#include "hb-ot-hhea-table.hh"
 #include "hb-ot-hmtx-table.hh"
-#include "hb-ot-os2-table.hh"
-#include "hb-ot-var-hvar-table.hh"
-//#include "hb-ot-post-table.hh"
+#include "hb-ot-kern-table.hh"
+#include "hb-ot-post-table.hh"
 
 
-struct hb_ot_face_metrics_accelerator_t
-{
-  unsigned int num_metrics;
-  unsigned int num_advances;
-  unsigned int default_advance;
-  unsigned short ascender;
-  unsigned short descender;
-  unsigned short line_gap;
-  bool has_font_extents;
-
-  const OT::hmtxvmtx *table;
-  hb_blob_t *blob;
-
-  const OT::HVARVVAR *var;
-  hb_blob_t *var_blob;
-
-  inline void init (hb_face_t *face,
-		    hb_tag_t _hea_tag,
-		    hb_tag_t _mtx_tag,
-		    hb_tag_t _var_tag,
-		    hb_tag_t os2_tag,
-		    unsigned int default_advance = 0)
-  {
-    this->default_advance = default_advance ? default_advance : face->get_upem ();
-
-    bool got_font_extents = false;
-    if (os2_tag)
-    {
-      hb_blob_t *os2_blob = OT::Sanitizer<OT::os2>::sanitize (face->reference_table (os2_tag));
-      const OT::os2 *os2 = OT::Sanitizer<OT::os2>::lock_instance (os2_blob);
-#define USE_TYPO_METRICS (1u<<7)
-      if (0 != (os2->fsSelection & USE_TYPO_METRICS))
-      {
-	this->ascender = os2->sTypoAscender;
-	this->descender = os2->sTypoDescender;
-	this->line_gap = os2->sTypoLineGap;
-	got_font_extents = (this->ascender | this->descender) != 0;
-      }
-      hb_blob_destroy (os2_blob);
-    }
-
-    hb_blob_t *_hea_blob = OT::Sanitizer<OT::_hea>::sanitize (face->reference_table (_hea_tag));
-    const OT::_hea *_hea = OT::Sanitizer<OT::_hea>::lock_instance (_hea_blob);
-    this->num_advances = _hea->numberOfLongMetrics;
-    if (!got_font_extents)
-    {
-      this->ascender = _hea->ascender;
-      this->descender = _hea->descender;
-      this->line_gap = _hea->lineGap;
-      got_font_extents = (this->ascender | this->descender) != 0;
-    }
-    hb_blob_destroy (_hea_blob);
-
-    this->has_font_extents = got_font_extents;
-
-    this->blob = OT::Sanitizer<OT::hmtxvmtx>::sanitize (face->reference_table (_mtx_tag));
-
-    /* Cap num_metrics() and num_advances() based on table length. */
-    unsigned int len = hb_blob_get_length (this->blob);
-    if (unlikely (this->num_advances * 4 > len))
-      this->num_advances = len / 4;
-    this->num_metrics = this->num_advances + (len - 4 * this->num_advances) / 2;
-
-    /* We MUST set num_metrics to zero if num_advances is zero.
-     * Our get_advance() depends on that. */
-    if (unlikely (!this->num_advances))
-    {
-      this->num_metrics = this->num_advances = 0;
-      hb_blob_destroy (this->blob);
-      this->blob = hb_blob_get_empty ();
-    }
-    this->table = OT::Sanitizer<OT::hmtxvmtx>::lock_instance (this->blob);
-
-    this->var_blob = OT::Sanitizer<OT::HVARVVAR>::sanitize (face->reference_table (_var_tag));
-    this->var = OT::Sanitizer<OT::HVARVVAR>::lock_instance (this->var_blob);
-  }
-
-  inline void fini (void)
-  {
-    hb_blob_destroy (this->blob);
-    hb_blob_destroy (this->var_blob);
-  }
-
-  inline unsigned int get_advance (hb_codepoint_t  glyph,
-				   hb_font_t      *font) const
-  {
-    if (unlikely (glyph >= this->num_metrics))
-    {
-      /* If this->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 (this->num_metrics)
-	return 0;
-      else
-	return this->default_advance;
-    }
-
-    return this->table->longMetric[MIN (glyph, this->num_advances - 1)].advance
-	 + this->var->get_advance_var (glyph, font->coords, font->num_coords); // TODO Optimize?!
-  }
-};
-
-struct hb_ot_face_glyf_accelerator_t
-{
-  bool short_offset;
-  unsigned int num_glyphs;
-  const OT::loca *loca;
-  const OT::glyf *glyf;
-  hb_blob_t *loca_blob;
-  hb_blob_t *glyf_blob;
-  unsigned int glyf_len;
-
-  inline void init (hb_face_t *face)
-  {
-    hb_blob_t *head_blob = OT::Sanitizer<OT::head>::sanitize (face->reference_table (HB_OT_TAG_head));
-    const OT::head *head = OT::Sanitizer<OT::head>::lock_instance (head_blob);
-    if ((unsigned int) head->indexToLocFormat > 1 || head->glyphDataFormat != 0)
-    {
-      /* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
-      hb_blob_destroy (head_blob);
-      return;
-    }
-    this->short_offset = 0 == head->indexToLocFormat;
-    hb_blob_destroy (head_blob);
-
-    this->loca_blob = OT::Sanitizer<OT::loca>::sanitize (face->reference_table (HB_OT_TAG_loca));
-    this->loca = OT::Sanitizer<OT::loca>::lock_instance (this->loca_blob);
-    this->glyf_blob = OT::Sanitizer<OT::glyf>::sanitize (face->reference_table (HB_OT_TAG_glyf));
-    this->glyf = OT::Sanitizer<OT::glyf>::lock_instance (this->glyf_blob);
-
-    this->num_glyphs = MAX (1u, hb_blob_get_length (this->loca_blob) / (this->short_offset ? 2 : 4)) - 1;
-    this->glyf_len = hb_blob_get_length (this->glyf_blob);
-  }
-
-  inline void fini (void)
-  {
-    hb_blob_destroy (this->loca_blob);
-    hb_blob_destroy (this->glyf_blob);
-  }
-
-  inline bool get_extents (hb_codepoint_t glyph,
-			   hb_glyph_extents_t *extents) const
-  {
-    if (unlikely (glyph >= this->num_glyphs))
-      return false;
-
-    unsigned int start_offset, end_offset;
-    if (this->short_offset)
-    {
-      start_offset = 2 * this->loca->u.shortsZ[glyph];
-      end_offset   = 2 * this->loca->u.shortsZ[glyph + 1];
-    }
-    else
-    {
-      start_offset = this->loca->u.longsZ[glyph];
-      end_offset   = this->loca->u.longsZ[glyph + 1];
-    }
-
-    if (start_offset > end_offset || end_offset > this->glyf_len)
-      return false;
-
-    if (end_offset - start_offset < OT::glyfGlyphHeader::static_size)
-      return true; /* Empty glyph; zero extents. */
-
-    const OT::glyfGlyphHeader &glyph_header = OT::StructAtOffset<OT::glyfGlyphHeader> (this->glyf, start_offset);
-
-    extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax);
-    extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax);
-    extents->width     = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing;
-    extents->height    = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing;
-
-    return true;
-  }
-};
-
-struct hb_ot_face_cbdt_accelerator_t
-{
-  hb_blob_t *cblc_blob;
-  hb_blob_t *cbdt_blob;
-  const OT::CBLC *cblc;
-  const OT::CBDT *cbdt;
-
-  unsigned int cbdt_len;
-  float upem;
-
-  inline void init (hb_face_t *face)
-  {
-    upem = face->get_upem();
-
-    cblc_blob = OT::Sanitizer<OT::CBLC>::sanitize (face->reference_table (HB_OT_TAG_CBLC));
-    cbdt_blob = OT::Sanitizer<OT::CBDT>::sanitize (face->reference_table (HB_OT_TAG_CBDT));
-    cbdt_len = hb_blob_get_length (cbdt_blob);
-
-    if (hb_blob_get_length (cblc_blob) == 0) {
-      cblc = NULL;
-      cbdt = NULL;
-      return;  /* Not a bitmap font. */
-    }
-    cblc = OT::Sanitizer<OT::CBLC>::lock_instance (cblc_blob);
-    cbdt = OT::Sanitizer<OT::CBDT>::lock_instance (cbdt_blob);
-
-  }
-
-  inline void fini (void)
-  {
-    hb_blob_destroy (this->cblc_blob);
-    hb_blob_destroy (this->cbdt_blob);
-  }
-
-  inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
-  {
-    unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */
-
-    if (cblc == NULL)
-      return false;  // Not a color bitmap font.
-
-    const OT::IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem);
-    if (subtable_record == NULL)
-      return false;
-
-    if (subtable_record->get_extents (extents))
-      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))
-      return false;
-
-    {
-      /* TODO Move the following into CBDT struct when adding more formats. */
-
-      if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length))
-	return false;
-
-      switch (image_format)
-      {
-	case 17: {
-	  if (unlikely (image_length < OT::GlyphBitmapDataFormat17::min_size))
-	    return false;
-
-	  const OT::GlyphBitmapDataFormat17& glyphFormat17 =
-	      OT::StructAtOffset<OT::GlyphBitmapDataFormat17> (this->cbdt, image_offset);
-	  glyphFormat17.glyphMetrics.get_extents (extents);
-	}
-	break;
-	default:
-	  // TODO: Support other image formats.
-	  return false;
-      }
-    }
-
-    /* Convert to the font units. */
-    extents->x_bearing *= upem / (float) x_ppem;
-    extents->y_bearing *= upem / (float) y_ppem;
-    extents->width *= upem / (float) x_ppem;
-    extents->height *= upem / (float) y_ppem;
-
-    return true;
-  }
-};
-
-typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
-					  hb_codepoint_t codepoint,
-					  hb_codepoint_t *glyph);
-
-template <typename Type>
-static inline bool get_glyph_from (const void *obj,
-				   hb_codepoint_t codepoint,
-				   hb_codepoint_t *glyph)
-{
-  const Type *typed_obj = (const Type *) obj;
-  return typed_obj->get_glyph (codepoint, glyph);
-}
-
-template <typename Type>
-static inline bool get_glyph_from_symbol (const void *obj,
-					  hb_codepoint_t codepoint,
-					  hb_codepoint_t *glyph)
-{
-  const Type *typed_obj = (const Type *) obj;
-  if (likely (typed_obj->get_glyph (codepoint, glyph)))
-    return true;
-
-  if (codepoint <= 0x00FFu)
-  {
-    /* For symbol-encoded OpenType fonts, we duplicate the
-     * U+F000..F0FF range at U+0000..U+00FF.  That's what
-     * Windows seems to do, and that's hinted about at:
-     * http://www.microsoft.com/typography/otspec/recom.htm
-     * under "Non-Standard (Symbol) Fonts". */
-    return typed_obj->get_glyph (0xF000u + codepoint, glyph);
-  }
-
-  return false;
-}
-
-struct hb_ot_face_cmap_accelerator_t
-{
-  hb_cmap_get_glyph_func_t get_glyph_func;
-  const void *get_glyph_data;
-  OT::CmapSubtableFormat4::accelerator_t format4_accel;
-
-  const OT::CmapSubtableFormat14 *uvs_table;
-  hb_blob_t *blob;
-
-  inline void init (hb_face_t *face)
-  {
-    this->blob = OT::Sanitizer<OT::cmap>::sanitize (face->reference_table (HB_OT_TAG_cmap));
-    const OT::cmap *cmap = OT::Sanitizer<OT::cmap>::lock_instance (this->blob);
-    const OT::CmapSubtable *subtable = NULL;
-    const OT::CmapSubtableFormat14 *subtable_uvs = NULL;
-
-    bool symbol = false;
-    /* 32-bit subtables. */
-    if (!subtable) subtable = cmap->find_subtable (3, 10);
-    if (!subtable) subtable = cmap->find_subtable (0, 6);
-    if (!subtable) subtable = cmap->find_subtable (0, 4);
-    /* 16-bit subtables. */
-    if (!subtable) subtable = cmap->find_subtable (3, 1);
-    if (!subtable) subtable = cmap->find_subtable (0, 3);
-    if (!subtable) subtable = cmap->find_subtable (0, 2);
-    if (!subtable) subtable = cmap->find_subtable (0, 1);
-    if (!subtable) subtable = cmap->find_subtable (0, 0);
-    if (!subtable)
-    {
-      subtable = cmap->find_subtable (3, 0);
-      if (subtable) symbol = true;
-    }
-    /* Meh. */
-    if (!subtable) subtable = &OT::Null(OT::CmapSubtable);
-
-    /* UVS subtable. */
-    if (!subtable_uvs)
-    {
-      const OT::CmapSubtable *st = cmap->find_subtable (0, 5);
-      if (st && st->u.format == 14)
-        subtable_uvs = &st->u.format14;
-    }
-    /* Meh. */
-    if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14);
-
-    this->uvs_table = subtable_uvs;
-
-    this->get_glyph_data = subtable;
-    if (unlikely (symbol))
-      this->get_glyph_func = get_glyph_from_symbol<OT::CmapSubtable>;
-    else
-      switch (subtable->u.format) {
-      /* Accelerate format 4 and format 12. */
-      default: this->get_glyph_func = get_glyph_from<OT::CmapSubtable>;		break;
-      case 12: this->get_glyph_func = get_glyph_from<OT::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;
-	}
-	break;
-      }
-  }
-
-  inline void fini (void)
-  {
-    hb_blob_destroy (this->blob);
-  }
-
-  inline bool get_nominal_glyph (hb_codepoint_t  unicode,
-				 hb_codepoint_t *glyph) const
-  {
-    return this->get_glyph_func (this->get_glyph_data, unicode, glyph);
-  }
-
-  inline bool get_variation_glyph (hb_codepoint_t  unicode,
-				   hb_codepoint_t  variation_selector,
-				   hb_codepoint_t *glyph) const
-  {
-    switch (this->uvs_table->get_glyph_variant (unicode,
-						variation_selector,
-						glyph))
-    {
-      case OT::GLYPH_VARIANT_NOT_FOUND:		return false;
-      case OT::GLYPH_VARIANT_FOUND:		return true;
-      case OT::GLYPH_VARIANT_USE_DEFAULT:	break;
-    }
-
-    return get_nominal_glyph (unicode, glyph);
-  }
-};
-
 struct hb_ot_font_t
 {
-  hb_ot_face_cmap_accelerator_t cmap;
-  hb_ot_face_metrics_accelerator_t h_metrics;
-  hb_ot_face_metrics_accelerator_t v_metrics;
-  OT::hb_lazy_loader_t<hb_ot_face_glyf_accelerator_t> glyf;
-  OT::hb_lazy_loader_t<hb_ot_face_cbdt_accelerator_t> cbdt;
+  OT::cmap::accelerator_t cmap;
+  OT::hmtx::accelerator_t h_metrics;
+  OT::vmtx::accelerator_t v_metrics;
+  OT::hb_lazy_loader_t<OT::glyf::accelerator_t> glyf;
+  OT::hb_lazy_loader_t<OT::CBDT::accelerator_t> cbdt;
+  OT::hb_lazy_loader_t<OT::post::accelerator_t> post;
+  OT::hb_lazy_loader_t<OT::kern::accelerator_t> kern;
 };
 
 
@@ -445,26 +56,31 @@
   hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t));
 
   if (unlikely (!ot_font))
-    return NULL;
+    return nullptr;
 
   ot_font->cmap.init (face);
-  ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, HB_OT_TAG_HVAR, HB_OT_TAG_os2);
-  ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, HB_OT_TAG_VVAR, HB_TAG_NONE,
-			   ot_font->h_metrics.ascender - ot_font->h_metrics.descender); /* TODO Can we do this lazily? */
+  ot_font->h_metrics.init (face);
+  ot_font->v_metrics.init (face, ot_font->h_metrics.ascender - ot_font->h_metrics.descender); /* TODO Can we do this lazily? */
   ot_font->glyf.init (face);
   ot_font->cbdt.init (face);
+  ot_font->post.init (face);
+  ot_font->kern.init (face);
 
   return ot_font;
 }
 
 static void
-_hb_ot_font_destroy (hb_ot_font_t *ot_font)
+_hb_ot_font_destroy (void *data)
 {
+  hb_ot_font_t *ot_font = (hb_ot_font_t *) data;
+
   ot_font->cmap.fini ();
   ot_font->h_metrics.fini ();
   ot_font->v_metrics.fini ();
   ot_font->glyf.fini ();
   ot_font->cbdt.fini ();
+  ot_font->post.fini ();
+  ot_font->kern.fini ();
 
   free (ot_font);
 }
@@ -514,6 +130,17 @@
   return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph, font));
 }
 
+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)
+{
+  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));
+}
+
 static hb_bool_t
 hb_ot_get_glyph_extents (hb_font_t *font HB_UNUSED,
 			 void *font_data,
@@ -534,6 +161,28 @@
 }
 
 static hb_bool_t
+hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED,
+                      void *font_data,
+                      hb_codepoint_t glyph,
+                      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);
+}
+
+static hb_bool_t
+hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED,
+                           void *font_data,
+                           const char *name, int len,
+                           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);
+}
+
+static hb_bool_t
 hb_ot_get_font_h_extents (hb_font_t *font HB_UNUSED,
 			  void *font_data,
 			  hb_font_extents_t *metrics,
@@ -561,7 +210,7 @@
   return ot_font->v_metrics.has_font_extents;
 }
 
-static hb_font_funcs_t *static_ot_funcs = NULL;
+static hb_font_funcs_t *static_ot_funcs = nullptr;
 
 #ifdef HB_USE_ATEXIT
 static
@@ -581,24 +230,24 @@
   {
     funcs = hb_font_funcs_create ();
 
-    hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, NULL, NULL);
-    hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, NULL, NULL);
-    hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, NULL, NULL);
-    hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, NULL, NULL);
-    hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ot_get_glyph_h_advance, NULL, NULL);
-    hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ot_get_glyph_v_advance, NULL, NULL);
-    //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, NULL, NULL);
-    //hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, NULL, NULL);
-    //hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ot_get_glyph_h_kerning, NULL, NULL); TODO
-    //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ot_get_glyph_v_kerning, NULL, NULL);
-    hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, NULL, NULL);
-    //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, NULL, NULL); TODO
-    //hb_font_funcs_set_glyph_name_func (funcs, hb_ot_get_glyph_name, NULL, NULL); TODO
-    //hb_font_funcs_set_glyph_from_name_func (funcs, hb_ot_get_glyph_from_name, NULL, NULL); TODO
+    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_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_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_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);
+    hb_font_funcs_set_glyph_from_name_func (funcs, hb_ot_get_glyph_from_name, nullptr, nullptr);
 
     hb_font_funcs_make_immutable (funcs);
 
-    if (!hb_atomic_ptr_cmpexch (&static_ot_funcs, NULL, funcs)) {
+    if (!hb_atomic_ptr_cmpexch (&static_ot_funcs, nullptr, funcs)) {
       hb_font_funcs_destroy (funcs);
       goto retry;
     }
@@ -627,5 +276,5 @@
   hb_font_set_funcs (font,
 		     _hb_ot_get_font_funcs (),
 		     ot_font,
-		     (hb_destroy_func_t) _hb_ot_font_destroy);
+		     _hb_ot_font_destroy);
 }
diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh
index dc7aa84..50a7111 100644
--- a/src/hb-ot-glyf-table.hh
+++ b/src/hb-ot-glyf-table.hh
@@ -28,7 +28,10 @@
 #define HB_OT_GLYF_TABLE_HH
 
 #include "hb-open-type-private.hh"
-
+#include "hb-ot-head-table.hh"
+#include "hb-subset-glyf.hh"
+#include "hb-subset-plan.hh"
+#include "hb-subset-private.hh"
 
 namespace OT {
 
@@ -42,6 +45,8 @@
 
 struct loca
 {
+  friend struct glyf;
+
   static const hb_tag_t tableTag = HB_OT_TAG_loca;
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -50,12 +55,9 @@
     return_trace (true);
   }
 
-  public:
-  union {
-    USHORT	shortsZ[VAR];		/* Location offset divided by 2. */
-    ULONG	longsZ[VAR];		/* Location offset. */
-  } u;
-  DEFINE_SIZE_ARRAY (0, u.longsZ);
+  protected:
+  HBUINT8		dataX[VAR];		/* Location data. */
+  DEFINE_SIZE_ARRAY (0, dataX);
 };
 
 
@@ -78,26 +80,263 @@
     return_trace (true);
   }
 
-  public:
-  BYTE		dataX[VAR];		/* Glyphs data. */
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_blob_t *glyf_prime = nullptr;
+    hb_blob_t *loca_prime = nullptr;
+
+    bool success = true;
+    bool use_short_loca = false;
+    if (hb_subset_glyf_and_loca (plan, &use_short_loca, &glyf_prime, &loca_prime)) {
+      success = success && hb_subset_plan_add_table (plan, HB_OT_TAG_glyf, glyf_prime);
+      success = success && hb_subset_plan_add_table (plan, HB_OT_TAG_loca, loca_prime);
+      success = success && _add_head_and_set_loca_version (plan->source, use_short_loca, plan->dest);
+    } else {
+      success = false;
+    }
+    hb_blob_destroy (loca_prime);
+    hb_blob_destroy (glyf_prime);
+
+    return success;
+  }
+
+  static bool
+  _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_t *dest)
+  {
+    hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (hb_face_reference_table (source, HB_OT_TAG_head));
+    hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob);
+    hb_blob_destroy (head_blob);
+
+    if (unlikely (!head_prime_blob))
+      return false;
+
+    OT::head *head_prime = (OT::head *) hb_blob_get_data_writable (head_prime_blob, nullptr);
+    head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1);
+    bool success = hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob);
+
+    hb_blob_destroy (head_prime_blob);
+    return success;
+  }
+
+  struct GlyphHeader
+  {
+    HBINT16		numberOfContours;	/* If the number of contours is
+                                                 * greater than or equal to zero,
+                                                 * this is a simple glyph; if negative,
+                                                 * this is a composite glyph. */
+    FWORD		xMin;			/* Minimum x for coordinate data. */
+    FWORD		yMin;			/* Minimum y for coordinate data. */
+    FWORD		xMax;			/* Maximum x for coordinate data. */
+    FWORD		yMax;			/* Maximum y for coordinate data. */
+
+    DEFINE_SIZE_STATIC (10);
+  };
+
+  struct CompositeGlyphHeader
+  {
+    static const uint16_t ARG_1_AND_2_ARE_WORDS =      0x0001;
+    static const uint16_t ARGS_ARE_XY_VALUES =         0x0002;
+    static const uint16_t ROUND_XY_TO_GRID =           0x0004;
+    static const uint16_t WE_HAVE_A_SCALE =            0x0008;
+    static const uint16_t MORE_COMPONENTS =            0x0020;
+    static const uint16_t WE_HAVE_AN_X_AND_Y_SCALE =   0x0040;
+    static const uint16_t WE_HAVE_A_TWO_BY_TWO =       0x0080;
+    static const uint16_t WE_HAVE_INSTRUCTIONS =       0x0100;
+    static const uint16_t USE_MY_METRICS =             0x0200;
+    static const uint16_t OVERLAP_COMPOUND =           0x0400;
+    static const uint16_t SCALED_COMPONENT_OFFSET =    0x0800;
+    static const uint16_t UNSCALED_COMPONENT_OFFSET =  0x1000;
+
+    HBUINT16 flags;
+    HBUINT16 glyphIndex;
+
+    inline unsigned int get_size (void) const
+    {
+      unsigned int size = min_size;
+      if (flags & ARG_1_AND_2_ARE_WORDS) {
+        // arg1 and 2 are int16
+        size += 4;
+      } else {
+        // arg1 and 2 are int8
+        size += 2;
+      }
+      if (flags & WE_HAVE_A_SCALE) {
+        // One x 16 bit (scale)
+        size += 2;
+      } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
+        // Two x 16 bit (xscale, yscale)
+        size += 4;
+      } else if (flags & WE_HAVE_A_TWO_BY_TWO) {
+        // Four x 16 bit (xscale, scale01, scale10, yscale)
+        size += 8;
+      }
+      return size;
+    }
+
+    struct Iterator
+    {
+      const char *glyph_start;
+      const char *glyph_end;
+      const CompositeGlyphHeader *current;
+
+      inline bool move_to_next ()
+      {
+	if (current->flags & CompositeGlyphHeader::MORE_COMPONENTS)
+	{
+	  const CompositeGlyphHeader *possible =
+	    &StructAfter<CompositeGlyphHeader, CompositeGlyphHeader> (*current);
+	  if (!in_range (possible))
+	    return false;
+	  current = possible;
+	  return true;
+	}
+	return false;
+      }
+
+      inline bool in_range (const CompositeGlyphHeader *composite) const
+      {
+	return (const char *) composite >= glyph_start
+	  && ((const char *) composite + CompositeGlyphHeader::min_size) <= glyph_end
+	  && ((const char *) composite + composite->get_size()) <= glyph_end;
+      }
+    };
+
+    static inline bool get_iterator (const char * glyph_data,
+				     unsigned int length,
+				     CompositeGlyphHeader::Iterator *iterator /* OUT */)
+    {
+      if (length < GlyphHeader::static_size)
+	return false; /* Empty glyph; zero extents. */
+
+      const GlyphHeader &glyph_header = StructAtOffset<GlyphHeader> (glyph_data, 0);
+      if (glyph_header.numberOfContours < 0)
+      {
+        const CompositeGlyphHeader *possible =
+	  &StructAfter<CompositeGlyphHeader, GlyphHeader> (glyph_header);
+
+	iterator->glyph_start = glyph_data;
+	iterator->glyph_end = (const char *) glyph_data + length;
+	if (!iterator->in_range (possible))
+          return false;
+        iterator->current = possible;
+        return true;
+      }
+
+      return false;
+    }
+
+    DEFINE_SIZE_MIN (4);
+  };
+
+  struct accelerator_t
+  {
+    inline void init (hb_face_t *face)
+    {
+      hb_blob_t *head_blob = Sanitizer<head>().sanitize (face->reference_table (HB_OT_TAG_head));
+      const head *head_table = Sanitizer<head>::lock_instance (head_blob);
+      if ((unsigned int) head_table->indexToLocFormat > 1 || head_table->glyphDataFormat != 0)
+      {
+	/* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
+	hb_blob_destroy (head_blob);
+	return;
+      }
+      short_offset = 0 == head_table->indexToLocFormat;
+      hb_blob_destroy (head_blob);
+
+      loca_blob = Sanitizer<loca>().sanitize (face->reference_table (HB_OT_TAG_loca));
+      loca_table = Sanitizer<loca>::lock_instance (loca_blob);
+      glyf_blob = Sanitizer<glyf>().sanitize (face->reference_table (HB_OT_TAG_glyf));
+      glyf_table = Sanitizer<glyf>::lock_instance (glyf_blob);
+
+      num_glyphs = MAX (1u, hb_blob_get_length (loca_blob) / (short_offset ? 2 : 4)) - 1;
+      glyf_len = hb_blob_get_length (glyf_blob);
+    }
+
+    inline void fini (void)
+    {
+      hb_blob_destroy (loca_blob);
+      hb_blob_destroy (glyf_blob);
+    }
+
+    /*
+     * Returns true if the referenced glyph is a valid glyph and a composite glyph.
+     * If true is returned a pointer to the composite glyph will be written into
+     * composite.
+     */
+    inline bool get_composite (hb_codepoint_t glyph,
+			       CompositeGlyphHeader::Iterator *composite /* OUT */) const
+    {
+      unsigned int start_offset, end_offset;
+      if (!get_offsets (glyph, &start_offset, &end_offset))
+        return false; /* glyph not found */
+
+      return CompositeGlyphHeader::get_iterator ((const char*) this->glyf_table + start_offset,
+						 end_offset - start_offset,
+						 composite);
+    }
+
+    inline bool get_offsets (hb_codepoint_t  glyph,
+                             unsigned int   *start_offset /* OUT */,
+                             unsigned int   *end_offset   /* OUT */) const
+    {
+      if (unlikely (glyph >= num_glyphs))
+	return false;
+
+      if (short_offset)
+      {
+        const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataX;
+	*start_offset = 2 * offsets[glyph];
+	*end_offset   = 2 * offsets[glyph + 1];
+      }
+      else
+      {
+        const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataX;
+	*start_offset = offsets[glyph];
+	*end_offset   = offsets[glyph + 1];
+      }
+
+      if (*start_offset > *end_offset || *end_offset > glyf_len)
+	return false;
+
+      return true;
+    }
+
+    inline bool get_extents (hb_codepoint_t glyph,
+			     hb_glyph_extents_t *extents) const
+    {
+      unsigned int start_offset, end_offset;
+      if (!get_offsets (glyph, &start_offset, &end_offset))
+        return false;
+
+      if (end_offset - start_offset < GlyphHeader::static_size)
+	return true; /* Empty glyph; zero extents. */
+
+      const GlyphHeader &glyph_header = StructAtOffset<GlyphHeader> (glyf_table, start_offset);
+
+      extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax);
+      extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax);
+      extents->width     = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing;
+      extents->height    = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing;
+
+      return true;
+    }
+
+    private:
+    bool short_offset;
+    unsigned int num_glyphs;
+    const loca *loca_table;
+    const glyf *glyf_table;
+    hb_blob_t *loca_blob;
+    hb_blob_t *glyf_blob;
+    unsigned int glyf_len;
+  };
+
+  protected:
+  HBUINT8		dataX[VAR];		/* Glyphs data. */
 
   DEFINE_SIZE_ARRAY (0, dataX);
 };
 
-struct glyfGlyphHeader
-{
-  SHORT		numberOfContours;	/* If the number of contours is
-					 * greater than or equal to zero,
-					 * this is a simple glyph; if negative,
-					 * this is a composite glyph. */
-  FWORD		xMin;			/* Minimum x for coordinate data. */
-  FWORD		yMin;			/* Minimum y for coordinate data. */
-  FWORD		xMax;			/* Maximum x for coordinate data. */
-  FWORD		yMax;			/* Maximum y for coordinate data. */
-
-  DEFINE_SIZE_STATIC (10);
-};
-
 } /* namespace OT */
 
 
diff --git a/src/hb-ot-hdmx-table.hh b/src/hb-ot-hdmx-table.hh
new file mode 100644
index 0000000..f08fe39
--- /dev/null
+++ b/src/hb-ot-hdmx-table.hh
@@ -0,0 +1,198 @@
+/*
+ * 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
+ */
+
+#ifndef HB_OT_HDMX_TABLE_HH
+#define HB_OT_HDMX_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+namespace OT {
+
+
+/*
+ * hdmx - Horizontal Device Metric
+ */
+
+#define HB_OT_TAG_hdmx HB_TAG('h','d','m','x')
+
+struct DeviceRecord
+{
+  struct SubsetView
+  {
+    const DeviceRecord *source_device_record;
+    hb_subset_plan_t *subset_plan;
+
+    inline void init(const DeviceRecord *source_device_record,
+		     hb_subset_plan_t   *subset_plan)
+    {
+      this->source_device_record = source_device_record;
+      this->subset_plan = subset_plan;
+    }
+
+    inline unsigned int len () const
+    {
+      return this->subset_plan->gids_to_retain_sorted.len;
+    }
+
+    inline const HBUINT8& operator [] (unsigned int i) const
+    {
+      if (unlikely (i >= len())) return Null(HBUINT8);
+      hb_codepoint_t gid = this->subset_plan->gids_to_retain_sorted [i];
+      return this->source_device_record->widths[gid];
+    }
+  };
+
+  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;
+  }
+
+  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()))))
+      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);
+
+    for (unsigned int i = 0; i < subset_view.len(); i++)
+      widths[i].set (subset_view[i]);
+
+    return_trace (true);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int size_device_record) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) &&
+			  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). */
+  public:
+  DEFINE_SIZE_ARRAY (2, widths);
+};
+
+
+struct hdmx
+{
+  static const hb_tag_t tableTag = HB_OT_TAG_hdmx;
+
+  inline unsigned int get_size (void) const
+  {
+    return min_size + num_records * size_device_record;
+  }
+
+  inline const DeviceRecord& operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= num_records)) return Null(DeviceRecord);
+    return StructAtOffset<DeviceRecord> (this, min_size + i * size_device_record);
+  }
+
+  inline bool serialize (hb_serialize_context_t *c, const hdmx *source_hdmx, hb_subset_plan_t *plan)
+  {
+    TRACE_SERIALIZE (this);
+
+    if (unlikely (!c->extend_min ((*this))))  return_trace (false);
+
+    this->version.set (source_hdmx->version);
+    this->num_records.set (source_hdmx->num_records);
+    this->size_device_record.set (DeviceRecord::get_size (plan->gids_to_retain_sorted.len));
+
+    for (unsigned int i = 0; i < source_hdmx->num_records; i++)
+    {
+      DeviceRecord::SubsetView subset_view;
+      subset_view.init (&(*source_hdmx)[i], plan);
+
+      c->start_embed<DeviceRecord> ()->serialize (c, subset_view);
+    }
+
+    return_trace (true);
+  }
+
+  static inline size_t get_subsetted_size (hb_subset_plan_t *plan)
+  {
+    return min_size + DeviceRecord::get_size (plan->gids_to_retain_sorted.len);
+  }
+
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    size_t dest_size = get_subsetted_size (plan);
+    hdmx *dest = (hdmx *) malloc (dest_size);
+    if (unlikely (!dest))
+    {
+      DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for hdmx subset output.", (unsigned long) dest_size);
+      return false;
+    }
+
+    hb_serialize_context_t c (dest, dest_size);
+    hdmx *hdmx_prime = c.start_serialize<hdmx> ();
+    if (!hdmx_prime || !hdmx_prime->serialize (&c, this, plan)) {
+      free (dest);
+      return false;
+    }
+    c.end_serialize ();
+
+    hb_blob_t *hdmx_prime_blob = hb_blob_create ((const char *) dest,
+						 dest_size,
+						 HB_MEMORY_MODE_READONLY,
+						 dest,
+						 free);
+    bool result = hb_subset_plan_add_table (plan, HB_OT_TAG_hdmx, hdmx_prime_blob);
+    hb_blob_destroy (hdmx_prime_blob);
+
+    return result;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && version == 0 &&
+		  !_hb_unsigned_int_mul_overflows (num_records, size_device_record) &&
+		  c->check_range (this, get_size()));
+  }
+
+  protected:
+  HBUINT16	version;		/* Table version number (0) */
+  HBUINT16	num_records;		/* Number of device records. */
+  HBUINT32	size_device_record;	/* Size of a device record, 32-bit aligned. */
+  HBUINT8	data[VAR];		/* Array of device records. */
+  public:
+  DEFINE_SIZE_ARRAY (8, data);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_HDMX_TABLE_HH */
diff --git a/src/hb-ot-head-table.hh b/src/hb-ot-head-table.hh
index 9c3e51e..1d45840 100644
--- a/src/hb-ot-head-table.hh
+++ b/src/hb-ot-head-table.hh
@@ -43,6 +43,8 @@
 
 struct head
 {
+  friend struct OffsetTable;
+
   static const hb_tag_t tableTag	= HB_OT_TAG_head;
 
   inline unsigned int get_upem (void) const
@@ -64,11 +66,11 @@
   FixedVersion<>version;		/* Version of the head table--currently
 					 * 0x00010000u for version 1.0. */
   FixedVersion<>fontRevision;		/* Set by font manufacturer. */
-  ULONG		checkSumAdjustment;	/* To compute: set it to 0, sum the
-					 * entire font as ULONG, then store
+  HBUINT32	checkSumAdjustment;	/* To compute: set it to 0, sum the
+					 * entire font as HBUINT32, then store
 					 * 0xB1B0AFBAu - sum. */
-  ULONG		magicNumber;		/* Set to 0x5F0F3CF5u. */
-  USHORT	flags;			/* Bit 0: Baseline for font at y=0;
+  HBUINT32	magicNumber;		/* Set to 0x5F0F3CF5u. */
+  HBUINT16	flags;			/* Bit 0: Baseline for font at y=0;
 					 * Bit 1: Left sidebearing point at x=0;
 					 * Bit 2: Instructions may depend on point size;
 					 * Bit 3: Force ppem to integer values for all
@@ -76,7 +78,6 @@
 					 *   ppem sizes if this bit is clear;
 					 * Bit 4: Instructions may alter advance width
 					 *   (the advance widths might not scale linearly);
-
 					 * Bits 5-10: These should be set according to
 					 *   Apple's specification. However, they are not
 					 *   implemented in OpenType.
@@ -96,7 +97,6 @@
 					 *   contains any strong right-to-left glyphs.
 					 * Bit 10: This bit should be set if the font
 					 *   contains Indic-style rearrangement effects.
-
 					 * Bit 11: Font data is 'lossless,' as a result
 					 *   of having been compressed and decompressed
 					 *   with the Agfa MicroType Express engine.
@@ -114,18 +114,18 @@
 					 * encoded in the cmap subtables represent proper
 					 * support for those code points.
 					 * Bit 15: Reserved, set to 0. */
-  USHORT	unitsPerEm;		/* Valid range is from 16 to 16384. This value
+  HBUINT16	unitsPerEm;		/* Valid range is from 16 to 16384. This value
 					 * should be a power of 2 for fonts that have
 					 * TrueType outlines. */
   LONGDATETIME	created;		/* Number of seconds since 12:00 midnight,
 					   January 1, 1904. 64-bit integer */
   LONGDATETIME	modified;		/* Number of seconds since 12:00 midnight,
 					   January 1, 1904. 64-bit integer */
-  SHORT		xMin;			/* For all glyph bounding boxes. */
-  SHORT		yMin;			/* For all glyph bounding boxes. */
-  SHORT		xMax;			/* For all glyph bounding boxes. */
-  SHORT		yMax;			/* For all glyph bounding boxes. */
-  USHORT	macStyle;		/* Bit 0: Bold (if set to 1);
+  HBINT16	xMin;			/* For all glyph bounding boxes. */
+  HBINT16	yMin;			/* For all glyph bounding boxes. */
+  HBINT16	xMax;			/* For all glyph bounding boxes. */
+  HBINT16	yMax;			/* For all glyph bounding boxes. */
+  HBUINT16	macStyle;		/* Bit 0: Bold (if set to 1);
 					 * Bit 1: Italic (if set to 1)
 					 * Bit 2: Underline (if set to 1)
 					 * Bit 3: Outline (if set to 1)
@@ -133,16 +133,16 @@
 					 * Bit 5: Condensed (if set to 1)
 					 * Bit 6: Extended (if set to 1)
 					 * Bits 7-15: Reserved (set to 0). */
-  USHORT	lowestRecPPEM;		/* Smallest readable size in pixels. */
-  SHORT		fontDirectionHint;	/* Deprecated (Set to 2).
+  HBUINT16	lowestRecPPEM;		/* Smallest readable size in pixels. */
+  HBINT16	fontDirectionHint;	/* Deprecated (Set to 2).
 					 * 0: Fully mixed directional glyphs;
 					 * 1: Only strongly left to right;
 					 * 2: Like 1 but also contains neutrals;
 					 * -1: Only strongly right to left;
 					 * -2: Like -1 but also contains neutrals. */
   public:
-  SHORT		indexToLocFormat;	/* 0 for short offsets, 1 for long. */
-  SHORT		glyphDataFormat;	/* 0 for current format. */
+  HBINT16	indexToLocFormat;	/* 0 for short offsets, 1 for long. */
+  HBINT16	glyphDataFormat;	/* 0 for current format. */
 
   DEFINE_SIZE_STATIC (54);
 };
diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh
index c8e9536..97952b4 100644
--- a/src/hb-ot-hhea-table.hh
+++ b/src/hb-ot-hhea-table.hh
@@ -41,14 +41,9 @@
 #define HB_OT_TAG_hhea HB_TAG('h','h','e','a')
 #define HB_OT_TAG_vhea HB_TAG('v','h','e','a')
 
-
+template <typename T>
 struct _hea
 {
-  static const hb_tag_t tableTag = HB_TAG('_','h','e','a');
-
-  static const hb_tag_t hheaTag	= HB_OT_TAG_hhea;
-  static const hb_tag_t vheaTag	= HB_OT_TAG_vhea;
-
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -69,30 +64,30 @@
 					 * (xMax - xMin)) for horizontal. */
   FWORD		maxExtent;		/* horizontal: Max(lsb + (xMax - xMin)),
 					 * vertical: minLeadingBearing+(yMax-yMin). */
-  SHORT		caretSlopeRise;		/* Used to calculate the slope of the
+  HBINT16		caretSlopeRise;		/* Used to calculate the slope of the
 					 * cursor (rise/run); 1 for vertical caret,
 					 * 0 for horizontal.*/
-  SHORT		caretSlopeRun;		/* 0 for vertical caret, 1 for horizontal. */
-  SHORT		caretOffset;		/* The amount by which a slanted
+  HBINT16		caretSlopeRun;		/* 0 for vertical caret, 1 for horizontal. */
+  HBINT16		caretOffset;		/* The amount by which a slanted
 					 * highlight on a glyph needs
 					 * to be shifted to produce the
 					 * best appearance. Set to 0 for
 					 * non-slanted fonts. */
-  SHORT		reserved1;		/* Set to 0. */
-  SHORT		reserved2;		/* Set to 0. */
-  SHORT		reserved3;		/* Set to 0. */
-  SHORT		reserved4;		/* Set to 0. */
-  SHORT		metricDataFormat;	/* 0 for current format. */
-  USHORT	numberOfLongMetrics;	/* Number of LongMetric entries in metric
+  HBINT16		reserved1;		/* Set to 0. */
+  HBINT16		reserved2;		/* Set to 0. */
+  HBINT16		reserved3;		/* Set to 0. */
+  HBINT16		reserved4;		/* Set to 0. */
+  HBINT16		metricDataFormat;	/* 0 for current format. */
+  HBUINT16	numberOfLongMetrics;	/* Number of LongMetric entries in metric
 					 * table. */
   public:
   DEFINE_SIZE_STATIC (36);
 };
 
-struct hhea : _hea {
+struct hhea : _hea<hhea> {
   static const hb_tag_t tableTag	= HB_OT_TAG_hhea;
 };
-struct vhea : _hea {
+struct vhea : _hea<vhea> {
   static const hb_tag_t tableTag	= HB_OT_TAG_vhea;
 };
 
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index 30aa625..3cd48a6 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -21,13 +21,16 @@
  * 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
+ * Google Author(s): Behdad Esfahbod, Roderick Sheeter
  */
 
 #ifndef HB_OT_HMTX_TABLE_HH
 #define HB_OT_HMTX_TABLE_HH
 
 #include "hb-open-type-private.hh"
+#include "hb-ot-hhea-table.hh"
+#include "hb-ot-os2-table.hh"
+#include "hb-ot-var-hvar-table.hh"
 
 
 namespace OT {
@@ -50,11 +53,9 @@
   DEFINE_SIZE_STATIC (4);
 };
 
+template <typename T, typename H>
 struct hmtxvmtx
 {
-  static const hb_tag_t hmtxTag	= HB_OT_TAG_hmtx;
-  static const hb_tag_t vmtxTag	= HB_OT_TAG_vmtx;
-
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -63,7 +64,228 @@
     return_trace (true);
   }
 
-  public:
+
+  inline bool subset_update_header (hb_subset_plan_t *plan,
+                                    unsigned int num_hmetrics) const
+  {
+    hb_blob_t *src_blob = OT::Sanitizer<H> ().sanitize (plan->source->reference_table (H::tableTag));
+    hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail(src_blob);
+    hb_blob_destroy (src_blob);
+
+    if (unlikely (!dest_blob)) {
+      return false;
+    }
+
+    unsigned int length;
+    H *table = (H *) hb_blob_get_data (dest_blob, &length);
+    table->numberOfLongMetrics.set (num_hmetrics);
+
+    bool result = hb_subset_plan_add_table (plan, H::tableTag, dest_blob);
+    hb_blob_destroy (dest_blob);
+
+    return result;
+  }
+
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    typename T::accelerator_t _mtx;
+    _mtx.init (plan->source);
+
+    /* All the trailing glyphs with the same advance can use one LongMetric
+     * and just keep LSB */
+    hb_prealloced_array_t<hb_codepoint_t> &gids = plan->gids_to_retain_sorted;
+    unsigned int num_advances = gids.len;
+    unsigned int last_advance = _mtx.get_advance (gids[num_advances - 1]);
+    while (num_advances > 1
+        && last_advance == _mtx.get_advance (gids[num_advances - 2]))
+    {
+      num_advances--;
+    }
+
+    /* alloc the new table */
+    size_t dest_sz = num_advances * 4
+                  + (gids.len - num_advances) * 2;
+    void *dest = (void *) malloc (dest_sz);
+    if (unlikely (!dest))
+    {
+      return false;
+    }
+    DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in src has %d advances, %d lsbs", HB_UNTAG(T::tableTag), _mtx.num_advances, _mtx.num_metrics - _mtx.num_advances);
+    DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %u bytes", HB_UNTAG(T::tableTag), num_advances, gids.len - num_advances, (unsigned int) dest_sz);
+
+    const char *source_table = hb_blob_get_data (_mtx.blob, nullptr);
+    // Copy everything over
+    LongMetric * old_metrics = (LongMetric *) source_table;
+    FWORD *lsbs = (FWORD *) (old_metrics + _mtx.num_advances);
+    char * dest_pos = (char *) dest;
+    for (unsigned int i = 0; i < gids.len; i++)
+    {
+      /* the last metric or the one for gids[i] */
+      LongMetric *src_metric = old_metrics + MIN ((hb_codepoint_t) _mtx.num_advances - 1, gids[i]);
+      if (gids[i] < _mtx.num_advances)
+      {
+        /* src is a LongMetric */
+        if (i < num_advances)
+        {
+          /* dest is a LongMetric, copy it */
+          *((LongMetric *) dest_pos) = *src_metric;
+        }
+        else
+        {
+          /* dest just lsb */
+          *((FWORD *) dest_pos) = src_metric->lsb;
+        }
+      }
+      else
+      {
+        FWORD src_lsb = *(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;
+        }
+        else
+        {
+          /* dest just needs an lsb */
+          *((FWORD *) dest_pos) = src_lsb;
+        }
+      }
+      dest_pos += (i < num_advances ? 4 : 2);
+    }
+    _mtx.fini ();
+
+    // Amend header num hmetrics
+    if (unlikely (!subset_update_header (plan, num_advances)))
+    {
+      free (dest);
+      return false;
+    }
+
+    hb_blob_t *result = hb_blob_create ((const char *)dest,
+                                        dest_sz,
+                                        HB_MEMORY_MODE_READONLY,
+                                        dest,
+                                        free);
+    bool success = hb_subset_plan_add_table (plan, T::tableTag, result);
+    hb_blob_destroy (result);
+    return success;
+  }
+
+  struct accelerator_t
+  {
+    friend struct hmtxvmtx;
+
+    inline void init (hb_face_t *face,
+		      unsigned int default_advance_ = 0)
+    {
+      default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);
+
+      bool got_font_extents = false;
+      if (T::os2Tag)
+      {
+	hb_blob_t *os2_blob = Sanitizer<os2> ().sanitize (face->reference_table (T::os2Tag));
+	const os2 *os2_table = Sanitizer<os2>::lock_instance (os2_blob);
+#define USE_TYPO_METRICS (1u<<7)
+	if (0 != (os2_table->fsSelection & USE_TYPO_METRICS))
+	{
+	  ascender = os2_table->sTypoAscender;
+	  descender = os2_table->sTypoDescender;
+	  line_gap = os2_table->sTypoLineGap;
+	  got_font_extents = (ascender | descender) != 0;
+	}
+	hb_blob_destroy (os2_blob);
+      }
+
+      hb_blob_t *_hea_blob = Sanitizer<H> ().sanitize (face->reference_table (H::tableTag));
+      const H *_hea_table = Sanitizer<H>::lock_instance (_hea_blob);
+      num_advances = _hea_table->numberOfLongMetrics;
+      if (!got_font_extents)
+      {
+	ascender = _hea_table->ascender;
+	descender = _hea_table->descender;
+	line_gap = _hea_table->lineGap;
+	got_font_extents = (ascender | descender) != 0;
+      }
+      hb_blob_destroy (_hea_blob);
+
+      has_font_extents = got_font_extents;
+
+      blob = Sanitizer<hmtxvmtx> ().sanitize (face->reference_table (T::tableTag));
+
+      /* Cap num_metrics() and num_advances() based on table length. */
+      unsigned int len = hb_blob_get_length (blob);
+      if (unlikely (num_advances * 4 > len))
+	num_advances = len / 4;
+      num_metrics = num_advances + (len - 4 * num_advances) / 2;
+
+      /* We MUST set num_metrics to zero if num_advances is zero.
+       * Our get_advance() depends on that. */
+      if (unlikely (!num_advances))
+      {
+	num_metrics = num_advances = 0;
+	hb_blob_destroy (blob);
+	blob = hb_blob_get_empty ();
+      }
+      table = Sanitizer<hmtxvmtx>::lock_instance (blob);
+
+      var_blob = Sanitizer<HVARVVAR> ().sanitize (face->reference_table (T::variationsTag));
+      var_table = Sanitizer<HVARVVAR>::lock_instance (var_blob);
+    }
+
+    inline void fini (void)
+    {
+      hb_blob_destroy (blob);
+      hb_blob_destroy (var_blob);
+    }
+
+    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;
+      }
+
+      return table->longMetric[MIN (glyph, (uint32_t) num_advances - 1)].advance;
+    }
+
+    inline unsigned int get_advance (hb_codepoint_t  glyph,
+                                     hb_font_t      *font) const
+    {
+      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?!
+      }
+      return advance;	        
+    }
+
+    public:
+    bool has_font_extents;
+    unsigned short ascender;
+    unsigned short descender;
+    unsigned short line_gap;
+
+    protected:
+    unsigned int num_metrics;
+    unsigned int num_advances;
+    unsigned int default_advance;
+
+    private:
+    const hmtxvmtx *table;
+    hb_blob_t *blob;
+    const HVARVVAR *var_table;
+    hb_blob_t *var_blob;
+  };
+
+  protected:
   LongMetric	longMetric[VAR];	/* Paired advance width and leading
 					 * bearing values for each glyph. The
 					 * value numOfHMetrics comes from
@@ -72,7 +294,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
+/*FWORD		leadingBearingX[VAR];*/	/* 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
@@ -86,14 +308,18 @@
 					 * font to vary the side bearing
 					 * values for each glyph. */
   public:
-  DEFINE_SIZE_ARRAY2 (0, longMetric, leadingBearingX);
+  DEFINE_SIZE_ARRAY (0, longMetric);
 };
 
-struct hmtx : hmtxvmtx {
+struct hmtx : hmtxvmtx<hmtx, hhea> {
   static const hb_tag_t tableTag	= HB_OT_TAG_hmtx;
+  static const hb_tag_t variationsTag	= HB_OT_TAG_HVAR;
+  static const hb_tag_t os2Tag		= HB_OT_TAG_os2;
 };
-struct vmtx : hmtxvmtx {
+struct vmtx : hmtxvmtx<vmtx, vhea> {
   static const hb_tag_t tableTag	= HB_OT_TAG_vmtx;
+  static const hb_tag_t variationsTag	= HB_OT_TAG_VVAR;
+  static const hb_tag_t os2Tag		= HB_TAG_NONE;
 };
 
 } /* namespace OT */
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
new file mode 100644
index 0000000..368f547
--- /dev/null
+++ b/src/hb-ot-kern-table.hh
@@ -0,0 +1,394 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_KERN_TABLE_HH
+#define HB_OT_KERN_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+namespace OT {
+
+
+/*
+ * kern -- Kerning
+ */
+
+#define HB_OT_TAG_kern HB_TAG('k','e','r','n')
+
+struct hb_glyph_pair_t
+{
+  hb_codepoint_t left;
+  hb_codepoint_t right;
+};
+
+struct KernPair
+{
+  inline int get_kerning (void) const
+  { return value; }
+
+  inline int cmp (const hb_glyph_pair_t &o) const
+  {
+    int ret = left.cmp (o.left);
+    if (ret) return ret;
+    return right.cmp (o.right);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  GlyphID	left;
+  GlyphID	right;
+  FWORD		value;
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct KernSubTableFormat0
+{
+  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 bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (pairs.sanitize (c));
+  }
+
+  protected:
+  BinSearchArrayOf<KernPair> pairs;	/* Array of kerning pairs. */
+  public:
+  DEFINE_SIZE_ARRAY (8, pairs);
+};
+
+struct KernClassTable
+{
+  inline unsigned int get_class (hb_codepoint_t g) const { return classes[g - firstGlyph]; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (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);
+};
+
+struct KernSubTableFormat2
+{
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
+  {
+    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))
+      return 0;
+    return *v;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (rowWidth.sanitize (c) &&
+		  leftClassTable.sanitize (c, this) &&
+		  rightClassTable.sanitize (c, this) &&
+		  array.sanitize (c, this));
+  }
+
+  protected:
+  HBUINT16	rowWidth;	/* The width, in bytes, of a row in the table. */
+  OffsetTo<KernClassTable>
+		leftClassTable;	/* Offset from beginning of this subtable to
+				 * left-hand class table. */
+  OffsetTo<KernClassTable>
+		rightClassTable;/* Offset from beginning of this subtable to
+				 * right-hand class table. */
+  OffsetTo<FWORD>
+		array;		/* Offset from beginning of this subtable to
+				 * the start of the kerning array. */
+  public:
+  DEFINE_SIZE_MIN (8);
+};
+
+struct KernSubTable
+{
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end, unsigned int format) const
+  {
+    switch (format) {
+    case 0: return u.format0.get_kerning (left, right);
+    case 2: return u.format2.get_kerning (left, right, end);
+    default:return 0;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int format) const
+  {
+    TRACE_SANITIZE (this);
+    switch (format) {
+    case 0: return_trace (u.format0.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    default:return_trace (true);
+    }
+  }
+
+  protected:
+  union {
+  KernSubTableFormat0	format0;
+  KernSubTableFormat2	format2;
+  } u;
+  public:
+  DEFINE_SIZE_MIN (0);
+};
+
+
+template <typename T>
+struct KernSubTableWrapper
+{
+  /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
+  inline const T* thiz (void) const { return static_cast<const T *> (this); }
+
+  inline bool is_horizontal (void) const
+  { return (thiz()->coverage & T::COVERAGE_CHECK_FLAGS) == T::COVERAGE_CHECK_HORIZONTAL; }
+
+  inline bool is_override (void) const
+  { return bool (thiz()->coverage & T::COVERAGE_OVERRIDE_FLAG); }
+
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
+  { return thiz()->subtable.get_kerning (left, right, end, thiz()->format); }
+
+  inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
+  { return is_horizontal () ? get_kerning (left, right, end) : 0; }
+
+  inline unsigned int get_size (void) const { return thiz()->length; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (thiz()) &&
+		  thiz()->length >= thiz()->min_size &&
+		  c->check_array (thiz(), 1, thiz()->length) &&
+		  thiz()->subtable.sanitize (c, thiz()->format));
+  }
+};
+
+template <typename T>
+struct KernTable
+{
+  /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
+  inline const T* thiz (void) const { return static_cast<const T *> (this); }
+
+  inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const
+  {
+    int v = 0;
+    const typename T::SubTableWrapper *st = CastP<typename T::SubTableWrapper> (thiz()->data);
+    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);
+      st = &StructAfter<typename T::SubTableWrapper> (*st);
+    }
+    return v;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (thiz()) ||
+		  thiz()->version != T::VERSION))
+      return_trace (false);
+
+    const typename T::SubTableWrapper *st = CastP<typename T::SubTableWrapper> (thiz()->data);
+    unsigned int count = thiz()->nTables;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (unlikely (!st->sanitize (c)))
+	return_trace (false);
+      st = &StructAfter<typename T::SubTableWrapper> (*st);
+    }
+
+    return_trace (true);
+  }
+};
+
+struct KernOT : KernTable<KernOT>
+{
+  friend struct KernTable<KernOT>;
+
+  static const uint16_t VERSION = 0x0000u;
+
+  struct SubTableWrapper : KernSubTableWrapper<SubTableWrapper>
+  {
+    friend struct KernSubTableWrapper<SubTableWrapper>;
+
+    enum coverage_flags_t {
+      COVERAGE_DIRECTION_FLAG	= 0x01u,
+      COVERAGE_MINIMUM_FLAG	= 0x02u,
+      COVERAGE_CROSSSTREAM_FLAG	= 0x04u,
+      COVERAGE_OVERRIDE_FLAG	= 0x08u,
+
+      COVERAGE_VARIATION_FLAG	= 0x00u, /* Not supported. */
+
+      COVERAGE_CHECK_FLAGS	= 0x07u,
+      COVERAGE_CHECK_HORIZONTAL	= 0x01u
+    };
+
+    protected:
+    HBUINT16	versionZ;	/* Unused. */
+    HBUINT16	length;		/* Length of the subtable (including this header). */
+    HBUINT8	format;		/* Subtable format. */
+    HBUINT8	coverage;	/* Coverage bits. */
+    KernSubTable subtable;	/* Subtable data. */
+    public:
+    DEFINE_SIZE_MIN (6);
+  };
+
+  protected:
+  HBUINT16	version;	/* Version--0x0000u */
+  HBUINT16	nTables;	/* Number of subtables in the kerning table. */
+  HBUINT8		data[VAR];
+  public:
+  DEFINE_SIZE_ARRAY (4, data);
+};
+
+struct KernAAT : KernTable<KernAAT>
+{
+  friend struct KernTable<KernAAT>;
+
+  static const uint32_t VERSION = 0x00010000u;
+
+  struct SubTableWrapper : KernSubTableWrapper<SubTableWrapper>
+  {
+    friend struct KernSubTableWrapper<SubTableWrapper>;
+
+    enum coverage_flags_t {
+      COVERAGE_DIRECTION_FLAG	= 0x80u,
+      COVERAGE_CROSSSTREAM_FLAG	= 0x40u,
+      COVERAGE_VARIATION_FLAG	= 0x20u,
+
+      COVERAGE_OVERRIDE_FLAG	= 0x00u, /* Not supported. */
+
+      COVERAGE_CHECK_FLAGS	= 0xE0u,
+      COVERAGE_CHECK_HORIZONTAL	= 0x00u
+    };
+
+    protected:
+    HBUINT32	length;		/* Length of the subtable (including this header). */
+    HBUINT8	coverage;	/* Coverage bits. */
+    HBUINT8	format;		/* Subtable format. */
+    HBUINT16	tupleIndex;	/* The tuple index (used for variations fonts).
+				 * This value specifies which tuple this subtable covers. */
+    KernSubTable subtable;	/* Subtable data. */
+    public:
+    DEFINE_SIZE_MIN (8);
+  };
+
+  protected:
+  HBUINT32		version;	/* Version--0x00010000u */
+  HBUINT32		nTables;	/* Number of subtables in the kerning table. */
+  HBUINT8		data[VAR];
+  public:
+  DEFINE_SIZE_ARRAY (8, data);
+};
+
+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
+  {
+    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);
+    default:return 0;
+    }
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.major.sanitize (c)) return_trace (false);
+    switch (u.major) {
+    case 0: return_trace (u.ot.sanitize (c));
+    case 1: return_trace (u.aat.sanitize (c));
+    default:return_trace (true);
+    }
+  }
+
+  struct accelerator_t
+  {
+    inline void init (hb_face_t *face)
+    {
+      blob = Sanitizer<kern>().sanitize (face->reference_table (HB_OT_TAG_kern));
+      table = Sanitizer<kern>::lock_instance (blob);
+      table_length = hb_blob_get_length (blob);
+    }
+    inline void fini (void)
+    {
+      hb_blob_destroy (blob);
+    }
+
+    inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+    { return table->get_h_kerning (left, right, table_length); }
+
+    private:
+    hb_blob_t *blob;
+    const kern *table;
+    unsigned int table_length;
+  };
+
+  protected:
+  union {
+  HBUINT16		major;
+  KernOT		ot;
+  KernAAT		aat;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, major);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_KERN_TABLE_HH */
diff --git a/src/hb-ot-layout-base-table.hh b/src/hb-ot-layout-base-table.hh
index 0d1e197..7801860 100644
--- a/src/hb-ot-layout-base-table.hh
+++ b/src/hb-ot-layout-base-table.hh
@@ -32,7 +32,7 @@
 
 namespace OT {
 
-#define NO_COORD Null(SHORT)//SHORT((short int) -32767)
+#define NO_COORD Null(HBINT16)//HBINT16((short int) -32767)
 
 #define NOT_INDEXED   ((unsigned int) -1)
 
@@ -42,7 +42,7 @@
 
 struct BaseCoordFormat1 {
 
-  inline SHORT get_coord (void) const
+  inline HBINT16 get_coord (void) const
   { return coordinate; }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -52,8 +52,8 @@
   }
 
   protected:
-  USHORT    baseCoordFormat;
-  SHORT     coordinate;
+  HBUINT16    baseCoordFormat;
+  HBINT16     coordinate;
 
   public:
   DEFINE_SIZE_STATIC (4);
@@ -61,7 +61,7 @@
 
 struct BaseCoordFormat2 {
 
-  inline SHORT get_coord (void) const
+  inline HBINT16 get_coord (void) const
   { return coordinate; }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -71,10 +71,10 @@
   }
 
   protected:
-  USHORT    baseCoordFormat;
-  SHORT     coordinate;
-  USHORT    referenceGlyph;
-  USHORT    baseCoordPoint;
+  HBUINT16    baseCoordFormat;
+  HBINT16     coordinate;
+  HBUINT16    referenceGlyph;
+  HBUINT16    baseCoordPoint;
 
   public:
   DEFINE_SIZE_STATIC (8);
@@ -82,7 +82,7 @@
 
 struct BaseCoordFormat3 {
 
-  inline SHORT get_coord (void) const
+  inline HBINT16 get_coord (void) const
   { return coordinate; }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -92,8 +92,8 @@
   }
 
   protected:
-  USHORT           baseCoordFormat;
-  SHORT            coordinate;
+  HBUINT16           baseCoordFormat;
+  HBINT16            coordinate;
   OffsetTo<Device> deviceTable;
 
   public:
@@ -102,7 +102,7 @@
 
 struct BaseCoord {
 
-  inline SHORT get_coord (void) const
+  inline HBINT16 get_coord (void) const
   { return u.format1.get_coord(); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -119,7 +119,7 @@
 
   protected:
   union {
-    USHORT            baseCoordFormat;
+    HBUINT16            baseCoordFormat;
     BaseCoordFormat1  format1;
     BaseCoordFormat2  format2;
     BaseCoordFormat3  format3;
@@ -131,13 +131,13 @@
 
 struct FeatMinMaxRecord {
 
-  inline SHORT get_min_value (void) const
+  inline HBINT16 get_min_value (void) const
   {
     if (minCoord == Null(OffsetTo<BaseCoord>)) return NO_COORD;
       return (this+minCoord).get_coord();
   }
 
-  inline SHORT get_max_value (void) const
+  inline HBINT16 get_max_value (void) const
   {
     if (minCoord == Null(OffsetTo<BaseCoord>)) return NO_COORD;
       return (this+maxCoord).get_coord();
@@ -180,7 +180,7 @@
     return NOT_INDEXED;
   }
 
-  inline SHORT get_min_value (unsigned int featureTableTagIndex) const
+  inline HBINT16 get_min_value (unsigned int featureTableTagIndex) const
   {
     if (featureTableTagIndex == NOT_INDEXED) {
       if (minCoord == Null(OffsetTo<BaseCoord>)) return NO_COORD;
@@ -190,7 +190,7 @@
     return featMinMaxRecords[featureTableTagIndex].get_min_value();
   }
 
-  inline SHORT get_max_value (unsigned int featureTableTagIndex) const
+  inline HBINT16 get_max_value (unsigned int featureTableTagIndex) const
   {
     if (featureTableTagIndex == NOT_INDEXED) {
       if (minCoord == Null(OffsetTo<BaseCoord>)) return NO_COORD;
@@ -213,7 +213,7 @@
   protected:
   OffsetTo<BaseCoord>       minCoord;
   OffsetTo<BaseCoord>       maxCoord;
-  USHORT                    featMinMaxCount;
+  HBUINT16                    featMinMaxCount;
   ArrayOf<FeatMinMaxRecord> featMinMaxRecords;
 
   public:
@@ -229,10 +229,10 @@
   inline unsigned int get_feature_tag_index (Tag featureTableTag) const
   { (this+minMax).get_feature_tag_index(featureTableTag); }
 
-  inline SHORT get_min_value (unsigned int featureTableTagIndex) const
+  inline HBINT16 get_min_value (unsigned int featureTableTagIndex) const
   { (this+minMax).get_min_value(featureTableTagIndex); }
 
-  inline SHORT get_max_value (unsigned int featureTableTagIndex) const
+  inline HBINT16 get_max_value (unsigned int featureTableTagIndex) const
   { (this+minMax).get_max_value(featureTableTagIndex); }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
@@ -257,7 +257,7 @@
   inline const unsigned int get_default_base_tag_index (void) const
   { return defaultIndex; }
 
-  inline SHORT get_base_coord (unsigned int baselineTagIndex) const
+  inline HBINT16 get_base_coord (unsigned int baselineTagIndex) const
   {
     if (unlikely(baselineTagIndex >= baseCoordCount)) return NO_COORD;
     return (this+baseCoords[baselineTagIndex]).get_coord();
@@ -273,7 +273,7 @@
 
   protected:
   Index                     defaultIndex;
-  USHORT                    baseCoordCount;
+  HBUINT16                    baseCoordCount;
   OffsetArrayOf<BaseCoord>  baseCoords;
 
   public:
@@ -307,7 +307,7 @@
     return baseLangSysRecords[baseLangSysIndex].get_feature_tag_index(featureTableTag);
   }
 
-  inline SHORT get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (baseLangSysIndex == NOT_INDEXED) {
       if (unlikely(defaultMinMax == Null(OffsetTo<MinMax>))) return NO_COORD;
@@ -317,7 +317,7 @@
     return baseLangSysRecords[baseLangSysIndex].get_max_value(featureTableTagIndex); 
   }
 
-  inline SHORT get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (baseLangSysIndex == NOT_INDEXED) {
       if (unlikely(defaultMinMax == Null(OffsetTo<MinMax>))) return NO_COORD;
@@ -330,7 +330,7 @@
   inline unsigned int get_default_base_tag_index (void) const
   { return (this+baseValues).get_default_base_tag_index(); }
 
-  inline SHORT get_base_coord (unsigned int baselineTagIndex) const
+  inline HBINT16 get_base_coord (unsigned int baselineTagIndex) const
   { return (this+baseValues).get_base_coord(baselineTagIndex); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -345,7 +345,7 @@
   protected:
   OffsetTo<BaseValues>        baseValues;
   OffsetTo<MinMax>            defaultMinMax;
-  USHORT                      baseLangSysCount;
+  HBUINT16                      baseLangSysCount;
   ArrayOf<BaseLangSysRecord>  baseLangSysRecords;
 
   public:
@@ -361,7 +361,7 @@
   inline unsigned int get_default_base_tag_index(void) const
   { return (this+baseScript).get_default_base_tag_index(); }
 
-  inline SHORT get_base_coord(unsigned int baselineTagIndex) const
+  inline HBINT16 get_base_coord(unsigned int baselineTagIndex) const
   { return (this+baseScript).get_base_coord(baselineTagIndex); }
 
   inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const
@@ -370,10 +370,10 @@
   inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const
   { return (this+baseScript).get_feature_tag_index(baseLangSysIndex, featureTableTag); }
 
-  inline SHORT get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   { return (this+baseScript).get_max_value(baseLangSysIndex, featureTableTagIndex); }
 
-  inline SHORT get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   { return (this+baseScript).get_min_value(baseLangSysIndex, featureTableTagIndex); }
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
@@ -408,7 +408,7 @@
     return baseScriptRecords[baseScriptIndex].get_default_base_tag_index();
   }
 
-  inline SHORT get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline HBINT16 get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   {
     if (unlikely(baseScriptIndex >= baseScriptCount)) return NO_COORD;
     return baseScriptRecords[baseScriptIndex].get_base_coord(baselineTagIndex);
@@ -426,13 +426,13 @@
     return baseScriptRecords[baseScriptIndex].get_feature_tag_index(baseLangSysIndex, featureTableTag);
   }
 
-  inline SHORT get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (unlikely(baseScriptIndex >= baseScriptCount)) return NO_COORD;
     return baseScriptRecords[baseScriptIndex].get_max_value(baseLangSysIndex, featureTableTagIndex);
   }
 
-  inline SHORT get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (unlikely(baseScriptIndex >= baseScriptCount)) return NO_COORD;
     return baseScriptRecords[baseScriptIndex].get_min_value(baseLangSysIndex, featureTableTagIndex);
@@ -446,7 +446,7 @@
   }
 
   protected:
-  USHORT                    baseScriptCount;
+  HBUINT16                    baseScriptCount;
   ArrayOf<BaseScriptRecord> baseScriptRecords;
 
   public:
@@ -472,7 +472,7 @@
   }
 
   protected:
-  USHORT        baseTagCount;
+  HBUINT16        baseTagCount;
   SortedArrayOf<Tag>  baselineTags;
 
   public:
@@ -494,7 +494,7 @@
     return (this+baseScriptList).get_default_base_tag_index(baseScriptIndex);
   }
 
-  inline SHORT get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline HBINT16 get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   {
     if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NO_COORD;
     return (this+baseScriptList).get_base_coord(baseScriptIndex, baselineTagIndex);
@@ -512,13 +512,13 @@
     return (this+baseScriptList).get_feature_tag_index(baseScriptIndex, baseLangSysIndex, featureTableTag);
   }
 
-  inline SHORT get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NO_COORD;
     return (this+baseScriptList).get_max_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
-  inline SHORT get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (unlikely(baseScriptList == Null(OffsetTo<BaseScriptList>))) return NO_COORD;
     return (this+baseScriptList).get_min_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
@@ -585,7 +585,7 @@
     return (this+horizAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
   }
 
-  inline SHORT get_horiz_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline HBINT16 get_horiz_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   {
     if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NO_COORD;
     return (this+horizAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
@@ -605,7 +605,7 @@
     return (this+vertAxis).get_default_base_tag_index_for_script_index(baseScriptIndex);
   }
 
-  inline SHORT get_vert_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline HBINT16 get_vert_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   {
     if (vertAxis == Null(OffsetTo<Axis>)) return NO_COORD;
     return (this+vertAxis).get_base_coord(baseScriptIndex, baselineTagIndex);
@@ -625,13 +625,13 @@
     return (this+horizAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
   }
 
-  inline SHORT get_horiz_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_horiz_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NO_COORD;
     return (this+horizAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
-  inline SHORT get_horiz_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_horiz_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (unlikely(horizAxis == Null(OffsetTo<Axis>))) return NO_COORD;
     return (this+horizAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
@@ -651,13 +651,13 @@
     return (this+vertAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag);
   }
 
-  inline SHORT get_vert_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_vert_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NO_COORD;
     return (this+vertAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
   }
 
-  inline SHORT get_vert_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_vert_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   {
     if (unlikely(vertAxis == Null(OffsetTo<Axis>))) return NO_COORD;
     return (this+vertAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex);
@@ -696,7 +696,7 @@
   inline unsigned int get_horiz_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
   { return u.format1_0.get_horiz_default_base_tag_index_for_script_index(baseScriptIndex); }
 
-  inline SHORT get_horiz_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline HBINT16 get_horiz_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   { return u.format1_0.get_horiz_base_coord(baseScriptIndex, baselineTagIndex); }
 
   inline unsigned int get_vert_base_tag_index(Tag baselineTag) const
@@ -705,7 +705,7 @@
   inline unsigned int get_vert_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const
   { return u.format1_0.get_vert_default_base_tag_index_for_script_index(baseScriptIndex); }
 
-  inline SHORT get_vert_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
+  inline HBINT16 get_vert_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const
   { return u.format1_0.get_vert_base_coord(baseScriptIndex, baselineTagIndex); }
 
   inline unsigned int get_horiz_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
@@ -714,10 +714,10 @@
   inline unsigned int get_horiz_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
   { return u.format1_0.get_horiz_feature_tag_index(baseScriptIndex, baseLangSysIndex, featureTableTag); }
 
-  inline SHORT get_horiz_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_horiz_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   { return u.format1_0.get_horiz_max_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex); }
 
-  inline SHORT get_horiz_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_horiz_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   { return u.format1_0.get_horiz_min_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex); }
 
   inline unsigned int get_vert_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const
@@ -726,10 +726,10 @@
   inline unsigned int get_vert_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const
   { return u.format1_0.get_vert_feature_tag_index(baseScriptIndex, baseLangSysIndex, featureTableTag); }
 
-  inline SHORT get_vert_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_vert_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   { return u.format1_0.get_vert_max_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex); }
 
-  inline SHORT get_vert_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
+  inline HBINT16 get_vert_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const
   { return u.format1_0.get_vert_min_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex); }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index 180e5f0..c5e7f52 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -29,6 +29,8 @@
 #ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH
 #define HB_OT_LAYOUT_COMMON_PRIVATE_HH
 
+#include "hb-private.hh"
+#include "hb-debug.hh"
 #include "hb-ot-layout-private.hh"
 #include "hb-open-type-private.hh"
 #include "hb-set-private.hh"
@@ -45,12 +47,6 @@
 namespace OT {
 
 
-#define TRACE_DISPATCH(this, format) \
-	hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
-	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
-	 "format %d", (int) format);
-
-
 #define NOT_COVERED		((unsigned int) -1)
 
 
@@ -159,13 +155,13 @@
   }
 
   template <typename set_t>
-  inline void add_coverage (set_t *glyphs) const {
-    glyphs->add_range (start, end);
+  inline bool add_coverage (set_t *glyphs) const {
+    return glyphs->add_range (start, end);
   }
 
   GlyphID	start;		/* First GlyphID in the range */
   GlyphID	end;		/* Last GlyphID in the range */
-  USHORT	value;		/* Value */
+  HBUINT16	value;		/* Value */
   public:
   DEFINE_SIZE_STATIC (6);
 };
@@ -179,7 +175,7 @@
 				   unsigned int *_indexes /* OUT */) const
   {
     if (_count) {
-      const USHORT *arr = this->sub_array (start_offset, _count);
+      const HBUINT16 *arr = this->sub_array (start_offset, _count);
       unsigned int count = *_count;
       for (unsigned int i = 0; i < count; i++)
 	_indexes[i] = arr[i];
@@ -214,15 +210,15 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<LangSys>::sanitize_closure_t * = NULL) const
+			const Record<LangSys>::sanitize_closure_t * = nullptr) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && featureIndex.sanitize (c));
   }
 
-  Offset<>	lookupOrderZ;	/* = Null (reserved for an offset to a
+  Offset16	lookupOrderZ;	/* = Null (reserved for an offset to a
 				 * reordering table) */
-  USHORT	reqFeatureIndex;/* Index of a feature required for this
+  HBUINT16	reqFeatureIndex;/* Index of a feature required for this
 				 * language system--if no required features
 				 * = 0xFFFFu */
   IndexArray	featureIndex;	/* Array of indices into the FeatureList */
@@ -254,7 +250,7 @@
   inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
 
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<Script>::sanitize_closure_t * = NULL) const
+			const Record<Script>::sanitize_closure_t * = nullptr) const
   {
     TRACE_SANITIZE (this);
     return_trace (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
@@ -303,7 +299,7 @@
      * better.
      *
      * Assume that the offset to the size feature is according to specification,
-     * and make the following value checks. If it fails, assume the the size
+     * and make the following value checks. If it fails, assume the size
      * feature is calculated as versions of MakeOTF before the AFDKO 2.0 built it.
      * If this fails, reject the 'size' feature. The older makeOTF's calculated the
      * offset from the beginning of the FeatureList table, rather than from the
@@ -347,12 +343,12 @@
       return_trace (true);
   }
 
-  USHORT	designSize;	/* Represents the design size in 720/inch
+  HBUINT16	designSize;	/* Represents the design size in 720/inch
 				 * units (decipoints).  The design size entry
 				 * must be non-zero.  When there is a design
 				 * size but no recommended size range, the
 				 * rest of the array will consist of zeros. */
-  USHORT	subfamilyID;	/* Has no independent meaning, but serves
+  HBUINT16	subfamilyID;	/* Has no independent meaning, but serves
 				 * as an identifier that associates fonts
 				 * in a subfamily. All fonts which share a
 				 * Preferred or Font Family name and which
@@ -362,7 +358,7 @@
 				 * same subfamily value. If this value is
 				 * zero, the remaining fields in the array
 				 * will be ignored. */
-  USHORT	subfamilyNameID;/* If the preceding value is non-zero, this
+  HBUINT16	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
@@ -376,10 +372,10 @@
 				 * subfamily in a menu.  Applications will
 				 * choose the appropriate version based on
 				 * their selection criteria. */
-  USHORT	rangeStart;	/* Large end of the recommended usage range
+  HBUINT16	rangeStart;	/* Large end of the recommended usage range
 				 * (inclusive), stored in 720/inch units
 				 * (decipoints). */
-  USHORT	rangeEnd;	/* Small end of the recommended usage range
+  HBUINT16	rangeEnd;	/* Small end of the recommended usage range
 				   (exclusive), stored in 720/inch units
 				 * (decipoints). */
   public:
@@ -397,12 +393,12 @@
     return_trace (c->check_struct (this));
   }
 
-  USHORT	version;	/* (set to 0): This corresponds to a “minor”
+  HBUINT16	version;	/* (set to 0): This corresponds to a “minor”
 				 * version number. Additional data may be
 				 * added to the end of this Feature Parameters
 				 * table in the future. */
 
-  USHORT	uiNameID;	/* The 'name' table name ID that specifies a
+  HBUINT16	uiNameID;	/* The 'name' table name ID that specifies a
 				 * string (or strings, for multiple languages)
 				 * for a user-interface label for this
 				 * feature.  The values of uiLabelNameId and
@@ -430,25 +426,25 @@
 		  characters.sanitize (c));
   }
 
-  USHORT	format;			/* Format number is set to 0. */
-  USHORT	featUILableNameID;	/* The ‘name’ table name ID that
+  HBUINT16	format;			/* Format number is set to 0. */
+  HBUINT16	featUILableNameID;	/* The ‘name’ table name ID that
 					 * specifies a string (or strings,
 					 * for multiple languages) for a
 					 * user-interface label for this
-					 * feature. (May be NULL.) */
-  USHORT	featUITooltipTextNameID;/* The ‘name’ table name ID that
+					 * feature. (May be nullptr.) */
+  HBUINT16	featUITooltipTextNameID;/* The ‘name’ table name ID that
 					 * specifies a string (or strings,
 					 * for multiple languages) that an
 					 * application can use for tooltip
 					 * text for this feature. (May be
-					 * NULL.) */
-  USHORT	sampleTextNameID;	/* The ‘name’ table name ID that
+					 * nullptr.) */
+  HBUINT16	sampleTextNameID;	/* The ‘name’ table name ID that
 					 * specifies sample text that
 					 * illustrates the effect of this
-					 * feature. (May be NULL.) */
-  USHORT	numNamedParameters;	/* Number of named parameters. (May
+					 * feature. (May be nullptr.) */
+  HBUINT16	numNamedParameters;	/* Number of named parameters. (May
 					 * be zero.) */
-  USHORT	firstParamUILabelNameID;/* The first ‘name’ table name ID
+  HBUINT16	firstParamUILabelNameID;/* The first ‘name’ table name ID
 					 * used to specify strings for
 					 * user-interface labels for the
 					 * feature parameters. (Must be zero
@@ -507,7 +503,7 @@
   { return this+featureParams; }
 
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<Feature>::sanitize_closure_t *closure = NULL) const
+			const Record<Feature>::sanitize_closure_t *closure = nullptr) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c))))
@@ -566,7 +562,7 @@
 typedef RecordListOf<Feature> FeatureList;
 
 
-struct LookupFlag : USHORT
+struct LookupFlag : HBUINT16
 {
   enum Flags {
     RightToLeft		= 0x0001u,
@@ -612,7 +608,7 @@
     unsigned int flag = lookupFlag;
     if (unlikely (flag & LookupFlag::UseMarkFilteringSet))
     {
-      const USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
+      const HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable);
       flag += (markFilteringSet << 16);
     }
     return flag;
@@ -644,7 +640,7 @@
     if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false);
     if (lookupFlag & LookupFlag::UseMarkFilteringSet)
     {
-      USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
+      HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable);
       markFilteringSet.set (lookup_props >> 16);
     }
     return_trace (true);
@@ -657,18 +653,18 @@
     if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false);
     if (lookupFlag & LookupFlag::UseMarkFilteringSet)
     {
-      const USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
+      const HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable);
       if (!markFilteringSet.sanitize (c)) return_trace (false);
     }
     return_trace (true);
   }
 
   private:
-  USHORT	lookupType;		/* Different enumerations for GSUB and GPOS */
-  USHORT	lookupFlag;		/* Lookup qualifiers */
-  ArrayOf<Offset<> >
+  HBUINT16	lookupType;		/* Different enumerations for GSUB and GPOS */
+  HBUINT16	lookupFlag;		/* Lookup qualifiers */
+  ArrayOf<Offset16>
 		subTable;		/* Array of SubTables */
-  USHORT	markFilteringSetX[VAR];	/* Index (base 0) into GDEF mark glyph sets
+  HBUINT16	markFilteringSetX[VAR];	/* Index (base 0) into GDEF mark glyph sets
 					 * structure. This field is only present if bit
 					 * UseMarkFilteringSet of lookup flags is set. */
   public:
@@ -690,7 +686,7 @@
   inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
   {
     int i = glyphArray.bsearch (glyph_id);
-    ASSERT_STATIC (((unsigned int) -1) == NOT_COVERED);
+    static_assert ((((unsigned int) -1) == NOT_COVERED), "");
     return i;
   }
 
@@ -704,7 +700,7 @@
     if (unlikely (!c->extend (glyphArray))) return_trace (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
       glyphArray[i] = glyphs[i];
-    glyphs.advance (num_glyphs);
+    glyphs += num_glyphs;
     return_trace (true);
   }
 
@@ -719,10 +715,8 @@
   }
 
   template <typename set_t>
-  inline void add_coverage (set_t *glyphs) const {
-    unsigned int count = glyphArray.len;
-    for (unsigned int i = 0; i < count; i++)
-      glyphs->add (glyphArray[i]);
+  inline bool add_coverage (set_t *glyphs) const {
+    return glyphs->add_sorted_array (glyphArray.array, glyphArray.len);
   }
 
   public:
@@ -741,7 +735,7 @@
   private:
 
   protected:
-  USHORT	coverageFormat;	/* Format identifier--format = 1 */
+  HBUINT16	coverageFormat;	/* Format identifier--format = 1 */
   SortedArrayOf<GlyphID>
 		glyphArray;	/* Array of GlyphIDs--in numerical order */
   public:
@@ -795,7 +789,7 @@
       } else {
         rangeRecord[range].end = glyphs[i];
       }
-    glyphs.advance (num_glyphs);
+    glyphs += num_glyphs;
     return_trace (true);
   }
 
@@ -821,10 +815,12 @@
   }
 
   template <typename set_t>
-  inline void 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++)
-      rangeRecord[i].add_coverage (glyphs);
+      if (unlikely (!rangeRecord[i].add_coverage (glyphs)))
+        return false;
+    return true;
   }
 
   public:
@@ -864,7 +860,7 @@
   private:
 
   protected:
-  USHORT	coverageFormat;	/* Format identifier--format = 2 */
+  HBUINT16	coverageFormat;	/* Format identifier--format = 2 */
   SortedArrayOf<RangeRecord>
 		rangeRecord;	/* Array of glyph ranges--ordered by
 				 * Start GlyphID. rangeCount entries
@@ -878,8 +874,8 @@
   inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
   {
     switch (u.format) {
-    case 1: return u.format1.get_coverage(glyph_id);
-    case 2: return u.format2.get_coverage(glyph_id);
+    case 1: return u.format1.get_coverage (glyph_id);
+    case 2: return u.format2.get_coverage (glyph_id);
     default:return NOT_COVERED;
     }
   }
@@ -931,17 +927,19 @@
     }
   }
 
+  /* Might return false if array looks unsorted.
+   * Used for faster rejection of corrupt data. */
   template <typename set_t>
-  inline void add_coverage (set_t *glyphs) const {
+  inline bool add_coverage (set_t *glyphs) const {
     switch (u.format) {
-    case 1: u.format1.add_coverage (glyphs); break;
-    case 2: u.format2.add_coverage (glyphs); break;
-    default:                                 break;
+    case 1: return u.format1.add_coverage (glyphs);
+    case 2: return u.format2.add_coverage (glyphs);
+    default:return false;
     }
   }
 
   struct Iter {
-    Iter (void) : format (0) {};
+    Iter (void) : format (0), u () {};
     inline void init (const Coverage &c_) {
       format = c_.u.format;
       switch (format) {
@@ -982,14 +980,14 @@
     private:
     unsigned int format;
     union {
+    CoverageFormat2::Iter	format2; /* Put this one first since it's larger; helps shut up compiler. */
     CoverageFormat1::Iter	format1;
-    CoverageFormat2::Iter	format2;
     } u;
   };
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   CoverageFormat1	format1;
   CoverageFormat2	format2;
   } u;
@@ -1022,11 +1020,36 @@
   }
 
   template <typename set_t>
-  inline void add_class (set_t *glyphs, unsigned int klass) const {
+  inline bool add_coverage (set_t *glyphs) const {
+    unsigned int start = 0;
     unsigned int count = classValue.len;
     for (unsigned int i = 0; i < count; i++)
+    {
+      if (classValue[i])
+        continue;
+
+      if (start != i)
+	if (unlikely (!glyphs->add_range (startGlyph + start, startGlyph + i)))
+	  return false;
+
+      start = i + 1;
+    }
+    if (start != count)
+      if (unlikely (!glyphs->add_range (startGlyph + start, startGlyph + count)))
+	return false;
+
+    return true;
+  }
+
+  template <typename set_t>
+  inline bool add_class (set_t *glyphs, unsigned int klass) const {
+    unsigned int count = classValue.len;
+    for (unsigned int i = 0; i < count; i++)
+    {
       if (classValue[i] == klass)
         glyphs->add (startGlyph + i);
+    }
+    return true;
   }
 
   inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
@@ -1034,7 +1057,7 @@
     if (klass == 0)
     {
       /* Match if there's any glyph that is not listed! */
-      hb_codepoint_t g = -1;
+      hb_codepoint_t g = HB_SET_VALUE_INVALID;
       if (!hb_set_next (glyphs, &g))
         return false;
       if (g < startGlyph)
@@ -1051,9 +1074,9 @@
   }
 
   protected:
-  USHORT	classFormat;		/* Format identifier--format = 1 */
+  HBUINT16	classFormat;		/* Format identifier--format = 1 */
   GlyphID	startGlyph;		/* First GlyphID of the classValueArray */
-  ArrayOf<USHORT>
+  ArrayOf<HBUINT16>
 		classValue;		/* Array of Class Values--one per GlyphID */
   public:
   DEFINE_SIZE_ARRAY (6, classValue);
@@ -1079,11 +1102,25 @@
   }
 
   template <typename set_t>
-  inline void add_class (set_t *glyphs, unsigned int klass) 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)
+	if (unlikely (!rangeRecord[i].add_coverage (glyphs)))
+	  return false;
+    return true;
+  }
+
+  template <typename set_t>
+  inline bool add_class (set_t *glyphs, unsigned int klass) const {
+    unsigned int count = rangeRecord.len;
+    for (unsigned int i = 0; i < count; i++)
+    {
       if (rangeRecord[i].value == klass)
-        rangeRecord[i].add_coverage (glyphs);
+        if (unlikely (!rangeRecord[i].add_coverage (glyphs)))
+	  return false;
+    }
+    return true;
   }
 
   inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
@@ -1091,7 +1128,7 @@
     if (klass == 0)
     {
       /* Match if there's any glyph that is not listed! */
-      hb_codepoint_t g = (hb_codepoint_t) -1;
+      hb_codepoint_t g = HB_SET_VALUE_INVALID;
       for (unsigned int i = 0; i < count; i++)
       {
 	if (!hb_set_next (glyphs, &g))
@@ -1100,7 +1137,7 @@
 	  return true;
 	g = rangeRecord[i].end;
       }
-      if (g != (hb_codepoint_t) -1 && hb_set_next (glyphs, &g))
+      if (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g))
         return true;
       /* Fall through. */
     }
@@ -1111,7 +1148,7 @@
   }
 
   protected:
-  USHORT	classFormat;	/* Format identifier--format = 2 */
+  HBUINT16	classFormat;	/* Format identifier--format = 2 */
   SortedArrayOf<RangeRecord>
 		rangeRecord;	/* Array of glyph ranges--ordered by
 				 * Start GlyphID */
@@ -1124,8 +1161,8 @@
   inline unsigned int get_class (hb_codepoint_t glyph_id) const
   {
     switch (u.format) {
-    case 1: return u.format1.get_class(glyph_id);
-    case 2: return u.format2.get_class(glyph_id);
+    case 1: return u.format1.get_class (glyph_id);
+    case 2: return u.format2.get_class (glyph_id);
     default:return 0;
     }
   }
@@ -1141,11 +1178,25 @@
     }
   }
 
-  inline void add_class (hb_set_t *glyphs, unsigned int klass) const {
+  /* 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) {
-    case 1: u.format1.add_class (glyphs, klass); return;
-    case 2: u.format2.add_class (glyphs, klass); return;
-    default:return;
+    case 1: return u.format1.add_coverage (glyphs);
+    case 2: return u.format2.add_coverage (glyphs);
+    default:return false;
+    }
+  }
+
+  /* Might return false if array looks unsorted.
+   * Used for faster rejection of corrupt data. */
+  template <typename set_t>
+  inline bool add_class (set_t *glyphs, unsigned int klass) const {
+    switch (u.format) {
+    case 1: return u.format1.add_class (glyphs, klass);
+    case 2: return u.format2.add_class (glyphs, klass);
+    default:return false;
     }
   }
 
@@ -1159,7 +1210,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   ClassDefFormat1	format1;
   ClassDefFormat2	format2;
   } u;
@@ -1224,10 +1275,11 @@
     const VarRegionAxis *axes = axesZ + (region_index * axisCount);
 
     float v = 1.;
-    unsigned int count = MIN (coord_len, (unsigned int) axisCount);
+    unsigned int count = axisCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      float factor = axes[i].evaluate (coords[i]);
+      int coord = i < coord_len ? coords[i] : 0;
+      float factor = axes[i].evaluate (coord);
       if (factor == 0.)
         return 0.;
       v *= factor;
@@ -1244,8 +1296,8 @@
   }
 
   protected:
-  USHORT	axisCount;
-  USHORT	regionCount;
+  HBUINT16	axisCount;
+  HBUINT16	regionCount;
   VarRegionAxis	axesZ[VAR];
   public:
   DEFINE_SIZE_ARRAY (4, axesZ);
@@ -1269,19 +1321,19 @@
    unsigned int count = regionIndices.len;
    unsigned int scount = shortCount;
 
-   const BYTE *bytes = &StructAfter<BYTE> (regionIndices);
-   const BYTE *row = bytes + inner * (scount + count);
+   const HBUINT8 *bytes = &StructAfter<HBUINT8> (regionIndices);
+   const HBUINT8 *row = bytes + inner * (scount + count);
 
    float delta = 0.;
    unsigned int i = 0;
 
-   const SHORT *scursor = reinterpret_cast<const SHORT *> (row);
+   const HBINT16 *scursor = reinterpret_cast<const HBINT16 *> (row);
    for (; i < scount; i++)
    {
      float scalar = regions.evaluate (regionIndices.array[i], coords, coord_count);
      delta += scalar * *scursor++;
    }
-   const INT8 *bcursor = reinterpret_cast<const INT8 *> (scursor);
+   const HBINT8 *bcursor = reinterpret_cast<const HBINT8 *> (scursor);
    for (; i < count; i++)
    {
      float scalar = regions.evaluate (regionIndices.array[i], coords, coord_count);
@@ -1297,15 +1349,15 @@
     return_trace (c->check_struct (this) &&
 		  regionIndices.sanitize(c) &&
 		  shortCount <= regionIndices.len &&
-		  c->check_array (&StructAfter<BYTE> (regionIndices),
+		  c->check_array (&StructAfter<HBUINT8> (regionIndices),
 				  get_row_size (), itemCount));
   }
 
   protected:
-  USHORT		itemCount;
-  USHORT		shortCount;
-  ArrayOf<USHORT>	regionIndices;
-  BYTE			bytesX[VAR];
+  HBUINT16		itemCount;
+  HBUINT16		shortCount;
+  ArrayOf<HBUINT16>	regionIndices;
+  HBUINT8			bytesX[VAR];
   public:
   DEFINE_SIZE_ARRAY2 (6, regionIndices, bytesX);
 };
@@ -1341,9 +1393,9 @@
   }
 
   protected:
-  USHORT				format;
+  HBUINT16				format;
   LOffsetTo<VarRegionList>		regions;
-  OffsetArrayOf<VarData, ULONG>		dataSets;
+  OffsetArrayOf<VarData, HBUINT32>	dataSets;
   public:
   DEFINE_SIZE_ARRAY (8, dataSets);
 };
@@ -1370,8 +1422,8 @@
   }
 
   protected:
-  USHORT	format;		/* Format identifier--format = 1 */
-  USHORT	axisIndex;
+  HBUINT16	format;		/* Format identifier--format = 1 */
+  HBUINT16	axisIndex;
   F2DOT14	filterRangeMinValue;
   F2DOT14	filterRangeMaxValue;
   public:
@@ -1400,7 +1452,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   ConditionFormat1	format1;
   } u;
   public:
@@ -1425,7 +1477,7 @@
   }
 
   protected:
-  OffsetArrayOf<Condition, ULONG> conditions;
+  OffsetArrayOf<Condition, HBUINT32> conditions;
   public:
   DEFINE_SIZE_ARRAY (2, conditions);
 };
@@ -1441,7 +1493,7 @@
   }
 
   protected:
-  USHORT		featureIndex;
+  HBUINT16		featureIndex;
   LOffsetTo<Feature>	feature;
   public:
   DEFINE_SIZE_STATIC (6);
@@ -1458,7 +1510,7 @@
       if (record.featureIndex == feature_index)
 	return &(this+record.feature);
     }
-    return NULL;
+    return nullptr;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -1561,8 +1613,8 @@
   inline unsigned int get_size (void) const
   {
     unsigned int f = deltaFormat;
-    if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * USHORT::static_size;
-    return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f)));
+    if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * HBUINT16::static_size;
+    return HBUINT16::static_size * (4 + ((endSize - startSize) >> (4 - f)));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -1607,14 +1659,14 @@
   }
 
   protected:
-  USHORT	startSize;		/* Smallest size to correct--in ppem */
-  USHORT	endSize;		/* Largest size to correct--in ppem */
-  USHORT	deltaFormat;		/* Format of DeltaValue array data: 1, 2, or 3
+  HBUINT16	startSize;		/* Smallest size to correct--in ppem */
+  HBUINT16	endSize;		/* Largest size to correct--in ppem */
+  HBUINT16	deltaFormat;		/* Format of DeltaValue array data: 1, 2, or 3
 					 * 1	Signed 2-bit value, 8 values per uint16
 					 * 2	Signed 4-bit value, 4 values per uint16
 					 * 3	Signed 8-bit value, 2 values per uint16
 					 */
-  USHORT	deltaValue[VAR];	/* Array of compressed data */
+  HBUINT16	deltaValue[VAR];	/* Array of compressed data */
   public:
   DEFINE_SIZE_ARRAY (6, deltaValue);
 };
@@ -1645,9 +1697,9 @@
   }
 
   protected:
-  USHORT	outerIndex;
-  USHORT	innerIndex;
-  USHORT	deltaFormat;	/* Format identifier for this table: 0x0x8000 */
+  HBUINT16	outerIndex;
+  HBUINT16	innerIndex;
+  HBUINT16	deltaFormat;	/* Format identifier for this table: 0x0x8000 */
   public:
   DEFINE_SIZE_STATIC (6);
 };
@@ -1655,10 +1707,10 @@
 struct DeviceHeader
 {
   protected:
-  USHORT		reserved1;
-  USHORT		reserved2;
+  HBUINT16		reserved1;
+  HBUINT16		reserved2;
   public:
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   public:
   DEFINE_SIZE_STATIC (6);
 };
diff --git a/src/hb-ot-layout-gdef-table.hh b/src/hb-ot-layout-gdef-table.hh
index 552df0f..aad7d60 100644
--- a/src/hb-ot-layout-gdef-table.hh
+++ b/src/hb-ot-layout-gdef-table.hh
@@ -41,7 +41,7 @@
  * Attachment List Table
  */
 
-typedef ArrayOf<USHORT> AttachPoint;	/* Array of contour point indices--in
+typedef ArrayOf<HBUINT16> AttachPoint;	/* Array of contour point indices--in
 					 * increasing numerical order */
 
 struct AttachList
@@ -62,7 +62,7 @@
     const AttachPoint &points = this+attachPoint[index];
 
     if (point_count) {
-      const USHORT *array = points.sub_array (start_offset, point_count);
+      const HBUINT16 *array = points.sub_array (start_offset, point_count);
       unsigned int count = *point_count;
       for (unsigned int i = 0; i < count; i++)
 	point_array[i] = array[i];
@@ -109,8 +109,8 @@
   }
 
   protected:
-  USHORT	caretValueFormat;	/* Format identifier--format = 1 */
-  SHORT		coordinate;		/* X or Y value, in design units */
+  HBUINT16	caretValueFormat;	/* Format identifier--format = 1 */
+  HBINT16		coordinate;		/* X or Y value, in design units */
   public:
   DEFINE_SIZE_STATIC (4);
 };
@@ -136,8 +136,8 @@
   }
 
   protected:
-  USHORT	caretValueFormat;	/* Format identifier--format = 2 */
-  USHORT	caretValuePoint;	/* Contour point index on glyph */
+  HBUINT16	caretValueFormat;	/* Format identifier--format = 2 */
+  HBUINT16	caretValuePoint;	/* Contour point index on glyph */
   public:
   DEFINE_SIZE_STATIC (4);
 };
@@ -160,8 +160,8 @@
   }
 
   protected:
-  USHORT	caretValueFormat;	/* Format identifier--format = 3 */
-  SHORT		coordinate;		/* X or Y value, in design units */
+  HBUINT16	caretValueFormat;	/* Format identifier--format = 3 */
+  HBINT16		coordinate;		/* X or Y value, in design units */
   OffsetTo<Device>
 		deviceTable;		/* Offset to Device table for X or Y
 					 * value--from beginning of CaretValue
@@ -199,7 +199,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   CaretValueFormat1	format1;
   CaretValueFormat2	format2;
   CaretValueFormat3	format3;
@@ -294,7 +294,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   ArrayOf<LOffsetTo<Coverage> >
 		coverage;		/* Array of long offsets to mark set
 					 * coverage tables */
@@ -324,7 +324,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   MarkGlyphSetsFormat1	format1;
   } u;
   public:
@@ -404,9 +404,9 @@
   {
     unsigned int klass = get_glyph_class (glyph);
 
-    ASSERT_STATIC ((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH == (unsigned int) LookupFlag::IgnoreBaseGlyphs);
-    ASSERT_STATIC ((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE == (unsigned int) LookupFlag::IgnoreLigatures);
-    ASSERT_STATIC ((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_MARK == (unsigned int) LookupFlag::IgnoreMarks);
+    static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH == (unsigned int) LookupFlag::IgnoreBaseGlyphs), "");
+    static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE == (unsigned int) LookupFlag::IgnoreLigatures), "");
+    static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_MARK == (unsigned int) LookupFlag::IgnoreMarks), "");
 
     switch (klass) {
     default:			return 0;
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 952fd60..4e1a10d 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -51,11 +51,11 @@
 
 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
 
-typedef USHORT Value;
+typedef HBUINT16 Value;
 
 typedef Value ValueRecord[VAR];
 
-struct ValueFormat : USHORT
+struct ValueFormat : HBUINT16
 {
   enum Flags {
     xPlacement	= 0x0001u,	/* Includes horizontal adjustment for placement */
@@ -74,14 +74,14 @@
 
 /* All fields are options.  Only those available advance the value pointer. */
 #if 0
-  SHORT		xPlacement;		/* Horizontal adjustment for
+  HBINT16		xPlacement;		/* Horizontal adjustment for
 					 * placement--in design units */
-  SHORT		yPlacement;		/* Vertical adjustment for
+  HBINT16		yPlacement;		/* Vertical adjustment for
 					 * placement--in design units */
-  SHORT		xAdvance;		/* Horizontal adjustment for
+  HBINT16		xAdvance;		/* Horizontal adjustment for
 					 * advance--in design units (only used
 					 * for horizontal writing) */
-  SHORT		yAdvance;		/* Vertical adjustment for advance--in
+  HBINT16		yAdvance;		/* Vertical adjustment for advance--in
 					 * design units (only used for vertical
 					 * writing) */
   Offset	xPlaDevice;		/* Offset to Device table for
@@ -99,11 +99,11 @@
 #endif
 
   inline unsigned int get_len (void) const
-  { return _hb_popcount32 ((unsigned int) *this); }
+  { return _hb_popcount ((unsigned int) *this); }
   inline unsigned int get_size (void) const
   { return get_len () * Value::static_size; }
 
-  void apply_value (hb_apply_context_t   *c,
+  void apply_value (hb_ot_apply_context_t   *c,
 		    const void           *base,
 		    const Value          *values,
 		    hb_glyph_position_t  &glyph_pos) const
@@ -178,8 +178,8 @@
   static inline const OffsetTo<Device>& get_device (const Value* value)
   { return *CastP<OffsetTo<Device> > (value); }
 
-  static inline const SHORT& get_short (const Value* value)
-  { return *CastP<SHORT> (value); }
+  static inline const HBINT16& get_short (const Value* value)
+  { return *CastP<HBINT16> (value); }
 
   public:
 
@@ -232,12 +232,12 @@
 
 struct AnchorFormat1
 {
-  inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
-			  hb_position_t *x, hb_position_t *y) const
+  inline void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
+			  float *x, float *y) const
   {
     hb_font_t *font = c->font;
-    *x = font->em_scale_x (xCoordinate);
-    *y = font->em_scale_y (yCoordinate);
+    *x = font->em_fscale_x (xCoordinate);
+    *y = font->em_fscale_y (yCoordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -247,17 +247,17 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
-  SHORT		xCoordinate;		/* Horizontal value--in design units */
-  SHORT		yCoordinate;		/* Vertical value--in design units */
+  HBUINT16	format;			/* Format identifier--format = 1 */
+  HBINT16		xCoordinate;		/* Horizontal value--in design units */
+  HBINT16		yCoordinate;		/* Vertical value--in design units */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 struct AnchorFormat2
 {
-  inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id,
-			  hb_position_t *x, hb_position_t *y) const
+  inline void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
+			  float *x, float *y) const
   {
     hb_font_t *font = c->font;
     unsigned int x_ppem = font->x_ppem;
@@ -267,8 +267,8 @@
 
     ret = (x_ppem || y_ppem) &&
 	   font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
-    *x = ret && x_ppem ? cx : font->em_scale_x (xCoordinate);
-    *y = ret && y_ppem ? cy : font->em_scale_y (yCoordinate);
+    *x = ret && x_ppem ? cx : font->em_fscale_x (xCoordinate);
+    *y = ret && y_ppem ? cy : font->em_fscale_y (yCoordinate);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -278,22 +278,22 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 2 */
-  SHORT		xCoordinate;		/* Horizontal value--in design units */
-  SHORT		yCoordinate;		/* Vertical value--in design units */
-  USHORT	anchorPoint;		/* Index to glyph contour point */
+  HBUINT16	format;			/* Format identifier--format = 2 */
+  HBINT16		xCoordinate;		/* Horizontal value--in design units */
+  HBINT16		yCoordinate;		/* Vertical value--in design units */
+  HBUINT16	anchorPoint;		/* Index to glyph contour point */
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
 struct AnchorFormat3
 {
-  inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
-			  hb_position_t *x, hb_position_t *y) const
+  inline void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
+			  float *x, float *y) const
   {
     hb_font_t *font = c->font;
-    *x = font->em_scale_x (xCoordinate);
-    *y = font->em_scale_y (yCoordinate);
+    *x = font->em_fscale_x (xCoordinate);
+    *y = font->em_fscale_y (yCoordinate);
 
     if (font->x_ppem || font->num_coords)
       *x += (this+xDeviceTable).get_x_delta (font, c->var_store);
@@ -308,9 +308,9 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 3 */
-  SHORT		xCoordinate;		/* Horizontal value--in design units */
-  SHORT		yCoordinate;		/* Vertical value--in design units */
+  HBUINT16	format;			/* Format identifier--format = 3 */
+  HBINT16		xCoordinate;		/* Horizontal value--in design units */
+  HBINT16		yCoordinate;		/* Vertical value--in design units */
   OffsetTo<Device>
 		xDeviceTable;		/* Offset to Device table for X
 					 * coordinate-- from beginning of
@@ -325,15 +325,15 @@
 
 struct Anchor
 {
-  inline void get_anchor (hb_apply_context_t *c, hb_codepoint_t glyph_id,
-			  hb_position_t *x, hb_position_t *y) const
+  inline void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
+			  float *x, float *y) const
   {
     *x = *y = 0;
     switch (u.format) {
     case 1: u.format1.get_anchor (c, glyph_id, x, y); return;
     case 2: u.format2.get_anchor (c, glyph_id, x, y); return;
     case 3: u.format3.get_anchor (c, glyph_id, x, y); return;
-    default:						 return;
+    default:					      return;
     }
   }
 
@@ -351,7 +351,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   AnchorFormat1		format1;
   AnchorFormat2		format2;
   AnchorFormat3		format3;
@@ -382,7 +382,7 @@
     return_trace (true);
   }
 
-  USHORT	rows;			/* Number of rows */
+  HBUINT16	rows;			/* Number of rows */
   protected:
   OffsetTo<Anchor>
 		matrixZ[VAR];		/* Matrix of offsets to Anchor tables--
@@ -403,7 +403,7 @@
   }
 
   protected:
-  USHORT	klass;			/* Class defined for this mark */
+  HBUINT16	klass;			/* Class defined for this mark */
   OffsetTo<Anchor>
 		markAnchor;		/* Offset to Anchor table--from
 					 * beginning of MarkArray table */
@@ -413,7 +413,7 @@
 
 struct MarkArray : ArrayOf<MarkRecord>	/* Array of MarkRecords--in Coverage order */
 {
-  inline bool apply (hb_apply_context_t *c,
+  inline bool apply (hb_ot_apply_context_t *c,
 		     unsigned int mark_index, unsigned int glyph_index,
 		     const AnchorMatrix &anchors, unsigned int class_count,
 		     unsigned int glyph_pos) const
@@ -430,14 +430,15 @@
      * return false such that the subsequent subtables have a chance at it. */
     if (unlikely (!found)) return_trace (false);
 
-    hb_position_t mark_x, mark_y, base_x, base_y;
+    float mark_x, mark_y, base_x, base_y;
 
+    buffer->unsafe_to_break (glyph_pos, buffer->idx);
     mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
     glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
 
     hb_glyph_position_t &o = buffer->cur_pos();
-    o.x_offset = base_x - mark_x;
-    o.y_offset = base_y - mark_y;
+    o.x_offset = round (base_x - mark_x);
+    o.y_offset = round (base_y - mark_y);
     o.attach_type() = ATTACH_TYPE_MARK;
     o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
     buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
@@ -461,7 +462,7 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    (this+coverage).add_coverage (c->input);
+    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
   }
 
   inline const Coverage &get_coverage (void) const
@@ -469,7 +470,7 @@
     return this+coverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -491,7 +492,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
@@ -509,7 +510,7 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    (this+coverage).add_coverage (c->input);
+    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
   }
 
   inline const Coverage &get_coverage (void) const
@@ -517,7 +518,7 @@
     return this+coverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -543,13 +544,13 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 2 */
+  HBUINT16	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
   ValueFormat	valueFormat;		/* Defines the types of data in the
 					 * ValueRecord */
-  USHORT	valueCount;		/* Number of ValueRecords */
+  HBUINT16	valueCount;		/* Number of ValueRecords */
   ValueRecord	values;			/* Array of ValueRecords--positioning
 					 * values applied to glyphs */
   public:
@@ -572,7 +573,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   SinglePosFormat1	format1;
   SinglePosFormat2	format2;
   } u;
@@ -603,18 +604,13 @@
     TRACE_COLLECT_GLYPHS (this);
     unsigned int len1 = valueFormats[0].get_len ();
     unsigned int len2 = valueFormats[1].get_len ();
-    unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
+    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
 
     const PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
-    unsigned int count = len;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      c->input->add (record->secondGlyph);
-      record = &StructAtOffset<PairValueRecord> (record, record_size);
-    }
+    c->input->add_array (&record->secondGlyph, len, record_size);
   }
 
-  inline bool apply (hb_apply_context_t *c,
+  inline bool apply (hb_ot_apply_context_t *c,
 		     const ValueFormat *valueFormats,
 		     unsigned int pos) const
   {
@@ -622,7 +618,7 @@
     hb_buffer_t *buffer = c->buffer;
     unsigned int len1 = valueFormats[0].get_len ();
     unsigned int len2 = valueFormats[1].get_len ();
-    unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
+    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
 
     const PairValueRecord *record_array = CastP<PairValueRecord> (arrayZ);
     unsigned int count = len;
@@ -643,6 +639,7 @@
         min = mid + 1;
       else
       {
+        buffer->unsafe_to_break (buffer->idx, pos + 1);
 	valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
 	valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
 	if (len2)
@@ -666,7 +663,7 @@
   {
     TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
-       && c->check_array (arrayZ, USHORT::static_size * closure->stride, len))) return_trace (false);
+       && c->check_array (arrayZ, HBUINT16::static_size * closure->stride, len))) return_trace (false);
 
     unsigned int count = len;
     const PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
@@ -675,8 +672,8 @@
   }
 
   protected:
-  USHORT	len;			/* Number of PairValueRecords */
-  USHORT	arrayZ[VAR];		/* Array of PairValueRecords--ordered
+  HBUINT16	len;			/* Number of PairValueRecords */
+  HBUINT16	arrayZ[VAR];		/* Array of PairValueRecords--ordered
 					 * by GlyphID of the second glyph */
   public:
   DEFINE_SIZE_ARRAY (2, arrayZ);
@@ -687,7 +684,7 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    (this+coverage).add_coverage (c->input);
+    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
     unsigned int count = pairSet.len;
     for (unsigned int i = 0; i < count; i++)
       (this+pairSet[i]).collect_glyphs (c, valueFormat);
@@ -698,14 +695,14 @@
     return this+coverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
-    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    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);
 
@@ -731,7 +728,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
@@ -753,17 +750,8 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    (this+coverage).add_coverage (c->input);
-
-    unsigned int count1 = class1Count;
-    const ClassDef &klass1 = this+classDef1;
-    for (unsigned int i = 0; i < count1; i++)
-      klass1.add_class (c->input, i);
-
-    unsigned int count2 = class2Count;
-    const ClassDef &klass2 = this+classDef2;
-    for (unsigned int i = 0; i < count2; i++)
-      klass2.add_class (c->input, i);
+    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
+    if (unlikely (!(this+classDef2).add_coverage (c->input))) return;
   }
 
   inline const Coverage &get_coverage (void) const
@@ -771,14 +759,14 @@
     return this+coverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
-    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    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);
 
@@ -790,6 +778,7 @@
     unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
 
+    buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
     valueFormat1.apply_value (c, this, v, buffer->cur_pos());
     valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
@@ -820,7 +809,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 2 */
+  HBUINT16	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
@@ -838,9 +827,9 @@
 		classDef2;		/* Offset to ClassDef table--from
 					 * beginning of PairPos subtable--for
 					 * the second glyph of the pair */
-  USHORT	class1Count;		/* Number of classes in ClassDef1
+  HBUINT16	class1Count;		/* Number of classes in ClassDef1
 					 * table--includes Class0 */
-  USHORT	class2Count;		/* Number of classes in ClassDef2
+  HBUINT16	class2Count;		/* Number of classes in ClassDef2
 					 * table--includes Class0 */
   ValueRecord	values;			/* Matrix of value pairs:
 					 * class1-major, class2-minor,
@@ -865,7 +854,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   PairPosFormat1	format1;
   PairPosFormat2	format2;
   } u;
@@ -903,7 +892,7 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    (this+coverage).add_coverage (c->input);
+    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
   }
 
   inline const Coverage &get_coverage (void) const
@@ -911,7 +900,7 @@
     return this+coverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -919,7 +908,7 @@
     const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (buffer->cur().codepoint)];
     if (!this_record.exitAnchor) return_trace (false);
 
-    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    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);
 
@@ -929,7 +918,8 @@
     unsigned int i = buffer->idx;
     unsigned int j = skippy_iter.idx;
 
-    hb_position_t entry_x, entry_y, exit_x, exit_y;
+    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);
 
@@ -939,32 +929,32 @@
     /* Main-direction adjustment */
     switch (c->direction) {
       case HB_DIRECTION_LTR:
-	pos[i].x_advance  =  exit_x + pos[i].x_offset;
+	pos[i].x_advance  = round (exit_x) + pos[i].x_offset;
 
-	d = entry_x + pos[j].x_offset;
+	d = round (entry_x) + pos[j].x_offset;
 	pos[j].x_advance -= d;
 	pos[j].x_offset  -= d;
 	break;
       case HB_DIRECTION_RTL:
-	d = exit_x + pos[i].x_offset;
+	d = round (exit_x) + pos[i].x_offset;
 	pos[i].x_advance -= d;
 	pos[i].x_offset  -= d;
 
-	pos[j].x_advance  =  entry_x + pos[j].x_offset;
+	pos[j].x_advance  = round (entry_x) + pos[j].x_offset;
 	break;
       case HB_DIRECTION_TTB:
-	pos[i].y_advance  =  exit_y + pos[i].y_offset;
+	pos[i].y_advance  = round (exit_y) + pos[i].y_offset;
 
-	d = entry_y + pos[j].y_offset;
+	d = round (entry_y) + pos[j].y_offset;
 	pos[j].y_advance -= d;
 	pos[j].y_offset  -= d;
 	break;
       case HB_DIRECTION_BTT:
-	d = exit_y + pos[i].y_offset;
+	d = round (exit_y) + pos[i].y_offset;
 	pos[i].y_advance -= d;
 	pos[i].y_offset  -= d;
 
-	pos[j].y_advance  =  entry_y;
+	pos[j].y_advance  = round (entry_y);
 	break;
       case HB_DIRECTION_INVALID:
       default:
@@ -1018,7 +1008,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of subtable */
@@ -1044,7 +1034,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   CursivePosFormat1	format1;
   } u;
 };
@@ -1060,8 +1050,8 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    (this+markCoverage).add_coverage (c->input);
-    (this+baseCoverage).add_coverage (c->input);
+    if (unlikely (!(this+markCoverage).add_coverage (c->input))) return;
+    if (unlikely (!(this+baseCoverage).add_coverage (c->input))) return;
   }
 
   inline const Coverage &get_coverage (void) const
@@ -1069,7 +1059,7 @@
     return this+markCoverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -1077,13 +1067,23 @@
     if (likely (mark_index == NOT_COVERED)) return_trace (false);
 
     /* Now we search backwards for a non-mark glyph */
-    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
     do {
       if (!skippy_iter.prev ()) return_trace (false);
-      /* We only want to attach to the first of a MultipleSubst sequence.  Reject others. */
-      if (0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx])) break;
+      /* We only want to attach to the first of a MultipleSubst sequence.
+       * https://github.com/harfbuzz/harfbuzz/issues/740
+       * Reject others. */
+      if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
+	  0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
+	  (skippy_iter.idx == 0 ||
+	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
+	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
+	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
+	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
+	   ))
+	break;
       skippy_iter.reject ();
     } while (1);
 
@@ -1107,14 +1107,14 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		markCoverage;		/* Offset to MarkCoverage table--from
 					 * beginning of MarkBasePos subtable */
   OffsetTo<Coverage>
 		baseCoverage;		/* Offset to BaseCoverage table--from
 					 * beginning of MarkBasePos subtable */
-  USHORT	classCount;		/* Number of classes defined for marks */
+  HBUINT16	classCount;		/* Number of classes defined for marks */
   OffsetTo<MarkArray>
 		markArray;		/* Offset to MarkArray table--from
 					 * beginning of MarkBasePos subtable */
@@ -1140,7 +1140,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   MarkBasePosFormat1	format1;
   } u;
 };
@@ -1161,8 +1161,8 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    (this+markCoverage).add_coverage (c->input);
-    (this+ligatureCoverage).add_coverage (c->input);
+    if (unlikely (!(this+markCoverage).add_coverage (c->input))) return;
+    if (unlikely (!(this+ligatureCoverage).add_coverage (c->input))) return;
   }
 
   inline const Coverage &get_coverage (void) const
@@ -1170,7 +1170,7 @@
     return this+markCoverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -1178,7 +1178,7 @@
     if (likely (mark_index == NOT_COVERED)) return_trace (false);
 
     /* Now we search backwards for a non-mark glyph */
-    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
     if (!skippy_iter.prev ()) return_trace (false);
@@ -1224,7 +1224,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		markCoverage;		/* Offset to Mark Coverage table--from
 					 * beginning of MarkLigPos subtable */
@@ -1232,7 +1232,7 @@
 		ligatureCoverage;	/* Offset to Ligature Coverage
 					 * table--from beginning of MarkLigPos
 					 * subtable */
-  USHORT	classCount;		/* Number of defined mark classes */
+  HBUINT16	classCount;		/* Number of defined mark classes */
   OffsetTo<MarkArray>
 		markArray;		/* Offset to MarkArray table--from
 					 * beginning of MarkLigPos subtable */
@@ -1258,7 +1258,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   MarkLigPosFormat1	format1;
   } u;
 };
@@ -1274,8 +1274,8 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    (this+mark1Coverage).add_coverage (c->input);
-    (this+mark2Coverage).add_coverage (c->input);
+    if (unlikely (!(this+mark1Coverage).add_coverage (c->input))) return;
+    if (unlikely (!(this+mark2Coverage).add_coverage (c->input))) return;
   }
 
   inline const Coverage &get_coverage (void) const
@@ -1283,7 +1283,7 @@
     return this+mark1Coverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
@@ -1291,7 +1291,7 @@
     if (likely (mark1_index == NOT_COVERED)) return_trace (false);
 
     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
-    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags);
     if (!skippy_iter.prev ()) return_trace (false);
@@ -1338,7 +1338,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		mark1Coverage;		/* Offset to Combining Mark1 Coverage
 					 * table--from beginning of MarkMarkPos
@@ -1347,7 +1347,7 @@
 		mark2Coverage;		/* Offset to Combining Mark2 Coverage
 					 * table--from beginning of MarkMarkPos
 					 * subtable */
-  USHORT	classCount;		/* Number of defined mark classes */
+  HBUINT16	classCount;		/* Number of defined mark classes */
   OffsetTo<MarkArray>
 		mark1Array;		/* Offset to Mark1Array table--from
 					 * beginning of MarkMarkPos subtable */
@@ -1373,7 +1373,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   MarkMarkPosFormat1	format1;
   } u;
 };
@@ -1432,7 +1432,7 @@
 
   protected:
   union {
-  USHORT		sub_format;
+  HBUINT16		sub_format;
   SinglePos		single;
   PairPos		pair;
   CursivePos		cursive;
@@ -1458,7 +1458,7 @@
     return false;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     return_trace (dispatch (c));
@@ -1477,7 +1477,7 @@
     dispatch (&c);
   }
 
-  static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index);
+  static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
 
   template <typename context_t>
   static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
@@ -1629,7 +1629,7 @@
   return l.dispatch (c);
 }
 
-/*static*/ inline bool PosLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index)
+/*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)->gpos);
   const PosLookup &l = gpos.get_lookup (lookup_index);
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 66fcb3f..97f1d21 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -44,7 +44,7 @@
     for (iter.init (this+coverage); iter.more (); iter.next ())
     {
       /* TODO Switch to range-based API to work around malicious fonts.
-       * https://github.com/behdad/harfbuzz/issues/363 */
+       * https://github.com/harfbuzz/harfbuzz/issues/363 */
       hb_codepoint_t glyph_id = iter.get_glyph ();
       if (c->glyphs->has (glyph_id))
 	c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
@@ -54,13 +54,13 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     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 ())
     {
       /* TODO Switch to range-based API to work around malicious fonts.
-       * https://github.com/behdad/harfbuzz/issues/363 */
+       * https://github.com/harfbuzz/harfbuzz/issues/363 */
       hb_codepoint_t glyph_id = iter.get_glyph ();
-      c->input->add (glyph_id);
       c->output->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
     }
   }
@@ -76,7 +76,7 @@
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
@@ -110,11 +110,11 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
-  SHORT		deltaGlyphID;		/* Add to original GlyphID to get
+  HBINT16		deltaGlyphID;		/* Add to original GlyphID to get
 					 * substitute GlyphID */
   public:
   DEFINE_SIZE_STATIC (6);
@@ -130,7 +130,7 @@
     for (iter.init (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
+        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
 	c->glyphs->add (substitute[iter.get_coverage ()]);
     }
@@ -139,13 +139,13 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     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 ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
-      c->input->add (iter.get_glyph ());
+        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       c->output->add (substitute[iter.get_coverage ()]);
     }
   }
@@ -161,7 +161,7 @@
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
@@ -195,7 +195,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 2 */
+  HBUINT16	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
@@ -249,7 +249,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   SingleSubstFormat1	format1;
   SingleSubstFormat2	format2;
   } u;
@@ -269,12 +269,10 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    unsigned int count = substitute.len;
-    for (unsigned int i = 0; i < count; i++)
-      c->output->add (substitute[i]);
+    c->output->add_array (substitute.array, substitute.len);
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int count = substitute.len;
@@ -287,7 +285,7 @@
       return_trace (true);
     }
     /* Spec disallows this, but Uniscribe allows it.
-     * https://github.com/behdad/harfbuzz/issues/253 */
+     * https://github.com/harfbuzz/harfbuzz/issues/253 */
     else if (unlikely (count == 0))
     {
       c->buffer->delete_glyph ();
@@ -339,7 +337,7 @@
     for (iter.init (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
+        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
 	(this+sequence[iter.get_coverage ()]).closure (c);
     }
@@ -348,7 +346,7 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    (this+coverage).add_coverage (c->input);
+    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);
@@ -365,7 +363,7 @@
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
@@ -388,7 +386,7 @@
       if (unlikely (!sequence[i].serialize (c, this).serialize (c,
 								substitute_glyphs_list,
 								substitute_len_list[i]))) return_trace (false);
-    substitute_len_list.advance (num_glyphs);
+    substitute_len_list += num_glyphs;
     if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
     return_trace (true);
   }
@@ -400,7 +398,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
@@ -442,7 +440,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   MultipleSubstFormat1	format1;
   } u;
 };
@@ -461,7 +459,7 @@
     for (iter.init (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
+        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;
@@ -474,17 +472,15 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     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 ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
-      c->input->add (iter.get_glyph ());
+        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
-      unsigned int count = alt_set.len;
-      for (unsigned int i = 0; i < count; i++)
-	c->output->add (alt_set[i]);
+      c->output->add_array (alt_set.array, alt_set.len);
     }
   }
 
@@ -499,7 +495,7 @@
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
@@ -540,7 +536,7 @@
       if (unlikely (!alternateSet[i].serialize (c, this).serialize (c,
 								    alternate_glyphs_list,
 								    alternate_len_list[i]))) return_trace (false);
-    alternate_len_list.advance (num_glyphs);
+    alternate_len_list += num_glyphs;
     if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
     return_trace (true);
   }
@@ -552,7 +548,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
@@ -594,7 +590,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   AlternateSubstFormat1	format1;
   } u;
 };
@@ -615,9 +611,7 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    unsigned int count = component.len;
-    for (unsigned int i = 1; i < count; i++)
-      c->input->add (component[i]);
+    c->input->add_array (component.array, component.len ? component.len - 1 : 0);
     c->output->add (ligGlyph);
   }
 
@@ -634,7 +628,7 @@
     return_trace (true);
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int count = component.len;
@@ -658,7 +652,7 @@
     if (likely (!match_input (c, count,
 			      &component[1],
 			      match_glyph,
-			      NULL,
+			      nullptr,
 			      &match_length,
 			      match_positions,
 			      &is_mark_ligature,
@@ -736,7 +730,7 @@
     return_trace (false);
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int num_ligs = ligature.len;
@@ -763,8 +757,8 @@
 								ligatures[i],
 								component_list,
 								component_count_list[i]))) return_trace (false);
-    ligatures.advance (num_ligatures);
-    component_count_list.advance (num_ligatures);
+    ligatures += num_ligatures;
+    component_count_list += num_ligatures;
     return_trace (true);
   }
 
@@ -792,7 +786,7 @@
     for (iter.init (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
+        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
 	(this+ligatureSet[iter.get_coverage ()]).closure (c);
     }
@@ -801,13 +795,13 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     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 ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
-      c->input->add (iter.get_glyph ());
+        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       (this+ligatureSet[iter.get_coverage ()]).collect_glyphs (c);
     }
   }
@@ -827,7 +821,7 @@
     return_trace (lig_set.would_apply (c));
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
@@ -856,7 +850,7 @@
 								   component_count_list,
 								   ligature_per_first_glyph_count_list[i],
 								   component_list))) return_trace (false);
-    ligature_per_first_glyph_count_list.advance (num_first_glyphs);
+    ligature_per_first_glyph_count_list += num_first_glyphs;
     if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return_trace (false);
     return_trace (true);
   }
@@ -868,7 +862,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of Substitution table */
@@ -918,7 +912,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   LigatureSubstFormat1	format1;
   } u;
 };
@@ -961,7 +955,7 @@
     for (iter.init (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/behdad/harfbuzz/issues/363 */
+        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
 	c->glyphs->add (substitute[iter.get_coverage ()]);
     }
@@ -970,25 +964,22 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-
-    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+    if (unlikely (!(this+coverage).add_coverage (c->input))) return;
 
     unsigned int count;
 
-    (this+coverage).add_coverage (c->input);
-
     count = backtrack.len;
     for (unsigned int i = 0; i < count; i++)
-      (this+backtrack[i]).add_coverage (c->before);
+      if (unlikely (!(this+backtrack[i]).add_coverage (c->before))) return;
 
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     count = lookahead.len;
     for (unsigned int i = 0; i < count; i++)
-      (this+lookahead[i]).add_coverage (c->after);
+      if (unlikely (!(this+lookahead[i]).add_coverage (c->after))) return;
 
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
     count = substitute.len;
-    for (unsigned int i = 0; i < count; i++)
-      c->output->add (substitute[i]);
+    c->output->add_array (substitute.array, substitute.len);
   }
 
   inline const Coverage &get_coverage (void) const
@@ -1002,7 +993,7 @@
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
@@ -1014,14 +1005,17 @@
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
 
+  unsigned int start_index = 0, end_index = 0;
     if (match_backtrack (c,
-			 backtrack.len, (USHORT *) backtrack.array,
-			 match_coverage, this) &&
-        match_lookahead (c,
-			 lookahead.len, (USHORT *) lookahead.array,
+			 backtrack.len, (HBUINT16 *) backtrack.array,
 			 match_coverage, this,
-			 1))
+			 &start_index) &&
+        match_lookahead (c,
+			 lookahead.len, (HBUINT16 *) lookahead.array,
+			 match_coverage, this,
+			 1, &end_index))
     {
+      c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
       c->replace_glyph_inplace (substitute[index]);
       /* Note: We DON'T decrease buffer->idx.  The main loop does it
        * for us.  This is useful for preventing surprises if someone
@@ -1045,7 +1039,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
@@ -1079,7 +1073,7 @@
 
   protected:
   union {
-  USHORT				format;		/* Format identifier */
+  HBUINT16				format;		/* Format identifier */
   ReverseChainSingleSubstFormat1	format1;
   } u;
 };
@@ -1125,7 +1119,7 @@
 
   protected:
   union {
-  USHORT			sub_format;
+  HBUINT16			sub_format;
   SingleSubst			single;
   MultipleSubst			multiple;
   AlternateSubst		alternate;
@@ -1156,7 +1150,7 @@
     return lookup_type_is_reverse (type);
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     return_trace (dispatch (c));
@@ -1192,7 +1186,7 @@
       return_trace (dispatch (c));
   }
 
-  static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index);
+  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)
@@ -1277,8 +1271,9 @@
     if (unlikely (get_type () == SubstLookupSubTable::Extension))
     {
       /* The spec says all subtables of an Extension lookup should
-       * have the same type.  This is specially important if one has
-       * a reverse type! */
+       * 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++)
@@ -1348,7 +1343,7 @@
   return l.dispatch (c);
 }
 
-/*static*/ inline bool SubstLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index)
+/*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)->gsub);
   const SubstLookup &l = gsub.get_lookup (lookup_index);
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 0c42352..9054634 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -29,6 +29,8 @@
 #ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
 #define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
 
+#include "hb-private.hh"
+#include "hb-debug.hh"
 #include "hb-buffer-private.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-set-private.hh"
@@ -37,15 +39,6 @@
 namespace OT {
 
 
-#ifndef HB_DEBUG_CLOSURE
-#define HB_DEBUG_CLOSURE (HB_DEBUG+0)
-#endif
-
-#define TRACE_CLOSURE(this) \
-	hb_auto_trace_t<HB_DEBUG_CLOSURE, hb_void_t> trace \
-	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
-	 "");
-
 struct hb_closure_context_t :
        hb_dispatch_context_t<hb_closure_context_t, hb_void_t, HB_DEBUG_CLOSURE>
 {
@@ -77,7 +70,7 @@
 		        unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
 			  face (face_),
 			  glyphs (glyphs_),
-			  recurse_func (NULL),
+			  recurse_func (nullptr),
 			  nesting_level_left (nesting_level_left_),
 			  debug_depth (0) {}
 
@@ -85,16 +78,6 @@
 };
 
 
-
-#ifndef HB_DEBUG_WOULD_APPLY
-#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
-#endif
-
-#define TRACE_WOULD_APPLY(this) \
-	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY, bool> trace \
-	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
-	 "%d glyphs", c->len);
-
 struct hb_would_apply_context_t :
        hb_dispatch_context_t<hb_would_apply_context_t, bool, HB_DEBUG_WOULD_APPLY>
 {
@@ -122,16 +105,6 @@
 };
 
 
-
-#ifndef HB_DEBUG_COLLECT_GLYPHS
-#define HB_DEBUG_COLLECT_GLYPHS (HB_DEBUG+0)
-#endif
-
-#define TRACE_COLLECT_GLYPHS(this) \
-	hb_auto_trace_t<HB_DEBUG_COLLECT_GLYPHS, hb_void_t> trace \
-	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
-	 "");
-
 struct hb_collect_glyphs_context_t :
        hb_dispatch_context_t<hb_collect_glyphs_context_t, hb_void_t, HB_DEBUG_COLLECT_GLYPHS>
 {
@@ -146,7 +119,7 @@
     if (unlikely (nesting_level_left == 0 || !recurse_func))
       return default_return_value ();
 
-    /* Note that GPOS sets recurse_func to NULL already, so it doesn't get
+    /* 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
      * glyphs in the recursion.  If output is not requested, we can go home now.
      *
@@ -160,7 +133,7 @@
       return HB_VOID;
 
     /* Return if new lookup was recursed to before. */
-    if (recursed_lookups.has (lookup_index))
+    if (recursed_lookups->has (lookup_index))
       return HB_VOID;
 
     hb_set_t *old_before = before;
@@ -176,7 +149,7 @@
     input  = old_input;
     after  = old_after;
 
-    recursed_lookups.add (lookup_index);
+    recursed_lookups->add (lookup_index);
 
     return HB_VOID;
   }
@@ -187,31 +160,31 @@
   hb_set_t *after;
   hb_set_t *output;
   recurse_func_t recurse_func;
-  hb_set_t recursed_lookups;
+  hb_set_t *recursed_lookups;
   unsigned int nesting_level_left;
   unsigned int debug_depth;
 
   hb_collect_glyphs_context_t (hb_face_t *face_,
-			       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 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 */
 			       unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
 			      face (face_),
 			      before (glyphs_before ? glyphs_before : hb_set_get_empty ()),
 			      input  (glyphs_input  ? glyphs_input  : hb_set_get_empty ()),
 			      after  (glyphs_after  ? glyphs_after  : hb_set_get_empty ()),
 			      output (glyphs_output ? glyphs_output : hb_set_get_empty ()),
-			      recurse_func (NULL),
-			      recursed_lookups (),
+			      recurse_func (nullptr),
+			      recursed_lookups (nullptr),
 			      nesting_level_left (nesting_level_left_),
 			      debug_depth (0)
   {
-    recursed_lookups.init ();
+    recursed_lookups = hb_set_create ();
   }
   ~hb_collect_glyphs_context_t (void)
   {
-    recursed_lookups.fini ();
+    hb_set_destroy (recursed_lookups);
   }
 
   void set_recurse_func (recurse_func_t func) { recurse_func = func; }
@@ -219,10 +192,6 @@
 
 
 
-#ifndef HB_DEBUG_GET_COVERAGE
-#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0)
-#endif
-
 /* XXX Can we remove this? */
 
 template <typename set_t>
@@ -249,19 +218,8 @@
 };
 
 
-
-#ifndef HB_DEBUG_APPLY
-#define HB_DEBUG_APPLY (HB_DEBUG+0)
-#endif
-
-#define TRACE_APPLY(this) \
-	hb_auto_trace_t<HB_DEBUG_APPLY, bool> trace \
-	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
-	 "idx %d gid %u lookup %d", \
-	 c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index);
-
-struct hb_apply_context_t :
-       hb_dispatch_context_t<hb_apply_context_t, bool, HB_DEBUG_APPLY>
+struct hb_ot_apply_context_t :
+       hb_dispatch_context_t<hb_ot_apply_context_t, bool, HB_DEBUG_APPLY>
 {
   struct matcher_t
   {
@@ -273,10 +231,10 @@
 #define arg1(arg) (arg) /* Remove the macro to see why it's needed! */
 	     syllable arg1(0),
 #undef arg1
-	     match_func (NULL),
-	     match_data (NULL) {};
+	     match_func (nullptr),
+	     match_data (nullptr) {};
 
-    typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
+    typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data);
 
     inline void set_ignore_zwnj (bool ignore_zwnj_) { ignore_zwnj = ignore_zwnj_; }
     inline void set_ignore_zwj (bool ignore_zwj_) { ignore_zwj = ignore_zwj_; }
@@ -294,7 +252,7 @@
     };
 
     inline may_match_t may_match (const hb_glyph_info_t &info,
-				  const USHORT          *glyph_data) const
+				  const HBUINT16          *glyph_data) const
     {
       if (!(info.mask & mask) ||
 	  (syllable && syllable != info.syllable ()))
@@ -313,13 +271,13 @@
     };
 
     inline may_skip_t
-    may_skip (const hb_apply_context_t *c,
+    may_skip (const hb_ot_apply_context_t *c,
 	      const hb_glyph_info_t    &info) const
     {
       if (!c->check_glyph_property (&info, lookup_props))
 	return SKIP_YES;
 
-      if (unlikely (_hb_glyph_info_is_default_ignorable_and_not_fvs (&info) &&
+      if (unlikely (_hb_glyph_info_is_default_ignorable_and_not_hidden (&info) &&
 		    (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) &&
 		    (ignore_zwj || !_hb_glyph_info_is_zwj (&info))))
 	return SKIP_MAYBE;
@@ -339,16 +297,16 @@
 
   struct skipping_iterator_t
   {
-    inline void init (hb_apply_context_t *c_, bool context_match = false)
+    inline void init (hb_ot_apply_context_t *c_, bool context_match = false)
     {
       c = c_;
-      match_glyph_data = NULL,
-      matcher.set_match_func (NULL, NULL);
+      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. */
-      matcher.set_ignore_zwnj (context_match || c->table_index == 1);
+      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 (context_match || c->table_index == 1 || c->auto_zwj);
+      matcher.set_ignore_zwj  (c->table_index == 1 || (context_match || c->auto_zwj));
       matcher.set_mask (context_match ? -1 : c->lookup_mask);
     }
     inline void set_lookup_props (unsigned int lookup_props)
@@ -357,7 +315,7 @@
     }
     inline void set_match_func (matcher_t::match_func_t match_func_,
 				const void *match_data_,
-				const USHORT glyph_data[])
+				const HBUINT16 glyph_data[])
     {
       matcher.set_match_func (match_func_, match_data_);
       match_glyph_data = glyph_data;
@@ -374,6 +332,12 @@
 
     inline void reject (void) { num_items++; match_glyph_data--; }
 
+    inline matcher_t::may_skip_t
+    may_skip (const hb_glyph_info_t    &info) const
+    {
+      return matcher.may_skip (c, info);
+    }
+
     inline bool next (void)
     {
       assert (num_items > 0);
@@ -431,9 +395,9 @@
 
     unsigned int idx;
     protected:
-    hb_apply_context_t *c;
+    hb_ot_apply_context_t *c;
     matcher_t matcher;
-    const USHORT *match_glyph_data;
+    const HBUINT16 *match_glyph_data;
 
     unsigned int num_items;
     unsigned int end;
@@ -441,61 +405,66 @@
 
 
   inline const char *get_name (void) { return "APPLY"; }
-  typedef return_t (*recurse_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
+  typedef return_t (*recurse_func_t) (hb_ot_apply_context_t *c, unsigned int lookup_index);
   template <typename T>
   inline return_t dispatch (const T &obj) { return obj.apply (this); }
   static return_t default_return_value (void) { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
-  return_t recurse (unsigned int lookup_index)
+  return_t recurse (unsigned int sub_lookup_index)
   {
-    if (unlikely (nesting_level_left == 0 || !recurse_func))
+    if (unlikely (nesting_level_left == 0 || !recurse_func || buffer->max_ops-- <= 0))
       return default_return_value ();
 
     nesting_level_left--;
-    bool ret = recurse_func (this, lookup_index);
+    bool ret = recurse_func (this, sub_lookup_index);
     nesting_level_left++;
     return ret;
   }
 
-  unsigned int table_index; /* GSUB/GPOS */
+  skipping_iterator_t iter_input, iter_context;
+
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t *buffer;
+  recurse_func_t recurse_func;
+  const GDEF &gdef;
+  const VariationStore &var_store;
+
   hb_direction_t direction;
   hb_mask_t lookup_mask;
-  bool auto_zwj;
-  recurse_func_t recurse_func;
-  unsigned int nesting_level_left;
-  unsigned int lookup_props;
-  const GDEF &gdef;
-  bool has_glyph_classes;
-  const VariationStore &var_store;
-  skipping_iterator_t iter_input, iter_context;
+  unsigned int table_index; /* GSUB/GPOS */
   unsigned int lookup_index;
+  unsigned int lookup_props;
+  unsigned int nesting_level_left;
   unsigned int debug_depth;
 
+  bool auto_zwnj;
+  bool auto_zwj;
+  bool has_glyph_classes;
 
-  hb_apply_context_t (unsigned int table_index_,
+
+  hb_ot_apply_context_t (unsigned int table_index_,
 		      hb_font_t *font_,
 		      hb_buffer_t *buffer_) :
-			table_index (table_index_),
+			iter_input (), iter_context (),
 			font (font_), face (font->face), buffer (buffer_),
+			recurse_func (nullptr),
+			gdef (*hb_ot_layout_from_face (face)->gdef),
+			var_store (gdef.get_var_store ()),
 			direction (buffer_->props.direction),
 			lookup_mask (1),
-			auto_zwj (true),
-			recurse_func (NULL),
-			nesting_level_left (HB_MAX_NESTING_LEVEL),
-			lookup_props (0),
-			gdef (*hb_ot_layout_from_face (face)->gdef),
-			has_glyph_classes (gdef.has_glyph_classes ()),
-			var_store (gdef.get_var_store ()),
-			iter_input (),
-			iter_context (),
+			table_index (table_index_),
 			lookup_index ((unsigned int) -1),
-			debug_depth (0) {}
+			lookup_props (0),
+			nesting_level_left (HB_MAX_NESTING_LEVEL),
+			debug_depth (0),
+			auto_zwnj (true),
+			auto_zwj (true),
+			has_glyph_classes (gdef.has_glyph_classes ()) {}
 
   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_)
@@ -598,9 +567,9 @@
 
 
 
-typedef bool (*intersects_func_t) (hb_set_t *glyphs, const USHORT &value, const void *data);
-typedef void (*collect_glyphs_func_t) (hb_set_t *glyphs, const USHORT &value, const void *data);
-typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
+typedef bool (*intersects_func_t) (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);
 
 struct ContextClosureFuncs
 {
@@ -616,16 +585,16 @@
 };
 
 
-static inline bool intersects_glyph (hb_set_t *glyphs, const USHORT &value, const void *data HB_UNUSED)
+static inline bool intersects_glyph (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 USHORT &value, const void *data)
+static inline bool intersects_class (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 USHORT &value, const void *data)
+static inline bool intersects_coverage (hb_set_t *glyphs, const HBUINT16 &value, const void *data)
 {
   const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
   return (data+coverage).intersects (glyphs);
@@ -633,7 +602,7 @@
 
 static inline bool intersects_array (hb_closure_context_t *c,
 				     unsigned int count,
-				     const USHORT values[],
+				     const HBUINT16 values[],
 				     intersects_func_t intersects_func,
 				     const void *intersects_data)
 {
@@ -644,16 +613,16 @@
 }
 
 
-static inline void collect_glyph (hb_set_t *glyphs, const USHORT &value, const void *data HB_UNUSED)
+static inline void collect_glyph (hb_set_t *glyphs, const HBUINT16 &value, const void *data HB_UNUSED)
 {
   glyphs->add (value);
 }
-static inline void collect_class (hb_set_t *glyphs, const USHORT &value, const void *data)
+static inline void collect_class (hb_set_t *glyphs, const HBUINT16 &value, const void *data)
 {
   const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
   class_def.add_class (glyphs, value);
 }
-static inline void collect_coverage (hb_set_t *glyphs, const USHORT &value, const void *data)
+static inline void collect_coverage (hb_set_t *glyphs, const HBUINT16 &value, const void *data)
 {
   const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
   (data+coverage).add_coverage (glyphs);
@@ -661,7 +630,7 @@
 static inline void collect_array (hb_collect_glyphs_context_t *c HB_UNUSED,
 				  hb_set_t *glyphs,
 				  unsigned int count,
-				  const USHORT values[],
+				  const HBUINT16 values[],
 				  collect_glyphs_func_t collect_func,
 				  const void *collect_data)
 {
@@ -670,16 +639,16 @@
 }
 
 
-static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
+static inline bool match_glyph (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data HB_UNUSED)
 {
   return glyph_id == value;
 }
-static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
+static inline bool match_class (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data)
 {
   const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
   return class_def.get_class (glyph_id) == value;
 }
-static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
+static inline bool match_coverage (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data)
 {
   const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
   return (data+coverage).get_coverage (glyph_id) != NOT_COVERED;
@@ -687,7 +656,7 @@
 
 static inline bool would_match_input (hb_would_apply_context_t *c,
 				      unsigned int count, /* Including the first glyph (not matched) */
-				      const USHORT input[], /* Array of input values--start with second glyph */
+				      const HBUINT16 input[], /* Array of input values--start with second glyph */
 				      match_func_t match_func,
 				      const void *match_data)
 {
@@ -700,23 +669,23 @@
 
   return true;
 }
-static inline bool match_input (hb_apply_context_t *c,
+static inline bool match_input (hb_ot_apply_context_t *c,
 				unsigned int count, /* Including the first glyph (not matched) */
-				const USHORT input[], /* Array of input values--start with second glyph */
+				const HBUINT16 input[], /* Array of input values--start with second glyph */
 				match_func_t match_func,
 				const void *match_data,
 				unsigned int *end_offset,
 				unsigned int match_positions[HB_MAX_CONTEXT_LENGTH],
-				bool *p_is_mark_ligature = NULL,
-				unsigned int *p_total_component_count = NULL)
+				bool *p_is_mark_ligature = nullptr,
+				unsigned int *p_total_component_count = nullptr)
 {
-  TRACE_APPLY (NULL);
+  TRACE_APPLY (nullptr);
 
   if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return_trace (false);
 
   hb_buffer_t *buffer = c->buffer;
 
-  hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+  hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
   skippy_iter.reset (buffer->idx, count - 1);
   skippy_iter.set_match_func (match_func, match_data, input);
 
@@ -731,11 +700,17 @@
    * - Ligatures cannot be formed across glyphs attached to different components
    *   of previous ligatures.  Eg. the sequence is LAM,SHADDA,LAM,FATHA,HEH, and
    *   LAM,LAM,HEH form a ligature, leaving SHADDA,FATHA next to eachother.
-   *   However, it would be wrong to ligate that SHADDA,FATHA sequence.o
-   *   There is an exception to this: If a ligature tries ligating with marks that
-   *   belong to it itself, go ahead, assuming that the font designer knows what
-   *   they are doing (otherwise it can break Indic stuff when a matra wants to
-   *   ligate with a conjunct...)
+   *   However, it would be wrong to ligate that SHADDA,FATHA sequence.
+   *   There are a couple of exceptions to this:
+   *
+   *   o If a ligature tries ligating with marks that belong to it itself, go ahead,
+   *     assuming that the font designer knows what they are doing (otherwise it can
+   *     break Indic stuff when a matra wants to ligate with a conjunct,
+   *
+   *   o If two marks want to ligate and they belong to different components of the
+   *     same ligature glyph, and said ligature glyph is to be ignored according to
+   *     mark-filtering rules, then allow.
+   *     https://github.com/harfbuzz/harfbuzz/issues/545
    */
 
   bool is_mark_ligature = _hb_glyph_info_is_mark (&buffer->cur());
@@ -746,6 +721,12 @@
   unsigned int first_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur());
   unsigned int first_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
 
+  enum {
+    LIGBASE_NOT_CHECKED,
+    LIGBASE_MAY_NOT_SKIP,
+    LIGBASE_MAY_SKIP
+  } ligbase = LIGBASE_NOT_CHECKED;
+
   match_positions[0] = buffer->idx;
   for (unsigned int i = 1; i < count; i++)
   {
@@ -756,13 +737,43 @@
     unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]);
     unsigned int this_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]);
 
-    if (first_lig_id && first_lig_comp) {
+    if (first_lig_id && first_lig_comp)
+    {
       /* If first component was attached to a previous ligature component,
        * all subsequent components should be attached to the same ligature
-       * component, otherwise we shouldn't ligate them. */
+       * component, otherwise we shouldn't ligate them... */
       if (first_lig_id != this_lig_id || first_lig_comp != this_lig_comp)
-	return_trace (false);
-    } else {
+      {
+        /* ...unless, we are attached to a base ligature and that base
+	 * ligature is ignorable. */
+        if (ligbase == LIGBASE_NOT_CHECKED)
+	{
+	  bool found = false;
+	  const hb_glyph_info_t *out = buffer->out_info;
+	  unsigned int j = buffer->out_len;
+	  while (j && _hb_glyph_info_get_lig_id (&out[j - 1]) == first_lig_id)
+	  {
+	    if (_hb_glyph_info_get_lig_comp (&out[j - 1]) == 0)
+	    {
+	      j--;
+	      found = true;
+	      break;
+	    }
+	    j--;
+	  }
+
+	  if (found && skippy_iter.may_skip (out[j]) == hb_ot_apply_context_t::matcher_t::SKIP_YES)
+	    ligbase = LIGBASE_MAY_SKIP;
+	  else
+	    ligbase = LIGBASE_MAY_NOT_SKIP;
+	}
+
+        if (ligbase == LIGBASE_MAY_NOT_SKIP)
+	  return_trace (false);
+      }
+    }
+    else
+    {
       /* If first component was NOT attached to a previous ligature component,
        * all subsequent components should also NOT be attached to any ligature
        * component, unless they are attached to the first component itself! */
@@ -784,7 +795,7 @@
 
   return_trace (true);
 }
-static inline bool ligate_input (hb_apply_context_t *c,
+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 */
 				 unsigned int match_length,
@@ -792,7 +803,7 @@
 				 bool is_mark_ligature,
 				 unsigned int total_component_count)
 {
-  TRACE_APPLY (NULL);
+  TRACE_APPLY (nullptr);
 
   hb_buffer_t *buffer = c->buffer;
 
@@ -882,15 +893,16 @@
   return_trace (true);
 }
 
-static inline bool match_backtrack (hb_apply_context_t *c,
+static inline bool match_backtrack (hb_ot_apply_context_t *c,
 				    unsigned int count,
-				    const USHORT backtrack[],
+				    const HBUINT16 backtrack[],
 				    match_func_t match_func,
-				    const void *match_data)
+				    const void *match_data,
+				    unsigned int *match_start)
 {
-  TRACE_APPLY (NULL);
+  TRACE_APPLY (nullptr);
 
-  hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
+  hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
   skippy_iter.reset (c->buffer->backtrack_len (), count);
   skippy_iter.set_match_func (match_func, match_data, backtrack);
 
@@ -898,19 +910,22 @@
     if (!skippy_iter.prev ())
       return_trace (false);
 
+  *match_start = skippy_iter.idx;
+
   return_trace (true);
 }
 
-static inline bool match_lookahead (hb_apply_context_t *c,
+static inline bool match_lookahead (hb_ot_apply_context_t *c,
 				    unsigned int count,
-				    const USHORT lookahead[],
+				    const HBUINT16 lookahead[],
 				    match_func_t match_func,
 				    const void *match_data,
-				    unsigned int offset)
+				    unsigned int offset,
+				    unsigned int *end_index)
 {
-  TRACE_APPLY (NULL);
+  TRACE_APPLY (nullptr);
 
-  hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
+  hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
   skippy_iter.reset (c->buffer->idx + offset - 1, count);
   skippy_iter.set_match_func (match_func, match_data, lookahead);
 
@@ -918,6 +933,8 @@
     if (!skippy_iter.next ())
       return_trace (false);
 
+  *end_index = skippy_iter.idx + 1;
+
   return_trace (true);
 }
 
@@ -931,9 +948,9 @@
     return_trace (c->check_struct (this));
   }
 
-  USHORT	sequenceIndex;		/* Index into current glyph
+  HBUINT16	sequenceIndex;		/* Index into current glyph
 					 * sequence--first glyph = 0 */
-  USHORT	lookupListIndex;	/* Lookup to apply to that
+  HBUINT16	lookupListIndex;	/* Lookup to apply to that
 					 * position--zero--based */
   public:
   DEFINE_SIZE_STATIC (4);
@@ -949,14 +966,14 @@
     c->recurse (lookupRecord[i].lookupListIndex);
 }
 
-static inline bool apply_lookup (hb_apply_context_t *c,
+static inline bool apply_lookup (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 */
 				 unsigned int lookupCount,
 				 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
 				 unsigned int match_length)
 {
-  TRACE_APPLY (NULL);
+  TRACE_APPLY (nullptr);
 
   hb_buffer_t *buffer = c->buffer;
   int end;
@@ -984,7 +1001,11 @@
     if (idx == 0 && lookupRecord[i].lookupListIndex == c->lookup_index)
       continue;
 
-    buffer->move_to (match_positions[idx]);
+    if (unlikely (!buffer->move_to (match_positions[idx])))
+      break;
+
+    if (unlikely (buffer->max_ops <= 0))
+      break;
 
     unsigned int orig_len = buffer->backtrack_len () + buffer->lookahead_len ();
     if (!c->recurse (lookupRecord[i].lookupListIndex))
@@ -996,7 +1017,29 @@
     if (!delta)
         continue;
 
-    /* Recursed lookup changed buffer len.  Adjust. */
+    /* Recursed lookup changed buffer len.  Adjust.
+     *
+     * TODO:
+     *
+     * Right now, if buffer length increased by n, we assume n new glyphs
+     * were added right after the current position, and if buffer length
+     * was decreased by n, we assume n match positions after the current
+     * one where removed.  The former (buffer length increased) case is
+     * fine, but the decrease case can be improved in at least two ways,
+     * both of which are significant:
+     *
+     *   - If recursed-to lookup is MultipleSubst and buffer length
+     *     decreased, then it's current match position that was deleted,
+     *     NOT the one after it.
+     *
+     *   - If buffer length was decreased by n, it does not necessarily
+     *     mean that n match positions where removed, as there might
+     *     have been marks and default-ignorables in the sequence.  We
+     *     should instead drop match positions between current-position
+     *     and current-position + n instead.
+     *
+     * It should be possible to construct tests for both of these cases.
+     */
 
     end += delta;
     if (end <= int (match_positions[idx]))
@@ -1068,7 +1111,7 @@
 
 static inline void context_closure_lookup (hb_closure_context_t *c,
 					   unsigned int inputCount, /* Including the first glyph (not matched) */
-					   const USHORT input[], /* Array of input values--start with second glyph */
+					   const HBUINT16 input[], /* Array of input values--start with second glyph */
 					   unsigned int lookupCount,
 					   const LookupRecord lookupRecord[],
 					   ContextClosureLookupContext &lookup_context)
@@ -1082,7 +1125,7 @@
 
 static inline void context_collect_glyphs_lookup (hb_collect_glyphs_context_t *c,
 						  unsigned int inputCount, /* Including the first glyph (not matched) */
-						  const USHORT input[], /* Array of input values--start with second glyph */
+						  const HBUINT16 input[], /* Array of input values--start with second glyph */
 						  unsigned int lookupCount,
 						  const LookupRecord lookupRecord[],
 						  ContextCollectGlyphsLookupContext &lookup_context)
@@ -1096,7 +1139,7 @@
 
 static inline bool context_would_apply_lookup (hb_would_apply_context_t *c,
 					       unsigned int inputCount, /* Including the first glyph (not matched) */
-					       const USHORT input[], /* Array of input values--start with second glyph */
+					       const HBUINT16 input[], /* Array of input values--start with second glyph */
 					       unsigned int lookupCount HB_UNUSED,
 					       const LookupRecord lookupRecord[] HB_UNUSED,
 					       ContextApplyLookupContext &lookup_context)
@@ -1105,9 +1148,9 @@
 			    inputCount, input,
 			    lookup_context.funcs.match, lookup_context.match_data);
 }
-static inline bool context_apply_lookup (hb_apply_context_t *c,
+static inline bool context_apply_lookup (hb_ot_apply_context_t *c,
 					 unsigned int inputCount, /* Including the first glyph (not matched) */
-					 const USHORT input[], /* Array of input values--start with second glyph */
+					 const HBUINT16 input[], /* Array of input values--start with second glyph */
 					 unsigned int lookupCount,
 					 const LookupRecord lookupRecord[],
 					 ContextApplyLookupContext &lookup_context)
@@ -1118,10 +1161,11 @@
 		      inputCount, input,
 		      lookup_context.funcs.match, lookup_context.match_data,
 		      &match_length, match_positions)
-      && apply_lookup (c,
+      && (c->buffer->unsafe_to_break (c->buffer->idx, c->buffer->idx + match_length),
+	  apply_lookup (c,
 		       inputCount, match_positions,
 		       lookupCount, lookupRecord,
-		       match_length);
+		       match_length));
 }
 
 struct Rule
@@ -1153,7 +1197,7 @@
     return_trace (context_would_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context));
   }
 
-  inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
+  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));
@@ -1164,24 +1208,24 @@
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return inputCount.sanitize (c)
-	&& lookupCount.sanitize (c)
-	&& c->check_range (inputZ,
-			   inputZ[0].static_size * inputCount
-			   + lookupRecordX[0].static_size * lookupCount);
+    return_trace (inputCount.sanitize (c) &&
+		  lookupCount.sanitize (c) &&
+		  c->check_range (inputZ,
+				  inputZ[0].static_size * inputCount +
+				  LookupRecord::static_size * lookupCount));
   }
 
   protected:
-  USHORT	inputCount;		/* Total number of glyphs in input
+  HBUINT16	inputCount;		/* Total number of glyphs in input
 					 * glyph sequence--includes the first
 					 * glyph */
-  USHORT	lookupCount;		/* Number of LookupRecords */
-  USHORT	inputZ[VAR];		/* Array of match inputs--start with
+  HBUINT16	lookupCount;		/* Number of LookupRecords */
+  HBUINT16	inputZ[VAR];		/* Array of match inputs--start with
 					 * second glyph */
-  LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
+/*LookupRecord	lookupRecordX[VAR];*/	/* Array of LookupRecords--in
 					 * design order */
   public:
-  DEFINE_SIZE_ARRAY2 (4, inputZ, lookupRecordX);
+  DEFINE_SIZE_ARRAY (4, inputZ);
 };
 
 struct RuleSet
@@ -1214,7 +1258,7 @@
     return_trace (false);
   }
 
-  inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
+  inline bool apply (hb_ot_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
     unsigned int num_rules = rule.len;
@@ -1251,7 +1295,7 @@
 
     struct ContextClosureLookupContext lookup_context = {
       {intersects_glyph},
-      NULL
+      nullptr
     };
 
     unsigned int count = ruleSet.len;
@@ -1269,7 +1313,7 @@
 
     struct ContextCollectGlyphsLookupContext lookup_context = {
       {collect_glyph},
-      NULL
+      nullptr
     };
 
     unsigned int count = ruleSet.len;
@@ -1284,7 +1328,7 @@
     const RuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])];
     struct ContextApplyLookupContext lookup_context = {
       {match_glyph},
-      NULL
+      nullptr
     };
     return_trace (rule_set.would_apply (c, lookup_context));
   }
@@ -1294,7 +1338,7 @@
     return this+coverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
@@ -1304,7 +1348,7 @@
     const RuleSet &rule_set = this+ruleSet[index];
     struct ContextApplyLookupContext lookup_context = {
       {match_glyph},
-      NULL
+      nullptr
     };
     return_trace (rule_set.apply (c, lookup_context));
   }
@@ -1316,7 +1360,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
@@ -1386,7 +1430,7 @@
     return this+coverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
@@ -1409,7 +1453,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 2 */
+  HBUINT16	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
@@ -1438,7 +1482,7 @@
       this
     };
     context_closure_lookup (c,
-			    glyphCount, (const USHORT *) (coverageZ + 1),
+			    glyphCount, (const HBUINT16 *) (coverageZ + 1),
 			    lookupCount, lookupRecord,
 			    lookup_context);
   }
@@ -1455,7 +1499,7 @@
     };
 
     context_collect_glyphs_lookup (c,
-				   glyphCount, (const USHORT *) (coverageZ + 1),
+				   glyphCount, (const HBUINT16 *) (coverageZ + 1),
 				   lookupCount, lookupRecord,
 				   lookup_context);
   }
@@ -1469,7 +1513,7 @@
       {match_coverage},
       this
     };
-    return_trace (context_would_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
+    return_trace (context_would_apply_lookup (c, glyphCount, (const HBUINT16 *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
   }
 
   inline const Coverage &get_coverage (void) const
@@ -1477,7 +1521,7 @@
     return this+coverageZ[0];
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint);
@@ -1488,7 +1532,7 @@
       {match_coverage},
       this
     };
-    return_trace (context_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
+    return_trace (context_apply_lookup (c, glyphCount, (const HBUINT16 *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -1505,17 +1549,17 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 3 */
-  USHORT	glyphCount;		/* Number of glyphs in the input glyph
+  HBUINT16	format;			/* Format identifier--format = 3 */
+  HBUINT16	glyphCount;		/* Number of glyphs in the input glyph
 					 * sequence */
-  USHORT	lookupCount;		/* Number of LookupRecords */
+  HBUINT16	lookupCount;		/* Number of LookupRecords */
   OffsetTo<Coverage>
 		coverageZ[VAR];		/* Array of offsets to Coverage
 					 * table in glyph sequence order */
-  LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
+/*LookupRecord	lookupRecordX[VAR];*/	/* Array of LookupRecords--in
 					 * design order */
   public:
-  DEFINE_SIZE_ARRAY2 (6, coverageZ, lookupRecordX);
+  DEFINE_SIZE_ARRAY (6, coverageZ);
 };
 
 struct Context
@@ -1535,7 +1579,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   ContextFormat1	format1;
   ContextFormat2	format2;
   ContextFormat3	format3;
@@ -1565,11 +1609,11 @@
 
 static inline void chain_context_closure_lookup (hb_closure_context_t *c,
 						 unsigned int backtrackCount,
-						 const USHORT backtrack[],
+						 const HBUINT16 backtrack[],
 						 unsigned int inputCount, /* Including the first glyph (not matched) */
-						 const USHORT input[], /* Array of input values--start with second glyph */
+						 const HBUINT16 input[], /* Array of input values--start with second glyph */
 						 unsigned int lookaheadCount,
-						 const USHORT lookahead[],
+						 const HBUINT16 lookahead[],
 						 unsigned int lookupCount,
 						 const LookupRecord lookupRecord[],
 						 ChainContextClosureLookupContext &lookup_context)
@@ -1589,11 +1633,11 @@
 
 static inline void chain_context_collect_glyphs_lookup (hb_collect_glyphs_context_t *c,
 						        unsigned int backtrackCount,
-						        const USHORT backtrack[],
+						        const HBUINT16 backtrack[],
 						        unsigned int inputCount, /* Including the first glyph (not matched) */
-						        const USHORT input[], /* Array of input values--start with second glyph */
+						        const HBUINT16 input[], /* Array of input values--start with second glyph */
 						        unsigned int lookaheadCount,
-						        const USHORT lookahead[],
+						        const HBUINT16 lookahead[],
 						        unsigned int lookupCount,
 						        const LookupRecord lookupRecord[],
 						        ChainContextCollectGlyphsLookupContext &lookup_context)
@@ -1613,11 +1657,11 @@
 
 static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c,
 						     unsigned int backtrackCount,
-						     const USHORT backtrack[] HB_UNUSED,
+						     const HBUINT16 backtrack[] HB_UNUSED,
 						     unsigned int inputCount, /* Including the first glyph (not matched) */
-						     const USHORT input[], /* Array of input values--start with second glyph */
+						     const HBUINT16 input[], /* Array of input values--start with second glyph */
 						     unsigned int lookaheadCount,
-						     const USHORT lookahead[] HB_UNUSED,
+						     const HBUINT16 lookahead[] HB_UNUSED,
 						     unsigned int lookupCount HB_UNUSED,
 						     const LookupRecord lookupRecord[] HB_UNUSED,
 						     ChainContextApplyLookupContext &lookup_context)
@@ -1628,18 +1672,18 @@
 			    lookup_context.funcs.match, lookup_context.match_data[1]);
 }
 
-static inline bool chain_context_apply_lookup (hb_apply_context_t *c,
+static inline bool chain_context_apply_lookup (hb_ot_apply_context_t *c,
 					       unsigned int backtrackCount,
-					       const USHORT backtrack[],
+					       const HBUINT16 backtrack[],
 					       unsigned int inputCount, /* Including the first glyph (not matched) */
-					       const USHORT input[], /* Array of input values--start with second glyph */
+					       const HBUINT16 input[], /* Array of input values--start with second glyph */
 					       unsigned int lookaheadCount,
-					       const USHORT lookahead[],
+					       const HBUINT16 lookahead[],
 					       unsigned int lookupCount,
 					       const LookupRecord lookupRecord[],
 					       ChainContextApplyLookupContext &lookup_context)
 {
-  unsigned int match_length = 0;
+  unsigned int start_index = 0, match_length = 0, end_index = 0;
   unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
   return match_input (c,
 		      inputCount, input,
@@ -1647,15 +1691,17 @@
 		      &match_length, match_positions)
       && match_backtrack (c,
 			  backtrackCount, backtrack,
-			  lookup_context.funcs.match, lookup_context.match_data[0])
+			  lookup_context.funcs.match, lookup_context.match_data[0],
+			  &start_index)
       && match_lookahead (c,
 			  lookaheadCount, lookahead,
 			  lookup_context.funcs.match, lookup_context.match_data[2],
-			  match_length)
-      && apply_lookup (c,
+			  match_length, &end_index)
+      && (c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index),
+          apply_lookup (c,
 		       inputCount, match_positions,
 		       lookupCount, lookupRecord,
-		       match_length);
+		       match_length));
 }
 
 struct ChainRule
@@ -1663,8 +1709,8 @@
   inline void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const
   {
     TRACE_CLOSURE (this);
-    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
-    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16> > (backtrack);
+    const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     chain_context_closure_lookup (c,
 				  backtrack.len, backtrack.array,
@@ -1677,8 +1723,8 @@
   inline void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
-    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16> > (backtrack);
+    const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     chain_context_collect_glyphs_lookup (c,
 					 backtrack.len, backtrack.array,
@@ -1691,8 +1737,8 @@
   inline bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_WOULD_APPLY (this);
-    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
-    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16> > (backtrack);
+    const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     return_trace (chain_context_would_apply_lookup (c,
 						    backtrack.len, backtrack.array,
@@ -1701,11 +1747,11 @@
 						    lookup.array, lookup_context));
   }
 
-  inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
+  inline bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
-    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
-    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16> > (backtrack);
+    const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     return_trace (chain_context_apply_lookup (c,
 					      backtrack.len, backtrack.array,
@@ -1718,23 +1764,23 @@
   {
     TRACE_SANITIZE (this);
     if (!backtrack.sanitize (c)) return_trace (false);
-    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
+    const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16> > (backtrack);
     if (!input.sanitize (c)) return_trace (false);
-    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16> > (input);
     if (!lookahead.sanitize (c)) return_trace (false);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     return_trace (lookup.sanitize (c));
   }
 
   protected:
-  ArrayOf<USHORT>
+  ArrayOf<HBUINT16>
 		backtrack;		/* Array of backtracking values
 					 * (to be matched before the input
 					 * sequence) */
-  HeadlessArrayOf<USHORT>
+  HeadlessArrayOf<HBUINT16>
 		inputX;			/* Array of input values (start with
 					 * second glyph) */
-  ArrayOf<USHORT>
+  ArrayOf<HBUINT16>
 		lookaheadX;		/* Array of lookahead values's (to be
 					 * matched after the input sequence) */
   ArrayOf<LookupRecord>
@@ -1773,7 +1819,7 @@
     return_trace (false);
   }
 
-  inline bool apply (hb_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
+  inline bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const
   {
     TRACE_APPLY (this);
     unsigned int num_rules = rule.len;
@@ -1807,7 +1853,7 @@
 
     struct ChainContextClosureLookupContext lookup_context = {
       {intersects_glyph},
-      {NULL, NULL, NULL}
+      {nullptr, nullptr, nullptr}
     };
 
     unsigned int count = ruleSet.len;
@@ -1825,7 +1871,7 @@
 
     struct ChainContextCollectGlyphsLookupContext lookup_context = {
       {collect_glyph},
-      {NULL, NULL, NULL}
+      {nullptr, nullptr, nullptr}
     };
 
     unsigned int count = ruleSet.len;
@@ -1840,7 +1886,7 @@
     const ChainRuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])];
     struct ChainContextApplyLookupContext lookup_context = {
       {match_glyph},
-      {NULL, NULL, NULL}
+      {nullptr, nullptr, nullptr}
     };
     return_trace (rule_set.would_apply (c, lookup_context));
   }
@@ -1850,7 +1896,7 @@
     return this+coverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
@@ -1859,7 +1905,7 @@
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
       {match_glyph},
-      {NULL, NULL, NULL}
+      {nullptr, nullptr, nullptr}
     };
     return_trace (rule_set.apply (c, lookup_context));
   }
@@ -1871,7 +1917,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 1 */
+  HBUINT16	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
@@ -1954,7 +2000,7 @@
     return this+coverage;
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
@@ -1986,7 +2032,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 2 */
+  HBUINT16	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
 		coverage;		/* Offset to Coverage table--from
 					 * beginning of table */
@@ -2026,9 +2072,9 @@
       {this, this, this}
     };
     chain_context_closure_lookup (c,
-				  backtrack.len, (const USHORT *) backtrack.array,
-				  input.len, (const USHORT *) input.array + 1,
-				  lookahead.len, (const USHORT *) lookahead.array,
+				  backtrack.len, (const HBUINT16 *) backtrack.array,
+				  input.len, (const HBUINT16 *) input.array + 1,
+				  lookahead.len, (const HBUINT16 *) lookahead.array,
 				  lookup.len, lookup.array,
 				  lookup_context);
   }
@@ -2047,9 +2093,9 @@
       {this, this, this}
     };
     chain_context_collect_glyphs_lookup (c,
-					 backtrack.len, (const USHORT *) backtrack.array,
-					 input.len, (const USHORT *) input.array + 1,
-					 lookahead.len, (const USHORT *) lookahead.array,
+					 backtrack.len, (const HBUINT16 *) backtrack.array,
+					 input.len, (const HBUINT16 *) input.array + 1,
+					 lookahead.len, (const HBUINT16 *) lookahead.array,
 					 lookup.len, lookup.array,
 					 lookup_context);
   }
@@ -2066,9 +2112,9 @@
       {this, this, this}
     };
     return_trace (chain_context_would_apply_lookup (c,
-						    backtrack.len, (const USHORT *) backtrack.array,
-						    input.len, (const USHORT *) input.array + 1,
-						    lookahead.len, (const USHORT *) lookahead.array,
+						    backtrack.len, (const HBUINT16 *) backtrack.array,
+						    input.len, (const HBUINT16 *) input.array + 1,
+						    lookahead.len, (const HBUINT16 *) lookahead.array,
 						    lookup.len, lookup.array, lookup_context));
   }
 
@@ -2078,7 +2124,7 @@
     return this+input[0];
   }
 
-  inline bool apply (hb_apply_context_t *c) const
+  inline bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
@@ -2093,9 +2139,9 @@
       {this, this, this}
     };
     return_trace (chain_context_apply_lookup (c,
-					      backtrack.len, (const USHORT *) backtrack.array,
-					      input.len, (const USHORT *) input.array + 1,
-					      lookahead.len, (const USHORT *) lookahead.array,
+					      backtrack.len, (const HBUINT16 *) backtrack.array,
+					      input.len, (const HBUINT16 *) input.array + 1,
+					      lookahead.len, (const HBUINT16 *) lookahead.array,
 					      lookup.len, lookup.array, lookup_context));
   }
 
@@ -2113,7 +2159,7 @@
   }
 
   protected:
-  USHORT	format;			/* Format identifier--format = 3 */
+  HBUINT16	format;			/* Format identifier--format = 3 */
   OffsetArrayOf<Coverage>
 		backtrack;		/* Array of coverage tables
 					 * in backtracking sequence, in  glyph
@@ -2150,7 +2196,7 @@
 
   protected:
   union {
-  USHORT		format;	/* Format identifier */
+  HBUINT16		format;	/* Format identifier */
   ChainContextFormat1	format1;
   ChainContextFormat2	format2;
   ChainContextFormat3	format3;
@@ -2183,15 +2229,17 @@
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && extensionOffset != 0);
+    return_trace (c->check_struct (this) &&
+		  extensionOffset != 0 &&
+		  extensionLookupType != T::LookupSubTable::Extension);
   }
 
   protected:
-  USHORT	format;			/* Format identifier. Set to 1. */
-  USHORT	extensionLookupType;	/* Lookup type of subtable referenced
+  HBUINT16	format;			/* Format identifier. Set to 1. */
+  HBUINT16	extensionLookupType;	/* Lookup type of subtable referenced
 					 * by ExtensionOffset (i.e. the
 					 * extension subtable). */
-  ULONG		extensionOffset;	/* Offset to the extension subtable,
+  HBUINT32	extensionOffset;	/* Offset to the extension subtable,
 					 * of lookup type subtable. */
   public:
   DEFINE_SIZE_STATIC (8);
@@ -2229,7 +2277,7 @@
 
   protected:
   union {
-  USHORT		format;		/* Format identifier */
+  HBUINT16		format;		/* Format identifier */
   ExtensionFormat1<T>	format1;
   } u;
 };
@@ -2241,9 +2289,6 @@
 
 struct GSUBGPOS
 {
-  static const hb_tag_t GSUBTag	= HB_OT_TAG_GSUB;
-  static const hb_tag_t GPOSTag	= HB_OT_TAG_GPOS;
-
   inline unsigned int get_script_count (void) const
   { return (this+scriptList).len; }
   inline const Tag& get_script_tag (unsigned int i) const
diff --git a/src/hb-ot-layout-jstf-table.hh b/src/hb-ot-layout-jstf-table.hh
index c306849..adbaad6 100644
--- a/src/hb-ot-layout-jstf-table.hh
+++ b/src/hb-ot-layout-jstf-table.hh
@@ -124,7 +124,7 @@
 struct JstfLangSys : OffsetListOf<JstfPriority>
 {
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<JstfLangSys>::sanitize_closure_t * = NULL) const
+			const Record<JstfLangSys>::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 * = NULL) const
+			const Record<JstfScript>::sanitize_closure_t * = nullptr) const
   {
     TRACE_SANITIZE (this);
     return_trace (extenderGlyphs.sanitize (c, this) &&
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index ee8e137..b8860a7 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -33,7 +33,7 @@
 
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
-#include "hb-set-private.hh"
+#include "hb-set-digest-private.hh"
 #include "hb-open-type-private.hh"
 
 
@@ -90,12 +90,12 @@
 struct hb_ot_layout_lookup_accelerator_t;
 
 namespace OT {
-  struct hb_apply_context_t;
+  struct hb_ot_apply_context_t;
   struct SubstLookup;
 }
 
 HB_INTERNAL void
-hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
+hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c,
 				const OT::SubstLookup &lookup,
 				const hb_ot_layout_lookup_accelerator_t &accel);
 
@@ -131,6 +131,12 @@
   struct avar;
 }
 
+namespace AAT {
+  struct morx;
+  struct kerx;
+  struct trak;
+}
+
 struct hb_ot_layout_lookup_accelerator_t
 {
   template <typename TLookup>
@@ -167,6 +173,9 @@
   OT::hb_lazy_table_loader_t<struct OT::MATH> math;
   OT::hb_lazy_table_loader_t<struct OT::fvar> fvar;
   OT::hb_lazy_table_loader_t<struct OT::avar> avar;
+  OT::hb_lazy_table_loader_t<struct AAT::morx> morx;
+  OT::hb_lazy_table_loader_t<struct AAT::kerx> kerx;
+  OT::hb_lazy_table_loader_t<struct AAT::trak> trak;
 
   unsigned int gsub_lookup_count;
   unsigned int gpos_lookup_count;
@@ -199,8 +208,7 @@
 #define syllable()		var1.u8[3] /* GSUB/GPOS shaping boundaries */
 
 
-/* loop over syllables */
-
+/* Loop over syllables. Based on foreach_cluster(). */
 #define foreach_syllable(buffer, start, end) \
   for (unsigned int \
        _count = buffer->len, \
@@ -229,7 +237,9 @@
  * - General_Category: 5 bits.
  * - A bit each for:
  *   * Is it Default_Ignorable(); we have a modified Default_Ignorable().
- *   * Whether it's one of the three Mongolian Free Variation Selectors.
+ *   * 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.
  *
  * The high-byte has different meanings, switched by the Gen-Cat:
@@ -242,7 +252,8 @@
 enum hb_unicode_props_flags_t {
   UPROPS_MASK_GEN_CAT	= 0x001Fu,
   UPROPS_MASK_IGNORABLE	= 0x0020u,
-  UPROPS_MASK_FVS	= 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..3 */
+  UPROPS_MASK_HIDDEN	= 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..3,
+                                    * or TAG characters */
 
   /* If GEN_CAT=FORMAT, top byte masks: */
   UPROPS_MASK_Cf_ZWJ	= 0x0100u,
@@ -266,16 +277,25 @@
       buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES;
       props |=  UPROPS_MASK_IGNORABLE;
       if (u == 0x200Cu) props |= UPROPS_MASK_Cf_ZWNJ;
-      if (u == 0x200Du) props |= UPROPS_MASK_Cf_ZWJ;
+      else if (u == 0x200Du) props |= UPROPS_MASK_Cf_ZWJ;
       /* Mongolian Free Variation Selectors need to be remembered
        * because although we need to hide them like default-ignorables,
        * they need to non-ignorable during shaping.  This is similar to
        * what we do for joiners in Indic-like shapers, but since the
        * FVSes are GC=Mn, we have use a separate bit to remember them.
        * Fixes:
-       * https://github.com/behdad/harfbuzz/issues/234
-       */
-      if (unlikely (hb_in_range (u, 0x180Bu, 0x180Du))) props |= UPROPS_MASK_FVS;
+       * https://github.com/harfbuzz/harfbuzz/issues/234 */
+      else if (unlikely (hb_in_range (u, 0x180Bu, 0x180Du))) props |= UPROPS_MASK_HIDDEN;
+      /* TAG characters need similar treatment. Fixes:
+       * https://github.com/harfbuzz/harfbuzz/issues/463 */
+      else if (unlikely (hb_in_range (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN;
+      /* COMBINING GRAPHEME JOINER should not be skipped; at least some times.
+       * https://github.com/harfbuzz/harfbuzz/issues/554 */
+      else if (unlikely (u == 0x034Fu))
+      {
+	buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_CGJ;
+	props |= UPROPS_MASK_HIDDEN;
+      }
     }
     else if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_NON_ENCLOSING_MARK_OR_MODIFIER_SYMBOL (gen_cat)))
     {
@@ -300,7 +320,7 @@
       /* 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/behdad/harfbuzz/issues/169
+       * https://github.com/harfbuzz/harfbuzz/issues/169
        */
       if (unlikely (hb_in_range (u, 0x1F3FBu, 0x1F3FFu)))
       {
@@ -345,6 +365,8 @@
   return _hb_glyph_info_is_unicode_mark (info) ? info->unicode_props()>>8 : 0;
 }
 
+#define info_cc(info) (_hb_glyph_info_get_modified_combining_class (&(info)))
+
 static inline bool
 _hb_glyph_info_is_unicode_space (const hb_glyph_info_t *info)
 {
@@ -375,12 +397,17 @@
 	 !_hb_glyph_info_ligated (info);
 }
 static inline hb_bool_t
-_hb_glyph_info_is_default_ignorable_and_not_fvs (const hb_glyph_info_t *info)
+_hb_glyph_info_is_default_ignorable_and_not_hidden (const hb_glyph_info_t *info)
 {
-  return ((info->unicode_props() & (UPROPS_MASK_IGNORABLE|UPROPS_MASK_FVS))
+  return ((info->unicode_props() & (UPROPS_MASK_IGNORABLE|UPROPS_MASK_HIDDEN))
 	  == UPROPS_MASK_IGNORABLE) &&
 	 !_hb_glyph_info_ligated (info);
 }
+static inline void
+_hb_glyph_info_unhide (hb_glyph_info_t *info)
+{
+  info->unicode_props() &= ~ UPROPS_MASK_HIDDEN;
+}
 
 static inline bool
 _hb_glyph_info_is_unicode_format (const hb_glyph_info_t *info)
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index d393f5c..68eb125 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -36,30 +36,37 @@
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
 #include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise.
+#include "hb-ot-name-table.hh" // Just so we compile it; unused otherwise.
 
 #include "hb-ot-map-private.hh"
 
 
+#ifndef HB_NO_VISIBILITY
+const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
+#endif
+
+
 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 NULL;
+    return nullptr;
 
-  layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
+  layout->gdef_blob = OT::Sanitizer<OT::GDEF>().sanitize (face->reference_table (HB_OT_TAG_GDEF));
   layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
 
-  layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
+  layout->gsub_blob = OT::Sanitizer<OT::GSUB>().sanitize (face->reference_table (HB_OT_TAG_GSUB));
   layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
 
-  layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
+  layout->gpos_blob = OT::Sanitizer<OT::GPOS>().sanitize (face->reference_table (HB_OT_TAG_GPOS));
   layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
 
   layout->math.init (face);
   layout->base.init (face);
   layout->fvar.init (face);
   layout->avar.init (face);
+  layout->morx.init (face);
 
   {
     /*
@@ -103,10 +110,18 @@
       || (928 == gdef_len && 59332 == gpos_len && 23298 == gsub_len)
       /* sha1sum:6d400781948517c3c0441ba42acb309584b73033  tahomabd.ttf from Windows 8.1 */
       || (940 == gdef_len && 60732 == gpos_len && 23310 == gsub_len)
+      /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+      || (964 == gdef_len && 60072 == gpos_len && 23836 == gsub_len)
+      /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+      || (976 == gdef_len && 61456 == gpos_len && 23832 == gsub_len)
       /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846  tahoma.ttf from Windows 10 */
       || (994 == gdef_len && 60336 == gpos_len && 24474 == gsub_len)
       /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343  tahomabd.ttf from Windows 10 */
       || (1006 == gdef_len && 61740 == gpos_len && 24470 == gsub_len)
+      /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+      || (1006 == gdef_len && 61346 == gpos_len && 24576 == gsub_len)
+      /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
+      || (1018 == gdef_len && 62828 == gpos_len && 24572 == gsub_len)
       /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5  tahoma.ttf from Windows 10 AU */
       || (1006 == gdef_len && 61352 == gpos_len && 24576 == gsub_len)
       /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2  tahomabd.ttf from Windows 10 AU */
@@ -127,6 +142,14 @@
       /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f  cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */
       /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b  cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */
       || (188 == gdef_len && 3426 == gpos_len && 264 == gsub_len)
+      /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */
+      || (1058 == gdef_len && 11818 == gpos_len && 47032 == gsub_len)
+      /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/
+      || (1046 == gdef_len && 12600 == gpos_len && 47030 == gsub_len)
+      /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */
+      || (1058 == gdef_len && 16770 == gpos_len && 71796 == gsub_len)
+      /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */
+      || (1046 == gdef_len && 17862 == gpos_len && 71790 == gsub_len)
       /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */
       || (1046 == gdef_len && 17112 == gpos_len && 71788 == gsub_len)
       /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */
@@ -135,6 +158,9 @@
       || (1330 == gdef_len && 57938 == gpos_len && 109904 == gsub_len)
       /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */
       || (1330 == gdef_len && 58972 == gpos_len && 109904 == gsub_len)
+      /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85  Padauk.ttf
+       *  "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */
+      || (1004 == gdef_len && 14836 == gpos_len && 59092 == gsub_len)
     )
     {
       /* Many versions of Tahoma have bad GDEF tables that incorrectly classify some spacing marks
@@ -159,7 +185,7 @@
 		(layout->gpos_lookup_count && !layout->gpos_accels)))
   {
     _hb_ot_layout_destroy (layout);
-    return NULL;
+    return nullptr;
   }
 
   for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
@@ -173,10 +199,12 @@
 void
 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
 {
-  for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
-    layout->gsub_accels[i].fini ();
-  for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
-    layout->gpos_accels[i].fini ();
+  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 ();
 
   free (layout->gsub_accels);
   free (layout->gpos_accels);
@@ -189,6 +217,7 @@
   layout->base.fini ();
   layout->fvar.fini ();
   layout->avar.fini ();
+  layout->morx.fini ();
 
   free (layout);
 }
@@ -263,7 +292,7 @@
 				  hb_codepoint_t  glyph,
 				  unsigned int    start_offset,
 				  unsigned int   *caret_count /* IN/OUT */,
-				  int            *caret_array /* OUT */)
+				  hb_position_t  *caret_array /* OUT */)
 {
   return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
 }
@@ -305,7 +334,7 @@
 				hb_tag_t      script_tag,
 				unsigned int *script_index)
 {
-  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
+  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), "");
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   if (g.find_script_index (script_tag, script_index))
@@ -336,7 +365,7 @@
 				  unsigned int   *script_index,
 				  hb_tag_t       *chosen_script)
 {
-  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
+  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), "");
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   while (*script_tags)
@@ -395,7 +424,7 @@
 				 hb_tag_t      feature_tag,
 				 unsigned int *feature_index)
 {
-  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
+  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), "");
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   unsigned int num_features = g.get_feature_count ();
@@ -432,7 +461,7 @@
 				   hb_tag_t      language_tag,
 				   unsigned int *language_index)
 {
-  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
+  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);
 
   if (s.find_lang_sys_index (language_tag, language_index))
@@ -458,7 +487,7 @@
 						     script_index,
 						     language_index,
 						     feature_index,
-						     NULL);
+						     nullptr);
 }
 
 /**
@@ -511,7 +540,7 @@
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 
-  ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
+  static_assert ((sizeof (unsigned int) == sizeof (hb_tag_t)), "");
   unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
 
   if (feature_tags) {
@@ -532,7 +561,7 @@
 				    hb_tag_t      feature_tag,
 				    unsigned int *feature_index)
 {
-  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
+  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), "");
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 
@@ -581,6 +610,7 @@
 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
 				     hb_tag_t      table_tag)
 {
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return 0;
   switch (table_tag)
   {
     case HB_OT_TAG_GSUB:
@@ -636,7 +666,7 @@
 						    script_index,
 						    language_index,
 						    &required_feature_index,
-						    NULL))
+						    nullptr))
       _hb_ot_layout_collect_lookups_lookups (face,
 					     table_tag,
 					     required_feature_index,
@@ -705,7 +735,7 @@
     unsigned int count = hb_ot_layout_script_get_language_tags (face,
 								table_tag,
 								script_index,
-								0, NULL, NULL);
+								0, nullptr, nullptr);
     for (unsigned int language_index = 0; language_index < count; language_index++)
       _hb_ot_layout_collect_lookups_features (face,
 					      table_tag,
@@ -752,7 +782,7 @@
     /* All scripts */
     unsigned int count = hb_ot_layout_table_get_script_tags (face,
 							     table_tag,
-							     0, NULL, NULL);
+							     0, nullptr, nullptr);
     for (unsigned int script_index = 0; script_index < count; script_index++)
       _hb_ot_layout_collect_lookups_languages (face,
 					       table_tag,
@@ -789,10 +819,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 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 */)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
 
@@ -843,7 +873,7 @@
 						  unsigned int *lookup_count /* IN/OUT */,
 						  unsigned int *lookup_indexes /* OUT */)
 {
-  ASSERT_STATIC (OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX);
+  static_assert ((OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX), "");
   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   const OT::Feature &f = g.get_feature_variation (feature_index, variations_index);
@@ -951,11 +981,11 @@
  **/
 hb_bool_t
 hb_ot_layout_get_size_params (hb_face_t    *face,
-			      unsigned int *design_size,       /* OUT.  May be NULL */
-			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
-			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
-			      unsigned int *range_start,       /* OUT.  May be NULL */
-			      unsigned int *range_end          /* OUT.  May be NULL */)
+			      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 */)
 {
   const OT::GPOS &gpos = _get_gpos (face);
   const hb_tag_t tag = HB_TAG ('s','i','z','e');
@@ -1034,13 +1064,13 @@
        OT::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_apply_context_t *c)
+  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_apply_context_t *c);
+  typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_ot_apply_context_t *c);
 
   struct hb_applicable_t
   {
@@ -1050,7 +1080,7 @@
       apply_func = apply_func_;
     }
 
-    inline bool apply (OT::hb_apply_context_t *c) const { return apply_func (obj, c); }
+    inline bool apply (OT::hb_ot_apply_context_t *c) const { return apply_func (obj, c); }
 
     private:
     const void *obj;
@@ -1081,7 +1111,7 @@
 };
 
 static inline bool
-apply_forward (OT::hb_apply_context_t *c,
+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)
 {
@@ -1111,7 +1141,7 @@
 }
 
 static inline bool
-apply_backward (OT::hb_apply_context_t *c,
+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)
 {
@@ -1140,7 +1170,7 @@
 
 template <typename Proxy>
 static inline void
-apply_string (OT::hb_apply_context_t *c,
+apply_string (OT::hb_ot_apply_context_t *c,
 	      const typename Proxy::Lookup &lookup,
 	      const hb_ot_layout_lookup_accelerator_t &accel)
 {
@@ -1191,7 +1221,7 @@
 {
   const unsigned int table_index = proxy.table_index;
   unsigned int i = 0;
-  OT::hb_apply_context_t c (table_index, font, buffer);
+  OT::hb_ot_apply_context_t c (table_index, font, buffer);
   c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
 
   for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) {
@@ -1203,6 +1233,7 @@
       c.set_lookup_index (lookup_index);
       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);
       apply_string<Proxy> (&c,
 			   proxy.table.get_lookup (lookup_index),
 			   proxy.accels[lookup_index]);
@@ -1229,8 +1260,8 @@
   apply (proxy, plan, font, buffer);
 }
 
-HB_INTERNAL void
-hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
+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)
 {
diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh
index 0395c9c..e6bd8ea 100644
--- a/src/hb-ot-map-private.hh
+++ b/src/hb-ot-map-private.hh
@@ -50,19 +50,25 @@
     hb_mask_t mask;
     hb_mask_t _1_mask; /* mask for value=1, for quick access */
     unsigned int needs_fallback : 1;
+    unsigned int auto_zwnj : 1;
     unsigned int auto_zwj : 1;
 
-    static int cmp (const feature_map_t *a, const feature_map_t *b)
-    { return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0; }
+    inline int cmp (const hb_tag_t *tag_) const
+    { return *tag_ < tag ? -1 : *tag_ > tag ? 1 : 0; }
   };
 
   struct lookup_map_t {
     unsigned short index;
+    unsigned short auto_zwnj : 1;
     unsigned short auto_zwj : 1;
     hb_mask_t mask;
 
-    static int cmp (const lookup_map_t *a, const lookup_map_t *b)
-    { return a->index < b->index ? -1 : a->index > b->index ? 1 : 0; }
+    static int cmp (const void *pa, const void *pb)
+    {
+      const lookup_map_t *a = (const lookup_map_t *) pa;
+      const lookup_map_t *b = (const lookup_map_t *) pb;
+      return a->index < b->index ? -1 : a->index > b->index ? 1 : 0;
+    }
   };
 
   typedef void (*pause_func_t) (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer);
@@ -77,43 +83,43 @@
 
   inline hb_mask_t get_global_mask (void) const { return global_mask; }
 
-  inline hb_mask_t get_mask (hb_tag_t feature_tag, unsigned int *shift = NULL) const {
-    const feature_map_t *map = features.bsearch (&feature_tag);
+  inline hb_mask_t get_mask (hb_tag_t feature_tag, unsigned int *shift = nullptr) const {
+    const feature_map_t *map = features.bsearch (feature_tag);
     if (shift) *shift = map ? map->shift : 0;
     return map ? map->mask : 0;
   }
 
   inline bool needs_fallback (hb_tag_t feature_tag) const {
-    const feature_map_t *map = features.bsearch (&feature_tag);
+    const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->needs_fallback : false;
   }
 
   inline hb_mask_t get_1_mask (hb_tag_t feature_tag) const {
-    const feature_map_t *map = features.bsearch (&feature_tag);
+    const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->_1_mask : 0;
   }
 
   inline unsigned int get_feature_index (unsigned int table_index, hb_tag_t feature_tag) const {
-    const feature_map_t *map = features.bsearch (&feature_tag);
+    const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->index[table_index] : HB_OT_LAYOUT_NO_FEATURE_INDEX;
   }
 
   inline unsigned int get_feature_stage (unsigned int table_index, hb_tag_t feature_tag) const {
-    const feature_map_t *map = features.bsearch (&feature_tag);
+    const feature_map_t *map = features.bsearch (feature_tag);
     return map ? map->stage[table_index] : (unsigned int) -1;
   }
 
   inline void get_stage_lookups (unsigned int table_index, unsigned int stage,
 				 const struct lookup_map_t **plookups, unsigned int *lookup_count) const {
     if (unlikely (stage == (unsigned int) -1)) {
-      *plookups = NULL;
+      *plookups = nullptr;
       *lookup_count = 0;
       return;
     }
     assert (stage <= stages[table_index].len);
     unsigned int start = stage ? stages[table_index][stage - 1].last_lookup : 0;
     unsigned int end   = stage < stages[table_index].len ? stages[table_index][stage].last_lookup : lookups[table_index].len;
-    *plookups = &lookups[table_index][start];
+    *plookups = end == start ? nullptr : &lookups[table_index][start];
     *lookup_count = end - start;
   }
 
@@ -150,8 +156,9 @@
   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_ZWJ		= 0x0004u, /* Don't skip over ZWJ when matching. */
-  F_GLOBAL_SEARCH	= 0x0008u  /* If feature not found in LangSys, look for it in global feature list and pick one. */
+  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. */
 };
 HB_MARK_AS_FLAG_T (hb_ot_map_feature_flags_t);
 /* Macro version for where const is desired. */
@@ -191,12 +198,12 @@
   private:
 
   HB_INTERNAL void add_lookups (hb_ot_map_t  &m,
-				hb_face_t    *face,
 				unsigned int  table_index,
 				unsigned int  feature_index,
 				unsigned int  variations_index,
 				hb_mask_t     mask,
-				bool          auto_zwj);
+				bool          auto_zwnj = true,
+				bool          auto_zwj = true);
 
   struct feature_info_t {
     hb_tag_t tag;
@@ -206,9 +213,13 @@
     unsigned int default_value; /* for non-global features, what should the unset glyphs take */
     unsigned int stage[2]; /* GSUB/GPOS */
 
-    static int cmp (const feature_info_t *a, const feature_info_t *b)
-    { return (a->tag != b->tag) ?  (a->tag < b->tag ? -1 : 1) :
-	     (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0); }
+    static int cmp (const void *pa, const void *pb)
+    {
+      const feature_info_t *a = (const feature_info_t *) pa;
+      const feature_info_t *b = (const feature_info_t *) pb;
+      return (a->tag != b->tag) ?  (a->tag < b->tag ? -1 : 1) :
+	     (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
+    }
   };
 
   struct stage_info_t {
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index 9b331d5..54b0ce3 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -80,11 +80,11 @@
 
 void
 hb_ot_map_builder_t::add_lookups (hb_ot_map_t  &m,
-				  hb_face_t    *face,
 				  unsigned int  table_index,
 				  unsigned int  feature_index,
 				  unsigned int  variations_index,
 				  hb_mask_t     mask,
+				  bool          auto_zwnj,
 				  bool          auto_zwj)
 {
   unsigned int lookup_indices[32];
@@ -112,6 +112,7 @@
         return;
       lookup->mask = mask;
       lookup->index = lookup_indices[i];
+      lookup->auto_zwnj = auto_zwnj;
       lookup->auto_zwj = auto_zwj;
     }
 
@@ -136,7 +137,11 @@
 			      const int    *coords,
 			      unsigned int  num_coords)
 {
-  m.global_mask = 1;
+  static_assert ((!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1))), "");
+  unsigned int global_bit_mask = HB_GLYPH_FLAG_DEFINED + 1;
+  unsigned int global_bit_shift = _hb_popcount (HB_GLYPH_FLAG_DEFINED);
+
+  m.global_mask = global_bit_mask;
 
   unsigned int required_feature_index[2];
   hb_tag_t required_feature_tag[2];
@@ -188,7 +193,8 @@
 
 
   /* Allocate bits now */
-  unsigned int next_bit = 1;
+  unsigned int next_bit = global_bit_shift + 1;
+
   for (unsigned int i = 0; i < feature_infos.len; i++)
   {
     const feature_info_t *info = &feature_infos[i];
@@ -243,11 +249,12 @@
     map->index[1] = feature_index[1];
     map->stage[0] = info->stage[0];
     map->stage[1] = info->stage[1];
+    map->auto_zwnj = !(info->flags & F_MANUAL_ZWNJ);
     map->auto_zwj = !(info->flags & F_MANUAL_ZWJ);
     if ((info->flags & F_GLOBAL) && info->max_value == 1) {
       /* Uses the global bit */
-      map->shift = 0;
-      map->mask = 1;
+      map->shift = global_bit_shift;
+      map->mask = global_bit_mask;
     } else {
       map->shift = next_bit;
       map->mask = (1u << (next_bit + bits_needed)) - (1u << next_bit);
@@ -261,8 +268,8 @@
   feature_infos.shrink (0); /* Done with these */
 
 
-  add_gsub_pause (NULL);
-  add_gpos_pause (NULL);
+  add_gsub_pause (nullptr);
+  add_gpos_pause (nullptr);
 
   for (unsigned int table_index = 0; table_index < 2; table_index++)
   {
@@ -281,18 +288,18 @@
     {
       if (required_feature_index[table_index] != HB_OT_LAYOUT_NO_FEATURE_INDEX &&
 	  required_feature_stage[table_index] == stage)
-	add_lookups (m, face, table_index,
+	add_lookups (m, table_index,
 		     required_feature_index[table_index],
 		     variations_index,
-		     1 /* mask */,
-		     true /* auto_zwj */);
+		     global_bit_mask);
 
       for (unsigned i = 0; i < m.features.len; i++)
         if (m.features[i].stage[table_index] == stage)
-	  add_lookups (m, face, table_index,
+	  add_lookups (m, table_index,
 		       m.features[i].index[table_index],
 		       variations_index,
 		       m.features[i].mask,
+		       m.features[i].auto_zwnj,
 		       m.features[i].auto_zwj);
 
       /* Sort lookups and merge duplicates */
@@ -307,6 +314,7 @@
 	  else
 	  {
 	    m.lookups[table_index][j].mask |= m.lookups[table_index][i].mask;
+	    m.lookups[table_index][j].auto_zwnj &= m.lookups[table_index][i].auto_zwnj;
 	    m.lookups[table_index][j].auto_zwj &= m.lookups[table_index][i].auto_zwj;
 	  }
 	m.lookups[table_index].shrink (j + 1);
diff --git a/src/hb-ot-math-table.hh b/src/hb-ot-math-table.hh
index 191d79e..571ce01 100644
--- a/src/hb-ot-math-table.hh
+++ b/src/hb-ot-math-table.hh
@@ -48,9 +48,9 @@
   }
 
   protected:
-  SHORT			value;		/* The X or Y value in design units */
+  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 NULL.
+					 * beginning of parent table. May be nullptr.
 					 * Suggested format for device table is 1. */
 
   public:
@@ -154,10 +154,10 @@
   }
 
   protected:
-  SHORT percentScaleDown[2];
-  USHORT minHeight[2];
+  HBINT16 percentScaleDown[2];
+  HBUINT16 minHeight[2];
   MathValueRecord mathValueRecords[51];
-  SHORT radicalDegreeBottomRaisePercent;
+  HBINT16 radicalDegreeBottomRaisePercent;
 
   public:
   DEFINE_SIZE_STATIC (214);
@@ -279,7 +279,7 @@
   }
 
   protected:
-  USHORT	  heightCount;
+  HBUINT16	  heightCount;
   MathValueRecord mathValueRecords[VAR]; /* Array of correction heights at
 					  * which the kern value changes.
 					  * Sorted by the height value in
@@ -319,7 +319,7 @@
 
   protected:
   /* Offset to MathKern table for each corner -
-   * from the beginning of MathKernInfo table. May be NULL. */
+   * from the beginning of MathKernInfo table. May be nullptr. */
   OffsetTo<MathKern> mathKern[4];
 
   public:
@@ -402,7 +402,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 NULL.. */
+   * vertical positioning purposes. May be nullptr.. */
   OffsetTo<Coverage> extendedShapeCoverage;
 
    /* Offset to MathKernInfo table -
@@ -425,7 +425,7 @@
 
   protected:
   GlyphID variantGlyph;       /* Glyph ID for the variant. */
-  USHORT  advanceMeasurement; /* Advance width/height, in design units, of the
+  HBUINT16  advanceMeasurement; /* Advance width/height, in design units, of the
 			       * variant, in the direction of requested
 			       * glyph extension. */
 
@@ -433,7 +433,7 @@
   DEFINE_SIZE_STATIC (4);
 };
 
-struct PartFlags : USHORT
+struct PartFlags : HBUINT16
 {
   enum Flags {
     Extender	= 0x0001u, /* If set, the part can be skipped or repeated. */
@@ -463,8 +463,8 @@
     out.end_connector_length	= font->em_scale (endConnectorLength, scale);
     out.full_advance		= font->em_scale (fullAdvance, scale);
 
-    ASSERT_STATIC ((unsigned int) HB_MATH_GLYPH_PART_FLAG_EXTENDER ==
-		   (unsigned int) PartFlags::Extender);
+    static_assert ((unsigned int) HB_MATH_GLYPH_PART_FLAG_EXTENDER ==
+		   (unsigned int) PartFlags::Extender, "");
 
     out.flags = (hb_ot_math_glyph_part_flags_t)
 		(unsigned int)
@@ -473,15 +473,15 @@
 
   protected:
   GlyphID   glyph;		  /* Glyph ID for the part. */
-  USHORT    startConnectorLength; /* Advance width/ height of the straight bar
+  HBUINT16    startConnectorLength; /* Advance width/ height of the straight bar
 				   * connector material, in design units, is at
 				   * the beginning of the glyph, in the
 				   * direction of the extension. */
-  USHORT    endConnectorLength;   /* Advance width/ height of the straight bar
+  HBUINT16    endConnectorLength;   /* Advance width/ height of the straight bar
 				   * connector material, in design units, is at
 				   * the end of the glyph, in the direction of
 				   * the extension. */
-  USHORT    fullAdvance;	  /* Full advance width/height for this part,
+  HBUINT16    fullAdvance;	  /* Full advance width/height for this part,
 				   * in the direction of the extension.
 				   * In design units. */
   PartFlags partFlags;		  /* Part qualifiers. */
@@ -571,7 +571,7 @@
 
   protected:
   /* Offset to MathGlyphAssembly table for this shape - from the beginning of
-     MathGlyphConstruction table. May be NULL. */
+     MathGlyphConstruction table. May be nullptr. */
   OffsetTo<MathGlyphAssembly>	  glyphAssembly;
 
   /* MathGlyphVariantRecords for alternative variants of the glyphs. */
@@ -651,7 +651,7 @@
   }
 
   protected:
-  USHORT	     minConnectorOverlap; /* Minimum overlap of connecting
+  HBUINT16	     minConnectorOverlap; /* Minimum overlap of connecting
 					   * glyphs during glyph construction,
 					   * in design units. */
   OffsetTo<Coverage> vertGlyphCoverage;   /* Offset to Coverage table -
@@ -660,10 +660,10 @@
   OffsetTo<Coverage> horizGlyphCoverage;  /* Offset to Coverage table -
 					   * from the beginning of MathVariants
 					   * table. */
-  USHORT	     vertGlyphCount;      /* Number of glyphs for which
+  HBUINT16	     vertGlyphCount;      /* Number of glyphs for which
 					   * information is provided for
 					   * vertically growing variants. */
-  USHORT	     horizGlyphCount;     /* Number of glyphs for which
+  HBUINT16	     horizGlyphCount;     /* Number of glyphs for which
 					   * information is provided for
 					   * horizontally growing variants. */
 
diff --git a/src/hb-ot-maxp-table.hh b/src/hb-ot-maxp-table.hh
index 943e390..1292922 100644
--- a/src/hb-ot-maxp-table.hh
+++ b/src/hb-ot-maxp-table.hh
@@ -28,7 +28,7 @@
 #define HB_OT_MAXP_TABLE_HH
 
 #include "hb-open-type-private.hh"
-
+#include "hb-subset-plan.hh"
 
 namespace OT {
 
@@ -48,6 +48,11 @@
     return numGlyphs;
   }
 
+  inline void set_num_glyphs (unsigned int count)
+  {
+    numGlyphs.set (count);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -56,11 +61,29 @@
 			  (version.major == 0 && version.minor == 0x5000u)));
   }
 
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_blob_t *maxp_blob = OT::Sanitizer<OT::maxp>().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_maxp));
+    hb_blob_t *maxp_prime_blob = hb_blob_copy_writable_or_fail (maxp_blob);
+    hb_blob_destroy (maxp_blob);
+
+    if (unlikely (!maxp_prime_blob)) {
+      return false;
+    }
+    OT::maxp *maxp_prime = (OT::maxp *) hb_blob_get_data (maxp_prime_blob, nullptr);
+
+    maxp_prime->set_num_glyphs (plan->gids_to_retain_sorted.len);
+
+    bool result = hb_subset_plan_add_table(plan, HB_OT_TAG_maxp, maxp_prime_blob);
+    hb_blob_destroy (maxp_prime_blob);
+    return result;
+  }
+
   /* We only implement version 0.5 as none of the extra fields in version 1.0 are useful. */
   protected:
   FixedVersion<>version;		/* Version of the maxp table (0.5 or 1.0),
 					 * 0x00005000u or 0x00010000u. */
-  USHORT	numGlyphs;		/* The number of glyphs in the font. */
+  HBUINT16	numGlyphs;		/* The number of glyphs in the font. */
   public:
   DEFINE_SIZE_STATIC (6);
 };
diff --git a/src/hb-ot-name-table.hh b/src/hb-ot-name-table.hh
index 870f123..eb01333 100644
--- a/src/hb-ot-name-table.hh
+++ b/src/hb-ot-name-table.hh
@@ -42,8 +42,10 @@
 
 struct NameRecord
 {
-  static int cmp (const NameRecord *a, const NameRecord *b)
+  static int cmp (const void *pa, const void *pb)
   {
+    const NameRecord *a = (const NameRecord *) pa;
+    const NameRecord *b = (const NameRecord *) pb;
     int ret;
     ret = b->platformID.cmp (a->platformID);
     if (ret) return ret;
@@ -63,12 +65,12 @@
     return_trace (c->check_struct (this) && c->check_range ((char *) base, (unsigned int) length + offset));
   }
 
-  USHORT	platformID;	/* Platform ID. */
-  USHORT	encodingID;	/* Platform-specific encoding ID. */
-  USHORT	languageID;	/* Language ID. */
-  USHORT	nameID;		/* Name ID. */
-  USHORT	length;		/* String length (in bytes). */
-  USHORT	offset;		/* String offset from start of storage area (in bytes). */
+  HBUINT16	platformID;	/* Platform ID. */
+  HBUINT16	encodingID;	/* Platform-specific encoding ID. */
+  HBUINT16	languageID;	/* Language ID. */
+  HBUINT16	nameID;		/* Name ID. */
+  HBUINT16	length;		/* String length (in bytes). */
+  HBUINT16	offset;		/* String offset from start of storage area (in bytes). */
   public:
   DEFINE_SIZE_STATIC (12);
 };
@@ -89,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]), (hb_compare_func_t) NameRecord::cmp);
+    NameRecord *match = (NameRecord *) bsearch (&key, nameRecord, count, sizeof (nameRecord[0]), NameRecord::cmp);
 
     if (!match)
       return 0;
@@ -121,9 +123,9 @@
   }
 
   /* We only implement format 0 for now. */
-  USHORT	format;			/* Format selector (=0/1). */
-  USHORT	count;			/* Number of name records. */
-  Offset<>	stringOffset;		/* Offset to start of string storage (from start of table). */
+  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. */
   public:
   DEFINE_SIZE_ARRAY (6, nameRecord);
diff --git a/src/hb-ot-os2-table.hh b/src/hb-ot-os2-table.hh
index 4709cd6..2d9d214 100644
--- a/src/hb-ot-os2-table.hh
+++ b/src/hb-ot-os2-table.hh
@@ -49,51 +49,98 @@
     return_trace (c->check_struct (this));
   }
 
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_blob_t *os2_blob = OT::Sanitizer<OT::os2>().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_os2));
+    hb_blob_t *os2_prime_blob = hb_blob_create_sub_blob (os2_blob, 0, -1);
+    // TODO(grieger): move to hb_blob_copy_writable_or_fail
+    hb_blob_destroy (os2_blob);
+
+    OT::os2 *os2_prime = (OT::os2 *) hb_blob_get_data_writable (os2_prime_blob, nullptr);
+    if (unlikely (!os2_prime)) {
+      hb_blob_destroy (os2_prime_blob);
+      return false;
+    }
+
+    uint16_t min_cp, max_cp;
+    find_min_and_max_codepoint (plan->codepoints, &min_cp, &max_cp);
+    os2_prime->usFirstCharIndex.set (min_cp);
+    os2_prime->usLastCharIndex.set (max_cp);
+
+    bool result = hb_subset_plan_add_table(plan, HB_OT_TAG_os2, os2_prime_blob);
+    hb_blob_destroy (os2_prime_blob);
+    return result;
+  }
+
+  static inline void find_min_and_max_codepoint (const hb_prealloced_array_t<hb_codepoint_t> &codepoints,
+                                                 uint16_t *min_cp, /* OUT */
+                                                 uint16_t *max_cp  /* OUT */)
+  {
+    hb_codepoint_t min = -1, max = 0;
+
+    for (unsigned int i = 0; i < codepoints.len; i++)
+    {
+      hb_codepoint_t cp = codepoints[i];
+      if (cp < min)
+        min = cp;
+      if (cp > max)
+        max = cp;
+    }
+
+    if (min > 0xFFFF)
+      min = 0xFFFF;
+    if (max > 0xFFFF)
+      max = 0xFFFF;
+
+    *min_cp = min;
+    *max_cp = max;
+  }
+
   public:
-  USHORT	version;
+  HBUINT16	version;
 
   /* Version 0 */
-  SHORT		xAvgCharWidth;
-  USHORT	usWeightClass;
-  USHORT	usWidthClass;
-  USHORT	fsType;
-  SHORT		ySubscriptXSize;
-  SHORT		ySubscriptYSize;
-  SHORT		ySubscriptXOffset;
-  SHORT		ySubscriptYOffset;
-  SHORT		ySuperscriptXSize;
-  SHORT		ySuperscriptYSize;
-  SHORT		ySuperscriptXOffset;
-  SHORT		ySuperscriptYOffset;
-  SHORT		yStrikeoutSize;
-  SHORT		yStrikeoutPosition;
-  SHORT		sFamilyClass;
-  BYTE		panose[10];
-  ULONG		ulUnicodeRange[4];
+  HBINT16	xAvgCharWidth;
+  HBUINT16	usWeightClass;
+  HBUINT16	usWidthClass;
+  HBUINT16	fsType;
+  HBINT16	ySubscriptXSize;
+  HBINT16	ySubscriptYSize;
+  HBINT16	ySubscriptXOffset;
+  HBINT16	ySubscriptYOffset;
+  HBINT16	ySuperscriptXSize;
+  HBINT16	ySuperscriptYSize;
+  HBINT16	ySuperscriptXOffset;
+  HBINT16	ySuperscriptYOffset;
+  HBINT16	yStrikeoutSize;
+  HBINT16	yStrikeoutPosition;
+  HBINT16	sFamilyClass;
+  HBUINT8	panose[10];
+  HBUINT32	ulUnicodeRange[4];
   Tag		achVendID;
-  USHORT	fsSelection;
-  USHORT	usFirstCharIndex;
-  USHORT	usLastCharIndex;
-  SHORT		sTypoAscender;
-  SHORT		sTypoDescender;
-  SHORT		sTypoLineGap;
-  USHORT	usWinAscent;
-  USHORT	usWinDescent;
+  HBUINT16	fsSelection;
+  HBUINT16	usFirstCharIndex;
+  HBUINT16	usLastCharIndex;
+  HBINT16	sTypoAscender;
+  HBINT16	sTypoDescender;
+  HBINT16	sTypoLineGap;
+  HBUINT16	usWinAscent;
+  HBUINT16	usWinDescent;
 
   /* Version 1 */
-  //ULONG ulCodePageRange1;
-  //ULONG ulCodePageRange2;
+  //HBUINT32	ulCodePageRange1;
+  //HBUINT32	ulCodePageRange2;
 
   /* Version 2 */
-  //SHORT sxHeight;
-  //SHORT sCapHeight;
-  //USHORT  usDefaultChar;
-  //USHORT  usBreakChar;
-  //USHORT  usMaxContext;
+  //HBINT16	sxHeight;
+  //HBINT16	sCapHeight;
+  //HBUINT16	usDefaultChar;
+  //HBUINT16	usBreakChar;
+  //HBUINT16	usMaxContext;
 
   /* Version 5 */
-  //USHORT  usLowerOpticalPointSize;
-  //USHORT  usUpperOpticalPointSize;
+  //HBUINT16	usLowerOpticalPointSize;
+  //HBUINT16	usUpperOpticalPointSize;
 
   public:
   DEFINE_SIZE_STATIC (78);
diff --git a/src/hb-ot-post-macroman.hh b/src/hb-ot-post-macroman.hh
new file mode 100644
index 0000000..dbbb97e
--- /dev/null
+++ b/src/hb-ot-post-macroman.hh
@@ -0,0 +1,294 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_POST_MACROMAN_HH
+#if 0 /* Make checks happy. */
+#define HB_OT_POST_MACROMAN_HH
+#include "hb-private.hh"
+#endif
+
+
+_S(".notdef")
+_S(".null")
+_S("nonmarkingreturn")
+_S("space")
+_S("exclam")
+_S("quotedbl")
+_S("numbersign")
+_S("dollar")
+_S("percent")
+_S("ampersand")
+_S("quotesingle")
+_S("parenleft")
+_S("parenright")
+_S("asterisk")
+_S("plus")
+_S("comma")
+_S("hyphen")
+_S("period")
+_S("slash")
+_S("zero")
+_S("one")
+_S("two")
+_S("three")
+_S("four")
+_S("five")
+_S("six")
+_S("seven")
+_S("eight")
+_S("nine")
+_S("colon")
+_S("semicolon")
+_S("less")
+_S("equal")
+_S("greater")
+_S("question")
+_S("at")
+_S("A")
+_S("B")
+_S("C")
+_S("D")
+_S("E")
+_S("F")
+_S("G")
+_S("H")
+_S("I")
+_S("J")
+_S("K")
+_S("L")
+_S("M")
+_S("N")
+_S("O")
+_S("P")
+_S("Q")
+_S("R")
+_S("S")
+_S("T")
+_S("U")
+_S("V")
+_S("W")
+_S("X")
+_S("Y")
+_S("Z")
+_S("bracketleft")
+_S("backslash")
+_S("bracketright")
+_S("asciicircum")
+_S("underscore")
+_S("grave")
+_S("a")
+_S("b")
+_S("c")
+_S("d")
+_S("e")
+_S("f")
+_S("g")
+_S("h")
+_S("i")
+_S("j")
+_S("k")
+_S("l")
+_S("m")
+_S("n")
+_S("o")
+_S("p")
+_S("q")
+_S("r")
+_S("s")
+_S("t")
+_S("u")
+_S("v")
+_S("w")
+_S("x")
+_S("y")
+_S("z")
+_S("braceleft")
+_S("bar")
+_S("braceright")
+_S("asciitilde")
+_S("Adieresis")
+_S("Aring")
+_S("Ccedilla")
+_S("Eacute")
+_S("Ntilde")
+_S("Odieresis")
+_S("Udieresis")
+_S("aacute")
+_S("agrave")
+_S("acircumflex")
+_S("adieresis")
+_S("atilde")
+_S("aring")
+_S("ccedilla")
+_S("eacute")
+_S("egrave")
+_S("ecircumflex")
+_S("edieresis")
+_S("iacute")
+_S("igrave")
+_S("icircumflex")
+_S("idieresis")
+_S("ntilde")
+_S("oacute")
+_S("ograve")
+_S("ocircumflex")
+_S("odieresis")
+_S("otilde")
+_S("uacute")
+_S("ugrave")
+_S("ucircumflex")
+_S("udieresis")
+_S("dagger")
+_S("degree")
+_S("cent")
+_S("sterling")
+_S("section")
+_S("bullet")
+_S("paragraph")
+_S("germandbls")
+_S("registered")
+_S("copyright")
+_S("trademark")
+_S("acute")
+_S("dieresis")
+_S("notequal")
+_S("AE")
+_S("Oslash")
+_S("infinity")
+_S("plusminus")
+_S("lessequal")
+_S("greaterequal")
+_S("yen")
+_S("mu")
+_S("partialdiff")
+_S("summation")
+_S("product")
+_S("pi")
+_S("integral")
+_S("ordfeminine")
+_S("ordmasculine")
+_S("Omega")
+_S("ae")
+_S("oslash")
+_S("questiondown")
+_S("exclamdown")
+_S("logicalnot")
+_S("radical")
+_S("florin")
+_S("approxequal")
+_S("Delta")
+_S("guillemotleft")
+_S("guillemotright")
+_S("ellipsis")
+_S("nonbreakingspace")
+_S("Agrave")
+_S("Atilde")
+_S("Otilde")
+_S("OE")
+_S("oe")
+_S("endash")
+_S("emdash")
+_S("quotedblleft")
+_S("quotedblright")
+_S("quoteleft")
+_S("quoteright")
+_S("divide")
+_S("lozenge")
+_S("ydieresis")
+_S("Ydieresis")
+_S("fraction")
+_S("currency")
+_S("guilsinglleft")
+_S("guilsinglright")
+_S("fi")
+_S("fl")
+_S("daggerdbl")
+_S("periodcentered")
+_S("quotesinglbase")
+_S("quotedblbase")
+_S("perthousand")
+_S("Acircumflex")
+_S("Ecircumflex")
+_S("Aacute")
+_S("Edieresis")
+_S("Egrave")
+_S("Iacute")
+_S("Icircumflex")
+_S("Idieresis")
+_S("Igrave")
+_S("Oacute")
+_S("Ocircumflex")
+_S("apple")
+_S("Ograve")
+_S("Uacute")
+_S("Ucircumflex")
+_S("Ugrave")
+_S("dotlessi")
+_S("circumflex")
+_S("tilde")
+_S("macron")
+_S("breve")
+_S("dotaccent")
+_S("ring")
+_S("cedilla")
+_S("hungarumlaut")
+_S("ogonek")
+_S("caron")
+_S("Lslash")
+_S("lslash")
+_S("Scaron")
+_S("scaron")
+_S("Zcaron")
+_S("zcaron")
+_S("brokenbar")
+_S("Eth")
+_S("eth")
+_S("Yacute")
+_S("yacute")
+_S("Thorn")
+_S("thorn")
+_S("minus")
+_S("multiply")
+_S("onesuperior")
+_S("twosuperior")
+_S("threesuperior")
+_S("onehalf")
+_S("onequarter")
+_S("threequarters")
+_S("franc")
+_S("Gbreve")
+_S("gbreve")
+_S("Idotaccent")
+_S("Scedilla")
+_S("scedilla")
+_S("Cacute")
+_S("cacute")
+_S("Ccaron")
+_S("ccaron")
+_S("dcroat")
+
+
+#endif /* HB_OT_POST_MACROMAN_HH */
diff --git a/src/hb-ot-post-table.hh b/src/hb-ot-post-table.hh
index 82ab388..9e47921 100644
--- a/src/hb-ot-post-table.hh
+++ b/src/hb-ot-post-table.hh
@@ -28,7 +28,15 @@
 #define HB_OT_POST_TABLE_HH
 
 #include "hb-open-type-private.hh"
+#include "hb-dsalgs.hh"
 
+#define HB_STRING_ARRAY_NAME format1_names
+#define HB_STRING_ARRAY_LIST "hb-ot-post-macroman.hh"
+#include "hb-string-array.hh"
+#undef HB_STRING_ARRAY_LIST
+#undef HB_STRING_ARRAY_NAME
+
+#define NUM_FORMAT1_NAMES 258
 
 namespace OT {
 
@@ -45,16 +53,13 @@
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (numberOfGlyphs.sanitize (c) &&
-		  c->check_array (glyphNameIndex, sizeof (USHORT), numberOfGlyphs));
+    return_trace (glyphNameIndex.sanitize (c));
   }
 
-  USHORT	numberOfGlyphs;		/* Number of glyphs (this should be the
-					 * same as numGlyphs in 'maxp' table). */
-  USHORT	glyphNameIndex[VAR];	/* 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. */
-  BYTE		namesX[VAR];		/* Glyph names with length bytes [variable]
+  HBUINT8		namesX[VAR];		/* Glyph names with length bytes [variable]
 					 * (a Pascal string). */
 
   DEFINE_SIZE_ARRAY2 (2, glyphNameIndex, namesX);
@@ -71,12 +76,170 @@
       return_trace (false);
     if (version.to_int () == 0x00020000)
     {
-      const postV2Tail &v2 = StructAfter<postV2Tail>(*this);
+      const postV2Tail &v2 = StructAfter<postV2Tail> (*this);
       return_trace (v2.sanitize (c));
     }
     return_trace (true);
   }
 
+  struct accelerator_t
+  {
+    inline void init (hb_face_t *face)
+    {
+      blob = Sanitizer<post>().sanitize (face->reference_table (HB_OT_TAG_post));
+      const post *table = Sanitizer<post>::lock_instance (blob);
+      unsigned int table_length = hb_blob_get_length (blob);
+
+      version = table->version.to_int ();
+      index_to_offset.init ();
+      if (version != 0x00020000)
+        return;
+
+      const postV2Tail &v2 = StructAfter<postV2Tail> (*table);
+
+      glyphNameIndex = &v2.glyphNameIndex;
+      pool = &StructAfter<uint8_t> (v2.glyphNameIndex);
+
+      const uint8_t *end = (uint8_t *) table + table_length;
+      for (const uint8_t *data = pool; data < end && data + *data <= end; data += 1 + *data)
+      {
+	uint32_t *offset = index_to_offset.push ();
+	if (unlikely (!offset))
+	  break;
+	*offset = data - pool;
+      }
+    }
+    inline void fini (void)
+    {
+      index_to_offset.finish ();
+      free (gids_sorted_by_name);
+    }
+
+    inline bool get_glyph_name (hb_codepoint_t glyph,
+				char *buf, unsigned int buf_len) const
+    {
+      hb_string_t s = find_glyph_name (glyph);
+      if (!s.len)
+        return false;
+      if (!buf_len)
+	return true;
+      if (buf_len <= s.len) /* What to do with truncation? Returning false for now. */
+        return false;
+      strncpy (buf, s.bytes, s.len);
+      buf[s.len] = '\0';
+      return true;
+    }
+
+    inline bool get_glyph_from_name (const char *name, int len,
+				     hb_codepoint_t *glyph) const
+    {
+      unsigned int count = get_glyph_count ();
+      if (unlikely (!count))
+        return false;
+
+      if (len < 0)
+	len = strlen (name);
+
+      if (unlikely (!len))
+	return false;
+
+    retry:
+      uint16_t *gids = (uint16_t *) hb_atomic_ptr_get (&gids_sorted_by_name);
+
+      if (unlikely (!gids))
+      {
+	gids = (uint16_t *) malloc (count * sizeof (gids[0]));
+	if (unlikely (!gids))
+	  return false; /* Anything better?! */
+
+	for (unsigned int i = 0; i < count; i++)
+	  gids[i] = i;
+	hb_sort_r (gids, count, sizeof (gids[0]), cmp_gids, (void *) this);
+
+	if (!hb_atomic_ptr_cmpexch (&gids_sorted_by_name, nullptr, gids)) {
+	  free (gids);
+	  goto retry;
+	}
+      }
+
+      hb_string_t st (name, len);
+      const uint16_t *gid = (const uint16_t *) hb_bsearch_r (&st, gids, count, sizeof (gids[0]), cmp_key, (void *) this);
+      if (gid)
+      {
+	*glyph = *gid;
+	return true;
+      }
+
+      return false;
+    }
+
+    protected:
+
+    inline unsigned int get_glyph_count (void) const
+    {
+      if (version == 0x00010000)
+        return NUM_FORMAT1_NAMES;
+
+      if (version == 0x00020000)
+        return glyphNameIndex->len;
+
+      return 0;
+    }
+
+    static inline int cmp_gids (const void *pa, const void *pb, void *arg)
+    {
+      const accelerator_t *thiz = (const accelerator_t *) arg;
+      uint16_t a = * (const uint16_t *) pa;
+      uint16_t b = * (const uint16_t *) pb;
+      return thiz->find_glyph_name (b).cmp (thiz->find_glyph_name (a));
+    }
+
+    static inline int cmp_key (const void *pk, const void *po, void *arg)
+    {
+      const accelerator_t *thiz = (const accelerator_t *) arg;
+      const hb_string_t *key = (const hb_string_t *) pk;
+      uint16_t o = * (const uint16_t *) po;
+      return thiz->find_glyph_name (o).cmp (*key);
+    }
+
+    inline hb_string_t find_glyph_name (hb_codepoint_t glyph) const
+    {
+      if (version == 0x00010000)
+      {
+	if (glyph >= NUM_FORMAT1_NAMES)
+	  return hb_string_t ();
+
+	return format1_names (glyph);
+      }
+
+      if (version != 0x00020000 || glyph >= glyphNameIndex->len)
+	return hb_string_t ();
+
+      unsigned int index = glyphNameIndex->array[glyph];
+      if (index < NUM_FORMAT1_NAMES)
+	return format1_names (index);
+      index -= NUM_FORMAT1_NAMES;
+
+      if (index >= index_to_offset.len)
+	return hb_string_t ();
+      unsigned int offset = index_to_offset.array[index];
+
+      const uint8_t *data = pool + offset;
+      unsigned int name_length = *data;
+      data++;
+
+      return hb_string_t ((const char *) data, name_length);
+    }
+
+    private:
+    hb_blob_t *blob;
+    uint32_t version;
+    const ArrayOf<HBUINT16> *glyphNameIndex;
+    hb_prealloced_array_t<uint32_t, 1> index_to_offset;
+    const uint8_t *pool;
+    mutable uint16_t *gids_sorted_by_name;
+  };
+
   public:
   FixedVersion<>version;		/* 0x00010000 for version 1.0
 					 * 0x00020000 for version 2.0
@@ -98,16 +261,16 @@
 					 * from the value of this field. */
   FWORD		underlineThickness;	/* Suggested values for the underline
 					   thickness. */
-  ULONG		isFixedPitch;		/* Set to 0 if the font is proportionally
+  HBUINT32	isFixedPitch;		/* Set to 0 if the font is proportionally
 					 * spaced, non-zero if the font is not
 					 * proportionally spaced (i.e. monospaced). */
-  ULONG		minMemType42;		/* Minimum memory usage when an OpenType font
+  HBUINT32	minMemType42;		/* Minimum memory usage when an OpenType font
 					 * is downloaded. */
-  ULONG		maxMemType42;		/* Maximum memory usage when an OpenType font
+  HBUINT32	maxMemType42;		/* Maximum memory usage when an OpenType font
 					 * is downloaded. */
-  ULONG		minMemType1;		/* Minimum memory usage when an OpenType font
+  HBUINT32	minMemType1;		/* Minimum memory usage when an OpenType font
 					 * is downloaded as a Type 1 font. */
-  ULONG		maxMemType1;		/* Maximum memory usage when an OpenType font
+  HBUINT32	maxMemType1;		/* Maximum memory usage when an OpenType font
 					 * is downloaded as a Type 1 font. */
 /*postV2Tail	v2[VAR];*/
   DEFINE_SIZE_STATIC (32);
diff --git a/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh
index d97d285..5a257f0 100644
--- a/src/hb-ot-shape-complex-arabic-fallback.hh
+++ b/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -73,11 +73,11 @@
   }
 
   if (!num_glyphs)
-    return NULL;
+    return nullptr;
 
   /* Bubble-sort or something equally good!
    * May not be good-enough for presidential candidate interviews, but good-enough for us... */
-  hb_stable_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]);
+  hb_stable_sort (&glyphs[0], num_glyphs, (int(*)(const OT::GlyphID*, const OT::GlyphID *)) OT::GlyphID::cmp, &substitutes[0]);
 
   OT::Supplier<OT::GlyphID> glyphs_supplier      (glyphs, num_glyphs);
   OT::Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs);
@@ -94,7 +94,7 @@
   c.end_serialize ();
   /* TODO sanitize the results? */
 
-  return ret ? c.copy<OT::SubstLookup> () : NULL;
+  return ret ? c.copy<OT::SubstLookup> () : nullptr;
 }
 
 static OT::SubstLookup *
@@ -126,7 +126,7 @@
     first_glyphs_indirection[num_first_glyphs] = first_glyph_idx;
     num_first_glyphs++;
   }
-  hb_stable_sort (&first_glyphs[0], num_first_glyphs, OT::GlyphID::cmp, &first_glyphs_indirection[0]);
+  hb_stable_sort (&first_glyphs[0], num_first_glyphs, (int(*)(const OT::GlyphID*, const OT::GlyphID *)) OT::GlyphID::cmp, &first_glyphs_indirection[0]);
 
   /* Now that the first-glyphs are sorted, walk again, populate ligatures. */
   for (unsigned int i = 0; i < num_first_glyphs; i++)
@@ -153,7 +153,7 @@
   }
 
   if (!num_ligatures)
-    return NULL;
+    return nullptr;
 
   OT::Supplier<OT::GlyphID>   first_glyphs_supplier                      (first_glyphs, num_first_glyphs);
   OT::Supplier<unsigned int > ligature_per_first_glyph_count_supplier    (ligature_per_first_glyph_count_list, num_first_glyphs);
@@ -177,7 +177,7 @@
   c.end_serialize ();
   /* TODO sanitize the results? */
 
-  return ret ? c.copy<OT::SubstLookup> () : NULL;
+  return ret ? c.copy<OT::SubstLookup> () : nullptr;
 }
 
 static OT::SubstLookup *
@@ -237,8 +237,8 @@
     return false;
 
   const Manifest &manifest = reinterpret_cast<const Manifest&> (arabic_win1256_gsub_lookups.manifest);
-  ASSERT_STATIC (sizeof (arabic_win1256_gsub_lookups.manifestData) / sizeof (ManifestLookup)
-		 <= ARABIC_FALLBACK_MAX_LOOKUPS);
+  static_assert (sizeof (arabic_win1256_gsub_lookups.manifestData) / sizeof (ManifestLookup)
+		 <= ARABIC_FALLBACK_MAX_LOOKUPS, "");
   /* TODO sanitize the table? */
 
   unsigned j = 0;
@@ -271,7 +271,7 @@
 				   const hb_ot_shape_plan_t *plan,
 				   hb_font_t *font)
 {
-  ASSERT_STATIC (ARRAY_LENGTH_CONST(arabic_fallback_features) <= ARABIC_FALLBACK_MAX_LOOKUPS);
+  static_assert ((ARRAY_LENGTH_CONST(arabic_fallback_features) <= ARABIC_FALLBACK_MAX_LOOKUPS), "");
   unsigned int j = 0;
   for (unsigned int i = 0; i < ARRAY_LENGTH(arabic_fallback_features) ; i++)
   {
@@ -340,7 +340,7 @@
 			    hb_font_t *font,
 			    hb_buffer_t *buffer)
 {
-  OT::hb_apply_context_t c (0, font, buffer);
+  OT::hb_ot_apply_context_t c (0, font, buffer);
   for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
     if (fallback_plan->lookup_array[i]) {
       c.set_lookup_mask (fallback_plan->mask_array[i]);
diff --git a/src/hb-ot-shape-complex-arabic-table.hh b/src/hb-ot-shape-complex-arabic-table.hh
index 736c7f7..cd6e405 100644
--- a/src/hb-ot-shape-complex-arabic-table.hh
+++ b/src/hb-ot-shape-complex-arabic-table.hh
@@ -6,10 +6,10 @@
  *
  * on files with these headers:
  *
- * # ArabicShaping-9.0.0.txt
- * # Date: 2016-02-24, 22:25:00 GMT [RP]
- * # Blocks-9.0.0.txt
- * # Date: 2016-02-05, 23:48:00 GMT [KW]
+ * # ArabicShaping-10.0.0.txt
+ * # Date: 2017-02-16, 00:00:00 GMT [RP, KW]
+ * # Blocks-10.0.0.txt
+ * # Date: 2017-04-12, 17:30:00 GMT [KW]
  * UnicodeData.txt does not have a header.
  */
 
@@ -72,7 +72,10 @@
   /* Mandaic */
 
   /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,U,U,U,X,X,X,X,X,X,X,
-  /* 0860 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+
+  /* Syriac Supplement */
+
+  /* 0860 */ D,U,D,D,D,D,U,R,D,R,R,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
   /* 0880 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
 
   /* Arabic Extended-A */
@@ -130,7 +133,7 @@
   /* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,
   /* 1E940 */ D,D,D,D,
 
-}; /* Table items: 1214; occupancy: 54% */
+}; /* Table items: 1214; occupancy: 55% */
 
 
 static unsigned int
@@ -139,28 +142,28 @@
   switch (u >> 12)
   {
     case 0x0u:
-      if (hb_in_range (u, 0x0600u, 0x08E2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x0600u, 0x08E2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u];
       break;
 
     case 0x1u:
-      if (hb_in_range (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u];
       break;
 
     case 0x2u:
-      if (hb_in_range (u, 0x200Cu, 0x2069u)) return joining_table[u - 0x200Cu + joining_offset_0x200cu];
+      if (hb_in_range<hb_codepoint_t> (u, 0x200Cu, 0x2069u)) return joining_table[u - 0x200Cu + joining_offset_0x200cu];
       break;
 
     case 0xAu:
-      if (hb_in_range (u, 0xA840u, 0xA873u)) return joining_table[u - 0xA840u + joining_offset_0xa840u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xA840u, 0xA873u)) return joining_table[u - 0xA840u + joining_offset_0xa840u];
       break;
 
     case 0x10u:
-      if (hb_in_range (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u];
-      if (hb_in_range (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u];
       break;
 
     case 0x1Eu:
-      if (hb_in_range (u, 0x1E900u, 0x1E943u)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1E900u, 0x1E943u)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u];
       break;
 
     default:
diff --git a/src/hb-ot-shape-complex-arabic-win1256.hh b/src/hb-ot-shape-complex-arabic-win1256.hh
index e70c48f..54c6cdc 100644
--- a/src/hb-ot-shape-complex-arabic-win1256.hh
+++ b/src/hb-ot-shape-complex-arabic-win1256.hh
@@ -43,16 +43,16 @@
 #define OT_TABLE_END			}
 #define OT_LABEL_START(Name)		unsigned char Name[
 #define OT_LABEL_END			];
-#define OT_BYTE(u8)			+1/*byte*/
-#define OT_USHORT(u16)			+2/*bytes*/
+#define OT_UINT8(u8)			+1/*byte*/
+#define OT_UINT16(u16)			+2/*bytes*/
 #else
 #undef  OT_MEASURE
 #define OT_TABLE_START			TABLE_NAME = {
 #define OT_TABLE_END			};
 #define OT_LABEL_START(Name)		{
 #define OT_LABEL_END			},
-#define OT_BYTE(u8)			(u8),
-#define OT_USHORT(u16)			(unsigned char)((u16)>>8), (unsigned char)((u16)&0xFFu),
+#define OT_UINT8(u8)			(u8),
+#define OT_UINT16(u16)			(unsigned char)((u16)>>8), (unsigned char)((u16)&0xFFu),
 #define OT_COUNT(Name, ItemSize)	((unsigned int) sizeof(((struct TABLE_NAME*)0)->Name) \
 					 / (unsigned int)(ItemSize) \
 					 /* OT_ASSERT it's divisible (and positive). */)
@@ -80,24 +80,24 @@
  */
 
 #define OT_TAG(a,b,c,d) \
-	OT_BYTE(a) OT_BYTE(b) OT_BYTE(c) OT_BYTE(d)
+	OT_UINT8(a) OT_UINT8(b) OT_UINT8(c) OT_UINT8(d)
 
 #define OT_OFFSET(From, To) /* Offset from From to To in bytes */ \
-	OT_USHORT(OT_DISTANCE(From, To))
+	OT_UINT16(OT_DISTANCE(From, To))
 
 #define OT_GLYPHID /* GlyphID */ \
-	OT_USHORT
+	OT_UINT16
 
 #define OT_UARRAY(Name, Items) \
 	OT_LABEL_START(Name) \
-	OT_USHORT(OT_COUNT(Name##Data, 2)) \
+	OT_UINT16(OT_COUNT(Name##Data, 2)) \
 	OT_LABEL(Name##Data) \
 	Items \
 	OT_LABEL_END
 
 #define OT_UHEADLESSARRAY(Name, Items) \
 	OT_LABEL_START(Name) \
-	OT_USHORT(OT_COUNT(Name##Data, 2) + 1) \
+	OT_UINT16(OT_COUNT(Name##Data, 2) + 1) \
 	OT_LABEL(Name##Data) \
 	Items \
 	OT_LABEL_END
@@ -111,19 +111,19 @@
 
 #define OT_LOOKUP(Name, LookupType, LookupFlag, SubLookupOffsets) \
 	OT_LABEL_START(Name) \
-	OT_USHORT(LookupType) \
-	OT_USHORT(LookupFlag) \
+	OT_UINT16(LookupType) \
+	OT_UINT16(LookupFlag) \
 	OT_LABEL_END \
 	OT_UARRAY(Name##SubLookupOffsetsArray, OT_LIST(SubLookupOffsets))
 
 #define OT_SUBLOOKUP(Name, SubFormat, Items) \
 	OT_LABEL_START(Name) \
-	OT_USHORT(SubFormat) \
+	OT_UINT16(SubFormat) \
 	Items
 
 #define OT_COVERAGE1(Name, Items) \
 	OT_LABEL_START(Name) \
-	OT_USHORT(1) \
+	OT_UINT16(1) \
 	OT_LABEL_END \
 	OT_UARRAY(Name##Glyphs, OT_LIST(Items))
 
@@ -174,7 +174,7 @@
 /* Table manifest. */
 #define MANIFEST(Items) \
 	OT_LABEL_START(manifest) \
-	OT_USHORT(OT_COUNT(manifestData, 6)) \
+	OT_UINT16(OT_COUNT(manifestData, 6)) \
 	OT_LABEL(manifestData) \
 	Items \
 	OT_LABEL_END
@@ -304,8 +304,8 @@
 #undef OT_TABLE_END
 #undef OT_LABEL_START
 #undef OT_LABEL_END
-#undef OT_BYTE
-#undef OT_USHORT
+#undef OT_UINT8
+#undef OT_UINT16
 #undef OT_DISTANCE
 #undef OT_COUNT
 
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index 57ffc1d..47961bf 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -24,24 +24,21 @@
  * Google Author(s): Behdad Esfahbod
  */
 
+#include "hb-private.hh"
+#include "hb-debug.hh"
 #include "hb-ot-shape-complex-arabic-private.hh"
 #include "hb-ot-shape-private.hh"
 
 
-#ifndef HB_DEBUG_ARABIC
-#define HB_DEBUG_ARABIC (HB_DEBUG+0)
-#endif
-
-
 /* buffer var allocations */
 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
 
 #define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0
 
 /* See:
- * https://github.com/behdad/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff57823d#commitcomment-14248516 */
+ * https://github.com/harfbuzz/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff57823d#commitcomment-14248516 */
 #define HB_ARABIC_GENERAL_CATEGORY_IS_WORD(gen_cat) \
-	(FLAG_SAFE (gen_cat) & \
+	(FLAG_UNSAFE (gen_cat) & \
 	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) | \
 	  FLAG (HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE) | \
 	  /*FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |*/ \
@@ -90,7 +87,7 @@
   if (likely (j_type != JOINING_TYPE_X))
     return j_type;
 
-  return (FLAG_SAFE(gen_cat) &
+  return (FLAG_UNSAFE(gen_cat) &
 	  (FLAG(HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) |
 	   FLAG(HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
 	   FLAG(HB_UNICODE_GENERAL_CATEGORY_FORMAT))
@@ -199,6 +196,9 @@
    * work.  However, testing shows that rlig and calt are applied
    * together for Mongolian in Uniscribe.  As such, we only add a
    * pause for Arabic, not other scripts.
+   *
+   * A pause after calt is required to make KFGQPC Uthmanic Script HAFS
+   * work correctly.  See https://github.com/harfbuzz/harfbuzz/issues/505
    */
 
   map->add_gsub_pause (nuke_joiners);
@@ -209,20 +209,23 @@
   map->add_global_bool_feature (HB_TAG('c','c','m','p'));
   map->add_global_bool_feature (HB_TAG('l','o','c','l'));
 
-  map->add_gsub_pause (NULL);
+  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_gsub_pause (NULL);
+    map->add_gsub_pause (nullptr);
   }
 
   map->add_feature (HB_TAG('r','l','i','g'), 1, F_GLOBAL|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->add_gsub_pause (nullptr);
 
   /* The spec includes 'cswh'.  Earlier versions of Windows
    * used to enable this by default, but testing suggests
@@ -232,7 +235,6 @@
    * Note that IranNastaliq uses this feature extensively
    * to fixup broken glyph sequences.  Oh well...
    * Test case: U+0643,U+0640,U+0631. */
-  //map->add_gsub_pause (NULL);
   //map->add_global_bool_feature (HB_TAG('c','s','w','h'));
   map->add_global_bool_feature (HB_TAG('m','s','e','t'));
 }
@@ -260,7 +262,7 @@
 {
   arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (arabic_shape_plan_t));
   if (unlikely (!arabic_plan))
-    return NULL;
+    return nullptr;
 
   arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC;
   arabic_plan->has_stch = !!plan->map.get_1_mask (HB_TAG ('s','t','c','h'));
@@ -316,7 +318,10 @@
     const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
 
     if (entry->prev_action != NONE && prev != (unsigned int) -1)
+    {
       info[prev].arabic_shaping_action() = entry->prev_action;
+      buffer->unsafe_to_break (prev, i + 1);
+    }
 
     info[i].arabic_shaping_action() = entry->curr_action;
 
@@ -345,7 +350,7 @@
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 1; i < count; i++)
-    if (unlikely (hb_in_range (info[i].codepoint, 0x180Bu, 0x180Du)))
+    if (unlikely (hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x180Bu, 0x180Du)))
       info[i].arabic_shaping_action() = info[i - 1].arabic_shaping_action();
 }
 
@@ -404,7 +409,7 @@
   {
     /* This sucks.  We need a font to build the fallback plan... */
     fallback_plan = arabic_fallback_plan_create (plan, font);
-    if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (arabic_plan))->fallback_plan, NULL, fallback_plan))) {
+    if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (arabic_plan))->fallback_plan, nullptr, fallback_plan))) {
       arabic_fallback_plan_destroy (fallback_plan);
       goto retry;
     }
@@ -524,11 +529,11 @@
       }
       i++; // Don't touch i again.
 
-      DEBUG_MSG (ARABIC, NULL, "%s stretch at (%d,%d,%d)",
+      DEBUG_MSG (ARABIC, nullptr, "%s stretch at (%d,%d,%d)",
 		 step == MEASURE ? "measuring" : "cutting", context, start, end);
-      DEBUG_MSG (ARABIC, NULL, "rest of word:    count=%d width %d", start - context, w_total);
-      DEBUG_MSG (ARABIC, NULL, "fixed tiles:     count=%d width=%d", n_fixed, w_fixed);
-      DEBUG_MSG (ARABIC, NULL, "repeating tiles: count=%d width=%d", n_repeating, w_repeating);
+      DEBUG_MSG (ARABIC, nullptr, "rest of word:    count=%d width %d", start - context, w_total);
+      DEBUG_MSG (ARABIC, nullptr, "fixed tiles:     count=%d width=%d", n_fixed, w_fixed);
+      DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%d", n_repeating, w_repeating);
 
       /* Number of additional times to repeat each repeating tile. */
       int n_copies = 0;
@@ -551,10 +556,11 @@
       if (step == MEASURE)
       {
 	extra_glyphs_needed += n_copies * n_repeating;
-	DEBUG_MSG (ARABIC, NULL, "will add extra %d copies of repeating tiles", n_copies);
+	DEBUG_MSG (ARABIC, nullptr, "will add extra %d copies of repeating tiles", n_copies);
       }
       else
       {
+	buffer->unsafe_to_break (context, end);
 	hb_position_t x_offset = 0;
 	for (unsigned int k = end; k > start; k--)
 	{
@@ -564,7 +570,7 @@
 	  if (info[k - 1].arabic_shaping_action() == STCH_REPEATING)
 	    repeat += n_copies;
 
-	  DEBUG_MSG (ARABIC, NULL, "appending %d copies of glyph %d; j=%d",
+	  DEBUG_MSG (ARABIC, nullptr, "appending %d copies of glyph %d; j=%d",
 		     repeat, info[k - 1].codepoint, j);
 	  for (unsigned int n = 0; n < repeat; n++)
 	  {
@@ -605,20 +611,111 @@
   HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
 }
 
+/* http://www.unicode.org/reports/tr53/tr53-1.pdf */
+
+static hb_codepoint_t
+modifier_combining_marks[] =
+{
+  0x0654u, /* ARABIC HAMZA ABOVE */
+  0x0655u, /* ARABIC HAMZA BELOW */
+  0x0658u, /* ARABIC MARK NOON GHUNNA */
+  0x06DCu, /* ARABIC SMALL HIGH SEEN */
+  0x06E3u, /* ARABIC SMALL LOW SEEN */
+  0x06E7u, /* ARABIC SMALL HIGH YEH */
+  0x06E8u, /* ARABIC SMALL HIGH NOON */
+  0x08F3u, /* ARABIC SMALL HIGH WAW */
+};
+
+static inline bool
+info_is_mcm (const hb_glyph_info_t &info)
+{
+  hb_codepoint_t u = info.codepoint;
+  for (unsigned int i = 0; i < ARRAY_LENGTH (modifier_combining_marks); i++)
+    if (u == modifier_combining_marks[i])
+      return true;
+  return false;
+}
+
+static void
+reorder_marks_arabic (const hb_ot_shape_plan_t *plan,
+		      hb_buffer_t              *buffer,
+		      unsigned int              start,
+		      unsigned int              end)
+{
+  hb_glyph_info_t *info = buffer->info;
+
+  DEBUG_MSG (ARABIC, buffer, "Reordering marks from %d to %d", start, end);
+
+  unsigned int i = start;
+  for (unsigned int cc = 220; cc <= 230; cc += 10)
+  {
+    DEBUG_MSG (ARABIC, buffer, "Looking for %d's starting at %d", cc, i);
+    while (i < end && info_cc(info[i]) < cc)
+      i++;
+    DEBUG_MSG (ARABIC, buffer, "Looking for %d's stopped at %d", cc, i);
+
+    if (i == end)
+      break;
+
+    if (info_cc(info[i]) > cc)
+      continue;
+
+    unsigned int j = i;
+    while (j < end && info_cc(info[j]) == cc && info_is_mcm (info[j]))
+      j++;
+
+    if (i == j)
+      continue;
+
+    DEBUG_MSG (ARABIC, buffer, "Found %d's from %d to %d", cc, i, j);
+
+    /* Shift it! */
+    DEBUG_MSG (ARABIC, buffer, "Shifting %d's: %d %d", cc, i, j);
+    hb_glyph_info_t temp[HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS];
+    assert (j - i <= ARRAY_LENGTH (temp));
+    buffer->merge_clusters (start, j);
+    memmove (temp, &info[i], (j - i) * sizeof (hb_glyph_info_t));
+    memmove (&info[start + j - i], &info[start], (i - start) * sizeof (hb_glyph_info_t));
+    memmove (&info[start], temp, (j - i) * sizeof (hb_glyph_info_t));
+
+    /* Renumber CC such that the reordered sequence is still sorted.
+     * 22 and 26 are chosen because they are smaller than all Arabic categories,
+     * and are folded back to 220/230 respectively during fallback mark positioning.
+     *
+     * We do this because the CGJ-handling logic in the normalizer relies on
+     * mark sequences having an increasing order even after this reordering.
+     * https://github.com/harfbuzz/harfbuzz/issues/554
+     * This, however, does break some obscure sequences, where the normalizer
+     * might compose a sequence that it should not.  For example, in the seequence
+     * ALEF, HAMZAH, MADDAH, we should NOT try to compose ALEF+MADDAH, but with this
+     * renumbering, we will.
+     */
+    unsigned int new_start = start + j - i;
+    unsigned int new_cc = cc == 220 ? HB_MODIFIED_COMBINING_CLASS_CCC22 : HB_MODIFIED_COMBINING_CLASS_CCC26;
+    while (start < new_start)
+    {
+      _hb_glyph_info_set_modified_combining_class (&info[start], new_cc);
+      start++;
+    }
+
+    i = j;
+  }
+}
+
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
 {
-  "arabic",
   collect_features_arabic,
-  NULL, /* override_features */
+  nullptr, /* override_features */
   data_create_arabic,
   data_destroy_arabic,
-  NULL, /* preprocess_text */
+  nullptr, /* preprocess_text */
   postprocess_glyphs_arabic,
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
-  NULL, /* decompose */
-  NULL, /* compose */
+  nullptr, /* decompose */
+  nullptr, /* compose */
   setup_masks_arabic,
-  NULL, /* disable_otl */
+  nullptr, /* disable_otl */
+  reorder_marks_arabic,
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
   true, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-default.cc b/src/hb-ot-shape-complex-default.cc
index 42830ab..68a62a1 100644
--- a/src/hb-ot-shape-complex-default.cc
+++ b/src/hb-ot-shape-complex-default.cc
@@ -29,18 +29,18 @@
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
 {
-  "default",
-  NULL, /* collect_features */
-  NULL, /* override_features */
-  NULL, /* data_create */
-  NULL, /* data_destroy */
-  NULL, /* preprocess_text */
-  NULL, /* postprocess_glyphs */
+  nullptr, /* collect_features */
+  nullptr, /* override_features */
+  nullptr, /* data_create */
+  nullptr, /* data_destroy */
+  nullptr, /* preprocess_text */
+  nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
-  NULL, /* decompose */
-  NULL, /* compose */
-  NULL, /* setup_masks */
-  NULL, /* disable_otl */
+  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-hangul.cc b/src/hb-ot-shape-complex-hangul.cc
index af50565..7508c22 100644
--- a/src/hb-ot-shape-complex-hangul.cc
+++ b/src/hb-ot-shape-complex-hangul.cc
@@ -80,7 +80,7 @@
 {
   hangul_shape_plan_t *hangul_plan = (hangul_shape_plan_t *) calloc (1, sizeof (hangul_shape_plan_t));
   if (unlikely (!hangul_plan))
-    return NULL;
+    return nullptr;
 
   for (unsigned int i = 0; i < HANGUL_FEATURE_COUNT; i++)
     hangul_plan->mask_array[i] = plan->map.get_1_mask (hangul_features[i]);
@@ -105,16 +105,16 @@
 #define NCount (VCount * TCount)
 #define SCount (LCount * NCount)
 
-#define isCombiningL(u) (hb_in_range ((u), LBase, LBase+LCount-1))
-#define isCombiningV(u) (hb_in_range ((u), VBase, VBase+VCount-1))
-#define isCombiningT(u) (hb_in_range ((u), TBase+1, TBase+TCount-1))
-#define isCombinedS(u) (hb_in_range ((u), SBase, SBase+SCount-1))
+#define isCombiningL(u) (hb_in_range<hb_codepoint_t> ((u), LBase, LBase+LCount-1))
+#define isCombiningV(u) (hb_in_range<hb_codepoint_t> ((u), VBase, VBase+VCount-1))
+#define isCombiningT(u) (hb_in_range<hb_codepoint_t> ((u), TBase+1, TBase+TCount-1))
+#define isCombinedS(u) (hb_in_range<hb_codepoint_t> ((u), SBase, SBase+SCount-1))
 
-#define isL(u) (hb_in_ranges ((u), 0x1100u, 0x115Fu, 0xA960u, 0xA97Cu))
-#define isV(u) (hb_in_ranges ((u), 0x1160u, 0x11A7u, 0xD7B0u, 0xD7C6u))
-#define isT(u) (hb_in_ranges ((u), 0x11A8u, 0x11FFu, 0xD7CBu, 0xD7FBu))
+#define isL(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1100u, 0x115Fu, 0xA960u, 0xA97Cu))
+#define isV(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1160u, 0x11A7u, 0xD7B0u, 0xD7C6u))
+#define isT(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x11A8u, 0x11FFu, 0xD7CBu, 0xD7FBu))
 
-#define isHangulTone(u) (hb_in_range ((u), 0x302Eu, 0x302Fu))
+#define isHangulTone(u) (hb_in_range<hb_codepoint_t> ((u), 0x302Eu, 0x302Fu))
 
 /* buffer var allocations */
 #define hangul_shaping_feature() complex_var_u8_0() /* hangul jamo shaping feature */
@@ -202,6 +202,7 @@
       if (start < end && end == buffer->out_len)
       {
 	/* Tone mark follows a valid syllable; move it in front, unless it's zero width. */
+        buffer->unsafe_to_break_from_outbuffer (start, buffer->idx);
 	buffer->next_glyph ();
 	if (!is_zero_width_char (font, u))
 	{
@@ -258,6 +259,7 @@
 	  else
 	    t = 0; /* The next character was not a trailing jamo. */
 	}
+	buffer->unsafe_to_break (buffer->idx, buffer->idx + (t ? 3 : 2));
 
 	/* We've got a syllable <L,V,T?>; see if it can potentially be composed. */
 	if (isCombiningL (l) && isCombiningV (v) && (t == 0 || isCombiningT (t)))
@@ -322,6 +324,8 @@
 	  end = start + 1;
 	  continue;
 	}
+	else
+	  buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */
       }
 
       /* Otherwise, decompose if font doesn't support <LV> or <LVT>,
@@ -368,6 +372,8 @@
 	    buffer->merge_out_clusters (start, end);
 	  continue;
 	}
+	else if ((!tindex && buffer->idx + 1 < count && isT (buffer->cur(+1).codepoint)))
+	  buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */
       }
 
       if (has_glyph)
@@ -408,18 +414,18 @@
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hangul =
 {
-  "hangul",
   collect_features_hangul,
   override_features_hangul,
   data_create_hangul,
   data_destroy_hangul,
   preprocess_text_hangul,
-  NULL, /* postprocess_glyphs */
+  nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
-  NULL, /* decompose */
-  NULL, /* compose */
+  nullptr, /* decompose */
+  nullptr, /* compose */
   setup_masks_hangul,
-  NULL, /* disable_otl */
+  nullptr, /* disable_otl */
+  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 96f2494..34cf28b 100644
--- a/src/hb-ot-shape-complex-hebrew.cc
+++ b/src/hb-ot-shape-complex-hebrew.cc
@@ -161,7 +161,7 @@
    * 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/behdad/harfbuzz/issues/347#issuecomment-267838368
+   * https://github.com/harfbuzz/harfbuzz/issues/347#issuecomment-267838368
    */
   return plan->map.chosen_script[1] != HB_TAG ('h','e','b','r');
 }
@@ -169,18 +169,18 @@
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew =
 {
-  "hebrew",
-  NULL, /* collect_features */
-  NULL, /* override_features */
-  NULL, /* data_create */
-  NULL, /* data_destroy */
-  NULL, /* preprocess_text */
-  NULL, /* postprocess_glyphs */
+  nullptr, /* collect_features */
+  nullptr, /* override_features */
+  nullptr, /* data_create */
+  nullptr, /* data_destroy */
+  nullptr, /* preprocess_text */
+  nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
-  NULL, /* decompose */
+  nullptr, /* decompose */
   compose_hebrew,
-  NULL, /* setup_masks */
+  nullptr, /* setup_masks */
   disable_otl_hebrew,
+  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
new file mode 100644
index 0000000..f3cea22
--- /dev/null
+++ b/src/hb-ot-shape-complex-indic-machine.hh
@@ -0,0 +1,1319 @@
+
+#line 1 "hb-ot-shape-complex-indic-machine.rl"
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
+
+#include "hb-private.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, 
+	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, 
+	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, 
+	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, 
+	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, 
+	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, 5, 1, 1, 5, 1, 1, 5, 
+	10, 5, 10, 5, 10, 5, 10, 5, 
+	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, 
+	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, 
+	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, 
+	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
+};
+
+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
+};
+
+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
+};
+
+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, 
+	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, 
+	2, 0, 0, 2, 0, 0, 2, 0, 
+	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, 
+	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, 0, 0, 2, 0, 0, 2, 0, 
+	0, 23, 2, 0, 0, 0, 0, 0, 
+	24
+};
+
+static const char _indic_syllable_machine_to_state_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 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
+};
+
+static const char _indic_syllable_machine_from_state_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 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
+};
+
+static const short _indic_syllable_machine_eof_trans[] = {
+	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, 1, 1, 1, 1, 1, 
+	1, 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, 73, 73, 
+	77, 77, 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, 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, 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
+};
+
+static const int indic_syllable_machine_start = 166;
+static const int indic_syllable_machine_first_final = 166;
+static const int indic_syllable_machine_error = -1;
+
+static const int indic_syllable_machine_en_main = 166;
+
+
+#line 36 "hb-ot-shape-complex-indic-machine.rl"
+
+
+
+#line 92 "hb-ot-shape-complex-indic-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++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  
+#line 1137 "hb-ot-shape-complex-indic-machine.hh"
+	{
+	cs = indic_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 113 "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"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const short *_inds;
+	if ( p == pe )
+		goto _test_eof;
+_resume:
+	switch ( _indic_syllable_machine_from_state_actions[cs] ) {
+	case 10:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 1168 "hb-ot-shape-complex-indic-machine.hh"
+	}
+
+	_keys = _indic_syllable_machine_trans_keys + (cs<<1);
+	_inds = _indic_syllable_machine_indicies + _indic_syllable_machine_index_offsets[cs];
+
+	_slen = _indic_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].indic_category()) &&
+		( info[p].indic_category()) <= _keys[1] ?
+		( info[p].indic_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+	cs = _indic_syllable_machine_trans_targs[_trans];
+
+	if ( _indic_syllable_machine_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _indic_syllable_machine_trans_actions[_trans] ) {
+	case 2:
+#line 1 "NONE"
+	{te = p+1;}
+	break;
+	case 14:
+#line 83 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (consonant_syllable); }}
+	break;
+	case 16:
+#line 84 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (vowel_syllable); }}
+	break;
+	case 21:
+#line 85 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (standalone_cluster); }}
+	break;
+	case 24:
+#line 86 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (symbol_cluster); }}
+	break;
+	case 18:
+#line 87 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (broken_cluster); }}
+	break;
+	case 11:
+#line 88 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (non_indic_cluster); }}
+	break;
+	case 13:
+#line 83 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (consonant_syllable); }}
+	break;
+	case 15:
+#line 84 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (vowel_syllable); }}
+	break;
+	case 20:
+#line 85 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (standalone_cluster); }}
+	break;
+	case 23:
+#line 86 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (symbol_cluster); }}
+	break;
+	case 17:
+#line 87 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (broken_cluster); }}
+	break;
+	case 19:
+#line 88 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (non_indic_cluster); }}
+	break;
+	case 1:
+#line 83 "hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
+	break;
+	case 3:
+#line 84 "hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (vowel_syllable); }}
+	break;
+	case 7:
+#line 85 "hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (standalone_cluster); }}
+	break;
+	case 8:
+#line 86 "hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (symbol_cluster); }}
+	break;
+	case 4:
+#line 87 "hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
+	break;
+	case 5:
+#line 1 "NONE"
+	{	switch( act ) {
+	case 1:
+	{{p = ((te))-1;} found_syllable (consonant_syllable); }
+	break;
+	case 5:
+	{{p = ((te))-1;} found_syllable (broken_cluster); }
+	break;
+	case 6:
+	{{p = ((te))-1;} found_syllable (non_indic_cluster); }
+	break;
+	}
+	}
+	break;
+	case 22:
+#line 1 "NONE"
+	{te = p+1;}
+#line 83 "hb-ot-shape-complex-indic-machine.rl"
+	{act = 1;}
+	break;
+	case 6:
+#line 1 "NONE"
+	{te = p+1;}
+#line 87 "hb-ot-shape-complex-indic-machine.rl"
+	{act = 5;}
+	break;
+	case 12:
+#line 1 "NONE"
+	{te = p+1;}
+#line 88 "hb-ot-shape-complex-indic-machine.rl"
+	{act = 6;}
+	break;
+#line 1291 "hb-ot-shape-complex-indic-machine.hh"
+	}
+
+_again:
+	switch ( _indic_syllable_machine_to_state_actions[cs] ) {
+	case 9:
+#line 1 "NONE"
+	{ts = 0;}
+	break;
+#line 1300 "hb-ot-shape-complex-indic-machine.hh"
+	}
+
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _indic_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _indic_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	}
+
+#line 122 "hb-ot-shape-complex-indic-machine.rl"
+
+}
+
+#endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index 86a7ceb..0ea91c0 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -38,7 +38,6 @@
 %%{
 
 # Same order as enum indic_category_t.  Not sure how to avoid duplication.
-X    = 0;
 C    = 1;
 V    = 2;
 N    = 3;
@@ -47,38 +46,35 @@
 ZWJ  = 6;
 M    = 7;
 SM   = 8;
-VD   = 9;
 A    = 10;
 PLACEHOLDER = 11;
 DOTTEDCIRCLE = 12;
 RS    = 13;
-Coeng = 14;
 Repha = 15;
 Ra    = 16;
 CM    = 17;
 Symbol= 18;
+CS    = 19;
 
 c = (C | Ra);			# is_consonant
 n = ((ZWNJ?.RS)? (N.N?)?);	# is_consonant_modifier
 z = ZWJ|ZWNJ;			# is_joiner
-h = H | Coeng;			# is_halant_or_coeng
 reph = (Ra H | Repha);		# possible reph
 
 cn = c.ZWJ?.n?;
 forced_rakar = ZWJ H ZWJ Ra;
 symbol = Symbol.N?;
 matra_group = z{0,3}.M.N?.(H | forced_rakar)?;
-syllable_tail = (z?.SM.SM?.ZWNJ?)? A{0,3}? VD{0,2};
-place_holder = PLACEHOLDER | DOTTEDCIRCLE;
-halant_group = (z?.h.(ZWJ.N?)?);
-final_halant_group = halant_group | h.ZWNJ;
+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}) (Coeng (cn|V))?;
+halant_or_matra_group = (final_halant_group | (H.ZWJ)? matra_group{0,4});
 
 
-consonant_syllable =	Repha? (cn.halant_group){0,4} cn medial_group 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? PLACEHOLDER | reph? DOTTEDCIRCLE).n? (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;
 symbol_cluster = 	symbol syllable_tail;
 broken_cluster =	reph? n? (halant_group.cn){0,4} medial_group halant_or_matra_group syllable_tail;
 other =			any;
diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index 5879c3e..867b936 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -34,6 +34,11 @@
 #include "hb-ot-shape-private.hh" /* XXX Remove */
 
 
+/* buffer var allocations */
+#define indic_category() complex_var_u8_0() /* indic_category_t */
+#define indic_position() complex_var_u8_1() /* indic_position_t */
+
+
 #define INDIC_TABLE_ELEMENT_TYPE uint16_t
 
 /* Cateories used in the OpenType spec:
@@ -51,7 +56,7 @@
   OT_ZWJ = 6,
   OT_M = 7,
   OT_SM = 8,
-  OT_VD = 9,
+  /* OT_VD = 9, UNUSED; we use OT_A instead. */
   OT_A = 10,
   OT_PLACEHOLDER = 11,
   OT_DOTTEDCIRCLE = 12,
@@ -60,7 +65,8 @@
   OT_Repha = 15, /* Atomically-encoded logical or visual repha. */
   OT_Ra = 16,
   OT_CM = 17,  /* Consonant-Medial. */
-  OT_Symbol = 18 /* Avagraha, etc that take marks (SM,A,VD). */
+  OT_Symbol = 18, /* Avagraha, etc that take marks (SM,A,VD). */
+  OT_CS = 19
 };
 
 #define MEDIAL_FLAGS (FLAG (OT_CM))
@@ -70,36 +76,35 @@
  * 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_Ra) | MEDIAL_FLAGS | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE))
+#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CS) | FLAG (OT_Ra) | MEDIAL_FLAGS | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE))
 #define JOINER_FLAGS (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ))
-#define HALANT_OR_COENG_FLAGS (FLAG (OT_H) | FLAG (OT_Coeng))
 
 
 /* Visual positions in a syllable from left to right. */
 enum indic_position_t {
-  POS_START,
+  POS_START = 0,
 
-  POS_RA_TO_BECOME_REPH,
-  POS_PRE_M,
-  POS_PRE_C,
+  POS_RA_TO_BECOME_REPH = 1,
+  POS_PRE_M = 2,
+  POS_PRE_C = 3,
 
-  POS_BASE_C,
-  POS_AFTER_MAIN,
+  POS_BASE_C = 4,
+  POS_AFTER_MAIN = 5,
 
-  POS_ABOVE_C,
+  POS_ABOVE_C = 6,
 
-  POS_BEFORE_SUB,
-  POS_BELOW_C,
-  POS_AFTER_SUB,
+  POS_BEFORE_SUB = 7,
+  POS_BELOW_C = 8,
+  POS_AFTER_SUB = 9,
 
-  POS_BEFORE_POST,
-  POS_POST_C,
-  POS_AFTER_POST,
+  POS_BEFORE_POST = 10,
+  POS_POST_C = 11,
+  POS_AFTER_POST = 12,
 
-  POS_FINAL_C,
-  POS_SMVD,
+  POS_FINAL_C = 13,
+  POS_SMVD = 14,
 
-  POS_END
+  POS_END = 15
 };
 
 /* Categories used in IndicSyllabicCategory.txt from UCD. */
@@ -121,8 +126,8 @@
   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_WITH_STACKER	= OT_Repha, /* TODO */
-  INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK		= OT_SM,
+  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,
   INDIC_SYLLABIC_CATEGORY_JOINER			= OT_ZWJ,
   INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER		= OT_X,
@@ -132,7 +137,7 @@
   INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER			= OT_PLACEHOLDER, /* Don't care. */
   INDIC_SYLLABIC_CATEGORY_PURE_KILLER			= OT_M, /* Is like a vowel matra. */
   INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER		= OT_RS,
-  INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER		= OT_M, /* Misc Khmer signs. */
+  INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER		= OT_SM,
   INDIC_SYLLABIC_CATEGORY_TONE_LETTER			= OT_X,
   INDIC_SYLLABIC_CATEGORY_TONE_MARK			= OT_N,
   INDIC_SYLLABIC_CATEGORY_VIRAMA			= OT_H,
@@ -186,4 +191,211 @@
 HB_INTERNAL INDIC_TABLE_ELEMENT_TYPE
 hb_indic_get_categories (hb_codepoint_t u);
 
+
+static inline bool
+is_one_of (const hb_glyph_info_t &info, unsigned int flags)
+{
+  /* If it ligated, all bets are off. */
+  if (_hb_glyph_info_ligated (&info)) return false;
+  return !!(FLAG_UNSAFE (info.indic_category()) & flags);
+}
+
+static inline bool
+is_joiner (const hb_glyph_info_t &info)
+{
+  return is_one_of (info, JOINER_FLAGS);
+}
+
+static inline bool
+is_consonant (const hb_glyph_info_t &info)
+{
+  return is_one_of (info, CONSONANT_FLAGS);
+}
+
+static inline bool
+is_halant (const hb_glyph_info_t &info)
+{
+  return is_one_of (info, FLAG (OT_H));
+}
+
+#define IN_HALF_BLOCK(u, Base) (((u) & ~0x7Fu) == (Base))
+
+#define IS_DEVA(u) (IN_HALF_BLOCK (u, 0x0900u))
+#define IS_BENG(u) (IN_HALF_BLOCK (u, 0x0980u))
+#define IS_GURU(u) (IN_HALF_BLOCK (u, 0x0A00u))
+#define IS_GUJR(u) (IN_HALF_BLOCK (u, 0x0A80u))
+#define IS_ORYA(u) (IN_HALF_BLOCK (u, 0x0B00u))
+#define IS_TAML(u) (IN_HALF_BLOCK (u, 0x0B80u))
+#define IS_TELU(u) (IN_HALF_BLOCK (u, 0x0C00u))
+#define IS_KNDA(u) (IN_HALF_BLOCK (u, 0x0C80u))
+#define IS_MLYM(u) (IN_HALF_BLOCK (u, 0x0D00u))
+#define IS_SINH(u) (IN_HALF_BLOCK (u, 0x0D80u))
+
+
+#define MATRA_POS_LEFT(u)	POS_PRE_M
+#define MATRA_POS_RIGHT(u)	( \
+				  IS_DEVA(u) ? POS_AFTER_SUB  : \
+				  IS_BENG(u) ? POS_AFTER_POST : \
+				  IS_GURU(u) ? POS_AFTER_POST : \
+				  IS_GUJR(u) ? POS_AFTER_POST : \
+				  IS_ORYA(u) ? POS_AFTER_POST : \
+				  IS_TAML(u) ? POS_AFTER_POST : \
+				  IS_TELU(u) ? (u <= 0x0C42u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
+				  IS_KNDA(u) ? (u < 0x0CC3u || u > 0xCD6u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
+				  IS_MLYM(u) ? POS_AFTER_POST : \
+				  IS_SINH(u) ? POS_AFTER_SUB  : \
+				  /*default*/  POS_AFTER_SUB    \
+				)
+#define MATRA_POS_TOP(u)	( /* BENG and MLYM don't have top matras. */ \
+				  IS_DEVA(u) ? POS_AFTER_SUB  : \
+				  IS_GURU(u) ? POS_AFTER_POST : /* Deviate from spec */ \
+				  IS_GUJR(u) ? POS_AFTER_SUB  : \
+				  IS_ORYA(u) ? POS_AFTER_MAIN : \
+				  IS_TAML(u) ? POS_AFTER_SUB  : \
+				  IS_TELU(u) ? POS_BEFORE_SUB : \
+				  IS_KNDA(u) ? POS_BEFORE_SUB : \
+				  IS_SINH(u) ? POS_AFTER_SUB  : \
+				  /*default*/  POS_AFTER_SUB    \
+				)
+#define MATRA_POS_BOTTOM(u)	( \
+				  IS_DEVA(u) ? POS_AFTER_SUB  : \
+				  IS_BENG(u) ? POS_AFTER_SUB  : \
+				  IS_GURU(u) ? POS_AFTER_POST : \
+				  IS_GUJR(u) ? POS_AFTER_POST : \
+				  IS_ORYA(u) ? POS_AFTER_SUB  : \
+				  IS_TAML(u) ? POS_AFTER_POST : \
+				  IS_TELU(u) ? POS_BEFORE_SUB : \
+				  IS_KNDA(u) ? POS_BEFORE_SUB : \
+				  IS_MLYM(u) ? POS_AFTER_POST : \
+				  IS_SINH(u) ? POS_AFTER_SUB  : \
+				  /*default*/  POS_AFTER_SUB    \
+				)
+
+static inline indic_position_t
+matra_position_indic (hb_codepoint_t u, indic_position_t side)
+{
+  switch ((int) side)
+  {
+    case POS_PRE_C:	return MATRA_POS_LEFT (u);
+    case POS_POST_C:	return MATRA_POS_RIGHT (u);
+    case POS_ABOVE_C:	return MATRA_POS_TOP (u);
+    case POS_BELOW_C:	return MATRA_POS_BOTTOM (u);
+  };
+  return side;
+}
+
+/* XXX
+ * This is a hack for now.  We should move this data into the main Indic table.
+ * Or completely remove it and just check in the tables.
+ */
+static const hb_codepoint_t ra_chars[] = {
+  0x0930u, /* Devanagari */
+  0x09B0u, /* Bengali */
+  0x09F0u, /* Bengali */
+  0x0A30u, /* Gurmukhi */	/* No Reph */
+  0x0AB0u, /* Gujarati */
+  0x0B30u, /* Oriya */
+  0x0BB0u, /* Tamil */		/* No Reph */
+  0x0C30u, /* Telugu */		/* Reph formed only with ZWJ */
+  0x0CB0u, /* Kannada */
+  0x0D30u, /* Malayalam */	/* No Reph, Logical Repha */
+
+  0x0DBBu, /* Sinhala */		/* Reph formed only with ZWJ */
+};
+
+static inline bool
+is_ra (hb_codepoint_t u)
+{
+  for (unsigned int i = 0; i < ARRAY_LENGTH (ra_chars); i++)
+    if (u == ra_chars[i])
+      return true;
+  return false;
+}
+
+static inline void
+set_indic_properties (hb_glyph_info_t &info)
+{
+  hb_codepoint_t u = info.codepoint;
+  unsigned int type = hb_indic_get_categories (u);
+  indic_category_t cat = (indic_category_t) (type & 0x7Fu);
+  indic_position_t pos = (indic_position_t) (type >> 8);
+
+
+  /*
+   * Re-assign category
+   */
+
+  /* The following act more like the Bindus. */
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x0953u, 0x0954u)))
+    cat = OT_SM;
+  /* The following act like consonants. */
+  else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0x0A72u, 0x0A73u,
+				      0x1CF5u, 0x1CF6u)))
+    cat = OT_C;
+  /* TODO: The following should only be allowed after a Visarga.
+   * For now, just treat them like regular tone marks. */
+  else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1CE2u, 0x1CE8u)))
+    cat = OT_A;
+  /* TODO: The following should only be allowed after some of
+   * the nasalization marks, maybe only for U+1CE9..U+1CF1.
+   * For now, just treat them like tone marks. */
+  else if (unlikely (u == 0x1CEDu))
+    cat = OT_A;
+  /* The following take marks in standalone clusters, similar to Avagraha. */
+  else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0xA8F2u, 0xA8F7u,
+				      0x1CE9u, 0x1CECu,
+				      0x1CEEu, 0x1CF1u)))
+  {
+    cat = OT_Symbol;
+    static_assert (((int) INDIC_SYLLABIC_CATEGORY_AVAGRAHA == OT_Symbol), "");
+  }
+  else if (unlikely (u == 0x0A51u))
+  {
+    /* https://github.com/harfbuzz/harfbuzz/issues/524 */
+    cat = OT_M;
+    pos = POS_BELOW_C;
+  }
+
+  /* According to ScriptExtensions.txt, these Grantha marks may also be used in Tamil,
+   * so the Indic shaper needs to know their categories. */
+  else if (unlikely (u == 0x11301u || u == 0x11303u)) cat = OT_SM;
+  else if (unlikely (u == 0x1133cu)) cat = OT_N;
+
+  else if (unlikely (u == 0x0AFBu)) cat = OT_N; /* https://github.com/harfbuzz/harfbuzz/issues/552 */
+
+  else if (unlikely (u == 0x0980u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/issues/538 */
+  else if (unlikely (u == 0x0C80u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/623 */
+  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 (is_ra (u))
+      cat = OT_Ra;
+  }
+  else if (cat == OT_M)
+  {
+    pos = matra_position_indic (u, pos);
+  }
+  else if ((FLAG_UNSAFE (cat) & (FLAG (OT_SM) /* | FLAG (OT_VD) */ | FLAG (OT_A) | FLAG (OT_Symbol))))
+  {
+    pos = POS_SMVD;
+  }
+
+  if (unlikely (u == 0x0B01u)) pos = POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */
+
+
+
+  info.indic_category() = cat;
+  info.indic_position() = pos;
+}
+
+
 #endif /* HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH */
diff --git a/src/hb-ot-shape-complex-indic-table.cc b/src/hb-ot-shape-complex-indic-table.cc
index e10a4d2..867cfb3 100644
--- a/src/hb-ot-shape-complex-indic-table.cc
+++ b/src/hb-ot-shape-complex-indic-table.cc
@@ -6,61 +6,62 @@
  *
  * on files with these headers:
  *
- * # IndicSyllabicCategory-9.0.0.txt
- * # Date: 2016-05-21, 02:46:00 GMT [RP]
- * # IndicPositionalCategory-9.0.0.txt
- * # Date: 2016-02-25, 00:48:00 GMT [RP]
- * # Blocks-9.0.0.txt
- * # Date: 2016-02-05, 23:48:00 GMT [KW]
+ * # IndicSyllabicCategory-10.0.0.txt
+ * # Date: 2017-05-31, 01:07:00 GMT [KW, RP]
+ * # IndicPositionalCategory-10.0.0.txt
+ * # Date: 2017-05-31, 01:07:00 GMT [RP]
+ * # Blocks-10.0.0.txt
+ * # Date: 2017-04-12, 17:30:00 GMT [KW]
  */
 
 #include "hb-ot-shape-complex-indic-private.hh"
 
 
 #define ISC_A	INDIC_SYLLABIC_CATEGORY_AVAGRAHA		/*  15 chars; Avagraha */
-#define ISC_Bi	INDIC_SYLLABIC_CATEGORY_BINDU			/*  67 chars; Bindu */
+#define ISC_Bi	INDIC_SYLLABIC_CATEGORY_BINDU			/*  80 chars; Bindu */
 #define ISC_BJN	INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER	/*  20 chars; Brahmi_Joining_Number */
-#define ISC_Ca	INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK	/*  53 chars; Cantillation_Mark */
-#define ISC_C	INDIC_SYLLABIC_CATEGORY_CONSONANT		/* 1907 chars; Consonant */
+#define ISC_Ca	INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK	/*  57 chars; Cantillation_Mark */
+#define ISC_C	INDIC_SYLLABIC_CATEGORY_CONSONANT		/* 2024 chars; Consonant */
 #define ISC_CD	INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD		/*  10 chars; Consonant_Dead */
-#define ISC_CF	INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL		/*  62 chars; Consonant_Final */
+#define ISC_CF	INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL		/*  68 chars; Consonant_Final */
 #define ISC_CHL	INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER	/*   5 chars; Consonant_Head_Letter */
 #define ISC_CK	INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER	/*   2 chars; Consonant_Killer */
-#define ISC_CM	INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL	/*  22 chars; Consonant_Medial */
-#define ISC_CP	INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER	/*  16 chars; Consonant_Placeholder */
-#define ISC_CPR	INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA	/*   1 chars; Consonant_Preceding_Repha */
-#define ISC_CPrf	INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED	/*   2 chars; Consonant_Prefixed */
-#define ISC_CS	INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED	/*  90 chars; Consonant_Subjoined */
-#define ISC_CSR	INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA	/*   4 chars; Consonant_Succeeding_Repha */
+#define ISC_CM	INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL	/*  27 chars; Consonant_Medial */
+#define ISC_CP	INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER	/*  18 chars; Consonant_Placeholder */
+#define ISC_CPR	INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA	/*   2 chars; Consonant_Preceding_Repha */
+#define ISC_CPrf	INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED	/*   7 chars; Consonant_Prefixed */
+#define ISC_CS	INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED	/*  95 chars; Consonant_Subjoined */
+#define ISC_CSR	INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA	/*   5 chars; Consonant_Succeeding_Repha */
 #define ISC_CWS	INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER	/*   4 chars; Consonant_With_Stacker */
-#define ISC_GM	INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK		/*   2 chars; Gemination_Mark */
-#define ISC_IS	INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER	/*   7 chars; Invisible_Stacker */
+#define ISC_GM	INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK		/*   3 chars; Gemination_Mark */
+#define ISC_IS	INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER	/*  10 chars; Invisible_Stacker */
 #define ISC_ZWJ	INDIC_SYLLABIC_CATEGORY_JOINER			/*   1 chars; Joiner */
 #define ISC_ML	INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER	/*   1 chars; Modifying_Letter */
 #define ISC_ZWNJ	INDIC_SYLLABIC_CATEGORY_NON_JOINER		/*   1 chars; Non_Joiner */
-#define ISC_N	INDIC_SYLLABIC_CATEGORY_NUKTA			/*  24 chars; Nukta */
-#define ISC_Nd	INDIC_SYLLABIC_CATEGORY_NUMBER			/* 459 chars; Number */
+#define ISC_N	INDIC_SYLLABIC_CATEGORY_NUKTA			/*  28 chars; Nukta */
+#define ISC_Nd	INDIC_SYLLABIC_CATEGORY_NUMBER			/* 469 chars; Number */
 #define ISC_NJ	INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER		/*   1 chars; Number_Joiner */
 #define ISC_x	INDIC_SYLLABIC_CATEGORY_OTHER			/*   1 chars; Other */
-#define ISC_PK	INDIC_SYLLABIC_CATEGORY_PURE_KILLER		/*  16 chars; Pure_Killer */
+#define ISC_PK	INDIC_SYLLABIC_CATEGORY_PURE_KILLER		/*  21 chars; Pure_Killer */
 #define ISC_RS	INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER	/*   2 chars; Register_Shifter */
 #define ISC_SM	INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER	/*  22 chars; Syllable_Modifier */
 #define ISC_TL	INDIC_SYLLABIC_CATEGORY_TONE_LETTER		/*   7 chars; Tone_Letter */
 #define ISC_TM	INDIC_SYLLABIC_CATEGORY_TONE_MARK		/*  42 chars; Tone_Mark */
 #define ISC_V	INDIC_SYLLABIC_CATEGORY_VIRAMA			/*  24 chars; Virama */
-#define ISC_Vs	INDIC_SYLLABIC_CATEGORY_VISARGA			/*  31 chars; Visarga */
+#define ISC_Vs	INDIC_SYLLABIC_CATEGORY_VISARGA			/*  34 chars; Visarga */
 #define ISC_Vo	INDIC_SYLLABIC_CATEGORY_VOWEL			/*  30 chars; Vowel */
-#define ISC_M	INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT		/* 602 chars; Vowel_Dependent */
-#define ISC_VI	INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT	/* 431 chars; Vowel_Independent */
+#define ISC_M	INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT		/* 633 chars; Vowel_Dependent */
+#define ISC_VI	INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT	/* 443 chars; Vowel_Independent */
 
-#define IMC_B	INDIC_MATRA_CATEGORY_BOTTOM			/* 300 chars; Bottom */
+#define IMC_B	INDIC_MATRA_CATEGORY_BOTTOM			/* 330 chars; Bottom */
+#define IMC_BL	INDIC_MATRA_CATEGORY_BOTTOM_AND_LEFT		/*   1 chars; Bottom_And_Left */
 #define IMC_BR	INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		/*   2 chars; Bottom_And_Right */
 #define IMC_L	INDIC_MATRA_CATEGORY_LEFT			/*  57 chars; Left */
 #define IMC_LR	INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		/*  21 chars; Left_And_Right */
 #define IMC_x	INDIC_MATRA_CATEGORY_NOT_APPLICABLE		/*   1 chars; Not_Applicable */
 #define IMC_O	INDIC_MATRA_CATEGORY_OVERSTRUCK			/*  10 chars; Overstruck */
-#define IMC_R	INDIC_MATRA_CATEGORY_RIGHT			/* 258 chars; Right */
-#define IMC_T	INDIC_MATRA_CATEGORY_TOP			/* 342 chars; Top */
+#define IMC_R	INDIC_MATRA_CATEGORY_RIGHT			/* 262 chars; Right */
+#define IMC_T	INDIC_MATRA_CATEGORY_TOP			/* 380 chars; Top */
 #define IMC_TB	INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		/*  10 chars; Top_And_Bottom */
 #define IMC_TBR	INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	/*   1 chars; Top_And_Bottom_And_Right */
 #define IMC_TL	INDIC_MATRA_CATEGORY_TOP_AND_LEFT		/*   6 chars; Top_And_Left */
@@ -133,7 +134,7 @@
   /* 09E0 */ _(VI,x), _(VI,x),  _(M,B),  _(M,B),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
   /* 09E8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
   /* 09F0 */  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 09F8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 09F8 */  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(Bi,x),  _(x,x),  _(x,x),  _(x,x),
 
   /* Gurmukhi */
 
@@ -171,7 +172,7 @@
   /* 0AE0 */ _(VI,x), _(VI,x),  _(M,B),  _(M,B),  _(x,x),  _(x,x), _(Nd,x), _(Nd,x),
   /* 0AE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
   /* 0AF0 */  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
-  /* 0AF8 */  _(x,x),  _(C,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* 0AF8 */  _(x,x),  _(C,x), _(Ca,T), _(Ca,T), _(Ca,T),  _(N,T),  _(N,T),  _(N,T),
 
   /* Oriya */
 
@@ -251,14 +252,14 @@
 
   /* Malayalam */
 
-  /* 0D00 */  _(x,x), _(Bi,T), _(Bi,R), _(Vs,R),  _(x,x), _(VI,x), _(VI,x), _(VI,x),
+  /* 0D00 */ _(Bi,T), _(Bi,T), _(Bi,R), _(Vs,R),  _(x,x), _(VI,x), _(VI,x), _(VI,x),
   /* 0D08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),  _(x,x), _(VI,x), _(VI,x),
   /* 0D10 */ _(VI,x),  _(x,x), _(VI,x), _(VI,x), _(VI,x),  _(C,x),  _(C,x),  _(C,x),
   /* 0D18 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
   /* 0D20 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
   /* 0D28 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
   /* 0D30 */  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),  _(C,x),
-  /* 0D38 */  _(C,x),  _(C,x),  _(C,x),  _(x,x),  _(x,x),  _(A,x),  _(M,R),  _(M,R),
+  /* 0D38 */  _(C,x),  _(C,x),  _(C,x), _(PK,T), _(PK,T),  _(A,x),  _(M,R),  _(M,R),
   /* 0D40 */  _(M,R),  _(M,R),  _(M,R),  _(M,B),  _(M,B),  _(x,x),  _(M,L),  _(M,L),
   /* 0D48 */  _(M,L),  _(x,x), _(M,LR), _(M,LR), _(M,LR),  _(V,T),_(CPR,x),  _(x,x),
   /* 0D50 */  _(x,x),  _(x,x),  _(x,x),  _(x,x), _(CD,x), _(CD,x), _(CD,x),  _(M,R),
@@ -341,7 +342,7 @@
   /* 1CD8 */ _(Ca,B), _(Ca,B), _(Ca,T), _(Ca,T), _(Ca,B), _(Ca,B), _(Ca,B), _(Ca,B),
   /* 1CE0 */ _(Ca,T), _(Ca,R),  _(x,O),  _(x,O),  _(x,O),  _(x,O),  _(x,O),  _(x,O),
   /* 1CE8 */  _(x,O),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,B),  _(x,x),  _(x,x),
-  /* 1CF0 */  _(x,x),  _(x,x), _(Vs,x), _(Vs,x), _(Ca,T),  _(x,x),  _(x,x),  _(x,x),
+  /* 1CF0 */  _(x,x),  _(x,x), _(Vs,x), _(Vs,x), _(Ca,T),  _(x,x),  _(x,x), _(Ca,R),
   /* 1CF8 */ _(Ca,x), _(Ca,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
 
 #define indic_offset_0x2008u 1656
@@ -368,7 +369,7 @@
 
   /* A8E0 */ _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T),
   /* A8E8 */ _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T),
-  /* A8F0 */ _(Ca,T), _(Ca,T),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
+  /* A8F0 */ _(Ca,T), _(Ca,T), _(Bi,x), _(Bi,x),  _(x,x),  _(x,x),  _(x,x),  _(x,x),
 
 #define indic_offset_0xa9e0u 1720
 
@@ -390,7 +391,7 @@
   /* AA70 */  _(x,x),  _(C,x),  _(C,x),  _(C,x), _(CP,x), _(CP,x), _(CP,x),  _(x,x),
   /* AA78 */  _(x,x),  _(x,x),  _(C,x), _(TM,R), _(TM,T), _(TM,R),  _(C,x),  _(C,x),
 
-}; /* Table items: 1784; occupancy: 69% */
+}; /* Table items: 1784; occupancy: 70% */
 
 INDIC_TABLE_ELEMENT_TYPE
 hb_indic_get_categories (hb_codepoint_t u)
@@ -398,35 +399,28 @@
   switch (u >> 12)
   {
     case 0x0u:
-      if (hb_in_range (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u];
-      if (hb_in_range (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u];
-      if (hb_in_range (u, 0x0900u, 0x0DF7u)) return indic_table[u - 0x0900u + indic_offset_0x0900u];
       if (unlikely (u == 0x00A0u)) return _(CP,x);
+      if (hb_in_range<hb_codepoint_t> (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x0900u, 0x0DF7u)) return indic_table[u - 0x0900u + indic_offset_0x0900u];
       break;
 
     case 0x1u:
-      if (hb_in_range (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u];
-      if (hb_in_range (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u];
-      if (hb_in_range (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u];
       break;
 
     case 0x2u:
-      if (hb_in_range (u, 0x2008u, 0x2017u)) return indic_table[u - 0x2008u + indic_offset_0x2008u];
-      if (hb_in_range (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u];
       if (unlikely (u == 0x25CCu)) return _(CP,x);
+      if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2017u)) return indic_table[u - 0x2008u + indic_offset_0x2008u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u];
       break;
 
     case 0xAu:
-      if (hb_in_range (u, 0xA8E0u, 0xA8F7u)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u];
-      if (hb_in_range (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u];
-      if (hb_in_range (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u];
-      break;
-
-    case 0x11u:
-      // According to ScriptExtensions.txt, these Grantha marks may also be used in Tamil,
-      // so the Indic shaper needs to know their categories.
-      if (unlikely (u == 0x11303)) return _(Vs,R);
-      if (unlikely (u == 0x1133c)) return _(N,B);
+      if (hb_in_range<hb_codepoint_t> (u, 0xA8E0u, 0xA8F7u)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u];
       break;
 
     default:
@@ -474,6 +468,7 @@
 #undef ISC_VI
 
 #undef IMC_B
+#undef IMC_BL
 #undef IMC_BR
 #undef IMC_L
 #undef IMC_LR
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index b48fb56..32ad86a 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -27,217 +27,12 @@
 #include "hb-ot-shape-complex-indic-private.hh"
 #include "hb-ot-layout-private.hh"
 
-/* buffer var allocations */
-#define indic_category() complex_var_u8_0() /* indic_category_t */
-#define indic_position() complex_var_u8_1() /* indic_position_t */
-
 
 /*
  * Indic shaper.
  */
 
 
-#define IN_HALF_BLOCK(u, Base) (((u) & ~0x7Fu) == (Base))
-
-#define IS_DEVA(u) (IN_HALF_BLOCK (u, 0x0900u))
-#define IS_BENG(u) (IN_HALF_BLOCK (u, 0x0980u))
-#define IS_GURU(u) (IN_HALF_BLOCK (u, 0x0A00u))
-#define IS_GUJR(u) (IN_HALF_BLOCK (u, 0x0A80u))
-#define IS_ORYA(u) (IN_HALF_BLOCK (u, 0x0B00u))
-#define IS_TAML(u) (IN_HALF_BLOCK (u, 0x0B80u))
-#define IS_TELU(u) (IN_HALF_BLOCK (u, 0x0C00u))
-#define IS_KNDA(u) (IN_HALF_BLOCK (u, 0x0C80u))
-#define IS_MLYM(u) (IN_HALF_BLOCK (u, 0x0D00u))
-#define IS_SINH(u) (IN_HALF_BLOCK (u, 0x0D80u))
-#define IS_KHMR(u) (IN_HALF_BLOCK (u, 0x1780u))
-
-
-#define MATRA_POS_LEFT(u)	POS_PRE_M
-#define MATRA_POS_RIGHT(u)	( \
-				  IS_DEVA(u) ? POS_AFTER_SUB  : \
-				  IS_BENG(u) ? POS_AFTER_POST : \
-				  IS_GURU(u) ? POS_AFTER_POST : \
-				  IS_GUJR(u) ? POS_AFTER_POST : \
-				  IS_ORYA(u) ? POS_AFTER_POST : \
-				  IS_TAML(u) ? POS_AFTER_POST : \
-				  IS_TELU(u) ? (u <= 0x0C42u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
-				  IS_KNDA(u) ? (u < 0x0CC3u || u > 0xCD6u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
-				  IS_MLYM(u) ? POS_AFTER_POST : \
-				  IS_SINH(u) ? POS_AFTER_SUB  : \
-				  IS_KHMR(u) ? POS_AFTER_POST : \
-				  /*default*/  POS_AFTER_SUB    \
-				)
-#define MATRA_POS_TOP(u)	( /* BENG and MLYM don't have top matras. */ \
-				  IS_DEVA(u) ? POS_AFTER_SUB  : \
-				  IS_GURU(u) ? POS_AFTER_POST : /* Deviate from spec */ \
-				  IS_GUJR(u) ? POS_AFTER_SUB  : \
-				  IS_ORYA(u) ? POS_AFTER_MAIN : \
-				  IS_TAML(u) ? POS_AFTER_SUB  : \
-				  IS_TELU(u) ? POS_BEFORE_SUB : \
-				  IS_KNDA(u) ? POS_BEFORE_SUB : \
-				  IS_SINH(u) ? POS_AFTER_SUB  : \
-				  IS_KHMR(u) ? POS_AFTER_POST : \
-				  /*default*/  POS_AFTER_SUB    \
-				)
-#define MATRA_POS_BOTTOM(u)	( \
-				  IS_DEVA(u) ? POS_AFTER_SUB  : \
-				  IS_BENG(u) ? POS_AFTER_SUB  : \
-				  IS_GURU(u) ? POS_AFTER_POST : \
-				  IS_GUJR(u) ? POS_AFTER_POST : \
-				  IS_ORYA(u) ? POS_AFTER_SUB  : \
-				  IS_TAML(u) ? POS_AFTER_POST : \
-				  IS_TELU(u) ? POS_BEFORE_SUB : \
-				  IS_KNDA(u) ? POS_BEFORE_SUB : \
-				  IS_MLYM(u) ? POS_AFTER_POST : \
-				  IS_SINH(u) ? POS_AFTER_SUB  : \
-				  IS_KHMR(u) ? POS_AFTER_POST : \
-				  /*default*/  POS_AFTER_SUB    \
-				)
-
-static inline indic_position_t
-matra_position (hb_codepoint_t u, indic_position_t side)
-{
-  switch ((int) side)
-  {
-    case POS_PRE_C:	return MATRA_POS_LEFT (u);
-    case POS_POST_C:	return MATRA_POS_RIGHT (u);
-    case POS_ABOVE_C:	return MATRA_POS_TOP (u);
-    case POS_BELOW_C:	return MATRA_POS_BOTTOM (u);
-  };
-  return side;
-}
-
-/* XXX
- * This is a hack for now.  We should move this data into the main Indic table.
- * Or completely remove it and just check in the tables.
- */
-static const hb_codepoint_t ra_chars[] = {
-  0x0930u, /* Devanagari */
-  0x09B0u, /* Bengali */
-  0x09F0u, /* Bengali */
-  0x0A30u, /* Gurmukhi */	/* No Reph */
-  0x0AB0u, /* Gujarati */
-  0x0B30u, /* Oriya */
-  0x0BB0u, /* Tamil */		/* No Reph */
-  0x0C30u, /* Telugu */		/* Reph formed only with ZWJ */
-  0x0CB0u, /* Kannada */
-  0x0D30u, /* Malayalam */	/* No Reph, Logical Repha */
-
-  0x0DBBu, /* Sinhala */		/* Reph formed only with ZWJ */
-
-  0x179Au, /* Khmer */		/* No Reph, Visual Repha */
-};
-
-static inline bool
-is_ra (hb_codepoint_t u)
-{
-  for (unsigned int i = 0; i < ARRAY_LENGTH (ra_chars); i++)
-    if (u == ra_chars[i])
-      return true;
-  return false;
-}
-
-static inline bool
-is_one_of (const hb_glyph_info_t &info, unsigned int flags)
-{
-  /* If it ligated, all bets are off. */
-  if (_hb_glyph_info_ligated (&info)) return false;
-  return !!(FLAG_SAFE (info.indic_category()) & flags);
-}
-
-static inline bool
-is_joiner (const hb_glyph_info_t &info)
-{
-  return is_one_of (info, JOINER_FLAGS);
-}
-
-static inline bool
-is_consonant (const hb_glyph_info_t &info)
-{
-  return is_one_of (info, CONSONANT_FLAGS);
-}
-
-static inline bool
-is_halant_or_coeng (const hb_glyph_info_t &info)
-{
-  return is_one_of (info, HALANT_OR_COENG_FLAGS);
-}
-
-static inline void
-set_indic_properties (hb_glyph_info_t &info)
-{
-  hb_codepoint_t u = info.codepoint;
-  unsigned int type = hb_indic_get_categories (u);
-  indic_category_t cat = (indic_category_t) (type & 0x7Fu);
-  indic_position_t pos = (indic_position_t) (type >> 8);
-
-
-  /*
-   * Re-assign category
-   */
-
-  /* The following act more like the Bindus. */
-  if (unlikely (hb_in_range (u, 0x0953u, 0x0954u)))
-    cat = OT_SM;
-  /* The following act like consonants. */
-  else if (unlikely (hb_in_ranges (u, 0x0A72u, 0x0A73u,
-				      0x1CF5u, 0x1CF6u)))
-    cat = OT_C;
-  /* TODO: The following should only be allowed after a Visarga.
-   * For now, just treat them like regular tone marks. */
-  else if (unlikely (hb_in_range (u, 0x1CE2u, 0x1CE8u)))
-    cat = OT_A;
-  /* TODO: The following should only be allowed after some of
-   * the nasalization marks, maybe only for U+1CE9..U+1CF1.
-   * For now, just treat them like tone marks. */
-  else if (unlikely (u == 0x1CEDu))
-    cat = OT_A;
-  /* The following take marks in standalone clusters, similar to Avagraha. */
-  else if (unlikely (hb_in_ranges (u, 0xA8F2u, 0xA8F7u,
-				      0x1CE9u, 0x1CECu,
-				      0x1CEEu, 0x1CF1u)))
-  {
-    cat = OT_Symbol;
-    ASSERT_STATIC ((int) INDIC_SYLLABIC_CATEGORY_AVAGRAHA == OT_Symbol);
-  }
-  else if (unlikely (u == 0x17C6u)) cat = OT_N; /* Khmer Bindu doesn't like to be repositioned. */
-  else if (unlikely (hb_in_range (u, 0x2010u, 0x2011u)))
-				    cat = OT_PLACEHOLDER;
-  else if (unlikely (u == 0x25CCu)) cat = OT_DOTTEDCIRCLE;
-
-
-  /*
-   * Re-assign position.
-   */
-
-  if ((FLAG_SAFE (cat) & CONSONANT_FLAGS))
-  {
-    pos = POS_BASE_C;
-    if (is_ra (u))
-      cat = OT_Ra;
-  }
-  else if (cat == OT_M)
-  {
-    pos = matra_position (u, pos);
-  }
-  else if ((FLAG_SAFE (cat) & (FLAG (OT_SM) | FLAG (OT_VD) | FLAG (OT_A) | FLAG (OT_Symbol))))
-  {
-    pos = POS_SMVD;
-  }
-
-  if (unlikely (u == 0x0B01u)) pos = POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */
-
-
-
-  info.indic_category() = cat;
-  info.indic_position() = pos;
-}
-
-/*
- * Things above this line should ideally be moved to the Indic table itself.
- */
-
-
 /*
  * Indic configurations.  Note that we do not want to keep every single script-specific
  * behavior in these tables necessarily.  This should mainly be used for per-script
@@ -247,7 +42,6 @@
  */
 
 enum base_position_t {
-  BASE_POS_FIRST,
   BASE_POS_LAST_SINHALA,
   BASE_POS_LAST
 };
@@ -256,13 +50,11 @@
   REPH_POS_BEFORE_SUB  = POS_BEFORE_SUB,
   REPH_POS_AFTER_SUB   = POS_AFTER_SUB,
   REPH_POS_BEFORE_POST = POS_BEFORE_POST,
-  REPH_POS_AFTER_POST  = POS_AFTER_POST,
-  REPH_POS_DONT_CARE   = POS_RA_TO_BECOME_REPH
+  REPH_POS_AFTER_POST  = POS_AFTER_POST
 };
 enum reph_mode_t {
   REPH_MODE_IMPLICIT,  /* Reph formed out of initial Ra,H sequence. */
   REPH_MODE_EXPLICIT,  /* Reph formed out of initial Ra,H,ZWJ sequence. */
-  REPH_MODE_VIS_REPHA, /* Encoded Repha character, no reordering needed. */
   REPH_MODE_LOG_REPHA  /* Encoded Repha character, needs reordering. */
 };
 enum blwf_mode_t {
@@ -295,7 +87,6 @@
   {HB_SCRIPT_MALAYALAM,	true, 0x0D4Du,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA,BLWF_MODE_PRE_AND_POST},
   {HB_SCRIPT_SINHALA,	false,0x0DCAu,BASE_POS_LAST_SINHALA,
 						     REPH_POS_AFTER_MAIN, REPH_MODE_EXPLICIT, BLWF_MODE_PRE_AND_POST},
-  {HB_SCRIPT_KHMER,	false,0x17D2u,BASE_POS_FIRST,REPH_POS_DONT_CARE,  REPH_MODE_VIS_REPHA,BLWF_MODE_PRE_AND_POST},
 };
 
 
@@ -327,7 +118,6 @@
   {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('c','f','a','r'), F_NONE},
   /*
    * Other features.
    * These features are applied all at once, after final_reordering.
@@ -361,7 +151,6 @@
   PSTF,
   _VATU,
   _CJCT,
-  CFAR,
 
   INIT,
   _PRES,
@@ -411,12 +200,12 @@
   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);
-    map->add_gsub_pause (NULL);
+    map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
+    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);
+    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'));
@@ -428,17 +217,6 @@
 static void
 override_features_indic (hb_ot_shape_planner_t *plan)
 {
-  /* Uniscribe does not apply 'kern' in Khmer. */
-  if (hb_options ().uniscribe_bug_compatible)
-  {
-    switch ((hb_tag_t) plan->props.script)
-    {
-      case HB_SCRIPT_KHMER:
-	plan->map.add_feature (HB_TAG('k','e','r','n'), 0, F_GLOBAL);
-	break;
-    }
-  }
-
   plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
 }
 
@@ -485,7 +263,7 @@
 
       /* Our get_nominal_glyph() function needs a font, so we can't get the virama glyph
        * during shape planning...  Instead, overwrite it here.  It's safe.  Don't worry! */
-      (const_cast<indic_shape_plan_t *> (this))->virama_glyph = glyph;
+      virama_glyph = glyph;
     }
 
     *pglyph = glyph;
@@ -495,7 +273,7 @@
   const indic_config_t *config;
 
   bool is_old_spec;
-  hb_codepoint_t virama_glyph;
+  mutable hb_codepoint_t virama_glyph;
 
   would_substitute_feature_t rphf;
   would_substitute_feature_t pref;
@@ -510,7 +288,7 @@
 {
   indic_shape_plan_t *indic_plan = (indic_shape_plan_t *) calloc (1, sizeof (indic_shape_plan_t));
   if (unlikely (!indic_plan))
-    return NULL;
+    return nullptr;
 
   indic_plan->config = &indic_configs[0];
   for (unsigned int i = 1; i < ARRAY_LENGTH (indic_configs); i++)
@@ -615,6 +393,8 @@
 		 hb_buffer_t *buffer)
 {
   find_syllables (buffer);
+  foreach_syllable (buffer, start, end)
+    buffer->unsafe_to_break (start, end);
 }
 
 static int
@@ -666,6 +446,21 @@
   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
   hb_glyph_info_t *info = buffer->info;
 
+  /* https://github.com/harfbuzz/harfbuzz/issues/435#issuecomment-335560167
+   * // For compatibility with legacy usage in Kannada,
+   * // Ra+h+ZWJ must behave like Ra+ZWJ+h...
+   */
+  if (buffer->props.script == HB_SCRIPT_KANNADA &&
+      start + 3 <= end &&
+      is_one_of (info[start  ], FLAG (OT_Ra)) &&
+      is_one_of (info[start+1], FLAG (OT_H)) &&
+      is_one_of (info[start+2], FLAG (OT_ZWJ)))
+  {
+    buffer->merge_clusters (start+1, start+3);
+    hb_glyph_info_t tmp = info[start+1];
+    info[start+1] = info[start+2];
+    info[start+2] = tmp;
+  }
 
   /* 1. Find base consonant:
    *
@@ -673,7 +468,7 @@
    * following algorithm: starting from the end of the syllable, move backwards
    * until a consonant is found that does not have a below-base or post-base
    * form (post-base forms have to follow below-base forms), or that is not a
-   * pre-base reordering Ra, or arrive at the first consonant. The consonant
+   * pre-base-reordering Ra, or arrive at the first consonant. The consonant
    * stopped at will be the base.
    *
    *   o If the syllable starts with Ra + Halant (in a script that has Reph)
@@ -689,8 +484,7 @@
      *    and has more than one consonant, Ra is excluded from candidates for
      *    base consonants. */
     unsigned int limit = start;
-    if (indic_plan->config->reph_pos != REPH_POS_DONT_CARE &&
-	indic_plan->mask_array[RPHF] &&
+    if (indic_plan->mask_array[RPHF] &&
 	start + 3 <= end &&
 	(
 	 (indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) ||
@@ -744,11 +538,11 @@
 	    if (info[i].indic_position() == POS_BELOW_C)
 	      seen_below = true;
 
-	    /* -> or that is not a pre-base reordering Ra,
+	    /* -> or that is not a pre-base-reordering Ra,
 	     *
 	     * IMPLEMENTATION NOTES:
 	     *
-	     * Our pre-base reordering Ra's are marked POS_POST_C, so will be skipped
+	     * Our pre-base-reordering Ra's are marked POS_POST_C, so will be skipped
 	     * by the logic above already.
 	     */
 
@@ -799,22 +593,6 @@
 	    info[i].indic_position() = POS_BELOW_C;
       }
       break;
-
-      case BASE_POS_FIRST:
-      {
-	/* The first consonant is always the base. */
-
-	assert (indic_plan->config->reph_mode == REPH_MODE_VIS_REPHA);
-	assert (!has_reph);
-
-	base = start;
-
-	/* Mark all subsequent consonants as below. */
-	for (unsigned int i = base + 1; i < end; i++)
-	  if (is_consonant (info[i]))
-	    info[i].indic_position() = POS_BELOW_C;
-      }
-      break;
     }
 
     /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
@@ -831,8 +609,8 @@
 
   /* 2. Decompose and reorder Matras:
    *
-   * Each matra and any syllable modifier sign in the cluster are moved to the
-   * appropriate position relative to the consonant(s) in the cluster. The
+   * Each matra and any syllable modifier sign in the syllable are moved to the
+   * appropriate position relative to the consonant(s) in the syllable. The
    * shaping engine decomposes two- or three-part matras into their constituent
    * parts before any repositioning. Matra characters are classified by which
    * consonant in a conjunct they have affinity for and are reordered to the
@@ -869,15 +647,15 @@
   if (base < end)
     info[base].indic_position() = POS_BASE_C;
 
-  /* Mark final consonants.  A final consonant is one appearing after a matra,
-   * like in Khmer. */
+  /* Mark final consonants.  A final consonant is one appearing after a matra.
+   * Happens in Sinhala. */
   for (unsigned int i = base + 1; i < end; i++)
     if (info[i].indic_category() == OT_M) {
       for (unsigned int j = i + 1; j < end; j++)
-        if (is_consonant (info[j])) {
-	  info[j].indic_position() = POS_FINAL_C;
-	  break;
-	}
+	if (is_consonant (info[j])) {
+	 info[j].indic_position() = POS_FINAL_C;
+	 break;
+       }
       break;
     }
 
@@ -928,7 +706,7 @@
     indic_position_t last_pos = POS_START;
     for (unsigned int i = start; i < end; i++)
     {
-      if ((FLAG_SAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | HALANT_OR_COENG_FLAGS)))
+      if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | FLAG (OT_H))))
       {
 	info[i].indic_position() = last_pos;
 	if (unlikely (info[i].indic_category() == OT_H &&
@@ -1083,7 +861,7 @@
   unsigned int pref_len = 2;
   if (indic_plan->mask_array[PREF] && base + pref_len < end)
   {
-    /* Find a Halant,Ra sequence and mark it for pre-base reordering processing. */
+    /* Find a Halant,Ra sequence and mark it for pre-base-reordering processing. */
     for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) {
       hb_codepoint_t glyphs[2];
       for (unsigned int j = 0; j < pref_len; j++)
@@ -1092,17 +870,6 @@
       {
 	for (unsigned int j = 0; j < pref_len; j++)
 	  info[i++].mask |= indic_plan->mask_array[PREF];
-
-	/* Mark the subsequent stuff with 'cfar'.  Used in Khmer.
-	 * Read the feature spec.
-	 * This allows distinguishing the following cases with MS Khmer fonts:
-	 * U+1784,U+17D2,U+179A,U+17D2,U+1782
-	 * U+1784,U+17D2,U+1782,U+17D2,U+179A
-	 */
-	if (indic_plan->mask_array[CFAR])
-	  for (; i < end; i++)
-	    info[i].mask |= indic_plan->mask_array[CFAR];
-
 	break;
       }
     }
@@ -1258,7 +1025,7 @@
 
 
   /* This function relies heavily on halant glyphs.  Lots of ligation
-   * and possibly multiplication substitutions happened prior to this
+   * and possibly multiple substitutions happened prior to this
    * phase, and that might have messed up our properties.  Recover
    * from a particular case of that where we're fairly sure that a
    * class of OT_H is desired but has been lost. */
@@ -1270,7 +1037,7 @@
 	  _hb_glyph_info_ligated (&info[i]) &&
 	  _hb_glyph_info_multiplied (&info[i]))
       {
-        /* This will make sure that this glyph passes is_halant_or_coeng() test. */
+        /* This will make sure that this glyph passes is_halant() test. */
 	info[i].indic_category() = OT_H;
 	_hb_glyph_info_clear_ligated_and_multiplied (&info[i]);
       }
@@ -1282,7 +1049,7 @@
    * After the localized forms and basic shaping forms GSUB features have been
    * applied (see below), the shaping engine performs some final glyph
    * reordering before applying all the remaining font features to the entire
-   * cluster.
+   * syllable.
    */
 
   bool try_pref = !!indic_plan->mask_array[PREF];
@@ -1303,7 +1070,7 @@
 	      /* Ok, this was a 'pref' candidate but didn't form any.
 	       * Base is around here... */
 	      base = i;
-	      while (base < end && is_halant_or_coeng (info[base]))
+	      while (base < end && is_halant (info[base]))
 		base++;
 	      info[base].indic_position() = POS_BASE_C;
 
@@ -1319,7 +1086,7 @@
 	{
 	  while (i < end && is_joiner (info[i]))
 	    i++;
-	  if (i == end || !is_halant_or_coeng (info[i]))
+	  if (i == end || !is_halant (info[i]))
 	    break;
 	  i++; /* Skip halant. */
 	  while (i < end && is_joiner (info[i]))
@@ -1341,7 +1108,7 @@
     base--;
   if (base < end)
     while (start < base &&
-	   is_one_of (info[base], (FLAG (OT_N) | HALANT_OR_COENG_FLAGS)))
+	   is_one_of (info[base], (FLAG (OT_N) | FLAG (OT_H))))
       base--;
 
 
@@ -1367,13 +1134,13 @@
     if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
     {
       while (new_pos > start &&
-	     !(is_one_of (info[new_pos], (FLAG (OT_M) | HALANT_OR_COENG_FLAGS))))
+	     !(is_one_of (info[new_pos], (FLAG (OT_M) | FLAG (OT_H)))))
 	new_pos--;
 
       /* If we found no Halant we are done.
        * Otherwise only proceed if the Halant does
        * not belong to the Matra itself! */
-      if (is_halant_or_coeng (info[new_pos]) &&
+      if (is_halant (info[new_pos]) &&
 	  info[new_pos].indic_position() != POS_PRE_M)
       {
 	/* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
@@ -1440,8 +1207,6 @@
     unsigned int new_reph_pos;
     reph_position_t reph_pos = indic_plan->config->reph_pos;
 
-    assert (reph_pos != REPH_POS_DONT_CARE);
-
     /*       1. If reph should be positioned after post-base consonant forms,
      *          proceed to step 5.
      */
@@ -1463,10 +1228,10 @@
      */
     {
       new_reph_pos = start + 1;
-      while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos]))
+      while (new_reph_pos < base && !is_halant (info[new_reph_pos]))
 	new_reph_pos++;
 
-      if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos]))
+      if (new_reph_pos < base && is_halant (info[new_reph_pos]))
       {
 	/* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
 	if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
@@ -1477,7 +1242,7 @@
 
     /*       3. If reph should be repositioned after the main consonant: find the
      *          first consonant not ligated with main, or find the first
-     *          consonant that is not a potential pre-base reordering Ra.
+     *          consonant that is not a potential pre-base-reordering Ra.
      */
     if (reph_pos == REPH_POS_AFTER_MAIN)
     {
@@ -1497,8 +1262,8 @@
     if (reph_pos == REPH_POS_AFTER_SUB)
     {
       new_reph_pos = base;
-      while (new_reph_pos < end &&
-	     !( FLAG_SAFE (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_AFTER_POST) | FLAG (POS_SMVD))))
+      while (new_reph_pos + 1 < end &&
+	     !( FLAG_UNSAFE (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_AFTER_POST) | FLAG (POS_SMVD))))
 	new_reph_pos++;
       if (new_reph_pos < end)
         goto reph_move;
@@ -1515,10 +1280,10 @@
     {
       /* Copied from step 2. */
       new_reph_pos = start + 1;
-      while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos]))
+      while (new_reph_pos < base && !is_halant (info[new_reph_pos]))
 	new_reph_pos++;
 
-      if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos]))
+      if (new_reph_pos < base && is_halant (info[new_reph_pos]))
       {
 	/* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
 	if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
@@ -1542,7 +1307,7 @@
        * TEST: U+0930,U+094D,U+0915,U+094B,U+094D
        */
       if (!hb_options ().uniscribe_bug_compatible &&
-	  unlikely (is_halant_or_coeng (info[new_reph_pos]))) {
+	  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) {
 	    /* Ok, got it. */
@@ -1566,13 +1331,13 @@
   }
 
 
-  /*   o Reorder pre-base reordering consonants:
+  /*   o Reorder pre-base-reordering consonants:
    *
-   *     If a pre-base reordering consonant is found, reorder it according to
+   *     If a pre-base-reordering consonant is found, reorder it according to
    *     the following rules:
    */
 
-  if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base reordering Ra. */
+  if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base-reordering Ra. */
   {
     for (unsigned int i = base + 1; i < end; i++)
       if ((info[i].mask & indic_plan->mask_array[PREF]) != 0)
@@ -1603,24 +1368,11 @@
 	  if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
 	  {
 	    while (new_pos > start &&
-		   !(is_one_of (info[new_pos - 1], FLAG(OT_M) | HALANT_OR_COENG_FLAGS)))
+		   !(is_one_of (info[new_pos - 1], FLAG(OT_M) | FLAG (OT_H))))
 	      new_pos--;
-
-	    /* In Khmer coeng model, a H,Ra can go *after* matras.  If it goes after a
-	     * split matra, it should be reordered to *before* the left part of such matra. */
-	    if (new_pos > start && info[new_pos - 1].indic_category() == OT_M)
-	    {
-	      unsigned int old_pos = i;
-	      for (unsigned int j = base + 1; j < old_pos; j++)
-		if (info[j].indic_category() == OT_M)
-		{
-		  new_pos--;
-		  break;
-		}
-	    }
 	  }
 
-	  if (new_pos > start && is_halant_or_coeng (info[new_pos - 1]))
+	  if (new_pos > start && is_halant (info[new_pos - 1]))
 	  {
 	    /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
 	    if (new_pos < end && is_joiner (info[new_pos]))
@@ -1646,11 +1398,15 @@
 
 
   /* Apply 'init' to the Left Matra if it's a word start. */
-  if (info[start].indic_position () == POS_PRE_M &&
-      (!start ||
-       !(FLAG_SAFE (_hb_glyph_info_get_general_category (&info[start - 1])) &
-	 FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
-    info[start].mask |= indic_plan->mask_array[INIT];
+  if (info[start].indic_position () == POS_PRE_M)
+  {
+    if (!start ||
+	!(FLAG_UNSAFE (_hb_glyph_info_get_general_category (&info[start - 1])) &
+	 FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
+      info[start].mask |= indic_plan->mask_array[INIT];
+    else
+      buffer->unsafe_to_break (start - 1, start + 1);
+  }
 
 
   /*
@@ -1665,8 +1421,8 @@
         break;
 
       default:
-	/* Uniscribe merges the entire cluster... Except for Tamil & Sinhala.
-	 * This means, half forms are submerged into the main consonants cluster.
+	/* Uniscribe merges the entire syllable into a single cluster... Except for Tamil & Sinhala.
+	 * This means, half forms are submerged into the main consonant's cluster.
 	 * This is unnecessary, and makes cursor positioning harder, but that's what
 	 * Uniscribe does. */
 	buffer->merge_clusters (start, end);
@@ -1721,13 +1477,6 @@
      * Decompose split matras that don't have Unicode decompositions.
      */
 
-    /* Khmer */
-    case 0x17BEu  : *a = 0x17C1u; *b= 0x17BEu; return true;
-    case 0x17BFu  : *a = 0x17C1u; *b= 0x17BFu; return true;
-    case 0x17C0u  : *a = 0x17C1u; *b= 0x17C0u; return true;
-    case 0x17C4u  : *a = 0x17C1u; *b= 0x17C4u; return true;
-    case 0x17C5u  : *a = 0x17C1u; *b= 0x17C5u; return true;
-
 #if 0
     /* Gujarati */
     /* This one has no decomposition in Unicode, but needs no decomposition either. */
@@ -1738,7 +1487,7 @@
 #endif
   }
 
-  if ((ab == 0x0DDAu || hb_in_range (ab, 0x0DDCu, 0x0DDEu)))
+  if ((ab == 0x0DDAu || hb_in_range<hb_codepoint_t> (ab, 0x0DDCu, 0x0DDEu)))
   {
     /*
      * Sinhala split matras...  Let the fun begin.
@@ -1803,18 +1552,18 @@
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
 {
-  "indic",
   collect_features_indic,
   override_features_indic,
   data_create_indic,
   data_destroy_indic,
-  NULL, /* preprocess_text */
-  NULL, /* postprocess_glyphs */
+  nullptr, /* preprocess_text */
+  nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   decompose_indic,
   compose_indic,
   setup_masks_indic,
-  NULL, /* disable_otl */
+  nullptr, /* disable_otl */
+  nullptr, /* reorder_marks */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   false, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-khmer-machine.hh b/src/hb-ot-shape-complex-khmer-machine.hh
new file mode 100644
index 0000000..380705a
--- /dev/null
+++ b/src/hb-ot-shape-complex-khmer-machine.hh
@@ -0,0 +1,294 @@
+
+#line 1 "hb-ot-shape-complex-khmer-machine.rl"
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
+
+#include "hb-private.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
+};
+
+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
+};
+
+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
+};
+
+static const char _khmer_syllable_machine_indicies[] = {
+	1, 0, 2, 2, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 2, 0, 3, 0, 4, 4, 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, 
+	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
+};
+
+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
+};
+
+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, 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, 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
+};
+
+static const int khmer_syllable_machine_start = 10;
+static const int khmer_syllable_machine_first_final = 10;
+static const int khmer_syllable_machine_error = -1;
+
+static const int khmer_syllable_machine_en_main = 10;
+
+
+#line 36 "hb-ot-shape-complex-khmer-machine.rl"
+
+
+
+#line 74 "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++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  
+#line 181 "hb-ot-shape-complex-khmer-machine.hh"
+	{
+	cs = khmer_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 95 "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"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+_resume:
+	switch ( _khmer_syllable_machine_from_state_actions[cs] ) {
+	case 5:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 212 "hb-ot-shape-complex-khmer-machine.hh"
+	}
+
+	_keys = _khmer_syllable_machine_trans_keys + (cs<<1);
+	_inds = _khmer_syllable_machine_indicies + _khmer_syllable_machine_index_offsets[cs];
+
+	_slen = _khmer_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].khmer_category()) &&
+		( info[p].khmer_category()) <= _keys[1] ?
+		( info[p].khmer_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+	cs = _khmer_syllable_machine_trans_targs[_trans];
+
+	if ( _khmer_syllable_machine_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _khmer_syllable_machine_trans_actions[_trans] ) {
+	case 2:
+#line 1 "NONE"
+	{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"
+	{te = p+1;{ found_syllable (non_khmer_cluster); }}
+	break;
+	case 7:
+#line 68 "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"
+	{te = p;p--;{ found_syllable (broken_cluster); }}
+	break;
+	case 11:
+#line 70 "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"
+	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
+	break;
+	case 3:
+#line 69 "hb-ot-shape-complex-khmer-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
+	break;
+#line 266 "hb-ot-shape-complex-khmer-machine.hh"
+	}
+
+_again:
+	switch ( _khmer_syllable_machine_to_state_actions[cs] ) {
+	case 4:
+#line 1 "NONE"
+	{ts = 0;}
+	break;
+#line 275 "hb-ot-shape-complex-khmer-machine.hh"
+	}
+
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _khmer_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _khmer_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	}
+
+#line 104 "hb-ot-shape-complex-khmer-machine.rl"
+
+}
+
+#endif /* HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-khmer-machine.rl b/src/hb-ot-shape-complex-khmer-machine.rl
new file mode 100644
index 0000000..8b00c37
--- /dev/null
+++ b/src/hb-ot-shape-complex-khmer-machine.rl
@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
+
+#include "hb-private.hh"
+
+%%{
+  machine khmer_syllable_machine;
+  alphtype unsigned char;
+  write data;
+}%%
+
+%%{
+
+# 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;
+
+c = (C | Ra | V);		# is_consonant
+n = ((ZWNJ?.RS)? (N.N?)?);	# is_consonant_modifier
+z = ZWJ|ZWNJ;			# is_joiner
+
+cn = c.n?;
+matra_group = z?.M.N?;
+syllable_tail = (SM.SM?)?;
+
+
+broken_cluster =	n? (Coeng.cn)* matra_group* (Coeng.cn)? syllable_tail;
+consonant_syllable =	(c|PLACEHOLDER|DOTTEDCIRCLE) broken_cluster;
+other =			any;
+
+main := |*
+	consonant_syllable	=> { found_syllable (consonant_syllable); };
+	broken_cluster		=> { found_syllable (broken_cluster); };
+	other			=> { found_syllable (non_khmer_cluster); };
+*|;
+
+
+}%%
+
+#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++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  %%{
+    write init;
+    getkey info[p].khmer_category();
+  }%%
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int last = 0;
+  unsigned int syllable_serial = 1;
+  %%{
+    write exec;
+  }%%
+}
+
+#endif /* HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-khmer-private.hh b/src/hb-ot-shape-complex-khmer-private.hh
new file mode 100644
index 0000000..f90ef96
--- /dev/null
+++ b/src/hb-ot-shape-complex-khmer-private.hh
@@ -0,0 +1,124 @@
+/*
+ * 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
new file mode 100644
index 0000000..304879d
--- /dev/null
+++ b/src/hb-ot-shape-complex-khmer.cc
@@ -0,0 +1,835 @@
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-ot-shape-complex-khmer-private.hh"
+#include "hb-ot-layout-private.hh"
+
+
+/*
+ * Khmer shaper.
+ */
+
+struct feature_list_t {
+  hb_tag_t tag;
+  hb_ot_map_feature_flags_t flags;
+};
+
+static const feature_list_t
+khmer_features[] =
+{
+  /*
+   * Basic features.
+   * These features are applied in order, one at a time, after initial_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},
+  /*
+   * 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('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('d','i','s','t'), F_GLOBAL},
+  {HB_TAG('a','b','v','m'), F_GLOBAL},
+  {HB_TAG('b','l','w','m'), F_GLOBAL},
+};
+
+/*
+ * Must be in the same order as the khmer_features array.
+ */
+enum {
+  PREF,
+  BLWF,
+  ABVF,
+  PSTF,
+  CFAR,
+
+  _PRES,
+  _ABVS,
+  _BLWS,
+  _PSTS,
+  _DIST,
+  _ABVM,
+  _BLWM,
+
+  KHMER_NUM_FEATURES,
+  KHMER_BASIC_FEATURES = _PRES /* Don't forget to update this! */
+};
+
+static void
+setup_syllables (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font,
+		 hb_buffer_t *buffer);
+static void
+initial_reordering (const hb_ot_shape_plan_t *plan,
+		    hb_font_t *font,
+		    hb_buffer_t *buffer);
+static void
+final_reordering (const hb_ot_shape_plan_t *plan,
+		  hb_font_t *font,
+		  hb_buffer_t *buffer);
+static void
+clear_syllables (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font,
+		 hb_buffer_t *buffer);
+
+static void
+collect_features_khmer (hb_ot_shape_planner_t *plan)
+{
+  hb_ot_map_builder_t *map = &plan->map;
+
+  /* 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'));
+  /* 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'));
+
+
+  unsigned int i = 0;
+  map->add_gsub_pause (initial_reordering);
+  for (; i < KHMER_BASIC_FEATURES; i++) {
+    map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
+    map->add_gsub_pause (nullptr);
+  }
+  map->add_gsub_pause (final_reordering);
+  for (; i < KHMER_NUM_FEATURES; i++) {
+    map->add_feature (khmer_features[i].tag, 1, khmer_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 (clear_syllables);
+}
+
+static void
+override_features_khmer (hb_ot_shape_planner_t *plan)
+{
+  /* 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.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
+}
+
+
+struct would_substitute_feature_t
+{
+  inline void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
+  {
+    zero_context = zero_context_;
+    map->get_stage_lookups (0/*GSUB*/,
+			    map->get_feature_stage (0/*GSUB*/, feature_tag),
+			    &lookups, &count);
+  }
+
+  inline bool would_substitute (const hb_codepoint_t *glyphs,
+				unsigned int          glyphs_count,
+				hb_face_t            *face) const
+  {
+    for (unsigned int i = 0; i < count; i++)
+      if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context))
+	return true;
+    return false;
+  }
+
+  private:
+  const hb_ot_map_t::lookup_map_t *lookups;
+  unsigned int count;
+  bool zero_context;
+};
+
+struct khmer_shape_plan_t
+{
+  ASSERT_POD ();
+
+  inline bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
+  {
+    hb_codepoint_t glyph = virama_glyph;
+    if (unlikely (virama_glyph == (hb_codepoint_t) -1))
+    {
+      if (!font->get_nominal_glyph (0x17D2u, &glyph))
+	glyph = 0;
+      /* Technically speaking, the spec says we should apply 'locl' to virama too.
+       * Maybe one day... */
+
+      /* Our get_nominal_glyph() function needs a font, so we can't get the virama glyph
+       * during shape planning...  Instead, overwrite it here.  It's safe.  Don't worry! */
+      virama_glyph = glyph;
+    }
+
+    *pglyph = glyph;
+    return glyph != 0;
+  }
+
+  mutable hb_codepoint_t virama_glyph;
+
+  would_substitute_feature_t pref;
+
+  hb_mask_t mask_array[KHMER_NUM_FEATURES];
+};
+
+static void *
+data_create_khmer (const hb_ot_shape_plan_t *plan)
+{
+  khmer_shape_plan_t *khmer_plan = (khmer_shape_plan_t *) calloc (1, sizeof (khmer_shape_plan_t));
+  if (unlikely (!khmer_plan))
+    return nullptr;
+
+  khmer_plan->virama_glyph = (hb_codepoint_t) -1;
+
+  khmer_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'), true);
+
+  for (unsigned int i = 0; i < ARRAY_LENGTH (khmer_plan->mask_array); i++)
+    khmer_plan->mask_array[i] = (khmer_features[i].flags & F_GLOBAL) ?
+				 0 : plan->map.get_1_mask (khmer_features[i].tag);
+
+  return khmer_plan;
+}
+
+static void
+data_destroy_khmer (void *data)
+{
+  free (data);
+}
+
+
+enum syllable_type_t {
+  consonant_syllable,
+  broken_cluster,
+  non_khmer_cluster,
+};
+
+#include "hb-ot-shape-complex-khmer-machine.hh"
+
+static void
+setup_masks_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		   hb_buffer_t              *buffer,
+		   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. */
+
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  for (unsigned int i = 0; i < count; i++)
+    set_khmer_properties (info[i]);
+}
+
+static void
+setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		 hb_font_t *font HB_UNUSED,
+		 hb_buffer_t *buffer)
+{
+  find_syllables (buffer);
+  foreach_syllable (buffer, start, end)
+    buffer->unsafe_to_break (start, end);
+}
+
+static int
+compare_khmer_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
+{
+  int a = pa->khmer_position();
+  int b = pb->khmer_position();
+
+  return a < b ? -1 : a == b ? 0 : +1;
+}
+
+
+/* Rules from:
+ * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
+
+static void
+initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
+				       hb_face_t *face,
+				       hb_buffer_t *buffer,
+				       unsigned int start, unsigned int end)
+{
+  const khmer_shape_plan_t *khmer_plan = (const khmer_shape_plan_t *) plan->data;
+  hb_glyph_info_t *info = buffer->info;
+
+  /* 1. Khmer shaping assumes that a syllable will begin with a Cons, IndV, or Number. */
+
+  /* The first consonant is always the base. */
+  unsigned int base = start;
+  info[base].khmer_position() = POS_BASE_C;
+
+  /* Mark all subsequent consonants as below. */
+  for (unsigned int i = base + 1; i < end; i++)
+    if (is_consonant_or_vowel (info[i]))
+      info[i].khmer_position() = POS_BELOW_C;
+
+  /* Mark final consonants.  A final consonant is one appearing after a matra,
+   * like in Khmer. */
+  for (unsigned int i = base + 1; i < end; i++)
+    if (info[i].khmer_category() == OT_M) {
+      for (unsigned int j = i + 1; j < end; j++)
+        if (is_consonant_or_vowel (info[j])) {
+	  info[j].khmer_position() = POS_FINAL_C;
+	  break;
+	}
+      break;
+    }
+
+  /* Attach misc marks to previous char to move with them. */
+  {
+    khmer_position_t last_pos = POS_START;
+    for (unsigned int i = start; i < end; i++)
+    {
+      if ((FLAG_UNSAFE (info[i].khmer_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | FLAG (OT_Coeng))))
+      {
+	info[i].khmer_position() = last_pos;
+	if (unlikely (info[i].khmer_category() == OT_H &&
+		      info[i].khmer_position() == POS_PRE_M))
+	{
+	  /*
+	   * Uniscribe doesn't move the Halant with Left Matra.
+	   * TEST: U+092B,U+093F,U+094DE
+	   * We follow.  This is important for the Sinhala
+	   * U+0DDA split matra since it decomposes to U+0DD9,U+0DCA
+	   * where U+0DD9 is a left matra and U+0DCA is the virama.
+	   * We don't want to move the virama with the left matra.
+	   * TEST: U+0D9A,U+0DDA
+	   */
+	  for (unsigned int j = i; j > start; j--)
+	    if (info[j - 1].khmer_position() != POS_PRE_M) {
+	      info[i].khmer_position() = info[j - 1].khmer_position();
+	      break;
+	    }
+	}
+      } else if (info[i].khmer_position() != POS_SMVD) {
+        last_pos = (khmer_position_t) info[i].khmer_position();
+      }
+    }
+  }
+  /* For post-base consonants let them own anything before them
+   * since the last consonant or matra. */
+  {
+    unsigned int last = base;
+    for (unsigned int i = base + 1; i < end; i++)
+      if (is_consonant_or_vowel (info[i]))
+      {
+	for (unsigned int j = last + 1; j < i; j++)
+	  if (info[j].khmer_position() < POS_SMVD)
+	    info[j].khmer_position() = info[i].khmer_position();
+	last = i;
+      } else if (info[i].khmer_category() == OT_M)
+        last = i;
+  }
+
+  {
+    /* Use syllable() for sort accounting temporarily. */
+    unsigned int syllable = info[start].syllable();
+    for (unsigned int i = start; i < end; i++)
+      info[i].syllable() = i - start;
+
+    /* Sit tight, rock 'n roll! */
+    hb_stable_sort (info + start, end - start, compare_khmer_order);
+    /* Find base again */
+    base = end;
+    for (unsigned int i = start; i < end; i++)
+      if (info[i].khmer_position() == POS_BASE_C)
+      {
+	base = i;
+	break;
+      }
+
+    /* Note!  syllable() is a one-byte field. */
+    for (unsigned int i = base; i < end; i++)
+      if (info[i].syllable() != 255)
+      {
+	unsigned int max = i;
+	unsigned int j = start + info[i].syllable();
+	while (j != i)
+	{
+	  max = MAX (max, j);
+	  unsigned int next = start + info[j].syllable();
+	  info[j].syllable() = 255; /* So we don't process j later again. */
+	  j = next;
+	}
+	if (i != max)
+	  buffer->merge_clusters (i, max + 1);
+      }
+
+    /* Put syllable back in. */
+    for (unsigned int i = start; i < end; i++)
+      info[i].syllable() = syllable;
+  }
+
+  /* Setup masks now */
+
+  {
+    hb_mask_t mask;
+
+    /* Post-base */
+    mask = khmer_plan->mask_array[BLWF] | khmer_plan->mask_array[ABVF] | khmer_plan->mask_array[PSTF];
+    for (unsigned int i = base + 1; i < end; i++)
+      info[i].mask  |= mask;
+  }
+
+  unsigned int pref_len = 2;
+  if (khmer_plan->mask_array[PREF] && base + pref_len < end)
+  {
+    /* Find a Halant,Ra sequence and mark it for pre-base-reordering processing. */
+    for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) {
+      hb_codepoint_t glyphs[2];
+      for (unsigned int j = 0; j < pref_len; j++)
+        glyphs[j] = info[i + j].codepoint;
+      if (khmer_plan->pref.would_substitute (glyphs, pref_len, face))
+      {
+	for (unsigned int j = 0; j < pref_len; j++)
+	  info[i++].mask |= khmer_plan->mask_array[PREF];
+
+	/* Mark the subsequent stuff with 'cfar'.  Used in Khmer.
+	 * Read the feature spec.
+	 * This allows distinguishing the following cases with MS Khmer fonts:
+	 * U+1784,U+17D2,U+179A,U+17D2,U+1782
+	 * U+1784,U+17D2,U+1782,U+17D2,U+179A
+	 */
+	if (khmer_plan->mask_array[CFAR])
+	  for (; i < end; i++)
+	    info[i].mask |= khmer_plan->mask_array[CFAR];
+
+	break;
+      }
+    }
+  }
+}
+
+static void
+initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
+			     hb_face_t *face,
+			     hb_buffer_t *buffer,
+			     unsigned int start, unsigned int end)
+{
+  syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
+  switch (syllable_type)
+  {
+    case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
+    case consonant_syllable:
+     initial_reordering_consonant_syllable (plan, face, buffer, start, end);
+     break;
+
+    case non_khmer_cluster:
+      break;
+  }
+}
+
+static inline void
+insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer)
+{
+  /* Note: This loop is extra overhead, but should not be measurable. */
+  bool has_broken_syllables = false;
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  for (unsigned int i = 0; i < count; i++)
+    if ((info[i].syllable() & 0x0F) == broken_cluster)
+    {
+      has_broken_syllables = true;
+      break;
+    }
+  if (likely (!has_broken_syllables))
+    return;
+
+
+  hb_codepoint_t dottedcircle_glyph;
+  if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph))
+    return;
+
+  hb_glyph_info_t dottedcircle = {0};
+  dottedcircle.codepoint = 0x25CCu;
+  set_khmer_properties (dottedcircle);
+  dottedcircle.codepoint = dottedcircle_glyph;
+
+  buffer->clear_output ();
+
+  buffer->idx = 0;
+  unsigned int last_syllable = 0;
+  while (buffer->idx < buffer->len && !buffer->in_error)
+  {
+    unsigned int syllable = buffer->cur().syllable();
+    syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
+    if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
+    {
+      last_syllable = syllable;
+
+      hb_glyph_info_t ginfo = dottedcircle;
+      ginfo.cluster = buffer->cur().cluster;
+      ginfo.mask = buffer->cur().mask;
+      ginfo.syllable() = buffer->cur().syllable();
+      /* TODO Set glyph_props? */
+
+      /* Insert dottedcircle after possible Repha. */
+      while (buffer->idx < buffer->len && !buffer->in_error &&
+	     last_syllable == buffer->cur().syllable() &&
+	     buffer->cur().khmer_category() == OT_Repha)
+        buffer->next_glyph ();
+
+      buffer->output_info (ginfo);
+    }
+    else
+      buffer->next_glyph ();
+  }
+
+  buffer->swap_buffers ();
+}
+
+static void
+initial_reordering (const hb_ot_shape_plan_t *plan,
+		    hb_font_t *font,
+		    hb_buffer_t *buffer)
+{
+  insert_dotted_circles (plan, font, buffer);
+
+  foreach_syllable (buffer, start, end)
+    initial_reordering_syllable (plan, font->face, buffer, start, end);
+}
+
+static void
+final_reordering_syllable (const hb_ot_shape_plan_t *plan,
+			   hb_buffer_t *buffer,
+			   unsigned int start, unsigned int end)
+{
+  const khmer_shape_plan_t *khmer_plan = (const khmer_shape_plan_t *) plan->data;
+  hb_glyph_info_t *info = buffer->info;
+
+
+  /* This function relies heavily on halant glyphs.  Lots of ligation
+   * and possibly multiple substitutions happened prior to this
+   * phase, and that might have messed up our properties.  Recover
+   * from a particular case of that where we're fairly sure that a
+   * class of OT_H is desired but has been lost. */
+  if (khmer_plan->virama_glyph)
+  {
+    unsigned int virama_glyph = khmer_plan->virama_glyph;
+    for (unsigned int i = start; i < end; i++)
+      if (info[i].codepoint == virama_glyph &&
+	  _hb_glyph_info_ligated (&info[i]) &&
+	  _hb_glyph_info_multiplied (&info[i]))
+      {
+        /* This will make sure that this glyph passes is_coeng() test. */
+	info[i].khmer_category() = OT_H;
+	_hb_glyph_info_clear_ligated_and_multiplied (&info[i]);
+      }
+  }
+
+
+  /* 4. Final reordering:
+   *
+   * After the localized forms and basic shaping forms GSUB features have been
+   * applied (see below), the shaping engine performs some final glyph
+   * reordering before applying all the remaining font features to the entire
+   * syllable.
+   */
+
+  bool try_pref = !!khmer_plan->mask_array[PREF];
+
+  /* Find base again */
+  unsigned int base;
+  for (base = start; base < end; base++)
+    if (info[base].khmer_position() >= POS_BASE_C)
+    {
+      if (try_pref && base + 1 < end)
+      {
+	for (unsigned int i = base + 1; i < end; i++)
+	  if ((info[i].mask & khmer_plan->mask_array[PREF]) != 0)
+	  {
+	    if (!(_hb_glyph_info_substituted (&info[i]) &&
+		  _hb_glyph_info_ligated_and_didnt_multiply (&info[i])))
+	    {
+	      /* Ok, this was a 'pref' candidate but didn't form any.
+	       * Base is around here... */
+	      base = i;
+	      while (base < end && is_coeng (info[base]))
+		base++;
+	      info[base].khmer_position() = POS_BASE_C;
+
+	      try_pref = false;
+	    }
+	    break;
+	  }
+      }
+
+      if (start < base && info[base].khmer_position() > POS_BASE_C)
+        base--;
+      break;
+    }
+  if (base == end && start < base &&
+      is_one_of (info[base - 1], FLAG (OT_ZWJ)))
+    base--;
+  if (base < end)
+    while (start < base &&
+	   is_one_of (info[base], (FLAG (OT_N) | FLAG (OT_Coeng))))
+      base--;
+
+
+  /*   o Reorder matras:
+   *
+   *     If a pre-base matra character had been reordered before applying basic
+   *     features, the glyph can be moved closer to the main consonant based on
+   *     whether half-forms had been formed. Actual position for the matra is
+   *     defined as “after last standalone halant glyph, after initial matra
+   *     position and before the main consonant”. If ZWJ or ZWNJ follow this
+   *     halant, position is moved after it.
+   */
+
+  if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */
+  {
+    /* If we lost track of base, alas, position before last thingy. */
+    unsigned int new_pos = base == end ? base - 2 : base - 1;
+
+    while (new_pos > start &&
+	   !(is_one_of (info[new_pos], (FLAG (OT_M) | FLAG (OT_Coeng)))))
+      new_pos--;
+
+    /* If we found no Halant we are done.
+     * Otherwise only proceed if the Halant does
+     * not belong to the Matra itself! */
+    if (is_coeng (info[new_pos]) &&
+	info[new_pos].khmer_position() != POS_PRE_M)
+    {
+      /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
+      if (new_pos + 1 < end && is_joiner (info[new_pos + 1]))
+	new_pos++;
+    }
+    else
+      new_pos = start; /* No move. */
+
+    if (start < new_pos && info[new_pos].khmer_position () != POS_PRE_M)
+    {
+      /* Now go see if there's actually any matras... */
+      for (unsigned int i = new_pos; i > start; i--)
+	if (info[i - 1].khmer_position () == POS_PRE_M)
+	{
+	  unsigned int old_pos = i - 1;
+	  if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */
+	    base--;
+
+	  hb_glyph_info_t tmp = info[old_pos];
+	  memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0]));
+	  info[new_pos] = tmp;
+
+	  /* Note: this merge_clusters() is intentionally *after* the reordering.
+	   * Indic matra reordering is special and tricky... */
+	  buffer->merge_clusters (new_pos, MIN (end, base + 1));
+
+	  new_pos--;
+	}
+    } else {
+      for (unsigned int i = start; i < base; i++)
+	if (info[i].khmer_position () == POS_PRE_M) {
+	  buffer->merge_clusters (i, MIN (end, base + 1));
+	  break;
+	}
+    }
+  }
+
+
+  /*   o Reorder pre-base-reordering consonants:
+   *
+   *     If a pre-base-reordering consonant is found, reorder it according to
+   *     the following rules:
+   */
+
+  if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base-reordering Ra. */
+  {
+    for (unsigned int i = base + 1; i < end; i++)
+      if ((info[i].mask & khmer_plan->mask_array[PREF]) != 0)
+      {
+	/*       1. Only reorder a glyph produced by substitution during application
+	 *          of the <pref> feature. (Note that a font may shape a Ra consonant with
+	 *          the feature generally but block it in certain contexts.)
+	 */
+        /* Note: We just check that something got substituted.  We don't check that
+	 * the <pref> feature actually did it...
+	 *
+	 * Reorder pref only if it ligated. */
+	if (_hb_glyph_info_ligated_and_didnt_multiply (&info[i]))
+	{
+	  /*
+	   *       2. Try to find a target position the same way as for pre-base matra.
+	   *          If it is found, reorder pre-base consonant glyph.
+	   *
+	   *       3. If position is not found, reorder immediately before main
+	   *          consonant.
+	   */
+
+	  unsigned int new_pos = base;
+	  while (new_pos > start &&
+		 !(is_one_of (info[new_pos - 1], FLAG(OT_M) | FLAG (OT_Coeng))))
+	    new_pos--;
+
+	  /* In Khmer coeng model, a H,Ra can go *after* matras.  If it goes after a
+	   * split matra, it should be reordered to *before* the left part of such matra. */
+	  if (new_pos > start && info[new_pos - 1].khmer_category() == OT_M)
+	  {
+	    unsigned int old_pos = i;
+	    for (unsigned int j = base + 1; j < old_pos; j++)
+	      if (info[j].khmer_category() == OT_M)
+	      {
+		new_pos--;
+		break;
+	      }
+	  }
+
+	  if (new_pos > start && is_coeng (info[new_pos - 1]))
+	  {
+	    /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
+	    if (new_pos < end && is_joiner (info[new_pos]))
+	      new_pos++;
+	  }
+
+	  {
+	    unsigned int old_pos = i;
+
+	    buffer->merge_clusters (new_pos, old_pos + 1);
+	    hb_glyph_info_t tmp = info[old_pos];
+	    memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * sizeof (info[0]));
+	    info[new_pos] = tmp;
+
+	    if (new_pos <= base && base < old_pos)
+	      base++;
+	  }
+	}
+
+        break;
+      }
+  }
+
+
+  /*
+   * Finish off the clusters and go home!
+   */
+  if (hb_options ().uniscribe_bug_compatible)
+  {
+    /* Uniscribe merges the entire syllable into a single cluster... Except for Tamil & Sinhala.
+     * This means, half forms are submerged into the main consonant's cluster.
+     * This is unnecessary, and makes cursor positioning harder, but that's what
+     * Uniscribe does. */
+    buffer->merge_clusters (start, end);
+  }
+}
+
+
+static void
+final_reordering (const hb_ot_shape_plan_t *plan,
+		  hb_font_t *font HB_UNUSED,
+		  hb_buffer_t *buffer)
+{
+  unsigned int count = buffer->len;
+  if (unlikely (!count)) return;
+
+  foreach_syllable (buffer, start, end)
+    final_reordering_syllable (plan, buffer, start, end);
+
+  HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category);
+  HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_position);
+}
+
+
+static void
+clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		 hb_font_t *font HB_UNUSED,
+		 hb_buffer_t *buffer)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    info[i].syllable() = 0;
+}
+
+
+static bool
+decompose_khmer (const hb_ot_shape_normalize_context_t *c,
+		 hb_codepoint_t  ab,
+		 hb_codepoint_t *a,
+		 hb_codepoint_t *b)
+{
+  switch (ab)
+  {
+    /*
+     * Decompose split matras that don't have Unicode decompositions.
+     */
+
+    /* Khmer */
+    case 0x17BEu  : *a = 0x17C1u; *b= 0x17BEu; return true;
+    case 0x17BFu  : *a = 0x17C1u; *b= 0x17BFu; return true;
+    case 0x17C0u  : *a = 0x17C1u; *b= 0x17C0u; return true;
+    case 0x17C4u  : *a = 0x17C1u; *b= 0x17C4u; return true;
+    case 0x17C5u  : *a = 0x17C1u; *b= 0x17C5u; return true;
+  }
+
+  return (bool) c->unicode->decompose (ab, a, b);
+}
+
+static bool
+compose_khmer (const hb_ot_shape_normalize_context_t *c,
+	       hb_codepoint_t  a,
+	       hb_codepoint_t  b,
+	       hb_codepoint_t *ab)
+{
+  /* Avoid recomposing split matras. */
+  if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a)))
+    return false;
+
+  return (bool) c->unicode->compose (a, b, ab);
+}
+
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_khmer =
+{
+  collect_features_khmer,
+  override_features_khmer,
+  data_create_khmer,
+  data_destroy_khmer,
+  nullptr, /* preprocess_text */
+  nullptr, /* postprocess_glyphs */
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
+  decompose_khmer,
+  compose_khmer,
+  setup_masks_khmer,
+  nullptr, /* disable_otl */
+  nullptr, /* reorder_marks */
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+  false, /* fallback_position */
+};
diff --git a/src/hb-ot-shape-complex-myanmar-machine.hh b/src/hb-ot-shape-complex-myanmar-machine.hh
new file mode 100644
index 0000000..fb67dd4
--- /dev/null
+++ b/src/hb-ot-shape-complex-myanmar-machine.hh
@@ -0,0 +1,413 @@
+
+#line 1 "hb-ot-shape-complex-myanmar-machine.rl"
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
+
+#include "hb-private.hh"
+
+
+#line 36 "hb-ot-shape-complex-myanmar-machine.hh"
+static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
+	1u, 32u, 3u, 30u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 
+	3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 16u, 3u, 29u, 3u, 29u, 3u, 29u, 
+	3u, 29u, 3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 5u, 29u, 
+	5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 3u, 29u, 3u, 29u, 3u, 29u, 
+	3u, 29u, 3u, 30u, 3u, 29u, 1u, 32u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 
+	3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 32u, 8u, 8u, 
+	0
+};
+
+static const char _myanmar_syllable_machine_key_spans[] = {
+	32, 28, 25, 4, 25, 23, 21, 21, 
+	27, 27, 27, 27, 16, 27, 27, 27, 
+	27, 27, 28, 27, 27, 27, 27, 25, 
+	4, 25, 23, 21, 21, 27, 27, 27, 
+	27, 28, 27, 32, 27, 27, 27, 27, 
+	27, 28, 27, 27, 27, 27, 32, 1
+};
+
+static const short _myanmar_syllable_machine_index_offsets[] = {
+	0, 33, 62, 88, 93, 119, 143, 165, 
+	187, 215, 243, 271, 299, 316, 344, 372, 
+	400, 428, 456, 485, 513, 541, 569, 597, 
+	623, 628, 654, 678, 700, 722, 750, 778, 
+	806, 834, 863, 891, 924, 952, 980, 1008, 
+	1036, 1064, 1093, 1121, 1149, 1177, 1205, 1238
+};
+
+static const char _myanmar_syllable_machine_indicies[] = {
+	1, 1, 2, 3, 4, 4, 0, 5, 
+	0, 6, 1, 0, 0, 0, 0, 7, 
+	0, 8, 9, 0, 10, 11, 12, 13, 
+	14, 15, 16, 17, 18, 19, 20, 1, 
+	0, 22, 23, 24, 24, 21, 25, 21, 
+	26, 21, 21, 21, 21, 21, 21, 21, 
+	27, 21, 21, 28, 29, 30, 31, 32, 
+	33, 34, 35, 36, 37, 21, 24, 24, 
+	21, 25, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 38, 21, 21, 21, 21, 
+	21, 21, 32, 21, 21, 21, 36, 21, 
+	24, 24, 21, 25, 21, 24, 24, 21, 
+	25, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 32, 21, 21, 21, 36, 21, 39, 
+	21, 24, 24, 21, 25, 21, 32, 21, 
+	21, 21, 21, 21, 21, 21, 40, 21, 
+	21, 21, 21, 21, 21, 32, 21, 24, 
+	24, 21, 25, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 40, 21, 21, 21, 
+	21, 21, 21, 32, 21, 24, 24, 21, 
+	25, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 32, 21, 22, 21, 24, 24, 21, 
+	25, 21, 26, 21, 21, 21, 21, 21, 
+	21, 21, 41, 21, 21, 41, 21, 21, 
+	21, 32, 42, 21, 21, 36, 21, 22, 
+	21, 24, 24, 21, 25, 21, 26, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 32, 21, 21, 
+	21, 36, 21, 22, 21, 24, 24, 21, 
+	25, 21, 26, 21, 21, 21, 21, 21, 
+	21, 21, 41, 21, 21, 21, 21, 21, 
+	21, 32, 42, 21, 21, 36, 21, 22, 
+	21, 24, 24, 21, 25, 21, 26, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 32, 42, 21, 
+	21, 36, 21, 1, 1, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 1, 21, 22, 21, 24, 24, 
+	21, 25, 21, 26, 21, 21, 21, 21, 
+	21, 21, 21, 27, 21, 21, 28, 29, 
+	30, 31, 32, 33, 34, 35, 36, 21, 
+	22, 21, 24, 24, 21, 25, 21, 26, 
+	21, 21, 21, 21, 21, 21, 21, 43, 
+	21, 21, 21, 21, 21, 21, 32, 33, 
+	34, 35, 36, 21, 22, 21, 24, 24, 
+	21, 25, 21, 26, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 32, 33, 34, 35, 36, 21, 
+	22, 21, 24, 24, 21, 25, 21, 26, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 32, 33, 
+	34, 21, 36, 21, 22, 21, 24, 24, 
+	21, 25, 21, 26, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 32, 21, 34, 21, 36, 21, 
+	22, 21, 24, 24, 21, 25, 21, 26, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 32, 33, 
+	34, 35, 36, 43, 21, 22, 21, 24, 
+	24, 21, 25, 21, 26, 21, 21, 21, 
+	21, 21, 21, 21, 43, 21, 21, 28, 
+	21, 30, 21, 32, 33, 34, 35, 36, 
+	21, 22, 21, 24, 24, 21, 25, 21, 
+	26, 21, 21, 21, 21, 21, 21, 21, 
+	43, 21, 21, 28, 21, 21, 21, 32, 
+	33, 34, 35, 36, 21, 22, 21, 24, 
+	24, 21, 25, 21, 26, 21, 21, 21, 
+	21, 21, 21, 21, 43, 21, 21, 28, 
+	29, 30, 21, 32, 33, 34, 35, 36, 
+	21, 22, 23, 24, 24, 21, 25, 21, 
+	26, 21, 21, 21, 21, 21, 21, 21, 
+	27, 21, 21, 28, 29, 30, 31, 32, 
+	33, 34, 35, 36, 21, 3, 3, 44, 
+	5, 44, 44, 44, 44, 44, 44, 44, 
+	44, 44, 45, 44, 44, 44, 44, 44, 
+	44, 14, 44, 44, 44, 18, 44, 3, 
+	3, 44, 5, 44, 3, 3, 44, 5, 
+	44, 44, 44, 44, 44, 44, 44, 44, 
+	44, 44, 44, 44, 44, 44, 44, 44, 
+	14, 44, 44, 44, 18, 44, 46, 44, 
+	3, 3, 44, 5, 44, 14, 44, 44, 
+	44, 44, 44, 44, 44, 47, 44, 44, 
+	44, 44, 44, 44, 14, 44, 3, 3, 
+	44, 5, 44, 44, 44, 44, 44, 44, 
+	44, 44, 44, 47, 44, 44, 44, 44, 
+	44, 44, 14, 44, 3, 3, 44, 5, 
+	44, 44, 44, 44, 44, 44, 44, 44, 
+	44, 44, 44, 44, 44, 44, 44, 44, 
+	14, 44, 2, 44, 3, 3, 44, 5, 
+	44, 6, 44, 44, 44, 44, 44, 44, 
+	44, 48, 44, 44, 48, 44, 44, 44, 
+	14, 49, 44, 44, 18, 44, 2, 44, 
+	3, 3, 44, 5, 44, 6, 44, 44, 
+	44, 44, 44, 44, 44, 44, 44, 44, 
+	44, 44, 44, 44, 14, 44, 44, 44, 
+	18, 44, 2, 44, 3, 3, 44, 5, 
+	44, 6, 44, 44, 44, 44, 44, 44, 
+	44, 48, 44, 44, 44, 44, 44, 44, 
+	14, 49, 44, 44, 18, 44, 2, 44, 
+	3, 3, 44, 5, 44, 6, 44, 44, 
+	44, 44, 44, 44, 44, 44, 44, 44, 
+	44, 44, 44, 44, 14, 49, 44, 44, 
+	18, 44, 22, 23, 24, 24, 21, 25, 
+	21, 26, 21, 21, 21, 21, 21, 21, 
+	21, 50, 21, 21, 28, 29, 30, 31, 
+	32, 33, 34, 35, 36, 37, 21, 22, 
+	51, 24, 24, 21, 25, 21, 26, 21, 
+	21, 21, 21, 21, 21, 21, 27, 21, 
+	21, 28, 29, 30, 31, 32, 33, 34, 
+	35, 36, 21, 1, 1, 2, 3, 3, 
+	3, 44, 5, 44, 6, 1, 44, 44, 
+	44, 44, 1, 44, 8, 44, 44, 10, 
+	11, 12, 13, 14, 15, 16, 17, 18, 
+	19, 44, 1, 44, 2, 44, 3, 3, 
+	44, 5, 44, 6, 44, 44, 44, 44, 
+	44, 44, 44, 8, 44, 44, 10, 11, 
+	12, 13, 14, 15, 16, 17, 18, 44, 
+	2, 44, 3, 3, 44, 5, 44, 6, 
+	44, 44, 44, 44, 44, 44, 44, 52, 
+	44, 44, 44, 44, 44, 44, 14, 15, 
+	16, 17, 18, 44, 2, 44, 3, 3, 
+	44, 5, 44, 6, 44, 44, 44, 44, 
+	44, 44, 44, 44, 44, 44, 44, 44, 
+	44, 44, 14, 15, 16, 17, 18, 44, 
+	2, 44, 3, 3, 44, 5, 44, 6, 
+	44, 44, 44, 44, 44, 44, 44, 44, 
+	44, 44, 44, 44, 44, 44, 14, 15, 
+	16, 44, 18, 44, 2, 44, 3, 3, 
+	44, 5, 44, 6, 44, 44, 44, 44, 
+	44, 44, 44, 44, 44, 44, 44, 44, 
+	44, 44, 14, 44, 16, 44, 18, 44, 
+	2, 44, 3, 3, 44, 5, 44, 6, 
+	44, 44, 44, 44, 44, 44, 44, 44, 
+	44, 44, 44, 44, 44, 44, 14, 15, 
+	16, 17, 18, 52, 44, 2, 44, 3, 
+	3, 44, 5, 44, 6, 44, 44, 44, 
+	44, 44, 44, 44, 52, 44, 44, 10, 
+	44, 12, 44, 14, 15, 16, 17, 18, 
+	44, 2, 44, 3, 3, 44, 5, 44, 
+	6, 44, 44, 44, 44, 44, 44, 44, 
+	52, 44, 44, 10, 44, 44, 44, 14, 
+	15, 16, 17, 18, 44, 2, 44, 3, 
+	3, 44, 5, 44, 6, 44, 44, 44, 
+	44, 44, 44, 44, 52, 44, 44, 10, 
+	11, 12, 44, 14, 15, 16, 17, 18, 
+	44, 2, 3, 3, 3, 44, 5, 44, 
+	6, 44, 44, 44, 44, 44, 44, 44, 
+	8, 44, 44, 10, 11, 12, 13, 14, 
+	15, 16, 17, 18, 44, 1, 1, 53, 
+	53, 53, 53, 53, 53, 53, 53, 1, 
+	53, 53, 53, 53, 1, 53, 53, 53, 
+	53, 53, 53, 53, 53, 53, 53, 53, 
+	53, 53, 53, 53, 1, 53, 54, 53, 
+	0
+};
+
+static const char _myanmar_syllable_machine_trans_targs[] = {
+	0, 1, 23, 0, 0, 24, 30, 33, 
+	36, 46, 37, 42, 43, 44, 26, 39, 
+	40, 41, 29, 45, 47, 0, 2, 12, 
+	0, 3, 9, 13, 14, 19, 20, 21, 
+	5, 16, 17, 18, 8, 22, 4, 6, 
+	7, 10, 11, 15, 0, 25, 27, 28, 
+	31, 32, 34, 35, 38, 0, 0
+};
+
+static const char _myanmar_syllable_machine_trans_actions[] = {
+	3, 0, 0, 4, 5, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 6, 0, 0, 
+	7, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 8, 0, 0, 0, 
+	0, 0, 0, 0, 0, 9, 10
+};
+
+static const char _myanmar_syllable_machine_to_state_actions[] = {
+	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, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const char _myanmar_syllable_machine_from_state_actions[] = {
+	2, 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
+};
+
+static const short _myanmar_syllable_machine_eof_trans[] = {
+	0, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 45, 
+	45, 45, 45, 45, 45, 45, 45, 45, 
+	45, 22, 22, 45, 45, 45, 45, 45, 
+	45, 45, 45, 45, 45, 45, 54, 54
+};
+
+static const int myanmar_syllable_machine_start = 0;
+static const int myanmar_syllable_machine_first_final = 0;
+static const int myanmar_syllable_machine_error = -1;
+
+static const int myanmar_syllable_machine_en_main = 0;
+
+
+#line 36 "hb-ot-shape-complex-myanmar-machine.rl"
+
+
+
+#line 94 "hb-ot-shape-complex-myanmar-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++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  
+#line 302 "hb-ot-shape-complex-myanmar-machine.hh"
+	{
+	cs = myanmar_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 115 "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"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+_resume:
+	switch ( _myanmar_syllable_machine_from_state_actions[cs] ) {
+	case 2:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 333 "hb-ot-shape-complex-myanmar-machine.hh"
+	}
+
+	_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
+	_inds = _myanmar_syllable_machine_indicies + _myanmar_syllable_machine_index_offsets[cs];
+
+	_slen = _myanmar_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].myanmar_category()) &&
+		( info[p].myanmar_category()) <= _keys[1] ?
+		( info[p].myanmar_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+	cs = _myanmar_syllable_machine_trans_targs[_trans];
+
+	if ( _myanmar_syllable_machine_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _myanmar_syllable_machine_trans_actions[_trans] ) {
+	case 7:
+#line 86 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (consonant_syllable); }}
+	break;
+	case 5:
+#line 87 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (non_myanmar_cluster); }}
+	break;
+	case 10:
+#line 88 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (punctuation_cluster); }}
+	break;
+	case 4:
+#line 89 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (broken_cluster); }}
+	break;
+	case 3:
+#line 90 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (non_myanmar_cluster); }}
+	break;
+	case 6:
+#line 86 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (consonant_syllable); }}
+	break;
+	case 8:
+#line 89 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (broken_cluster); }}
+	break;
+	case 9:
+#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"
+	}
+
+_again:
+	switch ( _myanmar_syllable_machine_to_state_actions[cs] ) {
+	case 1:
+#line 1 "NONE"
+	{ts = 0;}
+	break;
+#line 392 "hb-ot-shape-complex-myanmar-machine.hh"
+	}
+
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _myanmar_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _myanmar_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	}
+
+#line 124 "hb-ot-shape-complex-myanmar-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-myanmar-machine.rl b/src/hb-ot-shape-complex-myanmar-machine.rl
index 9649a91..0cd84fa 100644
--- a/src/hb-ot-shape-complex-myanmar-machine.rl
+++ b/src/hb-ot-shape-complex-myanmar-machine.rl
@@ -41,7 +41,7 @@
 A    = 10;
 As   = 18;
 C    = 1;
-D    = 19;
+D    = 32;
 D0   = 20;
 DB   = 3;
 GB   = 11;
@@ -62,6 +62,7 @@
 ZWNJ = 5;
 Ra   = 16;
 P    = 31;
+CS   = 19;
 
 j = ZWJ|ZWNJ;			# Joiners
 k = (Ra As H);			# Kinzi
@@ -69,14 +70,14 @@
 c = C|Ra;			# is_consonant
 
 medial_group = MY? MR? MW? MH? As?;
-main_vowel_group = VPre* VAbv* VBlw* A* (DB As?)?;
+main_vowel_group = (VPre.VS?)* VAbv* VBlw* A* (DB As?)?;
 post_vowel_group = VPst MH? As* VAbv* A* (DB As?)?;
 pwo_tone_group = PT A* DB? As?;
 
 complex_syllable_tail = As* medial_group main_vowel_group post_vowel_group* pwo_tone_group* V* j?;
 syllable_tail = (H | complex_syllable_tail);
 
-consonant_syllable =	k? (c|IV|D|GB).VS? (H (c|IV).VS?)* syllable_tail;
+consonant_syllable =	(k|CS)? (c|IV|D|GB).VS? (H (c|IV).VS?)* syllable_tail;
 punctuation_cluster = 	P V;
 broken_cluster =	k? VS? syllable_tail;
 other =			any;
diff --git a/src/hb-ot-shape-complex-myanmar-private.hh b/src/hb-ot-shape-complex-myanmar-private.hh
new file mode 100644
index 0000000..04f81bd
--- /dev/null
+++ b/src/hb-ot-shape-complex-myanmar-private.hh
@@ -0,0 +1,171 @@
+/*
+ * 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_MYANMAR_PRIVATE_HH
+#define HB_OT_SHAPE_COMPLEX_MYANMAR_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-ot-shape-complex-indic-private.hh"
+
+
+/* buffer var allocations */
+#define myanmar_category() indic_category() /* myanmar_category_t */
+#define myanmar_position() indic_position() /* myanmar_position_t */
+
+
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum myanmar_category_t {
+  OT_As  = 18,  /* Asat */
+  OT_D0  = 20, /* Digit zero */
+  OT_DB  = OT_N, /* Dot below */
+  OT_GB  = OT_PLACEHOLDER,
+  OT_MH  = 21, /* Various consonant medial types */
+  OT_MR  = 22, /* Various consonant medial types */
+  OT_MW  = 23, /* Various consonant medial types */
+  OT_MY  = 24, /* Various consonant medial types */
+  OT_PT  = 25, /* Pwo and other tones */
+  OT_VAbv = 26,
+  OT_VBlw = 27,
+  OT_VPre = 28,
+  OT_VPst = 29,
+  OT_VS   = 30, /* Variation selectors */
+  OT_P    = 31, /* Punctuation */
+  OT_D    = 32, /* Digits except zero */
+};
+
+
+static inline void
+set_myanmar_properties (hb_glyph_info_t &info)
+{
+  hb_codepoint_t u = info.codepoint;
+  unsigned int type = hb_indic_get_categories (u);
+  indic_category_t cat = (indic_category_t) (type & 0x7Fu);
+  indic_position_t pos = (indic_position_t) (type >> 8);
+
+  /* Myanmar
+   * http://www.microsoft.com/typography/OpenTypeDev/myanmar/intro.htm#analyze
+   */
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)))
+    cat = (indic_category_t) OT_VS;
+
+  switch (u)
+  {
+    case 0x104Eu:
+      cat = (indic_category_t) 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;
+      break;
+
+    case 0x1004u: case 0x101Bu: case 0x105Au:
+      cat = (indic_category_t) OT_Ra;
+      break;
+
+    case 0x1032u: case 0x1036u:
+      cat = (indic_category_t) OT_A;
+      break;
+
+    case 0x1039u:
+      cat = (indic_category_t) OT_H;
+      break;
+
+    case 0x103Au:
+      cat = (indic_category_t) OT_As;
+      break;
+
+    case 0x1041u: case 0x1042u: case 0x1043u: case 0x1044u:
+    case 0x1045u: case 0x1046u: case 0x1047u: case 0x1048u:
+    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;
+      break;
+
+    case 0x1040u:
+      cat = (indic_category_t) 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;
+      break;
+
+    case 0x103Cu:
+      cat = (indic_category_t) OT_MR;
+      break;
+
+    case 0x103Du: case 0x1082u:
+      cat = (indic_category_t) OT_MW;
+      break;
+
+    case 0x103Bu: case 0x105Eu: case 0x105Fu:
+      cat = (indic_category_t) 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;
+      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;
+      break;
+
+    case 0x104Au: case 0x104Bu:
+      cat = (indic_category_t) OT_P;
+      break;
+
+    case 0xAA74u: case 0xAA75u: case 0xAA76u:
+      /* https://github.com/roozbehp/unicode-data/issues/3 */
+      cat = (indic_category_t) OT_C;
+      break;
+  }
+
+  if (cat == OT_M)
+  {
+    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;
+    }
+  }
+
+  info.myanmar_category() = (myanmar_category_t) cat;
+  info.myanmar_position() = pos;
+}
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_PRIVATE_HH */
diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc
index bb68622..3c57bc1 100644
--- a/src/hb-ot-shape-complex-myanmar.cc
+++ b/src/hb-ot-shape-complex-myanmar.cc
@@ -24,11 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-indic-private.hh"
-
-/* buffer var allocations */
-#define myanmar_category() complex_var_u8_0() /* myanmar_category_t */
-#define myanmar_position() complex_var_u8_1() /* myanmar_position_t */
+#include "hb-ot-shape-complex-myanmar-private.hh"
 
 
 /*
@@ -103,7 +99,7 @@
   for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
   {
     map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
-    map->add_gsub_pause (NULL);
+    map->add_gsub_pause (nullptr);
   }
   map->add_gsub_pause (final_reordering);
   for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
@@ -127,153 +123,6 @@
 #include "hb-ot-shape-complex-myanmar-machine.hh"
 
 
-/* Note: This enum is duplicated in the -machine.rl source file.
- * Not sure how to avoid duplication. */
-enum myanmar_category_t {
-  OT_As  = 18, /* Asat */
-  OT_D   = 19, /* Digits except zero */
-  OT_D0  = 20, /* Digit zero */
-  OT_DB  = OT_N, /* Dot below */
-  OT_GB  = OT_PLACEHOLDER,
-  OT_MH  = 21, /* Various consonant medial types */
-  OT_MR  = 22, /* Various consonant medial types */
-  OT_MW  = 23, /* Various consonant medial types */
-  OT_MY  = 24, /* Various consonant medial types */
-  OT_PT  = 25, /* Pwo and other tones */
-  OT_VAbv = 26,
-  OT_VBlw = 27,
-  OT_VPre = 28,
-  OT_VPst = 29,
-  OT_VS   = 30, /* Variation selectors */
-  OT_P    = 31  /* Punctuation */
-};
-
-
-static inline bool
-is_one_of (const hb_glyph_info_t &info, unsigned int flags)
-{
-  /* If it ligated, all bets are off. */
-  if (_hb_glyph_info_ligated (&info)) return false;
-  return !!(FLAG_SAFE (info.myanmar_category()) & flags);
-}
-
-static inline bool
-is_consonant (const hb_glyph_info_t &info)
-{
-  return is_one_of (info, CONSONANT_FLAGS);
-}
-
-
-static inline void
-set_myanmar_properties (hb_glyph_info_t &info)
-{
-  hb_codepoint_t u = info.codepoint;
-  unsigned int type = hb_indic_get_categories (u);
-  indic_category_t cat = (indic_category_t) (type & 0x7Fu);
-  indic_position_t pos = (indic_position_t) (type >> 8);
-
-  /* Myanmar
-   * http://www.microsoft.com/typography/OpenTypeDev/myanmar/intro.htm#analyze
-   */
-  if (unlikely (hb_in_range (u, 0xFE00u, 0xFE0Fu)))
-    cat = (indic_category_t) OT_VS;
-
-  switch (u)
-  {
-    case 0x104Eu:
-      cat = (indic_category_t) 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;
-      break;
-
-    case 0x1004u: case 0x101Bu: case 0x105Au:
-      cat = (indic_category_t) OT_Ra;
-      break;
-
-    case 0x1032u: case 0x1036u:
-      cat = (indic_category_t) OT_A;
-      break;
-
-    case 0x1039u:
-      cat = (indic_category_t) OT_H;
-      break;
-
-    case 0x103Au:
-      cat = (indic_category_t) OT_As;
-      break;
-
-    case 0x1041u: case 0x1042u: case 0x1043u: case 0x1044u:
-    case 0x1045u: case 0x1046u: case 0x1047u: case 0x1048u:
-    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;
-      break;
-
-    case 0x1040u:
-      cat = (indic_category_t) 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;
-      break;
-
-    case 0x103Cu:
-      cat = (indic_category_t) OT_MR;
-      break;
-
-    case 0x103Du: case 0x1082u:
-      cat = (indic_category_t) OT_MW;
-      break;
-
-    case 0x103Bu: case 0x105Eu: case 0x105Fu:
-      cat = (indic_category_t) 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;
-      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;
-      break;
-
-    case 0x104Au: case 0x104Bu:
-      cat = (indic_category_t) OT_P;
-      break;
-
-    case 0xAA74u: case 0xAA75u: case 0xAA76u:
-      /* https://github.com/roozbehp/unicode-data/issues/3 */
-      cat = (indic_category_t) OT_C;
-      break;
-  }
-
-  if (cat == OT_M)
-  {
-    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;
-    }
-  }
-
-  info.myanmar_category() = (myanmar_category_t) cat;
-  info.myanmar_position() = pos;
-}
-
-
-
 static void
 setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		   hb_buffer_t              *buffer,
@@ -297,6 +146,8 @@
 		 hb_buffer_t *buffer)
 {
   find_syllables (buffer);
+  foreach_syllable (buffer, start, end)
+    buffer->unsafe_to_break (start, end);
 }
 
 static int
@@ -372,6 +223,11 @@
       {
 	continue;
       }
+      if (info[i].myanmar_category() == OT_VS)
+      {
+	info[i].myanmar_position() = info[i - 1].myanmar_position();
+	continue;
+      }
 
       if (pos == POS_AFTER_MAIN && info[i].myanmar_category() == OT_VBlw)
       {
@@ -510,36 +366,36 @@
  * generic shaper, except that it zeros mark advances GDEF_LATE. */
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_old =
 {
-  "default",
-  NULL, /* collect_features */
-  NULL, /* override_features */
-  NULL, /* data_create */
-  NULL, /* data_destroy */
-  NULL, /* preprocess_text */
-  NULL, /* postprocess_glyphs */
+  nullptr, /* collect_features */
+  nullptr, /* override_features */
+  nullptr, /* data_create */
+  nullptr, /* data_destroy */
+  nullptr, /* preprocess_text */
+  nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
-  NULL, /* decompose */
-  NULL, /* compose */
-  NULL, /* setup_masks */
-  NULL, /* disable_otl */
+  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 */
 };
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
 {
-  "myanmar",
   collect_features_myanmar,
   override_features_myanmar,
-  NULL, /* data_create */
-  NULL, /* data_destroy */
-  NULL, /* preprocess_text */
-  NULL, /* postprocess_glyphs */
+  nullptr, /* data_create */
+  nullptr, /* data_destroy */
+  nullptr, /* preprocess_text */
+  nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
-  NULL, /* decompose */
-  NULL, /* compose */
+  nullptr, /* decompose */
+  nullptr, /* compose */
   setup_masks_myanmar,
-  NULL, /* disable_otl */
+  nullptr, /* disable_otl */
+  nullptr, /* reorder_marks */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
   false, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index 952441b..08b6fe9 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -39,6 +39,8 @@
 #define complex_var_u8_1()	var2.u8[3]
 
 
+#define HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS 32
+
 enum hb_ot_shape_zero_width_marks_type_t {
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
@@ -52,9 +54,10 @@
   HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \
   HB_COMPLEX_SHAPER_IMPLEMENT (hebrew) \
-  HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_old) \
   HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
+  HB_COMPLEX_SHAPER_IMPLEMENT (khmer) \
   HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
+  HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_old) \
   HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
   HB_COMPLEX_SHAPER_IMPLEMENT (tibetan) \
   HB_COMPLEX_SHAPER_IMPLEMENT (use) \
@@ -63,12 +66,10 @@
 
 struct hb_ot_complex_shaper_t
 {
-  char name[8];
-
   /* collect_features()
    * Called during shape_plan().
    * Shapers should use plan->map to add their features and callbacks.
-   * May be NULL.
+   * May be nullptr.
    */
   void (*collect_features) (hb_ot_shape_planner_t *plan);
 
@@ -76,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 NULL.
+   * May be nullptr.
    */
   void (*override_features) (hb_ot_shape_planner_t *plan);
 
@@ -84,15 +85,15 @@
   /* data_create()
    * Called at the end of shape_plan().
    * Whatever shapers return will be accessible through plan->data later.
-   * If NULL is returned, means a plan failure.
+   * If nullptr is returned, means a plan failure.
    */
   void *(*data_create) (const hb_ot_shape_plan_t *plan);
 
   /* data_destroy()
    * Called when the shape_plan is being destroyed.
    * plan->data is passed here for destruction.
-   * If NULL is returned, means a plan failure.
-   * May be NULL.
+   * If nullptr is returned, means a plan failure.
+   * May be nullptr.
    */
   void (*data_destroy) (void *data);
 
@@ -100,7 +101,7 @@
   /* preprocess_text()
    * Called during shape().
    * Shapers can use to modify text before shaping starts.
-   * May be NULL.
+   * May be nullptr.
    */
   void (*preprocess_text) (const hb_ot_shape_plan_t *plan,
 			   hb_buffer_t              *buffer,
@@ -109,7 +110,7 @@
   /* postprocess_glyphs()
    * Called during shape().
    * Shapers can use to modify glyphs after shaping ends.
-   * May be NULL.
+   * May be nullptr.
    */
   void (*postprocess_glyphs) (const hb_ot_shape_plan_t *plan,
 			      hb_buffer_t              *buffer,
@@ -120,7 +121,7 @@
 
   /* decompose()
    * Called during shape()'s normalization.
-   * May be NULL.
+   * May be nullptr.
    */
   bool (*decompose) (const hb_ot_shape_normalize_context_t *c,
 		     hb_codepoint_t  ab,
@@ -129,7 +130,7 @@
 
   /* compose()
    * Called during shape()'s normalization.
-   * May be NULL.
+   * May be nullptr.
    */
   bool (*compose) (const hb_ot_shape_normalize_context_t *c,
 		   hb_codepoint_t  a,
@@ -140,7 +141,7 @@
    * Called during shape().
    * Shapers should use map to get feature masks and set on buffer.
    * Shapers may NOT modify characters.
-   * May be NULL.
+   * May be nullptr.
    */
   void (*setup_masks) (const hb_ot_shape_plan_t *plan,
 		       hb_buffer_t              *buffer,
@@ -150,10 +151,20 @@
    * Called during shape().
    * If set and returns true, GDEF/GSUB/GPOS of the font are ignored
    * and fallback operations used.
-   * May be NULL.
+   * May be nullptr.
    */
   bool (*disable_otl) (const hb_ot_shape_plan_t *plan);
 
+  /* reorder_marks()
+   * Called during shape().
+   * Shapers can use to modify ordering of combining marks.
+   * May be nullptr.
+   */
+  void (*reorder_marks) (const hb_ot_shape_plan_t *plan,
+			 hb_buffer_t              *buffer,
+			 unsigned int              start,
+			 unsigned int              end);
+
   hb_ot_shape_zero_width_marks_type_t zero_width_marks;
 
   bool fallback_position;
@@ -253,10 +264,12 @@
     case HB_SCRIPT_SINHALA:
 
       /* If the designer designed the font for the 'DFLT' script,
-       * use the default shaper.  Otherwise, use the specific shaper.
+       * (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 (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T'))
+      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
 	return &_hb_ot_complex_shaper_indic;
@@ -272,8 +285,8 @@
 					      planner->map.script_index[0],
 					      planner->map.language_index[0],
 					      HB_TAG ('p','r','e','f'),
-					      NULL))
-	return &_hb_ot_complex_shaper_indic;
+					      nullptr))
+	return &_hb_ot_complex_shaper_khmer;
       else
 	return &_hb_ot_complex_shaper_default;
 
@@ -362,11 +375,18 @@
     case HB_SCRIPT_MARCHEN:
     case HB_SCRIPT_NEWA:
 
+    /* Unicode-10.0 additions */
+    case HB_SCRIPT_MASARAM_GONDI:
+    case HB_SCRIPT_SOYOMBO:
+    case HB_SCRIPT_ZANABAZAR_SQUARE:
+
       /* If the designer designed the font for the 'DFLT' script,
-       * use the default shaper.  Otherwise, use the specific shaper.
+       * (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 (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T'))
+      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
 	return &_hb_ot_complex_shaper_use;
diff --git a/src/hb-ot-shape-complex-thai.cc b/src/hb-ot-shape-complex-thai.cc
index e6f80f5..6ba925c 100644
--- a/src/hb-ot-shape-complex-thai.cc
+++ b/src/hb-ot-shape-complex-thai.cc
@@ -52,7 +52,7 @@
     return RC;
   if (u == 0x0E0Eu || u == 0x0E0Fu)
     return DC;
-  if (hb_in_range (u, 0x0E01u, 0x0E2Eu))
+  if (hb_in_range<hb_codepoint_t> (u, 0x0E01u, 0x0E2Eu))
     return NC;
   return NOT_CONSONANT;
 }
@@ -70,12 +70,12 @@
 static thai_mark_type_t
 get_mark_type (hb_codepoint_t u)
 {
-  if (u == 0x0E31u || hb_in_range (u, 0x0E34u, 0x0E37u) ||
-      u == 0x0E47u || hb_in_range (u, 0x0E4Du, 0x0E4Eu))
+  if (u == 0x0E31u || hb_in_range<hb_codepoint_t> (u, 0x0E34u, 0x0E37u) ||
+      u == 0x0E47u || hb_in_range<hb_codepoint_t> (u, 0x0E4Du, 0x0E4Eu))
     return AV;
-  if (hb_in_range (u, 0x0E38u, 0x0E3Au))
+  if (hb_in_range<hb_codepoint_t> (u, 0x0E38u, 0x0E3Au))
     return BV;
-  if (hb_in_range (u, 0x0E48u, 0x0E4Cu))
+  if (hb_in_range<hb_codepoint_t> (u, 0x0E48u, 0x0E4Cu))
     return T;
   return NOT_MARK;
 }
@@ -97,7 +97,7 @@
     hb_codepoint_t u;
     hb_codepoint_t win_pua;
     hb_codepoint_t mac_pua;
-  } const *pua_mappings = NULL;
+  } const *pua_mappings = nullptr;
   static const thai_pua_mapping_t SD_mappings[] = {
     {0x0E48u, 0xF70Au, 0xF88Bu}, /* MAI EK */
     {0x0E49u, 0xF70Bu, 0xF88Eu}, /* MAI THO */
@@ -244,6 +244,7 @@
     /* At least one of the above/below actions is NOP. */
     thai_action_t action = above_edge.action != NOP ? above_edge.action : below_edge.action;
 
+    buffer->unsafe_to_break (base, i);
     if (action == RD)
       info[base].codepoint = thai_pua_shape (info[base].codepoint, action, font);
     else
@@ -310,7 +311,7 @@
 #define IS_SARA_AM(x) (((x) & ~0x0080u) == 0x0E33u)
 #define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0x0E33u + 0x0E4Du)
 #define SARA_AA_FROM_SARA_AM(x) ((x) - 1)
-#define IS_TONE_MARK(x) (hb_in_ranges ((x) & ~0x0080u, 0x0E34u, 0x0E37u, 0x0E47u, 0x0E4Eu, 0x0E31u, 0x0E31u))
+#define IS_TONE_MARK(x) (hb_in_ranges<hb_codepoint_t> ((x) & ~0x0080u, 0x0E34u, 0x0E37u, 0x0E47u, 0x0E4Eu, 0x0E31u, 0x0E31u))
 
   buffer->clear_output ();
   unsigned int count = buffer->len;
@@ -365,18 +366,18 @@
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai =
 {
-  "thai",
-  NULL, /* collect_features */
-  NULL, /* override_features */
-  NULL, /* data_create */
-  NULL, /* data_destroy */
+  nullptr, /* collect_features */
+  nullptr, /* override_features */
+  nullptr, /* data_create */
+  nullptr, /* data_destroy */
   preprocess_text_thai,
-  NULL, /* postprocess_glyphs */
+  nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
-  NULL, /* decompose */
-  NULL, /* compose */
-  NULL, /* setup_masks */
-  NULL, /* disable_otl */
+  nullptr, /* decompose */
+  nullptr, /* compose */
+  nullptr, /* setup_masks */
+  nullptr, /* disable_otl */
+  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
index aadf59f..eaac0bf 100644
--- a/src/hb-ot-shape-complex-tibetan.cc
+++ b/src/hb-ot-shape-complex-tibetan.cc
@@ -46,18 +46,18 @@
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_tibetan =
 {
-  "default",
   collect_features_tibetan,
-  NULL, /* override_features */
-  NULL, /* data_create */
-  NULL, /* data_destroy */
-  NULL, /* preprocess_text */
-  NULL, /* postprocess_glyphs */
+  nullptr, /* override_features */
+  nullptr, /* data_create */
+  nullptr, /* data_destroy */
+  nullptr, /* preprocess_text */
+  nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
-  NULL, /* decompose */
-  NULL, /* compose */
-  NULL, /* setup_masks */
-  NULL, /* disable_otl */
+  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
new file mode 100644
index 0000000..0bf3ad3
--- /dev/null
+++ b/src/hb-ot-shape-complex-use-machine.hh
@@ -0,0 +1,502 @@
+
+#line 1 "hb-ot-shape-complex-use-machine.rl"
+/*
+ * Copyright © 2015  Mozilla Foundation.
+ * Copyright © 2015  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.
+ *
+ * Mozilla Author(s): Jonathan Kew
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+
+#include "hb-private.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
+};
+
+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
+};
+
+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
+};
+
+static const char _use_syllable_machine_indicies[] = {
+	1, 0, 3, 2, 2, 2, 2, 2, 
+	2, 2, 2, 2, 2, 2, 2, 2, 
+	4, 2, 3, 2, 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, 
+	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, 
+	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
+};
+
+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
+};
+
+static const char _use_syllable_machine_trans_actions[] = {
+	1, 0, 2, 3, 0, 4, 0, 0, 
+	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
+};
+
+static const char _use_syllable_machine_to_state_actions[] = {
+	0, 0, 0, 0, 5, 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
+};
+
+static const char _use_syllable_machine_from_state_actions[] = {
+	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, 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
+};
+
+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
+};
+
+static const int use_syllable_machine_start = 4;
+static const int use_syllable_machine_first_final = 4;
+static const int use_syllable_machine_error = -1;
+
+static const int use_syllable_machine_en_main = 4;
+
+
+#line 38 "hb-ot-shape-complex-use-machine.rl"
+
+
+
+#line 141 "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++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  
+#line 339 "hb-ot-shape-complex-use-machine.hh"
+	{
+	cs = use_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 162 "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"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+_resume:
+	switch ( _use_syllable_machine_from_state_actions[cs] ) {
+	case 6:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 370 "hb-ot-shape-complex-use-machine.hh"
+	}
+
+	_keys = _use_syllable_machine_trans_keys + (cs<<1);
+	_inds = _use_syllable_machine_indicies + _use_syllable_machine_index_offsets[cs];
+
+	_slen = _use_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].use_category()) &&
+		( info[p].use_category()) <= _keys[1] ?
+		( info[p].use_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+	cs = _use_syllable_machine_trans_targs[_trans];
+
+	if ( _use_syllable_machine_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _use_syllable_machine_trans_actions[_trans] ) {
+	case 7:
+#line 1 "NONE"
+	{te = p+1;}
+	break;
+	case 12:
+#line 130 "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"
+	{te = p+1;{ found_syllable (standard_cluster); }}
+	break;
+	case 9:
+#line 136 "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"
+	{te = p+1;{ found_syllable (non_cluster); }}
+	break;
+	case 11:
+#line 130 "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"
+	{te = p;p--;{ found_syllable (virama_terminated_cluster); }}
+	break;
+	case 13:
+#line 132 "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"
+	{te = p;p--;{ found_syllable (number_joiner_terminated_cluster); }}
+	break;
+	case 16:
+#line 134 "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"
+	{te = p;p--;{ found_syllable (symbol_cluster); }}
+	break;
+	case 18:
+#line 136 "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"
+	{te = p;p--;{ found_syllable (non_cluster); }}
+	break;
+	case 1:
+#line 132 "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"
+	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
+	break;
+	case 2:
+#line 1 "NONE"
+	{	switch( act ) {
+	case 7:
+	{{p = ((te))-1;} found_syllable (broken_cluster); }
+	break;
+	case 8:
+	{{p = ((te))-1;} found_syllable (non_cluster); }
+	break;
+	}
+	}
+	break;
+	case 3:
+#line 1 "NONE"
+	{te = p+1;}
+#line 136 "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"
+	{act = 8;}
+	break;
+#line 472 "hb-ot-shape-complex-use-machine.hh"
+	}
+
+_again:
+	switch ( _use_syllable_machine_to_state_actions[cs] ) {
+	case 5:
+#line 1 "NONE"
+	{ts = 0;}
+	break;
+#line 481 "hb-ot-shape-complex-use-machine.hh"
+	}
+
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _use_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _use_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	}
+
+#line 171 "hb-ot-shape-complex-use-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-use-machine.rl b/src/hb-ot-shape-complex-use-machine.rl
index f6b814b..11fb470 100644
--- a/src/hb-ot-shape-complex-use-machine.rl
+++ b/src/hb-ot-shape-complex-use-machine.rl
@@ -86,21 +86,24 @@
 VMPre	= 23; # VOWEL_MOD_PRE
 SMAbv	= 41; # SYM_MOD_ABOVE
 SMBlw	= 42; # SYM_MOD_BELOW
+CS	= 43; # CONS_WITH_STACKER
 
 
-consonant_modifiers = CMAbv* CMBlw* ((H B | SUB) VS? CMAbv? CMBlw*)*;
-medial_consonants = MPre? MAbv? MBlw? MPst?;
+# Override: Adjoc ZWJ placement. https://github.com/harfbuzz/harfbuzz/issues/542#issuecomment-353169729
+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*;
 final_consonants = FAbv* FBlw* FPst* FM?;
 
 virama_terminated_cluster =
-	R? (B | GB) VS?
+	(R|CS)? (B | GB) VS?
 	consonant_modifiers
-	H
+	ZWJ?.H.ZWJ?
 ;
 standard_cluster =
-	R? (B | GB) VS?
+	(R|CS)? (B | GB) VS?
 	consonant_modifiers
 	medial_consonants
 	dependent_vowels
diff --git a/src/hb-ot-shape-complex-use-private.hh b/src/hb-ot-shape-complex-use-private.hh
index ae428cb..f7ded13 100644
--- a/src/hb-ot-shape-complex-use-private.hh
+++ b/src/hb-ot-shape-complex-use-private.hh
@@ -87,10 +87,11 @@
   USE_VMPst	= 39,	/* VOWEL_MOD_POST */
   USE_VMPre	= 23,	/* VOWEL_MOD_PRE */
   USE_SMAbv	= 41,	/* SYM_MOD_ABOVE */
-  USE_SMBlw	= 42	/* SYM_MOD_BELOW */
+  USE_SMBlw	= 42,	/* SYM_MOD_BELOW */
+  USE_CS	= 43	/* CONS_WITH_STACKER */
 };
 
 HB_INTERNAL USE_TABLE_ELEMENT_TYPE
-hb_use_get_categories (hb_codepoint_t u);
+hb_use_get_category (hb_codepoint_t u);
 
 #endif /* HB_OT_SHAPE_COMPLEX_USE_PRIVATE_HH */
diff --git a/src/hb-ot-shape-complex-use-table.cc b/src/hb-ot-shape-complex-use-table.cc
index 38c46d0..6823392 100644
--- a/src/hb-ot-shape-complex-use-table.cc
+++ b/src/hb-ot-shape-complex-use-table.cc
@@ -6,12 +6,12 @@
  *
  * on files with these headers:
  *
- * # IndicSyllabicCategory-9.0.0.txt
- * # Date: 2016-05-21, 02:46:00 GMT [RP]
- * # IndicPositionalCategory-9.0.0.txt
- * # Date: 2016-02-25, 00:48:00 GMT [RP]
- * # Blocks-9.0.0.txt
- * # Date: 2016-02-05, 23:48:00 GMT [KW]
+ * # IndicSyllabicCategory-10.0.0.txt
+ * # Date: 2017-05-31, 01:07:00 GMT [KW, RP]
+ * # IndicPositionalCategory-10.0.0.txt
+ * # Date: 2017-05-31, 01:07:00 GMT [RP]
+ * # Blocks-10.0.0.txt
+ * # Date: 2017-04-12, 17:30:00 GMT [KW]
  * UnicodeData.txt does not have a header.
  */
 
@@ -19,6 +19,7 @@
 
 #define B	USE_B	/* BASE */
 #define CGJ	USE_CGJ	/* CGJ */
+#define CS	USE_CS	/* CONS_WITH_STACKER */
 #define FM	USE_FM	/* CONS_FINAL_MOD */
 #define GB	USE_GB	/* BASE_OTHER */
 #define H	USE_H	/* HALANT */
@@ -74,7 +75,13 @@
   /* 00C0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 00D0 */     O,     O,     O,     O,     O,     O,     O,    GB,
 
-#define use_offset_0x0900u 80
+#define use_offset_0x0348u 80
+
+
+  /* Combining Diacritical Marks */
+                                                                         O,     O,     O,     O,     O,     O,     O,   CGJ,
+
+#define use_offset_0x0900u 88
 
 
   /* Devanagari */
@@ -97,7 +104,7 @@
   /* 09C0 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     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,     O,     O,     O,     O,
+  /* 09F0 */     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     B,     O,     O,     O,
 
   /* Gurmukhi */
 
@@ -119,7 +126,7 @@
   /* 0AC0 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,  VAbv,     O,  VAbv,  VAbv,  VAbv,     O,  VPst,  VPst,     H,     O,     O,
   /* 0AD0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 0AE0 */     B,     B,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* 0AF0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     B,     O,     O,     O,     O,     O,     O,
+  /* 0AF0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     B, VMAbv, VMAbv, VMAbv, CMAbv, CMAbv, CMAbv,
 
   /* Oriya */
 
@@ -163,14 +170,14 @@
   /* 0CC0 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,  VAbv,  VAbv,  VAbv,     O,  VAbv,  VAbv,  VAbv,     H,     O,     O,
   /* 0CD0 */     O,     O,     O,     O,     O,  VPst,  VPst,     O,     O,     O,     O,     O,     O,     O,     B,     O,
   /* 0CE0 */     B,     B,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* 0CF0 */     O,     R,     R,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 0CF0 */     O,    CS,    CS,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
 
   /* Malayalam */
 
-  /* 0D00 */     O, VMAbv, VMPst, VMPst,     O,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,
+  /* 0D00 */ VMAbv, VMAbv, VMPst, VMPst,     O,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,
   /* 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,     O,     O,     B,  VPst,  VPst,
+  /* 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,
   /* 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,
@@ -187,7 +194,7 @@
   /* 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 1352
+#define use_offset_0x1000u 1360
 
 
   /* Myanmar */
@@ -203,7 +210,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 1512
+#define use_offset_0x1700u 1520
 
 
   /* Tagalog */
@@ -236,7 +243,7 @@
   /* 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 1752
+#define use_offset_0x1900u 1760
 
 
   /* Limbu */
@@ -274,13 +281,13 @@
   /* 1A20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 1A30 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 1A40 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* 1A50 */     B,     B,     B,     B,     B,  MPre,  MBlw,  FPst,  FAbv,  FAbv,  FAbv,  FBlw,  FBlw,  FBlw,  FBlw,     O,
+  /* 1A50 */     B,     B,     B,     B,     B,  MPre,  MBlw,   SUB,  FAbv,  FAbv,  FAbv,   SUB,   SUB,   SUB,   SUB,     O,
   /* 1A60 */     H,  VPst,  VAbv,  VPst,  VPst,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VAbv,  VBlw,  VPst,  VPre,  VPre,
-  /* 1A70 */  VPre,  VPre,  VPre,  VAbv,  VAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,    FM,    FM,    FM,     O,     O,    FM,
+  /* 1A70 */  VPre,  VPre,  VPre,  VAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,  VAbv,    FM,    FM,     O,     O,  FBlw,
   /* 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 2168
+#define use_offset_0x1b00u 2176
 
 
   /* Balinese */
@@ -306,7 +313,7 @@
   /* 1BC0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 1BD0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 1BE0 */     B,     B,     B,     B,     B,     B, CMAbv,  VPst,  VAbv,  VAbv,  VPst,  VPst,  VPst,  VAbv,  VPst,  VAbv,
-  /* 1BF0 */  FAbv,  FAbv,  VPst,  VPst,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 1BF0 */  FAbv,  FAbv, CMBlw, CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
 
   /* Lepcha */
 
@@ -316,29 +323,29 @@
   /* 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 2504
+#define use_offset_0x1cd0u 2512
 
 
   /* Vedic Extensions */
 
   /* 1CD0 */ VMAbv, VMAbv, VMAbv,     O, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMAbv, VMAbv, VMBlw, VMBlw, VMBlw, VMBlw,
   /* 1CE0 */ VMAbv, VMPst, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw,     O,     O,     O,     O, VMBlw,     O,     O,
-  /* 1CF0 */     O,     O, VMPst, VMPst, VMAbv,     O,     O,     O, VMAbv, VMAbv,     O,     O,     O,     O,     O,     O,
+  /* 1CF0 */     O,     O, VMPst, VMPst, VMAbv,     O,     O, VMPst, VMAbv, VMAbv,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x1df8u 2552
+#define use_offset_0x1df8u 2560
 
 
   /* Combining Diacritical Marks Supplement */
                                                                          O,     O,     O,    FM,     O,     O,     O,     O,
 
-#define use_offset_0x2008u 2560
+#define use_offset_0x2008u 2568
 
 
   /* General Punctuation */
                                                                          O,     O,     O,     O,  ZWNJ,   ZWJ,     O,     O,
   /* 2010 */    GB,    GB,    GB,    GB,    GB,     O,     O,     O,
 
-#define use_offset_0x2060u 2576
+#define use_offset_0x2060u 2584
 
   /* 2060 */    WJ,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
 
@@ -347,7 +354,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_0xa800u 2616
+#define use_offset_0x20f0u 2624
+
+
+  /* Combining Diacritical Marks for Symbols */
+
+  /* 20F0 */ VMAbv,     O,     O,     O,     O,     O,     O,     O,
+
+#define use_offset_0x25c8u 2632
+
+
+  /* Geometric Shapes */
+                                                                         O,     O,     O,     O,    GB,     O,     O,     O,
+
+#define use_offset_0xa800u 2640
 
 
   /* Syloti Nagri */
@@ -369,14 +389,14 @@
   /* A880 */ VMPst, VMPst,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* A890 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* A8A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* A8B0 */     B,     B,     B,     B,  FPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,
+  /* A8B0 */     B,     B,     B,     B,  MPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,  VPst,
   /* A8C0 */  VPst,  VPst,  VPst,  VPst,     H, VMAbv,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* A8D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
   /* Devanagari Extended */
 
   /* A8E0 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,
-  /* A8F0 */ VMAbv, VMAbv,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* A8F0 */ VMAbv, VMAbv,     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
 
   /* Kayah Li */
 
@@ -397,7 +417,7 @@
   /* A980 */ VMAbv, VMAbv,  FAbv, VMPst,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* A990 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* A9A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* A9B0 */     B,     B,     B, CMAbv,  VPst,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VPre,  VAbv,   SUB,  MPst,  MPst,
+  /* A9B0 */     B,     B,     B, CMAbv,  VPst,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VPre,  VAbv,   SUB,  MPst,  MBlw,
   /* A9C0 */     H,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* A9D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
@@ -410,7 +430,7 @@
 
   /* AA00 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* AA10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* AA20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VAbv,  VPre,
+  /* AA20 */     B,     B,     B,     B,     B,     B,     B,     B,     B, VMAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VAbv,  VPre,
   /* AA30 */  VPre,  VAbv,  VBlw,  MPst,  MPre,  MBlw,  MBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* AA40 */     B,     B,     B,  FAbv,     B,     B,     B,     B,     B,     B,     B,     B,  FAbv,  FPst,     O,     O,
   /* AA50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
@@ -434,7 +454,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 3376
+#define use_offset_0xabc0u 3400
 
 
   /* Meetei Mayek */
@@ -444,14 +464,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 3440
+#define use_offset_0xfe00u 3464
 
 
   /* Variation Selectors */
 
   /* FE00 */    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,    VS,
 
-#define use_offset_0x10a00u 3456
+#define use_offset_0x10a00u 3480
 
 
   /* Kharoshthi */
@@ -462,19 +482,19 @@
   /* 10A30 */     B,     B,     B,     B,     O,     O,     O,     O, CMAbv, CMBlw, CMBlw,     O,     O,     O,     O,     H,
   /* 10A40 */     B,     B,     B,     B,     B,     B,     B,     B,
 
-#define use_offset_0x11000u 3528
+#define use_offset_0x11000u 3552
 
 
   /* Brahmi */
 
-  /* 11000 */ VMPst, VMAbv, VMPst,     R,     R,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11000 */ VMPst, VMAbv, VMPst,    CS,    CS,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 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,
   /* 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,     O,
+  /* 11070 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,    HN,
 
   /* Kaithi */
 
@@ -483,7 +503,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 3720
+#define use_offset_0x11100u 3744
 
 
   /* Chakma */
@@ -521,7 +541,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 4040
+#define use_offset_0x11280u 4064
 
 
   /* Multani */
@@ -545,11 +565,11 @@
   /* 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,     O, CMBlw,     B,  VPst,  VPst,
   /* 11340 */  VAbv,  VPst,  VPst,  VPst,  VPst,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,     O,     O,
-  /* 11350 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     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 4288
+#define use_offset_0x11400u 4312
 
 
   /* Newa */
@@ -572,7 +592,7 @@
   /* 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 4512
+#define use_offset_0x11580u 4536
 
 
   /* Siddham */
@@ -615,7 +635,26 @@
   /* 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_0x11c00u 4960
+#define use_offset_0x11a00u 4984
+
+
+  /* Zanabazar Square */
+
+  /* 11A00 */     B,  VAbv,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,     B,     B,     B,     B,     B,
+  /* 11A10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11A20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11A30 */     B,     B,     B,    FM,  VBlw, VMAbv, VMAbv, VMAbv, VMAbv, VMPst,     R,  MBlw,  MBlw,  MBlw,  MBlw,    GB,
+  /* 11A40 */     O,     O,     O,     O,     O,    GB,     O,     H,     O,     O,     O,     O,     O,     O,     O,     O,
+
+  /* Soyombo */
+
+  /* 11A50 */     B,  VAbv,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VPst,  VPst,  VBlw,  VBlw,  VBlw,     B,     B,     B,     B,
+  /* 11A60 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11A70 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 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,     O,     O,     O,
+
+#define use_offset_0x11c00u 5144
 
 
   /* Bhaiksuki */
@@ -636,56 +675,70 @@
   /* 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,
 
-}; /* Table items: 5144; occupancy: 72% */
+#define use_offset_0x11d00u 5328
+
+
+  /* Masaram Gondi */
+
+  /* 11D00 */     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     O,     B,     B,     B,     B,     B,
+  /* 11D10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11D20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
+  /* 11D30 */     B,  VAbv,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,     O,     O,     O,  VAbv,     O,  VAbv,  VAbv,     O,  VAbv,
+  /* 11D40 */ VMAbv, VMAbv, CMBlw,  VAbv,  VBlw,     H,     R,  MBlw,     O,     O,     O,     O,     O,     O,     O,     O,
+  /* 11D50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
+
+}; /* Table items: 5424; occupancy: 73% */
 
 USE_TABLE_ELEMENT_TYPE
-hb_use_get_categories (hb_codepoint_t u)
+hb_use_get_category (hb_codepoint_t u)
 {
   switch (u >> 12)
   {
     case 0x0u:
-      if (hb_in_range (u, 0x0028u, 0x003Fu)) return use_table[u - 0x0028u + use_offset_0x0028u];
-      if (hb_in_range (u, 0x00A0u, 0x00D7u)) return use_table[u - 0x00A0u + use_offset_0x00a0u];
-      if (hb_in_range (u, 0x0900u, 0x0DF7u)) return use_table[u - 0x0900u + use_offset_0x0900u];
-      if (unlikely (u == 0x034Fu)) return CGJ;
+      if (hb_in_range<hb_codepoint_t> (u, 0x0028u, 0x003Fu)) return use_table[u - 0x0028u + use_offset_0x0028u];
+      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];
       break;
 
     case 0x1u:
-      if (hb_in_range (u, 0x1000u, 0x109Fu)) return use_table[u - 0x1000u + use_offset_0x1000u];
-      if (hb_in_range (u, 0x1700u, 0x17EFu)) return use_table[u - 0x1700u + use_offset_0x1700u];
-      if (hb_in_range (u, 0x1900u, 0x1A9Fu)) return use_table[u - 0x1900u + use_offset_0x1900u];
-      if (hb_in_range (u, 0x1B00u, 0x1C4Fu)) return use_table[u - 0x1B00u + use_offset_0x1b00u];
-      if (hb_in_range (u, 0x1CD0u, 0x1CFFu)) return use_table[u - 0x1CD0u + use_offset_0x1cd0u];
-      if (hb_in_range (u, 0x1DF8u, 0x1DFFu)) return use_table[u - 0x1DF8u + use_offset_0x1df8u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1000u, 0x109Fu)) return use_table[u - 0x1000u + use_offset_0x1000u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1700u, 0x17EFu)) return use_table[u - 0x1700u + use_offset_0x1700u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1900u, 0x1A9Fu)) return use_table[u - 0x1900u + use_offset_0x1900u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1B00u, 0x1C4Fu)) return use_table[u - 0x1B00u + use_offset_0x1b00u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1CD0u, 0x1CFFu)) return use_table[u - 0x1CD0u + use_offset_0x1cd0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1DF8u, 0x1DFFu)) return use_table[u - 0x1DF8u + use_offset_0x1df8u];
       break;
 
     case 0x2u:
-      if (hb_in_range (u, 0x2008u, 0x2017u)) return use_table[u - 0x2008u + use_offset_0x2008u];
-      if (hb_in_range (u, 0x2060u, 0x2087u)) return use_table[u - 0x2060u + use_offset_0x2060u];
-      if (unlikely (u == 0x25CCu)) return GB;
+      if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2017u)) return use_table[u - 0x2008u + use_offset_0x2008u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x2060u, 0x2087u)) return use_table[u - 0x2060u + use_offset_0x2060u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x20F0u, 0x20F7u)) return use_table[u - 0x20F0u + use_offset_0x20f0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x25C8u, 0x25CFu)) return use_table[u - 0x25C8u + use_offset_0x25c8u];
       break;
 
     case 0xAu:
-      if (hb_in_range (u, 0xA800u, 0xAAF7u)) return use_table[u - 0xA800u + use_offset_0xa800u];
-      if (hb_in_range (u, 0xABC0u, 0xABFFu)) return use_table[u - 0xABC0u + use_offset_0xabc0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xA800u, 0xAAF7u)) return use_table[u - 0xA800u + use_offset_0xa800u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xABC0u, 0xABFFu)) return use_table[u - 0xABC0u + use_offset_0xabc0u];
       break;
 
     case 0xFu:
-      if (hb_in_range (u, 0xFE00u, 0xFE0Fu)) return use_table[u - 0xFE00u + use_offset_0xfe00u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)) return use_table[u - 0xFE00u + use_offset_0xfe00u];
       break;
 
     case 0x10u:
-      if (hb_in_range (u, 0x10A00u, 0x10A47u)) return use_table[u - 0x10A00u + use_offset_0x10a00u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x10A00u, 0x10A47u)) return use_table[u - 0x10A00u + use_offset_0x10a00u];
       break;
 
     case 0x11u:
-      if (hb_in_range (u, 0x11000u, 0x110BFu)) return use_table[u - 0x11000u + use_offset_0x11000u];
-      if (hb_in_range (u, 0x11100u, 0x1123Fu)) return use_table[u - 0x11100u + use_offset_0x11100u];
-      if (hb_in_range (u, 0x11280u, 0x11377u)) return use_table[u - 0x11280u + use_offset_0x11280u];
-      if (hb_in_range (u, 0x11400u, 0x114DFu)) return use_table[u - 0x11400u + use_offset_0x11400u];
-      if (hb_in_range (u, 0x11580u, 0x1173Fu)) return use_table[u - 0x11580u + use_offset_0x11580u];
-      if (hb_in_range (u, 0x11C00u, 0x11CB7u)) return use_table[u - 0x11C00u + use_offset_0x11c00u];
-      if (unlikely (u == 0x1107Fu)) return HN;
+      if (hb_in_range<hb_codepoint_t> (u, 0x11000u, 0x110BFu)) return use_table[u - 0x11000u + use_offset_0x11000u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11100u, 0x1123Fu)) return use_table[u - 0x11100u + use_offset_0x11100u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11280u, 0x11377u)) return use_table[u - 0x11280u + use_offset_0x11280u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11400u, 0x114DFu)) return use_table[u - 0x11400u + use_offset_0x11400u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11580u, 0x1173Fu)) return use_table[u - 0x11580u + use_offset_0x11580u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11A00u, 0x11A9Fu)) return use_table[u - 0x11A00u + use_offset_0x11a00u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11C00u, 0x11CB7u)) return use_table[u - 0x11C00u + use_offset_0x11c00u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11D00u, 0x11D5Fu)) return use_table[u - 0x11D00u + use_offset_0x11d00u];
       break;
 
     default:
@@ -696,6 +749,7 @@
 
 #undef B
 #undef CGJ
+#undef CS
 #undef FM
 #undef GB
 #undef H
diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc
index af68706..ee7653b 100644
--- a/src/hb-ot-shape-complex-use.cc
+++ b/src/hb-ot-shape-complex-use.cc
@@ -144,7 +144,7 @@
   /* "Topographical features" */
   for (unsigned int i = 0; i < ARRAY_LENGTH (arabic_features); i++)
     map->add_feature (arabic_features[i], 1, F_NONE);
-  map->add_gsub_pause (NULL);
+  map->add_gsub_pause (nullptr);
 
   /* "Standard typographic presentation" and "Positional feature application" */
   for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
@@ -199,7 +199,7 @@
 {
   use_shape_plan_t *use_plan = (use_shape_plan_t *) calloc (1, sizeof (use_shape_plan_t));
   if (unlikely (!use_plan))
-    return NULL;
+    return nullptr;
 
   use_plan->rphf_mask = plan->map.get_1_mask (HB_TAG('r','p','h','f'));
 
@@ -209,7 +209,7 @@
     if (unlikely (!use_plan->arabic_plan))
     {
       free (use_plan);
-      return NULL;
+      return nullptr;
     }
   }
 
@@ -262,7 +262,7 @@
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
-    info[i].use_category() = hb_use_get_categories (info[i].codepoint);
+    info[i].use_category() = hb_use_get_category (info[i].codepoint);
 }
 
 static void
@@ -292,7 +292,7 @@
   if (use_plan->arabic_plan)
     return;
 
-  ASSERT_STATIC (INIT < 4 && ISOL < 4 && MEDI < 4 && FINA < 4);
+  static_assert ((INIT < 4 && ISOL < 4 && MEDI < 4 && FINA < 4), "");
   hb_mask_t masks[4], all_masks = 0;
   for (unsigned int i = 0; i < 4; i++)
   {
@@ -354,6 +354,8 @@
 		 hb_buffer_t *buffer)
 {
   find_syllables (buffer);
+  foreach_syllable (buffer, start, end)
+    buffer->unsafe_to_break (start, end);
   setup_rphf_mask (plan, buffer);
   setup_topographical_masks (plan, buffer);
 }
@@ -422,7 +424,7 @@
 {
   syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
   /* Only a few syllable types need reordering. */
-  if (unlikely (!(FLAG_SAFE (syllable_type) &
+  if (unlikely (!(FLAG_UNSAFE (syllable_type) &
 		  (FLAG (virama_terminated_cluster) |
 		   FLAG (standard_cluster) |
 		   FLAG (broken_cluster) |
@@ -503,7 +505,7 @@
   hb_glyph_info_t dottedcircle = {0};
   if (!font->get_nominal_glyph (0x25CCu, &dottedcircle.codepoint))
     return;
-  dottedcircle.use_category() = hb_use_get_categories (0x25CC);
+  dottedcircle.use_category() = hb_use_get_category (0x25CC);
 
   buffer->clear_output ();
 
@@ -593,18 +595,18 @@
 
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use =
 {
-  "use",
   collect_features_use,
-  NULL, /* override_features */
+  nullptr, /* override_features */
   data_create_use,
   data_destroy_use,
-  NULL, /* preprocess_text */
-  NULL, /* postprocess_glyphs */
+  nullptr, /* preprocess_text */
+  nullptr, /* postprocess_glyphs */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   decompose_use,
   compose_use,
   setup_masks_use,
-  NULL, /* disable_otl */
+  nullptr, /* disable_otl */
+  nullptr, /* reorder_marks */
   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
   false, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-fallback.cc b/src/hb-ot-shape-fallback.cc
index ea8312b..c7b4605 100644
--- a/src/hb-ot-shape-fallback.cc
+++ b/src/hb-ot-shape-fallback.cc
@@ -200,8 +200,7 @@
 	       unsigned int combining_class)
 {
   hb_glyph_extents_t mark_extents;
-  if (!font->get_glyph_extents (buffer->info[i].codepoint,
-				&mark_extents))
+  if (!font->get_glyph_extents (buffer->info[i].codepoint, &mark_extents))
     return;
 
   hb_position_t y_gap = font->y_scale / 16;
@@ -210,7 +209,7 @@
   pos.x_offset = pos.y_offset = 0;
 
 
-  /* We dont position LEFT and RIGHT marks. */
+  /* We don't position LEFT and RIGHT marks. */
 
   /* X positioning */
   switch (combining_class)
@@ -218,10 +217,10 @@
     case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW:
     case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE:
       if (buffer->props.direction == HB_DIRECTION_LTR) {
-	pos.x_offset += base_extents.x_bearing - mark_extents.width / 2 - mark_extents.x_bearing;
+	pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width / 2 - mark_extents.x_bearing;
         break;
       } else if (buffer->props.direction == HB_DIRECTION_RTL) {
-	pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width / 2 - mark_extents.x_bearing;
+	pos.x_offset += base_extents.x_bearing - mark_extents.width / 2 - mark_extents.x_bearing;
         break;
       }
       HB_FALLTHROUGH;
@@ -307,6 +306,9 @@
 		      unsigned int end)
 {
   hb_direction_t horiz_dir = HB_DIRECTION_INVALID;
+
+  buffer->unsafe_to_break (base, end);
+
   hb_glyph_extents_t base_extents;
   if (!font->get_glyph_extents (buffer->info[base].codepoint,
 				&base_extents))
@@ -319,7 +321,9 @@
   base_extents.y_bearing += buffer->pos[base].y_offset;
 
   unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[base]);
-  unsigned int num_lig_components = _hb_glyph_info_get_lig_num_comps (&buffer->info[base]);
+  /* Use integer for num_lig_components such that it doesn't convert to unsigned
+   * when we divide or multiply by it. */
+  int num_lig_components = _hb_glyph_info_get_lig_num_comps (&buffer->info[base]);
 
   hb_position_t x_offset = 0, y_offset = 0;
   if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
@@ -328,7 +332,7 @@
   }
 
   hb_glyph_extents_t component_extents = base_extents;
-  unsigned int last_lig_component = (unsigned int) -1;
+  int last_lig_component = -1;
   unsigned int last_combining_class = 255;
   hb_glyph_extents_t cluster_extents = base_extents; /* Initialization is just to shut gcc up. */
   hb_glyph_info_t *info = buffer->info;
@@ -337,7 +341,7 @@
     {
       if (num_lig_components > 1) {
 	unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&info[i]);
-	unsigned int this_lig_component = _hb_glyph_info_get_lig_comp (&info[i]) - 1;
+	int this_lig_component = _hb_glyph_info_get_lig_comp (&info[i]) - 1;
 	/* Conditions for attaching to the last component. */
 	if (!lig_id || lig_id != this_lig_id || this_lig_component >= num_lig_components)
 	  this_lig_component = num_lig_components - 1;
@@ -439,10 +443,10 @@
 {
   if (!plan->has_kern) return;
 
-  OT::hb_apply_context_t c (1, font, buffer);
+  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_apply_context_t::skipping_iterator_t &skippy_iter = c.iter_input;
+  OT::hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c.iter_input;
   skippy_iter.init (&c);
 
   unsigned int count = buffer->len;
@@ -470,6 +474,7 @@
       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)
@@ -479,6 +484,7 @@
       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;
@@ -521,7 +527,7 @@
 	  break;
 
 	case t::SPACE_4_EM_18:
-	  pos[i].x_advance = font->x_scale * 4 / 18;
+	  pos[i].x_advance = (int64_t) font->x_scale * 4 / 18;
 	  break;
 
 	case t::SPACE_FIGURE:
diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index 94a3d7d..62cbb9d 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -346,13 +346,16 @@
         break;
 
     /* We are going to do a O(n^2).  Only do this if the sequence is short. */
-    if (end - i > 10) {
+    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;
   }
 
@@ -377,39 +380,54 @@
 	 * 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 there's anything between the starter and this char, they should have CCC
-	 * smaller than this character's. */
-	(starter == buffer->out_len - 1 ||
-	 _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&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))
+	HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur())))
     {
-      /* Composes. */
-      buffer->next_glyph (); /* Copy to out-buffer. */
-      if (unlikely (buffer->in_error))
-        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 (/* 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->in_error))
+	  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;
+	continue;
+      }
     }
 
     /* Blocked, or doesn't compose. */
     buffer->next_glyph ();
 
-    if (_hb_glyph_info_get_modified_combining_class (&buffer->prev()) == 0)
+    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.
+     * If it did NOT, then make it skippable.
+     * https://github.com/harfbuzz/harfbuzz/issues/554
+     */
+    for (unsigned int i = 1; i + 1 < buffer->len; i++)
+      if (buffer->info[i].codepoint == 0x034Fu/*CGJ*/ &&
+	  info_cc(buffer->info[i-1]) <= info_cc(buffer->info[i+1]))
+      {
+	_hb_glyph_info_unhide (&buffer->info[i]);
+      }
+  }
 }
diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape-private.hh
index 594e54c..fe5d2b7 100644
--- a/src/hb-ot-shape-private.hh
+++ b/src/hb-ot-shape-private.hh
@@ -73,7 +73,7 @@
   hb_ot_shape_planner_t (const hb_shape_plan_t *master_plan) :
 			 face (master_plan->face_unsafe),
 			 props (master_plan->props),
-			 shaper (NULL),
+			 shaper (nullptr),
 			 map (face, &props) {}
   ~hb_ot_shape_planner_t (void) { map.finish (); }
 
@@ -99,7 +99,9 @@
   }
 
   private:
-  NO_COPY (hb_ot_shape_planner_t);
+  /* No copy. */
+  hb_ot_shape_planner_t (const hb_ot_shape_planner_t &);
+  hb_ot_shape_planner_t &operator = (const hb_ot_shape_planner_t &);
 };
 
 
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 2eacb34..d9ba0f6 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -40,6 +40,8 @@
 #include "hb-unicode-private.hh"
 #include "hb-set-private.hh"
 
+#include "hb-ot-layout-gsubgpos-private.hh"
+#include "hb-aat-layout-private.hh"
 
 static hb_tag_t common_features[] = {
   HB_TAG('c','c','m','p'),
@@ -70,7 +72,7 @@
   hb_ot_map_builder_t *map = &planner->map;
 
   map->add_global_bool_feature (HB_TAG('r','v','r','n'));
-  map->add_gsub_pause (NULL);
+  map->add_gsub_pause (nullptr);
 
   switch (props->direction) {
     case HB_DIRECTION_LTR:
@@ -108,7 +110,7 @@
     /* 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/behdad/harfbuzz/issues/63 */
+     * https://github.com/harfbuzz/harfbuzz/issues/63 */
     map->add_feature (HB_TAG ('v','e','r','t'), 1, F_GLOBAL | F_GLOBAL_SEARCH);
   }
 
@@ -176,7 +178,7 @@
 {
   hb_ot_shape_plan_t *plan = (hb_ot_shape_plan_t *) calloc (1, sizeof (hb_ot_shape_plan_t));
   if (unlikely (!plan))
-    return NULL;
+    return nullptr;
 
   hb_ot_shape_planner_t planner (shape_plan);
 
@@ -190,7 +192,7 @@
   if (plan->shaper->data_create) {
     plan->data = plan->shaper->data_create (plan);
     if (unlikely (!plan->data))
-      return NULL;
+      return nullptr;
   }
 
   return plan;
@@ -275,8 +277,7 @@
 static void
 hb_form_clusters (hb_buffer_t *buffer)
 {
-  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) ||
-      buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII))
     return;
 
   /* Loop duplicated in hb_ensure_native_direction(), and in _hb-coretext.cc */
@@ -288,11 +289,17 @@
     if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])) &&
 		!_hb_glyph_info_is_joiner (&info[i])))
     {
-      buffer->merge_clusters (base, 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;
     }
   }
-  buffer->merge_clusters (base, count);
+  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+    buffer->merge_clusters (base, count);
+  else
+    buffer->unsafe_to_break (base, count);
 }
 
 static void
@@ -378,7 +385,6 @@
     post_mask = c->plan->numr_mask | c->plan->frac_mask;
   }
 
-  /* TODO look in pre/post context text also. */
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
@@ -395,6 +401,8 @@
 	     HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
         end++;
 
+      buffer->unsafe_to_break (start, end);
+
       for (unsigned int j = start; j < i; j++)
         info[j].mask |= pre_mask;
       info[i].mask |= c->plan->frac_mask;
@@ -444,7 +452,8 @@
   hb_buffer_t *buffer = c->buffer;
 
   if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) ||
-      (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES))
+      (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) ||
+      (buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES))
     return;
 
   unsigned int count = buffer->len;
@@ -480,7 +489,8 @@
     return;
 
   hb_codepoint_t space;
-  if (c->font->get_nominal_glyph (' ', &space))
+  if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) &&
+      c->font->get_nominal_glyph (' ', &space))
   {
     /* Replace default-ignorables with a zero-advance space glyph. */
     for (/*continue*/; i < count; i++)
@@ -510,9 +520,10 @@
 	  /* Merge cluster backward. */
 	  if (cluster < info[j - 1].cluster)
 	  {
+	    unsigned int mask = info[i].mask;
 	    unsigned int old_cluster = info[j - 1].cluster;
 	    for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--)
-	      info[k - 1].cluster = cluster;
+	      buffer->set_cluster (info[k - 1], cluster, mask);
 	  }
 	  continue;
 	}
@@ -578,8 +589,6 @@
 {
   hb_buffer_t *buffer = c->buffer;
 
-  hb_ot_shape_initialize_masks (c);
-
   hb_ot_mirror_chars (c);
 
   HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index);
@@ -609,7 +618,8 @@
 
   c->plan->substitute (c->font, buffer);
 
-  return;
+  /* XXX Call morx instead. */
+  //hb_aat_layout_substitute (c->font, c->buffer);
 }
 
 static inline void
@@ -688,9 +698,9 @@
 static inline void
 hb_ot_position_complex (hb_ot_shape_context_t *c)
 {
-  hb_ot_layout_position_start (c->font, c->buffer);
-
   unsigned int count = c->buffer->len;
+  hb_glyph_info_t *info = c->buffer->info;
+  hb_glyph_position_t *pos = c->buffer->pos;
 
   /* If the font has no GPOS, AND, no fallback positioning will
    * happen, AND, direction is forward, then when zeroing mark
@@ -705,6 +715,17 @@
 				     !c->plan->shaper->fallback_position &&
 				     HB_DIRECTION_IS_FORWARD (c->buffer->props.direction);
 
+  /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */
+
+  /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
+  if (c->font->has_glyph_h_origin_func ())
+    for (unsigned int i = 0; i < count; i++)
+      c->font->add_glyph_h_origin (info[i].codepoint,
+				   &pos[i].x_offset,
+				   &pos[i].y_offset);
+
+  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:
@@ -718,30 +739,8 @@
   }
 
   if (likely (!c->fallback_positioning))
-  {
-    hb_glyph_info_t *info = c->buffer->info;
-    hb_glyph_position_t *pos = c->buffer->pos;
-
-    /* Change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */
-
-    /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
-    if (c->font->has_glyph_h_origin_func ())
-      for (unsigned int i = 0; i < count; i++)
-	c->font->add_glyph_h_origin (info[i].codepoint,
-				     &pos[i].x_offset,
-				     &pos[i].y_offset);
-
     c->plan->position (c->font, c->buffer);
 
-    /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
-    if (c->font->has_glyph_h_origin_func ())
-      for (unsigned int i = 0; i < count; i++)
-	c->font->subtract_glyph_h_origin (info[i].codepoint,
-					  &pos[i].x_offset,
-					  &pos[i].y_offset);
-
-  }
-
   switch (c->plan->shaper->zero_width_marks)
   {
     case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
@@ -758,6 +757,13 @@
   hb_ot_layout_position_finish_advances (c->font, c->buffer);
   hb_ot_zero_width_default_ignorables (c);
   hb_ot_layout_position_finish_offsets (c->font, c->buffer);
+
+  /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
+  if (c->font->has_glyph_h_origin_func ())
+    for (unsigned int i = 0; i < count; i++)
+      c->font->subtract_glyph_h_origin (info[i].codepoint,
+					&pos[i].x_offset,
+					&pos[i].y_offset);
 }
 
 static inline void
@@ -783,6 +789,31 @@
   _hb_buffer_deallocate_gsubgpos_vars (c->buffer);
 }
 
+static inline void
+hb_propagate_flags (hb_buffer_t *buffer)
+{
+  /* Propagate cluster-level glyph flags to be the same on all cluster glyphs.
+   * Simplifies using them. */
+
+  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK))
+    return;
+
+  hb_glyph_info_t *info = buffer->info;
+
+  foreach_cluster (buffer, start, end)
+  {
+    unsigned int mask = 0;
+    for (unsigned int i = start; i < end; i++)
+      if (info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
+      {
+	 mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+	 break;
+      }
+    if (mask)
+      for (unsigned int i = start; i < end; i++)
+	info[i].mask |= mask;
+  }
+}
 
 /* Pull it all together! */
 
@@ -791,11 +822,16 @@
 {
   c->buffer->deallocate_var_all ();
   c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
-  if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_EXPANSION_FACTOR)))
+  if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR)))
   {
-    c->buffer->max_len = MAX (c->buffer->len * HB_BUFFER_MAX_EXPANSION_FACTOR,
+    c->buffer->max_len = MAX (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR,
 			      (unsigned) HB_BUFFER_MAX_LEN_MIN);
   }
+  if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR)))
+  {
+    c->buffer->max_ops = MAX (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR,
+			      (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);
@@ -809,8 +845,10 @@
 
   c->buffer->clear_output ();
 
+  hb_ot_shape_initialize_masks (c);
   hb_set_unicode_props (c->buffer);
   hb_insert_dotted_circle (c->buffer, c->font);
+
   hb_form_clusters (c->buffer);
 
   hb_ensure_native_direction (c->buffer);
@@ -826,11 +864,14 @@
   if (c->plan->shaper->postprocess_glyphs)
     c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
 
+  hb_propagate_flags (c->buffer);
+
   _hb_buffer_deallocate_unicode_vars (c->buffer);
 
   c->buffer->props.direction = c->target_direction;
 
   c->buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT;
+  c->buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT;
   c->buffer->deallocate_var_all ();
 }
 
@@ -898,7 +939,7 @@
 {
   hb_ot_shape_plan_t plan;
 
-  const char *shapers[] = {"ot", NULL};
+  const char *shapers[] = {"ot", nullptr};
   hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
 							     features, num_features, shapers);
 
@@ -909,18 +950,19 @@
   for (unsigned int i = 0; i < count; i++)
     add_char (font, buffer->unicode, mirror, info[i].codepoint, glyphs);
 
-  hb_set_t lookups;
-  lookups.init ();
-  hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, &lookups);
+  hb_set_t *lookups = hb_set_create ();
+  hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, lookups);
 
   /* And find transitive closure. */
-  hb_set_t copy;
-  copy.init ();
+  hb_set_t *copy = hb_set_create ();
   do {
-    copy.set (glyphs);
-    for (hb_codepoint_t lookup_index = -1; hb_set_next (&lookups, &lookup_index);)
+    copy->set (glyphs);
+    for (hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID; hb_set_next (lookups, &lookup_index);)
       hb_ot_layout_lookup_substitute_closure (font->face, lookup_index, glyphs);
-  } while (!copy.is_equal (glyphs));
+  } while (!copy->is_equal (glyphs));
+  hb_set_destroy (copy);
+
+  hb_set_destroy (lookups);
 
   hb_shape_plan_destroy (shape_plan);
 }
diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc
index 9b0db50..1338c31 100644
--- a/src/hb-ot-tag.cc
+++ b/src/hb-ot-tag.cc
@@ -270,28 +270,36 @@
   {"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 */
@@ -302,15 +310,21 @@
   {"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 */
@@ -380,7 +394,6 @@
   {"gkp",	HB_TAG('G','K','P',' ')},	/* Kpelle (Guinea) */
   {"gl",	HB_TAG('G','A','L',' ')},	/* Galician */
   {"gld",	HB_TAG('N','A','N',' ')},	/* Nanai */
-  {"gle",	HB_TAG('I','R','T',' ')},	/* Irish Traditional */
   {"glk",	HB_TAG('G','L','K',' ')},	/* Gilaki */
   {"gn",	HB_TAG('G','U','A',' ')},	/* Guarani [macrolanguage] */
   {"gnn",	HB_TAG('G','N','N',' ')},	/* Gumatj */
@@ -538,7 +551,6 @@
   {"mag",	HB_TAG('M','A','G',' ')},	/* Magahi */
   {"mai",	HB_TAG('M','T','H',' ')},	/* Maithili */
   {"mak",	HB_TAG('M','K','R',' ')},	/* Makasar */
-  {"mal",	HB_TAG('M','A','L',' ')},	/* Malayalam */
   {"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) */
@@ -867,9 +879,11 @@
 };
 
 static int
-lang_compare_first_component (const char *a,
-			      const char *b)
+lang_compare_first_component (const void *pa,
+			      const void *pb)
 {
+  const char *a = (const char *) pa;
+  const char *b = (const char *) pb;
   unsigned int da, db;
   const char *p;
 
@@ -906,12 +920,12 @@
     char tag[4];
     int i;
     s += 6;
-    for (i = 0; i < 4 && ISALPHA (s[i]); i++)
+    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_CHAR4 (tag);
+      return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
     }
   }
 
@@ -960,7 +974,7 @@
     const LangTag *lang_tag;
     lang_tag = (LangTag *) bsearch (lang_str, ot_languages,
 				    ARRAY_LENGTH (ot_languages), sizeof (LangTag),
-				    (hb_compare_func_t) lang_compare_first_component);
+				    lang_compare_first_component);
     if (lang_tag)
       return lang_tag->tag;
   }
@@ -1008,7 +1022,7 @@
   unsigned int i;
 
   if (tag == HB_OT_TAG_DEFAULT_LANGUAGE)
-    return NULL;
+    return nullptr;
 
   /* struct LangTag has only room for 3-letter language tags. */
   switch (tag) {
diff --git a/src/hb-ot-var-avar-table.hh b/src/hb-ot-var-avar-table.hh
index ace0f5f..e305a67 100644
--- a/src/hb-ot-var-avar-table.hh
+++ b/src/hb-ot-var-avar-table.hh
@@ -57,8 +57,13 @@
      * that at least -1, 0, and +1 must be mapped. But we include these as
      * part of a better error recovery scheme. */
 
-    if (!len)
-      return value;
+    if (len < 2)
+    {
+      if (!len)
+	return value;
+      else /* len == 1*/
+	return value - array[0].fromCoord + array[0].toCoord;
+    }
 
     if (value <= array[0].fromCoord)
       return value - array[0].fromCoord + array[0].toCoord;
@@ -76,8 +81,8 @@
 
     int denom = array[i].fromCoord - array[i-1].fromCoord;
     return array[i-1].toCoord +
-	   (array[i].toCoord - array[i-1].toCoord) *
-	   (value - array[i-1].fromCoord + denom/2) / denom;
+	   ((array[i].toCoord - array[i-1].toCoord) *
+	    (value - array[i-1].fromCoord) + denom/2) / denom;
   }
 
   DEFINE_SIZE_ARRAY (2, array);
@@ -128,8 +133,8 @@
   protected:
   FixedVersion<>version;	/* Version of the avar table
 				 * initially set to 0x00010000u */
-  USHORT	reserved;	/* This field is permanently reserved. Set to 0. */
-  USHORT	axisCount;	/* The number of variation axes in the font. This
+  HBUINT16	reserved;	/* This field is permanently reserved. Set to 0. */
+  HBUINT16	axisCount;	/* The number of variation axes in the font. This
 				 * must be the same number as axisCount in the
 				 * 'fvar' table. */
   SegmentMaps	axisSegmentMapsZ;
diff --git a/src/hb-ot-var-fvar-table.hh b/src/hb-ot-var-fvar-table.hh
index 9f6fb32..999b723 100644
--- a/src/hb-ot-var-fvar-table.hh
+++ b/src/hb-ot-var-fvar-table.hh
@@ -42,11 +42,11 @@
   }
 
   protected:
-  USHORT	subfamilyNameID;/* The name ID for entries in the 'name' table
+  HBUINT16	subfamilyNameID;/* The name ID for entries in the 'name' table
 				 * that provide subfamily names for this instance. */
-  USHORT	reserved;	/* Reserved for future use — set to 0. */
+  HBUINT16	reserved;	/* Reserved for future use — set to 0. */
   Fixed		coordinates[VAR];/* The coordinates array for this instance. */
-  //USHORT	postScriptNameIDX;/*Optional. The name ID for entries in the 'name'
+  //HBUINT16	postScriptNameIDX;/*Optional. The name ID for entries in the 'name'
   //				  * table that provide PostScript names for this
   //				  * instance. */
 
@@ -67,8 +67,8 @@
   Fixed		minValue;	/* The minimum coordinate value for the axis. */
   Fixed		defaultValue;	/* The default coordinate value for the axis. */
   Fixed		maxValue;	/* The maximum coordinate value for the axis. */
-  USHORT	reserved;	/* Reserved for future use — set to 0. */
-  USHORT	axisNameID;	/* The name ID for entries in the 'name' table that
+  HBUINT16	reserved;	/* Reserved for future use — set to 0. */
+  HBUINT16	axisNameID;	/* The name ID for entries in the 'name' table that
 				 * provide a display name for this axis. */
 
   public:
@@ -186,16 +186,16 @@
   protected:
   FixedVersion<>version;	/* Version of the fvar table
 				 * initially set to 0x00010000u */
-  Offset<>	things;		/* Offset in bytes from the beginning of the table
+  Offset16	things;		/* Offset in bytes from the beginning of the table
 				 * to the start of the AxisRecord array. */
-  USHORT	reserved;	/* This field is permanently reserved. Set to 2. */
-  USHORT	axisCount;	/* The number of variation axes in the font (the
+  HBUINT16	reserved;	/* This field is permanently reserved. Set to 2. */
+  HBUINT16	axisCount;	/* The number of variation axes in the font (the
 				 * number of records in the axes array). */
-  USHORT	axisSize;	/* The size in bytes of each VariationAxisRecord —
+  HBUINT16	axisSize;	/* The size in bytes of each VariationAxisRecord —
 				 * set to 20 (0x0014) for this version. */
-  USHORT	instanceCount;	/* The number of named instances defined in the font
+  HBUINT16	instanceCount;	/* The number of named instances defined in the font
 				 * (the number of records in the instances array). */
-  USHORT	instanceSize;	/* The size in bytes of each InstanceRecord — set
+  HBUINT16	instanceSize;	/* The size in bytes of each InstanceRecord — set
 				 * to either axisCount * sizeof(Fixed) + 4, or to
 				 * axisCount * sizeof(Fixed) + 6. */
 
diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh
index f9d801e..e20131b 100644
--- a/src/hb-ot-var-hvar-table.hh
+++ b/src/hb-ot-var-hvar-table.hh
@@ -55,7 +55,7 @@
     unsigned int u = 0;
     { /* Fetch it. */
       unsigned int w = get_width ();
-      const BYTE *p = mapData + w * v;
+      const HBUINT8 *p = mapData + w * v;
       for (; w; w--)
 	u = (u << 8) + *p++;
     }
@@ -78,10 +78,10 @@
   { return (format & 0xF) + 1; }
 
   protected:
-  USHORT	format;		/* A packed field that describes the compressed
+  HBUINT16	format;		/* A packed field that describes the compressed
 				 * representation of delta-set indices. */
-  USHORT	mapCount;	/* The number of mapping entries. */
-  BYTE		mapData[VAR];	/* The delta-set index mapping data. */
+  HBUINT16	mapCount;	/* The number of mapping entries. */
+  HBUINT8		mapData[VAR];	/* The delta-set index mapping data. */
 
   public:
   DEFINE_SIZE_ARRAY (4, mapData);
diff --git a/src/hb-ot-var-mvar-table.hh b/src/hb-ot-var-mvar-table.hh
index 3cb7498..e835768 100644
--- a/src/hb-ot-var-mvar-table.hh
+++ b/src/hb-ot-var-mvar-table.hh
@@ -43,7 +43,7 @@
 
   public:
   Tag		valueTag;	/* Four-byte tag identifying a font-wide measure. */
-  ULONG		varIdx;		/* Outer/inner index into VariationStore item. */
+  HBUINT32		varIdx;		/* Outer/inner index into VariationStore item. */
 
   public:
   DEFINE_SIZE_STATIC (8);
@@ -77,7 +77,7 @@
     const VariationValueRecord *record;
     record = (VariationValueRecord *) bsearch (&tag, values,
 					       valueRecordCount, valueRecordSize,
-					       (hb_compare_func_t) tag_compare);
+					       tag_compare);
     if (!record)
       return 0.;
 
@@ -85,19 +85,23 @@
   }
 
 protected:
-  static inline int tag_compare (const hb_tag_t *a, const Tag *b)
-  { return b->cmp (*a); }
+  static inline int tag_compare (const void *pa, const void *pb)
+  {
+    const hb_tag_t *a = (const hb_tag_t *) pa;
+    const Tag *b = (const Tag *) pb;
+    return b->cmp (*a);
+  }
 
   protected:
   FixedVersion<>version;	/* Version of the metrics variation table
 				 * initially set to 0x00010000u */
-  USHORT	reserved;	/* Not used; set to 0. */
-  USHORT	valueRecordSize;/* The size in bytes of each value record —
+  HBUINT16	reserved;	/* Not used; set to 0. */
+  HBUINT16	valueRecordSize;/* The size in bytes of each value record —
 				 * must be greater than zero. */
-  USHORT	valueRecordCount;/* The number of value records — may be zero. */
+  HBUINT16	valueRecordCount;/* The number of value records — may be zero. */
   OffsetTo<VariationStore>
 		varStore;	/* Offset to item variation store table. */
-  BYTE		values[VAR];	/* Array of value records. The records must be
+  HBUINT8		values[VAR];	/* Array of value records. The records must be
 				 * in binary order of their valueTag field. */
 
   public:
diff --git a/src/hb-ot-var.cc b/src/hb-ot-var.cc
index 691196d..90ba0bd 100644
--- a/src/hb-ot-var.cc
+++ b/src/hb-ot-var.cc
@@ -130,7 +130,7 @@
   for (unsigned int i = 0; i < variations_length; i++)
   {
     unsigned int axis_index;
-    if (hb_ot_var_find_axis (face, variations[i].tag, &axis_index, NULL) &&
+    if (hb_ot_var_find_axis (face, variations[i].tag, &axis_index, nullptr) &&
 	axis_index < coords_length)
       coords[axis_index] = fvar.normalize_axis_value (axis_index, variations[i].value);
   }
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 666af62..daa496e 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -29,6 +29,8 @@
 #ifndef HB_PRIVATE_HH
 #define HB_PRIVATE_HH
 
+#define _GNU_SOURCE 1
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -40,19 +42,21 @@
 #define HB_OT_H_IN
 #endif
 
+#include <math.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
 #include <assert.h>
-
-/* We only use these two for debug output.  However, the debug code is
- * always seen by the compiler (and optimized out in non-debug builds.
- * If including these becomes a problem, we can start thinking about
- * someway around that. */
-#include <stdio.h>
 #include <errno.h>
+#include <stdio.h>
 #include <stdarg.h>
 
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#include <intrin.h>
+#endif
+
+#define HB_PASTE1(a,b) a##b
+#define HB_PASTE(a,b) HB_PASTE1(a,b)
 
 /* Compile-time custom allocator support. */
 
@@ -74,10 +78,23 @@
 /* Compiler attributes */
 
 
-#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
-#define _HB_BOOLEAN_EXPR(expr) ((expr) ? 1 : 0)
-#define likely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 1))
-#define unlikely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 0))
+#if __cplusplus < 201103L
+
+#ifndef nullptr
+#define nullptr NULL
+#endif
+
+// Static assertions
+#ifndef static_assert
+#define static_assert(e, msg) \
+	HB_UNUSED typedef int HB_PASTE(static_assertion_failed_at_line_, __LINE__) [(e) ? 1 : -1]
+#endif // static_assert
+
+#endif // __cplusplus < 201103L
+
+#if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE__)
+#define likely(expr) (__builtin_expect (!!(expr), 1))
+#define unlikely(expr) (__builtin_expect (!!(expr), 0))
 #else
 #define likely(expr) (expr)
 #define unlikely(expr) (expr)
@@ -99,15 +116,18 @@
 #endif
 #if __GNUC__ >= 4
 #define HB_UNUSED	__attribute__((unused))
+#elif defined(_MSC_VER) /* https://github.com/harfbuzz/harfbuzz/issues/635 */
+#define HB_UNUSED __pragma(warning(suppress: 4100 4101))
 #else
 #define HB_UNUSED
 #endif
 
 #ifndef HB_INTERNAL
-# if !defined(__MINGW32__) && !defined(__CYGWIN__)
+# if !defined(HB_NO_VISIBILITY) && !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_MSC_VER) && !defined(__SUNPRO_CC)
 #  define HB_INTERNAL __attribute__((__visibility__("hidden")))
 # else
 #  define HB_INTERNAL
+#  define HB_NO_VISIBILITY 1
 # endif
 #endif
 
@@ -119,6 +139,11 @@
 #define HB_FUNC __func__
 #endif
 
+#ifdef __SUNPRO_CC
+/* https://github.com/harfbuzz/harfbuzz/issues/630 */
+#define __restrict
+#endif
+
 /*
  * Borrowed from https://bugzilla.mozilla.org/show_bug.cgi?id=1215411
  * HB_FALLTHROUGH is an annotation to suppress compiler warnings about switch
@@ -138,6 +163,9 @@
 #if defined(__clang__) && __cplusplus >= 201103L
    /* clang's fallthrough annotations are only available starting in C++11. */
 #  define HB_FALLTHROUGH [[clang::fallthrough]]
+#elif __GNUC__ >= 7
+   /* GNU fallthrough attribute is available from GCC7 */
+#  define HB_FALLTHROUGH __attribute__((fallthrough))
 #elif defined(_MSC_VER)
    /*
     * MSVC's __fallthrough annotations are checked by /analyze (Code Analysis):
@@ -168,21 +196,17 @@
 
 #  if defined(_WIN32_WCE)
      /* Some things not defined on Windows CE. */
-#    define strdup _strdup
 #    define vsnprintf _vsnprintf
-#    define getenv(Name) NULL
+#    define getenv(Name) nullptr
 #    if _WIN32_WCE < 0x800
 #      define setlocale(Category, Locale) "C"
 static int errno = 0; /* Use something better? */
 #    endif
 #  elif defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
-#    define getenv(Name) NULL
+#    define getenv(Name) nullptr
 #  endif
 #  if defined(_MSC_VER) && _MSC_VER < 1900
 #    define snprintf _snprintf
-#  elif defined(_MSC_VER) && _MSC_VER >= 1900
-#    /* Covers VC++ Error for strdup being a deprecated POSIX name and to instead use _strdup instead */
-#    define strdup _strdup
 #  endif
 #endif
 
@@ -209,16 +233,17 @@
  * https://developer.android.com/tools/sdk/ndk/index.html
  */
 #    define HB_USE_ATEXIT 1
+#  elif defined(__APPLE__)
+/* For macOS and related platforms, the atexit man page indicates
+ * that it will be invoked when the library is unloaded, not only
+ * at application exit.
+ */
+#    define HB_USE_ATEXIT 1
 #  endif
 #endif
 
 /* Basics */
 
-
-#ifndef NULL
-# define NULL ((void *) 0)
-#endif
-
 #undef MIN
 template <typename Type>
 static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
@@ -240,32 +265,26 @@
 #define HB_STMT_START do
 #define HB_STMT_END   while (0)
 
-#define _ASSERT_STATIC1(_line, _cond)	HB_UNUSED typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1]
-#define _ASSERT_STATIC0(_line, _cond)	_ASSERT_STATIC1 (_line, (_cond))
-#define ASSERT_STATIC(_cond)		_ASSERT_STATIC0 (__LINE__, (_cond))
-
-template <unsigned int cond> class hb_assert_constant_t {};
+template <unsigned int cond> class hb_assert_constant_t;
+template <> class hb_assert_constant_t<1> {};
 
 #define ASSERT_STATIC_EXPR_ZERO(_cond) (0 * (unsigned int) sizeof (hb_assert_constant_t<_cond>))
 
-#define _PASTE1(a,b) a##b
-#define PASTE(a,b) _PASTE1(a,b)
-
 /* Lets assert int types.  Saves trouble down the road. */
 
-ASSERT_STATIC (sizeof (int8_t) == 1);
-ASSERT_STATIC (sizeof (uint8_t) == 1);
-ASSERT_STATIC (sizeof (int16_t) == 2);
-ASSERT_STATIC (sizeof (uint16_t) == 2);
-ASSERT_STATIC (sizeof (int32_t) == 4);
-ASSERT_STATIC (sizeof (uint32_t) == 4);
-ASSERT_STATIC (sizeof (int64_t) == 8);
-ASSERT_STATIC (sizeof (uint64_t) == 8);
+static_assert ((sizeof (int8_t) == 1), "");
+static_assert ((sizeof (uint8_t) == 1), "");
+static_assert ((sizeof (int16_t) == 2), "");
+static_assert ((sizeof (uint16_t) == 2), "");
+static_assert ((sizeof (int32_t) == 4), "");
+static_assert ((sizeof (uint32_t) == 4), "");
+static_assert ((sizeof (int64_t) == 8), "");
+static_assert ((sizeof (uint64_t) == 8), "");
 
-ASSERT_STATIC (sizeof (hb_codepoint_t) == 4);
-ASSERT_STATIC (sizeof (hb_position_t) == 4);
-ASSERT_STATIC (sizeof (hb_mask_t) == 4);
-ASSERT_STATIC (sizeof (hb_var_int_t) == 4);
+static_assert ((sizeof (hb_codepoint_t) == 4), "");
+static_assert ((sizeof (hb_position_t) == 4), "");
+static_assert ((sizeof (hb_mask_t) == 4), "");
+static_assert ((sizeof (hb_var_int_t) == 4), "");
 
 
 /* We like our types POD */
@@ -297,57 +316,195 @@
 
 /* Misc */
 
-/* Void! */
-struct _hb_void_t {};
-typedef const _hb_void_t *hb_void_t;
-#define HB_VOID ((const _hb_void_t *) NULL)
+/*
+ * Void!
+ */
+typedef const struct _hb_void_t *hb_void_t;
+#define HB_VOID ((const _hb_void_t *) nullptr)
 
-/* Return the number of 1 bits in mask. */
+/* Return the number of 1 bits in v. */
+template <typename T>
 static inline HB_CONST_FUNC unsigned int
-_hb_popcount32 (uint32_t mask)
+_hb_popcount (T v)
 {
-#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
-  return __builtin_popcount (mask);
-#else
-  /* "HACKMEM 169" */
-  uint32_t y;
-  y = (mask >> 1) &033333333333;
-  y = mask - y - ((y >>1) & 033333333333);
-  return (((y + (y >> 3)) & 030707070707) % 077);
+#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && defined(__OPTIMIZE__)
+  if (sizeof (T) <= sizeof (unsigned int))
+    return __builtin_popcount (v);
+
+  if (sizeof (T) <= sizeof (unsigned long))
+    return __builtin_popcountl (v);
+
+  if (sizeof (T) <= sizeof (unsigned long long))
+    return __builtin_popcountll (v);
 #endif
+
+  if (sizeof (T) <= 4)
+  {
+    /* "HACKMEM 169" */
+    uint32_t y;
+    y = (v >> 1) &033333333333;
+    y = v - y - ((y >>1) & 033333333333);
+    return (((y + (y >> 3)) & 030707070707) % 077);
+  }
+
+  if (sizeof (T) == 8)
+  {
+    unsigned int shift = 32;
+    return _hb_popcount<uint32_t> ((uint32_t) v) + _hb_popcount ((uint32_t) (v >> shift));
+  }
+
+  if (sizeof (T) == 16)
+  {
+    unsigned int shift = 64;
+    return _hb_popcount<uint64_t> ((uint64_t) v) + _hb_popcount ((uint64_t) (v >> shift));
+  }
+
+  assert (0);
 }
 
 /* Returns the number of bits needed to store number */
+template <typename T>
 static inline HB_CONST_FUNC unsigned int
-_hb_bit_storage (unsigned int number)
+_hb_bit_storage (T v)
 {
+  if (unlikely (!v)) return 0;
+
 #if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
-  return likely (number) ? (sizeof (unsigned int) * 8 - __builtin_clz (number)) : 0;
-#else
-  unsigned int n_bits = 0;
-  while (number) {
-    n_bits++;
-    number >>= 1;
-  }
-  return n_bits;
+  if (sizeof (T) <= sizeof (unsigned int))
+    return sizeof (unsigned int) * 8 - __builtin_clz (v);
+
+  if (sizeof (T) <= sizeof (unsigned long))
+    return sizeof (unsigned long) * 8 - __builtin_clzl (v);
+
+  if (sizeof (T) <= sizeof (unsigned long long))
+    return sizeof (unsigned long long) * 8 - __builtin_clzll (v);
 #endif
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+  if (sizeof (T) <= sizeof (unsigned int))
+  {
+    unsigned long where;
+    _BitScanReverse (&where, v);
+    return 1 + where;
+  }
+# if _WIN64
+  if (sizeof (T) <= 8)
+  {
+    unsigned long where;
+    _BitScanReverse64 (&where, v);
+    return 1 + where;
+  }
+# endif
+#endif
+
+  if (sizeof (T) <= 4)
+  {
+    /* "bithacks" */
+    const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
+    const unsigned int S[] = {1, 2, 4, 8, 16};
+    unsigned int r = 0;
+    for (int i = 4; i >= 0; i--)
+      if (v & b[i])
+      {
+	v >>= S[i];
+	r |= S[i];
+      }
+    return r + 1;
+  }
+  if (sizeof (T) <= 8)
+  {
+    /* "bithacks" */
+    const uint64_t b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000, 0xFFFFFFFF00000000};
+    const unsigned int S[] = {1, 2, 4, 8, 16, 32};
+    unsigned int r = 0;
+    for (int i = 5; i >= 0; i--)
+      if (v & b[i])
+      {
+	v >>= S[i];
+	r |= S[i];
+      }
+    return r + 1;
+  }
+  if (sizeof (T) == 16)
+  {
+    unsigned int shift = 64;
+    return (v >> shift) ? _hb_bit_storage<uint64_t> ((uint64_t) v >> shift) + shift :
+			  _hb_bit_storage<uint64_t> ((uint64_t) v);
+  }
+
+  assert (0);
 }
 
-/* Returns the number of zero bits in the least significant side of number */
+/* Returns the number of zero bits in the least significant side of v */
+template <typename T>
 static inline HB_CONST_FUNC unsigned int
-_hb_ctz (unsigned int number)
+_hb_ctz (T v)
 {
+  if (unlikely (!v)) return 0;
+
 #if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
-  return likely (number) ? __builtin_ctz (number) : 0;
-#else
-  unsigned int n_bits = 0;
-  if (unlikely (!number)) return 0;
-  while (!(number & 1)) {
-    n_bits++;
-    number >>= 1;
-  }
-  return n_bits;
+  if (sizeof (T) <= sizeof (unsigned int))
+    return __builtin_ctz (v);
+
+  if (sizeof (T) <= sizeof (unsigned long))
+    return __builtin_ctzl (v);
+
+  if (sizeof (T) <= sizeof (unsigned long long))
+    return __builtin_ctzll (v);
 #endif
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+  if (sizeof (T) <= sizeof (unsigned int))
+  {
+    unsigned long where;
+    _BitScanForward (&where, v);
+    return where;
+  }
+# if _WIN64
+  if (sizeof (T) <= 8)
+  {
+    unsigned long where;
+    _BitScanForward64 (&where, v);
+    return where;
+  }
+# endif
+#endif
+
+  if (sizeof (T) <= 4)
+  {
+    /* "bithacks" */
+    unsigned int c = 32;
+    v &= - (int32_t) v;
+    if (v) c--;
+    if (v & 0x0000FFFF) c -= 16;
+    if (v & 0x00FF00FF) c -= 8;
+    if (v & 0x0F0F0F0F) c -= 4;
+    if (v & 0x33333333) c -= 2;
+    if (v & 0x55555555) c -= 1;
+    return c;
+  }
+  if (sizeof (T) <= 8)
+  {
+    /* "bithacks" */
+    unsigned int c = 64;
+    v &= - (int64_t) (v);
+    if (v) c--;
+    if (v & 0x00000000FFFFFFFF) c -= 32;
+    if (v & 0x0000FFFF0000FFFF) c -= 16;
+    if (v & 0x00FF00FF00FF00FF) c -= 8;
+    if (v & 0x0F0F0F0F0F0F0F0F) c -= 4;
+    if (v & 0x3333333333333333) c -= 2;
+    if (v & 0x5555555555555555) c -= 1;
+    return c;
+  }
+  if (sizeof (T) == 16)
+  {
+    unsigned int shift = 64;
+    return (uint64_t) v ? _hb_bit_storage<uint64_t> ((uint64_t) v) :
+			  _hb_bit_storage<uint64_t> ((uint64_t) v >> shift) + shift;
+  }
+
+  assert (0);
 }
 
 static inline bool
@@ -356,17 +513,18 @@
   return (size > 0) && (count >= ((unsigned int) -1) / size);
 }
 
-
-/* Type of bsearch() / qsort() compare function */
-typedef int (*hb_compare_func_t) (const void *, const void *);
-
+static inline unsigned int
+_hb_ceil_to_4 (unsigned int v)
+{
+  return ((v - 1) | 3) + 1;
+}
 
 
 
 /* arrays and maps */
 
 
-#define HB_PREALLOCED_ARRAY_INIT {0, 0, NULL}
+#define HB_PREALLOCED_ARRAY_INIT {0, 0, nullptr}
 template <typename Type, unsigned int StaticSize=16>
 struct hb_prealloced_array_t
 {
@@ -375,41 +533,65 @@
   Type *array;
   Type static_array[StaticSize];
 
-  void init (void) { memset (this, 0, sizeof (*this)); }
+  void init (void)
+  {
+    len = 0;
+    allocated = ARRAY_LENGTH (static_array);
+    array = static_array;
+  }
 
   inline Type& operator [] (unsigned int i) { return array[i]; }
   inline const Type& operator [] (unsigned int i) const { return array[i]; }
 
   inline Type *push (void)
   {
-    if (!array) {
-      array = static_array;
-      allocated = ARRAY_LENGTH (static_array);
-    }
-    if (likely (len < allocated))
-      return &array[len++];
+    if (unlikely (!resize (len + 1)))
+      return nullptr;
 
-    /* Need to reallocate */
-    unsigned int new_allocated = allocated + (allocated >> 1) + 8;
-    Type *new_array = NULL;
+    return &array[len - 1];
+  }
+
+  /* Allocate for size but don't adjust len. */
+  inline bool alloc(unsigned int size)
+  {
+    if (likely (size <= allocated))
+      return true;
+
+    /* Reallocate */
+
+    unsigned int new_allocated = allocated;
+    while (size >= new_allocated)
+      new_allocated += (new_allocated >> 1) + 8;
+
+    Type *new_array = nullptr;
 
     if (array == static_array) {
       new_array = (Type *) calloc (new_allocated, sizeof (Type));
       if (new_array)
         memcpy (new_array, array, len * sizeof (Type));
-    } else {
+          } else {
       bool overflows = (new_allocated < allocated) || _hb_unsigned_int_mul_overflows (new_allocated, sizeof (Type));
       if (likely (!overflows)) {
-	new_array = (Type *) realloc (array, new_allocated * sizeof (Type));
+        new_array = (Type *) realloc (array, new_allocated * sizeof (Type));
       }
     }
 
     if (unlikely (!new_array))
-      return NULL;
+      return false;
 
     array = new_array;
     allocated = new_allocated;
-    return &array[len++];
+
+    return true;
+  }
+
+  inline bool resize (unsigned int size)
+  {
+    if (!alloc (size))
+      return false;
+
+    len = size;
+    return true;
   }
 
   inline void pop (void)
@@ -438,42 +620,81 @@
     for (unsigned int i = 0; i < len; i++)
       if (array[i] == v)
 	return &array[i];
-    return NULL;
+    return nullptr;
   }
   template <typename T>
   inline const Type *find (T v) const {
     for (unsigned int i = 0; i < len; i++)
       if (array[i] == v)
 	return &array[i];
-    return NULL;
+    return nullptr;
+  }
+
+  inline void qsort (int (*cmp)(const void*, const void*))
+  {
+    ::qsort (array, len, sizeof (Type), cmp);
   }
 
   inline void qsort (void)
   {
-    ::qsort (array, len, sizeof (Type), (hb_compare_func_t) Type::cmp);
+    ::qsort (array, len, sizeof (Type), Type::cmp);
   }
 
   inline void qsort (unsigned int start, unsigned int end)
   {
-    ::qsort (array + start, end - start, sizeof (Type), (hb_compare_func_t) Type::cmp);
+    ::qsort (array + start, end - start, sizeof (Type), Type::cmp);
   }
 
   template <typename T>
-  inline Type *bsearch (T *key)
+  inline Type *lsearch (const T &x)
   {
-    return (Type *) ::bsearch (key, array, len, sizeof (Type), (hb_compare_func_t) Type::cmp);
+    for (unsigned int i = 0; i < len; i++)
+      if (0 == this->array[i].cmp (&x))
+	return &array[i];
+    return nullptr;
+  }
+
+  template <typename T>
+  inline Type *bsearch (const T &x)
+  {
+    unsigned int i;
+    return bfind (x, &i) ? &array[i] : nullptr;
   }
   template <typename T>
-  inline const Type *bsearch (T *key) const
+  inline const Type *bsearch (const T &x) const
   {
-    return (const Type *) ::bsearch (key, array, len, sizeof (Type), (hb_compare_func_t) Type::cmp);
+    unsigned int i;
+    return bfind (x, &i) ? &array[i] : nullptr;
+  }
+  template <typename T>
+  inline bool bfind (const T &x, unsigned int *i) const
+  {
+    int min = 0, max = (int) this->len - 1;
+    while (min <= max)
+    {
+      int mid = (min + max) / 2;
+      int c = this->array[mid].cmp (&x);
+      if (c < 0)
+        max = mid - 1;
+      else if (c > 0)
+        min = mid + 1;
+      else
+      {
+        *i = mid;
+	return true;
+      }
+    }
+    if (max < 0 || (max < (int) this->len && this->array[max].cmp (&x) > 0))
+      max++;
+    *i = max;
+    return false;
   }
 
   inline void finish (void)
   {
     if (array != static_array)
       free (array);
-    array = NULL;
+    array = nullptr;
     allocated = len = 0;
   }
 };
@@ -490,7 +711,7 @@
 template <typename item_t, typename lock_t>
 struct hb_lockable_set_t
 {
-  hb_prealloced_array_t <item_t, 2> items;
+  hb_prealloced_array_t <item_t, 1> items;
 
   inline void init (void) { items.init (); }
 
@@ -507,7 +728,7 @@
 	old.finish ();
       }
       else {
-        item = NULL;
+        item = nullptr;
 	l.unlock ();
       }
     } else {
@@ -595,22 +816,6 @@
 static inline unsigned char TOLOWER (unsigned char c)
 { return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; }
 
-#define HB_TAG_CHAR4(s)   (HB_TAG(((const char *) s)[0], \
-				  ((const char *) s)[1], \
-				  ((const char *) s)[2], \
-				  ((const char *) s)[3]))
-
-
-/* C++ helpers */
-
-/* Makes class uncopyable.  Use in private: section. */
-#define NO_COPY(T) \
-  T (const T &o); \
-  T &operator = (const T &o)
-
-
-/* Debug */
-
 
 /* HB_NDEBUG disables some sanity checks that are very safe to disable and
  * should be disabled in production systems.  If NDEBUG is defined, enable
@@ -621,255 +826,6 @@
 #define HB_NDEBUG
 #endif
 
-#ifndef HB_DEBUG
-#define HB_DEBUG 0
-#endif
-
-static inline bool
-_hb_debug (unsigned int level,
-	   unsigned int max_level)
-{
-  return level < max_level;
-}
-
-#define DEBUG_LEVEL_ENABLED(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT))
-#define DEBUG_ENABLED(WHAT) (DEBUG_LEVEL_ENABLED (WHAT, 0))
-
-static inline void
-_hb_print_func (const char *func)
-{
-  if (func)
-  {
-    unsigned int func_len = strlen (func);
-    /* Skip "static" */
-    if (0 == strncmp (func, "static ", 7))
-      func += 7;
-    /* Skip "typename" */
-    if (0 == strncmp (func, "typename ", 9))
-      func += 9;
-    /* Skip return type */
-    const char *space = strchr (func, ' ');
-    if (space)
-      func = space + 1;
-    /* Skip parameter list */
-    const char *paren = strchr (func, '(');
-    if (paren)
-      func_len = paren - func;
-    fprintf (stderr, "%.*s", func_len, func);
-  }
-}
-
-template <int max_level> static inline void
-_hb_debug_msg_va (const char *what,
-		  const void *obj,
-		  const char *func,
-		  bool indented,
-		  unsigned int level,
-		  int level_dir,
-		  const char *message,
-		  va_list ap) HB_PRINTF_FUNC(7, 0);
-template <int max_level> static inline void
-_hb_debug_msg_va (const char *what,
-		  const void *obj,
-		  const char *func,
-		  bool indented,
-		  unsigned int level,
-		  int level_dir,
-		  const char *message,
-		  va_list ap)
-{
-  if (!_hb_debug (level, max_level))
-    return;
-
-  fprintf (stderr, "%-10s", what ? what : "");
-
-  if (obj)
-    fprintf (stderr, "(%0*lx) ", (unsigned int) (2 * sizeof (void *)), (unsigned long) obj);
-  else
-    fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), "");
-
-  if (indented) {
-#define VBAR	"\342\224\202"	/* U+2502 BOX DRAWINGS LIGHT VERTICAL */
-#define VRBAR	"\342\224\234"	/* U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
-#define DLBAR	"\342\225\256"	/* U+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT */
-#define ULBAR	"\342\225\257"	/* U+256F BOX DRAWINGS LIGHT ARC UP AND LEFT */
-#define LBAR	"\342\225\264"	/* U+2574 BOX DRAWINGS LIGHT LEFT */
-    static const char bars[] =
-      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
-      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
-      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
-      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
-      VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR;
-    fprintf (stderr, "%2u %s" VRBAR "%s",
-	     level,
-	     bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level),
-	     level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR);
-  } else
-    fprintf (stderr, "   " VRBAR LBAR);
-
-  _hb_print_func (func);
-
-  if (message)
-  {
-    fprintf (stderr, ": ");
-    vfprintf (stderr, message, ap);
-  }
-
-  fprintf (stderr, "\n");
-}
-template <> inline void
-_hb_debug_msg_va<0> (const char *what HB_UNUSED,
-		     const void *obj HB_UNUSED,
-		     const char *func HB_UNUSED,
-		     bool indented HB_UNUSED,
-		     unsigned int level HB_UNUSED,
-		     int level_dir HB_UNUSED,
-		     const char *message HB_UNUSED,
-		     va_list ap HB_UNUSED) {}
-
-template <int max_level> static inline void
-_hb_debug_msg (const char *what,
-	       const void *obj,
-	       const char *func,
-	       bool indented,
-	       unsigned int level,
-	       int level_dir,
-	       const char *message,
-	       ...) HB_PRINTF_FUNC(7, 8);
-template <int max_level> static inline void
-_hb_debug_msg (const char *what,
-	       const void *obj,
-	       const char *func,
-	       bool indented,
-	       unsigned int level,
-	       int level_dir,
-	       const char *message,
-	       ...)
-{
-  va_list ap;
-  va_start (ap, message);
-  _hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap);
-  va_end (ap);
-}
-template <> inline void
-_hb_debug_msg<0> (const char *what HB_UNUSED,
-		  const void *obj HB_UNUSED,
-		  const char *func HB_UNUSED,
-		  bool indented HB_UNUSED,
-		  unsigned int level HB_UNUSED,
-		  int level_dir HB_UNUSED,
-		  const char *message HB_UNUSED,
-		  ...) HB_PRINTF_FUNC(7, 8);
-template <> inline void
-_hb_debug_msg<0> (const char *what HB_UNUSED,
-		  const void *obj HB_UNUSED,
-		  const char *func HB_UNUSED,
-		  bool indented HB_UNUSED,
-		  unsigned int level HB_UNUSED,
-		  int level_dir HB_UNUSED,
-		  const char *message HB_UNUSED,
-		  ...) {}
-
-#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...)	_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL,    true, (LEVEL), (LEVEL_DIR), __VA_ARGS__)
-#define DEBUG_MSG(WHAT, OBJ, ...) 				_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL,    false, 0, 0, __VA_ARGS__)
-#define DEBUG_MSG_FUNC(WHAT, OBJ, ...)				_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__)
-
-
-/*
- * Printer
- */
-
-template <typename T>
-struct hb_printer_t {
-  const char *print (const T&) { return "something"; }
-};
-
-template <>
-struct hb_printer_t<bool> {
-  const char *print (bool v) { return v ? "true" : "false"; }
-};
-
-template <>
-struct hb_printer_t<hb_void_t> {
-  const char *print (hb_void_t) { return ""; }
-};
-
-
-/*
- * Trace
- */
-
-template <typename T>
-static inline void _hb_warn_no_return (bool returned)
-{
-  if (unlikely (!returned)) {
-    fprintf (stderr, "OUCH, returned with no call to return_trace().  This is a bug, please report.\n");
-  }
-}
-template <>
-/*static*/ inline void _hb_warn_no_return<hb_void_t> (bool returned HB_UNUSED)
-{}
-
-template <int max_level, typename ret_t>
-struct hb_auto_trace_t {
-  explicit inline hb_auto_trace_t (unsigned int *plevel_,
-				   const char *what_,
-				   const void *obj_,
-				   const char *func,
-				   const char *message,
-				   ...) : plevel (plevel_), what (what_), obj (obj_), returned (false)
-  {
-    if (plevel) ++*plevel;
-
-    va_list ap;
-    va_start (ap, message);
-    _hb_debug_msg_va<max_level> (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap);
-    va_end (ap);
-  }
-  inline ~hb_auto_trace_t (void)
-  {
-    _hb_warn_no_return<ret_t> (returned);
-    if (!returned) {
-      _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1, " ");
-    }
-    if (plevel) --*plevel;
-  }
-
-  inline ret_t ret (ret_t v, unsigned int line = 0)
-  {
-    if (unlikely (returned)) {
-      fprintf (stderr, "OUCH, double calls to return_trace().  This is a bug, please report.\n");
-      return v;
-    }
-
-    _hb_debug_msg<max_level> (what, obj, NULL, true, plevel ? *plevel : 1, -1,
-			      "return %s (line %d)",
-			      hb_printer_t<ret_t>().print (v), line);
-    if (plevel) --*plevel;
-    plevel = NULL;
-    returned = true;
-    return v;
-  }
-
-  private:
-  unsigned int *plevel;
-  const char *what;
-  const void *obj;
-  bool returned;
-};
-template <typename ret_t> /* Optimize when tracing is disabled */
-struct hb_auto_trace_t<0, ret_t> {
-  explicit inline hb_auto_trace_t (unsigned int *plevel_ HB_UNUSED,
-				   const char *what HB_UNUSED,
-				   const void *obj HB_UNUSED,
-				   const char *func HB_UNUSED,
-				   const char *message HB_UNUSED,
-				   ...) {}
-
-  inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; }
-};
-
-#define return_trace(RET) return trace.ret (RET, __LINE__)
 
 /* Misc */
 
@@ -887,7 +843,7 @@
    * one right now.  Declaring a variable won't work as HB_UNUSED
    * is unusable on some platforms and unused types are less likely
    * to generate a warning than unused variables. */
-  ASSERT_STATIC (sizeof (hb_assert_unsigned_t<T>) >= 0);
+  static_assert ((sizeof (hb_assert_unsigned_t<T>) >= 0), "");
 
   /* The casts below are important as if T is smaller than int,
    * the subtract results will become a signed int! */
@@ -912,7 +868,7 @@
  * one enum to another...  So this doesn't provide the type-checking that I
  * originally had in mind... :(.
  *
- * For MSVC warnings, see: https://github.com/behdad/harfbuzz/pull/163
+ * For MSVC warnings, see: https://github.com/harfbuzz/harfbuzz/pull/163
  */
 #ifdef _MSC_VER
 # pragma warning(disable:4200)
@@ -932,11 +888,10 @@
 
 /* Useful for set-operations on small enums.
  * For example, for testing "x ∈ {x1, x2, x3}" use:
- * (FLAG_SAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3)))
+ * (FLAG_UNSAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3)))
  */
-#define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((x) < 32) + (1U << (x)))
-#define FLAG_SAFE(x) (1U << (x))
-#define FLAG_UNSAFE(x) ((x) < 32 ? FLAG_SAFE(x) : 0)
+#define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((unsigned int)(x) < 32) + (1U << (unsigned int)(x)))
+#define FLAG_UNSAFE(x) ((unsigned int)(x) < 32 ? (1U << (unsigned int)(x)) : 0)
 #define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x))
 
 
@@ -968,7 +923,7 @@
 template <typename T> static inline void
 hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
 {
-  hb_stable_sort (array, len, compar, (int *) NULL);
+  hb_stable_sort (array, len, compar, (int *) nullptr);
 }
 
 static inline hb_bool_t
@@ -990,6 +945,73 @@
 }
 
 
+/* Vectorization */
+
+struct HbOpOr
+{
+  static const bool passthru_left = true;
+  static const bool passthru_right = true;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a | b; }
+};
+struct HbOpAnd
+{
+  static const bool passthru_left = false;
+  static const bool passthru_right = false;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a & b; }
+};
+struct HbOpMinus
+{
+  static const bool passthru_left = true;
+  static const bool passthru_right = false;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a & ~b; }
+};
+struct HbOpXor
+{
+  static const bool passthru_left = true;
+  static const bool passthru_right = true;
+  template <typename T> static void process (T &o, const T &a, const T &b) { o = a ^ b; }
+};
+
+/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))). */
+template <typename elt_t, unsigned int byte_size>
+struct hb_vector_size_t
+{
+  elt_t& operator [] (unsigned int i) { return v[i]; }
+  const elt_t& operator [] (unsigned int i) const { return v[i]; }
+
+  template <class Op>
+  inline hb_vector_size_t process (const hb_vector_size_t &o) const
+  {
+    hb_vector_size_t r;
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      Op::process (r.v[i], v[i], o.v[i]);
+    return r;
+  }
+  inline hb_vector_size_t operator | (const hb_vector_size_t &o) const
+  { return process<HbOpOr> (o); }
+  inline hb_vector_size_t operator & (const hb_vector_size_t &o) const
+  { return process<HbOpAnd> (o); }
+  inline hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
+  { return process<HbOpXor> (o); }
+  inline hb_vector_size_t operator ~ () const
+  {
+    hb_vector_size_t r;
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      r.v[i] = ~v[i];
+    return r;
+  }
+
+  private:
+  static_assert (byte_size / sizeof (elt_t) * sizeof (elt_t) == byte_size, "");
+  elt_t v[byte_size / sizeof (elt_t)];
+};
+
+/* The `vector_size' attribute was introduced in gcc 3.1. */
+#if defined( __GNUC__ ) && ( __GNUC__ >= 4 )
+#define HAVE_VECTOR_SIZE 1
+#endif
+
+
 /* Global runtime options. */
 
 struct hb_options_t
@@ -1002,7 +1024,7 @@
   unsigned int i;
   hb_options_t opts;
 };
-ASSERT_STATIC (sizeof (int) == sizeof (hb_options_union_t));
+static_assert ((sizeof (int) == sizeof (hb_options_union_t)), "");
 
 HB_INTERNAL void
 _hb_options_init (void);
@@ -1021,4 +1043,31 @@
 /* Size signifying variable-sized array */
 #define VAR 1
 
+
+/* String type. */
+
+struct hb_string_t
+{
+  inline hb_string_t (void) : bytes (nullptr), len (0) {}
+  inline hb_string_t (const char *bytes_, unsigned int len_) : bytes (bytes_), len (len_) {}
+
+  inline int cmp (const hb_string_t &a) const
+  {
+    if (len != a.len)
+      return (int) a.len - (int) len;
+
+    return memcmp (a.bytes, bytes, len);
+  }
+  static inline int cmp (const void *pa, const void *pb)
+  {
+    hb_string_t *a = (hb_string_t *) pa;
+    hb_string_t *b = (hb_string_t *) pb;
+    return b->cmp (*a);
+  }
+
+  const char *bytes;
+  unsigned int len;
+};
+
+
 #endif /* HB_PRIVATE_HH */
diff --git a/src/hb-set-digest-private.hh b/src/hb-set-digest-private.hh
new file mode 100644
index 0000000..e099a82
--- /dev/null
+++ b/src/hb-set-digest-private.hh
@@ -0,0 +1,179 @@
+/*
+ * 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_SET_DIGEST_PRIVATE_HH
+#define HB_SET_DIGEST_PRIVATE_HH
+
+#include "hb-private.hh"
+
+/*
+ * The set digests here implement various "filters" that support
+ * "approximate member query".  Conceptually these are like Bloom
+ * Filter and Quotient Filter, however, much smaller, faster, and
+ * designed to fit the requirements of our uses for glyph coverage
+ * queries.
+ *
+ * Our filters are highly accurate if the lookup covers fairly local
+ * set of glyphs, but fully flooded and ineffective if coverage is
+ * all over the place.
+ *
+ * The frozen-set can be used instead of a digest, to trade more
+ * memory for 100% accuracy, but in practice, that doesn't look like
+ * an attractive trade-off.
+ */
+
+template <typename mask_t, unsigned int shift>
+struct hb_set_digest_lowest_bits_t
+{
+  ASSERT_POD ();
+
+  static const unsigned int mask_bytes = sizeof (mask_t);
+  static const unsigned int mask_bits = sizeof (mask_t) * 8;
+  static const unsigned int num_bits = 0
+				     + (mask_bytes >= 1 ? 3 : 0)
+				     + (mask_bytes >= 2 ? 1 : 0)
+				     + (mask_bytes >= 4 ? 1 : 0)
+				     + (mask_bytes >= 8 ? 1 : 0)
+				     + (mask_bytes >= 16? 1 : 0)
+				     + 0;
+
+  static_assert ((shift < sizeof (hb_codepoint_t) * 8), "");
+  static_assert ((shift + num_bits <= sizeof (hb_codepoint_t) * 8), "");
+
+  inline void init (void) {
+    mask = 0;
+  }
+
+  inline void add (hb_codepoint_t g) {
+    mask |= mask_for (g);
+  }
+
+  inline bool add_range (hb_codepoint_t a, hb_codepoint_t b) {
+    if ((b >> shift) - (a >> shift) >= mask_bits - 1)
+      mask = (mask_t) -1;
+    else {
+      mask_t ma = mask_for (a);
+      mask_t mb = mask_for (b);
+      mask |= mb + (mb - ma) - (mb < ma);
+    }
+    return true;
+  }
+
+  template <typename T>
+  inline void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      add (*array);
+      array = (const T *) (stride + (const char *) array);
+    }
+  }
+  template <typename T>
+  inline bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      add (*array);
+      array = (const T *) (stride + (const char *) array);
+    }
+    return true;
+  }
+
+  inline bool may_have (hb_codepoint_t g) const {
+    return !!(mask & mask_for (g));
+  }
+
+  private:
+
+  static inline mask_t mask_for (hb_codepoint_t g) {
+    return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1));
+  }
+  mask_t mask;
+};
+
+template <typename head_t, typename tail_t>
+struct hb_set_digest_combiner_t
+{
+  ASSERT_POD ();
+
+  inline void init (void) {
+    head.init ();
+    tail.init ();
+  }
+
+  inline void add (hb_codepoint_t g) {
+    head.add (g);
+    tail.add (g);
+  }
+
+  inline bool add_range (hb_codepoint_t a, hb_codepoint_t b) {
+    head.add_range (a, b);
+    tail.add_range (a, b);
+    return true;
+  }
+  template <typename T>
+  inline void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+  {
+    head.add_array (array, count, stride);
+    tail.add_array (array, count, stride);
+  }
+  template <typename T>
+  inline bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+  {
+    head.add_sorted_array (array, count, stride);
+    tail.add_sorted_array (array, count, stride);
+    return true;
+  }
+
+  inline bool may_have (hb_codepoint_t g) const {
+    return head.may_have (g) && tail.may_have (g);
+  }
+
+  private:
+  head_t head;
+  tail_t tail;
+};
+
+
+/*
+ * hb_set_digest_t
+ *
+ * This is a combination of digests that performs "best".
+ * There is not much science to this: it's a result of intuition
+ * and testing.
+ */
+typedef hb_set_digest_combiner_t
+<
+  hb_set_digest_lowest_bits_t<unsigned long, 4>,
+  hb_set_digest_combiner_t
+  <
+    hb_set_digest_lowest_bits_t<unsigned long, 0>,
+    hb_set_digest_lowest_bits_t<unsigned long, 9>
+  >
+> hb_set_digest_t;
+
+
+#endif /* HB_SET_DIGEST_PRIVATE_HH */
diff --git a/src/hb-set-private.hh b/src/hb-set-private.hh
index e2010d7..49cd791 100644
--- a/src/hb-set-private.hh
+++ b/src/hb-set-private.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2012  Google, Inc.
+ * Copyright © 2012,2017  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -32,254 +32,503 @@
 
 
 /*
- * The set digests here implement various "filters" that support
- * "approximate member query".  Conceptually these are like Bloom
- * Filter and Quotient Filter, however, much smaller, faster, and
- * designed to fit the requirements of our uses for glyph coverage
- * queries.
- *
- * Our filters are highly accurate if the lookup covers fairly local
- * set of glyphs, but fully flooded and ineffective if coverage is
- * all over the place.
- *
- * The frozen-set can be used instead of a digest, to trade more
- * memory for 100% accuracy, but in practice, that doesn't look like
- * an attractive trade-off.
- */
-
-template <typename mask_t, unsigned int shift>
-struct hb_set_digest_lowest_bits_t
-{
-  ASSERT_POD ();
-
-  static const unsigned int mask_bytes = sizeof (mask_t);
-  static const unsigned int mask_bits = sizeof (mask_t) * 8;
-  static const unsigned int num_bits = 0
-				     + (mask_bytes >= 1 ? 3 : 0)
-				     + (mask_bytes >= 2 ? 1 : 0)
-				     + (mask_bytes >= 4 ? 1 : 0)
-				     + (mask_bytes >= 8 ? 1 : 0)
-				     + (mask_bytes >= 16? 1 : 0)
-				     + 0;
-
-  ASSERT_STATIC (shift < sizeof (hb_codepoint_t) * 8);
-  ASSERT_STATIC (shift + num_bits <= sizeof (hb_codepoint_t) * 8);
-
-  inline void init (void) {
-    mask = 0;
-  }
-
-  inline void add (hb_codepoint_t g) {
-    mask |= mask_for (g);
-  }
-
-  inline void add_range (hb_codepoint_t a, hb_codepoint_t b) {
-    if ((b >> shift) - (a >> shift) >= mask_bits - 1)
-      mask = (mask_t) -1;
-    else {
-      mask_t ma = mask_for (a);
-      mask_t mb = mask_for (b);
-      mask |= mb + (mb - ma) - (mb < ma);
-    }
-  }
-
-  inline bool may_have (hb_codepoint_t g) const {
-    return !!(mask & mask_for (g));
-  }
-
-  private:
-
-  static inline mask_t mask_for (hb_codepoint_t g) {
-    return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1));
-  }
-  mask_t mask;
-};
-
-template <typename head_t, typename tail_t>
-struct hb_set_digest_combiner_t
-{
-  ASSERT_POD ();
-
-  inline void init (void) {
-    head.init ();
-    tail.init ();
-  }
-
-  inline void add (hb_codepoint_t g) {
-    head.add (g);
-    tail.add (g);
-  }
-
-  inline void add_range (hb_codepoint_t a, hb_codepoint_t b) {
-    head.add_range (a, b);
-    tail.add_range (a, b);
-  }
-
-  inline bool may_have (hb_codepoint_t g) const {
-    return head.may_have (g) && tail.may_have (g);
-  }
-
-  private:
-  head_t head;
-  tail_t tail;
-};
-
-
-/*
- * hb_set_digest_t
- *
- * This is a combination of digests that performs "best".
- * There is not much science to this: it's a result of intuition
- * and testing.
- */
-typedef hb_set_digest_combiner_t
-<
-  hb_set_digest_lowest_bits_t<unsigned long, 4>,
-  hb_set_digest_combiner_t
-  <
-    hb_set_digest_lowest_bits_t<unsigned long, 0>,
-    hb_set_digest_lowest_bits_t<unsigned long, 9>
-  >
-> hb_set_digest_t;
-
-
-
-/*
  * hb_set_t
  */
 
-
-/* TODO Make this faster and memmory efficient. */
+/* TODO Keep a free-list so we can free pages that are completely zeroed.  At that
+ * point maybe also use a sentinel value for "all-1" pages? */
 
 struct hb_set_t
 {
-  friend struct hb_frozen_set_t;
+  struct page_map_t
+  {
+    inline int cmp (const page_map_t *o) const { return (int) o->major - (int) major; }
+
+    uint32_t major;
+    uint32_t index;
+  };
+
+  struct page_t
+  {
+    inline void init0 (void) { memset (&v, 0, sizeof (v)); }
+    inline void init1 (void) { memset (&v, 0xff, sizeof (v)); }
+
+    inline unsigned int len (void) const
+    { return ARRAY_LENGTH_CONST (v); }
+
+    inline bool is_empty (void) const
+    {
+      for (unsigned int i = 0; i < len (); i++)
+        if (v[i])
+	  return false;
+      return true;
+    }
+
+    inline void add (hb_codepoint_t g) { elt (g) |= mask (g); }
+    inline void del (hb_codepoint_t g) { elt (g) &= ~mask (g); }
+    inline bool has (hb_codepoint_t g) const { return !!(elt (g) & mask (g)); }
+
+    inline void add_range (hb_codepoint_t a, hb_codepoint_t b)
+    {
+      elt_t *la = &elt (a);
+      elt_t *lb = &elt (b);
+      if (la == lb)
+        *la |= (mask (b) << 1) - mask(a);
+      else
+      {
+	*la |= ~(mask (a) - 1);
+	la++;
+
+	memset (la, 0xff, (char *) lb - (char *) la);
+
+	*lb |= ((mask (b) << 1) - 1);
+      }
+    }
+
+    inline bool is_equal (const page_t *other) const
+    {
+      return 0 == memcmp (&v, &other->v, sizeof (v));
+    }
+
+    inline unsigned int get_population (void) const
+    {
+      unsigned int pop = 0;
+      for (unsigned int i = 0; i < len (); i++)
+        pop += _hb_popcount (v[i]);
+      return pop;
+    }
+
+    inline bool next (hb_codepoint_t *codepoint) const
+    {
+      unsigned int m = (*codepoint + 1) & MASK;
+      if (!m)
+      {
+	*codepoint = INVALID;
+	return false;
+      }
+      unsigned int i = m / ELT_BITS;
+      unsigned int j = m & ELT_MASK;
+
+      const elt_t vv = v[i] & ~((elt_t (1) << j) - 1);
+      for (const elt_t *p = &vv; i < len (); p = &v[++i])
+	if (*p)
+	{
+	  *codepoint = i * ELT_BITS + elt_get_min (*p);
+	  return true;
+	}
+
+      *codepoint = INVALID;
+      return false;
+    }
+    inline bool previous (hb_codepoint_t *codepoint) const
+    {
+      unsigned int m = (*codepoint - 1) & MASK;
+      if (m == MASK)
+      {
+	*codepoint = INVALID;
+	return false;
+      }
+      unsigned int i = m / ELT_BITS;
+      unsigned int j = m & ELT_MASK;
+
+      const elt_t vv = v[i] & ((elt_t (1) << (j + 1)) - 1);
+      for (const elt_t *p = &vv; (int) i >= 0; p = &v[--i])
+	if (*p)
+	{
+	  *codepoint = i * ELT_BITS + elt_get_max (*p);
+	  return true;
+	}
+
+      *codepoint = INVALID;
+      return false;
+    }
+    inline hb_codepoint_t get_min (void) const
+    {
+      for (unsigned int i = 0; i < len (); i++)
+        if (v[i])
+	  return i * ELT_BITS + elt_get_min (v[i]);
+      return INVALID;
+    }
+    inline hb_codepoint_t get_max (void) const
+    {
+      for (int i = len () - 1; i >= 0; i--)
+        if (v[i])
+	  return i * ELT_BITS + elt_get_max (v[i]);
+      return 0;
+    }
+
+    typedef unsigned long long elt_t;
+    static const unsigned int PAGE_BITS = 1024;
+    static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, "");
+
+    static inline unsigned int elt_get_min (const elt_t &elt) { return _hb_ctz (elt); }
+    static inline unsigned int elt_get_max (const elt_t &elt) { return _hb_bit_storage (elt) - 1; }
+
+#if 0 && HAVE_VECTOR_SIZE
+    /* The vectorized version does not work with clang as non-const
+     * elt() errs "non-const reference cannot bind to vector element". */
+    typedef elt_t vector_t __attribute__((vector_size (PAGE_BITS / 8)));
+#else
+    typedef hb_vector_size_t<elt_t, PAGE_BITS / 8> vector_t;
+#endif
+
+    vector_t v;
+
+    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, "");
+
+    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]; }
+    elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & ELT_MASK); }
+  };
+  static_assert (page_t::PAGE_BITS == sizeof (page_t) * 8, "");
 
   hb_object_header_t header;
   ASSERT_POD ();
   bool in_error;
+  hb_prealloced_array_t<page_map_t, 8> page_map;
+  hb_prealloced_array_t<page_t, 1> pages;
 
-  inline void init (void) {
-    hb_object_init (this);
-    clear ();
+  inline void init (void)
+  {
+    in_error = false;
+    page_map.init ();
+    pages.init ();
   }
-  inline void fini (void) {
+  inline void finish (void)
+  {
+    page_map.finish ();
+    pages.finish ();
   }
+
+  inline bool resize (unsigned int count)
+  {
+    if (unlikely (in_error)) return false;
+    if (!pages.resize (count) || !page_map.resize (count))
+    {
+      pages.resize (page_map.len);
+      in_error = true;
+      return false;
+    }
+    return true;
+  }
+
   inline void clear (void) {
     if (unlikely (hb_object_is_inert (this)))
       return;
     in_error = false;
-    memset (elts, 0, sizeof elts);
+    page_map.resize (0);
+    pages.resize (0);
   }
   inline bool is_empty (void) const {
-    for (unsigned int i = 0; i < ARRAY_LENGTH (elts); i++)
-      if (elts[i])
+    unsigned int count = pages.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!pages[i].is_empty ())
         return false;
     return true;
   }
+
   inline void add (hb_codepoint_t g)
   {
     if (unlikely (in_error)) return;
     if (unlikely (g == INVALID)) return;
-    if (unlikely (g > MAX_G)) return;
-    elt (g) |= mask (g);
+    page_t *page = page_for_insert (g); if (unlikely (!page)) return;
+    page->add (g);
   }
-  inline void add_range (hb_codepoint_t a, hb_codepoint_t b)
+  inline bool add_range (hb_codepoint_t a, hb_codepoint_t b)
+  {
+    if (unlikely (in_error)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */
+    if (unlikely (a > b || a == INVALID || b == INVALID)) return false;
+    unsigned int ma = get_major (a);
+    unsigned int mb = get_major (b);
+    if (ma == mb)
+    {
+      page_t *page = page_for_insert (a); if (unlikely (!page)) return false;
+      page->add_range (a, b);
+    }
+    else
+    {
+      page_t *page = page_for_insert (a); if (unlikely (!page)) return false;
+      page->add_range (a, major_start (ma + 1) - 1);
+
+      for (unsigned int m = ma + 1; m < mb; m++)
+      {
+	page = page_for_insert (major_start (m)); if (unlikely (!page)) return false;
+	page->init1 ();
+      }
+
+      page = page_for_insert (b); if (unlikely (!page)) return false;
+      page->add_range (major_start (mb), b);
+    }
+    return true;
+  }
+
+  template <typename T>
+  inline void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
   {
     if (unlikely (in_error)) return;
-    /* TODO Speedup */
-    for (unsigned int i = a; i < b + 1; i++)
-      add (i);
+    if (!count) return;
+    hb_codepoint_t g = *array;
+    while (count)
+    {
+      unsigned int m = get_major (g);
+      page_t *page = page_for_insert (g); if (unlikely (!page)) return;
+      unsigned int start = major_start (m);
+      unsigned int end = major_start (m + 1);
+      do
+      {
+	page->add (g);
+
+	array = (const T *) ((const char *) array + stride);
+	count--;
+      }
+      while (count && (g = *array, start <= g && g < end));
+    }
   }
+
+  /* Might return false if array looks unsorted.
+   * Used for faster rejection of corrupt data. */
+  template <typename T>
+  inline bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+  {
+    if (unlikely (in_error)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */
+    if (!count) return true;
+    hb_codepoint_t g = *array;
+    hb_codepoint_t last_g = g;
+    while (count)
+    {
+      unsigned int m = get_major (g);
+      page_t *page = page_for_insert (g); if (unlikely (!page)) return false;
+      unsigned int end = major_start (m + 1);
+      do
+      {
+        /* If we try harder we can change the following comparison to <=;
+	 * Not sure if it's worth it. */
+        if (g < last_g) return false;
+	last_g = g;
+	page->add (g);
+
+	array = (const T *) ((const char *) array + stride);
+	count--;
+      }
+      while (count && (g = *array, g < end));
+    }
+    return true;
+  }
+
   inline void del (hb_codepoint_t g)
   {
     if (unlikely (in_error)) return;
-    if (unlikely (g > MAX_G)) return;
-    elt (g) &= ~mask (g);
+    page_t *p = page_for (g);
+    if (!p)
+      return;
+    p->del (g);
   }
   inline void del_range (hb_codepoint_t a, hb_codepoint_t b)
   {
+    /* TODO Optimize, like add_range(). */
     if (unlikely (in_error)) return;
-    /* TODO Speedup */
     for (unsigned int i = a; i < b + 1; i++)
       del (i);
   }
   inline bool has (hb_codepoint_t g) const
   {
-    if (unlikely (g > MAX_G)) return false;
-    return !!(elt (g) & mask (g));
+    const page_t *p = page_for (g);
+    if (!p)
+      return false;
+    return p->has (g);
   }
   inline bool intersects (hb_codepoint_t first,
 			  hb_codepoint_t last) const
   {
-    if (unlikely (first > MAX_G)) return false;
-    if (unlikely (last  > MAX_G)) last = MAX_G;
-    unsigned int end = last + 1;
-    for (hb_codepoint_t i = first; i < end; i++)
-      if (has (i))
-        return true;
-    return false;
-  }
-  inline bool is_equal (const hb_set_t *other) const
-  {
-    for (unsigned int i = 0; i < ELTS; i++)
-      if (elts[i] != other->elts[i])
-        return false;
-    return true;
+    hb_codepoint_t c = first - 1;
+    return next (&c) && c <= last;
   }
   inline void set (const hb_set_t *other)
   {
     if (unlikely (in_error)) return;
-    for (unsigned int i = 0; i < ELTS; i++)
-      elts[i] = other->elts[i];
+    unsigned int count = other->pages.len;
+    if (!resize (count))
+      return;
+
+    memcpy (pages.array, other->pages.array, count * sizeof (pages.array[0]));
+    memcpy (page_map.array, other->page_map.array, count * sizeof (page_map.array[0]));
   }
-  inline void union_ (const hb_set_t *other)
+
+  inline bool is_equal (const hb_set_t *other) const
+  {
+    unsigned int na = pages.len;
+    unsigned int nb = other->pages.len;
+
+    unsigned int a = 0, b = 0;
+    for (; a < na && b < nb; )
+    {
+      if (page_at (a).is_empty ()) { a++; continue; }
+      if (other->page_at (b).is_empty ()) { b++; continue; }
+      if (page_map[a].major != other->page_map[b].major ||
+	  !page_at (a).is_equal (&other->page_at (b)))
+        return false;
+      a++;
+      b++;
+    }
+    for (; a < na; a++)
+      if (!page_at (a).is_empty ()) { return false; }
+    for (; b < nb; b++)
+      if (!other->page_at (b).is_empty ()) { return false; }
+
+    return true;
+  }
+
+  template <class Op>
+  inline void process (const hb_set_t *other)
   {
     if (unlikely (in_error)) return;
-    for (unsigned int i = 0; i < ELTS; i++)
-      elts[i] |= other->elts[i];
+
+    unsigned int na = pages.len;
+    unsigned int nb = other->pages.len;
+
+    unsigned int count = 0;
+    unsigned int a = 0, b = 0;
+    for (; a < na && b < nb; )
+    {
+      if (page_map[a].major == other->page_map[b].major)
+      {
+        count++;
+	a++;
+	b++;
+      }
+      else if (page_map[a].major < other->page_map[b].major)
+      {
+        if (Op::passthru_left)
+	  count++;
+        a++;
+      }
+      else
+      {
+        if (Op::passthru_right)
+	  count++;
+        b++;
+      }
+    }
+    if (Op::passthru_left)
+      count += na - a;
+    if (Op::passthru_right)
+      count += nb - b;
+
+    if (!resize (count))
+      return;
+
+    /* Process in-place backward. */
+    a = na;
+    b = nb;
+    for (; a && b; )
+    {
+      if (page_map[a - 1].major == other->page_map[b - 1].major)
+      {
+	a--;
+	b--;
+        Op::process (page_at (--count).v, page_at (a).v, other->page_at (b).v);
+      }
+      else if (page_map[a - 1].major > other->page_map[b - 1].major)
+      {
+        a--;
+        if (Op::passthru_left)
+	  page_at (--count).v = page_at (a).v;
+      }
+      else
+      {
+        b--;
+        if (Op::passthru_right)
+	  page_at (--count).v = other->page_at (b).v;
+      }
+    }
+    if (Op::passthru_left)
+      while (a)
+	page_at (--count).v = page_at (--a).v;
+    if (Op::passthru_right)
+      while (b)
+	page_at (--count).v = other->page_at (--b).v;
+    assert (!count);
+  }
+
+  inline void union_ (const hb_set_t *other)
+  {
+    process<HbOpOr> (other);
   }
   inline void intersect (const hb_set_t *other)
   {
-    if (unlikely (in_error)) return;
-    for (unsigned int i = 0; i < ELTS; i++)
-      elts[i] &= other->elts[i];
+    process<HbOpAnd> (other);
   }
   inline void subtract (const hb_set_t *other)
   {
-    if (unlikely (in_error)) return;
-    for (unsigned int i = 0; i < ELTS; i++)
-      elts[i] &= ~other->elts[i];
+    process<HbOpMinus> (other);
   }
   inline void symmetric_difference (const hb_set_t *other)
   {
-    if (unlikely (in_error)) return;
-    for (unsigned int i = 0; i < ELTS; i++)
-      elts[i] ^= other->elts[i];
-  }
-  inline void invert (void)
-  {
-    if (unlikely (in_error)) return;
-    for (unsigned int i = 0; i < ELTS; i++)
-      elts[i] = ~elts[i];
+    process<HbOpXor> (other);
   }
   inline bool next (hb_codepoint_t *codepoint) const
   {
     if (unlikely (*codepoint == INVALID)) {
-      hb_codepoint_t i = get_min ();
-      if (i != INVALID) {
-        *codepoint = i;
+      *codepoint = get_min ();
+      return *codepoint != INVALID;
+    }
+
+    page_map_t map = {get_major (*codepoint), 0};
+    unsigned int i;
+    page_map.bfind (map, &i);
+    if (i < page_map.len && page_map[i].major == map.major)
+    {
+      if (pages[page_map[i].index].next (codepoint))
+      {
+	*codepoint += page_map[i].major * page_t::PAGE_BITS;
 	return true;
-      } else {
-	*codepoint = INVALID;
-        return false;
+      }
+      i++;
+    }
+    for (; i < page_map.len; i++)
+    {
+      hb_codepoint_t m = pages[page_map[i].index].get_min ();
+      if (m != INVALID)
+      {
+	*codepoint = page_map[i].major * page_t::PAGE_BITS + m;
+	return true;
       }
     }
-    for (hb_codepoint_t i = *codepoint + 1; i < MAX_G + 1; i++)
-      if (has (i)) {
-        *codepoint = i;
+    *codepoint = INVALID;
+    return false;
+  }
+  inline bool previous (hb_codepoint_t *codepoint) const
+  {
+    if (unlikely (*codepoint == INVALID)) {
+      *codepoint = get_max ();
+      return *codepoint != INVALID;
+    }
+
+    page_map_t map = {get_major (*codepoint), 0};
+    unsigned int i;
+    page_map.bfind (map, &i);
+    if (i < page_map.len && page_map[i].major == map.major)
+    {
+      if (pages[page_map[i].index].previous (codepoint))
+      {
+	*codepoint += page_map[i].major * page_t::PAGE_BITS;
 	return true;
       }
+    }
+    i--;
+    for (; (int) i >= 0; i--)
+    {
+      hb_codepoint_t m = pages[page_map[i].index].get_max ();
+      if (m != INVALID)
+      {
+	*codepoint = page_map[i].major * page_t::PAGE_BITS + m;
+	return true;
+      }
+    }
     *codepoint = INVALID;
     return false;
   }
@@ -294,108 +543,94 @@
       return false;
     }
 
+    /* TODO Speed up. */
     *last = *first = i;
     while (next (&i) && i == *last + 1)
       (*last)++;
 
     return true;
   }
+  inline bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const
+  {
+    hb_codepoint_t i;
+
+    i = *first;
+    if (!previous (&i))
+    {
+      *last = *first = INVALID;
+      return false;
+    }
+
+    /* TODO Speed up. */
+    *last = *first = i;
+    while (previous (&i) && i == *first - 1)
+      (*first)--;
+
+    return true;
+  }
 
   inline unsigned int get_population (void) const
   {
-    unsigned int count = 0;
-    for (unsigned int i = 0; i < ELTS; i++)
-      count += _hb_popcount32 (elts[i]);
-    return count;
+    unsigned int pop = 0;
+    unsigned int count = pages.len;
+    for (unsigned int i = 0; i < count; i++)
+      pop += pages[i].get_population ();
+    return pop;
   }
   inline hb_codepoint_t get_min (void) const
   {
-    for (unsigned int i = 0; i < ELTS; i++)
-      if (elts[i])
-	for (unsigned int j = 0; j < BITS; j++)
-	  if (elts[i] & (1u << j))
-	    return i * BITS + j;
+    unsigned int count = pages.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!page_at (i).is_empty ())
+        return page_map[i].major * page_t::PAGE_BITS + page_at (i).get_min ();
     return INVALID;
   }
   inline hb_codepoint_t get_max (void) const
   {
-    for (unsigned int i = ELTS; i; i--)
-      if (elts[i - 1])
-	for (unsigned int j = BITS; j; j--)
-	  if (elts[i - 1] & (1u << (j - 1)))
-	    return (i - 1) * BITS + (j - 1);
+    unsigned int count = pages.len;
+    for (int i = count - 1; i >= 0; i++)
+      if (!page_at (i).is_empty ())
+        return page_map[i].major * page_t::PAGE_BITS + page_at (i).get_max ();
     return INVALID;
   }
 
-  typedef uint32_t elt_t;
-  static const unsigned int MAX_G = 65536 - 1; /* XXX Fix this... */
-  static const unsigned int SHIFT = 5;
-  static const unsigned int BITS = (1 << SHIFT);
-  static const unsigned int MASK = BITS - 1;
-  static const unsigned int ELTS = (MAX_G + 1 + (BITS - 1)) / BITS;
   static  const hb_codepoint_t INVALID = HB_SET_VALUE_INVALID;
 
-  elt_t &elt (hb_codepoint_t g) { return elts[g >> SHIFT]; }
-  elt_t const &elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; }
-  elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & MASK); }
-
-  elt_t elts[ELTS]; /* XXX 8kb */
-
-  ASSERT_STATIC (sizeof (elt_t) * 8 == BITS);
-  ASSERT_STATIC (sizeof (elt_t) * 8 * ELTS > MAX_G);
-};
-
-struct hb_frozen_set_t
-{
-  static const unsigned int SHIFT = hb_set_t::SHIFT;
-  static const unsigned int BITS = hb_set_t::BITS;
-  static const unsigned int MASK = hb_set_t::MASK;
-  typedef hb_set_t::elt_t elt_t;
-
-  inline void init (const hb_set_t &set)
+  inline page_t *page_for_insert (hb_codepoint_t g)
   {
-    start = count = 0;
-    elts = NULL;
-
-    unsigned int max = set.get_max ();
-    if (max == set.INVALID)
-      return;
-    unsigned int min = set.get_min ();
-    const elt_t &min_elt = set.elt (min);
-
-    start = min & ~MASK;
-    count = max - start + 1;
-    unsigned int num_elts = (count + BITS - 1) / BITS;
-    unsigned int elts_size = num_elts * sizeof (elt_t);
-    elts = (elt_t *) malloc (elts_size);
-    if (unlikely (!elts))
+    page_map_t map = {get_major (g), pages.len};
+    unsigned int i;
+    if (!page_map.bfind (map, &i))
     {
-      start = count = 0;
-      return;
+      if (!resize (pages.len + 1))
+	return nullptr;
+
+      pages[map.index].init0 ();
+      memmove (&page_map[i + 1], &page_map[i], (page_map.len - 1 - i) * sizeof (page_map[0]));
+      page_map[i] = map;
     }
-    memcpy (elts, &min_elt, elts_size);
+    return &pages[page_map[i].index];
   }
-
-  inline void fini (void)
+  inline page_t *page_for (hb_codepoint_t g)
   {
-    if (elts)
-      free (elts);
+    page_map_t key = {get_major (g)};
+    const page_map_t *found = page_map.bsearch (key);
+    if (found)
+      return &pages[found->index];
+    return nullptr;
   }
-
-  inline bool has (hb_codepoint_t g) const
+  inline const page_t *page_for (hb_codepoint_t g) const
   {
-    /* hb_codepoint_t is unsigned. */
-    g -= start;
-    if (unlikely (g > count)) return false;
-    return !!(elt (g) & mask (g));
+    page_map_t key = {get_major (g)};
+    const page_map_t *found = page_map.bsearch (key);
+    if (found)
+      return &pages[found->index];
+    return nullptr;
   }
-
-  elt_t const &elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; }
-  elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & MASK); }
-
-  private:
-  hb_codepoint_t start, count;
-  elt_t *elts;
+  inline page_t &page_at (unsigned int i) { return pages[page_map[i].index]; }
+  inline const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; }
+  inline unsigned int get_major (hb_codepoint_t g) const { return g / page_t::PAGE_BITS; }
+  inline hb_codepoint_t major_start (unsigned int major) const { return major * page_t::PAGE_BITS; }
 };
 
 
diff --git a/src/hb-set.cc b/src/hb-set.cc
index f3fe1ba..07cf9d0 100644
--- a/src/hb-set.cc
+++ b/src/hb-set.cc
@@ -45,11 +45,18 @@
   if (!(set = hb_object_create<hb_set_t> ()))
     return hb_set_get_empty ();
 
-  set->clear ();
+  set->init ();
 
   return set;
 }
 
+static const hb_set_t _hb_set_nil = {
+  HB_OBJECT_HEADER_STATIC,
+  true, /* in_error */
+
+  {0} /* elts */
+};
+
 /**
  * hb_set_get_empty:
  *
@@ -60,13 +67,6 @@
 hb_set_t *
 hb_set_get_empty (void)
 {
-  static const hb_set_t _hb_set_nil = {
-    HB_OBJECT_HEADER_STATIC,
-    true, /* in_error */
-
-    {0} /* elts */
-  };
-
   return const_cast<hb_set_t *> (&_hb_set_nil);
 }
 
@@ -95,7 +95,7 @@
 {
   if (!hb_object_destroy (set)) return;
 
-  set->fini ();
+  set->finish ();
 
   free (set);
 }
@@ -376,11 +376,12 @@
  * 
  *
  * Since: 0.9.10
+ *
+ * Deprecated: 1.6.1
  **/
 void
 hb_set_invert (hb_set_t *set)
 {
-  set->invert ();
 }
 
 /**
@@ -436,7 +437,9 @@
  * @set: a set.
  * @codepoint: (inout):
  *
- * 
+ * Gets the next number in @set that is greater than current value of @codepoint.
+ *
+ * Set @codepoint to %HB_SET_VALUE_INVALID to get started.
  *
  * Return value: whether there was a next value.
  *
@@ -450,6 +453,26 @@
 }
 
 /**
+ * hb_set_previous:
+ * @set: a set.
+ * @codepoint: (inout):
+ *
+ * Gets the previous number in @set that is slower than current value of @codepoint.
+ *
+ * Set @codepoint to %HB_SET_VALUE_INVALID to get started.
+ *
+ * Return value: whether there was a previous value.
+ *
+ * Since: 1.8.0
+ **/
+hb_bool_t
+hb_set_previous (const hb_set_t *set,
+		 hb_codepoint_t *codepoint)
+{
+  return set->previous (codepoint);
+}
+
+/**
  * hb_set_next_range:
  * @set: a set.
  * @first: (out): output first codepoint in the range.
@@ -458,6 +481,8 @@
  * Gets the next consecutive range of numbers in @set that
  * are greater than current value of @last.
  *
+ * Set @last to %HB_SET_VALUE_INVALID to get started.
+ *
  * Return value: whether there was a next range.
  *
  * Since: 0.9.7
@@ -469,3 +494,26 @@
 {
   return set->next_range (first, last);
 }
+
+/**
+ * hb_set_previous_range:
+ * @set: a set.
+ * @first: (inout): input current first and output first codepoint in the range.
+ * @last: (out): output last codepoint in the range.
+ *
+ * Gets the previous consecutive range of numbers in @set that
+ * are greater than current value of @last.
+ *
+ * Set @first to %HB_SET_VALUE_INVALID to get started.
+ *
+ * Return value: whether there was a previous range.
+ *
+ * Since: 1.8.0
+ **/
+hb_bool_t
+hb_set_previous_range (const hb_set_t *set,
+		       hb_codepoint_t *first,
+		       hb_codepoint_t *last)
+{
+  return set->previous_range (first, last);
+}
diff --git a/src/hb-set.h b/src/hb-set.h
index 2164c1a..b0f82f8 100644
--- a/src/hb-set.h
+++ b/src/hb-set.h
@@ -126,31 +126,39 @@
 hb_set_symmetric_difference (hb_set_t       *set,
 			     const hb_set_t *other);
 
-HB_EXTERN void
-hb_set_invert (hb_set_t *set);
-
 HB_EXTERN unsigned int
 hb_set_get_population (const hb_set_t *set);
 
-/* Returns -1 if set empty. */
+/* Returns HB_SET_VALUE_INVALID if set empty. */
 HB_EXTERN hb_codepoint_t
 hb_set_get_min (const hb_set_t *set);
 
-/* Returns -1 if set empty. */
+/* Returns HB_SET_VALUE_INVALID if set empty. */
 HB_EXTERN hb_codepoint_t
 hb_set_get_max (const hb_set_t *set);
 
-/* Pass -1 in to get started. */
+/* Pass HB_SET_VALUE_INVALID in to get started. */
 HB_EXTERN hb_bool_t
 hb_set_next (const hb_set_t *set,
 	     hb_codepoint_t *codepoint);
 
-/* Pass -1 for first and last to get started. */
+/* Pass HB_SET_VALUE_INVALID in to get started. */
+HB_EXTERN hb_bool_t
+hb_set_previous (const hb_set_t *set,
+		 hb_codepoint_t *codepoint);
+
+/* Pass HB_SET_VALUE_INVALID for first and last to get started. */
 HB_EXTERN hb_bool_t
 hb_set_next_range (const hb_set_t *set,
 		   hb_codepoint_t *first,
 		   hb_codepoint_t *last);
 
+/* Pass HB_SET_VALUE_INVALID for first and last to get started. */
+HB_EXTERN hb_bool_t
+hb_set_previous_range (const hb_set_t *set,
+		       hb_codepoint_t *first,
+		       hb_codepoint_t *last);
+
 
 HB_END_DECLS
 
diff --git a/src/hb-shape-plan.cc b/src/hb-shape-plan.cc
index 1ac77be..6eeba2b 100644
--- a/src/hb-shape-plan.cc
+++ b/src/hb-shape-plan.cc
@@ -24,17 +24,14 @@
  * Google Author(s): Behdad Esfahbod
  */
 
+#include "hb-private.hh"
+#include "hb-debug.hh"
 #include "hb-shape-plan-private.hh"
 #include "hb-shaper-private.hh"
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 
 
-#ifndef HB_DEBUG_SHAPE_PLAN
-#define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0)
-#endif
-
-
 static void
 hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
 		    const hb_feature_t *user_features,
@@ -115,7 +112,7 @@
 {
   return hb_shape_plan_create2 (face, props,
 				user_features, num_user_features,
-				NULL, 0,
+				nullptr, 0,
 				shaper_list);
 }
 
@@ -128,7 +125,7 @@
 		       unsigned int                   num_coords,
 		       const char * const            *shaper_list)
 {
-  DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
+  DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
 		  "face=%p num_features=%d num_coords=%d shaper_list=%p",
 		  face,
 		  num_user_features,
@@ -136,8 +133,8 @@
 		  shaper_list);
 
   hb_shape_plan_t *shape_plan;
-  hb_feature_t *features = NULL;
-  int *coords = NULL;
+  hb_feature_t *features = nullptr;
+  int *coords = nullptr;
 
   if (unlikely (!face))
     face = hb_face_get_empty ();
@@ -160,7 +157,7 @@
   assert (props->direction != HB_DIRECTION_INVALID);
 
   hb_face_make_immutable (face);
-  shape_plan->default_shaper_list = shaper_list == NULL;
+  shape_plan->default_shaper_list = !shaper_list;
   shape_plan->face_unsafe = face;
   shape_plan->props = *props;
   shape_plan->num_user_features = num_user_features;
@@ -196,16 +193,16 @@
     HB_OBJECT_HEADER_STATIC,
 
     true, /* default_shaper_list */
-    NULL, /* face */
+    nullptr, /* face */
     HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
 
-    NULL, /* shaper_func */
-    NULL, /* shaper_name */
+    nullptr, /* shaper_func */
+    nullptr, /* shaper_name */
 
-    NULL, /* user_features */
+    nullptr, /* user_features */
     0,    /* num_user_featurs */
 
-    NULL, /* coords */
+    nullptr, /* coords */
     0,    /* num_coords */
 
     {
@@ -423,7 +420,7 @@
   return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
 	 hb_shape_plan_user_features_match (shape_plan, proposal) &&
 	 hb_shape_plan_coords_match (shape_plan, proposal) &&
-	 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
+	 ((shape_plan->default_shaper_list && !proposal->shaper_list) ||
 	  (shape_plan->shaper_func == proposal->shaper_func));
 }
 
@@ -431,11 +428,12 @@
 hb_non_global_user_features_present (const hb_feature_t *user_features,
 				     unsigned int        num_user_features)
 {
-  while (num_user_features)
+  while (num_user_features) {
     if (user_features->start != 0 || user_features->end != (unsigned int) -1)
       return true;
-    else
-      num_user_features--, user_features++;
+    num_user_features--;
+    user_features++;
+  }
   return false;
 }
 
@@ -469,7 +467,7 @@
 {
   return hb_shape_plan_create_cached2 (face, props,
 				       user_features, num_user_features,
-				       NULL, 0,
+				       nullptr, 0,
 				       shaper_list);
 }
 
@@ -482,7 +480,7 @@
 			      unsigned int                   num_coords,
 			      const char * const            *shaper_list)
 {
-  DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
+  DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
 		  "face=%p num_features=%d shaper_list=%p",
 		  face,
 		  num_user_features,
@@ -493,7 +491,7 @@
     shaper_list,
     user_features,
     num_user_features,
-    NULL
+    nullptr
   };
 
   if (shaper_list) {
@@ -519,15 +517,17 @@
 
 retry:
   hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
-  for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
-    if (hb_shape_plan_matches (node->shape_plan, &proposal))
-    {
-      DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
-      return hb_shape_plan_reference (node->shape_plan);
-    }
+
+  /* Don't look for plan in the cache if there were variation coordinates XXX Fix me. */
+  if (!hb_coords_present (coords, num_coords))
+    for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
+      if (hb_shape_plan_matches (node->shape_plan, &proposal))
+      {
+        DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
+        return hb_shape_plan_reference (node->shape_plan);
+      }
 
   /* Not found. */
-
   hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props,
 						       user_features, num_user_features,
 						       coords, num_coords,
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index f080a15..39355b3 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -76,7 +76,7 @@
     /* Not found; allocate one. */
     shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *));
     if (unlikely (!shaper_list)) {
-      static const char *nil_shaper_list[] = {NULL};
+      static const char *nil_shaper_list[] = {nullptr};
       return nil_shaper_list;
     }
 
@@ -84,9 +84,9 @@
     unsigned int i;
     for (i = 0; i < HB_SHAPERS_COUNT; i++)
       shaper_list[i] = shapers[i].name;
-    shaper_list[i] = NULL;
+    shaper_list[i] = nullptr;
 
-    if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) {
+    if (!hb_atomic_ptr_cmpexch (&static_shaper_list, nullptr, shaper_list)) {
       free (shaper_list);
       goto retry;
     }
@@ -157,5 +157,5 @@
 	  const hb_feature_t  *features,
 	  unsigned int         num_features)
 {
-  hb_shape_full (font, buffer, features, num_features, NULL);
+  hb_shape_full (font, buffer, features, num_features, nullptr);
 }
diff --git a/src/hb-shaper-private.hh b/src/hb-shaper-private.hh
index 381398a..ce2d9f2 100644
--- a/src/hb-shaper-private.hh
+++ b/src/hb-shaper-private.hh
@@ -88,16 +88,28 @@
         HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data);
 
 #define HB_SHAPER_DATA_ENSURE_DEFINE(shaper, object) \
+	HB_SHAPER_DATA_ENSURE_DEFINE_WITH_CONDITION(shaper, object, true)
+
+#define HB_SHAPER_DATA_ENSURE_DEFINE_WITH_CONDITION(shaper, object, condition) \
 bool \
 HB_SHAPER_DATA_ENSURE_FUNC(shaper, object) (hb_##object##_t *object) \
 {\
   retry: \
   HB_SHAPER_DATA_TYPE (shaper, object) *data = (HB_SHAPER_DATA_TYPE (shaper, object) *) hb_atomic_ptr_get (&HB_SHAPER_DATA (shaper, object)); \
+  if (likely (data) && !(condition)) { \
+    /* Drop and recreate. */ \
+    /* If someone dropped it in the mean time, throw it away and don't touch it. \
+     * Otherwise, destruct it. */ \
+    if (hb_atomic_ptr_cmpexch (&HB_SHAPER_DATA (shaper, object), data, nullptr)) { \
+      HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data); \
+    } \
+    goto retry; \
+  } \
   if (unlikely (!data)) { \
     data = HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (object); \
     if (unlikely (!data)) \
       data = (HB_SHAPER_DATA_TYPE (shaper, object) *) HB_SHAPER_DATA_INVALID; \
-    if (!hb_atomic_ptr_cmpexch (&HB_SHAPER_DATA (shaper, object), NULL, data)) { \
+    if (!hb_atomic_ptr_cmpexch (&HB_SHAPER_DATA (shaper, object), nullptr, data)) { \
       if (data && \
 	  data != HB_SHAPER_DATA_INVALID && \
 	  data != HB_SHAPER_DATA_SUCCEEDED) \
@@ -105,7 +117,7 @@
       goto retry; \
     } \
   } \
-  return data != NULL && !HB_SHAPER_DATA_IS_INVALID (data); \
+  return data != nullptr && !HB_SHAPER_DATA_IS_INVALID (data); \
 }
 
 
diff --git a/src/hb-shaper.cc b/src/hb-shaper.cc
index b25566d..2c44cf2 100644
--- a/src/hb-shaper.cc
+++ b/src/hb-shaper.cc
@@ -59,14 +59,14 @@
   {
     char *env = getenv ("HB_SHAPER_LIST");
     if (!env || !*env) {
-      (void) hb_atomic_ptr_cmpexch (&static_shapers, NULL, &all_shapers[0]);
+      (void) hb_atomic_ptr_cmpexch (&static_shapers, nullptr, &all_shapers[0]);
       return (const hb_shaper_pair_t *) all_shapers;
     }
 
     /* Not found; allocate one. */
     shapers = (hb_shaper_pair_t *) calloc (1, sizeof (all_shapers));
     if (unlikely (!shapers)) {
-      (void) hb_atomic_ptr_cmpexch (&static_shapers, NULL, &all_shapers[0]);
+      (void) hb_atomic_ptr_cmpexch (&static_shapers, nullptr, &all_shapers[0]);
       return (const hb_shaper_pair_t *) all_shapers;
     }
 
@@ -97,7 +97,7 @@
 	p = end + 1;
     }
 
-    if (!hb_atomic_ptr_cmpexch (&static_shapers, NULL, shapers)) {
+    if (!hb_atomic_ptr_cmpexch (&static_shapers, nullptr, shapers)) {
       free (shapers);
       goto retry;
     }
diff --git a/src/hb-string-array.hh b/src/hb-string-array.hh
new file mode 100644
index 0000000..ba829b0
--- /dev/null
+++ b/src/hb-string-array.hh
@@ -0,0 +1,81 @@
+/*
+ * 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_STRING_ARRAY_HH
+#if 0 /* Make checks happy. */
+#define HB_STRING_ARRAY_HH
+#endif
+
+#include "hb-private.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 */
+
+#define HB_STRING_ARRAY_TYPE_NAME	HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr_t)
+#define HB_STRING_ARRAY_POOL_NAME	HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr)
+#define HB_STRING_ARRAY_OFFS_NAME	HB_PASTE(HB_STRING_ARRAY_NAME, _msgidx)
+
+static const union HB_STRING_ARRAY_TYPE_NAME {
+  struct {
+/* I like to avoid storing the nul-termination byte since we don't need it,
+ * but C++ does not allow that.
+ * https://stackoverflow.com/questions/28433862/why-initializer-string-for-array-of-chars-is-too-long-compiles-fine-in-c-not
+ */
+#define _S(s) char HB_PASTE (str, __LINE__)[sizeof (s)];
+#include HB_STRING_ARRAY_LIST
+#undef _S
+  } st;
+  char str[VAR];
+}
+HB_STRING_ARRAY_POOL_NAME =
+{
+  {
+#define _S(s) s,
+#include HB_STRING_ARRAY_LIST
+#undef _S
+  }
+};
+static const unsigned int HB_STRING_ARRAY_OFFS_NAME[] =
+{
+#define _S(s) offsetof (union HB_STRING_ARRAY_TYPE_NAME, st.HB_PASTE(str, __LINE__)),
+#include HB_STRING_ARRAY_LIST
+#undef _S
+  sizeof (HB_STRING_ARRAY_TYPE_NAME)
+};
+
+static inline hb_string_t
+HB_STRING_ARRAY_NAME (unsigned int i)
+{
+  assert (i < ARRAY_LENGTH (HB_STRING_ARRAY_OFFS_NAME) - 1);
+  return hb_string_t (HB_STRING_ARRAY_POOL_NAME.str + HB_STRING_ARRAY_OFFS_NAME[i],
+		      HB_STRING_ARRAY_OFFS_NAME[i + 1] - HB_STRING_ARRAY_OFFS_NAME[i] - 1);
+}
+
+#undef HB_STRING_ARRAY_TYPE_NAME
+#undef HB_STRING_ARRAY_POOL_NAME
+#undef HB_STRING_ARRAY_OFFS_NAME
+
+#endif /* HB_STRING_ARRAY_HH */
diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc
new file mode 100644
index 0000000..d29efe9
--- /dev/null
+++ b/src/hb-subset-glyf.cc
@@ -0,0 +1,242 @@
+/*
+ * 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
+ */
+
+#include "hb-open-type-private.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,
+                                     hb_prealloced_array_t<hb_codepoint_t> &glyph_ids,
+                                     bool *use_short_loca, /* OUT */
+                                     unsigned int *glyf_size, /* OUT */
+                                     unsigned int *loca_size /* OUT */)
+{
+  unsigned int total = 0;
+  unsigned int count = 0;
+  for (unsigned int i = 0; i < glyph_ids.len; i++)
+  {
+    hb_codepoint_t next_glyph = glyph_ids[i];
+    unsigned int start_offset, end_offset;
+    if (unlikely (!glyf.get_offsets (next_glyph, &start_offset, &end_offset)))
+      end_offset = start_offset = 0;
+
+    total += end_offset - start_offset;
+    count++;
+  }
+
+  *glyf_size = total;
+  *use_short_loca = (total <= 131070);
+  *loca_size = (count + 1)
+      * (*use_short_loca ? sizeof(OT::HBUINT16) : sizeof(OT::HBUINT32));
+
+  DEBUG_MSG(SUBSET, nullptr, "preparing to subset glyf: final size %d, loca size %d, using %s loca",
+            total,
+            *loca_size,
+            *use_short_loca ? "short" : "long");
+  return true;
+}
+
+static bool
+_write_loca_entry (unsigned int  id,
+                   unsigned int  offset,
+                   bool          is_short,
+                   void         *loca_prime,
+                   unsigned int  loca_size)
+{
+  unsigned int entry_size = is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32);
+  if ((id + 1) * entry_size <= loca_size)
+  {
+    if (is_short) {
+      ((OT::HBUINT16*) loca_prime) [id].set (offset / 2);
+    } else {
+      ((OT::HBUINT32*) loca_prime) [id].set (offset);
+    }
+    return true;
+  }
+
+  // Offset was not written because the write is out of bounds.
+  DEBUG_MSG (SUBSET,
+             nullptr,
+             "WARNING: Attempted to write an out of bounds loca entry at index %d. Loca size is %d.",
+             id,
+             loca_size);
+  return false;
+}
+
+static void
+_update_components (hb_subset_plan_t * plan,
+		    char * glyph_start,
+		    unsigned int length)
+
+{
+  OT::glyf::CompositeGlyphHeader::Iterator iterator;
+  if (OT::glyf::CompositeGlyphHeader::get_iterator (glyph_start,
+						    length,
+						    &iterator))
+  {
+    do
+    {
+      hb_codepoint_t new_gid;
+      if (!hb_subset_plan_new_gid_for_old_id (plan,
+					      iterator.current->glyphIndex,
+					      &new_gid))
+	continue;
+
+      ((OT::glyf::CompositeGlyphHeader *) iterator.current)->glyphIndex.set (new_gid);
+    } while (iterator.move_to_next());
+  }
+}
+
+static bool
+_write_glyf_and_loca_prime (hb_subset_plan_t              *plan,
+			    const OT::glyf::accelerator_t &glyf,
+                            const char                    *glyf_data,
+                            bool                           use_short_loca,
+                            unsigned int                   glyf_prime_size,
+                            char                          *glyf_prime_data /* OUT */,
+                            unsigned int                   loca_prime_size,
+                            char                          *loca_prime_data /* OUT */)
+{
+  hb_prealloced_array_t<hb_codepoint_t> &glyph_ids = plan->gids_to_retain_sorted;
+  char *glyf_prime_data_next = glyf_prime_data;
+
+  bool success = true;
+  for (unsigned int i = 0; i < glyph_ids.len; i++)
+  {
+    unsigned int start_offset, end_offset;
+    if (unlikely (!glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset)))
+      end_offset = start_offset = 0;
+
+    int length = end_offset - start_offset;
+
+    if (glyf_prime_data_next + length > glyf_prime_data + glyf_prime_size)
+    {
+      DEBUG_MSG (SUBSET,
+                 nullptr,
+                 "WARNING: Attempted to write an out of bounds glyph entry for gid %d",
+                 i);
+      return false;
+    }
+    memcpy (glyf_prime_data_next, glyf_data + start_offset, length);
+
+    success = success && _write_loca_entry (i,
+                                            glyf_prime_data_next - glyf_prime_data,
+                                            use_short_loca,
+                                            loca_prime_data,
+                                            loca_prime_size);
+    _update_components (plan, glyf_prime_data_next, end_offset - start_offset);
+
+    glyf_prime_data_next += length;
+  }
+
+  success = success && _write_loca_entry (glyph_ids.len,
+                                          glyf_prime_data_next - glyf_prime_data,
+                                          use_short_loca,
+                                          loca_prime_data,
+                                          loca_prime_size);
+  return success;
+}
+
+static bool
+_hb_subset_glyf_and_loca (const OT::glyf::accelerator_t  &glyf,
+                          const char                     *glyf_data,
+			  hb_subset_plan_t               *plan,
+                          bool                           *use_short_loca,
+                          hb_blob_t                     **glyf_prime /* OUT */,
+                          hb_blob_t                     **loca_prime /* OUT */)
+{
+  // TODO(grieger): Sanity check allocation size for the new table.
+  hb_prealloced_array_t<hb_codepoint_t> &glyphs_to_retain = plan->gids_to_retain_sorted;
+
+  unsigned int glyf_prime_size;
+  unsigned int loca_prime_size;
+
+  if (unlikely (!_calculate_glyf_and_loca_prime_size (glyf,
+                                                      glyphs_to_retain,
+                                                      use_short_loca,
+                                                      &glyf_prime_size,
+                                                      &loca_prime_size))) {
+    return false;
+  }
+
+  char *glyf_prime_data = (char *) malloc (glyf_prime_size);
+  char *loca_prime_data = (char *) malloc (loca_prime_size);
+  if (unlikely (!_write_glyf_and_loca_prime (plan, glyf, glyf_data,
+                                             *use_short_loca,
+                                             glyf_prime_size, glyf_prime_data,
+                                             loca_prime_size, loca_prime_data))) {
+    free (glyf_prime_data);
+    free (loca_prime_data);
+    return false;
+  }
+
+  *glyf_prime = hb_blob_create (glyf_prime_data,
+                                glyf_prime_size,
+                                HB_MEMORY_MODE_READONLY,
+                                glyf_prime_data,
+                                free);
+  *loca_prime = hb_blob_create (loca_prime_data,
+                                loca_prime_size,
+                                HB_MEMORY_MODE_READONLY,
+                                loca_prime_data,
+                                free);
+  return true;
+}
+
+/**
+ * hb_subset_glyf:
+ * Subsets the glyph table according to a provided plan.
+ *
+ * Return value: subsetted glyf table.
+ *
+ * Since: 1.7.5
+ **/
+bool
+hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
+                         bool             *use_short_loca, /* OUT */
+                         hb_blob_t       **glyf_prime, /* OUT */
+                         hb_blob_t       **loca_prime /* OUT */)
+{
+  hb_blob_t *glyf_blob = OT::Sanitizer<OT::glyf>().sanitize (plan->source->reference_table (HB_OT_TAG_glyf));
+  const char *glyf_data = hb_blob_get_data(glyf_blob, nullptr);
+
+  OT::glyf::accelerator_t glyf;
+  glyf.init(plan->source);
+  bool result = _hb_subset_glyf_and_loca (glyf,
+                                          glyf_data,
+                                          plan,
+                                          use_short_loca,
+                                          glyf_prime,
+                                          loca_prime);
+
+  hb_blob_destroy (glyf_blob);
+  glyf.fini();
+
+  return result;
+}
diff --git a/src/hb-subset-glyf.hh b/src/hb-subset-glyf.hh
new file mode 100644
index 0000000..99b76db
--- /dev/null
+++ b/src/hb-subset-glyf.hh
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2018  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#ifndef HB_SUBSET_GLYF_HH
+#define HB_SUBSET_GLYF_HH
+
+#include "hb-private.hh"
+
+#include "hb-subset-plan.hh"
+
+HB_INTERNAL bool
+hb_subset_glyf_and_loca (hb_subset_plan_t *plan,
+                         bool             *use_short_loca, /* OUT */
+                         hb_blob_t       **glyf_prime /* OUT */,
+                         hb_blob_t       **loca_prime /* OUT */);
+
+#endif /* HB_SUBSET_GLYF_HH */
diff --git a/src/hb-subset-input.cc b/src/hb-subset-input.cc
new file mode 100644
index 0000000..d41cff6
--- /dev/null
+++ b/src/hb-subset-input.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2018  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Garret Rieger, Rod Sheeter, Behdad Esfahbod
+ */
+
+#include "hb-object-private.hh"
+#include "hb-subset-private.hh"
+#include "hb-set-private.hh"
+
+/**
+ * hb_subset_input_create_or_fail:
+ *
+ * Return value: New subset input.
+ *
+ * Since: 1.8.0
+ **/
+hb_subset_input_t *
+hb_subset_input_create_or_fail (void)
+{
+  hb_subset_input_t *input = hb_object_create<hb_subset_input_t>();
+
+  if (unlikely (!input))
+    return nullptr;
+
+  input->unicodes = hb_set_create ();
+  input->glyphs = hb_set_create ();
+
+  return input;
+}
+
+/**
+ * hb_subset_input_reference: (skip)
+ * @subset_input: a subset_input.
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 1.8.0
+ **/
+hb_subset_input_t *
+hb_subset_input_reference (hb_subset_input_t *subset_input)
+{
+  return hb_object_reference (subset_input);
+}
+
+/**
+ * hb_subset_input_destroy:
+ * @subset_input: a subset_input.
+ *
+ * Since: 1.8.0
+ **/
+void
+hb_subset_input_destroy(hb_subset_input_t *subset_input)
+{
+  if (!hb_object_destroy (subset_input)) return;
+
+  hb_set_destroy (subset_input->unicodes);
+  hb_set_destroy (subset_input->glyphs);
+
+  free (subset_input);
+}
+
+/**
+ * hb_subset_input_unicode_set:
+ * @subset_input: a subset_input.
+ *
+ * Since: 1.8.0
+ **/
+HB_EXTERN hb_set_t *
+hb_subset_input_unicode_set (hb_subset_input_t *subset_input)
+{
+  return subset_input->unicodes;
+}
+
+/**
+ * hb_subset_input_glyph_set:
+ * @subset_input: a subset_input.
+ *
+ * Since: 1.8.0
+ **/
+HB_EXTERN hb_set_t *
+hb_subset_input_glyph_set (hb_subset_input_t *subset_input)
+{
+  return subset_input->glyphs;
+}
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
new file mode 100644
index 0000000..27c2c7f
--- /dev/null
+++ b/src/hb-subset-plan.cc
@@ -0,0 +1,223 @@
+/*
+ * 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
+ */
+
+#include "hb-subset-private.hh"
+
+#include "hb-subset-plan.hh"
+#include "hb-ot-cmap-table.hh"
+#include "hb-ot-glyf-table.hh"
+
+static int
+_hb_codepoint_t_cmp (const void *pa, const void *pb)
+{
+  hb_codepoint_t a = * (hb_codepoint_t *) pa;
+  hb_codepoint_t b = * (hb_codepoint_t *) pb;
+
+  return a < b ? -1 : a > b ? +1 : 0;
+}
+
+hb_bool_t
+hb_subset_plan_new_gid_for_codepoint (hb_subset_plan_t *plan,
+                                      hb_codepoint_t codepoint,
+                                      hb_codepoint_t *new_gid)
+{
+  // TODO actual map, delete this garbage.
+  for (unsigned int i = 0; i < plan->codepoints.len; i++)
+  {
+    if (plan->codepoints[i] != codepoint) continue;
+    if (!hb_subset_plan_new_gid_for_old_id(plan, plan->gids_to_retain[i], new_gid))
+    {
+      return false;
+    }
+    return true;
+  }
+  return false;
+}
+
+hb_bool_t
+hb_subset_plan_new_gid_for_old_id (hb_subset_plan_t *plan,
+                                   hb_codepoint_t old_gid,
+                                   hb_codepoint_t *new_gid)
+{
+  // the index in old_gids is the new gid; only up to codepoints.len are valid
+  for (unsigned int i = 0; i < plan->gids_to_retain_sorted.len; i++)
+  {
+    if (plan->gids_to_retain_sorted[i] == old_gid)
+    {
+      *new_gid = i;
+      return true;
+    }
+  }
+  return false;
+}
+
+hb_bool_t
+hb_subset_plan_add_table (hb_subset_plan_t *plan,
+                          hb_tag_t tag,
+                          hb_blob_t *contents)
+{
+  return hb_subset_face_add_table(plan->dest, tag, contents);
+}
+
+static void
+_populate_codepoints (hb_set_t *input_codepoints,
+                      hb_prealloced_array_t<hb_codepoint_t>& plan_codepoints)
+{
+  plan_codepoints.alloc (hb_set_get_population (input_codepoints));
+  hb_codepoint_t cp = -1;
+  while (hb_set_next (input_codepoints, &cp)) {
+    hb_codepoint_t *wr = plan_codepoints.push();
+    *wr = cp;
+  }
+  plan_codepoints.qsort (_hb_codepoint_t_cmp);
+}
+
+static void
+_add_gid_and_children (const OT::glyf::accelerator_t &glyf,
+		       hb_codepoint_t gid,
+		       hb_set_t *gids_to_retain)
+{
+  if (hb_set_has (gids_to_retain, gid))
+    // Already visited this gid, ignore.
+    return;
+
+  hb_set_add (gids_to_retain, gid);
+
+  OT::glyf::CompositeGlyphHeader::Iterator composite;
+  if (glyf.get_composite (gid, &composite))
+  {
+    do
+    {
+      _add_gid_and_children (glyf, (hb_codepoint_t) composite.current->glyphIndex, gids_to_retain);
+    } while (composite.move_to_next());
+  }
+}
+
+static void
+_populate_gids_to_retain (hb_face_t *face,
+                          hb_prealloced_array_t<hb_codepoint_t>& codepoints,
+                          hb_prealloced_array_t<hb_codepoint_t>& old_gids,
+                          hb_prealloced_array_t<hb_codepoint_t>& old_gids_sorted)
+{
+  OT::cmap::accelerator_t cmap;
+  OT::glyf::accelerator_t glyf;
+  cmap.init (face);
+  glyf.init (face);
+
+  hb_auto_array_t<unsigned int> bad_indices;
+
+  old_gids.alloc (codepoints.len);
+  for (unsigned int i = 0; i < codepoints.len; i++)
+  {
+    hb_codepoint_t gid;
+    if (!cmap.get_nominal_glyph (codepoints[i], &gid))
+    {
+      gid = -1;
+      *(bad_indices.push ()) = i;
+    }
+    *(old_gids.push ()) = gid;
+  }
+
+  /* Generally there shouldn't be any */
+  while (bad_indices.len > 0)
+  {
+    unsigned int i = bad_indices[bad_indices.len - 1];
+    bad_indices.pop ();
+    DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", codepoints[i]);
+    codepoints.remove (i);
+    old_gids.remove (i);
+  }
+
+  // Populate a full set of glyphs to retain by adding all referenced
+  // composite glyphs.
+  // TODO expand with glyphs reached by G*
+  hb_set_t * all_gids_to_retain = hb_set_create ();
+  _add_gid_and_children (glyf, 0, all_gids_to_retain);
+  for (unsigned int i = 0; i < old_gids.len; i++)
+    _add_gid_and_children (glyf, old_gids[i], all_gids_to_retain);
+
+  // Transfer to a sorted list.
+  old_gids_sorted.alloc (hb_set_get_population (all_gids_to_retain));
+  hb_codepoint_t gid = HB_SET_VALUE_INVALID;
+  while (hb_set_next (all_gids_to_retain, &gid))
+    *(old_gids_sorted.push ()) = gid;
+
+  hb_set_destroy (all_gids_to_retain);
+  glyf.fini ();
+  cmap.fini ();
+}
+
+/**
+ * hb_subset_plan_create:
+ * Computes a plan for subsetting the supplied face according
+ * to a provide profile and input. The plan describes
+ * which tables and glyphs should be retained.
+ *
+ * Return value: New subset plan.
+ *
+ * Since: 1.7.5
+ **/
+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->codepoints.init();
+  plan->gids_to_retain.init();
+  plan->gids_to_retain_sorted.init();
+  plan->source = hb_face_reference (face);
+  plan->dest = hb_subset_face_create ();
+
+  _populate_codepoints (input->unicodes, plan->codepoints);
+  _populate_gids_to_retain (face,
+                            plan->codepoints,
+                            plan->gids_to_retain,
+                            plan->gids_to_retain_sorted);
+
+  return plan;
+}
+
+/**
+ * hb_subset_plan_destroy:
+ *
+ * Since: 1.7.5
+ **/
+void
+hb_subset_plan_destroy (hb_subset_plan_t *plan)
+{
+  if (!hb_object_destroy (plan)) return;
+
+  plan->codepoints.finish ();
+  plan->gids_to_retain.finish ();
+  plan->gids_to_retain_sorted.finish ();
+
+  hb_face_destroy (plan->source);
+  hb_face_destroy (plan->dest);
+
+  free (plan);
+}
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh
new file mode 100644
index 0000000..a741528
--- /dev/null
+++ b/src/hb-subset-plan.hh
@@ -0,0 +1,83 @@
+/*
+ * 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_PLAN_HH
+#define HB_SUBSET_PLAN_HH
+
+#include "hb-private.hh"
+
+#include "hb-subset.h"
+
+#include "hb-object-private.hh"
+
+struct hb_subset_plan_t {
+  hb_object_header_t header;
+  ASSERT_POD ();
+
+  // TODO(Q1) actual map, drop this crap
+  // Look at me ma, I'm a poor mans map codepoint : new gid
+  // codepoints is sorted and aligned with gids_to_retain.
+
+  // These first two lists provide a mapping from cp -> gid
+  // As a result it does not list the full set of glyphs to retain.
+  hb_prealloced_array_t<hb_codepoint_t> codepoints;
+  hb_prealloced_array_t<hb_codepoint_t> gids_to_retain;
+
+  // This list contains the complete set of glyphs to retain and may contain
+  // more glyphs then the lists above.
+  hb_prealloced_array_t<hb_codepoint_t> gids_to_retain_sorted;
+
+  // Plan is only good for a specific source/dest so keep them with it
+  hb_face_t *source;
+  hb_face_t *dest;
+};
+
+typedef struct hb_subset_plan_t hb_subset_plan_t;
+
+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 hb_bool_t
+hb_subset_plan_new_gid_for_old_id(hb_subset_plan_t *plan,
+                                  hb_codepoint_t old_gid,
+                                  hb_codepoint_t *new_gid /* OUT */);
+
+HB_INTERNAL hb_bool_t
+hb_subset_plan_new_gid_for_codepoint(hb_subset_plan_t *plan,
+                                     hb_codepoint_t codepont,
+                                     hb_codepoint_t *new_gid /* OUT */);
+
+HB_INTERNAL hb_bool_t
+hb_subset_plan_add_table(hb_subset_plan_t *plan,
+                         hb_tag_t tag,
+                         hb_blob_t *contents);
+
+HB_INTERNAL void
+hb_subset_plan_destroy (hb_subset_plan_t *plan);
+
+#endif /* HB_SUBSET_PLAN_HH */
diff --git a/src/hb-subset-private.hh b/src/hb-subset-private.hh
new file mode 100644
index 0000000..cf784d7
--- /dev/null
+++ b/src/hb-subset-private.hh
@@ -0,0 +1,61 @@
+/*
+ * 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_PRIVATE_HH
+#define HB_SUBSET_PRIVATE_HH
+
+
+#include "hb-private.hh"
+
+#include "hb-subset.h"
+
+#include "hb-font-private.hh"
+
+typedef struct hb_subset_face_data_t hb_subset_face_data_t;
+
+struct hb_subset_input_t {
+  hb_object_header_t header;
+  ASSERT_POD ();
+
+  hb_set_t *unicodes;
+  hb_set_t *glyphs;
+
+  /* TODO
+   *
+   * features
+   * lookups
+   * nameIDs
+   * ...
+   */
+};
+
+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 */
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
new file mode 100644
index 0000000..12cdb1d
--- /dev/null
+++ b/src/hb-subset.cc
@@ -0,0 +1,331 @@
+/*
+ * 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, Rod Sheeter, Behdad Esfahbod
+ */
+
+#include "hb-private.hh"
+#include "hb-object-private.hh"
+#include "hb-open-type-private.hh"
+
+#include "hb-subset-glyf.hh"
+#include "hb-subset-private.hh"
+#include "hb-subset-plan.hh"
+
+#include "hb-open-file-private.hh"
+#include "hb-ot-cmap-table.hh"
+#include "hb-ot-glyf-table.hh"
+#include "hb-ot-hdmx-table.hh"
+#include "hb-ot-head-table.hh"
+#include "hb-ot-hhea-table.hh"
+#include "hb-ot-hmtx-table.hh"
+#include "hb-ot-maxp-table.hh"
+#include "hb-ot-os2-table.hh"
+
+
+#ifndef HB_NO_VISIBILITY
+const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
+#endif
+
+
+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 ()
+{
+  return hb_object_create<hb_subset_profile_t>();
+}
+
+/**
+ * hb_subset_profile_destroy:
+ *
+ * Since: 1.8.0
+ **/
+void
+hb_subset_profile_destroy (hb_subset_profile_t *profile)
+{
+  if (!hb_object_destroy (profile)) return;
+
+  free (profile);
+}
+
+template<typename TableType>
+static bool
+_subset (hb_subset_plan_t *plan)
+{
+  OT::Sanitizer<TableType> sanitizer;
+
+  hb_blob_t *source_blob = sanitizer.sanitize (plan->source->reference_table (TableType::tableTag));
+  const TableType *table = OT::Sanitizer<TableType>::lock_instance (source_blob);
+
+  hb_bool_t result = false;
+  if (table != &OT::Null(TableType))
+    result = table->subset(plan);
+
+  hb_blob_destroy (source_blob);
+  hb_tag_t tag = TableType::tableTag;
+  DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG(tag), result ? "success" : "FAILED!");
+  return result;
+}
+
+
+/*
+ * 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_prealloced_array_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;
+
+  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 (int i = 0; i < data->tables.len; i++)
+    hb_blob_destroy (data->tables[i].blob);
+
+  data->tables.finish ();
+
+  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.array[i].blob));
+
+  char *buf = (char *) malloc (face_length);
+  if (unlikely (!buf))
+    return nullptr;
+
+  OT::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;
+
+  OT::Supplier<hb_tag_t>    tags_supplier  (&data->tables[0].tag, table_count, sizeof (data->tables[0]));
+  OT::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_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 ();
+  if (unlikely (!entry))
+    return false;
+
+  entry->tag = tag;
+  entry->blob = hb_blob_reference (blob);
+
+  return true;
+}
+
+static bool
+_subset_table (hb_subset_plan_t *plan,
+               hb_tag_t          tag)
+{
+  DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG(tag));
+  bool result = true;
+  switch (tag) {
+    case HB_OT_TAG_glyf:
+      result = _subset<const OT::glyf> (plan);
+      break;
+    case HB_OT_TAG_hdmx:
+      result = _subset<const OT::hdmx> (plan);
+      break;
+    case HB_OT_TAG_head:
+      // SKIP head, it's handled by glyf
+      result = true;
+      break;
+    case HB_OT_TAG_hhea:
+      // SKIP hhea, it's handled by hmtx
+      return true;
+    case HB_OT_TAG_hmtx:
+      result = _subset<const OT::hmtx> (plan);
+      break;
+    case HB_OT_TAG_maxp:
+      result = _subset<const OT::maxp> (plan);
+      break;
+    case HB_OT_TAG_loca:
+      // SKIP loca, it's handle by glyf
+      return true;
+    case HB_OT_TAG_cmap:
+      result = _subset<const OT::cmap> (plan);
+      break;
+    case HB_OT_TAG_os2:
+      result = _subset<const OT::os2> (plan);
+      break;
+    default:
+      hb_blob_t *source_table = hb_face_reference_table(plan->source, tag);
+      if (likely (source_table))
+        result = hb_subset_plan_add_table(plan, tag, source_table);
+      else
+        result = false;
+      hb_blob_destroy (source_table);
+      break;
+  }
+  DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG(tag), result ? "ok" : "FAILED");
+  return result;
+}
+
+static bool
+_should_drop_table(hb_tag_t tag)
+{
+    switch (tag) {
+      case HB_TAG ('G', 'D', 'E', 'F'): /* temporary */
+      case HB_TAG ('G', 'P', 'O', 'S'): /* temporary */
+      case HB_TAG ('G', 'S', 'U', 'B'): /* temporary */
+      case HB_TAG ('D', 'S', 'I', 'G'):
+        return true;
+      default:
+        return false;
+  }
+}
+
+/**
+ * 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.
+ **/
+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();
+
+  hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input);
+
+  hb_tag_t table_tags[32];
+  unsigned int offset = 0, count;
+  bool success = true;
+  do {
+    count = ARRAY_LENGTH (table_tags);
+    hb_face_get_table_tags (source, offset, &count, table_tags);
+    for (unsigned int i = 0; i < count; i++)
+    {
+      hb_tag_t tag = table_tags[i];
+      if (_should_drop_table(tag))
+      {
+        DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG(tag));
+        continue;
+      }
+      success = success && _subset_table (plan, tag);
+    }
+  } while (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;
+}
diff --git a/src/hb-subset.h b/src/hb-subset.h
new file mode 100644
index 0000000..de7759b
--- /dev/null
+++ b/src/hb-subset.h
@@ -0,0 +1,81 @@
+/*
+ * 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): Rod Sheeter
+ */
+
+#ifndef HB_SUBSET_H
+#define HB_SUBSET_H
+
+#include "hb.h"
+
+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.
+ */
+
+typedef struct hb_subset_input_t hb_subset_input_t;
+
+HB_EXTERN hb_subset_input_t *
+hb_subset_input_create_or_fail (void);
+
+HB_EXTERN hb_subset_input_t *
+hb_subset_input_reference (hb_subset_input_t *subset_input);
+
+HB_EXTERN void
+hb_subset_input_destroy (hb_subset_input_t *subset_input);
+
+HB_EXTERN hb_set_t *
+hb_subset_input_unicode_set (hb_subset_input_t *subset_input);
+
+HB_EXTERN hb_set_t *
+hb_subset_input_glyph_set (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_END_DECLS
+
+#endif /* HB_SUBSET_H */
diff --git a/src/hb-ucdn.cc b/src/hb-ucdn.cc
index a884e3f..9515bda 100644
--- a/src/hb-ucdn.cc
+++ b/src/hb-ucdn.cc
@@ -160,17 +160,23 @@
     HB_SCRIPT_NEWA,
     HB_SCRIPT_OSAGE,
     HB_SCRIPT_TANGUT,
+    HB_SCRIPT_MASARAM_GONDI,
+    HB_SCRIPT_NUSHU,
+    HB_SCRIPT_SOYOMBO,
+    HB_SCRIPT_ZANABAZAR_SQUARE,
 };
 
 static hb_unicode_combining_class_t
-hb_ucdn_combining_class(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
+hb_ucdn_combining_class(hb_unicode_funcs_t *ufuncs HB_UNUSED,
+			hb_codepoint_t unicode,
 			void *user_data HB_UNUSED)
 {
     return (hb_unicode_combining_class_t) ucdn_get_combining_class(unicode);
 }
 
 static unsigned int
-hb_ucdn_eastasian_width(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
+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);
@@ -178,28 +184,31 @@
 }
 
 static hb_unicode_general_category_t
-hb_ucdn_general_category(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
+hb_ucdn_general_category(hb_unicode_funcs_t *ufuncs HB_UNUSED,
+			 hb_codepoint_t unicode,
 			 void *user_data HB_UNUSED)
 {
     return (hb_unicode_general_category_t)ucdn_get_general_category(unicode);
 }
 
 static hb_codepoint_t
-hb_ucdn_mirroring(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
+hb_ucdn_mirroring(hb_unicode_funcs_t *ufuncs HB_UNUSED,
+		  hb_codepoint_t unicode,
 		  void *user_data HB_UNUSED)
 {
     return ucdn_mirror(unicode);
 }
 
 static hb_script_t
-hb_ucdn_script(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
+hb_ucdn_script(hb_unicode_funcs_t *ufuncs HB_UNUSED,
+	       hb_codepoint_t unicode,
 	       void *user_data HB_UNUSED)
 {
     return ucdn_script_translate[ucdn_get_script(unicode)];
 }
 
 static hb_bool_t
-hb_ucdn_compose(hb_unicode_funcs_t *ufuncs,
+hb_ucdn_compose(hb_unicode_funcs_t *ufuncs HB_UNUSED,
 		hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab,
 		void *user_data HB_UNUSED)
 {
@@ -207,7 +216,7 @@
 }
 
 static hb_bool_t
-hb_ucdn_decompose(hb_unicode_funcs_t *ufuncs,
+hb_ucdn_decompose(hb_unicode_funcs_t *ufuncs HB_UNUSED,
 		  hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b,
 		  void *user_data HB_UNUSED)
 {
@@ -215,29 +224,50 @@
 }
 
 static unsigned int
-hb_ucdn_decompose_compatibility(hb_unicode_funcs_t *ufuncs,
+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);
 }
 
+static hb_unicode_funcs_t *static_ucdn_funcs = nullptr;
+
+#ifdef HB_USE_ATEXIT
+static
+void free_static_ucdn_funcs (void)
+{
+  hb_unicode_funcs_destroy (static_ucdn_funcs);
+}
+#endif
+
 extern "C" HB_INTERNAL
 hb_unicode_funcs_t *
 hb_ucdn_get_unicode_funcs (void)
 {
-  static const hb_unicode_funcs_t _hb_ucdn_unicode_funcs = {
-    HB_OBJECT_HEADER_STATIC,
+retry:
+  hb_unicode_funcs_t *funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_ucdn_funcs);
 
-    NULL, /* parent */
-    true, /* immutable */
-    {
-#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_ucdn_##name,
+  if (unlikely (!funcs))
+  {
+    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_make_immutable (funcs);
+
+    if (!hb_atomic_ptr_cmpexch (&static_ucdn_funcs, nullptr, funcs)) {
+      hb_unicode_funcs_destroy (funcs);
+      goto retry;
     }
+
+#ifdef HB_USE_ATEXIT
+    atexit (free_static_ucdn_funcs); /* First person registers atexit() callback. */
+#endif
   };
 
-  return const_cast<hb_unicode_funcs_t *> (&_hb_ucdn_unicode_funcs);
+  return hb_unicode_funcs_reference (funcs);
 }
-
diff --git a/src/hb-ucdn/Makefile.sources b/src/hb-ucdn/Makefile.sources
index d5f87b2..cb823b6 100644
--- a/src/hb-ucdn/Makefile.sources
+++ b/src/hb-ucdn/Makefile.sources
@@ -1,4 +1,7 @@
+NULL =
+
 LIBHB_UCDN_sources = \
 	ucdn.h \
 	ucdn.c \
-	unicodedata_db.h
+	ucdn_db.h \
+	$(NULL)
diff --git a/src/hb-ucdn/README b/src/hb-ucdn/README
index fcb97b9..2203ae6 100644
--- a/src/hb-ucdn/README
+++ b/src/hb-ucdn/README
@@ -31,10 +31,10 @@
 
 How to Use
 
-Include ucdn.c, ucdn.h and unicodedata_db.h in your project. Now,
-just use the functions as documented in ucdn.h.
+Include ucdn.c, ucdn.h and ucdn_db.h in your project. Now, just use the
+functions as documented in ucdn.h.
 
 In some cases, it might be necessary to regenerate the Unicode
 database file. The script makeunicodedata.py (Python 3.x required)
 fetches the appropriate files and dumps the compressed database into
-unicodedata_db.h.
+ucdn_db.h.
diff --git a/src/hb-ucdn/ucdn.c b/src/hb-ucdn/ucdn.c
index f4e9be1..30747fe 100644
--- a/src/hb-ucdn/ucdn.c
+++ b/src/hb-ucdn/ucdn.c
@@ -23,7 +23,6 @@
     unsigned char category;
     unsigned char combining;
     unsigned char bidi_class;
-    unsigned char mirrored;
     unsigned char east_asian_width;
     unsigned char script;
     unsigned char linebreak_class;
@@ -43,7 +42,7 @@
     short count, index;
 } Reindex;
 
-#include "unicodedata_db.h"
+#include "ucdn_db.h"
 
 /* constants required for Hangul (de)composition */
 #define SBASE 0xAC00
@@ -91,20 +90,30 @@
     return &decomp_data[index];
 }
 
-static int get_comp_index(uint32_t code, const Reindex *idx)
+static int compare_reindex(const void *a, const void *b)
 {
-    int i;
+    Reindex *ra = (Reindex *)a;
+    Reindex *rb = (Reindex *)b;
 
-    for (i = 0; idx[i].start; i++) {
-        const Reindex *cur = &idx[i];
-        if (code < cur->start)
-            return -1;
-        if (code <= cur->start + cur->count) {
-            return cur->index + (code - cur->start);
-        }
-    }
+    if (ra->start < rb->start)
+        return -1;
+    else if (ra->start > (rb->start + rb->count))
+        return 1;
+    else
+        return 0;
+}
 
-    return -1;
+static int get_comp_index(uint32_t code, const Reindex *idx, size_t len)
+{
+    Reindex *res;
+    Reindex r = {0, 0, 0};
+    r.start = code;
+    res = (Reindex *) bsearch(&r, idx, len, sizeof(Reindex), compare_reindex);
+
+    if (res != NULL)
+        return res->index + (code - res->start);
+    else
+        return -1;
 }
 
 static int compare_mp(const void *a, const void *b)
@@ -127,8 +136,8 @@
     BracketPair *res;
 
     bp.from = code;
-    res = bsearch(&bp, bracket_pairs, BIDI_BRACKET_LEN, sizeof(BracketPair),
-            compare_bp);
+    res = (BracketPair *) bsearch(&bp, bracket_pairs, BIDI_BRACKET_LEN,
+                                 sizeof(BracketPair), compare_bp);
     return res;
 }
 
@@ -154,23 +163,18 @@
 
 static int hangul_pair_compose(uint32_t *code, uint32_t a, uint32_t b)
 {
-    if (b < VBASE || b >= (TBASE + TCOUNT))
-        return 0;
-
-    if ((a < LBASE || a >= (LBASE + LCOUNT))
-            && (a < SBASE || a >= (SBASE + SCOUNT)))
-        return 0;
-
-    if (a >= SBASE) {
+    if (a >= SBASE && a < (SBASE + SCOUNT) && b >= TBASE && b < (TBASE + TCOUNT)) {
         /* LV,T */
         *code = a + (b - TBASE);
         return 3;
-    } else {
+    } else if (a >= LBASE && a < (LBASE + LCOUNT) && b >= VBASE && b < (VBASE + VCOUNT)) {
         /* L,V */
         int li = a - LBASE;
         int vi = b - VBASE;
         *code = SBASE + li * NCOUNT + vi * TCOUNT;
         return 2;
+    } else {
+        return 0;
     }
 }
 
@@ -178,7 +182,7 @@
 {
     const unsigned short *code = *code_ptr;
 
-    if ((code[0] & 0xd800) != 0xd800) {
+    if (code[0] < 0xd800 || code[0] > 0xdc00) {
         *code_ptr += 1;
         return (uint32_t)code[0];
     } else {
@@ -215,7 +219,7 @@
 
 int ucdn_get_mirrored(uint32_t code)
 {
-    return get_ucd_record(code)->mirrored;
+    return ucdn_mirror(code) != code;
 }
 
 int ucdn_get_script(uint32_t code)
@@ -264,12 +268,9 @@
     MirrorPair mp = {0};
     MirrorPair *res;
 
-    if (get_ucd_record(code)->mirrored == 0)
-        return code;
-
     mp.from = code;
-    res = bsearch(&mp, mirror_pairs, BIDI_MIRROR_LEN, sizeof(MirrorPair),
-            compare_mp);
+    res = (MirrorPair *) bsearch(&mp, mirror_pairs, BIDI_MIRROR_LEN,
+                                sizeof(MirrorPair), compare_mp);
 
     if (res == NULL)
         return code;
@@ -326,8 +327,8 @@
     if (hangul_pair_compose(code, a, b))
         return 1;
 
-    l = get_comp_index(a, nfc_first);
-    r = get_comp_index(b, nfc_last);
+    l = get_comp_index(a, nfc_first, sizeof(nfc_first) / sizeof(Reindex));
+    r = get_comp_index(b, nfc_last, sizeof(nfc_last) / sizeof(Reindex));
 
     if (l < 0 || r < 0)
         return 0;
diff --git a/src/hb-ucdn/ucdn.h b/src/hb-ucdn/ucdn.h
index f694dc5..2d5fc35 100644
--- a/src/hb-ucdn/ucdn.h
+++ b/src/hb-ucdn/ucdn.h
@@ -36,30 +36,16 @@
 
 HB_BEGIN_HEADER
 
-#if !defined (HB_DONT_DEFINE_STDINT)
-
 #if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \
     defined (_sgi) || defined (__sun) || defined (sun) || \
     defined (__digital__) || defined (__HP_cc)
 #  include <inttypes.h>
 #elif defined (_AIX)
 #  include <sys/inttypes.h>
-/* VS 2010 (_MSC_VER 1600) has stdint.h */
-#elif defined (_MSC_VER) && _MSC_VER < 1600
-typedef __int8 int8_t;
-typedef unsigned __int8 uint8_t;
-typedef __int16 int16_t;
-typedef unsigned __int16 uint16_t;
-typedef __int32 int32_t;
-typedef unsigned __int32 uint32_t;
-typedef __int64 int64_t;
-typedef unsigned __int64 uint64_t;
 #else
 #  include <stdint.h>
 #endif
 
-#endif
-
 
 #define UCDN_EAST_ASIAN_F 0
 #define UCDN_EAST_ASIAN_H 1
@@ -206,6 +192,10 @@
 #define UCDN_SCRIPT_NEWA 135
 #define UCDN_SCRIPT_OSAGE 136
 #define UCDN_SCRIPT_TANGUT 137
+#define UCDN_SCRIPT_MASARAM_GONDI 138
+#define UCDN_SCRIPT_NUSHU 139
+#define UCDN_SCRIPT_SOYOMBO 140
+#define UCDN_SCRIPT_ZANABAZAR_SQUARE 141
 
 #define UCDN_LINEBREAK_CLASS_OP 0
 #define UCDN_LINEBREAK_CLASS_CL 1
@@ -247,6 +237,9 @@
 #define UCDN_LINEBREAK_CLASS_SG 37
 #define UCDN_LINEBREAK_CLASS_SP 38
 #define UCDN_LINEBREAK_CLASS_XX 39
+#define UCDN_LINEBREAK_CLASS_ZWJ 40
+#define UCDN_LINEBREAK_CLASS_EB 41
+#define UCDN_LINEBREAK_CLASS_EM 42
 
 #define UCDN_GENERAL_CATEGORY_CC 0
 #define UCDN_GENERAL_CATEGORY_CF 1
diff --git a/src/hb-ucdn/unicodedata_db.h b/src/hb-ucdn/ucdn_db.h
similarity index 64%
rename from src/hb-ucdn/unicodedata_db.h
rename to src/hb-ucdn/ucdn_db.h
index c9cd59b..8d2d8de 100644
--- a/src/hb-ucdn/unicodedata_db.h
+++ b/src/hb-ucdn/ucdn_db.h
@@ -1,999 +1,1012 @@
 /* this file was generated by makeunicodedata.py 3.2 */
 
-#define UNIDATA_VERSION "9.0.0"
+#define UNIDATA_VERSION "10.0.0"
 /* a list of unique database records */
 static const UCDRecord ucd_records[] = {
-    {2, 0, 18, 0, 5, 102, 41},
-    {0, 0, 14, 0, 5, 0, 21},
-    {0, 0, 16, 0, 5, 0, 17},
-    {0, 0, 15, 0, 5, 0, 35},
-    {0, 0, 16, 0, 5, 0, 30},
-    {0, 0, 17, 0, 5, 0, 30},
-    {0, 0, 15, 0, 5, 0, 33},
-    {0, 0, 15, 0, 5, 0, 21},
-    {0, 0, 16, 0, 5, 0, 21},
-    {29, 0, 17, 0, 3, 0, 40},
-    {21, 0, 18, 0, 3, 0, 6},
-    {21, 0, 18, 0, 3, 0, 3},
-    {21, 0, 10, 0, 3, 0, 12},
-    {23, 0, 10, 0, 3, 0, 9},
-    {21, 0, 10, 0, 3, 0, 10},
-    {21, 0, 18, 0, 3, 0, 12},
-    {22, 0, 18, 1, 3, 0, 0},
-    {18, 0, 18, 1, 3, 0, 2},
-    {25, 0, 9, 0, 3, 0, 9},
-    {21, 0, 12, 0, 3, 0, 8},
-    {17, 0, 9, 0, 3, 0, 16},
-    {21, 0, 12, 0, 3, 0, 7},
-    {13, 0, 8, 0, 3, 0, 11},
-    {21, 0, 18, 0, 3, 0, 8},
-    {25, 0, 18, 1, 3, 0, 12},
-    {25, 0, 18, 0, 3, 0, 12},
-    {9, 0, 0, 0, 3, 1, 12},
-    {21, 0, 18, 0, 3, 0, 9},
-    {24, 0, 18, 0, 3, 0, 12},
-    {16, 0, 18, 0, 3, 0, 12},
-    {5, 0, 0, 0, 3, 1, 12},
-    {25, 0, 18, 0, 3, 0, 17},
-    {18, 0, 18, 1, 3, 0, 1},
-    {0, 0, 15, 0, 5, 0, 36},
-    {29, 0, 12, 0, 5, 0, 4},
-    {21, 0, 18, 0, 4, 0, 0},
-    {23, 0, 10, 0, 3, 0, 10},
-    {23, 0, 10, 0, 4, 0, 9},
-    {26, 0, 18, 0, 3, 0, 12},
-    {21, 0, 18, 0, 4, 0, 29},
-    {24, 0, 18, 0, 4, 0, 29},
-    {26, 0, 18, 0, 5, 0, 12},
-    {7, 0, 0, 0, 4, 1, 29},
-    {20, 0, 18, 1, 5, 0, 3},
-    {1, 0, 14, 0, 4, 0, 17},
-    {26, 0, 18, 0, 4, 0, 12},
-    {26, 0, 10, 0, 4, 0, 10},
-    {25, 0, 10, 0, 4, 0, 9},
-    {15, 0, 8, 0, 4, 0, 29},
-    {24, 0, 18, 0, 4, 0, 18},
-    {5, 0, 0, 0, 5, 0, 12},
-    {19, 0, 18, 1, 5, 0, 3},
-    {15, 0, 18, 0, 4, 0, 29},
-    {9, 0, 0, 0, 5, 1, 12},
-    {9, 0, 0, 0, 4, 1, 12},
-    {25, 0, 18, 0, 4, 0, 29},
-    {5, 0, 0, 0, 4, 1, 12},
-    {5, 0, 0, 0, 5, 1, 12},
-    {7, 0, 0, 0, 5, 1, 12},
-    {8, 0, 0, 0, 5, 1, 12},
-    {6, 0, 0, 0, 5, 1, 12},
-    {6, 0, 18, 0, 5, 0, 12},
-    {6, 0, 0, 0, 5, 0, 12},
-    {24, 0, 18, 0, 5, 0, 12},
-    {24, 0, 18, 0, 4, 0, 12},
-    {6, 0, 18, 0, 4, 0, 29},
-    {6, 0, 18, 0, 5, 0, 18},
-    {6, 0, 0, 0, 4, 0, 29},
-    {24, 0, 18, 0, 5, 34, 12},
-    {12, 230, 13, 0, 4, 40, 21},
-    {12, 232, 13, 0, 4, 40, 21},
-    {12, 220, 13, 0, 4, 40, 21},
-    {12, 216, 13, 0, 4, 40, 21},
-    {12, 202, 13, 0, 4, 40, 21},
-    {12, 1, 13, 0, 4, 40, 21},
-    {12, 240, 13, 0, 4, 40, 21},
-    {12, 0, 13, 0, 4, 40, 4},
-    {12, 233, 13, 0, 4, 40, 4},
-    {12, 234, 13, 0, 4, 40, 4},
-    {9, 0, 0, 0, 5, 2, 12},
-    {5, 0, 0, 0, 5, 2, 12},
-    {24, 0, 18, 0, 5, 2, 12},
-    {2, 0, 18, 0, 5, 102, 41},
-    {6, 0, 0, 0, 5, 2, 12},
-    {21, 0, 18, 0, 5, 0, 8},
-    {21, 0, 18, 0, 5, 0, 12},
-    {9, 0, 0, 0, 4, 2, 12},
-    {5, 0, 0, 0, 4, 2, 12},
-    {9, 0, 0, 0, 5, 54, 12},
-    {5, 0, 0, 0, 5, 54, 12},
-    {25, 0, 18, 0, 5, 2, 12},
-    {9, 0, 0, 0, 5, 3, 12},
-    {9, 0, 0, 0, 4, 3, 12},
-    {5, 0, 0, 0, 4, 3, 12},
-    {5, 0, 0, 0, 5, 3, 12},
-    {26, 0, 0, 0, 5, 3, 12},
-    {12, 230, 13, 0, 5, 3, 21},
-    {12, 230, 13, 0, 5, 40, 21},
-    {11, 0, 13, 0, 5, 3, 21},
-    {9, 0, 0, 0, 5, 4, 12},
-    {6, 0, 0, 0, 5, 4, 12},
-    {21, 0, 0, 0, 5, 4, 12},
-    {5, 0, 0, 0, 5, 4, 12},
-    {21, 0, 0, 0, 5, 0, 8},
-    {17, 0, 18, 0, 5, 4, 17},
-    {26, 0, 18, 0, 5, 4, 12},
-    {23, 0, 10, 0, 5, 4, 9},
-    {12, 220, 13, 0, 5, 5, 21},
-    {12, 230, 13, 0, 5, 5, 21},
-    {12, 222, 13, 0, 5, 5, 21},
-    {12, 228, 13, 0, 5, 5, 21},
-    {12, 10, 13, 0, 5, 5, 21},
-    {12, 11, 13, 0, 5, 5, 21},
-    {12, 12, 13, 0, 5, 5, 21},
-    {12, 13, 13, 0, 5, 5, 21},
-    {12, 14, 13, 0, 5, 5, 21},
-    {12, 15, 13, 0, 5, 5, 21},
-    {12, 16, 13, 0, 5, 5, 21},
-    {12, 17, 13, 0, 5, 5, 21},
-    {12, 18, 13, 0, 5, 5, 21},
-    {12, 19, 13, 0, 5, 5, 21},
-    {12, 20, 13, 0, 5, 5, 21},
-    {12, 21, 13, 0, 5, 5, 21},
-    {12, 22, 13, 0, 5, 5, 21},
-    {17, 0, 3, 0, 5, 5, 17},
-    {12, 23, 13, 0, 5, 5, 21},
-    {21, 0, 3, 0, 5, 5, 12},
-    {12, 24, 13, 0, 5, 5, 21},
-    {12, 25, 13, 0, 5, 5, 21},
-    {21, 0, 3, 0, 5, 5, 6},
-    {7, 0, 3, 0, 5, 5, 13},
-    {1, 0, 11, 0, 5, 6, 12},
-    {1, 0, 11, 0, 5, 0, 12},
-    {25, 0, 18, 0, 5, 6, 12},
-    {25, 0, 4, 0, 5, 6, 12},
-    {21, 0, 10, 0, 5, 6, 10},
-    {23, 0, 4, 0, 5, 6, 10},
-    {21, 0, 12, 0, 5, 0, 8},
-    {21, 0, 4, 0, 5, 6, 8},
-    {26, 0, 18, 0, 5, 6, 12},
-    {12, 230, 13, 0, 5, 6, 21},
-    {12, 30, 13, 0, 5, 6, 21},
-    {12, 31, 13, 0, 5, 6, 21},
-    {12, 32, 13, 0, 5, 6, 21},
-    {21, 0, 4, 0, 5, 0, 6},
-    {1, 0, 4, 0, 5, 0, 21},
-    {21, 0, 4, 0, 5, 6, 6},
-    {7, 0, 4, 0, 5, 6, 12},
-    {6, 0, 4, 0, 5, 0, 12},
-    {12, 27, 13, 0, 5, 40, 21},
-    {12, 28, 13, 0, 5, 40, 21},
-    {12, 29, 13, 0, 5, 40, 21},
-    {12, 30, 13, 0, 5, 40, 21},
-    {12, 31, 13, 0, 5, 40, 21},
-    {12, 32, 13, 0, 5, 40, 21},
-    {12, 33, 13, 0, 5, 40, 21},
-    {12, 34, 13, 0, 5, 40, 21},
-    {12, 220, 13, 0, 5, 40, 21},
-    {12, 220, 13, 0, 5, 6, 21},
-    {13, 0, 11, 0, 5, 6, 11},
-    {21, 0, 11, 0, 5, 6, 11},
-    {21, 0, 4, 0, 5, 6, 12},
-    {12, 35, 13, 0, 5, 40, 21},
-    {6, 0, 4, 0, 5, 6, 12},
-    {13, 0, 8, 0, 5, 6, 11},
-    {26, 0, 4, 0, 5, 6, 12},
-    {21, 0, 4, 0, 5, 7, 12},
-    {1, 0, 4, 0, 5, 7, 12},
-    {7, 0, 4, 0, 5, 7, 12},
-    {12, 36, 13, 0, 5, 7, 21},
-    {12, 230, 13, 0, 5, 7, 21},
-    {12, 220, 13, 0, 5, 7, 21},
-    {7, 0, 4, 0, 5, 8, 12},
-    {12, 0, 13, 0, 5, 8, 21},
-    {13, 0, 3, 0, 5, 65, 11},
-    {7, 0, 3, 0, 5, 65, 12},
-    {12, 230, 13, 0, 5, 65, 21},
-    {12, 220, 13, 0, 5, 65, 21},
-    {6, 0, 3, 0, 5, 65, 12},
-    {26, 0, 18, 0, 5, 65, 12},
-    {21, 0, 18, 0, 5, 65, 12},
-    {21, 0, 18, 0, 5, 65, 8},
-    {21, 0, 18, 0, 5, 65, 6},
-    {7, 0, 3, 0, 5, 81, 12},
-    {12, 230, 13, 0, 5, 81, 21},
-    {6, 0, 3, 0, 5, 81, 12},
-    {21, 0, 3, 0, 5, 81, 12},
-    {7, 0, 3, 0, 5, 94, 12},
-    {12, 220, 13, 0, 5, 94, 21},
-    {21, 0, 3, 0, 5, 94, 12},
-    {12, 27, 13, 0, 5, 6, 21},
-    {12, 28, 13, 0, 5, 6, 21},
-    {12, 29, 13, 0, 5, 6, 21},
-    {12, 0, 13, 0, 5, 9, 21},
-    {10, 0, 0, 0, 5, 9, 21},
-    {7, 0, 0, 0, 5, 9, 12},
-    {12, 7, 13, 0, 5, 9, 21},
-    {12, 9, 13, 0, 5, 9, 21},
-    {12, 230, 13, 0, 5, 9, 21},
-    {21, 0, 0, 0, 5, 0, 17},
-    {13, 0, 0, 0, 5, 9, 11},
-    {21, 0, 0, 0, 5, 9, 12},
-    {6, 0, 0, 0, 5, 9, 12},
-    {7, 0, 0, 0, 5, 10, 12},
-    {12, 0, 13, 0, 5, 10, 21},
-    {10, 0, 0, 0, 5, 10, 21},
-    {12, 7, 13, 0, 5, 10, 21},
-    {12, 9, 13, 0, 5, 10, 21},
-    {13, 0, 0, 0, 5, 10, 11},
-    {23, 0, 10, 0, 5, 10, 10},
-    {15, 0, 0, 0, 5, 10, 12},
-    {15, 0, 0, 0, 5, 10, 10},
-    {26, 0, 0, 0, 5, 10, 12},
-    {23, 0, 10, 0, 5, 10, 9},
-    {12, 0, 13, 0, 5, 11, 21},
-    {10, 0, 0, 0, 5, 11, 21},
-    {7, 0, 0, 0, 5, 11, 12},
-    {12, 7, 13, 0, 5, 11, 21},
-    {12, 9, 13, 0, 5, 11, 21},
-    {13, 0, 0, 0, 5, 11, 11},
-    {12, 0, 13, 0, 5, 12, 21},
-    {10, 0, 0, 0, 5, 12, 21},
-    {7, 0, 0, 0, 5, 12, 12},
-    {12, 7, 13, 0, 5, 12, 21},
-    {12, 9, 13, 0, 5, 12, 21},
-    {13, 0, 0, 0, 5, 12, 11},
-    {21, 0, 0, 0, 5, 12, 12},
-    {23, 0, 10, 0, 5, 12, 9},
-    {12, 0, 13, 0, 5, 13, 21},
-    {10, 0, 0, 0, 5, 13, 21},
-    {7, 0, 0, 0, 5, 13, 12},
-    {12, 7, 13, 0, 5, 13, 21},
-    {12, 9, 13, 0, 5, 13, 21},
-    {13, 0, 0, 0, 5, 13, 11},
-    {26, 0, 0, 0, 5, 13, 12},
-    {15, 0, 0, 0, 5, 13, 12},
-    {12, 0, 13, 0, 5, 14, 21},
-    {7, 0, 0, 0, 5, 14, 12},
-    {10, 0, 0, 0, 5, 14, 21},
-    {12, 9, 13, 0, 5, 14, 21},
-    {13, 0, 0, 0, 5, 14, 11},
-    {15, 0, 0, 0, 5, 14, 12},
-    {26, 0, 18, 0, 5, 14, 12},
-    {23, 0, 10, 0, 5, 14, 9},
-    {12, 0, 13, 0, 5, 15, 21},
-    {10, 0, 0, 0, 5, 15, 21},
-    {7, 0, 0, 0, 5, 15, 12},
-    {12, 9, 13, 0, 5, 15, 21},
-    {12, 84, 13, 0, 5, 15, 21},
-    {12, 91, 13, 0, 5, 15, 21},
-    {13, 0, 0, 0, 5, 15, 11},
-    {15, 0, 18, 0, 5, 15, 12},
-    {26, 0, 0, 0, 5, 15, 12},
-    {7, 0, 0, 0, 5, 16, 12},
-    {12, 0, 13, 0, 5, 16, 21},
-    {10, 0, 0, 0, 5, 16, 21},
-    {12, 7, 13, 0, 5, 16, 21},
-    {12, 0, 0, 0, 5, 16, 21},
-    {12, 9, 13, 0, 5, 16, 21},
-    {13, 0, 0, 0, 5, 16, 11},
-    {12, 0, 13, 0, 5, 17, 21},
-    {10, 0, 0, 0, 5, 17, 21},
-    {7, 0, 0, 0, 5, 17, 12},
-    {12, 9, 13, 0, 5, 17, 21},
-    {26, 0, 0, 0, 5, 17, 12},
-    {15, 0, 0, 0, 5, 17, 12},
-    {13, 0, 0, 0, 5, 17, 11},
-    {26, 0, 0, 0, 5, 17, 10},
-    {10, 0, 0, 0, 5, 18, 21},
-    {7, 0, 0, 0, 5, 18, 12},
-    {12, 9, 13, 0, 5, 18, 21},
-    {12, 0, 13, 0, 5, 18, 21},
-    {13, 0, 0, 0, 5, 18, 11},
-    {21, 0, 0, 0, 5, 18, 12},
-    {7, 0, 0, 0, 5, 19, 38},
-    {12, 0, 13, 0, 5, 19, 38},
-    {12, 103, 13, 0, 5, 19, 38},
-    {12, 9, 13, 0, 5, 19, 38},
-    {23, 0, 10, 0, 5, 0, 9},
-    {6, 0, 0, 0, 5, 19, 38},
-    {12, 107, 13, 0, 5, 19, 38},
-    {21, 0, 0, 0, 5, 19, 12},
-    {13, 0, 0, 0, 5, 19, 11},
-    {21, 0, 0, 0, 5, 19, 17},
-    {7, 0, 0, 0, 5, 20, 38},
-    {12, 0, 13, 0, 5, 20, 38},
-    {12, 118, 13, 0, 5, 20, 38},
-    {6, 0, 0, 0, 5, 20, 38},
-    {12, 122, 13, 0, 5, 20, 38},
-    {13, 0, 0, 0, 5, 20, 11},
-    {7, 0, 0, 0, 5, 21, 12},
-    {26, 0, 0, 0, 5, 21, 18},
-    {21, 0, 0, 0, 5, 21, 18},
-    {21, 0, 0, 0, 5, 21, 12},
-    {21, 0, 0, 0, 5, 21, 4},
-    {21, 0, 0, 0, 5, 21, 17},
-    {21, 0, 0, 0, 5, 21, 6},
-    {26, 0, 0, 0, 5, 21, 12},
-    {12, 220, 13, 0, 5, 21, 21},
-    {13, 0, 0, 0, 5, 21, 11},
-    {15, 0, 0, 0, 5, 21, 12},
-    {26, 0, 0, 0, 5, 21, 17},
-    {12, 216, 13, 0, 5, 21, 21},
-    {22, 0, 18, 1, 5, 21, 0},
-    {18, 0, 18, 1, 5, 21, 1},
-    {10, 0, 0, 0, 5, 21, 21},
-    {12, 129, 13, 0, 5, 21, 21},
-    {12, 130, 13, 0, 5, 21, 21},
-    {12, 0, 13, 0, 5, 21, 21},
-    {12, 132, 13, 0, 5, 21, 21},
-    {10, 0, 0, 0, 5, 21, 17},
-    {12, 230, 13, 0, 5, 21, 21},
-    {12, 9, 13, 0, 5, 21, 21},
-    {26, 0, 0, 0, 5, 0, 12},
-    {7, 0, 0, 0, 5, 22, 38},
-    {10, 0, 0, 0, 5, 22, 38},
-    {12, 0, 13, 0, 5, 22, 38},
-    {12, 7, 13, 0, 5, 22, 38},
-    {12, 9, 13, 0, 5, 22, 38},
-    {13, 0, 0, 0, 5, 22, 11},
-    {21, 0, 0, 0, 5, 22, 17},
-    {21, 0, 0, 0, 5, 22, 12},
-    {12, 220, 13, 0, 5, 22, 38},
-    {26, 0, 0, 0, 5, 22, 38},
-    {9, 0, 0, 0, 5, 23, 12},
-    {7, 0, 0, 0, 5, 23, 12},
-    {21, 0, 0, 0, 5, 0, 12},
-    {6, 0, 0, 0, 5, 23, 12},
-    {7, 0, 0, 0, 2, 24, 25},
-    {7, 0, 0, 0, 5, 24, 26},
-    {7, 0, 0, 0, 5, 24, 27},
-    {7, 0, 0, 0, 5, 25, 12},
-    {12, 230, 13, 0, 5, 25, 21},
-    {21, 0, 0, 0, 5, 25, 12},
-    {21, 0, 0, 0, 5, 25, 17},
-    {15, 0, 0, 0, 5, 25, 12},
-    {26, 0, 18, 0, 5, 25, 12},
-    {9, 0, 0, 0, 5, 26, 12},
-    {5, 0, 0, 0, 5, 26, 12},
-    {17, 0, 18, 0, 5, 27, 17},
-    {7, 0, 0, 0, 5, 27, 12},
-    {21, 0, 0, 0, 5, 27, 12},
-    {29, 0, 17, 0, 5, 28, 17},
-    {7, 0, 0, 0, 5, 28, 12},
-    {22, 0, 18, 1, 5, 28, 0},
-    {18, 0, 18, 1, 5, 28, 1},
-    {7, 0, 0, 0, 5, 29, 12},
-    {14, 0, 0, 0, 5, 29, 12},
-    {7, 0, 0, 0, 5, 41, 12},
-    {12, 0, 13, 0, 5, 41, 21},
-    {12, 9, 13, 0, 5, 41, 21},
-    {7, 0, 0, 0, 5, 42, 12},
-    {12, 0, 13, 0, 5, 42, 21},
-    {12, 9, 13, 0, 5, 42, 21},
-    {7, 0, 0, 0, 5, 43, 12},
-    {12, 0, 13, 0, 5, 43, 21},
-    {7, 0, 0, 0, 5, 44, 12},
-    {12, 0, 13, 0, 5, 44, 21},
-    {7, 0, 0, 0, 5, 30, 38},
-    {12, 0, 13, 0, 5, 30, 38},
-    {10, 0, 0, 0, 5, 30, 38},
-    {12, 9, 13, 0, 5, 30, 38},
-    {21, 0, 0, 0, 5, 30, 17},
-    {21, 0, 0, 0, 5, 30, 5},
-    {6, 0, 0, 0, 5, 30, 38},
-    {21, 0, 0, 0, 5, 30, 12},
-    {23, 0, 10, 0, 5, 30, 9},
-    {12, 230, 13, 0, 5, 30, 38},
-    {13, 0, 0, 0, 5, 30, 11},
-    {15, 0, 18, 0, 5, 30, 12},
-    {21, 0, 18, 0, 5, 31, 12},
-    {21, 0, 18, 0, 5, 0, 6},
-    {21, 0, 18, 0, 5, 31, 17},
-    {21, 0, 18, 0, 5, 0, 17},
-    {17, 0, 18, 0, 5, 31, 18},
-    {21, 0, 18, 0, 5, 31, 6},
-    {12, 0, 13, 0, 5, 31, 21},
-    {1, 0, 14, 0, 5, 31, 4},
-    {13, 0, 0, 0, 5, 31, 11},
-    {7, 0, 0, 0, 5, 31, 12},
-    {6, 0, 0, 0, 5, 31, 12},
-    {12, 228, 13, 0, 5, 31, 21},
-    {7, 0, 0, 0, 5, 45, 12},
-    {12, 0, 13, 0, 5, 45, 21},
-    {10, 0, 0, 0, 5, 45, 21},
-    {12, 222, 13, 0, 5, 45, 21},
-    {12, 230, 13, 0, 5, 45, 21},
-    {12, 220, 13, 0, 5, 45, 21},
-    {26, 0, 18, 0, 5, 45, 12},
-    {21, 0, 18, 0, 5, 45, 6},
-    {13, 0, 0, 0, 5, 45, 11},
-    {7, 0, 0, 0, 5, 46, 38},
-    {7, 0, 0, 0, 5, 55, 38},
-    {13, 0, 0, 0, 5, 55, 11},
-    {15, 0, 0, 0, 5, 55, 38},
-    {26, 0, 18, 0, 5, 55, 38},
-    {26, 0, 18, 0, 5, 30, 12},
-    {7, 0, 0, 0, 5, 53, 12},
-    {12, 230, 13, 0, 5, 53, 21},
-    {12, 220, 13, 0, 5, 53, 21},
-    {10, 0, 0, 0, 5, 53, 21},
-    {12, 0, 13, 0, 5, 53, 21},
-    {21, 0, 0, 0, 5, 53, 12},
-    {7, 0, 0, 0, 5, 77, 38},
-    {10, 0, 0, 0, 5, 77, 38},
-    {12, 0, 13, 0, 5, 77, 38},
-    {12, 9, 13, 0, 5, 77, 38},
-    {12, 230, 13, 0, 5, 77, 38},
-    {12, 220, 13, 0, 5, 77, 21},
-    {13, 0, 0, 0, 5, 77, 11},
-    {21, 0, 0, 0, 5, 77, 38},
-    {6, 0, 0, 0, 5, 77, 38},
-    {11, 0, 13, 0, 5, 40, 21},
-    {12, 0, 13, 0, 5, 61, 21},
-    {10, 0, 0, 0, 5, 61, 21},
-    {7, 0, 0, 0, 5, 61, 12},
-    {12, 7, 13, 0, 5, 61, 21},
-    {10, 9, 0, 0, 5, 61, 21},
-    {13, 0, 0, 0, 5, 61, 11},
-    {21, 0, 0, 0, 5, 61, 17},
-    {21, 0, 0, 0, 5, 61, 12},
-    {26, 0, 0, 0, 5, 61, 12},
-    {12, 230, 13, 0, 5, 61, 21},
-    {12, 220, 13, 0, 5, 61, 21},
-    {12, 0, 13, 0, 5, 66, 21},
-    {10, 0, 0, 0, 5, 66, 21},
-    {7, 0, 0, 0, 5, 66, 12},
-    {10, 9, 0, 0, 5, 66, 21},
-    {12, 9, 13, 0, 5, 66, 21},
-    {13, 0, 0, 0, 5, 66, 11},
-    {7, 0, 0, 0, 5, 92, 12},
-    {12, 7, 13, 0, 5, 92, 21},
-    {10, 0, 0, 0, 5, 92, 21},
-    {12, 0, 13, 0, 5, 92, 21},
-    {10, 9, 0, 0, 5, 92, 21},
-    {21, 0, 0, 0, 5, 92, 12},
-    {7, 0, 0, 0, 5, 67, 12},
-    {10, 0, 0, 0, 5, 67, 21},
-    {12, 0, 13, 0, 5, 67, 21},
-    {12, 7, 13, 0, 5, 67, 21},
-    {21, 0, 0, 0, 5, 67, 17},
-    {13, 0, 0, 0, 5, 67, 11},
-    {13, 0, 0, 0, 5, 68, 11},
-    {7, 0, 0, 0, 5, 68, 12},
-    {6, 0, 0, 0, 5, 68, 12},
-    {21, 0, 0, 0, 5, 68, 17},
-    {21, 0, 0, 0, 5, 66, 12},
-    {12, 1, 13, 0, 5, 40, 21},
-    {10, 0, 0, 0, 5, 0, 21},
-    {7, 0, 0, 0, 5, 0, 12},
-    {6, 0, 0, 0, 5, 3, 12},
-    {12, 234, 13, 0, 5, 40, 21},
-    {12, 214, 13, 0, 5, 40, 21},
-    {12, 202, 13, 0, 5, 40, 21},
-    {12, 233, 13, 0, 5, 40, 21},
-    {8, 0, 0, 0, 5, 2, 12},
-    {24, 0, 18, 0, 5, 2, 18},
-    {29, 0, 17, 0, 5, 0, 17},
-    {29, 0, 17, 0, 5, 0, 4},
-    {1, 0, 14, 0, 5, 0, 20},
-    {1, 0, 14, 0, 5, 40, 21},
-    {1, 0, 14, 0, 5, 40, 41},
-    {1, 0, 0, 0, 5, 0, 21},
-    {1, 0, 3, 0, 5, 0, 21},
-    {17, 0, 18, 0, 4, 0, 17},
-    {17, 0, 18, 0, 5, 0, 4},
-    {17, 0, 18, 0, 5, 0, 17},
-    {17, 0, 18, 0, 4, 0, 19},
-    {17, 0, 18, 0, 4, 0, 29},
-    {20, 0, 18, 0, 4, 0, 3},
-    {19, 0, 18, 0, 4, 0, 3},
-    {22, 0, 18, 0, 5, 0, 0},
-    {20, 0, 18, 0, 5, 0, 3},
-    {21, 0, 18, 0, 4, 0, 12},
-    {21, 0, 18, 0, 4, 0, 15},
-    {21, 0, 18, 0, 4, 0, 17},
-    {27, 0, 17, 0, 5, 0, 30},
-    {28, 0, 15, 0, 5, 0, 30},
-    {1, 0, 1, 0, 5, 0, 21},
-    {1, 0, 5, 0, 5, 0, 21},
-    {1, 0, 7, 0, 5, 0, 21},
-    {1, 0, 2, 0, 5, 0, 21},
-    {1, 0, 6, 0, 5, 0, 21},
-    {21, 0, 10, 0, 4, 0, 10},
-    {21, 0, 10, 0, 5, 0, 10},
-    {21, 0, 18, 0, 4, 0, 10},
-    {21, 0, 18, 0, 5, 0, 10},
-    {21, 0, 18, 0, 5, 0, 5},
-    {16, 0, 18, 0, 5, 0, 12},
-    {25, 0, 12, 0, 5, 0, 8},
-    {22, 0, 18, 1, 5, 0, 0},
-    {18, 0, 18, 1, 5, 0, 1},
-    {25, 0, 18, 0, 5, 0, 12},
-    {1, 0, 14, 0, 5, 0, 22},
-    {1, 0, 14, 0, 5, 0, 12},
-    {1, 0, 19, 0, 5, 0, 21},
-    {1, 0, 20, 0, 5, 0, 21},
-    {1, 0, 21, 0, 5, 0, 21},
-    {1, 0, 22, 0, 5, 0, 21},
-    {1, 0, 14, 0, 5, 0, 21},
-    {15, 0, 8, 0, 5, 0, 12},
-    {25, 0, 9, 0, 5, 0, 12},
-    {6, 0, 0, 0, 4, 1, 29},
-    {23, 0, 10, 0, 5, 0, 10},
-    {23, 0, 10, 0, 1, 0, 9},
-    {2, 0, 18, 0, 5, 102, 9},
-    {9, 0, 0, 0, 5, 0, 12},
-    {26, 0, 18, 0, 4, 0, 10},
-    {26, 0, 18, 0, 4, 0, 29},
-    {5, 0, 0, 0, 4, 0, 29},
-    {26, 0, 18, 0, 4, 0, 9},
-    {9, 0, 0, 0, 4, 1, 29},
-    {26, 0, 10, 0, 5, 0, 12},
-    {25, 0, 18, 1, 5, 0, 12},
-    {15, 0, 18, 0, 5, 0, 12},
-    {15, 0, 18, 0, 4, 0, 12},
-    {15, 0, 18, 0, 5, 0, 29},
-    {14, 0, 0, 0, 4, 1, 29},
-    {14, 0, 0, 0, 5, 1, 12},
-    {25, 0, 18, 1, 4, 0, 29},
-    {25, 0, 9, 0, 5, 0, 9},
-    {25, 0, 10, 0, 5, 0, 9},
-    {25, 0, 18, 0, 5, 0, 15},
-    {26, 0, 18, 0, 2, 0, 14},
-    {22, 0, 18, 1, 2, 0, 0},
-    {18, 0, 18, 1, 2, 0, 1},
-    {26, 0, 18, 0, 2, 0, 12},
-    {26, 0, 18, 0, 5, 0, 14},
-    {26, 0, 0, 0, 4, 0, 29},
-    {26, 0, 18, 0, 5, 0, 29},
-    {25, 0, 18, 0, 2, 0, 12},
-    {26, 0, 18, 0, 4, 0, 14},
-    {26, 0, 18, 0, 5, 0, 41},
-    {26, 0, 18, 0, 4, 0, 41},
-    {26, 0, 18, 0, 2, 0, 41},
-    {26, 0, 18, 0, 2, 0, 29},
-    {26, 0, 18, 0, 5, 0, 3},
-    {26, 0, 18, 0, 5, 0, 6},
-    {26, 0, 0, 0, 5, 52, 12},
-    {9, 0, 0, 0, 5, 56, 12},
-    {5, 0, 0, 0, 5, 56, 12},
-    {26, 0, 18, 0, 5, 54, 12},
-    {12, 230, 13, 0, 5, 54, 21},
-    {21, 0, 18, 0, 5, 54, 6},
-    {21, 0, 18, 0, 5, 54, 17},
-    {15, 0, 18, 0, 5, 54, 12},
-    {5, 0, 0, 0, 5, 23, 12},
-    {7, 0, 0, 0, 5, 57, 12},
-    {6, 0, 0, 0, 5, 57, 12},
-    {21, 0, 0, 0, 5, 57, 17},
-    {12, 9, 13, 0, 5, 57, 21},
-    {21, 0, 18, 0, 5, 0, 3},
-    {21, 0, 18, 0, 5, 0, 0},
-    {17, 0, 18, 0, 5, 0, 12},
-    {17, 0, 18, 0, 5, 0, 19},
-    {26, 0, 18, 0, 2, 35, 14},
-    {29, 0, 17, 0, 0, 0, 17},
-    {21, 0, 18, 0, 2, 0, 1},
-    {21, 0, 18, 0, 2, 0, 14},
-    {6, 0, 0, 0, 2, 35, 5},
-    {7, 0, 0, 0, 2, 0, 14},
-    {14, 0, 0, 0, 2, 35, 14},
-    {17, 0, 18, 0, 2, 0, 5},
-    {22, 0, 18, 0, 2, 0, 0},
-    {18, 0, 18, 0, 2, 0, 1},
-    {12, 218, 13, 0, 2, 40, 21},
-    {12, 228, 13, 0, 2, 40, 21},
-    {12, 232, 13, 0, 2, 40, 21},
-    {12, 222, 13, 0, 2, 40, 21},
-    {10, 224, 0, 0, 2, 24, 21},
-    {17, 0, 18, 0, 2, 0, 14},
-    {6, 0, 0, 0, 2, 0, 14},
-    {6, 0, 0, 0, 2, 0, 21},
-    {7, 0, 0, 0, 2, 0, 5},
-    {7, 0, 0, 0, 2, 32, 32},
-    {7, 0, 0, 0, 2, 32, 14},
-    {12, 8, 13, 0, 2, 40, 21},
-    {24, 0, 18, 0, 2, 0, 5},
-    {6, 0, 0, 0, 2, 32, 5},
-    {7, 0, 0, 0, 2, 33, 32},
-    {7, 0, 0, 0, 2, 33, 14},
-    {21, 0, 18, 0, 2, 0, 5},
-    {6, 0, 0, 0, 2, 0, 32},
-    {6, 0, 0, 0, 2, 33, 5},
-    {7, 0, 0, 0, 2, 34, 14},
-    {7, 0, 0, 0, 2, 24, 14},
-    {26, 0, 0, 0, 2, 0, 14},
-    {15, 0, 0, 0, 2, 0, 14},
-    {26, 0, 0, 0, 2, 24, 14},
-    {26, 0, 18, 0, 2, 24, 14},
-    {15, 0, 0, 0, 4, 0, 29},
-    {15, 0, 18, 0, 2, 0, 14},
-    {26, 0, 0, 0, 2, 33, 14},
-    {7, 0, 0, 0, 2, 35, 14},
-    {2, 0, 18, 0, 2, 102, 14},
-    {7, 0, 0, 0, 2, 36, 14},
-    {6, 0, 0, 0, 2, 36, 5},
-    {26, 0, 18, 0, 2, 36, 14},
-    {7, 0, 0, 0, 5, 82, 12},
-    {6, 0, 0, 0, 5, 82, 12},
-    {21, 0, 0, 0, 5, 82, 17},
-    {7, 0, 0, 0, 5, 69, 12},
-    {6, 0, 0, 0, 5, 69, 12},
-    {21, 0, 18, 0, 5, 69, 17},
-    {21, 0, 18, 0, 5, 69, 6},
-    {13, 0, 0, 0, 5, 69, 11},
-    {7, 0, 0, 0, 5, 3, 12},
-    {21, 0, 18, 0, 5, 3, 12},
-    {6, 0, 18, 0, 5, 3, 12},
-    {7, 0, 0, 0, 5, 83, 12},
-    {14, 0, 0, 0, 5, 83, 12},
-    {12, 230, 13, 0, 5, 83, 21},
-    {21, 0, 0, 0, 5, 83, 12},
-    {21, 0, 0, 0, 5, 83, 17},
-    {24, 0, 0, 0, 5, 0, 12},
-    {7, 0, 0, 0, 5, 58, 12},
-    {12, 0, 13, 0, 5, 58, 21},
-    {12, 9, 13, 0, 5, 58, 21},
-    {10, 0, 0, 0, 5, 58, 21},
-    {26, 0, 18, 0, 5, 58, 12},
-    {15, 0, 0, 0, 5, 0, 12},
-    {7, 0, 0, 0, 5, 64, 12},
-    {21, 0, 18, 0, 5, 64, 18},
-    {21, 0, 18, 0, 5, 64, 6},
-    {10, 0, 0, 0, 5, 70, 21},
-    {7, 0, 0, 0, 5, 70, 12},
-    {12, 9, 13, 0, 5, 70, 21},
-    {12, 0, 13, 0, 5, 70, 21},
-    {21, 0, 0, 0, 5, 70, 17},
-    {13, 0, 0, 0, 5, 70, 11},
-    {21, 0, 0, 0, 5, 9, 18},
-    {13, 0, 0, 0, 5, 71, 11},
-    {7, 0, 0, 0, 5, 71, 12},
-    {12, 0, 13, 0, 5, 71, 21},
-    {12, 220, 13, 0, 5, 71, 21},
-    {21, 0, 0, 0, 5, 71, 17},
-    {7, 0, 0, 0, 5, 72, 12},
-    {12, 0, 13, 0, 5, 72, 21},
-    {10, 0, 0, 0, 5, 72, 21},
-    {10, 9, 0, 0, 5, 72, 21},
-    {21, 0, 0, 0, 5, 72, 12},
-    {12, 0, 13, 0, 5, 84, 21},
-    {10, 0, 0, 0, 5, 84, 21},
-    {7, 0, 0, 0, 5, 84, 12},
-    {12, 7, 13, 0, 5, 84, 21},
-    {10, 9, 0, 0, 5, 84, 21},
-    {21, 0, 0, 0, 5, 84, 12},
-    {21, 0, 0, 0, 5, 84, 17},
-    {13, 0, 0, 0, 5, 84, 11},
-    {6, 0, 0, 0, 5, 22, 38},
-    {7, 0, 0, 0, 5, 76, 12},
-    {12, 0, 13, 0, 5, 76, 21},
-    {10, 0, 0, 0, 5, 76, 21},
-    {13, 0, 0, 0, 5, 76, 11},
-    {21, 0, 0, 0, 5, 76, 12},
-    {21, 0, 0, 0, 5, 76, 17},
-    {7, 0, 0, 0, 5, 78, 38},
-    {12, 230, 13, 0, 5, 78, 38},
-    {12, 220, 13, 0, 5, 78, 38},
-    {6, 0, 0, 0, 5, 78, 38},
-    {21, 0, 0, 0, 5, 78, 38},
-    {7, 0, 0, 0, 5, 85, 12},
-    {10, 0, 0, 0, 5, 85, 21},
-    {12, 0, 13, 0, 5, 85, 21},
-    {21, 0, 0, 0, 5, 85, 17},
-    {6, 0, 0, 0, 5, 85, 12},
-    {12, 9, 13, 0, 5, 85, 21},
-    {13, 0, 0, 0, 5, 85, 11},
-    {7, 0, 0, 0, 2, 24, 23},
-    {7, 0, 0, 0, 2, 24, 24},
-    {4, 0, 0, 0, 5, 102, 39},
-    {3, 0, 0, 0, 4, 102, 41},
-    {12, 26, 13, 0, 5, 5, 21},
-    {25, 0, 9, 0, 5, 5, 12},
-    {24, 0, 4, 0, 5, 6, 12},
-    {18, 0, 18, 0, 5, 0, 1},
-    {12, 0, 13, 0, 4, 40, 21},
-    {21, 0, 18, 0, 2, 0, 8},
-    {21, 0, 18, 0, 2, 0, 6},
-    {21, 0, 18, 0, 2, 0, 15},
-    {16, 0, 18, 0, 2, 0, 14},
-    {21, 0, 12, 0, 2, 0, 1},
-    {21, 0, 12, 0, 2, 0, 5},
-    {21, 0, 10, 0, 2, 0, 14},
-    {25, 0, 9, 0, 2, 0, 14},
-    {17, 0, 9, 0, 2, 0, 14},
-    {25, 0, 18, 1, 2, 0, 14},
-    {25, 0, 18, 0, 2, 0, 14},
-    {23, 0, 10, 0, 2, 0, 9},
-    {21, 0, 10, 0, 2, 0, 10},
-    {21, 0, 18, 0, 0, 0, 6},
-    {21, 0, 18, 0, 0, 0, 14},
-    {21, 0, 10, 0, 0, 0, 14},
-    {23, 0, 10, 0, 0, 0, 9},
-    {21, 0, 10, 0, 0, 0, 10},
-    {22, 0, 18, 1, 0, 0, 0},
-    {18, 0, 18, 1, 0, 0, 1},
-    {25, 0, 9, 0, 0, 0, 14},
-    {21, 0, 12, 0, 0, 0, 1},
-    {17, 0, 9, 0, 0, 0, 14},
-    {21, 0, 12, 0, 0, 0, 14},
-    {13, 0, 8, 0, 0, 0, 14},
-    {21, 0, 12, 0, 0, 0, 5},
-    {21, 0, 18, 0, 0, 0, 5},
-    {25, 0, 18, 1, 0, 0, 14},
-    {25, 0, 18, 0, 0, 0, 14},
-    {9, 0, 0, 0, 0, 1, 14},
-    {24, 0, 18, 0, 0, 0, 14},
-    {16, 0, 18, 0, 0, 0, 14},
-    {5, 0, 0, 0, 0, 1, 14},
-    {21, 0, 18, 0, 1, 0, 1},
-    {22, 0, 18, 1, 1, 0, 0},
-    {18, 0, 18, 1, 1, 0, 1},
-    {21, 0, 18, 0, 1, 0, 5},
-    {7, 0, 0, 0, 1, 33, 14},
-    {7, 0, 0, 0, 1, 33, 32},
-    {6, 0, 0, 0, 1, 0, 32},
-    {6, 0, 0, 0, 1, 0, 5},
-    {7, 0, 0, 0, 1, 24, 14},
-    {23, 0, 10, 0, 0, 0, 10},
-    {26, 0, 18, 0, 0, 0, 14},
-    {26, 0, 18, 0, 1, 0, 12},
-    {25, 0, 18, 0, 1, 0, 12},
-    {1, 0, 18, 0, 5, 0, 21},
-    {26, 0, 18, 0, 5, 0, 31},
-    {7, 0, 0, 0, 5, 47, 12},
-    {14, 0, 18, 0, 5, 2, 12},
-    {15, 0, 18, 0, 5, 2, 12},
-    {26, 0, 18, 0, 5, 2, 12},
-    {26, 0, 0, 0, 5, 2, 12},
-    {7, 0, 0, 0, 5, 73, 12},
-    {7, 0, 0, 0, 5, 74, 12},
-    {7, 0, 0, 0, 5, 37, 12},
-    {15, 0, 0, 0, 5, 37, 12},
-    {7, 0, 0, 0, 5, 38, 12},
-    {14, 0, 0, 0, 5, 38, 12},
-    {7, 0, 0, 0, 5, 118, 12},
-    {12, 230, 13, 0, 5, 118, 21},
-    {7, 0, 0, 0, 5, 48, 12},
-    {21, 0, 0, 0, 5, 48, 17},
-    {7, 0, 0, 0, 5, 59, 12},
-    {21, 0, 0, 0, 5, 59, 17},
-    {14, 0, 0, 0, 5, 59, 12},
-    {9, 0, 0, 0, 5, 39, 12},
-    {5, 0, 0, 0, 5, 39, 12},
-    {7, 0, 0, 0, 5, 49, 12},
-    {7, 0, 0, 0, 5, 50, 12},
-    {13, 0, 0, 0, 5, 50, 11},
-    {9, 0, 0, 0, 5, 136, 12},
-    {5, 0, 0, 0, 5, 136, 12},
-    {7, 0, 0, 0, 5, 106, 12},
-    {7, 0, 0, 0, 5, 104, 12},
-    {21, 0, 0, 0, 5, 104, 12},
-    {7, 0, 0, 0, 5, 110, 12},
-    {7, 0, 3, 0, 5, 51, 12},
-    {7, 0, 3, 0, 5, 86, 12},
-    {21, 0, 3, 0, 5, 86, 17},
-    {15, 0, 3, 0, 5, 86, 12},
-    {7, 0, 3, 0, 5, 120, 12},
-    {26, 0, 3, 0, 5, 120, 12},
-    {15, 0, 3, 0, 5, 120, 12},
-    {7, 0, 3, 0, 5, 116, 12},
-    {15, 0, 3, 0, 5, 116, 12},
-    {7, 0, 3, 0, 5, 128, 12},
-    {15, 0, 3, 0, 5, 128, 12},
-    {7, 0, 3, 0, 5, 63, 12},
-    {15, 0, 3, 0, 5, 63, 12},
-    {21, 0, 18, 0, 5, 63, 17},
-    {7, 0, 3, 0, 5, 75, 12},
-    {21, 0, 3, 0, 5, 75, 12},
-    {7, 0, 3, 0, 5, 97, 12},
-    {7, 0, 3, 0, 5, 96, 12},
-    {15, 0, 3, 0, 5, 96, 12},
-    {7, 0, 3, 0, 5, 60, 12},
-    {12, 0, 13, 0, 5, 60, 21},
-    {12, 220, 13, 0, 5, 60, 21},
-    {12, 230, 13, 0, 5, 60, 21},
-    {12, 1, 13, 0, 5, 60, 21},
-    {12, 9, 13, 0, 5, 60, 21},
-    {15, 0, 3, 0, 5, 60, 12},
-    {21, 0, 3, 0, 5, 60, 17},
-    {21, 0, 3, 0, 5, 60, 12},
-    {7, 0, 3, 0, 5, 87, 12},
-    {15, 0, 3, 0, 5, 87, 12},
-    {21, 0, 3, 0, 5, 87, 12},
-    {7, 0, 3, 0, 5, 117, 12},
-    {15, 0, 3, 0, 5, 117, 12},
-    {7, 0, 3, 0, 5, 112, 12},
-    {26, 0, 3, 0, 5, 112, 12},
-    {12, 230, 13, 0, 5, 112, 21},
-    {12, 220, 13, 0, 5, 112, 21},
-    {15, 0, 3, 0, 5, 112, 12},
-    {21, 0, 3, 0, 5, 112, 17},
-    {21, 0, 3, 0, 5, 112, 15},
-    {7, 0, 3, 0, 5, 79, 12},
-    {21, 0, 18, 0, 5, 79, 17},
-    {7, 0, 3, 0, 5, 88, 12},
-    {15, 0, 3, 0, 5, 88, 12},
-    {7, 0, 3, 0, 5, 89, 12},
-    {15, 0, 3, 0, 5, 89, 12},
-    {7, 0, 3, 0, 5, 122, 12},
-    {21, 0, 3, 0, 5, 122, 12},
-    {15, 0, 3, 0, 5, 122, 12},
-    {7, 0, 3, 0, 5, 90, 12},
-    {9, 0, 3, 0, 5, 130, 12},
-    {5, 0, 3, 0, 5, 130, 12},
-    {15, 0, 3, 0, 5, 130, 12},
-    {15, 0, 11, 0, 5, 6, 12},
-    {10, 0, 0, 0, 5, 93, 21},
-    {12, 0, 13, 0, 5, 93, 21},
-    {7, 0, 0, 0, 5, 93, 12},
-    {12, 9, 13, 0, 5, 93, 21},
-    {21, 0, 0, 0, 5, 93, 17},
-    {21, 0, 0, 0, 5, 93, 12},
-    {15, 0, 18, 0, 5, 93, 12},
-    {13, 0, 0, 0, 5, 93, 11},
-    {12, 0, 13, 0, 5, 91, 21},
-    {10, 0, 0, 0, 5, 91, 21},
-    {7, 0, 0, 0, 5, 91, 12},
-    {12, 9, 13, 0, 5, 91, 21},
-    {12, 7, 13, 0, 5, 91, 21},
-    {21, 0, 0, 0, 5, 91, 12},
-    {1, 0, 0, 0, 5, 91, 12},
-    {21, 0, 0, 0, 5, 91, 17},
-    {7, 0, 0, 0, 5, 100, 12},
-    {13, 0, 0, 0, 5, 100, 11},
-    {12, 230, 13, 0, 5, 95, 21},
-    {7, 0, 0, 0, 5, 95, 12},
-    {12, 0, 13, 0, 5, 95, 21},
-    {10, 0, 0, 0, 5, 95, 21},
-    {12, 9, 13, 0, 5, 95, 21},
-    {13, 0, 0, 0, 5, 95, 11},
-    {21, 0, 0, 0, 5, 95, 17},
-    {7, 0, 0, 0, 5, 111, 12},
-    {12, 7, 13, 0, 5, 111, 21},
-    {21, 0, 0, 0, 5, 111, 12},
-    {21, 0, 0, 0, 5, 111, 18},
-    {12, 0, 13, 0, 5, 99, 21},
-    {10, 0, 0, 0, 5, 99, 21},
-    {7, 0, 0, 0, 5, 99, 12},
-    {10, 9, 0, 0, 5, 99, 21},
-    {21, 0, 0, 0, 5, 99, 17},
-    {21, 0, 0, 0, 5, 99, 12},
-    {12, 7, 13, 0, 5, 99, 21},
-    {13, 0, 0, 0, 5, 99, 11},
-    {21, 0, 0, 0, 5, 99, 18},
-    {15, 0, 0, 0, 5, 18, 12},
-    {7, 0, 0, 0, 5, 108, 12},
-    {10, 0, 0, 0, 5, 108, 21},
-    {12, 0, 13, 0, 5, 108, 21},
-    {10, 9, 0, 0, 5, 108, 21},
-    {12, 7, 13, 0, 5, 108, 21},
-    {21, 0, 0, 0, 5, 108, 17},
-    {21, 0, 0, 0, 5, 108, 12},
-    {7, 0, 0, 0, 5, 129, 12},
-    {21, 0, 0, 0, 5, 129, 17},
-    {7, 0, 0, 0, 5, 109, 12},
-    {12, 0, 13, 0, 5, 109, 21},
-    {10, 0, 0, 0, 5, 109, 21},
-    {12, 7, 13, 0, 5, 109, 21},
-    {12, 9, 13, 0, 5, 109, 21},
-    {13, 0, 0, 0, 5, 109, 11},
-    {12, 0, 13, 0, 5, 107, 21},
-    {10, 0, 0, 0, 5, 107, 21},
-    {7, 0, 0, 0, 5, 107, 12},
-    {12, 7, 13, 0, 5, 107, 21},
-    {10, 9, 0, 0, 5, 107, 21},
-    {12, 230, 13, 0, 5, 107, 21},
-    {7, 0, 0, 0, 5, 135, 12},
-    {10, 0, 0, 0, 5, 135, 21},
-    {12, 0, 13, 0, 5, 135, 21},
-    {12, 9, 13, 0, 5, 135, 21},
-    {12, 7, 13, 0, 5, 135, 21},
-    {21, 0, 0, 0, 5, 135, 17},
-    {21, 0, 0, 0, 5, 135, 12},
-    {13, 0, 0, 0, 5, 135, 11},
-    {7, 0, 0, 0, 5, 124, 12},
-    {10, 0, 0, 0, 5, 124, 21},
-    {12, 0, 13, 0, 5, 124, 21},
-    {12, 9, 13, 0, 5, 124, 21},
-    {12, 7, 13, 0, 5, 124, 21},
-    {21, 0, 0, 0, 5, 124, 12},
-    {13, 0, 0, 0, 5, 124, 11},
-    {7, 0, 0, 0, 5, 123, 12},
-    {10, 0, 0, 0, 5, 123, 21},
-    {12, 0, 13, 0, 5, 123, 21},
-    {12, 9, 13, 0, 5, 123, 21},
-    {12, 7, 13, 0, 5, 123, 21},
-    {21, 0, 0, 0, 5, 123, 18},
-    {21, 0, 0, 0, 5, 123, 17},
-    {21, 0, 0, 0, 5, 123, 6},
-    {21, 0, 0, 0, 5, 123, 12},
-    {7, 0, 0, 0, 5, 114, 12},
-    {10, 0, 0, 0, 5, 114, 21},
-    {12, 0, 13, 0, 5, 114, 21},
-    {12, 9, 13, 0, 5, 114, 21},
-    {21, 0, 0, 0, 5, 114, 17},
-    {21, 0, 0, 0, 5, 114, 12},
-    {13, 0, 0, 0, 5, 114, 11},
-    {21, 0, 18, 0, 5, 31, 18},
-    {7, 0, 0, 0, 5, 101, 12},
-    {12, 0, 13, 0, 5, 101, 21},
-    {10, 0, 0, 0, 5, 101, 21},
-    {10, 9, 0, 0, 5, 101, 21},
-    {12, 7, 13, 0, 5, 101, 21},
-    {13, 0, 0, 0, 5, 101, 11},
-    {7, 0, 0, 0, 5, 126, 38},
-    {12, 0, 13, 0, 5, 126, 38},
-    {10, 0, 0, 0, 5, 126, 38},
-    {12, 9, 13, 0, 5, 126, 38},
-    {13, 0, 0, 0, 5, 126, 11},
-    {15, 0, 0, 0, 5, 126, 38},
-    {21, 0, 0, 0, 5, 126, 17},
-    {26, 0, 0, 0, 5, 126, 38},
-    {9, 0, 0, 0, 5, 125, 12},
-    {5, 0, 0, 0, 5, 125, 12},
-    {13, 0, 0, 0, 5, 125, 11},
-    {15, 0, 0, 0, 5, 125, 12},
-    {7, 0, 0, 0, 5, 125, 12},
-    {7, 0, 0, 0, 5, 121, 12},
-    {7, 0, 0, 0, 5, 133, 12},
-    {10, 0, 0, 0, 5, 133, 21},
-    {12, 0, 13, 0, 5, 133, 21},
-    {12, 9, 0, 0, 5, 133, 21},
-    {21, 0, 0, 0, 5, 133, 17},
-    {13, 0, 0, 0, 5, 133, 11},
-    {15, 0, 0, 0, 5, 133, 12},
-    {21, 0, 0, 0, 5, 134, 18},
-    {21, 0, 0, 0, 5, 134, 6},
-    {7, 0, 0, 0, 5, 134, 12},
-    {12, 0, 13, 0, 5, 134, 21},
-    {10, 0, 0, 0, 5, 134, 21},
-    {7, 0, 0, 0, 5, 62, 12},
-    {14, 0, 0, 0, 5, 62, 12},
-    {21, 0, 0, 0, 5, 62, 17},
-    {7, 0, 0, 0, 5, 80, 12},
-    {7, 0, 0, 0, 5, 80, 0},
-    {7, 0, 0, 0, 5, 80, 1},
-    {7, 0, 0, 0, 5, 127, 12},
-    {7, 0, 0, 0, 5, 127, 0},
-    {7, 0, 0, 0, 5, 127, 1},
-    {7, 0, 0, 0, 5, 115, 12},
-    {13, 0, 0, 0, 5, 115, 11},
-    {21, 0, 0, 0, 5, 115, 17},
-    {7, 0, 0, 0, 5, 103, 12},
-    {12, 1, 13, 0, 5, 103, 21},
-    {21, 0, 0, 0, 5, 103, 17},
-    {7, 0, 0, 0, 5, 119, 12},
-    {12, 230, 13, 0, 5, 119, 21},
-    {21, 0, 0, 0, 5, 119, 17},
-    {21, 0, 0, 0, 5, 119, 12},
-    {26, 0, 0, 0, 5, 119, 12},
-    {6, 0, 0, 0, 5, 119, 12},
-    {13, 0, 0, 0, 5, 119, 11},
-    {15, 0, 0, 0, 5, 119, 12},
-    {7, 0, 0, 0, 5, 98, 12},
-    {10, 0, 0, 0, 5, 98, 21},
-    {12, 0, 13, 0, 5, 98, 21},
-    {6, 0, 0, 0, 5, 98, 12},
-    {6, 0, 0, 0, 2, 137, 5},
-    {7, 0, 0, 0, 2, 137, 14},
-    {7, 0, 0, 0, 5, 105, 12},
-    {26, 0, 0, 0, 5, 105, 12},
-    {12, 0, 13, 0, 5, 105, 21},
-    {12, 1, 13, 0, 5, 105, 21},
-    {21, 0, 0, 0, 5, 105, 17},
-    {10, 216, 0, 0, 5, 0, 21},
-    {10, 226, 0, 0, 5, 0, 21},
-    {12, 230, 13, 0, 5, 2, 21},
-    {25, 0, 0, 0, 5, 0, 12},
-    {13, 0, 8, 0, 5, 0, 11},
-    {26, 0, 0, 0, 5, 131, 12},
-    {12, 0, 13, 0, 5, 131, 21},
-    {21, 0, 0, 0, 5, 131, 17},
-    {21, 0, 0, 0, 5, 131, 12},
-    {12, 230, 13, 0, 5, 56, 21},
-    {7, 0, 3, 0, 5, 113, 12},
-    {15, 0, 3, 0, 5, 113, 12},
-    {12, 220, 13, 0, 5, 113, 21},
-    {9, 0, 3, 0, 5, 132, 12},
-    {5, 0, 3, 0, 5, 132, 12},
-    {12, 230, 13, 0, 5, 132, 21},
-    {12, 7, 13, 0, 5, 132, 21},
-    {13, 0, 3, 0, 5, 132, 11},
-    {21, 0, 3, 0, 5, 132, 0},
-    {2, 0, 18, 0, 5, 102, 14},
-    {26, 0, 0, 0, 2, 0, 29},
-    {26, 0, 0, 0, 5, 0, 28},
-    {26, 0, 0, 0, 2, 32, 14},
-    {24, 0, 18, 0, 2, 0, 41},
-    {26, 0, 18, 0, 5, 0, 5},
+    {2, 0, 18, 5, 102, 39},
+    {0, 0, 14, 5, 0, 21},
+    {0, 0, 16, 5, 0, 17},
+    {0, 0, 15, 5, 0, 34},
+    {0, 0, 16, 5, 0, 30},
+    {0, 0, 17, 5, 0, 30},
+    {0, 0, 15, 5, 0, 33},
+    {0, 0, 15, 5, 0, 21},
+    {0, 0, 16, 5, 0, 21},
+    {29, 0, 17, 3, 0, 38},
+    {21, 0, 18, 3, 0, 6},
+    {21, 0, 18, 3, 0, 3},
+    {21, 0, 10, 3, 0, 12},
+    {23, 0, 10, 3, 0, 9},
+    {21, 0, 10, 3, 0, 10},
+    {21, 0, 18, 3, 0, 12},
+    {22, 0, 18, 3, 0, 0},
+    {18, 0, 18, 3, 0, 2},
+    {25, 0, 9, 3, 0, 9},
+    {21, 0, 12, 3, 0, 8},
+    {17, 0, 9, 3, 0, 16},
+    {21, 0, 12, 3, 0, 7},
+    {13, 0, 8, 3, 0, 11},
+    {21, 0, 18, 3, 0, 8},
+    {25, 0, 18, 3, 0, 12},
+    {9, 0, 0, 3, 1, 12},
+    {21, 0, 18, 3, 0, 9},
+    {24, 0, 18, 3, 0, 12},
+    {16, 0, 18, 3, 0, 12},
+    {5, 0, 0, 3, 1, 12},
+    {25, 0, 18, 3, 0, 17},
+    {18, 0, 18, 3, 0, 1},
+    {0, 0, 15, 5, 0, 35},
+    {29, 0, 12, 5, 0, 4},
+    {21, 0, 18, 4, 0, 0},
+    {23, 0, 10, 3, 0, 10},
+    {23, 0, 10, 4, 0, 9},
+    {26, 0, 18, 3, 0, 12},
+    {21, 0, 18, 4, 0, 29},
+    {24, 0, 18, 4, 0, 29},
+    {26, 0, 18, 5, 0, 12},
+    {7, 0, 0, 4, 1, 29},
+    {20, 0, 18, 5, 0, 3},
+    {1, 0, 14, 4, 0, 17},
+    {26, 0, 18, 4, 0, 12},
+    {26, 0, 10, 4, 0, 10},
+    {25, 0, 10, 4, 0, 9},
+    {15, 0, 8, 4, 0, 29},
+    {24, 0, 18, 4, 0, 18},
+    {5, 0, 0, 5, 0, 12},
+    {19, 0, 18, 5, 0, 3},
+    {15, 0, 18, 4, 0, 29},
+    {9, 0, 0, 5, 1, 12},
+    {9, 0, 0, 4, 1, 12},
+    {25, 0, 18, 4, 0, 29},
+    {5, 0, 0, 4, 1, 12},
+    {5, 0, 0, 5, 1, 12},
+    {7, 0, 0, 5, 1, 12},
+    {8, 0, 0, 5, 1, 12},
+    {6, 0, 0, 5, 1, 12},
+    {6, 0, 18, 5, 0, 12},
+    {6, 0, 0, 5, 0, 12},
+    {24, 0, 18, 5, 0, 12},
+    {24, 0, 18, 4, 0, 12},
+    {6, 0, 18, 4, 0, 29},
+    {6, 0, 18, 5, 0, 18},
+    {6, 0, 0, 4, 0, 29},
+    {24, 0, 18, 5, 34, 12},
+    {12, 230, 13, 4, 40, 21},
+    {12, 232, 13, 4, 40, 21},
+    {12, 220, 13, 4, 40, 21},
+    {12, 216, 13, 4, 40, 21},
+    {12, 202, 13, 4, 40, 21},
+    {12, 1, 13, 4, 40, 21},
+    {12, 240, 13, 4, 40, 21},
+    {12, 0, 13, 4, 40, 4},
+    {12, 233, 13, 4, 40, 4},
+    {12, 234, 13, 4, 40, 4},
+    {9, 0, 0, 5, 2, 12},
+    {5, 0, 0, 5, 2, 12},
+    {24, 0, 18, 5, 2, 12},
+    {2, 0, 18, 5, 102, 39},
+    {6, 0, 0, 5, 2, 12},
+    {21, 0, 18, 5, 0, 8},
+    {21, 0, 18, 5, 0, 12},
+    {9, 0, 0, 4, 2, 12},
+    {5, 0, 0, 4, 2, 12},
+    {9, 0, 0, 5, 54, 12},
+    {5, 0, 0, 5, 54, 12},
+    {25, 0, 18, 5, 2, 12},
+    {9, 0, 0, 5, 3, 12},
+    {9, 0, 0, 4, 3, 12},
+    {5, 0, 0, 4, 3, 12},
+    {5, 0, 0, 5, 3, 12},
+    {26, 0, 0, 5, 3, 12},
+    {12, 230, 13, 5, 3, 21},
+    {12, 230, 13, 5, 40, 21},
+    {11, 0, 13, 5, 3, 21},
+    {9, 0, 0, 5, 4, 12},
+    {6, 0, 0, 5, 4, 12},
+    {21, 0, 0, 5, 4, 12},
+    {5, 0, 0, 5, 4, 12},
+    {21, 0, 0, 5, 0, 8},
+    {17, 0, 18, 5, 4, 17},
+    {26, 0, 18, 5, 4, 12},
+    {23, 0, 10, 5, 4, 9},
+    {12, 220, 13, 5, 5, 21},
+    {12, 230, 13, 5, 5, 21},
+    {12, 222, 13, 5, 5, 21},
+    {12, 228, 13, 5, 5, 21},
+    {12, 10, 13, 5, 5, 21},
+    {12, 11, 13, 5, 5, 21},
+    {12, 12, 13, 5, 5, 21},
+    {12, 13, 13, 5, 5, 21},
+    {12, 14, 13, 5, 5, 21},
+    {12, 15, 13, 5, 5, 21},
+    {12, 16, 13, 5, 5, 21},
+    {12, 17, 13, 5, 5, 21},
+    {12, 18, 13, 5, 5, 21},
+    {12, 19, 13, 5, 5, 21},
+    {12, 20, 13, 5, 5, 21},
+    {12, 21, 13, 5, 5, 21},
+    {12, 22, 13, 5, 5, 21},
+    {17, 0, 3, 5, 5, 17},
+    {12, 23, 13, 5, 5, 21},
+    {21, 0, 3, 5, 5, 12},
+    {12, 24, 13, 5, 5, 21},
+    {12, 25, 13, 5, 5, 21},
+    {21, 0, 3, 5, 5, 6},
+    {7, 0, 3, 5, 5, 13},
+    {1, 0, 11, 5, 6, 12},
+    {1, 0, 11, 5, 0, 12},
+    {25, 0, 18, 5, 6, 12},
+    {25, 0, 4, 5, 6, 12},
+    {21, 0, 10, 5, 6, 10},
+    {23, 0, 4, 5, 6, 10},
+    {21, 0, 12, 5, 0, 8},
+    {21, 0, 4, 5, 6, 8},
+    {26, 0, 18, 5, 6, 12},
+    {12, 230, 13, 5, 6, 21},
+    {12, 30, 13, 5, 6, 21},
+    {12, 31, 13, 5, 6, 21},
+    {12, 32, 13, 5, 6, 21},
+    {21, 0, 4, 5, 0, 6},
+    {1, 0, 4, 5, 6, 21},
+    {21, 0, 4, 5, 6, 6},
+    {7, 0, 4, 5, 6, 12},
+    {6, 0, 4, 5, 0, 12},
+    {12, 27, 13, 5, 40, 21},
+    {12, 28, 13, 5, 40, 21},
+    {12, 29, 13, 5, 40, 21},
+    {12, 30, 13, 5, 40, 21},
+    {12, 31, 13, 5, 40, 21},
+    {12, 32, 13, 5, 40, 21},
+    {12, 33, 13, 5, 40, 21},
+    {12, 34, 13, 5, 40, 21},
+    {12, 220, 13, 5, 40, 21},
+    {12, 220, 13, 5, 6, 21},
+    {13, 0, 11, 5, 6, 11},
+    {21, 0, 11, 5, 6, 11},
+    {21, 0, 4, 5, 6, 12},
+    {12, 35, 13, 5, 40, 21},
+    {6, 0, 4, 5, 6, 12},
+    {13, 0, 8, 5, 6, 11},
+    {26, 0, 4, 5, 6, 12},
+    {21, 0, 4, 5, 7, 12},
+    {1, 0, 4, 5, 7, 12},
+    {7, 0, 4, 5, 7, 12},
+    {12, 36, 13, 5, 7, 21},
+    {12, 230, 13, 5, 7, 21},
+    {12, 220, 13, 5, 7, 21},
+    {7, 0, 4, 5, 8, 12},
+    {12, 0, 13, 5, 8, 21},
+    {13, 0, 3, 5, 65, 11},
+    {7, 0, 3, 5, 65, 12},
+    {12, 230, 13, 5, 65, 21},
+    {12, 220, 13, 5, 65, 21},
+    {6, 0, 3, 5, 65, 12},
+    {26, 0, 18, 5, 65, 12},
+    {21, 0, 18, 5, 65, 12},
+    {21, 0, 18, 5, 65, 8},
+    {21, 0, 18, 5, 65, 6},
+    {7, 0, 3, 5, 81, 12},
+    {12, 230, 13, 5, 81, 21},
+    {6, 0, 3, 5, 81, 12},
+    {21, 0, 3, 5, 81, 12},
+    {7, 0, 3, 5, 94, 12},
+    {12, 220, 13, 5, 94, 21},
+    {21, 0, 3, 5, 94, 12},
+    {12, 27, 13, 5, 6, 21},
+    {12, 28, 13, 5, 6, 21},
+    {12, 29, 13, 5, 6, 21},
+    {12, 0, 13, 5, 9, 21},
+    {10, 0, 0, 5, 9, 21},
+    {7, 0, 0, 5, 9, 12},
+    {12, 7, 13, 5, 9, 21},
+    {12, 9, 13, 5, 9, 21},
+    {12, 230, 13, 5, 9, 21},
+    {21, 0, 0, 5, 0, 17},
+    {13, 0, 0, 5, 9, 11},
+    {21, 0, 0, 5, 9, 12},
+    {6, 0, 0, 5, 9, 12},
+    {7, 0, 0, 5, 10, 12},
+    {12, 0, 13, 5, 10, 21},
+    {10, 0, 0, 5, 10, 21},
+    {12, 7, 13, 5, 10, 21},
+    {12, 9, 13, 5, 10, 21},
+    {13, 0, 0, 5, 10, 11},
+    {23, 0, 10, 5, 10, 10},
+    {15, 0, 0, 5, 10, 12},
+    {15, 0, 0, 5, 10, 10},
+    {26, 0, 0, 5, 10, 12},
+    {23, 0, 10, 5, 10, 9},
+    {21, 0, 0, 5, 10, 12},
+    {12, 0, 13, 5, 11, 21},
+    {10, 0, 0, 5, 11, 21},
+    {7, 0, 0, 5, 11, 12},
+    {12, 7, 13, 5, 11, 21},
+    {12, 9, 13, 5, 11, 21},
+    {13, 0, 0, 5, 11, 11},
+    {12, 0, 13, 5, 12, 21},
+    {10, 0, 0, 5, 12, 21},
+    {7, 0, 0, 5, 12, 12},
+    {12, 7, 13, 5, 12, 21},
+    {12, 9, 13, 5, 12, 21},
+    {13, 0, 0, 5, 12, 11},
+    {21, 0, 0, 5, 12, 12},
+    {23, 0, 10, 5, 12, 9},
+    {12, 0, 13, 5, 13, 21},
+    {10, 0, 0, 5, 13, 21},
+    {7, 0, 0, 5, 13, 12},
+    {12, 7, 13, 5, 13, 21},
+    {12, 9, 13, 5, 13, 21},
+    {13, 0, 0, 5, 13, 11},
+    {26, 0, 0, 5, 13, 12},
+    {15, 0, 0, 5, 13, 12},
+    {12, 0, 13, 5, 14, 21},
+    {7, 0, 0, 5, 14, 12},
+    {10, 0, 0, 5, 14, 21},
+    {12, 9, 13, 5, 14, 21},
+    {13, 0, 0, 5, 14, 11},
+    {15, 0, 0, 5, 14, 12},
+    {26, 0, 18, 5, 14, 12},
+    {23, 0, 10, 5, 14, 9},
+    {12, 0, 13, 5, 15, 21},
+    {10, 0, 0, 5, 15, 21},
+    {7, 0, 0, 5, 15, 12},
+    {12, 9, 13, 5, 15, 21},
+    {12, 84, 13, 5, 15, 21},
+    {12, 91, 13, 5, 15, 21},
+    {13, 0, 0, 5, 15, 11},
+    {15, 0, 18, 5, 15, 12},
+    {26, 0, 0, 5, 15, 12},
+    {7, 0, 0, 5, 16, 12},
+    {12, 0, 13, 5, 16, 21},
+    {10, 0, 0, 5, 16, 21},
+    {12, 7, 13, 5, 16, 21},
+    {12, 0, 0, 5, 16, 21},
+    {12, 9, 13, 5, 16, 21},
+    {13, 0, 0, 5, 16, 11},
+    {12, 0, 13, 5, 17, 21},
+    {10, 0, 0, 5, 17, 21},
+    {7, 0, 0, 5, 17, 12},
+    {12, 9, 13, 5, 17, 21},
+    {26, 0, 0, 5, 17, 12},
+    {15, 0, 0, 5, 17, 12},
+    {13, 0, 0, 5, 17, 11},
+    {26, 0, 0, 5, 17, 10},
+    {10, 0, 0, 5, 18, 21},
+    {7, 0, 0, 5, 18, 12},
+    {12, 9, 13, 5, 18, 21},
+    {12, 0, 13, 5, 18, 21},
+    {13, 0, 0, 5, 18, 11},
+    {21, 0, 0, 5, 18, 12},
+    {7, 0, 0, 5, 19, 36},
+    {12, 0, 13, 5, 19, 36},
+    {12, 103, 13, 5, 19, 36},
+    {12, 9, 13, 5, 19, 36},
+    {23, 0, 10, 5, 0, 9},
+    {6, 0, 0, 5, 19, 36},
+    {12, 107, 13, 5, 19, 36},
+    {21, 0, 0, 5, 19, 12},
+    {13, 0, 0, 5, 19, 11},
+    {21, 0, 0, 5, 19, 17},
+    {7, 0, 0, 5, 20, 36},
+    {12, 0, 13, 5, 20, 36},
+    {12, 118, 13, 5, 20, 36},
+    {6, 0, 0, 5, 20, 36},
+    {12, 122, 13, 5, 20, 36},
+    {13, 0, 0, 5, 20, 11},
+    {7, 0, 0, 5, 21, 12},
+    {26, 0, 0, 5, 21, 18},
+    {21, 0, 0, 5, 21, 18},
+    {21, 0, 0, 5, 21, 12},
+    {21, 0, 0, 5, 21, 4},
+    {21, 0, 0, 5, 21, 17},
+    {21, 0, 0, 5, 21, 6},
+    {26, 0, 0, 5, 21, 12},
+    {12, 220, 13, 5, 21, 21},
+    {13, 0, 0, 5, 21, 11},
+    {15, 0, 0, 5, 21, 12},
+    {26, 0, 0, 5, 21, 17},
+    {12, 216, 13, 5, 21, 21},
+    {22, 0, 18, 5, 21, 0},
+    {18, 0, 18, 5, 21, 1},
+    {10, 0, 0, 5, 21, 21},
+    {12, 129, 13, 5, 21, 21},
+    {12, 130, 13, 5, 21, 21},
+    {12, 0, 13, 5, 21, 21},
+    {12, 132, 13, 5, 21, 21},
+    {10, 0, 0, 5, 21, 17},
+    {12, 230, 13, 5, 21, 21},
+    {12, 9, 13, 5, 21, 21},
+    {26, 0, 0, 5, 0, 12},
+    {7, 0, 0, 5, 22, 36},
+    {10, 0, 0, 5, 22, 36},
+    {12, 0, 13, 5, 22, 36},
+    {12, 7, 13, 5, 22, 36},
+    {12, 9, 13, 5, 22, 36},
+    {13, 0, 0, 5, 22, 11},
+    {21, 0, 0, 5, 22, 17},
+    {21, 0, 0, 5, 22, 12},
+    {12, 220, 13, 5, 22, 36},
+    {26, 0, 0, 5, 22, 36},
+    {9, 0, 0, 5, 23, 12},
+    {7, 0, 0, 5, 23, 12},
+    {21, 0, 0, 5, 0, 12},
+    {6, 0, 0, 5, 23, 12},
+    {7, 0, 0, 2, 24, 25},
+    {7, 0, 0, 5, 24, 26},
+    {7, 0, 0, 5, 24, 27},
+    {7, 0, 0, 5, 25, 12},
+    {12, 230, 13, 5, 25, 21},
+    {21, 0, 0, 5, 25, 12},
+    {21, 0, 0, 5, 25, 17},
+    {15, 0, 0, 5, 25, 12},
+    {26, 0, 18, 5, 25, 12},
+    {9, 0, 0, 5, 26, 12},
+    {5, 0, 0, 5, 26, 12},
+    {17, 0, 18, 5, 27, 17},
+    {7, 0, 0, 5, 27, 12},
+    {21, 0, 0, 5, 27, 12},
+    {29, 0, 17, 5, 28, 17},
+    {7, 0, 0, 5, 28, 12},
+    {22, 0, 18, 5, 28, 0},
+    {18, 0, 18, 5, 28, 1},
+    {7, 0, 0, 5, 29, 12},
+    {14, 0, 0, 5, 29, 12},
+    {7, 0, 0, 5, 41, 12},
+    {12, 0, 13, 5, 41, 21},
+    {12, 9, 13, 5, 41, 21},
+    {7, 0, 0, 5, 42, 12},
+    {12, 0, 13, 5, 42, 21},
+    {12, 9, 13, 5, 42, 21},
+    {7, 0, 0, 5, 43, 12},
+    {12, 0, 13, 5, 43, 21},
+    {7, 0, 0, 5, 44, 12},
+    {12, 0, 13, 5, 44, 21},
+    {7, 0, 0, 5, 30, 36},
+    {12, 0, 13, 5, 30, 36},
+    {10, 0, 0, 5, 30, 36},
+    {12, 9, 13, 5, 30, 36},
+    {21, 0, 0, 5, 30, 17},
+    {21, 0, 0, 5, 30, 5},
+    {6, 0, 0, 5, 30, 36},
+    {21, 0, 0, 5, 30, 12},
+    {23, 0, 10, 5, 30, 9},
+    {12, 230, 13, 5, 30, 36},
+    {13, 0, 0, 5, 30, 11},
+    {15, 0, 18, 5, 30, 12},
+    {21, 0, 18, 5, 31, 12},
+    {21, 0, 18, 5, 0, 6},
+    {21, 0, 18, 5, 31, 17},
+    {21, 0, 18, 5, 0, 17},
+    {17, 0, 18, 5, 31, 18},
+    {21, 0, 18, 5, 31, 6},
+    {12, 0, 13, 5, 31, 21},
+    {1, 0, 14, 5, 31, 4},
+    {13, 0, 0, 5, 31, 11},
+    {7, 0, 0, 5, 31, 12},
+    {6, 0, 0, 5, 31, 12},
+    {12, 228, 13, 5, 31, 21},
+    {7, 0, 0, 5, 45, 12},
+    {12, 0, 13, 5, 45, 21},
+    {10, 0, 0, 5, 45, 21},
+    {12, 222, 13, 5, 45, 21},
+    {12, 230, 13, 5, 45, 21},
+    {12, 220, 13, 5, 45, 21},
+    {26, 0, 18, 5, 45, 12},
+    {21, 0, 18, 5, 45, 6},
+    {13, 0, 0, 5, 45, 11},
+    {7, 0, 0, 5, 46, 36},
+    {7, 0, 0, 5, 55, 36},
+    {13, 0, 0, 5, 55, 11},
+    {15, 0, 0, 5, 55, 36},
+    {26, 0, 18, 5, 55, 36},
+    {26, 0, 18, 5, 30, 12},
+    {7, 0, 0, 5, 53, 12},
+    {12, 230, 13, 5, 53, 21},
+    {12, 220, 13, 5, 53, 21},
+    {10, 0, 0, 5, 53, 21},
+    {12, 0, 13, 5, 53, 21},
+    {21, 0, 0, 5, 53, 12},
+    {7, 0, 0, 5, 77, 36},
+    {10, 0, 0, 5, 77, 36},
+    {12, 0, 13, 5, 77, 36},
+    {12, 9, 13, 5, 77, 36},
+    {12, 230, 13, 5, 77, 36},
+    {12, 220, 13, 5, 77, 21},
+    {13, 0, 0, 5, 77, 11},
+    {21, 0, 0, 5, 77, 36},
+    {6, 0, 0, 5, 77, 36},
+    {11, 0, 13, 5, 40, 21},
+    {12, 0, 13, 5, 61, 21},
+    {10, 0, 0, 5, 61, 21},
+    {7, 0, 0, 5, 61, 12},
+    {12, 7, 13, 5, 61, 21},
+    {10, 9, 0, 5, 61, 21},
+    {13, 0, 0, 5, 61, 11},
+    {21, 0, 0, 5, 61, 17},
+    {21, 0, 0, 5, 61, 12},
+    {26, 0, 0, 5, 61, 12},
+    {12, 230, 13, 5, 61, 21},
+    {12, 220, 13, 5, 61, 21},
+    {12, 0, 13, 5, 66, 21},
+    {10, 0, 0, 5, 66, 21},
+    {7, 0, 0, 5, 66, 12},
+    {10, 9, 0, 5, 66, 21},
+    {12, 9, 13, 5, 66, 21},
+    {13, 0, 0, 5, 66, 11},
+    {7, 0, 0, 5, 92, 12},
+    {12, 7, 13, 5, 92, 21},
+    {10, 0, 0, 5, 92, 21},
+    {12, 0, 13, 5, 92, 21},
+    {10, 9, 0, 5, 92, 21},
+    {21, 0, 0, 5, 92, 12},
+    {7, 0, 0, 5, 67, 12},
+    {10, 0, 0, 5, 67, 21},
+    {12, 0, 13, 5, 67, 21},
+    {12, 7, 13, 5, 67, 21},
+    {21, 0, 0, 5, 67, 17},
+    {13, 0, 0, 5, 67, 11},
+    {13, 0, 0, 5, 68, 11},
+    {7, 0, 0, 5, 68, 12},
+    {6, 0, 0, 5, 68, 12},
+    {21, 0, 0, 5, 68, 17},
+    {21, 0, 0, 5, 66, 12},
+    {12, 1, 13, 5, 40, 21},
+    {10, 0, 0, 5, 0, 21},
+    {7, 0, 0, 5, 0, 12},
+    {6, 0, 0, 5, 3, 12},
+    {12, 234, 13, 5, 40, 21},
+    {12, 214, 13, 5, 40, 21},
+    {12, 202, 13, 5, 40, 21},
+    {12, 232, 13, 5, 40, 21},
+    {12, 228, 13, 5, 40, 21},
+    {12, 233, 13, 5, 40, 21},
+    {8, 0, 0, 5, 2, 12},
+    {24, 0, 18, 5, 2, 18},
+    {29, 0, 17, 5, 0, 17},
+    {29, 0, 17, 5, 0, 4},
+    {1, 0, 14, 5, 0, 20},
+    {1, 0, 14, 5, 40, 21},
+    {1, 0, 14, 5, 40, 40},
+    {1, 0, 0, 5, 0, 21},
+    {1, 0, 3, 5, 0, 21},
+    {17, 0, 18, 4, 0, 17},
+    {17, 0, 18, 5, 0, 4},
+    {17, 0, 18, 5, 0, 17},
+    {17, 0, 18, 4, 0, 19},
+    {17, 0, 18, 4, 0, 29},
+    {20, 0, 18, 4, 0, 3},
+    {19, 0, 18, 4, 0, 3},
+    {22, 0, 18, 5, 0, 0},
+    {21, 0, 18, 4, 0, 12},
+    {21, 0, 18, 4, 0, 15},
+    {21, 0, 18, 4, 0, 17},
+    {27, 0, 17, 5, 0, 30},
+    {28, 0, 15, 5, 0, 30},
+    {1, 0, 1, 5, 0, 21},
+    {1, 0, 5, 5, 0, 21},
+    {1, 0, 7, 5, 0, 21},
+    {1, 0, 2, 5, 0, 21},
+    {1, 0, 6, 5, 0, 21},
+    {21, 0, 10, 4, 0, 10},
+    {21, 0, 10, 5, 0, 10},
+    {21, 0, 18, 4, 0, 10},
+    {21, 0, 18, 5, 0, 10},
+    {21, 0, 18, 5, 0, 5},
+    {16, 0, 18, 5, 0, 12},
+    {25, 0, 12, 5, 0, 8},
+    {18, 0, 18, 5, 0, 1},
+    {25, 0, 18, 5, 0, 12},
+    {1, 0, 14, 5, 0, 22},
+    {1, 0, 14, 5, 0, 12},
+    {1, 0, 19, 5, 0, 21},
+    {1, 0, 20, 5, 0, 21},
+    {1, 0, 21, 5, 0, 21},
+    {1, 0, 22, 5, 0, 21},
+    {1, 0, 14, 5, 0, 21},
+    {15, 0, 8, 5, 0, 12},
+    {25, 0, 9, 5, 0, 12},
+    {6, 0, 0, 4, 1, 29},
+    {23, 0, 10, 5, 0, 10},
+    {23, 0, 10, 1, 0, 9},
+    {2, 0, 18, 5, 102, 9},
+    {9, 0, 0, 5, 0, 12},
+    {26, 0, 18, 4, 0, 10},
+    {26, 0, 18, 4, 0, 29},
+    {5, 0, 0, 4, 0, 29},
+    {26, 0, 18, 4, 0, 9},
+    {9, 0, 0, 4, 1, 29},
+    {26, 0, 10, 5, 0, 12},
+    {15, 0, 18, 5, 0, 12},
+    {15, 0, 18, 4, 0, 12},
+    {15, 0, 18, 5, 0, 29},
+    {14, 0, 0, 4, 1, 29},
+    {14, 0, 0, 5, 1, 12},
+    {25, 0, 9, 5, 0, 9},
+    {25, 0, 10, 5, 0, 9},
+    {25, 0, 18, 5, 0, 15},
+    {26, 0, 18, 2, 0, 14},
+    {22, 0, 18, 2, 0, 0},
+    {18, 0, 18, 2, 0, 1},
+    {26, 0, 18, 2, 0, 12},
+    {26, 0, 18, 5, 0, 14},
+    {26, 0, 0, 4, 0, 29},
+    {26, 0, 18, 5, 0, 29},
+    {25, 0, 18, 2, 0, 12},
+    {26, 0, 18, 4, 0, 14},
+    {26, 0, 18, 5, 0, 41},
+    {26, 0, 18, 4, 0, 41},
+    {26, 0, 18, 2, 0, 41},
+    {26, 0, 18, 2, 0, 29},
+    {26, 0, 18, 5, 0, 3},
+    {26, 0, 18, 5, 0, 6},
+    {26, 0, 0, 5, 52, 12},
+    {9, 0, 0, 5, 56, 12},
+    {5, 0, 0, 5, 56, 12},
+    {26, 0, 18, 5, 54, 12},
+    {12, 230, 13, 5, 54, 21},
+    {21, 0, 18, 5, 54, 6},
+    {21, 0, 18, 5, 54, 17},
+    {15, 0, 18, 5, 54, 12},
+    {5, 0, 0, 5, 23, 12},
+    {7, 0, 0, 5, 57, 12},
+    {6, 0, 0, 5, 57, 12},
+    {21, 0, 0, 5, 57, 17},
+    {12, 9, 13, 5, 57, 21},
+    {21, 0, 18, 5, 0, 3},
+    {21, 0, 18, 5, 0, 0},
+    {17, 0, 18, 5, 0, 12},
+    {17, 0, 18, 5, 0, 19},
+    {26, 0, 18, 2, 35, 14},
+    {29, 0, 17, 0, 0, 17},
+    {21, 0, 18, 2, 0, 1},
+    {21, 0, 18, 2, 0, 14},
+    {6, 0, 0, 2, 35, 5},
+    {7, 0, 0, 2, 0, 14},
+    {14, 0, 0, 2, 35, 14},
+    {17, 0, 18, 2, 0, 5},
+    {12, 218, 13, 2, 40, 21},
+    {12, 228, 13, 2, 40, 21},
+    {12, 232, 13, 2, 40, 21},
+    {12, 222, 13, 2, 40, 21},
+    {10, 224, 0, 2, 24, 21},
+    {17, 0, 18, 2, 0, 14},
+    {6, 0, 0, 2, 0, 14},
+    {6, 0, 0, 2, 0, 21},
+    {7, 0, 0, 2, 0, 5},
+    {7, 0, 0, 2, 32, 32},
+    {7, 0, 0, 2, 32, 14},
+    {12, 8, 13, 2, 40, 21},
+    {24, 0, 18, 2, 0, 5},
+    {6, 0, 0, 2, 32, 5},
+    {7, 0, 0, 2, 33, 32},
+    {7, 0, 0, 2, 33, 14},
+    {21, 0, 18, 2, 0, 5},
+    {6, 0, 0, 2, 0, 32},
+    {6, 0, 0, 2, 33, 5},
+    {7, 0, 0, 2, 34, 14},
+    {7, 0, 0, 2, 24, 14},
+    {26, 0, 0, 2, 0, 14},
+    {15, 0, 0, 2, 0, 14},
+    {26, 0, 0, 2, 24, 14},
+    {26, 0, 18, 2, 24, 14},
+    {15, 0, 0, 4, 0, 29},
+    {15, 0, 18, 2, 0, 14},
+    {26, 0, 0, 2, 33, 14},
+    {7, 0, 0, 2, 35, 14},
+    {2, 0, 18, 2, 102, 14},
+    {7, 0, 0, 2, 36, 14},
+    {6, 0, 0, 2, 36, 5},
+    {26, 0, 18, 2, 36, 14},
+    {7, 0, 0, 5, 82, 12},
+    {6, 0, 0, 5, 82, 12},
+    {21, 0, 0, 5, 82, 17},
+    {7, 0, 0, 5, 69, 12},
+    {6, 0, 0, 5, 69, 12},
+    {21, 0, 18, 5, 69, 17},
+    {21, 0, 18, 5, 69, 6},
+    {13, 0, 0, 5, 69, 11},
+    {7, 0, 0, 5, 3, 12},
+    {21, 0, 18, 5, 3, 12},
+    {6, 0, 18, 5, 3, 12},
+    {7, 0, 0, 5, 83, 12},
+    {14, 0, 0, 5, 83, 12},
+    {12, 230, 13, 5, 83, 21},
+    {21, 0, 0, 5, 83, 12},
+    {21, 0, 0, 5, 83, 17},
+    {24, 0, 0, 5, 0, 12},
+    {7, 0, 0, 5, 58, 12},
+    {12, 0, 13, 5, 58, 21},
+    {12, 9, 13, 5, 58, 21},
+    {10, 0, 0, 5, 58, 21},
+    {26, 0, 18, 5, 58, 12},
+    {15, 0, 0, 5, 0, 12},
+    {7, 0, 0, 5, 64, 12},
+    {21, 0, 18, 5, 64, 18},
+    {21, 0, 18, 5, 64, 6},
+    {10, 0, 0, 5, 70, 21},
+    {7, 0, 0, 5, 70, 12},
+    {12, 9, 13, 5, 70, 21},
+    {12, 0, 13, 5, 70, 21},
+    {21, 0, 0, 5, 70, 17},
+    {13, 0, 0, 5, 70, 11},
+    {21, 0, 0, 5, 9, 18},
+    {13, 0, 0, 5, 71, 11},
+    {7, 0, 0, 5, 71, 12},
+    {12, 0, 13, 5, 71, 21},
+    {12, 220, 13, 5, 71, 21},
+    {21, 0, 0, 5, 71, 17},
+    {7, 0, 0, 5, 72, 12},
+    {12, 0, 13, 5, 72, 21},
+    {10, 0, 0, 5, 72, 21},
+    {10, 9, 0, 5, 72, 21},
+    {21, 0, 0, 5, 72, 12},
+    {12, 0, 13, 5, 84, 21},
+    {10, 0, 0, 5, 84, 21},
+    {7, 0, 0, 5, 84, 12},
+    {12, 7, 13, 5, 84, 21},
+    {10, 9, 0, 5, 84, 21},
+    {21, 0, 0, 5, 84, 12},
+    {21, 0, 0, 5, 84, 17},
+    {13, 0, 0, 5, 84, 11},
+    {6, 0, 0, 5, 22, 36},
+    {7, 0, 0, 5, 76, 12},
+    {12, 0, 13, 5, 76, 21},
+    {10, 0, 0, 5, 76, 21},
+    {13, 0, 0, 5, 76, 11},
+    {21, 0, 0, 5, 76, 12},
+    {21, 0, 0, 5, 76, 17},
+    {7, 0, 0, 5, 78, 36},
+    {12, 230, 13, 5, 78, 36},
+    {12, 220, 13, 5, 78, 36},
+    {6, 0, 0, 5, 78, 36},
+    {21, 0, 0, 5, 78, 36},
+    {7, 0, 0, 5, 85, 12},
+    {10, 0, 0, 5, 85, 21},
+    {12, 0, 13, 5, 85, 21},
+    {21, 0, 0, 5, 85, 17},
+    {6, 0, 0, 5, 85, 12},
+    {12, 9, 13, 5, 85, 21},
+    {13, 0, 0, 5, 85, 11},
+    {7, 0, 0, 2, 24, 23},
+    {7, 0, 0, 2, 24, 24},
+    {4, 0, 0, 5, 102, 37},
+    {3, 0, 0, 4, 102, 39},
+    {12, 26, 13, 5, 5, 21},
+    {25, 0, 9, 5, 5, 12},
+    {24, 0, 4, 5, 6, 12},
+    {12, 0, 13, 4, 40, 21},
+    {21, 0, 18, 2, 0, 8},
+    {21, 0, 18, 2, 0, 6},
+    {21, 0, 18, 2, 0, 15},
+    {16, 0, 18, 2, 0, 14},
+    {21, 0, 12, 2, 0, 1},
+    {21, 0, 12, 2, 0, 5},
+    {21, 0, 10, 2, 0, 14},
+    {25, 0, 9, 2, 0, 14},
+    {17, 0, 9, 2, 0, 14},
+    {25, 0, 18, 2, 0, 14},
+    {23, 0, 10, 2, 0, 9},
+    {21, 0, 10, 2, 0, 10},
+    {21, 0, 18, 0, 0, 6},
+    {21, 0, 18, 0, 0, 14},
+    {21, 0, 10, 0, 0, 14},
+    {23, 0, 10, 0, 0, 9},
+    {21, 0, 10, 0, 0, 10},
+    {22, 0, 18, 0, 0, 0},
+    {18, 0, 18, 0, 0, 1},
+    {25, 0, 9, 0, 0, 14},
+    {21, 0, 12, 0, 0, 1},
+    {17, 0, 9, 0, 0, 14},
+    {21, 0, 12, 0, 0, 14},
+    {13, 0, 8, 0, 0, 14},
+    {21, 0, 12, 0, 0, 5},
+    {21, 0, 18, 0, 0, 5},
+    {25, 0, 18, 0, 0, 14},
+    {9, 0, 0, 0, 1, 14},
+    {24, 0, 18, 0, 0, 14},
+    {16, 0, 18, 0, 0, 14},
+    {5, 0, 0, 0, 1, 14},
+    {21, 0, 18, 1, 0, 1},
+    {22, 0, 18, 1, 0, 0},
+    {18, 0, 18, 1, 0, 1},
+    {21, 0, 18, 1, 0, 5},
+    {7, 0, 0, 1, 33, 14},
+    {7, 0, 0, 1, 33, 32},
+    {6, 0, 0, 1, 0, 32},
+    {6, 0, 0, 1, 0, 5},
+    {7, 0, 0, 1, 24, 14},
+    {23, 0, 10, 0, 0, 10},
+    {26, 0, 18, 0, 0, 14},
+    {26, 0, 18, 1, 0, 12},
+    {25, 0, 18, 1, 0, 12},
+    {1, 0, 18, 5, 0, 21},
+    {26, 0, 18, 5, 0, 31},
+    {7, 0, 0, 5, 47, 12},
+    {14, 0, 18, 5, 2, 12},
+    {15, 0, 18, 5, 2, 12},
+    {26, 0, 18, 5, 2, 12},
+    {26, 0, 0, 5, 2, 12},
+    {7, 0, 0, 5, 73, 12},
+    {7, 0, 0, 5, 74, 12},
+    {7, 0, 0, 5, 37, 12},
+    {15, 0, 0, 5, 37, 12},
+    {7, 0, 0, 5, 38, 12},
+    {14, 0, 0, 5, 38, 12},
+    {7, 0, 0, 5, 118, 12},
+    {12, 230, 13, 5, 118, 21},
+    {7, 0, 0, 5, 48, 12},
+    {21, 0, 0, 5, 48, 17},
+    {7, 0, 0, 5, 59, 12},
+    {21, 0, 0, 5, 59, 17},
+    {14, 0, 0, 5, 59, 12},
+    {9, 0, 0, 5, 39, 12},
+    {5, 0, 0, 5, 39, 12},
+    {7, 0, 0, 5, 49, 12},
+    {7, 0, 0, 5, 50, 12},
+    {13, 0, 0, 5, 50, 11},
+    {9, 0, 0, 5, 136, 12},
+    {5, 0, 0, 5, 136, 12},
+    {7, 0, 0, 5, 106, 12},
+    {7, 0, 0, 5, 104, 12},
+    {21, 0, 0, 5, 104, 12},
+    {7, 0, 0, 5, 110, 12},
+    {7, 0, 3, 5, 51, 12},
+    {7, 0, 3, 5, 86, 12},
+    {21, 0, 3, 5, 86, 17},
+    {15, 0, 3, 5, 86, 12},
+    {7, 0, 3, 5, 120, 12},
+    {26, 0, 3, 5, 120, 12},
+    {15, 0, 3, 5, 120, 12},
+    {7, 0, 3, 5, 116, 12},
+    {15, 0, 3, 5, 116, 12},
+    {7, 0, 3, 5, 128, 12},
+    {15, 0, 3, 5, 128, 12},
+    {7, 0, 3, 5, 63, 12},
+    {15, 0, 3, 5, 63, 12},
+    {21, 0, 18, 5, 63, 17},
+    {7, 0, 3, 5, 75, 12},
+    {21, 0, 3, 5, 75, 12},
+    {7, 0, 3, 5, 97, 12},
+    {7, 0, 3, 5, 96, 12},
+    {15, 0, 3, 5, 96, 12},
+    {7, 0, 3, 5, 60, 12},
+    {12, 0, 13, 5, 60, 21},
+    {12, 220, 13, 5, 60, 21},
+    {12, 230, 13, 5, 60, 21},
+    {12, 1, 13, 5, 60, 21},
+    {12, 9, 13, 5, 60, 21},
+    {15, 0, 3, 5, 60, 12},
+    {21, 0, 3, 5, 60, 17},
+    {21, 0, 3, 5, 60, 12},
+    {7, 0, 3, 5, 87, 12},
+    {15, 0, 3, 5, 87, 12},
+    {21, 0, 3, 5, 87, 12},
+    {7, 0, 3, 5, 117, 12},
+    {15, 0, 3, 5, 117, 12},
+    {7, 0, 3, 5, 112, 12},
+    {26, 0, 3, 5, 112, 12},
+    {12, 230, 13, 5, 112, 21},
+    {12, 220, 13, 5, 112, 21},
+    {15, 0, 3, 5, 112, 12},
+    {21, 0, 3, 5, 112, 17},
+    {21, 0, 3, 5, 112, 15},
+    {7, 0, 3, 5, 79, 12},
+    {21, 0, 18, 5, 79, 17},
+    {7, 0, 3, 5, 88, 12},
+    {15, 0, 3, 5, 88, 12},
+    {7, 0, 3, 5, 89, 12},
+    {15, 0, 3, 5, 89, 12},
+    {7, 0, 3, 5, 122, 12},
+    {21, 0, 3, 5, 122, 12},
+    {15, 0, 3, 5, 122, 12},
+    {7, 0, 3, 5, 90, 12},
+    {9, 0, 3, 5, 130, 12},
+    {5, 0, 3, 5, 130, 12},
+    {15, 0, 3, 5, 130, 12},
+    {15, 0, 11, 5, 6, 12},
+    {10, 0, 0, 5, 93, 21},
+    {12, 0, 13, 5, 93, 21},
+    {7, 0, 0, 5, 93, 12},
+    {12, 9, 13, 5, 93, 21},
+    {21, 0, 0, 5, 93, 17},
+    {21, 0, 0, 5, 93, 12},
+    {15, 0, 18, 5, 93, 12},
+    {13, 0, 0, 5, 93, 11},
+    {12, 0, 13, 5, 91, 21},
+    {10, 0, 0, 5, 91, 21},
+    {7, 0, 0, 5, 91, 12},
+    {12, 9, 13, 5, 91, 21},
+    {12, 7, 13, 5, 91, 21},
+    {21, 0, 0, 5, 91, 12},
+    {1, 0, 0, 5, 91, 12},
+    {21, 0, 0, 5, 91, 17},
+    {7, 0, 0, 5, 100, 12},
+    {13, 0, 0, 5, 100, 11},
+    {12, 230, 13, 5, 95, 21},
+    {7, 0, 0, 5, 95, 12},
+    {12, 0, 13, 5, 95, 21},
+    {10, 0, 0, 5, 95, 21},
+    {12, 9, 13, 5, 95, 21},
+    {13, 0, 0, 5, 95, 11},
+    {21, 0, 0, 5, 95, 17},
+    {7, 0, 0, 5, 111, 12},
+    {12, 7, 13, 5, 111, 21},
+    {21, 0, 0, 5, 111, 12},
+    {21, 0, 0, 5, 111, 18},
+    {12, 0, 13, 5, 99, 21},
+    {10, 0, 0, 5, 99, 21},
+    {7, 0, 0, 5, 99, 12},
+    {10, 9, 0, 5, 99, 21},
+    {21, 0, 0, 5, 99, 17},
+    {21, 0, 0, 5, 99, 12},
+    {12, 7, 13, 5, 99, 21},
+    {13, 0, 0, 5, 99, 11},
+    {21, 0, 0, 5, 99, 18},
+    {15, 0, 0, 5, 18, 12},
+    {7, 0, 0, 5, 108, 12},
+    {10, 0, 0, 5, 108, 21},
+    {12, 0, 13, 5, 108, 21},
+    {10, 9, 0, 5, 108, 21},
+    {12, 7, 13, 5, 108, 21},
+    {21, 0, 0, 5, 108, 17},
+    {21, 0, 0, 5, 108, 12},
+    {7, 0, 0, 5, 129, 12},
+    {21, 0, 0, 5, 129, 17},
+    {7, 0, 0, 5, 109, 12},
+    {12, 0, 13, 5, 109, 21},
+    {10, 0, 0, 5, 109, 21},
+    {12, 7, 13, 5, 109, 21},
+    {12, 9, 13, 5, 109, 21},
+    {13, 0, 0, 5, 109, 11},
+    {12, 0, 13, 5, 107, 21},
+    {10, 0, 0, 5, 107, 21},
+    {7, 0, 0, 5, 107, 12},
+    {12, 7, 13, 5, 107, 21},
+    {10, 9, 0, 5, 107, 21},
+    {12, 230, 13, 5, 107, 21},
+    {7, 0, 0, 5, 135, 12},
+    {10, 0, 0, 5, 135, 21},
+    {12, 0, 13, 5, 135, 21},
+    {12, 9, 13, 5, 135, 21},
+    {12, 7, 13, 5, 135, 21},
+    {21, 0, 0, 5, 135, 17},
+    {21, 0, 0, 5, 135, 12},
+    {13, 0, 0, 5, 135, 11},
+    {7, 0, 0, 5, 124, 12},
+    {10, 0, 0, 5, 124, 21},
+    {12, 0, 13, 5, 124, 21},
+    {12, 9, 13, 5, 124, 21},
+    {12, 7, 13, 5, 124, 21},
+    {21, 0, 0, 5, 124, 12},
+    {13, 0, 0, 5, 124, 11},
+    {7, 0, 0, 5, 123, 12},
+    {10, 0, 0, 5, 123, 21},
+    {12, 0, 13, 5, 123, 21},
+    {12, 9, 13, 5, 123, 21},
+    {12, 7, 13, 5, 123, 21},
+    {21, 0, 0, 5, 123, 18},
+    {21, 0, 0, 5, 123, 17},
+    {21, 0, 0, 5, 123, 6},
+    {21, 0, 0, 5, 123, 12},
+    {7, 0, 0, 5, 114, 12},
+    {10, 0, 0, 5, 114, 21},
+    {12, 0, 13, 5, 114, 21},
+    {12, 9, 13, 5, 114, 21},
+    {21, 0, 0, 5, 114, 17},
+    {21, 0, 0, 5, 114, 12},
+    {13, 0, 0, 5, 114, 11},
+    {21, 0, 18, 5, 31, 18},
+    {7, 0, 0, 5, 101, 12},
+    {12, 0, 13, 5, 101, 21},
+    {10, 0, 0, 5, 101, 21},
+    {10, 9, 0, 5, 101, 21},
+    {12, 7, 13, 5, 101, 21},
+    {13, 0, 0, 5, 101, 11},
+    {7, 0, 0, 5, 126, 36},
+    {12, 0, 13, 5, 126, 36},
+    {10, 0, 0, 5, 126, 36},
+    {12, 9, 13, 5, 126, 36},
+    {13, 0, 0, 5, 126, 11},
+    {15, 0, 0, 5, 126, 36},
+    {21, 0, 0, 5, 126, 17},
+    {26, 0, 0, 5, 126, 36},
+    {9, 0, 0, 5, 125, 12},
+    {5, 0, 0, 5, 125, 12},
+    {13, 0, 0, 5, 125, 11},
+    {15, 0, 0, 5, 125, 12},
+    {7, 0, 0, 5, 125, 12},
+    {7, 0, 0, 5, 141, 12},
+    {12, 0, 13, 5, 141, 21},
+    {10, 0, 0, 5, 141, 21},
+    {12, 9, 13, 5, 141, 21},
+    {21, 0, 0, 5, 141, 18},
+    {21, 0, 0, 5, 141, 12},
+    {21, 0, 0, 5, 141, 17},
+    {7, 0, 0, 5, 140, 12},
+    {12, 0, 13, 5, 140, 21},
+    {10, 0, 0, 5, 140, 21},
+    {12, 9, 13, 5, 140, 21},
+    {21, 0, 0, 5, 140, 17},
+    {21, 0, 0, 5, 140, 18},
+    {7, 0, 0, 5, 121, 12},
+    {7, 0, 0, 5, 133, 12},
+    {10, 0, 0, 5, 133, 21},
+    {12, 0, 13, 5, 133, 21},
+    {12, 9, 0, 5, 133, 21},
+    {21, 0, 0, 5, 133, 17},
+    {13, 0, 0, 5, 133, 11},
+    {15, 0, 0, 5, 133, 12},
+    {21, 0, 0, 5, 134, 18},
+    {21, 0, 0, 5, 134, 6},
+    {7, 0, 0, 5, 134, 12},
+    {12, 0, 13, 5, 134, 21},
+    {10, 0, 0, 5, 134, 21},
+    {7, 0, 0, 5, 138, 12},
+    {12, 0, 13, 5, 138, 21},
+    {12, 7, 13, 5, 138, 21},
+    {12, 9, 13, 5, 138, 21},
+    {13, 0, 0, 5, 138, 11},
+    {7, 0, 0, 5, 62, 12},
+    {14, 0, 0, 5, 62, 12},
+    {21, 0, 0, 5, 62, 17},
+    {7, 0, 0, 5, 80, 12},
+    {7, 0, 0, 5, 80, 0},
+    {7, 0, 0, 5, 80, 1},
+    {7, 0, 0, 5, 127, 12},
+    {7, 0, 0, 5, 127, 0},
+    {7, 0, 0, 5, 127, 1},
+    {7, 0, 0, 5, 115, 12},
+    {13, 0, 0, 5, 115, 11},
+    {21, 0, 0, 5, 115, 17},
+    {7, 0, 0, 5, 103, 12},
+    {12, 1, 13, 5, 103, 21},
+    {21, 0, 0, 5, 103, 17},
+    {7, 0, 0, 5, 119, 12},
+    {12, 230, 13, 5, 119, 21},
+    {21, 0, 0, 5, 119, 17},
+    {21, 0, 0, 5, 119, 12},
+    {26, 0, 0, 5, 119, 12},
+    {6, 0, 0, 5, 119, 12},
+    {13, 0, 0, 5, 119, 11},
+    {15, 0, 0, 5, 119, 12},
+    {7, 0, 0, 5, 98, 12},
+    {10, 0, 0, 5, 98, 21},
+    {12, 0, 13, 5, 98, 21},
+    {6, 0, 0, 5, 98, 12},
+    {6, 0, 0, 2, 137, 5},
+    {6, 0, 0, 2, 139, 5},
+    {7, 0, 0, 2, 137, 14},
+    {7, 0, 0, 2, 139, 14},
+    {7, 0, 0, 5, 105, 12},
+    {26, 0, 0, 5, 105, 12},
+    {12, 0, 13, 5, 105, 21},
+    {12, 1, 13, 5, 105, 21},
+    {21, 0, 0, 5, 105, 17},
+    {10, 216, 0, 5, 0, 21},
+    {10, 226, 0, 5, 0, 21},
+    {12, 230, 13, 5, 2, 21},
+    {25, 0, 0, 5, 0, 12},
+    {13, 0, 8, 5, 0, 11},
+    {26, 0, 0, 5, 131, 12},
+    {12, 0, 13, 5, 131, 21},
+    {21, 0, 0, 5, 131, 17},
+    {21, 0, 0, 5, 131, 12},
+    {12, 230, 13, 5, 56, 21},
+    {7, 0, 3, 5, 113, 12},
+    {15, 0, 3, 5, 113, 12},
+    {12, 220, 13, 5, 113, 21},
+    {9, 0, 3, 5, 132, 12},
+    {5, 0, 3, 5, 132, 12},
+    {12, 230, 13, 5, 132, 21},
+    {12, 7, 13, 5, 132, 21},
+    {13, 0, 3, 5, 132, 11},
+    {21, 0, 3, 5, 132, 0},
+    {2, 0, 18, 5, 102, 14},
+    {26, 0, 0, 2, 0, 29},
+    {26, 0, 0, 5, 0, 28},
+    {26, 0, 0, 2, 32, 14},
+    {24, 0, 18, 2, 0, 42},
+    {26, 0, 18, 5, 0, 5},
 };
 
 #define BIDI_MIRROR_LEN 364
@@ -1893,6 +1906,10 @@
 #define UCDN_SCRIPT_NEWA 135
 #define UCDN_SCRIPT_OSAGE 136
 #define UCDN_SCRIPT_TANGUT 137
+#define UCDN_SCRIPT_MASARAM_GONDI 138
+#define UCDN_SCRIPT_NUSHU 139
+#define UCDN_SCRIPT_SOYOMBO 140
+#define UCDN_SCRIPT_ZANABAZAR_SQUARE 141
 
 #define UCDN_GENERAL_CATEGORY_CC 0
 #define UCDN_GENERAL_CATEGORY_CF 1
@@ -1968,21 +1985,21 @@
     73, 73, 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 52, 75, 76, 77, 78, 79, 
     80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 94, 96, 
-    97, 98, 99, 100, 101, 102, 103, 104, 94, 105, 94, 106, 94, 94, 94, 107, 
-    107, 107, 108, 109, 110, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 111, 
-    111, 112, 113, 114, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
-    94, 94, 115, 116, 117, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
+    97, 98, 99, 100, 101, 102, 103, 104, 94, 105, 94, 106, 107, 94, 94, 108, 
+    108, 108, 109, 110, 111, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 112, 
+    112, 113, 114, 115, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
+    94, 94, 116, 117, 118, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
-    94, 94, 94, 118, 118, 119, 120, 94, 94, 94, 121, 122, 122, 122, 122, 122, 
-    122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 
-    122, 122, 122, 122, 123, 122, 122, 124, 94, 94, 94, 94, 94, 94, 94, 94, 
+    94, 94, 94, 119, 119, 120, 121, 94, 94, 94, 122, 123, 123, 123, 123, 123, 
+    123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 
+    123, 123, 123, 123, 124, 123, 123, 125, 94, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
-    94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 125, 94, 94, 94, 94, 94, 94, 
-    94, 94, 94, 94, 94, 126, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
-    94, 94, 94, 94, 94, 94, 94, 127, 128, 129, 130, 131, 132, 133, 134, 135, 
-    135, 136, 94, 94, 94, 94, 94, 137, 94, 94, 94, 94, 94, 94, 94, 138, 139, 
-    94, 94, 94, 94, 140, 94, 141, 142, 143, 144, 145, 146, 147, 148, 149, 
-    150, 151, 151, 151, 151, 151, 152, 52, 52, 52, 52, 52, 52, 52, 52, 52, 
+    94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 126, 127, 128, 94, 94, 94, 
+    94, 94, 94, 94, 94, 94, 129, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
+    94, 94, 94, 94, 94, 94, 94, 94, 130, 131, 132, 133, 134, 135, 136, 137, 
+    138, 138, 139, 94, 94, 94, 94, 94, 140, 94, 94, 94, 94, 94, 94, 94, 141, 
+    142, 94, 94, 94, 94, 143, 94, 144, 145, 146, 147, 148, 149, 150, 151, 
+    152, 153, 154, 154, 154, 154, 154, 155, 52, 52, 52, 52, 52, 52, 52, 52, 
     52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 
     52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 
     52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 
@@ -1991,32 +2008,31 @@
     52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 
     52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 
     52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 
-    52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 153, 52, 52, 52, 52, 
-    52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 154, 155, 52, 52, 52, 52, 
-    52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 156, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 52, 52, 
-    158, 157, 157, 157, 157, 159, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 
-    157, 157, 157, 157, 157, 157, 157, 157, 157, 159, 94, 94, 94, 94, 94, 94, 
-    94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
+    52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 156, 52, 52, 52, 
+    52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 157, 158, 52, 52, 52, 
+    52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 
+    159, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 
+    52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 160, 161, 161, 161, 161, 161, 
+    161, 161, 161, 161, 161, 161, 161, 52, 52, 162, 161, 161, 161, 161, 163, 
+    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, 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, 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, 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, 161, 161, 161, 161, 161, 161, 161, 161, 
+    161, 161, 161, 163, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
@@ -2157,8 +2173,8 @@
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
-    94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 160, 161, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
+    94, 94, 94, 94, 94, 94, 94, 94, 94, 164, 165, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
@@ -2172,7 +2188,7 @@
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
     94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 
-    94, 94, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
+    94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 74, 74, 74, 74, 74, 
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
@@ -2186,7 +2202,7 @@
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
-    74, 74, 74, 74, 74, 162, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
+    74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 166, 74, 
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
@@ -2200,7 +2216,8 @@
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
     74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
-    74, 74, 74, 74, 74, 74, 74, 74, 74, 162, 
+    74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 
+    74, 74, 166, 
 };
 
 static const unsigned short index1[] = {
@@ -2220,1136 +2237,1145 @@
     128, 128, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 145, 145, 
     146, 147, 148, 149, 128, 128, 128, 128, 128, 128, 150, 150, 150, 150, 
     151, 152, 153, 120, 154, 155, 156, 156, 156, 157, 158, 159, 160, 160, 
-    161, 162, 163, 164, 165, 166, 167, 167, 167, 168, 120, 120, 120, 120, 
-    120, 120, 120, 120, 128, 128, 169, 170, 120, 120, 171, 126, 172, 173, 
-    174, 175, 176, 177, 177, 177, 177, 177, 177, 178, 179, 180, 181, 177, 
-    182, 183, 184, 177, 185, 186, 187, 188, 188, 189, 190, 191, 192, 193, 
-    194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 203, 204, 205, 206, 
-    207, 208, 209, 210, 211, 212, 213, 120, 214, 215, 216, 217, 217, 218, 
-    219, 220, 221, 222, 223, 120, 224, 225, 226, 227, 228, 229, 230, 231, 
-    231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 120, 242, 243, 
-    244, 245, 246, 243, 247, 248, 249, 250, 251, 120, 252, 253, 254, 255, 
-    256, 257, 258, 259, 259, 258, 259, 260, 261, 262, 263, 264, 265, 266, 
-    120, 267, 268, 269, 270, 271, 271, 270, 272, 273, 274, 275, 276, 277, 
-    278, 279, 280, 120, 281, 282, 283, 284, 284, 284, 284, 285, 286, 287, 
-    288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 295, 295, 298, 299, 
-    296, 300, 301, 302, 303, 304, 305, 120, 306, 307, 307, 307, 307, 307, 
-    308, 309, 310, 311, 312, 313, 120, 120, 120, 120, 314, 315, 316, 317, 
-    318, 319, 320, 321, 322, 323, 324, 325, 120, 120, 120, 120, 326, 327, 
-    328, 329, 330, 331, 332, 333, 334, 335, 334, 334, 334, 336, 337, 338, 
-    339, 340, 341, 342, 341, 341, 341, 343, 344, 345, 346, 347, 120, 120, 
-    120, 120, 348, 348, 348, 348, 348, 349, 350, 351, 352, 353, 354, 355, 
-    356, 357, 358, 348, 359, 360, 352, 361, 362, 362, 362, 362, 363, 364, 
-    365, 365, 365, 365, 365, 366, 367, 367, 367, 367, 367, 367, 367, 367, 
-    367, 367, 367, 367, 368, 368, 368, 368, 368, 368, 368, 368, 368, 369, 
-    369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 370, 370, 370, 370, 
-    370, 370, 370, 370, 370, 371, 372, 371, 370, 370, 370, 370, 370, 371, 
-    370, 370, 370, 370, 371, 372, 371, 370, 372, 370, 370, 370, 370, 370, 
-    370, 370, 371, 370, 370, 370, 370, 370, 370, 370, 370, 373, 374, 375, 
-    376, 377, 370, 370, 378, 379, 380, 380, 380, 380, 380, 380, 380, 380, 
-    380, 380, 381, 382, 383, 384, 384, 384, 384, 384, 384, 384, 384, 384, 
-    384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 
-    384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 
-    384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 
-    384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 
-    384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 385, 384, 384, 
-    386, 387, 387, 388, 389, 389, 389, 389, 389, 389, 389, 389, 389, 390, 
-    391, 392, 393, 394, 395, 120, 396, 396, 397, 120, 398, 398, 399, 120, 
-    400, 401, 402, 120, 403, 403, 403, 403, 403, 403, 404, 405, 406, 407, 
-    408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 418, 418, 418, 
-    419, 418, 418, 418, 418, 418, 418, 120, 420, 418, 418, 418, 418, 421, 
-    384, 384, 384, 384, 384, 384, 384, 384, 422, 120, 423, 423, 423, 424, 
-    425, 426, 427, 428, 429, 430, 431, 431, 431, 432, 433, 120, 434, 434, 
-    434, 434, 434, 435, 434, 434, 434, 436, 437, 438, 439, 439, 439, 439, 
-    440, 440, 441, 442, 443, 443, 443, 443, 443, 443, 444, 445, 446, 447, 
-    448, 449, 450, 451, 450, 451, 452, 453, 454, 455, 120, 120, 120, 120, 
-    120, 120, 120, 120, 456, 457, 457, 457, 457, 457, 458, 459, 460, 461, 
-    462, 463, 464, 465, 466, 467, 468, 469, 469, 469, 470, 471, 472, 473, 
-    474, 474, 474, 474, 475, 476, 477, 478, 479, 479, 479, 479, 480, 481, 
-    482, 483, 484, 485, 486, 487, 488, 488, 488, 489, 100, 490, 120, 120, 
-    120, 120, 120, 120, 491, 120, 492, 493, 494, 495, 496, 497, 54, 54, 54, 
-    54, 498, 499, 56, 56, 56, 56, 56, 500, 501, 502, 54, 503, 54, 54, 54, 
-    504, 56, 56, 56, 505, 506, 507, 508, 509, 509, 509, 510, 511, 27, 27, 27, 
-    27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 512, 513, 27, 
-    27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 514, 515, 516, 517, 514, 515, 
-    514, 515, 516, 517, 514, 518, 514, 515, 514, 516, 514, 519, 514, 519, 
-    514, 519, 520, 521, 522, 523, 524, 525, 514, 526, 527, 528, 529, 530, 
-    531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 
-    545, 546, 56, 547, 548, 549, 550, 551, 552, 552, 553, 554, 555, 556, 557, 
-    120, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 
-    571, 570, 572, 573, 574, 575, 576, 577, 578, 579, 580, 579, 581, 582, 
-    579, 583, 579, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 
-    595, 596, 597, 598, 599, 594, 594, 600, 601, 602, 603, 604, 594, 594, 
-    605, 585, 606, 607, 594, 594, 608, 594, 594, 579, 609, 610, 611, 612, 
-    613, 614, 615, 615, 615, 615, 615, 615, 615, 615, 616, 579, 579, 617, 
-    618, 585, 585, 619, 579, 579, 579, 579, 584, 620, 621, 622, 623, 579, 
-    579, 579, 579, 623, 120, 120, 120, 579, 624, 120, 120, 625, 625, 625, 
-    625, 625, 626, 626, 627, 628, 628, 628, 628, 628, 628, 628, 628, 628, 
-    629, 625, 630, 631, 631, 631, 631, 631, 631, 631, 631, 631, 632, 631, 
-    631, 631, 631, 633, 579, 631, 631, 634, 579, 635, 636, 637, 638, 639, 
-    640, 636, 579, 634, 641, 579, 642, 643, 644, 645, 646, 579, 579, 579, 
-    647, 648, 649, 650, 579, 651, 652, 579, 653, 579, 579, 654, 655, 656, 
-    657, 579, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 579, 
-    579, 579, 669, 579, 670, 579, 671, 672, 673, 674, 675, 676, 625, 677, 
-    677, 678, 579, 579, 579, 669, 679, 680, 681, 682, 683, 684, 685, 585, 
-    585, 686, 686, 686, 686, 686, 686, 686, 686, 686, 686, 686, 686, 686, 
-    686, 686, 686, 686, 686, 686, 686, 686, 686, 686, 686, 686, 686, 686, 
-    686, 686, 686, 686, 686, 585, 585, 585, 585, 585, 585, 585, 585, 585, 
-    585, 585, 585, 585, 585, 585, 585, 687, 688, 688, 689, 594, 594, 585, 
-    690, 691, 692, 693, 694, 695, 696, 697, 698, 585, 699, 594, 700, 701, 
-    702, 703, 683, 585, 585, 597, 690, 703, 704, 705, 706, 594, 594, 594, 
-    594, 707, 708, 594, 594, 594, 594, 709, 710, 711, 683, 712, 713, 579, 
-    579, 579, 714, 579, 579, 585, 585, 715, 716, 717, 636, 579, 579, 718, 
-    579, 579, 579, 719, 579, 579, 579, 579, 720, 579, 721, 722, 120, 120, 
-    723, 120, 120, 724, 724, 724, 724, 724, 725, 726, 726, 726, 726, 726, 
-    727, 728, 729, 730, 731, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 
-    732, 733, 734, 735, 736, 736, 736, 736, 737, 738, 739, 739, 739, 739, 
-    739, 739, 739, 740, 741, 742, 370, 370, 372, 120, 372, 372, 372, 372, 
-    372, 372, 372, 372, 743, 743, 743, 743, 744, 745, 746, 747, 748, 749, 
-    750, 751, 752, 120, 120, 120, 120, 120, 120, 120, 753, 753, 753, 754, 
-    753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 755, 120, 753, 753, 
-    753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 
-    753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 756, 120, 120, 120, 
-    757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 769, 
-    770, 769, 769, 769, 771, 772, 773, 774, 775, 776, 777, 777, 778, 777, 
-    777, 777, 779, 780, 781, 782, 783, 784, 784, 784, 784, 785, 786, 787, 
-    787, 787, 787, 787, 787, 787, 787, 787, 787, 788, 789, 790, 784, 784, 
-    784, 791, 757, 757, 757, 757, 758, 120, 792, 792, 793, 793, 793, 794, 
-    795, 796, 790, 790, 790, 797, 798, 799, 793, 793, 793, 800, 795, 796, 
-    790, 790, 790, 790, 801, 799, 790, 802, 803, 803, 803, 803, 803, 804, 
-    803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 803, 790, 790, 790, 
-    805, 806, 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, 807, 
-    790, 790, 790, 805, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 809, 810, 579, 579, 579, 579, 579, 579, 579, 579, 808, 808, 
-    808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 809, 810, 810, 810, 
-    810, 810, 811, 811, 812, 811, 811, 811, 811, 811, 811, 811, 811, 811, 
-    811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 
-    811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 
-    811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 
-    811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 
-    811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 813, 
-    814, 814, 814, 814, 814, 814, 815, 120, 816, 816, 816, 816, 816, 817, 
-    818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 
-    818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 
-    818, 818, 818, 818, 818, 819, 818, 818, 820, 821, 120, 120, 101, 101, 
-    101, 101, 101, 822, 823, 824, 101, 101, 101, 825, 826, 826, 826, 826, 
-    826, 826, 826, 826, 827, 828, 829, 120, 64, 64, 830, 831, 832, 27, 833, 
-    27, 27, 27, 27, 27, 27, 27, 834, 835, 27, 836, 837, 27, 27, 838, 839, 
-    120, 120, 120, 120, 120, 120, 120, 840, 841, 842, 843, 844, 844, 845, 
-    846, 847, 848, 849, 849, 849, 849, 849, 849, 850, 120, 851, 852, 852, 
-    852, 852, 852, 853, 854, 855, 856, 857, 858, 859, 859, 860, 861, 862, 
-    863, 864, 864, 865, 866, 867, 867, 868, 869, 870, 871, 367, 367, 367, 
-    872, 873, 874, 874, 874, 874, 874, 875, 876, 877, 878, 879, 880, 881, 
-    348, 352, 882, 883, 883, 883, 883, 883, 884, 885, 120, 886, 887, 888, 
-    889, 348, 348, 890, 891, 892, 892, 892, 892, 892, 892, 893, 894, 895, 
-    120, 120, 896, 897, 898, 899, 120, 900, 900, 900, 120, 372, 372, 54, 54, 
-    54, 54, 54, 901, 902, 120, 903, 903, 903, 903, 903, 903, 903, 903, 903, 
-    903, 897, 897, 897, 897, 904, 905, 906, 907, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 
-    909, 909, 908, 909, 909, 910, 909, 909, 909, 909, 909, 909, 908, 909, 
-    909, 910, 909, 909, 909, 908, 909, 909, 910, 909, 909, 909, 908, 909, 
-    909, 911, 120, 368, 368, 912, 913, 369, 369, 369, 369, 369, 914, 915, 
-    915, 915, 915, 915, 915, 915, 915, 915, 915, 915, 915, 915, 915, 915, 
-    915, 915, 915, 915, 915, 915, 915, 915, 915, 915, 915, 915, 915, 915, 
-    915, 915, 915, 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 
-    916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 
-    916, 916, 916, 916, 916, 916, 916, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 809, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 917, 810, 810, 810, 810, 918, 120, 919, 
-    920, 121, 921, 922, 923, 924, 121, 128, 128, 128, 128, 128, 128, 128, 
-    128, 128, 128, 128, 128, 925, 926, 927, 120, 928, 128, 128, 128, 128, 
+    161, 162, 163, 164, 165, 166, 167, 167, 167, 168, 145, 169, 120, 120, 
+    120, 120, 120, 120, 128, 128, 170, 171, 120, 120, 172, 126, 173, 174, 
+    175, 176, 177, 178, 178, 178, 178, 178, 178, 179, 180, 181, 182, 178, 
+    183, 184, 185, 178, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 
+    195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 204, 205, 206, 207, 
+    208, 209, 210, 211, 212, 213, 214, 120, 215, 216, 217, 218, 218, 219, 
+    220, 221, 222, 223, 224, 120, 225, 226, 227, 228, 229, 230, 231, 232, 
+    232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 120, 243, 244, 
+    245, 246, 247, 244, 248, 249, 250, 251, 252, 120, 253, 254, 255, 256, 
+    257, 258, 259, 260, 260, 259, 260, 261, 262, 263, 264, 265, 266, 267, 
+    120, 268, 269, 270, 271, 272, 272, 271, 273, 274, 275, 276, 277, 278, 
+    279, 280, 281, 120, 282, 283, 284, 285, 285, 285, 285, 286, 287, 288, 
+    289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 296, 296, 299, 300, 
+    297, 301, 302, 303, 304, 305, 306, 120, 307, 308, 308, 308, 308, 308, 
+    309, 310, 311, 312, 313, 314, 120, 120, 120, 120, 315, 316, 317, 318, 
+    319, 320, 321, 322, 323, 324, 325, 326, 120, 120, 120, 120, 327, 328, 
+    329, 330, 331, 332, 333, 334, 335, 336, 335, 335, 335, 337, 338, 339, 
+    340, 341, 342, 343, 342, 342, 342, 344, 345, 346, 347, 348, 120, 120, 
+    120, 120, 349, 349, 349, 349, 349, 350, 351, 352, 353, 354, 355, 356, 
+    357, 358, 359, 349, 360, 361, 353, 362, 363, 363, 363, 363, 364, 365, 
+    366, 366, 366, 366, 366, 367, 368, 368, 368, 368, 368, 368, 368, 368, 
+    368, 368, 368, 368, 369, 369, 369, 369, 369, 369, 369, 369, 369, 370, 
+    370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 371, 371, 371, 371, 
+    371, 371, 371, 371, 371, 372, 373, 372, 371, 371, 371, 371, 371, 372, 
+    371, 371, 371, 371, 372, 373, 372, 371, 373, 371, 371, 371, 371, 371, 
+    371, 371, 372, 371, 371, 371, 371, 371, 371, 371, 371, 374, 375, 376, 
+    377, 378, 371, 371, 379, 380, 381, 381, 381, 381, 381, 381, 381, 381, 
+    381, 381, 382, 383, 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, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 386, 385, 385, 
+    387, 388, 388, 389, 390, 390, 390, 390, 390, 390, 390, 390, 390, 391, 
+    392, 393, 394, 395, 396, 120, 397, 397, 398, 120, 399, 399, 400, 120, 
+    401, 402, 403, 120, 404, 404, 404, 404, 404, 404, 405, 406, 407, 408, 
+    409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 419, 419, 419, 
+    420, 419, 419, 419, 419, 419, 419, 120, 421, 419, 419, 419, 419, 422, 
+    385, 385, 385, 385, 385, 385, 385, 385, 423, 120, 424, 424, 424, 425, 
+    426, 427, 428, 429, 430, 431, 432, 432, 432, 433, 434, 120, 435, 435, 
+    435, 435, 435, 436, 435, 435, 435, 437, 438, 439, 440, 440, 440, 440, 
+    441, 441, 442, 443, 444, 444, 444, 444, 444, 444, 445, 446, 447, 448, 
+    449, 450, 451, 452, 451, 452, 453, 454, 455, 456, 120, 120, 120, 120, 
+    120, 120, 120, 120, 457, 458, 458, 458, 458, 458, 459, 460, 461, 462, 
+    463, 464, 465, 466, 467, 468, 469, 470, 470, 470, 471, 472, 473, 474, 
+    475, 475, 475, 475, 476, 477, 478, 479, 480, 480, 480, 480, 481, 482, 
+    483, 484, 485, 486, 487, 488, 489, 489, 489, 490, 100, 491, 120, 120, 
+    120, 120, 120, 120, 492, 120, 493, 494, 495, 496, 497, 498, 54, 54, 54, 
+    54, 499, 500, 56, 56, 56, 56, 56, 501, 502, 503, 54, 504, 54, 54, 54, 
+    505, 56, 56, 56, 506, 507, 508, 509, 510, 510, 510, 511, 512, 27, 27, 27, 
+    27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 513, 514, 27, 
+    27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 515, 516, 517, 518, 515, 516, 
+    515, 516, 517, 518, 515, 519, 515, 516, 515, 517, 515, 520, 515, 520, 
+    515, 520, 521, 522, 523, 524, 525, 526, 515, 527, 528, 529, 530, 531, 
+    532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 
+    546, 547, 56, 548, 549, 550, 551, 552, 553, 553, 554, 555, 556, 557, 558, 
+    120, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 
+    572, 571, 573, 574, 575, 576, 577, 578, 579, 580, 581, 580, 582, 583, 
+    580, 584, 580, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 586, 
+    595, 596, 586, 597, 598, 586, 586, 598, 586, 599, 600, 599, 586, 586, 
+    601, 586, 586, 586, 586, 586, 602, 586, 586, 580, 603, 604, 605, 606, 
+    607, 608, 609, 609, 609, 609, 609, 609, 609, 609, 610, 580, 580, 611, 
+    612, 586, 586, 613, 580, 580, 580, 580, 585, 606, 614, 615, 580, 580, 
+    580, 580, 580, 616, 120, 120, 120, 580, 617, 120, 120, 618, 618, 618, 
+    618, 618, 619, 619, 620, 621, 621, 621, 621, 621, 621, 621, 621, 621, 
+    622, 618, 623, 624, 624, 624, 624, 624, 624, 624, 624, 624, 625, 624, 
+    624, 624, 624, 626, 580, 624, 624, 627, 580, 628, 629, 630, 631, 632, 
+    633, 629, 580, 627, 634, 580, 635, 636, 637, 638, 639, 580, 580, 580, 
+    640, 641, 642, 643, 580, 644, 645, 580, 646, 580, 580, 647, 648, 649, 
+    650, 580, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 580, 
+    580, 580, 662, 580, 663, 580, 664, 665, 666, 667, 668, 669, 618, 670, 
+    670, 671, 580, 580, 580, 662, 672, 673, 586, 586, 586, 674, 675, 586, 
+    586, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 
+    676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 
+    676, 676, 676, 676, 676, 586, 586, 586, 586, 586, 586, 586, 586, 586, 
+    586, 586, 586, 586, 586, 586, 586, 677, 678, 678, 679, 586, 586, 586, 
+    586, 586, 586, 586, 680, 586, 586, 586, 681, 586, 586, 586, 586, 586, 
+    586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 
+    586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 580, 
+    580, 580, 682, 580, 580, 586, 586, 683, 684, 685, 629, 580, 580, 686, 
+    580, 580, 580, 687, 580, 580, 580, 580, 688, 580, 689, 617, 120, 120, 
+    690, 120, 120, 691, 691, 691, 691, 691, 692, 693, 693, 693, 693, 693, 
+    694, 695, 696, 697, 698, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 
+    699, 700, 701, 702, 703, 703, 703, 703, 704, 705, 706, 706, 706, 706, 
+    706, 706, 706, 707, 708, 709, 371, 371, 373, 120, 373, 373, 373, 373, 
+    373, 373, 373, 373, 710, 710, 710, 710, 711, 712, 713, 714, 715, 716, 
+    717, 718, 719, 720, 120, 120, 120, 120, 120, 120, 721, 721, 721, 722, 
+    721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 723, 120, 721, 721, 
+    721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 
+    721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 724, 120, 120, 120, 
+    725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 737, 
+    738, 737, 737, 737, 739, 740, 741, 742, 743, 744, 745, 745, 746, 745, 
+    745, 745, 747, 748, 749, 750, 751, 752, 752, 752, 752, 753, 754, 755, 
+    755, 755, 755, 755, 755, 755, 755, 755, 755, 756, 757, 758, 752, 752, 
+    752, 759, 725, 725, 725, 725, 726, 120, 760, 760, 761, 761, 761, 762, 
+    763, 764, 758, 758, 758, 765, 766, 767, 761, 761, 761, 768, 763, 764, 
+    758, 758, 758, 758, 769, 767, 758, 770, 771, 771, 771, 771, 771, 772, 
+    771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 758, 758, 758, 
+    773, 774, 758, 758, 758, 758, 758, 758, 758, 758, 758, 758, 758, 775, 
+    758, 758, 758, 773, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 777, 778, 580, 580, 580, 580, 580, 580, 580, 580, 776, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 779, 
+    778, 778, 780, 780, 781, 780, 780, 780, 780, 780, 780, 780, 780, 780, 
+    780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 
+    780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 
+    780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 
+    780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 
+    780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 782, 
+    783, 783, 783, 783, 783, 783, 784, 120, 785, 785, 785, 785, 785, 786, 
+    787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 
+    787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 
+    787, 787, 787, 787, 787, 788, 787, 787, 789, 790, 120, 120, 101, 101, 
+    101, 101, 101, 791, 792, 793, 101, 101, 101, 794, 795, 795, 795, 795, 
+    795, 795, 795, 795, 796, 797, 798, 120, 64, 64, 799, 800, 801, 27, 802, 
+    27, 27, 27, 27, 27, 27, 27, 803, 804, 27, 805, 806, 27, 27, 807, 808, 
+    120, 120, 120, 120, 120, 120, 120, 809, 810, 811, 812, 813, 813, 814, 
+    815, 816, 817, 818, 818, 818, 818, 818, 818, 819, 120, 820, 821, 821, 
+    821, 821, 821, 822, 823, 824, 825, 826, 827, 828, 828, 829, 830, 831, 
+    832, 833, 833, 834, 835, 836, 836, 837, 838, 839, 840, 368, 368, 368, 
+    841, 842, 843, 843, 843, 843, 843, 844, 845, 846, 847, 848, 849, 850, 
+    349, 353, 851, 852, 852, 852, 852, 852, 853, 854, 120, 855, 856, 857, 
+    858, 349, 349, 859, 860, 861, 861, 861, 861, 861, 861, 862, 863, 864, 
+    120, 120, 865, 866, 867, 868, 120, 869, 869, 869, 120, 373, 373, 54, 54, 
+    54, 54, 54, 870, 871, 120, 872, 872, 872, 872, 872, 872, 872, 872, 872, 
+    872, 866, 866, 866, 866, 873, 874, 875, 876, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 
+    878, 878, 877, 878, 878, 879, 878, 878, 878, 878, 878, 878, 877, 878, 
+    878, 879, 878, 878, 878, 877, 878, 878, 879, 878, 878, 878, 877, 878, 
+    878, 880, 120, 369, 369, 881, 882, 370, 370, 370, 370, 370, 883, 884, 
+    884, 884, 884, 884, 884, 884, 884, 884, 884, 884, 884, 884, 884, 884, 
+    884, 884, 884, 884, 884, 884, 884, 884, 884, 884, 884, 884, 884, 884, 
+    884, 884, 884, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 
+    885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 
+    885, 885, 885, 885, 885, 885, 885, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 777, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 886, 778, 778, 778, 778, 887, 120, 888, 
+    889, 121, 890, 891, 892, 893, 121, 128, 128, 128, 128, 128, 128, 128, 
+    128, 128, 128, 128, 128, 894, 895, 896, 120, 897, 128, 128, 128, 128, 
     128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 
     128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 
-    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 929, 120, 
-    120, 128, 128, 128, 128, 128, 128, 128, 128, 930, 128, 128, 128, 128, 
-    128, 128, 120, 120, 120, 120, 120, 128, 931, 932, 932, 933, 934, 935, 
-    936, 937, 938, 939, 940, 941, 942, 943, 944, 169, 128, 128, 128, 128, 
-    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 945, 946, 
-    947, 948, 949, 950, 951, 951, 952, 953, 954, 954, 955, 956, 957, 958, 
-    959, 959, 959, 959, 960, 961, 961, 961, 962, 963, 963, 963, 964, 965, 
-    966, 120, 967, 968, 969, 968, 968, 970, 968, 968, 971, 968, 972, 968, 
-    972, 120, 120, 120, 120, 968, 968, 968, 968, 968, 968, 968, 968, 968, 
-    968, 968, 968, 968, 968, 968, 973, 974, 975, 975, 975, 975, 975, 976, 
-    615, 977, 977, 977, 977, 977, 977, 978, 979, 980, 981, 579, 982, 983, 
-    120, 120, 120, 120, 120, 615, 615, 615, 615, 615, 984, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 985, 
-    985, 985, 986, 987, 987, 987, 987, 987, 987, 988, 120, 989, 990, 990, 
-    991, 992, 992, 992, 992, 993, 120, 994, 994, 995, 996, 997, 997, 997, 
-    997, 998, 999, 1000, 1000, 1000, 1001, 1002, 1002, 1002, 1002, 1003, 
-    1002, 1004, 120, 120, 120, 120, 120, 1005, 1005, 1005, 1005, 1005, 1006, 
-    1006, 1006, 1006, 1006, 1007, 1007, 1007, 1007, 1007, 1007, 1008, 1008, 
-    1008, 1009, 1010, 1011, 1012, 1012, 1012, 1012, 1013, 1014, 1014, 1014, 
-    1014, 1015, 1016, 1016, 1016, 1016, 1016, 120, 1017, 1017, 1017, 1017, 
-    1017, 1017, 1018, 1019, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 1020, 1020, 1020, 1020, 1020, 
-    1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 
-    1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 
-    1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1021, 120, 1020, 
-    1020, 1022, 120, 1020, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 1023, 1024, 1025, 1025, 
-    1025, 1025, 1026, 1027, 1028, 1028, 1029, 1030, 1031, 1031, 1032, 1033, 
-    1034, 1034, 1034, 1035, 1036, 1037, 120, 120, 120, 120, 120, 120, 1038, 
-    1038, 1039, 1040, 1041, 1041, 1042, 1043, 1044, 1044, 1044, 1045, 120, 
-    120, 120, 120, 120, 120, 120, 120, 1046, 1046, 1046, 1046, 1047, 1047, 
-    1047, 1048, 1049, 1049, 1050, 1049, 1049, 1049, 1049, 1049, 1051, 1052, 
-    1053, 1054, 1055, 1055, 1056, 1057, 1058, 120, 1059, 1060, 1061, 1061, 
-    1061, 1062, 1063, 1063, 1063, 1064, 120, 120, 120, 120, 1065, 1066, 1065, 
-    1065, 1067, 1068, 1069, 120, 1070, 1070, 1070, 1070, 1070, 1070, 1071, 
-    1072, 1073, 1073, 1074, 1075, 1076, 1076, 1077, 1078, 1079, 1079, 1080, 
-    1081, 120, 1082, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1083, 
-    1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1084, 120, 120, 120, 120, 
-    120, 120, 1085, 1085, 1085, 1085, 1085, 1085, 1086, 120, 1087, 1087, 
-    1087, 1087, 1087, 1087, 1088, 1089, 120, 120, 120, 120, 120, 120, 120, 
+    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 898, 120, 
+    120, 128, 128, 128, 128, 128, 128, 128, 128, 899, 128, 128, 128, 128, 
+    128, 128, 120, 120, 120, 120, 120, 128, 900, 901, 901, 902, 903, 904, 
+    905, 906, 907, 908, 909, 910, 911, 912, 913, 170, 128, 128, 128, 128, 
+    128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 914, 915, 
+    916, 917, 918, 919, 920, 920, 921, 922, 923, 923, 924, 925, 926, 927, 
+    928, 928, 928, 928, 929, 930, 930, 930, 931, 932, 932, 932, 933, 934, 
+    935, 120, 936, 937, 938, 937, 937, 939, 937, 937, 940, 937, 941, 937, 
+    941, 120, 120, 120, 120, 937, 937, 937, 937, 937, 937, 937, 937, 937, 
+    937, 937, 937, 937, 937, 937, 942, 943, 944, 944, 944, 944, 944, 945, 
+    609, 946, 946, 946, 946, 946, 946, 947, 948, 949, 950, 580, 951, 952, 
+    120, 120, 120, 120, 120, 609, 609, 609, 609, 609, 953, 120, 120, 120, 
+    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 954, 
+    954, 954, 955, 956, 956, 956, 956, 956, 956, 957, 120, 958, 959, 959, 
+    960, 961, 961, 961, 961, 962, 963, 964, 964, 965, 966, 967, 967, 967, 
+    967, 968, 969, 970, 970, 970, 971, 972, 972, 972, 972, 973, 972, 974, 
+    120, 120, 120, 120, 120, 975, 975, 975, 975, 975, 976, 976, 976, 976, 
+    976, 977, 977, 977, 977, 977, 977, 978, 978, 978, 979, 980, 981, 982, 
+    982, 982, 982, 983, 984, 984, 984, 984, 985, 986, 986, 986, 986, 986, 
+    120, 987, 987, 987, 987, 987, 987, 988, 989, 120, 120, 120, 120, 120, 
+    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 990, 
+    990, 990, 990, 990, 990, 990, 990, 990, 990, 990, 990, 990, 990, 990, 
+    990, 990, 990, 990, 990, 990, 990, 990, 990, 990, 990, 990, 990, 990, 
+    990, 990, 990, 990, 990, 990, 990, 990, 990, 991, 120, 990, 990, 992, 
+    120, 990, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
+    120, 120, 120, 120, 120, 120, 120, 993, 994, 995, 995, 995, 995, 996, 
+    997, 998, 998, 999, 1000, 1001, 1001, 1002, 1003, 1004, 1004, 1004, 1005, 
+    1006, 1007, 120, 120, 120, 120, 120, 120, 1008, 1008, 1009, 1010, 1011, 
+    1011, 1012, 1013, 1014, 1014, 1014, 1015, 120, 120, 120, 120, 120, 120, 
+    120, 120, 1016, 1016, 1016, 1016, 1017, 1017, 1017, 1018, 1019, 1019, 
+    1020, 1019, 1019, 1019, 1019, 1019, 1021, 1022, 1023, 1024, 1025, 1025, 
+    1026, 1027, 1028, 120, 1029, 1030, 1031, 1031, 1031, 1032, 1033, 1033, 
+    1033, 1034, 120, 120, 120, 120, 1035, 1036, 1035, 1035, 1037, 1038, 1039, 
+    120, 1040, 1040, 1040, 1040, 1040, 1040, 1041, 1042, 1043, 1043, 1044, 
+    1045, 1046, 1046, 1047, 1048, 1049, 1049, 1050, 1051, 120, 1052, 120, 
+    120, 120, 120, 120, 120, 120, 120, 120, 120, 1053, 1053, 1053, 1053, 
+    1053, 1053, 1053, 1053, 1053, 1054, 120, 120, 120, 120, 120, 120, 1055, 
+    1055, 1055, 1055, 1055, 1055, 1056, 120, 1057, 1057, 1057, 1057, 1057, 
+    1057, 1058, 1059, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
     120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
     120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 1090, 1090, 1090, 1091, 120, 
+    120, 120, 120, 120, 120, 1060, 1060, 1060, 1061, 120, 120, 120, 120, 120, 
+    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1062, 1063, 1063, 
+    1063, 1063, 1063, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 120, 
+    1071, 1072, 1073, 1073, 1073, 1073, 1073, 1074, 1075, 1076, 120, 1077, 
+    1077, 1077, 1078, 1079, 1080, 1081, 1082, 1082, 1082, 1083, 1084, 1085, 
+    1086, 1087, 120, 1088, 1088, 1088, 1088, 1089, 120, 1090, 1091, 1091, 
+    1091, 1091, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 
+    120, 1101, 1101, 1102, 1101, 1101, 1103, 1104, 1105, 120, 120, 120, 120, 
+    120, 120, 120, 120, 1106, 1107, 1108, 1109, 1108, 1110, 1111, 1111, 1111, 
+    1111, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1120, 
+    1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1129, 120, 120, 
     120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 1092, 1093, 1093, 1093, 1093, 1093, 1093, 1094, 1095, 1096, 1097, 
-    1098, 1099, 1100, 120, 1101, 1102, 1103, 1103, 1103, 1103, 1103, 1104, 
-    1105, 1106, 120, 1107, 1107, 1107, 1108, 1109, 1110, 1111, 1112, 1112, 
-    1112, 1113, 1114, 1115, 1116, 1117, 120, 1118, 1118, 1118, 1118, 1119, 
-    120, 1120, 1121, 1121, 1121, 1121, 1121, 1122, 1123, 1124, 1125, 1126, 
-    1127, 1128, 1129, 1130, 120, 1131, 1131, 1132, 1131, 1131, 1133, 1134, 
-    1135, 120, 120, 120, 120, 120, 120, 120, 120, 1136, 1137, 1138, 1139, 
-    1138, 1140, 1141, 1141, 1141, 1141, 1141, 1142, 1143, 1144, 1145, 1146, 
-    1147, 1148, 1149, 1150, 1150, 1151, 1152, 1153, 1154, 1155, 1156, 1157, 
-    1158, 1159, 1159, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 1160, 1160, 1160, 1160, 1160, 1160, 1161, 
-    1162, 1163, 1164, 1165, 1166, 120, 120, 120, 120, 1167, 1167, 1167, 1167, 
-    1167, 1167, 1168, 1169, 1170, 120, 1171, 1172, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 1173, 1173, 1173, 1173, 1173, 1174, 1175, 1176, 1177, 1178, 1179, 
-    1180, 120, 120, 120, 120, 1181, 1181, 1181, 1181, 1181, 1181, 1182, 1183, 
-    1184, 120, 1185, 1186, 1187, 1188, 120, 120, 1189, 1189, 1189, 1189, 
-    1189, 1190, 1191, 120, 1192, 1193, 120, 120, 120, 120, 120, 120, 1194, 
-    1194, 1194, 1195, 1196, 1197, 1198, 1199, 120, 120, 120, 120, 120, 120, 
+    120, 1130, 1130, 1130, 1130, 1130, 1130, 1131, 1132, 1133, 1134, 1135, 
+    1136, 120, 120, 120, 120, 1137, 1137, 1137, 1137, 1137, 1137, 1138, 1139, 
+    1140, 120, 1141, 1142, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
+    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1143, 1143, 1143, 1143, 
+    1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 120, 120, 120, 120, 1151, 
+    1151, 1151, 1151, 1151, 1151, 1152, 1153, 1154, 120, 1155, 1156, 1157, 
+    1158, 120, 120, 1159, 1159, 1159, 1159, 1159, 1160, 1161, 120, 1162, 
+    1163, 120, 120, 120, 120, 120, 120, 1164, 1164, 1164, 1165, 1166, 1167, 
+    1168, 1169, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
     120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
     120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1200, 1200, 1200, 1200, 
-    1201, 1201, 1201, 1201, 1202, 1203, 1204, 1205, 120, 120, 120, 120, 120, 
+    120, 120, 120, 120, 1170, 1170, 1170, 1170, 1171, 1171, 1171, 1171, 1172, 
+    1173, 1174, 1175, 1176, 1177, 1178, 1178, 1178, 1178, 1179, 1180, 1181, 
+    120, 1182, 1183, 1184, 1184, 1184, 1184, 1185, 1186, 1187, 1188, 1189, 
+    120, 120, 120, 1190, 1190, 1190, 1190, 1190, 1190, 1190, 1191, 1192, 
+    1193, 1192, 1192, 1192, 1194, 1195, 1196, 1197, 120, 1198, 1199, 1200, 
+    1201, 1202, 1203, 1203, 1203, 1204, 1205, 1205, 1206, 1207, 120, 120, 
+    120, 120, 120, 120, 120, 120, 120, 1208, 1209, 1210, 1210, 1210, 1210, 
+    1211, 1212, 1213, 120, 1214, 1215, 120, 120, 120, 120, 120, 120, 120, 
+    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1216, 
+    1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 
+    1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 
+    1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 
+    1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 
+    1216, 1216, 1217, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
+    120, 1218, 1218, 1218, 1218, 1218, 1218, 1218, 1218, 1218, 1218, 1218, 
+    1218, 1218, 1219, 1220, 120, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 
+    1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 1216, 
+    1216, 1216, 1216, 1216, 1216, 1221, 120, 120, 120, 120, 120, 120, 120, 
     120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 1206, 1206, 1206, 1206, 1206, 1206, 1206, 1207, 
-    1208, 1209, 1208, 1208, 1208, 1210, 1211, 1212, 1213, 120, 1214, 1215, 
-    1216, 1217, 1218, 1219, 1219, 1219, 1220, 1221, 1221, 1222, 1223, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 1224, 1224, 1224, 1224, 1224, 
-    1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 
-    1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 
-    1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 
-    1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1225, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1226, 1226, 1226, 
-    1226, 1226, 1226, 1226, 1226, 1226, 1226, 1226, 1226, 1226, 1227, 1228, 
-    120, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 
-    1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 1224, 
-    1224, 1229, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1230, 1230, 1230, 
-    1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 
-    1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 
-    1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 
-    1230, 1230, 1230, 1230, 1231, 1230, 1230, 1230, 1230, 1232, 1233, 1230, 
-    1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 
-    1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 
-    1230, 1230, 1230, 1230, 1234, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 
-    1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 1230, 
-    1230, 1230, 1235, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
+    120, 120, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 
+    1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 
+    1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 
+    1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1223, 1222, 1222, 
+    1222, 1222, 1224, 1225, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 
+    1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 
+    1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1226, 1222, 1222, 
+    1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 1222, 
+    1222, 1222, 1222, 1222, 1222, 1222, 1222, 1227, 120, 120, 120, 120, 120, 
     120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 
-    1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 
-    1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 
-    1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 
-    1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1237, 1236, 
-    1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 
-    1236, 1238, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 826, 826, 826, 
-    826, 826, 826, 826, 826, 826, 826, 826, 826, 826, 826, 826, 826, 826, 
-    826, 826, 826, 826, 826, 826, 826, 826, 826, 826, 826, 826, 826, 826, 
-    826, 826, 826, 826, 826, 826, 826, 826, 1239, 1240, 1240, 1240, 1241, 
-    1242, 1243, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    1244, 1244, 1244, 1245, 1246, 120, 1247, 1247, 1247, 1247, 1247, 1247, 
-    1248, 1249, 1250, 120, 1251, 1252, 1253, 1247, 1247, 1254, 1247, 1247, 
+    120, 120, 120, 120, 120, 120, 120, 1228, 1228, 1228, 1228, 1228, 1228, 
+    1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 
+    1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 
+    1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 
+    1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 
+    1228, 1228, 1228, 1229, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 1228, 
+    1228, 1228, 1228, 1228, 1228, 1228, 1230, 120, 120, 120, 120, 120, 120, 
     120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1256, 120, 1257, 1258, 
-    1258, 1258, 1258, 1259, 120, 1260, 1261, 1262, 120, 120, 120, 120, 120, 
-    120, 120, 120, 1263, 120, 120, 120, 1264, 1264, 1264, 1264, 1264, 1264, 
-    1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 
-    1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 
-    1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 
-    1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 
-    1264, 1264, 1264, 1264, 1264, 1264, 1264, 1265, 120, 120, 1264, 1264, 
-    1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 
-    1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 1264, 
-    1264, 1264, 1264, 1264, 1266, 120, 1267, 120, 120, 120, 120, 120, 120, 
+    120, 120, 120, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 
+    795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 
+    795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, 
+    1231, 1232, 1232, 1232, 1233, 1234, 1235, 120, 120, 120, 120, 120, 120, 
+    120, 120, 120, 120, 120, 120, 1236, 1236, 1236, 1237, 1238, 120, 1239, 
+    1239, 1239, 1239, 1239, 1239, 1240, 1241, 1242, 120, 1243, 1244, 1245, 
+    1239, 1239, 1246, 1239, 1239, 120, 120, 120, 120, 120, 120, 120, 120, 
+    120, 120, 120, 120, 120, 120, 1247, 1247, 1247, 1247, 1247, 1247, 1247, 
+    1247, 1248, 120, 1249, 1250, 1250, 1250, 1250, 1251, 120, 1252, 1253, 
+    1254, 120, 120, 120, 120, 120, 120, 120, 120, 1255, 120, 120, 120, 1256, 
+    1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 
+    1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 
+    1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 
+    1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 
+    1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 
+    1257, 120, 120, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 
+    1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 
+    1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1258, 120, 1259, 
+    737, 737, 737, 737, 737, 737, 737, 737, 737, 737, 737, 737, 737, 737, 
+    737, 737, 737, 737, 737, 737, 737, 737, 737, 737, 737, 737, 737, 737, 
+    737, 737, 737, 737, 737, 737, 1260, 120, 120, 120, 120, 120, 120, 120, 
+    120, 120, 120, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 
+    1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 
+    1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 
+    1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 1261, 
+    1261, 1261, 1261, 1261, 1262, 1263, 1263, 1263, 1263, 1263, 1263, 1263, 
+    1263, 1263, 1263, 1263, 1263, 1263, 1264, 1263, 1265, 1263, 1266, 1263, 
+    1267, 1268, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 609, 
+    609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 
+    609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 
+    609, 1269, 120, 609, 609, 609, 609, 1270, 1271, 609, 609, 609, 609, 609, 
+    609, 1272, 1273, 1274, 1275, 1276, 1277, 609, 609, 609, 1278, 609, 609, 
+    609, 609, 609, 609, 609, 1279, 120, 120, 949, 949, 949, 949, 949, 949, 
+    949, 949, 1280, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
+    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 580, 580, 
+    580, 580, 580, 580, 580, 580, 580, 580, 616, 120, 944, 944, 1281, 120, 
     120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1268, 1268, 1268, 
-    1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1269, 1268, 
-    1270, 1268, 1271, 1268, 1272, 1273, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 
-    615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 
-    615, 615, 615, 615, 615, 615, 1274, 120, 615, 615, 615, 615, 1275, 1276, 
-    615, 615, 615, 615, 615, 615, 1277, 1278, 1279, 1280, 1281, 1282, 615, 
-    615, 615, 1283, 615, 615, 615, 615, 615, 615, 615, 1284, 120, 120, 980, 
-    980, 980, 980, 980, 980, 980, 980, 1285, 120, 120, 120, 120, 120, 120, 
+    120, 120, 1282, 1282, 1282, 1283, 1284, 1284, 1285, 1282, 1282, 1286, 
+    1287, 1284, 1284, 1282, 1282, 1282, 1283, 1284, 1284, 1288, 1289, 1290, 
+    1286, 1291, 1292, 1284, 1282, 1282, 1282, 1283, 1284, 1284, 1293, 1294, 
+    1295, 1296, 1284, 1284, 1284, 1297, 1298, 1299, 1300, 1284, 1284, 1285, 
+    1282, 1282, 1286, 1284, 1284, 1284, 1282, 1282, 1282, 1283, 1284, 1284, 
+    1285, 1282, 1282, 1286, 1284, 1284, 1284, 1282, 1282, 1282, 1283, 1284, 
+    1284, 1285, 1282, 1282, 1286, 1284, 1284, 1284, 1282, 1282, 1282, 1283, 
+    1284, 1284, 1301, 1282, 1282, 1282, 1302, 1284, 1284, 1303, 1304, 1282, 
+    1282, 1305, 1284, 1284, 1306, 1285, 1282, 1282, 1307, 1284, 1284, 1308, 
+    1309, 1282, 1282, 1310, 1284, 1284, 1284, 1311, 1282, 1282, 1282, 1302, 
+    1284, 1284, 1303, 1312, 1313, 1313, 1313, 1313, 1313, 1313, 1314, 1314, 
+    1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 
+    1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 1314, 
+    1314, 1314, 1314, 1314, 1314, 1314, 1315, 1315, 1315, 1315, 1315, 1315, 
+    1316, 1317, 1315, 1315, 1315, 1315, 1315, 1318, 1319, 1314, 1320, 1321, 
+    120, 1322, 1323, 1315, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
+    1324, 1325, 1325, 1326, 1327, 1328, 120, 120, 120, 120, 120, 120, 120, 
     120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 579, 579, 579, 579, 579, 579, 579, 579, 579, 579, 623, 
-    120, 975, 975, 1286, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 1287, 1287, 1287, 1288, 1289, 1289, 
-    1290, 1287, 1287, 1291, 1292, 1289, 1289, 1287, 1287, 1287, 1288, 1289, 
-    1289, 1293, 1294, 1295, 1291, 1296, 1297, 1289, 1287, 1287, 1287, 1288, 
-    1289, 1289, 1298, 1299, 1300, 1301, 1289, 1289, 1289, 1302, 1303, 1304, 
-    1305, 1289, 1289, 1290, 1287, 1287, 1291, 1289, 1289, 1289, 1287, 1287, 
-    1287, 1288, 1289, 1289, 1290, 1287, 1287, 1291, 1289, 1289, 1289, 1287, 
-    1287, 1287, 1288, 1289, 1289, 1290, 1287, 1287, 1291, 1289, 1289, 1289, 
-    1287, 1287, 1287, 1288, 1289, 1289, 1306, 1287, 1287, 1287, 1307, 1289, 
-    1289, 1308, 1309, 1287, 1287, 1310, 1289, 1289, 1311, 1290, 1287, 1287, 
-    1312, 1289, 1289, 1313, 1314, 1287, 1287, 1315, 1289, 1289, 1289, 1316, 
-    1287, 1287, 1287, 1307, 1289, 1289, 1308, 1317, 1318, 1318, 1318, 1318, 
-    1318, 1318, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 
-    1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 
-    1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1320, 1320, 
-    1320, 1320, 1320, 1320, 1321, 1322, 1320, 1320, 1320, 1320, 1320, 1323, 
-    1324, 1319, 1325, 1326, 120, 1327, 1328, 1320, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 1329, 1330, 1330, 1331, 1332, 1333, 120, 120, 
+    120, 120, 120, 120, 120, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 
+    1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 
+    1329, 1329, 1329, 1329, 1330, 1331, 1332, 120, 120, 120, 120, 120, 1333, 
+    1333, 1333, 1333, 1334, 1335, 1335, 1335, 1336, 1337, 1338, 1339, 120, 
     120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 1334, 1334, 1334, 1334, 
-    1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 
-    1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1335, 1336, 1337, 120, 
-    120, 120, 120, 120, 1338, 1338, 1338, 1338, 1339, 1340, 1340, 1340, 1341, 
-    1342, 1343, 1344, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 1345, 128, 128, 128, 1346, 
-    1347, 1348, 1349, 1350, 1351, 1346, 1352, 1346, 1348, 1348, 1353, 128, 
-    1354, 128, 1355, 1356, 1354, 128, 1355, 120, 120, 120, 120, 120, 120, 
-    1357, 120, 1358, 1359, 1359, 1359, 1359, 1360, 1359, 1359, 1359, 1359, 
-    1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1360, 1361, 1359, 1362, 
-    1363, 1359, 1363, 1364, 1363, 1359, 1359, 1359, 1365, 1361, 626, 1366, 
-    628, 628, 628, 1367, 628, 628, 628, 628, 628, 628, 628, 1368, 628, 628, 
-    628, 1369, 1370, 1371, 628, 1372, 1361, 1361, 1361, 1361, 1361, 1361, 
-    1373, 1374, 1374, 1374, 1375, 1361, 790, 790, 790, 790, 790, 1376, 790, 
-    1377, 1378, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 
-    1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 757, 
-    757, 757, 757, 1379, 1380, 1381, 757, 757, 757, 757, 757, 757, 757, 757, 
-    1382, 1383, 757, 1384, 1385, 757, 757, 1386, 1387, 1388, 1389, 1384, 
-    1359, 757, 757, 1390, 1391, 757, 757, 757, 757, 757, 757, 757, 1392, 
-    1393, 1394, 1395, 757, 1396, 1397, 1394, 1398, 1399, 757, 757, 757, 1400, 
-    1401, 1402, 757, 757, 757, 757, 757, 757, 757, 757, 1403, 1404, 757, 
-    1405, 649, 1406, 757, 1407, 1408, 579, 1409, 757, 757, 757, 1359, 1410, 
-    1411, 1359, 1359, 1412, 1359, 1358, 1359, 1359, 1359, 1359, 1359, 1413, 
-    1414, 1359, 1359, 1413, 1415, 757, 757, 757, 757, 757, 757, 757, 757, 
-    1416, 1417, 579, 579, 579, 579, 1418, 1419, 757, 757, 757, 757, 1420, 
-    757, 1421, 757, 1422, 1358, 1423, 1361, 1359, 1424, 1425, 1361, 579, 579, 
-    579, 579, 579, 579, 579, 579, 579, 579, 579, 579, 579, 579, 1426, 1361, 
-    579, 579, 579, 579, 579, 579, 579, 579, 579, 579, 1427, 1361, 1361, 1361, 
-    1361, 1361, 579, 1426, 579, 579, 579, 579, 579, 579, 579, 1361, 579, 
-    1428, 579, 579, 579, 579, 579, 1361, 579, 579, 579, 1429, 1361, 1361, 
-    1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 757, 1430, 
-    1431, 1361, 1432, 1433, 757, 1434, 757, 1435, 1361, 1361, 1361, 1361, 
-    757, 757, 1436, 1361, 1361, 1361, 1361, 1361, 1437, 1361, 1361, 1361, 
-    1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 
-    1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 
-    1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 
-    1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 
-    1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 
-    1361, 1361, 1361, 1361, 1361, 1361, 1361, 1438, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 808, 1439, 810, 810, 810, 810, 810, 808, 
-    808, 808, 808, 808, 808, 1440, 810, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 809, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 
-    808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 808, 917, 
-    810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 
-    810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 
-    810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 
-    810, 808, 808, 808, 809, 810, 810, 810, 810, 810, 810, 810, 810, 810, 
-    810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 
-    810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 
-    810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 810, 
-    810, 810, 810, 810, 810, 810, 810, 810, 1441, 1442, 120, 120, 120, 1443, 
-    1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 120, 
-    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 
-    120, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 
-    932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 
-    932, 932, 932, 120, 120, 916, 916, 916, 916, 916, 916, 916, 916, 916, 
-    916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 
-    916, 916, 916, 916, 916, 916, 916, 916, 1444, 
+    120, 120, 120, 120, 120, 1340, 128, 128, 128, 1341, 1342, 1343, 1344, 
+    1345, 1346, 1341, 1347, 1341, 1343, 1343, 1348, 128, 1349, 128, 1350, 
+    1351, 1349, 128, 1350, 120, 120, 120, 120, 120, 120, 1352, 120, 1353, 
+    1354, 1354, 1354, 1354, 1355, 1354, 1354, 1354, 1354, 1354, 1354, 1354, 
+    1354, 1354, 1354, 1354, 1354, 1355, 1356, 1354, 1357, 1358, 1354, 1358, 
+    1359, 1358, 1354, 1354, 1354, 1360, 1356, 619, 1361, 621, 621, 621, 1362, 
+    621, 621, 621, 621, 621, 621, 621, 1363, 621, 621, 621, 1364, 1365, 1366, 
+    621, 1367, 1356, 1356, 1356, 1356, 1356, 1356, 1368, 1369, 1369, 1369, 
+    1370, 1356, 758, 758, 758, 758, 758, 1371, 758, 1372, 1373, 1356, 1374, 
+    1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 
+    1356, 1356, 1356, 1356, 1356, 1356, 1356, 725, 725, 725, 725, 1375, 1376, 
+    1377, 725, 725, 725, 725, 725, 725, 725, 725, 1378, 1379, 725, 1380, 
+    1381, 725, 725, 1382, 1383, 1384, 1385, 1380, 1354, 725, 725, 1386, 1387, 
+    725, 725, 725, 725, 725, 725, 725, 1388, 1389, 1390, 1391, 725, 1392, 
+    1393, 1390, 1394, 1395, 725, 725, 725, 1396, 1397, 1398, 725, 725, 725, 
+    725, 725, 725, 725, 725, 1399, 1400, 725, 1401, 642, 1402, 725, 1403, 
+    1404, 580, 1405, 725, 725, 725, 1354, 1406, 1407, 1354, 1354, 1408, 1354, 
+    1353, 1354, 1354, 1354, 1354, 1354, 1409, 1410, 1354, 1354, 1409, 1411, 
+    725, 725, 725, 725, 725, 725, 725, 725, 1412, 1413, 580, 580, 580, 580, 
+    1414, 1415, 725, 725, 725, 725, 1416, 725, 1417, 725, 1418, 1419, 1420, 
+    1356, 1354, 1421, 1422, 1423, 580, 580, 580, 580, 580, 580, 580, 580, 
+    580, 580, 580, 580, 580, 580, 1424, 1356, 580, 580, 580, 580, 580, 580, 
+    580, 580, 580, 580, 1425, 1356, 1356, 1356, 1356, 1356, 580, 1424, 580, 
+    580, 580, 580, 580, 580, 580, 1356, 580, 1426, 580, 580, 580, 580, 580, 
+    1356, 580, 580, 580, 1427, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 
+    1356, 1356, 1356, 580, 1424, 725, 1428, 1429, 725, 1390, 1430, 725, 1431, 
+    725, 725, 725, 1432, 1356, 1356, 725, 725, 725, 1356, 1356, 1356, 1356, 
+    1356, 1423, 1356, 1433, 1434, 1435, 1356, 1356, 1356, 1356, 1356, 1356, 
+    1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 
+    1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 
+    1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 
+    1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 
+    1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 1356, 
+    1436, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 1437, 
+    778, 778, 778, 778, 778, 776, 776, 776, 776, 776, 776, 1438, 778, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 777, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 886, 778, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 
+    776, 776, 776, 1439, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 
+    778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 
+    778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 776, 776, 776, 
+    777, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 
+    778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 
+    778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 
+    778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 
+    778, 778, 778, 778, 1440, 1441, 120, 120, 120, 1442, 1442, 1442, 1442, 
+    1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 120, 120, 120, 120, 120, 
+    120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 901, 901, 901, 
+    901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 
+    901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 120, 
+    120, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 
+    885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 885, 
+    885, 885, 885, 885, 1443, 
 };
 
 static const unsigned short index2[] = {
     1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 1, 1, 1, 1, 1, 1, 7, 7, 7, 8, 
     9, 10, 11, 12, 13, 14, 15, 11, 16, 17, 15, 18, 19, 20, 19, 21, 22, 22, 
-    22, 22, 22, 22, 22, 22, 22, 22, 19, 23, 24, 25, 24, 10, 15, 26, 26, 26, 
-    26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 16, 27, 17, 
-    28, 29, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 
-    30, 30, 30, 16, 31, 32, 25, 1, 1, 1, 1, 1, 1, 33, 1, 1, 34, 35, 36, 13, 
-    37, 13, 38, 39, 40, 41, 42, 43, 25, 44, 45, 28, 46, 47, 48, 48, 49, 50, 
-    39, 39, 40, 48, 42, 51, 52, 52, 52, 35, 53, 53, 53, 53, 53, 53, 54, 53, 
-    53, 53, 53, 53, 53, 53, 53, 53, 54, 53, 53, 53, 53, 53, 53, 55, 54, 53, 
-    53, 53, 53, 53, 54, 56, 56, 56, 57, 57, 57, 57, 56, 57, 56, 56, 56, 57, 
-    56, 56, 57, 57, 56, 57, 56, 56, 57, 57, 57, 55, 56, 56, 56, 57, 56, 57, 
-    56, 57, 53, 56, 53, 57, 53, 57, 53, 57, 53, 57, 53, 57, 53, 57, 53, 57, 
-    53, 56, 53, 56, 53, 57, 53, 57, 53, 57, 53, 56, 53, 57, 53, 57, 53, 57, 
-    53, 57, 53, 57, 54, 56, 53, 56, 54, 56, 53, 57, 53, 57, 56, 53, 57, 53, 
-    57, 53, 57, 54, 56, 54, 56, 53, 56, 53, 57, 53, 56, 56, 54, 56, 53, 56, 
-    53, 57, 53, 57, 54, 56, 53, 57, 53, 57, 53, 53, 57, 53, 57, 53, 57, 57, 
-    57, 53, 53, 57, 53, 57, 53, 53, 57, 53, 53, 53, 57, 57, 53, 53, 53, 53, 
-    57, 53, 53, 57, 53, 53, 53, 57, 57, 57, 53, 53, 57, 53, 53, 57, 53, 57, 
-    53, 57, 53, 53, 57, 53, 57, 57, 53, 57, 53, 53, 57, 53, 53, 53, 57, 53, 
-    57, 53, 53, 57, 57, 58, 53, 57, 57, 57, 58, 58, 58, 58, 53, 59, 57, 53, 
-    59, 57, 53, 59, 57, 53, 56, 53, 56, 53, 56, 53, 56, 53, 56, 53, 56, 53, 
-    56, 53, 56, 57, 53, 57, 57, 53, 59, 57, 53, 57, 53, 53, 53, 57, 53, 57, 
-    57, 57, 57, 57, 57, 57, 53, 53, 57, 53, 53, 57, 57, 53, 57, 53, 53, 53, 
-    53, 57, 57, 56, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 
-    57, 57, 57, 57, 58, 57, 57, 57, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 
-    61, 62, 62, 62, 62, 62, 62, 62, 63, 63, 64, 63, 61, 65, 66, 65, 65, 65, 
-    66, 65, 61, 61, 67, 62, 63, 63, 63, 63, 63, 63, 40, 40, 40, 40, 63, 40, 
-    63, 49, 60, 60, 60, 60, 60, 63, 63, 63, 63, 63, 68, 68, 61, 63, 62, 63, 
-    63, 63, 63, 63, 63, 63, 63, 63, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 
-    69, 69, 69, 70, 71, 71, 71, 71, 70, 72, 71, 71, 71, 71, 71, 73, 73, 71, 
-    71, 71, 71, 73, 73, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 74, 74, 
-    74, 74, 74, 71, 71, 71, 71, 69, 69, 69, 69, 69, 69, 69, 69, 75, 69, 71, 
-    71, 71, 69, 69, 69, 71, 71, 76, 69, 69, 69, 71, 71, 71, 71, 69, 70, 71, 
-    71, 69, 77, 78, 78, 77, 78, 78, 77, 69, 69, 69, 69, 69, 79, 80, 79, 80, 
-    61, 81, 79, 80, 82, 82, 83, 80, 80, 80, 84, 79, 82, 82, 82, 82, 81, 63, 
-    79, 85, 79, 79, 79, 82, 79, 82, 79, 79, 80, 86, 86, 86, 86, 86, 86, 86, 
-    86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 82, 86, 86, 86, 86, 86, 86, 86, 
-    79, 79, 80, 80, 80, 80, 80, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 
-    87, 87, 87, 87, 87, 87, 80, 87, 87, 87, 87, 87, 87, 87, 80, 80, 80, 80, 
-    80, 79, 80, 80, 79, 79, 79, 80, 80, 80, 79, 80, 79, 80, 79, 80, 79, 80, 
-    79, 80, 88, 89, 88, 89, 88, 89, 88, 89, 88, 89, 88, 89, 88, 89, 80, 80, 
-    80, 80, 79, 80, 90, 79, 80, 79, 79, 80, 80, 79, 79, 79, 91, 92, 91, 91, 
-    91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 
-    92, 92, 93, 93, 93, 93, 93, 93, 93, 93, 94, 93, 94, 94, 94, 94, 94, 94, 
-    94, 94, 94, 94, 94, 94, 94, 94, 91, 94, 91, 94, 91, 94, 91, 94, 91, 94, 
-    95, 96, 96, 97, 97, 96, 98, 98, 91, 94, 91, 94, 91, 94, 91, 91, 94, 91, 
-    94, 91, 94, 91, 94, 91, 94, 91, 94, 91, 94, 94, 82, 99, 99, 99, 99, 99, 
-    99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 82, 
-    82, 100, 101, 101, 101, 101, 101, 101, 82, 102, 102, 102, 102, 102, 102, 
-    102, 102, 102, 102, 102, 102, 102, 102, 102, 82, 103, 104, 82, 82, 105, 
-    105, 106, 82, 107, 108, 108, 108, 108, 107, 108, 108, 108, 109, 107, 108, 
-    108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 108, 108, 107, 
-    108, 108, 109, 110, 108, 111, 112, 113, 114, 115, 116, 117, 118, 119, 
-    120, 120, 121, 122, 123, 124, 125, 126, 127, 128, 126, 108, 107, 129, 
-    119, 82, 82, 82, 82, 82, 82, 82, 82, 130, 130, 130, 130, 130, 130, 130, 
-    130, 130, 130, 130, 82, 82, 82, 82, 82, 130, 130, 130, 126, 126, 82, 82, 
-    82, 131, 131, 131, 131, 131, 132, 133, 133, 134, 135, 135, 136, 137, 138, 
-    139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 142, 143, 144, 
-    145, 82, 146, 144, 147, 147, 147, 147, 147, 147, 147, 147, 148, 147, 147, 
-    147, 147, 147, 147, 147, 147, 147, 147, 149, 150, 151, 152, 153, 154, 
-    155, 156, 97, 97, 157, 158, 140, 140, 140, 140, 140, 158, 140, 140, 158, 
-    159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 135, 160, 160, 161, 
-    147, 147, 162, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 
-    146, 147, 140, 140, 140, 140, 140, 140, 140, 132, 139, 140, 140, 140, 
-    140, 158, 140, 163, 163, 140, 140, 139, 158, 140, 140, 158, 147, 147, 
-    164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 147, 147, 147, 165, 
-    165, 147, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 
-    166, 166, 82, 167, 168, 169, 168, 168, 168, 168, 168, 168, 168, 168, 168, 
-    168, 168, 168, 168, 168, 170, 171, 170, 170, 171, 170, 170, 171, 171, 
-    171, 170, 171, 171, 170, 171, 170, 170, 170, 171, 170, 171, 170, 171, 
-    170, 171, 170, 170, 82, 82, 168, 168, 168, 172, 172, 172, 172, 172, 172, 
-    172, 172, 172, 172, 172, 172, 172, 172, 173, 173, 173, 173, 173, 173, 
-    173, 173, 173, 173, 173, 172, 82, 82, 82, 82, 82, 82, 174, 174, 174, 174, 
-    174, 174, 174, 174, 174, 174, 175, 175, 175, 175, 175, 175, 175, 175, 
-    175, 175, 175, 175, 175, 175, 175, 175, 175, 176, 176, 176, 176, 176, 
-    176, 176, 177, 176, 178, 178, 179, 180, 181, 182, 178, 82, 82, 82, 82, 
-    82, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 
-    184, 184, 184, 184, 185, 184, 184, 184, 184, 184, 184, 184, 184, 184, 
-    185, 184, 184, 184, 185, 184, 184, 184, 184, 184, 82, 82, 186, 186, 186, 
-    186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 82, 187, 187, 
-    187, 187, 187, 187, 187, 187, 187, 188, 188, 188, 82, 82, 189, 82, 147, 
-    147, 147, 147, 147, 82, 147, 147, 147, 147, 147, 147, 147, 147, 82, 82, 
-    82, 82, 82, 82, 140, 140, 140, 140, 140, 140, 132, 158, 140, 140, 158, 
-    140, 140, 158, 140, 140, 140, 158, 158, 158, 190, 191, 192, 140, 140, 
-    140, 158, 140, 140, 158, 158, 140, 140, 140, 140, 140, 193, 193, 193, 
-    194, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 
-    195, 193, 194, 196, 195, 194, 194, 194, 193, 193, 193, 193, 193, 193, 
-    193, 193, 194, 194, 194, 194, 197, 194, 194, 195, 97, 157, 198, 198, 193, 
-    193, 193, 195, 195, 193, 193, 199, 199, 200, 200, 200, 200, 200, 200, 
-    200, 200, 200, 200, 201, 202, 195, 195, 195, 195, 195, 195, 203, 204, 
-    205, 205, 82, 203, 203, 203, 203, 203, 203, 203, 203, 82, 82, 203, 203, 
-    82, 82, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 
-    203, 82, 203, 203, 203, 203, 203, 203, 203, 82, 203, 82, 82, 82, 203, 
-    203, 203, 203, 82, 82, 206, 203, 205, 205, 205, 204, 204, 204, 204, 82, 
-    82, 205, 205, 82, 82, 205, 205, 207, 203, 82, 82, 82, 82, 82, 82, 82, 82, 
-    205, 82, 82, 82, 82, 203, 203, 82, 203, 203, 203, 204, 204, 82, 82, 208, 
-    208, 208, 208, 208, 208, 208, 208, 208, 208, 203, 203, 209, 209, 210, 
-    210, 210, 210, 210, 211, 212, 213, 82, 82, 82, 82, 82, 214, 214, 215, 82, 
-    216, 216, 216, 216, 216, 216, 82, 82, 82, 82, 216, 216, 82, 82, 216, 216, 
-    216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 82, 216, 216, 
-    216, 216, 216, 216, 216, 82, 216, 216, 82, 216, 216, 82, 216, 216, 82, 
-    82, 217, 82, 215, 215, 215, 214, 214, 82, 82, 82, 82, 214, 214, 82, 82, 
-    214, 214, 218, 82, 82, 82, 214, 82, 82, 82, 82, 82, 82, 82, 216, 216, 
-    216, 216, 82, 216, 82, 82, 82, 82, 82, 82, 82, 219, 219, 219, 219, 219, 
-    219, 219, 219, 219, 219, 214, 214, 216, 216, 216, 214, 82, 82, 82, 220, 
-    220, 221, 82, 222, 222, 222, 222, 222, 222, 222, 222, 222, 82, 222, 222, 
-    222, 82, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 
-    222, 82, 222, 222, 222, 222, 222, 222, 222, 82, 222, 222, 82, 222, 222, 
-    222, 222, 222, 82, 82, 223, 222, 221, 221, 221, 220, 220, 220, 220, 220, 
-    82, 220, 220, 221, 82, 221, 221, 224, 82, 82, 222, 82, 82, 82, 82, 82, 
-    82, 82, 222, 222, 220, 220, 82, 82, 225, 225, 225, 225, 225, 225, 225, 
-    225, 225, 225, 226, 227, 82, 82, 82, 82, 82, 82, 82, 222, 82, 82, 82, 82, 
-    82, 82, 82, 228, 229, 229, 82, 230, 230, 230, 230, 230, 230, 230, 230, 
-    82, 82, 230, 230, 82, 82, 230, 230, 230, 230, 230, 230, 230, 230, 230, 
-    230, 230, 230, 230, 230, 82, 230, 230, 230, 230, 230, 230, 230, 82, 230, 
-    230, 82, 230, 230, 230, 230, 230, 82, 82, 231, 230, 229, 228, 229, 228, 
-    228, 228, 228, 82, 82, 229, 229, 82, 82, 229, 229, 232, 82, 82, 82, 82, 
-    82, 82, 82, 82, 228, 229, 82, 82, 82, 82, 230, 230, 82, 230, 230, 230, 
-    228, 228, 82, 82, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 234, 
-    230, 235, 235, 235, 235, 235, 235, 82, 82, 236, 237, 82, 237, 237, 237, 
-    237, 237, 237, 82, 82, 82, 237, 237, 237, 82, 237, 237, 237, 237, 82, 82, 
-    82, 237, 237, 82, 237, 82, 237, 237, 82, 82, 82, 237, 237, 82, 82, 82, 
-    237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 82, 82, 82, 82, 238, 
-    238, 236, 238, 238, 82, 82, 82, 238, 238, 238, 82, 238, 238, 238, 239, 
-    82, 82, 237, 82, 82, 82, 82, 82, 82, 238, 82, 82, 82, 82, 82, 82, 240, 
-    240, 240, 240, 240, 240, 240, 240, 240, 240, 241, 241, 241, 242, 242, 
-    242, 242, 242, 242, 243, 242, 82, 82, 82, 82, 82, 244, 245, 245, 245, 82, 
-    246, 246, 246, 246, 246, 246, 246, 246, 82, 246, 246, 246, 82, 246, 246, 
-    246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 82, 
-    82, 82, 246, 244, 244, 244, 245, 245, 245, 245, 82, 244, 244, 244, 82, 
-    244, 244, 244, 247, 82, 82, 82, 82, 82, 82, 82, 248, 249, 82, 246, 246, 
-    246, 82, 82, 82, 82, 82, 246, 246, 244, 244, 82, 82, 250, 250, 250, 250, 
-    250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251, 251, 251, 252, 
-    253, 254, 255, 255, 82, 253, 253, 253, 253, 253, 253, 253, 253, 82, 253, 
-    253, 253, 82, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 
-    253, 253, 253, 253, 253, 253, 82, 253, 253, 253, 253, 253, 82, 82, 256, 
-    253, 255, 257, 255, 255, 255, 255, 255, 82, 257, 255, 255, 82, 255, 255, 
-    254, 258, 82, 82, 82, 82, 82, 82, 82, 255, 255, 82, 82, 82, 82, 82, 82, 
-    82, 253, 82, 253, 253, 254, 254, 82, 82, 259, 259, 259, 259, 259, 259, 
-    259, 259, 259, 259, 82, 253, 253, 82, 82, 82, 82, 82, 82, 260, 261, 261, 
-    82, 262, 262, 262, 262, 262, 262, 262, 262, 82, 262, 262, 262, 82, 262, 
-    262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 
-    262, 262, 82, 82, 262, 261, 261, 261, 260, 260, 260, 260, 82, 261, 261, 
-    261, 82, 261, 261, 261, 263, 262, 264, 82, 82, 82, 82, 262, 262, 262, 
-    261, 265, 265, 265, 265, 265, 265, 265, 262, 262, 262, 260, 260, 82, 82, 
-    266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 265, 265, 265, 265, 
-    265, 265, 265, 265, 265, 267, 262, 262, 262, 262, 262, 262, 82, 82, 268, 
-    268, 82, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 
-    269, 269, 269, 269, 269, 82, 82, 82, 269, 269, 269, 269, 269, 269, 269, 
-    269, 82, 269, 269, 269, 269, 269, 269, 269, 269, 269, 82, 269, 82, 82, 
-    82, 82, 270, 82, 82, 82, 82, 268, 268, 268, 271, 271, 271, 82, 271, 82, 
-    268, 268, 268, 268, 268, 268, 268, 268, 82, 82, 82, 82, 82, 82, 272, 272, 
-    272, 272, 272, 272, 272, 272, 272, 272, 82, 82, 268, 268, 273, 82, 82, 
-    82, 82, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 
-    274, 274, 274, 275, 274, 274, 275, 275, 275, 275, 276, 276, 277, 82, 82, 
-    82, 82, 278, 274, 274, 274, 274, 274, 274, 279, 275, 280, 280, 280, 280, 
-    275, 275, 275, 281, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 
-    283, 283, 82, 82, 82, 82, 82, 284, 284, 82, 284, 82, 82, 284, 284, 82, 
-    284, 82, 82, 284, 82, 82, 82, 82, 82, 82, 284, 284, 284, 284, 82, 284, 
-    284, 284, 284, 284, 284, 284, 82, 284, 284, 284, 82, 284, 82, 284, 82, 
-    82, 284, 284, 82, 284, 284, 284, 284, 285, 284, 284, 285, 285, 285, 285, 
-    286, 286, 82, 285, 285, 284, 82, 82, 284, 284, 284, 284, 284, 82, 287, 
-    82, 288, 288, 288, 288, 285, 285, 82, 82, 289, 289, 289, 289, 289, 289, 
-    289, 289, 289, 289, 82, 82, 284, 284, 284, 284, 290, 291, 291, 291, 292, 
-    293, 292, 292, 294, 292, 292, 295, 294, 296, 296, 296, 296, 296, 294, 
-    297, 296, 297, 297, 297, 298, 298, 297, 297, 297, 297, 297, 297, 299, 
-    299, 299, 299, 299, 299, 299, 299, 299, 299, 300, 300, 300, 300, 300, 
-    300, 300, 300, 300, 300, 301, 298, 297, 298, 297, 302, 303, 304, 303, 
-    304, 305, 305, 290, 290, 290, 290, 290, 290, 290, 290, 82, 290, 290, 290, 
-    290, 290, 290, 290, 290, 290, 290, 290, 290, 82, 82, 82, 82, 306, 307, 
-    308, 309, 308, 308, 308, 308, 308, 307, 307, 307, 307, 308, 310, 307, 
-    308, 311, 311, 312, 295, 311, 311, 290, 290, 290, 290, 290, 308, 308, 
-    308, 308, 308, 308, 308, 308, 308, 308, 308, 82, 308, 308, 308, 308, 308, 
-    308, 308, 308, 308, 308, 308, 308, 82, 301, 301, 297, 297, 297, 297, 297, 
-    297, 298, 297, 297, 297, 297, 297, 297, 82, 297, 297, 292, 292, 295, 292, 
-    293, 313, 313, 313, 313, 294, 294, 82, 82, 82, 82, 82, 314, 314, 314, 
-    314, 314, 314, 314, 314, 314, 314, 314, 315, 315, 316, 316, 316, 316, 
-    315, 316, 316, 316, 316, 316, 317, 315, 318, 318, 315, 315, 316, 316, 
-    314, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 320, 320, 321, 
-    321, 321, 321, 314, 314, 314, 314, 314, 314, 315, 315, 316, 316, 314, 
-    314, 314, 314, 316, 316, 316, 314, 315, 315, 315, 314, 314, 315, 315, 
-    315, 315, 315, 315, 315, 314, 314, 314, 316, 316, 316, 316, 314, 314, 
-    314, 314, 314, 316, 315, 315, 316, 316, 315, 315, 315, 315, 315, 315, 
-    322, 314, 315, 319, 319, 315, 315, 315, 316, 323, 323, 324, 324, 324, 
-    324, 324, 324, 324, 324, 324, 324, 324, 324, 324, 324, 82, 324, 82, 82, 
-    82, 82, 82, 324, 82, 82, 325, 325, 325, 325, 325, 325, 325, 325, 325, 
-    325, 325, 326, 327, 325, 325, 325, 328, 328, 328, 328, 328, 328, 328, 
-    328, 329, 329, 329, 329, 329, 329, 329, 329, 330, 330, 330, 330, 330, 
-    330, 330, 330, 331, 331, 331, 331, 331, 331, 331, 331, 331, 82, 331, 331, 
-    331, 331, 82, 82, 331, 331, 331, 331, 331, 331, 331, 82, 331, 331, 331, 
-    82, 82, 332, 332, 332, 333, 334, 333, 333, 333, 333, 333, 333, 333, 335, 
-    335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 
-    335, 335, 335, 335, 335, 82, 82, 82, 336, 336, 336, 336, 336, 336, 336, 
-    336, 336, 336, 82, 82, 82, 82, 82, 82, 337, 337, 337, 337, 337, 337, 337, 
-    337, 337, 337, 337, 337, 337, 337, 82, 82, 338, 338, 338, 338, 338, 338, 
-    82, 82, 339, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 
-    340, 340, 340, 340, 340, 340, 340, 340, 341, 341, 340, 342, 343, 343, 
-    343, 343, 343, 343, 343, 343, 343, 343, 343, 343, 343, 343, 343, 343, 
-    343, 343, 344, 345, 82, 82, 82, 346, 346, 346, 346, 346, 346, 346, 346, 
-    346, 346, 346, 199, 199, 199, 347, 347, 347, 346, 346, 346, 346, 346, 
-    346, 346, 346, 82, 82, 82, 82, 82, 82, 82, 348, 348, 348, 348, 348, 348, 
-    348, 348, 348, 348, 348, 348, 348, 82, 348, 348, 348, 348, 349, 349, 350, 
-    82, 82, 82, 351, 351, 351, 351, 351, 351, 351, 351, 351, 351, 352, 352, 
-    353, 199, 199, 82, 354, 354, 354, 354, 354, 354, 354, 354, 354, 354, 355, 
-    355, 82, 82, 82, 82, 356, 356, 356, 356, 356, 356, 356, 356, 356, 356, 
-    356, 356, 356, 82, 356, 356, 356, 82, 357, 357, 82, 82, 82, 82, 358, 358, 
-    358, 358, 358, 358, 358, 358, 358, 358, 358, 358, 359, 359, 360, 359, 
-    359, 359, 359, 359, 359, 359, 360, 360, 360, 360, 360, 360, 360, 360, 
-    359, 360, 360, 359, 359, 359, 359, 359, 359, 359, 359, 359, 361, 359, 
-    362, 362, 363, 364, 362, 365, 362, 366, 358, 367, 82, 82, 368, 368, 368, 
-    368, 368, 368, 368, 368, 368, 368, 82, 82, 82, 82, 82, 82, 369, 369, 369, 
-    369, 369, 369, 369, 369, 369, 369, 82, 82, 82, 82, 82, 82, 370, 370, 371, 
-    371, 372, 373, 374, 370, 375, 375, 370, 376, 376, 376, 377, 82, 378, 378, 
-    378, 378, 378, 378, 378, 378, 378, 378, 82, 82, 82, 82, 82, 82, 379, 379, 
-    379, 379, 379, 379, 379, 379, 379, 379, 379, 380, 379, 379, 379, 379, 
-    379, 379, 379, 379, 379, 376, 376, 379, 379, 381, 379, 82, 82, 82, 82, 
-    82, 340, 340, 340, 340, 340, 340, 82, 82, 382, 382, 382, 382, 382, 382, 
-    382, 382, 382, 382, 382, 382, 382, 382, 382, 82, 383, 383, 383, 384, 384, 
-    384, 384, 383, 383, 384, 384, 384, 82, 82, 82, 82, 384, 384, 383, 384, 
-    384, 384, 384, 384, 384, 385, 386, 387, 82, 82, 82, 82, 388, 82, 82, 82, 
-    389, 389, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 391, 391, 
-    391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 82, 82, 391, 
-    391, 391, 391, 391, 82, 82, 82, 392, 392, 392, 392, 392, 392, 392, 392, 
-    392, 392, 392, 392, 82, 82, 82, 82, 392, 392, 82, 82, 82, 82, 82, 82, 
-    393, 393, 393, 393, 393, 393, 393, 393, 393, 393, 394, 82, 82, 82, 395, 
-    395, 396, 396, 396, 396, 396, 396, 396, 396, 397, 397, 397, 397, 397, 
-    397, 397, 397, 397, 397, 397, 397, 397, 397, 397, 398, 399, 400, 400, 
-    401, 82, 82, 402, 402, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 
-    403, 403, 403, 404, 405, 404, 405, 405, 405, 405, 405, 405, 405, 82, 406, 
-    404, 405, 404, 404, 405, 405, 405, 405, 405, 405, 405, 405, 404, 404, 
-    404, 404, 404, 404, 405, 405, 407, 407, 407, 407, 407, 407, 407, 407, 82, 
-    82, 408, 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, 82, 82, 82, 
-    82, 82, 82, 410, 410, 410, 410, 410, 410, 410, 411, 410, 410, 410, 410, 
-    410, 410, 82, 82, 97, 97, 97, 97, 97, 157, 157, 157, 157, 157, 157, 97, 
-    97, 157, 412, 82, 413, 413, 413, 413, 414, 415, 415, 415, 415, 415, 415, 
-    415, 415, 415, 415, 415, 415, 415, 415, 415, 416, 414, 413, 413, 413, 
-    413, 413, 414, 413, 414, 414, 414, 414, 414, 413, 414, 417, 415, 415, 
-    415, 415, 415, 415, 415, 82, 82, 82, 82, 418, 418, 418, 418, 418, 418, 
-    418, 418, 418, 418, 419, 419, 420, 419, 419, 419, 419, 421, 421, 421, 
-    421, 421, 421, 421, 421, 421, 421, 422, 423, 422, 422, 422, 422, 422, 
-    422, 422, 421, 421, 421, 421, 421, 421, 421, 421, 421, 82, 82, 82, 424, 
-    424, 425, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, 426, 
-    426, 426, 425, 424, 424, 424, 424, 425, 425, 424, 424, 427, 428, 424, 
-    424, 426, 426, 429, 429, 429, 429, 429, 429, 429, 429, 429, 429, 426, 
-    426, 426, 426, 426, 426, 430, 430, 430, 430, 430, 430, 430, 430, 430, 
-    430, 430, 430, 430, 430, 431, 432, 433, 433, 432, 432, 432, 433, 432, 
-    433, 433, 433, 434, 434, 82, 82, 82, 82, 82, 82, 82, 82, 435, 435, 435, 
-    435, 436, 436, 436, 436, 436, 436, 436, 436, 436, 436, 436, 436, 437, 
-    437, 437, 437, 437, 437, 437, 437, 438, 438, 438, 438, 438, 438, 438, 
-    438, 437, 437, 438, 439, 82, 82, 82, 440, 440, 440, 440, 440, 441, 441, 
-    441, 441, 441, 441, 441, 441, 441, 441, 82, 82, 82, 436, 436, 436, 442, 
-    442, 442, 442, 442, 442, 442, 442, 442, 442, 443, 443, 443, 443, 443, 
-    443, 443, 443, 443, 443, 443, 443, 443, 443, 444, 444, 444, 444, 444, 
-    444, 445, 445, 94, 82, 82, 82, 82, 82, 82, 82, 446, 446, 446, 446, 446, 
-    446, 446, 446, 97, 97, 97, 326, 447, 157, 157, 157, 157, 157, 97, 97, 
-    157, 157, 157, 157, 97, 448, 447, 447, 447, 447, 447, 447, 447, 449, 449, 
-    449, 449, 157, 449, 449, 449, 449, 448, 448, 97, 449, 449, 82, 97, 97, 
-    82, 82, 82, 82, 82, 82, 57, 57, 57, 57, 57, 57, 80, 80, 80, 80, 80, 94, 
-    60, 60, 60, 60, 60, 60, 60, 60, 60, 83, 83, 83, 83, 83, 60, 60, 60, 60, 
-    83, 83, 83, 83, 83, 57, 57, 57, 57, 57, 450, 57, 57, 57, 57, 57, 57, 57, 
-    57, 57, 57, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 83, 97, 97, 
-    157, 97, 97, 97, 97, 97, 97, 97, 157, 97, 97, 451, 452, 157, 453, 97, 97, 
-    97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 
-    97, 82, 82, 82, 82, 82, 97, 454, 157, 97, 157, 53, 57, 53, 57, 53, 57, 
-    57, 57, 57, 57, 57, 57, 57, 57, 53, 57, 80, 80, 80, 80, 80, 80, 80, 80, 
-    79, 79, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 82, 82, 79, 79, 
-    79, 79, 79, 79, 82, 82, 82, 79, 82, 79, 82, 79, 82, 79, 455, 455, 455, 
-    455, 455, 455, 455, 455, 80, 80, 80, 80, 80, 82, 80, 80, 79, 79, 79, 79, 
-    455, 81, 80, 81, 81, 81, 80, 80, 80, 82, 80, 80, 79, 79, 79, 79, 455, 81, 
-    81, 81, 80, 80, 80, 80, 82, 82, 80, 80, 79, 79, 79, 79, 82, 81, 81, 81, 
-    79, 79, 79, 79, 79, 81, 81, 81, 82, 82, 80, 80, 80, 82, 80, 80, 79, 79, 
-    79, 79, 455, 456, 81, 82, 457, 457, 457, 457, 457, 457, 457, 458, 457, 
-    457, 457, 459, 460, 461, 462, 463, 464, 465, 466, 464, 467, 468, 39, 85, 
-    469, 470, 471, 472, 469, 470, 471, 472, 39, 39, 473, 85, 474, 474, 474, 
-    475, 476, 477, 478, 479, 480, 481, 482, 34, 483, 484, 483, 483, 484, 485, 
-    486, 486, 85, 43, 51, 39, 487, 487, 473, 488, 488, 85, 85, 85, 489, 490, 
-    491, 487, 487, 487, 85, 85, 85, 85, 85, 85, 85, 85, 492, 85, 488, 85, 
-    373, 85, 373, 373, 373, 373, 85, 373, 373, 457, 493, 494, 494, 494, 494, 
-    82, 495, 496, 497, 498, 499, 499, 499, 499, 499, 499, 500, 60, 82, 82, 
-    48, 500, 500, 500, 500, 500, 501, 501, 492, 490, 491, 502, 500, 48, 48, 
-    48, 48, 500, 500, 500, 500, 500, 501, 501, 492, 490, 491, 82, 60, 60, 60, 
-    60, 60, 82, 82, 82, 278, 278, 278, 278, 278, 278, 278, 503, 278, 504, 
-    278, 278, 37, 278, 278, 278, 278, 278, 278, 278, 278, 278, 503, 278, 278, 
-    278, 278, 503, 278, 278, 503, 505, 505, 505, 505, 505, 505, 505, 505, 
-    505, 97, 97, 447, 447, 97, 97, 97, 97, 447, 447, 447, 97, 97, 412, 412, 
-    412, 412, 97, 412, 412, 412, 447, 447, 97, 157, 97, 447, 447, 157, 157, 
-    157, 157, 97, 82, 82, 82, 82, 82, 82, 82, 41, 41, 506, 507, 41, 508, 41, 
-    506, 41, 507, 50, 506, 506, 506, 50, 50, 506, 506, 506, 509, 41, 506, 
-    510, 41, 492, 506, 506, 506, 506, 506, 41, 41, 41, 508, 508, 41, 506, 41, 
-    86, 41, 506, 41, 53, 511, 506, 506, 512, 50, 506, 506, 53, 506, 50, 449, 
-    449, 449, 449, 50, 41, 41, 50, 50, 506, 506, 513, 492, 492, 492, 492, 
-    506, 50, 50, 50, 50, 41, 492, 41, 41, 57, 313, 514, 514, 514, 515, 52, 
-    516, 514, 514, 514, 514, 514, 52, 515, 515, 52, 514, 517, 517, 517, 517, 
-    517, 517, 517, 517, 517, 517, 517, 517, 518, 518, 518, 518, 517, 517, 
-    518, 518, 518, 518, 518, 518, 518, 518, 518, 53, 57, 518, 518, 518, 518, 
-    52, 41, 41, 82, 82, 82, 82, 55, 55, 55, 55, 55, 508, 508, 508, 508, 508, 
-    492, 492, 41, 41, 41, 41, 492, 41, 41, 492, 41, 41, 492, 41, 41, 41, 41, 
-    41, 41, 41, 492, 41, 41, 41, 41, 41, 41, 41, 41, 41, 45, 45, 41, 41, 41, 
-    41, 41, 41, 41, 41, 41, 41, 41, 41, 492, 492, 41, 41, 55, 41, 55, 41, 41, 
-    41, 41, 41, 41, 41, 41, 41, 41, 45, 41, 41, 41, 41, 492, 492, 492, 492, 
-    492, 492, 492, 492, 492, 492, 492, 492, 55, 513, 519, 519, 513, 492, 492, 
-    55, 519, 513, 513, 519, 513, 513, 492, 55, 492, 519, 520, 521, 492, 519, 
-    513, 492, 492, 492, 519, 513, 513, 519, 55, 519, 519, 513, 513, 55, 513, 
-    55, 513, 55, 55, 55, 55, 519, 519, 513, 519, 513, 513, 513, 513, 513, 55, 
-    55, 55, 55, 492, 513, 492, 513, 519, 519, 513, 513, 513, 513, 513, 513, 
-    513, 513, 513, 513, 519, 513, 513, 513, 519, 492, 492, 492, 492, 492, 
-    519, 513, 513, 513, 492, 492, 492, 492, 492, 492, 492, 492, 492, 513, 
-    519, 55, 513, 492, 519, 519, 519, 519, 513, 513, 519, 519, 492, 492, 519, 
-    519, 513, 513, 519, 519, 513, 513, 519, 519, 513, 513, 513, 513, 513, 
-    492, 492, 513, 513, 513, 513, 492, 492, 55, 492, 492, 513, 55, 492, 492, 
-    492, 492, 492, 492, 492, 492, 513, 513, 492, 55, 513, 513, 513, 492, 492, 
-    492, 492, 492, 513, 519, 492, 513, 513, 513, 513, 513, 492, 492, 513, 
-    513, 492, 492, 492, 492, 513, 513, 513, 513, 513, 513, 513, 513, 492, 
-    522, 490, 491, 490, 491, 41, 41, 41, 41, 41, 41, 508, 41, 41, 41, 41, 41, 
-    41, 41, 523, 523, 41, 41, 41, 41, 513, 513, 41, 41, 41, 41, 41, 41, 41, 
-    524, 525, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 313, 313, 313, 313, 
-    313, 313, 313, 313, 313, 313, 313, 313, 313, 41, 492, 41, 41, 41, 41, 41, 
-    41, 41, 41, 313, 41, 41, 41, 41, 41, 492, 492, 492, 492, 492, 492, 492, 
-    492, 492, 41, 41, 41, 41, 492, 492, 41, 41, 41, 41, 41, 41, 41, 526, 526, 
-    526, 526, 41, 41, 41, 523, 527, 527, 523, 41, 41, 41, 41, 41, 41, 41, 41, 
-    41, 41, 41, 82, 41, 41, 41, 82, 82, 82, 82, 82, 52, 52, 52, 52, 52, 52, 
-    52, 52, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 528, 528, 528, 
-    528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 516, 52, 52, 52, 
-    52, 52, 52, 52, 52, 52, 52, 52, 52, 515, 508, 508, 508, 508, 508, 508, 
-    508, 508, 508, 508, 508, 508, 41, 41, 41, 41, 508, 508, 508, 508, 529, 
-    41, 41, 41, 41, 41, 508, 508, 508, 508, 41, 41, 508, 508, 41, 508, 508, 
-    508, 508, 508, 508, 508, 41, 41, 41, 41, 41, 41, 41, 41, 508, 508, 41, 
-    41, 508, 55, 41, 41, 41, 41, 508, 508, 41, 41, 508, 55, 41, 41, 41, 41, 
-    508, 508, 508, 41, 41, 508, 41, 41, 508, 508, 41, 41, 41, 41, 41, 41, 41, 
-    508, 492, 492, 492, 492, 492, 530, 530, 492, 527, 527, 527, 527, 41, 508, 
-    508, 41, 41, 508, 41, 41, 41, 41, 508, 508, 41, 41, 41, 41, 523, 523, 
-    529, 529, 527, 41, 527, 527, 531, 532, 531, 527, 41, 527, 527, 527, 41, 
-    41, 41, 41, 508, 41, 508, 41, 41, 41, 41, 41, 526, 526, 526, 526, 526, 
-    526, 526, 526, 526, 526, 526, 526, 41, 41, 41, 41, 508, 508, 41, 508, 
-    508, 508, 41, 508, 531, 508, 508, 41, 508, 508, 41, 55, 41, 41, 41, 41, 
-    41, 41, 41, 523, 41, 41, 41, 526, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 
-    508, 508, 41, 526, 41, 41, 41, 41, 41, 41, 41, 41, 526, 526, 313, 41, 41, 
-    41, 41, 41, 41, 41, 41, 523, 523, 531, 527, 527, 527, 527, 523, 523, 531, 
-    531, 531, 508, 508, 508, 508, 531, 526, 531, 531, 531, 508, 531, 523, 
-    508, 508, 508, 531, 531, 508, 508, 531, 508, 508, 531, 531, 531, 41, 508, 
-    41, 41, 41, 41, 508, 508, 523, 508, 508, 508, 508, 508, 508, 531, 523, 
-    523, 531, 523, 508, 531, 531, 533, 523, 508, 508, 523, 531, 531, 527, 
-    527, 527, 527, 527, 526, 41, 41, 527, 527, 534, 534, 532, 532, 41, 41, 
-    526, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 45, 41, 41, 41, 41, 
-    41, 41, 526, 41, 526, 41, 41, 41, 41, 526, 526, 526, 41, 535, 41, 41, 41, 
-    536, 536, 536, 536, 536, 536, 41, 537, 537, 527, 41, 41, 41, 490, 491, 
-    490, 491, 490, 491, 490, 491, 490, 491, 490, 491, 490, 491, 52, 52, 516, 
-    516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 41, 526, 526, 526, 
-    41, 41, 41, 41, 41, 41, 41, 526, 513, 492, 492, 513, 513, 490, 491, 492, 
-    513, 513, 492, 513, 513, 513, 492, 492, 492, 492, 492, 513, 513, 513, 
-    513, 492, 492, 492, 492, 492, 513, 513, 513, 492, 492, 492, 513, 513, 
-    513, 513, 16, 32, 16, 32, 16, 32, 16, 32, 490, 491, 538, 538, 538, 538, 
-    538, 538, 538, 538, 492, 492, 492, 490, 491, 16, 32, 490, 491, 490, 491, 
-    490, 491, 490, 491, 490, 491, 492, 492, 513, 513, 513, 513, 513, 513, 
-    492, 492, 492, 492, 492, 492, 492, 513, 513, 513, 513, 513, 513, 492, 
-    492, 492, 513, 492, 492, 492, 492, 513, 513, 513, 513, 513, 492, 513, 
-    513, 492, 492, 490, 491, 490, 491, 513, 492, 492, 492, 492, 513, 492, 
-    513, 513, 513, 492, 492, 513, 513, 492, 492, 492, 492, 492, 492, 492, 
-    492, 492, 492, 513, 513, 513, 513, 513, 513, 492, 492, 490, 491, 492, 
-    492, 492, 492, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 
-    492, 513, 513, 513, 513, 492, 492, 513, 492, 513, 492, 492, 513, 492, 
-    513, 513, 513, 513, 492, 492, 492, 492, 492, 513, 513, 492, 492, 492, 
-    492, 513, 513, 513, 513, 492, 513, 513, 492, 492, 513, 513, 492, 492, 
-    492, 492, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 492, 
-    492, 513, 513, 513, 513, 513, 513, 513, 513, 492, 513, 513, 513, 513, 
-    513, 513, 513, 513, 492, 492, 492, 492, 492, 513, 492, 513, 492, 492, 
-    492, 513, 513, 513, 513, 513, 492, 492, 492, 492, 513, 492, 492, 492, 
-    513, 513, 513, 513, 513, 492, 513, 492, 492, 41, 41, 41, 526, 526, 41, 
-    41, 41, 492, 492, 492, 492, 492, 41, 41, 492, 492, 492, 492, 492, 492, 
-    41, 41, 41, 526, 41, 41, 41, 41, 535, 508, 508, 41, 41, 41, 41, 82, 82, 
-    41, 41, 41, 41, 41, 41, 41, 41, 82, 82, 41, 41, 82, 82, 82, 41, 41, 41, 
-    41, 82, 41, 41, 41, 41, 41, 41, 41, 41, 82, 82, 82, 82, 82, 82, 82, 82, 
-    82, 82, 41, 41, 41, 41, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 
-    539, 539, 539, 539, 539, 82, 540, 540, 540, 540, 540, 540, 540, 540, 540, 
-    540, 540, 540, 540, 540, 540, 82, 53, 57, 53, 53, 53, 57, 57, 53, 57, 53, 
-    57, 53, 57, 53, 53, 53, 53, 57, 53, 57, 57, 53, 57, 57, 57, 57, 57, 57, 
-    60, 60, 53, 53, 88, 89, 88, 89, 89, 541, 541, 541, 541, 541, 541, 88, 89, 
-    88, 89, 542, 542, 542, 88, 89, 82, 82, 82, 82, 82, 543, 544, 544, 544, 
-    545, 543, 544, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 
-    546, 546, 546, 82, 546, 82, 82, 82, 82, 82, 546, 82, 82, 547, 547, 547, 
-    547, 547, 547, 547, 547, 82, 82, 82, 82, 82, 82, 82, 548, 549, 82, 82, 
-    82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 550, 96, 96, 96, 96, 96, 
-    96, 96, 96, 551, 551, 43, 51, 43, 51, 551, 551, 551, 43, 51, 551, 43, 51, 
-    373, 373, 373, 373, 373, 373, 373, 373, 85, 466, 552, 373, 553, 85, 43, 
-    51, 85, 85, 43, 51, 490, 491, 490, 491, 490, 491, 490, 491, 373, 373, 
-    373, 373, 371, 61, 373, 373, 85, 373, 373, 85, 85, 85, 85, 85, 554, 554, 
-    373, 373, 373, 85, 466, 373, 471, 373, 373, 82, 82, 82, 555, 555, 555, 
-    555, 555, 555, 555, 555, 555, 555, 82, 555, 555, 555, 555, 555, 555, 555, 
-    555, 555, 82, 82, 82, 82, 555, 555, 555, 555, 555, 555, 82, 82, 523, 523, 
-    523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 82, 82, 82, 82, 556, 
-    557, 557, 558, 523, 559, 560, 561, 524, 525, 524, 525, 524, 525, 524, 
-    525, 524, 525, 523, 523, 524, 525, 524, 525, 524, 525, 524, 525, 562, 
-    563, 564, 564, 523, 561, 561, 561, 561, 561, 561, 561, 561, 561, 565, 
-    566, 567, 568, 569, 569, 570, 571, 571, 571, 571, 572, 523, 523, 561, 
-    561, 561, 559, 573, 558, 523, 527, 82, 574, 575, 574, 575, 574, 575, 574, 
-    575, 574, 575, 575, 575, 575, 575, 575, 575, 575, 575, 575, 575, 575, 
-    575, 575, 575, 575, 575, 574, 575, 575, 575, 575, 575, 575, 575, 574, 
-    575, 574, 575, 574, 575, 575, 575, 575, 575, 575, 574, 575, 575, 575, 
-    575, 575, 575, 574, 574, 82, 82, 576, 576, 577, 577, 578, 578, 575, 562, 
-    579, 580, 579, 580, 579, 580, 579, 580, 579, 580, 580, 580, 580, 580, 
-    580, 580, 580, 580, 580, 580, 580, 580, 580, 580, 580, 580, 579, 580, 
-    580, 580, 580, 580, 580, 580, 579, 580, 579, 580, 579, 580, 580, 580, 
-    580, 580, 580, 579, 580, 580, 580, 580, 580, 580, 579, 579, 580, 580, 
-    580, 580, 581, 582, 583, 583, 580, 82, 82, 82, 82, 82, 584, 584, 584, 
-    584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 82, 
-    82, 82, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 
-    585, 585, 585, 585, 585, 585, 585, 585, 585, 82, 586, 586, 587, 587, 587, 
-    587, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 584, 584, 584, 82, 
-    82, 82, 82, 82, 579, 579, 579, 579, 579, 579, 579, 579, 588, 588, 588, 
-    588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 589, 589, 82, 587, 587, 
-    587, 587, 587, 587, 587, 587, 587, 587, 586, 586, 586, 586, 586, 586, 
-    590, 590, 590, 590, 590, 590, 590, 590, 523, 591, 591, 591, 591, 591, 
-    591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 588, 588, 588, 588, 
-    589, 589, 589, 586, 586, 591, 591, 591, 591, 591, 591, 591, 586, 586, 
-    586, 586, 523, 523, 523, 523, 592, 592, 592, 592, 592, 592, 592, 592, 
-    592, 592, 592, 592, 592, 592, 592, 82, 586, 586, 586, 586, 586, 586, 586, 
-    523, 523, 523, 523, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 
-    586, 523, 523, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 
-    593, 593, 593, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 595, 
-    595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 596, 595, 
-    595, 595, 595, 595, 595, 595, 82, 82, 82, 597, 597, 597, 597, 597, 597, 
-    597, 597, 597, 597, 597, 597, 597, 597, 597, 82, 598, 598, 598, 598, 598, 
-    598, 598, 598, 599, 599, 599, 599, 599, 599, 600, 600, 601, 601, 601, 
-    601, 601, 601, 601, 601, 601, 601, 601, 601, 602, 603, 604, 603, 605, 
-    605, 605, 605, 605, 605, 605, 605, 605, 605, 601, 601, 82, 82, 82, 82, 
-    91, 94, 91, 94, 91, 94, 606, 96, 98, 98, 98, 607, 96, 96, 96, 96, 96, 96, 
-    96, 96, 96, 96, 607, 608, 91, 94, 91, 94, 450, 450, 96, 96, 609, 609, 
-    609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 610, 610, 
-    610, 610, 610, 610, 610, 610, 610, 610, 611, 611, 612, 613, 613, 613, 
-    613, 613, 63, 63, 63, 63, 63, 63, 63, 61, 61, 61, 61, 61, 61, 61, 61, 61, 
-    63, 63, 53, 57, 53, 57, 53, 57, 57, 57, 53, 57, 53, 57, 53, 57, 60, 57, 
-    57, 57, 57, 57, 57, 57, 57, 53, 57, 53, 57, 53, 53, 57, 61, 614, 614, 53, 
-    57, 53, 57, 58, 53, 57, 53, 57, 57, 57, 53, 57, 53, 57, 53, 53, 53, 53, 
-    53, 82, 53, 53, 53, 53, 53, 57, 53, 57, 82, 82, 82, 82, 82, 82, 82, 58, 
-    60, 60, 57, 58, 58, 58, 58, 58, 615, 615, 616, 615, 615, 615, 617, 615, 
-    615, 615, 615, 616, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 
-    615, 615, 615, 615, 615, 618, 618, 616, 616, 618, 619, 619, 619, 619, 82, 
-    82, 82, 82, 620, 620, 620, 620, 620, 620, 313, 313, 503, 512, 82, 82, 82, 
-    82, 82, 82, 621, 621, 621, 621, 621, 621, 621, 621, 621, 621, 621, 621, 
-    622, 622, 623, 623, 624, 624, 625, 625, 625, 625, 625, 625, 625, 625, 
-    625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 624, 624, 624, 624, 
-    624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 626, 627, 82, 
-    82, 82, 82, 82, 82, 82, 82, 628, 628, 629, 629, 629, 629, 629, 629, 629, 
-    629, 629, 629, 82, 82, 82, 82, 82, 82, 198, 198, 198, 198, 198, 198, 198, 
-    198, 198, 198, 195, 195, 195, 195, 195, 195, 201, 201, 201, 195, 630, 
-    195, 82, 82, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 632, 632, 
-    632, 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, 632, 
-    632, 632, 632, 632, 633, 633, 633, 633, 633, 634, 634, 634, 199, 635, 
-    636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 
-    636, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 637, 638, 639, 82, 
-    82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 640, 328, 328, 328, 328, 328, 82, 
-    82, 82, 641, 641, 641, 642, 643, 643, 643, 643, 643, 643, 643, 643, 643, 
-    643, 643, 643, 643, 643, 643, 644, 642, 642, 641, 641, 641, 641, 642, 
-    642, 641, 642, 642, 642, 645, 646, 646, 646, 646, 646, 646, 647, 647, 
-    647, 646, 646, 646, 646, 82, 62, 648, 648, 648, 648, 648, 648, 648, 648, 
-    648, 648, 82, 82, 82, 82, 646, 646, 314, 314, 314, 314, 314, 316, 649, 
-    314, 319, 319, 314, 314, 314, 314, 314, 82, 650, 650, 650, 650, 650, 650, 
-    650, 650, 650, 651, 651, 651, 651, 651, 651, 652, 652, 651, 651, 652, 
-    652, 651, 651, 82, 650, 650, 650, 651, 650, 650, 650, 650, 650, 650, 650, 
-    650, 651, 652, 82, 82, 653, 653, 653, 653, 653, 653, 653, 653, 653, 653, 
-    82, 82, 654, 655, 655, 655, 649, 314, 314, 314, 314, 314, 314, 323, 323, 
-    323, 314, 315, 316, 315, 314, 314, 656, 656, 656, 656, 656, 656, 656, 
-    656, 657, 656, 657, 657, 658, 656, 656, 657, 657, 656, 656, 656, 656, 
-    656, 657, 657, 656, 657, 656, 82, 82, 82, 82, 82, 82, 82, 82, 656, 656, 
-    659, 660, 660, 661, 661, 661, 661, 661, 661, 661, 661, 661, 661, 661, 
-    662, 663, 663, 662, 662, 664, 664, 661, 665, 665, 662, 666, 82, 82, 331, 
-    331, 331, 331, 331, 331, 82, 57, 57, 57, 614, 60, 60, 60, 60, 57, 57, 57, 
-    57, 57, 80, 82, 82, 338, 338, 338, 338, 338, 338, 338, 338, 661, 661, 
-    661, 662, 662, 663, 662, 662, 663, 662, 662, 664, 662, 666, 82, 82, 667, 
-    667, 667, 667, 667, 667, 667, 667, 667, 667, 82, 82, 82, 82, 82, 82, 668, 
-    669, 669, 669, 669, 669, 669, 669, 669, 669, 669, 669, 669, 669, 669, 
-    669, 669, 669, 669, 669, 668, 669, 669, 669, 669, 669, 669, 669, 82, 82, 
-    82, 82, 329, 329, 329, 329, 329, 329, 329, 82, 82, 82, 82, 330, 330, 330, 
-    330, 330, 330, 330, 330, 330, 82, 82, 82, 82, 670, 670, 670, 670, 670, 
-    670, 670, 670, 671, 671, 671, 671, 671, 671, 671, 671, 593, 593, 594, 
-    594, 594, 594, 594, 594, 57, 57, 57, 57, 57, 57, 57, 82, 82, 82, 82, 102, 
-    102, 102, 102, 102, 82, 82, 82, 82, 82, 130, 672, 130, 130, 673, 130, 
-    130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 82, 130, 130, 
-    130, 130, 130, 82, 130, 82, 130, 130, 82, 130, 130, 82, 130, 130, 147, 
-    147, 674, 674, 674, 674, 674, 674, 674, 674, 674, 674, 674, 674, 674, 
-    674, 674, 674, 82, 82, 82, 82, 82, 82, 82, 82, 82, 147, 147, 147, 147, 
-    147, 147, 147, 147, 147, 147, 147, 675, 471, 82, 82, 147, 147, 147, 147, 
-    147, 147, 147, 147, 147, 147, 136, 139, 82, 82, 676, 676, 676, 676, 676, 
-    676, 676, 676, 677, 557, 557, 677, 677, 678, 678, 563, 564, 679, 82, 82, 
-    82, 82, 82, 82, 97, 97, 97, 97, 97, 97, 97, 157, 157, 157, 157, 157, 157, 
-    157, 96, 96, 558, 570, 570, 680, 680, 563, 564, 563, 564, 563, 564, 563, 
-    564, 563, 564, 563, 564, 563, 564, 563, 564, 558, 558, 563, 564, 558, 
-    558, 558, 558, 680, 680, 680, 681, 558, 681, 82, 581, 682, 678, 678, 570, 
-    524, 525, 524, 525, 524, 525, 683, 558, 558, 684, 685, 686, 686, 687, 82, 
-    558, 688, 689, 558, 82, 82, 82, 82, 147, 147, 147, 147, 147, 82, 82, 493, 
-    82, 690, 691, 692, 693, 694, 691, 691, 695, 696, 691, 697, 698, 699, 698, 
-    700, 701, 701, 701, 701, 701, 701, 701, 701, 701, 701, 702, 703, 704, 
-    705, 704, 690, 691, 706, 706, 706, 706, 706, 706, 706, 706, 706, 706, 
-    706, 706, 706, 706, 706, 706, 706, 706, 695, 691, 696, 707, 708, 707, 
-    709, 709, 709, 709, 709, 709, 709, 709, 709, 709, 709, 709, 709, 709, 
-    709, 709, 709, 709, 695, 705, 696, 705, 695, 696, 710, 711, 712, 710, 
-    713, 714, 715, 715, 715, 715, 715, 715, 715, 715, 715, 716, 714, 714, 
-    714, 714, 714, 714, 714, 714, 714, 714, 714, 714, 714, 714, 714, 714, 
-    714, 714, 714, 714, 714, 717, 717, 718, 718, 718, 718, 718, 718, 718, 
-    718, 718, 718, 718, 718, 718, 718, 718, 82, 82, 82, 718, 718, 718, 718, 
-    718, 718, 82, 82, 718, 718, 718, 82, 82, 82, 719, 693, 705, 707, 720, 
-    693, 693, 82, 721, 722, 722, 722, 722, 721, 721, 82, 82, 723, 723, 723, 
-    724, 508, 82, 82, 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, 
-    725, 82, 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, 82, 725, 725, 
-    725, 82, 725, 725, 82, 725, 725, 725, 725, 725, 725, 725, 82, 82, 725, 
-    725, 725, 82, 82, 82, 82, 82, 199, 373, 199, 82, 82, 82, 82, 620, 620, 
-    620, 620, 620, 620, 620, 620, 620, 620, 620, 620, 620, 82, 82, 82, 313, 
-    726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 727, 
-    727, 727, 727, 728, 728, 728, 728, 728, 728, 728, 728, 728, 728, 728, 
-    728, 728, 728, 728, 728, 728, 727, 727, 728, 729, 729, 82, 41, 41, 41, 
-    41, 82, 82, 82, 82, 728, 82, 82, 82, 82, 82, 82, 82, 313, 313, 313, 313, 
-    313, 157, 82, 82, 730, 730, 730, 730, 730, 730, 730, 730, 730, 730, 730, 
-    730, 730, 82, 82, 82, 731, 731, 731, 731, 731, 731, 731, 731, 731, 82, 
-    82, 82, 82, 82, 82, 82, 157, 500, 500, 500, 500, 500, 500, 500, 500, 500, 
-    500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 82, 82, 82, 82, 732, 
-    732, 732, 732, 732, 732, 732, 732, 733, 733, 733, 733, 82, 82, 82, 82, 
-    734, 734, 734, 734, 734, 734, 734, 734, 734, 735, 734, 734, 734, 734, 
-    734, 734, 734, 734, 735, 82, 82, 82, 82, 82, 736, 736, 736, 736, 736, 
-    736, 736, 736, 736, 736, 736, 736, 736, 736, 737, 737, 737, 737, 737, 82, 
-    82, 82, 82, 82, 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, 738, 
-    738, 738, 738, 82, 739, 740, 740, 740, 740, 740, 740, 740, 740, 740, 740, 
-    740, 740, 82, 82, 82, 82, 741, 742, 742, 742, 742, 742, 82, 82, 743, 743, 
-    743, 743, 743, 743, 743, 743, 744, 744, 744, 744, 744, 744, 744, 744, 
-    745, 745, 745, 745, 745, 745, 745, 745, 746, 746, 746, 746, 746, 746, 
-    746, 746, 746, 746, 746, 746, 746, 746, 82, 82, 747, 747, 747, 747, 747, 
-    747, 747, 747, 747, 747, 82, 82, 82, 82, 82, 82, 748, 748, 748, 748, 748, 
-    748, 748, 748, 748, 748, 748, 748, 82, 82, 82, 82, 749, 749, 749, 749, 
-    749, 749, 749, 749, 749, 749, 749, 749, 82, 82, 82, 82, 750, 750, 750, 
-    750, 750, 750, 750, 750, 751, 751, 751, 751, 751, 751, 751, 751, 751, 
-    751, 751, 751, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 752, 753, 753, 
-    753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 82, 753, 
-    753, 753, 753, 753, 753, 82, 82, 754, 754, 754, 754, 754, 754, 82, 82, 
-    754, 82, 754, 754, 754, 754, 754, 754, 754, 754, 754, 754, 754, 754, 754, 
-    754, 754, 754, 754, 754, 754, 754, 82, 754, 754, 82, 82, 82, 754, 82, 82, 
-    754, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 
-    755, 82, 756, 757, 757, 757, 757, 757, 757, 757, 757, 758, 758, 758, 758, 
-    758, 758, 758, 758, 758, 758, 758, 758, 758, 758, 758, 759, 759, 760, 
-    760, 760, 760, 760, 760, 760, 761, 761, 761, 761, 761, 761, 761, 761, 
-    761, 761, 761, 761, 761, 761, 761, 82, 82, 82, 82, 82, 82, 82, 82, 762, 
-    762, 762, 762, 762, 762, 762, 762, 762, 763, 763, 763, 763, 763, 763, 
-    763, 763, 763, 763, 763, 82, 763, 763, 82, 82, 82, 82, 82, 764, 764, 764, 
-    764, 764, 765, 765, 765, 765, 765, 765, 765, 765, 765, 765, 765, 765, 
-    765, 765, 766, 766, 766, 766, 766, 766, 82, 82, 82, 767, 768, 768, 768, 
-    768, 768, 768, 768, 768, 768, 768, 82, 82, 82, 82, 82, 769, 770, 770, 
-    770, 770, 770, 770, 770, 770, 771, 771, 771, 771, 771, 771, 771, 771, 82, 
-    82, 82, 82, 772, 772, 771, 771, 772, 772, 772, 772, 772, 772, 772, 772, 
-    82, 82, 772, 772, 772, 772, 772, 772, 773, 774, 774, 774, 82, 774, 774, 
-    82, 82, 82, 82, 82, 774, 775, 774, 776, 773, 773, 773, 773, 82, 773, 773, 
-    773, 82, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 
-    773, 773, 773, 773, 773, 773, 82, 82, 82, 82, 776, 777, 775, 82, 82, 82, 
-    82, 778, 779, 779, 779, 779, 779, 779, 779, 779, 780, 780, 780, 780, 780, 
-    780, 780, 780, 781, 82, 82, 82, 82, 82, 82, 82, 782, 782, 782, 782, 782, 
-    782, 782, 782, 782, 782, 782, 782, 782, 783, 783, 784, 785, 785, 785, 
-    785, 785, 785, 785, 785, 785, 785, 785, 785, 785, 786, 786, 786, 787, 
-    787, 787, 787, 787, 787, 787, 787, 788, 787, 787, 787, 787, 787, 787, 
-    787, 787, 787, 787, 787, 787, 789, 790, 82, 82, 82, 82, 791, 791, 791, 
-    791, 791, 792, 792, 792, 792, 792, 792, 793, 82, 794, 794, 794, 794, 794, 
-    794, 794, 794, 794, 794, 794, 794, 794, 794, 82, 82, 82, 795, 795, 795, 
-    795, 795, 795, 795, 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, 
-    796, 796, 796, 796, 82, 82, 797, 797, 797, 797, 797, 797, 797, 797, 798, 
-    798, 798, 798, 798, 798, 798, 798, 798, 798, 798, 82, 82, 82, 82, 82, 
-    799, 799, 799, 799, 799, 799, 799, 799, 800, 800, 800, 800, 800, 800, 
-    800, 800, 800, 800, 82, 82, 82, 82, 82, 82, 82, 801, 801, 801, 801, 82, 
-    82, 82, 82, 802, 802, 802, 802, 802, 802, 802, 803, 803, 803, 803, 803, 
-    803, 803, 803, 803, 82, 82, 82, 82, 82, 82, 82, 804, 804, 804, 804, 804, 
-    804, 804, 804, 804, 804, 804, 82, 82, 82, 82, 82, 805, 805, 805, 805, 
-    805, 805, 805, 805, 805, 805, 805, 82, 82, 82, 82, 82, 82, 82, 806, 806, 
-    806, 806, 806, 806, 807, 807, 807, 807, 807, 807, 807, 807, 807, 807, 
-    807, 807, 807, 807, 807, 82, 808, 809, 808, 810, 810, 810, 810, 810, 810, 
-    810, 810, 810, 810, 810, 810, 810, 809, 809, 809, 809, 809, 809, 809, 
-    809, 809, 809, 809, 809, 809, 809, 811, 812, 812, 813, 813, 813, 813, 
-    813, 82, 82, 82, 82, 814, 814, 814, 814, 814, 814, 814, 814, 814, 814, 
-    814, 814, 814, 814, 814, 814, 814, 814, 814, 814, 815, 815, 815, 815, 
-    815, 815, 815, 815, 815, 815, 82, 82, 82, 82, 82, 82, 82, 811, 816, 816, 
-    817, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 818, 
-    817, 817, 817, 816, 816, 816, 816, 817, 817, 819, 820, 821, 821, 822, 
-    823, 823, 823, 823, 82, 82, 82, 82, 82, 82, 824, 824, 824, 824, 824, 824, 
-    824, 824, 824, 82, 82, 82, 82, 82, 82, 82, 825, 825, 825, 825, 825, 825, 
-    825, 825, 825, 825, 82, 82, 82, 82, 82, 82, 826, 826, 826, 827, 827, 827, 
-    827, 827, 827, 827, 827, 827, 827, 827, 827, 827, 827, 827, 827, 827, 
-    827, 827, 827, 828, 828, 828, 828, 828, 829, 828, 828, 828, 828, 828, 
-    828, 830, 830, 82, 831, 831, 831, 831, 831, 831, 831, 831, 831, 831, 832, 
-    832, 832, 832, 82, 82, 82, 82, 833, 833, 833, 833, 833, 833, 833, 833, 
-    833, 833, 833, 834, 835, 836, 833, 82, 837, 837, 838, 839, 839, 839, 839, 
-    839, 839, 839, 839, 839, 839, 839, 839, 839, 839, 839, 839, 838, 838, 
-    838, 837, 837, 837, 837, 837, 837, 837, 837, 837, 838, 840, 839, 839, 
-    839, 839, 841, 841, 842, 841, 842, 843, 837, 837, 842, 82, 82, 844, 844, 
-    844, 844, 844, 844, 844, 844, 844, 844, 839, 845, 839, 841, 841, 841, 82, 
-    846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 846, 
-    846, 846, 846, 846, 846, 846, 82, 82, 82, 847, 847, 847, 847, 847, 847, 
-    847, 847, 847, 847, 82, 847, 847, 847, 847, 847, 847, 847, 847, 847, 848, 
-    848, 848, 849, 849, 849, 848, 848, 849, 850, 851, 849, 852, 852, 853, 
-    852, 852, 853, 849, 82, 854, 854, 854, 854, 854, 854, 854, 82, 854, 82, 
-    854, 854, 854, 854, 82, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 
-    854, 854, 854, 854, 854, 82, 854, 854, 855, 82, 82, 82, 82, 82, 82, 856, 
-    856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 
-    857, 858, 858, 858, 857, 857, 857, 857, 857, 857, 859, 860, 82, 82, 82, 
-    82, 82, 861, 861, 861, 861, 861, 861, 861, 861, 861, 861, 82, 82, 82, 82, 
-    82, 82, 862, 862, 863, 863, 82, 864, 864, 864, 864, 864, 864, 864, 864, 
-    82, 82, 864, 864, 82, 82, 864, 864, 864, 864, 864, 864, 864, 864, 864, 
-    864, 864, 864, 864, 864, 82, 864, 864, 864, 864, 864, 864, 864, 82, 864, 
-    864, 82, 864, 864, 864, 864, 864, 82, 82, 865, 864, 863, 863, 862, 863, 
-    863, 863, 863, 82, 82, 863, 863, 82, 82, 863, 863, 866, 82, 82, 864, 82, 
-    82, 82, 82, 82, 82, 863, 82, 82, 82, 82, 82, 864, 864, 864, 864, 864, 
-    863, 863, 82, 82, 867, 867, 867, 867, 867, 867, 867, 82, 82, 82, 868, 
-    868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 868, 869, 869, 
-    869, 870, 870, 870, 870, 870, 870, 870, 870, 869, 869, 871, 870, 870, 
-    869, 872, 868, 868, 868, 868, 873, 873, 873, 873, 874, 875, 875, 875, 
-    875, 875, 875, 875, 875, 875, 875, 82, 873, 82, 874, 82, 82, 876, 876, 
-    876, 876, 876, 876, 876, 876, 877, 877, 877, 878, 878, 878, 878, 878, 
-    878, 877, 878, 877, 877, 877, 877, 878, 878, 877, 879, 880, 876, 876, 
-    881, 876, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 82, 82, 82, 
-    82, 82, 82, 883, 883, 883, 883, 883, 883, 883, 883, 883, 883, 883, 883, 
-    883, 883, 883, 884, 884, 884, 885, 885, 885, 885, 82, 82, 884, 884, 884, 
-    884, 885, 885, 884, 886, 887, 888, 889, 889, 890, 890, 891, 891, 891, 
-    889, 889, 889, 889, 889, 889, 889, 889, 889, 889, 889, 889, 889, 889, 
-    889, 883, 883, 883, 883, 885, 885, 82, 82, 892, 892, 892, 892, 892, 892, 
-    892, 892, 893, 893, 893, 894, 894, 894, 894, 894, 894, 894, 894, 893, 
-    893, 894, 893, 895, 894, 896, 896, 897, 892, 82, 82, 82, 898, 898, 898, 
-    898, 898, 898, 898, 898, 898, 898, 82, 82, 82, 82, 82, 82, 899, 899, 899, 
-    899, 899, 899, 899, 899, 899, 899, 899, 899, 899, 82, 82, 82, 900, 900, 
-    900, 900, 900, 900, 900, 900, 900, 900, 900, 901, 902, 901, 902, 902, 
-    901, 901, 901, 901, 901, 901, 903, 904, 905, 905, 905, 905, 905, 905, 
-    905, 905, 905, 905, 82, 82, 82, 82, 82, 82, 906, 906, 906, 906, 906, 906, 
-    906, 906, 906, 906, 82, 82, 82, 907, 907, 907, 908, 908, 907, 907, 907, 
-    907, 908, 907, 907, 907, 907, 909, 82, 82, 82, 82, 910, 910, 910, 910, 
-    910, 910, 910, 910, 910, 910, 911, 911, 912, 912, 912, 913, 914, 914, 
-    914, 914, 914, 914, 914, 914, 915, 915, 915, 915, 915, 915, 915, 915, 
-    916, 916, 916, 916, 916, 916, 916, 916, 916, 916, 917, 917, 917, 917, 
-    917, 917, 917, 917, 917, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 
-    918, 919, 919, 919, 919, 919, 919, 919, 919, 919, 82, 82, 82, 82, 82, 82, 
-    82, 920, 920, 920, 920, 920, 920, 920, 920, 920, 82, 920, 920, 920, 920, 
-    920, 920, 920, 920, 920, 920, 920, 920, 920, 921, 922, 922, 922, 922, 
-    922, 922, 922, 82, 922, 922, 922, 922, 922, 922, 921, 923, 920, 924, 924, 
-    924, 924, 924, 82, 82, 925, 925, 925, 925, 925, 925, 925, 925, 925, 925, 
-    926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 
-    926, 926, 926, 926, 926, 82, 82, 82, 927, 928, 929, 929, 929, 929, 929, 
-    929, 929, 929, 929, 929, 929, 929, 929, 929, 82, 82, 930, 930, 930, 930, 
-    930, 930, 930, 930, 930, 930, 930, 930, 930, 930, 82, 931, 930, 930, 930, 
-    930, 930, 930, 930, 931, 930, 930, 931, 930, 930, 82, 932, 932, 932, 932, 
-    932, 932, 932, 932, 932, 932, 82, 82, 82, 82, 82, 82, 933, 933, 933, 933, 
-    933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 82, 934, 934, 934, 
-    934, 934, 82, 82, 82, 932, 932, 932, 932, 82, 82, 82, 82, 935, 935, 935, 
-    935, 935, 935, 935, 935, 936, 936, 936, 937, 937, 937, 935, 935, 935, 
-    935, 937, 935, 935, 935, 936, 937, 936, 937, 935, 935, 935, 935, 935, 
-    935, 935, 936, 937, 937, 935, 935, 935, 935, 935, 935, 935, 935, 935, 
-    935, 935, 82, 938, 938, 938, 938, 938, 938, 938, 938, 938, 938, 938, 938, 
-    938, 938, 939, 940, 938, 938, 938, 938, 938, 938, 938, 82, 609, 82, 82, 
-    82, 82, 82, 82, 82, 941, 941, 941, 941, 941, 941, 941, 941, 941, 941, 
-    941, 941, 941, 941, 941, 82, 942, 942, 942, 942, 942, 942, 942, 942, 942, 
-    942, 82, 82, 82, 82, 943, 943, 944, 944, 944, 944, 944, 944, 944, 944, 
-    944, 944, 944, 944, 944, 944, 82, 82, 945, 945, 945, 945, 945, 946, 82, 
-    82, 947, 947, 947, 947, 947, 947, 947, 947, 948, 948, 948, 948, 948, 948, 
-    948, 949, 949, 949, 950, 950, 951, 951, 951, 951, 952, 952, 952, 952, 
-    949, 951, 82, 82, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 82, 
-    954, 954, 954, 954, 954, 954, 954, 82, 947, 947, 947, 947, 947, 82, 82, 
-    82, 82, 82, 947, 947, 947, 955, 955, 955, 955, 955, 955, 955, 955, 955, 
-    955, 955, 955, 955, 82, 82, 82, 955, 956, 956, 956, 956, 956, 956, 956, 
-    956, 956, 956, 956, 956, 956, 956, 956, 956, 956, 956, 956, 956, 956, 
-    956, 82, 82, 82, 82, 82, 82, 82, 82, 957, 957, 957, 957, 958, 958, 958, 
-    958, 958, 958, 958, 958, 958, 958, 958, 958, 958, 959, 82, 82, 82, 82, 
-    82, 82, 82, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 
-    960, 82, 82, 82, 960, 960, 960, 82, 82, 82, 82, 82, 580, 575, 82, 82, 82, 
-    82, 82, 82, 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, 82, 
-    82, 82, 82, 82, 961, 961, 961, 961, 961, 82, 82, 82, 961, 82, 82, 82, 82, 
-    82, 82, 82, 961, 961, 82, 82, 962, 963, 964, 965, 499, 499, 499, 499, 82, 
-    82, 82, 82, 313, 313, 313, 313, 313, 313, 82, 82, 313, 313, 313, 313, 
-    313, 313, 313, 82, 82, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, 
-    313, 313, 966, 966, 447, 447, 447, 313, 313, 313, 967, 966, 966, 966, 
-    966, 966, 499, 499, 499, 499, 499, 499, 499, 499, 157, 157, 157, 157, 
-    157, 157, 157, 157, 313, 313, 97, 97, 97, 97, 97, 157, 157, 313, 313, 
-    313, 313, 313, 313, 97, 97, 97, 97, 313, 313, 313, 82, 82, 82, 82, 82, 
-    82, 82, 728, 728, 968, 968, 968, 728, 82, 82, 620, 620, 82, 82, 82, 82, 
-    82, 82, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 50, 50, 50, 50, 
-    50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 506, 506, 506, 
-    506, 506, 506, 506, 506, 506, 506, 50, 50, 50, 50, 50, 50, 50, 82, 50, 
-    50, 50, 50, 50, 50, 506, 82, 506, 506, 82, 82, 506, 82, 82, 506, 506, 82, 
-    82, 506, 506, 506, 506, 82, 506, 506, 50, 50, 82, 50, 82, 50, 50, 50, 50, 
-    50, 50, 50, 82, 50, 50, 50, 50, 50, 50, 50, 506, 506, 82, 506, 506, 506, 
-    506, 82, 82, 506, 506, 506, 506, 506, 506, 506, 506, 82, 506, 506, 506, 
-    506, 506, 506, 506, 82, 50, 50, 506, 506, 82, 506, 506, 506, 506, 82, 
-    506, 506, 506, 506, 506, 82, 506, 82, 82, 82, 506, 506, 506, 506, 506, 
-    506, 506, 82, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 82, 82, 
-    506, 969, 50, 50, 50, 50, 50, 50, 50, 50, 50, 513, 50, 50, 50, 50, 50, 
-    50, 506, 506, 506, 506, 506, 506, 506, 506, 506, 969, 50, 50, 50, 50, 50, 
-    50, 50, 50, 50, 513, 50, 50, 506, 506, 506, 506, 506, 969, 50, 50, 50, 
-    50, 50, 50, 50, 50, 50, 513, 50, 50, 50, 50, 50, 50, 506, 506, 506, 506, 
-    506, 506, 506, 506, 506, 969, 50, 513, 50, 50, 50, 50, 50, 50, 50, 50, 
-    506, 50, 82, 82, 970, 970, 970, 970, 970, 970, 970, 970, 970, 970, 971, 
-    971, 971, 971, 971, 971, 971, 971, 972, 972, 972, 972, 972, 972, 972, 
-    972, 972, 972, 972, 972, 972, 972, 972, 971, 971, 971, 971, 972, 972, 
-    972, 972, 972, 972, 972, 972, 972, 972, 971, 971, 971, 971, 971, 971, 
-    971, 971, 972, 971, 971, 971, 971, 971, 971, 972, 971, 971, 973, 973, 
-    973, 973, 974, 82, 82, 82, 82, 82, 82, 82, 972, 972, 972, 972, 972, 82, 
-    972, 972, 972, 972, 972, 972, 972, 975, 975, 975, 975, 975, 975, 975, 82, 
-    975, 975, 975, 975, 975, 975, 975, 975, 975, 82, 82, 975, 975, 975, 975, 
-    975, 975, 975, 82, 975, 975, 82, 975, 975, 975, 975, 975, 82, 82, 82, 82, 
-    82, 976, 976, 976, 976, 976, 976, 976, 976, 976, 976, 976, 976, 976, 82, 
-    82, 977, 977, 977, 977, 977, 977, 977, 977, 977, 978, 978, 978, 978, 978, 
-    978, 978, 82, 979, 979, 979, 979, 979, 979, 979, 979, 979, 979, 980, 980, 
-    980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 
-    980, 980, 981, 981, 981, 981, 981, 981, 982, 82, 82, 82, 82, 82, 983, 
-    983, 983, 983, 983, 983, 983, 983, 983, 983, 82, 82, 82, 82, 984, 984, 
-    147, 147, 147, 147, 82, 147, 147, 147, 82, 147, 147, 82, 147, 82, 82, 
-    147, 82, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 82, 147, 147, 
-    147, 147, 82, 147, 82, 147, 82, 82, 82, 82, 82, 82, 147, 82, 82, 82, 82, 
-    147, 82, 147, 82, 147, 82, 147, 147, 147, 82, 147, 82, 147, 82, 147, 82, 
-    147, 82, 147, 147, 147, 147, 82, 147, 82, 147, 147, 82, 147, 147, 147, 
-    147, 147, 147, 147, 147, 147, 82, 82, 82, 82, 82, 147, 147, 147, 82, 147, 
-    147, 147, 133, 133, 82, 82, 82, 82, 82, 82, 527, 527, 527, 527, 523, 527, 
-    527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 
-    985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 527, 527, 
-    527, 527, 527, 527, 527, 985, 985, 527, 527, 527, 527, 527, 527, 527, 
-    527, 527, 527, 527, 527, 527, 527, 523, 527, 527, 527, 527, 527, 527, 
-    985, 985, 48, 48, 48, 516, 516, 985, 985, 985, 528, 528, 528, 528, 528, 
-    528, 313, 985, 528, 528, 41, 41, 985, 985, 985, 985, 528, 528, 528, 528, 
-    528, 528, 986, 528, 528, 986, 986, 986, 986, 986, 986, 986, 986, 986, 
-    986, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 985, 985, 985, 
-    985, 985, 985, 985, 985, 985, 987, 987, 987, 987, 987, 987, 987, 987, 
-    987, 987, 988, 586, 586, 985, 985, 985, 985, 985, 586, 586, 586, 586, 
-    985, 985, 985, 985, 586, 985, 985, 985, 985, 985, 985, 985, 586, 586, 
-    985, 985, 985, 985, 985, 985, 523, 527, 527, 527, 527, 527, 527, 527, 
-    527, 527, 527, 527, 527, 523, 523, 523, 523, 523, 523, 523, 523, 523, 
-    527, 523, 523, 523, 523, 523, 523, 527, 523, 523, 523, 523, 523, 523, 
-    523, 534, 523, 523, 523, 523, 523, 523, 527, 527, 527, 527, 527, 527, 
-    527, 527, 41, 41, 527, 527, 523, 523, 523, 523, 523, 526, 526, 523, 523, 
-    523, 523, 523, 526, 523, 523, 523, 523, 523, 523, 534, 534, 523, 523, 
-    523, 523, 523, 534, 532, 527, 527, 527, 523, 523, 527, 527, 527, 523, 
-    527, 527, 527, 523, 523, 523, 989, 989, 989, 989, 989, 523, 523, 523, 
-    523, 523, 523, 523, 527, 523, 527, 534, 534, 523, 523, 534, 534, 534, 
-    534, 534, 534, 534, 534, 534, 534, 534, 523, 523, 523, 523, 523, 523, 
-    523, 523, 523, 523, 523, 523, 523, 534, 534, 534, 534, 523, 523, 523, 
-    523, 534, 523, 534, 523, 523, 523, 534, 523, 523, 523, 523, 534, 534, 
-    534, 523, 534, 534, 534, 526, 523, 526, 523, 526, 523, 523, 523, 523, 
-    523, 534, 523, 523, 523, 523, 526, 523, 526, 526, 523, 523, 523, 523, 
-    523, 523, 523, 523, 523, 523, 527, 527, 523, 526, 526, 526, 526, 526, 
-    526, 526, 523, 523, 523, 523, 523, 523, 523, 523, 526, 526, 526, 526, 
-    526, 526, 523, 523, 523, 523, 523, 526, 526, 526, 526, 526, 526, 526, 
-    526, 526, 526, 526, 526, 41, 41, 41, 41, 527, 523, 523, 523, 523, 527, 
-    527, 527, 527, 527, 527, 532, 527, 527, 527, 527, 534, 527, 527, 527, 
-    527, 527, 532, 527, 527, 527, 527, 534, 534, 527, 527, 527, 527, 527, 41, 
-    41, 41, 41, 41, 41, 41, 41, 527, 527, 527, 527, 41, 41, 527, 523, 523, 
-    523, 523, 523, 523, 523, 523, 523, 523, 534, 534, 534, 523, 523, 523, 
-    534, 534, 534, 534, 534, 41, 41, 41, 41, 41, 41, 536, 536, 536, 990, 990, 
-    990, 41, 41, 41, 41, 523, 523, 523, 534, 523, 523, 523, 523, 523, 523, 
-    523, 523, 534, 534, 534, 523, 534, 523, 523, 523, 523, 523, 527, 527, 
-    523, 523, 523, 985, 985, 985, 985, 985, 527, 527, 527, 523, 523, 985, 
-    985, 985, 527, 527, 527, 527, 523, 523, 523, 985, 41, 41, 41, 41, 985, 
-    985, 985, 985, 41, 41, 41, 41, 41, 985, 985, 985, 41, 41, 985, 985, 985, 
-    985, 985, 985, 41, 41, 41, 41, 41, 41, 985, 985, 534, 534, 534, 534, 534, 
-    534, 534, 985, 523, 523, 523, 523, 523, 523, 534, 523, 534, 985, 985, 
-    534, 534, 534, 534, 534, 534, 534, 523, 523, 534, 534, 534, 985, 523, 
-    523, 523, 523, 985, 985, 985, 985, 523, 523, 523, 523, 523, 523, 523, 
-    985, 523, 523, 985, 985, 985, 985, 985, 985, 523, 985, 985, 985, 985, 
-    985, 985, 985, 985, 985, 985, 985, 985, 985, 82, 82, 593, 593, 593, 593, 
-    593, 593, 593, 594, 593, 593, 593, 593, 593, 594, 594, 594, 594, 594, 
-    594, 594, 594, 594, 82, 82, 82, 499, 82, 82, 82, 82, 82, 82, 499, 499, 
-    499, 499, 499, 499, 499, 499, 671, 671, 671, 671, 671, 671, 82, 82, 
+    22, 22, 22, 22, 22, 22, 22, 22, 19, 23, 24, 24, 24, 10, 15, 25, 25, 25, 
+    25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 16, 26, 17, 
+    27, 28, 27, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 
+    29, 29, 29, 16, 30, 31, 24, 1, 1, 1, 1, 1, 1, 32, 1, 1, 33, 34, 35, 13, 
+    36, 13, 37, 38, 39, 40, 41, 42, 24, 43, 44, 27, 45, 46, 47, 47, 48, 49, 
+    38, 38, 39, 47, 41, 50, 51, 51, 51, 34, 52, 52, 52, 52, 52, 52, 53, 52, 
+    52, 52, 52, 52, 52, 52, 52, 52, 53, 52, 52, 52, 52, 52, 52, 54, 53, 52, 
+    52, 52, 52, 52, 53, 55, 55, 55, 56, 56, 56, 56, 55, 56, 55, 55, 55, 56, 
+    55, 55, 56, 56, 55, 56, 55, 55, 56, 56, 56, 54, 55, 55, 55, 56, 55, 56, 
+    55, 56, 52, 55, 52, 56, 52, 56, 52, 56, 52, 56, 52, 56, 52, 56, 52, 56, 
+    52, 55, 52, 55, 52, 56, 52, 56, 52, 56, 52, 55, 52, 56, 52, 56, 52, 56, 
+    52, 56, 52, 56, 53, 55, 52, 55, 53, 55, 52, 56, 52, 56, 55, 52, 56, 52, 
+    56, 52, 56, 53, 55, 53, 55, 52, 55, 52, 56, 52, 55, 55, 53, 55, 52, 55, 
+    52, 56, 52, 56, 53, 55, 52, 56, 52, 56, 52, 52, 56, 52, 56, 52, 56, 56, 
+    56, 52, 52, 56, 52, 56, 52, 52, 56, 52, 52, 52, 56, 56, 52, 52, 52, 52, 
+    56, 52, 52, 56, 52, 52, 52, 56, 56, 56, 52, 52, 56, 52, 52, 56, 52, 56, 
+    52, 56, 52, 52, 56, 52, 56, 56, 52, 56, 52, 52, 56, 52, 52, 52, 56, 52, 
+    56, 52, 52, 56, 56, 57, 52, 56, 56, 56, 57, 57, 57, 57, 52, 58, 56, 52, 
+    58, 56, 52, 58, 56, 52, 55, 52, 55, 52, 55, 52, 55, 52, 55, 52, 55, 52, 
+    55, 52, 55, 56, 52, 56, 56, 52, 58, 56, 52, 56, 52, 52, 52, 56, 52, 56, 
+    56, 56, 56, 56, 56, 56, 52, 52, 56, 52, 52, 56, 56, 52, 56, 52, 52, 52, 
+    52, 56, 56, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 
+    56, 56, 56, 56, 57, 56, 56, 56, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, 
+    60, 61, 61, 61, 61, 61, 61, 61, 62, 62, 63, 62, 60, 64, 65, 64, 64, 64, 
+    65, 64, 60, 60, 66, 61, 62, 62, 62, 62, 62, 62, 39, 39, 39, 39, 62, 39, 
+    62, 48, 59, 59, 59, 59, 59, 62, 62, 62, 62, 62, 67, 67, 60, 62, 61, 62, 
+    62, 62, 62, 62, 62, 62, 62, 62, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 
+    68, 68, 68, 69, 70, 70, 70, 70, 69, 71, 70, 70, 70, 70, 70, 72, 72, 70, 
+    70, 70, 70, 72, 72, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 73, 73, 
+    73, 73, 73, 70, 70, 70, 70, 68, 68, 68, 68, 68, 68, 68, 68, 74, 68, 70, 
+    70, 70, 68, 68, 68, 70, 70, 75, 68, 68, 68, 70, 70, 70, 70, 68, 69, 70, 
+    70, 68, 76, 77, 77, 76, 77, 77, 76, 68, 68, 68, 68, 68, 78, 79, 78, 79, 
+    60, 80, 78, 79, 81, 81, 82, 79, 79, 79, 83, 78, 81, 81, 81, 81, 80, 62, 
+    78, 84, 78, 78, 78, 81, 78, 81, 78, 78, 79, 85, 85, 85, 85, 85, 85, 85, 
+    85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 81, 85, 85, 85, 85, 85, 85, 85, 
+    78, 78, 79, 79, 79, 79, 79, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 
+    86, 86, 86, 86, 86, 86, 79, 86, 86, 86, 86, 86, 86, 86, 79, 79, 79, 79, 
+    79, 78, 79, 79, 78, 78, 78, 79, 79, 79, 78, 79, 78, 79, 78, 79, 78, 79, 
+    78, 79, 87, 88, 87, 88, 87, 88, 87, 88, 87, 88, 87, 88, 87, 88, 79, 79, 
+    79, 79, 78, 79, 89, 78, 79, 78, 78, 79, 79, 78, 78, 78, 90, 91, 90, 90, 
+    90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 
+    91, 91, 92, 92, 92, 92, 92, 92, 92, 92, 93, 92, 93, 93, 93, 93, 93, 93, 
+    93, 93, 93, 93, 93, 93, 93, 93, 90, 93, 90, 93, 90, 93, 90, 93, 90, 93, 
+    94, 95, 95, 96, 96, 95, 97, 97, 90, 93, 90, 93, 90, 93, 90, 90, 93, 90, 
+    93, 90, 93, 90, 93, 90, 93, 90, 93, 90, 93, 93, 81, 98, 98, 98, 98, 98, 
+    98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 81, 
+    81, 99, 100, 100, 100, 100, 100, 100, 81, 101, 101, 101, 101, 101, 101, 
+    101, 101, 101, 101, 101, 101, 101, 101, 101, 81, 102, 103, 81, 81, 104, 
+    104, 105, 81, 106, 107, 107, 107, 107, 106, 107, 107, 107, 108, 106, 107, 
+    107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 107, 107, 106, 
+    107, 107, 108, 109, 107, 110, 111, 112, 113, 114, 115, 116, 117, 118, 
+    119, 119, 120, 121, 122, 123, 124, 125, 126, 127, 125, 107, 106, 128, 
+    118, 81, 81, 81, 81, 81, 81, 81, 81, 129, 129, 129, 129, 129, 129, 129, 
+    129, 129, 129, 129, 81, 81, 81, 81, 81, 129, 129, 129, 125, 125, 81, 81, 
+    81, 130, 130, 130, 130, 130, 131, 132, 132, 133, 134, 134, 135, 136, 137, 
+    138, 138, 139, 139, 139, 139, 139, 139, 139, 139, 140, 141, 142, 143, 
+    144, 81, 145, 143, 146, 146, 146, 146, 146, 146, 146, 146, 147, 146, 146, 
+    146, 146, 146, 146, 146, 146, 146, 146, 148, 149, 150, 151, 152, 153, 
+    154, 155, 96, 96, 156, 157, 139, 139, 139, 139, 139, 157, 139, 139, 157, 
+    158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 134, 159, 159, 160, 
+    146, 146, 161, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 
+    145, 146, 139, 139, 139, 139, 139, 139, 139, 131, 138, 139, 139, 139, 
+    139, 157, 139, 162, 162, 139, 139, 138, 157, 139, 139, 157, 146, 146, 
+    163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 146, 146, 146, 164, 
+    164, 146, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 
+    165, 165, 81, 166, 167, 168, 167, 167, 167, 167, 167, 167, 167, 167, 167, 
+    167, 167, 167, 167, 167, 169, 170, 169, 169, 170, 169, 169, 170, 170, 
+    170, 169, 170, 170, 169, 170, 169, 169, 169, 170, 169, 170, 169, 170, 
+    169, 170, 169, 169, 81, 81, 167, 167, 167, 171, 171, 171, 171, 171, 171, 
+    171, 171, 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, 172, 172, 
+    172, 172, 172, 172, 172, 171, 81, 81, 81, 81, 81, 81, 173, 173, 173, 173, 
+    173, 173, 173, 173, 173, 173, 174, 174, 174, 174, 174, 174, 174, 174, 
+    174, 174, 174, 174, 174, 174, 174, 174, 174, 175, 175, 175, 175, 175, 
+    175, 175, 176, 175, 177, 177, 178, 179, 180, 181, 177, 81, 81, 81, 81, 
+    81, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 
+    183, 183, 183, 183, 184, 183, 183, 183, 183, 183, 183, 183, 183, 183, 
+    184, 183, 183, 183, 184, 183, 183, 183, 183, 183, 81, 81, 185, 185, 185, 
+    185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 81, 186, 186, 
+    186, 186, 186, 186, 186, 186, 186, 187, 187, 187, 81, 81, 188, 81, 167, 
+    167, 167, 81, 81, 81, 81, 81, 146, 146, 146, 146, 146, 81, 146, 146, 146, 
+    146, 146, 146, 146, 146, 81, 81, 81, 81, 81, 81, 139, 139, 139, 139, 139, 
+    139, 131, 157, 139, 139, 157, 139, 139, 157, 139, 139, 139, 157, 157, 
+    157, 189, 190, 191, 139, 139, 139, 157, 139, 139, 157, 157, 139, 139, 
+    139, 139, 139, 192, 192, 192, 193, 194, 194, 194, 194, 194, 194, 194, 
+    194, 194, 194, 194, 194, 194, 194, 192, 193, 195, 194, 193, 193, 193, 
+    192, 192, 192, 192, 192, 192, 192, 192, 193, 193, 193, 193, 196, 193, 
+    193, 194, 96, 156, 197, 197, 192, 192, 192, 194, 194, 192, 192, 198, 198, 
+    199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 200, 201, 194, 194, 
+    194, 194, 194, 194, 202, 203, 204, 204, 81, 202, 202, 202, 202, 202, 202, 
+    202, 202, 81, 81, 202, 202, 81, 81, 202, 202, 202, 202, 202, 202, 202, 
+    202, 202, 202, 202, 202, 202, 202, 81, 202, 202, 202, 202, 202, 202, 202, 
+    81, 202, 81, 81, 81, 202, 202, 202, 202, 81, 81, 205, 202, 204, 204, 204, 
+    203, 203, 203, 203, 81, 81, 204, 204, 81, 81, 204, 204, 206, 202, 81, 81, 
+    81, 81, 81, 81, 81, 81, 204, 81, 81, 81, 81, 202, 202, 81, 202, 202, 202, 
+    203, 203, 81, 81, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 202, 
+    202, 208, 208, 209, 209, 209, 209, 209, 210, 211, 212, 202, 213, 81, 81, 
+    81, 214, 214, 215, 81, 216, 216, 216, 216, 216, 216, 81, 81, 81, 81, 216, 
+    216, 81, 81, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 
+    216, 216, 81, 216, 216, 216, 216, 216, 216, 216, 81, 216, 216, 81, 216, 
+    216, 81, 216, 216, 81, 81, 217, 81, 215, 215, 215, 214, 214, 81, 81, 81, 
+    81, 214, 214, 81, 81, 214, 214, 218, 81, 81, 81, 214, 81, 81, 81, 81, 81, 
+    81, 81, 216, 216, 216, 216, 81, 216, 81, 81, 81, 81, 81, 81, 81, 219, 
+    219, 219, 219, 219, 219, 219, 219, 219, 219, 214, 214, 216, 216, 216, 
+    214, 81, 81, 81, 220, 220, 221, 81, 222, 222, 222, 222, 222, 222, 222, 
+    222, 222, 81, 222, 222, 222, 81, 222, 222, 222, 222, 222, 222, 222, 222, 
+    222, 222, 222, 222, 222, 222, 81, 222, 222, 222, 222, 222, 222, 222, 81, 
+    222, 222, 81, 222, 222, 222, 222, 222, 81, 81, 223, 222, 221, 221, 221, 
+    220, 220, 220, 220, 220, 81, 220, 220, 221, 81, 221, 221, 224, 81, 81, 
+    222, 81, 81, 81, 81, 81, 81, 81, 222, 222, 220, 220, 81, 81, 225, 225, 
+    225, 225, 225, 225, 225, 225, 225, 225, 226, 227, 81, 81, 81, 81, 81, 81, 
+    81, 222, 220, 220, 220, 220, 220, 220, 81, 228, 229, 229, 81, 230, 230, 
+    230, 230, 230, 230, 230, 230, 81, 81, 230, 230, 81, 81, 230, 230, 230, 
+    230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 81, 230, 230, 230, 
+    230, 230, 230, 230, 81, 230, 230, 81, 230, 230, 230, 230, 230, 81, 81, 
+    231, 230, 229, 228, 229, 228, 228, 228, 228, 81, 81, 229, 229, 81, 81, 
+    229, 229, 232, 81, 81, 81, 81, 81, 81, 81, 81, 228, 229, 81, 81, 81, 81, 
+    230, 230, 81, 230, 230, 230, 228, 228, 81, 81, 233, 233, 233, 233, 233, 
+    233, 233, 233, 233, 233, 234, 230, 235, 235, 235, 235, 235, 235, 81, 81, 
+    236, 237, 81, 237, 237, 237, 237, 237, 237, 81, 81, 81, 237, 237, 237, 
+    81, 237, 237, 237, 237, 81, 81, 81, 237, 237, 81, 237, 81, 237, 237, 81, 
+    81, 81, 237, 237, 81, 81, 81, 237, 237, 237, 237, 237, 237, 237, 237, 
+    237, 237, 81, 81, 81, 81, 238, 238, 236, 238, 238, 81, 81, 81, 238, 238, 
+    238, 81, 238, 238, 238, 239, 81, 81, 237, 81, 81, 81, 81, 81, 81, 238, 
+    81, 81, 81, 81, 81, 81, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 
+    241, 241, 241, 242, 242, 242, 242, 242, 242, 243, 242, 81, 81, 81, 81, 
+    81, 244, 245, 245, 245, 81, 246, 246, 246, 246, 246, 246, 246, 246, 81, 
+    246, 246, 246, 81, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 
+    246, 246, 246, 246, 246, 81, 81, 81, 246, 244, 244, 244, 245, 245, 245, 
+    245, 81, 244, 244, 244, 81, 244, 244, 244, 247, 81, 81, 81, 81, 81, 81, 
+    81, 248, 249, 81, 246, 246, 246, 81, 81, 81, 81, 81, 246, 246, 244, 244, 
+    81, 81, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 
+    251, 251, 251, 251, 252, 253, 254, 255, 255, 81, 253, 253, 253, 253, 253, 
+    253, 253, 253, 81, 253, 253, 253, 81, 253, 253, 253, 253, 253, 253, 253, 
+    253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 81, 253, 253, 253, 
+    253, 253, 81, 81, 256, 253, 255, 257, 255, 255, 255, 255, 255, 81, 257, 
+    255, 255, 81, 255, 255, 254, 258, 81, 81, 81, 81, 81, 81, 81, 255, 255, 
+    81, 81, 81, 81, 81, 81, 81, 253, 81, 253, 253, 254, 254, 81, 81, 259, 
+    259, 259, 259, 259, 259, 259, 259, 259, 259, 81, 253, 253, 81, 81, 81, 
+    81, 81, 260, 260, 261, 261, 81, 262, 262, 262, 262, 262, 262, 262, 262, 
+    81, 262, 262, 262, 81, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 
+    262, 262, 262, 262, 262, 262, 262, 263, 263, 262, 261, 261, 261, 260, 
+    260, 260, 260, 81, 261, 261, 261, 81, 261, 261, 261, 263, 262, 264, 81, 
+    81, 81, 81, 262, 262, 262, 261, 265, 265, 265, 265, 265, 265, 265, 262, 
+    262, 262, 260, 260, 81, 81, 266, 266, 266, 266, 266, 266, 266, 266, 266, 
+    266, 265, 265, 265, 265, 265, 265, 265, 265, 265, 267, 262, 262, 262, 
+    262, 262, 262, 81, 81, 268, 268, 81, 269, 269, 269, 269, 269, 269, 269, 
+    269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 81, 81, 81, 269, 
+    269, 269, 269, 269, 269, 269, 269, 81, 269, 269, 269, 269, 269, 269, 269, 
+    269, 269, 81, 269, 81, 81, 81, 81, 270, 81, 81, 81, 81, 268, 268, 268, 
+    271, 271, 271, 81, 271, 81, 268, 268, 268, 268, 268, 268, 268, 268, 81, 
+    81, 81, 81, 81, 81, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 81, 
+    81, 268, 268, 273, 81, 81, 81, 81, 274, 274, 274, 274, 274, 274, 274, 
+    274, 274, 274, 274, 274, 274, 274, 274, 274, 275, 274, 274, 275, 275, 
+    275, 275, 276, 276, 277, 81, 81, 81, 81, 278, 274, 274, 274, 274, 274, 
+    274, 279, 275, 280, 280, 280, 280, 275, 275, 275, 281, 282, 282, 282, 
+    282, 282, 282, 282, 282, 282, 282, 283, 283, 81, 81, 81, 81, 81, 284, 
+    284, 81, 284, 81, 81, 284, 284, 81, 284, 81, 81, 284, 81, 81, 81, 81, 81, 
+    81, 284, 284, 284, 284, 81, 284, 284, 284, 284, 284, 284, 284, 81, 284, 
+    284, 284, 81, 284, 81, 284, 81, 81, 284, 284, 81, 284, 284, 284, 284, 
+    285, 284, 284, 285, 285, 285, 285, 286, 286, 81, 285, 285, 284, 81, 81, 
+    284, 284, 284, 284, 284, 81, 287, 81, 288, 288, 288, 288, 285, 285, 81, 
+    81, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 81, 81, 284, 284, 
+    284, 284, 290, 291, 291, 291, 292, 293, 292, 292, 294, 292, 292, 295, 
+    294, 296, 296, 296, 296, 296, 294, 297, 296, 297, 297, 297, 298, 298, 
+    297, 297, 297, 297, 297, 297, 299, 299, 299, 299, 299, 299, 299, 299, 
+    299, 299, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 301, 298, 
+    297, 298, 297, 302, 303, 304, 303, 304, 305, 305, 290, 290, 290, 290, 
+    290, 290, 290, 290, 81, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 
+    290, 290, 81, 81, 81, 81, 306, 307, 308, 309, 308, 308, 308, 308, 308, 
+    307, 307, 307, 307, 308, 310, 307, 308, 311, 311, 312, 295, 311, 311, 
+    290, 290, 290, 290, 290, 308, 308, 308, 308, 308, 308, 308, 308, 308, 
+    308, 308, 81, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 
+    81, 301, 301, 297, 297, 297, 297, 297, 297, 298, 297, 297, 297, 297, 297, 
+    297, 81, 297, 297, 292, 292, 295, 292, 293, 313, 313, 313, 313, 294, 294, 
+    81, 81, 81, 81, 81, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 
+    314, 315, 315, 316, 316, 316, 316, 315, 316, 316, 316, 316, 316, 317, 
+    315, 318, 318, 315, 315, 316, 316, 314, 319, 319, 319, 319, 319, 319, 
+    319, 319, 319, 319, 320, 320, 321, 321, 321, 321, 314, 314, 314, 314, 
+    314, 314, 315, 315, 316, 316, 314, 314, 314, 314, 316, 316, 316, 314, 
+    315, 315, 315, 314, 314, 315, 315, 315, 315, 315, 315, 315, 314, 314, 
+    314, 316, 316, 316, 316, 314, 314, 314, 314, 314, 316, 315, 315, 316, 
+    316, 315, 315, 315, 315, 315, 315, 322, 314, 315, 319, 319, 315, 315, 
+    315, 316, 323, 323, 324, 324, 324, 324, 324, 324, 324, 324, 324, 324, 
+    324, 324, 324, 324, 81, 324, 81, 81, 81, 81, 81, 324, 81, 81, 325, 325, 
+    325, 325, 325, 325, 325, 325, 325, 325, 325, 326, 327, 325, 325, 325, 
+    328, 328, 328, 328, 328, 328, 328, 328, 329, 329, 329, 329, 329, 329, 
+    329, 329, 330, 330, 330, 330, 330, 330, 330, 330, 331, 331, 331, 331, 
+    331, 331, 331, 331, 331, 81, 331, 331, 331, 331, 81, 81, 331, 331, 331, 
+    331, 331, 331, 331, 81, 331, 331, 331, 81, 81, 332, 332, 332, 333, 334, 
+    333, 333, 333, 333, 333, 333, 333, 335, 335, 335, 335, 335, 335, 335, 
+    335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 335, 81, 81, 
+    81, 336, 336, 336, 336, 336, 336, 336, 336, 336, 336, 81, 81, 81, 81, 81, 
+    81, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 
+    81, 81, 338, 338, 338, 338, 338, 338, 81, 81, 339, 340, 340, 340, 340, 
+    340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 
+    340, 340, 341, 341, 340, 342, 343, 343, 343, 343, 343, 343, 343, 343, 
+    343, 343, 343, 343, 343, 343, 343, 343, 343, 343, 344, 345, 81, 81, 81, 
+    346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 198, 198, 198, 
+    347, 347, 347, 346, 346, 346, 346, 346, 346, 346, 346, 81, 81, 81, 81, 
+    81, 81, 81, 348, 348, 348, 348, 348, 348, 348, 348, 348, 348, 348, 348, 
+    348, 81, 348, 348, 348, 348, 349, 349, 350, 81, 81, 81, 351, 351, 351, 
+    351, 351, 351, 351, 351, 351, 351, 352, 352, 353, 198, 198, 81, 354, 354, 
+    354, 354, 354, 354, 354, 354, 354, 354, 355, 355, 81, 81, 81, 81, 356, 
+    356, 356, 356, 356, 356, 356, 356, 356, 356, 356, 356, 356, 81, 356, 356, 
+    356, 81, 357, 357, 81, 81, 81, 81, 358, 358, 358, 358, 358, 358, 358, 
+    358, 358, 358, 358, 358, 359, 359, 360, 359, 359, 359, 359, 359, 359, 
+    359, 360, 360, 360, 360, 360, 360, 360, 360, 359, 360, 360, 359, 359, 
+    359, 359, 359, 359, 359, 359, 359, 361, 359, 362, 362, 363, 364, 362, 
+    365, 362, 366, 358, 367, 81, 81, 368, 368, 368, 368, 368, 368, 368, 368, 
+    368, 368, 81, 81, 81, 81, 81, 81, 369, 369, 369, 369, 369, 369, 369, 369, 
+    369, 369, 81, 81, 81, 81, 81, 81, 370, 370, 371, 371, 372, 373, 374, 370, 
+    375, 375, 370, 376, 376, 376, 377, 81, 378, 378, 378, 378, 378, 378, 378, 
+    378, 378, 378, 81, 81, 81, 81, 81, 81, 379, 379, 379, 379, 379, 379, 379, 
+    379, 379, 379, 379, 380, 379, 379, 379, 379, 379, 379, 379, 379, 379, 
+    376, 376, 379, 379, 381, 379, 81, 81, 81, 81, 81, 340, 340, 340, 340, 
+    340, 340, 81, 81, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 
+    382, 382, 382, 382, 81, 383, 383, 383, 384, 384, 384, 384, 383, 383, 384, 
+    384, 384, 81, 81, 81, 81, 384, 384, 383, 384, 384, 384, 384, 384, 384, 
+    385, 386, 387, 81, 81, 81, 81, 388, 81, 81, 81, 389, 389, 390, 390, 390, 
+    390, 390, 390, 390, 390, 390, 390, 391, 391, 391, 391, 391, 391, 391, 
+    391, 391, 391, 391, 391, 391, 391, 81, 81, 391, 391, 391, 391, 391, 81, 
+    81, 81, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 81, 
+    81, 81, 81, 392, 392, 81, 81, 81, 81, 81, 81, 393, 393, 393, 393, 393, 
+    393, 393, 393, 393, 393, 394, 81, 81, 81, 395, 395, 396, 396, 396, 396, 
+    396, 396, 396, 396, 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, 
+    397, 397, 397, 397, 397, 398, 399, 400, 400, 401, 81, 81, 402, 402, 403, 
+    403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 404, 405, 
+    404, 405, 405, 405, 405, 405, 405, 405, 81, 406, 404, 405, 404, 404, 405, 
+    405, 405, 405, 405, 405, 405, 405, 404, 404, 404, 404, 404, 404, 405, 
+    405, 407, 407, 407, 407, 407, 407, 407, 407, 81, 81, 408, 409, 409, 409, 
+    409, 409, 409, 409, 409, 409, 409, 81, 81, 81, 81, 81, 81, 410, 410, 410, 
+    410, 410, 410, 410, 411, 410, 410, 410, 410, 410, 410, 81, 81, 96, 96, 
+    96, 96, 96, 156, 156, 156, 156, 156, 156, 96, 96, 156, 412, 81, 413, 413, 
+    413, 413, 414, 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, 415, 
+    415, 415, 415, 415, 416, 414, 413, 413, 413, 413, 413, 414, 413, 414, 
+    414, 414, 414, 414, 413, 414, 417, 415, 415, 415, 415, 415, 415, 415, 81, 
+    81, 81, 81, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 419, 419, 
+    420, 419, 419, 419, 419, 421, 421, 421, 421, 421, 421, 421, 421, 421, 
+    421, 422, 423, 422, 422, 422, 422, 422, 422, 422, 421, 421, 421, 421, 
+    421, 421, 421, 421, 421, 81, 81, 81, 424, 424, 425, 426, 426, 426, 426, 
+    426, 426, 426, 426, 426, 426, 426, 426, 426, 426, 425, 424, 424, 424, 
+    424, 425, 425, 424, 424, 427, 428, 424, 424, 426, 426, 429, 429, 429, 
+    429, 429, 429, 429, 429, 429, 429, 426, 426, 426, 426, 426, 426, 430, 
+    430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, 431, 
+    432, 433, 433, 432, 432, 432, 433, 432, 433, 433, 433, 434, 434, 81, 81, 
+    81, 81, 81, 81, 81, 81, 435, 435, 435, 435, 436, 436, 436, 436, 436, 436, 
+    436, 436, 436, 436, 436, 436, 437, 437, 437, 437, 437, 437, 437, 437, 
+    438, 438, 438, 438, 438, 438, 438, 438, 437, 437, 438, 439, 81, 81, 81, 
+    440, 440, 440, 440, 440, 441, 441, 441, 441, 441, 441, 441, 441, 441, 
+    441, 81, 81, 81, 436, 436, 436, 442, 442, 442, 442, 442, 442, 442, 442, 
+    442, 442, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 
+    443, 443, 444, 444, 444, 444, 444, 444, 445, 445, 93, 81, 81, 81, 81, 81, 
+    81, 81, 446, 446, 446, 446, 446, 446, 446, 446, 96, 96, 96, 326, 447, 
+    156, 156, 156, 156, 156, 96, 96, 156, 156, 156, 156, 96, 448, 447, 447, 
+    447, 447, 447, 447, 447, 449, 449, 449, 449, 156, 449, 449, 449, 449, 
+    448, 448, 96, 449, 449, 448, 96, 96, 81, 81, 81, 81, 81, 81, 56, 56, 56, 
+    56, 56, 56, 79, 79, 79, 79, 79, 93, 59, 59, 59, 59, 59, 59, 59, 59, 59, 
+    82, 82, 82, 82, 82, 59, 59, 59, 59, 82, 82, 82, 82, 82, 56, 56, 56, 56, 
+    56, 450, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 59, 59, 59, 59, 59, 59, 
+    59, 59, 59, 59, 59, 59, 82, 96, 96, 156, 96, 96, 96, 96, 96, 96, 96, 156, 
+    96, 96, 451, 452, 156, 453, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 
+    96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 454, 455, 455, 156, 81, 96, 456, 
+    156, 96, 156, 52, 56, 52, 56, 52, 56, 56, 56, 56, 56, 56, 56, 56, 56, 52, 
+    56, 79, 79, 79, 79, 79, 79, 79, 79, 78, 78, 78, 78, 78, 78, 78, 78, 79, 
+    79, 79, 79, 79, 79, 81, 81, 78, 78, 78, 78, 78, 78, 81, 81, 81, 78, 81, 
+    78, 81, 78, 81, 78, 457, 457, 457, 457, 457, 457, 457, 457, 79, 79, 79, 
+    79, 79, 81, 79, 79, 78, 78, 78, 78, 457, 80, 79, 80, 80, 80, 79, 79, 79, 
+    81, 79, 79, 78, 78, 78, 78, 457, 80, 80, 80, 79, 79, 79, 79, 81, 81, 79, 
+    79, 78, 78, 78, 78, 81, 80, 80, 80, 78, 78, 78, 78, 78, 80, 80, 80, 81, 
+    81, 79, 79, 79, 81, 79, 79, 78, 78, 78, 78, 457, 458, 80, 81, 459, 459, 
+    459, 459, 459, 459, 459, 460, 459, 459, 459, 461, 462, 463, 464, 465, 
+    466, 467, 468, 466, 469, 470, 38, 84, 471, 472, 473, 42, 471, 472, 473, 
+    42, 38, 38, 474, 84, 475, 475, 475, 476, 477, 478, 479, 480, 481, 482, 
+    483, 33, 484, 485, 484, 484, 485, 486, 487, 487, 84, 42, 50, 38, 488, 
+    488, 474, 489, 489, 84, 84, 84, 490, 473, 491, 488, 488, 488, 84, 84, 84, 
+    84, 84, 84, 84, 84, 492, 84, 489, 84, 373, 84, 373, 373, 373, 373, 84, 
+    373, 373, 459, 493, 494, 494, 494, 494, 81, 495, 496, 497, 498, 499, 499, 
+    499, 499, 499, 499, 500, 59, 81, 81, 47, 500, 500, 500, 500, 500, 501, 
+    501, 492, 473, 491, 502, 500, 47, 47, 47, 47, 500, 500, 500, 500, 500, 
+    501, 501, 492, 473, 491, 81, 59, 59, 59, 59, 59, 81, 81, 81, 278, 278, 
+    278, 278, 278, 278, 278, 503, 278, 504, 278, 278, 36, 278, 278, 278, 278, 
+    278, 278, 278, 278, 278, 503, 278, 278, 278, 278, 503, 278, 278, 503, 
+    278, 505, 505, 505, 505, 505, 505, 505, 505, 96, 96, 447, 447, 96, 96, 
+    96, 96, 447, 447, 447, 96, 96, 412, 412, 412, 412, 96, 412, 412, 412, 
+    447, 447, 96, 156, 96, 447, 447, 156, 156, 156, 156, 96, 81, 81, 81, 81, 
+    81, 81, 81, 40, 40, 506, 507, 40, 508, 40, 506, 40, 507, 49, 506, 506, 
+    506, 49, 49, 506, 506, 506, 509, 40, 506, 510, 40, 492, 506, 506, 506, 
+    506, 506, 40, 40, 40, 508, 508, 40, 506, 40, 85, 40, 506, 40, 52, 511, 
+    506, 506, 512, 49, 506, 506, 52, 506, 49, 449, 449, 449, 449, 49, 40, 40, 
+    49, 49, 506, 506, 492, 492, 492, 492, 492, 506, 49, 49, 49, 49, 40, 492, 
+    40, 40, 56, 313, 513, 513, 513, 514, 51, 515, 513, 513, 513, 513, 513, 
+    51, 514, 514, 51, 513, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 
+    516, 516, 517, 517, 517, 517, 516, 516, 517, 517, 517, 517, 517, 517, 
+    517, 517, 517, 52, 56, 517, 517, 517, 517, 51, 40, 40, 81, 81, 81, 81, 
+    54, 54, 54, 54, 54, 508, 508, 508, 508, 508, 492, 492, 40, 40, 40, 40, 
+    492, 40, 40, 492, 40, 40, 492, 40, 40, 40, 40, 40, 40, 40, 492, 40, 40, 
+    40, 40, 40, 40, 40, 40, 40, 44, 44, 40, 40, 40, 40, 40, 40, 40, 40, 40, 
+    40, 40, 40, 492, 492, 40, 40, 54, 40, 54, 40, 40, 40, 40, 40, 40, 40, 40, 
+    40, 40, 44, 40, 40, 40, 40, 492, 492, 492, 492, 492, 492, 492, 492, 492, 
+    492, 492, 492, 54, 492, 54, 54, 492, 492, 492, 54, 54, 492, 492, 54, 492, 
+    492, 492, 54, 492, 54, 518, 519, 492, 54, 492, 492, 492, 492, 54, 492, 
+    492, 54, 54, 54, 54, 492, 492, 54, 492, 54, 492, 54, 54, 54, 54, 54, 54, 
+    492, 54, 492, 492, 492, 492, 492, 54, 54, 54, 54, 492, 492, 492, 492, 54, 
+    54, 492, 492, 54, 492, 492, 492, 54, 492, 492, 492, 492, 492, 54, 492, 
+    492, 492, 492, 492, 54, 54, 492, 492, 54, 54, 54, 54, 492, 492, 54, 54, 
+    492, 492, 54, 54, 492, 492, 492, 492, 492, 54, 492, 492, 492, 54, 492, 
+    492, 492, 492, 492, 492, 492, 492, 492, 492, 492, 492, 492, 54, 492, 492, 
+    492, 492, 492, 492, 492, 520, 473, 491, 473, 491, 40, 40, 40, 40, 40, 40, 
+    508, 40, 40, 40, 40, 40, 40, 40, 521, 521, 40, 40, 40, 40, 492, 492, 40, 
+    40, 40, 40, 40, 40, 40, 522, 523, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 
+    40, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, 40, 
+    492, 40, 40, 40, 40, 40, 40, 40, 40, 313, 40, 40, 40, 40, 40, 492, 492, 
+    492, 492, 492, 492, 492, 492, 492, 40, 40, 40, 40, 40, 524, 524, 524, 
+    524, 40, 40, 40, 521, 525, 525, 521, 40, 40, 40, 40, 40, 40, 40, 40, 40, 
+    40, 40, 81, 40, 40, 40, 81, 81, 81, 81, 81, 51, 51, 51, 51, 51, 51, 51, 
+    51, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 526, 526, 526, 526, 
+    526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 515, 51, 51, 51, 51, 
+    51, 51, 51, 51, 51, 51, 51, 51, 514, 508, 508, 508, 508, 508, 508, 508, 
+    508, 508, 508, 508, 508, 40, 40, 40, 40, 508, 508, 508, 508, 527, 40, 40, 
+    40, 40, 40, 508, 508, 508, 508, 40, 40, 508, 508, 40, 508, 508, 508, 508, 
+    508, 508, 508, 40, 40, 40, 40, 40, 40, 40, 40, 508, 508, 40, 40, 508, 54, 
+    40, 40, 40, 40, 508, 508, 40, 40, 508, 54, 40, 40, 40, 40, 508, 508, 508, 
+    40, 40, 508, 40, 40, 508, 508, 40, 40, 40, 40, 40, 40, 40, 508, 492, 492, 
+    492, 492, 492, 528, 528, 492, 525, 525, 525, 525, 40, 508, 508, 40, 40, 
+    508, 40, 40, 40, 40, 508, 508, 40, 40, 40, 40, 521, 521, 527, 527, 525, 
+    40, 525, 525, 529, 530, 529, 525, 40, 525, 525, 525, 40, 40, 40, 40, 508, 
+    40, 508, 40, 40, 40, 40, 40, 524, 524, 524, 524, 524, 524, 524, 524, 524, 
+    524, 524, 524, 40, 40, 40, 40, 508, 508, 40, 508, 508, 508, 40, 508, 529, 
+    508, 508, 40, 508, 508, 40, 54, 40, 40, 40, 40, 40, 40, 40, 521, 40, 40, 
+    40, 524, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 508, 508, 40, 524, 40, 
+    40, 40, 40, 40, 40, 40, 40, 524, 524, 313, 40, 40, 40, 40, 40, 40, 40, 
+    40, 521, 521, 529, 525, 525, 525, 525, 521, 521, 529, 529, 529, 508, 508, 
+    508, 508, 529, 524, 529, 529, 529, 508, 529, 521, 508, 508, 508, 529, 
+    529, 508, 508, 529, 508, 508, 529, 529, 529, 40, 508, 40, 40, 40, 40, 
+    508, 508, 521, 508, 508, 508, 508, 508, 508, 529, 521, 521, 529, 521, 
+    508, 529, 529, 531, 521, 508, 508, 521, 529, 529, 525, 525, 525, 525, 
+    525, 524, 40, 40, 525, 525, 532, 532, 530, 530, 40, 40, 524, 40, 40, 40, 
+    40, 40, 40, 40, 40, 40, 40, 40, 40, 44, 40, 40, 40, 40, 40, 40, 524, 40, 
+    524, 40, 40, 40, 40, 524, 524, 524, 40, 533, 40, 40, 40, 534, 534, 534, 
+    534, 534, 534, 40, 535, 535, 525, 40, 40, 40, 473, 491, 473, 491, 473, 
+    491, 473, 491, 473, 491, 473, 491, 473, 491, 51, 51, 515, 515, 515, 515, 
+    515, 515, 515, 515, 515, 515, 515, 515, 40, 524, 524, 524, 40, 40, 40, 
+    40, 40, 40, 40, 524, 492, 492, 492, 492, 492, 473, 491, 492, 492, 492, 
+    492, 492, 492, 492, 16, 31, 16, 31, 16, 31, 16, 31, 473, 491, 536, 536, 
+    536, 536, 536, 536, 536, 536, 492, 492, 492, 473, 491, 16, 31, 473, 491, 
+    473, 491, 473, 491, 473, 491, 473, 491, 492, 492, 492, 492, 492, 492, 
+    492, 473, 491, 473, 491, 492, 492, 492, 492, 492, 492, 492, 492, 473, 
+    491, 492, 492, 40, 40, 40, 524, 524, 40, 40, 40, 492, 492, 492, 492, 492, 
+    40, 40, 492, 492, 492, 492, 492, 492, 40, 40, 40, 524, 40, 40, 40, 40, 
+    533, 508, 508, 40, 40, 40, 40, 81, 81, 40, 40, 40, 40, 40, 40, 40, 40, 
+    81, 81, 40, 40, 81, 81, 81, 40, 40, 40, 40, 81, 40, 40, 40, 40, 40, 40, 
+    81, 81, 81, 81, 40, 40, 40, 40, 537, 537, 537, 537, 537, 537, 537, 537, 
+    537, 537, 537, 537, 537, 537, 537, 81, 538, 538, 538, 538, 538, 538, 538, 
+    538, 538, 538, 538, 538, 538, 538, 538, 81, 52, 56, 52, 52, 52, 56, 56, 
+    52, 56, 52, 56, 52, 56, 52, 52, 52, 52, 56, 52, 56, 56, 52, 56, 56, 56, 
+    56, 56, 56, 59, 59, 52, 52, 87, 88, 87, 88, 88, 539, 539, 539, 539, 539, 
+    539, 87, 88, 87, 88, 540, 540, 540, 87, 88, 81, 81, 81, 81, 81, 541, 542, 
+    542, 542, 543, 541, 542, 544, 544, 544, 544, 544, 544, 544, 544, 544, 
+    544, 544, 544, 544, 544, 81, 544, 81, 81, 81, 81, 81, 544, 81, 81, 545, 
+    545, 545, 545, 545, 545, 545, 545, 81, 81, 81, 81, 81, 81, 81, 546, 547, 
+    81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 548, 95, 95, 95, 
+    95, 95, 95, 95, 95, 549, 549, 42, 50, 42, 50, 549, 549, 549, 42, 50, 549, 
+    42, 50, 373, 373, 373, 373, 373, 373, 373, 373, 84, 468, 550, 373, 551, 
+    84, 42, 50, 84, 84, 42, 50, 473, 491, 473, 491, 473, 491, 473, 491, 373, 
+    373, 373, 373, 371, 60, 373, 373, 84, 373, 373, 84, 84, 84, 84, 84, 552, 
+    552, 373, 373, 373, 84, 468, 373, 473, 373, 373, 373, 373, 373, 373, 373, 
+    81, 81, 81, 81, 81, 81, 553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 
+    81, 553, 553, 553, 553, 553, 553, 553, 553, 553, 81, 81, 81, 81, 553, 
+    553, 553, 553, 553, 553, 81, 81, 521, 521, 521, 521, 521, 521, 521, 521, 
+    521, 521, 521, 521, 81, 81, 81, 81, 554, 555, 555, 556, 521, 557, 558, 
+    559, 522, 523, 522, 523, 522, 523, 522, 523, 522, 523, 521, 521, 522, 
+    523, 522, 523, 522, 523, 522, 523, 560, 522, 523, 523, 521, 559, 559, 
+    559, 559, 559, 559, 559, 559, 559, 561, 562, 563, 564, 565, 565, 566, 
+    567, 567, 567, 567, 568, 521, 521, 559, 559, 559, 557, 569, 556, 521, 
+    525, 81, 570, 571, 570, 571, 570, 571, 570, 571, 570, 571, 571, 571, 571, 
+    571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 570, 
+    571, 571, 571, 571, 571, 571, 571, 570, 571, 570, 571, 570, 571, 571, 
+    571, 571, 571, 571, 570, 571, 571, 571, 571, 571, 571, 570, 570, 81, 81, 
+    572, 572, 573, 573, 574, 574, 571, 560, 575, 576, 575, 576, 575, 576, 
+    575, 576, 575, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 
+    576, 576, 576, 576, 576, 576, 575, 576, 576, 576, 576, 576, 576, 576, 
+    575, 576, 575, 576, 575, 576, 576, 576, 576, 576, 576, 575, 576, 576, 
+    576, 576, 576, 576, 575, 575, 576, 576, 576, 576, 577, 578, 579, 579, 
+    576, 81, 81, 81, 81, 81, 580, 580, 580, 580, 580, 580, 580, 580, 580, 
+    580, 580, 580, 580, 580, 580, 580, 580, 580, 81, 81, 581, 581, 581, 581, 
+    581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 
+    581, 581, 581, 581, 81, 582, 582, 583, 583, 583, 583, 582, 582, 582, 582, 
+    582, 582, 582, 582, 582, 582, 580, 580, 580, 81, 81, 81, 81, 81, 575, 
+    575, 575, 575, 575, 575, 575, 575, 584, 584, 584, 584, 584, 584, 584, 
+    584, 584, 584, 584, 584, 584, 585, 585, 81, 583, 583, 583, 583, 583, 583, 
+    583, 583, 583, 583, 582, 582, 582, 582, 582, 582, 586, 586, 586, 586, 
+    586, 586, 586, 586, 521, 587, 587, 587, 587, 587, 587, 587, 587, 587, 
+    587, 587, 587, 587, 587, 587, 584, 584, 584, 584, 585, 585, 585, 582, 
+    582, 587, 587, 587, 587, 587, 587, 587, 582, 582, 582, 582, 521, 521, 
+    521, 521, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 
+    588, 588, 588, 81, 582, 582, 582, 582, 582, 582, 582, 521, 521, 521, 521, 
+    582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 521, 521, 589, 
+    589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 590, 
+    590, 590, 590, 590, 590, 590, 590, 590, 590, 589, 589, 589, 590, 590, 
+    590, 590, 590, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 
+    591, 591, 592, 591, 591, 591, 591, 591, 591, 591, 81, 81, 81, 593, 593, 
+    593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 81, 594, 
+    594, 594, 594, 594, 594, 594, 594, 595, 595, 595, 595, 595, 595, 596, 
+    596, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 598, 
+    599, 600, 599, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 597, 
+    597, 81, 81, 81, 81, 90, 93, 90, 93, 90, 93, 602, 95, 97, 97, 97, 603, 
+    95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 603, 604, 90, 93, 90, 93, 450, 
+    450, 95, 95, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 
+    605, 605, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 607, 607, 
+    608, 609, 609, 609, 609, 609, 62, 62, 62, 62, 62, 62, 62, 60, 60, 60, 60, 
+    60, 60, 60, 60, 60, 62, 62, 52, 56, 52, 56, 52, 56, 56, 56, 52, 56, 52, 
+    56, 52, 56, 59, 56, 56, 56, 56, 56, 56, 56, 56, 52, 56, 52, 56, 52, 52, 
+    56, 60, 610, 610, 52, 56, 52, 56, 57, 52, 56, 52, 56, 56, 56, 52, 56, 52, 
+    56, 52, 52, 52, 52, 52, 81, 52, 52, 52, 52, 52, 56, 52, 56, 81, 81, 81, 
+    81, 81, 81, 81, 57, 59, 59, 56, 57, 57, 57, 57, 57, 611, 611, 612, 611, 
+    611, 611, 613, 611, 611, 611, 611, 612, 611, 611, 611, 611, 611, 611, 
+    611, 611, 611, 611, 611, 611, 611, 611, 611, 614, 614, 612, 612, 614, 
+    615, 615, 615, 615, 81, 81, 81, 81, 616, 616, 616, 616, 616, 616, 313, 
+    313, 503, 512, 81, 81, 81, 81, 81, 81, 617, 617, 617, 617, 617, 617, 617, 
+    617, 617, 617, 617, 617, 618, 618, 619, 619, 620, 620, 621, 621, 621, 
+    621, 621, 621, 621, 621, 621, 621, 621, 621, 621, 621, 621, 621, 621, 
+    621, 620, 620, 620, 620, 620, 620, 620, 620, 620, 620, 620, 620, 620, 
+    620, 620, 620, 622, 623, 81, 81, 81, 81, 81, 81, 81, 81, 624, 624, 625, 
+    625, 625, 625, 625, 625, 625, 625, 625, 625, 81, 81, 81, 81, 81, 81, 197, 
+    197, 197, 197, 197, 197, 197, 197, 197, 197, 194, 194, 194, 194, 194, 
+    194, 200, 200, 200, 194, 626, 194, 81, 81, 627, 627, 627, 627, 627, 627, 
+    627, 627, 627, 627, 628, 628, 628, 628, 628, 628, 628, 628, 628, 628, 
+    628, 628, 628, 628, 628, 628, 628, 628, 628, 628, 629, 629, 629, 629, 
+    629, 630, 630, 630, 198, 631, 632, 632, 632, 632, 632, 632, 632, 632, 
+    632, 632, 632, 632, 632, 632, 632, 633, 633, 633, 633, 633, 633, 633, 
+    633, 633, 633, 633, 634, 635, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 
+    636, 328, 328, 328, 328, 328, 81, 81, 81, 637, 637, 637, 638, 639, 639, 
+    639, 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, 640, 
+    638, 638, 637, 637, 637, 637, 638, 638, 637, 638, 638, 638, 641, 642, 
+    642, 642, 642, 642, 642, 643, 643, 643, 642, 642, 642, 642, 81, 61, 644, 
+    644, 644, 644, 644, 644, 644, 644, 644, 644, 81, 81, 81, 81, 642, 642, 
+    314, 314, 314, 314, 314, 316, 645, 314, 319, 319, 314, 314, 314, 314, 
+    314, 81, 646, 646, 646, 646, 646, 646, 646, 646, 646, 647, 647, 647, 647, 
+    647, 647, 648, 648, 647, 647, 648, 648, 647, 647, 81, 646, 646, 646, 647, 
+    646, 646, 646, 646, 646, 646, 646, 646, 647, 648, 81, 81, 649, 649, 649, 
+    649, 649, 649, 649, 649, 649, 649, 81, 81, 650, 651, 651, 651, 645, 314, 
+    314, 314, 314, 314, 314, 323, 323, 323, 314, 315, 316, 315, 314, 314, 
+    652, 652, 652, 652, 652, 652, 652, 652, 653, 652, 653, 653, 654, 652, 
+    652, 653, 653, 652, 652, 652, 652, 652, 653, 653, 652, 653, 652, 81, 81, 
+    81, 81, 81, 81, 81, 81, 652, 652, 655, 656, 656, 657, 657, 657, 657, 657, 
+    657, 657, 657, 657, 657, 657, 658, 659, 659, 658, 658, 660, 660, 657, 
+    661, 661, 658, 662, 81, 81, 331, 331, 331, 331, 331, 331, 81, 56, 56, 56, 
+    610, 59, 59, 59, 59, 56, 56, 56, 56, 56, 79, 81, 81, 338, 338, 338, 338, 
+    338, 338, 338, 338, 657, 657, 657, 658, 658, 659, 658, 658, 659, 658, 
+    658, 660, 658, 662, 81, 81, 663, 663, 663, 663, 663, 663, 663, 663, 663, 
+    663, 81, 81, 81, 81, 81, 81, 664, 665, 665, 665, 665, 665, 665, 665, 665, 
+    665, 665, 665, 665, 665, 665, 665, 665, 665, 665, 665, 664, 665, 665, 
+    665, 665, 665, 665, 665, 81, 81, 81, 81, 329, 329, 329, 329, 329, 329, 
+    329, 81, 81, 81, 81, 330, 330, 330, 330, 330, 330, 330, 330, 330, 81, 81, 
+    81, 81, 666, 666, 666, 666, 666, 666, 666, 666, 667, 667, 667, 667, 667, 
+    667, 667, 667, 589, 589, 590, 590, 590, 590, 590, 590, 56, 56, 56, 56, 
+    56, 56, 56, 81, 81, 81, 81, 101, 101, 101, 101, 101, 81, 81, 81, 81, 81, 
+    129, 668, 129, 129, 669, 129, 129, 129, 129, 129, 129, 129, 129, 129, 
+    129, 129, 129, 129, 81, 129, 129, 129, 129, 129, 81, 129, 81, 129, 129, 
+    81, 129, 129, 81, 129, 129, 146, 146, 670, 670, 670, 670, 670, 670, 670, 
+    670, 670, 670, 670, 670, 670, 670, 670, 670, 81, 81, 81, 81, 81, 81, 81, 
+    81, 81, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 491, 473, 
+    81, 81, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 135, 138, 81, 
+    81, 671, 671, 671, 671, 671, 671, 671, 671, 672, 555, 555, 672, 672, 673, 
+    673, 522, 523, 674, 81, 81, 81, 81, 81, 81, 96, 96, 96, 96, 96, 96, 96, 
+    156, 156, 156, 156, 156, 156, 156, 95, 95, 556, 566, 566, 675, 675, 522, 
+    523, 522, 523, 522, 523, 522, 523, 522, 523, 522, 523, 522, 523, 522, 
+    523, 556, 556, 522, 523, 556, 556, 556, 556, 675, 675, 675, 676, 556, 
+    676, 81, 577, 677, 673, 673, 566, 522, 523, 522, 523, 522, 523, 678, 556, 
+    556, 679, 680, 681, 681, 681, 81, 556, 682, 683, 556, 81, 81, 81, 81, 
+    146, 146, 146, 146, 146, 81, 81, 493, 81, 684, 685, 686, 687, 688, 685, 
+    685, 689, 690, 685, 691, 692, 693, 692, 694, 695, 695, 695, 695, 695, 
+    695, 695, 695, 695, 695, 696, 697, 698, 698, 698, 684, 685, 699, 699, 
+    699, 699, 699, 699, 699, 699, 699, 699, 699, 699, 699, 699, 699, 699, 
+    699, 699, 689, 685, 690, 700, 701, 700, 702, 702, 702, 702, 702, 702, 
+    702, 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, 689, 698, 
+    690, 698, 689, 690, 703, 704, 705, 703, 706, 707, 708, 708, 708, 708, 
+    708, 708, 708, 708, 708, 709, 707, 707, 707, 707, 707, 707, 707, 707, 
+    707, 707, 707, 707, 707, 707, 707, 707, 707, 707, 707, 707, 707, 710, 
+    710, 711, 711, 711, 711, 711, 711, 711, 711, 711, 711, 711, 711, 711, 
+    711, 711, 81, 81, 81, 711, 711, 711, 711, 711, 711, 81, 81, 711, 711, 
+    711, 81, 81, 81, 712, 687, 698, 700, 713, 687, 687, 81, 714, 715, 715, 
+    715, 715, 714, 714, 81, 81, 716, 716, 716, 717, 508, 81, 81, 718, 718, 
+    718, 718, 718, 718, 718, 718, 718, 718, 718, 718, 81, 718, 718, 718, 718, 
+    718, 718, 718, 718, 718, 718, 81, 718, 718, 718, 81, 718, 718, 81, 718, 
+    718, 718, 718, 718, 718, 718, 81, 81, 718, 718, 718, 81, 81, 81, 81, 81, 
+    198, 373, 198, 81, 81, 81, 81, 616, 616, 616, 616, 616, 616, 616, 616, 
+    616, 616, 616, 616, 616, 81, 81, 81, 313, 719, 719, 719, 719, 719, 719, 
+    719, 719, 719, 719, 719, 719, 719, 720, 720, 720, 720, 721, 721, 721, 
+    721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 
+    720, 720, 721, 722, 722, 81, 40, 40, 40, 40, 81, 81, 81, 81, 721, 81, 81, 
+    81, 81, 81, 81, 81, 313, 313, 313, 313, 313, 156, 81, 81, 723, 723, 723, 
+    723, 723, 723, 723, 723, 723, 723, 723, 723, 723, 81, 81, 81, 724, 724, 
+    724, 724, 724, 724, 724, 724, 724, 81, 81, 81, 81, 81, 81, 81, 156, 500, 
+    500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 
+    500, 500, 500, 500, 81, 81, 81, 81, 725, 725, 725, 725, 725, 725, 725, 
+    725, 726, 726, 726, 726, 81, 81, 81, 81, 81, 81, 81, 81, 81, 725, 725, 
+    725, 727, 727, 727, 727, 727, 727, 727, 727, 727, 728, 727, 727, 727, 
+    727, 727, 727, 727, 727, 728, 81, 81, 81, 81, 81, 729, 729, 729, 729, 
+    729, 729, 729, 729, 729, 729, 729, 729, 729, 729, 730, 730, 730, 730, 
+    730, 81, 81, 81, 81, 81, 731, 731, 731, 731, 731, 731, 731, 731, 731, 
+    731, 731, 731, 731, 731, 81, 732, 733, 733, 733, 733, 733, 733, 733, 733, 
+    733, 733, 733, 733, 81, 81, 81, 81, 734, 735, 735, 735, 735, 735, 81, 81, 
+    736, 736, 736, 736, 736, 736, 736, 736, 737, 737, 737, 737, 737, 737, 
+    737, 737, 738, 738, 738, 738, 738, 738, 738, 738, 739, 739, 739, 739, 
+    739, 739, 739, 739, 739, 739, 739, 739, 739, 739, 81, 81, 740, 740, 740, 
+    740, 740, 740, 740, 740, 740, 740, 81, 81, 81, 81, 81, 81, 741, 741, 741, 
+    741, 741, 741, 741, 741, 741, 741, 741, 741, 81, 81, 81, 81, 742, 742, 
+    742, 742, 742, 742, 742, 742, 742, 742, 742, 742, 81, 81, 81, 81, 743, 
+    743, 743, 743, 743, 743, 743, 743, 744, 744, 744, 744, 744, 744, 744, 
+    744, 744, 744, 744, 744, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 745, 
+    746, 746, 746, 746, 746, 746, 746, 746, 746, 746, 746, 746, 746, 746, 
+    746, 81, 746, 746, 746, 746, 746, 746, 81, 81, 747, 747, 747, 747, 747, 
+    747, 81, 81, 747, 81, 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, 
+    747, 747, 747, 747, 747, 747, 747, 747, 747, 747, 81, 747, 747, 81, 81, 
+    81, 747, 81, 81, 747, 748, 748, 748, 748, 748, 748, 748, 748, 748, 748, 
+    748, 748, 748, 748, 81, 749, 750, 750, 750, 750, 750, 750, 750, 750, 751, 
+    751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 
+    752, 752, 753, 753, 753, 753, 753, 753, 753, 754, 754, 754, 754, 754, 
+    754, 754, 754, 754, 754, 754, 754, 754, 754, 754, 81, 81, 81, 81, 81, 81, 
+    81, 81, 755, 755, 755, 755, 755, 755, 755, 755, 755, 756, 756, 756, 756, 
+    756, 756, 756, 756, 756, 756, 756, 81, 756, 756, 81, 81, 81, 81, 81, 757, 
+    757, 757, 757, 757, 758, 758, 758, 758, 758, 758, 758, 758, 758, 758, 
+    758, 758, 758, 758, 759, 759, 759, 759, 759, 759, 81, 81, 81, 760, 761, 
+    761, 761, 761, 761, 761, 761, 761, 761, 761, 81, 81, 81, 81, 81, 762, 
+    763, 763, 763, 763, 763, 763, 763, 763, 764, 764, 764, 764, 764, 764, 
+    764, 764, 81, 81, 81, 81, 765, 765, 764, 764, 765, 765, 765, 765, 765, 
+    765, 765, 765, 81, 81, 765, 765, 765, 765, 765, 765, 766, 767, 767, 767, 
+    81, 767, 767, 81, 81, 81, 81, 81, 767, 768, 767, 769, 766, 766, 766, 766, 
+    81, 766, 766, 766, 81, 766, 766, 766, 766, 766, 766, 766, 766, 766, 766, 
+    766, 766, 766, 766, 766, 766, 766, 766, 766, 81, 81, 81, 81, 769, 770, 
+    768, 81, 81, 81, 81, 771, 772, 772, 772, 772, 772, 772, 772, 772, 773, 
+    773, 773, 773, 773, 773, 773, 773, 774, 81, 81, 81, 81, 81, 81, 81, 775, 
+    775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 776, 776, 
+    777, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 
+    779, 779, 779, 780, 780, 780, 780, 780, 780, 780, 780, 781, 780, 780, 
+    780, 780, 780, 780, 780, 780, 780, 780, 780, 780, 782, 783, 81, 81, 81, 
+    81, 784, 784, 784, 784, 784, 785, 785, 785, 785, 785, 785, 786, 81, 787, 
+    787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, 81, 81, 
+    81, 788, 788, 788, 788, 788, 788, 788, 789, 789, 789, 789, 789, 789, 789, 
+    789, 789, 789, 789, 789, 789, 789, 81, 81, 790, 790, 790, 790, 790, 790, 
+    790, 790, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 81, 81, 
+    81, 81, 81, 792, 792, 792, 792, 792, 792, 792, 792, 793, 793, 793, 793, 
+    793, 793, 793, 793, 793, 793, 81, 81, 81, 81, 81, 81, 81, 794, 794, 794, 
+    794, 81, 81, 81, 81, 795, 795, 795, 795, 795, 795, 795, 796, 796, 796, 
+    796, 796, 796, 796, 796, 796, 81, 81, 81, 81, 81, 81, 81, 797, 797, 797, 
+    797, 797, 797, 797, 797, 797, 797, 797, 81, 81, 81, 81, 81, 798, 798, 
+    798, 798, 798, 798, 798, 798, 798, 798, 798, 81, 81, 81, 81, 81, 81, 81, 
+    799, 799, 799, 799, 799, 799, 800, 800, 800, 800, 800, 800, 800, 800, 
+    800, 800, 800, 800, 800, 800, 800, 81, 801, 802, 801, 803, 803, 803, 803, 
+    803, 803, 803, 803, 803, 803, 803, 803, 803, 802, 802, 802, 802, 802, 
+    802, 802, 802, 802, 802, 802, 802, 802, 802, 804, 805, 805, 806, 806, 
+    806, 806, 806, 81, 81, 81, 81, 807, 807, 807, 807, 807, 807, 807, 807, 
+    807, 807, 807, 807, 807, 807, 807, 807, 807, 807, 807, 807, 808, 808, 
+    808, 808, 808, 808, 808, 808, 808, 808, 81, 81, 81, 81, 81, 81, 81, 804, 
+    809, 809, 810, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 811, 
+    811, 811, 810, 810, 810, 809, 809, 809, 809, 810, 810, 812, 813, 814, 
+    814, 815, 816, 816, 816, 816, 81, 81, 81, 81, 81, 81, 817, 817, 817, 817, 
+    817, 817, 817, 817, 817, 81, 81, 81, 81, 81, 81, 81, 818, 818, 818, 818, 
+    818, 818, 818, 818, 818, 818, 81, 81, 81, 81, 81, 81, 819, 819, 819, 820, 
+    820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 
+    820, 820, 820, 820, 820, 821, 821, 821, 821, 821, 822, 821, 821, 821, 
+    821, 821, 821, 823, 823, 81, 824, 824, 824, 824, 824, 824, 824, 824, 824, 
+    824, 825, 825, 825, 825, 81, 81, 81, 81, 826, 826, 826, 826, 826, 826, 
+    826, 826, 826, 826, 826, 827, 828, 829, 826, 81, 830, 830, 831, 832, 832, 
+    832, 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, 
+    831, 831, 831, 830, 830, 830, 830, 830, 830, 830, 830, 830, 831, 833, 
+    832, 832, 832, 832, 834, 834, 835, 834, 835, 836, 830, 830, 835, 81, 81, 
+    837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 832, 838, 832, 834, 
+    834, 834, 81, 839, 839, 839, 839, 839, 839, 839, 839, 839, 839, 839, 839, 
+    839, 839, 839, 839, 839, 839, 839, 839, 81, 81, 81, 840, 840, 840, 840, 
+    840, 840, 840, 840, 840, 840, 81, 840, 840, 840, 840, 840, 840, 840, 840, 
+    840, 841, 841, 841, 842, 842, 842, 841, 841, 842, 843, 844, 842, 845, 
+    845, 846, 845, 845, 846, 842, 81, 847, 847, 847, 847, 847, 847, 847, 81, 
+    847, 81, 847, 847, 847, 847, 81, 847, 847, 847, 847, 847, 847, 847, 847, 
+    847, 847, 847, 847, 847, 847, 847, 81, 847, 847, 848, 81, 81, 81, 81, 81, 
+    81, 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, 
+    849, 850, 851, 851, 851, 850, 850, 850, 850, 850, 850, 852, 853, 81, 81, 
+    81, 81, 81, 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 81, 81, 81, 
+    81, 81, 81, 855, 855, 856, 856, 81, 857, 857, 857, 857, 857, 857, 857, 
+    857, 81, 81, 857, 857, 81, 81, 857, 857, 857, 857, 857, 857, 857, 857, 
+    857, 857, 857, 857, 857, 857, 81, 857, 857, 857, 857, 857, 857, 857, 81, 
+    857, 857, 81, 857, 857, 857, 857, 857, 81, 81, 858, 857, 856, 856, 855, 
+    856, 856, 856, 856, 81, 81, 856, 856, 81, 81, 856, 856, 859, 81, 81, 857, 
+    81, 81, 81, 81, 81, 81, 856, 81, 81, 81, 81, 81, 857, 857, 857, 857, 857, 
+    856, 856, 81, 81, 860, 860, 860, 860, 860, 860, 860, 81, 81, 81, 861, 
+    861, 861, 861, 861, 861, 861, 861, 861, 861, 861, 861, 861, 862, 862, 
+    862, 863, 863, 863, 863, 863, 863, 863, 863, 862, 862, 864, 863, 863, 
+    862, 865, 861, 861, 861, 861, 866, 866, 866, 866, 867, 868, 868, 868, 
+    868, 868, 868, 868, 868, 868, 868, 81, 866, 81, 867, 81, 81, 869, 869, 
+    869, 869, 869, 869, 869, 869, 870, 870, 870, 871, 871, 871, 871, 871, 
+    871, 870, 871, 870, 870, 870, 870, 871, 871, 870, 872, 873, 869, 869, 
+    874, 869, 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, 81, 81, 81, 
+    81, 81, 81, 876, 876, 876, 876, 876, 876, 876, 876, 876, 876, 876, 876, 
+    876, 876, 876, 877, 877, 877, 878, 878, 878, 878, 81, 81, 877, 877, 877, 
+    877, 878, 878, 877, 879, 880, 881, 882, 882, 883, 883, 884, 884, 884, 
+    882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 
+    882, 876, 876, 876, 876, 878, 878, 81, 81, 885, 885, 885, 885, 885, 885, 
+    885, 885, 886, 886, 886, 887, 887, 887, 887, 887, 887, 887, 887, 886, 
+    886, 887, 886, 888, 887, 889, 889, 890, 885, 81, 81, 81, 891, 891, 891, 
+    891, 891, 891, 891, 891, 891, 891, 81, 81, 81, 81, 81, 81, 892, 892, 892, 
+    892, 892, 892, 892, 892, 892, 892, 892, 892, 892, 81, 81, 81, 893, 893, 
+    893, 893, 893, 893, 893, 893, 893, 893, 893, 894, 895, 894, 895, 895, 
+    894, 894, 894, 894, 894, 894, 896, 897, 898, 898, 898, 898, 898, 898, 
+    898, 898, 898, 898, 81, 81, 81, 81, 81, 81, 899, 899, 899, 899, 899, 899, 
+    899, 899, 899, 899, 81, 81, 81, 900, 900, 900, 901, 901, 900, 900, 900, 
+    900, 901, 900, 900, 900, 900, 902, 81, 81, 81, 81, 903, 903, 903, 903, 
+    903, 903, 903, 903, 903, 903, 904, 904, 905, 905, 905, 906, 907, 907, 
+    907, 907, 907, 907, 907, 907, 908, 908, 908, 908, 908, 908, 908, 908, 
+    909, 909, 909, 909, 909, 909, 909, 909, 909, 909, 910, 910, 910, 910, 
+    910, 910, 910, 910, 910, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 
+    911, 912, 913, 913, 913, 913, 913, 913, 914, 914, 913, 913, 912, 912, 
+    912, 912, 912, 912, 912, 912, 912, 912, 912, 912, 912, 912, 912, 912, 
+    913, 915, 913, 913, 913, 913, 914, 912, 913, 913, 913, 913, 916, 917, 
+    918, 918, 918, 918, 916, 917, 915, 919, 920, 920, 920, 920, 920, 920, 
+    921, 921, 920, 920, 920, 919, 919, 919, 919, 919, 919, 919, 919, 919, 
+    919, 919, 919, 919, 919, 919, 919, 81, 81, 919, 919, 919, 919, 920, 920, 
+    920, 920, 920, 920, 920, 920, 920, 920, 920, 920, 920, 921, 920, 922, 
+    923, 923, 923, 81, 924, 924, 924, 923, 923, 81, 81, 81, 81, 81, 925, 925, 
+    925, 925, 925, 925, 925, 925, 925, 81, 81, 81, 81, 81, 81, 81, 926, 926, 
+    926, 926, 926, 926, 926, 926, 926, 81, 926, 926, 926, 926, 926, 926, 926, 
+    926, 926, 926, 926, 926, 926, 927, 928, 928, 928, 928, 928, 928, 928, 81, 
+    928, 928, 928, 928, 928, 928, 927, 929, 926, 930, 930, 930, 930, 930, 81, 
+    81, 931, 931, 931, 931, 931, 931, 931, 931, 931, 931, 932, 932, 932, 932, 
+    932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, 
+    932, 81, 81, 81, 933, 934, 935, 935, 935, 935, 935, 935, 935, 935, 935, 
+    935, 935, 935, 935, 935, 81, 81, 936, 936, 936, 936, 936, 936, 936, 936, 
+    936, 936, 936, 936, 936, 936, 81, 937, 936, 936, 936, 936, 936, 936, 936, 
+    937, 936, 936, 937, 936, 936, 81, 938, 938, 938, 938, 938, 938, 938, 81, 
+    938, 938, 81, 938, 938, 938, 938, 938, 938, 938, 938, 938, 938, 938, 938, 
+    938, 938, 939, 939, 939, 939, 939, 939, 81, 81, 81, 939, 81, 939, 939, 
+    81, 939, 939, 939, 940, 939, 941, 941, 938, 939, 942, 942, 942, 942, 942, 
+    942, 942, 942, 942, 942, 81, 81, 81, 81, 81, 81, 943, 943, 943, 943, 943, 
+    943, 943, 943, 943, 943, 81, 81, 81, 81, 81, 81, 944, 944, 944, 944, 944, 
+    944, 944, 944, 944, 944, 944, 944, 944, 944, 944, 81, 945, 945, 945, 945, 
+    945, 81, 81, 81, 943, 943, 943, 943, 81, 81, 81, 81, 946, 946, 946, 946, 
+    946, 946, 946, 946, 947, 947, 947, 948, 948, 948, 946, 946, 946, 946, 
+    948, 946, 946, 946, 947, 948, 947, 948, 946, 946, 946, 946, 946, 946, 
+    946, 947, 948, 948, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 
+    946, 81, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 
+    949, 950, 951, 949, 949, 949, 949, 949, 949, 949, 81, 605, 81, 81, 81, 
+    81, 81, 81, 81, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 
+    952, 952, 952, 952, 81, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 
+    81, 81, 81, 81, 954, 954, 955, 955, 955, 955, 955, 955, 955, 955, 955, 
+    955, 955, 955, 955, 955, 81, 81, 956, 956, 956, 956, 956, 957, 81, 81, 
+    958, 958, 958, 958, 958, 958, 958, 958, 959, 959, 959, 959, 959, 959, 
+    959, 960, 960, 960, 961, 961, 962, 962, 962, 962, 963, 963, 963, 963, 
+    960, 962, 81, 81, 964, 964, 964, 964, 964, 964, 964, 964, 964, 964, 81, 
+    965, 965, 965, 965, 965, 965, 965, 81, 958, 958, 958, 958, 958, 81, 81, 
+    81, 81, 81, 958, 958, 958, 966, 966, 966, 966, 966, 966, 966, 966, 966, 
+    966, 966, 966, 966, 81, 81, 81, 966, 967, 967, 967, 967, 967, 967, 967, 
+    967, 967, 967, 967, 967, 967, 967, 967, 967, 967, 967, 967, 967, 967, 
+    967, 81, 81, 81, 81, 81, 81, 81, 81, 968, 968, 968, 968, 969, 969, 969, 
+    969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 970, 971, 81, 81, 81, 
+    81, 81, 81, 972, 972, 972, 972, 972, 972, 972, 972, 972, 972, 972, 972, 
+    972, 81, 81, 81, 972, 972, 972, 81, 81, 81, 81, 81, 576, 571, 571, 571, 
+    571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 81, 973, 973, 973, 
+    973, 973, 973, 973, 973, 973, 973, 973, 973, 81, 81, 81, 81, 974, 974, 
+    974, 974, 974, 974, 974, 974, 974, 974, 974, 81, 81, 81, 81, 81, 974, 
+    974, 974, 974, 974, 81, 81, 81, 974, 81, 81, 81, 81, 81, 81, 81, 974, 
+    974, 81, 81, 975, 976, 977, 978, 499, 499, 499, 499, 81, 81, 81, 81, 313, 
+    313, 313, 313, 313, 313, 81, 81, 313, 313, 313, 313, 313, 313, 313, 81, 
+    81, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, 979, 979, 
+    447, 447, 447, 313, 313, 313, 980, 979, 979, 979, 979, 979, 499, 499, 
+    499, 499, 499, 499, 499, 499, 156, 156, 156, 156, 156, 156, 156, 156, 
+    313, 313, 96, 96, 96, 96, 96, 156, 156, 313, 313, 313, 313, 313, 313, 96, 
+    96, 96, 96, 313, 313, 313, 81, 81, 81, 81, 81, 81, 81, 721, 721, 981, 
+    981, 981, 721, 81, 81, 616, 616, 81, 81, 81, 81, 81, 81, 506, 506, 506, 
+    506, 506, 506, 506, 506, 506, 506, 49, 49, 49, 49, 49, 49, 49, 49, 49, 
+    49, 49, 49, 49, 49, 49, 49, 49, 49, 506, 506, 506, 506, 506, 506, 506, 
+    506, 506, 506, 49, 49, 49, 49, 49, 49, 49, 81, 49, 49, 49, 49, 49, 49, 
+    506, 81, 506, 506, 81, 81, 506, 81, 81, 506, 506, 81, 81, 506, 506, 506, 
+    506, 81, 506, 506, 49, 49, 81, 49, 81, 49, 49, 49, 49, 49, 49, 49, 81, 
+    49, 49, 49, 49, 49, 49, 49, 506, 506, 81, 506, 506, 506, 506, 81, 81, 
+    506, 506, 506, 506, 506, 506, 506, 506, 81, 506, 506, 506, 506, 506, 506, 
+    506, 81, 49, 49, 506, 506, 81, 506, 506, 506, 506, 81, 506, 506, 506, 
+    506, 506, 81, 506, 81, 81, 81, 506, 506, 506, 506, 506, 506, 506, 81, 49, 
+    49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 81, 81, 506, 982, 49, 49, 49, 
+    49, 49, 49, 49, 49, 49, 492, 49, 49, 49, 49, 49, 49, 506, 506, 506, 506, 
+    506, 506, 506, 506, 506, 982, 49, 49, 49, 49, 49, 49, 49, 49, 49, 492, 
+    49, 49, 506, 506, 506, 506, 506, 982, 49, 49, 49, 49, 49, 49, 49, 49, 49, 
+    492, 49, 49, 49, 49, 49, 49, 506, 506, 506, 506, 506, 506, 506, 506, 506, 
+    982, 49, 492, 49, 49, 49, 49, 49, 49, 49, 49, 506, 49, 81, 81, 983, 983, 
+    983, 983, 983, 983, 983, 983, 983, 983, 984, 984, 984, 984, 984, 984, 
+    984, 984, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 
+    985, 985, 985, 984, 984, 984, 984, 985, 985, 985, 985, 985, 985, 985, 
+    985, 985, 985, 984, 984, 984, 984, 984, 984, 984, 984, 985, 984, 984, 
+    984, 984, 984, 984, 985, 984, 984, 986, 986, 986, 986, 987, 81, 81, 81, 
+    81, 81, 81, 81, 985, 985, 985, 985, 985, 81, 985, 985, 985, 985, 985, 
+    985, 985, 988, 988, 988, 988, 988, 988, 988, 81, 988, 988, 988, 988, 988, 
+    988, 988, 988, 988, 81, 81, 988, 988, 988, 988, 988, 988, 988, 81, 988, 
+    988, 81, 988, 988, 988, 988, 988, 81, 81, 81, 81, 81, 989, 989, 989, 989, 
+    989, 989, 989, 989, 989, 989, 989, 989, 989, 81, 81, 990, 990, 990, 990, 
+    990, 990, 990, 990, 990, 991, 991, 991, 991, 991, 991, 991, 81, 992, 992, 
+    992, 992, 992, 992, 992, 992, 992, 992, 993, 993, 993, 993, 993, 993, 
+    993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 993, 994, 994, 
+    994, 994, 994, 994, 995, 81, 81, 81, 81, 81, 996, 996, 996, 996, 996, 
+    996, 996, 996, 996, 996, 81, 81, 81, 81, 997, 997, 146, 146, 146, 146, 
+    81, 146, 146, 146, 81, 146, 146, 81, 146, 81, 81, 146, 81, 146, 146, 146, 
+    146, 146, 146, 146, 146, 146, 146, 81, 146, 146, 146, 146, 81, 146, 81, 
+    146, 81, 81, 81, 81, 81, 81, 146, 81, 81, 81, 81, 146, 81, 146, 81, 146, 
+    81, 146, 146, 146, 81, 146, 81, 146, 81, 146, 81, 146, 81, 146, 146, 146, 
+    146, 81, 146, 81, 146, 146, 81, 146, 146, 146, 146, 146, 146, 146, 146, 
+    146, 81, 81, 81, 81, 81, 146, 146, 146, 81, 146, 146, 146, 132, 132, 81, 
+    81, 81, 81, 81, 81, 525, 525, 525, 525, 521, 525, 525, 525, 525, 525, 
+    525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 998, 998, 998, 998, 
+    998, 998, 998, 998, 998, 998, 998, 998, 525, 525, 525, 525, 525, 525, 
+    525, 998, 998, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 
+    525, 525, 525, 521, 525, 525, 525, 525, 525, 525, 998, 998, 47, 47, 47, 
+    515, 515, 998, 998, 998, 526, 526, 526, 526, 526, 526, 313, 998, 526, 
+    526, 40, 40, 998, 998, 998, 998, 526, 526, 526, 526, 526, 526, 999, 526, 
+    526, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 526, 526, 526, 
+    526, 526, 526, 526, 526, 526, 526, 998, 998, 998, 998, 998, 998, 998, 
+    998, 998, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 
+    1001, 582, 582, 998, 998, 998, 998, 998, 582, 582, 582, 582, 998, 998, 
+    998, 998, 582, 998, 998, 998, 998, 998, 998, 998, 582, 582, 998, 998, 
+    998, 998, 998, 998, 521, 521, 521, 521, 521, 521, 998, 998, 521, 525, 
+    525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 521, 521, 521, 
+    521, 521, 521, 521, 521, 521, 525, 521, 521, 521, 521, 521, 521, 525, 
+    521, 521, 521, 521, 521, 521, 521, 532, 521, 521, 521, 521, 521, 521, 
+    525, 525, 525, 525, 525, 525, 525, 525, 40, 40, 525, 525, 521, 521, 521, 
+    521, 521, 524, 524, 521, 521, 521, 521, 521, 524, 521, 521, 521, 521, 
+    521, 532, 532, 532, 521, 521, 532, 521, 521, 532, 530, 530, 525, 525, 
+    521, 521, 525, 525, 525, 521, 525, 525, 525, 521, 521, 521, 1002, 1002, 
+    1002, 1002, 1002, 521, 521, 521, 521, 521, 521, 521, 525, 521, 525, 532, 
+    532, 521, 521, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 
+    521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 532, 
+    532, 532, 532, 521, 521, 521, 521, 532, 521, 532, 521, 521, 521, 532, 
+    521, 521, 521, 521, 532, 532, 532, 521, 532, 532, 532, 524, 521, 524, 
+    521, 524, 521, 521, 521, 521, 521, 532, 521, 521, 521, 521, 524, 521, 
+    524, 524, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 525, 525, 
+    521, 524, 524, 524, 524, 524, 524, 524, 521, 521, 521, 521, 521, 521, 
+    521, 521, 524, 524, 524, 524, 524, 524, 521, 521, 521, 521, 521, 524, 
+    524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 40, 40, 40, 40, 
+    525, 521, 521, 521, 521, 525, 525, 525, 525, 525, 530, 530, 525, 525, 
+    525, 525, 532, 525, 525, 525, 525, 525, 530, 525, 525, 525, 525, 532, 
+    532, 525, 525, 525, 525, 525, 40, 40, 40, 40, 40, 40, 40, 40, 525, 525, 
+    525, 525, 40, 40, 525, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 
+    532, 532, 532, 521, 521, 521, 532, 532, 532, 532, 532, 40, 40, 40, 40, 
+    40, 40, 534, 534, 534, 1003, 1003, 1003, 40, 40, 40, 40, 521, 521, 521, 
+    532, 521, 521, 521, 521, 521, 521, 521, 521, 532, 532, 532, 521, 532, 
+    521, 521, 521, 521, 521, 525, 525, 525, 525, 525, 525, 532, 525, 525, 
+    525, 521, 521, 521, 525, 525, 998, 998, 998, 525, 525, 525, 521, 521, 
+    998, 998, 998, 525, 525, 525, 525, 521, 521, 521, 521, 521, 998, 998, 
+    998, 998, 998, 998, 998, 40, 40, 40, 40, 998, 998, 998, 998, 40, 40, 40, 
+    40, 40, 998, 998, 998, 40, 40, 998, 998, 998, 998, 998, 998, 40, 40, 40, 
+    40, 40, 40, 998, 998, 532, 532, 532, 532, 532, 521, 532, 532, 521, 521, 
+    521, 521, 521, 521, 532, 521, 532, 532, 521, 521, 521, 532, 532, 998, 
+    521, 521, 521, 521, 521, 998, 998, 998, 521, 521, 521, 521, 998, 998, 
+    998, 998, 521, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 
+    532, 532, 521, 521, 521, 521, 521, 521, 521, 521, 521, 998, 998, 998, 
+    998, 998, 998, 998, 81, 81, 589, 589, 589, 589, 589, 589, 589, 590, 589, 
+    589, 589, 589, 589, 590, 590, 590, 589, 590, 590, 590, 590, 590, 590, 
+    590, 590, 590, 590, 590, 590, 590, 81, 81, 81, 499, 81, 81, 81, 81, 81, 
+    81, 499, 499, 499, 499, 499, 499, 499, 499, 667, 667, 667, 667, 667, 667, 
+    81, 81, 
 };
 
 /* decomposition data */
diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh
index a4d118b..82bb9a4 100644
--- a/src/hb-unicode-private.hh
+++ b/src/hb-unicode-private.hh
@@ -108,7 +108,7 @@
     /* XXX This hack belongs to the Myanmar shaper. */
     if (unlikely (unicode == 0x1037u)) unicode = 0x103Au;
 
-    /* XXX This hack belongs to the SEA shaper (for Tai Tham):
+    /* 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;
 
@@ -126,7 +126,7 @@
   {
     /* U+180B..180D MONGOLIAN FREE VARIATION SELECTORs are handled in the
      * Arabic shaper.  No need to match them here. */
-    return unlikely (hb_in_ranges (unicode,
+    return unlikely (hb_in_ranges<hb_codepoint_t> (unicode,
 				   0xFE00u, 0xFE0Fu, /* VARIATION SELECTOR-1..16 */
 				   0xE0100u, 0xE01EFu));  /* VARIATION SELECTOR-17..256 */
   }
@@ -137,6 +137,7 @@
    * we do NOT want to hide them, as the way Uniscribe has implemented them
    * is with regular spacing glyphs, and that's the way fonts are made to work.
    * As such, we make exceptions for those four.
+   * Also ignoring U+1BCA0..1BCA3. https://github.com/harfbuzz/harfbuzz/issues/503
    *
    * Unicode 7.0:
    * $ grep '; Default_Ignorable_Code_Point ' DerivedCoreProperties.txt | sed 's/;.*#/#/'
@@ -179,13 +180,13 @@
 	case 0x00: return unlikely (ch == 0x00ADu);
 	case 0x03: return unlikely (ch == 0x034Fu);
 	case 0x06: return unlikely (ch == 0x061Cu);
-	case 0x17: return hb_in_range (ch, 0x17B4u, 0x17B5u);
-	case 0x18: return hb_in_range (ch, 0x180Bu, 0x180Eu);
-	case 0x20: return hb_in_ranges (ch, 0x200Bu, 0x200Fu,
+	case 0x17: return hb_in_range<hb_codepoint_t> (ch, 0x17B4u, 0x17B5u);
+	case 0x18: return hb_in_range<hb_codepoint_t> (ch, 0x180Bu, 0x180Eu);
+	case 0x20: return hb_in_ranges<hb_codepoint_t> (ch, 0x200Bu, 0x200Fu,
 					    0x202Au, 0x202Eu,
 					    0x2060u, 0x206Fu);
-	case 0xFE: return hb_in_range (ch, 0xFE00u, 0xFE0Fu) || ch == 0xFEFFu;
-	case 0xFF: return hb_in_range (ch, 0xFFF0u, 0xFFF8u);
+	case 0xFE: return hb_in_range<hb_codepoint_t> (ch, 0xFE00u, 0xFE0Fu) || ch == 0xFEFFu;
+	case 0xFF: return hb_in_range<hb_codepoint_t> (ch, 0xFFF0u, 0xFFF8u);
 	default: return false;
       }
     }
@@ -193,9 +194,8 @@
     {
       /* Other planes */
       switch (plane) {
-	case 0x01: return hb_in_ranges (ch, 0x1BCA0u, 0x1BCA3u,
-					    0x1D173u, 0x1D17Au);
-	case 0x0E: return hb_in_range (ch, 0xE0000u, 0xE0FFFu);
+	case 0x01: return hb_in_range<hb_codepoint_t> (ch, 0x1D173u, 0x1D17Au);
+	case 0x0E: return hb_in_range<hb_codepoint_t> (ch, 0xE0000u, 0xE0FFFu);
 	default: return false;
       }
     }
@@ -346,23 +346,24 @@
 #define HB_MODIFIED_COMBINING_CLASS_CCC122 122 /* mai * */
 
 /* Tibetan
- * Modify U+0F74 (ccc=132) to reorder before ccc=130 marks.
+ * 
+ * In case of multiple vowel-signs, use u first (but after achung) 
+ * this allows Dzongkha multi-vowel shortcuts to render correctly 
  */
 #define HB_MODIFIED_COMBINING_CLASS_CCC129 129 /* sign aa */
-#define HB_MODIFIED_COMBINING_CLASS_CCC130 130 /* sign i */
-#define HB_MODIFIED_COMBINING_CLASS_CCC132 128 /* sign u */
-
+#define HB_MODIFIED_COMBINING_CLASS_CCC130 132 /* sign i */
+#define HB_MODIFIED_COMBINING_CLASS_CCC132 131 /* sign u */
 
 /* Misc */
 
 #define HB_UNICODE_GENERAL_CATEGORY_IS_MARK(gen_cat) \
-	(FLAG_SAFE (gen_cat) & \
+	(FLAG_UNSAFE (gen_cat) & \
 	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \
 	  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_SAFE (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)))
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index d553a71..726baeb 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -188,7 +188,7 @@
 const hb_unicode_funcs_t _hb_unicode_funcs_nil = {
   HB_OBJECT_HEADER_STATIC,
 
-  NULL, /* parent */
+  nullptr, /* parent */
   true, /* immutable */
   {
 #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil,
@@ -365,7 +365,7 @@
   } else {									\
     ufuncs->func.name = ufuncs->parent->func.name;				\
     ufuncs->user_data.name = ufuncs->parent->user_data.name;			\
-    ufuncs->destroy.name = NULL;						\
+    ufuncs->destroy.name = nullptr;						\
   }										\
 }
 
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 58f983d..cd25769 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -24,6 +24,8 @@
  * Google Author(s): Behdad Esfahbod
  */
 
+#include "hb-private.hh"
+#include "hb-debug.hh"
 #define HB_SHAPER uniscribe
 #include "hb-shaper-impl-private.hh"
 
@@ -38,11 +40,6 @@
 #include "hb-ot-tag.h"
 
 
-#ifndef HB_DEBUG_UNISCRIBE
-#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0)
-#endif
-
-
 static inline uint16_t hb_uint16_swap (const uint16_t v)
 { return (v >> 8) | (v << 8); }
 static inline uint32_t hb_uint32_swap (const uint32_t v)
@@ -202,9 +199,9 @@
   inline void init (void)
   {
     HMODULE hinstLib;
-    this->ScriptItemizeOpenType = NULL;
-    this->ScriptShapeOpenType   = NULL;
-    this->ScriptPlaceOpenType   = NULL;
+    this->ScriptItemizeOpenType = nullptr;
+    this->ScriptShapeOpenType   = nullptr;
+    this->ScriptPlaceOpenType   = nullptr;
 
     hinstLib = GetModuleHandle (TEXT ("usp10.dll"));
     if (hinstLib)
@@ -217,7 +214,7 @@
 	!this->ScriptShapeOpenType   ||
 	!this->ScriptPlaceOpenType)
     {
-      DEBUG_MSG (UNISCRIBE, NULL, "OpenType versions of functions not found; falling back.");
+      DEBUG_MSG (UNISCRIBE, nullptr, "OpenType versions of functions not found; falling back.");
       this->ScriptItemizeOpenType = hb_ScriptItemizeOpenType;
       this->ScriptShapeOpenType   = hb_ScriptShapeOpenType;
       this->ScriptPlaceOpenType   = hb_ScriptPlaceOpenType;
@@ -242,11 +239,11 @@
   {
     funcs = (hb_uniscribe_shaper_funcs_t *) calloc (1, sizeof (hb_uniscribe_shaper_funcs_t));
     if (unlikely (!funcs))
-      return NULL;
+      return nullptr;
 
     funcs->init ();
 
-    if (!hb_atomic_ptr_cmpexch (&uniscribe_funcs, NULL, funcs)) {
+    if (!hb_atomic_ptr_cmpexch (&uniscribe_funcs, nullptr, funcs)) {
       free (funcs);
       goto retry;
     }
@@ -264,7 +261,9 @@
   OPENTYPE_FEATURE_RECORD rec;
   unsigned int order;
 
-  static int cmp (const active_feature_t *a, const active_feature_t *b) {
+  static int cmp (const void *pa, const void *pb) {
+    const active_feature_t *a = (const active_feature_t *) pa;
+    const active_feature_t *b = (const active_feature_t *) pb;
     return a->rec.tagFeature < b->rec.tagFeature ? -1 : a->rec.tagFeature > b->rec.tagFeature ? 1 :
 	   a->order < b->order ? -1 : a->order > b->order ? 1 :
 	   a->rec.lParameter < b->rec.lParameter ? -1 : a->rec.lParameter > b->rec.lParameter ? 1 :
@@ -280,7 +279,9 @@
   bool start;
   active_feature_t feature;
 
-  static int cmp (const feature_event_t *a, const feature_event_t *b) {
+  static int cmp (const void *pa, const void *pb) {
+    const feature_event_t *a = (const feature_event_t *) pa;
+    const feature_event_t *b = (const feature_event_t *) pb;
     return a->index < b->index ? -1 : a->index > b->index ? 1 :
 	   a->start < b->start ? -1 : a->start > b->start ? 1 :
 	   active_feature_t::cmp (&a->feature, &b->feature);
@@ -316,7 +317,7 @@
   const char *enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
   UUID id;
   UuidCreate ((UUID*) &id);
-  ASSERT_STATIC (2 + 3 * (16/2) < LF_FACESIZE);
+  static_assert ((2 + 3 * (16/2) < LF_FACESIZE), "");
   unsigned int name_str_len = 0;
   face_name[name_str_len++] = 'F';
   face_name[name_str_len++] = '_';
@@ -350,7 +351,7 @@
    * full, PS. All of them point to the same name data with our unique name.
    */
 
-  blob = OT::Sanitizer<OT::OpenTypeFontFile>::sanitize (blob);
+  blob = OT::Sanitizer<OT::OpenTypeFontFile>().sanitize (blob);
 
   unsigned int length, new_length, name_str_len;
   const char *orig_sfnt_data = hb_blob_get_data (blob, &length);
@@ -369,7 +370,7 @@
   if (!new_sfnt_data)
   {
     hb_blob_destroy (blob);
-    return NULL;
+    return nullptr;
   }
 
   memcpy(new_sfnt_data, orig_sfnt_data, length);
@@ -417,7 +418,7 @@
     {
       free (new_sfnt_data);
       hb_blob_destroy (blob);
-      return NULL;
+      return nullptr;
     }
   }
 
@@ -427,7 +428,7 @@
 
   hb_blob_destroy (blob);
   return hb_blob_create ((const char *) new_sfnt_data, new_length,
-			 HB_MEMORY_MODE_WRITABLE, NULL, free);
+			 HB_MEMORY_MODE_WRITABLE, nullptr, free);
 }
 
 hb_uniscribe_shaper_face_data_t *
@@ -435,13 +436,13 @@
 {
   hb_uniscribe_shaper_face_data_t *data = (hb_uniscribe_shaper_face_data_t *) calloc (1, sizeof (hb_uniscribe_shaper_face_data_t));
   if (unlikely (!data))
-    return NULL;
+    return nullptr;
 
   data->funcs = hb_uniscribe_shaper_get_funcs ();
   if (unlikely (!data->funcs))
   {
     free (data);
-    return NULL;
+    return nullptr;
   }
 
   hb_blob_t *blob = hb_face_reference_blob (face);
@@ -452,18 +453,18 @@
   if (unlikely (!blob))
   {
     free (data);
-    return NULL;
+    return nullptr;
   }
 
   DWORD num_fonts_installed;
-  data->fh = AddFontMemResourceEx ((void *) hb_blob_get_data (blob, NULL),
+  data->fh = AddFontMemResourceEx ((void *) hb_blob_get_data (blob, nullptr),
 				   hb_blob_get_length (blob),
 				   0, &num_fonts_installed);
   if (unlikely (!data->fh))
   {
     DEBUG_MSG (UNISCRIBE, face, "Face AddFontMemResourceEx() failed");
     free (data);
-    return NULL;
+    return nullptr;
   }
 
   return data;
@@ -495,7 +496,7 @@
 		   unsigned int font_size)
 {
   memset (lf, 0, sizeof (*lf));
-  lf->lfHeight = -font_size;
+  lf->lfHeight = - (int) font_size;
   lf->lfCharSet = DEFAULT_CHARSET;
 
   hb_face_t *face = font->face;
@@ -509,11 +510,11 @@
 hb_uniscribe_shaper_font_data_t *
 _hb_uniscribe_shaper_font_data_create (hb_font_t *font)
 {
-  if (unlikely (!hb_uniscribe_shaper_face_data_ensure (font->face))) return NULL;
+  if (unlikely (!hb_uniscribe_shaper_face_data_ensure (font->face))) return nullptr;
 
   hb_uniscribe_shaper_font_data_t *data = (hb_uniscribe_shaper_font_data_t *) calloc (1, sizeof (hb_uniscribe_shaper_font_data_t));
   if (unlikely (!data))
-    return NULL;
+    return nullptr;
 
   int font_size = font->face->get_upem (); /* Default... */
   /* No idea if the following is even a good idea. */
@@ -525,25 +526,25 @@
   data->x_mult = (double) font->x_scale / font_size;
   data->y_mult = (double) font->y_scale / font_size;
 
-  data->hdc = GetDC (NULL);
+  data->hdc = GetDC (nullptr);
 
   if (unlikely (!populate_log_font (&data->log_font, font, font_size))) {
     DEBUG_MSG (UNISCRIBE, font, "Font populate_log_font() failed");
     _hb_uniscribe_shaper_font_data_destroy (data);
-    return NULL;
+    return nullptr;
   }
 
   data->hfont = CreateFontIndirectW (&data->log_font);
   if (unlikely (!data->hfont)) {
     DEBUG_MSG (UNISCRIBE, font, "Font CreateFontIndirectW() failed");
     _hb_uniscribe_shaper_font_data_destroy (data);
-     return NULL;
+     return nullptr;
   }
 
   if (!SelectObject (data->hdc, data->hfont)) {
     DEBUG_MSG (UNISCRIBE, font, "Font SelectObject() failed");
     _hb_uniscribe_shaper_font_data_destroy (data);
-     return NULL;
+     return nullptr;
   }
 
   return data;
@@ -553,7 +554,7 @@
 _hb_uniscribe_shaper_font_data_destroy (hb_uniscribe_shaper_font_data_t *data)
 {
   if (data->hdc)
-    ReleaseDC (NULL, data->hdc);
+    ReleaseDC (nullptr, data->hdc);
   if (data->hfont)
     DeleteObject (data->hfont);
   if (data->script_cache)
@@ -564,7 +565,7 @@
 LOGFONTW *
 hb_uniscribe_font_get_logfontw (hb_font_t *font)
 {
-  if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return NULL;
+  if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return nullptr;
   hb_uniscribe_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
   return &font_data->log_font;
 }
@@ -572,7 +573,7 @@
 HFONT
 hb_uniscribe_font_get_hfont (hb_font_t *font)
 {
-  if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return NULL;
+  if (unlikely (!hb_uniscribe_shaper_font_data_ensure (font))) return nullptr;
   hb_uniscribe_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
   return font_data->hfont;
 }
@@ -738,7 +739,7 @@
 
 #define FAIL(...) \
   HB_STMT_START { \
-    DEBUG_MSG (UNISCRIBE, NULL, __VA_ARGS__); \
+    DEBUG_MSG (UNISCRIBE, nullptr, __VA_ARGS__); \
     return false; \
   } HB_STMT_END;
 
@@ -854,7 +855,7 @@
   unsigned int glyphs_offset = 0;
   unsigned int glyphs_len;
   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
-  for (unsigned int i = 0; i < item_count; i++)
+  for (int i = 0; i < item_count; i++)
   {
     unsigned int chars_offset = items[i].iCharPos;
     unsigned int item_chars_len = items[i + 1].iCharPos - chars_offset;
@@ -960,7 +961,7 @@
 				     /* out */
 				     advances + glyphs_offset,
 				     offsets + glyphs_offset,
-				     NULL);
+				     nullptr);
     if (unlikely (FAILED (hr)))
       FAIL ("ScriptPlaceOpenType() failed: 0x%08xL", hr);
 
@@ -1029,6 +1030,8 @@
   if (backward)
     hb_buffer_reverse (buffer);
 
+  buffer->unsafe_to_break_all ();
+
   /* Wow, done! */
   return true;
 }
diff --git a/src/hb-utf-private.hh b/src/hb-utf-private.hh
index 74cf5d6..211eb4d 100644
--- a/src/hb-utf-private.hh
+++ b/src/hb-utf-private.hh
@@ -48,7 +48,7 @@
 
     if (c > 0x7Fu)
     {
-      if (hb_in_range (c, 0xC2u, 0xDFu)) /* Two-byte */
+      if (hb_in_range<hb_codepoint_t> (c, 0xC2u, 0xDFu)) /* Two-byte */
       {
 	unsigned int t1;
 	if (likely (text < end &&
@@ -60,7 +60,7 @@
 	else
 	  goto error;
       }
-      else if (hb_in_range (c, 0xE0u, 0xEFu)) /* Three-byte */
+      else if (hb_in_range<hb_codepoint_t> (c, 0xE0u, 0xEFu)) /* Three-byte */
       {
 	unsigned int t1, t2;
 	if (likely (1 < end - text &&
@@ -68,14 +68,14 @@
 		    (t2 = text[1] - 0x80u) <= 0x3Fu))
 	{
 	  c = ((c&0xFu)<<12) | (t1<<6) | t2;
-	  if (unlikely (c < 0x0800u || hb_in_range (c, 0xD800u, 0xDFFFu)))
+	  if (unlikely (c < 0x0800u || hb_in_range<hb_codepoint_t> (c, 0xD800u, 0xDFFFu)))
 	    goto error;
 	  text += 2;
 	}
 	else
 	  goto error;
       }
-      else if (hb_in_range (c, 0xF0u, 0xF4u)) /* Four-byte */
+      else if (hb_in_range<hb_codepoint_t> (c, 0xF0u, 0xF4u)) /* Four-byte */
       {
 	unsigned int t1, t2, t3;
 	if (likely (2 < end - text &&
@@ -84,7 +84,7 @@
 		    (t3 = text[2] - 0x80u) <= 0x3Fu))
 	{
 	  c = ((c&0x7u)<<18) | (t1<<12) | (t2<<6) | t3;
-	  if (unlikely (!hb_in_range (c, 0x10000u, 0x10FFFFu)))
+	  if (unlikely (!hb_in_range<hb_codepoint_t> (c, 0x10000u, 0x10FFFFu)))
 	    goto error;
 	  text += 3;
 	}
@@ -140,7 +140,7 @@
   {
     hb_codepoint_t c = *text++;
 
-    if (likely (!hb_in_range (c, 0xD800u, 0xDFFFu)))
+    if (likely (!hb_in_range<hb_codepoint_t> (c, 0xD800u, 0xDFFFu)))
     {
       *unicode = c;
       return text;
@@ -150,7 +150,7 @@
     {
       /* High-surrogate in c */
       hb_codepoint_t l = *text;
-      if (likely (hb_in_range (l, 0xDC00u, 0xDFFFu)))
+      if (likely (hb_in_range<hb_codepoint_t> (l, 0xDC00u, 0xDFFFu)))
       {
 	/* Low-surrogate in l */
 	*unicode = (c << 10) + l - ((0xD800u << 10) - 0x10000u + 0xDC00u);
@@ -172,7 +172,7 @@
   {
     hb_codepoint_t c = *--text;
 
-    if (likely (!hb_in_range (c, 0xD800u, 0xDFFFu)))
+    if (likely (!hb_in_range<hb_codepoint_t> (c, 0xD800u, 0xDFFFu)))
     {
       *unicode = c;
       return text;
@@ -182,7 +182,7 @@
     {
       /* Low-surrogate in c */
       hb_codepoint_t h = text[-1];
-      if (likely (hb_in_range (h, 0xD800u, 0xDBFFu)))
+      if (likely (hb_in_range<hb_codepoint_t> (h, 0xD800u, 0xDBFFu)))
       {
         /* High-surrogate in h */
         *unicode = (h << 10) + c - ((0xD800u << 10) - 0x10000u + 0xDC00u);
diff --git a/src/main.cc b/src/main.cc
index f9708cc..d221e9d 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -24,7 +24,6 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#include "hb-mutex-private.hh"
 #include "hb-open-file-private.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsubgpos-private.hh"
@@ -38,6 +37,9 @@
 
 using namespace OT;
 
+#ifndef HB_NO_VISIBILITY
+const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
+#endif
 
 int
 main (int argc, char **argv)
@@ -47,11 +49,11 @@
     exit (1);
   }
 
-  const char *font_data = NULL;
+  const char *font_data = nullptr;
   int len = 0;
 
 #ifdef HAVE_GLIB
-  GMappedFile *mf = g_mapped_file_new (argv[1], false, NULL);
+  GMappedFile *mf = g_mapped_file_new (argv[1], false, nullptr);
   font_data = g_mapped_file_get_contents (mf);
   len = g_mapped_file_get_length (mf);
 #else
@@ -105,8 +107,8 @@
 
       switch (table.tag) {
 
-      case GSUBGPOS::GSUBTag:
-      case GSUBGPOS::GPOSTag:
+      case HB_OT_TAG_GSUB:
+      case HB_OT_TAG_GPOS:
 	{
 
 	const GSUBGPOS &g = *CastP<GSUBGPOS> (font_data + table.offset);
diff --git a/src/sample.py b/src/sample.py
index c2cb94d..844fa4c 100755
--- a/src/sample.py
+++ b/src/sample.py
@@ -45,7 +45,7 @@
 ## Add text to buffer
 ##
 #
-# See https://github.com/behdad/harfbuzz/pull/271
+# See https://github.com/harfbuzz/harfbuzz/pull/271
 #
 if False:
 	# If you do not care about cluster values reflecting Python
diff --git a/src/test-buffer-serialize.cc b/src/test-buffer-serialize.cc
index 18c46e9..636b003 100644
--- a/src/test-buffer-serialize.cc
+++ b/src/test-buffer-serialize.cc
@@ -24,11 +24,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include "hb-private.hh"
 
 #include "hb.h"
+#include "hb-ot.h"
 #ifdef HAVE_FREETYPE
 #include "hb-ft.h"
 #endif
@@ -45,7 +44,7 @@
 int
 main (int argc, char **argv)
 {
-  hb_blob_t *blob = NULL;
+  hb_blob_t *blob = nullptr;
 
   if (argc != 2) {
     fprintf (stderr, "usage: %s font-file\n", argv[0]);
@@ -61,7 +60,7 @@
     hb_memory_mode_t mm;
 
 #ifdef HAVE_GLIB
-    GMappedFile *mf = g_mapped_file_new (argv[1], false, NULL);
+    GMappedFile *mf = g_mapped_file_new (argv[1], false, nullptr);
     font_data = g_mapped_file_get_contents (mf);
     len = g_mapped_file_get_length (mf);
     destroy = (hb_destroy_func_t) g_mapped_file_unref;
@@ -86,14 +85,15 @@
 
   hb_face_t *face = hb_face_create (blob, 0 /* first face */);
   hb_blob_destroy (blob);
-  blob = NULL;
+  blob = nullptr;
 
   unsigned int upem = hb_face_get_upem (face);
   hb_font_t *font = hb_font_create (face);
   hb_face_destroy (face);
   hb_font_set_scale (font, upem, upem);
+  hb_ot_font_set_funcs (font);
 #ifdef HAVE_FREETYPE
-  hb_ft_font_set_funcs (font);
+  //hb_ft_font_set_funcs (font);
 #endif
 
   hb_buffer_t *buf;
@@ -101,7 +101,7 @@
 
   bool ret = true;
   char line[BUFSIZ], out[BUFSIZ];
-  while (fgets (line, sizeof(line), stdin) != 0)
+  while (fgets (line, sizeof(line), stdin) != nullptr)
   {
     hb_buffer_clear_contents (buf);
 
@@ -115,7 +115,7 @@
       ret = false;
 
     hb_buffer_serialize_glyphs (buf, 0, hb_buffer_get_length (buf),
-				out, sizeof (out), NULL,
+				out, sizeof (out), nullptr,
 				font, HB_BUFFER_SERIALIZE_FORMAT_JSON,
 				HB_BUFFER_SERIALIZE_FLAG_DEFAULT);
     puts (out);
diff --git a/src/test-size-params.cc b/src/test-size-params.cc
index 35d9e3c..9741b87 100644
--- a/src/test-size-params.cc
+++ b/src/test-size-params.cc
@@ -24,9 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include "hb-private.hh"
 
 #include "hb.h"
 #include "hb-ot.h"
@@ -43,7 +41,7 @@
 int
 main (int argc, char **argv)
 {
-  hb_blob_t *blob = NULL;
+  hb_blob_t *blob = nullptr;
 
   if (argc != 2) {
     fprintf (stderr, "usage: %s font-file\n", argv[0]);
@@ -59,7 +57,7 @@
     hb_memory_mode_t mm;
 
 #ifdef HAVE_GLIB
-    GMappedFile *mf = g_mapped_file_new (argv[1], false, NULL);
+    GMappedFile *mf = g_mapped_file_new (argv[1], false, nullptr);
     font_data = g_mapped_file_get_contents (mf);
     len = g_mapped_file_get_length (mf);
     destroy = (hb_destroy_func_t) g_mapped_file_unref;
@@ -85,7 +83,7 @@
   /* Create the face */
   hb_face_t *face = hb_face_create (blob, 0 /* first face */);
   hb_blob_destroy (blob);
-  blob = NULL;
+  blob = nullptr;
 
   unsigned int p[5];
   bool ret = hb_ot_layout_get_size_params (face, p, p+1, p+2, p+3, p+4);
diff --git a/src/test-would-substitute.cc b/src/test-would-substitute.cc
index 8ea87cd..efebf2d 100644
--- a/src/test-would-substitute.cc
+++ b/src/test-would-substitute.cc
@@ -24,9 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include "hb-private.hh"
 
 #include "hb.h"
 #include "hb-ot.h"
@@ -47,7 +45,7 @@
 int
 main (int argc, char **argv)
 {
-  hb_blob_t *blob = NULL;
+  hb_blob_t *blob = nullptr;
 
   if (argc != 4 && argc != 5) {
     fprintf (stderr, "usage: %s font-file lookup-index first-glyph [second-glyph]\n", argv[0]);
@@ -63,7 +61,7 @@
     hb_memory_mode_t mm;
 
 #ifdef HAVE_GLIB
-    GMappedFile *mf = g_mapped_file_new (argv[1], false, NULL);
+    GMappedFile *mf = g_mapped_file_new (argv[1], false, nullptr);
     font_data = g_mapped_file_get_contents (mf);
     len = g_mapped_file_get_length (mf);
     destroy = (hb_destroy_func_t) g_mapped_file_unref;
@@ -89,7 +87,7 @@
   /* Create the face */
   hb_face_t *face = hb_face_create (blob, 0 /* first face */);
   hb_blob_destroy (blob);
-  blob = NULL;
+  blob = nullptr;
 
   hb_font_t *font = hb_font_create (face);
 #ifdef HAVE_FREETYPE
@@ -102,5 +100,5 @@
       (argc > 4 &&
        !hb_font_glyph_from_string (font, argv[4], -1, &glyphs[1])))
     return 2;
-  return !hb_ot_layout_lookup_would_substitute (face, strtol (argv[2], NULL, 0), glyphs, len, false);
+  return !hb_ot_layout_lookup_would_substitute (face, strtol (argv[2], nullptr, 0), glyphs, len, false);
 }
diff --git a/src/test.cc b/src/test.cc
index 0c90f8f..9592b37 100644
--- a/src/test.cc
+++ b/src/test.cc
@@ -24,9 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include "hb-private.hh"
 
 #include "hb.h"
 
@@ -46,7 +44,7 @@
 int
 main (int argc, char **argv)
 {
-  hb_blob_t *blob = NULL;
+  hb_blob_t *blob = nullptr;
 
   if (argc != 2) {
     fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]);
@@ -62,7 +60,7 @@
     hb_memory_mode_t mm;
 
 #ifdef HAVE_GLIB
-    GMappedFile *mf = g_mapped_file_new (argv[1], false, NULL);
+    GMappedFile *mf = g_mapped_file_new (argv[1], false, nullptr);
     font_data = g_mapped_file_get_contents (mf);
     len = g_mapped_file_get_length (mf);
     destroy = (hb_destroy_func_t) g_mapped_file_unref;
@@ -90,7 +88,7 @@
   /* Create the face */
   hb_face_t *face = hb_face_create (blob, 0 /* first face */);
   hb_blob_destroy (blob);
-  blob = NULL;
+  blob = nullptr;
   unsigned int upem = hb_face_get_upem (face);
 
   hb_font_t *font = hb_font_create (face);
@@ -105,11 +103,11 @@
   hb_buffer_add_utf8 (buffer, "\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x8d\xe0\xa4\x95", -1, 0, -1);
   hb_buffer_guess_segment_properties (buffer);
 
-  hb_shape (font, buffer, NULL, 0);
+  hb_shape (font, buffer, nullptr, 0);
 
   unsigned int count = hb_buffer_get_length (buffer);
-  hb_glyph_info_t *infos = hb_buffer_get_glyph_infos (buffer, NULL);
-  hb_glyph_position_t *positions = hb_buffer_get_glyph_positions (buffer, NULL);
+  hb_glyph_info_t *infos = hb_buffer_get_glyph_infos (buffer, nullptr);
+  hb_glyph_position_t *positions = hb_buffer_get_glyph_positions (buffer, nullptr);
 
   for (unsigned int i = 0; i < count; i++)
   {
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..d2b1994
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_subdirectory(api)
+add_subdirectory(shaping)
+add_subdirectory(subset)
+add_subdirectory(fuzzing)
diff --git a/test/Makefile.am b/test/Makefile.am
index ec77822..66b3e6e 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,6 +1,12 @@
 # Process this file with automake to produce Makefile.in
 
-SUBDIRS = api shaping fuzzing
+NULL =
+EXTRA_DIST =
+SUBDIRS = api shaping fuzzing subset
+
+EXTRA_DIST += \
+	CMakeLists.txt \
+	$(NULL)
 
 # Convenience targets:
 lib:
diff --git a/test/api/CMakeLists.txt b/test/api/CMakeLists.txt
new file mode 100644
index 0000000..6a62b65
--- /dev/null
+++ b/test/api/CMakeLists.txt
@@ -0,0 +1,29 @@
+if (HB_HAVE_GLIB AND NOT HB_DISABLE_TEST_PROGS)
+  file (READ "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.am" MAKEFILEAM)
+  extract_make_variable (TEST_PROGS ${MAKEFILEAM})
+
+  list (APPEND TEST_PROGS
+    test-ot-tag
+    test-c
+    test-cplusplus
+  )
+
+  if (HB_HAVE_FREETYPE)
+    list (APPEND TEST_PROGS test-ot-math)
+  endif ()
+
+  foreach (test_name IN ITEMS ${TEST_PROGS})
+    if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${test_name}.c)
+      add_executable (${test_name} ${test_name}.c)
+    elseif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${test_name}.cc)
+      add_executable (${test_name} ${test_name}.cc)
+    else ()
+      message (FATAL_ERROR "No source file found for test ${test_name}")
+    endif ()
+    target_link_libraries (${test_name} harfbuzz harfbuzz-subset)
+    add_test (${test_name} ${test_name})
+  endforeach ()
+  set_tests_properties (${TEST_PROGS} PROPERTIES ENVIRONMENT
+    "G_TEST_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR};G_TEST_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}"
+  )
+endif ()
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index 530bf3e..e2e6166 100644
--- a/test/api/Makefile.am
+++ b/test/api/Makefile.am
@@ -10,11 +10,15 @@
 lib:
 	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
 
+EXTRA_DIST += CMakeLists.txt
+
+EXTRA_DIST += fonts
+
 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)
 
-EXTRA_DIST += hb-test.h
+EXTRA_DIST += hb-test.h hb-subset-test.h
 
 check_PROGRAMS = $(TEST_PROGS)
 noinst_PROGRAMS = $(TEST_PROGS)
@@ -27,15 +31,27 @@
 	test-object \
 	test-set \
 	test-shape \
+	test-subset-cmap \
+	test-subset-glyf \
+	test-subset-hdmx \
+	test-subset-hmtx \
+	test-subset-os2 \
 	test-unicode \
 	test-version \
 	$(NULL)
 
-test_unicode_CPPFLAGS = $(AM_CPPFLAGS)
+test_subset_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
+test_subset_cmap_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
+test_subset_glyf_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
+test_subset_hdmx_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
+test_subset_hmtx_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
+test_subset_os2_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
+
+test_unicode_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	$(GLIB_CFLAGS) \
+	$(NULL)
 test_unicode_LDADD = $(LDADD)
-if HAVE_GLIB
-test_unicode_CPPFLAGS += $(GLIB_CFLAGS)
-endif
 if HAVE_ICU
 test_unicode_CPPFLAGS += $(ICU_CFLAGS)
 test_unicode_LDADD += $(top_builddir)/src/libharfbuzz-icu.la $(ICU_LIBS)
@@ -54,15 +70,6 @@
 	$(NULL)
 test_ot_math_LDADD = $(LDADD) $(FREETYPE_LIBS)
 test_ot_math_CPPFLAGS = $(AM_CPPFLAGS) $(FREETYPE_CFLAGS)
-EXTRA_DIST += \
-	fonts/MathTestFontEmpty.otf \
-	fonts/MathTestFontFull.otf \
-	fonts/MathTestFontNone.otf \
-	fonts/MathTestFontPartial1.otf \
-	fonts/MathTestFontPartial2.otf \
-	fonts/MathTestFontPartial3.otf \
-	fonts/MathTestFontPartial4.otf \
-	$(NULL)
 endif # HAVE_FREETYPE
 
 endif # HAVE_OT
@@ -149,12 +156,12 @@
 
 
 
-else
+else # !HAVE_GLIB
 check-am: err-glib
 err-glib:
 	@echo "You need to have glib support enabled to run the tests"
 	@exit 77
-endif
+endif # HAVE_GLIB
 
 .PHONY: check-symbols check-tool check-valgrind
 
diff --git a/test/api/fonts/Inconsolata-Regular.ab.ttf b/test/api/fonts/Inconsolata-Regular.ab.ttf
new file mode 100644
index 0000000..455cc15
--- /dev/null
+++ b/test/api/fonts/Inconsolata-Regular.ab.ttf
Binary files differ
diff --git a/test/api/fonts/Inconsolata-Regular.abc.ttf b/test/api/fonts/Inconsolata-Regular.abc.ttf
new file mode 100644
index 0000000..34cf051
--- /dev/null
+++ b/test/api/fonts/Inconsolata-Regular.abc.ttf
Binary files differ
diff --git a/test/api/fonts/Inconsolata-Regular.abc.widerc.ttf b/test/api/fonts/Inconsolata-Regular.abc.widerc.ttf
new file mode 100644
index 0000000..352f269
--- /dev/null
+++ b/test/api/fonts/Inconsolata-Regular.abc.widerc.ttf
Binary files differ
diff --git a/test/api/fonts/Inconsolata-Regular.ac.ttf b/test/api/fonts/Inconsolata-Regular.ac.ttf
new file mode 100644
index 0000000..991b8de
--- /dev/null
+++ b/test/api/fonts/Inconsolata-Regular.ac.ttf
Binary files differ
diff --git a/test/api/fonts/Inconsolata-Regular.ac.widerc.ttf b/test/api/fonts/Inconsolata-Regular.ac.widerc.ttf
new file mode 100644
index 0000000..2050c28
--- /dev/null
+++ b/test/api/fonts/Inconsolata-Regular.ac.widerc.ttf
Binary files differ
diff --git a/test/api/fonts/README b/test/api/fonts/README
new file mode 100644
index 0000000..7e7783c
--- /dev/null
+++ b/test/api/fonts/README
@@ -0,0 +1,3 @@
+cmap-format12-only files created by ttx & remove all other cmap entries
+
+Inconsolata-Regular.abc.widerc.ttf has the hmtx width of "c" set to 600; everything else is 500. Subsetting out c should reduce numberOfHMetrics to 1.
diff --git a/test/api/fonts/Roboto-Regular.abc.cmap-format12-only.ttf b/test/api/fonts/Roboto-Regular.abc.cmap-format12-only.ttf
new file mode 100644
index 0000000..46b4801
--- /dev/null
+++ b/test/api/fonts/Roboto-Regular.abc.cmap-format12-only.ttf
Binary files differ
diff --git a/test/api/fonts/Roboto-Regular.abc.ttf b/test/api/fonts/Roboto-Regular.abc.ttf
new file mode 100644
index 0000000..9d791f7
--- /dev/null
+++ b/test/api/fonts/Roboto-Regular.abc.ttf
Binary files differ
diff --git a/test/api/fonts/Roboto-Regular.ac.cmap-format12-only.ttf b/test/api/fonts/Roboto-Regular.ac.cmap-format12-only.ttf
new file mode 100644
index 0000000..47c9faf
--- /dev/null
+++ b/test/api/fonts/Roboto-Regular.ac.cmap-format12-only.ttf
Binary files differ
diff --git a/test/api/fonts/Roboto-Regular.ac.ttf b/test/api/fonts/Roboto-Regular.ac.ttf
new file mode 100644
index 0000000..6ac1b33
--- /dev/null
+++ b/test/api/fonts/Roboto-Regular.ac.ttf
Binary files differ
diff --git a/test/api/fonts/Roboto-Regular.b.ttf b/test/api/fonts/Roboto-Regular.b.ttf
new file mode 100644
index 0000000..8e44886
--- /dev/null
+++ b/test/api/fonts/Roboto-Regular.b.ttf
Binary files differ
diff --git a/test/api/fonts/Roboto-Regular.components.subset.ttf b/test/api/fonts/Roboto-Regular.components.subset.ttf
new file mode 100644
index 0000000..e759d77
--- /dev/null
+++ b/test/api/fonts/Roboto-Regular.components.subset.ttf
Binary files differ
diff --git a/test/api/fonts/Roboto-Regular.components.ttf b/test/api/fonts/Roboto-Regular.components.ttf
new file mode 100644
index 0000000..816e3a2
--- /dev/null
+++ b/test/api/fonts/Roboto-Regular.components.ttf
Binary files differ
diff --git a/test/api/hb-subset-test.h b/test/api/hb-subset-test.h
new file mode 100644
index 0000000..12f7868
--- /dev/null
+++ b/test/api/hb-subset-test.h
@@ -0,0 +1,132 @@
+/*
+ * 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
+ */
+
+#ifndef HB_SUBSET_TEST_H
+#define HB_SUBSET_TEST_H
+
+#include <stdio.h>
+
+#include "hb-test.h"
+#include "hb-subset.h"
+
+
+HB_BEGIN_DECLS
+
+static inline char *
+hb_subset_test_read_file (const char *path,
+                          size_t     *length /* OUT */)
+{
+  FILE *fp = fopen (path, "rb");
+
+  long file_length = 0;
+  char *buffer = NULL;
+  if (fp && fseek (fp, 0, SEEK_END) == 0)
+  {
+    file_length = ftell(fp);
+    rewind (fp);
+  }
+
+  if (file_length > 0)
+  {
+    buffer = (char *) calloc (file_length + 1, sizeof (char));
+    if (buffer && fread (buffer, 1, file_length, fp) == (size_t) file_length)
+    {
+      *length = file_length;
+    }
+    else
+    {
+      free (buffer);
+      buffer = NULL;
+    }
+  }
+
+  if (fp)
+    fclose(fp);
+
+  return buffer;
+}
+
+static inline hb_face_t *
+hb_subset_test_open_font (const char *font_path)
+{
+#if GLIB_CHECK_VERSION(2,37,2)
+  gchar* path = g_test_build_filename(G_TEST_DIST, font_path, NULL);
+#else
+  gchar* path = g_strdup(fontFile);
+#endif
+
+  size_t length;
+  char *font_data = hb_subset_test_read_file(path, &length);
+
+  if (font_data != NULL) {
+    hb_blob_t *blob = hb_blob_create (font_data,
+                                      length,
+                                      HB_MEMORY_MODE_READONLY,
+                                      font_data,
+                                      free);
+    hb_face_t *face = hb_face_create (blob, 0);
+    hb_blob_destroy (blob);
+    return face;
+  }
+  g_assert (false);
+  return NULL; /* Shut up, compiler! */
+}
+
+static inline hb_face_t *
+hb_subset_test_create_subset (hb_face_t *source,
+                              const hb_set_t  *codepoints)
+{
+  hb_subset_profile_t *profile = hb_subset_profile_create();
+  hb_subset_input_t *input = hb_subset_input_create_or_fail ();
+
+  hb_set_t * input_codepoints = hb_subset_input_unicode_set (input);
+
+  hb_set_union (input_codepoints, codepoints);
+
+  hb_face_t *subset = hb_subset (source, profile, input);
+  g_assert (subset);
+
+  hb_subset_profile_destroy (profile);
+  hb_subset_input_destroy (input);
+  return subset;
+}
+
+static inline void
+hb_subset_test_check (hb_face_t *expected,
+                      hb_face_t *actual,
+                      hb_tag_t   table)
+{
+  hb_blob_t *expected_blob = hb_face_reference_table (expected, table);
+  hb_blob_t *actual_blob = hb_face_reference_table (actual, table);
+  hb_test_assert_blobs_equal (expected_blob, actual_blob);
+  hb_blob_destroy (expected_blob);
+  hb_blob_destroy (actual_blob);
+}
+
+
+HB_END_DECLS
+
+#endif /* HB_SUBSET_TEST_H */
diff --git a/test/api/hb-test.h b/test/api/hb-test.h
index 4d41218..a88b00c 100644
--- a/test/api/hb-test.h
+++ b/test/api/hb-test.h
@@ -27,7 +27,9 @@
 #ifndef HB_TEST_H
 #define HB_TEST_H
 
+#ifdef HAVE_CONFIG_H
 #include <config.h>
+#endif
 
 #include <hb-glib.h>
 
@@ -158,6 +160,21 @@
 #if !GLIB_CHECK_VERSION(2,30,0)
 #define g_test_fail() g_error("Test failed")
 #endif
+#ifndef g_assert_true
+#define g_assert_true g_assert
+#endif
+#ifndef g_assert_cmpmem
+#define g_assert_cmpmem(m1, l1, m2, l2) g_assert_true (l1 == l2 && memcmp (m1, m2, l1) == 0)
+#endif
+
+static inline void hb_test_assert_blobs_equal (hb_blob_t *expected_blob, hb_blob_t *actual_blob)
+{
+  unsigned int expected_length, actual_length;
+  const char *raw_expected = hb_blob_get_data (expected_blob, &expected_length);
+  const char *raw_actual = hb_blob_get_data (actual_blob, &actual_length);
+  g_assert_cmpint(expected_length, ==, actual_length);
+  g_assert_cmpint(0, ==, memcmp(raw_expected, raw_actual, expected_length));
+}
 
 static inline void
 hb_test_add_func (const char *test_path,
diff --git a/test/api/test-blob.c b/test/api/test-blob.c
index f671331..d566f4e 100644
--- a/test/api/test-blob.c
+++ b/test/api/test-blob.c
@@ -116,7 +116,7 @@
 static uintptr_t
 get_pagesize (void)
 {
-  uintptr_t pagesize = -1;
+  uintptr_t pagesize = (uintptr_t) -1;
 
 #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
   pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);
diff --git a/test/api/test-buffer.c b/test/api/test-buffer.c
index 17607f1..b9a0268 100644
--- a/test/api/test-buffer.c
+++ b/test/api/test-buffer.c
@@ -196,7 +196,7 @@
   g_assert_cmpint (len, ==, 5);
 
   for (i = 0; i < len; i++) {
-    g_assert_cmphex (glyphs[i].mask,      ==, 1);
+    g_assert_cmphex (glyphs[i].mask,      ==, 0);
     g_assert_cmphex (glyphs[i].var1.u32,  ==, 0);
     g_assert_cmphex (glyphs[i].var2.u32,  ==, 0);
   }
@@ -369,10 +369,10 @@
 
 /* note: we skip the first and last byte when adding to buffer */
 static const utf8_conversion_test_t utf8_conversion_tests[] = {
-  {"a\303\207", {-1}},
+  {"a\303\207", {(hb_codepoint_t) -1}},
   {"a\303\207b", {0xC7}},
-  {"ab\303cd", {'b', -1, 'c'}},
-  {"ab\303\302\301cd", {'b', -1, -1, -1, 'c'}}
+  {"ab\303cd", {'b', (hb_codepoint_t) -1, 'c'}},
+  {"ab\303\302\301cd", {'b', (hb_codepoint_t) -1, (hb_codepoint_t) -1, (hb_codepoint_t) -1, 'c'}}
 };
 
 static void
@@ -715,10 +715,10 @@
 static const utf16_conversion_test_t utf16_conversion_tests[] = {
   {{0x41, 0x004D, 0x0430, 0x4E8C, 0xD800, 0xDF02, 0x61} , {0x004D, 0x0430, 0x4E8C, 0x10302}},
   {{0x41, 0xD800, 0xDF02, 0x61}, {0x10302}},
-  {{0x41, 0xD800, 0xDF02}, {-1}},
-  {{0x41, 0x61, 0xD800, 0xDF02}, {0x61, -1}},
-  {{0x41, 0xD800, 0x61, 0xDF02}, {-1, 0x61}},
-  {{0x41, 0xDF00, 0x61}, {-1}},
+  {{0x41, 0xD800, 0xDF02}, {(hb_codepoint_t) -1}},
+  {{0x41, 0x61, 0xD800, 0xDF02}, {0x61, (hb_codepoint_t) -1}},
+  {{0x41, 0xD800, 0x61, 0xDF02}, {(hb_codepoint_t) -1, 0x61}},
+  {{0x41, 0xDF00, 0x61}, {(hb_codepoint_t) -1}},
   {{0x41, 0x61}, {0}}
 };
 
@@ -764,15 +764,15 @@
 
 /* note: we skip the first and last item from utf32 when adding to buffer */
 static const utf32_conversion_test_t utf32_conversion_tests[] = {
-  {{0x41, 0x004D, 0x0430, 0x4E8C, 0xD800, 0xDF02, 0x61} , {0x004D, 0x0430, 0x4E8C, -3, -3}},
+  {{0x41, 0x004D, 0x0430, 0x4E8C, 0xD800, 0xDF02, 0x61} , {0x004D, 0x0430, 0x4E8C, (hb_codepoint_t) -3, (hb_codepoint_t) -3}},
   {{0x41, 0x004D, 0x0430, 0x4E8C, 0x10302, 0x61} , {0x004D, 0x0430, 0x4E8C, 0x10302}},
-  {{0x41, 0xD800, 0xDF02, 0x61}, {-3, -3}},
-  {{0x41, 0xD800, 0xDF02}, {-3}},
-  {{0x41, 0x61, 0xD800, 0xDF02}, {0x61, -3}},
-  {{0x41, 0xD800, 0x61, 0xDF02}, {-3, 0x61}},
-  {{0x41, 0xDF00, 0x61}, {-3}},
+  {{0x41, 0xD800, 0xDF02, 0x61}, {(hb_codepoint_t) -3, (hb_codepoint_t) -3}},
+  {{0x41, 0xD800, 0xDF02}, {(hb_codepoint_t) -3}},
+  {{0x41, 0x61, 0xD800, 0xDF02}, {0x61, (hb_codepoint_t) -3}},
+  {{0x41, 0xD800, 0x61, 0xDF02}, {(hb_codepoint_t) -3, 0x61}},
+  {{0x41, 0xDF00, 0x61}, {(hb_codepoint_t) -3}},
   {{0x41, 0x10FFFF, 0x61}, {0x10FFFF}},
-  {{0x41, 0x110000, 0x61}, {-3}},
+  {{0x41, 0x110000, 0x61}, {(hb_codepoint_t) -3}},
   {{0x41, 0x61}, {0}}
 };
 
diff --git a/test/api/test-c.c b/test/api/test-c.c
index 6e8602f..4b43b83 100644
--- a/test/api/test-c.c
+++ b/test/api/test-c.c
@@ -27,7 +27,9 @@
 /* This file tests that all headers can be included from .c files */
 
 
+#ifdef HAVE_CONFIG_H
 #include <config.h>
+#endif
 
 #include <hb.h>
 
diff --git a/test/api/test-ot-tag.c b/test/api/test-ot-tag.c
index f5cbd9d..6fe79b4 100644
--- a/test/api/test-ot-tag.c
+++ b/test/api/test-ot-tag.c
@@ -37,7 +37,7 @@
 test_simple_tags (const char *s, hb_script_t script)
 {
   hb_script_t tag;
-  hb_script_t t1, t2;
+  hb_tag_t t1, t2;
 
   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);
@@ -72,7 +72,7 @@
 static void
 test_ot_tag_script_degenerate (void)
 {
-  hb_script_t t1, t2;
+  hb_tag_t t1, t2;
 
   g_assert_cmphex (HB_TAG_CHAR4 ("DFLT"), ==, HB_OT_TAG_DEFAULT_SCRIPT);
 
diff --git a/test/api/test-set.c b/test/api/test-set.c
index 9634951..60e11d9 100644
--- a/test/api/test-set.c
+++ b/test/api/test-set.c
@@ -32,25 +32,33 @@
 static void
 test_empty (hb_set_t *s)
 {
-  hb_codepoint_t next = HB_SET_VALUE_INVALID;
+  hb_codepoint_t next;
   g_assert_cmpint (hb_set_get_population (s), ==, 0);
   g_assert_cmpint (hb_set_get_min (s), ==, HB_SET_VALUE_INVALID);
   g_assert_cmpint (hb_set_get_max (s), ==, HB_SET_VALUE_INVALID);
   g_assert (!hb_set_has (s, 13));
+  next = 53043;
   g_assert (!hb_set_next (s, &next));
   g_assert_cmpint (next, ==, HB_SET_VALUE_INVALID);
+  next = 07734;
+  g_assert (!hb_set_previous (s, &next));
+  g_assert_cmpint (next, ==, HB_SET_VALUE_INVALID);
   g_assert (hb_set_is_empty (s));
 }
 
 static void
 test_not_empty (hb_set_t *s)
 {
-  hb_codepoint_t next = HB_SET_VALUE_INVALID;
+  hb_codepoint_t next;
   g_assert_cmpint (hb_set_get_population (s), !=, 0);
   g_assert_cmpint (hb_set_get_min (s), !=, HB_SET_VALUE_INVALID);
   g_assert_cmpint (hb_set_get_max (s), !=, HB_SET_VALUE_INVALID);
+  next = HB_SET_VALUE_INVALID;
   g_assert (hb_set_next (s, &next));
   g_assert_cmpint (next, !=, HB_SET_VALUE_INVALID);
+  next = HB_SET_VALUE_INVALID;
+  g_assert (hb_set_previous (s, &next));
+  g_assert_cmpint (next, !=, HB_SET_VALUE_INVALID);
 }
 
 static void
@@ -76,12 +84,6 @@
   g_assert_cmpint (hb_set_get_min (s), ==, 10);
   g_assert_cmpint (hb_set_get_max (s), ==, 29);
 
-  hb_set_invert (s);
-  test_not_empty (s);
-  g_assert (!hb_set_has (s, 13));
-  g_assert_cmpint (hb_set_get_min (s), ==, 0);
-
-  hb_set_invert (s);
   test_not_empty (s);
   g_assert (hb_set_has (s, 13));
   g_assert_cmpint (hb_set_get_population (s), ==, 20);
@@ -92,9 +94,43 @@
   test_not_empty (s);
   g_assert (!hb_set_has (s, 13));
 
+  hb_set_add_range (s, 200, 800);
+  test_not_empty (s);
+  g_assert (!hb_set_has (s, 100));
+  g_assert (!hb_set_has (s, 199));
+  g_assert (hb_set_has (s, 200));
+  g_assert (hb_set_has (s, 201));
+  g_assert (hb_set_has (s, 243));
+  g_assert (hb_set_has (s, 254));
+  g_assert (hb_set_has (s, 255));
+  g_assert (hb_set_has (s, 256));
+  g_assert (hb_set_has (s, 257));
+  g_assert (hb_set_has (s, 511));
+  g_assert (hb_set_has (s, 512));
+  g_assert (hb_set_has (s, 600));
+  g_assert (hb_set_has (s, 767));
+  g_assert (hb_set_has (s, 768));
+  g_assert (hb_set_has (s, 769));
+  g_assert (hb_set_has (s, 782));
+  g_assert (hb_set_has (s, 798));
+  g_assert (hb_set_has (s, 799));
+  g_assert (hb_set_has (s, 800));
+  g_assert (!hb_set_has (s, 801));
+  g_assert (!hb_set_has (s, 802));
+
   hb_set_destroy (s);
 }
 
+static inline void
+print_set (hb_set_t *s)
+{
+  hb_codepoint_t next;
+  printf ("{");
+  for (next = HB_SET_VALUE_INVALID; hb_set_next (s, &next); )
+    printf ("%d, ", next);
+  printf ("}\n");
+}
+
 static void
 test_set_algebra (void)
 {
@@ -155,7 +191,56 @@
   g_assert (!hb_set_has (s, 13));
   g_assert (hb_set_has (s, 19));
 
+  /* https://github.com/harfbuzz/harfbuzz/issues/579 */
+  hb_set_clear (s);
+  test_empty (s);
+  hb_set_add_range (s, 886, 895);
+  hb_set_add (s, 1024);
+  hb_set_add (s, 1152);
+  hb_set_clear (o);
+  test_empty (o);
+  hb_set_add (o, 889);
+  hb_set_add (o, 1024);
+  g_assert (!hb_set_is_equal (s, o));
+  hb_set_intersect (o, s);
+  test_not_empty (o);
+  g_assert (!hb_set_is_equal (s, o));
+  g_assert_cmpint (hb_set_get_population (o), ==, 2);
+  g_assert (hb_set_has (o, 889));
+  g_assert (hb_set_has (o, 1024));
+  hb_set_clear (o);
+  test_empty (o);
+  hb_set_add_range (o, 887, 889);
+  hb_set_add (o, 1121);
+  g_assert (!hb_set_is_equal (s, o));
+  hb_set_intersect (o, s);
+  test_not_empty (o);
+  g_assert (!hb_set_is_equal (s, o));
+  g_assert_cmpint (hb_set_get_population (o), ==, 3);
+  g_assert (hb_set_has (o, 887));
+  g_assert (hb_set_has (o, 888));
+  g_assert (hb_set_has (o, 889));
+
+  hb_set_clear (s);
+  test_empty (s);
+  hb_set_add_range (s, 886, 895);
+  hb_set_add (s, 1014);
+  hb_set_add (s, 1017);
+  hb_set_add (s, 1024);
+  hb_set_add (s, 1113);
+  hb_set_add (s, 1121);
+  g_assert_cmpint (hb_set_get_population (s), ==, 15);
+
+  hb_set_clear (o);
+  test_empty (o);
+  hb_set_add (o, 889);
+  g_assert_cmpint (hb_set_get_population (o), ==, 1);
+  hb_set_intersect (o, s);
+  g_assert_cmpint (hb_set_get_population (o), ==, 1);
+  g_assert (hb_set_has (o, 889));
+
   hb_set_destroy (s);
+  hb_set_destroy (o);
 }
 
 static void
@@ -167,6 +252,8 @@
   hb_set_add (s, 13);
   hb_set_add_range (s, 6, 6);
   hb_set_add_range (s, 10, 15);
+  hb_set_add (s, 1100);
+  hb_set_add (s, 1200);
   hb_set_add (s, 20005);
 
   test_not_empty (s);
@@ -184,10 +271,35 @@
   g_assert (hb_set_next (s, &next));
   g_assert_cmpint (next, ==, 15);
   g_assert (hb_set_next (s, &next));
+  g_assert_cmpint (next, ==, 1100);
+  g_assert (hb_set_next (s, &next));
+  g_assert_cmpint (next, ==, 1200);
+  g_assert (hb_set_next (s, &next));
   g_assert_cmpint (next, ==, 20005);
   g_assert (!hb_set_next (s, &next));
   g_assert_cmpint (next, ==, HB_SET_VALUE_INVALID);
 
+  next = HB_SET_VALUE_INVALID;
+  g_assert (hb_set_previous (s, &next));
+  g_assert_cmpint (next, ==, 20005);
+  g_assert (hb_set_previous (s, &next));
+  g_assert_cmpint (next, ==, 1200);
+  g_assert (hb_set_previous (s, &next));
+  g_assert_cmpint (next, ==, 1100);
+  g_assert (hb_set_previous (s, &next));
+  g_assert_cmpint (next, ==, 15);
+  g_assert (hb_set_previous (s, &next));
+  g_assert (hb_set_previous (s, &next));
+  g_assert_cmpint (next, ==, 13);
+  g_assert (hb_set_previous (s, &next));
+  g_assert (hb_set_previous (s, &next));
+  g_assert (hb_set_previous (s, &next));
+  g_assert_cmpint (next, ==, 10);
+  g_assert (hb_set_previous (s, &next));
+  g_assert_cmpint (next, ==, 6);
+  g_assert (!hb_set_previous (s, &next));
+  g_assert_cmpint (next, ==, HB_SET_VALUE_INVALID);
+
   first = last = HB_SET_VALUE_INVALID;
   g_assert (hb_set_next_range (s, &first, &last));
   g_assert_cmpint (first, ==, 6);
@@ -196,12 +308,38 @@
   g_assert_cmpint (first, ==, 10);
   g_assert_cmpint (last,  ==, 15);
   g_assert (hb_set_next_range (s, &first, &last));
+  g_assert_cmpint (first, ==, 1100);
+  g_assert_cmpint (last,  ==, 1100);
+  g_assert (hb_set_next_range (s, &first, &last));
+  g_assert_cmpint (first, ==, 1200);
+  g_assert_cmpint (last,  ==, 1200);
+  g_assert (hb_set_next_range (s, &first, &last));
   g_assert_cmpint (first, ==, 20005);
   g_assert_cmpint (last,  ==, 20005);
   g_assert (!hb_set_next_range (s, &first, &last));
   g_assert_cmpint (first, ==, HB_SET_VALUE_INVALID);
   g_assert_cmpint (last,  ==, HB_SET_VALUE_INVALID);
 
+  first = last = HB_SET_VALUE_INVALID;
+  g_assert (hb_set_previous_range (s, &first, &last));
+  g_assert_cmpint (first, ==, 20005);
+  g_assert_cmpint (last,  ==, 20005);
+  g_assert (hb_set_previous_range (s, &first, &last));
+  g_assert_cmpint (first, ==, 1200);
+  g_assert_cmpint (last,  ==, 1200);
+  g_assert (hb_set_previous_range (s, &first, &last));
+  g_assert_cmpint (first, ==, 1100);
+  g_assert_cmpint (last,  ==, 1100);
+  g_assert (hb_set_previous_range (s, &first, &last));
+  g_assert_cmpint (first, ==, 10);
+  g_assert_cmpint (last,  ==, 15);
+  g_assert (hb_set_previous_range (s, &first, &last));
+  g_assert_cmpint (first, ==, 6);
+  g_assert_cmpint (last,  ==, 6);
+  g_assert (!hb_set_previous_range (s, &first, &last));
+  g_assert_cmpint (first, ==, HB_SET_VALUE_INVALID);
+  g_assert_cmpint (last,  ==, HB_SET_VALUE_INVALID);
+
   hb_set_destroy (s);
 }
 
diff --git a/test/api/test-subset-cmap.c b/test/api/test-subset-cmap.c
new file mode 100644
index 0000000..8798475
--- /dev/null
+++ b/test/api/test-subset-cmap.c
@@ -0,0 +1,82 @@
+/*
+ * 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): Roderick Sheeter
+ */
+
+#include <stdbool.h>
+
+#include "hb-test.h"
+#include "hb-subset-test.h"
+
+/* Unit tests for cmap subsetting */
+
+static void
+test_subset_cmap (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.cmap-format12-only.ttf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_set_add (codepoints, 97);
+  hb_set_add (codepoints, 99);
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('c','m','a','p'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+static void
+test_subset_cmap_noop (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font("fonts/Roboto-Regular.abc.cmap-format12-only.ttf");
+
+  hb_set_t *codepoints = hb_set_create();
+  hb_set_add (codepoints, 97);
+  hb_set_add (codepoints, 98);
+  hb_set_add (codepoints, 99);
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('c','m','a','p'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+}
+
+// TODO(rsheeter) test cmap to no codepoints
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_subset_cmap);
+  hb_test_add (test_subset_cmap_noop);
+
+  return hb_test_run();
+}
diff --git a/test/api/test-subset-glyf.c b/test/api/test-subset-glyf.c
new file mode 100644
index 0000000..96e37bb
--- /dev/null
+++ b/test/api/test-subset-glyf.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2018  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#include <stdbool.h>
+
+#include "hb-test.h"
+#include "hb-subset-test.h"
+
+/* Unit tests for hb-subset-glyf.h */
+
+static void
+test_subset_glyf (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.ttf");
+
+  hb_set_t *codepoints = hb_set_create();
+  hb_set_add (codepoints, 97);
+  hb_set_add (codepoints, 99);
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','l','y','f'));
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('l','o','c', 'a'));
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('m','a','x', 'p'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+static void
+test_subset_glyf_with_components (void)
+{
+  hb_face_t *face_components = hb_subset_test_open_font ("fonts/Roboto-Regular.components.ttf");
+  hb_face_t *face_subset = hb_subset_test_open_font ("fonts/Roboto-Regular.components.subset.ttf");
+
+  hb_set_t *codepoints = hb_set_create();
+  hb_set_add (codepoints, 0x1fc);
+  hb_face_t *face_generated_subset = hb_subset_test_create_subset (face_components, codepoints);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('g','l','y','f'));
+  hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('l','o','c', 'a'));
+  hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('m','a','x', 'p'));
+
+  hb_face_destroy (face_generated_subset);
+  hb_face_destroy (face_subset);
+  hb_face_destroy (face_components);
+}
+
+static void
+test_subset_glyf_noop (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf");
+
+  hb_set_t *codepoints = hb_set_create();
+  hb_set_add (codepoints, 97);
+  hb_set_add (codepoints, 98);
+  hb_set_add (codepoints, 99);
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('g','l','y','f'));
+  hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('l','o','c', 'a'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+}
+
+// TODO(grieger): test for long loca generation.
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_subset_glyf);
+  hb_test_add (test_subset_glyf_with_components);
+  hb_test_add (test_subset_glyf_noop);
+
+  return hb_test_run();
+}
diff --git a/test/api/test-subset-hdmx.c b/test/api/test-subset-hdmx.c
new file mode 100644
index 0000000..291eb79
--- /dev/null
+++ b/test/api/test-subset-hdmx.c
@@ -0,0 +1,81 @@
+/*
+ * 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
+ */
+
+#include <stdbool.h>
+
+#include "hb-test.h"
+#include "hb-subset-test.h"
+
+/* Unit tests for hdmx subsetting */
+
+
+static void
+test_subset_hdmx_simple_subset (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.ttf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('h','d','m','x'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+static void
+test_subset_hdmx_noop (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf");
+
+  hb_set_t *codepoints = hb_set_create();
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'b');
+  hb_set_add (codepoints, 'c');
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('h','d','m','x'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_subset_hdmx_simple_subset);
+  hb_test_add (test_subset_hdmx_noop);
+
+  return hb_test_run();
+}
diff --git a/test/api/test-subset-hmtx.c b/test/api/test-subset-hmtx.c
new file mode 100644
index 0000000..36d7d9e
--- /dev/null
+++ b/test/api/test-subset-hmtx.c
@@ -0,0 +1,162 @@
+/*
+ * 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): Roderick Sheeter
+ */
+
+#include <stdbool.h>
+
+#include "hb-test.h"
+#include "hb-subset-test.h"
+
+/* Unit tests for hmtx subsetting */
+
+static void check_num_hmetrics(hb_face_t *face, uint16_t expected_num_hmetrics)
+{
+  hb_blob_t *hhea_blob = hb_face_reference_table (face, HB_TAG ('h','h','e','a'));
+  hb_blob_t *hmtx_blob = hb_face_reference_table (face, HB_TAG ('h','m','t','x'));
+
+  // TODO I sure wish I could just use the hmtx table struct!
+  unsigned int hhea_len;
+  uint8_t *raw_hhea = (uint8_t *) hb_blob_get_data(hhea_blob, &hhea_len);
+  uint16_t num_hmetrics = (raw_hhea[hhea_len - 2] << 8) + raw_hhea[hhea_len - 1];
+  g_assert_cmpuint(expected_num_hmetrics, ==, num_hmetrics);
+
+  hb_blob_destroy (hhea_blob);
+  hb_blob_destroy (hmtx_blob);
+}
+
+static void
+test_subset_hmtx_simple_subset (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.ttf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  check_num_hmetrics(face_abc_subset, 3); /* nothing has same width */
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('h','m','t','x'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+
+static void
+test_subset_hmtx_monospace (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Inconsolata-Regular.abc.ttf");
+  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Inconsolata-Regular.ac.ttf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  check_num_hmetrics(face_abc_subset, 1); /* everything has same width */
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('h','m','t','x'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+
+static void
+test_subset_hmtx_keep_num_metrics (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Inconsolata-Regular.abc.widerc.ttf");
+  hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Inconsolata-Regular.ac.widerc.ttf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'c');
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  check_num_hmetrics(face_abc_subset, 3); /* c is wider */
+  hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('h','m','t','x'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ac);
+}
+
+static void
+test_subset_hmtx_decrease_num_metrics (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Inconsolata-Regular.abc.widerc.ttf");
+  hb_face_t *face_ab = hb_subset_test_open_font ("fonts/Inconsolata-Regular.ab.ttf");
+
+  hb_set_t *codepoints = hb_set_create ();
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'b');
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  check_num_hmetrics(face_abc_subset, 1); /* everything left has same width */
+  hb_subset_test_check (face_ab, face_abc_subset, HB_TAG ('h','m','t','x'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_ab);
+}
+
+static void
+test_subset_hmtx_noop (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf");
+
+  hb_set_t *codepoints = hb_set_create();
+  hb_set_add (codepoints, 'a');
+  hb_set_add (codepoints, 'b');
+  hb_set_add (codepoints, 'c');
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  check_num_hmetrics(face_abc_subset, 4); /* nothing has same width */
+  hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('h','m','t','x'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+}
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_subset_hmtx_simple_subset);
+  hb_test_add (test_subset_hmtx_monospace);
+  hb_test_add (test_subset_hmtx_keep_num_metrics);
+  hb_test_add (test_subset_hmtx_decrease_num_metrics);
+  hb_test_add (test_subset_hmtx_noop);
+
+  return hb_test_run();
+}
diff --git a/test/api/test-subset-os2.c b/test/api/test-subset-os2.c
new file mode 100644
index 0000000..e2b96f9
--- /dev/null
+++ b/test/api/test-subset-os2.c
@@ -0,0 +1,59 @@
+/*
+ * 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
+ */
+
+#include <stdbool.h>
+
+#include "hb-test.h"
+#include "hb-subset-test.h"
+
+static void
+test_subset_os2 (void)
+{
+  hb_face_t *face_abc = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf");
+  hb_face_t *face_b = hb_subset_test_open_font("fonts/Roboto-Regular.b.ttf");
+
+  hb_set_t *codepoints = hb_set_create();
+  hb_set_add (codepoints, 98);
+  hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints);
+  hb_set_destroy (codepoints);
+
+  hb_subset_test_check (face_b, face_abc_subset, HB_TAG ('O','S','/','2'));
+
+  hb_face_destroy (face_abc_subset);
+  hb_face_destroy (face_abc);
+  hb_face_destroy (face_b);
+}
+
+
+int
+main (int argc, char **argv)
+{
+  hb_test_init (&argc, &argv);
+
+  hb_test_add (test_subset_os2);
+
+  return hb_test_run();
+}
diff --git a/test/fuzzing/CMakeLists.txt b/test/fuzzing/CMakeLists.txt
new file mode 100644
index 0000000..e31c744
--- /dev/null
+++ b/test/fuzzing/CMakeLists.txt
@@ -0,0 +1,18 @@
+if (HB_CHECK)
+  file (READ "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.am" MAKEFILEAM)
+  extract_make_variable (hb_fuzzer_SOURCES ${MAKEFILEAM})
+
+  # TODO: enable these two
+  #extract_make_variable (FUZZING_CPPFLAGS ${MAKEFILEAM}) # extracting regex fail
+  #add_executable (hb-fuzzer # it should be run only after ragel execution
+  #  ${project_sources} ${project_extra_sources} ${project_headers}
+  #  ${hb_fuzzer_SOURCES})
+
+  add_executable (hb-fuzzer ${hb_fuzzer_SOURCES})
+  target_link_libraries (hb-fuzzer harfbuzz)
+
+  target_compile_definitions(hb-fuzzer PUBLIC ${FUZZING_CPPFLAGS})
+  add_test (NAME hb-fuzzer
+    COMMAND python run-fuzzer-tests.py $<TARGET_FILE:hb-fuzzer>
+    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+endif ()
diff --git a/test/fuzzing/Makefile.am b/test/fuzzing/Makefile.am
index 3ea8605..a7f7362 100644
--- a/test/fuzzing/Makefile.am
+++ b/test/fuzzing/Makefile.am
@@ -14,6 +14,8 @@
 
 EXTRA_DIST += \
 	README \
+	run-fuzzer-tests.py \
+	CMakeLists.txt \
 	$(NULL)
 
 check_PROGRAMS = \
@@ -45,9 +47,6 @@
 	$(NULL)
 
 check:
-	cat $(srcdir)/../shaping/tests/fuzzed.tests | \
-	cut -d: -f1 | while read x; do \
-		./hb-fuzzer $(srcdir)/../shaping/$$x; \
-	done
+	EXEEXT="$(EXEEXT)" srcdir="$(srcdir)" builddir="$(builddir)" $(srcdir)/run-fuzzer-tests.py
 
 -include $(top_srcdir)/git.mk
diff --git a/test/fuzzing/README b/test/fuzzing/README
index c858f5d..af99cf9 100644
--- a/test/fuzzing/README
+++ b/test/fuzzing/README
@@ -18,4 +18,4 @@
 For more details consult the following locations:
   - http://llvm.org/docs/LibFuzzer.html or
   - https://github.com/google/libfuzzer-bot/tree/master/harfbuzz
-  - https://github.com/behdad/harfbuzz/issues/139
+  - https://github.com/harfbuzz/harfbuzz/issues/139
diff --git a/test/fuzzing/run-fuzzer-tests.py b/test/fuzzing/run-fuzzer-tests.py
new file mode 100755
index 0000000..9455a4b
--- /dev/null
+++ b/test/fuzzing/run-fuzzer-tests.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+import sys, os, subprocess
+
+srcdir = os.environ.get ("srcdir", ".")
+EXEEXT = os.environ.get ("EXEEXT", "")
+top_builddir = os.environ.get ("top_builddir", ".")
+hb_fuzzer = os.path.join (top_builddir, "hb-fuzzer" + EXEEXT)
+
+if not os.path.exists (hb_fuzzer):
+	if len (sys.argv) == 1 or not os.path.exists (sys.argv[1]):
+		print ("""Failed to find hb-fuzzer binary automatically,
+please provide it as the first argument to the tool""")
+		sys.exit (1)
+
+	hb_fuzzer = sys.argv[1]
+
+print ('hb_fuzzer:', hb_fuzzer)
+fails = 0
+
+for line in open (os.path.join (srcdir, "..", "shaping", "data", "in-house", "tests", "fuzzed.tests")):
+	font = line.split (":")[0]
+
+	p = subprocess.Popen ([hb_fuzzer, os.path.join (srcdir, "..", "shaping", font)])
+
+	if p.wait () != 0:
+		fails = fails + 1
+
+if fails:
+	print ("%i fuzzer related tests failed." % fails)
+	sys.exit (1)
diff --git a/test/shaping/CMakeLists.txt b/test/shaping/CMakeLists.txt
new file mode 100644
index 0000000..4bdc5c9
--- /dev/null
+++ b/test/shaping/CMakeLists.txt
@@ -0,0 +1,17 @@
+if (HB_BUILD_UTILS)
+  file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/in-house/Makefile.sources" INHOUSE)
+  extract_make_variable (TESTS ${INHOUSE})
+  foreach (test IN ITEMS ${TESTS})
+    add_test (NAME ${test}
+      COMMAND python run-tests.py $<TARGET_FILE:hb-shape> "data/in-house/${test}"
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+  endforeach ()
+
+  file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/text-rendering-tests/Makefile.sources" TEXTRENDERING)
+  extract_make_variable (TESTS ${TEXTRENDERING})
+  foreach (test IN ITEMS ${TESTS})
+    add_test (NAME ${test}
+      COMMAND python run-tests.py $<TARGET_FILE:hb-shape> "data/text-rendering-tests/${test}"
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+  endforeach ()
+endif ()
diff --git a/test/shaping/Makefile.am b/test/shaping/Makefile.am
index 89578e1..7320336 100644
--- a/test/shaping/Makefile.am
+++ b/test/shaping/Makefile.am
@@ -3,33 +3,25 @@
 NULL =
 EXTRA_DIST =
 CLEANFILES =
-DISTCLEANFILES =
-MAINTAINERCLEANFILES =
+SUBDIRS = data
 
 # Convenience targets:
 lib:
 	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
 
-manifests:
-	@$(srcdir)/hb-manifest-update "$(srcdir)/texts" "$(srcdir)/fonts" "$(srcdir)/tests"
-
 EXTRA_DIST += \
 	README.md \
+	CMakeLists.txt \
 	hb-diff \
 	hb-diff-colorize \
 	hb-diff-filter-failures \
-	hb-diff-ngrams \
 	hb-diff-stat \
-	hb-manifest-read \
-	hb-manifest-update \
 	hb-unicode-decode \
 	hb-unicode-encode \
 	hb-unicode-prettyname \
 	record-test.sh \
-	run-tests.sh \
-	texts/in-tree \
-	fonts/sha1sum \
-	$(TESTS) \
+	run-tests.py \
+	texts/in-house \
 	$(NULL)
 
 # TODO Figure out Python stuff
@@ -40,57 +32,4 @@
 	hb_test_tools.py[co] \
 	$(NULL)
 
-TESTS = \
-	tests/arabic-fallback-shaping.tests \
-	tests/arabic-feature-order.tests \
-	tests/arabic-like-joining.tests \
-	tests/automatic-fractions.tests \
-	tests/cluster.tests \
-	tests/color-fonts.tests \
-	tests/context-matching.tests \
-	tests/cursive-positioning.tests \
-	tests/default-ignorables.tests \
-	tests/fallback-positioning.tests \
-	tests/fuzzed.tests \
-	tests/hangul-jamo.tests \
-	tests/hyphens.tests \
-	tests/indic-joiner-candrabindu.tests \
-	tests/indic-old-spec.tests \
-	tests/indic-pref-blocking.tests \
-	tests/language-tags.tests \
-	tests/ligature-id.tests \
-	tests/mark-filtering-sets.tests \
-	tests/mongolian-variation-selector.tests \
-	tests/spaces.tests \
-	tests/simple.tests \
-	tests/use.tests \
-	tests/use-marchen.tests \
-	tests/vertical.tests \
-	tests/zero-width-marks.tests \
-	$(NULL)
-
-TEST_EXTENSIONS = \
-	.tests \
-	$(NULL)
-
-AM_TESTS_ENVIRONMENT = \
-	EXEEXT="$(EXEEXT)"; \
-	export EXEEXT; \
-	srcdir="$(srcdir)"; \
-	export srcdir; \
-	builddir="$(builddir)"; \
-	export builddir; \
-	$(NULL)
-
-if AUTOMAKE_OLDER_THAN_1_13
-TESTS_ENVIRONMENT = \
-	$(AM_TESTS_ENVIRONMENT) \
-	$(TESTS_LOG_COMPILER) \
-	$(NULL)
-endif
-
-TESTS_LOG_COMPILER = sh $(srcdir)/run-tests.sh
-
-.PHONY: manifests
-
 -include $(top_srcdir)/git.mk
diff --git a/test/shaping/README.md b/test/shaping/README.md
index bf09909..99498e6 100644
--- a/test/shaping/README.md
+++ b/test/shaping/README.md
@@ -14,7 +14,7 @@
 
 To use `record-test.sh`, just put it right before the `hb-shape` invocation:
 ```sh
-$ ./hb-unicode-encode 41 42 43 627 | ./record-it.sh ../../util/hb-shape font.ttf
+$ ./hb-unicode-encode 41 42 43 627 | ./record-test.sh ../../util/hb-shape font.ttf
 ```
 what this does is:
   * Subset the font for the sequence of Unicode characters requested,
@@ -23,18 +23,19 @@
   * If the outputs differ, perhaps it is because the font does not have
     glyph names; it then compares the output of `hb-view` for both fonts.
   * If the outputs differ, recording fails.  Otherwise, it will move the
-    subset font file into `fonts/sha1sum` and name it after its hash,
-    and prints out the test case input, which you can then redirect to
-    an existing or new test file in `tests`, eg.:
+    subset font file into `data/in-house/fonts` and name it after its
+    hash, and print out the test case input, which you can then redirect
+    to an existing or new test file in `data/in-house/tests` using `-o=`,
+    e.g.:
 ```sh
-$ ./hb-unicode-encode 41 42 43 627 | ./record-it.sh ../../util/hb-shape font.ttf >> tests/test-name.test
+$ ./hb-unicode-encode 41 42 43 627 | ./record-test.sh -o=data/in-house/tests/test-name.test ../../util/hb-shape font.ttf
 ```
 
-If you created a new test file, add it to `Makefile.am` so it is run.
-Check that `make test` does indeed run it, and that the test passes.
-When everything looks good, `git add` the new font as well as new
-test file if you created any.  You can see what new files are there
-by running `git status tests fonts/sha1sum`.  And commit!
+If you created a new test file, add it to `data/in-house/Makefile.sources`
+so it is run.  Check that `make check` does indeed run it, and that the
+test passes.  When everything looks good, `git add` the new font as well
+as the new test file if you created any.  You can see what new files are
+there by running `git status data/in-house`.  And commit!
 
 *Note!*  Please only add tests using Open Source fonts, preferably under
 OFL or similar license.
diff --git a/test/shaping/data/Makefile.am b/test/shaping/data/Makefile.am
new file mode 100644
index 0000000..4f2c113
--- /dev/null
+++ b/test/shaping/data/Makefile.am
@@ -0,0 +1,13 @@
+# Process this file with automake to produce Makefile.in
+
+NULL =
+SUBDIRS = \
+	in-house \
+	text-rendering-tests \
+	$(NULL)
+
+# Convenience targets:
+lib:
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
+
+-include $(top_srcdir)/git.mk
diff --git a/test/shaping/data/in-house/COPYING b/test/shaping/data/in-house/COPYING
new file mode 100644
index 0000000..52c48c4
--- /dev/null
+++ b/test/shaping/data/in-house/COPYING
@@ -0,0 +1,95 @@
+The following license applies to many of the fonts in this folder.
+
+
+This Font Software is licensed under the SIL Open Font License,
+Version 1.1.
+
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to
+provide a free and open framework in which fonts may be shared and
+improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software
+components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to,
+deleting, or substituting -- in part or in whole -- any of the
+components of the Original Version, by changing formats or by porting
+the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed,
+modify, redistribute, and sell modified and unmodified copies of the
+Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in
+Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the
+corresponding Copyright Holder. This restriction only applies to the
+primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created using
+the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/test/shaping/data/in-house/Makefile.am b/test/shaping/data/in-house/Makefile.am
new file mode 100644
index 0000000..8a2a076
--- /dev/null
+++ b/test/shaping/data/in-house/Makefile.am
@@ -0,0 +1,20 @@
+# Process this file with automake to produce Makefile.in
+
+NULL =
+
+# Convenience targets:
+lib:
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
+
+EXTRA_DIST = \
+	COPYING \
+	fonts \
+	$(TESTS) \
+	$(NULL)
+
+TEST_EXTENSIONS = .tests
+TESTS_LOG_COMPILER = $(srcdir)/../../run-tests.py $(top_builddir)/util/hb-shape$(EXEEXT)
+
+include Makefile.sources
+
+-include $(top_srcdir)/git.mk
diff --git a/test/shaping/data/in-house/Makefile.sources b/test/shaping/data/in-house/Makefile.sources
new file mode 100644
index 0000000..1922fd4
--- /dev/null
+++ b/test/shaping/data/in-house/Makefile.sources
@@ -0,0 +1,47 @@
+TESTS = \
+	tests/arabic-fallback-shaping.tests \
+	tests/arabic-feature-order.tests \
+	tests/arabic-like-joining.tests \
+	tests/arabic-mark-order.tests \
+	tests/arabic-stch.tests \
+	tests/automatic-fractions.tests \
+	tests/cluster.tests \
+	tests/color-fonts.tests \
+	tests/context-matching.tests \
+	tests/cursive-positioning.tests \
+	tests/default-ignorables.tests \
+	tests/emoji-flag-tags.tests \
+	tests/fallback-positioning.tests \
+	tests/fuzzed.tests \
+	tests/hangul-jamo.tests \
+	tests/hyphens.tests \
+	tests/indic-consonant-with-stacker.tests \
+	tests/indic-init.tests \
+	tests/indic-joiner-candrabindu.tests \
+	tests/indic-joiners.tests \
+	tests/indic-old-spec.tests \
+	tests/indic-pref-blocking.tests \
+	tests/indic-script-extensions.tests \
+	tests/indic-special-cases.tests \
+	tests/indic-syllable.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/spaces.tests \
+	tests/simple.tests \
+	tests/tibetan-contractions-1.tests \
+	tests/tibetan-contractions-2.tests \
+	tests/tibetan-vowels.tests \
+	tests/use.tests \
+	tests/use-marchen.tests \
+	tests/use-syllable.tests \
+	tests/variations-rvrn.tests \
+	tests/vertical.tests \
+	tests/zero-width-marks.tests \
+	$(NULL)
+
+DISABLED_TESTS = \
+	$(NULL)
diff --git a/test/shaping/fonts/sha1sum/0509e80afb379d16560e9e47bdd7d888bebdebc6.ttf b/test/shaping/data/in-house/fonts/0509e80afb379d16560e9e47bdd7d888bebdebc6.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/0509e80afb379d16560e9e47bdd7d888bebdebc6.ttf
rename to test/shaping/data/in-house/fonts/0509e80afb379d16560e9e47bdd7d888bebdebc6.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/051d92f8bc6ff724511b296c27623f824de256e9.ttf b/test/shaping/data/in-house/fonts/051d92f8bc6ff724511b296c27623f824de256e9.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/051d92f8bc6ff724511b296c27623f824de256e9.ttf
rename to test/shaping/data/in-house/fonts/051d92f8bc6ff724511b296c27623f824de256e9.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/074a5ae6b19de8f29772fdd5df2d3d833f81f5e6.ttf b/test/shaping/data/in-house/fonts/074a5ae6b19de8f29772fdd5df2d3d833f81f5e6.ttf
new file mode 100644
index 0000000..ad8ad84
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/074a5ae6b19de8f29772fdd5df2d3d833f81f5e6.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/07f054357ff8638bac3711b422a1e31180bba863.ttf b/test/shaping/data/in-house/fonts/07f054357ff8638bac3711b422a1e31180bba863.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/07f054357ff8638bac3711b422a1e31180bba863.ttf
rename to test/shaping/data/in-house/fonts/07f054357ff8638bac3711b422a1e31180bba863.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf b/test/shaping/data/in-house/fonts/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf
rename to test/shaping/data/in-house/fonts/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/1735326da89f0818cd8c51a0600e9789812c0f94.ttf b/test/shaping/data/in-house/fonts/1735326da89f0818cd8c51a0600e9789812c0f94.ttf
new file mode 100644
index 0000000..b1e4cdb
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/1735326da89f0818cd8c51a0600e9789812c0f94.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/191826b9643e3f124d865d617ae609db6a2ce203.ttf b/test/shaping/data/in-house/fonts/191826b9643e3f124d865d617ae609db6a2ce203.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/191826b9643e3f124d865d617ae609db6a2ce203.ttf
rename to test/shaping/data/in-house/fonts/191826b9643e3f124d865d617ae609db6a2ce203.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/1a3d8f381387dd29be1e897e4b5100ac8b4829e1.ttf b/test/shaping/data/in-house/fonts/1a3d8f381387dd29be1e897e4b5100ac8b4829e1.ttf
new file mode 100644
index 0000000..d060ea9
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/1a3d8f381387dd29be1e897e4b5100ac8b4829e1.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/1a6f1687b7a221f9f2c834b0b360d3c8463b6daf.ttf b/test/shaping/data/in-house/fonts/1a6f1687b7a221f9f2c834b0b360d3c8463b6daf.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/1a6f1687b7a221f9f2c834b0b360d3c8463b6daf.ttf
rename to test/shaping/data/in-house/fonts/1a6f1687b7a221f9f2c834b0b360d3c8463b6daf.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/1c04a16f32a39c26c851b7fc014d2e8d298ba2b8.ttf b/test/shaping/data/in-house/fonts/1c04a16f32a39c26c851b7fc014d2e8d298ba2b8.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/1c04a16f32a39c26c851b7fc014d2e8d298ba2b8.ttf
rename to test/shaping/data/in-house/fonts/1c04a16f32a39c26c851b7fc014d2e8d298ba2b8.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf b/test/shaping/data/in-house/fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf
rename to test/shaping/data/in-house/fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf b/test/shaping/data/in-house/fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf
rename to test/shaping/data/in-house/fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/205edd09bd3d141cc9580f650109556cc28b22cb.ttf b/test/shaping/data/in-house/fonts/205edd09bd3d141cc9580f650109556cc28b22cb.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/205edd09bd3d141cc9580f650109556cc28b22cb.ttf
rename to test/shaping/data/in-house/fonts/205edd09bd3d141cc9580f650109556cc28b22cb.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/217a934cfe15c548b572c203dceb2befdf026462.ttf b/test/shaping/data/in-house/fonts/217a934cfe15c548b572c203dceb2befdf026462.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/217a934cfe15c548b572c203dceb2befdf026462.ttf
rename to test/shaping/data/in-house/fonts/217a934cfe15c548b572c203dceb2befdf026462.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/21b7fb9c1eeae260473809fbc1fe330f66a507cd.ttf b/test/shaping/data/in-house/fonts/21b7fb9c1eeae260473809fbc1fe330f66a507cd.ttf
new file mode 100644
index 0000000..76d9c30
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/21b7fb9c1eeae260473809fbc1fe330f66a507cd.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/226bc2deab3846f1a682085f70c67d0421014144.ttf b/test/shaping/data/in-house/fonts/226bc2deab3846f1a682085f70c67d0421014144.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/226bc2deab3846f1a682085f70c67d0421014144.ttf
rename to test/shaping/data/in-house/fonts/226bc2deab3846f1a682085f70c67d0421014144.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/233c1e252e737ca79e03a9fd56b71aaa4a230f2b.ttf b/test/shaping/data/in-house/fonts/233c1e252e737ca79e03a9fd56b71aaa4a230f2b.ttf
new file mode 100644
index 0000000..999f296
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/233c1e252e737ca79e03a9fd56b71aaa4a230f2b.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/243798dd281c1c77c065958e1ff467420faa9bde.ttf b/test/shaping/data/in-house/fonts/243798dd281c1c77c065958e1ff467420faa9bde.ttf
new file mode 100644
index 0000000..dd8506e
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/243798dd281c1c77c065958e1ff467420faa9bde.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/24b8d24d00ae86f49791b746da4c9d3f717a51a8.ttf b/test/shaping/data/in-house/fonts/24b8d24d00ae86f49791b746da4c9d3f717a51a8.ttf
new file mode 100644
index 0000000..dc290ad
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/24b8d24d00ae86f49791b746da4c9d3f717a51a8.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/270b89df543a7e48e206a2d830c0e10e5265c630.ttf b/test/shaping/data/in-house/fonts/270b89df543a7e48e206a2d830c0e10e5265c630.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/270b89df543a7e48e206a2d830c0e10e5265c630.ttf
rename to test/shaping/data/in-house/fonts/270b89df543a7e48e206a2d830c0e10e5265c630.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/298c9e1d955f10f6f72c6915c3c6ff9bf9695cec.ttf b/test/shaping/data/in-house/fonts/298c9e1d955f10f6f72c6915c3c6ff9bf9695cec.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/298c9e1d955f10f6f72c6915c3c6ff9bf9695cec.ttf
rename to test/shaping/data/in-house/fonts/298c9e1d955f10f6f72c6915c3c6ff9bf9695cec.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf b/test/shaping/data/in-house/fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf
new file mode 100644
index 0000000..c4b4791
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/341421e629668b1a1242245d39238ca48432d35d.ttf b/test/shaping/data/in-house/fonts/341421e629668b1a1242245d39238ca48432d35d.ttf
new file mode 100644
index 0000000..5b82bb5
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/341421e629668b1a1242245d39238ca48432d35d.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/3493e92eaded2661cadde752a39f9d58b11f0326.ttf b/test/shaping/data/in-house/fonts/3493e92eaded2661cadde752a39f9d58b11f0326.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/3493e92eaded2661cadde752a39f9d58b11f0326.ttf
rename to test/shaping/data/in-house/fonts/3493e92eaded2661cadde752a39f9d58b11f0326.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/3511ff5c1647150595846ac414c595cccac34f18.ttf b/test/shaping/data/in-house/fonts/3511ff5c1647150595846ac414c595cccac34f18.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/3511ff5c1647150595846ac414c595cccac34f18.ttf
rename to test/shaping/data/in-house/fonts/3511ff5c1647150595846ac414c595cccac34f18.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/37033cc5cf37bb223d7355153016b6ccece93b28.ttf b/test/shaping/data/in-house/fonts/37033cc5cf37bb223d7355153016b6ccece93b28.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/37033cc5cf37bb223d7355153016b6ccece93b28.ttf
rename to test/shaping/data/in-house/fonts/37033cc5cf37bb223d7355153016b6ccece93b28.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/373e67bf41ca264e260a9716162b71a23549e885.ttf b/test/shaping/data/in-house/fonts/373e67bf41ca264e260a9716162b71a23549e885.ttf
new file mode 100644
index 0000000..214a195
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/373e67bf41ca264e260a9716162b71a23549e885.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/375d6ae32a3cbe52fbf81a4e5777e3377675d5a3.ttf b/test/shaping/data/in-house/fonts/375d6ae32a3cbe52fbf81a4e5777e3377675d5a3.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/375d6ae32a3cbe52fbf81a4e5777e3377675d5a3.ttf
rename to test/shaping/data/in-house/fonts/375d6ae32a3cbe52fbf81a4e5777e3377675d5a3.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/3cae6bfe5b57c07ba81ddbd54c02fe4f3a1e3bf6.ttf b/test/shaping/data/in-house/fonts/3cae6bfe5b57c07ba81ddbd54c02fe4f3a1e3bf6.ttf
new file mode 100644
index 0000000..2cacb68
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/3cae6bfe5b57c07ba81ddbd54c02fe4f3a1e3bf6.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/3d0b77a2360aa6faa1385aaa510509ab70dfbeff.ttf b/test/shaping/data/in-house/fonts/3d0b77a2360aa6faa1385aaa510509ab70dfbeff.ttf
new file mode 100644
index 0000000..d7db1de
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/3d0b77a2360aa6faa1385aaa510509ab70dfbeff.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/43979b90b2dd929723cf4fe1715990bcb9c9a56b.ttf b/test/shaping/data/in-house/fonts/43979b90b2dd929723cf4fe1715990bcb9c9a56b.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/43979b90b2dd929723cf4fe1715990bcb9c9a56b.ttf
rename to test/shaping/data/in-house/fonts/43979b90b2dd929723cf4fe1715990bcb9c9a56b.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/43ef465752be9af900745f72fe29cb853a1401a5.ttf b/test/shaping/data/in-house/fonts/43ef465752be9af900745f72fe29cb853a1401a5.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/43ef465752be9af900745f72fe29cb853a1401a5.ttf
rename to test/shaping/data/in-house/fonts/43ef465752be9af900745f72fe29cb853a1401a5.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/45855bc8d46332b39c4ab9e2ee1a26b1f896da6b.ttf b/test/shaping/data/in-house/fonts/45855bc8d46332b39c4ab9e2ee1a26b1f896da6b.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/45855bc8d46332b39c4ab9e2ee1a26b1f896da6b.ttf
rename to test/shaping/data/in-house/fonts/45855bc8d46332b39c4ab9e2ee1a26b1f896da6b.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/49c9f7485c1392fa09a1b801bc2ffea79275f22e.ttf b/test/shaping/data/in-house/fonts/49c9f7485c1392fa09a1b801bc2ffea79275f22e.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/49c9f7485c1392fa09a1b801bc2ffea79275f22e.ttf
rename to test/shaping/data/in-house/fonts/49c9f7485c1392fa09a1b801bc2ffea79275f22e.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/4cce528e99f600ed9c25a2b69e32eb94a03b4ae8.ttf b/test/shaping/data/in-house/fonts/4cce528e99f600ed9c25a2b69e32eb94a03b4ae8.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/4cce528e99f600ed9c25a2b69e32eb94a03b4ae8.ttf
rename to test/shaping/data/in-house/fonts/4cce528e99f600ed9c25a2b69e32eb94a03b4ae8.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/4fac3929fc3332834e93673780ec0fe94342d193.ttf b/test/shaping/data/in-house/fonts/4fac3929fc3332834e93673780ec0fe94342d193.ttf
new file mode 100644
index 0000000..0f691e1
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/4fac3929fc3332834e93673780ec0fe94342d193.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/5028afb650b1bb718ed2131e872fbcce57828fff.ttf b/test/shaping/data/in-house/fonts/5028afb650b1bb718ed2131e872fbcce57828fff.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/5028afb650b1bb718ed2131e872fbcce57828fff.ttf
rename to test/shaping/data/in-house/fonts/5028afb650b1bb718ed2131e872fbcce57828fff.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/53374c7ca3657be37efde7ed02ae34229a56ae1f.ttf b/test/shaping/data/in-house/fonts/53374c7ca3657be37efde7ed02ae34229a56ae1f.ttf
new file mode 100644
index 0000000..d7f2bdb
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/53374c7ca3657be37efde7ed02ae34229a56ae1f.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/54674a3111d209fb6be0ed31745314b7a8d2c244.ttf b/test/shaping/data/in-house/fonts/54674a3111d209fb6be0ed31745314b7a8d2c244.ttf
new file mode 100644
index 0000000..837e8d2
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/54674a3111d209fb6be0ed31745314b7a8d2c244.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/558661aa659912f4d30ecd27bd09835171a8e2b0.ttf b/test/shaping/data/in-house/fonts/558661aa659912f4d30ecd27bd09835171a8e2b0.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/558661aa659912f4d30ecd27bd09835171a8e2b0.ttf
rename to test/shaping/data/in-house/fonts/558661aa659912f4d30ecd27bd09835171a8e2b0.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/55c88ebbe938680b08f92c3de20713183e0c7481.ttf b/test/shaping/data/in-house/fonts/55c88ebbe938680b08f92c3de20713183e0c7481.ttf
new file mode 100644
index 0000000..f654067
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/55c88ebbe938680b08f92c3de20713183e0c7481.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/56cfd0e18d07f41c38e9598545a6d369127fc6f9.ttf b/test/shaping/data/in-house/fonts/56cfd0e18d07f41c38e9598545a6d369127fc6f9.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/56cfd0e18d07f41c38e9598545a6d369127fc6f9.ttf
rename to test/shaping/data/in-house/fonts/56cfd0e18d07f41c38e9598545a6d369127fc6f9.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/573d3a3177c9a8646e94c8a0d7b224334340946a.ttf b/test/shaping/data/in-house/fonts/573d3a3177c9a8646e94c8a0d7b224334340946a.ttf
new file mode 100644
index 0000000..2fc9e9c
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/573d3a3177c9a8646e94c8a0d7b224334340946a.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/57a9d9f83020155cbb1d2be1f43d82388cbecc88.ttf b/test/shaping/data/in-house/fonts/57a9d9f83020155cbb1d2be1f43d82388cbecc88.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/57a9d9f83020155cbb1d2be1f43d82388cbecc88.ttf
rename to test/shaping/data/in-house/fonts/57a9d9f83020155cbb1d2be1f43d82388cbecc88.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/59a585a63b3df608fbeef00956c8c108deec7de6.ttf b/test/shaping/data/in-house/fonts/59a585a63b3df608fbeef00956c8c108deec7de6.ttf
new file mode 100644
index 0000000..70f0513
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/59a585a63b3df608fbeef00956c8c108deec7de6.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/5a5daf5eb5a4db77a2baa3ad9c7a6ed6e0655fa8.ttf b/test/shaping/data/in-house/fonts/5a5daf5eb5a4db77a2baa3ad9c7a6ed6e0655fa8.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/5a5daf5eb5a4db77a2baa3ad9c7a6ed6e0655fa8.ttf
rename to test/shaping/data/in-house/fonts/5a5daf5eb5a4db77a2baa3ad9c7a6ed6e0655fa8.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/5dfad7735c6a67085f1b90d4d497e32907db4c78.ttf b/test/shaping/data/in-house/fonts/5dfad7735c6a67085f1b90d4d497e32907db4c78.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/5dfad7735c6a67085f1b90d4d497e32907db4c78.ttf
rename to test/shaping/data/in-house/fonts/5dfad7735c6a67085f1b90d4d497e32907db4c78.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/641bd9db850193064d17575053ae2bf8ec149ddc.ttf b/test/shaping/data/in-house/fonts/641bd9db850193064d17575053ae2bf8ec149ddc.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/641bd9db850193064d17575053ae2bf8ec149ddc.ttf
rename to test/shaping/data/in-house/fonts/641bd9db850193064d17575053ae2bf8ec149ddc.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/663aef6b019dbf45ffd74089e2b5f2496ceceb18.ttf b/test/shaping/data/in-house/fonts/663aef6b019dbf45ffd74089e2b5f2496ceceb18.ttf
new file mode 100644
index 0000000..8f7fda5
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/663aef6b019dbf45ffd74089e2b5f2496ceceb18.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf b/test/shaping/data/in-house/fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf
rename to test/shaping/data/in-house/fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/6ff0fbead4462d9f229167b4e6839eceb8465058.ttf b/test/shaping/data/in-house/fonts/6ff0fbead4462d9f229167b4e6839eceb8465058.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/6ff0fbead4462d9f229167b4e6839eceb8465058.ttf
rename to test/shaping/data/in-house/fonts/6ff0fbead4462d9f229167b4e6839eceb8465058.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/706c5d7b625f207bc0d874c67237aad6f1e9cd6f.ttf b/test/shaping/data/in-house/fonts/706c5d7b625f207bc0d874c67237aad6f1e9cd6f.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/706c5d7b625f207bc0d874c67237aad6f1e9cd6f.ttf
rename to test/shaping/data/in-house/fonts/706c5d7b625f207bc0d874c67237aad6f1e9cd6f.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/757ebd573617a24aa9dfbf0b885c54875c6fe06b.ttf b/test/shaping/data/in-house/fonts/757ebd573617a24aa9dfbf0b885c54875c6fe06b.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/757ebd573617a24aa9dfbf0b885c54875c6fe06b.ttf
rename to test/shaping/data/in-house/fonts/757ebd573617a24aa9dfbf0b885c54875c6fe06b.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/7a37dc4d5bf018456aea291cee06daf004c0221c.ttf b/test/shaping/data/in-house/fonts/7a37dc4d5bf018456aea291cee06daf004c0221c.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/7a37dc4d5bf018456aea291cee06daf004c0221c.ttf
rename to test/shaping/data/in-house/fonts/7a37dc4d5bf018456aea291cee06daf004c0221c.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/7e14e7883ed152baa158b80e207b66114c823a8b.ttf b/test/shaping/data/in-house/fonts/7e14e7883ed152baa158b80e207b66114c823a8b.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/7e14e7883ed152baa158b80e207b66114c823a8b.ttf
rename to test/shaping/data/in-house/fonts/7e14e7883ed152baa158b80e207b66114c823a8b.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/8099955657a54e9ee38a6ba1d6f950ce58e3cc25.ttf b/test/shaping/data/in-house/fonts/8099955657a54e9ee38a6ba1d6f950ce58e3cc25.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/8099955657a54e9ee38a6ba1d6f950ce58e3cc25.ttf
rename to test/shaping/data/in-house/fonts/8099955657a54e9ee38a6ba1d6f950ce58e3cc25.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/813c2f8e5512187fd982417a7fb4286728e6f4a8.ttf b/test/shaping/data/in-house/fonts/813c2f8e5512187fd982417a7fb4286728e6f4a8.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/813c2f8e5512187fd982417a7fb4286728e6f4a8.ttf
rename to test/shaping/data/in-house/fonts/813c2f8e5512187fd982417a7fb4286728e6f4a8.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/81c368a33816fb20e9f647e8f24e2180f4720263.ttf b/test/shaping/data/in-house/fonts/81c368a33816fb20e9f647e8f24e2180f4720263.ttf
new file mode 100644
index 0000000..59198b2
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/81c368a33816fb20e9f647e8f24e2180f4720263.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/8228d035fcd65d62ec9728fb34f42c63be93a5d3.ttf b/test/shaping/data/in-house/fonts/8228d035fcd65d62ec9728fb34f42c63be93a5d3.ttf
new file mode 100644
index 0000000..a541546
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/8228d035fcd65d62ec9728fb34f42c63be93a5d3.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/8240789f6d12d4cfc4b5e8e6f246c3701bcf861f.ttf b/test/shaping/data/in-house/fonts/8240789f6d12d4cfc4b5e8e6f246c3701bcf861f.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/8240789f6d12d4cfc4b5e8e6f246c3701bcf861f.ttf
rename to test/shaping/data/in-house/fonts/8240789f6d12d4cfc4b5e8e6f246c3701bcf861f.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf b/test/shaping/data/in-house/fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf
new file mode 100644
index 0000000..a8fd495
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/8454d22037f892e76614e1645d066689a0200e61.ttf b/test/shaping/data/in-house/fonts/8454d22037f892e76614e1645d066689a0200e61.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/8454d22037f892e76614e1645d066689a0200e61.ttf
rename to test/shaping/data/in-house/fonts/8454d22037f892e76614e1645d066689a0200e61.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf b/test/shaping/data/in-house/fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf
rename to test/shaping/data/in-house/fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/856ff9562451293cbeff6f396d4e3877c4f0a436.ttf b/test/shaping/data/in-house/fonts/856ff9562451293cbeff6f396d4e3877c4f0a436.ttf
new file mode 100644
index 0000000..23da109
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/856ff9562451293cbeff6f396d4e3877c4f0a436.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/85fe0be440c64ac77699e21c2f1bd933a919167e.ttf b/test/shaping/data/in-house/fonts/85fe0be440c64ac77699e21c2f1bd933a919167e.ttf
new file mode 100644
index 0000000..6198614
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/85fe0be440c64ac77699e21c2f1bd933a919167e.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/87f85d17d26f1fe9ad28d7365101958edaefb967.ttf b/test/shaping/data/in-house/fonts/87f85d17d26f1fe9ad28d7365101958edaefb967.ttf
new file mode 100644
index 0000000..89b6257
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/87f85d17d26f1fe9ad28d7365101958edaefb967.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/8a9fea2a7384f2116e5b84a9b31f83be7850ce21.ttf b/test/shaping/data/in-house/fonts/8a9fea2a7384f2116e5b84a9b31f83be7850ce21.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/8a9fea2a7384f2116e5b84a9b31f83be7850ce21.ttf
rename to test/shaping/data/in-house/fonts/8a9fea2a7384f2116e5b84a9b31f83be7850ce21.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/94a5d6fb15a27521fba9ea4aee9cb39b2d03322a.ttf b/test/shaping/data/in-house/fonts/94a5d6fb15a27521fba9ea4aee9cb39b2d03322a.ttf
new file mode 100644
index 0000000..baf544f
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/94a5d6fb15a27521fba9ea4aee9cb39b2d03322a.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/96490dd2ff81233b335a650e7eb660e0e7b2eeea.ttf b/test/shaping/data/in-house/fonts/96490dd2ff81233b335a650e7eb660e0e7b2eeea.ttf
new file mode 100644
index 0000000..78518c0
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/96490dd2ff81233b335a650e7eb660e0e7b2eeea.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/98b7887cff91f722b92a8ff800120954606354f9.ttf b/test/shaping/data/in-house/fonts/98b7887cff91f722b92a8ff800120954606354f9.ttf
new file mode 100644
index 0000000..4835c76
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/98b7887cff91f722b92a8ff800120954606354f9.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/9d8a94a67932a3ab75a596fc8b5c6d0392ca9e49.ttf b/test/shaping/data/in-house/fonts/9d8a94a67932a3ab75a596fc8b5c6d0392ca9e49.ttf
new file mode 100644
index 0000000..3fb9951
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/9d8a94a67932a3ab75a596fc8b5c6d0392ca9e49.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/a014549f766436cf55b2ceb40e462038938ee899.ttf b/test/shaping/data/in-house/fonts/a014549f766436cf55b2ceb40e462038938ee899.ttf
new file mode 100644
index 0000000..9a34ba5
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/a014549f766436cf55b2ceb40e462038938ee899.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf b/test/shaping/data/in-house/fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf
new file mode 100644
index 0000000..7d0809e
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/a34a7b00f22ffb5fd7eef6933b81c7e71bc2cdfb.ttf b/test/shaping/data/in-house/fonts/a34a7b00f22ffb5fd7eef6933b81c7e71bc2cdfb.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/a34a7b00f22ffb5fd7eef6933b81c7e71bc2cdfb.ttf
rename to test/shaping/data/in-house/fonts/a34a7b00f22ffb5fd7eef6933b81c7e71bc2cdfb.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/a34a9191d9376bda419836effeef7e75c1386016.ttf b/test/shaping/data/in-house/fonts/a34a9191d9376bda419836effeef7e75c1386016.ttf
new file mode 100644
index 0000000..a358833
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/a34a9191d9376bda419836effeef7e75c1386016.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/a69118c2c2ada48ff803d9149daa54c9ebdae30e.ttf b/test/shaping/data/in-house/fonts/a69118c2c2ada48ff803d9149daa54c9ebdae30e.ttf
new file mode 100644
index 0000000..3cd5b56
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/a69118c2c2ada48ff803d9149daa54c9ebdae30e.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/a6c76d1bafde4a0b1026ebcc932d2e5c6fd02442.ttf b/test/shaping/data/in-house/fonts/a6c76d1bafde4a0b1026ebcc932d2e5c6fd02442.ttf
new file mode 100644
index 0000000..7930a96
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/a6c76d1bafde4a0b1026ebcc932d2e5c6fd02442.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/a919b33197965846f21074b24e30250d67277bce.ttf b/test/shaping/data/in-house/fonts/a919b33197965846f21074b24e30250d67277bce.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/a919b33197965846f21074b24e30250d67277bce.ttf
rename to test/shaping/data/in-house/fonts/a919b33197965846f21074b24e30250d67277bce.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/a98e908e2ed21b22228ea59ebcc0f05034c86f2e.ttf b/test/shaping/data/in-house/fonts/a98e908e2ed21b22228ea59ebcc0f05034c86f2e.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/a98e908e2ed21b22228ea59ebcc0f05034c86f2e.ttf
rename to test/shaping/data/in-house/fonts/a98e908e2ed21b22228ea59ebcc0f05034c86f2e.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/af3086380b743099c54a3b11b96766039ea62fcd.ttf b/test/shaping/data/in-house/fonts/af3086380b743099c54a3b11b96766039ea62fcd.ttf
new file mode 100644
index 0000000..5945b16
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/af3086380b743099c54a3b11b96766039ea62fcd.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/b151cfcdaa77585d77f17a42158e0873fc8e2633.ttf b/test/shaping/data/in-house/fonts/b151cfcdaa77585d77f17a42158e0873fc8e2633.ttf
new file mode 100644
index 0000000..531f255
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/b151cfcdaa77585d77f17a42158e0873fc8e2633.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/b6acef662e0beb8d5fcf5b61c6b0ca69537b7402.ttf b/test/shaping/data/in-house/fonts/b6acef662e0beb8d5fcf5b61c6b0ca69537b7402.ttf
new file mode 100644
index 0000000..41897b6
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/b6acef662e0beb8d5fcf5b61c6b0ca69537b7402.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/b9e2aaa0d75fcef6971ec3a96d806ba4a6b31fe2.ttf b/test/shaping/data/in-house/fonts/b9e2aaa0d75fcef6971ec3a96d806ba4a6b31fe2.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/b9e2aaa0d75fcef6971ec3a96d806ba4a6b31fe2.ttf
rename to test/shaping/data/in-house/fonts/b9e2aaa0d75fcef6971ec3a96d806ba4a6b31fe2.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/bb0c53752e85c3d28973ebc913287b8987d3dfe8.ttf b/test/shaping/data/in-house/fonts/bb0c53752e85c3d28973ebc913287b8987d3dfe8.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/bb0c53752e85c3d28973ebc913287b8987d3dfe8.ttf
rename to test/shaping/data/in-house/fonts/bb0c53752e85c3d28973ebc913287b8987d3dfe8.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/bb29ce50df2bdba2d10726427c6b7609bf460e04.ttf b/test/shaping/data/in-house/fonts/bb29ce50df2bdba2d10726427c6b7609bf460e04.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/bb29ce50df2bdba2d10726427c6b7609bf460e04.ttf
rename to test/shaping/data/in-house/fonts/bb29ce50df2bdba2d10726427c6b7609bf460e04.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/bb9473d2403488714043bcfb946c9f78b86ad627.ttf b/test/shaping/data/in-house/fonts/bb9473d2403488714043bcfb946c9f78b86ad627.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/bb9473d2403488714043bcfb946c9f78b86ad627.ttf
rename to test/shaping/data/in-house/fonts/bb9473d2403488714043bcfb946c9f78b86ad627.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/bbf4a308c402f0678c3e82844892a4da2ebe598f.ttf b/test/shaping/data/in-house/fonts/bbf4a308c402f0678c3e82844892a4da2ebe598f.ttf
new file mode 100644
index 0000000..eb37400
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/bbf4a308c402f0678c3e82844892a4da2ebe598f.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/bf39b0e91ef9807f15a9e283a21a14a209fd2cfc.ttf b/test/shaping/data/in-house/fonts/bf39b0e91ef9807f15a9e283a21a14a209fd2cfc.ttf
new file mode 100644
index 0000000..ce017a3
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/bf39b0e91ef9807f15a9e283a21a14a209fd2cfc.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/bf962d3202883a820aed019d9b5c1838c2ff69c6.ttf b/test/shaping/data/in-house/fonts/bf962d3202883a820aed019d9b5c1838c2ff69c6.ttf
new file mode 100644
index 0000000..4cf9a32
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/bf962d3202883a820aed019d9b5c1838c2ff69c6.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/c4e48b0886ef460f532fb49f00047ec92c432ec0.ttf b/test/shaping/data/in-house/fonts/c4e48b0886ef460f532fb49f00047ec92c432ec0.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/c4e48b0886ef460f532fb49f00047ec92c432ec0.ttf
rename to test/shaping/data/in-house/fonts/c4e48b0886ef460f532fb49f00047ec92c432ec0.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/cc5f3d2d717fb6bd4dfae1c16d48a2cb8e12233b.ttf b/test/shaping/data/in-house/fonts/cc5f3d2d717fb6bd4dfae1c16d48a2cb8e12233b.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/cc5f3d2d717fb6bd4dfae1c16d48a2cb8e12233b.ttf
rename to test/shaping/data/in-house/fonts/cc5f3d2d717fb6bd4dfae1c16d48a2cb8e12233b.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf b/test/shaping/data/in-house/fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf
new file mode 100644
index 0000000..351fc3a
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/d629e7fedc0b350222d7987345fe61613fa3929a.ttf b/test/shaping/data/in-house/fonts/d629e7fedc0b350222d7987345fe61613fa3929a.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/d629e7fedc0b350222d7987345fe61613fa3929a.ttf
rename to test/shaping/data/in-house/fonts/d629e7fedc0b350222d7987345fe61613fa3929a.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/d9b8bc10985f24796826c29f7ccba3d0ae11ec02.ttf b/test/shaping/data/in-house/fonts/d9b8bc10985f24796826c29f7ccba3d0ae11ec02.ttf
new file mode 100644
index 0000000..112146e
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/d9b8bc10985f24796826c29f7ccba3d0ae11ec02.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/dd9f0c7c7c36f75a18be0cab1cddf8f3ab0f366b.ttf b/test/shaping/data/in-house/fonts/dd9f0c7c7c36f75a18be0cab1cddf8f3ab0f366b.ttf
new file mode 100644
index 0000000..ba80928
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/dd9f0c7c7c36f75a18be0cab1cddf8f3ab0f366b.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/df768b9c257e0c9c35786c47cae15c46571d56be.ttf b/test/shaping/data/in-house/fonts/df768b9c257e0c9c35786c47cae15c46571d56be.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/df768b9c257e0c9c35786c47cae15c46571d56be.ttf
rename to test/shaping/data/in-house/fonts/df768b9c257e0c9c35786c47cae15c46571d56be.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/e207635780b42f898d58654b65098763e340f5c7.ttf b/test/shaping/data/in-house/fonts/e207635780b42f898d58654b65098763e340f5c7.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/e207635780b42f898d58654b65098763e340f5c7.ttf
rename to test/shaping/data/in-house/fonts/e207635780b42f898d58654b65098763e340f5c7.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/e68a88939e0f06e34d2bc911f09b70890289c8fd.ttf b/test/shaping/data/in-house/fonts/e68a88939e0f06e34d2bc911f09b70890289c8fd.ttf
new file mode 100644
index 0000000..ada70f7
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/e68a88939e0f06e34d2bc911f09b70890289c8fd.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/e88c339237f52d21e01c55f01b9c1b4cc14a0467.ttf b/test/shaping/data/in-house/fonts/e88c339237f52d21e01c55f01b9c1b4cc14a0467.ttf
new file mode 100644
index 0000000..e9884ea
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/e88c339237f52d21e01c55f01b9c1b4cc14a0467.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf b/test/shaping/data/in-house/fonts/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf
rename to test/shaping/data/in-house/fonts/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/ef2511f215aa3ca847cbfffbf861793b42170875.ttf b/test/shaping/data/in-house/fonts/ef2511f215aa3ca847cbfffbf861793b42170875.ttf
new file mode 100644
index 0000000..6a3af46
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/ef2511f215aa3ca847cbfffbf861793b42170875.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/ef86fe710cfea877bbe0dbb6946a1f88d0661031.ttf b/test/shaping/data/in-house/fonts/ef86fe710cfea877bbe0dbb6946a1f88d0661031.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/ef86fe710cfea877bbe0dbb6946a1f88d0661031.ttf
rename to test/shaping/data/in-house/fonts/ef86fe710cfea877bbe0dbb6946a1f88d0661031.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf b/test/shaping/data/in-house/fonts/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf
rename to test/shaping/data/in-house/fonts/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/fonts/f443753e8ffe8e8aae606cfba158e00334b6efb1.ttf b/test/shaping/data/in-house/fonts/f443753e8ffe8e8aae606cfba158e00334b6efb1.ttf
new file mode 100644
index 0000000..93c2f58
--- /dev/null
+++ b/test/shaping/data/in-house/fonts/f443753e8ffe8e8aae606cfba158e00334b6efb1.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/f499fbc23865022234775c43503bba2e63978fe1.ttf b/test/shaping/data/in-house/fonts/f499fbc23865022234775c43503bba2e63978fe1.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/f499fbc23865022234775c43503bba2e63978fe1.ttf
rename to test/shaping/data/in-house/fonts/f499fbc23865022234775c43503bba2e63978fe1.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/f518eb6f6b5eec2946c9fbbbde44e45d46f5e2ac.ttf b/test/shaping/data/in-house/fonts/f518eb6f6b5eec2946c9fbbbde44e45d46f5e2ac.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/f518eb6f6b5eec2946c9fbbbde44e45d46f5e2ac.ttf
rename to test/shaping/data/in-house/fonts/f518eb6f6b5eec2946c9fbbbde44e45d46f5e2ac.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf b/test/shaping/data/in-house/fonts/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf
rename to test/shaping/data/in-house/fonts/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/fab39d60d758cb586db5a504f218442cd1395725.ttf b/test/shaping/data/in-house/fonts/fab39d60d758cb586db5a504f218442cd1395725.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/fab39d60d758cb586db5a504f218442cd1395725.ttf
rename to test/shaping/data/in-house/fonts/fab39d60d758cb586db5a504f218442cd1395725.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/fbb6c84c9e1fe0c39e152fbe845e51fd81f6748e.ttf b/test/shaping/data/in-house/fonts/fbb6c84c9e1fe0c39e152fbe845e51fd81f6748e.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/fbb6c84c9e1fe0c39e152fbe845e51fd81f6748e.ttf
rename to test/shaping/data/in-house/fonts/fbb6c84c9e1fe0c39e152fbe845e51fd81f6748e.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/fcdcffbdf1c4c97c05308d7600e4c283eb47dbca.ttf b/test/shaping/data/in-house/fonts/fcdcffbdf1c4c97c05308d7600e4c283eb47dbca.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/fcdcffbdf1c4c97c05308d7600e4c283eb47dbca.ttf
rename to test/shaping/data/in-house/fonts/fcdcffbdf1c4c97c05308d7600e4c283eb47dbca.ttf
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/ffa0f5d2d9025486d8469d8b1fdd983e7632499b.ttf b/test/shaping/data/in-house/fonts/ffa0f5d2d9025486d8469d8b1fdd983e7632499b.ttf
similarity index 100%
rename from test/shaping/fonts/sha1sum/ffa0f5d2d9025486d8469d8b1fdd983e7632499b.ttf
rename to test/shaping/data/in-house/fonts/ffa0f5d2d9025486d8469d8b1fdd983e7632499b.ttf
Binary files differ
diff --git a/test/shaping/data/in-house/tests/arabic-fallback-shaping.tests b/test/shaping/data/in-house/tests/arabic-fallback-shaping.tests
new file mode 100644
index 0000000..274829c
--- /dev/null
+++ b/test/shaping/data/in-house/tests/arabic-fallback-shaping.tests
@@ -0,0 +1 @@
+../fonts/df768b9c257e0c9c35786c47cae15c46571d56be.ttf::U+0633,U+064F,U+0644,U+064E,U+0651,U+0627,U+0651,U+0650,U+0645,U+062A,U+06CC:[uni06CC.fina=10+1655|uni062A.medi=9+868|uni0645.init=8+1098|uni0650=2@221,0+0|uni0651=2@260,736+0|uni064E=2@935,1259+0|uni0651=2@974,736+0|uni06440627.fina=2+1470|uni064F=0@558,-10+0|uni0633.init=0+1585]
diff --git a/test/shaping/data/in-house/tests/arabic-feature-order.tests b/test/shaping/data/in-house/tests/arabic-feature-order.tests
new file mode 100644
index 0000000..0f7f58e
--- /dev/null
+++ b/test/shaping/data/in-house/tests/arabic-feature-order.tests
@@ -0,0 +1,4 @@
+../fonts/813c2f8e5512187fd982417a7fb4286728e6f4a8.ttf::U+1820,U+180B:[uni2048.E81A=0+1550]
+../fonts/8a9fea2a7384f2116e5b84a9b31f83be7850ce21.ttf::U+1820,U+180B:[uni2048.E81A=0+1550]
+../fonts/a919b33197965846f21074b24e30250d67277bce.ttf::U+0644,U+0644,U+0647:[Lellah=0+1503]
+../fonts/bf39b0e91ef9807f15a9e283a21a14a209fd2cfc.ttf::U+0644,U+064E,U+0670,U+0653,U+0626:[afii57414.zz04=4+1202|uni0670_uni0653=0@50,350+0|afii57454=0@550,1425+0|afii57444.calt=0+1065]
diff --git a/test/shaping/data/in-house/tests/arabic-like-joining.tests b/test/shaping/data/in-house/tests/arabic-like-joining.tests
new file mode 100644
index 0000000..2e34186
--- /dev/null
+++ b/test/shaping/data/in-house/tests/arabic-like-joining.tests
@@ -0,0 +1 @@
+../fonts/5dfad7735c6a67085f1b90d4d497e32907db4c78.ttf::U+1E922,U+1E923,U+1E924,U+1E925,U+1E926,U+1E927,U+1E928,U+1E929,U+1E92A,U+1E92B,U+1E92C,U+1E92D,U+1E92E,U+1E92F,U+1E930,U+1E931,U+1E932,U+1E933,U+1E934,U+1E935,U+1E936,U+1E937,U+1E938,U+1E939,U+1E93A,U+1E93B,U+1E93C,U+1E93D,U+1E93E,U+1E93F,U+1E940,U+1E941,U+1E942,U+1E943:[sha_adlam.fina=33+711|kpo_adlam.medi=32+573|zal_adlam.medi=31+773|gbe_adlam.medi=30+594|kha_adlam.medi=29+686|va_adlam.medi=28+621|nha_adlam.medi=27+587|tu_adlam.medi=26+772|nya_adlam.medi=25+577|ga_adlam.medi=24+552|qaaf_adlam.medi=23+694|ha_adlam.medi=22+600|chi_adlam.medi=21+662|jiim_adlam.medi=20+781|u_adlam.medi=19+678|ya_adlam.medi=18+553|kaf_adlam.medi=17+808|nun_adlam.medi=16+561|waw_adlam.medi=15+651|yhe_adlam.medi=14+674|dha_adlam.medi=13+674|o_adlam.medi=12+640|i_adlam.medi=11+657|fa_adlam.medi=10+590|e_adlam.medi=9+628|ra_adlam.medi=8+599|bhe_adlam.medi=7+594|pe_adlam.medi=6+492|sinnyiiyhe_adlam.medi=5+777|ba_adlam.medi=4+655|miim_adlam.medi=3+525|laam_adlam.medi=2+554|daali_adlam.medi=1+600|alif_adlam.init=0+597]
diff --git a/test/shaping/data/in-house/tests/arabic-mark-order.tests b/test/shaping/data/in-house/tests/arabic-mark-order.tests
new file mode 100644
index 0000000..18ddb88
--- /dev/null
+++ b/test/shaping/data/in-house/tests/arabic-mark-order.tests
@@ -0,0 +1,6 @@
+../fonts/94a5d6fb15a27521fba9ea4aee9cb39b2d03322a.ttf::U+064A,U+064E,U+0670,U+0653,U+0640,U+0654,U+064E,U+0627:[afii57415.zz04=7+481|afii57454=4@25,975+0|uni0654=4@-50,50+0|afii57440=4+650|uni0670_uni0653=0@75,400+0|afii57454=0@750,1125+0|afii57450.calt=0+1331]
+../fonts/24b8d24d00ae86f49791b746da4c9d3f717a51a8.ttf::U+0628,U+0618,U+0619,U+064E,U+064F,U+0654,U+0658,U+0653,U+0654,U+0651,U+0656,U+0651,U+065C,U+0655,U+0650:[uni0653.small=0@266,2508+0|uni0654=0@308,2151+0|uni0655=0@518,-1544+0|uni065C=0@501,-1453+0|uni0656=0@573,-659+0|uni0650=0@500,133+0|uni0619=0@300,1807+0|uni0618=0@357,1674+0|uni0651064E=0@387,1178+0|uni0651=0@402,764+0|uni0658=0@424,404+0|uni0654064F=0@540,-435+0|uni0628=0+1352]
+../fonts/21b7fb9c1eeae260473809fbc1fe330f66a507cd.ttf::U+0649,U+0655,U+034F,U+0650:[uni0650.small2=0@727,-774+0|space=0+0|uni0655=0@727,-209+0|uni0649=0+1566]
+../fonts/21b7fb9c1eeae260473809fbc1fe330f66a507cd.ttf::U+0649,U+0655,U+0650:[uni0650.small2=0@727,-774+0|uni0655=0@727,-209+0|uni0649=0+1566]
+../fonts/21b7fb9c1eeae260473809fbc1fe330f66a507cd.ttf::U+0649,U+0650,U+0655:[uni0650.small2=0@727,-774+0|uni0655=0@727,-209+0|uni0649=0+1566]
+../fonts/21b7fb9c1eeae260473809fbc1fe330f66a507cd.ttf::U+0649,U+0650,U+034F,U+0655:[uni0655=0+0|space=0+0|uni0650=0@166,0+0|uni0649=0+1566]
diff --git a/test/shaping/data/in-house/tests/arabic-stch.tests b/test/shaping/data/in-house/tests/arabic-stch.tests
new file mode 100644
index 0000000..1ba8f60
--- /dev/null
+++ b/test/shaping/data/in-house/tests/arabic-stch.tests
@@ -0,0 +1 @@
+../fonts/d9b8bc10985f24796826c29f7ccba3d0ae11ec02.ttf:--no-glyph-names:U+0718,U+070F,U+0718,U+0718,U+002E:[1=4+168|3=3+502|3=2+502|4=1@-1004,0+0|5=1@-876,0+0|5=1@-799,0+0|5=1@-722,0+0|5=1@-645,0+0|4=1@-566,0+0|5=1@-438,0+0|5=1@-361,0+0|5=1@-284,0+0|5=1@-207,0+0|4=1@-128,0+0|3=0+502]
diff --git a/test/shaping/data/in-house/tests/automatic-fractions.tests b/test/shaping/data/in-house/tests/automatic-fractions.tests
new file mode 100644
index 0000000..58ec26c
--- /dev/null
+++ b/test/shaping/data/in-house/tests/automatic-fractions.tests
@@ -0,0 +1,3 @@
+../fonts/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf::U+0031,U+0032,U+0033,U+2044,U+0034,U+0035,U+0036:[one.numr=0+600|two.numr=1+600|three.numr=2+600|fraction=3+252|four.small=4+600|five.small=5+600|six.small=6+600]
+../fonts/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf:--direction=l --script=arab:U+0031,U+0032,U+0033,U+2044,U+0034,U+0035,U+0036:[one.numr=0+600|two.numr=1+600|three.numr=2+600|fraction=3+252|four.small=4+600|five.small=5+600|six.small=6+600]
+../fonts/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf:--direction=l:U+0661,U+0662,U+0663,U+2044,U+0664,U+0665,U+0666:[uni0661.numr=0+600|uni0662.numr=1+600|uni0663.numr=2+600|fraction=3+252|uni0664.small=4+600|uni0665.small=5+600|uni0666.small=6+600]
diff --git a/test/shaping/data/in-house/tests/cluster.tests b/test/shaping/data/in-house/tests/cluster.tests
new file mode 100644
index 0000000..fd0a0fe
--- /dev/null
+++ b/test/shaping/data/in-house/tests/cluster.tests
@@ -0,0 +1,2 @@
+../fonts/4fac3929fc3332834e93673780ec0fe94342d193.ttf:--cluster-level=2:U+0078,U+030A,U+0058,U+030A:[gid2=0+1083|gid3=1@-1131,-8+0|gid1=2+1200|gid3=3@-1190,349+0]
+../fonts/43ef465752be9af900745f72fe29cb853a1401a5.ttf:--cluster-level=1:U+05D4,U+05B7,U+05E9,U+05BC,U+05C1,U+05B8,U+05DE,U+05B4,U+05DD:[uni05DD=8+1359|uni05B4=7@111,0+0|uni05DE=6+1391|uni05B8=5+0|uni05BC=3+0|uni05C1=3+0|uni05E9=2+1451|uni05B7=1@28,0+0|uni05D4=0+1338]
diff --git a/test/shaping/data/in-house/tests/color-fonts.tests b/test/shaping/data/in-house/tests/color-fonts.tests
new file mode 100644
index 0000000..e7311bc
--- /dev/null
+++ b/test/shaping/data/in-house/tests/color-fonts.tests
@@ -0,0 +1 @@
+../fonts/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf:--font-funcs=ot --show-extents:U+1F42F:[gid1=0+2963<0,2178,2963,-2788>]
diff --git a/test/shaping/data/in-house/tests/context-matching.tests b/test/shaping/data/in-house/tests/context-matching.tests
new file mode 100644
index 0000000..5673edc
--- /dev/null
+++ b/test/shaping/data/in-house/tests/context-matching.tests
@@ -0,0 +1,3 @@
+../fonts/4cce528e99f600ed9c25a2b69e32eb94a03b4ae8.ttf::U+1A48,U+1A58,U+1A25,U+1A48,U+1A58,U+1A25,U+1A6E,U+1A63:[uni1A48=0+1212|uni1A25=0+1912|uni1A58=0+0|uni1A48=3+1212|uni1A6E=3+0|uni1A25=3+1912|uni1A58=3+0|uni1A63=3+1212]
+../fonts/d629e7fedc0b350222d7987345fe61613fa3929a.ttf::U+0915,U+093F,U+0915,U+093F:[ivowelsign03deva=0+530|kadeva=0+1561|ivowelsign03deva=2+530|kadeva=2+1561]
+../fonts/f499fbc23865022234775c43503bba2e63978fe1.ttf::U+09B0,U+09CD,U+09A5,U+09CD,U+09AF,U+09C0:[gid1=0+1320|gid13=0+523|gid18=0+545]
diff --git a/test/shaping/data/in-house/tests/cursive-positioning.tests b/test/shaping/data/in-house/tests/cursive-positioning.tests
new file mode 100644
index 0000000..74d283d
--- /dev/null
+++ b/test/shaping/data/in-house/tests/cursive-positioning.tests
@@ -0,0 +1,4 @@
+../fonts/c4e48b0886ef460f532fb49f00047ec92c432ec0.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+452|gid9=1@0,977+452|gid10=0@20,1577+207]
+../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]
diff --git a/test/shaping/data/in-house/tests/default-ignorables.tests b/test/shaping/data/in-house/tests/default-ignorables.tests
new file mode 100644
index 0000000..a27b67a
--- /dev/null
+++ b/test/shaping/data/in-house/tests/default-ignorables.tests
@@ -0,0 +1,2 @@
+../fonts/051d92f8bc6ff724511b296c27623f824de256e9.ttf::U+0075,U+0361,U+034F,U+0301,U+0069:[gid2=0+1266|gid7=0@-617,442+0|gid5=0@-7,0+0|gid1=4+528]
+../fonts/bf962d3202883a820aed019d9b5c1838c2ff69c6.ttf::U+0020,U+06CC,U+064E,U+034F,U+0651:[uni0651=1+0|space=1+0|uni064E=1@236,-432+0|uni06CC=1+1266|space=0+452]
diff --git a/test/shaping/data/in-house/tests/emoji-flag-tags.tests b/test/shaping/data/in-house/tests/emoji-flag-tags.tests
new file mode 100644
index 0000000..189de55
--- /dev/null
+++ b/test/shaping/data/in-house/tests/emoji-flag-tags.tests
@@ -0,0 +1,2 @@
+../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/fallback-positioning.tests b/test/shaping/data/in-house/tests/fallback-positioning.tests
new file mode 100644
index 0000000..5047d84
--- /dev/null
+++ b/test/shaping/data/in-house/tests/fallback-positioning.tests
@@ -0,0 +1,2 @@
+../fonts/8228d035fcd65d62ec9728fb34f42c63be93a5d3.ttf::U+0078,U+0301,U+0058,U+0301:[x=0+1030|acutecomb=0@-21,-27+0|X=2+1295|acutecomb=2@-147,320+0]
+../fonts/856ff9562451293cbeff6f396d4e3877c4f0a436.ttf::U+0061,U+035C,U+0062:[uni0061=0+512|uni035C=0@-64,-128+0|uni0062=2+512]
diff --git a/test/shaping/data/in-house/tests/fuzzed.tests b/test/shaping/data/in-house/tests/fuzzed.tests
new file mode 100644
index 0000000..43a1933
--- /dev/null
+++ b/test/shaping/data/in-house/tests/fuzzed.tests
@@ -0,0 +1,23 @@
+../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/hangul-jamo.tests b/test/shaping/data/in-house/tests/hangul-jamo.tests
new file mode 100644
index 0000000..6e2fecc
--- /dev/null
+++ b/test/shaping/data/in-house/tests/hangul-jamo.tests
@@ -0,0 +1,2 @@
+../fonts/757ebd573617a24aa9dfbf0b885c54875c6fe06b.ttf::U+115F,U+11A2:[gid3=0+920|gid4=0+0]
+../fonts/7e14e7883ed152baa158b80e207b66114c823a8b.ttf::U+11A2:[gid1=0+920]
diff --git a/test/shaping/data/in-house/tests/hyphens.tests b/test/shaping/data/in-house/tests/hyphens.tests
new file mode 100644
index 0000000..acfe8f4
--- /dev/null
+++ b/test/shaping/data/in-house/tests/hyphens.tests
@@ -0,0 +1,2 @@
+../fonts/1c04a16f32a39c26c851b7fc014d2e8d298ba2b8.ttf::U+2010:[gid1=0+739]
+../fonts/1c04a16f32a39c26c851b7fc014d2e8d298ba2b8.ttf::U+2011:[gid1=0+739]
diff --git a/test/shaping/data/in-house/tests/indic-consonant-with-stacker.tests b/test/shaping/data/in-house/tests/indic-consonant-with-stacker.tests
new file mode 100644
index 0000000..43a3f27
--- /dev/null
+++ b/test/shaping/data/in-house/tests/indic-consonant-with-stacker.tests
@@ -0,0 +1,4 @@
+../fonts/a014549f766436cf55b2ceb40e462038938ee899.ttf:--no-glyph-names:U+0CF1,U+0C95:[2=0+1129|3=1+358]
+../fonts/55c88ebbe938680b08f92c3de20713183e0c7481.ttf:--no-glyph-names:U+0CF2,U+0CAA:[2=0+1539|3=1+245]
+../fonts/341421e629668b1a1242245d39238ca48432d35d.ttf:--no-glyph-names:U+0CF1:[1=0+1129]
+../fonts/663aef6b019dbf45ffd74089e2b5f2496ceceb18.ttf:--no-glyph-names:U+0CF2:[1=0+1539]
diff --git a/test/shaping/data/in-house/tests/indic-init.tests b/test/shaping/data/in-house/tests/indic-init.tests
new file mode 100644
index 0000000..fee8635
--- /dev/null
+++ b/test/shaping/data/in-house/tests/indic-init.tests
@@ -0,0 +1 @@
+../fonts/1a3d8f381387dd29be1e897e4b5100ac8b4829e1.ttf:--no-glyph-names:U+09AC,U+09C7,U+09AC,U+09C7:[3=0+273|1=0+460|2=2+307|1=2+460]
diff --git a/test/shaping/data/in-house/tests/indic-joiner-candrabindu.tests b/test/shaping/data/in-house/tests/indic-joiner-candrabindu.tests
new file mode 100644
index 0000000..87b3603
--- /dev/null
+++ b/test/shaping/data/in-house/tests/indic-joiner-candrabindu.tests
@@ -0,0 +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]
diff --git a/test/shaping/data/in-house/tests/indic-joiners.tests b/test/shaping/data/in-house/tests/indic-joiners.tests
new file mode 100644
index 0000000..57107d5
--- /dev/null
+++ b/test/shaping/data/in-house/tests/indic-joiners.tests
@@ -0,0 +1,2 @@
+../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+17CA,U+17B8:[uni179a=0+775|uni1784=1+1550|uni179f.sub=1+775|uni17bb=1@-75,-700+0|uni17b8=1+0]
diff --git a/test/shaping/data/in-house/tests/indic-old-spec.tests b/test/shaping/data/in-house/tests/indic-old-spec.tests
new file mode 100644
index 0000000..cd56319
--- /dev/null
+++ b/test/shaping/data/in-house/tests/indic-old-spec.tests
@@ -0,0 +1,2 @@
+../fonts/57a9d9f83020155cbb1d2be1f43d82388cbecc88.ttf::U+0C9A,U+0CCD,U+0C9A,U+0CCD:[U0C9A_U0CCD.haln=0+1066|U0C9A_0CCD.blwf=0+0]
+../fonts/270b89df543a7e48e206a2d830c0e10e5265c630.ttf::U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D:[glyph201=0+1183|U0D4D=0+0]
diff --git a/test/shaping/data/in-house/tests/indic-pref-blocking.tests b/test/shaping/data/in-house/tests/indic-pref-blocking.tests
new file mode 100644
index 0000000..516753d
--- /dev/null
+++ b/test/shaping/data/in-house/tests/indic-pref-blocking.tests
@@ -0,0 +1,2 @@
+../fonts/226bc2deab3846f1a682085f70c67d0421014144.ttf::U+0D2F,U+0D4D,U+0D30,U+0D46:[evowelsignmlym=0+1465|rapostmlym=0+499|yamlym=0+2120]
+../fonts/e207635780b42f898d58654b65098763e340f5c7.ttf::U+0D2F,U+0D4D,U+0D30,U+0D46:[yamlym=0+2120|viramamlym=0+0|evowelsignmlym=0+1465|ramlym=0+1507]
diff --git a/test/shaping/data/in-house/tests/indic-script-extensions.tests b/test/shaping/data/in-house/tests/indic-script-extensions.tests
new file mode 100644
index 0000000..0589627
--- /dev/null
+++ b/test/shaping/data/in-house/tests/indic-script-extensions.tests
@@ -0,0 +1,2 @@
+../fonts/3493e92eaded2661cadde752a39f9d58b11f0326.ttf::U+0BA4,U+0BC6,U+1133C,U+0BAA,U+1133C,U+0BC6,U+1133C:[u0BC6=0+2093|u1133C=0+0|u0BA4=0+1863|u0BC6=3+2093|u1133C=3+0|u0BAA=3+1706|u1133C=3+0]
+../fonts/b151cfcdaa77585d77f17a42158e0873fc8e2633.ttf:--no-glyph-names:U+0BAA,U+11301,U+11303:[1=0+535|2=0+0|3=0+310]
diff --git a/test/shaping/data/in-house/tests/indic-special-cases.tests b/test/shaping/data/in-house/tests/indic-special-cases.tests
new file mode 100644
index 0000000..f51651f
--- /dev/null
+++ b/test/shaping/data/in-house/tests/indic-special-cases.tests
@@ -0,0 +1,3 @@
+../fonts/3cae6bfe5b57c07ba81ddbd54c02fe4f3a1e3bf6.ttf::U+0CB0,U+0CCD,U+0C95:[gid1=0+1176|gid5=0+1161]
+../fonts/3cae6bfe5b57c07ba81ddbd54c02fe4f3a1e3bf6.ttf::U+0CB0,U+200D,U+0CCD,U+0C95:[gid2=0+1334|gid6=0+358]
+../fonts/3cae6bfe5b57c07ba81ddbd54c02fe4f3a1e3bf6.ttf::U+0CB0,U+0CCD,U+200D,U+0C95:[gid2=0+1334|gid6=0+358]
diff --git a/test/shaping/data/in-house/tests/indic-syllable.tests b/test/shaping/data/in-house/tests/indic-syllable.tests
new file mode 100644
index 0000000..4c7d651
--- /dev/null
+++ b/test/shaping/data/in-house/tests/indic-syllable.tests
@@ -0,0 +1,8 @@
+../fonts/54674a3111d209fb6be0ed31745314b7a8d2c244.ttf::U+0BA4,U+0BCD,U+00B3:[taprehalftamil=0+1509|uni00B3=2+674]
+../fonts/3d0b77a2360aa6faa1385aaa510509ab70dfbeff.ttf::U+0CF1:[gid1=0+1129]
+../fonts/3d0b77a2360aa6faa1385aaa510509ab70dfbeff.ttf::U+0CF2:[gid2=0+1539]
+../fonts/87f85d17d26f1fe9ad28d7365101958edaefb967.ttf:--font-funcs=ot:U+0980,U+0981:[anjibeng=0+520|candrabindubeng=0+0]
+../fonts/85fe0be440c64ac77699e21c2f1bd933a919167e.ttf::U+0A15,U+0A51,U+0A47:[kaguru=0+1273|udaatguru=0@75,0+0|eematraguru=0@-40,0+0]
+../fonts/1735326da89f0818cd8c51a0600e9789812c0f94.ttf::U+0A51:[uni25CC=0+1044|udaatguru=0+0]
+../fonts/1735326da89f0818cd8c51a0600e9789812c0f94.ttf::U+25CC,U+0A51:[uni25CC=0+1044|udaatguru=0+0]
+../fonts/81c368a33816fb20e9f647e8f24e2180f4720263.ttf:--no-glyph-names:U+0C80,U+0C82:[1=0+502|2=0+502]
diff --git a/test/shaping/data/in-house/tests/language-tags.tests b/test/shaping/data/in-house/tests/language-tags.tests
new file mode 100644
index 0000000..4c62113
--- /dev/null
+++ b/test/shaping/data/in-house/tests/language-tags.tests
@@ -0,0 +1,12 @@
+../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=fa:U+004A:[gid2=0+1000]
+../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=ja:U+004A:[gid2=0+1000]
+../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh:U+004A:[gid4=0+1000]
+../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-cn:U+004A:[gid4=0+1000]
+../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-sg:U+004A:[gid4=0+1000]
+../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-tw:U+004A:[gid5=0+1000]
+../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-hans:U+004A:[gid4=0+1000]
+../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-hant:U+004A:[gid5=0+1000]
+../fonts/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-hant-hk:U+004A:[gid6=0+1000]
+../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]
diff --git a/test/shaping/data/in-house/tests/ligature-id.tests b/test/shaping/data/in-house/tests/ligature-id.tests
new file mode 100644
index 0000000..3daaca3
--- /dev/null
+++ b/test/shaping/data/in-house/tests/ligature-id.tests
@@ -0,0 +1,36 @@
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|space=3+213|u0995_u09B0_u09CD.blwf.vatu=4+643|u0995_u09CD.half_u09B2.pres=7+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|space=6+213|u0995_u09B0_u09CD.blwf.vatu=7+643|u0995_u09CD.half_u09B2.pres=10+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|space=9+213|u0995_u09B0_u09CD.blwf.vatu=10+643|u0995_u09CD.half_u09B2.pres=13+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|space=12+213|u0995_u09B0_u09CD.blwf.vatu=13+643|u0995_u09CD.half_u09B2.pres=16+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|space=15+213|u0995_u09B0_u09CD.blwf.vatu=16+643|u0995_u09CD.half_u09B2.pres=19+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|space=18+213|u0995_u09B0_u09CD.blwf.vatu=19+643|u0995_u09CD.half_u09B2.pres=22+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|space=21+213|u0995_u09B0_u09CD.blwf.vatu=22+643|u0995_u09CD.half_u09B2.pres=25+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|space=24+213|u0995_u09B0_u09CD.blwf.vatu=25+643|u0995_u09CD.half_u09B2.pres=28+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|space=27+213|u0995_u09B0_u09CD.blwf.vatu=28+643|u0995_u09CD.half_u09B2.pres=31+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|space=30+213|u0995_u09B0_u09CD.blwf.vatu=31+643|u0995_u09CD.half_u09B2.pres=34+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|space=33+213|u0995_u09B0_u09CD.blwf.vatu=34+643|u0995_u09CD.half_u09B2.pres=37+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|space=36+213|u0995_u09B0_u09CD.blwf.vatu=37+643|u0995_u09CD.half_u09B2.pres=40+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|space=39+213|u0995_u09B0_u09CD.blwf.vatu=40+643|u0995_u09CD.half_u09B2.pres=43+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|space=42+213|u0995_u09B0_u09CD.blwf.vatu=43+643|u0995_u09CD.half_u09B2.pres=46+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|space=45+213|u0995_u09B0_u09CD.blwf.vatu=46+643|u0995_u09CD.half_u09B2.pres=49+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|space=48+213|u0995_u09B0_u09CD.blwf.vatu=49+643|u0995_u09CD.half_u09B2.pres=52+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|space=51+213|u0995_u09B0_u09CD.blwf.vatu=52+643|u0995_u09CD.half_u09B2.pres=55+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|space=54+213|u0995_u09B0_u09CD.blwf.vatu=55+643|u0995_u09CD.half_u09B2.pres=58+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|space=57+213|u0995_u09B0_u09CD.blwf.vatu=58+643|u0995_u09CD.half_u09B2.pres=61+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|space=60+213|u0995_u09B0_u09CD.blwf.vatu=61+643|u0995_u09CD.half_u09B2.pres=64+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|space=63+213|u0995_u09B0_u09CD.blwf.vatu=64+643|u0995_u09CD.half_u09B2.pres=67+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|space=66+213|u0995_u09B0_u09CD.blwf.vatu=67+643|u0995_u09CD.half_u09B2.pres=70+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|space=69+213|u0995_u09B0_u09CD.blwf.vatu=70+643|u0995_u09CD.half_u09B2.pres=73+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|space=72+213|u0995_u09B0_u09CD.blwf.vatu=73+643|u0995_u09CD.half_u09B2.pres=76+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|space=75+213|u0995_u09B0_u09CD.blwf.vatu=76+643|u0995_u09CD.half_u09B2.pres=79+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|space=78+213|u0995_u09B0_u09CD.blwf.vatu=79+643|u0995_u09CD.half_u09B2.pres=82+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|space=81+213|u0995_u09B0_u09CD.blwf.vatu=82+643|u0995_u09CD.half_u09B2.pres=85+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|space=84+213|u0995_u09B0_u09CD.blwf.vatu=85+643|u0995_u09CD.half_u09B2.pres=88+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|space=87+213|u0995_u09B0_u09CD.blwf.vatu=88+643|u0995_u09CD.half_u09B2.pres=91+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|space=90+213|u0995_u09B0_u09CD.blwf.vatu=91+643|u0995_u09CD.half_u09B2.pres=94+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|u0995_u09CD.half_u0995.pres=90+566|space=93+213|u0995_u09B0_u09CD.blwf.vatu=94+643|u0995_u09CD.half_u09B2.pres=97+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|u0995_u09CD.half_u0995.pres=90+566|u0995_u09CD.half_u0995.pres=93+566|space=96+213|u0995_u09B0_u09CD.blwf.vatu=97+643|u0995_u09CD.half_u09B2.pres=100+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|u0995_u09CD.half_u0995.pres=90+566|u0995_u09CD.half_u0995.pres=93+566|u0995_u09CD.half_u0995.pres=96+566|space=99+213|u0995_u09B0_u09CD.blwf.vatu=100+643|u0995_u09CD.half_u09B2.pres=103+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|u0995_u09CD.half_u0995.pres=90+566|u0995_u09CD.half_u0995.pres=93+566|u0995_u09CD.half_u0995.pres=96+566|u0995_u09CD.half_u0995.pres=99+566|space=102+213|u0995_u09B0_u09CD.blwf.vatu=103+643|u0995_u09CD.half_u09B2.pres=106+602]
+../fonts/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|u0995_u09CD.half_u0995.pres=90+566|u0995_u09CD.half_u0995.pres=93+566|u0995_u09CD.half_u0995.pres=96+566|u0995_u09CD.half_u0995.pres=99+566|u0995_u09CD.half_u0995.pres=102+566|space=105+213|u0995_u09B0_u09CD.blwf.vatu=106+643|u0995_u09CD.half_u09B2.pres=109+602]
+../fonts/a6c76d1bafde4a0b1026ebcc932d2e5c6fd02442.ttf::U+1004,U+103A,U+1039,U+101B,U+103D,U+102D:[uni101B103D=0+450|uni1004103A1039102D=0@-50,0+0]
diff --git a/test/shaping/data/in-house/tests/mark-attachment.tests b/test/shaping/data/in-house/tests/mark-attachment.tests
new file mode 100644
index 0000000..9a9b8ff
--- /dev/null
+++ b/test/shaping/data/in-house/tests/mark-attachment.tests
@@ -0,0 +1 @@
+../fonts/98b7887cff91f722b92a8ff800120954606354f9.ttf::U+100F,U+103C,U+102F,U+1036:[uni103C102F=0+150|uni100F=0+550|uni1036=0@-150,0+0]
diff --git a/test/shaping/data/in-house/tests/mark-filtering-sets.tests b/test/shaping/data/in-house/tests/mark-filtering-sets.tests
new file mode 100644
index 0000000..d30e021
--- /dev/null
+++ b/test/shaping/data/in-house/tests/mark-filtering-sets.tests
@@ -0,0 +1,5 @@
+../fonts/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf::U+062A,U+062E,U+062A,U+0629:[glyph837=3@299,1170+0|uni06C1.1=3+502|glyph837=2@149,690+0|uni0628.8=2+532|glyph836=1@-51,1259+0|glyph514=1+196|glyph837=0@655,1751+0|glyph112=0@0,-358+905]
+../fonts/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf::U+062A,U+062E,U+0646,U+0629:[glyph837=3@299,1170+0|uni06C1.1=3+502|glyph836=2@149,690+0|uni0628.8=2+532|glyph836=1@-51,1259+0|glyph514=1+196|glyph837=0@655,1751+0|glyph112=0@0,-358+905]
+../fonts/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf::U+062A,U+062E,U+0626,U+0629:[glyph837=3@299,1170+0|uni06C1.1=3+502|glyph847=2@149,690+0|uni0628.8=2+532|glyph836=1@-51,1259+0|glyph514=1+196|glyph837=0@655,1751+0|glyph112=0@0,-358+905]
+../fonts/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf::U+062A,U+062E,U+062B,U+0629:[glyph837=3@299,1520+0|uni06C1.1=3+502|glyph838=2@149,690+0|uni0628.8=2+532|glyph836=1@-51,1259+0|glyph514=1+196|glyph837=0@655,1751+0|glyph112=0@0,-358+905]
+../fonts/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf::U+062A,U+062E,U+0679,U+0629:[glyph837=3@299,1520+0|uni06C1.1=3+502|glyph842=2@149,690+0|uni0628.8=2+532|glyph836=1@-51,1259+0|glyph514=1+196|glyph837=0@655,1751+0|glyph112=0@0,-358+905]
diff --git a/test/shaping/data/in-house/tests/mongolian-variation-selector.tests b/test/shaping/data/in-house/tests/mongolian-variation-selector.tests
new file mode 100644
index 0000000..efb4cf4
--- /dev/null
+++ b/test/shaping/data/in-house/tests/mongolian-variation-selector.tests
@@ -0,0 +1,4 @@
+../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]
diff --git a/test/shaping/data/in-house/tests/myanmar-syllable.tests b/test/shaping/data/in-house/tests/myanmar-syllable.tests
new file mode 100644
index 0000000..4666ef9
--- /dev/null
+++ b/test/shaping/data/in-house/tests/myanmar-syllable.tests
@@ -0,0 +1 @@
+../fonts/af3086380b743099c54a3b11b96766039ea62fcd.ttf:--no-glyph-names:U+101D,U+FE00,U+1031,U+FE00,U+1031,U+FE00:[6=0+465|6=0+465|5=0+502]
diff --git a/test/shaping/data/in-house/tests/simple.tests b/test/shaping/data/in-house/tests/simple.tests
new file mode 100644
index 0000000..64cae0e
--- /dev/null
+++ b/test/shaping/data/in-house/tests/simple.tests
@@ -0,0 +1,2 @@
+../fonts/49c9f7485c1392fa09a1b801bc2ffea79275f22e.ttf:--shaper=ot:U+0056,U+0041,U+0042,U+0045,U+0061,U+0062,U+0063,U+0064:[V=0+1142|A=1+1295|B=2+1295|E=3+1123|a=4+1126|b=5+1164|c=6+1072|d=7+1164]
+../fonts/49c9f7485c1392fa09a1b801bc2ffea79275f22e.ttf:--shaper=fallback:U+0056,U+0041,U+0042,U+0045,U+0061,U+0062,U+0063,U+0064:[V=0+1295|A=1+1295|B=2+1295|E=3+1123|a=4+1126|b=5+1164|c=6+1072|d=7+1164]
diff --git a/test/shaping/data/in-house/tests/spaces.tests b/test/shaping/data/in-house/tests/spaces.tests
new file mode 100644
index 0000000..3ebaac5
--- /dev/null
+++ b/test/shaping/data/in-house/tests/spaces.tests
@@ -0,0 +1,17 @@
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+0020:[gid1=0+560]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+00A0:[gid1=0+560]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+1680:[gid0=0+692]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2000:[gid1=0+1024]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2001:[gid1=0+2048]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2002:[gid1=0+1024]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2003:[gid1=0+2048]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2004:[gid1=0+683]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2005:[gid1=0+512]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2006:[gid1=0+341]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2007:[gid1=0+560]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2008:[gid1=0+560]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2009:[gid1=0+410]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+200A:[gid1=0+128]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+202F:[gid1=0+280]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+205F:[gid1=0+455]
+../fonts/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+3000:[gid1=0+2048]
diff --git a/test/shaping/data/in-house/tests/tibetan-contractions-1.tests b/test/shaping/data/in-house/tests/tibetan-contractions-1.tests
new file mode 100644
index 0000000..ccc0c9c
--- /dev/null
+++ b/test/shaping/data/in-house/tests/tibetan-contractions-1.tests
@@ -0,0 +1,60 @@
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+FEFF,U+0F40,U+0F72,U+0F72,U+0F0B,U+0F66,U+0FAD,U+0F7C,U+0F7C,U+0F0B:[uni0F40=0+680|uni0F720F72=0+0|uni0F0B=4+190|uni0F660FAD=5+680|uni0F7D=5+0|uni0F0B=9+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F40,U+0F74,U+0F72,U+0F66,U+0F0B:[uni0F400F740F72=0+680|uni0F66=3+680|uni0F0B=4+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F40,U+0F74,U+0F7A,U+0F53,U+0F0B:[uni0F400F74=0+680|uni0F7A=0+0|uni0F53=3+590|uni0F0B=4@-30,0+160]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F40,U+0F74,U+0F7C,U+0F56,U+0F39,U+0F0B:[uni0F400F74=0+680|uni0F7C=0+0|uni0F56=3+610|uni0F39=3+0|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F40,U+0F74,U+0F72,U+0F42,U+0F66,U+0F0B:[uni0F400F740F72=0+680|uni0F42=3+680|uni0F66=4+680|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F40,U+0F74,U+0F7A,U+0F66,U+0F0B:[uni0F400F74=0+680|uni0F7A=0+0|uni0F66=3+680|uni0F0B=4+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F40,U+0FB3,U+0F74,U+0F7A,U+0F56,U+0F66,U+0F0B:[uni0F400FB30F740F7A=0+660|uni0F56=4+610|uni0F66=5+680|uni0F0B=6+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F40,U+0FB3,U+0F74,U+0F7C,U+0F42,U+0F0B:[uni0F400FB30F74=0+660|uni0F7C=0+0|uni0F42=4+680|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F51,U+0F40,U+0F7C,U+0F7C,U+0F42,U+0F0B:[uni0F51=0+600|uni0F400F7D=1+680|uni0F42=4+680|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F51,U+0F40,U+0F7C,U+0F7C,U+0F62,U+0F0B:[uni0F51=0+600|uni0F400F7D=1+680|uni0F62=4+620|uni0F0B=5@-65,0+130]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F51,U+0F40,U+0FB1,U+0F7C,U+0F72,U+0F62,U+0F0B:[uni0F51=0+600|uni0F400FB10F7C0F72=1+660|uni0F62=5+620|uni0F0B=6@-65,0+130]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F66,U+0F90,U+0FB1,U+0F74,U+0F7A,U+0F0B:[uni0F660F900FB10F74=0+680|uni0F7A=0+0|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F56,U+0F66,U+0F90,U+0FB1,U+0F7A,U+0F7A,U+0F51,U+0F0B:[uni0F56=0+610|uni0F660F900FB1=1+660|uni0F7B=1+0|uni0F51=6+600|uni0F0B=7@-70,0+106]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F56,U+0F66,U+0F90,U+0FB1,U+0F7A,U+0F7A,U+0F7A,U+0F51,U+0F0B:[uni0F56=0+610|uni0F660F900FB1=1+660|uni0F7B0F7A=1+0|uni0F51=7+600|uni0F0B=8@-70,0+106]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F41,U+0F58,U+0F66,U+0F74,U+0F7E,U+0F0B:[uni0F41=0+660|uni0F58=1+660|uni0F660F740F7E=2+680|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F41,U+0F74,U+0F7C,U+0F66,U+0F39,U+0F0B:[uni0F410F74=0+680|uni0F7C=0+0|uni0F66=3+680|uni0F39=3+0|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F41,U+0FB1,U+0F74,U+0F7C,U+0F42,U+0F0B:[uni0F410FB10F74=0+670|uni0F7C=0+0|uni0F42=4+680|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F41,U+0FB2,U+0F74,U+0F7A,U+0F51,U+0F0B:[uni0F410FB20F74=0+660|uni0F7A=0+0|uni0F51=4+600|uni0F0B=5@-70,0+106]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F41,U+0FB2,U+0F74,U+0F72,U+0F44,U+0F0B:[uni0F410FB20F74=0+660|uni0F72=0+0|uni0F44=4+560|uni0F0B=5@-20,0+110]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F41,U+0FB2,U+0F74,U+0F7C,U+0F51,U+0F0B:[uni0F410FB20F74=0+660|uni0F7C=0+0|uni0F51=4+600|uni0F0B=5@-70,0+106]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F41,U+0FB2,U+0F74,U+0F7E,U+0F51,U+0F0B:[uni0F410FB20F740F7E=0+660|uni0F51=4+600|uni0F0B=5@-70,0+106]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F58,U+0F41,U+0FB1,U+0F7A,U+0F7A,U+0F7A,U+0F53,U+0F0B:[uni0F58=0+660|uni0F410FB1=1+680|uni0F7B0F7A=1+0|uni0F53=6+590|uni0F0B=7@-30,0+160]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F60,U+0F41,U+0F7C,U+0F7C,U+0F62,U+0F0B:[uni0F60=0+600|uni0F410F7D=1+660|uni0F62=4+620|uni0F0B=5@-65,0+130]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F42,U+0F74,U+0F7C,U+0F42,U+0F0B:[uni0F420F74=0+680|uni0F7C=0+0|uni0F42=3+680|uni0F0B=4+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F42,U+0FB1,U+0F74,U+0F72,U+0F42,U+0F0B:[uni0F420FB10F74=0+700|uni0F72=0+0|uni0F42=4+680|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F42,U+0FB2,U+0F74,U+0F72,U+0F53,U+0F0B:[uni0F420FB20F74=0+680|uni0F72=0+0|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F42,U+0FB2,U+0F74,U+0F72,U+0F0B:[uni0F420FB20F74=0+680|uni0F72=0+0|uni0F0B=4+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F42,U+0FB2,U+0F74,U+0F7C,U+0F53,U+0F0B:[uni0F420FB20F74=0+680|uni0F7C=0+0|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F42,U+0FB2,U+0F74,U+0F7C,U+0F56,U+0F0B:[uni0F420FB20F74=0+680|uni0F7C=0+0|uni0F56=4+610|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F42,U+0FB2,U+0F7C,U+0F72,U+0F53,U+0F0B:[uni0F420FB2=0+680|uni0F7C0F72=0+0|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F42,U+0FB2,U+0F7C,U+0F7A,U+0F62,U+0F0B:[uni0F420FB2=0+680|uni0F7C0F7A=0+0|uni0F62=4+620|uni0F0B=5@-65,0+130]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F51,U+0F42,U+0F74,U+0F72,U+0F42,U+0F0B:[uni0F51=0+600|uni0F420F740F72=1+680|uni0F42=4+680|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F51,U+0F42,U+0F74,U+0F7A,U+0F53,U+0F0B:[uni0F51=0+600|uni0F420F74=1+680|uni0F7A=1+0|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F51,U+0F42,U+0F74,U+0F7A,U+0F42,U+0F66,U+0F0B:[uni0F51=0+600|uni0F420F74=1+680|uni0F7A=1+0|uni0F42=4+680|uni0F66=5+680|uni0F0B=6+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F51,U+0F42,U+0FB3,U+0F7C,U+0F7A,U+0F44,U+0F0B:[uni0F51=0+600|uni0F420FB3=1+680|uni0F7C0F7A=1+0|uni0F44=5+560|uni0F0B=6@-20,0+110]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F58,U+0F42,U+0F7C,U+0F7C,U+0F53,U+0F0B:[uni0F58=0+660|uni0F420F7D=1+680|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F44,U+0F74,U+0F72,U+0F42,U+0F0B:[uni0F440F74=0+610|uni0F72=0+0|uni0F42=3+680|uni0F0B=4+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F51,U+0F42,U+0FB2,U+0F74,U+0F7C,U+0F56,U+0F0B:[uni0F51=0+600|uni0F420FB20F74=1+680|uni0F7C=1+0|uni0F56=5+610|uni0F0B=6+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F56,U+0F45,U+0F74,U+0F72,U+0F42,U+0F0B:[uni0F56=0+610|uni0F450F740F72=1+630|uni0F42=4+680|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F56,U+0F45,U+0F74,U+0F72,U+0F66,U+0F0B:[uni0F56=0+610|uni0F450F740F72=1+630|uni0F66=4+680|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F56,U+0F45,U+0FB2,U+0F74,U+0F42,U+0F0B:[uni0F56=0+610|uni0F450FB20F74=1+640|uni0F42=4+680|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F56,U+0F45,U+0F74,U+0F72,U+0F0B:[uni0F56=0+610|uni0F450F740F72=1+630|uni0F0B=4+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F56,U+0F45,U+0F74,U+0F7E,U+0F0B:[uni0F56=0+610|uni0F450F740F7E=1+630|uni0F0B=4+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F46,U+0F74,U+0F72,U+0F63,U+0F0B:[uni0F460F74=0+650|uni0F72=0+0|uni0F63=3+700|uni0F0B=4+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F46,U+0F74,U+0F7C,U+0F51,U+0F0B:[uni0F460F74=0+650|uni0F7C=0+0|uni0F51=3+600|uni0F0B=4@-70,0+106]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F46,U+0F74,U+0F7C,U+0F51,U+0F0B:[uni0F460F74=0+650|uni0F7C=0+0|uni0F51=3+600|uni0F0B=4@-70,0+106]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F46,U+0F74,U+0F7E,U+0F51,U+0F0B:[uni0F460F740F7E=0+650|uni0F51=3+600|uni0F0B=4@-70,0+106]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F46,U+0F39,U+0F74,U+0F7C,U+0F51,U+0F0B:[uni0F46=0+620|uni0F39=0+0|uni0F74=0+0|uni0F7C=0+0|uni0F51=4+600|uni0F0B=5@-70,0+106]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F46,U+0FB2,U+0F74,U+0F72,U+0F53,U+0F0B:[uni0F460FB20F740F72=0+660|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F46,U+0FB2,U+0F74,U+0F7C,U+0F63,U+0F0B:[uni0F460FB20F74=0+660|uni0F7C=0+0|uni0F63=4+700|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F58,U+0F46,U+0F7C,U+0F7A,U+0F53,U+0F0B:[uni0F58=0+660|uni0F46=1+620|uni0F7C0F7A=1+0|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F60,U+0F46,U+0FB1,U+0F7C,U+0F72,U+0F62,U+0F0B:[uni0F60=0+600|uni0F460FB10F7C0F72=1+660|uni0F62=5+620|uni0F0B=6@-65,0+130]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F47,U+0F7C,U+0F7C,U+0F0B:[uni0F470F7D=0+570|uni0F0B=3+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F62,U+0F97,U+0F74,U+0F7A,U+0F53,U+0F39,U+0F0B:[uni0F620F970F74=0+600|uni0F7A=0+0|uni0F53=4+590|uni0F39=4+0|uni0F0B=6+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F49,U+0F74,U+0F72,U+0F0B:[uni0F490F74=0+580|uni0F72=0+0|uni0F0B=3+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F49,U+0F74,U+0F72,U+0F44,U+0F0B:[uni0F490F74=0+580|uni0F72=0+0|uni0F44=3+560|uni0F0B=4@-20,0+110]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F58,U+0F49,U+0F72,U+0F7E,U+0F51,U+0F0B:[uni0F58=0+660|uni0F49=1+580|uni0F720F7E=1+0|uni0F51=4+600|uni0F0B=5@-70,0+106]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F42,U+0F4F,U+0F74,U+0F72,U+0F42,U+0F0B:[uni0F42=0+680|uni0F4F0F740F72=1+600|uni0F42=4+680|uni0F0B=5+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F56,U+0F4F,U+0F44,U+0F7C,U+0F7E,U+0F66,U+0F0B:[uni0F56=0+610|uni0F4F=1+560|uni0F44=2+560|uni0F7C0F7E=2+0|uni0F66=5+680|uni0F0B=6+190]
+../fonts/a02a7f0ad42c2922cb37ad1358c9df4eb81f1bca.ttf::U+0F50,U+0F39,U+0F74,U+0F7A,U+0F4A,U+0F0B:[uni0F50=0+600|uni0F39=0+0|uni0F74=0+0|uni0F7A=0+0|uni0F4A=4+590|uni0F0B=5+190]
diff --git a/test/shaping/data/in-house/tests/tibetan-contractions-2.tests b/test/shaping/data/in-house/tests/tibetan-contractions-2.tests
new file mode 100644
index 0000000..b44445c
--- /dev/null
+++ b/test/shaping/data/in-house/tests/tibetan-contractions-2.tests
@@ -0,0 +1,53 @@
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F50,U+0F74,U+0F72,U+0F53,U+0F0B:[uni0F500F74=0+600|uni0F72=0+0|uni0F53=3+590|uni0F0B=4@-30,0+160]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F58,U+0F50,U+0F74,U+0F7C,U+0F44,U+0F0B:[uni0F58=0+660|uni0F500F74=1+600|uni0F7C=1+0|uni0F44=4+560|uni0F0B=5@-20,0+110]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F58,U+0F50,U+0F7C,U+0F7A,U+0F44,U+0F0B:[uni0F58=0+660|uni0F50=1+600|uni0F7C0F7A=1+0|uni0F44=4+560|uni0F0B=5@-20,0+110]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F58,U+0F50,U+0F7C,U+0F72,U+0F66,U+0F0B:[uni0F58=0+660|uni0F50=1+600|uni0F7C0F72=1+0|uni0F66=4+680|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F51,U+0F74,U+0F62,U+0FB2,U+0F7C,U+0F51,U+0F0B:[uni0F510F74=0+600|uni0F620FB2=2+600|uni0F7C=2+0|uni0F51=5+600|uni0F0B=6@-70,0+106]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F51,U+0FB2,U+0F74,U+0F72,U+0F42,U+0F0B:[uni0F510FB20F74=0+600|uni0F72=0+0|uni0F42=4+680|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F42,U+0F51,U+0F74,U+0F7A,U+0F53,U+0F0B:[uni0F42=0+680|uni0F510F740F7A=1+600|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F56,U+0F51,U+0F7B,U+0F42,U+0F66,U+0F0B:[uni0F56=0+610|uni0F510F7B=1+579|uni0F42=3+680|uni0F66=4+680|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F60,U+0F51,U+0F74,U+0F7A,U+0F51,U+0F0B:[uni0F60=0+600|uni0F510F740F7A=1+600|uni0F51=4+600|uni0F0B=5@-70,0+106]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F62,U+0FA1,U+0F7C,U+0F7A,U+0F0B:[uni0F620FA10F7C0F7A=0+580|uni0F0B=4+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F66,U+0FA1,U+0F74,U+0F72,U+0F56,U+0F0B:[uni0F660FA10F74=0+680|uni0F72=0+0|uni0F56=4+610|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F53,U+0F74,U+0F7C,U+0F42,U+0F66,U+0F0B:[uni0F530F74=0+600|uni0F7C=0+0|uni0F42=3+680|uni0F66=4+680|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F53,U+0F74,U+0F7C,U+0F62,U+0F0B:[uni0F530F74=0+600|uni0F7C=0+0|uni0F62=3+620|uni0F0B=4@-65,0+130]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F42,U+0F53,U+0FB1,U+0F7C,U+0F7E,U+0F62,U+0F0B:[uni0F42=0+680|uni0F530FB1=1+600|uni0F7C0F7E=1+0|uni0F62=5+620|uni0F0B=6@-65,0+130]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F51,U+0F54,U+0F74,U+0F7C,U+0F42,U+0F66,U+0F0B:[uni0F51=0+600|uni0F540F74=1+610|uni0F7C=1+0|uni0F42=4+680|uni0F66=5+680|uni0F0B=6+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F56,U+0FB1,U+0F74,U+0F7E,U+0F56,U+0F0B:[uni0F560FB10F74=0+620|uni0F7E=0+0|uni0F56=4+610|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F56,U+0FB3,U+0F74,U+0F7C,U+0F53,U+0F0B:[uni0F560FB30F74=0+650|uni0F7C=0+0|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F56,U+0FB3,U+0F7C,U+0F7C,U+0F53,U+0F0B:[uni0F560FB3=0+650|uni0F7D=0+0|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F51,U+0F56,U+0F74,U+0F7C,U+0F51,U+0F0B:[uni0F51=0+600|uni0F560F74=1+610|uni0F7C=1+0|uni0F51=4+600|uni0F0B=5@-70,0+106]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F51,U+0F56,U+0F74,U+0F7C,U+0F56,U+0F66,U+0F0B:[uni0F51=0+600|uni0F560F74=1+610|uni0F7C=1+0|uni0F56=4+610|uni0F66=5+680|uni0F0B=6+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F58,U+0F74,U+0F7A,U+0F42,U+0F66,U+0F0B:[uni0F580F74=0+680|uni0F7A=0+0|uni0F42=3+680|uni0F66=4+680|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F58,U+0F74,U+0F72,U+0F42,U+0F0B:[uni0F580F74=0+680|uni0F72=0+0|uni0F42=3+680|uni0F0B=4+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F58,U+0F74,U+0F7A,U+0F42,U+0F66,U+0F0B:[uni0F580F74=0+680|uni0F7A=0+0|uni0F42=3+680|uni0F66=4+680|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F58,U+0F74,U+0F7A,U+0F53,U+0F0B:[uni0F580F74=0+680|uni0F7A=0+0|uni0F53=3+590|uni0F0B=4@-30,0+160]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F58,U+0F9F,U+0F7C,U+0F7A,U+0F42,U+0F0B:[uni0F580F9F0F7C0F7A=0+660|uni0F42=4+680|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F58,U+0F7C,U+0F7A,U+0F44,U+0F0B:[uni0F58=0+660|uni0F7C0F7A=0+0|uni0F44=3+560|uni0F0B=4@-20,0+110]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F42,U+0F59,U+0F74,U+0F7C,U+0F62,U+0F0B:[uni0F42=0+680|uni0F590F74=1+620|uni0F7C=1+0|uni0F62=4+620|uni0F0B=5@-65,0+130]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F58,U+0F5A,U+0FAE,U+0F74,U+0F7E,U+0F66,U+0F0B:[uni0F58=0+660|uni0F5A0FAE0F740F7E=1+620|uni0F66=5+680|uni0F0B=6+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F62,U+0FAB,U+0F74,U+0F7A,U+0F66,U+0F0B:[uni0F620FAB0F74=0+660|uni0F7A=0+0|uni0F66=4+680|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F62,U+0FAB,U+0F74,U+0F7A,U+0F53,U+0F0B:[uni0F620FAB0F74=0+660|uni0F7A=0+0|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F5E,U+0F74,U+0F7C,U+0F63,U+0F0B:[uni0F5E0F74=0+660|uni0F7C=0+0|uni0F63=3+700|uni0F0B=4+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F42,U+0F5E,U+0F74,U+0F7C,U+0F42,U+0F0B:[uni0F42=0+680|uni0F5E0F74=1+660|uni0F7C=1+0|uni0F42=4+680|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F42,U+0F5E,U+0F74,U+0F7C,U+0F58,U+0F66,U+0F0B:[uni0F42=0+680|uni0F5E0F74=1+660|uni0F7C=1+0|uni0F58=4+660|uni0F66=5+680|uni0F0B=6+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F42,U+0F5F,U+0F74,U+0F7C,U+0F0B:[uni0F42=0+680|uni0F5F0F74=1+610|uni0F7C=1+0|uni0F0B=4+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F42,U+0F5F,U+0F74,U+0F72,U+0F44,U+0F0B:[uni0F42=0+680|uni0F5F0F740F72=1+610|uni0F44=4+560|uni0F0B=5@-20,0+110]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F42,U+0F5F,U+0F74,U+0F7A,U+0F62,U+0F0B:[uni0F42=0+680|uni0F5F0F74=1+610|uni0F7A=1+0|uni0F62=4+620|uni0F0B=5@-65,0+130]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F42,U+0F5F,U+0F74,U+0F7A,U+0F62,U+0F0B:[uni0F42=0+680|uni0F5F0F74=1+610|uni0F7A=1+0|uni0F62=4+620|uni0F0B=5@-65,0+130]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F42,U+0F5F,U+0F74,U+0F7A,U+0F51,U+0F0B:[uni0F42=0+680|uni0F5F0F74=1+610|uni0F7A=1+0|uni0F51=4+600|uni0F0B=5@-70,0+106]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F60,U+0F7C,U+0F7A,U+0F62,U+0F0B:[uni0F60=0+600|uni0F7C0F7A=0+0|uni0F62=3+620|uni0F0B=4@-65,0+130]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F61,U+0F72,U+0F7A,U+0F0B:[uni0F61=0+700|uni0F720F7A=0+0|uni0F0B=3+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F61,U+0F7A,U+0F7A,U+0F66,U+0F0B:[uni0F61=0+700|uni0F7B=0+0|uni0F66=3+680|uni0F0B=4+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F62,U+0F72,U+0F53,U+0F7C,U+0F7A,U+0F0B:[uni0F620F72=0+620|uni0F530F7C0F7A=2+590|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F62,U+0F74,U+0F7C,U+0F63,U+0F0B:[uni0F620F74=0+601|uni0F7C=0+0|uni0F63=3+700|uni0F0B=4+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F66,U+0F7A,U+0F7E,U+0F53,U+0F0B:[uni0F66=0+680|uni0F7A0F7E=0+0|uni0F53=3+590|uni0F0B=4@-30,0+160]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F66,U+0F7A,U+0F7E,U+0F51,U+0F60,U+0F0B:[uni0F66=0+680|uni0F7A0F7E=0+0|uni0F51=3+600|uni0F60=4+600|uni0F0B=5@-40,0+150]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F66,U+0F7C,U+0F7C,U+0F56,U+0F0B:[uni0F660F7D=0+680|uni0F56=3+610|uni0F0B=4+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F66,U+0F7C,U+0F7C,U+0F62,U+0F0B:[uni0F660F7D=0+680|uni0F62=3+620|uni0F0B=4@-65,0+130]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F66,U+0FB2,U+0F7C,U+0F7A,U+0F66,U+0F0B:[uni0F660FB2=0+680|uni0F7C0F7A=0+0|uni0F66=4+680|uni0F0B=5+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F66,U+0FB3,U+0F7C,U+0F7C,U+0F51,U+0F0B:[uni0F660FB3=0+680|uni0F7D=0+0|uni0F51=4+600|uni0F0B=5@-70,0+106]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F66,U+0FB3,U+0F7C,U+0F7C,U+0F53,U+0F0B:[uni0F660FB3=0+680|uni0F7D=0+0|uni0F53=4+590|uni0F0B=5@-30,0+160]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F56,U+0F66,U+0F99,U+0F7C,U+0F7E,U+0F51,U+0F66,U+0F0B:[uni0F56=0+610|uni0F660F99=1+670|uni0F7C0F7E=1+0|uni0F51=5+600|uni0F66=6+680|uni0F0B=7+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F63,U+0FB7,U+0FB1,U+0F7C,U+0F42,U+0F66,U+0F0B:[uni0F630FB70FB1=0+680|uni0F7C=0+0|uni0F42=4+680|uni0F66=5+680|uni0F0B=6+190]
+../fonts/2de1ab4907ab688c0cfc236b0bf51151db38bf2e.ttf::U+0F68,U+0FB1,U+0F7C,U+0F53,U+0F0B:[uni0F680FB1=0+740|uni0F7C=0+0|uni0F53=3+590|uni0F0B=4@-30,0+160]
diff --git a/test/shaping/data/in-house/tests/tibetan-vowels.tests b/test/shaping/data/in-house/tests/tibetan-vowels.tests
new file mode 100644
index 0000000..0bb0743
--- /dev/null
+++ b/test/shaping/data/in-house/tests/tibetan-vowels.tests
@@ -0,0 +1,11 @@
+../fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf::U+0F68,U+0F72:[uni0F680F72=0+730]
+../fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf::U+0F68,U+0F74:[uni0F680F74=0+730]
+../fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf::U+0F68,U+0F7A:[uni0F680F7A=0+730]
+../fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf::U+0F68,U+0F7C:[uni0F680F7C=0+730]
+../fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf::U+0F68,U+0F71,U+0F72:[uni0F680F710F72=0+720]
+../fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf::U+0F68,U+0F71,U+0F74:[uni0F680F75=0+720]
+../fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf::U+0F68,U+0F7B:[uni0F680F7B=0+720]
+../fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf::U+0F68,U+0F7D:[uni0F680F7D=0+730]
+../fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf::U+0F68,U+0F7E:[uni0F680F7E=0+730]
+../fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf::U+0F68,U+0F7F:[uni0F68=0+730|uni0F7F=0+408]
+../fonts/82f4f3b57bb55344e72e70231380202a52af5805.ttf::U+0F00:[uni0F00=0+730]
diff --git a/test/shaping/data/in-house/tests/use-marchen.tests b/test/shaping/data/in-house/tests/use-marchen.tests
new file mode 100644
index 0000000..850c3e7
--- /dev/null
+++ b/test/shaping/data/in-house/tests/use-marchen.tests
@@ -0,0 +1,35 @@
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8F:[u11C8F=0+3000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C71:[u11C71=0+1600]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11CB5:[u11C8A=0+2000|u11CB5=0@-2000,0+0]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C84,U+11C71:[u11C84=0+2200|u11C71=1+1600]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C7E,U+11C8A:[u11C7E=0+2600|u11C8A=1+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11C92,U+11CA9:[u11C8A.11C92.11CA9=0+2600]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11C94,U+11CA9:[u11C8A.11C94.11CA9=0+2600]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C92,U+11CA9:[u11C8D.11C92.11CA9=0+2600]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C94,U+11CA9:[u11C8D.11C94.11CA9=0+2600]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C9E,U+11CA9:[u11C8D.11C9E.11CA9=0+3200]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA0,U+11CA9:[u11C8D.11CA0.11CA9=0+3000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C92,U+11CAA:[u11C8D.11C92.11CAA=0+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C94,U+11CAA:[u11C8D.11C94.11CAA=0+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C9D,U+11CAA:[u11C8D.11C9D.11CAA=0+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C9E,U+11CAA:[u11C8D.11C9E.11CAA=0+2600]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA0,U+11CAA:[u11C8D.11CA0.11CAA=0+2400]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C72,U+11CAA:[u11C80=0+2400|u11C72.11CAA=1+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8C,U+11CB1,U+11C8D:[u11C8C.11CB1=0+2793|u11C8D=2+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C7C,U+11CB3:[u11C80=0+2400|u11C7C.11CB3=1+2200]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C7F,U+11CB2,U+11C7D:[u11C7F.11CB2=0+2400|u11C7D=2+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CB2,U+11C81:[u11C8D.11CB2=0+2000|u11C81=2+2400]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8C,U+11CB4,U+11C74:[u11C8C.11CB4=0+2800|u11C74=2+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11CA1,U+11CA9,U+11C71:[u11C8A.11CA1.11CA9=0+3000|u11C71=3+1600]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA1,U+11CA9,U+11C71:[u11C8D.11CA1.11CA9=0+3000|u11C71=3+1600]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA1,U+11CAA,U+11C71:[u11C8D.11CA1.11CAA=0+2400|u11C71=3+1600]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8F,U+11CB0,U+11CB4,U+11CB6:[u11C8F.11CB0.11CB4=0+3600|u11CB6=0@-3200,0+0]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8E,U+11CB0,U+11CB2,U+11CB5:[u11C8E.11CB0.11CB2=0+2000|u11CB5=0@-2000,0+0]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C74,U+11C89,U+11CB2,U+11C75:[u11C74=0+2000|u11C89.11CB2=1+2000|u11C75=3+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C7C,U+11CAA,U+11CB2,U+11C75:[u11C7C.11CAA.11CB2=0+2200|u11C75=3+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C81,U+11C74,U+11CB2,U+11C8B:[u11C81=0+2400|u11C74.11CB2=1+2000|u11C8B=3+2400]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8B,U+11CB3,U+11C74,U+11C8D:[u11C8B.11CB3=0+2400|u11C74=2+2000|u11C8D=3+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C83,U+11CB4,U+11C74,U+11C8D:[u11C83.11CB4=0+2800|u11C74=2+2000|u11C8D=3+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8B,U+11CB3,U+11C74,U+11C8D,U+11C71:[u11C8B.11CB3=0+2400|u11C74=2+2000|u11C8D=3+2000|u11C71=4+1600]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C76,U+11CB1,U+11C75,U+11C8D:[u11C80=0+2400|u11C76.11CB1=1+3200|u11C75=3+2000|u11C8D=4+2000]
+../fonts/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C8D,U+11C94,U+11CAA,U+11CB1,U+11C74,U+11C8D:[u11C80=0+2400|u11C8D.11C94.11CAA.11CB1.shorti=1+2600|u11C74=5+2000|u11C8D=6+2000]
diff --git a/test/shaping/data/in-house/tests/use-syllable.tests b/test/shaping/data/in-house/tests/use-syllable.tests
new file mode 100644
index 0000000..213b848
--- /dev/null
+++ b/test/shaping/data/in-house/tests/use-syllable.tests
@@ -0,0 +1,7 @@
+../fonts/96490dd2ff81233b335a650e7eb660e0e7b2eeea.ttf::U+AA00,U+AA2D,U+AA29:[a_cham=0+1121|uSign_cham=0@14,0+0|.notdef=0+600]
+../fonts/e68a88939e0f06e34d2bc911f09b70890289c8fd.ttf::U+AA00,U+AA34,U+AA36:[raMedial_cham_pre=0+400|a_cham=0+1121|waMedial_cham=0@-32,0+0]
+../fonts/e68a88939e0f06e34d2bc911f09b70890289c8fd.ttf::U+AA00,U+AA35,U+AA33:[a_cham=0+1121|laMedial_cham=0@-32,0+0|yaMedial_cham=0+542]
+../fonts/e68a88939e0f06e34d2bc911f09b70890289c8fd.ttf::U+AA00,U+AA35,U+AA36:[a_cham=0+1121|laMedial_waMedial_cham=0@43,0+0]
+../fonts/074a5ae6b19de8f29772fdd5df2d3d833f81f5e6.ttf:--no-glyph-names:U+11320,U+20F0,U+11367:[3=0+502|1=0@33,0+0|4=0@300,8+0]
+../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]
diff --git a/test/shaping/data/in-house/tests/use.tests b/test/shaping/data/in-house/tests/use.tests
new file mode 100644
index 0000000..172946c
--- /dev/null
+++ b/test/shaping/data/in-house/tests/use.tests
@@ -0,0 +1,5 @@
+../fonts/fbb6c84c9e1fe0c39e152fbe845e51fd81f6748e.ttf::U+1B1B,U+1B44,U+1B13,U+1B3E:[gid3=0+990|gid7=0+2473|gid5=0@-293,-400+0]
+../fonts/4cce528e99f600ed9c25a2b69e32eb94a03b4ae8.ttf::U+1A48,U+1A58,U+1A25,U+1A48,U+1A58,U+1A25,U+1A6E,U+1A63:[uni1A48=0+1212|uni1A25=0+1912|uni1A58=0+0|uni1A48=3+1212|uni1A6E=3+0|uni1A25=3+1912|uni1A58=3+0|uni1A63=3+1212]
+../fonts/f518eb6f6b5eec2946c9fbbbde44e45d46f5e2ac.ttf::U+1A48,U+1A58,U+1A25,U+1A48,U+1A58,U+1A25,U+1A6E,U+1A63:[uni1A48=0+1212|uni1A25=0+1912|uni1A58=0+0|uni1A48=3+1212|uni1A6E=3+1211|uni1A25=3+1912|uni1A58=3+0|uni1A63=3+1212]
+../fonts/6ff0fbead4462d9f229167b4e6839eceb8465058.ttf:--font-funcs=ot:U+11103,U+11128:[u11103=0+837|u11128=0+0]
+../fonts/573d3a3177c9a8646e94c8a0d7b224334340946a.ttf:--font-funcs=ft:U+11410,U+11442,U+11411,U+11440,U+11443,U+11410,U+11442,U+11411,U+11441,U+11443:[E_dv.alt=0+275|Ga.icd=0+367|Gha.diag=0@100,0+386|AA_dv.alt=0+208|Candrabindu=0@17,-8+0|E_dv.alt=5+275|Ga.icd=5+367|Gha.diag=5@100,0+386|AU_dv_part.alt=5+213|Candrabindu.sm=5@-52,179+0]
diff --git a/test/shaping/data/in-house/tests/variations-rvrn.tests b/test/shaping/data/in-house/tests/variations-rvrn.tests
new file mode 100644
index 0000000..78ebb92
--- /dev/null
+++ b/test/shaping/data/in-house/tests/variations-rvrn.tests
@@ -0,0 +1,100 @@
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=1:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=11:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=21:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=31:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=41:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=51:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=61:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=71:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=81:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=91:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=101:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=111:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=121:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=131:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=141:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=151:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=161:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=171:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=181:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=191:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=201:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=211:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=221:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=231:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=241:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=251:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=261:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=271:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=281:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=291:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=301:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=311:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=321:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=331:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=341:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=351:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=361:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=371:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=381:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=391:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=401:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=411:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=421:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=431:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=441:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=451:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=461:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=471:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=481:U+0072:[rvrn_base=0+1529]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=491:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=501:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=511:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=521:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=531:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=541:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=551:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=561:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=571:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=581:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=591:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=601:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=611:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=621:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=631:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=641:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=651:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=661:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=671:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=681:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=691:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=701:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=711:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=721:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=731:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=741:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=751:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=761:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=771:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=781:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=791:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=801:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=811:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=821:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=831:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=841:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=851:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=861:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=871:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=881:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=891:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=901:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=911:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=921:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=931:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=941:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=951:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=961:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=971:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=981:U+0072:[rvrn_subst=0+1825]
+../fonts/d23d76ea0909c14972796937ba072b5a40c1e257.ttf:--variations=FVTT=991:U+0072:[rvrn_subst=0+1825]
diff --git a/test/shaping/data/in-house/tests/vertical.tests b/test/shaping/data/in-house/tests/vertical.tests
new file mode 100644
index 0000000..17df28e
--- /dev/null
+++ b/test/shaping/data/in-house/tests/vertical.tests
@@ -0,0 +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]
diff --git a/test/shaping/data/in-house/tests/zero-width-marks.tests b/test/shaping/data/in-house/tests/zero-width-marks.tests
new file mode 100644
index 0000000..1a3474a
--- /dev/null
+++ b/test/shaping/data/in-house/tests/zero-width-marks.tests
@@ -0,0 +1,11 @@
+../fonts/bb9473d2403488714043bcfb946c9f78b86ad627.ttf::U+1030:[circledash=0+636|u1030.med=0@-162,0+0]
+../fonts/8454d22037f892e76614e1645d066689a0200e61.ttf::U+05E0,U+05B8,U+0591,U+05DA,U+05B0:[uni05DA05B0=3+991|uni2009=0+200|uni0591=0@75,0+0|uni05B8=0@495,0+0|uni05E0=0+683]
+../fonts/45855bc8d46332b39c4ab9e2ee1a26b1f896da6b.ttf::U+0E01,U+0E34,U+0E01:[gid1=0+1264|gid2=0@20,0+0|gid1=2+1264]
+../fonts/7a37dc4d5bf018456aea291cee06daf004c0221c.ttf::U+0E01,U+0E34,U+0E01:[gid1=0+1264|gid2=0@20,0+1000|gid1=2+1264]
+../fonts/8099955657a54e9ee38a6ba1d6f950ce58e3cc25.ttf::U+0E01,U+0E34,U+0E01:[gid1=0+1264|gid2=0+0|gid1=2+1264]
+../fonts/bb0c53752e85c3d28973ebc913287b8987d3dfe8.ttf::U+0E01,U+0E34,U+0E01:[gid1=0+1264|gid2=0+0|gid1=2+1264]
+../fonts/ffa0f5d2d9025486d8469d8b1fdd983e7632499b.ttf::U+0058,U+0303,U+0078,U+0303,U+006A,U+006A,U+006A,U+0303,U+006A,U+0303,U+006A,U+006A:[gid1=0+1200|gid6=0@-1029,340+0|gid3=2+1083|gid6=2@-992,0+0|gid2=4+528|gid2=5+528|gid5=6+528|gid6=6@-693,0+0|gid5=8+528|gid6=8@-693,0+0|gid2=10+528|gid2=11+528]
+../fonts/cc5f3d2d717fb6bd4dfae1c16d48a2cb8e12233b.ttf::U+0058,U+0303,U+0078,U+0303,U+006A,U+006A,U+006A,U+0303,U+006A,U+0303,U+006A,U+006A:[gid1=0+1200|gid6=0@-1029,340+1200|gid3=2+1083|gid6=2@-992,0+1200|gid2=4+528|gid2=5+528|gid5=6+528|gid6=6@-693,0+1200|gid5=8+528|gid6=8@-693,0+1200|gid2=10+528|gid2=11+528]
+../fonts/fcdcffbdf1c4c97c05308d7600e4c283eb47dbca.ttf::U+0058,U+0303,U+0078,U+0303,U+006A,U+006A,U+006A,U+0303,U+006A,U+0303,U+006A,U+006A:[gid1=0+1200|gid6=0+0|gid3=2+1083|gid6=2+0|gid2=4+528|gid2=5+528|gid5=6+528|gid6=6+0|gid5=8+528|gid6=8+0|gid2=10+528|gid2=11+528]
+../fonts/56cfd0e18d07f41c38e9598545a6d369127fc6f9.ttf::U+0058,U+0303,U+0078,U+0303,U+006A,U+006A,U+006A,U+0303,U+006A,U+0303,U+006A,U+006A:[gid1=0+1200|gid6=0@-1029,340+0|gid3=2+1083|gid6=2@-992,0+0|gid2=4+528|gid2=5+528|gid5=6+528|gid6=6@-693,0+0|gid5=8+528|gid6=8@-693,0+0|gid2=10+528|gid2=11+528]
+../fonts/a98e908e2ed21b22228ea59ebcc0f05034c86f2e.ttf::U+0041,U+0042,U+0041:[A=0+1368|B=1+0|A=2+1368]
diff --git a/test/shaping/data/text-rendering-tests/COPYING b/test/shaping/data/text-rendering-tests/COPYING
new file mode 100644
index 0000000..7758b2e
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/COPYING
@@ -0,0 +1,13 @@
+Copyright 2016 Unicode Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the “License”);
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an “AS IS” BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/test/shaping/data/text-rendering-tests/DISABLED b/test/shaping/data/text-rendering-tests/DISABLED
new file mode 100644
index 0000000..dbe870f
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/DISABLED
@@ -0,0 +1,35 @@
+# 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
+
+# Rounding differences
+tests/SHARAN-1.tests
+tests/SHBALI-1.tests
+tests/SHBALI-2.tests
+tests/SHKNDA-2.tests
+tests/SHKNDA-3.tests
diff --git a/test/shaping/data/text-rendering-tests/Makefile.am b/test/shaping/data/text-rendering-tests/Makefile.am
new file mode 100644
index 0000000..cad0358
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/Makefile.am
@@ -0,0 +1,26 @@
+# Process this file with automake to produce Makefile.in
+
+NULL =
+
+# Convenience targets:
+lib:
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
+
+update:
+	(cd $(srcdir) && ./update.sh)
+
+EXTRA_DIST = \
+	README \
+	COPYING \
+	update.sh \
+	extract-tests.py \
+	fonts \
+	$(TESTS) \
+	$(NULL)
+
+TEST_EXTENSIONS = .tests
+TESTS_LOG_COMPILER = $(srcdir)/../../run-tests.py $(top_builddir)/util/hb-shape$(EXEEXT)
+
+include Makefile.sources
+
+-include $(top_srcdir)/git.mk
diff --git a/test/shaping/data/text-rendering-tests/Makefile.sources b/test/shaping/data/text-rendering-tests/Makefile.sources
new file mode 100644
index 0000000..f77c1bc
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/Makefile.sources
@@ -0,0 +1,66 @@
+TESTS = \
+	tests/AVAR-1.tests \
+	tests/CFF-1.tests \
+	tests/CFF2-1.tests \
+	tests/CFF-2.tests \
+	tests/CMAP-1.tests \
+	tests/CMAP-2.tests \
+	tests/CVAR-1.tests \
+	tests/CVAR-2.tests \
+	tests/GLYF-1.tests \
+	tests/GPOS-1.tests \
+	tests/GPOS-2.tests \
+	tests/GPOS-3.tests \
+	tests/GPOS-4.tests \
+	tests/GPOS-5.tests \
+	tests/GSUB-1.tests \
+	tests/GSUB-2.tests \
+	tests/GVAR-1.tests \
+	tests/GVAR-2.tests \
+	tests/GVAR-3.tests \
+	tests/GVAR-4.tests \
+	tests/GVAR-5.tests \
+	tests/GVAR-6.tests \
+	tests/GVAR-7.tests \
+	tests/GVAR-8.tests \
+	tests/GVAR-9.tests \
+	tests/HVAR-1.tests \
+	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 \
+	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-1.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-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/SHARAN-1.tests \
+	tests/SHBALI-1.tests \
+	tests/SHBALI-2.tests \
+	tests/SHKNDA-2.tests \
+	tests/SHKNDA-3.tests \
+	$(NULL)
diff --git a/test/shaping/data/text-rendering-tests/README b/test/shaping/data/text-rendering-tests/README
new file mode 100644
index 0000000..c46d3ff
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/README
@@ -0,0 +1,7 @@
+Tests in this directory are automatically imported from the
+text-rendering-tests repository owned by Unicode Consortium:
+
+  https://github.com/unicode-org/text-rendering-tests
+
+To import the latest version of that test suite just run
+"make update" from a build in a git checkout.
diff --git a/test/shaping/data/text-rendering-tests/extract-tests.py b/test/shaping/data/text-rendering-tests/extract-tests.py
new file mode 100755
index 0000000..8e5909f
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/extract-tests.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+import sys
+import xml.etree.ElementTree as ET
+
+# Can we extract this from HTML element itself? I couldn't.
+namespaces = {
+	'ft': 'https://github.com/OpenType/fonttest',
+	'xlink': 'http://www.w3.org/1999/xlink',
+}
+def ns(s):
+	ns,s = s.split(':')
+	return '{%s}%s' % (namespaces[ns], s)
+
+def unistr(s):
+	return ','.join('U+%04X' % ord(c) for c in s)
+
+def glyphstr(glyphs):
+	out = []
+	for glyphname,x,y in glyphs:
+		if x or y:
+			out.append('%s@%d,%d' % (glyphname, x, y))
+		else:
+			out.append(glyphname)
+	return '['+'|'.join(out)+']'
+
+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(';', ',')
+	glyphs = []
+	for use in elt.findall(".//use"):
+		x = int(use.get('x'))
+		y = int(use.get('y'))
+		href = use.get(ns('xlink:href'))
+		assert href[0] == '#'
+		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
+	print ("../fonts/%s:%s:%s:%s" % (font, opts, unistr(text), glyphstr(glyphs)))
+
+sys.exit(0 if found else 1)
diff --git a/test/shaping/data/text-rendering-tests/fonts/AdobeVFPrototype-Subset.otf b/test/shaping/data/text-rendering-tests/fonts/AdobeVFPrototype-Subset.otf
new file mode 100644
index 0000000..5cc7279
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/AdobeVFPrototype-Subset.otf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/FDArrayTest257.otf b/test/shaping/data/text-rendering-tests/fonts/FDArrayTest257.otf
new file mode 100644
index 0000000..a97b1f4
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/FDArrayTest257.otf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/FDArrayTest65535.otf b/test/shaping/data/text-rendering-tests/fonts/FDArrayTest65535.otf
new file mode 100644
index 0000000..835beb2
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/FDArrayTest65535.otf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/NotoSansBalinese-Regular.ttf b/test/shaping/data/text-rendering-tests/fonts/NotoSansBalinese-Regular.ttf
new file mode 100644
index 0000000..0b0f58f
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/NotoSansBalinese-Regular.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/NotoSansKannada-Regular.ttf b/test/shaping/data/text-rendering-tests/fonts/NotoSansKannada-Regular.ttf
new file mode 100644
index 0000000..7366f37
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/NotoSansKannada-Regular.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/NotoSerifKannada-Regular.ttf b/test/shaping/data/text-rendering-tests/fonts/NotoSerifKannada-Regular.ttf
new file mode 100644
index 0000000..30ca550
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/NotoSerifKannada-Regular.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/Selawik-README.md b/test/shaping/data/text-rendering-tests/fonts/Selawik-README.md
new file mode 100644
index 0000000..9c5a80f
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/Selawik-README.md
@@ -0,0 +1,60 @@
+# Overview
+This version of Selawik is a test case and demonstration of OpenType 1.8 Font Variations technology and tables. It also includes some handy debugging characters.
+
+This version of Selawik is intended for testing only, and is not recommended for shipping in applications, etc. For that, it is better to use the main branch of [Selawik](https://github.com/Microsoft/Selawik).
+
+# Features
+
+* Full TrueType hinting with VTT source tables included. See [Hinting](#hinting) for details.
+* All tables required for OpenType 1.8 are present (see [Table Status](#table-status), below). This includes cvar (varied CVTs), GPOS/GDEF (kerning varies), and avar (coordinate space warping to match Segoe UI).
+
+	Note: This version of Selawik does not include an MVAR because its vertical metrics do not change anywhere in the design space, thus there is no need for MVAR. A future release will contain an axis that varies vertical metrics as an excuse to have an MVAR.
+
+* Numerous interesting debugging glyphs (requires liga to be enabled). For example, \axis1 will show the current normalized wght coordinate. See [Debugging Glyphs](#debugging-glyphs) for details.
+* 1 axis: weight
+
+## Table status
+The following tables are currently supported:
+
+- [x] fvar
+- [x] gvar
+- [x] cvar
+- [x] avar (to match Segoe UI weights and metrics)
+- [x] STAT
+- [x] GPOS/GDEF - kerning
+- [x] HVAR
+
+Not yet complete: 
+
+- [ ] GSUB/GDEF - to change dollar signs in the bold
+- [ ] MVAR (future release)
+
+## To do
+* Add a second axis that varies vertical metrics so we need an MVAR table. This axis will not be one of the standard axes listed in the [OpenType 1.8 specification] (https://www.microsoft.com/typography/otspec/fvar.htm), so that it becomes an example of out to do a foundry-defined axis.
+* Add Feature Variations (GPOS/GDEF) to switch dollar sign glyphs across weights.
+
+
+# Debugging glyphs
+Thanks to Greg Hitchcock's TrueType coding wizardry, this font includes many glyphs that are helpful for debugging implementations of variable fonts. It has a number of substitutions implemented as liga features:
+
+Feature | Description
+-------- | ----------
+\axis1 | Shows the normalized coordinate on the wght axis for the currently selected instance (e.g. 1.0 for bold, -1.0 for light, or something in between).
+\axis2 | Shows 0 because this font doesn't yet have a second axis.
+\axis1hex | Same as \axis1 but in hex.
+\axis2hex | Same as \axis2 but in hex.
+\pointsize | Shows the point size passed to the TrueType rasterizer. Note that depending on how the application calls the rasterizer, this may not be what you expect - e.g. on Safari on MacOS, this is always 1024.
+\ppem | Shows the pixels per em passed to the TrueType rastersize. Note that depending on how the application calls the rasterizer, this may not be what you expect - e.g. on Safari on MacOS, this is always 1024.
+\ttversion | Shows the version of the TrueType rasterizer.
+\ttmode | Shows the current TrueType rasterizer mode flags.
+\boldtest | A glyph to help you detect artificial emboldening. The glyph has a vertical bar and a circle. The vertical bar's weight varies with the weight of the rest of the font: it gets bolder at bolder weights, lighter at lighter weights. The circle changes weight (and size) in opposition to the rest of the font: lighter at bold weights and vice versa. Thus, if you use this character and see both the circle and bar look bold, you're not looking at a true bold instance, but an algorithmically emboldened one.
+\family | Shows the family name of the font.
+\version | Shows the version of this font
+
+
+
+
+# Hinting
+This version of Selawik is primarily hinted with the light Latin hinting style Microsoft recommends for variable Latin fonts. The VTT Light Latin autohinter was used to create the first round of hints, which were then reviewed and touched up. 
+
+This hinting style only uses CVTs for vertical metrics anchors (ascender, descender, cap height, x-height, and baseline). While this makes for an easy job hinting a Latin font, it makes for a poor test case because Selawik doesn't vary vertical metrics with weight, thus doesn't vary CVTs, thus doesn't need a cvar. So, to make it more interesting, we added CVT-based stem hints to the lowercase only. This provided the need to vary CVTs and thus require a cvar. Note that this was only done for testing purposes. For variable fonts, Microsoft recommends the light hinting style using the `ResYDist()` command instead of a CVT-based stem hint. 
diff --git a/test/shaping/data/text-rendering-tests/fonts/Selawik-variable.ttf b/test/shaping/data/text-rendering-tests/fonts/Selawik-variable.ttf
new file mode 100644
index 0000000..ff347e0
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/Selawik-variable.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestAVAR.ttf b/test/shaping/data/text-rendering-tests/fonts/TestAVAR.ttf
new file mode 100644
index 0000000..5df9867
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestAVAR.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestCMAP14.otf b/test/shaping/data/text-rendering-tests/fonts/TestCMAP14.otf
new file mode 100644
index 0000000..da485d9
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestCMAP14.otf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestCMAPMacTurkish.ttf b/test/shaping/data/text-rendering-tests/fonts/TestCMAPMacTurkish.ttf
new file mode 100644
index 0000000..4f89b09
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestCMAPMacTurkish.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestCVARGVAROne.ttf b/test/shaping/data/text-rendering-tests/fonts/TestCVARGVAROne.ttf
new file mode 100644
index 0000000..84cd1ea
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestCVARGVAROne.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestCVARGVARTwo.ttf b/test/shaping/data/text-rendering-tests/fonts/TestCVARGVARTwo.ttf
new file mode 100644
index 0000000..f20918a
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestCVARGVARTwo.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGLYFOne.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGLYFOne.ttf
new file mode 100644
index 0000000..8f634d9
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGLYFOne.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGPOSFour.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGPOSFour.ttf
new file mode 100644
index 0000000..bd929b5
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGPOSFour.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGPOSOne.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGPOSOne.ttf
new file mode 100644
index 0000000..ea74dd0
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGPOSOne.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGPOSThree.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGPOSThree.ttf
new file mode 100644
index 0000000..158a77a
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGPOSThree.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGPOSTwo.otf b/test/shaping/data/text-rendering-tests/fonts/TestGPOSTwo.otf
new file mode 100644
index 0000000..76d04eb
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGPOSTwo.otf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGSUBOne.otf b/test/shaping/data/text-rendering-tests/fonts/TestGSUBOne.otf
new file mode 100644
index 0000000..9be638f
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGSUBOne.otf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGVAREight.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGVAREight.ttf
new file mode 100644
index 0000000..271dc4b
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGVAREight.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGVARFour.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGVARFour.ttf
new file mode 100644
index 0000000..3524f37
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGVARFour.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGVARNine.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGVARNine.ttf
new file mode 100644
index 0000000..0ecd326
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGVARNine.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGVAROne.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGVAROne.ttf
new file mode 100644
index 0000000..e155d8f
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGVAROne.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGVARThree.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGVARThree.ttf
new file mode 100644
index 0000000..ac2d7eb
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGVARThree.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestGVARTwo.ttf b/test/shaping/data/text-rendering-tests/fonts/TestGVARTwo.ttf
new file mode 100644
index 0000000..bd144c6
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestGVARTwo.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestHVAROne.otf b/test/shaping/data/text-rendering-tests/fonts/TestHVAROne.otf
new file mode 100644
index 0000000..a87395c
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestHVAROne.otf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestHVARTwo.ttf b/test/shaping/data/text-rendering-tests/fonts/TestHVARTwo.ttf
new file mode 100644
index 0000000..2e81f94
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestHVARTwo.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestKERNOne.otf b/test/shaping/data/text-rendering-tests/fonts/TestKERNOne.otf
new file mode 100644
index 0000000..35369d1
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestKERNOne.otf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXEight.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXEight.ttf
new file mode 100644
index 0000000..9255e99
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXEight.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXEighteen.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXEighteen.ttf
new file mode 100644
index 0000000..91c364f
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXEighteen.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXEleven.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXEleven.ttf
new file mode 100644
index 0000000..92b889c
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXEleven.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXFour.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXFour.ttf
new file mode 100644
index 0000000..0028972
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXFour.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXFourteen.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXFourteen.ttf
new file mode 100644
index 0000000..31c30c0
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXFourteen.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXNine.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXNine.ttf
new file mode 100644
index 0000000..4371df4
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXNine.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXOne.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXOne.ttf
new file mode 100644
index 0000000..88b8dec
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXOne.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXSeventeen.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXSeventeen.ttf
new file mode 100644
index 0000000..9dd3a84
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXSeventeen.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXSixteen.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXSixteen.ttf
new file mode 100644
index 0000000..e34e1fe
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXSixteen.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXTen.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXTen.ttf
new file mode 100644
index 0000000..5827ec5
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXTen.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXThirteen.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirteen.ttf
new file mode 100644
index 0000000..f3c6f0f
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXThirteen.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXThree.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXThree.ttf
new file mode 100644
index 0000000..56984f2
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXThree.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXTwelve.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwelve.ttf
new file mode 100644
index 0000000..b1e4bc4
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwelve.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXTwenty.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwenty.ttf
new file mode 100644
index 0000000..769e29b
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwenty.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentyfive.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentyfive.ttf
new file mode 100644
index 0000000..e3fadf5
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentyfive.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentyone.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentyone.ttf
new file mode 100644
index 0000000..4101680
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentyone.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentysix.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentysix.ttf
new file mode 100644
index 0000000..603b1c6
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentysix.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentythree.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentythree.ttf
new file mode 100644
index 0000000..df34912
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentythree.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentytwo.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentytwo.ttf
new file mode 100644
index 0000000..4459e8a
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwentytwo.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestMORXTwo.ttf b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwo.ttf
new file mode 100644
index 0000000..39f2db5
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestMORXTwo.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestShapeAran.ttf b/test/shaping/data/text-rendering-tests/fonts/TestShapeAran.ttf
new file mode 100644
index 0000000..c73f569
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestShapeAran.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/TestShapeEthi.ttf b/test/shaping/data/text-rendering-tests/fonts/TestShapeEthi.ttf
new file mode 100644
index 0000000..391dddd
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/TestShapeEthi.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/fonts/Zycon.ttf b/test/shaping/data/text-rendering-tests/fonts/Zycon.ttf
new file mode 100644
index 0000000..3a6761b
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/fonts/Zycon.ttf
Binary files differ
diff --git a/test/shaping/data/text-rendering-tests/tests/AVAR-1.tests b/test/shaping/data/text-rendering-tests/tests/AVAR-1.tests
new file mode 100644
index 0000000..19223eb
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/AVAR-1.tests
@@ -0,0 +1,17 @@
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=100:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=150:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=200:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=250:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=300:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=350:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=400:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=450:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=500:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=550:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=600:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=650:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=700:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=750:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=800:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=850:U+2A01:[gid1]
+../fonts/TestAVAR.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=900:U+2A01:[gid1]
diff --git a/test/shaping/data/text-rendering-tests/tests/CFF-1.tests b/test/shaping/data/text-rendering-tests/tests/CFF-1.tests
new file mode 100644
index 0000000..6788253
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/CFF-1.tests
@@ -0,0 +1,13 @@
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041:[gid66]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+211D:[gid30]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+24EA:[gid235]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+2460:[gid97]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+2461:[gid98]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+4EFF:[gid256]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+FF21:[gid34]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+10133:[gid52]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1D4D0:[gid209]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1F33A:[gid59]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1F33B:[gid60]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1F4A7:[gid168]
+../fonts/FDArrayTest257.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1F95D:[gid94]
diff --git a/test/shaping/data/text-rendering-tests/tests/CFF-2.tests b/test/shaping/data/text-rendering-tests/tests/CFF-2.tests
new file mode 100644
index 0000000..6f190a8
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/CFF-2.tests
@@ -0,0 +1,13 @@
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041:[gid66]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+211D:[gid8478]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+24EA:[gid9451]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+2460:[gid9313]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+2461:[gid9314]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+4EFF:[gid20224]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+FF21:[gid65314]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+10133:[gid308]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1D4D0:[gid54481]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1F33A:[gid62267]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1F33B:[gid62268]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1F4A7:[gid62632]
+../fonts/FDArrayTest65535.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1F95D:[gid63838]
diff --git a/test/shaping/data/text-rendering-tests/tests/CFF2-1.tests b/test/shaping/data/text-rendering-tests/tests/CFF2-1.tests
new file mode 100644
index 0000000..84cb14d
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/CFF2-1.tests
@@ -0,0 +1,9 @@
+../fonts/AdobeVFPrototype-Subset.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=100:U+0024:[dollar]
+../fonts/AdobeVFPrototype-Subset.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=200:U+0024:[dollar]
+../fonts/AdobeVFPrototype-Subset.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=300:U+0024:[dollar]
+../fonts/AdobeVFPrototype-Subset.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=400:U+0024:[dollar]
+../fonts/AdobeVFPrototype-Subset.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=500:U+0024:[dollar]
+../fonts/AdobeVFPrototype-Subset.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=600:U+0024:[dollar]
+../fonts/AdobeVFPrototype-Subset.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=700:U+0024:[dollar]
+../fonts/AdobeVFPrototype-Subset.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=800:U+0024:[dollar.nostroke]
+../fonts/AdobeVFPrototype-Subset.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=900:U+0024:[dollar.nostroke]
diff --git a/test/shaping/data/text-rendering-tests/tests/CMAP-1.tests b/test/shaping/data/text-rendering-tests/tests/CMAP-1.tests
new file mode 100644
index 0000000..337b9f8
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/CMAP-1.tests
@@ -0,0 +1,4 @@
+../fonts/TestCMAP14.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+82A6:[uni82A6_uE0100]
+../fonts/TestCMAP14.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+82A6,U+E0100:[uni82A6_uE0100]
+../fonts/TestCMAP14.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+82A6,U+E0101:[uni82A6_uE0101]
+../fonts/TestCMAP14.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+82A6,U+E0102:[uni82A6_uE0100]
diff --git a/test/shaping/data/text-rendering-tests/tests/CMAP-2.tests b/test/shaping/data/text-rendering-tests/tests/CMAP-2.tests
new file mode 100644
index 0000000..861f2e4
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/CMAP-2.tests
@@ -0,0 +1,2 @@
+../fonts/TestCMAP14.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+2269:[uni2269]
+../fonts/TestCMAP14.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+2269,U+FE00:[uni2269FE00]
diff --git a/test/shaping/data/text-rendering-tests/tests/CMAP-3.tests b/test/shaping/data/text-rendering-tests/tests/CMAP-3.tests
new file mode 100644
index 0000000..d8758d3
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/CMAP-3.tests
@@ -0,0 +1,20 @@
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+201C:[gid200]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041:[gid34]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0042:[gid35]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+00C7:[gid126]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+011E:[gid176]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0049:[gid42]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0130:[gid178]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+00D6:[gid140]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+015E:[gid181]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+00DC:[gid145]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+201D:[gid201]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0061:[gid66]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0062:[gid67]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+00E7:[gid154]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+011F:[gid177]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0131:[gid222]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0069:[gid74]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+00F6:[gid168]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+015F:[gid182]
+../fonts/TestCMAPMacTurkish.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+00FC:[gid174]
diff --git a/test/shaping/data/text-rendering-tests/tests/CVAR-1.tests b/test/shaping/data/text-rendering-tests/tests/CVAR-1.tests
new file mode 100644
index 0000000..c874a14
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/CVAR-1.tests
@@ -0,0 +1,3 @@
+../fonts/TestCVARGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=28,wdth=100,opsz=72:U+0068,U+006F,U+006E:[uni0068|uni006F@595,0|uni006E@1126,0]
+../fonts/TestCVARGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=94,wdth=100,opsz=72:U+0068,U+006F,U+006E:[uni0068|uni006F@635,0|uni006E@1212,0]
+../fonts/TestCVARGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=194,wdth=100,opsz=72:U+0068,U+006F,U+006E:[uni0068|uni006F@691,0|uni006E@1331,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/CVAR-2.tests b/test/shaping/data/text-rendering-tests/tests/CVAR-2.tests
new file mode 100644
index 0000000..6bd42e1
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/CVAR-2.tests
@@ -0,0 +1,3 @@
+../fonts/TestCVARGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=28,wdth=100,opsz=72:U+0068,U+006F,U+006E:[uni0068|uni006F@595,0|uni006E@1126,0]
+../fonts/TestCVARGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=94,wdth=100,opsz=72:U+0068,U+006F,U+006E:[uni0068|uni006F@635,0|uni006E@1212,0]
+../fonts/TestCVARGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=194,wdth=100,opsz=72:U+0068,U+006F,U+006E:[uni0068|uni006F@691,0|uni006E@1331,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/GLYF-1.tests b/test/shaping/data/text-rendering-tests/tests/GLYF-1.tests
new file mode 100644
index 0000000..bdc0346
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GLYF-1.tests
@@ -0,0 +1 @@
+../fonts/TestGLYFOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0123:[gcommaabove]
diff --git a/test/shaping/data/text-rendering-tests/tests/GPOS-1.tests b/test/shaping/data/text-rendering-tests/tests/GPOS-1.tests
new file mode 100644
index 0000000..221d16b
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GPOS-1.tests
@@ -0,0 +1,19 @@
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0104,U+004A:[Aogonek|J@732,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0104,U+0067:[Aogonek|g@692,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0104,U+0123:[Aogonek|gcommaabove@692,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0104,U+006A:[Aogonek|j@752,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0104,U+0237:[Aogonek|dotlessj@752,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0051,U+0237:[Q|dotlessj@734,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0105,U+006A:[aogonek|j@588,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0105,U+0237:[aogonek|dotlessj@588,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0067,U+0237:[g|dotlessj@563,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0123,U+0237:[gcommaabove|dotlessj@563,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0131,U+0237:[dotlessi|dotlessj@334,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0173,U+0237:[uogonek|dotlessj@656,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0076,U+0237:[v|dotlessj@587,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0056,U+0061:[V|a@594,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0056,U+00E1:[V|aacute@594,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0056,U+0105:[V|aogonek@594,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0056,U+0066:[V|f@634,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0056,U+FB02:[V|fl@634,0]
+../fonts/TestGPOSOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0056,U+002E:[V|period@504,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/GPOS-2.tests b/test/shaping/data/text-rendering-tests/tests/GPOS-2.tests
new file mode 100644
index 0000000..03fcc36
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GPOS-2.tests
@@ -0,0 +1,3 @@
+../fonts/TestGPOSTwo.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+25EF:[uni25EF]
+../fonts/TestGPOSTwo.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+263C:[sun]
+../fonts/TestGPOSTwo.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+25EF,U+263C:[uni25EF|sun]
diff --git a/test/shaping/data/text-rendering-tests/tests/GPOS-3.tests b/test/shaping/data/text-rendering-tests/tests/GPOS-3.tests
new file mode 100644
index 0000000..32aeb6e
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GPOS-3.tests
@@ -0,0 +1,4 @@
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1208:[uni1208]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1208,U+135E:[uni1208|uni135E@303,0]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1208,U+135F:[uni1208|uni135F@303,0]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1208,U+135D:[uni1208|uni135D@303,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/GPOS-4.tests b/test/shaping/data/text-rendering-tests/tests/GPOS-4.tests
new file mode 100644
index 0000000..fd77542
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GPOS-4.tests
@@ -0,0 +1,4 @@
+../fonts/TestGPOSThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0075,U+0308,U+0301:[u|uni0308@529,-31|acutecomb@537,138]
+../fonts/TestGPOSThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0075,U+0308,U+0304:[u|uni0308@529,-31|uni0304@526,138]
+../fonts/TestGPOSThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0075,U+0308,U+0308:[u|uni0308@529,-31|uni0308@529,138]
+../fonts/TestGPOSThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0075,U+0308,U+0308,U+0308:[u|uni0308@529,-31|uni0308@529,138|uni0308@529,307]
diff --git a/test/shaping/data/text-rendering-tests/tests/GPOS-5.tests b/test/shaping/data/text-rendering-tests/tests/GPOS-5.tests
new file mode 100644
index 0000000..2d7ce14
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GPOS-5.tests
@@ -0,0 +1,5 @@
+../fonts/TestGPOSFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=100:U+0634,U+0652:[uni0652@663,144|uni0634]
+../fonts/TestGPOSFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=300:U+0634,U+0652:[uni0652@680,165|uni0634]
+../fonts/TestGPOSFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=600:U+0634,U+0652:[uni0652@730,246|uni0634]
+../fonts/TestGPOSFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=700:U+0634,U+0652:[uni0652@750,282|uni0634]
+../fonts/TestGPOSFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=900:U+0634,U+0652:[uni0652@784,351|uni0634]
diff --git a/test/shaping/data/text-rendering-tests/tests/GSUB-1.tests b/test/shaping/data/text-rendering-tests/tests/GSUB-1.tests
new file mode 100644
index 0000000..e33a6f1
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GSUB-1.tests
@@ -0,0 +1 @@
+../fonts/TestGSUBOne.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0061,U+0020,U+0061:[a.alt|space@500,0|a@1000,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/GSUB-2.tests b/test/shaping/data/text-rendering-tests/tests/GSUB-2.tests
new file mode 100644
index 0000000..34c8deb
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GSUB-2.tests
@@ -0,0 +1,11 @@
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1373:[uni1373]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+136B:[uni136B]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1375:[uni1375]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+136D:[uni136D]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1373,U+136B:[uni1373.init|uni136B.fina@621,0]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1375,U+136D:[uni1375.init|uni136D.fina@662,0]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+137B:[uni137B]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1373,U+136B,U+137B:[uni1373.init|uni136B.medi@621,0|uni137B.fina@1102,0]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1373,U+136B,U+137B,U+1373,U+136B:[uni1373.init|uni136B.medi@621,0|uni137B.medi@1102,0|uni1373.medi@1489,0|uni136B.fina@2110,0]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1373,U+136B,U+137B,U+1375,U+136D:[uni1373.init|uni136B.medi@621,0|uni137B.medi@1102,0|uni1375.medi@1489,0|uni136D.fina@2157,0]
+../fonts/TestShapeEthi.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1375,U+136D,U+137B,U+1373,U+136B:[uni1375.init|uni136D.medi@662,0|uni137B.medi@1203,0|uni1373.medi@1590,0|uni136B.fina@2211,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/GVAR-1.tests b/test/shaping/data/text-rendering-tests/tests/GVAR-1.tests
new file mode 100644
index 0000000..fc00a4e
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GVAR-1.tests
@@ -0,0 +1,9 @@
+../fonts/TestGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=300:U+5F4C:[gid2]
+../fonts/TestGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=350:U+5F4C:[gid2]
+../fonts/TestGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=400:U+5F4C:[gid2]
+../fonts/TestGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=450:U+5F4C:[gid2]
+../fonts/TestGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=500:U+5F4C:[gid2]
+../fonts/TestGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=550:U+5F4C:[gid2]
+../fonts/TestGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=600:U+5F4C:[gid2]
+../fonts/TestGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=650:U+5F4C:[gid2]
+../fonts/TestGVAROne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=700:U+5F4C:[gid2]
diff --git a/test/shaping/data/text-rendering-tests/tests/GVAR-2.tests b/test/shaping/data/text-rendering-tests/tests/GVAR-2.tests
new file mode 100644
index 0000000..10e22e4
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GVAR-2.tests
@@ -0,0 +1,9 @@
+../fonts/TestGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=300:U+5F4C:[gid2]
+../fonts/TestGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=350:U+5F4C:[gid2]
+../fonts/TestGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=400:U+5F4C:[gid2]
+../fonts/TestGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=450:U+5F4C:[gid2]
+../fonts/TestGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=500:U+5F4C:[gid2]
+../fonts/TestGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=550:U+5F4C:[gid2]
+../fonts/TestGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=600:U+5F4C:[gid2]
+../fonts/TestGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=650:U+5F4C:[gid2]
+../fonts/TestGVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=700:U+5F4C:[gid2]
diff --git a/test/shaping/data/text-rendering-tests/tests/GVAR-3.tests b/test/shaping/data/text-rendering-tests/tests/GVAR-3.tests
new file mode 100644
index 0000000..c3b8049
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GVAR-3.tests
@@ -0,0 +1,9 @@
+../fonts/TestGVARThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=300:U+5F4C:[gid2]
+../fonts/TestGVARThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=350:U+5F4C:[gid2]
+../fonts/TestGVARThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=400:U+5F4C:[gid2]
+../fonts/TestGVARThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=450:U+5F4C:[gid2]
+../fonts/TestGVARThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=500:U+5F4C:[gid2]
+../fonts/TestGVARThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=550:U+5F4C:[gid2]
+../fonts/TestGVARThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=600:U+5F4C:[gid2]
+../fonts/TestGVARThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=650:U+5F4C:[gid2]
+../fonts/TestGVARThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=700:U+5F4C:[gid2]
diff --git a/test/shaping/data/text-rendering-tests/tests/GVAR-4.tests b/test/shaping/data/text-rendering-tests/tests/GVAR-4.tests
new file mode 100644
index 0000000..1c0964d
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GVAR-4.tests
@@ -0,0 +1,11 @@
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=-1.0,T1=0.0:U+1F98E:[gid5]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=-0.8,T1=0.1:U+1F98E:[gid5]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=-0.6,T1=0.2:U+1F98E:[gid5]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=-0.4,T1=0.3:U+1F98E:[gid5]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=-0.2,T1=0.4:U+1F98E:[gid5]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=0.0,T1=0.5:U+1F98E:[gid5]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=0.2,T1=0.6:U+1F98E:[gid5]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=0.4,T1=0.7:U+1F98E:[gid5]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=0.6,T1=0.8:U+1F98E:[gid5]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=0.8,T1=0.9:U+1F98E:[gid5]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=1.0,T1=1.0:U+1F98E:[gid5]
diff --git a/test/shaping/data/text-rendering-tests/tests/GVAR-5.tests b/test/shaping/data/text-rendering-tests/tests/GVAR-5.tests
new file mode 100644
index 0000000..b2ff710
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GVAR-5.tests
@@ -0,0 +1,11 @@
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=-1.0:U+1F31D:[gid15]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=-0.8:U+1F31D:[gid15]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=-0.6:U+1F31D:[gid15]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=-0.4:U+1F31D:[gid15]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=-0.2:U+1F31D:[gid15]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=0.0:U+1F31D:[gid15]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=0.2:U+1F31D:[gid15]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=0.4:U+1F31D:[gid15]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=0.6:U+1F31D:[gid15]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=0.8:U+1F31D:[gid15]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=M1=1.0:U+1F31D:[gid15]
diff --git a/test/shaping/data/text-rendering-tests/tests/GVAR-6.tests b/test/shaping/data/text-rendering-tests/tests/GVAR-6.tests
new file mode 100644
index 0000000..f96292b
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GVAR-6.tests
@@ -0,0 +1,11 @@
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=T1=0.0:U+1F422:[gid12]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=T1=0.1:U+1F422:[gid12]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=T1=0.2:U+1F422:[gid12]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=T1=0.3:U+1F422:[gid12]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=T1=0.4:U+1F422:[gid12]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=T1=0.5:U+1F422:[gid12]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=T1=0.6:U+1F422:[gid12]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=T1=0.7:U+1F422:[gid12]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=T1=0.8:U+1F422:[gid12]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=T1=0.9:U+1F422:[gid12]
+../fonts/Zycon.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=T1=1.0:U+1F422:[gid12]
diff --git a/test/shaping/data/text-rendering-tests/tests/GVAR-7.tests b/test/shaping/data/text-rendering-tests/tests/GVAR-7.tests
new file mode 100644
index 0000000..594da3b
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GVAR-7.tests
@@ -0,0 +1,7 @@
+../fonts/TestGVARFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=150:U+004F,U+0049,U+004F:[uni004F|uni0049@706,0|uni004F@1072,0]
+../fonts/TestGVARFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=200:U+004F,U+0049,U+004F:[uni004F|uni0049@707,0|uni004F@1074,0]
+../fonts/TestGVARFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=250:U+004F,U+0049,U+004F:[uni004F|uni0049@707,0|uni004F@1075,0]
+../fonts/TestGVARFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=300:U+004F,U+0049,U+004F:[uni004F|uni0049@707,0|uni004F@1076,0]
+../fonts/TestGVARFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=350:U+004F,U+0049,U+004F:[uni004F|uni0049@707,0|uni004F@1077,0]
+../fonts/TestGVARFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=400:U+004F,U+0049,U+004F:[uni004F|uni0049@707,0|uni004F@1078,0]
+../fonts/TestGVARFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=450:U+004F,U+0049,U+004F:[uni004F|uni0049@706,0|uni004F@1079,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/GVAR-8.tests b/test/shaping/data/text-rendering-tests/tests/GVAR-8.tests
new file mode 100644
index 0000000..e5c8209
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GVAR-8.tests
@@ -0,0 +1,6 @@
+../fonts/TestGVAREight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=HV=0.0:U+0048:[H]
+../fonts/TestGVAREight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=HV=-0.2:U+0048:[H]
+../fonts/TestGVAREight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=HV=-0.4:U+0048:[H]
+../fonts/TestGVAREight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=HV=-0.6:U+0048:[H]
+../fonts/TestGVAREight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=HV=-0.8:U+0048:[H]
+../fonts/TestGVAREight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=HV=-1.0:U+0048:[H]
diff --git a/test/shaping/data/text-rendering-tests/tests/GVAR-9.tests b/test/shaping/data/text-rendering-tests/tests/GVAR-9.tests
new file mode 100644
index 0000000..19e2ed8
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/GVAR-9.tests
@@ -0,0 +1,10 @@
+../fonts/TestGVARNine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=-1.0:U+0041:[A]
+../fonts/TestGVARNine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=-0.5:U+0041:[A]
+../fonts/TestGVARNine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=0.0:U+0041:[A]
+../fonts/TestGVARNine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=0.5:U+0041:[A]
+../fonts/TestGVARNine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=0.6:U+0041:[A]
+../fonts/TestGVARNine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=0.7:U+0041:[A]
+../fonts/TestGVARNine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=0.8:U+0041:[A]
+../fonts/TestGVARNine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=0.9:U+0041:[A]
+../fonts/TestGVARNine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=0.944444:U+0041:[A]
+../fonts/TestGVARNine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=TEST=1.0:U+0041:[A]
diff --git a/test/shaping/data/text-rendering-tests/tests/HVAR-1.tests b/test/shaping/data/text-rendering-tests/tests/HVAR-1.tests
new file mode 100644
index 0000000..189d9f0
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/HVAR-1.tests
@@ -0,0 +1,6 @@
+../fonts/TestHVAROne.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=0:U+0041,U+0042,U+0043:[A|B@520,0|C@1094,0]
+../fonts/TestHVAROne.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=200:U+0041,U+0042,U+0043:[A|B@533,0|C@1115,0]
+../fonts/TestHVAROne.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=400:U+0041,U+0042,U+0043:[A|B@546,0|C@1135,0]
+../fonts/TestHVAROne.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=600:U+0041,U+0042,U+0043:[A|B@558,0|C@1155,0]
+../fonts/TestHVAROne.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=800:U+0041,U+0042,U+0043:[A|B@571,0|C@1175,0]
+../fonts/TestHVAROne.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=1000:U+0041,U+0042,U+0043:[A|B@584,0|C@1196,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/HVAR-2.tests b/test/shaping/data/text-rendering-tests/tests/HVAR-2.tests
new file mode 100644
index 0000000..db93be9
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/HVAR-2.tests
@@ -0,0 +1,6 @@
+../fonts/TestHVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=0:U+0041,U+0042:[uni0041|uni0042@450,0]
+../fonts/TestHVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=200:U+0041,U+0042:[uni0041|uni0042@515,0]
+../fonts/TestHVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=400:U+0041,U+0042:[uni0041|uni0042@584,0]
+../fonts/TestHVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=600:U+0041,U+0042:[uni0041|uni0042@673,0]
+../fonts/TestHVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=800:U+0041,U+0042:[uni0041|uni0042@761,0]
+../fonts/TestHVARTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft --variations=wght=1000:U+0041,U+0042:[uni0041|uni0042@850,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/KERN-1.tests b/test/shaping/data/text-rendering-tests/tests/KERN-1.tests
new file mode 100644
index 0000000..f0c0214
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/KERN-1.tests
@@ -0,0 +1 @@
+../fonts/TestKERNOne.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0131,U+0054,U+0075,U+0054,U+0075,U+0054,U+0131:[dotlessi|T|u@400,0|T@600,0|u@1000,0|T@1200,0|dotlessi@1600,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/KERN-2.tests b/test/shaping/data/text-rendering-tests/tests/KERN-2.tests
new file mode 100644
index 0000000..fdffa4a
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/KERN-2.tests
@@ -0,0 +1 @@
+../fonts/TestKERNOne.otf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0075,U+0131,U+0131,U+0054,U+0131,U+0131,U+0054,U+0131,U+0131,U+0075:[u|dotlessi@400,0|dotlessi@1100,0|T@1100,0|dotlessi@1500,0|dotlessi@2200,0|T@2200,0|dotlessi@2600,0|dotlessi@3300,0|u@3500,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-1.tests b/test/shaping/data/text-rendering-tests/tests/MORX-1.tests
new file mode 100644
index 0000000..794b01b
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-1.tests
@@ -0,0 +1 @@
+../fonts/TestMORXOne.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0043:[A.alt|B@1000,0|C.alt@2000,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-10.tests b/test/shaping/data/text-rendering-tests/tests/MORX-10.tests
new file mode 100644
index 0000000..a6a5444
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-10.tests
@@ -0,0 +1 @@
+../fonts/TestMORXTen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0041,U+0042,U+0041,U+0042:[A|B@638,0|A@1288,0|B@1926,0|B@2576,0|A@3226,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-11.tests b/test/shaping/data/text-rendering-tests/tests/MORX-11.tests
new file mode 100644
index 0000000..1fce69b
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-11.tests
@@ -0,0 +1 @@
+../fonts/TestMORXEleven.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0042,U+0041,U+0042,U+0042,U+0041,U+0041,U+0042,U+0058:[B|A@650,0|B@1288,0|B@1938,0|A@2588,0|X@3226,0|A@3812,0|B@4450,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-12.tests b/test/shaping/data/text-rendering-tests/tests/MORX-12.tests
new file mode 100644
index 0000000..8b9886f
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-12.tests
@@ -0,0 +1,3 @@
+../fonts/TestMORXTwelve.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0041,U+0042,U+0043,U+0058,U+0031:[X|C@598,0|A@1230,0|B@1868,0|X@2518,0|one@3116,0]
+../fonts/TestMORXTwelve.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0041,U+0042,U+0043,U+0058,U+0032:[X|C@598,0|A@1230,0|B@1868,0|X@2518,0|two@3116,0]
+../fonts/TestMORXTwelve.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0058,U+0041,U+0042,U+0043,U+0058,U+0033:[X|B@598,0|C@1248,0|A@1880,0|X@2518,0|three@3116,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-13.tests b/test/shaping/data/text-rendering-tests/tests/MORX-13.tests
new file mode 100644
index 0000000..42d8107
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-13.tests
@@ -0,0 +1 @@
+../fonts/TestMORXThirteen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0043,U+0044,U+0045:[B|C@626,0|D@1222,0|E@1896,0|A@2452,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-14.tests b/test/shaping/data/text-rendering-tests/tests/MORX-14.tests
new file mode 100644
index 0000000..1369247
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-14.tests
@@ -0,0 +1,2 @@
+../fonts/TestMORXFourteen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0043,U+0044,U+0045:[B|C@626,0|D@1222,0|E@1896,0|A@2452,0]
+../fonts/TestMORXFourteen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0042,U+0042,U+0043,U+0043,U+0043,U+0044,U+0044,U+0044,U+0042,U+0043,U+0044,U+0043,U+0045:[B|B@626,0|B@1252,0|C@1878,0|C@2474,0|C@3070,0|D@3666,0|D@4340,0|D@5014,0|B@5688,0|C@6314,0|D@6910,0|C@7584,0|E@8180,0|A@8736,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-16.tests b/test/shaping/data/text-rendering-tests/tests/MORX-16.tests
new file mode 100644
index 0000000..ce0d5b2
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-16.tests
@@ -0,0 +1 @@
+../fonts/TestMORXSixteen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0043,U+0044,U+0045:[B|C@626,0|D@1222,0|E@1896,0|A@2452,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-17.tests b/test/shaping/data/text-rendering-tests/tests/MORX-17.tests
new file mode 100644
index 0000000..6e1c94c
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-17.tests
@@ -0,0 +1 @@
+../fonts/TestMORXSeventeen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042:[B|A@626,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-18.tests b/test/shaping/data/text-rendering-tests/tests/MORX-18.tests
new file mode 100644
index 0000000..b032a76
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-18.tests
@@ -0,0 +1,4 @@
+../fonts/TestMORXEighteen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0043,U+0044,U+0045:[A|B.alt@639,0|C@1639,0|D.alt1@2235,0|E@3235,0]
+../fonts/TestMORXEighteen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0042,U+0042,U+0044,U+0045:[A|B@639,0|B@1265,0|B.alt@1891,0|D.alt1@2891,0|E@3891,0]
+../fonts/TestMORXEighteen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0044,U+0045:[A|B.alt@639,0|D.alt1@1639,0|E@2639,0]
+../fonts/TestMORXEighteen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0045:[A|B@639,0|E@1265,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-19.tests b/test/shaping/data/text-rendering-tests/tests/MORX-19.tests
new file mode 100644
index 0000000..e9b9dc4
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-19.tests
@@ -0,0 +1,2 @@
+../fonts/TestMORXEighteen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0043,U+0044,U+0045:[A.alt|C@1000,0|D.alt1@1596,0|E@2596,0]
+../fonts/TestMORXEighteen.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0044:[D.alt]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-2.tests b/test/shaping/data/text-rendering-tests/tests/MORX-2.tests
new file mode 100644
index 0000000..3e64d23
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-2.tests
@@ -0,0 +1,16 @@
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+24FF:[O|O@418,0|O@836,0|A@1254,0|B@2084,0|X@2914,0|Y@3744,0|Z@4574,0|C@5404,0|D@6234,0|O@7064,0|O@7482,0|O@7900,0|zero@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+278A:[O|O@418,0|O@836,0|B@1254,0|X@2084,0|Y@2914,0|Z@3744,0|C@4574,0|D@5404,0|A@6234,0|O@7064,0|O@7482,0|O@7900,0|one@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+278B:[O|O@418,0|O@836,0|D@1254,0|A@2084,0|B@2914,0|X@3744,0|Y@4574,0|Z@5404,0|C@6234,0|O@7064,0|O@7482,0|O@7900,0|two@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+0033:[O|O@418,0|O@836,0|D@1254,0|B@2084,0|X@2914,0|Y@3744,0|Z@4574,0|C@5404,0|A@6234,0|O@7064,0|O@7482,0|O@7900,0|three@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+0034:[O|O@418,0|O@836,0|X@1254,0|Y@2084,0|Z@2914,0|C@3744,0|D@4574,0|A@5404,0|B@6234,0|O@7064,0|O@7482,0|O@7900,0|four@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+0035:[O|O@418,0|O@836,0|X@1254,0|Y@2084,0|Z@2914,0|C@3744,0|D@4574,0|B@5404,0|A@6234,0|O@7064,0|O@7482,0|O@7900,0|five@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+0036:[O|O@418,0|O@836,0|C@1254,0|D@2084,0|A@2914,0|B@3744,0|X@4574,0|Y@5404,0|Z@6234,0|O@7064,0|O@7482,0|O@7900,0|six@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+0037:[O|O@418,0|O@836,0|D@1254,0|C@2084,0|A@2914,0|B@3744,0|X@4574,0|Y@5404,0|Z@6234,0|O@7064,0|O@7482,0|O@7900,0|seven@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+0038:[O|O@418,0|O@836,0|C@1254,0|D@2084,0|B@2914,0|X@3744,0|Y@4574,0|Z@5404,0|A@6234,0|O@7064,0|O@7482,0|O@7900,0|eight@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+0039:[O|O@418,0|O@836,0|D@1254,0|C@2084,0|B@2914,0|X@3744,0|Y@4574,0|Z@5404,0|A@6234,0|O@7064,0|O@7482,0|O@7900,0|nine@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+2793:[O|O@418,0|O@836,0|D@1254,0|X@2084,0|Y@2914,0|Z@3744,0|C@4574,0|A@5404,0|B@6234,0|O@7064,0|O@7482,0|O@7900,0|one_zero@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+24EB:[O|O@418,0|O@836,0|D@1254,0|X@2084,0|Y@2914,0|Z@3744,0|C@4574,0|B@5404,0|A@6234,0|O@7064,0|O@7482,0|O@7900,0|one_one@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+24EC:[O|O@418,0|O@836,0|C@1254,0|D@2084,0|X@2914,0|Y@3744,0|Z@4574,0|A@5404,0|B@6234,0|O@7064,0|O@7482,0|O@7900,0|one_two@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+24ED:[O|O@418,0|O@836,0|C@1254,0|D@2084,0|X@2914,0|Y@3744,0|Z@4574,0|B@5404,0|A@6234,0|O@7064,0|O@7482,0|O@7900,0|one_three@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+24EE:[O|O@418,0|O@836,0|D@1254,0|C@2084,0|X@2914,0|Y@3744,0|Z@4574,0|A@5404,0|B@6234,0|O@7064,0|O@7482,0|O@7900,0|one_four@8318,0]
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0058,U+0059,U+005A,U+0043,U+0044,U+004F,U+004F,U+004F,U+24EF:[O|O@418,0|O@836,0|D@1254,0|C@2084,0|X@2914,0|Y@3744,0|Z@4574,0|B@5404,0|A@6234,0|O@7064,0|O@7482,0|O@7900,0|one_five@8318,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-20.tests b/test/shaping/data/text-rendering-tests/tests/MORX-20.tests
new file mode 100644
index 0000000..8d04192
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-20.tests
@@ -0,0 +1,7 @@
+../fonts/TestMORXTwenty.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0043,U+0044,U+0045:[A|B@639,0|C.alt@1265,0|D@2265,0|E.alt1@2939,0]
+../fonts/TestMORXTwenty.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0043:[A|B@639,0|C.alt@1265,0]
+../fonts/TestMORXTwenty.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0045:[A|B.alt@639,0|E.alt1@1639,0]
+../fonts/TestMORXTwenty.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0045:[A.alt|E.alt1@1000,0]
+../fonts/TestMORXTwenty.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0045,U+0045:[E|E@556,0]
+../fonts/TestMORXTwenty.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041:[A.alt]
+../fonts/TestMORXTwenty.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0045:[E]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-21.tests b/test/shaping/data/text-rendering-tests/tests/MORX-21.tests
new file mode 100644
index 0000000..a608755
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-21.tests
@@ -0,0 +1 @@
+../fonts/TestMORXTwentyone.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0043,U+0044,U+0045:[A|B.alt@639,0|C@1639,0|D@2235,0|E@2909,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-22.tests b/test/shaping/data/text-rendering-tests/tests/MORX-22.tests
new file mode 100644
index 0000000..960874c
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-22.tests
@@ -0,0 +1 @@
+../fonts/TestMORXTwentytwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041:[C]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-23.tests b/test/shaping/data/text-rendering-tests/tests/MORX-23.tests
new file mode 100644
index 0000000..9575a80
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-23.tests
@@ -0,0 +1 @@
+../fonts/TestMORXTwentythree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0043,U+0044,U+0045:[E|E@556,0|E@1112,0|E@1668,0|E@2224,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-25.tests b/test/shaping/data/text-rendering-tests/tests/MORX-25.tests
new file mode 100644
index 0000000..ccd0563
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-25.tests
@@ -0,0 +1,9 @@
+../fonts/TestMORXTwentyfive.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0043,U+0044,U+0045:[A.alt|B.alt@1000,0|C.alt@2000,0|D.alt@3000,0|E.alt@4000,0]
+../fonts/TestMORXTwentyfive.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0045,U+0042,U+0043,U+0044,U+0041:[E|B@556,0|C@1182,0|D@1778,0|A@2452,0]
+../fonts/TestMORXTwentyfive.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0043,U+0042,U+0041,U+0042,U+0043:[C|B@596,0|A.alt@1222,0|B.alt@2222,0|C.alt@3222,0]
+../fonts/TestMORXTwentyfive.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0043:[A.alt|B.alt@1000,0|C.alt@2000,0]
+../fonts/TestMORXTwentyfive.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0043,U+0042,U+0041:[C|B@596,0|A@1222,0]
+../fonts/TestMORXTwentyfive.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042:[A.alt|B.alt@1000,0]
+../fonts/TestMORXTwentyfive.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0042,U+0041:[B|A@626,0]
+../fonts/TestMORXTwentyfive.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041:[A]
+../fonts/TestMORXTwentyfive.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0042:[B]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-26.tests b/test/shaping/data/text-rendering-tests/tests/MORX-26.tests
new file mode 100644
index 0000000..bebae60
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-26.tests
@@ -0,0 +1,2 @@
+../fonts/TestMORXTwentysix.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042:[A|B@639,0]
+../fonts/TestMORXTwentysix.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0042:[B.alt]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-3.tests b/test/shaping/data/text-rendering-tests/tests/MORX-3.tests
new file mode 100644
index 0000000..fc7fe94
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-3.tests
@@ -0,0 +1,16 @@
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+0030:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|zero@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+0031:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|one@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+0032:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|two@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+0033:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|three@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+0034:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|four@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+0035:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|five@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+0036:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|six@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+0037:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|seven@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+0038:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|eight@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+0039:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|nine@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+2793:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|one_zero@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+24EB:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|one_one@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+24EC:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|one_two@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+24ED:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|one_three@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+24EE:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|one_four@1793,0]
+../fonts/TestMORXThree.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0043,U+0044,U+24EF:[A|B@363,0|X@722,0|C@1086,0|D@1402,0|one_five@1793,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-4.tests b/test/shaping/data/text-rendering-tests/tests/MORX-4.tests
new file mode 100644
index 0000000..cbb1ce8
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-4.tests
@@ -0,0 +1,15 @@
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0031:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|one@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0032:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|two@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0044,U+0058,U+0059,U+005A,U+0033:[P|Q@333,0|R@699,0|D@1050,0|A@1880,0|X@2710,0|Y@3074,0|Z@3406,0|three@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+0034:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|X@2710,0|Y@3074,0|Z@3406,0|four@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+0035:[P|Q@333,0|R@699,0|B@1050,0|A@1880,0|X@2710,0|Y@3074,0|Z@3406,0|five@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+0036:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|X@2710,0|Y@3074,0|Z@3406,0|six@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+0037:[P|Q@333,0|R@699,0|B@1050,0|A@1880,0|X@2710,0|Y@3074,0|Z@3406,0|seven@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0043,U+0044,U+0058,U+0059,U+005A,U+0038:[P|Q@333,0|R@699,0|C@1050,0|D@1880,0|A@2710,0|X@3540,0|Y@3904,0|Z@4236,0|eight@4558,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0043,U+0044,U+0058,U+0059,U+005A,U+0039:[P|Q@333,0|R@699,0|D@1050,0|C@1880,0|A@2710,0|X@3540,0|Y@3904,0|Z@4236,0|nine@4558,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0044,U+0058,U+0059,U+005A,U+2793:[P|Q@333,0|R@699,0|D@1050,0|A@1880,0|B@2710,0|X@3540,0|Y@3904,0|Z@4236,0|one_zero@4558,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0044,U+0058,U+0059,U+005A,U+24EB:[P|Q@333,0|R@699,0|D@1050,0|B@1880,0|A@2710,0|X@3540,0|Y@3904,0|Z@4236,0|one_one@4558,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0043,U+0044,U+0058,U+0059,U+005A,U+24EC:[P|Q@333,0|R@699,0|C@1050,0|D@1880,0|A@2710,0|B@3540,0|X@4370,0|Y@4734,0|Z@5066,0|one_two@5388,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0043,U+0044,U+0058,U+0059,U+005A,U+24ED:[P|Q@333,0|R@699,0|C@1050,0|D@1880,0|B@2710,0|A@3540,0|X@4370,0|Y@4734,0|Z@5066,0|one_three@5388,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0043,U+0044,U+0058,U+0059,U+005A,U+24EE:[P|Q@333,0|R@699,0|D@1050,0|C@1880,0|A@2710,0|B@3540,0|X@4370,0|Y@4734,0|Z@5066,0|one_four@5388,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0043,U+0044,U+0058,U+0059,U+005A,U+24EF:[P|Q@333,0|R@699,0|D@1050,0|C@1880,0|B@2710,0|A@3540,0|X@4370,0|Y@4734,0|Z@5066,0|one_five@5388,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-5.tests b/test/shaping/data/text-rendering-tests/tests/MORX-5.tests
new file mode 100644
index 0000000..ca8d086
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-5.tests
@@ -0,0 +1,25 @@
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0033:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|three@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0034:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|four@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0035:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|five@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0036:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|six@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0037:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|seven@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0038:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|eight@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+0038:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|X@2710,0|Y@3074,0|Z@3406,0|eight@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+0039:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|nine@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+0039:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|X@2710,0|Y@3074,0|Z@3406,0|nine@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+2793:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|one_zero@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+2793:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|X@2710,0|Y@3074,0|Z@3406,0|one_zero@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+24EB:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|one_one@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+24EB:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|X@2710,0|Y@3074,0|Z@3406,0|one_one@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+24EC:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|one_two@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+24EC:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|X@2710,0|Y@3074,0|Z@3406,0|one_two@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0043,U+0058,U+0059,U+005A,U+24EC:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|C@2710,0|X@3540,0|Y@3904,0|Z@4236,0|one_two@4558,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+24ED:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|one_three@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+24ED:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|X@2710,0|Y@3074,0|Z@3406,0|one_three@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0043,U+0058,U+0059,U+005A,U+24ED:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|C@2710,0|X@3540,0|Y@3904,0|Z@4236,0|one_three@4558,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+24EE:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|one_four@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+24EE:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|X@2710,0|Y@3074,0|Z@3406,0|one_four@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0043,U+0058,U+0059,U+005A,U+24EE:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|C@2710,0|X@3540,0|Y@3904,0|Z@4236,0|one_four@4558,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0058,U+0059,U+005A,U+24EF:[P|Q@333,0|R@699,0|A@1050,0|X@1880,0|Y@2244,0|Z@2576,0|one_five@2898,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0058,U+0059,U+005A,U+24EF:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|X@2710,0|Y@3074,0|Z@3406,0|one_five@3728,0]
+../fonts/TestMORXFour.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0050,U+0051,U+0052,U+0041,U+0042,U+0043,U+0058,U+0059,U+005A,U+24EF:[P|Q@333,0|R@699,0|A@1050,0|B@1880,0|C@2710,0|X@3540,0|Y@3904,0|Z@4236,0|one_five@4558,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-6.tests b/test/shaping/data/text-rendering-tests/tests/MORX-6.tests
new file mode 100644
index 0000000..ff9c061
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-6.tests
@@ -0,0 +1 @@
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+004F,U+004F,U+0041,U+0042,U+0043,U+0044,U+0045,U+0046,U+0047,U+004F,U+004F,U+004F,U+0033,U+0031,U+0034,U+0031:[O|O@418,0|O@836,0|E@1254,0|F@2084,0|A@2914,0|G@3744,0|B@4574,0|C@5404,0|D@6234,0|O@7064,0|O@7482,0|O@7900,0|three@8318,0|one@9168,0|four@10018,0|one@10868,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-7.tests b/test/shaping/data/text-rendering-tests/tests/MORX-7.tests
new file mode 100644
index 0000000..f250848
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-7.tests
@@ -0,0 +1 @@
+../fonts/TestMORXTwo.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+004F,U+0042,U+0043,U+0044,U+0031:[B|C@830,0|D@1660,0|O@2490,0|one@2908,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-8.tests b/test/shaping/data/text-rendering-tests/tests/MORX-8.tests
new file mode 100644
index 0000000..aa0d28b
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-8.tests
@@ -0,0 +1,3 @@
+../fonts/TestMORXEight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0030,U+0041,U+0042,U+0043:[zero|A@914,0|B@1552,0|C@2202,0]
+../fonts/TestMORXEight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0031,U+0041,U+0042,U+0043:[one|B@914,0|C@1564,0|A@2196,0]
+../fonts/TestMORXEight.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0032,U+0041,U+0042,U+0043:[two|C@914,0|A@1546,0|B@2184,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/MORX-9.tests b/test/shaping/data/text-rendering-tests/tests/MORX-9.tests
new file mode 100644
index 0000000..a899908
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/MORX-9.tests
@@ -0,0 +1 @@
+../fonts/TestMORXNine.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0041,U+0042,U+0058,U+0041,U+0042:[B|A@650,0|X@1288,0|A@1874,0|B@2512,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/SHARAN-1.tests b/test/shaping/data/text-rendering-tests/tests/SHARAN-1.tests
new file mode 100644
index 0000000..56e83ab
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/SHARAN-1.tests
@@ -0,0 +1,6 @@
+../fonts/TestShapeAran.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0644,U+0633,U+0627,U+0646:[OneDotEnclNS@398,-1|NoonxSep|AlefFin@861,0|SeenMed.inT2outT1@1125,0|sp0@1664,0|LamIni.outT2@1664,223]
+../fonts/TestShapeAran.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+06CC,U+0648,U+0646,U+06CC,U+06A9,U+0648,U+0688:[TahSmallNS@118,-213|DalSep|WawFin.cut@300,0|KafMed.outT3@573,206|TwoDotsBelowNS@1115,220|BehxMed.inT2outT1@903,304|OneDotAboveNS@1271,-71|sp1@1170,0|BehxIni.outT2@1170,449|WawFin.inD2@1387,0|TwoDotsBelowNS@1867,1|sp0@1758,0|BehxIni.outD2WQ@1758,323]
+../fonts/TestShapeAran.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0641,U+0648,U+0646,U+0679:[TahSmallNS@595,-331|BehxFin.soft|OneDotAboveNS@1163,-182|sp0@1184,0|BehxIni.outT2B@1184,300|WawFin.inD2alt@1340,0|OneDotAboveNS@1784,108|sp0@1599,0|FehxIni.outD2WQ@1599,237]
+../fonts/TestShapeAran.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0679,U+0627,U+0626,U+067E,U+0020,U+0641,U+06CC,U+0633:[SeenFin|TwoDotsBelowNS@1216,269|BehxMed.inT1outT2SeenWide@1041,455|OneDotAboveNS@1454,224|sp0@1271,0|FehxIni@1271,490|space@1584,0|ThreeDotsDownBelowNS@2290,-159|BehxFin.soft@1715,0|HamzaAboveNS@2878,-201|sp0@2899,0|BehxIni.outT2B@2899,300|AlefFin.narrow@3056,0|TahSmallNS@3442,-420|sp0@3295,0|BehxIni.A@3295,0]
+../fonts/TestShapeAran.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0641,U+0646,U+0020,U+062E,U+0637,U+0627,U+0637,U+06CC:[YehxFin|sp0@521,0|TahIni.outD2@521,380|AlefFin@1119,0|TahMed.inD1outT1@1382,0|OneDotAboveNS@2081,-47|sp0@1451,0|HahIni.outD1@1451,36|space@2326,0|OneDotEnclNS@2855,-2|NoonxFin@2458,0|OneDotAboveNS@3361,188|sp0@3208,0|FehxIni.outT2N@3208,336]
+../fonts/TestShapeAran.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0646,U+0633,U+062A,U+0639,U+0644,U+06CC,U+0642:[TwoDotsAboveNS@519,-199|QafxFin.cut|TwoDotsBelowNS@977,141|BehxMed.inT2outD2WQ@692,272|LamMed.outT2@1023,434|AinMed.inT3outT1@1301,507|TwoDotsAboveNS@1785,209|BehxMed.inT2outT3@1563,603|SeenMed.inT2outT2@1865,735|OneDotAboveNS@2574,670|sp0@2434,0|BehxIni.outT2tall@2434,952]
diff --git a/test/shaping/data/text-rendering-tests/tests/SHBALI-1.tests b/test/shaping/data/text-rendering-tests/tests/SHBALI-1.tests
new file mode 100644
index 0000000..4c727b0
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/SHBALI-1.tests
@@ -0,0 +1,22 @@
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B38,U+1B00:[gid23|gid60@1113,0|gid4@1064,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B15,U+1B44,U+1B16,U+1B02:[gid25|gid132@1092,0|gid6@942,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B18,U+1B3B:[gid28|gid62@796,0|gid57@794,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B19,U+1B40:[gid66|gid29@483,0|gid57@1536,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B1A,U+1B3F:[gid67|gid30@483,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B14,U+1B36:[gid24|gid58@828,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B13,U+1B01:[gid23|gid129@1111,0|gid5@1064,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B1B,U+1B01:[gid23|gid137@1111,0|gid5@1379,181]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B26,U+1B03:[gid23|gid148@1111,0|gid7@991,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B13,U+1B38:[gid23|gid129@1111,0|gid60@1111,-488]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B13,U+1B3C:[gid23|gid129@1111,0|gid70@1128,0|gid170@1113,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B13,U+1B3D:[gid23|gid129@1111,0|gid70@1128,0|gid170@1113,0|gid57@1111,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B3E:[gid66|gid23@483,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B36,U+1B3E:[gid23|gid58@1064,0|gid66@1111,0|gid128@1594,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B38,U+1B3E:[gid23|gid60@1113,0|gid66@1111,0|gid128@1594,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B15,U+1B3E:[gid66|gid23@483,0|gid131@1594,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B40:[gid66|gid23@483,0|gid57@1594,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B3E:[gid66|gid23@483,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B3E,U+1B36:[gid66|gid23@483,0|gid58@1548,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B3E,U+1B38:[gid66|gid23@483,0|gid60@1597,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B15,U+1B3E:[gid66|gid23@483,0|gid131@1594,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B40:[gid66|gid23@483,0|gid57@1594,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/SHBALI-2.tests b/test/shaping/data/text-rendering-tests/tests/SHBALI-2.tests
new file mode 100644
index 0000000..3878fcc
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/SHBALI-2.tests
@@ -0,0 +1,12 @@
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B27,U+1B3E:[gid66|gid23@483,0|gid149@1594,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B28,U+1B3F:[gid67|gid23@483,0|gid150@1594,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B31,U+1B3E:[gid66|gid23@483,0|gid159@1594,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B32,U+1B3E:[gid66|gid23@483,0|gid60@1597,0|gid149@1594,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B4A,U+1B3E:[gid66|gid23@483,0|gid60@1597,0|gid165@1594,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B1B,U+1B44,U+1B13:[gid181|gid129@1064,-195]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B1B,U+1B44,U+1B13,U+1B3E:[gid66|gid181@483,0|gid129@1548,-195]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B1B,U+1B44,U+1B13,U+1B38,U+1B00:[gid181|gid129@1064,-195|gid60@1064,-684|gid4@855,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B13,U+1B38:[gid23|gid129@1111,0|gid60@1111,-488]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B1B,U+1B39:[gid23|gid137@1111,0|gid61@1261,-488]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B31,U+1B3A:[gid23|gid159@1111,0|gid62@1753,0]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B13,U+1B44,U+1B45,U+1B38:[gid23|gid162@1111,0|gid60@1111,-488]
diff --git a/test/shaping/data/text-rendering-tests/tests/SHBALI-3.tests b/test/shaping/data/text-rendering-tests/tests/SHBALI-3.tests
new file mode 100644
index 0000000..a0f3a32
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/SHBALI-3.tests
@@ -0,0 +1,9 @@
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B66,U+1B6B:[gid102|gid107@560,-10]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B66,U+1B6C:[gid102|gid108@573,49]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B66,U+1B6D:[gid102|gid109@652,-10]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B66,U+1B6E:[gid102|gid110@652,-98]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B66,U+1B6F:[gid102|gid111@667,-10]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B66,U+1B70:[gid102|gid112@667,-10]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B66,U+1B71:[gid102|gid113@667,-10]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B66,U+1B72:[gid102|gid114@667,-10]
+../fonts/NotoSansBalinese-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+1B66,U+1B73:[gid102|gid115@599,-10]
diff --git a/test/shaping/data/text-rendering-tests/tests/SHKNDA-1.tests b/test/shaping/data/text-rendering-tests/tests/SHKNDA-1.tests
new file mode 100644
index 0000000..adb5aa7
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/SHKNDA-1.tests
@@ -0,0 +1,34 @@
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB2,U+0CCD,U+0CB2,U+0CBF:[knLI|knLAc2@757,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9F,U+0CCD,U+0CB8,U+0CCD:[knTT|knSAc2@1021,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB3,U+0CBF:[knLLI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA1,U+0CBF:[knDDI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAE,U+0CC6:[knME]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB0,U+0CBF:[knRI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C96,U+0CCD,U+0CAF,U+0CC6:[knKHE|knYAc2@846,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAB,U+0CCD,U+0CB0,U+0CBF:[knPHI|knRAc2@735,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA8,U+0CC6:[knNE]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C97,U+0CBF:[knGI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB7,U+0CCD,U+0C9F,U+0CBF:[knSSI|knTTAc2@746,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAF,U+0CBF,U+0C82:[knYI|knAnusvara@1252,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9A,U+0CC0:[knCI|knLengthmark@766,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA8,U+0CBF:[knNI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C97,U+0CCD,U+0CB2,U+0CBF:[knGI|knLAc2@621,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB7,U+0CBF:[knSSI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C97,U+0CC6:[knGE]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA6,U+0CCD,U+0CB5,U+0CBF:[knDI|knVAc2@740,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA4,U+0CC0:[knTI|knLengthmark@613,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAE,U+0CBF:[knMI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB2,U+0CBF:[knLI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C97,U+0CBF:[knGI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA8,U+0CCD:[knN]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAC,U+0CBF:[knBI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB2,U+0CBF:[knLI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA8,U+0CCD,U+0CA8,U+0CBF,U+0C82:[knNI|knNAc2@678,0|knAnusvara@755,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB2,U+0CCD,U+0CB2,U+0CBF:[knLI|knLAc2@757,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA7,U+0CBF:[knDHI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAA,U+0CCC:[knPA.base|knmAU@739,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB5,U+0CBF,U+0C82:[knVI|knAnusvara@749,0]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA1,U+0CBF:[knDDI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9F,U+0CBF:[knTTI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA8,U+0CBF:[knNI]
+../fonts/NotoSerifKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA7,U+0CBF:[knDHI]
diff --git a/test/shaping/data/text-rendering-tests/tests/SHKNDA-2.tests b/test/shaping/data/text-rendering-tests/tests/SHKNDA-2.tests
new file mode 100644
index 0000000..7936308
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/SHKNDA-2.tests
@@ -0,0 +1,16 @@
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA8,U+0CCD,U+0CA8,U+0CBE:[gid150|gid57@711,0|gid116@1160,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA8,U+0CCD,U+0CA8,U+0CBE:[gid150|gid57@711,0|gid116@1160,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA4,U+0CCD,U+0CA4,U+0CBE:[gid146|gid57@623,0|gid112@1071,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9F,U+0CCD,U+0C9F,U+0CBE:[gid141|gid57@815,0|gid107@1264,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA1,U+0CCB,U+0C82,U+0C97,U+0CBF:[gid249|gid61@768,0|gid71@1513,0|gid4@1925,0|gid207@2475,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9C,U+0CBF,U+0CBC,U+0CD5,U+0CAC,U+0CC6,U+0CA8,U+0CCD:[gid211|gid55@652,0|gid71@776,0|gid259@1188,0|gid186@1994,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9C,U+0CBE,U+0CBC,U+0C95,U+0CBF,U+0CB0,U+0CCD:[gid139|gid57@776,0|gid55@652,0|gid205@1225,0|gid193@1799,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C87,U+0CA8,U+0CCD,U+0CAB,U+0CCD,U+0CB2,U+0CC6,U+0C95,U+0CCD,U+0CB7,U+0CA8,U+0CB2,U+0CCD:[gid8|gid256@711,0|gid118@1422,0|gid335@1591,0|gid282@1978,0|gid39@2552,0|gid195@3263,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C87,U+0CA8,U+0CCD,U+0CAB,U+0CCD,U+0CB2,U+0CC6,U+0C95,U+0CCD,U+0CB7,U+0CA8,U+0CCD:[gid8|gid256@711,0|gid118@1422,0|gid335@1591,0|gid282@1978,0|gid186@2552,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA6,U+0C9F,U+0CCD,U+0CB8,U+0CCD:[gid37|gid177@765,0|gid130@1814,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C8E,U+0C95,U+0CCD,U+0CB8,U+0CCD:[gid14|gid167@787,0|gid130@1596,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAE,U+0CBE,U+0CB0,U+0CCD,U+0C9A,U+0CCD:[gid155|gid57@1156,0|gid172@1605,0|gid94@2718,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9F,U+0CC6,U+0C95,U+0CCD,U+0CB8,U+0CCD,U+0C9F,U+0CCD:[gid247|gid167@815,0|gid130@1624,0|gid317@1792,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAC,U+0CC1,U+0C95,U+0CCD,U+0CB8,U+0CCD:[gid42|gid60@801,0|gid167@1165,0|gid130@1974,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB8,U+0CBE,U+0CAB,U+0CCD,U+0C9F,U+0CCD:[gid163|gid57@709,0|gid188@1158,0|gid107@2184,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9C,U+0CB8,U+0CCD,U+0C9F,U+0CCD:[gid27|gid200@776,0|gid107@1720,0]
diff --git a/test/shaping/data/text-rendering-tests/tests/SHKNDA-3.tests b/test/shaping/data/text-rendering-tests/tests/SHKNDA-3.tests
new file mode 100644
index 0000000..460ff0f
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/tests/SHKNDA-3.tests
@@ -0,0 +1,31 @@
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C95,U+0CCB,U+0C82:[gid239|gid61@574,0|gid71@1319,0|gid4@1731,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C96,U+0CCB,U+0C82:[gid240|gid61@865,0|gid71@1610,0|gid4@2022,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C97,U+0CCB,U+0C82:[gid241|gid61@648,0|gid71@1393,0|gid4@1805,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C98,U+0CCB,U+0C82:[gid242|gid279@997,0|gid71@1742,0|gid4@2153,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C99,U+0CCB,U+0C82:[gid24|gid67@737,0|gid71@1718,0|gid4@2130,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9A,U+0CCB,U+0C82:[gid243|gid61@795,0|gid71@1540,0|gid4@1952,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9B,U+0CCB,U+0C82:[gid244|gid61@843,0|gid71@1588,0|gid4@2000,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9C,U+0CCB,U+0C82:[gid245|gid61@776,0|gid71@1522,0|gid4@1933,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9D,U+0CCB,U+0C82:[gid246|gid61@1379,0|gid71@2124,0|gid4@2536,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9E,U+0CCB,U+0C82:[gid29|gid67@968,0|gid71@1949,0|gid4@2360,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C9F,U+0CCB,U+0C82:[gid247|gid61@815,0|gid71@1560,0|gid4@1972,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA0,U+0CCB,U+0C82:[gid248|gid61@651,0|gid71@1397,0|gid4@1808,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA1,U+0CCB,U+0C82:[gid249|gid61@768,0|gid71@1513,0|gid4@1925,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA2,U+0CCB,U+0C82:[gid250|gid61@768,0|gid71@1513,0|gid4@1925,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA3,U+0CCB,U+0C82:[gid251|gid61@867,0|gid71@1612,0|gid4@2023,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA4,U+0CCB,U+0C82:[gid252|gid61@623,0|gid71@1368,0|gid4@1779,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA5,U+0CCB,U+0C82:[gid253|gid61@765,0|gid71@1510,0|gid4@1921,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA6,U+0CCB,U+0C82:[gid254|gid61@765,0|gid71@1510,0|gid4@1921,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA7,U+0CCB,U+0C82,U+0020:[gid255|gid61@765,0|gid71@1510,0|gid4@1921,0|gid3@2472,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CA8,U+0CCB,U+0C82:[gid256|gid61@711,0|gid71@1456,0|gid4@1868,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAA,U+0CCB,U+0C82:[gid257|gid275@792,0|gid71@1434,0|gid4@1846,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAB,U+0CCB,U+0C82:[gid258|gid277@792,0|gid71@1434,0|gid4@1846,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAC,U+0CCB,U+0C82:[gid259|gid61@806,0|gid71@1551,0|gid4@1963,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAD,U+0CCB,U+0C82:[gid260|gid61@806,0|gid71@1551,0|gid4@1963,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAE,U+0CCB,U+0C82:[gid280|gid71@1539,0|gid4@1951,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CAF,U+0CCB,U+0C82:[gid281|gid71@1712,0|gid4@2124,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB0,U+0CCB,U+0C82:[gid263|gid61@651,0|gid71@1397,0|gid4@1808,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB1,U+0CCB,U+0C82:[gid47|gid67@831,0|gid71@1812,0|gid4@2223,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB2,U+0CCB,U+0C82:[gid264|gid61@769,0|gid71@1514,0|gid4@1925,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0CB5,U+0CCB,U+0C82:[gid266|gid275@794,0|gid71@1437,0|gid4@1848,0]
+../fonts/NotoSansKannada-Regular.ttf:--font-size=1000 --ned --remove-default-ignorables --font-funcs=ft:U+0C86,U+0CCD,U+0CAF,U+0C95,U+0CCD,U+0CB7,U+0CBF,U+0CB8,U+0CCD,U+200C:[gid7|gid122@838,0|gid285@1098,0|gid200@1672,0|gid3@2694,0]
diff --git a/test/shaping/data/text-rendering-tests/update.sh b/test/shaping/data/text-rendering-tests/update.sh
new file mode 100755
index 0000000..47545bc
--- /dev/null
+++ b/test/shaping/data/text-rendering-tests/update.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+dir=`dirname "$0"`
+
+set -ex
+if test -d text-rendering-tests; then
+	cd text-rendering-tests
+	git pull
+	cd ..
+else
+	git clone https://github.com/unicode-org/text-rendering-tests
+fi
+
+test -d fonts && git rm -rf fonts
+test -d fonts && (echo "fonts/ dir not empty; investigate."; false)
+cp -a text-rendering-tests/fonts .
+git add fonts
+
+rmdir tests || true
+test -d tests && git rm -rf tests || true
+test -d tests && (echo "tests/ dir not empty; investigate."; false)
+mkdir tests
+
+echo "TESTS = \\" > Makefile.sources
+
+DISABLED="DISBALED_TESTS = \\"
+for x in text-rendering-tests/testcases/*.html; do
+	test "x$x" = xtext-rendering-tests/testcases/index.html && continue
+	out=tests/`basename "$x" .html`.tests
+	"$dir"/extract-tests.py < "$x" > "$out"
+	if grep -q "^$out$" DISABLED; then
+		DISABLED="$DISABLED
+	$out \\"
+	else
+		echo "	$out \\" >> Makefile.sources
+	fi
+done
+git add tests
+
+echo '	$(NULL)' >> Makefile.sources
+echo >> Makefile.sources
+echo "$DISABLED" >> Makefile.sources
+echo '	$(NULL)' >> Makefile.sources
+git add Makefile.sources
+
+git commit -e -m "[test/text-rendering-tests] Update from upstream"
diff --git a/test/shaping/fonts/sha1sum/6466d38c62e73a39202435a4f73bf5d6acbb73c0.ttf b/test/shaping/fonts/sha1sum/6466d38c62e73a39202435a4f73bf5d6acbb73c0.ttf
deleted file mode 100644
index 33c4229..0000000
--- a/test/shaping/fonts/sha1sum/6466d38c62e73a39202435a4f73bf5d6acbb73c0.ttf
+++ /dev/null
Binary files differ
diff --git a/test/shaping/fonts/sha1sum/7ef276fc886ea502a03b9b0e5c8b547d5dc2b61c.ttf b/test/shaping/fonts/sha1sum/7ef276fc886ea502a03b9b0e5c8b547d5dc2b61c.ttf
deleted file mode 100644
index fb4534a..0000000
--- a/test/shaping/fonts/sha1sum/7ef276fc886ea502a03b9b0e5c8b547d5dc2b61c.ttf
+++ /dev/null
Binary files differ
diff --git a/test/shaping/hb-diff-ngrams b/test/shaping/hb-diff-ngrams
deleted file mode 100755
index c02f541..0000000
--- a/test/shaping/hb-diff-ngrams
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env python
-
-from hb_test_tools import *
-
-UtilMains.process_multiple_files (DiffSinks.print_ngrams)
diff --git a/test/shaping/hb-manifest-read b/test/shaping/hb-manifest-read
deleted file mode 100755
index b1b36ba..0000000
--- a/test/shaping/hb-manifest-read
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env python
-
-from hb_test_tools import *
-
-UtilMains.process_multiple_args (FilterHelpers.filter_printer_function (Manifest.read), mnemonic="DIR")
diff --git a/test/shaping/hb-manifest-update b/test/shaping/hb-manifest-update
deleted file mode 100755
index eeb84b8..0000000
--- a/test/shaping/hb-manifest-update
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env python
-
-from hb_test_tools import *
-
-UtilMains.process_multiple_args (Manifest.update_recursive, mnemonic="DIR")
diff --git a/test/shaping/hb_test_tools.py b/test/shaping/hb_test_tools.py
index 7473982..f7e5943 100644
--- a/test/shaping/hb_test_tools.py
+++ b/test/shaping/hb_test_tools.py
@@ -7,6 +7,9 @@
 diff_symbols = "-+=*&^%$#@!~/"
 diff_colors = ['red', 'green', 'blue']
 
+def codepoints(s):
+	return (ord (u) for u in s)
+
 try:
 	unichr = unichr
 
@@ -43,9 +46,42 @@
 				except UnicodeDecodeError:
 					raise ValueError('unichr() arg not in range(0x110000)')
 
+		def codepoints(s):
+			high_surrogate = None
+			for u in s:
+				cp = ord (u)
+				if 0xDC00 <= cp <= 0xDFFF:
+					if high_surrogate:
+						yield 0x10000 + (high_surrogate - 0xD800) * 0x400 + (cp - 0xDC00)
+						high_surrogate = None
+					else:
+						yield 0xFFFC
+				else:
+					if high_surrogate:
+						yield 0xFFFC
+						high_surrogate = None
+					if 0xD800 <= cp <= 0xDBFF:
+						high_surrogate = cp
+					else:
+						yield cp
+						high_surrogate = None
+			if high_surrogate:
+				yield 0xFFFC
+
 except NameError:
 	unichr = chr
 
+try:
+	unicode = unicode
+except NameError:
+	unicode = str
+
+def tounicode(s, encoding='ascii', errors='strict'):
+	if not isinstance(s, unicode):
+		return s.decode(encoding, errors)
+	else:
+		return s
+
 class ColorFormatter:
 
 	class Null:
@@ -257,32 +293,6 @@
 		total = passed + failed
 		print ("%d out of %d tests passed.  %d failed (%g%%)" % (passed, total, failed, 100. * failed / total))
 
-	@staticmethod
-	def print_ngrams (f, ns=(1,2,3)):
-		gens = tuple (Ngram.generator (n) for n in ns)
-		allstats = Stats ()
-		allgrams = {}
-		for key, lines in DiffHelpers.separate_test_cases (f):
-			test = Test (lines)
-			allstats.add (test)
-
-			for gen in gens:
-				for ngram in gen (test.unicodes):
-					if ngram not in allgrams:
-						allgrams[ngram] = Stats ()
-					allgrams[ngram].add (test)
-
-		importantgrams = {}
-		for ngram, stats in allgrams.iteritems ():
-			if stats.failed.count >= 30: # for statistical reasons
-				importantgrams[ngram] = stats
-		allgrams = importantgrams
-		del importantgrams
-
-		for ngram, stats in allgrams.iteritems ():
-			print ("zscore: %9f failed: %6d passed: %6d ngram: <%s>" % (stats.zscore (allstats), stats.failed.count, stats.passed.count, ','.join ("U+%04X" % u for u in ngram)))
-
-
 
 class Test:
 
@@ -445,12 +455,12 @@
 
 	@staticmethod
 	def decode (s):
-		return u','.join ("U+%04X" % ord (u) for u in unicode (s, 'utf-8')).encode ('utf-8')
+		return u','.join ("U+%04X" % cp for cp in codepoints (tounicode (s, 'utf-8')))
 
 	@staticmethod
 	def parse (s):
 		s = re.sub (r"0[xX]", " ", s)
-		s = re.sub (r"[<+>{},;&#\\xXuUnNiI\n	]", " ", s)
+		s = re.sub (r"[<+>{},;&#\\xXuUnNiI\n\t]", " ", s)
 		return [int (x, 16) for x in s.split ()]
 
 	@staticmethod
diff --git a/test/shaping/record-test.sh b/test/shaping/record-test.sh
index b2a74f7..8ce1653 100755
--- a/test/shaping/record-test.sh
+++ b/test/shaping/record-test.sh
@@ -1,7 +1,12 @@
 #!/bin/bash
 
-dir=`mktemp --directory`
+dir=`mktemp -d`
 
+out=/dev/stdout
+if test "x${1:0:3}" == 'x-o='; then
+	out=${1:3}
+	shift
+fi
 hb_shape=$1
 shift
 fontfile=$1
@@ -41,31 +46,34 @@
 	echo "hb-shape failed." >&2
 	exit 2
 fi
+glyph_names=`echo "$text" | $hb_shape $options --no-clusters --no-positions "$fontfile" | sed 's/[][]//g; s/|/,/g'`
 
 cp "$fontfile" "$dir/font.ttf"
-pyftsubset \
+fonttools subset \
 	--glyph-names \
 	--no-hinting \
+	--layout-features='*' \
 	"$dir/font.ttf" \
+	--glyphs="$glyph_names" \
 	--text="$text"
-if ! test -s "$dir/font.ttf.subset"; then
-	echo "Subsetter didn't produce nonempty subset font in $dir/font.ttf.subset" >&2
+if ! test -s "$dir/font.subset.ttf"; then
+	echo "Subsetter didn't produce nonempty subset font in $dir/font.subset.ttf" >&2
 	exit 2
 fi
 
 # Verify that subset font produces same glyphs!
-glyphs_subset=`echo "$text" | $hb_shape $options "$dir/font.ttf.subset"`
+glyphs_subset=`echo "$text" | $hb_shape $options "$dir/font.subset.ttf"`
 
 if ! test "x$glyphs" = "x$glyphs_subset"; then
 	echo "Subset font produced different glyphs!" >&2
 	echo "Perhaps font doesn't have glyph names; checking visually..." >&2
 	hb_view=${hb_shape/shape/view}
 	echo "$text" | $hb_view $options "$dir/font.ttf" --output-format=png --output-file="$dir/orig.png"
-	echo "$text" | $hb_view $options "$dir/font.ttf.subset" --output-format=png --output-file="$dir/subset.png"
+	echo "$text" | $hb_view $options "$dir/font.subset.ttf" --output-format=png --output-file="$dir/subset.png"
 	if ! cmp "$dir/orig.png" "$dir/subset.png"; then
 		echo "Images differ.  Please inspect $dir/*.png." >&2
-		echo "$glyphs"
-		echo "$glyphs_subset"
+		echo "$glyphs" >> "$out"
+		echo "$glyphs_subset" >> "$out"
 		exit 2
 	fi
 	echo "Yep; all good." >&2
@@ -74,9 +82,9 @@
 	glyphs=$glyphs_subset
 fi
 
-sha1sum=`sha1sum "$dir/font.ttf.subset" | cut -d' ' -f1`
-subset="fonts/sha1sum/$sha1sum.ttf"
-mv "$dir/font.ttf.subset" "$subset"
+sha1sum=`sha1sum "$dir/font.subset.ttf" | cut -d' ' -f1`
+subset="data/in-house/fonts/$sha1sum.ttf"
+mv "$dir/font.subset.ttf" "$subset"
 
 # There ought to be an easier way to do this, but it escapes me...
 unicodes_file=`mktemp`
@@ -86,8 +94,12 @@
 # Open the "file"s
 exec 3<"$unicodes_file"
 exec 4<"$glyphs_file"
+relative_subset="$subset"
+if test "$out" != "/dev/stdout"; then
+	relative_subset="$(/usr/bin/python -c 'import os, sys; print (os.path.relpath (sys.argv[1], sys.argv[2]))' "$subset" "$(dirname "$out")")"
+fi
 while read uline <&3 && read gline <&4; do
-	echo "$subset:$options:$uline:$gline"
+	echo "$relative_subset:$options:$uline:$gline" >> "$out"
 done
 
 
diff --git a/test/shaping/run-tests.py b/test/shaping/run-tests.py
new file mode 100755
index 0000000..c65e714
--- /dev/null
+++ b/test/shaping/run-tests.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+import sys, os, subprocess
+
+
+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
+
+
+args = sys.argv[1:]
+if not args or sys.argv[1].find('hb-shape') == -1 or not os.path.exists (sys.argv[1]):
+	print ("""First argument does not seem to point to usable hb-shape.""")
+	sys.exit (1)
+hb_shape, args = args[0], args[1:]
+
+extra_options = "--verify"
+
+fails = 0
+
+reference = False
+if len (args) and args[0] == "--reference":
+	reference = True
+	args = args[1:]
+
+if not len (args):
+	args = ['-']
+
+for filename in args:
+	if not reference:
+		if filename == '-':
+			print ("Running tests from standard input")
+		else:
+			print ("Running tests in " + filename)
+
+	if filename == '-':
+		f = sys.stdin
+	else:
+		f = open (filename)
+
+	for line in f:
+		fontfile, options, unicodes, glyphs_expected = line.split (":")
+		cwd = os.path.dirname(filename)
+		fontfile = os.path.normpath (os.path.join (cwd, fontfile))
+
+		if line.startswith ("#"):
+			if not reference:
+				print ("# %s %s --unicodes %s" % (hb_shape, fontfile, unicodes))
+			continue
+
+		if not reference:
+			print ("%s %s %s %s --unicodes %s" %
+					 (hb_shape, fontfile, extra_options, options, unicodes))
+
+		glyphs1, returncode = cmd ([hb_shape, "--font-funcs=ft",
+			fontfile, extra_options, "--unicodes",
+			unicodes] + (options.split (' ') if options else []))
+
+		if returncode:
+			print ("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",
+			unicodes] + (options.split (' ') if options else []))
+
+		if returncode:
+			print ("ERROR: hb-shape --font-funcs=ot failed.") # file=sys.stderr
+			fails = fails + 1
+			#continue
+
+		if glyphs1 != glyphs2:
+			print ("FT funcs: " + glyphs1) # file=sys.stderr
+			print ("OT funcs: " + glyphs2) # file=sys.stderr
+			fails = fails + 1
+
+		if reference:
+			print (":".join ([fontfile, options, unicodes, glyphs1]))
+			continue
+
+		if glyphs1.strip() != glyphs_expected.strip():
+			print ("Actual:   " + glyphs1) # file=sys.stderr
+			print ("Expected: " + glyphs_expected) # file=sys.stderr
+			fails = fails + 1
+
+if fails != 0:
+	if not reference:
+		print (str (fails) + " tests failed.") # file=sys.stderr
+	sys.exit (1)
+
+else:
+	if not reference:
+		print ("All tests passed.")
diff --git a/test/shaping/run-tests.sh b/test/shaping/run-tests.sh
deleted file mode 100755
index 021c6f8..0000000
--- a/test/shaping/run-tests.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/bin/sh
-
-test "x$srcdir" = x && srcdir=.
-test "x$builddir" = x && builddir=.
-test "x$top_builddir" = x && top_builddir=../..
-
-hb_shape=$top_builddir/util/hb-shape$EXEEXT
-
-fails=0
-
-reference=false
-if test "x$1" = x--reference; then
-	reference=true
-	shift
-fi
-
-if test $# = 0; then
-	set /dev/stdin
-fi
-
-for f in "$@"; do
-	$reference || echo "Running tests in $f"
-	while IFS=: read fontfile options unicodes glyphs_expected; do
-		if echo "$fontfile" | grep -q '^#'; then
-			$reference || echo "Skipping $fontfile:$unicodes"
-			continue
-		fi
-		$reference || echo "Testing $fontfile:$unicodes"
-		glyphs=`$srcdir/hb-unicode-encode "$unicodes" | $hb_shape $options "$srcdir/$fontfile"`
-		if test $? != 0; then
-			echo "hb-shape failed." >&2
-			fails=$((fails+1))
-			continue
-		fi
-		if $reference; then
-			echo "$fontfile:$options:$unicodes:$glyphs"
-			continue
-		fi
-		if ! test "x$glyphs" = "x$glyphs_expected"; then
-			echo "Actual:   $glyphs" >&2
-			echo "Expected: $glyphs_expected" >&2
-			fails=$((fails+1))
-		fi
-	done < "$f"
-done
-
-if test $fails != 0; then
-	$reference || echo "$fails tests failed."
-	exit 1
-else
-	$reference || echo "All tests passed."
-fi
diff --git a/test/shaping/tests/arabic-fallback-shaping.tests b/test/shaping/tests/arabic-fallback-shaping.tests
deleted file mode 100644
index 6f1cb8b..0000000
--- a/test/shaping/tests/arabic-fallback-shaping.tests
+++ /dev/null
@@ -1 +0,0 @@
-fonts/sha1sum/df768b9c257e0c9c35786c47cae15c46571d56be.ttf::U+0633,U+064F,U+0644,U+064E,U+0651,U+0627,U+0651,U+0650,U+0645,U+062A,U+06CC:[uni06CC.fina=10+1655|uni062A.medi=9+868|uni0645.init=8+1098|uni0650=2@221,0+0|uni0651=2@260,736+0|uni064E=2@935,1259+0|uni0651=2@974,736+0|uni06440627.fina=2+1470|uni064F=0@558,-10+0|uni0633.init=0+1585]
diff --git a/test/shaping/tests/arabic-feature-order.tests b/test/shaping/tests/arabic-feature-order.tests
deleted file mode 100644
index e60ab1a..0000000
--- a/test/shaping/tests/arabic-feature-order.tests
+++ /dev/null
@@ -1,3 +0,0 @@
-fonts/sha1sum/813c2f8e5512187fd982417a7fb4286728e6f4a8.ttf::U+1820,U+180B:[uni2048.E81A=0+1550]
-fonts/sha1sum/8a9fea2a7384f2116e5b84a9b31f83be7850ce21.ttf::U+1820,U+180B:[uni2048.E81A=0+1550]
-fonts/sha1sum/a919b33197965846f21074b24e30250d67277bce.ttf::U+0644,U+0644,U+0647:[Lellah=0+1503]
diff --git a/test/shaping/tests/arabic-like-joining.tests b/test/shaping/tests/arabic-like-joining.tests
deleted file mode 100644
index ec994d0..0000000
--- a/test/shaping/tests/arabic-like-joining.tests
+++ /dev/null
@@ -1 +0,0 @@
-fonts/sha1sum/5dfad7735c6a67085f1b90d4d497e32907db4c78.ttf::U+1E922,U+1E923,U+1E924,U+1E925,U+1E926,U+1E927,U+1E928,U+1E929,U+1E92A,U+1E92B,U+1E92C,U+1E92D,U+1E92E,U+1E92F,U+1E930,U+1E931,U+1E932,U+1E933,U+1E934,U+1E935,U+1E936,U+1E937,U+1E938,U+1E939,U+1E93A,U+1E93B,U+1E93C,U+1E93D,U+1E93E,U+1E93F,U+1E940,U+1E941,U+1E942,U+1E943:[sha_adlam.fina=33+711|kpo_adlam.medi=32+573|zal_adlam.medi=31+773|gbe_adlam.medi=30+594|kha_adlam.medi=29+686|va_adlam.medi=28+621|nha_adlam.medi=27+587|tu_adlam.medi=26+772|nya_adlam.medi=25+577|ga_adlam.medi=24+552|qaaf_adlam.medi=23+694|ha_adlam.medi=22+600|chi_adlam.medi=21+662|jiim_adlam.medi=20+781|u_adlam.medi=19+678|ya_adlam.medi=18+553|kaf_adlam.medi=17+808|nun_adlam.medi=16+561|waw_adlam.medi=15+651|yhe_adlam.medi=14+674|dha_adlam.medi=13+674|o_adlam.medi=12+640|i_adlam.medi=11+657|fa_adlam.medi=10+590|e_adlam.medi=9+628|ra_adlam.medi=8+599|bhe_adlam.medi=7+594|pe_adlam.medi=6+492|sinnyiiyhe_adlam.medi=5+777|ba_adlam.medi=4+655|miim_adlam.medi=3+525|laam_adlam.medi=2+554|daali_adlam.medi=1+600|alif_adlam.init=0+597]
diff --git a/test/shaping/tests/automatic-fractions.tests b/test/shaping/tests/automatic-fractions.tests
deleted file mode 100644
index f9510e2..0000000
--- a/test/shaping/tests/automatic-fractions.tests
+++ /dev/null
@@ -1,3 +0,0 @@
-fonts/sha1sum/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf::U+0031,U+0032,U+0033,U+2044,U+0034,U+0035,U+0036:[one.numr=0+600|two.numr=1+600|three.numr=2+600|fraction=3+252|four.small=4+600|five.small=5+600|six.small=6+600]
-fonts/sha1sum/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf:--direction=l --script=arab:U+0031,U+0032,U+0033,U+2044,U+0034,U+0035,U+0036:[one.numr=0+600|two.numr=1+600|three.numr=2+600|fraction=3+252|four.small=4+600|five.small=5+600|six.small=6+600]
-fonts/sha1sum/15dfc433a135a658b9f4b1a861b5cdd9658ccbb9.ttf:--direction=l:U+0661,U+0662,U+0663,U+2044,U+0664,U+0665,U+0666:[uni0661.numr=0+600|uni0662.numr=1+600|uni0663.numr=2+600|fraction=3+252|uni0664.small=4+600|uni0665.small=5+600|uni0666.small=6+600]
diff --git a/test/shaping/tests/cluster.tests b/test/shaping/tests/cluster.tests
deleted file mode 100644
index 24f04dd..0000000
--- a/test/shaping/tests/cluster.tests
+++ /dev/null
@@ -1,2 +0,0 @@
-fonts/sha1sum/6466d38c62e73a39202435a4f73bf5d6acbb73c0.ttf:--cluster-level=2:U+0078,U+030A,U+0058,U+030A:[gid2=0+1083|gid4=1@-555,-8+0|gid1=2+1200|gid4=3@-614,349+0]
-fonts/sha1sum/43ef465752be9af900745f72fe29cb853a1401a5.ttf:--cluster-level=1:U+05D4,U+05B7,U+05E9,U+05BC,U+05C1,U+05B8,U+05DE,U+05B4,U+05DD:[uni05DD=8+1359|uni05B4=7@111,0+0|uni05DE=6+1391|uni05B8=5+0|uni05BC=3+0|uni05C1=3+0|uni05E9=2+1451|uni05B7=1@28,0+0|uni05D4=0+1338]
diff --git a/test/shaping/tests/color-fonts.tests b/test/shaping/tests/color-fonts.tests
deleted file mode 100644
index 397796a..0000000
--- a/test/shaping/tests/color-fonts.tests
+++ /dev/null
@@ -1 +0,0 @@
-fonts/sha1sum/ee39587d13b2afa5499cc79e45780aa79293bbd4.ttf:--font-funcs=ot --show-extents:U+1F42F:[gid1=0+2963<0,2178,2963,-2788>]
diff --git a/test/shaping/tests/context-matching.tests b/test/shaping/tests/context-matching.tests
deleted file mode 100644
index e20616e..0000000
--- a/test/shaping/tests/context-matching.tests
+++ /dev/null
@@ -1,3 +0,0 @@
-fonts/sha1sum/4cce528e99f600ed9c25a2b69e32eb94a03b4ae8.ttf::U+1A48,U+1A58,U+1A25,U+1A48,U+1A58,U+1A25,U+1A6E,U+1A63:[uni1A48=0+1212|uni1A25=0+1912|uni1A58=0+0|uni1A48=3+1212|uni1A6E=3+0|uni1A25=3+1912|uni1A58=3+0|uni1A63=3+1212]
-fonts/sha1sum/d629e7fedc0b350222d7987345fe61613fa3929a.ttf::U+0915,U+093F,U+0915,U+093F:[ivowelsign03deva=0+530|kadeva=0+1561|ivowelsign03deva=2+530|kadeva=2+1561]
-fonts/sha1sum/f499fbc23865022234775c43503bba2e63978fe1.ttf::U+09B0,U+09CD,U+09A5,U+09CD,U+09AF,U+09C0:[gid1=0+1320|gid13=0+523|gid18=0+545]
diff --git a/test/shaping/tests/cursive-positioning.tests b/test/shaping/tests/cursive-positioning.tests
deleted file mode 100644
index ce63bd1..0000000
--- a/test/shaping/tests/cursive-positioning.tests
+++ /dev/null
@@ -1,4 +0,0 @@
-fonts/sha1sum/c4e48b0886ef460f532fb49f00047ec92c432ec0.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+452|gid9=1@0,977+452|gid10=0@20,1577+207]
-fonts/sha1sum/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/sha1sum/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/sha1sum/07f054357ff8638bac3711b422a1e31180bba863.ttf:--font-funcs=ot --no-glyph-names:U+0606,U+06E1:[2=0@40,502+0|1=0+1000]
diff --git a/test/shaping/tests/default-ignorables.tests b/test/shaping/tests/default-ignorables.tests
deleted file mode 100644
index 2d3ce97..0000000
--- a/test/shaping/tests/default-ignorables.tests
+++ /dev/null
@@ -1 +0,0 @@
-fonts/sha1sum/051d92f8bc6ff724511b296c27623f824de256e9.ttf::U+0075,U+0361,U+034F,U+0301,U+0069:[gid2=0+1266|gid7=0@-617,442+0|gid5=0@-7,0+0|gid1=4+528]
diff --git a/test/shaping/tests/fallback-positioning.tests b/test/shaping/tests/fallback-positioning.tests
deleted file mode 100644
index 499db7d..0000000
--- a/test/shaping/tests/fallback-positioning.tests
+++ /dev/null
@@ -1,2 +0,0 @@
-fonts/sha1sum/7ef276fc886ea502a03b9b0e5c8b547d5dc2b61c.ttf:--font-funcs=ft:U+0078,U+0301,U+0058,U+0301:[x=0+1030|acutecomb=0@-45,-32+0|X=2+1295|acutecomb=2@-171,310+0]
-fonts/sha1sum/7ef276fc886ea502a03b9b0e5c8b547d5dc2b61c.ttf:--font-funcs=ot:U+0078,U+0301,U+0058,U+0301:[gid2=0+1030|gid4=0@-21,-27+0|gid1=2+1295|gid4=2@-147,321+0]
diff --git a/test/shaping/tests/fuzzed.tests b/test/shaping/tests/fuzzed.tests
deleted file mode 100644
index d9bace3..0000000
--- a/test/shaping/tests/fuzzed.tests
+++ /dev/null
@@ -1,13 +0,0 @@
-fonts/sha1sum/1a6f1687b7a221f9f2c834b0b360d3c8463b6daf.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-fonts/sha1sum/5a5daf5eb5a4db77a2baa3ad9c7a6ed6e0655fa8.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-fonts/sha1sum/0509e80afb379d16560e9e47bdd7d888bebdebc6.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-fonts/sha1sum/641bd9db850193064d17575053ae2bf8ec149ddc.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-fonts/sha1sum/375d6ae32a3cbe52fbf81a4e5777e3377675d5a3.ttf:--font-funcs=ot:U+0041:[gid0=0+4352]
-fonts/sha1sum/8240789f6d12d4cfc4b5e8e6f246c3701bcf861f.ttf:--font-funcs=ot:U+0041:[gid0=0+1024]
-fonts/sha1sum/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/sha1sum/43979b90b2dd929723cf4fe1715990bcb9c9a56b.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-fonts/sha1sum/3511ff5c1647150595846ac414c595cccac34f18.ttf:--font-funcs=ot:U+0041:[gid0=0+1000|gid512=0+1000|gid15104=0+1000|gid11004=0+1000|gid3408=0+1000|gid18244=0+1000|gid17872=0+1000|gid17961=0+1000|gid0=0+1000|gid992=0+1000|gid15616=0+1000|gid0=0+1000|gid14151=0+1000|gid20559=0+1000|gid20992=0+1000|gid5440=0+1000|gid256=0+1000|gid0=0+1000|gid10=0+1000|gid8960=0+1000|gid256=0+1000|gid1024=0+1000|gid1490=0+1000|gid0=0+1000|gid768=0+1000|gid4096=0+1000|gid256=0+1000|gid2216=0+1000|gid0=0+1000|gid256=0+1000|gid256=0+1000|gid0=0+1000|gid768=0+1000|gid10752=0+1000|gid11004=0+1000|gid3408=0+1000|gid18244=0+1000|gid17734=0+1000|gid53248=0+1000|gid256=0+1000|gid0=0+1000|gid512=0+1000|gid14848=0+1000|gid10793=0+1000|gid57344=0+1000|gid768=0+1000|gid18227=0+1000|gid20285=0+1000|gid20480=0+1000|gid0=0+1000|gid256=0+1000|gid0=0+1000|gid810=0+1000|gid0=0+1000|gid11004=0+1000|gid3408=0+1000|gid18244=0+1000|gid17734=0+1000|gid53289=0+1000|gid57344=0+1000|gid768=0+1000|gid15667=0+1000|gid71=0+1000|gid0=0+1000|gid20559=0+1000|gid21248=0+1000|gid256=0+1000|gid0=0+1000|gid2816=0+1000|gid2776=0+1000|gid0=0+1000|gid51516=0+1000|gid0=0+1000|gid32=0+1000|gid26209=0+1000|gid28005=0+1000|gid65249=0+1000|gid29690=0+1000|gid0=0+1000|gid51548=0+1000|gid0=0+1000|gid2454=0+1000|gid28783=0+1000|gid29556=0+1000|gid1291=0+1000|gid3458=0+1000|gid80=0+1000|gid0=0+1000|gid2804=0+1000|gid210=0+1000|gid28786=0+1000|gid25968=0+1000|gid45763=0+1000|gid50546=0+1000|gid0=0+1000|gid59136=0+1000|gid0=0+1000|gid38144=0+1000|gid256=0+1000|gid0=0+1000|gid2560=0+1000|gid30208=0+1000|gid52224=0+1000|gid580=0+1000|gid17996=0+1000|gid21504=0+1000|gid6734=0+1000|gid108=0+1000|gid116=0+1000|gid24846=0+1000|gid1024=0+1000|gid0=0+1000|gid255=0+1000|gid65280=0+1000|gid256=0+1000|gid0=0+1000|gid8704=0+1000|gid1345=0+1000|gid23109=0+1000|gid8192=0+1000|gid10823=0+1000|gid21076=0+1000|gid8192=0+1000|gid12877=0+1000|gid20300=0+1000|gid8192=0+1000|gid6738=0+1000|gid20301=0+1000|gid8192=0+1000|gid16980=0+1000|gid21067=0+1000|gid8251=0+1000|gid18944=0+1000|gid255=0+1000|gid65280=0+1000|gid15360=0+1000|gid256=0+1000|gid255=0+1000|gid65280=0+1000|gid256=0+1000|gid768=0+1000|gid255=0+1000|gid65280=0+1000|gid256=0+1000|gid768=0+1000|gid255=0+1000|gid65280=0+1000|gid256=0+1000|gid1024=0+1000|gid12=0+1000|gid65280=0+1000|gid256=0+1000|gid1280=0+1000|gid255=0+1000|gid65280=0+1000|gid256=0+1000|gid1536=0+1000|gid1899=0+1000|gid25970=0+1000|gid110=0+1000|gid11264=0+1000|gid27502=0+1000|gid29285=0+1000|gid12907=0+1000|gid25974=0+1000|gid28160=0+1000|gid14443=0+1000|gid25970=0+1000|gid28288=0+1000|gid3=0+1000|gid118=0+1000|gid18259=0+1000|gid21826=0+1000|gid45716=0+1000|gid46369=0+1000|gid0=0+1000|gid0=0+1000|gid1=0+1000|gid16=0+1000|gid17=0+1000|gid256=0+1000|gid4=0+1000|gid16=0+1000|gid18244=0+1000|gid17734=0+1000|gid28=0+1000|gid12=0+1000|gid0=0+1000|gid284=0+1000|gid0=0+1000|gid28=0+1000|gid18256=0+1000|gid20307=0+1000|gid45114=0+1000|gid47616=0+1000|gid226=0+1000|gid10296=0+1000|gid0=0+1000|gid57927=0+1000|gid1=0+1000|gid0=0+1000|gid0=0+1000|gid21248=0+1000|gid5440=0+1000|gid256=0+1000|gid0=0+1000|gid10=0+1000|gid768=0+1000|gid256=0+1000|gid1024=0+1000|gid512=0+1000|gid0=0+1000|gid297=0+1000|gid16=0+1000|gid24833=0+1000|gid28774=0+1000|gid10794=0+1000|gid2304=0+1000|gid29=0+1000|gid32=0+1000|gid42=0+1000|gid64515=0+1000|gid42=0+1000|gid42=0+1000|gid64525=0+1000|gid20551=0+1000|gid17477=0+1000|gid18128=0+1000|gid10720=0+1000|gid3=0+1000|gid61=0+1000|gid3408=0+1000|gid18244=0+1000|gid17734=0+1000|gid53289=0+1000|gid57344=0+1000|gid768=0+1000|gid15616=0+1000|gid512=0+1000|gid55=0+1000|gid10576=0+1000|gid20307=0+1000|gid0=0+1000|gid255=0+1000|gid56063=0+1000|gid53504=0+1000|gid42=0+1000|gid42=0+1000|gid64525=0+1000|gid12288=0+1000|gid18176=0+1000|gid80=0+1000|gid20307=0+1000|gid1=0+1000|gid0=0+1000|gid62=0+1000]
-fonts/sha1sum/fab39d60d758cb586db5a504f218442cd1395725.ttf:--font-funcs=ot:U+0041,U+0041:[gid0=0+1000|gid0=1+1000]
-fonts/sha1sum/205edd09bd3d141cc9580f650109556cc28b22cb.ttf:--font-funcs=ot:U+0041:[gid0=0+1000]
-fonts/sha1sum/217a934cfe15c548b572c203dceb2befdf026462.ttf:--font-funcs=ot:U+0061,U+0061,U+0061:[]
-fonts/sha1sum/558661aa659912f4d30ecd27bd09835171a8e2b0.ttf:--font-funcs=ot:U+FFFD,U+E0100,U+FFFD,U+E0010:[]
diff --git a/test/shaping/tests/hangul-jamo.tests b/test/shaping/tests/hangul-jamo.tests
deleted file mode 100644
index fe9973f..0000000
--- a/test/shaping/tests/hangul-jamo.tests
+++ /dev/null
@@ -1,2 +0,0 @@
-fonts/sha1sum/757ebd573617a24aa9dfbf0b885c54875c6fe06b.ttf::U+115F,U+11A2:[gid3=0+920|gid4=0+0]
-fonts/sha1sum/7e14e7883ed152baa158b80e207b66114c823a8b.ttf::U+11A2:[gid1=0+920]
diff --git a/test/shaping/tests/hyphens.tests b/test/shaping/tests/hyphens.tests
deleted file mode 100644
index d2cb186..0000000
--- a/test/shaping/tests/hyphens.tests
+++ /dev/null
@@ -1,2 +0,0 @@
-fonts/sha1sum/1c04a16f32a39c26c851b7fc014d2e8d298ba2b8.ttf::U+2010:[gid1=0+739]
-fonts/sha1sum/1c04a16f32a39c26c851b7fc014d2e8d298ba2b8.ttf::U+2011:[gid1=0+739]
diff --git a/test/shaping/tests/indic-joiner-candrabindu.tests b/test/shaping/tests/indic-joiner-candrabindu.tests
deleted file mode 100644
index 80ad8ce..0000000
--- a/test/shaping/tests/indic-joiner-candrabindu.tests
+++ /dev/null
@@ -1,2 +0,0 @@
-fonts/sha1sum/5028afb650b1bb718ed2131e872fbcce57828fff.ttf::U+0B13,U+200D,U+0B01:[omorya=0+1450]
-fonts/sha1sum/5028afb650b1bb718ed2131e872fbcce57828fff.ttf::U+0B13,U+200C,U+0B01:[oorya=0+1309|space=0+0|candrabinduorya=0+0]
diff --git a/test/shaping/tests/indic-old-spec.tests b/test/shaping/tests/indic-old-spec.tests
deleted file mode 100644
index 5410a6a..0000000
--- a/test/shaping/tests/indic-old-spec.tests
+++ /dev/null
@@ -1,2 +0,0 @@
-fonts/sha1sum/57a9d9f83020155cbb1d2be1f43d82388cbecc88.ttf::U+0C9A,U+0CCD,U+0C9A,U+0CCD:[U0C9A_U0CCD.haln=0+1066|U0C9A_0CCD.blwf=0+0]
-fonts/sha1sum/270b89df543a7e48e206a2d830c0e10e5265c630.ttf::U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D:[glyph201=0+1183|U0D4D=0+0]
diff --git a/test/shaping/tests/indic-pref-blocking.tests b/test/shaping/tests/indic-pref-blocking.tests
deleted file mode 100644
index 204b92a..0000000
--- a/test/shaping/tests/indic-pref-blocking.tests
+++ /dev/null
@@ -1,2 +0,0 @@
-fonts/sha1sum/226bc2deab3846f1a682085f70c67d0421014144.ttf::U+0D2F,U+0D4D,U+0D30,U+0D46:[evowelsignmlym=0+1465|rapostmlym=0+499|yamlym=0+2120]
-fonts/sha1sum/e207635780b42f898d58654b65098763e340f5c7.ttf::U+0D2F,U+0D4D,U+0D30,U+0D46:[yamlym=0+2120|viramamlym=0+0|evowelsignmlym=0+1465|ramlym=0+1507]
diff --git a/test/shaping/tests/indic-script-extensions.tests b/test/shaping/tests/indic-script-extensions.tests
deleted file mode 100644
index 52b6aa0..0000000
--- a/test/shaping/tests/indic-script-extensions.tests
+++ /dev/null
@@ -1 +0,0 @@
-fonts/sha1sum/3493e92eaded2661cadde752a39f9d58b11f0326.ttf::U+0BA4,U+0BC6,U+D804,U+DF3C,U+0BAA,U+D804,U+DF3C,U+0BC6,U+D804,U+DF3C:[u0BC6=0+2093|u1133C=0+0|u0BA4=0+1863|u0BC6=3+2093|u1133C=3+0|u0BAA=3+1706|u1133C=3+0]
diff --git a/test/shaping/tests/language-tags.tests b/test/shaping/tests/language-tags.tests
deleted file mode 100644
index 17adafa..0000000
--- a/test/shaping/tests/language-tags.tests
+++ /dev/null
@@ -1,12 +0,0 @@
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=fa:U+004A:[gid2=0+1000]
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=ja:U+004A:[gid2=0+1000]
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh:U+004A:[gid4=0+1000]
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-cn:U+004A:[gid4=0+1000]
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-sg:U+004A:[gid4=0+1000]
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-tw:U+004A:[gid5=0+1000]
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-hans:U+004A:[gid4=0+1000]
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-hant:U+004A:[gid5=0+1000]
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-hant-hk:U+004A:[gid6=0+1000]
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-HK:U+004A:[gid6=0+1000]
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-mo:U+004A:[gid6=0+1000]
-fonts/sha1sum/6991b13ce889466be6de3f66e891de2bc0f117ee.ttf:--language=zh-Hant-mo:U+004A:[gid6=0+1000]
diff --git a/test/shaping/tests/ligature-id.tests b/test/shaping/tests/ligature-id.tests
deleted file mode 100644
index a1ce2bb..0000000
--- a/test/shaping/tests/ligature-id.tests
+++ /dev/null
@@ -1,35 +0,0 @@
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|space=3+213|u0995_u09B0_u09CD.blwf.vatu=4+643|u0995_u09CD.half_u09B2.pres=7+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|space=6+213|u0995_u09B0_u09CD.blwf.vatu=7+643|u0995_u09CD.half_u09B2.pres=10+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|space=9+213|u0995_u09B0_u09CD.blwf.vatu=10+643|u0995_u09CD.half_u09B2.pres=13+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|space=12+213|u0995_u09B0_u09CD.blwf.vatu=13+643|u0995_u09CD.half_u09B2.pres=16+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|space=15+213|u0995_u09B0_u09CD.blwf.vatu=16+643|u0995_u09CD.half_u09B2.pres=19+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|space=18+213|u0995_u09B0_u09CD.blwf.vatu=19+643|u0995_u09CD.half_u09B2.pres=22+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|space=21+213|u0995_u09B0_u09CD.blwf.vatu=22+643|u0995_u09CD.half_u09B2.pres=25+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|space=24+213|u0995_u09B0_u09CD.blwf.vatu=25+643|u0995_u09CD.half_u09B2.pres=28+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|space=27+213|u0995_u09B0_u09CD.blwf.vatu=28+643|u0995_u09CD.half_u09B2.pres=31+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|space=30+213|u0995_u09B0_u09CD.blwf.vatu=31+643|u0995_u09CD.half_u09B2.pres=34+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|space=33+213|u0995_u09B0_u09CD.blwf.vatu=34+643|u0995_u09CD.half_u09B2.pres=37+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|space=36+213|u0995_u09B0_u09CD.blwf.vatu=37+643|u0995_u09CD.half_u09B2.pres=40+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|space=39+213|u0995_u09B0_u09CD.blwf.vatu=40+643|u0995_u09CD.half_u09B2.pres=43+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|space=42+213|u0995_u09B0_u09CD.blwf.vatu=43+643|u0995_u09CD.half_u09B2.pres=46+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|space=45+213|u0995_u09B0_u09CD.blwf.vatu=46+643|u0995_u09CD.half_u09B2.pres=49+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|space=48+213|u0995_u09B0_u09CD.blwf.vatu=49+643|u0995_u09CD.half_u09B2.pres=52+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|space=51+213|u0995_u09B0_u09CD.blwf.vatu=52+643|u0995_u09CD.half_u09B2.pres=55+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|space=54+213|u0995_u09B0_u09CD.blwf.vatu=55+643|u0995_u09CD.half_u09B2.pres=58+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|space=57+213|u0995_u09B0_u09CD.blwf.vatu=58+643|u0995_u09CD.half_u09B2.pres=61+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|space=60+213|u0995_u09B0_u09CD.blwf.vatu=61+643|u0995_u09CD.half_u09B2.pres=64+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|space=63+213|u0995_u09B0_u09CD.blwf.vatu=64+643|u0995_u09CD.half_u09B2.pres=67+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|space=66+213|u0995_u09B0_u09CD.blwf.vatu=67+643|u0995_u09CD.half_u09B2.pres=70+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|space=69+213|u0995_u09B0_u09CD.blwf.vatu=70+643|u0995_u09CD.half_u09B2.pres=73+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|space=72+213|u0995_u09B0_u09CD.blwf.vatu=73+643|u0995_u09CD.half_u09B2.pres=76+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|space=75+213|u0995_u09B0_u09CD.blwf.vatu=76+643|u0995_u09CD.half_u09B2.pres=79+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|space=78+213|u0995_u09B0_u09CD.blwf.vatu=79+643|u0995_u09CD.half_u09B2.pres=82+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|space=81+213|u0995_u09B0_u09CD.blwf.vatu=82+643|u0995_u09CD.half_u09B2.pres=85+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|space=84+213|u0995_u09B0_u09CD.blwf.vatu=85+643|u0995_u09CD.half_u09B2.pres=88+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|space=87+213|u0995_u09B0_u09CD.blwf.vatu=88+643|u0995_u09CD.half_u09B2.pres=91+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|space=90+213|u0995_u09B0_u09CD.blwf.vatu=91+643|u0995_u09CD.half_u09B2.pres=94+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|u0995_u09CD.half_u0995.pres=90+566|space=93+213|u0995_u09B0_u09CD.blwf.vatu=94+643|u0995_u09CD.half_u09B2.pres=97+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|u0995_u09CD.half_u0995.pres=90+566|u0995_u09CD.half_u0995.pres=93+566|space=96+213|u0995_u09B0_u09CD.blwf.vatu=97+643|u0995_u09CD.half_u09B2.pres=100+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|u0995_u09CD.half_u0995.pres=90+566|u0995_u09CD.half_u0995.pres=93+566|u0995_u09CD.half_u0995.pres=96+566|space=99+213|u0995_u09B0_u09CD.blwf.vatu=100+643|u0995_u09CD.half_u09B2.pres=103+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|u0995_u09CD.half_u0995.pres=90+566|u0995_u09CD.half_u0995.pres=93+566|u0995_u09CD.half_u0995.pres=96+566|u0995_u09CD.half_u0995.pres=99+566|space=102+213|u0995_u09B0_u09CD.blwf.vatu=103+643|u0995_u09CD.half_u09B2.pres=106+602]
-fonts/sha1sum/1c2fb74c1b2aa173262734c1f616148f1648cfd6.ttf::U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0995,U+09CD,U+0995,U+0020,U+0995,U+09CD,U+09B0,U+0995,U+09CD,U+09B2:[u0995_u09CD.half_u0995.pres=0+566|u0995_u09CD.half_u0995.pres=3+566|u0995_u09CD.half_u0995.pres=6+566|u0995_u09CD.half_u0995.pres=9+566|u0995_u09CD.half_u0995.pres=12+566|u0995_u09CD.half_u0995.pres=15+566|u0995_u09CD.half_u0995.pres=18+566|u0995_u09CD.half_u0995.pres=21+566|u0995_u09CD.half_u0995.pres=24+566|u0995_u09CD.half_u0995.pres=27+566|u0995_u09CD.half_u0995.pres=30+566|u0995_u09CD.half_u0995.pres=33+566|u0995_u09CD.half_u0995.pres=36+566|u0995_u09CD.half_u0995.pres=39+566|u0995_u09CD.half_u0995.pres=42+566|u0995_u09CD.half_u0995.pres=45+566|u0995_u09CD.half_u0995.pres=48+566|u0995_u09CD.half_u0995.pres=51+566|u0995_u09CD.half_u0995.pres=54+566|u0995_u09CD.half_u0995.pres=57+566|u0995_u09CD.half_u0995.pres=60+566|u0995_u09CD.half_u0995.pres=63+566|u0995_u09CD.half_u0995.pres=66+566|u0995_u09CD.half_u0995.pres=69+566|u0995_u09CD.half_u0995.pres=72+566|u0995_u09CD.half_u0995.pres=75+566|u0995_u09CD.half_u0995.pres=78+566|u0995_u09CD.half_u0995.pres=81+566|u0995_u09CD.half_u0995.pres=84+566|u0995_u09CD.half_u0995.pres=87+566|u0995_u09CD.half_u0995.pres=90+566|u0995_u09CD.half_u0995.pres=93+566|u0995_u09CD.half_u0995.pres=96+566|u0995_u09CD.half_u0995.pres=99+566|u0995_u09CD.half_u0995.pres=102+566|space=105+213|u0995_u09B0_u09CD.blwf.vatu=106+643|u0995_u09CD.half_u09B2.pres=109+602]
diff --git a/test/shaping/tests/mark-filtering-sets.tests b/test/shaping/tests/mark-filtering-sets.tests
deleted file mode 100644
index 7b5c910..0000000
--- a/test/shaping/tests/mark-filtering-sets.tests
+++ /dev/null
@@ -1,5 +0,0 @@
-fonts/sha1sum/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf::U+062A,U+062E,U+062A,U+0629:[glyph837=3@299,1170+0|uni06C1.1=3+502|glyph837=2@149,690+0|uni0628.8=2+532|glyph836=1@-51,1259+0|glyph514=1+196|glyph837=0@655,1751+0|glyph112=0@0,-358+905]
-fonts/sha1sum/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf::U+062A,U+062E,U+0646,U+0629:[glyph837=3@299,1170+0|uni06C1.1=3+502|glyph836=2@149,690+0|uni0628.8=2+532|glyph836=1@-51,1259+0|glyph514=1+196|glyph837=0@655,1751+0|glyph112=0@0,-358+905]
-fonts/sha1sum/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf::U+062A,U+062E,U+0626,U+0629:[glyph837=3@299,1170+0|uni06C1.1=3+502|glyph847=2@149,690+0|uni0628.8=2+532|glyph836=1@-51,1259+0|glyph514=1+196|glyph837=0@655,1751+0|glyph112=0@0,-358+905]
-fonts/sha1sum/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf::U+062A,U+062E,U+062B,U+0629:[glyph837=3@299,1520+0|uni06C1.1=3+502|glyph838=2@149,690+0|uni0628.8=2+532|glyph836=1@-51,1259+0|glyph514=1+196|glyph837=0@655,1751+0|glyph112=0@0,-358+905]
-fonts/sha1sum/f22416c692720a7d46fadf4af99f4c9e094f00b9.ttf::U+062A,U+062E,U+0679,U+0629:[glyph837=3@299,1520+0|uni06C1.1=3+502|glyph842=2@149,690+0|uni0628.8=2+532|glyph836=1@-51,1259+0|glyph514=1+196|glyph837=0@655,1751+0|glyph112=0@0,-358+905]
diff --git a/test/shaping/tests/mongolian-variation-selector.tests b/test/shaping/tests/mongolian-variation-selector.tests
deleted file mode 100644
index 0a2e580..0000000
--- a/test/shaping/tests/mongolian-variation-selector.tests
+++ /dev/null
@@ -1,4 +0,0 @@
-fonts/sha1sum/37033cc5cf37bb223d7355153016b6ccece93b28.ttf::U+1826,U+180B,U+1826:[uni1826.E85E_ue.init1=0+599|uni1826.E856_ue.fina=2+750]
-fonts/sha1sum/ef86fe710cfea877bbe0dbb6946a1f88d0661031.ttf::U+1820,U+180B:[uni1820.E821_a.isol1=0+1199]
-fonts/sha1sum/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/sha1sum/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]
diff --git a/test/shaping/tests/simple.tests b/test/shaping/tests/simple.tests
deleted file mode 100644
index bebe008..0000000
--- a/test/shaping/tests/simple.tests
+++ /dev/null
@@ -1,2 +0,0 @@
-fonts/sha1sum/49c9f7485c1392fa09a1b801bc2ffea79275f22e.ttf:--shaper=ot:U+0056,U+0041,U+0042,U+0045,U+0061,U+0062,U+0063,U+0064:[V=0+1142|A=1+1295|B=2+1295|E=3+1123|a=4+1126|b=5+1164|c=6+1072|d=7+1164]
-fonts/sha1sum/49c9f7485c1392fa09a1b801bc2ffea79275f22e.ttf:--shaper=fallback:U+0056,U+0041,U+0042,U+0045,U+0061,U+0062,U+0063,U+0064:[V=0+1295|A=1+1295|B=2+1295|E=3+1123|a=4+1126|b=5+1164|c=6+1072|d=7+1164]
diff --git a/test/shaping/tests/spaces.tests b/test/shaping/tests/spaces.tests
deleted file mode 100644
index cb386de..0000000
--- a/test/shaping/tests/spaces.tests
+++ /dev/null
@@ -1,17 +0,0 @@
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+0020:[gid1=0+560]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+00A0:[gid1=0+560]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+1680:[gid0=0+692]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2000:[gid1=0+1024]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2001:[gid1=0+2048]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2002:[gid1=0+1024]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2003:[gid1=0+2048]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2004:[gid1=0+683]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2005:[gid1=0+512]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2006:[gid1=0+341]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2007:[gid1=0+560]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2008:[gid1=0+560]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+2009:[gid1=0+410]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+200A:[gid1=0+128]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+202F:[gid1=0+280]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+205F:[gid1=0+455]
-fonts/sha1sum/1c2c3fc37b2d4c3cb2ef726c6cdaaabd4b7f3eb9.ttf:--font-funcs=ot:U+3000:[gid1=0+2048]
diff --git a/test/shaping/tests/use-marchen.tests b/test/shaping/tests/use-marchen.tests
deleted file mode 100644
index 6497178..0000000
--- a/test/shaping/tests/use-marchen.tests
+++ /dev/null
@@ -1,35 +0,0 @@
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8F:[u11C8F=0+3000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C71:[u11C71=0+1600]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11CB5:[u11C8A=0+2000|u11CB5=0@-2000,0+0]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C84,U+11C71:[u11C84=0+2200|u11C71=1+1600]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C7E,U+11C8A:[u11C7E=0+2600|u11C8A=1+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11C92,U+11CA9:[u11C8A.11C92.11CA9=0+2600]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11C94,U+11CA9:[u11C8A.11C94.11CA9=0+2600]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C92,U+11CA9:[u11C8D.11C92.11CA9=0+2600]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C94,U+11CA9:[u11C8D.11C94.11CA9=0+2600]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C9E,U+11CA9:[u11C8D.11C9E.11CA9=0+3200]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA0,U+11CA9:[u11C8D.11CA0.11CA9=0+3000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C92,U+11CAA:[u11C8D.11C92.11CAA=0+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C94,U+11CAA:[u11C8D.11C94.11CAA=0+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C9D,U+11CAA:[u11C8D.11C9D.11CAA=0+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11C9E,U+11CAA:[u11C8D.11C9E.11CAA=0+2600]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA0,U+11CAA:[u11C8D.11CA0.11CAA=0+2400]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C72,U+11CAA:[u11C80=0+2400|u11C72.11CAA=1+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8C,U+11CB1,U+11C8D:[u11C8C.11CB1=0+2793|u11C8D=2+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C7C,U+11CB3:[u11C80=0+2400|u11C7C.11CB3=1+2200]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C7F,U+11CB2,U+11C7D:[u11C7F.11CB2=0+2400|u11C7D=2+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CB2,U+11C81:[u11C8D.11CB2=0+2000|u11C81=2+2400]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8C,U+11CB4,U+11C74:[u11C8C.11CB4=0+2800|u11C74=2+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8A,U+11CA1,U+11CA9,U+11C71:[u11C8A.11CA1.11CA9=0+3000|u11C71=3+1600]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA1,U+11CA9,U+11C71:[u11C8D.11CA1.11CA9=0+3000|u11C71=3+1600]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8D,U+11CA1,U+11CAA,U+11C71:[u11C8D.11CA1.11CAA=0+2400|u11C71=3+1600]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8F,U+11CB0,U+11CB4,U+11CB6:[u11C8F.11CB0.11CB4=0+3600|u11CB6=0@-3200,0+0]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8E,U+11CB0,U+11CB2,U+11CB5:[u11C8E.11CB0.11CB2=0+2000|u11CB5=0@-2000,0+0]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C74,U+11C89,U+11CB2,U+11C75:[u11C74=0+2000|u11C89.11CB2=1+2000|u11C75=3+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C7C,U+11CAA,U+11CB2,U+11C75:[u11C7C.11CAA.11CB2=0+2200|u11C75=3+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C81,U+11C74,U+11CB2,U+11C8B:[u11C81=0+2400|u11C74.11CB2=1+2000|u11C8B=3+2400]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8B,U+11CB3,U+11C74,U+11C8D:[u11C8B.11CB3=0+2400|u11C74=2+2000|u11C8D=3+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C83,U+11CB4,U+11C74,U+11C8D:[u11C83.11CB4=0+2800|u11C74=2+2000|u11C8D=3+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C8B,U+11CB3,U+11C74,U+11C8D,U+11C71:[u11C8B.11CB3=0+2400|u11C74=2+2000|u11C8D=3+2000|u11C71=4+1600]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C76,U+11CB1,U+11C75,U+11C8D:[u11C80=0+2400|u11C76.11CB1=1+3200|u11C75=3+2000|u11C8D=4+2000]
-fonts/sha1sum/85414f2552b654585b7a8d13dcc3e8fd9f7970a3.ttf::U+11C80,U+11C8D,U+11C94,U+11CAA,U+11CB1,U+11C74,U+11C8D:[u11C80=0+2400|u11C8D.11C94.11CAA.11CB1.shorti=1+2600|u11C74=5+2000|u11C8D=6+2000]
diff --git a/test/shaping/tests/use.tests b/test/shaping/tests/use.tests
deleted file mode 100644
index bfce117..0000000
--- a/test/shaping/tests/use.tests
+++ /dev/null
@@ -1,4 +0,0 @@
-fonts/sha1sum/fbb6c84c9e1fe0c39e152fbe845e51fd81f6748e.ttf::U+1B1B,U+1B44,U+1B13,U+1B3E:[gid3=0+990|gid7=0+2473|gid5=0@-293,-400+0]
-fonts/sha1sum/4cce528e99f600ed9c25a2b69e32eb94a03b4ae8.ttf::U+1A48,U+1A58,U+1A25,U+1A48,U+1A58,U+1A25,U+1A6E,U+1A63:[uni1A48=0+1212|uni1A25=0+1912|uni1A58=0+0|uni1A48=3+1212|uni1A6E=3+0|uni1A25=3+1912|uni1A58=3+0|uni1A63=3+1212]
-fonts/sha1sum/f518eb6f6b5eec2946c9fbbbde44e45d46f5e2ac.ttf::U+1A48,U+1A58,U+1A25,U+1A48,U+1A58,U+1A25,U+1A6E,U+1A63:[uni1A48=0+1212|uni1A25=0+1912|uni1A58=0+0|uni1A48=3+1212|uni1A6E=3+1211|uni1A25=3+1912|uni1A58=3+0|uni1A63=3+1212]
-fonts/sha1sum/6ff0fbead4462d9f229167b4e6839eceb8465058.ttf:--font-funcs=ot:U+11103,U+11128:[gid1=0+837|gid2=0+0]
diff --git a/test/shaping/tests/vertical.tests b/test/shaping/tests/vertical.tests
deleted file mode 100644
index 3586080..0000000
--- a/test/shaping/tests/vertical.tests
+++ /dev/null
@@ -1,3 +0,0 @@
-fonts/sha1sum/191826b9643e3f124d865d617ae609db6a2ce203.ttf:--direction=t:U+300C:[uni300C.vert=0@-512,-578+0,-1024]
-fonts/sha1sum/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf:--direction=t --font-funcs=ft:U+0041,U+0042:[gid1=0@-654,-2128+0,-2789|gid2=1@-665,-2125+0,-2789]
-fonts/sha1sum/f9b1dd4dcb515e757789a22cb4241107746fd3d0.ttf:--direction=t --font-funcs=ot:U+0041,U+0042:[gid1=0@-654,-2189+0,-2789|gid2=1@-665,-2189+0,-2789]
diff --git a/test/shaping/tests/zero-width-marks.tests b/test/shaping/tests/zero-width-marks.tests
deleted file mode 100644
index c08f26b..0000000
--- a/test/shaping/tests/zero-width-marks.tests
+++ /dev/null
@@ -1,11 +0,0 @@
-fonts/sha1sum/bb9473d2403488714043bcfb946c9f78b86ad627.ttf::U+1030:[circledash=0+636|u1030.med=0@-162,0+0]
-fonts/sha1sum/8454d22037f892e76614e1645d066689a0200e61.ttf::U+05E0,U+05B8,U+0591,U+05DA,U+05B0:[uni05DA05B0=3+991|uni2009=0+200|uni0591=0@75,0+0|uni05B8=0@495,0+0|uni05E0=0+683]
-fonts/sha1sum/45855bc8d46332b39c4ab9e2ee1a26b1f896da6b.ttf::U+0E01,U+0E34,U+0E01:[gid1=0+1264|gid2=0@20,0+0|gid1=2+1264]
-fonts/sha1sum/7a37dc4d5bf018456aea291cee06daf004c0221c.ttf::U+0E01,U+0E34,U+0E01:[gid1=0+1264|gid2=0@20,0+1000|gid1=2+1264]
-fonts/sha1sum/8099955657a54e9ee38a6ba1d6f950ce58e3cc25.ttf::U+0E01,U+0E34,U+0E01:[gid1=0+1264|gid2=0+0|gid1=2+1264]
-fonts/sha1sum/bb0c53752e85c3d28973ebc913287b8987d3dfe8.ttf::U+0E01,U+0E34,U+0E01:[gid1=0+1264|gid2=0+0|gid1=2+1264]
-fonts/sha1sum/ffa0f5d2d9025486d8469d8b1fdd983e7632499b.ttf::U+0058,U+0303,U+0078,U+0303,U+006A,U+006A,U+006A,U+0303,U+006A,U+0303,U+006A,U+006A:[gid1=0+1200|gid6=0@-1029,340+0|gid3=2+1083|gid6=2@-992,0+0|gid2=4+528|gid2=5+528|gid5=6+528|gid6=6@-693,0+0|gid5=8+528|gid6=8@-693,0+0|gid2=10+528|gid2=11+528]
-fonts/sha1sum/cc5f3d2d717fb6bd4dfae1c16d48a2cb8e12233b.ttf::U+0058,U+0303,U+0078,U+0303,U+006A,U+006A,U+006A,U+0303,U+006A,U+0303,U+006A,U+006A:[gid1=0+1200|gid6=0@-1029,340+1200|gid3=2+1083|gid6=2@-992,0+1200|gid2=4+528|gid2=5+528|gid5=6+528|gid6=6@-693,0+1200|gid5=8+528|gid6=8@-693,0+1200|gid2=10+528|gid2=11+528]
-fonts/sha1sum/fcdcffbdf1c4c97c05308d7600e4c283eb47dbca.ttf::U+0058,U+0303,U+0078,U+0303,U+006A,U+006A,U+006A,U+0303,U+006A,U+0303,U+006A,U+006A:[gid1=0+1200|gid6=0+0|gid3=2+1083|gid6=2+0|gid2=4+528|gid2=5+528|gid5=6+528|gid6=6+0|gid5=8+528|gid6=8+0|gid2=10+528|gid2=11+528]
-fonts/sha1sum/56cfd0e18d07f41c38e9598545a6d369127fc6f9.ttf::U+0058,U+0303,U+0078,U+0303,U+006A,U+006A,U+006A,U+0303,U+006A,U+0303,U+006A,U+006A:[gid1=0+1200|gid6=0@-1029,340+0|gid3=2+1083|gid6=2@-992,0+0|gid2=4+528|gid2=5+528|gid5=6+528|gid6=6@-693,0+0|gid5=8+528|gid6=8@-693,0+0|gid2=10+528|gid2=11+528]
-fonts/sha1sum/a98e908e2ed21b22228ea59ebcc0f05034c86f2e.ttf::U+0041,U+0042,U+0041:[A=0+1368|B=1+0|A=2+1368]
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-persian/mehran.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-persian/mehran.txt
similarity index 99%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-persian/mehran.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/language-persian/mehran.txt
index 4be0786..b5b670f 100755
--- a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-persian/mehran.txt
+++ b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-persian/mehran.txt
@@ -1,8 +1,8 @@
-‫دَر فارسی گَچْ‌پَژْ هست. این «ی» فارسی است.‬

-‫حرف «ع» را به چٰهار شکلِ «ع‍» و «‍ع‍» و «‍ع» و «‌ع‌» می‌توان نشان داد.‬

-‫تشخیصِ اِعْ‌ًٌَُراب ناهمخوان از وظایف حروفْ‌چین است.‬

-‫دو اِعراب همخوان مانند « َ» و « ّ» به شکل « َّ» باهم ترکیب می‌شوند.‬

-‫لازم است حروف‌چین رفتار درستی با کشیدهٔ یونی‌کدی داشته باشد.‬

-‫مثلاً بتواند کلـمهٔ «پیِٓـــــــچ» یا حروف «ــٖٓـ» را به درستی نمایش دهد.‬

-‫حرف «لام» و «الف» باید به شکل لیگاتوری نمایش داده شوند.‬

+‫دَر فارسی گَچْ‌پَژْ هست. این «ی» فارسی است.‬
+‫حرف «ع» را به چٰهار شکلِ «ع‍» و «‍ع‍» و «‍ع» و «‌ع‌» می‌توان نشان داد.‬
+‫تشخیصِ اِعْ‌ًٌَُراب ناهمخوان از وظایف حروفْ‌چین است.‬
+‫دو اِعراب همخوان مانند « َ» و « ّ» به شکل « َّ» باهم ترکیب می‌شوند.‬
+‫لازم است حروف‌چین رفتار درستی با کشیدهٔ یونی‌کدی داشته باشد.‬
+‫مثلاً بتواند کلـمهٔ «پیِٓـــــــچ» یا حروف «ــٖٓـ» را به درستی نمایش دهد.‬
+‫حرف «لام» و «الف» باید به شکل لیگاتوری نمایش داده شوند.‬
 ‫کلمهٔ «بَلَاٰ» از آزمون‌های سطح پائین حروف‌چین است.‬
\ No newline at end of file
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/2grams.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/2grams.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/2grams.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/2grams.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/3grams.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/3grams.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/3grams.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/3grams.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/4grams.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/4grams.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/4grams.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/4grams.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/5grams.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/5grams.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/5grams.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/5grams.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/6grams.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/6grams.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/6grams.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/6grams.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/7grams.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/7grams.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/7grams.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/7grams.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/8grams.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/8grams.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/8grams.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/8grams.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/LICENSE b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/LICENSE
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/README b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/README
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/README
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/SOURCES b/test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/SOURCES
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/language-urdu/crulp/ligatures/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/lam-alef.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/lam-alef.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/lam-alef.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/lam-alef.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/language-arabic.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/language-arabic.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/language-arabic.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/language-arabic.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/language-persian.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/language-persian.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/language-persian.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/language-persian.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/language-urdu.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/language-urdu.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/language-urdu.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/language-urdu.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/ligature-components.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/ligature-components.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/ligature-components.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/ligature-components.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/ligature-diacritics.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/ligature-diacritics.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/ligature-diacritics.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/ligature-diacritics.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/mark-skipping.txt b/test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/mark-skipping.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-arabic/misc/diacritics/mark-skipping.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-arabic/misc/diacritics/mark-skipping.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/misc.txt b/test/shaping/texts/in-house/shaper-arabic/script-mongolian/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-mongolian/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/non-joining.txt b/test/shaping/texts/in-house/shaper-arabic/script-mongolian/misc/non-joining.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/non-joining.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-mongolian/misc/non-joining.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/poem.txt b/test/shaping/texts/in-house/shaper-arabic/script-mongolian/misc/poem.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/poem.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-mongolian/misc/poem.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/variation-selectors.txt b/test/shaping/texts/in-house/shaper-arabic/script-mongolian/misc/variation-selectors.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-mongolian/misc/variation-selectors.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-mongolian/misc/variation-selectors.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-nko/misc/misc.txt b/test/shaping/texts/in-house/shaper-arabic/script-nko/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-nko/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-nko/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-phags-pa/misc/misc.txt b/test/shaping/texts/in-house/shaper-arabic/script-phags-pa/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-phags-pa/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-phags-pa/misc/misc.txt
diff --git a/test/shaping/texts/in-house/shaper-arabic/script-syriac/misc/abbreviation-mark.txt b/test/shaping/texts/in-house/shaper-arabic/script-syriac/misc/abbreviation-mark.txt
new file mode 100644
index 0000000..52bdbea
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-arabic/script-syriac/misc/abbreviation-mark.txt
@@ -0,0 +1,11 @@
+ܐܒ
+ܐ܏
+ܐ܏ܒ
+ܐ܏ܒܓ
+ܐ܏ܒܓܕ
+ܐ܏ܒܓܕܐ
+ܐ܏ܒܓܕܐܐܐܐܐܐܐܐܐ
+ܐ܏ܒܓܕܐ܏ܐܐܐ܏ܐ܏ܐܐܐܐ
+ܐ܏ܒܓܕܓܓܓܓܓܓ
+ܐ܏ܒܓ
+܏ܫܘabcܒ.
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/alaph.txt b/test/shaping/texts/in-house/shaper-arabic/script-syriac/misc/alaph.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/alaph.txt
rename to test/shaping/texts/in-house/shaper-arabic/script-syriac/misc/alaph.txt
diff --git a/test/shaping/texts/in-tree/shaper-default/script-ethiopic/misc/misc.txt b/test/shaping/texts/in-house/shaper-default/script-ethiopic/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-default/script-ethiopic/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-default/script-ethiopic/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-default/script-han/misc/cjk-compat.txt b/test/shaping/texts/in-house/shaper-default/script-han/misc/cjk-compat.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-default/script-han/misc/cjk-compat.txt
rename to test/shaping/texts/in-house/shaper-default/script-han/misc/cjk-compat.txt
diff --git a/test/shaping/texts/in-tree/shaper-default/script-hiragana/misc/kazuraki-liga-lines.txt b/test/shaping/texts/in-house/shaper-default/script-hiragana/misc/kazuraki-liga-lines.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-default/script-hiragana/misc/kazuraki-liga-lines.txt
rename to test/shaping/texts/in-house/shaper-default/script-hiragana/misc/kazuraki-liga-lines.txt
diff --git a/test/shaping/texts/in-tree/shaper-default/script-hiragana/misc/kazuraki-liga.txt b/test/shaping/texts/in-house/shaper-default/script-hiragana/misc/kazuraki-liga.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-default/script-hiragana/misc/kazuraki-liga.txt
rename to test/shaping/texts/in-house/shaper-default/script-hiragana/misc/kazuraki-liga.txt
diff --git a/test/shaping/texts/in-tree/shaper-default/script-linear-b/misc/misc.txt b/test/shaping/texts/in-house/shaper-default/script-linear-b/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-default/script-linear-b/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-default/script-linear-b/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-default/script-tifinagh/misc/misc.txt b/test/shaping/texts/in-house/shaper-default/script-tifinagh/misc/misc.txt
similarity index 98%
rename from test/shaping/texts/in-tree/shaper-default/script-tifinagh/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-default/script-tifinagh/misc/misc.txt
index 0c307eb..9fc069a 100644
--- a/test/shaping/texts/in-tree/shaper-default/script-tifinagh/misc/misc.txt
+++ b/test/shaping/texts/in-house/shaper-default/script-tifinagh/misc/misc.txt
@@ -8,4 +8,3 @@
 ⵔ⵿ⵜ
 ⵙⵜ
 ⵙ⵿ⵜ
-
diff --git a/test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/misc.txt b/test/shaping/texts/in-house/shaper-hangul/script-hangul/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-hangul/script-hangul/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-hebrew/script-hebrew/misc/diacritics.txt b/test/shaping/texts/in-house/shaper-hebrew/script-hebrew/misc/diacritics.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-hebrew/script-hebrew/misc/diacritics.txt
rename to test/shaping/texts/in-house/shaper-hebrew/script-hebrew/misc/diacritics.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/README b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/README
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-assamese/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-assamese/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/misc/reph.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/misc/reph.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/misc/reph.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/misc/reph.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/README b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/README
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-bengali/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/dottedcircle.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/dottedcircle.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/dottedcircle.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/dottedcircle.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/eyelash.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/eyelash.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/eyelash.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/eyelash.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/joiners.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/joiners.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/joiners.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/joiners.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/spec-deviations.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/spec-deviations.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/spec-deviations.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/spec-deviations.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/README b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/README
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DevnagariSpecificAddition.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DevnagariSpecificAddition.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DevnagariSpecificAddition.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-DevnagariSpecificAddition.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-GenericPunctuation.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-GenericPunctuation.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-GenericPunctuation.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-GenericPunctuation.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-devanagari/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/README b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/README
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gujarati/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gujarati/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/README b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/README
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-GurmukhiSpecific.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-GurmukhiSpecific.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-GurmukhiSpecific.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-GurmukhiSpecific.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-gurmukhi/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-gurmukhi/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/misc/right-matras.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/misc/right-matras.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/misc/right-matras.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/misc/right-matras.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/README b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/README
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-kannada/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/cibu.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/cibu.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/cibu.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/cibu.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/dot-reph.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/dot-reph.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/dot-reph.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/dot-reph.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/README b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/README
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-malayalam/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/misc/bindu.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/misc/bindu.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/misc/bindu.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/misc/bindu.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/README b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/README
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalConsonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-OriyaSpecific.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-OriyaSpecific.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-OriyaSpecific.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-OriyaSpecific.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-oriya/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-oriya/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/extensive.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/extensive.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/extensive.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/extensive.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/reph.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/reph.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/reph.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/reph.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/split-matras.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/split-matras.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/split-matras.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/misc/split-matras.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/README b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/README
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Punctuation.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Punctuation.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Punctuation.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-Punctuation.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gpos/IndicFontFeatureGPOS.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gpos/IndicFontFeatureGPOS.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gpos/IndicFontFeatureGPOS.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gpos/IndicFontFeatureGPOS.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Conjunct.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Conjunct.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Conjunct.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Conjunct.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Rakaaraansaya.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Rakaaraansaya.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Rakaaraansaya.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Rakaaraansaya.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Repaya.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Repaya.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Repaya.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Repaya.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Special-Cases.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Special-Cases.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Special-Cases.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Special-Cases.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-TouchingLetters.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-TouchingLetters.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-TouchingLetters.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-TouchingLetters.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Yansaya.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Yansaya.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Yansaya.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB-Yansaya.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-sinhala/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/README b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/README
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-CurrencySymbols.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-CurrencySymbols.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-CurrencySymbols.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-CurrencySymbols.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Numerics.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Numerics.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Numerics.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Numerics.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Symbols.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Symbols.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Symbols.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-Symbols.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-TamilSymbol.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-TamilSymbol.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-TamilSymbol.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-TamilSymbol.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gpos/IndicFontFeatureGPOS-BelowBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-tamil/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-tamil/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/LICENSE b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/LICENSE
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/LICENSE
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/LICENSE
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/README b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/README
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/README
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/README
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/SOURCES b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/SOURCES
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/SOURCES
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/SOURCES
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-AdditionalVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Consonants.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-DependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Digits.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-IndependentVowels.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-Reserved.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/codepoint/IndicFontFeatureCodepoint-VariousSigns.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/gpos/IndicFontFeatureGPOS-AboveBase.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/gsub/IndicFontFeatureGSUB.txt b/test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/gsub/IndicFontFeatureGSUB.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/indic/script-telugu/utrrs/gsub/IndicFontFeatureGSUB.txt
rename to test/shaping/texts/in-house/shaper-indic/indic/script-telugu/utrrs/gsub/IndicFontFeatureGSUB.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-javanese/misc.txt b/test/shaping/texts/in-house/shaper-indic/south-east-asian/script-javanese/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-javanese/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/south-east-asian/script-javanese/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-khmer/misc/misc.txt b/test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-khmer/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-khmer/misc/other-marks-invalid.txt b/test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/other-marks-invalid.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-khmer/misc/other-marks-invalid.txt
rename to test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/other-marks-invalid.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-khmer/misc/other-marks.txt b/test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/other-marks.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-khmer/misc/other-marks.txt
rename to test/shaping/texts/in-house/shaper-indic/south-east-asian/script-khmer/misc/other-marks.txt
diff --git a/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/misc.txt b/test/shaping/texts/in-house/shaper-myanmar/script-myanmar/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-myanmar/script-myanmar/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/otspec.txt b/test/shaping/texts/in-house/shaper-myanmar/script-myanmar/misc/otspec.txt
similarity index 98%
rename from test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/otspec.txt
rename to test/shaping/texts/in-house/shaper-myanmar/script-myanmar/misc/otspec.txt
index 66779cb..e3d460d 100644
--- a/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/otspec.txt
+++ b/test/shaping/texts/in-house/shaper-myanmar/script-myanmar/misc/otspec.txt
@@ -1,2 +1 @@
 င်္က္ကျြွှေို့်ာှီ့ၤဲံ့းႍ
-
diff --git a/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/utn11.txt b/test/shaping/texts/in-house/shaper-myanmar/script-myanmar/misc/utn11.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/utn11.txt
rename to test/shaping/texts/in-house/shaper-myanmar/script-myanmar/misc/utn11.txt
diff --git a/test/shaping/texts/in-tree/shaper-thai/script-lao/misc/sara-am.txt b/test/shaping/texts/in-house/shaper-thai/script-lao/misc/sara-am.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-thai/script-lao/misc/sara-am.txt
rename to test/shaping/texts/in-house/shaper-thai/script-lao/misc/sara-am.txt
diff --git a/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/misc.txt b/test/shaping/texts/in-house/shaper-thai/script-thai/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-thai/script-thai/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-thai/script-thai/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/phinthu.txt b/test/shaping/texts/in-house/shaper-thai/script-thai/misc/phinthu.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-thai/script-thai/misc/phinthu.txt
rename to test/shaping/texts/in-house/shaper-thai/script-thai/misc/phinthu.txt
diff --git a/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/pua-shaping.txt b/test/shaping/texts/in-house/shaper-thai/script-thai/misc/pua-shaping.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-thai/script-thai/misc/pua-shaping.txt
rename to test/shaping/texts/in-house/shaper-thai/script-thai/misc/pua-shaping.txt
diff --git a/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/sara-am.txt b/test/shaping/texts/in-house/shaper-thai/script-thai/misc/sara-am.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-thai/script-thai/misc/sara-am.txt
rename to test/shaping/texts/in-house/shaper-thai/script-thai/misc/sara-am.txt
diff --git a/test/shaping/texts/in-tree/shaper-tibetan/script-tibetan/misc/contractions.txt b/test/shaping/texts/in-house/shaper-tibetan/script-tibetan/misc/contractions.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-tibetan/script-tibetan/misc/contractions.txt
rename to test/shaping/texts/in-house/shaper-tibetan/script-tibetan/misc/contractions.txt
diff --git a/test/shaping/texts/in-tree/shaper-tibetan/script-tibetan/misc/misc.txt b/test/shaping/texts/in-house/shaper-tibetan/script-tibetan/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-tibetan/script-tibetan/misc/misc.txt
rename to test/shaping/texts/in-house/shaper-tibetan/script-tibetan/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-use/script-batak/misc.txt b/test/shaping/texts/in-house/shaper-use/script-batak/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-use/script-batak/misc.txt
rename to test/shaping/texts/in-house/shaper-use/script-batak/misc.txt
diff --git a/test/shaping/texts/in-house/shaper-use/script-buginese/misc.txt b/test/shaping/texts/in-house/shaper-use/script-buginese/misc.txt
new file mode 100644
index 0000000..5c1a03a
--- /dev/null
+++ b/test/shaping/texts/in-house/shaper-use/script-buginese/misc.txt
@@ -0,0 +1,70 @@
+ᨒᨚᨈᨑ
+ᨔᨑ
+ᨅᨔ ᨈᨚ ᨅᨙᨀ
+ᨕᨒᨚ ᨆᨒᨗᨕᨘ ᨅᨛᨈᨘᨕᨊ
+ᨕᨗᨉᨚ ᨔᨘᨑᨛ
+ᨕᨗᨊ ᨔᨘᨑᨛ
+ᨕᨊ ᨔᨘᨑᨛ
+
+ᨊᨀᨚ	ᨕᨛᨃ	ᨈᨕᨘᨄᨔᨒ᨞	ᨕᨍ	ᨆᨘᨄᨈᨒᨒᨚᨓᨗ	ᨄᨌᨒᨆᨘ	ᨑᨗᨈᨚᨄᨔᨒᨕᨙ᨞
+ᨄᨔᨗᨈᨘᨍᨘᨓᨗᨆᨘᨈᨚᨓᨗᨔ	ᨕᨔᨒᨊ	ᨄᨌᨒᨆᨘ᨞	ᨕᨄ	ᨕᨗᨀᨚᨊᨈᨘ	ᨊᨁᨗᨒᨗ	ᨉᨙᨓᨈᨕᨙ᨞
+ᨊᨀᨚ	ᨅᨕᨗᨌᨘᨆᨘᨄᨗ	ᨕᨔᨒᨊ	ᨈᨕᨘᨓᨙ᨞	ᨆᨘᨄᨙᨑᨍᨕᨗᨔ	ᨄᨉᨈᨚᨓᨗ᨞
+ᨊᨀᨚ	ᨄᨔᨒᨕᨗ	ᨈᨕᨘᨓᨙ᨞	ᨕᨍ	ᨈᨗᨆᨘᨌᨒᨕᨗ	ᨑᨗᨔᨗᨈᨗᨊᨍᨊᨕᨙᨈᨚᨔ	ᨕᨔᨒᨊ᨞
+
+ᨕᨛᨛᨃ	ᨕᨛᨃ	ᨄ ᨙᨑ᨞	ᨕᨛᨃ	 ᨙᨔᨕᨘᨓ	ᨓᨛᨈᨘ᨞
+ᨕᨛᨃ	 ᨙᨔᨕᨘᨓ	ᨕᨑᨘ	ᨆᨀᨘᨋᨕᨗ	ᨑᨗ	ᨒᨘᨓᨘ᨞	ᨆᨔᨒ	ᨕᨘᨒᨗ᨞
+
+ᨄᨘᨑᨊᨗᨀᨚ	ᨆᨙᨋ?
+ᨉᨙᨄ
+
+ᨆᨙᨒᨚ ᨀ ᨌᨛᨙᨆ
+ᨔᨙᨉᨗ
+ᨉᨘᨓ
+ᨈᨛᨒᨘ
+ᨕᨛᨄ
+ᨒᨗᨆ
+ᨕᨛᨊᨛ
+ᨄᨗᨈᨘ
+ᨕᨑᨘᨓ
+ᨕᨙᨔᨑ
+ᨔᨄᨘᨒᨚ
+ᨉᨘᨓᨄᨘᨒᨚ
+ᨈᨛᨒᨘᨄᨘᨒᨚ
+ᨄᨈᨄᨘᨒᨚ
+ᨒᨗᨆᨄᨘᨒᨚ
+ᨕᨛᨊᨛᨄᨘᨒᨚᨊ
+ᨄᨗᨈᨘᨄᨘᨒᨚ
+ᨕᨑᨘᨓᨄᨘᨒᨚᨊ
+ᨕᨙᨔᨑᨄᨘᨒᨚᨊ
+ᨔᨗᨑᨈᨘ
+ᨔᨗᨔᨛᨅᨘ
+ᨔᨗᨒᨔ
+ᨔᨗᨀᨚᨈᨗ
+
+ᨅᨔ ᨕᨘᨁᨗ
+
+ᨅᨔ ᨆᨀᨔᨑ
+ᨅᨒ
+ᨅᨚᨒᨚ
+ᨅᨅ
+ᨌᨗᨄᨘᨑᨘ
+ᨉᨚᨕᨙ
+ᨕᨗᨐᨚ
+ᨒᨚᨄᨚ
+ᨔᨒᨚ
+ᨈ ᨅᨙᨙ
+ᨈᨙᨊ
+ᨀᨑᨕᨙ
+ᨕᨄ ᨀᨑᨙᨅ?
+ᨒᨀᨙᨀᨚ ᨆᨕᨙ?
+ᨅᨒ
+ᨅᨚᨈᨚ
+ᨑᨈᨔ
+ᨅᨈᨒ
+ᨅᨗᨒ
+ᨁᨙᨒᨙ ᨁᨙᨒᨙ
+ᨀᨚᨀᨚ
+ᨍᨑ
+ᨅᨙᨅᨙ
+ᨆᨚᨈᨙᨑᨙ
+ᨂᨑᨙ
diff --git a/test/shaping/texts/in-tree/shaper-use/script-cham/misc.txt b/test/shaping/texts/in-house/shaper-use/script-cham/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-use/script-cham/misc.txt
rename to test/shaping/texts/in-house/shaper-use/script-cham/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-use/script-kaithi/misc.txt b/test/shaping/texts/in-house/shaper-use/script-kaithi/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-use/script-kaithi/misc.txt
rename to test/shaping/texts/in-house/shaper-use/script-kaithi/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-use/script-kharoshti/misc.txt b/test/shaping/texts/in-house/shaper-use/script-kharoshti/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-use/script-kharoshti/misc.txt
rename to test/shaping/texts/in-house/shaper-use/script-kharoshti/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-use/script-tai-tham/misc.txt b/test/shaping/texts/in-house/shaper-use/script-tai-tham/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-use/script-tai-tham/misc.txt
rename to test/shaping/texts/in-house/shaper-use/script-tai-tham/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-use/script-tai-tham/torture.txt b/test/shaping/texts/in-house/shaper-use/script-tai-tham/torture.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-use/script-tai-tham/torture.txt
rename to test/shaping/texts/in-house/shaper-use/script-tai-tham/torture.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/abbreviation-mark.txt b/test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/abbreviation-mark.txt
deleted file mode 100644
index a450678..0000000
--- a/test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/abbreviation-mark.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-ܐܒ 
-ܐ܏ 
-ܐ܏ܒ 
-ܐ܏ܒܓ 
-ܐ܏ܒܓܕ 
-ܐ܏ܒܓܕܐ 
-ܐ܏ܒܓܕܐܐܐܐܐܐܐܐܐ 
-ܐ܏ܒܓܕܐ܏ܐܐܐ܏ܐ܏ܐܐܐܐ 
-ܐ܏ܒܓܕܓܓܓܓܓܓ 
-ܐ܏ܒܓ 
-܏ܫܘabcܒ.
diff --git a/test/shaping/texts/in-tree/shaper-use/script-buginese/misc.txt b/test/shaping/texts/in-tree/shaper-use/script-buginese/misc.txt
deleted file mode 100644
index fe1b76d..0000000
--- a/test/shaping/texts/in-tree/shaper-use/script-buginese/misc.txt
+++ /dev/null
@@ -1,70 +0,0 @@
-ᨒᨚᨈᨑ
-ᨔᨑ
-ᨅᨔ ᨈᨚ ᨅᨙᨀ
-ᨕᨒᨚ ᨆᨒᨗᨕᨘ ᨅᨛᨈᨘᨕᨊ
-ᨕᨗᨉᨚ ᨔᨘᨑᨛ
-ᨕᨗᨊ ᨔᨘᨑᨛ
-ᨕᨊ ᨔᨘᨑᨛ
-
-ᨊᨀᨚ	ᨕᨛᨃ	ᨈᨕᨘᨄᨔᨒ᨞	ᨕᨍ	ᨆᨘᨄᨈᨒᨒᨚᨓᨗ	ᨄᨌᨒᨆᨘ	ᨑᨗᨈᨚᨄᨔᨒᨕᨙ᨞
-ᨄᨔᨗᨈᨘᨍᨘᨓᨗᨆᨘᨈᨚᨓᨗᨔ	ᨕᨔᨒᨊ	ᨄᨌᨒᨆᨘ᨞	ᨕᨄ	ᨕᨗᨀᨚᨊᨈᨘ	ᨊᨁᨗᨒᨗ	ᨉᨙᨓᨈᨕᨙ᨞
-ᨊᨀᨚ	ᨅᨕᨗᨌᨘᨆᨘᨄᨗ	ᨕᨔᨒᨊ	ᨈᨕᨘᨓᨙ᨞	ᨆᨘᨄᨙᨑᨍᨕᨗᨔ	ᨄᨉᨈᨚᨓᨗ᨞
-ᨊᨀᨚ	ᨄᨔᨒᨕᨗ	ᨈᨕᨘᨓᨙ᨞	ᨕᨍ	ᨈᨗᨆᨘᨌᨒᨕᨗ	ᨑᨗᨔᨗᨈᨗᨊᨍᨊᨕᨙᨈᨚᨔ	ᨕᨔᨒᨊ᨞
-
-ᨕᨛᨛᨃ	ᨕᨛᨃ	ᨄ ᨙᨑ᨞	ᨕᨛᨃ	 ᨙᨔᨕᨘᨓ	ᨓᨛᨈᨘ᨞
-ᨕᨛᨃ	 ᨙᨔᨕᨘᨓ	ᨕᨑᨘ	ᨆᨀᨘᨋᨕᨗ	ᨑᨗ	ᨒᨘᨓᨘ᨞	ᨆᨔᨒ	ᨕᨘᨒᨗ᨞
-
-ᨄᨘᨑᨊᨗᨀᨚ	ᨆᨙᨋ?
-ᨉᨙᨄ
-
-ᨆᨙᨒᨚ ᨀ ᨌᨛᨙᨆ
-ᨔᨙᨉᨗ	
-ᨉᨘᨓ	
-ᨈᨛᨒᨘ	
-ᨕᨛᨄ	
-ᨒᨗᨆ	
-ᨕᨛᨊᨛ	
-ᨄᨗᨈᨘ	
-ᨕᨑᨘᨓ	
-ᨕᨙᨔᨑ	
-ᨔᨄᨘᨒᨚ	
-ᨉᨘᨓᨄᨘᨒᨚ	
-ᨈᨛᨒᨘᨄᨘᨒᨚ	
-ᨄᨈᨄᨘᨒᨚ	
-ᨒᨗᨆᨄᨘᨒᨚ	
-ᨕᨛᨊᨛᨄᨘᨒᨚᨊ	
-ᨄᨗᨈᨘᨄᨘᨒᨚ	
-ᨕᨑᨘᨓᨄᨘᨒᨚᨊ	
-ᨕᨙᨔᨑᨄᨘᨒᨚᨊ	
-ᨔᨗᨑᨈᨘ	
-ᨔᨗᨔᨛᨅᨘ	
-ᨔᨗᨒᨔ	
-ᨔᨗᨀᨚᨈᨗ	
-
-ᨅᨔ ᨕᨘᨁᨗ
-
-ᨅᨔ ᨆᨀᨔᨑ
-ᨅᨒ	
-ᨅᨚᨒᨚ	
-ᨅᨅ	
-ᨌᨗᨄᨘᨑᨘ	
-ᨉᨚᨕᨙ	
-ᨕᨗᨐᨚ	
-ᨒᨚᨄᨚ	
-ᨔᨒᨚ	
-ᨈ ᨅᨙᨙ	
-ᨈᨙᨊ	
-ᨀᨑᨕᨙ	
-ᨕᨄ ᨀᨑᨙᨅ?	
-ᨒᨀᨙᨀᨚ ᨆᨕᨙ?	
-ᨅᨒ	
-ᨅᨚᨈᨚ	
-ᨑᨈᨔ	
-ᨅᨈᨒ	
-ᨅᨗᨒ	
-ᨁᨙᨒᨙ ᨁᨙᨒᨙ	
-ᨀᨚᨀᨚ	
-ᨍᨑ	
-ᨅᨙᨅᨙ	
-ᨆᨚᨈᨙᨑᨙ	
-ᨂᨑᨙ	
diff --git a/test/subset/CMakeLists.txt b/test/subset/CMakeLists.txt
new file mode 100644
index 0000000..ea04105
--- /dev/null
+++ b/test/subset/CMakeLists.txt
@@ -0,0 +1,10 @@
+if (HB_BUILD_UTILS)
+  file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/Makefile.sources" SOURCES)
+  extract_make_variable (TESTS ${SOURCES})
+  foreach (test IN ITEMS ${TESTS})
+    add_test (NAME ${test}
+      COMMAND python run-tests.py $<TARGET_FILE:hb-subset> "data/${test}"
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+    set_property(TEST ${test} PROPERTY SKIP_RETURN_CODE 77)
+  endforeach ()
+endif ()
diff --git a/test/subset/Makefile.am b/test/subset/Makefile.am
new file mode 100644
index 0000000..336d33d
--- /dev/null
+++ b/test/subset/Makefile.am
@@ -0,0 +1,22 @@
+# Process this file with automake to produce Makefile.in
+
+NULL =
+EXTRA_DIST =
+CLEANFILES =
+SUBDIRS = data
+
+# Convenience targets:
+lib:
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
+
+EXTRA_DIST += \
+	CMakeLists.txt \
+	run-tests.py \
+	subset_test_suite.py \
+	$(NULL)
+
+CLEANFILES += \
+	subset_test_suite.py[co] \
+	$(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am
new file mode 100644
index 0000000..f1234db
--- /dev/null
+++ b/test/subset/data/Makefile.am
@@ -0,0 +1,23 @@
+# Process this file with automake to produce Makefile.in
+
+NULL =
+EXTRA_DIST =
+CLEANFILES =
+SUBDIRS =
+
+EXTRA_DIST = \
+	$(TESTS) \
+	expected/basics \
+	fonts \
+	profiles \
+	$(NULL)
+
+# Convenience targets:
+lib:
+	@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
+
+TEST_EXTENSIONS = .tests
+TESTS_LOG_COMPILER = $(srcdir)/../run-tests.py $(top_builddir)/util/hb-subset$(EXEEXT)
+include Makefile.sources
+
+-include $(top_srcdir)/git.mk
diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources
new file mode 100644
index 0000000..37550b6
--- /dev/null
+++ b/test/subset/data/Makefile.sources
@@ -0,0 +1,9 @@
+TESTS = \
+	tests/basics.tests \
+	$(NULL)
+
+XFAIL_TESTS = \
+	$(NULL)
+
+DISABLED_TESTS = \
+	$(NULL)
diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf
new file mode 100644
index 0000000..8d7e6b2
--- /dev/null
+++ b/test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf
Binary files differ
diff --git a/test/subset/data/fonts/Roboto-Regular.abc.ttf b/test/subset/data/fonts/Roboto-Regular.abc.ttf
new file mode 100644
index 0000000..9d791f7
--- /dev/null
+++ b/test/subset/data/fonts/Roboto-Regular.abc.ttf
Binary files differ
diff --git a/test/subset/data/profiles/default.txt b/test/subset/data/profiles/default.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/subset/data/profiles/default.txt
diff --git a/test/subset/data/tests/basics.tests b/test/subset/data/tests/basics.tests
new file mode 100644
index 0000000..8a7246b
--- /dev/null
+++ b/test/subset/data/tests/basics.tests
@@ -0,0 +1,8 @@
+FONTS:
+Roboto-Regular.abc.ttf
+
+PROFILES:
+default.txt
+
+SUBSETS:
+b
diff --git a/test/subset/generate-expected-outputs.py b/test/subset/generate-expected-outputs.py
new file mode 100755
index 0000000..6dac890
--- /dev/null
+++ b/test/subset/generate-expected-outputs.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+# Pre-generates the expected output subset files (via fonttools) for
+# specified subset test suite(s).
+
+import io
+import os
+import sys
+
+from subprocess import check_call
+from subset_test_suite import SubsetTestSuite
+
+
+def usage():
+	print "Usage: generate-expected-outputs.py <test suite file> ..."
+
+
+def generate_expected_output(input_file, unicodes, output_path):
+	check_call(["fonttools", "subset",
+							input_file,
+							"--drop-tables+=DSIG,GPOS,GSUB,GDEF",
+							"--unicodes=%s" % unicodes,
+							"--output-file=%s" % output_path])
+
+
+args = sys.argv[1:]
+if not args:
+	usage()
+
+for path in args:
+	with io.open(path, mode="r", encoding="utf-8") as f:
+		test_suite = SubsetTestSuite(path, f.read())
+		output_directory = test_suite.get_output_directory()
+
+		print "Generating output files for %s" % output_directory
+		for test in test_suite.tests():
+			unicodes = test.unicodes()
+			font_name = test.get_font_name()
+			print "Creating subset %s/%s" % (output_directory, font_name)
+			generate_expected_output(test.font_path, unicodes,
+															 os.path.join(output_directory,
+																						font_name))
diff --git a/test/subset/run-tests.py b/test/subset/run-tests.py
new file mode 100755
index 0000000..99f9782
--- /dev/null
+++ b/test/subset/run-tests.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+
+# Runs a subsetting test suite. Compares the results of subsetting via harfbuz
+# to subsetting via fonttools.
+
+from __future__ import print_function
+
+import io
+from difflib import unified_diff
+import os
+import re
+import subprocess
+import sys
+import tempfile
+
+from subset_test_suite import SubsetTestSuite
+
+
+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 (), p.returncode
+
+def read_binary(file_path):
+	with open(file_path, 'rb') as f:
+		return f.read()
+
+def fail_test(test, cli_args, message):
+	print ('ERROR: %s' % message)
+	print ('Test State:')
+	print ('  test.font_path    %s' % os.path.abspath(test.font_path))
+	print ('  test.profile_path %s' % os.path.abspath(test.profile_path))
+	print ('  test.unicodes	    %s' % test.unicodes())
+	expected_file = os.path.join(test_suite.get_output_directory(),
+				     test.get_font_name())
+	print ('  expected_file	    %s' % os.path.abspath(expected_file))
+	return 1
+
+def run_test(test):
+	out_file = os.path.join(tempfile.mkdtemp(), test.get_font_name() + '-subset.ttf')
+	cli_args = [hb_subset,
+		    "--font-file=" + test.font_path,
+		    "--output-file=" + out_file,
+		    "--unicodes=%s" % test.unicodes()]
+	print (' '.join(cli_args))
+	_, return_code = cmd(cli_args)
+
+	if return_code:
+		return fail_test(test, cli_args, "%s returned %d" % (' '.join(cli_args), return_code))
+
+	expected_ttx, return_code = run_ttx(os.path.join(test_suite.get_output_directory(),
+					    test.get_font_name()))
+	if return_code:
+		return fail_test(test, cli_args, "ttx (expected) returned %d" % (return_code))
+
+	actual_ttx, return_code = run_ttx(out_file)
+	if return_code:
+		return fail_test(test, cli_args, "ttx (actual) returned %d" % (return_code))
+
+	expected_ttx = strip_check_sum (expected_ttx)
+	actual_ttx = strip_check_sum (actual_ttx)
+
+	if not actual_ttx == expected_ttx:
+		for line in unified_diff(expected_ttx.splitlines(1), actual_ttx.splitlines(1)):
+			sys.stdout.write(line)
+		sys.stdout.flush()
+		return fail_test(test, cli_args, 'ttx for expected and actual does not match.')
+
+	return 0
+
+def run_ttx(file):
+	cli_args = ["ttx",
+		    "-o-",
+		    file]
+	return cmd(cli_args)
+
+def strip_check_sum (ttx_string):
+	return re.sub ('checkSumAdjustment value=["]0x([0-9a-fA-F])+["]',
+                       'checkSumAdjustment value="0x00000000"',
+		       ttx_string, count=1)
+
+args = sys.argv[1:]
+if not args or sys.argv[1].find('hb-subset') == -1 or not os.path.exists (sys.argv[1]):
+	print ("First argument does not seem to point to usable hb-subset.")
+	sys.exit (1)
+hb_subset, args = args[0], args[1:]
+
+if not len(args):
+	print ("No tests supplied.")
+	sys.exit (1)
+
+_, returncode = cmd(["which", "ttx"])
+if returncode:
+	print("TTX is not present, skipping test.")
+	sys.exit (77)
+
+fails = 0
+for path in args:
+	with io.open(path, mode="r", encoding="utf-8") as f:
+		print ("Running tests in " + path)
+		test_suite = SubsetTestSuite(path, f.read())
+		for test in test_suite.tests():
+			fails += run_test(test)
+
+if fails != 0:
+	print (str (fails) + " test(s) failed.")
+	sys.exit(1)
+else:
+	print ("All tests passed.")
diff --git a/test/subset/subset_test_suite.py b/test/subset/subset_test_suite.py
new file mode 100644
index 0000000..256e207
--- /dev/null
+++ b/test/subset/subset_test_suite.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+import os
+
+# A single test in a subset test suite. Identifies a font
+# a subsetting profile, and a subset to be cut.
+class Test:
+	def __init__(self, font_path, profile_path, subset):
+		self.font_path = font_path
+		self.profile_path = profile_path
+		self.subset = subset
+
+	def unicodes(self):
+		return ",".join("%X" % ord(c) for (i, c) in enumerate(self.subset))
+
+	def get_font_name(self):
+		font_base_name = os.path.basename(self.font_path)
+		font_base_name_parts = os.path.splitext(font_base_name)
+		profile_name = os.path.splitext(os.path.basename(self.profile_path))[0]
+
+		return "%s.%s.%s%s" % (font_base_name_parts[0],
+													 profile_name,
+													 self.unicodes(),
+													 font_base_name_parts[1])
+
+# A group of tests to perform on the subsetter. Each test
+# Identifies a font a subsetting profile, and a subset to be cut.
+class SubsetTestSuite:
+
+	def __init__(self, test_path, definition):
+		self.test_path = test_path
+		self.fonts = set()
+		self.profiles = set()
+		self.subsets = set()
+		self._parse(definition)
+
+	def get_output_directory(self):
+		test_name = os.path.splitext(os.path.basename(self.test_path))[0]
+		data_dir = os.path.join(os.path.dirname(self.test_path), "..")
+
+		output_dir = os.path.normpath(os.path.join(data_dir, "expected", test_name))
+		if not os.path.exists(output_dir):
+			os.mkdir(output_dir)
+		if not os.path.isdir(output_dir):
+			raise Error("%s is not a directory." % output_dir)
+
+		return output_dir
+
+	def tests(self):
+		for font in self.fonts:
+			font = os.path.join(self._base_path(), "fonts", font)
+			for profile in self.profiles:
+				profile = os.path.join(self._base_path(), "profiles", profile)
+				for subset in self.subsets:
+					yield Test(font, profile, subset)
+
+	def _base_path(self):
+		return os.path.dirname(os.path.dirname(self.test_path))
+
+	def _parse(self, definition):
+		destinations = {
+				"FONTS:": self.fonts,
+				"PROFILES:": self.profiles,
+				"SUBSETS:": self.subsets
+		}
+
+		current_destination = None
+		for line in definition.splitlines():
+			line = line.strip()
+
+			if line.startswith("#"):
+				continue
+
+			if not line:
+				continue
+
+			if line in destinations:
+				current_destination = destinations[line]
+			elif current_destination is not None:
+				current_destination.add(line)
+			else:
+				raise Exception("Failed to parse test suite file.")
diff --git a/util/Makefile.am b/util/Makefile.am
index 2543a60..d4ab9cd 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -1,5 +1,6 @@
 # Process this file with automake to produce Makefile.in
 
+NULL =
 EXTRA_DIST =
 CLEANFILES =
 DISTCLEANFILES =
@@ -45,6 +46,10 @@
 hb_shape_SOURCES = $(HB_SHAPE_sources)
 bin_PROGRAMS += hb-shape
 
+hb_subset_SOURCES = $(HB_SUBSET_CLI_sources)
+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
diff --git a/util/Makefile.sources b/util/Makefile.sources
index 368fdb0..6c815d2 100644
--- a/util/Makefile.sources
+++ b/util/Makefile.sources
@@ -1,5 +1,3 @@
-NULL =
-
 HB_VIEW_sources = \
 	hb-view.cc \
 	options.cc \
@@ -30,3 +28,10 @@
 	options.hh \
 	main-font-text.hh \
 	$(NULL)
+
+HB_SUBSET_CLI_sources = \
+	hb-subset.cc \
+	options.cc \
+	options.hh \
+	main-font-text.hh \
+	$(NULL)
diff --git a/util/ansi-print.cc b/util/ansi-print.cc
index e0ce7b3..0daee1f 100644
--- a/util/ansi-print.cc
+++ b/util/ansi-print.cc
@@ -353,7 +353,7 @@
 	} else
 	  qs += quad[i][j];
     if (qs < score) {
-      const char *c = NULL;
+      const char *c = nullptr;
       bool inv = false;
       switch (q) {
 	case 1:  c = "▟"; inv = true;  break;
diff --git a/util/ansi-print.hh b/util/ansi-print.hh
index dad4d4c..1ea5b37 100644
--- a/util/ansi-print.hh
+++ b/util/ansi-print.hh
@@ -27,6 +27,7 @@
 #ifndef ANSI_PRINT_HH
 #define ANSI_PRINT_HH
 
+#include "hb-private.hh"
 #include <hb.h> /* for int types */
 
 void
diff --git a/util/hb-fc.cc b/util/hb-fc.cc
index e99b1ae..cb89991 100644
--- a/util/hb-fc.cc
+++ b/util/hb-fc.cc
@@ -82,7 +82,7 @@
   {
     hb_font_funcs_t *newfuncs = hb_font_funcs_create ();
 
-    hb_font_funcs_set_glyph_func (newfuncs, hb_fc_get_glyph, NULL, NULL);
+    hb_font_funcs_set_glyph_func (newfuncs, hb_fc_get_glyph, nullptr, nullptr);
 
     /* XXX MT-unsafe */
     if (fc_ffuncs)
@@ -121,7 +121,7 @@
 hb_bool_t
 hb_fc_can_render (hb_font_t *font, const char *text)
 {
-  static const char *ot[] = {"ot", NULL};
+  static const char *ot[] = {"ot", nullptr};
 
   hb_buffer_t *buffer = hb_buffer_create ();
   hb_buffer_add_utf8 (buffer, text, -1, 0, -1);
@@ -132,7 +132,7 @@
    * Might be better to force generic shaper perhaps. */
   hb_buffer_guess_segment_properties (buffer);
 
-  if (!hb_shape_full (font, buffer, NULL, 0, ot))
+  if (!hb_shape_full (font, buffer, nullptr, 0, ot))
     abort (); /* hb-ot shaper not enabled? */
 
   unsigned int len;
diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc
index 859f9a6..77ca201 100644
--- a/util/hb-ot-shape-closure.cc
+++ b/util/hb-ot-shape-closure.cc
@@ -43,8 +43,8 @@
   {
     GOptionEntry entries[] =
     {
-      {"no-glyph-names",	0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,	&this->show_glyph_names,	"Use glyph indices instead of names",	NULL},
-      {NULL}
+      {"no-glyph-names",	0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,	&this->show_glyph_names,	"Use glyph indices instead of names",	nullptr},
+      {nullptr}
     };
     parser->add_group (entries,
 		       "format",
@@ -53,14 +53,15 @@
 		       this);
   }
 
-  void init (const font_options_t *font_opts)
+  void init (hb_buffer_t  *buffer_,
+	     const font_options_t *font_opts)
   {
     glyphs = hb_set_create ();
     font = hb_font_reference (font_opts->get_font ());
     failed = false;
+    buffer = hb_buffer_reference (buffer_);
   }
-  void consume_line (hb_buffer_t  *buffer,
-		     const char   *text,
+  void consume_line (const char   *text,
 		     unsigned int  text_len,
 		     const char   *text_before,
 		     const char   *text_after)
@@ -92,9 +93,11 @@
   {
     printf ("\n");
     hb_font_destroy (font);
-    font = NULL;
+    font = nullptr;
     hb_set_destroy (glyphs);
-    glyphs = NULL;
+    glyphs = nullptr;
+    hb_buffer_destroy (buffer);
+    buffer = nullptr;
   }
 
   bool failed;
@@ -105,6 +108,7 @@
 
   hb_set_t *glyphs;
   hb_font_t *font;
+  hb_buffer_t *buffer;
 };
 
 int
diff --git a/util/hb-shape.cc b/util/hb-shape.cc
index 75c3793..337cd43 100644
--- a/util/hb-shape.cc
+++ b/util/hb-shape.cc
@@ -33,16 +33,16 @@
   output_buffer_t (option_parser_t *parser)
 		  : options (parser, hb_buffer_serialize_list_formats ()),
 		    format (parser),
-		    gs (NULL),
+		    gs (nullptr),
 		    line_no (0),
-		    font (NULL),
+		    font (nullptr),
 		    output_format (HB_BUFFER_SERIALIZE_FORMAT_INVALID),
 		    format_flags (HB_BUFFER_SERIALIZE_FLAG_DEFAULT) {}
 
-  void init (const font_options_t *font_opts)
+  void init (hb_buffer_t *buffer, const font_options_t *font_opts)
   {
     options.get_file_handle ();
-    gs = g_string_new (NULL);
+    gs = g_string_new (nullptr);
     line_no = 0;
     font = hb_font_reference (font_opts->get_font ());
 
@@ -72,9 +72,16 @@
       flags |= HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS;
     if (!format.show_positions)
       flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS;
+    if (!format.show_advances)
+      flags |= HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES;
     if (format.show_extents)
       flags |= HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS;
+    if (format.show_flags)
+      flags |= HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS;
     format_flags = (hb_buffer_serialize_flags_t) flags;
+
+    if (format.trace)
+      hb_buffer_set_message_func (buffer, message_func, this, nullptr);
   }
   void new_line (void)
   {
@@ -89,13 +96,10 @@
     format.serialize_buffer_of_text (buffer, line_no, text, text_len, font, gs);
     fprintf (options.fp, "%s", gs->str);
   }
-  void shape_failed (hb_buffer_t  *buffer,
-		     const char   *text,
-		     unsigned int  text_len,
-		     hb_bool_t     utf8_clusters)
+  void error (const char *message)
   {
     g_string_set_size (gs, 0);
-    format.serialize_message (line_no, "msg: all shapers failed", gs);
+    format.serialize_message (line_no, "error", message, gs);
     fprintf (options.fp, "%s", gs->str);
   }
   void consume_glyphs (hb_buffer_t  *buffer,
@@ -108,14 +112,40 @@
 				       output_format, format_flags, gs);
     fprintf (options.fp, "%s", gs->str);
   }
-  void finish (const font_options_t *font_opts)
+  void finish (hb_buffer_t *buffer, const font_options_t *font_opts)
   {
+    hb_buffer_set_message_func (buffer, nullptr, nullptr, nullptr);
     hb_font_destroy (font);
     g_string_free (gs, true);
-    gs = NULL;
-    font = NULL;
+    gs = nullptr;
+    font = nullptr;
   }
 
+  static hb_bool_t
+  message_func (hb_buffer_t *buffer,
+		hb_font_t *font,
+		const char *message,
+		void *user_data)
+  {
+    output_buffer_t *that = (output_buffer_t *) user_data;
+    that->trace (buffer, font, message);
+    return true;
+  }
+
+  void
+  trace (hb_buffer_t *buffer,
+	 hb_font_t *font,
+	 const char *message)
+  {
+    g_string_set_size (gs, 0);
+    format.serialize_line_no (line_no, gs);
+    g_string_append_printf (gs, "trace: %s	buffer: ", message);
+    format.serialize_glyphs (buffer, font, output_format, format_flags, gs);
+    g_string_append_c (gs, '\n');
+    fprintf (options.fp, "%s", gs->str);
+  }
+
+
   protected:
   output_options_t options;
   format_options_t format;
diff --git a/util/hb-subset.cc b/util/hb-subset.cc
new file mode 100644
index 0000000..ea657af
--- /dev/null
+++ b/util/hb-subset.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright © 2010  Behdad Esfahbod
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Garret Rieger, Rod Sheeter
+ */
+
+#include <stdio.h>
+
+#include "main-font-text.hh"
+#include "hb-subset.h"
+
+/*
+ * Command line interface to the harfbuzz font subsetter.
+ */
+
+struct subset_consumer_t
+{
+  subset_consumer_t (option_parser_t *parser)
+      : failed (false), options (parser), font (nullptr), input (nullptr) {}
+
+  void init (hb_buffer_t  *buffer_,
+             const font_options_t *font_opts)
+  {
+    font = hb_font_reference (font_opts->get_font ());
+    input = hb_subset_input_create_or_fail ();
+  }
+
+  void consume_line (const char   *text,
+                     unsigned int  text_len,
+                     const char   *text_before,
+                     const char   *text_after)
+  {
+    // TODO(Q1) does this only get called with at least 1 codepoint?
+    hb_set_t *codepoints = hb_subset_input_unicode_set (input);
+    gchar *c = (gchar *)text;
+    do {
+      gunichar cp = g_utf8_get_char(c);
+      hb_codepoint_t hb_cp = cp;
+      hb_set_add (codepoints, hb_cp);
+    } while ((c = g_utf8_find_next_char(c, text + text_len)) != nullptr);
+  }
+
+  hb_bool_t
+  write_file (const char *output_file, hb_blob_t *blob) {
+    unsigned int data_length;
+    const char* data = hb_blob_get_data (blob, &data_length);
+
+    FILE *fp_out = fopen(output_file, "wb");
+    if (fp_out == nullptr) {
+      fprintf(stderr, "Unable to open output file\n");
+      return false;
+    }
+    int bytes_written = fwrite(data, 1, data_length, fp_out);
+
+    fclose (fp_out);
+
+    if (bytes_written == -1) {
+      fprintf(stderr, "Unable to write output file\n");
+      return false;
+    }
+    if ((unsigned int) bytes_written != data_length) {
+      fprintf(stderr, "Expected %u bytes written, got %d\n", data_length,
+              bytes_written);
+      return false;
+    }
+    return true;
+  }
+
+  void finish (const font_options_t *font_opts)
+  {
+    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_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);
+    hb_font_destroy (font);
+  }
+
+  public:
+  bool failed;
+
+  private:
+  output_options_t options;
+  hb_font_t *font;
+  hb_subset_input_t *input;
+};
+
+int
+main (int argc, char **argv)
+{
+  main_font_text_t<subset_consumer_t, 10, 0> driver;
+  return driver.main (argc, argv);
+}
diff --git a/util/helper-cairo-ansi.hh b/util/helper-cairo-ansi.hh
index eeaaa50..cf18ea4 100644
--- a/util/helper-cairo-ansi.hh
+++ b/util/helper-cairo-ansi.hh
@@ -24,11 +24,12 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include <cairo.h>
-
 #ifndef HELPER_CAIRO_ANSI_HH
 #define HELPER_CAIRO_ANSI_HH
 
+#include "hb-private.hh"
+
+#include <cairo.h>
 
 cairo_status_t
 helper_cairo_surface_write_to_ansi_stream (cairo_surface_t	*surface,
diff --git a/util/helper-cairo.cc b/util/helper-cairo.cc
index 2e2952b..b9f4985 100644
--- a/util/helper-cairo.cc
+++ b/util/helper-cairo.cc
@@ -79,7 +79,7 @@
   /* We cannot use the FT_Face from hb_font_t, as doing so will confuse hb_font_t because
    * cairo will reset the face size.  As such, create new face...
    * TODO Perhaps add API to hb-ft to encapsulate this code. */
-  FT_Face ft_face = NULL;//hb_ft_font_get_face (font);
+  FT_Face ft_face = nullptr;//hb_ft_font_get_face (font);
   if (!ft_face)
   {
     if (!ft_library)
@@ -103,6 +103,7 @@
   }
   else
   {
+#ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES
     unsigned int num_coords;
     const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
     if (num_coords)
@@ -116,6 +117,7 @@
 	free (ft_coords);
       }
     }
+#endif
 
     cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
   }
@@ -325,7 +327,7 @@
     "eps",
    #endif
   #endif
-  NULL
+  nullptr
 };
 
 cairo_t *
@@ -337,12 +339,12 @@
   cairo_surface_t *(*constructor) (cairo_write_func_t write_func,
 				   void *closure,
 				   double width,
-				   double height) = NULL;
+				   double height) = nullptr;
   cairo_surface_t *(*constructor2) (cairo_write_func_t write_func,
 				    void *closure,
 				    double width,
 				    double height,
-				    cairo_content_t content) = NULL;
+				    cairo_content_t content) = nullptr;
 
   const char *extension = out_opts->output_format;
   if (!extension) {
@@ -471,8 +473,8 @@
   memset (l, 0, sizeof (*l));
 
   l->num_glyphs = hb_buffer_get_length (buffer);
-  hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, NULL);
-  hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, NULL);
+  hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, nullptr);
+  hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, nullptr);
   l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1);
 
   if (text) {
diff --git a/util/helper-cairo.hh b/util/helper-cairo.hh
index 27b6eb3..50bc0af 100644
--- a/util/helper-cairo.hh
+++ b/util/helper-cairo.hh
@@ -24,13 +24,14 @@
  * Google Author(s): Behdad Esfahbod
  */
 
+#ifndef HELPER_CAIRO_HH
+#define HELPER_CAIRO_HH
+
+#include "hb-private.hh"
 #include "options.hh"
 
 #include <cairo.h>
 
-#ifndef HELPER_CAIRO_HH
-#define HELPER_CAIRO_HH
-
 
 cairo_scaled_font_t *
 helper_cairo_create_scaled_font (const font_options_t *font_opts);
diff --git a/util/main-font-text.hh b/util/main-font-text.hh
index 55de567..3390371 100644
--- a/util/main-font-text.hh
+++ b/util/main-font-text.hh
@@ -24,20 +24,21 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "options.hh"
-
 #ifndef HB_MAIN_FONT_TEXT_HH
 #define HB_MAIN_FONT_TEXT_HH
 
+#include "hb-private.hh"
+#include "options.hh"
+
 /* main() body for utilities taking font and processing text.*/
 
 static char *
 locale_to_utf8 (char *s)
 {
   char *t;
-  GError *error = NULL;
+  GError *error = nullptr;
 
-  t = g_locale_to_utf8 (s, -1, NULL, NULL, &error);
+  t = g_locale_to_utf8 (s, -1, nullptr, nullptr, &error);
   if (!t)
   {
      fail (true, "Failed converting text to UTF-8");
@@ -46,23 +47,6 @@
   return t;
 }
 
-static hb_bool_t
-message_func (hb_buffer_t *buffer,
-	      hb_font_t *font,
-	      const char *message,
-	      void *user_data)
-{
-  fprintf (stderr, "HB: %s\n", message);
-  char buf[4096];
-  hb_buffer_serialize_glyphs (buffer, 0, hb_buffer_get_length (buffer),
-			      buf, sizeof (buf), NULL,
-			      font,
-			      HB_BUFFER_SERIALIZE_FORMAT_TEXT,
-			      HB_BUFFER_SERIALIZE_FLAG_DEFAULT);
-  fprintf (stderr, "HB: buffer [%s]\n", buf);
-  return true;
-}
-
 template <typename consumer_t, int default_font_size, int subpixel_bits>
 struct main_font_text_t
 {
@@ -87,16 +71,14 @@
     if (!input.text && !input.text_file)
       input.text_file = g_strdup ("-");
 
-    consumer.init (&font_opts);
-
     hb_buffer_t *buffer = hb_buffer_create ();
-    if (debug)
-      hb_buffer_set_message_func (buffer, message_func, NULL, NULL);
+    consumer.init (buffer, &font_opts);
+    hb_buffer_destroy (buffer);
+
     unsigned int text_len;
     const char *text;
     while ((text = input.get_line (&text_len)))
-      consumer.consume_line (buffer, text, text_len, input.text_before, input.text_after);
-    hb_buffer_destroy (buffer);
+      consumer.consume_line (text, text_len, input.text_before, input.text_after);
 
     consumer.finish (&font_opts);
 
diff --git a/util/options.cc b/util/options.cc
index 0f2e207..d2444a4 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -70,7 +70,7 @@
 static gchar *
 shapers_to_string (void)
 {
-  GString *shapers = g_string_new (NULL);
+  GString *shapers = g_string_new (nullptr);
   const char **shaper_list = hb_shape_list_shapers ();
 
   for (; *shaper_list; shaper_list++) {
@@ -106,11 +106,11 @@
   GOptionEntry entries[] =
   {
     {"version",		0, G_OPTION_FLAG_NO_ARG,
-			      G_OPTION_ARG_CALLBACK,	(gpointer) &show_version,	"Show version numbers",			NULL},
-    {"debug",		0, 0, G_OPTION_ARG_NONE,	&debug,				"Free all resources before exit",	NULL},
-    {NULL}
+			      G_OPTION_ARG_CALLBACK,	(gpointer) &show_version,	"Show version numbers",			nullptr},
+    {"debug",		0, 0, G_OPTION_ARG_NONE,	&debug,				"Free all resources before exit",	nullptr},
+    {nullptr}
   };
-  g_option_context_add_main_entries (context, entries, NULL);
+  g_option_context_add_main_entries (context, entries, nullptr);
 }
 
 static gboolean
@@ -121,7 +121,7 @@
 {
   option_group_t *option_group = (option_group_t *) data;
   option_group->pre_parse (error);
-  return *error == NULL;
+  return *error == nullptr;
 }
 
 static gboolean
@@ -132,7 +132,7 @@
 {
   option_group_t *option_group = static_cast<option_group_t *>(data);
   option_group->post_parse (error);
-  return *error == NULL;
+  return *error == nullptr;
 }
 
 void
@@ -143,7 +143,7 @@
 			    option_group_t *option_group)
 {
   GOptionGroup *group = g_option_group_new (name, description, help_description,
-					    static_cast<gpointer>(option_group), NULL);
+					    static_cast<gpointer>(option_group), nullptr);
   g_option_group_add_entries (group, entries);
   g_option_group_set_parse_hooks (group, pre_parse, post_parse);
   g_option_context_add_group (context, group);
@@ -154,10 +154,10 @@
 {
   setlocale (LC_ALL, "");
 
-  GError *parse_error = NULL;
+  GError *parse_error = nullptr;
   if (!g_option_context_parse (context, argc, argv, &parse_error))
   {
-    if (parse_error != NULL) {
+    if (parse_error != nullptr) {
       fail (true, "%s", parse_error->message);
       //g_error_free (parse_error);
     } else
@@ -225,7 +225,7 @@
 
   shape_opts->num_features = 0;
   g_free (shape_opts->features);
-  shape_opts->features = NULL;
+  shape_opts->features = nullptr;
 
   if (!*s)
     return true;
@@ -240,6 +240,8 @@
   } while (p);
 
   shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features));
+  if (!shape_opts->features)
+    return false;
 
   /* now do the actual parsing */
   p = s;
@@ -248,7 +250,7 @@
     char *end = strchr (p, ',');
     if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features]))
       shape_opts->num_features++;
-    p = end ? end + 1 : NULL;
+    p = end ? end + 1 : nullptr;
   }
 
   return true;
@@ -266,7 +268,7 @@
 
   font_opts->num_variations = 0;
   g_free (font_opts->variations);
-  font_opts->variations = NULL;
+  font_opts->variations = nullptr;
 
   if (!*s)
     return true;
@@ -281,6 +283,8 @@
   } while (p);
 
   font_opts->variations = (hb_variation_t *) calloc (font_opts->num_variations, sizeof (*font_opts->variations));
+  if (!font_opts->variations)
+    return false;
 
   /* now do the actual parsing */
   p = s;
@@ -289,24 +293,86 @@
     char *end = strchr (p, ',');
     if (hb_variation_from_string (p, end ? end - p : -1, &font_opts->variations[font_opts->num_variations]))
       font_opts->num_variations++;
-    p = end ? end + 1 : NULL;
+    p = end ? end + 1 : nullptr;
   }
 
   return true;
 }
 
+static gboolean
+parse_text (const char *name G_GNUC_UNUSED,
+	    const char *arg,
+	    gpointer    data,
+	    GError    **error G_GNUC_UNUSED)
+{
+  text_options_t *text_opts = (text_options_t *) data;
+
+  if (text_opts->text)
+  {
+    g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+		 "Either --text or --unicodes can be provided but not both");
+    return false;
+  }
+
+  text_opts->text = g_strdup (arg);
+  return true;
+}
+
+
+static gboolean
+parse_unicodes (const char *name G_GNUC_UNUSED,
+	        const char *arg,
+	        gpointer    data,
+	        GError    **error G_GNUC_UNUSED)
+{
+  text_options_t *text_opts = (text_options_t *) data;
+
+  if (text_opts->text)
+  {
+    g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+		 "Either --text or --unicodes can be provided but not both");
+    return false;
+  }
+
+  GString *gs = g_string_new (nullptr);
+  char *s = (char *) arg;
+  char *p;
+
+  while (s && *s)
+  {
+    while (*s && strchr ("<+>{},;&#\\xXuUnNiI\n\t", *s))
+      s++;
+
+    errno = 0;
+    hb_codepoint_t u = strtoul (s, &p, 16);
+    if (errno || s == p)
+    {
+      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+		   "Failed parsing Unicode values at: '%s'", s);
+      return false;
+    }
+
+    g_string_append_unichar (gs, u);
+
+    s = p;
+  }
+
+  text_opts->text = g_string_free (gs, FALSE);
+  return true;
+}
+
 
 void
 view_options_t::add_options (option_parser_t *parser)
 {
   GOptionEntry entries[] =
   {
-    {"annotate",	0, 0, G_OPTION_ARG_NONE,	&this->annotate,		"Annotate output rendering",				NULL},
+    {"annotate",	0, 0, G_OPTION_ARG_NONE,	&this->annotate,		"Annotate output rendering",				nullptr},
     {"background",	0, 0, G_OPTION_ARG_STRING,	&this->back,			"Set background color (default: " DEFAULT_BACK ")",	"rrggbb/rrggbbaa"},
     {"foreground",	0, 0, G_OPTION_ARG_STRING,	&this->fore,			"Set foreground color (default: " DEFAULT_FORE ")",	"rrggbb/rrggbbaa"},
     {"line-space",	0, 0, G_OPTION_ARG_DOUBLE,	&this->line_space,		"Set space between lines (default: 0)",			"units"},
     {"margin",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_margin,	"Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"},
-    {NULL}
+    {nullptr}
   };
   parser->add_group (entries,
 		     "view",
@@ -321,21 +387,23 @@
   GOptionEntry entries[] =
   {
     {"list-shapers",	0, G_OPTION_FLAG_NO_ARG,
-			      G_OPTION_ARG_CALLBACK,	(gpointer) &list_shapers,	"List available shapers and quit",	NULL},
+			      G_OPTION_ARG_CALLBACK,	(gpointer) &list_shapers,	"List available shapers and quit",	nullptr},
     {"shaper",		0, G_OPTION_FLAG_HIDDEN,
-			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_shapers,	"Hidden duplicate of --shapers",	NULL},
+			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_shapers,	"Hidden duplicate of --shapers",	nullptr},
     {"shapers",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_shapers,	"Set comma-separated list of shapers to try","list"},
     {"direction",	0, 0, G_OPTION_ARG_STRING,	&this->direction,		"Set text direction (default: auto)",	"ltr/rtl/ttb/btt"},
     {"language",	0, 0, G_OPTION_ARG_STRING,	&this->language,		"Set text language (default: $LANG)",	"langstr"},
     {"script",		0, 0, G_OPTION_ARG_STRING,	&this->script,			"Set text script (default: auto)",	"ISO-15924 tag"},
-    {"bot",		0, 0, G_OPTION_ARG_NONE,	&this->bot,			"Treat text as beginning-of-paragraph",	NULL},
-    {"eot",		0, 0, G_OPTION_ARG_NONE,	&this->eot,			"Treat text as end-of-paragraph",	NULL},
-    {"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE,	&this->preserve_default_ignorables,	"Preserve Default-Ignorable characters",	NULL},
-    {"utf8-clusters",	0, 0, G_OPTION_ARG_NONE,	&this->utf8_clusters,		"Use UTF8 byte indices, not char indices",	NULL},
+    {"bot",		0, 0, G_OPTION_ARG_NONE,	&this->bot,			"Treat text as beginning-of-paragraph",	nullptr},
+    {"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},
+    {"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",	NULL},
+    {"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"},
-    {NULL}
+    {nullptr}
   };
   parser->add_group (entries,
 		     "shape",
@@ -382,7 +450,7 @@
   GOptionEntry entries2[] =
   {
     {"features",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_features,	features_help,	"list"},
-    {NULL}
+    {nullptr}
   };
   parser->add_group (entries2,
 		     "features",
@@ -408,19 +476,39 @@
     case 2: return true;
     default:
       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
-		   "%s argument should be one to four space-separated numbers",
+		   "%s argument should be one or two space-separated numbers",
 		   name);
       return false;
   }
 }
+
+static gboolean
+parse_font_ppem (const char *name G_GNUC_UNUSED,
+		 const char *arg,
+		 gpointer    data,
+		 GError    **error G_GNUC_UNUSED)
+{
+  font_options_t *font_opts = (font_options_t *) data;
+  switch (sscanf (arg, "%d%*[ ,]%d", &font_opts->x_ppem, &font_opts->y_ppem)) {
+    case 1: font_opts->y_ppem = font_opts->x_ppem;
+    case 2: return true;
+    default:
+      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+		   "%s argument should be one or two space-separated numbers",
+		   name);
+      return false;
+  }
+}
+
 void
 font_options_t::add_options (option_parser_t *parser)
 {
-  char *text = NULL;
+  char *text = nullptr;
 
   {
-    ASSERT_STATIC (ARRAY_LENGTH_CONST (supported_font_funcs) > 0);
-    GString *s = g_string_new (NULL);
+    static_assert ((ARRAY_LENGTH_CONST (supported_font_funcs) > 0),
+		   "No supported font-funcs found.");
+    GString *s = g_string_new (nullptr);
     g_string_printf (s, "Set font functions implementation to use (default: %s)\n\n    Supported font function implementations are: %s",
 		     supported_font_funcs[0].name,
 		     supported_font_funcs[0].name);
@@ -444,12 +532,14 @@
 
   GOptionEntry entries[] =
   {
-    {"font-file",	0, 0, G_OPTION_ARG_STRING,	&this->font_file,		"Set font file-name",			"filename"},
-    {"face-index",	0, 0, G_OPTION_ARG_INT,		&this->face_index,		"Set face index (default: 0)",		"index"},
+    {"font-file",	0, 0, G_OPTION_ARG_STRING,	&this->font_file,		"Set font file-name",				"filename"},
+    {"face-index",	0, 0, G_OPTION_ARG_INT,		&this->face_index,		"Set face index (default: 0)",			"index"},
     {"font-size",	0, default_font_size ? 0 : G_OPTION_FLAG_HIDDEN,
-			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_font_size,	font_size_text,				"1/2 numbers or 'upem'"},
-    {"font-funcs",	0, 0, G_OPTION_ARG_STRING,	&this->font_funcs,		text,					"impl"},
-    {NULL}
+			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_font_size,	font_size_text,					"1/2 integers or 'upem'"},
+    {"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"},
+    {nullptr}
   };
   parser->add_group (entries,
 		     "font",
@@ -472,11 +562,11 @@
   GOptionEntry entries2[] =
   {
     {"variations",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_variations,	variations_help,	"list"},
-    {NULL}
+    {nullptr}
   };
   parser->add_group (entries2,
 		     "variations",
-		     "Varitions options:",
+		     "Variations options:",
 		     "Options for font variations used",
 		     this);
 }
@@ -486,11 +576,12 @@
 {
   GOptionEntry entries[] =
   {
-    {"text",		0, 0, G_OPTION_ARG_STRING,	&this->text,			"Set input text",			"string"},
+    {"text",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_text,		"Set input text",			"string"},
     {"text-file",	0, 0, G_OPTION_ARG_STRING,	&this->text_file,		"Set input text file-name\n\n    If no text is provided, standard input is used for input.\n",		"filename"},
+    {"unicodes",      'u', 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_unicodes,		"Set input Unicode codepoints",		"list of hex numbers"},
     {"text-before",	0, 0, G_OPTION_ARG_STRING,	&this->text_before,		"Set text context before each line",	"string"},
     {"text-after",	0, 0, G_OPTION_ARG_STRING,	&this->text_after,		"Set text context after each line",	"string"},
-    {NULL}
+    {nullptr}
   };
   parser->add_group (entries,
 		     "text",
@@ -504,7 +595,7 @@
 {
   const char *text;
 
-  if (NULL == supported_formats)
+  if (nullptr == supported_formats)
     text = "Set output serialization format";
   else
   {
@@ -516,9 +607,9 @@
 
   GOptionEntry entries[] =
   {
-    {"output-file",	0, 0, G_OPTION_ARG_STRING,	&this->output_file,		"Set output file-name (default: stdout)","filename"},
-    {"output-format",	0, 0, G_OPTION_ARG_STRING,	&this->output_format,		text,					"format"},
-    {NULL}
+    {"output-file",   'o', 0, G_OPTION_ARG_STRING,	&this->output_file,		"Set output file-name (default: stdout)","filename"},
+    {"output-format", 'O', 0, G_OPTION_ARG_STRING,	&this->output_format,		text,					"format"},
+    {nullptr}
   };
   parser->add_group (entries,
 		     "output",
@@ -535,7 +626,7 @@
   if (font)
     return font;
 
-  hb_blob_t *blob = NULL;
+  hb_blob_t *blob = nullptr;
 
   /* Create the blob */
   {
@@ -551,7 +642,7 @@
 
     if (0 == strcmp (font_file, "-")) {
       /* read it */
-      GString *gs = g_string_new (NULL);
+      GString *gs = g_string_new (nullptr);
       char buf[BUFSIZ];
 #if defined(_WIN32) || defined(__CYGWIN__)
       setmode (fileno (stdin), O_BINARY);
@@ -569,7 +660,7 @@
       destroy = (hb_destroy_func_t) g_free;
       mm = HB_MEMORY_MODE_WRITABLE;
     } else {
-      GError *error = NULL;
+      GError *error = nullptr;
       GMappedFile *mf = g_mapped_file_new (font_file, false, &error);
       if (mf) {
 	font_data = g_mapped_file_get_contents (mf);
@@ -588,7 +679,7 @@
 	/* GMappedFile is buggy, it doesn't fail if file isn't regular.
 	 * Try reading.
 	 * https://bugzilla.gnome.org/show_bug.cgi?id=659212 */
-        GError *error = NULL;
+        GError *error = nullptr;
 	gsize l;
 	if (g_file_get_contents (font_file, &font_data, &l, &error)) {
 	  len = l;
@@ -620,6 +711,9 @@
   if (font_size_y == FONT_SIZE_UPEM)
     font_size_y = hb_face_get_upem (face);
 
+  hb_font_set_ppem (font, x_ppem, y_ppem);
+  hb_font_set_ptem (font, ptem);
+
   int scale_x = (int) scalbnf (font_size_x, subpixel_bits);
   int scale_y = (int) scalbnf (font_size_y, subpixel_bits);
   hb_font_set_scale (font, scale_x, scale_y);
@@ -627,7 +721,7 @@
 
   hb_font_set_variations (font, variations, num_variations);
 
-  void (*set_font_funcs) (hb_font_t *) = NULL;
+  void (*set_font_funcs) (hb_font_t *) = nullptr;
   if (!font_funcs)
   {
     set_font_funcs = supported_font_funcs[0].func;
@@ -642,7 +736,7 @@
       }
     if (!set_font_funcs)
     {
-      GString *s = g_string_new (NULL);
+      GString *s = g_string_new (nullptr);
       for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++)
       {
         if (i)
@@ -673,7 +767,7 @@
 
     if (!line_len) {
       *len = 0;
-      return NULL;
+      return nullptr;
     }
 
     const char *ret = line;
@@ -706,9 +800,15 @@
       fail (false, "Failed opening text file `%s': %s",
 	    text_file, strerror (errno));
 
-    gs = g_string_new (NULL);
+    gs = g_string_new (nullptr);
   }
 
+#ifdef HAVE_SETLINEBUF
+  setlinebuf (fp);
+#else
+  setvbuf(fp, NULL, _IOLBF, BUFSIZ);
+#endif
+
   g_string_set_size (gs, 0);
   char buf[BUFSIZ];
   while (fgets (buf, sizeof (buf), fp)) {
@@ -724,7 +824,7 @@
     fail (false, "Failed reading text: %s",
 	  strerror (errno));
   *len = gs->len;
-  return !*len && feof (fp) ? NULL : gs->str;
+  return !*len && feof (fp) ? nullptr : gs->str;
 }
 
 
@@ -746,6 +846,12 @@
     fail (false, "Cannot open output file `%s': %s",
 	  g_filename_display_name (output_file), strerror (errno));
 
+#ifdef HAVE_SETLINEBUF
+  setlinebuf (fp);
+#else
+  setvbuf(fp, NULL, _IOLBF, BUFSIZ);
+#endif
+
   return fp;
 }
 
@@ -760,24 +866,41 @@
   return true;
 }
 
+static gboolean
+parse_ned (const char *name G_GNUC_UNUSED,
+	   const char *arg G_GNUC_UNUSED,
+	   gpointer    data G_GNUC_UNUSED,
+	   GError    **error G_GNUC_UNUSED)
+{
+  format_options_t *format_opts = (format_options_t *) data;
+  format_opts->show_clusters = format_opts->show_advances = false;
+  return true;
+}
+
 void
 format_options_t::add_options (option_parser_t *parser)
 {
   GOptionEntry entries[] =
   {
-    {"show-text",	0, 0, G_OPTION_ARG_NONE,	&this->show_text,		"Prefix each line of output with its corresponding input text",		NULL},
-    {"show-unicode",	0, 0, G_OPTION_ARG_NONE,	&this->show_unicode,		"Prefix each line of output with its corresponding input codepoint(s)",	NULL},
-    {"show-line-num",	0, 0, G_OPTION_ARG_NONE,	&this->show_line_num,		"Prefix each line of output with its corresponding input line number",	NULL},
-    {"verbose",		0, G_OPTION_FLAG_NO_ARG,
-			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_verbose,	"Prefix each line of output with all of the above",			NULL},
+    {"show-text",	0, 0, G_OPTION_ARG_NONE,	&this->show_text,		"Prefix each line of output with its corresponding input text",		nullptr},
+    {"show-unicode",	0, 0, G_OPTION_ARG_NONE,	&this->show_unicode,		"Prefix each line of output with its corresponding input codepoint(s)",	nullptr},
+    {"show-line-num",	0, 0, G_OPTION_ARG_NONE,	&this->show_line_num,		"Prefix each line of output with its corresponding input line number",	nullptr},
+    {"verbose",	      'v', G_OPTION_FLAG_NO_ARG,
+			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_verbose,	"Prefix each line of output with all of the above",			nullptr},
     {"no-glyph-names",	0, G_OPTION_FLAG_REVERSE,
-			      G_OPTION_ARG_NONE,	&this->show_glyph_names,	"Output glyph indices instead of names",				NULL},
+			      G_OPTION_ARG_NONE,	&this->show_glyph_names,	"Output glyph indices instead of names",				nullptr},
     {"no-positions",	0, G_OPTION_FLAG_REVERSE,
-			      G_OPTION_ARG_NONE,	&this->show_positions,		"Do not output glyph positions",					NULL},
+			      G_OPTION_ARG_NONE,	&this->show_positions,		"Do not output glyph positions",					nullptr},
+    {"no-advances",	0, G_OPTION_FLAG_REVERSE,
+			      G_OPTION_ARG_NONE,	&this->show_advances,		"Do not output glyph advances",						nullptr},
     {"no-clusters",	0, G_OPTION_FLAG_REVERSE,
-			      G_OPTION_ARG_NONE,	&this->show_clusters,		"Do not output cluster indices",					NULL},
-    {"show-extents",	0, 0, G_OPTION_ARG_NONE,	&this->show_extents,		"Output glyph extents",							NULL},
-    {NULL}
+			      G_OPTION_ARG_NONE,	&this->show_clusters,		"Do not output cluster indices",					nullptr},
+    {"show-extents",	0, 0, G_OPTION_ARG_NONE,	&this->show_extents,		"Output glyph extents",							nullptr},
+    {"show-flags",	0, 0, G_OPTION_ARG_NONE,	&this->show_flags,		"Output glyph flags",							nullptr},
+    {"ned",	      'v', G_OPTION_FLAG_NO_ARG,
+			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_ned,		"No Extra Data; Do not output clusters or advances",			nullptr},
+    {"trace",	      'V', 0, G_OPTION_ARG_NONE,	&this->trace,			"Output interim shaping results",					nullptr},
+    {nullptr}
   };
   parser->add_group (entries,
 		     "output-syntax",
@@ -794,7 +917,7 @@
 				     GString     *gs)
 {
   unsigned int num_glyphs = hb_buffer_get_length (buffer);
-  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
 
   g_string_append_c (gs, '<');
   for (unsigned int i = 0; i < num_glyphs; i++)
@@ -818,7 +941,8 @@
   unsigned int num_glyphs = hb_buffer_get_length (buffer);
   unsigned int start = 0;
 
-  while (start < num_glyphs) {
+  while (start < num_glyphs)
+  {
     char buf[1024];
     unsigned int consumed;
     start += hb_buffer_serialize_glyphs (buffer, start, num_glyphs,
@@ -845,7 +969,8 @@
 					    hb_font_t    *font,
 					    GString      *gs)
 {
-  if (show_text) {
+  if (show_text)
+  {
     serialize_line_no (line_no, gs);
     g_string_append_c (gs, '(');
     g_string_append_len (gs, text, text_len);
@@ -853,7 +978,8 @@
     g_string_append_c (gs, '\n');
   }
 
-  if (show_unicode) {
+  if (show_unicode)
+  {
     serialize_line_no (line_no, gs);
     serialize_unicode (buffer, gs);
     g_string_append_c (gs, '\n');
@@ -861,11 +987,12 @@
 }
 void
 format_options_t::serialize_message (unsigned int  line_no,
+				     const char   *type,
 				     const char   *msg,
 				     GString      *gs)
 {
   serialize_line_no (line_no, gs);
-  g_string_append_printf (gs, "%s", msg);
+  g_string_append_printf (gs, "%s: %s", type, msg);
   g_string_append_c (gs, '\n');
 }
 void
diff --git a/util/options.hh b/util/options.hh
index 9ed4fd0..cfbbade 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -27,15 +27,13 @@
 #ifndef OPTIONS_HH
 #define OPTIONS_HH
 
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include "hb-private.hh"
 
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
 #include <stdio.h>
+#include <assert.h>
 #include <math.h>
 #include <locale.h>
 #include <errno.h>
@@ -58,35 +56,8 @@
 # define g_mapped_file_unref g_mapped_file_free
 #endif
 
-
-/* A few macros copied from hb-private.hh. */
-
-#if __GNUC__ >= 4
-#define HB_UNUSED	__attribute__((unused))
-#else
-#define HB_UNUSED
-#endif
-
-#undef MIN
-template <typename Type> static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
-
-#undef MAX
-template <typename Type> static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
-
-#undef  ARRAY_LENGTH
-template <typename Type, unsigned int n>
-static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; }
-/* A const version, but does not detect erratically being called on pointers. */
-#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
-
-#define _ASSERT_STATIC1(_line, _cond)	HB_UNUSED typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1]
-#define _ASSERT_STATIC0(_line, _cond)	_ASSERT_STATIC1 (_line, (_cond))
-#define ASSERT_STATIC(_cond)		_ASSERT_STATIC0 (__LINE__, (_cond))
-
-
 void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN G_GNUC_PRINTF (2, 3);
 
-
 extern hb_bool_t debug;
 
 struct option_group_t
@@ -110,7 +81,7 @@
   }
   ~option_parser_t (void) {
     g_option_context_free (context);
-    g_ptr_array_foreach (to_free, (GFunc) g_free, NULL);
+    g_ptr_array_foreach (to_free, (GFunc) g_free, nullptr);
     g_ptr_array_free (to_free, TRUE);
   }
 
@@ -150,8 +121,8 @@
 {
   view_options_t (option_parser_t *parser) {
     annotate = false;
-    fore = NULL;
-    back = NULL;
+    fore = nullptr;
+    back = nullptr;
     line_space = 0;
     margin.t = margin.r = margin.b = margin.l = DEFAULT_MARGIN;
 
@@ -179,14 +150,15 @@
 {
   shape_options_t (option_parser_t *parser)
   {
-    direction = language = script = NULL;
-    bot = eot = preserve_default_ignorables = false;
-    features = NULL;
+    direction = language = script = nullptr;
+    bot = eot = preserve_default_ignorables = remove_default_ignorables = false;
+    features = nullptr;
     num_features = 0;
-    shapers = NULL;
+    shapers = nullptr;
     utf8_clusters = false;
     cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT;
     normalize_glyphs = false;
+    verify = false;
     num_iterations = 1;
 
     add_options (parser);
@@ -207,14 +179,26 @@
     hb_buffer_set_direction (buffer, hb_direction_from_string (direction, -1));
     hb_buffer_set_script (buffer, hb_script_from_string (script, -1));
     hb_buffer_set_language (buffer, hb_language_from_string (language, -1));
-    hb_buffer_set_flags (buffer, (hb_buffer_flags_t) (HB_BUFFER_FLAG_DEFAULT |
-			 (bot ? HB_BUFFER_FLAG_BOT : 0) |
-			 (eot ? HB_BUFFER_FLAG_EOT : 0) |
-			 (preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0)));
+    hb_buffer_set_flags (buffer, (hb_buffer_flags_t)
+				 (HB_BUFFER_FLAG_DEFAULT |
+				  (bot ? HB_BUFFER_FLAG_BOT : 0) |
+				  (eot ? HB_BUFFER_FLAG_EOT : 0) |
+				  (preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0) |
+				  (remove_default_ignorables ? HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES : 0) |
+				  0));
     hb_buffer_set_cluster_level (buffer, cluster_level);
     hb_buffer_guess_segment_properties (buffer);
   }
 
+  static void copy_buffer_properties (hb_buffer_t *dst, hb_buffer_t *src)
+  {
+    hb_segment_properties_t props;
+    hb_buffer_get_segment_properties (src, &props);
+    hb_buffer_set_segment_properties (dst, &props);
+    hb_buffer_set_flags (dst, hb_buffer_get_flags (src));
+    hb_buffer_set_cluster_level (dst, hb_buffer_get_cluster_level (src));
+  }
+
   void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len,
 			const char *text_before, const char *text_after)
   {
@@ -232,7 +216,7 @@
       /* Reset cluster values to refer to Unicode character index
        * instead of UTF-8 index. */
       unsigned int num_glyphs = hb_buffer_get_length (buffer);
-      hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+      hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
       for (unsigned int i = 0; i < num_glyphs; i++)
       {
 	info->cluster = i;
@@ -243,12 +227,188 @@
     setup_buffer (buffer);
   }
 
-  hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer)
+  hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer, const char **error=nullptr)
   {
-    hb_bool_t res = hb_shape_full (font, buffer, features, num_features, shapers);
+    hb_buffer_t *text_buffer = nullptr;
+    if (verify)
+    {
+      text_buffer = hb_buffer_create ();
+      hb_buffer_append (text_buffer, buffer, 0, -1);
+    }
+
+    if (!hb_shape_full (font, buffer, features, num_features, shapers))
+    {
+      if (error)
+        *error = "all shapers failed.";
+      goto fail;
+    }
+
     if (normalize_glyphs)
       hb_buffer_normalize_glyphs (buffer);
-    return res;
+
+    if (verify && !verify_buffer (buffer, text_buffer, font, error))
+      goto fail;
+
+    if (text_buffer)
+      hb_buffer_destroy (text_buffer);
+
+    return true;
+
+  fail:
+    if (text_buffer)
+      hb_buffer_destroy (text_buffer);
+
+    return false;
+  }
+
+  bool verify_buffer (hb_buffer_t  *buffer,
+		      hb_buffer_t  *text_buffer,
+		      hb_font_t    *font,
+		      const char  **error=nullptr)
+  {
+    if (!verify_buffer_monotone (buffer, error))
+      return false;
+    if (!verify_buffer_safe_to_break (buffer, text_buffer, font, error))
+      return false;
+    return true;
+  }
+
+  bool verify_buffer_monotone (hb_buffer_t *buffer, const char **error=nullptr)
+  {
+    /* Check that clusters are monotone. */
+    if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES ||
+	cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
+    {
+      bool is_forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer));
+
+      unsigned int num_glyphs;
+      hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs);
+
+      for (unsigned int i = 1; i < num_glyphs; i++)
+	if (info[i-1].cluster != info[i].cluster &&
+	    (info[i-1].cluster < info[i].cluster) != is_forward)
+	{
+	  if (error)
+	    *error = "clusters are not monotone.";
+	  return false;
+	}
+    }
+
+    return true;
+  }
+
+  bool verify_buffer_safe_to_break (hb_buffer_t  *buffer,
+				    hb_buffer_t  *text_buffer,
+				    hb_font_t    *font,
+				    const char  **error=nullptr)
+  {
+    if (cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES &&
+	cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
+    {
+      /* Cannot perform this check without monotone clusters.
+       * Then again, unsafe-to-break flag is much harder to use without
+       * monotone clusters. */
+      return true;
+    }
+
+    /* Check that breaking up shaping at safe-to-break is indeed safe. */
+
+    hb_buffer_t *fragment = hb_buffer_create ();
+    hb_buffer_t *reconstruction = hb_buffer_create ();
+    copy_buffer_properties (reconstruction, buffer);
+
+    unsigned int num_glyphs;
+    hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs);
+
+    unsigned int num_chars;
+    hb_glyph_info_t *text = hb_buffer_get_glyph_infos (text_buffer, &num_chars);
+
+    /* Chop text and shape fragments. */
+    bool forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer));
+    unsigned int start = 0;
+    unsigned int text_start = forward ? 0 : num_chars;
+    unsigned int text_end = text_start;
+    for (unsigned int end = 1; end < num_glyphs + 1; end++)
+    {
+      if (end < num_glyphs &&
+	  (info[end].cluster == info[end-1].cluster ||
+	   info[end-(forward?0:1)].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK))
+	  continue;
+
+      /* Shape segment corresponding to glyphs start..end. */
+      if (end == num_glyphs)
+      {
+        if (forward)
+	  text_end = num_chars;
+	else
+	  text_start = 0;
+      }
+      else
+      {
+	if (forward)
+	{
+	  unsigned int cluster = info[end].cluster;
+	  while (text_end < num_chars && text[text_end].cluster < cluster)
+	    text_end++;
+	}
+	else
+	{
+	  unsigned int cluster = info[end - 1].cluster;
+	  while (text_start && text[text_start - 1].cluster >= cluster)
+	    text_start--;
+	}
+      }
+      assert (text_start < text_end);
+
+      if (0)
+	printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end);
+
+      hb_buffer_clear_contents (fragment);
+      copy_buffer_properties (fragment, buffer);
+
+      /* TODO: Add pre/post context text. */
+      hb_buffer_flags_t flags = hb_buffer_get_flags (fragment);
+      if (0 < text_start)
+        flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_BOT);
+      if (text_end < num_chars)
+        flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_EOT);
+      hb_buffer_set_flags (fragment, flags);
+
+      hb_buffer_append (fragment, text_buffer, text_start, text_end);
+      if (!hb_shape_full (font, fragment, features, num_features, shapers))
+      {
+	if (error)
+	  *error = "all shapers failed while shaping fragment.";
+	hb_buffer_destroy (reconstruction);
+	hb_buffer_destroy (fragment);
+	return false;
+      }
+      hb_buffer_append (reconstruction, fragment, 0, -1);
+
+      start = end;
+      if (forward)
+	text_start = text_end;
+      else
+	text_end = text_start;
+    }
+
+    bool ret = true;
+    hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0);
+    if (diff)
+    {
+      if (error)
+	*error = "Safe-to-break test failed.";
+      ret = false;
+
+      /* Return the reconstructed result instead so it can be inspected. */
+      hb_buffer_set_length (buffer, 0);
+      hb_buffer_append (buffer, reconstruction, 0, -1);
+    }
+
+    hb_buffer_destroy (reconstruction);
+    hb_buffer_destroy (fragment);
+
+    return ret;
   }
 
   void shape_closure (const char *text, int text_len,
@@ -270,6 +430,7 @@
   hb_bool_t bot;
   hb_bool_t eot;
   hb_bool_t preserve_default_ignorables;
+  hb_bool_t remove_default_ignorables;
 
   hb_feature_t *features;
   unsigned int num_features;
@@ -277,6 +438,7 @@
   hb_bool_t utf8_clusters;
   hb_buffer_cluster_level_t cluster_level;
   hb_bool_t normalize_glyphs;
+  hb_bool_t verify;
   unsigned int num_iterations;
 };
 
@@ -287,16 +449,19 @@
 		  int default_font_size_,
 		  unsigned int subpixel_bits_)
   {
-    variations = NULL;
+    variations = nullptr;
     num_variations = 0;
     default_font_size = default_font_size_;
+    x_ppem = 0;
+    y_ppem = 0;
+    ptem = .0;
     subpixel_bits = subpixel_bits_;
-    font_file = NULL;
+    font_file = nullptr;
     face_index = 0;
     font_size_x = font_size_y = default_font_size;
-    font_funcs = NULL;
+    font_funcs = nullptr;
 
-    font = NULL;
+    font = nullptr;
 
     add_options (parser);
   }
@@ -316,6 +481,9 @@
   hb_variation_t *variations;
   unsigned int num_variations;
   int default_font_size;
+  int x_ppem;
+  int y_ppem;
+  double ptem;
   unsigned int subpixel_bits;
   mutable double font_size_x;
   mutable double font_size_y;
@@ -329,15 +497,15 @@
 struct text_options_t : option_group_t
 {
   text_options_t (option_parser_t *parser) {
-    text_before = NULL;
-    text_after = NULL;
+    text_before = nullptr;
+    text_after = nullptr;
 
-    text = NULL;
-    text_file = NULL;
+    text = nullptr;
+    text_file = nullptr;
 
-    fp = NULL;
-    gs = NULL;
-    line = NULL;
+    fp = nullptr;
+    gs = nullptr;
+    line = nullptr;
     line_len = (unsigned int) -1;
 
     add_options (parser);
@@ -380,13 +548,13 @@
 struct output_options_t : option_group_t
 {
   output_options_t (option_parser_t *parser,
-		    const char **supported_formats_ = NULL) {
-    output_file = NULL;
-    output_format = NULL;
+		    const char **supported_formats_ = nullptr) {
+    output_file = nullptr;
+    output_format = nullptr;
     supported_formats = supported_formats_;
     explicit_output_format = false;
 
-    fp = NULL;
+    fp = nullptr;
 
     add_options (parser);
   }
@@ -414,7 +582,7 @@
     }
 
     if (output_file && 0 == strcmp (output_file, "-"))
-      output_file = NULL; /* STDOUT */
+      output_file = nullptr; /* STDOUT */
   }
 
   FILE *get_file_handle (void);
@@ -432,11 +600,14 @@
   format_options_t (option_parser_t *parser) {
     show_glyph_names = true;
     show_positions = true;
+    show_advances = true;
     show_clusters = true;
     show_text = false;
     show_unicode = false;
     show_line_num = false;
     show_extents = false;
+    show_flags = false;
+    trace = false;
 
     add_options (parser);
   }
@@ -459,6 +630,7 @@
 				 hb_font_t    *font,
 				 GString      *gs);
   void serialize_message (unsigned int  line_no,
+			  const char   *type,
 			  const char   *msg,
 			  GString      *gs);
   void serialize_buffer_of_glyphs (hb_buffer_t  *buffer,
@@ -473,11 +645,14 @@
 
   hb_bool_t show_glyph_names;
   hb_bool_t show_positions;
+  hb_bool_t show_advances;
   hb_bool_t show_clusters;
   hb_bool_t show_text;
   hb_bool_t show_unicode;
   hb_bool_t show_line_num;
   hb_bool_t show_extents;
+  hb_bool_t show_flags;
+  hb_bool_t trace;
 };
 
 /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */
diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh
index 422c8cd..fa419f1 100644
--- a/util/shape-consumer.hh
+++ b/util/shape-consumer.hh
@@ -24,11 +24,12 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "options.hh"
-
 #ifndef HB_SHAPE_CONSUMER_HH
 #define HB_SHAPE_CONSUMER_HH
 
+#include "hb-private.hh"
+#include "options.hh"
+
 
 template <typename output_t>
 struct shape_consumer_t
@@ -37,16 +38,19 @@
 		  : failed (false),
 		    shaper (parser),
 		    output (parser),
-		    font (NULL) {}
+		    font (nullptr),
+		    buffer (nullptr) {}
 
-  void init (const font_options_t *font_opts)
+  void init (hb_buffer_t  *buffer_,
+	     const font_options_t *font_opts)
   {
     font = hb_font_reference (font_opts->get_font ());
-    output.init (font_opts);
     failed = false;
+    buffer = hb_buffer_reference (buffer_);
+
+    output.init (buffer, font_opts);
   }
-  void consume_line (hb_buffer_t  *buffer,
-		     const char   *text,
+  void consume_line (const char   *text,
 		     unsigned int  text_len,
 		     const char   *text_before,
 		     const char   *text_after)
@@ -55,14 +59,19 @@
 
     for (unsigned int n = shaper.num_iterations; n; n--)
     {
+      const char *error = nullptr;
+
       shaper.populate_buffer (buffer, text, text_len, text_before, text_after);
       if (n == 1)
 	output.consume_text (buffer, text, text_len, shaper.utf8_clusters);
-      if (!shaper.shape (font, buffer)) {
+      if (!shaper.shape (font, buffer, &error))
+      {
 	failed = true;
-	hb_buffer_set_length (buffer, 0);
-	output.shape_failed (buffer, text, text_len, shaper.utf8_clusters);
-	return;
+	output.error (error);
+	if (hb_buffer_get_content_type (buffer) == HB_BUFFER_CONTENT_TYPE_GLYPHS)
+	  break;
+	else
+	  return;
       }
     }
 
@@ -70,9 +79,11 @@
   }
   void finish (const font_options_t *font_opts)
   {
-    output.finish (font_opts);
+    output.finish (buffer, font_opts);
     hb_font_destroy (font);
-    font = NULL;
+    font = nullptr;
+    hb_buffer_destroy (buffer);
+    buffer = nullptr;
   }
 
   public:
@@ -83,6 +94,7 @@
   output_t output;
 
   hb_font_t *font;
+  hb_buffer_t *buffer;
 };
 
 
diff --git a/util/view-cairo.hh b/util/view-cairo.hh
index f55d4bb..d28c3cd 100644
--- a/util/view-cairo.hh
+++ b/util/view-cairo.hh
@@ -24,12 +24,13 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "options.hh"
-#include "helper-cairo.hh"
-
 #ifndef VIEW_CAIRO_HH
 #define VIEW_CAIRO_HH
 
+#include "hb-private.hh"
+#include "options.hh"
+#include "helper-cairo.hh"
+
 
 struct view_cairo_t
 {
@@ -43,7 +44,7 @@
       cairo_debug_reset_static_data ();
   }
 
-  void init (const font_options_t *font_opts)
+  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;
@@ -57,12 +58,9 @@
 		     hb_bool_t     utf8_clusters)
   {
   }
-  void shape_failed (hb_buffer_t  *buffer,
-		     const char   *text,
-		     unsigned int  text_len,
-		     hb_bool_t     utf8_clusters)
+  void error (const char *message)
   {
-    fail (false, "all shapers failed");
+    g_printerr ("%s: %s\n", g_get_prgname (), message);
   }
   void consume_glyphs (hb_buffer_t  *buffer,
 		       const char   *text,
@@ -74,7 +72,7 @@
     helper_cairo_line_from_buffer (&l, buffer, text, text_len, scale_bits, utf8_clusters);
     g_array_append_val (lines, l);
   }
-  void finish (const font_options_t *font_opts)
+  void finish (hb_buffer_t *buffer, const font_options_t *font_opts)
   {
     render (font_opts);
 
diff --git a/win32/Makefile.am b/win32/Makefile.am
deleted file mode 100644
index 63ba468..0000000
--- a/win32/Makefile.am
+++ /dev/null
@@ -1,16 +0,0 @@
-EXTRA_DIST = \
-	build-rules-msvc.mak \
-	config-msvc.mak \
-	config.h.win32 \
-	create-lists.bat \
-	create-lists-msvc.mak \
-	detectenv-msvc.mak \
-	generate-msvc.mak \
-	hb-introspection-msvc.mak \
-	info-msvc.mak \
-	install.mak \
-	introspection-msvc.mak \
-	Makefile.vc \
-	README.txt
-
--include $(top_srcdir)/git.mk
diff --git a/win32/Makefile.vc b/win32/Makefile.vc
deleted file mode 100644
index fdde7a7..0000000
--- a/win32/Makefile.vc
+++ /dev/null
@@ -1,52 +0,0 @@
-# NMake Makefile for building HarfBuzz as a DLL on Windows
-
-# The items below this line should not be changed, unless one is maintaining
-# the NMake Makefiles.  Customizations can be done in the following NMake Makefile
-# portions (please see comments in the these files to see what can be customized):
-#
-# detectenv-msvc.mak
-# config-msvc.mak
-
-!include detectenv-msvc.mak
-
-# Include the Makefile portions with the source listings
-!include ..\src\Makefile.sources
-!include ..\src\hb-ucdn\Makefile.sources
-!include ..\util\Makefile.sources
-
-# Include the Makefile portion that enables features based on user input
-!include config-msvc.mak
-
-!if "$(VALID_CFGSET)" == "TRUE"
-
-# Include the Makefile portion to convert the source and header lists
-# into the lists we need for compilation and introspection
-!include create-lists-msvc.mak
-
-all: $(HB_LIBS) $(HB_UTILS) $(EXTRA_TARGETS) all-build-info
-
-tests: all $(HB_TESTS)
-
-# Include the build rules for sources, DLLs and executables
-!include build-rules-msvc.mak
-
-# Include the rules for build directory creation and code generation
-!include generate-msvc.mak
-
-# Generate the introspection files
-
-!if "$(INTROSPECTION)" == "1"
-# Include the rules for building the introspection files
-!include introspection-msvc.mak
-!include hb-introspection-msvc.mak
-!endif
-
-!include install.mak
-
-!else
-all: help
-	@echo You need to specify a valid configuration, via
-	@echo CFG=release or CFG=debug
-!endif
-
-!include info-msvc.mak
diff --git a/win32/README.txt b/win32/README.txt
deleted file mode 100644
index af0dc15..0000000
--- a/win32/README.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-Instructions for building HarfBuzz on Visual Studio

-===================================================

-Building the HarfBuzz DLL on Windows is now also supported using Visual Studio

-versions 2008 through 2015, in both 32-bit and 64-bit (x64) flavors, via NMake

-Makefiles.

-

-The following are instructions for performing such a build, as there is a

-number of build configurations supported for the build.  Note that for all

-build configurations, the OpenType and Simple TrueType layout (fallback)

-backends are enabled, and this is the base configuration that is built if no

-options (see below) are specified.  A 'clean' target is provided-it is recommended

-that one cleans the build and redo the build if any configuration option changed.

-An 'install' target is also provided to copy the built items in their appropriate

-locations under $(PREFIX), which is described below.

-

-Invoke the build by issuing the command:

-nmake /f Makefile.vc CFG=[release|debug] [PREFIX=...] <option1=1 option2=1 ...>

-where:

-

-CFG: Required.  Choose from a release or debug build.  Note that 

-     all builds generate a .pdb file for each .dll and .exe built--this refers

-     to the C/C++ runtime that the build uses.

-

-PREFIX: Optional.  Base directory of where the third-party headers, libraries

-        and needed tools can be found, i.e. headers in $(PREFIX)\include,

-        libraries in $(PREFIX)\lib and tools in $(PREFIX)\bin.  If not

-        specified, $(PREFIX) is set as $(srcroot)\..\vs$(X)\$(platform), where

-        $(platform) is win32 for 32-bit builds or x64 for 64-bit builds, and

-        $(X) is the short version of the Visual Studio used, as follows:

-        2008: 9

-        2010: 10

-        2012: 11

-        2013: 12

-        2015: 14

-

-Explanation of options, set by <option>=1:

-------------------------------------------

-GLIB: Enable GLib support in HarfBuzz, which also uses the GLib unicode

-      callback instead of the bundled UCDN unicode callback.  This requires the

-      GLib libraries, and is required for building all tool and test programs.

-

-GOBJECT: Enable building the HarfBuzz-GObject DLL, and thus implies GLib

-         support.  This requires the GObject libraries and glib-mkenums script,

-         along with PERL to generate the enum sources and headers, which is

-         required for the build.

-

-INTROSPECTION: Enable build of introspection files, for making HarfBuzz

-               bindings for other programming languages available, such as

-               Python, available.  This requires the GObject-Introspection

-               libraries and tools, along with the Python interpretor that was

-               used during the build of GObject-Introspection.  Please see

-               $(srcroot)\README.python for more related details.  This implies

-               the build of the HarfBuzz-GObject DLL, along with GLib support.

-

-FREETYPE: Enable the FreeType font callbacks.  Requires the FreeType2 library.

-

-CAIRO: Enable Cairo support.  Requires the Cairo library.

-

-CAIRO_FT: Enable the build of the hb-view tool, which makes use of Cairo, and

-          thus implies FreeType font callback support and Cairo support.

-          Requires Cairo libraries built with FreeType support.  Note that the

-          hb-view tool requires GLib support as well.

-

-GRAPHITE2: Enable the Graphite2 shaper, requires the SIL Graphite2 library.

-

-ICU: Enables the build of ICU Unicode functions. Requires the ICU libraries.
-

-UNISCRIBE: Enable Uniscribe platform shaper support.

-

-DIRECTWRITE: Enable DirectWrite platform shaper support,

-             requires a rather recent Windows SDK, and at least Windows Vista/

-             Server 2008 with SP2 and platform update.

-

-PYTHON: Full path to the Python interpretor to be used, if it is not in %PATH%.

-

-PERL: Full path to the PERL interpretor to be used, if it is not in %PATH%.

-

-LIBTOOL_DLL_NAME: Enable libtool-style DLL names.
\ No newline at end of file
diff --git a/win32/build-rules-msvc.mak b/win32/build-rules-msvc.mak
deleted file mode 100644
index bfe0286..0000000
--- a/win32/build-rules-msvc.mak
+++ /dev/null
@@ -1,127 +0,0 @@
-# NMake Makefile portion for compilation rules
-# Items in here should not need to be edited unless
-# one is maintaining the NMake build files.  The format
-# of NMake Makefiles here are different from the GNU
-# Makefiles.  Please see the comments about these formats.
-
-# Inference rules for compiling the .obj files.
-# Used for libs and programs with more than a single source file.
-# Format is as follows
-# (all dirs must have a trailing '\'):
-#
-# {$(srcdir)}.$(srcext){$(destdir)}.obj::
-# 	$(CC)|$(CXX) $(cflags) /Fo$(destdir) /c @<<
-# $<
-# <<
-{..\src\}.cc{$(CFG)\$(PLAT)\harfbuzz\}.obj::
-	$(CXX) $(CFLAGS) $(HB_DEFINES) $(HB_LIB_CFLAGS) /Fo$(CFG)\$(PLAT)\harfbuzz\ /c @<<
-$<
-<<
-
-{..\src\hb-ucdn\}.c{$(CFG)\$(PLAT)\harfbuzz\}.obj::
-	$(CC) $(CFLAGS) /Fo$(CFG)\$(PLAT)\harfbuzz\ /c @<<
-$<
-<<
-
-{..\util\}.cc{$(CFG)\$(PLAT)\util\}.obj::
-	$(CXX) $(CFLAGS) $(HB_DEFINES) $(HB_CFLAGS) /Fo$(CFG)\$(PLAT)\util\ /c @<<
-$<
-<<
-
-# Inference rules for building the test programs
-# Used for programs with a single source file.
-# Format is as follows
-# (all dirs must have a trailing '\'):
-#
-# {$(srcdir)}.$(srcext){$(destdir)}.exe::
-# 	$(CC)|$(CXX) $(cflags) $< /Fo$*.obj  /Fe$@ [/link $(linker_flags) $(dep_libs)]
-{..\src\}.cc{$(CFG)\$(PLAT)\}.exe:
-	$(CXX) $(CFLAGS) $(HB_DEFINES) $(HB_CFLAGS) $< /Fo$*.obj  /Fe$@ /link $(LDFLAGS) $(CFG)\$(PLAT)\harfbuzz.lib $(HB_TESTS_DEP_LIBS)
-
-{..\test\api\}.c{$(CFG)\$(PLAT)\}.exe:
-	$(CXX) $(CFLAGS) $(HB_DEFINES) $(HB_CFLAGS) /DSRCDIR="\"../../../test/api\"" $< /Fo$*.obj /Fe$@ /link $(LDFLAGS) $(CFG)\$(PLAT)\harfbuzz.lib $(HB_TESTS_DEP_LIBS)
-
-# Rules for building .lib files
-$(CFG)\$(PLAT)\harfbuzz.lib: $(HARFBUZZ_DLL_FILENAME).dll
-$(CFG)\$(PLAT)\harfbuzz-gobject.lib: $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll
-
-# Rules for linking DLLs
-# Format is as follows (the mt command is needed for MSVC 2005/2008 builds):
-# $(dll_name_with_path): $(dependent_libs_files_objects_and_items)
-#	link /DLL [$(linker_flags)] [$(dependent_libs)] [/def:$(def_file_if_used)] [/implib:$(lib_name_if_needed)] -out:$@ @<<
-# $(dependent_objects)
-# <<
-# 	@-if exist $@.manifest mt /manifest $@.manifest /outputresource:$@;2
-$(HARFBUZZ_DLL_FILENAME).dll: config.h $(harfbuzz_dll_OBJS) $(CFG)\$(PLAT)\harfbuzz
-	link /DLL $(LDFLAGS) $(HB_DEP_LIBS) /implib:$(CFG)\$(PLAT)\harfbuzz.lib -out:$@ @<<
-$(harfbuzz_dll_OBJS)
-<<
-	@-if exist $@.manifest mt /manifest $@.manifest /outputresource:$@;2
-
-$(HARFBUZZ_GOBJECT_DLL_FILENAME).dll: $(CFG)\$(PLAT)\harfbuzz.lib $(harfbuzz_gobject_OBJS) $(CFG)\$(PLAT)\harfbuzz-gobject
-	link /DLL $(LDFLAGS) $(CFG)\$(PLAT)\harfbuzz.lib $(HB_GOBJECT_DEP_LIBS) /implib:$(CFG)\$(PLAT)\harfbuzz-gobject.lib -out:$@ @<<
-$(harfbuzz_gobject_OBJS)
-<<
-	@-if exist $@.manifest mt /manifest $@.manifest /outputresource:$@;2
-
-# Rules for linking Executables
-# Format is as follows (the mt command is needed for MSVC 2005/2008 builds):
-# $(dll_name_with_path): $(dependent_libs_files_objects_and_items)
-#	link [$(linker_flags)] [$(dependent_libs)] -out:$@ @<<
-# $(dependent_objects)
-# <<
-# 	@-if exist $@.manifest mt /manifest $@.manifest /outputresource:$@;1
-$(CFG)\$(PLAT)\hb-view.exe: $(CFG)\$(PLAT)\harfbuzz.lib $(CFG)\$(PLAT)\util $(hb_view_OBJS)
-	link $(LDFLAGS) $(CFG)\$(PLAT)\harfbuzz.lib $(HB_UTILS_DEP_LIBS) -out:$@ @<<
-$(hb_view_OBJS)
-<<
-	@-if exist $@.manifest mt /manifest $@.manifest /outputresource:$@;1
-
-$(CFG)\$(PLAT)\hb-shape.exe: $(CFG)\$(PLAT)\harfbuzz.lib $(CFG)\$(PLAT)\util $(hb_shape_OBJS)
-	link $(LDFLAGS) $(CFG)\$(PLAT)\harfbuzz.lib $(HB_UTILS_DEP_LIBS) -out:$@ @<<
-$(hb_shape_OBJS)
-<<
-	@-if exist $@.manifest mt /manifest $@.manifest /outputresource:$@;1
-
-$(CFG)\$(PLAT)\hb-ot-shape-closure.exe: $(CFG)\$(PLAT)\harfbuzz.lib $(CFG)\$(PLAT)\util $(hb_ot_shape_closure_OBJS)
-	link $(LDFLAGS) $(CFG)\$(PLAT)\harfbuzz.lib $(HB_UTILS_DEP_LIBS) -out:$@ @<<
-$(hb_ot_shape_closure_OBJS)
-<<
-	@-if exist $@.manifest mt /manifest $@.manifest /outputresource:$@;1
-
-# Other .obj files requiring individual attention, that could not be covered by the inference rules.
-# Format is as follows (all dirs must have a trailing '\'):
-#
-# $(obj_file):
-# 	$(CC)|$(CXX) $(cflags) /Fo$(obj_destdir) /c @<<
-# $(srcfile)
-# <<
-$(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-structs.obj:	$(CFG)\$(PLAT)\harfbuzz-gobject $(HB_GOBJECT_ENUM_GENERATED_SOURCES)
-	$(CXX) $(CFLAGS) $(HB_DEFINES) $(HB_LIB_CFLAGS) /I$(CFG)\$(PLAT)\harfbuzz-gobject /Fo$(CFG)\$(PLAT)\harfbuzz-gobject\ /c @<<
-..\src\hb-gobject-structs.cc
-<<
-
-$(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.obj: $(CFG)\$(PLAT)\harfbuzz-gobject $(HB_GOBJECT_ENUM_GENERATED_SOURCES)
-	$(CXX) $(CFLAGS) $(HB_DEFINES) $(HB_LIB_CFLAGS) /I$(CFG)\$(PLAT)\harfbuzz-gobject /Fo$(CFG)\$(PLAT)\harfbuzz-gobject\ /c @<<
-$(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.cc
-<<
-
-clean:
-	@-if exist $(CFG)\$(PLAT)\HarfBuzz-0.0.typelib del /f /q $(CFG)\$(PLAT)\HarfBuzz-0.0.typelib
-	@-if exist $(CFG)\$(PLAT)\HarfBuzz-0.0.gir del /f /q $(CFG)\$(PLAT)\HarfBuzz-0.0.gir
-	@-if exist $(CFG)\$(PLAT)\hb_list del /f /q $(CFG)\$(PLAT)\hb_list
-	@-del /f /q $(CFG)\$(PLAT)\*.pdb
-	@-if exist $(CFG)\$(PLAT)\.exe.manifest del /f /q $(CFG)\$(PLAT)\*.exe.manifest
-	@-if exist $(CFG)\$(PLAT)\.exe del /f /q $(CFG)\$(PLAT)\*.exe
-	@-del /f /q $(CFG)\$(PLAT)\*.dll.manifest
-	@-del /f /q $(CFG)\$(PLAT)\*.dll
-	@-del /f /q $(CFG)\$(PLAT)\*.ilk
-	@-del /f /q $(CFG)\$(PLAT)\*.obj
-	@-if exist $(CFG)\$(PLAT)\util del /f /q $(CFG)\$(PLAT)\util\*.obj
-	@-if exist $(CFG)\$(PLAT)\harfbuzz-gobject del /f /q $(CFG)\$(PLAT)\harfbuzz-gobject\*.obj
-	@-del /f /q $(CFG)\$(PLAT)\harfbuzz\*.obj
-	@-rmdir /s /q $(CFG)\$(PLAT)
-	@-if exist $(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.h del $(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.h
-	@-if exist $(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.cc del $(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.cc
-	@-del vc$(VSVER)0.pdb
-	@-del config.h
diff --git a/win32/config-msvc.mak b/win32/config-msvc.mak
deleted file mode 100644
index 4cffaea..0000000
--- a/win32/config-msvc.mak
+++ /dev/null
@@ -1,217 +0,0 @@
-# NMake Makefile portion for enabling features for Windows builds
-
-# You may change these lines to customize the .lib files that will be linked to
-# Additional Libraries for building HarfBuzz-ICU
-# icudt.lib may be required for static ICU builds
-HB_ICU_DEP_LIBS = icuuc.lib
-
-# GLib is required for all utility programs and tests
-HB_GLIB_LIBS = glib-2.0.lib
-
-# Needed for building HarfBuzz-GObject
-HB_GOBJECT_DEP_LIBS = gobject-2.0.lib $(HB_GLIB_LIBS)
-
-# Freetype is needed for building FreeType support and hb-view
-!if "$(CFG)" == "debug"
-FREETYPE_LIB = freetyped.lib
-!else
-FREETYPE_LIB = freetype.lib
-!endif
-
-# Cairo is needed for building hb-view
-CAIRO_LIB = cairo.lib
-
-# Graphite2 is needed for building SIL Graphite2 support
-GRAPHITE2_LIB = graphite2.lib
-
-# Uniscribe is needed for Uniscribe shaping support
-UNISCRIBE_LIB = usp10.lib gdi32.lib rpcrt4.lib user32.lib
-
-# Directwrite is needed for DirectWrite shaping support
-DIRECTWRITE_LIB = dwrite.lib
-
-# Please do not change anything beneath this line unless maintaining the NMake Makefiles
-# Bare minimum features and sources built into HarfBuzz on Windows
-HB_DEFINES =
-HB_CFLAGS = /DHAVE_CONFIG_H
-HB_UCDN_CFLAGS = /I..\src\hb-ucdn
-HB_SOURCES =	\
-	$(HB_BASE_sources)		\
-	$(HB_FALLBACK_sources)	\
-	$(HB_OT_sources)
-
-HB_HEADERS =	\
-	$(HB_BASE_headers)		\
-	$(HB_NODIST_headers)	\
-	$(HB_OT_headers)
-
-# Minimal set of (system) libraries needed for the HarfBuzz DLL
-HB_DEP_LIBS =
-
-# We build the HarfBuzz DLL/LIB at least
-HB_LIBS = $(CFG)\$(PLAT)\harfbuzz.lib
-
-# Note: All the utility and test programs require GLib support to be present!
-HB_UTILS =
-HB_UTILS_DEP_LIBS = $(HB_GLIB_LIBS)
-HB_TESTS =
-HB_TESTS_DEP_LIBS = $(HB_GLIB_LIBS)
-
-# Use libtool-style DLL names, if desired
-!if "$(LIBTOOL_DLL_NAME)" == "1"
-HARFBUZZ_DLL_FILENAME = $(CFG)\$(PLAT)\libharfbuzz-0
-HARFBUZZ_GOBJECT_DLL_FILENAME = $(CFG)\$(PLAT)\libharfbuzz-gobject-0
-!else
-HARFBUZZ_DLL_FILENAME = $(CFG)\$(PLAT)\harfbuzz-vs$(VSVER)
-HARFBUZZ_GOBJECT_DLL_FILENAME = $(CFG)\$(PLAT)\harfbuzz-gobject-vs$(VSVER)
-!endif
-
-# Enable Introspection (enables HarfBuzz-Gobject as well)
-!if "$(INTROSPECTION)" == "1"
-GOBJECT = 1
-CHECK_PACKAGE = gobject-2.0
-EXTRA_TARGETS = $(CFG)\$(PLAT)\HarfBuzz-0.0.gir $(CFG)\$(PLAT)\HarfBuzz-0.0.typelib
-!else
-EXTRA_TARGETS =
-!endif
-
-# Enable HarfBuzz-GObject (enables GLib support as well)
-!if "$(GOBJECT)" == "1"
-GLIB = 1
-HB_LIBS =	\
-	$(HB_LIBS)	\
-	$(CFG)\$(PLAT)\harfbuzz-gobject.lib
-
-HB_GOBJECT_ENUM_GENERATED_SOURCES = \
-	$(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.cc	\
-	$(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.h
-
-!endif
-
-# Enable cairo-ft (enables cairo and freetype as well)
-!if "$(CAIRO_FT)" == "1"
-HB_DEFINES = $(HB_DEFINES) /DHAVE_CAIRO_FT=1
-CAIRO = 1
-FREETYPE = 1
-!if "$(GLIB)" == "1"
-HB_UTILS = \
-	$(HB_UTILS)	\
-	$(CFG)\$(PLAT)\hb-view.exe
-
-HB_UTILS_DEP_LIBS = $(HB_UTILS_DEP_LIBS) $(CAIRO_LIB) $(FREETYPE_LIB)
-!else
-!if [echo Warning: GLib support not enabled, hb-view not built]
-!endif
-!endif
-!endif
-
-# Enable cairo
-!if "$(CAIRO)" == "1"
-HB_DEFINES = $(HB_DEFINES) /DHAVE_CAIRO=1
-!endif
-
-# Enable freetype if desired
-!if "$(FREETYPE)" == "1"
-!if "$(FREETYPE_DIR)" != ""
-HB_CFLAGS = $(HB_CFLAGS) /I$(FREETYPE_DIR)
-!endif
-HB_DEFINES = $(HB_DEFINES) /DHAVE_FREETYPE=1
-HB_SOURCES = $(HB_SOURCES) $(HB_FT_sources)
-HB_HEADERS = $(HB_HEADERS) $(HB_FT_headers)
-HB_DEP_LIBS = $(HB_DEP_LIBS) $(FREETYPE_LIB)
-!endif
-
-# Enable graphite2 if desired
-!if "$(GRAPHITE2)" == "1"
-HB_DEFINES = $(HB_DEFINES) /DHAVE_GRAPHITE2=1
-HB_SOURCES = $(HB_SOURCES) $(HB_GRAPHITE2_sources)
-HB_HEADERS = $(HB_HEADERS) $(HB_GRAPHITE2_headers)
-HB_DEP_LIBS = $(HB_DEP_LIBS) $(GRAPHITE2_LIB)
-!endif
-
-# Enable GLib if desired
-!if "$(GLIB)" == "1"
-HB_DEFINES = $(HB_DEFINES) /DHAVE_GLIB=1
-HB_CFLAGS =	\
-	$(HB_CFLAGS)					\
-	/FImsvc_recommended_pragmas.h	\
-	/I$(PREFIX)\include\glib-2.0	\
-	/I$(PREFIX)\lib\glib-2.0\include
-
-HB_SOURCES = $(HB_SOURCES) $(HB_GLIB_sources)
-HB_HEADERS = $(HB_HEADERS) $(HB_GLIB_headers)
-HB_DEP_LIBS = $(HB_DEP_LIBS) $(HB_GLIB_LIBS)
-
-HB_UTILS = \
-	$(HB_UTILS)					\
-	$(CFG)\$(PLAT)\hb-shape.exe	\
-	$(CFG)\$(PLAT)\hb-ot-shape-closure.exe
-
-HB_TESTS = \
-	$(HB_TESTS)	\
-	$(CFG)\$(PLAT)\main.exe						\
-	$(CFG)\$(PLAT)\test.exe						\
-	$(CFG)\$(PLAT)\test-buffer-serialize.exe	\
-	$(CFG)\$(PLAT)\test-size-params.exe			\
-	$(CFG)\$(PLAT)\test-would-substitute.exe	\
-	$(CFG)\$(PLAT)\test-blob.exe				\
-	$(CFG)\$(PLAT)\test-buffer.exe				\
-	$(CFG)\$(PLAT)\test-common.exe				\
-	$(CFG)\$(PLAT)\test-font.exe				\
-	$(CFG)\$(PLAT)\test-object.exe				\
-	$(CFG)\$(PLAT)\test-set.exe					\
-	$(CFG)\$(PLAT)\test-shape.exe				\
-	$(CFG)\$(PLAT)\test-unicode.exe				\
-	$(CFG)\$(PLAT)\test-version.exe
-
-!elseif "$(ICU)" == "1"
-# use ICU for Unicode functions
-# and define some of the macros in GLib's msvc_recommended_pragmas.h
-# to reduce some unneeded build-time warnings
-HB_DEFINES = $(HB_DEFINES) /DHAVE_ICU=1 /DHAVE_ICU_BUILTIN=1
-HB_CFLAGS =	\
-	$(HB_CFLAGS)					\
-	/wd4244							\
-	/D_CRT_SECURE_NO_WARNINGS		\
-	/D_CRT_NONSTDC_NO_WARNINGS
-
-# We don't want ICU to re-define int8_t in VS 2008, will cause build breakage
-# as we define it in hb-common.h, and we ought to use the definitions there.
-!if "$(VSVER)" == "9"
-HB_CFLAGS =	$(HB_CFLAGS) /DU_HAVE_INT8_T
-!endif
-
-HB_SOURCES = $(HB_SOURCES) $(HB_ICU_sources)
-HB_HEADERS = $(HB_HEADERS) $(HB_ICU_headers)
-HB_DEP_LIBS = $(HB_DEP_LIBS) $(HB_ICU_DEP_LIBS)
-!endif
-
-!if "$(UCDN)" != "0"
-# Define some of the macros in GLib's msvc_recommended_pragmas.h
-# to reduce some unneeded build-time warnings
-HB_DEFINES = $(HB_DEFINES) /DHAVE_UCDN=1
-HB_CFLAGS =	\
-	$(HB_CFLAGS)					\
-	$(HB_UCDN_CFLAGS)				\
-	/wd4244							\
-	/D_CRT_SECURE_NO_WARNINGS		\
-	/D_CRT_NONSTDC_NO_WARNINGS
-
-HB_SOURCES = $(HB_SOURCES) $(LIBHB_UCDN_sources) $(HB_UCDN_sources)
-!endif
-
-!if "$(UNISCRIBE)" == "1"
-HB_CFLAGS = $(HB_CFLAGS) /DHAVE_UNISCRIBE
-HB_SOURCES = $(HB_SOURCES) $(HB_UNISCRIBE_sources)
-HB_HEADERS = $(HB_HEADERS) $(HB_UNISCRIBE_headers)
-HB_DEP_LIBS = $(HB_DEP_LIBS) $(UNISCRIBE_LIB)
-!endif
-
-!if "$(DIRECTWRITE)" == "1"
-HB_CFLAGS = $(HB_CFLAGS) /DHAVE_DIRECTWRITE
-HB_SOURCES = $(HB_SOURCES) $(HB_DIRECTWRITE_sources)
-HB_HEADERS = $(HB_HEADERS) $(HB_DIRECTWRITE_headers)
-HB_DEP_LIBS = $(HB_DEP_LIBS) $(DIRECTWRITE_LIB)
-!endif
-
-HB_LIB_CFLAGS = $(HB_CFLAGS) /DHB_EXTERN="__declspec (dllexport) extern"
diff --git a/win32/config.h.win32.in b/win32/config.h.win32.in
deleted file mode 100644
index d45cefb..0000000
--- a/win32/config.h.win32.in
+++ /dev/null
@@ -1,158 +0,0 @@
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* The normal alignment of `struct{char;}', in bytes. */
-#define ALIGNOF_STRUCT_CHAR__ 1
-
-/* Define to 1 if you have the `atexit' function. */
-#define HAVE_ATEXIT 1
-
-/* Have cairo graphics library */
-/* #undef HAVE_CAIRO */
-
-/* Have cairo-ft support in cairo graphics library */
-/* #undef HAVE_CAIRO_FT */
-
-/* Have Core Text backend */
-/* #undef HAVE_CORETEXT */
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-/* #undef HAVE_DLFCN_H */
-
-/* Have DirectWrite Library */
-/* #undef HAVE_DIRECTWRITE */
-
-/* Have simple TrueType Layout backend */
-#define HAVE_FALLBACK 1
-
-/* Have fontconfig library */
-/* #undef HAVE_FONTCONFIG */
-
-/* Have FreeType 2 library */
-/* #undef HAVE_FREETYPE */
-
-/* Define to 1 if you have the `getpagesize' function. */
-/* #undef HAVE_GETPAGESIZE */
-
-/* Have glib2 library */
-/* #undef HAVE_GLIB */
-
-/* Have gobject2 library */
-/* #undef HAVE_GOBJECT */
-
-/* Have Graphite2 library */
-/* #undef HAVE_GRAPHITE2 */
-
-/* Have ICU library */
-/* #undef HAVE_ICU */
-
-/* Have Intel __sync_* atomic primitives */
-/* #undef HAVE_INTEL_ATOMIC_PRIMITIVES */
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#if !defined (_MSC_VER) || (_MSC_VER >= 1800)
-#define HAVE_INTTYPES_H 1
-#endif
-
-/* Define to 1 if you have the `isatty' function. */
-#define HAVE_ISATTY 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the `mmap' function. */
-/* #undef HAVE_MMAP */
-
-/* Define to 1 if you have the `mprotect' function. */
-/* #undef HAVE_MPROTECT */
-
-/* Have native OpenType Layout backend */
-#define HAVE_OT 1
-
-/* Have POSIX threads */
-/* #undef HAVE_PTHREAD */
-
-/* Have PTHREAD_PRIO_INHERIT. */
-/* #undef HAVE_PTHREAD_PRIO_INHERIT */
-
-/* Define to 1 if you have the <sched.h> header file. */
-/* #undef HAVE_SCHED_H */
-
-/* Have sched_yield */
-/* #undef HAVE_SCHED_YIELD */
-
-/* Have Solaris __machine_*_barrier and atomic_* operations */
-/* #undef HAVE_SOLARIS_ATOMIC_OPS */
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#if !defined (_MSC_VER) || (_MSC_VER >= 1600)
-#define HAVE_STDINT_H 1
-#endif
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#ifndef _MSC_VER
-#define HAVE_STRINGS_H 1
-#endif
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the `sysconf' function. */
-/* #undef HAVE_SYSCONF */
-
-/* Define to 1 if you have the <sys/mman.h> header file. */
-/* #undef HAVE_SYS_MMAN_H */
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Have UCDN Unicode functions */
-#define HAVE_UCDN 1
-
-/* Have Uniscribe library */
-/* #undef HAVE_UNISCRIBE */
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#ifndef _MSC_VER
-#define HAVE_UNISTD_H 1
-#endif
-
-/* Define to 1 if you have the <usp10.h> header file. */
-#define HAVE_USP10_H 1
-
-/* Define to 1 if you have the <windows.h> header file. */
-#define HAVE_WINDOWS_H 1
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
-   */
-#define LT_OBJDIR ".libs/"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "@PACKAGE_NAME@"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "@PACKAGE_NAME@ @PACKAGE_VERSION@"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "@PACKAGE_TARNAME@"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL "@PACKAGE_URL@"
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "@PACKAGE_VERSION@"
-
-/* Define to necessary symbol if this constant uses a non-standard name on
-   your system. */
-/* #undef PTHREAD_CREATE_JOINABLE */
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
diff --git a/win32/create-lists-msvc.mak b/win32/create-lists-msvc.mak
deleted file mode 100644
index dbd2a57..0000000
--- a/win32/create-lists-msvc.mak
+++ /dev/null
@@ -1,138 +0,0 @@
-# Convert the source listing to object (.obj) listing in
-# another NMake Makefile module, include it, and clean it up.
-# This is a "fact-of-life" regarding NMake Makefiles...
-# This file does not need to be changed unless one is maintaining the NMake Makefiles
-
-# For those wanting to add things here:
-# To add a list, do the following:
-# # $(description_of_list)
-# if [call create-lists.bat header $(makefile_snippet_file) $(variable_name)]
-# endif
-#
-# if [call create-lists.bat file $(makefile_snippet_file) $(file_name)]
-# endif
-#
-# if [call create-lists.bat footer $(makefile_snippet_file)]
-# endif
-# ... (repeat the if [call ...] lines in the above order if needed)
-# !include $(makefile_snippet_file)
-#
-# (add the following after checking the entries in $(makefile_snippet_file) is correct)
-# (the batch script appends to $(makefile_snippet_file), you will need to clear the file unless the following line is added)
-#!if [del /f /q $(makefile_snippet_file)]
-#!endif
-
-# In order to obtain the .obj filename that is needed for NMake Makefiles to build DLLs/static LIBs or EXEs, do the following
-# instead when doing 'if [call create-lists.bat file $(makefile_snippet_file) $(file_name)]'
-# (repeat if there are multiple $(srcext)'s in $(source_list), ignore any headers):
-# !if [for %c in ($(source_list)) do @if "%~xc" == ".$(srcext)" @call create-lists.bat file $(makefile_snippet_file) $(intdir)\%~nc.obj]
-#
-# $(intdir)\%~nc.obj needs to correspond to the rules added in build-rules-msvc.mak
-# %~xc gives the file extension of a given file, %c in this case, so if %c is a.cc, %~xc means .cc
-# %~nc gives the file name of a given file without extension, %c in this case, so if %c is a.cc, %~nc means a
-
-NULL=
-
-# For HarfBuzz
-!if [call create-lists.bat header hb_objs.mak harfbuzz_dll_OBJS]
-!endif
-
-!if [for %c in ($(HB_SOURCES)) do @if "%~xc" == ".cc" @call create-lists.bat file hb_objs.mak ^$(CFG)\^$(PLAT)\harfbuzz\%~nc.obj]
-!endif
-
-!if [for %c in ($(HB_SOURCES)) do @if "%~xc" == ".c" @call create-lists.bat file hb_objs.mak ^$(CFG)\^$(PLAT)\harfbuzz\%~nc.obj]
-!endif
-
-!if [call create-lists.bat footer hb_objs.mak]
-!endif
-
-# For HarfBuzz-GObject
-!if "$(GOBJECT)" == "1"
-
-!if [call create-lists.bat header hb_objs.mak harfbuzz_gobject_OBJS]
-!endif
-
-!if [for %c in ($(HB_GOBJECT_sources) $(HB_GOBJECT_ENUM_sources)) do @if "%~xc" == ".cc" @call create-lists.bat file hb_objs.mak ^$(CFG)\^$(PLAT)\harfbuzz-gobject\%~nc.obj]
-!endif
-
-!if [call create-lists.bat footer hb_objs.mak]
-!endif
-!endif
-
-# For the utility programs (GLib support is required)
-!if "$(GLIB)" == "1"
-
-# For hb-view, Cairo-FT support is required
-!if "$(CAIRO_FT)" == "1"
-
-!if [call create-lists.bat header hb_objs.mak hb_view_OBJS]
-!endif
-
-!if [for %c in ($(HB_VIEW_sources)) do @if "%~xc" == ".cc" @call create-lists.bat file hb_objs.mak ^$(CFG)\^$(PLAT)\util\%~nc.obj]
-!endif
-
-!if [call create-lists.bat footer hb_objs.mak]
-!endif
-!endif
-
-# For hb-shape
-!if [call create-lists.bat header hb_objs.mak hb_shape_OBJS]
-!endif
-
-!if [for %c in ($(HB_SHAPE_sources)) do @if "%~xc" == ".cc" @call create-lists.bat file hb_objs.mak ^$(CFG)\^$(PLAT)\util\%~nc.obj]
-!endif
-
-!if [call create-lists.bat footer hb_objs.mak]
-!endif
-
-# For hb-ot-shape-closure
-
-!if [call create-lists.bat header hb_objs.mak hb_ot_shape_closure_OBJS]
-!endif
-
-!if [for %c in ($(HB_OT_SHAPE_CLOSURE_sources)) do @if "%~xc" == ".cc" @call create-lists.bat file hb_objs.mak ^$(CFG)\^$(PLAT)\util\%~nc.obj]
-!endif
-
-!if [call create-lists.bat footer hb_objs.mak]
-!endif
-
-!endif
-
-!include hb_objs.mak
-
-!if [del /f /q hb_objs.mak]
-!endif
-
-# Gather the list of headers and sources for introspection and glib-mkenums
-!if [call create-lists.bat header hb_srcs.mak HB_ACTUAL_HEADERS]
-!endif
-
-!if [for %h in ($(HB_HEADERS)) do @call create-lists.bat file hb_srcs.mak ..\src\%h]
-!endif
-
-!if [call create-lists.bat footer hb_srcs.mak]
-!endif
-
-# Gather the lists of sources for introspection
-!if [call create-lists.bat header hb_srcs.mak HB_ACTUAL_SOURCES]
-!endif
-
-!if [for %s in ($(HB_SOURCES)) do @call create-lists.bat file hb_srcs.mak ..\src\%s]
-!endif
-
-!if [call create-lists.bat footer hb_srcs.mak]
-!endif
-
-!if [call create-lists.bat header hb_srcs.mak HB_GOBJECT_ACTUAL_SOURCES]
-!endif
-
-!if [for %s in ($(HB_GOBJECT_sources) $(HB_GOBJECT_STRUCTS_headers)) do @call create-lists.bat file hb_srcs.mak ..\src\%s]
-!endif
-
-!if [call create-lists.bat footer hb_srcs.mak]
-!endif
-
-!include hb_srcs.mak
-
-!if [del /f /q hb_srcs.mak]
-!endif
diff --git a/win32/create-lists.bat b/win32/create-lists.bat
deleted file mode 100644
index ef60d5c..0000000
--- a/win32/create-lists.bat
+++ /dev/null
@@ -1,42 +0,0 @@
-@echo off
-rem Simple .bat script for creating the NMake Makefile snippets.
-
-if not "%1" == "header" if not "%1" == "file" if not "%1" == "footer" goto :error_cmd
-if "%2" == "" goto error_no_destfile
-
-if "%1" == "header" goto :header
-if "%1" == "file" goto :addfile
-if "%1" == "footer" goto :footer
-
-:header
-if "%3" == "" goto error_var
-echo %3 =	\>>%2
-goto done
-
-:addfile
-if "%3" == "" goto error_file
-echo.	%3	\>>%2
-goto done
-
-:footer
-echo.	$(NULL)>>%2
-echo.>>%2
-goto done
-
-:error_cmd
-echo Specified command '%1' was invalid.  Valid commands are: header file footer.
-goto done
-
-:error_no_destfile
-echo Destination NMake snippet file must be specified
-goto done
-
-:error_var
-echo A name must be specified for using '%1'.
-goto done
-
-:error_file
-echo A file must be specified for using '%1'.
-goto done
-
-:done
\ No newline at end of file
diff --git a/win32/detectenv-msvc.mak b/win32/detectenv-msvc.mak
deleted file mode 100644
index ca09793..0000000
--- a/win32/detectenv-msvc.mak
+++ /dev/null
@@ -1,144 +0,0 @@
-# Change this (or specify PREFIX= when invoking this NMake Makefile) if
-# necessary, so that the libs and headers of the dependent third-party
-# libraries can be located.  For instance, if building from GLib's
-# included Visual Studio projects, this should be able to locate the GLib
-# build out-of-the-box if they were not moved.  GLib's headers will be
-# found in $(GLIB_PREFIX)\include\glib-2.0 and
-# $(GLIB_PREFIX)\lib\glib-2.0\include and its import library will be found
-# in $(GLIB_PREFIX)\lib.
-
-!if "$(PREFIX)" == ""
-PREFIX = ..\..\vs$(VSVER)\$(PLAT)
-!endif
-
-# Location of the PERL interpretor, for running glib-mkenums.  glib-mkenums
-# needs to be found in $(PREFIX)\bin.  Using either a 32-bit or x64 PERL
-# interpretor are supported for either a 32-bit or x64 build.
-
-!if "$(PERL)" == ""
-PERL = perl
-!endif
-
-# Location of the Python interpretor, for building introspection.  The complete set
-# of Python Modules for introspection (the giscanner Python scripts and the _giscanner.pyd
-# compiled module) needs to be found in $(PREFIX)\lib\gobject-introspection\giscanner, and
-# the g-ir-scanner Python script and g-ir-compiler utility program needs to be found
-# in $(PREFIX)\bin, together with any DLLs they will depend on, if those DLLs are not already
-# in your PATH.
-# Note that the Python interpretor and the introspection modules and utility progam must
-# correspond to the build type (i.e. 32-bit Release for 32-bit Release builds, and so on).
-#
-# For introspection, currently only Python 2.7.x is supported.  This may change when Python 3.x
-# support is added upstream in gobject-introspection--when this happens, the _giscanner.pyd must
-# be the one that is built against the release series of Python that is used here.
-
-!if "$(PYTHON)" == ""
-PYTHON = python
-!endif
-
-# Location of the pkg-config utility program, for building introspection.  It needs to be able
-# to find the pkg-config (.pc) files so that the correct libraries and headers for the needed libraries
-# can be located, using PKG_CONFIG_PATH.  Using either a 32-bit or x64 pkg-config are supported for
-# either a 32-bit or x64 build.
-
-!if "$(PKG_CONFIG)" == ""
-PKG_CONFIG = pkg-config
-!endif
-
-# The items below this line should not be changed, unless one is maintaining
-# the NMake Makefiles.  The exception is for the CFLAGS_ADD line(s) where one
-# could use his/her desired compiler optimization flags, if he/she knows what is
-# being done.
-
-# Check to see we are configured to build with MSVC (MSDEVDIR, MSVCDIR or
-# VCINSTALLDIR) or with the MS Platform SDK (MSSDK or WindowsSDKDir)
-!if !defined(VCINSTALLDIR) && !defined(WINDOWSSDKDIR)
-MSG = ^
-This Makefile is only for Visual Studio 2008 and later.^
-You need to ensure that the Visual Studio Environment is properly set up^
-before running this Makefile.
-!error $(MSG)
-!endif
-
-ERRNUL  = 2>NUL
-_HASH=^#
-
-!if ![echo VCVERSION=_MSC_VER > vercl.x] \
-    && ![echo $(_HASH)if defined(_M_IX86) >> vercl.x] \
-    && ![echo PLAT=Win32 >> vercl.x] \
-    && ![echo $(_HASH)elif defined(_M_AMD64) >> vercl.x] \
-    && ![echo PLAT=x64 >> vercl.x] \
-    && ![echo $(_HASH)endif >> vercl.x] \
-    && ![cl -nologo -TC -P vercl.x $(ERRNUL)]
-!include vercl.i
-!if ![echo VCVER= ^\> vercl.vc] \
-    && ![set /a $(VCVERSION) / 100 - 6 >> vercl.vc]
-!include vercl.vc
-!endif
-!endif
-!if ![del $(ERRNUL) /q/f vercl.x vercl.i vercl.vc]
-!endif
-
-!if $(VCVERSION) > 1499 && $(VCVERSION) < 1600
-VSVER = 9
-!elseif $(VCVERSION) > 1599 && $(VCVERSION) < 1700
-VSVER = 10
-!elseif $(VCVERSION) > 1699 && $(VCVERSION) < 1800
-VSVER = 11
-!elseif $(VCVERSION) > 1799 && $(VCVERSION) < 1900
-VSVER = 12
-!elseif $(VCVERSION) > 1899 && $(VCVERSION) < 2000
-VSVER = 14
-!else
-VSVER = 0
-!endif
-
-!if "$(VSVER)" == "0"
-MSG = ^
-This NMake Makefile set supports Visual Studio^
-9 (2008) through 14 (2015).  Your Visual Studio^
-version is not supported.
-!error $(MSG)
-!endif
-
-VALID_CFGSET = FALSE
-!if "$(CFG)" == "release" || "$(CFG)" == "debug"
-VALID_CFGSET = TRUE
-!endif
-
-# One may change these items, but be sure to test
-# the resulting binaries
-!if "$(CFG)" == "release"
-CFLAGS_ADD = /MD /O2 /GL /MP
-!if $(VSVER) > 9 && $(VSVER) < 14
-# Undocumented "enhance optimized debugging" switch. Became documented
-# as "/Zo" in VS 2013 Update 3, and is turned on by default in VS 2015.
-CFLAGS_ADD = $(CFLAGS_ADD) /d2Zi+
-!endif
-!else
-CFLAGS_ADD = /MDd /Od
-!endif
-
-!if "$(PLAT)" == "x64"
-LDFLAGS_ARCH = /machine:x64
-!elseif "$(PLAT)" == "arm"
-LDFLAGS_ARCH = /machine:arm
-CFLAGS_ADD = $(CFLAGS_ADD) /DWINAPI_FAMILY=3
-!else
-LDFLAGS_ARCH = /machine:x86
-!endif
-
-!if "$(VALID_CFGSET)" == "TRUE"
-CFLAGS = $(CFLAGS_ADD) /W3 /Zi /I.. /I..\src /I. /I$(PREFIX)\include
-
-!if "$(ADDITIONAL_LIB_DIR)" != ""
-ADDITIONAL_LIB_ARG = /libpath:$(ADDITIONAL_LIB_DIR)
-!endif
-LDFLAGS_BASE = $(LDFLAGS_ARCH) /libpath:$(PREFIX)\lib $(ADDITIONAL_LIB_ARG) /DEBUG
-
-!if "$(CFG)" == "debug"
-LDFLAGS = $(LDFLAGS_BASE)
-!else
-LDFLAGS = $(LDFLAGS_BASE) /opt:ref /LTCG
-!endif
-!endif
diff --git a/win32/generate-msvc.mak b/win32/generate-msvc.mak
deleted file mode 100644
index 32214eb..0000000
--- a/win32/generate-msvc.mak
+++ /dev/null
@@ -1,26 +0,0 @@
-# NMake Makefile portion for code generation and
-# intermediate build directory creation
-# Items in here should not need to be edited unless
-# one is maintaining the NMake build files.
-
-# Copy the pre-defined config.h.win32
-config.h: config.h.win32
-	@-copy $@.win32 $@
-
-# Generate the enumeration sources and headers
-# sed is not normally available on Windows, but since
-# we are already using PERL, use PERL one-liners.
-!if "$(GOBJECT)" == "1"
-$(HB_GOBJECT_ENUM_GENERATED_SOURCES): ..\src\hb-gobject-enums.h.tmpl ..\src\hb-gobject-enums.cc.tmpl $(HB_ACTUAL_HEADERS)
-	$(PERL) $(PREFIX)\bin\glib-mkenums \
-		--identifier-prefix hb_ --symbol-prefix hb_gobject \
-		--template ..\src\$(@F).tmpl  $(HB_ACTUAL_HEADERS) > $@
-	$(PERL) -p -i.tmp1 -e "s/_t_get_type/_get_type/g" $@
-	$(PERL) -p -i.tmp2 -e "s/_T \(/ (/g" $@
-	@-del $@.tmp1
-	@-del $@.tmp2
-!endif
-
-# Create the build directories
-$(CFG)\$(PLAT)\harfbuzz $(CFG)\$(PLAT)\harfbuzz-gobject $(CFG)\$(PLAT)\util:
-	@-md $@
diff --git a/win32/hb-introspection-msvc.mak b/win32/hb-introspection-msvc.mak
deleted file mode 100644
index 67a0c5e..0000000
--- a/win32/hb-introspection-msvc.mak
+++ /dev/null
@@ -1,42 +0,0 @@
-
-!if "$(BUILD_INTROSPECTION)" == "TRUE"
-# Create the file list for introspection (to avoid the dreaded command-line-too-long problem on Windows)
-$(CFG)\$(PLAT)\hb_list: $(HB_ACTUAL_HEADERS) $(HB_ACTUAL_SOURCES) $(HB_GOBJECT_ENUM_GENERATED_SOURCES) $(HB_GOBJECT_ACTUAL_SOURCES)
-	@for %f in ($(HB_ACTUAL_HEADERS) $(HB_ACTUAL_SOURCES) $(HB_GOBJECT_ENUM_GENERATED_SOURCES) $(HB_GOBJECT_ACTUAL_SOURCES)) do @echo %f >> $@
-
-$(CFG)\$(PLAT)\HarfBuzz-0.0.gir: $(CFG)\$(PLAT)\harfbuzz-gobject.lib $(CFG)\$(PLAT)\hb_list
-	@set LIB=$(CFG)\$(PLAT);$(PREFIX)\lib;$(LIB)
-	@set PATH=$(CFG)\$(PLAT);$(PREFIX)\bin;$(PATH)
-	@-echo Generating $@...
-	$(PYTHON) $(G_IR_SCANNER)	\
-	--verbose -no-libtool	\
-	-I..\src -n hb --identifier-prefix=hb_ --warn-all	\
-	--namespace=HarfBuzz	\
-	--nsversion=0.0	\
-	--include=GObject-2.0	\
-	--library=harfbuzz-gobject	\
-	--library=harfbuzz	\
-	--add-include-path=$(G_IR_INCLUDEDIR)	\
-	--pkg-export=harfbuzz	\
-	--cflags-begin	\
-	$(CFLAGS) $(HB_DEFINES) $(HB_CFLAGS)	\
-	-DHB_H \
-	-DHB_H_IN \
-	-DHB_OT_H \
-	-DHB_OT_H_IN \
-	-DHB_GOBJECT_H \
-	-DHB_GOBJECT_H_IN \
-	--cflags-end	\
-	--filelist=$(CFG)\$(PLAT)\hb_list	\
-	-o $@
-
-$(CFG)\$(PLAT)\HarfBuzz-0.0.typelib: $(CFG)\$(PLAT)\HarfBuzz-0.0.gir
-	@copy $*.gir $(@B).gir
-	$(PREFIX)\bin\g-ir-compiler	\
-	--includedir=$(CFG)\$(PLAT) --debug --verbose	\
-	$(@B).gir	\
-	-o $@
-	@del $(@B).gir
-!else
-!error $(ERROR_MSG)
-!endif
diff --git a/win32/info-msvc.mak b/win32/info-msvc.mak
deleted file mode 100644
index 3ec11d4..0000000
--- a/win32/info-msvc.mak
+++ /dev/null
@@ -1,142 +0,0 @@
-# NMake Makefile portion for displaying config info
-
-INC_FEATURES = Fallback OT
-BUILT_TOOLS =
-BUILT_LIBRARIES = HarfBuzz
-
-!if "$(GLIB)" == "1"
-UNICODE_IMPL = GLib
-INC_FEATURES = $(INC_FEATURES) GLib
-BUILT_TOOLS = hb-shape.exe hb-ot-shape-closure.exe
-!if "$(CAIRO_FT)" == "1"
-BUILT_TOOLS = hb-view.exe $(BUILT_TOOLS)
-!endif
-!elseif "$(ICU)" == "1"
-UNICODE_IMPL = ICU
-!else
-UNICODE_IMPL = ucdn
-!endif
-
-!if "$(FREETYPE)" == "1"
-INC_FEATURES = $(INC_FEATURES) FreeType
-!endif
-
-!if "$(GRAPHITE2)" == "1"
-INC_FEATURES = $(INC_FEATURES) Graphite2
-!endif
-
-!if "$(UNISCRIBE)" == "1"
-INC_FEATURES = $(INC_FEATURES) Uniscribe
-!endif
-
-!if "$(DIRECTWRITE)" == "1"
-INC_FEATURES = $(INC_FEATURES) DirectWrite
-!endif
-
-!if "$(GOBJECT)" == "1"
-BUILT_LIBRARIES = $(BUILT_LIBRARIES) HarfBuzz-GObject
-!endif
-
-!if "$(INTROSPECTION)" == "1"
-BUILD_INTROSPECTION = yes
-!else
-BUILD_INTROSPECTION = no
-!endif
-
-build-info-hb:
-	@echo.
-	@echo ==================================
-	@echo Configuration for HarfBuzz Library
-	@echo ==================================
-	@echo Unicode Implementation: $(UNICODE_IMPL)
-	@echo Enabled Features: $(INC_FEATURES)
-
-all-build-info: build-info-hb
-	@echo.
-	@echo ----------------
-	@echo Other build info
-	@echo ----------------
-	@echo Built Libraries: $(BUILT_LIBRARIES)
-	@echo Built Tools: $(BUILT_TOOLS)
-	@echo Introspection: $(BUILD_INTROSPECTION)
-
-help:
-	@echo.
-	@echo =============================
-	@echo Building HarfBuzz Using NMake
-	@echo =============================
-	@echo nmake /f Makefile.vc CFG=[release^|debug] ^<PREFIX=PATH^> OPTION=1 ...
-	@echo.
-	@echo Where:
-	@echo ------
-	@echo CFG: Required, use CFG=release for an optimized build and CFG=debug
-	@echo for a debug build.  PDB files are generated for all builds.
-	@echo.
-	@echo PREFIX: Optional, the path where dependent libraries and tools may be
-	@echo found, default is ^$(srcrootdir)\..\vs^$(short_vs_ver)\^$(platform),
-	@echo where ^$(short_vs_ver) is 9 for VS 2008, 10 for VS 2010 and so on; and
-	@echo ^$(platform) is Win32 for 32-bit builds and x64 for x64 builds.
-	@echo.
-	@echo OPTION: Optional, may be any of the following, use OPTION=1 to enable;
-	@echo multiple OPTION's may be used.  If no OPTION is specified, a default
-	@echo HarfBuzz DLL is built with OpenType and fallback support
-	@echo with a bundled Unicode implementation (UCDN).
-	@echo ======
-	@echo UNISCRIBE:
-	@echo Enable Uniscribe support.
-	@echo.
-	@echo DIRECTWRITE:
-	@echo Enable DirectWrite support, requires a recent enough Windows SDK.
-	@echo.
-	@echo GRAPHITE2:
-	@echo Enable graphite2 support, requires the SIL Graphite2 library
-	@echo.
-	@echo FREETYPE:
-	@echo Enable FreeType2 support, requires the FreeType2 library
-	@echo.
-	@echo GLIB:
-	@echo Enable GLib2 support, with GLib Unicode support, requires the GNOME GLib2
-	@echo library.  Enables the build of utility programs.
-	@echo.
-	@echo ICU:
-	@echo Enable build with ICU Unicode functions, requires the International
-	@echo Components for Unicode (ICU) libraries.
-	@echo.
-	@echo GOBJECT:
-	@echo Enable the HarfBuzz-GObject library, also implies GLib2 support,
-	@echo requires the GNOME GLib2 libraries and tools, notably the glib-mkenums
-	@echo tool script, which will require a PERL interpreter (use
-	@echo PERL=^$(PATH_TO_PERL_INTERPRETOR)) if it is not already in your PATH).
-	@echo.
-	@echo INTROSPECTION:
-	@echo Enable the build of introspection files, also implies GObject/GLib2 support,
-	@echo requires the GNOME gobject-introspection libraries and tools.  You will need
-	@echo to ensure the pkg-config (.pc) files can be found for GObject-2.0 and the
-	@echo Python interpreter (that was used to build the gobject-introspection tools)
-	@echo can be found by setting PKG_CONFIG_PATH beforehand, and passing in PYTHON=
-	@echo ^$(PATH_TO_PYTHON_INTERPRETOR) respectively, if python.exe is not already
-	@echo in your PATH.
-	@echo.
-	@echo CAIRO_FT:
-	@echo Enables Cairo-Freetype support, needed for the build of the hb-view utility.
-	@echo Implies FreeType2 support and also requires Cairo built with FreeType2
-	@echo support; GLib2 support must also be enabled.
-	@echo.
-	@echo LIBTOOL_DLL_NAME:
-	@echo Use a libtool-style DLL name to mimic the DLL file naming generated by
-	@echo MinGW builds.
-	@echo.
-	@echo Note that GLib2 support is required for all utility and test programs.
-	@echo ======
-	@echo A 'clean' target is supported to remove all generated files, intermediate
-	@echo object files and binaries for the specified configuration.
-	@echo.
-	@echo A 'tests' target is supported to build the test programs, if GLib2 support
-	@echo is enabled.  Use after building the libraries and utilities.
-	@echo.
-	@echo An 'install' target is supported to copy the build (DLLs, utility programs,
-	@echo LIBs, along with the introspection files if applicable) to appropriate
-	@echo locations under ^$(PREFIX).
-	@echo ======
-	@echo.
-	
diff --git a/win32/install.mak b/win32/install.mak
deleted file mode 100644
index e0a38e3..0000000
--- a/win32/install.mak
+++ /dev/null
@@ -1,25 +0,0 @@
-# NMake Makefile snippet for copying the built libraries, utilities and headers to
-# a path under $(PREFIX).
-
-install: all
-	@if not exist $(PREFIX)\bin\ mkdir $(PREFIX)\bin
-	@if not exist $(PREFIX)\lib\ mkdir $(PREFIX)\lib
-	@if not exist $(PREFIX)\include\harfbuzz\ mkdir $(PREFIX)\include\harfbuzz
-	@copy /b $(HARFBUZZ_DLL_FILENAME).dll $(PREFIX)\bin
-	@copy /b $(HARFBUZZ_DLL_FILENAME).pdb $(PREFIX)\bin
-	@copy /b $(CFG)\$(PLAT)\harfbuzz.lib $(PREFIX)\lib
-	@if exist $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll copy /b $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll $(PREFIX)\bin
-	@if exist $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll copy /b $(HARFBUZZ_GOBJECT_DLL_FILENAME).pdb $(PREFIX)\bin
-	@if exist $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll copy /b $(CFG)\$(PLAT)\harfbuzz-gobject.lib $(PREFIX)\lib
-	@if exist $(CFG)\$(PLAT)\hb-view.exe copy /b $(CFG)\$(PLAT)\hb-view.exe $(PREFIX)\bin
-	@if exist $(CFG)\$(PLAT)\hb-view.exe copy /b $(CFG)\$(PLAT)\hb-view.pdb $(PREFIX)\bin
-	@if exist $(CFG)\$(PLAT)\hb-ot-shape-closure.exe copy /b $(CFG)\$(PLAT)\hb-ot-shape-closure.exe $(PREFIX)\bin
-	@if exist $(CFG)\$(PLAT)\hb-ot-shape-closure.exe copy /b $(CFG)\$(PLAT)\hb-ot-shape-closure.pdb $(PREFIX)\bin
-	@if exist $(CFG)\$(PLAT)\hb-shape.exe copy /b $(CFG)\$(PLAT)\hb-shape.exe $(PREFIX)\bin
-	@if exist $(CFG)\$(PLAT)\hb-shape.exe copy /b $(CFG)\$(PLAT)\hb-shape.pdb $(PREFIX)\bin
-	@for %h in ($(HB_ACTUAL_HEADERS)) do @copy %h $(PREFIX)\include\harfbuzz
-	@if exist $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll for %h in ($(HB_GOBJECT_headers)) do @copy ..\src\%h $(PREFIX)\include\harfbuzz
-	@if exist $(HARFBUZZ_GOBJECT_DLL_FILENAME).dll copy $(CFG)\$(PLAT)\harfbuzz-gobject\hb-gobject-enums.h $(PREFIX)\include\harfbuzz
-	@rem Copy the generated introspection files
-	@if exist $(CFG)\$(PLAT)\HarfBuzz-0.0.gir copy $(CFG)\$(PLAT)\HarfBuzz-0.0.gir $(PREFIX)\share\gir-1.0
-	@if exist $(CFG)\$(PLAT)\HarfBuzz-0.0.typelib copy /b $(CFG)\$(PLAT)\HarfBuzz-0.0.typelib $(PREFIX)\lib\girepository-1.0
diff --git a/win32/introspection-msvc.mak b/win32/introspection-msvc.mak
deleted file mode 100644
index d32f7cf..0000000
--- a/win32/introspection-msvc.mak
+++ /dev/null
@@ -1,73 +0,0 @@
-# Common NMake Makefile module for checking the build environment is sane
-# for building introspection files under MSVC/NMake.
-# This can be copied from $(gi_srcroot)\build\win32 for GNOME items
-# that support MSVC builds and introspection under MSVC.
-
-# Can override with env vars as needed
-# You will need to have built gobject-introspection for this to work.
-# Change or pass in or set the following to suit your environment
-
-!if "$(PREFIX)" == ""
-PREFIX = ..\..\..\vs$(VSVER)\$(PLAT)
-!endif
-
-# Note: The PYTHON must be the Python release series that was used to build
-# the GObject-introspection scanner Python module!
-# Either having python.exe your PATH will work or passing in
-# PYTHON=<full path to your Python interpretor> will do
-
-# This is required, and gobject-introspection needs to be built
-# before this can be successfully run.
-!if "$(PYTHON)" == ""
-PYTHON=python
-!endif
-
-# Don't change anything following this line!
-
-GIR_SUBDIR = share\gir-1.0
-GIR_TYPELIBDIR = lib\girepository-1.0
-G_IR_SCANNER = $(PREFIX)\bin\g-ir-scanner
-G_IR_COMPILER = $(PREFIX)\bin\g-ir-compiler.exe
-G_IR_INCLUDEDIR = $(PREFIX)\$(GIR_SUBDIR)
-G_IR_TYPELIBDIR = $(PREFIX)\$(GIR_TYPELIBDIR)
-
-VALID_PKG_CONFIG_PATH = FALSE
-
-MSG_INVALID_PKGCONFIG = You must set or specifiy a valid PKG_CONFIG_PATH
-MSG_INVALID_CFG = You need to specify or set CFG to be release or debug to use this Makefile to build the Introspection Files
-
-ERROR_MSG =
-
-BUILD_INTROSPECTION = TRUE
-
-!if ![pkg-config --print-errors --errors-to-stdout $(CHECK_PACKAGE) > pkgconfig.x]	\
-	&& ![setlocal]	\
-	&& ![set file="pkgconfig.x"]	\
-	&& ![FOR %A IN (%file%) DO @echo PKG_CHECK_SIZE=%~zA > pkgconfig.chksize]	\
-	&& ![del $(ERRNUL) /q/f pkgconfig.x]
-!endif
-
-!include pkgconfig.chksize
-!if "$(PKG_CHECK_SIZE)" == "0"
-VALID_PKG_CONFIG_PATH = TRUE
-!else
-VALID_PKG_CONFIG_PATH = FALSE
-!endif
-
-!if ![del $(ERRNUL) /q/f pkgconfig.chksize]
-!endif
-
-VALID_CFGSET = FALSE
-!if "$(CFG)" == "release" || "$(CFG)" == "debug"
-VALID_CFGSET = TRUE
-!endif
-
-!if "$(VALID_PKG_CONFIG_PATH)" != "TRUE"
-BUILD_INTROSPECTION = FALSE
-ERROR_MSG = $(MSG_INVALID_PKGCONFIG)
-!endif
-
-!if "$(VALID_CFGSET)" != "TRUE"
-BUILD_INTROSPECTION = FALSE
-ERROR_MSG = $(MSG_INVALID_CFG)
-!endif