Merge "Merge commit '3a74ee528255cc027d84b204a87b5c25e47bff79'"
am: b469bb31ee

Change-Id: I810ca976d79c1e927a99f436e8d4581f3e189f1e
diff --git a/.ci/build-freetype.sh b/.ci/build-freetype.sh
new file mode 100644
index 0000000..0f09f92
--- /dev/null
+++ b/.ci/build-freetype.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -x
+set -o errexit -o nounset
+
+# 22.0.16 is the libtool version of 2.9.0
+if pkg-config --atleast-version 22.0.16 freetype2; then exit; fi
+
+pushd $HOME
+wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2
+tar xf freetype-2.9.tar.bz2
+pushd freetype-2.9
+./autogen.sh
+./configure --prefix=$HOME/.local
+make -j4 install
+popd
+popd
diff --git a/.circleci/config.yml b/.circleci/config.yml
index e6a5a1d..2975ea8 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -4,10 +4,10 @@
 
   macos-10.12.6-aat-fonts:
     macos:
-      xcode: "9.2.0"
+      xcode: "9.0.1"
     steps:
       - checkout
-      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel freetype glib cairo
+      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo
       - run: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
       - run: make -j4
       - run: make check || .ci/fail.sh
@@ -17,49 +17,25 @@
       xcode: "10.1.0"
     steps:
       - checkout
-      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel freetype glib cairo
+      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo
       - run: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
       - run: make -j4
       - run: make check || .ci/fail.sh
 
-  macos-llvm-gcc-4.2:
+  macos-10.14.4-aat-fonts:
     macos:
-      xcode: "8.3.3"
+      xcode: "11.0.0"
     steps:
       - checkout
-      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel freetype glib cairo
-      - run: wget https://packages.macports.org/llvm-gcc42/llvm-gcc42-2336.11_3+universal.darwin_15.i386-x86_64.tbz2 && tar zxvf llvm-gcc42-2336.11_3+universal.darwin_15.i386-x86_64.tbz2
-      - run: CC=$PWD/opt/local/bin/llvm-gcc-4.2 CXX=$PWD/opt/local/bin/llvm-g++-4.2 ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
-      # Ignoring assembler complains, https://stackoverflow.com/a/39867021
-      - run: make 2>&1 | grep -v -e '^/var/folders/*' -e '^[[:space:]]*\.section' -e '^[[:space:]]*\^[[:space:]]*~*'
+      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo icu4c graphite2 cmake
+      - run: export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig" && ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-coretext --with-graphite2
+      - run: make -j4
       - run: make check || .ci/fail.sh
-
-  macos-notest-apple-gcc-i686-4.2:
-    macos:
-      xcode: "8.3.3"
-    steps:
-      - checkout
-      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel
-      - run: wget https://packages.macports.org/apple-gcc42/apple-gcc42-5666.3_15+universal.darwin_15.i386-x86_64.tbz2 && tar zxvf apple-gcc42-5666.3_15+universal.darwin_15.i386-x86_64.tbz2
-      - run: CPP=$PWD/opt/local/bin/i686-apple-darwin15-cpp-apple-4.2.1 CC=$PWD/opt/local/bin/i686-apple-darwin15-gcc-apple-4.2.1 CXX=$PWD/opt/local/bin/i686-apple-darwin15-g++-apple-4.2.1 ./autogen.sh
-      # Ignoring assembler complains, https://stackoverflow.com/a/39867021
-      - run: make 2>&1 | grep -v -e '^/var/folders/*' -e '^[[:space:]]*\.section' -e '^[[:space:]]*\^[[:space:]]*~*'
-
-  macos-notest-ios:
-    macos:
-      xcode: "10.0.0"
-    steps:
-      - checkout
-      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
-      # not needed to be a framework but we like to test that also
-      # TODO: wrong way of targeting iOS as it doesn't point to iOS headers thus building
-      # CoreText support is not possible, after the fix feel free HB_IOS from CMake altogether
-      - run: cmake -DBUILD_FRAMEWORK=ON -H. -Bbuild -GXcode -DHB_HAVE_CORETEXT=OFF -DHB_BUILD_SUBSET=OFF -DHB_BUILD_TESTS=OFF
-      - run: cd build && xcodebuild -sdk iphoneos12.0 -configuration Release build -arch arm64
+      - run: cmake -Bbuild -H. -DHB_HAVE_CORETEXT=1 -DHB_BUILD_TESTS=0 && cmake --build build
 
   distcheck:
     docker:
-      - image: ubuntu:17.10
+      - image: ubuntu:19.04
     steps:
       - checkout
       - run: apt update && apt install -y ninja-build binutils libtool autoconf automake make cmake gcc g++ pkg-config ragel gtk-doc-tools libfontconfig1-dev libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
@@ -70,36 +46,50 @@
       - 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-NOMMAP:
+  alpine-O3-Os-NOMMAP:
     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
+      - run: apk update && apk add ragel make pkgconfig libtool autoconf automake gettext gcc g++ glib-dev freetype-dev cairo-dev python
       # C??FLAGS are not needed for a regular build
       - run: CFLAGS="-O3" CXXFLAGS="-O3 -DHB_NO_MMAP" ./autogen.sh
       - run: make -j32
       - run: make check || .ci/fail.sh
-
-  archlinux-debug-O0-py3:
-    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 python-pip
-      - run: pip install fonttools
-      # C??FLAGS are not needed for a regular build
-      - run: CFLAGS="-O0" CXXFLAGS="-O0" CPPFLAGS="-DHB_DEBUG" ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2
+      - run: make clean
+      - run: CFLAGS="-Os -DHB_OPTIMIZE_SIZE" CXXFLAGS="-Os -DHB_NO_MMAP -DHB_OPTIMIZE_SIZE" ./autogen.sh
       - run: make -j32
       - run: make check || .ci/fail.sh
 
-  clang-O3-O0:
+  archlinux-py3-all:
     docker:
-      - image: multiarch/crossbuild
+      - image: archlinux/base
+    steps:
+      - checkout
+      - run: pacman --noconfirm -Syu freetype2 cairo icu gettext gobject-introspection gcc gcc-libs glib2 graphite pkg-config ragel python python-pip make which base-devel
+      - run: pip install flake8 fonttools
+      - run: flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
+      # C??FLAGS are not needed for a regular build
+      - run: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2
+      - run: make -j32 CPPFLAGS="-Werror"
+      - run: make check CPPFLAGS="-Werror" || .ci/fail.sh
+
+  ## Doesn't play well with CircleCI apparently
+  #void-notest:
+  #  docker:
+  #    - image: voidlinux/voidlinux
+  #  steps:
+  #    - checkout
+  #    - run: xbps-install -Suy freetype gettext gcc glib graphite pkg-config ragel libtool autoconf automake make
+  #    - run: ./autogen.sh && make -j32 && make check
+
+  clang-O3-O0-and-nobuildsystem:
+    docker:
+      - image: ubuntu:18.10
     steps:
       - checkout
       - run: apt update || true
-      - run: apt install -y ragel libfreetype6-dev libfontconfig1-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
+      - run: apt install -y clang wget autoconf automake libtool pkg-config ragel libfreetype6-dev libfontconfig1-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
       - run: pip install fonttools
       - run: wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure && make -j32 && cd ..
       - run: CFLAGS="-O3" CXXFLAGS="-O3" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-fontconfig --with-glib --with-cairo --with-icu --with-graphite2
@@ -108,6 +98,9 @@
       - run: CFLAGS="-O0" CXXFLAGS="-O0" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-fontconfig --with-glib --with-cairo --with-icu --with-graphite2
       - run: make -j32
       - run: LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" make check || .ci/fail.sh
+      - run: make clean
+      - run: make -Csrc CPPFLAGS="-DHB_TINY -DHB_NO_OT_FONT" libharfbuzz-subset.la && make clean
+      - run: clang -c src/hb-*.cc -DHB_NO_MT
 
   gcc-valgrind:
     docker:
@@ -121,7 +114,7 @@
       - run: make -j32
       # run-shape-fuzzer-tests.py automatically runs valgrind if see available
       # but test/api runs it by request, we probably should normalize the approaches
-      - run: RUN_VALGRIND=1 make check && make -Ctest/api check-valgrind || .ci/fail.sh
+      - run: HB_TEST_SHAPE_FUZZER_TIMEOUT=3 HB_TEST_SUBSET_FUZZER_TIMEOUT=30 RUN_VALGRIND=1 make check && make -Ctest/api check-valgrind || .ci/fail.sh
       # informational for now
       - run: make -Ctest/api check-symbols || true
 
@@ -137,7 +130,7 @@
       - run: apt update || true
       - run: apt install -y clang lld binutils libtool autoconf automake make pkg-config gtk-doc-tools ragel libfreetype6-dev libfontconfig1-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
       - run: pip install fonttools
-      - run: CFLAGS="-Weverything -Wno-reserved-id-macro -Wno-conversion -Wno-padded -Wno-sign-conversion -Wno-cast-qual -Wno-documentation -Wno-documentation-unknown-command" CXXFLAGS="-Weverything -Wno-old-style-cast -Wno-documentation -Wno-documentation-unknown-command -Wno-c++98-compat -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-sign-conversion -Wno-padded -Wno-shorten-64-to-32 -Wno-reserved-id-macro -Wno-float-conversion -Wno-format-pedantic -Wno-shadow -Wno-conversion -Wno-zero-as-null-pointer-constant -Wno-missing-field-initializers -Wno-used-but-marked-unused -Wno-unused-macros -Wno-comma -Wno-float-equal -Wno-disabled-macro-expansion -Wno-weak-vtables -Wno-unused-parameter -Wno-covered-switch-default -Wno-unreachable-code" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 --with-fontconfig
+      - run: CFLAGS="-Weverything -Wno-reserved-id-macro -Wno-conversion -Wno-padded -Wno-sign-conversion -Wno-cast-qual -Wno-documentation -Wno-documentation-unknown-command -DHB_WITH_WIN1256" CXXFLAGS="-Weverything -Wno-old-style-cast -Wno-documentation -Wno-documentation-unknown-command -Wno-c++98-compat -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-sign-conversion -Wno-padded -Wno-shorten-64-to-32 -Wno-reserved-id-macro -Wno-float-conversion -Wno-format-pedantic -Wno-shadow -Wno-conversion -Wno-zero-as-null-pointer-constant -Wno-missing-field-initializers -Wno-used-but-marked-unused -Wno-unused-macros -Wno-comma -Wno-float-equal -Wno-disabled-macro-expansion -Wno-weak-vtables -Wno-unused-parameter -Wno-covered-switch-default -Wno-unreachable-code -Wno-unused-template -DHB_WITH_WIN1256" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 --with-fontconfig
       - run: make -j32 CPPFLAGS="-Werror"
       - run: make check CPPFLAGS="-Werror" || .ci/fail.sh
 
@@ -173,7 +166,7 @@
       - run: wget https://ftp.gnome.org/pub/gnome/sources/glib/2.58/glib-2.58.1.tar.xz && tar xf glib-2.58.1.tar.xz && cd glib-2.58.1 && ./autogen.sh --with-pcre CPPFLAGS="-fsanitize=memory" LDFLAGS="-fsanitize=memory" CFLAGS="-fsanitize=memory" CXXFLAGS="-fsanitize=memory" LD=ld.lld CC=clang CXX=clang++ && make -j32 && make install && cd ..
       - run: wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure CPPFLAGS="-fsanitize=memory" LDFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ && make -j32 && make install && cd ..
       - run: CPPFLAGS="-fsanitize=memory -fsanitize-memory-track-origins" LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --without-icu
-      - run: make -j32 && MSAN_OPTIONS=exitcode=42 make check || .ci/fail.sh | asan_symbolize | c++filt
+      - run: make -j32 && MSAN_OPTIONS=exitcode=42 HB_TEST_SUBSET_FUZZER_TIMEOUT=12 make check || .ci/fail.sh | asan_symbolize | c++filt
 
   clang-tsan:
     docker:
@@ -189,7 +182,7 @@
       - run: pip install fonttools
       - run: CPPFLAGS="-fsanitize=thread" LDFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
       - run: make -j32
-      - run: make check || .ci/fail.sh | asan_symbolize | c++filt
+      - run: HB_TEST_SUBSET_FUZZER_TIMEOUT=40 make check || .ci/fail.sh | asan_symbolize | c++filt
 
   clang-ubsan:
     docker:
@@ -203,22 +196,31 @@
       - run: apt update || true
       - run: apt install -y clang lld binutils libtool autoconf automake make pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
       - run: pip install fonttools
-      - run: CPPFLAGS="-fsanitize=undefined" LDFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
+      - run: CPPFLAGS="-fsanitize=undefined -fno-sanitize-recover=undefined" LDFLAGS="-fsanitize=undefined -fno-sanitize-recover=undefined -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
       - run: make -j32
-      - run: make check || .ci/fail.sh | asan_symbolize | c++filt
+      - run: UBSAN_OPTIONS=print_stacktrace=1 make check || .ci/fail.sh | asan_symbolize | c++filt
 
-  fedora-outoftreebuild:
+  fedora-O0-debug-outoftreebuild-mingw:
     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)
+      - 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 mingw32-gcc-c++ mingw64-gcc-c++ mingw32-glib2 mingw32-cairo mingw32-freetype mingw64-glib2 mingw64-cairo mingw64-freetype glibc-devel.i686 || true
+      - run: NOCONFIGURE=1 ./autogen.sh
+      - run: mkdir build && cd build && CFLAGS="-O0" CXXFLAGS="-O0" CPPFLAGS="-DHB_DEBUG" ../configure --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 && make -j32 && (make check || ../.ci/fail.sh)
+      - run: pip install pefile
+      - run: mkdir winbuild32 && cd winbuild32 && ../mingw32.sh && make -j32 && make dist-win && cp harfbuzz-*-win32.zip harfbuzz-win32.zip
+      - run: mkdir winbuild64 && cd winbuild64 && ../mingw64.sh && make -j32 && make dist-win && cp harfbuzz-*-win64.zip harfbuzz-win64.zip
+      - store_artifacts:
+          path: winbuild32/harfbuzz-win32.zip
+          destination: harfbuzz-win32.zip
+      - store_artifacts:
+          path: winbuild64/harfbuzz-win64.zip
+          destination: harfbuzz-win64.zip
 
   cmake-gcc:
     docker:
-      - image: ubuntu:17.10
+      - image: ubuntu:19.04
     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
@@ -228,17 +230,17 @@
       - 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 libnsl || true
-      - run: wget http://$ODSUSER:$ODSPASS@behdad.org/harfbuzz-private/OracleDeveloperStudio12.6-linux-x86-bin.tar.bz2 && tar xf OracleDeveloperStudio12.6-linux-x86-bin.tar.bz2 --owner root --group root --no-same-owner
-      - run: CC=/root/project/OracleDeveloperStudio12.6-linux-x86-bin/developerstudio12.6/bin/suncc CXX=/root/project/OracleDeveloperStudio12.6-linux-x86-bin/developerstudio12.6/bin/sunCC cmake -DHB_HAVE_GRAPHITE2=ON -DHB_BUILTIN_UCDN=ON -DHB_HAVE_GLIB=ON -DHB_HAVE_FREETYPE=ON -Bbuild -H.
-      - run: make -Cbuild -j32
-      - run: CTEST_OUTPUT_ON_FAILURE=1 make -Cbuild test
-      - run: make -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 libnsl || true
+  #    - run: wget http://$ODSUSER:$ODSPASS@behdad.org/harfbuzz-private/OracleDeveloperStudio12.6-linux-x86-bin.tar.bz2 && tar xf OracleDeveloperStudio12.6-linux-x86-bin.tar.bz2 --owner root --group root --no-same-owner
+  #    - run: CC=/root/project/OracleDeveloperStudio12.6-linux-x86-bin/developerstudio12.6/bin/suncc CXX=/root/project/OracleDeveloperStudio12.6-linux-x86-bin/developerstudio12.6/bin/sunCC cmake -DHB_HAVE_GRAPHITE2=ON -DHB_HAVE_GLIB=ON -DHB_HAVE_FREETYPE=ON -Bbuild -H.
+  #    - run: make -Cbuild -j32
+  #    - run: CTEST_OUTPUT_ON_FAILURE=1 make -Cbuild test
+  #    - run: make -Cbuild install
 
   crosscompile-notest-djgpp:
     docker:
@@ -250,22 +252,13 @@
       - run: CFLAGS="-Wno-attributes" CXXFLAGS="-Wno-attributes" ./autogen.sh --prefix=/usr/local/djgpp --host=i586-pc-msdosdjgpp
       - run: make -j32
 
-  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 -j32
-
   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: echo '#!/bin/true' > /usr/bin/ragel && chmod +x /usr/bin/ragel
       - run: ./autogen.sh --prefix=/usr/local/vitasdk/arm-vita-eabi --host=arm-vita-eabi
       - run: make -j32
 
@@ -274,17 +267,15 @@
       - image: dockcross/android-arm
     steps:
       - checkout
-      - run: apt update && apt install ragel
-      - run: cmake -Bbuild -H. -GNinja
+      - run: cmake -Bbuild -H. -GNinja -DHB_BUILD_TESTS=OFF
       - run: ninja -Cbuild
 
-  crosscompile-cmake-notest-browser-asmjs:
+  crosscompile-cmake-notest-browser-asmjs-hb_tiny:
     docker:
       - image: dockcross/browser-asmjs
     steps:
       - checkout
-      - run: apt update && apt install ragel
-      - run: cmake -Bbuild -H. -GNinja
+      - run: cmake -Bbuild -H. -GNinja -DCMAKE_CXX_FLAGS="-DHB_TINY" -DHB_BUILD_TESTS=OFF
       - run: ninja -Cbuild
 
   crosscompile-cmake-notest-linux-arm64:
@@ -292,8 +283,7 @@
       - image: dockcross/linux-arm64
     steps:
       - checkout
-      - run: apt update && apt install ragel
-      - run: cmake -Bbuild -H. -GNinja
+      - run: cmake -Bbuild -H. -GNinja -DHB_BUILD_TESTS=OFF
       - run: ninja -Cbuild
 
   crosscompile-cmake-notest-linux-mips:
@@ -301,8 +291,7 @@
       - image: dockcross/linux-mips
     steps:
       - checkout
-      - run: apt update && apt install ragel
-      - run: cmake -Bbuild -H. -GNinja
+      - run: cmake -Bbuild -H. -GNinja -DHB_BUILD_TESTS=OFF
       - run: ninja -Cbuild
 
   #crosscompile-cmake-notest-windows-x64:
@@ -310,7 +299,6 @@
   #    - image: dockcross/windows-x64
   #  steps:
   #    - checkout
-  #    - run: apt update && apt install ragel
   #    - run: cmake -Bbuild -H. -GNinja
   #    - run: ninja -Cbuild
 
@@ -321,39 +309,37 @@
       # macOS
       - macos-10.12.6-aat-fonts
       - macos-10.13.6-aat-fonts
-      - macos-llvm-gcc-4.2
-      - macos-notest-apple-gcc-i686-4.2
-      - macos-notest-ios
+      - macos-10.14.4-aat-fonts
 
       # both autotools and cmake
       - distcheck
 
       # autotools based builds
-      - alpine-O3-NOMMAP
-      - archlinux-debug-O0-py3
+      - alpine-O3-Os-NOMMAP
+      - archlinux-py3-all
+      #- void-notest
       - gcc-valgrind
-      - clang-O3-O0
+      - clang-O3-O0-and-nobuildsystem
       - clang-everything
       - clang-asan
       - clang-msan
       - clang-tsan
       - clang-ubsan
-      - fedora-outoftreebuild
+      - fedora-O0-debug-outoftreebuild-mingw
 
       # cmake based builds
       - cmake-gcc
-      - cmake-oracledeveloperstudio
+      #- 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-browser-asmjs-hb_tiny
       - crosscompile-cmake-notest-linux-arm64
       - crosscompile-cmake-notest-linux-mips
       #- crosscompile-cmake-notest-windows-x64
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..259427c
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,38 @@
+# The following tries to match the current code style, is imperfect for now
+# but good for new codes be added
+
+IndentWidth: 2
+TabWidth: 8
+UseTab: Always
+SpaceBeforeParens: Always
+AllowShortLoopsOnASingleLine: true
+BreakBeforeBraces: Custom
+BraceWrapping:
+  AfterEnum: true
+  AfterStruct: false
+  SplitEmptyFunction: false
+  AfterClass: true
+  AfterControlStatement: true
+  AfterEnum: false
+  AfterFunction: true
+  AfterNamespace: false
+  AfterStruct: true
+  AfterUnion: true
+  BeforeElse: true
+AlwaysBreakTemplateDeclarations: true
+AlignTrailingComments: true
+AlignEscapedNewlines: Left
+AllowShortBlocksOnASingleLine: true
+SpaceAfterCStyleCast: true
+AlwaysBreakAfterDefinitionReturnType: TopLevel
+BinPackParameters: false
+AllowShortFunctionsOnASingleLine: Inline
+AccessModifierOffset: 0
+AlignTrailingComments: true
+AllowShortIfStatementsOnASingleLine: true
+AlignAfterOpenBracket: Align
+AlignOperands: true
+AllowShortCaseLabelsOnASingleLine: true
+
+# We like to have this only for function parameters and structs fields, not always
+# AlignConsecutiveDeclarations: true
diff --git a/.editorconfig b/.editorconfig
index bd981d1..4291676 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,10 +6,10 @@
 end_of_line = lf
 insert_final_newline = true
 
-[*.{c,cc,h,hh}]
-indent_size = 2
-indent_style = space
+[*.{c,cc,h,hh,rl}]
 tab_width = 8
+indent_size = 2
+indent_style = tab # should be space
 
 [*.{py,sh}]
 indent_style = tab
diff --git a/.travis.yml b/.travis.yml
index 3e77b26..703fac2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,8 +6,6 @@
 env:
   global:
     - CPPFLAGS=""
-    - CFLAGS="-Werror -Werror=unused -Werror=unused-function"
-    - CXXFLAGS="-Werror -Werror=unused -Werror=unused-function -Wno-deprecated-register" # glib uses register and clang raises a warning
     - CONFIGURE_OPTS="--with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2"
     - NOCONFIGURE=1
     # COVERITY_SCAN_TOKEN
@@ -18,9 +16,10 @@
     - 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"
+        # Remove the following three lines when Travis updates its distro
+        - export PKG_CONFIG_PATH="$HOME/.local/lib/pkgconfig"
+        - export LD_LIBRARY_PATH="$HOME/.local/lib"
+        - bash .ci/build-freetype.sh
 
         - ./autogen.sh
         - ./configure $CONFIGURE_OPTS --enable-gtk-doc --enable-code-coverage
@@ -36,35 +35,24 @@
     - 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"
+        # Remove the following three lines when Travis updates its distro
+        - export PKG_CONFIG_PATH="$HOME/.local/lib/pkgconfig"
+        - export LD_LIBRARY_PATH="$HOME/.local/lib"
+        - bash .ci/build-freetype.sh
 
         - ./autogen.sh
         - ./configure $CONFIGURE_OPTS
         - make
         - make check || .ci/fail.sh
 
-    - os: osx
-      compiler: clang
-      install:
-        - brew update;
-          # Workaround Travis/brew bug
-        - brew uninstall libtool && brew install libtool
-        - brew install ragel freetype glib gobject-introspection cairo graphite2 || true
-        - brew upgrade icu4c || true
-        - export PATH="/usr/local/opt/icu4c/sbin:/usr/local/opt/icu4c/bin:$PATH"
-        - export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig"
-      script:
-        - ./autogen.sh
-        - ./configure $CONFIGURE_OPTS --with-coretext
-        - make
-        - make check || .ci/fail.sh
-
 notifications:
   irc: "irc.freenode.org#harfbuzz"
   email: harfbuzz-bots-chatter@googlegroups.com
 
+cache:
+  directories:
+    - /home/travis/.local
+
 addons:
   apt:
     packages:
diff --git a/AUTHORS b/AUTHORS
index 0763761..83c0c66 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,11 +1,14 @@
 Behdad Esfahbod
+David Corbett
 David Turner
 Ebrahim Byagowi
+Garret Rieger
 Jonathan Kew
 Khaled Hosny
 Lars Knoll
 Martin Hosken
 Owen Taylor
+Roderick Sheeter
 Roozbeh Pournader
 Simon Hausmann
 Werner Lemberg
diff --git a/Android.bp b/Android.bp
index 95d9b70..9f29062 100644
--- a/Android.bp
+++ b/Android.bp
@@ -67,8 +67,10 @@
         "src/hb-buffer.cc",
         "src/hb-common.cc",
         "src/hb-face.cc",
+        "src/hb-fallback-shape.cc",
         "src/hb-font.cc",
         "src/hb-icu.cc",
+        "src/hb-number.cc",
         "src/hb-ot-cff1-table.cc",
         "src/hb-ot-cff2-table.cc",
         "src/hb-ot-face.cc",
@@ -76,6 +78,7 @@
         "src/hb-ot-layout.cc",
         "src/hb-ot-map.cc",
         "src/hb-ot-math.cc",
+        "src/hb-ot-metrics.cc",
         "src/hb-ot-shape-complex-arabic.cc",
         "src/hb-ot-shape-complex-default.cc",
         "src/hb-ot-shape-complex-hangul.cc",
@@ -98,8 +101,8 @@
         "src/hb-shape.cc",
         "src/hb-shaper.cc",
         "src/hb-static.cc",
+        "src/hb-ucd.cc",
         "src/hb-unicode.cc",
-        "src/hb-warning.cc",
     ],
 
     target: {
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b6241e9..2a8fd8b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -35,7 +35,6 @@
 ## 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)
@@ -44,6 +43,7 @@
 endif ()
 if (WIN32)
   option(HB_HAVE_UNISCRIBE "Enable Uniscribe shaper backend on Windows" OFF)
+  option(HB_HAVE_GDI "Enable GDI integration helpers on Windows" OFF)
   option(HB_HAVE_DIRECTWRITE "Enable DirectWrite shaper backend on Windows" OFF)
 endif ()
 option(HB_BUILD_UTILS "Build harfbuzz utils, needs cairo, freetype, and glib properly be installed" OFF)
@@ -70,7 +70,6 @@
 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)
@@ -79,6 +78,7 @@
   set (HB_HAVE_GRAPHITE2 ON)
   if (WIN32)
     set (HB_HAVE_UNISCRIBE ON)
+    set (HB_HAVE_GDI ON)
     set (HB_HAVE_DIRECTWRITE ON)
   elseif (APPLE)
     set (HB_HAVE_CORETEXT ON)
@@ -90,8 +90,6 @@
   ${PROJECT_BINARY_DIR}/src
 )
 
-add_definitions(-DHAVE_FALLBACK)
-
 # We need PYTHON_EXECUTABLE to be set for running the tests...
 include (FindPythonInterp)
 
@@ -110,7 +108,7 @@
 if (UNIX)
   list(APPEND CMAKE_REQUIRED_LIBRARIES m)
 endif ()
-check_funcs(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l round)
+check_funcs(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l roundf)
 check_include_file(unistd.h HAVE_UNISTD_H)
 if (${HAVE_UNISTD_H})
   add_definitions(-DHAVE_UNISTD_H)
@@ -144,8 +142,8 @@
 
 ## Extract variables from Makefile files
 function (extract_make_variable variable makefile_source)
-  string(REGEX MATCH "${variable} = ([^$]+)\\$" temp ${makefile_source})
-  string(REGEX MATCHALL "[^ \n\t\\]+" listVar ${CMAKE_MATCH_1})
+  string(REGEX MATCH "${variable} = ([^$]+)\\$" temp "${makefile_source}")
+  string(REGEX MATCHALL "[^ \n\t\\]+" listVar "${CMAKE_MATCH_1}")
   set (${variable} ${listVar} PARENT_SCOPE)
 endfunction ()
 
@@ -160,14 +158,9 @@
 
 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_SUBSET_sources ${SRCSOURCES})
 add_prefix_to_list(HB_SUBSET_sources "${PROJECT_SOURCE_DIR}/src/")
@@ -191,9 +184,6 @@
 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})
@@ -202,61 +192,12 @@
 set (HB_VERSION_MINOR ${CMAKE_MATCH_3})
 set (HB_VERSION_MICRO ${CMAKE_MATCH_4})
 
-
-## Define ragel tasks
-# if (NOT IN_HB_DIST)
-#  foreach (ragel_output IN ITEMS ${HB_BASE_RAGEL_GENERATED_sources})
-#    string(REGEX MATCH "([^/]+)\\.hh" temp ${ragel_output})
-#    set (target_name ${CMAKE_MATCH_1})
-#    add_custom_command(OUTPUT ${ragel_output}
-#      COMMAND ${RAGEL} -G2 -o ${ragel_output} ${PROJECT_SOURCE_DIR}/src/${target_name}.rl -I ${PROJECT_SOURCE_DIR} ${ARGN}
-#      DEPENDS ${PROJECT_SOURCE_DIR}/src/${target_name}.rl
-#    )
-#    add_custom_target(harfbuzz_${target_name} DEPENDS ${PROJECT_BINARY_DIR}/src/${target_name})
-#  endforeach ()
-
-#  mark_as_advanced(RAGEL)
-# endif ()
-
-
-## Generate hb-version.h
-# if (NOT IN_HB_DIST)
-#  set (HB_VERSION_H_IN "${PROJECT_SOURCE_DIR}/src/hb-version.h.in")
-#  set (HB_VERSION_H "${PROJECT_BINARY_DIR}/src/hb-version.h")
-#  set_source_files_properties("${HB_VERSION_H}" PROPERTIES GENERATED true)
-#  configure_file("${HB_VERSION_H_IN}" "${HB_VERSION_H}.tmp" @ONLY)
-#  execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different
-#    "${HB_VERSION_H}.tmp"
-#    "${HB_VERSION_H}"
-#  )
-#  file(REMOVE "${HB_VERSION_H}.tmp")
-# endif ()
-
-
 ## Define sources and headers of the project
-set (project_sources
-  ${HB_BASE_sources}
-  ${HB_BASE_RAGEL_GENERATED_sources}
-
-  ${HB_FALLBACK_sources}
-)
-
-set (subset_project_sources
-  ${HB_SUBSET_sources}
-)
-
+set (project_sources ${PROJECT_SOURCE_DIR}/src/harfbuzz.cc) # use amalgam source
+set (subset_project_sources ${HB_SUBSET_sources})
 set (project_extra_sources)
-
-set (project_headers
-  #${HB_VERSION_H}
-
-  ${HB_BASE_headers}
-)
-
-set (subset_project_headers
-  ${HB_SUBSET_headers}
-)
-
+set (project_headers ${HB_BASE_headers})
+set (subset_project_headers ${HB_SUBSET_headers})
 
 ## Find and include needed header folders and libraries
 if (HB_HAVE_FREETYPE)
@@ -269,7 +210,6 @@
   include_directories(AFTER ${FREETYPE_INCLUDE_DIRS})
   add_definitions(-DHAVE_FREETYPE=1)
 
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-ft.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-ft.h)
 
   # So check_funcs can find its headers
@@ -287,7 +227,6 @@
 
   include_directories(${GRAPHITE2_INCLUDE_DIR})
 
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-graphite2.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-graphite2.h)
 
   list(APPEND THIRD_PARTY_LIBS ${GRAPHITE2_LIBRARY})
@@ -295,14 +234,6 @@
   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)
 
@@ -316,7 +247,6 @@
 
   include_directories(${GLIBCONFIG_INCLUDE_DIR} ${GLIB_INCLUDE_DIR})
 
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-glib.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-glib.h)
 
   list(APPEND THIRD_PARTY_LIBS ${GLIB_LIBRARIES})
@@ -336,7 +266,6 @@
 
   include_directories(${ICU_INCLUDE_DIR})
 
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-icu.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-icu.h)
 
   list(APPEND THIRD_PARTY_LIBS ${ICU_LIBRARY})
@@ -348,7 +277,6 @@
   # Apple Advanced Typography
   add_definitions(-DHAVE_CORETEXT)
 
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-coretext.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-coretext.h)
 
   if (HB_IOS)
@@ -379,21 +307,21 @@
   endif ()
 endif ()
 
+if (WIN32 AND HB_HAVE_GDI)
+  add_definitions(-DHAVE_GDI)
+  list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-gdi.h)
+  list(APPEND THIRD_PARTY_LIBS gdi32)
+endif ()
+
 if (WIN32 AND HB_HAVE_UNISCRIBE)
   add_definitions(-DHAVE_UNISCRIBE)
-
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-uniscribe.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-uniscribe.h)
-
   list(APPEND THIRD_PARTY_LIBS usp10 gdi32 rpcrt4)
 endif ()
 
 if (WIN32 AND HB_HAVE_DIRECTWRITE)
   add_definitions(-DHAVE_DIRECTWRITE)
-
-  list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-directwrite.cc)
   list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-directwrite.h)
-
   list(APPEND THIRD_PARTY_LIBS dwrite rpcrt4)
 endif ()
 
@@ -501,7 +429,6 @@
   )
 endif ()
 
-
 ## Atomic ops availability detection
 file(WRITE "${PROJECT_BINARY_DIR}/try_compile_intel_atomic_primitives.c"
 "		void memory_barrier (void) { __sync_synchronize (); }
@@ -552,7 +479,7 @@
 if (UNIX OR MINGW)
   # Make symbols link locally
   include (CheckCXXCompilerFlag)
-  check_cxx_compiler_flag(-Bsymbolic-functions CXX_SUPPORTS_FLAG_BSYMB_FUNCS)
+  CHECK_CXX_COMPILER_FLAG(-Bsymbolic-functions CXX_SUPPORTS_FLAG_BSYMB_FUNCS)
   if (CXX_SUPPORTS_FLAG_BSYMB_FUNCS)
     link_libraries(-Bsymbolic-functions)
   endif ()
@@ -570,8 +497,16 @@
     # No threadsafe statics as we do it ourselves
     set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics")
   endif ()
+
+  CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
+  if (COMPILER_SUPPORTS_CXX11)
+    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+  else()
+    message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
+  endif()
 endif ()
 
+
 ## Define harfbuzz-gobject library
 if (HB_HAVE_GOBJECT)
   add_library(harfbuzz-gobject
@@ -602,7 +537,6 @@
 endif ()
 
 if (HB_HAVE_INTROSPECTION)
-
   find_package(PkgConfig)
   pkg_check_modules(PC_GI QUIET gobject-introspection-1.0)
 
@@ -834,21 +768,9 @@
   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 ()
-
-
 if (HB_BUILD_TESTS)
   ## src/ executables
-  foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges)
+  foreach (prog main test test-gsub-would-substitute test-gpos-size-params test-buffer-serialize test-unicode-ranges) # hb-ot-tag
     set (prog_name ${prog})
     if (${prog_name} STREQUAL "test")
       # test can not be used as a valid executable name on cmake, lets special case it
@@ -857,7 +779,7 @@
     add_executable(${prog_name} ${PROJECT_SOURCE_DIR}/src/${prog}.cc)
     target_link_libraries(${prog_name} harfbuzz ${THIRD_PARTY_LIBS})
   endforeach ()
-  set_target_properties(hb-ot-tag PROPERTIES COMPILE_FLAGS "-DMAIN")
+  # set_target_properties(hb-ot-tag PROPERTIES COMPILE_FLAGS "-DMAIN")
 
   ## Tests
   if (UNIX OR MINGW)
diff --git a/CONFIG.md b/CONFIG.md
new file mode 100644
index 0000000..46971b0
--- /dev/null
+++ b/CONFIG.md
@@ -0,0 +1,151 @@
+# Configuring HarfBuzz
+
+Most of the time you will not need any custom configuration.  The configuration
+options provided by `configure` or `cmake` should be enough.  In particular,
+if you just want HarfBuzz library plus hb-shape / hb-view utilities, make sure
+FreeType and Cairo are available and found during configuration.
+
+If you are building for distribution, you should more carefully consider whether
+you need Glib, ICU, Graphite2, as well as CoreText / Uniscribe / DWrite.  Make
+sure the relevant ones are enabled.
+
+If you are building for custom environment (embedded, downloadable app, etc)
+where you mostly just want to call `hb_shape()` and the binary size of the
+resulting library is very important to you, the rest of this file guides you
+through your options to disable features you may not need, in exchange for
+binary size savings.
+
+## Compiler Options
+
+Make sure you build with your compiler's "optimize for size" option.  On `gcc`
+this is `-Os`, and can be enabled by passing `CXXFLAGS=-Os` either to `configure`
+(sticky) or to `make` (non-sticky).  On clang there is an even more extreme flag,
+`-Oz`.
+
+HarfBuzz heavily uses inline functions and the optimize-size flag can make the
+library smaller by 20% or more.  Moreover, sometimes, based on the target CPU,
+the optimize-size builds perform *faster* as well, thanks to lower code
+footprint and caching effects.  So, definitely try that even if size is not
+extremely tight but you have a huge application.  For example, Chrome does
+that.  Note that this configuration also automatically enables certain internal
+optimizations.  Search for `HB_OPTIMIZE_SIZE` for details, if you are using
+other compilers, or continue reading.
+
+Another compiler option to consider is "link-time optimization", also known as
+'lto'.  To enable that, with `gcc` or `clang`, add `-flto` to both `CXXFLAGS`
+and `LDFLAGS`, either on `configure` invocation (sticky) or on `make` (non-sticky).
+This, also, can have a huge impact on the final size, 20% or more.
+
+Finally, if you are making a static library build or otherwise linking the
+library into your app, make sure your linker removes unused functions.  This
+can be tricky and differ from environment to environment, but you definitely
+want to make sure this happens.  Otherwise, every unused public function will
+be adding unneeded bytes to your binary.  The following pointers might come
+handy:
+
+ * https://lwn.net/Articles/741494/ (all of the four-part series)
+ * https://elinux.org/images/2/2d/ELC2010-gc-sections_Denys_Vlasenko.pdf
+
+Combining the above three build options should already shrink your library a lot.
+The rest of this file shows you ways to shrink the library even further at the
+expense of removing functionality (that may not be needed).  The remaining
+options are all enabled by defining pre-processor macros, which can be done
+via `CXXFLAGS` or `CPPFLAGS` similarly.
+
+
+## Unicode-functions
+
+Access to Unicode data can be configured at compile time as well as run-time.
+By default, HarfBuzz ships with its own compact subset of properties from
+Unicode Character Database that it needs.  This is a highly-optimized
+implementation that depending on compile settings (optimize-size or not)
+takes around ~40kb or ~60kb.  Using this implementation (default) is highly
+recommended, as HarfBuzz always ships with data from latest version of Unicode.
+This implementation can be disabled by defining `HB_NO_UCD`.
+
+For example, if you are enabling ICU as a built-in option, or GLib, those
+can provide Unicode data as well, so defining `HB_NO_UCD` might save you
+space without reducing functionality (to the extent that the Unicode version
+of those implementations is recent.)
+
+If, however, you provide your own Unicode data to HarfBuzz at run-time by
+calling `hb_buffer_set_unicode_funcs` on every buffer you create, and you do
+not rely on `hb_unicode_funcs_get_default()` results, you can disable the
+internal implementation by defining both `HB_NO_UCD` and `HB_NO_UNICODE_FUNCS`.
+The latter is needed to guard against accidentally building a library without
+any default Unicode implementations.
+
+
+## Font-functions
+
+Access to certain font functionalities can also be configured at run-time.  By
+default, HarfBuzz uses an efficient internal implementation of OpenType
+functionality for this.  This internal implementation is called `hb-ot-font`.
+All newly-created `hb_font_t` objects by default use `hb-ot-font`.  Using this
+is highly recommended, and is what fonts use by default when they are created.
+
+Most embedded uses will probably use HarfBuzz with FreeType using `hb-ft.h`.
+In that case, or if you otherwise provide those functions by calling
+`hb_font_set_funcs()` on every font you create, you can disable `hb-ot-font`
+without loss of functionality by defining `HB_NO_OT_FONT`.
+
+
+## Shapers
+
+Most HarfBuzz clients use it for the main shaper, called "ot".  However, it
+is legitimate to want to compile HarfBuzz with only another backend, eg.
+CoreText, for example for an iOS app.  For that, you want `HB_NO_OT_SHAPE`.
+If you are going down that route, check if you want `HB_NO_OT`.
+
+This is very rarely what you need.  Make sure you understand exactly what you
+are doing.
+
+Defining `HB_NO_FALLBACK_SHAPE` however is pretty harmless.  That removes the
+(unused) "fallback" shaper.
+
+
+## Thread-safety
+
+By default HarfBuzz builds as a thread-safe library.  The exception is that
+the `HB_TINY` predefined configuring (more below) disables thread-safety.
+
+If you do /not/ need thread-safety in the library (eg. you always call into
+HarfBuzz from the same thread), you can disable thread-safety by defining
+`HB_NO_MT`.  As noted already, this is enabled by `HB_TINY`.
+
+
+## Pre-defined configurations
+
+The [`hb-config.hh`](src/hb-config.hh) internal header supports three
+pre-defined configurations as well grouping of various configuration options.
+The pre-defined configurations are:
+
+  * `HB_MINI`: Disables shaping of AAT as well as legacy fonts.  Ie. it produces
+    a capable OpenType shaper only.
+
+  * `HB_LEAN`: Disables various non-shaping functionality in the library, as well
+    as esoteric or rarely-used shaping features.  See the definition for details.
+
+  * `HB_TINY`: Enables both `HB_MINI` and `HB_LEAN` configurations, as well as
+    disabling thread-safety and debugging, and use even more size-optimized data
+    tables.
+
+
+## Tailoring configuration
+
+Most of the time, one of the pre-defined configuration is exactly what one needs.
+Sometimes, however, the pre-defined configuration cuts out features that might
+be desired in the library.  Unfortunately there is no quick way to undo those
+configurations from the command-line.  But one can add a header file called
+`config-override.h` to undefine certain `HB_NO_*` symbols as desired.  Then
+define `HAVE_CONFIG_OVERRIDE_H` to make `hb-config.hh` include your configuration
+overrides at the end.
+
+
+## Notes
+
+Note that the config option `HB_NO_CFF`, which is enabled by `HB_LEAN` and
+`HB_TINY` does /not/ mean that the resulting library won't work with CFF fonts.
+The library can shape valid CFF fonts just fine, with or without this option.
+This option disables (among other things) the code to calculate glyph exntents
+for CFF fonts.
diff --git a/COPYING b/COPYING
index 9d1056f..0278e60 100644
--- a/COPYING
+++ b/COPYING
@@ -2,7 +2,8 @@
 For parts of HarfBuzz that are licensed under different licenses see individual
 files names COPYING in subdirectories where applicable.
 
-Copyright © 2010,2011,2012  Google, Inc.
+Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019  Google, Inc.
+Copyright © 2019  Facebook, Inc.
 Copyright © 2012  Mozilla Foundation
 Copyright © 2011  Codethink Limited
 Copyright © 2008,2010  Nokia Corporation and/or its subsidiary(-ies)
diff --git a/Makefile.am b/Makefile.am
index eb46cea..2bbd3c5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,12 +9,19 @@
 EXTRA_DIST = \
 	autogen.sh \
 	harfbuzz.doap \
+	README.md \
+	README.mingw.md \
 	README.python.md \
-	README.wine.md \
 	BUILD.md \
+	CONFIG.md \
 	RELEASING.md \
+	TESTING.md \
 	CMakeLists.txt \
 	replace-enum-strings.cmake \
+	mingw-configure.sh \
+	mingw-ldd.py \
+	mingw32.sh \
+	mingw64.sh \
 	$(NULL)
 
 MAINTAINERCLEANFILES = \
@@ -36,7 +43,7 @@
 $(srcdir)/ChangeLog:
 	$(AM_V_GEN) if test -d "$(top_srcdir)/.git"; then \
 	  (GIT_DIR=$(top_srcdir)/.git \
-	   $(GIT) log $(CHANGELOG_RANGE) --stat) | fmt --split-only > $@.tmp \
+	   $(GIT) log $(CHANGELOG_RANGE) --stat) > $@.tmp \
 	  && mv -f $@.tmp "$(srcdir)/ChangeLog" \
 	  || ($(RM) $@.tmp; \
 	      echo Failed to generate ChangeLog, your ChangeLog may be outdated >&2; \
@@ -60,8 +67,6 @@
 	--enable-introspection \
 	$(NULL)
 
-# TODO: Copy infrastructure from cairo
-
 # TAR_OPTIONS is not set as env var for 'make dist'.  How to fix that?
 TAR_OPTIONS = --owner=0 --group=0
 
@@ -70,8 +75,7 @@
 dist-clear-sticky-bits:
 	chmod -R a-s $(distdir)
 
-
-tar_file = $(PACKAGE_TARNAME)-$(VERSION).tar.bz2
+tar_file = $(PACKAGE_TARNAME)-$(VERSION).tar.xz
 sha256_file = $(tar_file).sha256
 gpg_file = $(sha256_file).asc
 $(sha256_file): $(tar_file)
@@ -82,5 +86,18 @@
 
 release-files: $(tar_file) $(sha256_file) $(gpg_file)
 
+dist-win:
+	@case $(host_triplet) in *-w64-mingw32) ;; *) echo "Error: Requires mingw build. See README.mingw.md.">&2; exit 1 ;; esac
+	@DIR=$(PACKAGE_TARNAME)-$(VERSION)-win`case $(host_triplet) in i686-*) echo 32 ;; x86_64-*) echo 64 ;; esac`; \
+	$(RM) -r $$DIR; $(MKDIR_P) $$DIR || exit 1; \
+	cp util/.libs/hb-{shape,view,subset}.exe $$DIR && \
+	$(top_srcdir)/mingw-ldd.py $$DIR/hb-view.exe | grep -v 'not found' | cut -d '>' -f 2 | xargs cp -t $$DIR && \
+	cp src/.libs/libharfbuzz{,-subset}-0.dll $$DIR && \
+	chmod a+x $$DIR/*.{exe,dll} && \
+	$(STRIP) $$DIR/*.{exe,dll} && \
+	zip -r $$DIR.zip $$DIR && \
+	$(RM) -r $$DIR && \
+	echo "$$DIR.zip is ready."
+
 
 -include $(top_srcdir)/git.mk
diff --git a/NEWS b/NEWS
index 890c258..7dde119 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,120 @@
+Overview of changes leading to 2.6.4
+Monday, October 29, 2019
+====================================
+- Small bug fix.
+- Build fixes.
+
+
+Overview of changes leading to 2.6.3
+Monday, October 28, 2019
+====================================
+- Misc small fixes, mostly to build-related issues.
+- New API:
++hb_font_get_nominal_glyphs()
+
+
+Overview of changes leading to 2.6.2
+Monday, September 30, 2019
+====================================
+- Misc small fixes, mostly to build-related issues.
+
+
+Overview of changes leading to 2.6.1
+Thursday, August 22, 2019
+====================================
+- Fix regression with hb_font_create_sub_font scaling introduced in 2.6.0.
+- Change interpretation of font PTEM size / CoreText font size handling.
+  See https://github.com/harfbuzz/harfbuzz/pull/1484
+- hb-ot-font: Prefer symbol cmap subtable if present.
+- Apply 'dist'/'abvm'/'blwm' features to all scripts.
+- Drop experimental DirectWrite API.
+
+
+Overview of changes leading to 2.6.0
+Tuesday, August 13, 2019
+====================================
+- New OpenType metrics, baseline, and metadata table access APIs.
+- New API to set font variations to a named-instance.
+- New hb-gdi.h header and API for creating hb_face_t from HFONT.
+- Amalgam: Provide a single-file harfbuzz.cc file for easier alternate building.
+- More size-reduction configurable options, enabled by HB_TINY.
+- New API:
++hb_font_set_var_named_instance()
++hb_gdi_face_create()
++hb_ot_layout_baseline_tag_t
++hb_ot_layout_get_baseline()
++hb_ot_meta_tag_t
++hb_ot_meta_get_entry_tags()
++hb_ot_meta_reference_entry()
++hb_ot_metrics_tag_t
++hb_ot_metrics_get_position()
++hb_ot_metrics_get_variation()
++hb_ot_metrics_get_x_variation()
++hb_ot_metrics_get_y_variation()
+
+
+Overview of changes leading to 2.5.3
+Wednesday, June 26, 2019
+====================================
+- Fix UCD script data for Unicode 10+ scripts.  This was broken since 2.5.0.
+- More optimizations for HB_TINY.
+
+
+Overview of changes leading to 2.5.2
+Thursday, June 20, 2019
+====================================
+- More hb-config.hh facilities to shrink library size, namely when built as
+  HB_TINY.
+- New documentation of custom configurations in CONFIG.md.
+- Fix build on gcc 4.8.  That's supported again.
+- Universal Shaping Engine improvements thanks to David Corbett.
+- API Changes: Undeprecate some horizontal-kerning API and re-enable in hb-ft,
+  such that Type1 fonts will continue kerning.
+
+
+Overview of changes leading to 2.5.1
+Friday, May 31, 2019
+====================================
+- Fix build with various versions of Visual Studio.
+- Improved documentation, thanks to Nathan Willis.
+- Bugfix in subsetting glyf table.
+- Improved scripts for cross-compiling for Windows using mingw.
+- Rename HB_MATH_GLYPH_PART_FLAG_EXTENDER to HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER.
+  A deprecated macro is added for backwards-compatibility.
+
+
+Overview of changes leading to 2.5.0
+Friday, May 24, 2019
+====================================
+- This release does not include much functional changes, but includes major internal
+  code-base changes.  We now require C++11.  Support for gcc 4.8 and earlier has been
+  dropped.
+- New hb-config.hh facility for compiling smaller library for embedded and web usecases.
+- New Unicode Character Databse implementation that is half the size of previously-used
+  UCDN.
+- Subsetter improvements.
+- Improved documentation, thanks to Nathan Willis.
+- Misc shaping fixes.
+
+
+Overview of changes leading to 2.4.0
+Monday, March 25, 2019
+====================================
+- Unicode 12.
+- Misc fixes.
+- Subsetter improvements.
+- New API:
+HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE
+hb_directwrite_face_create()
+
+
+Overview of changes leading to 2.3.1
+Wednesday, January 30, 2019
+====================================
+- AAT bug fixes.
+- Misc internal housekeeping cleanup.
+
+
 Overview of changes leading to 2.3.0
 Thursday, December 20, 2018
 ====================================
diff --git a/README b/README
deleted file mode 100644
index e50f8f0..0000000
--- a/README
+++ /dev/null
@@ -1,18 +0,0 @@
-[![Travis Build Status](https://travis-ci.org/harfbuzz/harfbuzz.svg)](https://travis-ci.org/harfbuzz/harfbuzz)
-[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/0t0flrxpstj9lb9w?svg=true)](https://ci.appveyor.com/project/harfbuzz/harfbuzz)
-[![CircleCI Build Status](https://circleci.com/gh/harfbuzz/harfbuzz.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz)
-[![Coverity Code Health](https://img.shields.io/coverity/scan/5450.svg)](https://scan.coverity.com/projects/behdad-harfbuzz)
-[![Codacy Code Health](https://api.codacy.com/project/badge/Grade/f17f1708783c447488bc8dd317150eaa)](https://app.codacy.com/app/behdad/harfbuzz)
-[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/master/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz)
-[![Coverals Code Coverage](https://img.shields.io/coveralls/harfbuzz/harfbuzz.svg)](https://coveralls.io/r/harfbuzz/harfbuzz)
-[ABI Tracker](http://abi-laboratory.pro/tracker/timeline/harfbuzz/)
-
-This is HarfBuzz, a text shaping library.
-
-For bug reports, mailing list, and other information please visit:
-
-  http://harfbuzz.org/
-
-For license information, see the file COPYING.
-
-Documentation: https://harfbuzz.github.io
diff --git a/README b/README
new file mode 120000
index 0000000..42061c0
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+README.md
\ No newline at end of file
diff --git a/README.md b/README.md
deleted file mode 120000
index 100b938..0000000
--- a/README.md
+++ /dev/null
@@ -1 +0,0 @@
-README
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e0ef935
--- /dev/null
+++ b/README.md
@@ -0,0 +1,34 @@
+[![Travis Build Status](https://travis-ci.org/harfbuzz/harfbuzz.svg?branch=master)](https://travis-ci.org/harfbuzz/harfbuzz)
+[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/0t0flrxpstj9lb9w?svg=true&branch=master)](https://ci.appveyor.com/project/harfbuzz/harfbuzz)
+[![CircleCI Build Status](https://circleci.com/gh/harfbuzz/harfbuzz/tree/master.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz/tree/master)
+[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/harfbuzz.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html)
+[![Coverity Code Health](https://img.shields.io/coverity/scan/5450.svg)](https://scan.coverity.com/projects/behdad-harfbuzz)
+[![Codacy Code Health](https://api.codacy.com/project/badge/Grade/f17f1708783c447488bc8dd317150eaa)](https://app.codacy.com/app/behdad/harfbuzz)
+[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/master/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz)
+[![Coverals Code Coverage](https://img.shields.io/coveralls/harfbuzz/harfbuzz.svg)](https://coveralls.io/r/harfbuzz/harfbuzz)
+[![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions)
+[ABI Tracker](http://abi-laboratory.pro/tracker/timeline/harfbuzz/)
+
+This is HarfBuzz, a text shaping library.
+
+For bug reports, mailing list, and other information please visit:
+
+  http://harfbuzz.org/
+
+For license information, see [COPYING](COPYING).
+
+For build information, see [BUILD.md](BUILD.md).
+
+For custom configurations, see [CONFIG.md](CONFIG.md).
+
+For test execution, see [TESTING.md](TESTING.md).
+
+Documentation: https://harfbuzz.github.io
+
+
+<details>
+  <summary>Packaging status of HarfBuzz</summary
+
+[![Packaging status](https://repology.org/badge/vertical-allrepos/harfbuzz.svg?header=harfbuzz)](https://repology.org/project/harfbuzz/versions)  
+
+</details>
diff --git a/README.mingw.md b/README.mingw.md
new file mode 100644
index 0000000..76d1a87
--- /dev/null
+++ b/README.mingw.md
@@ -0,0 +1,48 @@
+For the development of HarfBuzz, the Microsoft shaping technology, Uniscribe,
+as a widely used and tested shaper is used as more-or-less OpenType reference
+implementation and that specially is important where OpenType specification
+is or wasn't that clear. For having access to Uniscribe on Linux/macOS these
+steps are recommended:
+
+1. Install Wine from your favorite package manager.  On Fedora that's `dnf install wine`.
+
+2. And `mingw-w64` compiler.
+   With `brew` on macOS, you can have it like `brew install mingw-w64`.
+   On Fedora, with `dnf install mingw32-gcc-c++`, or `dnf install mingw64-gcc-c++` for the
+   64-bit Windows.
+
+3. Install cross-compiled dependency packages.  Alternatively see [^1] below.
+   On Fedora that would be `dnf install mingw32-glib2 mingw32-cairo mingw32-freetype`
+   for 32-bit, or `dnf install mingw64-glib2 mingw64-cairo mingw64-freetype` for 64-bit.
+
+5. `NOCONFIGURE=1 ./autogen.sh && mkdir winbuild && cd winbuild`
+
+6. Run `../mingw32.sh` for 32-bit build, or `../mingw64.sh` for 64-bit.  This configures
+   HarfBuzz for cross-compiling.  It enables Uniscribe backend as well.
+
+7. `make`
+
+Now you can use hb-shape using `wine util/hb-shape.exe` but if you like to shape with
+the Microsoft Uniscribe,
+
+8. Bring a 32bit version of `usp10.dll` for yourself from `C:\Windows\SysWOW64\usp10.dll` of your
+   Windows installation (assuming you have a 64-bit installation, otherwise
+   `C:\Windows\System32\usp10.dll`) that it is not a DirectWrite proxy
+   ([for more info](https://en.wikipedia.org/wiki/Uniscribe)).
+   Rule of thumb, your `usp10.dll` should have a size more than 500kb, otherwise
+   it is designed to work with DirectWrite which Wine can't work with its original one.
+   You want a Uniscribe from Windows 7 or older.
+
+   Put the DLL in the folder you are going to run the next command,
+
+9. `WINEDLLOVERRIDES="usp10=n" wine util/hb-shape.exe fontname.ttf -u 0061,0062,0063 --shaper=uniscribe`
+
+(`0061,0062,0063` means `abc`, use test/shaping/hb-unicode-decode to generate ones you need)
+
+
+[^1] Download and put [this](https://drive.google.com/open?id=0B3_fQkxDZZXXbWltRGd5bjVrUDQ)
+     in your `~/.local/i686-w64-mingw32`.  Then replace all the instances of
+     `/home/behdad/.local/i586-mingw32msvc` and `/home/behdad/.local/i686-w64-mingw32`
+     with `<$HOME>/.local/i686-w64-mingw32` on that folder.
+     (`<$HOME>` replace it with `/home/XXX` or `/Users/XXX` on macOS)
+     You shouldn't replace the instances of those inside binary files.
diff --git a/README.python.md b/README.python.md
index 7cf091a..d9aaf89 100644
--- a/README.python.md
+++ b/README.python.md
@@ -6,21 +6,21 @@
 sudo apt-get install libgirepository1.0-dev
 ```
 
-And then run autogen.sh (if building from git), and then:
+And then run `autogen.sh` (if building from git), and then:
 
 ```bash
 ./configure --with-gobject --enable-introspection
 ```
 
-Make sure that gobject-introspection is enabled then in the final report.
+Make sure that gobject-introspection is reported enabled then in the `configure` script output.
 
 Compile and install.
 
-Make sure you have the installation lib dir in LD_LIBRARY_PATH, as needed
+Make sure you have the installation lib dir in `LD_LIBRARY_PATH`, as needed
 for the linker to find the library.
 
-Then make sure you also have GI_TYPELIB_PATH pointing to the resulting
-$prefix/lib/girepository-* directory.
+Then make sure you also have `GI_TYPELIB_PATH` pointing to the resulting
+`$prefix/lib/girepository-*` directory.
 
 Make sure you have pygobject installed.  Then check that the following
 import works in your Python interpreter:
@@ -30,7 +30,7 @@
 ```
 
 If it does, you are ready to call HarfBuzz from Python!  Congratulations.
-See src/sample.py.
+See [`src/sample.py`](src/sample.py).
 
 The Python API will change.  Let us know on the mailing list if you are
 using it, and send lots of feedback.
diff --git a/README.wine.md b/README.wine.md
deleted file mode 100644
index 799eb63..0000000
--- a/README.wine.md
+++ /dev/null
@@ -1,40 +0,0 @@
-For the development of HarfBuzz, the Microsoft shaping technology, Uniscribe,
-as a widely used and tested shaper is used as more-or-less OpenType reference
-implementation and that specially is important where OpenType specification
-is or wasn't that clear. For having access to Uniscribe on Linux/macOS these
-steps are recommended:
-
-1. Install Wine from your favorite package manager.
-
-2. And `mingw-w64` compiler.
-   With `brew` on macOS, you can have it like `brew install mingw-w64`
-
-3. Download and put [this](https://drive.google.com/open?id=0B3_fQkxDZZXXbWltRGd5bjVrUDQ)
-   on your `~/.local/i686-w64-mingw32`.
-
-4. Replace all the instances of `/home/behdad/.local/i586-mingw32msvc`
-   and `/home/behdad/.local/i686-w64-mingw32` with `<$HOME>/.local/i686-w64-mingw32`
-   on that folder. (`<$HOME>` replace it with `/home/XXX` or `/Users/XXX` on macOS)
-
-   Probably you shouldn't replace the ones are inside binaries.
-
-5. `NOCONFIGURE=1 ./autogen.sh && mkdir winbuild && cd winbuild`
-
-6. `../mingw32.sh --with-uniscribe && cd ..`
-
-7. `make -Cwinbuild`
-
-Now you can use hb-shape using `wine winbuild/util/hb-shape.exe` but if you like to
-to use the original Uniscribe,
-
-8. Bring a 32bit version of `usp10.dll` for yourself from `C:\Windows\SysWOW64\usp10.dll` of your
-   Windows installation (assuming you have a 64-bit installation, otherwise `C:\Windows\System32\usp10.dll`)
-   that it is not a DirectWrite proxy ([for more info](https://en.wikipedia.org/wiki/Uniscribe)).
-   Rule of thumb, your `usp10.dll` should have a size more than 500kb, otherwise
-   it is designed to work with DirectWrite which Wine can't work with its original one.
-
-   Put the dll on the folder you are going to run the next command,
-
-9. `WINEDLLOVERRIDES="usp10=n" wine winbuild/util/hb-shape.exe fontname.ttf -u 0061,0062,0063 --shaper=uniscribe`
-
-(`0061,0062,0063` means `abc`, use test/shaping/hb-unicode-decode to generate ones you need)
diff --git a/RELEASING.md b/RELEASING.md
index 1fd8365..360aea7 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -33,39 +33,34 @@
    That's what happened to 2.0.0 going out with 1.8.0 hb-version.h...  So, that's
    a clue.
 
-7. "make release-files".  Enter your GPG password.  This creates a sha256 hash
-   and signs it.
-
-8. Now that you have release files, commit NEWS, configure.ac, and src/hb-version.h,
+7. Now that you have release files, commit NEWS, configure.ac, and src/hb-version.h,
    as well as any REPLACEME changes you made.  The commit message is simply the
    release number.  Eg. "1.4.7"
 
+8. "make dist" again to get a tarball with your new commit in the ChangeLog.  Then
+   "make release-files".  Enter your GPG password.  This creates a sha256 hash
+   and signs it.  Check the size of the three resulting files.
+
 9. Tag the release and sign it: Eg. "git tag -s 1.4.7 -m 1.4.7".  Enter your
    GPG password again.
 
 10. Build win32 bundle.
 
-   a. Put contents of [this](https://drive.google.com/open?id=0B3_fQkxDZZXXbWltRGd5bjVrUDQ) on your `~/.local/i686-w64-mingw32`,
+   a. Build Win32 binaries.  See [README.mingw.md](README.mingw.md).
 
-   b. Run `../mingw32.sh --with-uniscribe` script 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.
+   b. Run "make dist-win" to build Win32 bundle.
 
 11. 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 1592693 Jul 18 11:25 harfbuzz-1.4.7.tar.xz
+-rw-r--r--  1 behdad eng      89 Jul 18 11:34 harfbuzz-1.4.7.tar.xz.sha256
+-rw-r--r--  1 behdad eng     339 Jul 18 11:34 harfbuzz-1.4.7.tar.xz.sha256.asc
 -rw-r--r--  1 behdad eng 2895619 Jul 18 11:34 harfbuzz-1.4.7-win32.zip
 ```
 
-12. While doing that, quickly double-check the size of the .tar.bz2 and .zip
+12. While doing that, quickly double-check the size of the .tar.xz 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.
@@ -75,39 +70,3 @@
 
 14. 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/TESTING.md b/TESTING.md
new file mode 100644
index 0000000..94be3a0
--- /dev/null
+++ b/TESTING.md
@@ -0,0 +1,86 @@
+## Build & Run
+
+Depending on what area you are working in change or add `HB_DEBUG_<whatever>`.
+Values defined in `hb-debug.hh`.
+
+```shell
+# quick sanity check
+time (make -j4 CPPFLAGS='-DHB_DEBUG_SUBSET=100' \
+  && (make -j4 -C test/api check || cat test/api/test-suite.log))
+
+# slower sanity check
+time (make -j4 CPPFLAGS='-DHB_DEBUG_SUBSET=100' \
+   && make -j4 -C src check \
+   && make -j4 -C test/api check \
+   && make -j4 -C test/subset check)
+
+# confirm you didn't break anything else
+time (make -j4 CPPFLAGS='-DHB_DEBUG_SUBSET=100' \
+  && make -j4 check)
+
+# often catches files you didn't add, e.g. test fonts to EXTRA_DIST
+make distcheck
+```
+
+### Run tests with asan
+
+**NOTE**: this sometimes yields harder to read results than the full fuzzer
+
+```shell
+# For nice symbols tell asan how to symoblize. Note that it doesn't like versioned copies like llvm-symbolizer-3.8
+# export ASAN_SYMBOLIZER_PATH=path to version-less llvm-symbolizer
+# ex
+export ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-3.8/bin/llvm-symbolizer
+
+./configure CC=clang CXX=clang++ CPPFLAGS=-fsanitize=address LDFLAGS=-fsanitize=address
+# make/run tests as usual
+```
+
+### Debug with GDB
+
+```
+cd ./util
+../libtool --mode=execute gdb --args ./hb-subset ...
+```
+
+### Enable Debug Logging
+
+```shell
+# make clean if you previously build w/o debug logging
+make CPPFLAGS=-DHB_DEBUG_SUBSET=100
+```
+
+## Build and Test via CMake
+
+Note: You'll need to first install ninja-build via apt-get.
+
+```shell
+cd harfbuzz
+mkdir buid
+cmake -DHB_CHECK=ON -Bbuild -H. -GNinja && ninja -Cbuild && CTEST_OUTPUT_ON_FAILURE=1 ninja -Cbuild test
+```
+## Test with the Fuzzer
+
+```shell
+# push your changs to a branch on googlefonts/harfbuzz
+# In a local copy of oss-fuzz, edit projects/harfbuzz/Dockerfile
+# Change the git clone to pull your branch
+
+# Do this periodically
+sudo python infra/helper.py build_image harfbuzz
+
+# Do these to update/run
+sudo python infra/helper.py build_fuzzers --sanitizer address harfbuzz
+sudo python infra/helper.py run_fuzzer harfbuzz hb-subset-fuzzer
+```
+
+## Profiling
+
+```
+make clean
+./configure CXXFLAGS="-fno-omit-frame-pointer -g"
+make
+perf record -o <perf output file> -g <command to run>
+perf report -i<perf output file>
+```
+
diff --git a/THANKS b/THANKS
index 940cfde..88cb7e9 100644
--- a/THANKS
+++ b/THANKS
@@ -1,6 +1,6 @@
 Bradley Grainger
-Khaled Hosny
 Kenichi Ishibashi
+Ivan Kuckir <https://photopea.com/>
 Ryan Lortie
 Jeff Muizelaar
 suzuki toshiya
diff --git a/appveyor.yml b/appveyor.yml
index 21d4ea7..7fbcf49 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -2,16 +2,18 @@
 
 environment:
   matrix:
-    - compiler: msvc
-      generator: Visual Studio 14
-      platform: Win32
-      configuration: Debug
-      triplet: x86-windows
-    - compiler: msvc
-      generator: Visual Studio 14 Win64
-      platform: x64
-      configuration: Debug
-      triplet: x64-windows
+
+    #- compiler: msvc
+    #  generator: Visual Studio 14
+    #  platform: Win32
+    #  configuration: Debug
+    #  triplet: x86-windows
+
+    #- compiler: msvc
+    #  generator: Visual Studio 14 Win64
+    #  platform: x64
+    #  configuration: Debug
+    #  triplet: x64-windows
 
     - compiler: msvc
       generator: Visual Studio 14 ARM
@@ -19,10 +21,35 @@
       configuration: Debug
 
 
+    # Build only
+
+    #- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
+    #  compiler: msvc2
+    #  generator: Visual Studio 12
+    #  platform: Win32
+    #  configuration: Release
+    #  triplet: x86-windows
+
+    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+      compiler: msvc2
+      generator: Visual Studio 15
+      platform: Win32
+      configuration: Release
+      triplet: x86-windows
+
+    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      compiler: msvc2
+      generator: Visual Studio 16
+      platform: Win32
+      configuration: Release
+      triplet: x86-windows
+
+
     - compiler: msys2
       MINGW_PREFIX: /mingw64
       MINGW_CHOST: x86_64-w64-mingw32
       MSYS2_ARCH: x86_64
+
     - compiler: msys2
       MINGW_PREFIX: /mingw32
       MINGW_CHOST: i686-w64-mingw32
@@ -30,30 +57,26 @@
 
 
 install:
-# - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm --force -Sy && pacman --noconfirm --force -S pacman-mirrors && pacman --force -Syu --noconfirm"'
-  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm --force -S --needed mingw-w64-$MSYS2_ARCH-{gcc,freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config,python2}"'
-  - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-ragel"
-  - set PATH=%PATH%;C:\msys64\mingw64\bin # msys2 is added just for having "ragel" on PATH
-  - 'if "%compiler%"=="cygwin" %CYGWIN_PREFIX%\setup-%CYGWIN_ARCH%.exe -g -q -P cygwin-devel,libfreetype-devel,libcairo-devel,libicu-devel,gcc,gcc-g++,gobject-introspection,libglib2.0-devel,libgraphite2-devel,pkg-config,python2'
+  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm --force -S --needed mingw-w64-$MSYS2_ARCH-{gcc,freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config,python2,ragel}"'
   - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" vcpkg install glib:%triplet% freetype:%triplet% cairo:%triplet%'
 
 build_script:
-  - 'if "%compiler%"=="msvc" md build'
-  - 'if "%compiler%"=="msvc" cd build'
+  - 'if "%compiler%"=="msvc" if "%platform%"=="ARM" cmake -Bbuild -H. -DHB_HAVE_UNISCRIBE=ON -DHB_HAVE_DIRECTWRITE=ON -G "%generator%"'
+  - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" cmake -Bbuild -H. -DHB_HAVE_UNISCRIBE=ON -DHB_HAVE_DIRECTWRITE=ON -DHB_HAVE_GLIB=ON -DHB_HAVE_FREETYPE=ON -DHB_BUILD_UTILS=ON -G "%generator%" -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake'
+
   - 'if "%compiler%"=="msvc" set PATH=%PATH%;C:\Program Files (x86)\MSBuild\14.0\Bin'
-
-  - 'if "%compiler%"=="msvc" if "%platform%"=="ARM" cmake -DHB_HAVE_UNISCRIBE=ON -DHB_HAVE_DIRECTWRITE=ON -G "%generator%" ../'
-  - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" cmake -DHB_HAVE_UNISCRIBE=ON -DHB_HAVE_DIRECTWRITE=ON -DHB_HAVE_GLIB=ON -DHB_HAVE_FREETYPE=ON -DHB_BUILD_UTILS=ON -G "%generator%" -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake ../'
-
+  - 'if "%compiler%"=="msvc" cd build'
   - '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%"=="msvc2" cmake -G "%generator%" -Bbuild -H.'
+  - 'if "%compiler%"=="msvc2" cmake --build build --config %configuration%'
+
   - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "curl https://raw.githubusercontent.com/mirror/mingw-w64/023eb04c396d4e8d8fcf604cfababc53dae13398/mingw-w64-headers/include/dwrite_1.h > %MINGW_PREFIX%/%MINGW_CHOST%/include/dwrite_1.h"'
-  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER; PATH=$PATH:/mingw64/bin:/mingw32/bin; ./autogen.sh --with-uniscribe --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 --with-directwrite --build=%MINGW_CHOST% --host=%MINGW_CHOST% --prefix=%MINGW_PREFIX%; make -j3 check || .ci/fail.sh"'
+  - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER; PATH=$PATH:/mingw64/bin:/mingw32/bin; ./autogen.sh --with-uniscribe --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 --with-directwrite --with-gdi --build=%MINGW_CHOST% --host=%MINGW_CHOST% --prefix=%MINGW_PREFIX%; make -j3 check || .ci/fail.sh"'
 
 cache:
   - c:\tools\vcpkg\installed\
-  - '%CYGWIN_PREFIX%\var\cache\setup'
 
 notifications:
   - provider: Email
diff --git a/configure.ac b/configure.ac
index 0315537..4125756 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [2.3.0],
+        [2.6.4],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -9,7 +9,7 @@
 AC_CONFIG_SRCDIR([src/harfbuzz.pc.in])
 AC_CONFIG_HEADERS([config.h])
 
-AM_INIT_AUTOMAKE([1.13.0 gnits tar-ustar dist-bzip2 no-dist-gzip -Wall no-define color-tests -Wno-portability])
+AM_INIT_AUTOMAKE([1.13.0 gnits tar-ustar dist-xz no-dist-gzip -Wall no-define color-tests -Wno-portability])
 AM_SILENT_RULES([yes])
 AX_CODE_COVERAGE
 
@@ -23,7 +23,7 @@
 AC_PROG_CC_C99
 AM_PROG_CC_C_O
 AC_PROG_CXX
-dnl AX_CXX_COMPILE_STDCXX(11, noext, optional)
+AX_CXX_COMPILE_STDCXX(11,, optional)
 AC_SYS_LARGEFILE
 PKG_PROG_PKG_CONFIG([0.20])
 AM_MISSING_PROG([RAGEL], [ragel])
@@ -77,13 +77,7 @@
 ])
 
 # Functions and headers
-AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l posix_memalign)
-
-save_libs="$LIBS"
-LIBS="$LIBS -lm"
-AC_CHECK_FUNCS([round], ,[AC_CHECK_DECLS([round], , ,[#include <math.h>])])
-LIBS="$save_libs"
-
+AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l roundf)
 AC_CHECK_HEADERS(unistd.h sys/mman.h xlocale.h stdbool.h)
 
 # Compiler flags
@@ -102,9 +96,6 @@
 	# by overriding CXXFLAGS.
 	CXXFLAGS="-fno-rtti $CXXFLAGS -fno-exceptions -fno-threadsafe-statics"
 
-	# Assorted warnings
-	CXXFLAGS="$CXXFLAGS -Wcast-align"
-
 	case "$host" in
 		*-*-mingw*)
 		;;
@@ -137,9 +128,7 @@
 AM_CONDITIONAL(OS_WIN32, test "$hb_os_win32" = "yes")
 
 have_pthread=false
-if test "$hb_os_win32" = no; then
-	AX_PTHREAD([have_pthread=true])
-fi
+AX_PTHREAD([have_pthread=true])
 if $have_pthread; then
 	AC_DEFINE(HAVE_PTHREAD, 1, [Have POSIX threads])
 fi
@@ -147,14 +136,6 @@
 
 dnl ==========================================================================
 
-have_fallback=true
-if $have_fallback; then
-	AC_DEFINE(HAVE_FALLBACK, 1, [Have simple TrueType Layout backend])
-fi
-AM_CONDITIONAL(HAVE_FALLBACK, $have_fallback)
-
-dnl ===========================================================================
-
 AC_ARG_WITH(glib,
 	[AS_HELP_STRING([--with-glib=@<:@yes/no/auto@:>@],
 			[Use glib @<:@default=auto@:>@])],,
@@ -303,21 +284,6 @@
 
 dnl ===========================================================================
 
-AC_ARG_WITH(ucdn,
-	[AS_HELP_STRING([--with-ucdn=@<:@yes/no@:>@],
-			[Use builtin UCDN library @<:@default=yes@:>@])],,
-	[with_ucdn=yes])
-have_ucdn=false
-if test "x$with_ucdn" = "xyes"; then
-	have_ucdn=true
-fi
-if $have_ucdn; then
-	AC_DEFINE(HAVE_UCDN, 1, [Have UCDN Unicode functions])
-fi
-AM_CONDITIONAL(HAVE_UCDN, $have_ucdn)
-
-dnl ==========================================================================
-
 AC_ARG_WITH(graphite2,
 	[AS_HELP_STRING([--with-graphite2=@<:@yes/no/auto@:>@],
 			[Use the graphite2 library @<:@default=no@:>@])],,
@@ -395,6 +361,28 @@
 
 dnl ===========================================================================
 
+AC_ARG_WITH(gdi,
+	[AS_HELP_STRING([--with-gdi=@<:@yes/no/auto@:>@],
+			[Provide GDI integration helpers @<:@default=no@:>@])],,
+	[with_gdi=no])
+have_gdi=false
+if test "x$with_gdi" = "xyes" -o "x$with_gdi" = "xauto"; then
+	AC_CHECK_HEADERS(windows.h, have_gdi=true)
+fi
+if test "x$with_gdi" = "xyes" -a "x$have_gdi" != "xtrue"; then
+	AC_MSG_ERROR([gdi support requested but not found])
+fi
+if $have_gdi; then
+	GDI_CFLAGS=
+	GDI_LIBS="-lgdi32"
+	AC_SUBST(GDI_CFLAGS)
+	AC_SUBST(GDI_LIBS)
+	AC_DEFINE(HAVE_GDI, 1, [Have GDI library])
+fi
+AM_CONDITIONAL(HAVE_GDI, $have_gdi)
+
+dnl ===========================================================================
+
 AC_ARG_WITH(directwrite,
 	[AS_HELP_STRING([--with-directwrite=@<:@yes/no/auto@:>@],
 			[Use the DirectWrite library (experimental) @<:@default=no@:>@])],,
@@ -410,7 +398,7 @@
 fi
 if $have_directwrite; then
 	DIRECTWRITE_CXXFLAGS=
-	DIRECTWRITE_LIBS="-ldwrite"
+	DIRECTWRITE_LIBS=
 	AC_SUBST(DIRECTWRITE_CXXFLAGS)
 	AC_SUBST(DIRECTWRITE_LIBS)
 	AC_DEFINE(HAVE_DIRECTWRITE, 1, [Have DirectWrite library])
@@ -500,7 +488,6 @@
 Makefile
 src/Makefile
 src/harfbuzz-config.cmake
-src/hb-ucdn/Makefile
 util/Makefile
 test/Makefile
 test/api/Makefile
@@ -518,12 +505,17 @@
 
 AC_OUTPUT
 
+echo
+echo "C++ compiler version:"
+$CXX --version
+echo
+
 AC_MSG_NOTICE([
 
 Build configuration:
 
 Unicode callbacks (you want at least one):
-	Builtin (UCDN):		${have_ucdn}
+	Builtin			true
 	Glib:			${have_glib}
 	ICU:			${have_icu}
 
@@ -540,6 +532,7 @@
 Platform shapers (not normally needed):
 	CoreText:		${have_coretext}
 	DirectWrite:		${have_directwrite}
+	GDI:			${have_gdi}
 	Uniscribe:		${have_uniscribe}
 
 Other features:
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 9b54b40..f4bf2fd 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -33,7 +33,7 @@
 
 # Header files or dirs to ignore when scanning. Use base file/dir names
 # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
-IGNORE_HFILES=`cd $(top_srcdir)/src; find . -path './hb-*/*.h' | sed 's@^.*/@@'`
+IGNORE_HFILES=`cd $(top_srcdir)/src; find . -path './*/*.h' | sed 's@^.*/@@'`
 if HAVE_GOBJECT
 else
 IGNORE_HFILES+=hb-gobject.h hb-gobject-enums.h hb-gobject-structs.h
@@ -75,12 +75,14 @@
 	usermanual-what-is-harfbuzz.xml \
 	usermanual-install-harfbuzz.xml \
 	usermanual-getting-started.xml \
+	usermanual-glyph-information.xml \
 	usermanual-shaping-concepts.xml \
+	usermanual-object-model.xml \
 	usermanual-buffers-language-script-and-direction.xml \
 	usermanual-fonts-and-faces.xml \
-	usermanual-clusters.xml \
 	usermanual-opentype-features.xml \
-	usermanual-glyph-information.xml \
+	usermanual-clusters.xml \
+	usermanual-utilities.xml \
 	version.xml
 
 # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
diff --git a/docs/harfbuzz-docs.xml b/docs/harfbuzz-docs.xml
index 2735338..433c206 100644
--- a/docs/harfbuzz-docs.xml
+++ b/docs/harfbuzz-docs.xml
@@ -20,11 +20,7 @@
 
       <para>
 	The canonical source-code tree is available at
-        <ulink
-	    url="https://github.com/harfbuzz/harfbuzz">github.com/harfbuzz/harfbuzz</ulink>
-        and is also available at
-        <ulink
-	    url="http://cgit.freedesktop.org/harfbuzz/">cgit.freedesktop.org/harfbuzz</ulink>.
+        <ulink url="https://github.com/harfbuzz/harfbuzz">github.com/harfbuzz/harfbuzz</ulink>.
 	See <xref linkend="download" endterm="download.title"/> for
 	release tarballs.
       </para>
@@ -37,11 +33,12 @@
       <xi:include href="usermanual-install-harfbuzz.xml"/>
       <xi:include href="usermanual-getting-started.xml"/>
       <xi:include href="usermanual-shaping-concepts.xml"/>
+      <xi:include href="usermanual-object-model.xml"/>
       <xi:include href="usermanual-buffers-language-script-and-direction.xml"/>
       <xi:include href="usermanual-fonts-and-faces.xml"/>
-      <xi:include href="usermanual-clusters.xml"/>
       <xi:include href="usermanual-opentype-features.xml"/>
-      <xi:include href="usermanual-glyph-information.xml"/>
+      <xi:include href="usermanual-clusters.xml"/>
+      <xi:include href="usermanual-utilities.xml"/>
   </part>
 
   <part>
@@ -136,6 +133,11 @@
       <index id="api-index-full"><title>API Index</title><xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include></index>
       <index id="deprecated-api-index" role="deprecated"><title>Index of deprecated API</title><xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include></index>
 
+      <index id="api-index-2-6-0" role="2.6.0"><title>Index of new symbols in 2.6.0</title><xi:include href="xml/api-index-2.6.0.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-2-5-0" role="2.5.0"><title>Index of new symbols in 2.5.0</title><xi:include href="xml/api-index-2.5.0.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-2-4-0" role="2.4.0"><title>Index of new symbols in 2.4.0</title><xi:include href="xml/api-index-2.4.0.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-2-3-0" role="2.3.0"><title>Index of new symbols in 2.3.0</title><xi:include href="xml/api-index-2.3.0.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-2-2-0" role="2.2.0"><title>Index of new symbols in 2.2.0</title><xi:include href="xml/api-index-2.2.0.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-2-1-0" role="2.1.0"><title>Index of new symbols in 2.1.0</title><xi:include href="xml/api-index-2.1.0.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-2-0-0" role="2.0.0"><title>Index of new symbols in 2.0.0</title><xi:include href="xml/api-index-2.0.0.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-9-0" role="1.9.0"><title>Index of new symbols in 1.9.0</title><xi:include href="xml/api-index-1.9.0.xml"><xi:fallback /></xi:include></index>
@@ -149,7 +151,6 @@
       <index id="api-index-1-5-0" role="1.5.0"><title>Index of new symbols in 1.5.0</title><xi:include href="xml/api-index-1.5.0.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-4-3" role="1.4.3"><title>Index of new symbols in 1.4.3</title><xi:include href="xml/api-index-1.4.3.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-4-2" role="1.4.2"><title>Index of new symbols in 1.4.2</title><xi:include href="xml/api-index-1.4.2.xml"><xi:fallback /></xi:include></index>
-      <index id="api-index-1-4-0" role="1.4.0"><title>Index of new symbols in 1.4.0</title><xi:include href="xml/api-index-1.4.0.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-3-3" role="1.3.3"><title>Index of new symbols in 1.3.3</title><xi:include href="xml/api-index-1.3.3.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-2-3" role="1.2.3"><title>Index of new symbols in 1.2.3</title><xi:include href="xml/api-index-1.2.3.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-1-3" role="1.1.3"><title>Index of new symbols in 1.1.3</title><xi:include href="xml/api-index-1.1.3.xml"><xi:fallback /></xi:include></index>
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index fd7682e..c625b92 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -1,6 +1,7 @@
 <SUBSECTION Private>
 HB_H_IN
 HB_OT_H_IN
+HB_AAT_H_IN
 </SECTION>
 
 <SECTION>
@@ -124,11 +125,17 @@
 hb_language_from_string
 hb_language_to_string
 hb_language_get_default
+hb_feature_from_string
+hb_feature_to_string
+hb_variation_from_string
+hb_variation_to_string
 hb_bool_t
 hb_codepoint_t
 hb_destroy_func_t
 hb_direction_t
 hb_language_t
+hb_feature_t
+hb_variation_t
 hb_mask_t
 hb_position_t
 hb_tag_t
@@ -147,6 +154,8 @@
 HB_DIRECTION_IS_VALID
 HB_DIRECTION_IS_VERTICAL
 HB_LANGUAGE_INVALID
+HB_FEATURE_GLOBAL_END
+HB_FEATURE_GLOBAL_START
 <SUBSECTION Private>
 HB_BEGIN_DECLS
 HB_END_DECLS
@@ -171,6 +180,7 @@
 HB_SCRIPT_CANADIAN_ABORIGINAL
 hb_font_funcs_set_glyph_func
 hb_font_get_glyph_func_t
+HB_MATH_GLYPH_PART_FLAG_EXTENDER
 hb_ot_layout_table_choose_script
 hb_ot_layout_table_find_script
 hb_ot_tag_from_language
@@ -187,12 +197,7 @@
 hb_unicode_decompose_compatibility_func_t
 hb_unicode_decompose_compatibility
 hb_unicode_funcs_set_decompose_compatibility_func
-hb_font_funcs_set_glyph_h_kerning_func
 hb_font_funcs_set_glyph_v_kerning_func
-hb_font_get_glyph_h_kerning
-hb_font_get_glyph_h_kerning_func_t
-hb_font_get_glyph_kerning_for_direction
-hb_font_get_glyph_kerning_func_t
 hb_font_get_glyph_v_kerning
 hb_font_get_glyph_v_kerning_func_t
 </SECTION>
@@ -209,6 +214,14 @@
 </SECTION>
 
 <SECTION>
+<FILE>hb-directwrite</FILE>
+hb_directwrite_face_create
+hb_directwrite_face_get_font_face
+<SUBSECTION Private>
+hb_directwrite_shape_experimental_width
+</SECTION>
+
+<SECTION>
 <FILE>hb-face</FILE>
 hb_face_count
 hb_face_t
@@ -255,6 +268,7 @@
 hb_font_funcs_set_glyph_from_name_func
 hb_font_funcs_set_glyph_h_advance_func
 hb_font_funcs_set_glyph_h_advances_func
+hb_font_funcs_set_glyph_h_kerning_func
 hb_font_funcs_set_glyph_h_origin_func
 hb_font_funcs_set_glyph_name_func
 hb_font_funcs_set_glyph_v_advance_func
@@ -284,8 +298,12 @@
 hb_font_get_glyph_h_advance_func_t
 hb_font_get_glyph_h_advances
 hb_font_get_glyph_h_advances_func_t
+hb_font_get_glyph_h_kerning
+hb_font_get_glyph_h_kerning_func_t
 hb_font_get_glyph_h_origin
 hb_font_get_glyph_h_origin_func_t
+hb_font_get_glyph_kerning_for_direction
+hb_font_get_glyph_kerning_func_t
 hb_font_get_glyph_name
 hb_font_get_glyph_name_func_t
 hb_font_get_glyph_origin_for_direction
@@ -321,12 +339,10 @@
 hb_font_set_ptem
 hb_font_set_scale
 hb_font_set_user_data
-hb_variation_t
-hb_variation_from_string
-hb_variation_to_string
 hb_font_set_variations
 hb_font_set_var_coords_design
 hb_font_set_var_coords_normalized
+hb_font_set_var_named_instance
 hb_font_subtract_glyph_origin_for_direction
 hb_font_t
 hb_reference_table_func_t
@@ -355,6 +371,11 @@
 </SECTION>
 
 <SECTION>
+<FILE>hb-gdi</FILE>
+hb_gdi_face_create
+</SECTION>
+
+<SECTION>
 <FILE>hb-glib</FILE>
 hb_glib_get_unicode_funcs
 hb_glib_script_from_script
@@ -527,6 +548,7 @@
 HB_OT_TAG_GPOS
 HB_OT_TAG_GSUB
 HB_OT_TAG_JSTF
+hb_ot_layout_baseline_tag_t
 hb_ot_layout_collect_lookups
 hb_ot_layout_collect_features
 hb_ot_layout_feature_get_characters
@@ -534,6 +556,7 @@
 hb_ot_layout_feature_get_name_ids
 hb_ot_layout_feature_with_variations_get_lookups
 hb_ot_layout_get_attach_points
+hb_ot_layout_get_baseline
 hb_ot_layout_get_glyph_class
 hb_ot_layout_get_glyphs_in_class
 hb_ot_layout_get_ligature_carets
@@ -588,6 +611,22 @@
 </SECTION>
 
 <SECTION>
+<FILE>hb-ot-meta</FILE>
+hb_ot_meta_tag_t
+hb_ot_meta_get_entry_tags
+hb_ot_meta_reference_entry
+</SECTION>
+
+<SECTION>
+<FILE>hb-ot-metrics</FILE>
+hb_ot_metrics_tag_t
+hb_ot_metrics_get_position
+hb_ot_metrics_get_variation
+hb_ot_metrics_get_x_variation
+hb_ot_metrics_get_y_variation
+</SECTION>
+
+<SECTION>
 <FILE>hb-ot-shape</FILE>
 hb_ot_shape_glyphs_closure
 </SECTION>
@@ -649,11 +688,6 @@
 
 <SECTION>
 <FILE>hb-shape</FILE>
-HB_FEATURE_GLOBAL_END
-HB_FEATURE_GLOBAL_START
-hb_feature_t
-hb_feature_from_string
-hb_feature_to_string
 hb_shape
 hb_shape_full
 hb_shape_list_shapers
@@ -715,8 +749,6 @@
 <FILE>hb-uniscribe</FILE>
 hb_uniscribe_font_get_hfont
 hb_uniscribe_font_get_logfontw
-<SUBSECTION Private>
-hb_directwrite_shape_experimental_width
 </SECTION>
 
 <SECTION>
diff --git a/docs/usermanual-buffers-language-script-and-direction.xml b/docs/usermanual-buffers-language-script-and-direction.xml
index 68ce9bd..2865426 100644
--- a/docs/usermanual-buffers-language-script-and-direction.xml
+++ b/docs/usermanual-buffers-language-script-and-direction.xml
@@ -7,29 +7,38 @@
 <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 the HarfBuzz shaper 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.
+    the text that we want and how to customize the properties of the
+    buffer. We'll also look at a piece of lower-level machinery that
+    you will need to understand before proceeding: the functions that
+    HarfBuzz uses to retrieve Unicode information.
+  </para>
+  <para>
+    After shaping is complete, HarfBuzz puts its output back
+    into the buffer. But getting that output requires setting up a
+    face and a font first, so we will look at that in the next chapter
+    instead of here.
   </para>
   <section id="creating-and-destroying-buffers">
     <title>Creating and destroying buffers</title>
     <para>
-      As we saw in our initial example, a buffer is created and
-      initialized with <literal>hb_buffer_create()</literal>. This
+      As we saw in our <emphasis>Getting Started</emphasis> example, a
+      buffer is created and 
+      initialized with <function>hb_buffer_create()</function>. This
       produces a new, empty buffer object, instantiated with some
       default values and ready to accept your Unicode strings.
     </para>
     <para>
-      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>:
+      HarfBuzz manages the memory of objects (such as buffers) that it
+      creates, so you don't have to. When you have finished working on 
+      a buffer, you can call <function>hb_buffer_destroy()</function>:
     </para>
     <programlisting language="C">
-  hb_buffer_t *buffer = hb_buffer_create();
-  ...
-  hb_buffer_destroy(buffer);
-</programlisting>
+      hb_buffer_t *buf = hb_buffer_create();
+      ...
+      hb_buffer_destroy(buf);
+    </programlisting>
     <para>
       This will destroy the object and free its associated memory -
       unless some other part of the program holds a reference to this
@@ -38,46 +47,364 @@
       else destroying it, you should increase its reference count:
     </para>
     <programlisting language="C">
-void somefunc(hb_buffer_t *buffer) {
-  buffer = hb_buffer_reference(buffer);
-  ...
-</programlisting>
+      void somefunc(hb_buffer_t *buf) {
+      buf = hb_buffer_reference(buf);
+      ...
+    </programlisting>
     <para>
       And then decrease it once you're done with it:
     </para>
     <programlisting language="C">
-  hb_buffer_destroy(buffer);
-}
-</programlisting>
+      hb_buffer_destroy(buf);
+      }
+    </programlisting>
+    <para>
+      While we are on the subject of reference-counting buffers, it is
+      worth noting that an individual buffer can only meaningfully be
+      used by one thread at a time.
+    </para>
     <para>
       To throw away all the data in your buffer and start from scratch,
-      call <literal>hb_buffer_reset(buffer)</literal>. If you want to
+      call <function>hb_buffer_reset(buf)</function>. If you want to
       throw away the string in the buffer but keep the options, you can
-      instead call <literal>hb_buffer_clear_contents(buffer)</literal>.
+      instead call <function>hb_buffer_clear_contents(buf)</function>.
     </para>
   </section>
+  
   <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
-      of Unicode codepoints, but your input string is probably in one of
-      the standard Unicode character encodings (UTF-8, UTF-16, UTF-32)
+      of Unicode code points, but your input string is probably in one of
+      the standard Unicode character encodings (UTF-8, UTF-16, or
+      UTF-32). HarfBuzz provides convenience functions that accept
+      each of these encodings:
+      <function>hb_buffer_add_utf8()</function>,
+      <function>hb_buffer_add_utf16()</function>, and
+      <function>hb_buffer_add_utf32()</function>. Other than the
+      character encoding they accept, they function identically.
     </para>
+    <para>
+      You can add UTF-8 text to a buffer by passing in the text array,
+      the array's length, an offset into the array for the first
+      character to add, and the length of the segment to add:
+    </para>
+    <programlisting language="C">
+    hb_buffer_add_utf8 (hb_buffer_t *buf,
+                    const char *text,
+                    int text_length,
+                    unsigned int item_offset,
+                    int item_length)
+    </programlisting>
+    <para>
+      So, in practice, you can say:
+    </para>
+    <programlisting language="C">
+      hb_buffer_add_utf8(buf, text, strlen(text), 0, strlen(text));
+    </programlisting>
+    <para>
+      This will append your new characters to
+      <parameter>buf</parameter>, not replace its existing
+      contents. Also, note that you can use <literal>-1</literal> in
+      place of the first instance of <function>strlen(text)</function>
+      if your text array is NULL-terminated. Similarly, you can also use
+      <literal>-1</literal> as the final argument want to add its full
+      contents.
+    </para>
+    <para>
+      Whatever start <parameter>item_offset</parameter> and
+      <parameter>item_length</parameter> you provide, HarfBuzz will also
+      attempt to grab the five characters <emphasis>before</emphasis>
+      the offset point and the five characters
+      <emphasis>after</emphasis> the designated end. These are the
+      before and after "context" segments, which are used internally
+      for HarfBuzz to make shaping decisions. They will not be part of
+      the final output, but they ensure that HarfBuzz's
+      script-specific shaping operations are correct. If there are
+      fewer than five characters available for the before or after
+      contexts, HarfBuzz will just grab what is there.
+    </para>
+    <para>
+      For longer text runs, such as full paragraphs, it might be
+      tempting to only add smaller sub-segments to a buffer and
+      shape them in piecemeal fashion. Generally, this is not a good
+      idea, however, because a lot of shaping decisions are
+      dependent on this context information. For example, in Arabic
+      and other connected scripts, HarfBuzz needs to know the code
+      points before and after each character in order to correctly
+      determine which glyph to return.
+    </para>
+    <para>
+      The safest approach is to add all of the text available, then
+      use <parameter>item_offset</parameter> and
+      <parameter>item_length</parameter> to indicate which characters you
+      want shaped, so that HarfBuzz has access to any context.
+    </para>
+    <para>
+      You can also add Unicode code points directly with
+      <function>hb_buffer_add_codepoints()</function>. The arguments
+      to this function are the same as those for the UTF
+      encodings. But it is particularly important to note that
+      HarfBuzz does not do validity checking on the text that is added
+      to a buffer. Invalid code points will be replaced, but it is up
+      to you to do any deep-sanity checking necessary.
+    </para>
+    
   </section>
+  
   <section id="setting-buffer-properties">
     <title>Setting buffer properties</title>
     <para>
+      Buffers containing input characters still need several
+      properties set before HarfBuzz can shape their text correctly.
     </para>
-  </section>
-  <section id="what-about-the-other-scripts">
-    <title>What about the other scripts?</title>
     <para>
+      Initially, all buffers are set to the
+      <literal>HB_BUFFER_CONTENT_TYPE_INVALID</literal> content
+      type. After adding text, the buffer should be set to
+      <literal>HB_BUFFER_CONTENT_TYPE_UNICODE</literal> instead, which
+      indicates that it contains un-shaped input
+      characters. After shaping, the buffer will have the
+      <literal>HB_BUFFER_CONTENT_TYPE_GLYPHS</literal> content type.
+    </para>
+    <para>
+      <function>hb_buffer_add_utf8()</function> and the
+      other UTF functions set the content type of their buffer
+      automatically. But if you are reusing a buffer you may want to
+      check its state with
+      <function>hb_buffer_get_content_type(buffer)</function>. If
+      necessary you can set the content type with
+    </para>
+    <programlisting language="C">
+      hb_buffer_set_content_type(buf, HB_BUFFER_CONTENT_TYPE_UNICODE);
+    </programlisting>
+    <para>
+      to prepare for shaping.
+    </para>
+    <para>
+      Buffers also need to carry information about the script,
+      language, and text direction of their contents. You can set
+      these properties individually:
+    </para>
+    <programlisting language="C">
+      hb_buffer_set_direction(buf, HB_DIRECTION_LTR);
+      hb_buffer_set_script(buf, HB_SCRIPT_LATIN);
+      hb_buffer_set_language(buf, hb_language_from_string("en", -1));
+    </programlisting>
+    <para>
+      However, since these properties are often the repeated for
+      multiple text runs, you can also save them in a
+      <literal>hb_segment_properties_t</literal> for reuse:
+    </para>
+    <programlisting language="C">
+      hb_segment_properties_t *savedprops;
+      hb_buffer_get_segment_properties (buf, savedprops);
+      ...
+      hb_buffer_set_segment_properties (buf2, savedprops);
+    </programlisting>
+    <para>
+      HarfBuzz also provides getter functions to retrieve a buffer's
+      direction, script, and language properties individually.
+    </para>
+    <para>
+      HarfBuzz recognizes four text directions in
+      <type>hb_direction_t</type>: left-to-right
+      (<literal>HB_DIRECTION_LTR</literal>), right-to-left (<literal>HB_DIRECTION_RTL</literal>),
+      top-to-bottom (<literal>HB_DIRECTION_TTB</literal>), and
+      bottom-to-top (<literal>HB_DIRECTION_BTT</literal>).  For the
+      script property, HarfBuzz uses identifiers based on the
+      <ulink
+      url="https://unicode.org/iso15924/">ISO 15924
+      standard</ulink>. For languages, HarfBuzz uses tags based on the
+      <ulink url="https://tools.ietf.org/html/bcp47">IETF BCP 47</ulink> standard.
+    </para>
+    <para>
+      Helper functions are provided to convert character strings into
+      the necessary script and language tag types.
+    </para>
+    <para>
+      Two additional buffer properties to be aware of are the
+      "invisible glyph" and the replacement code point. The
+      replacement code point is inserted into buffer output in place of
+      any invalid code points encountered in the input. By default, it
+      is the Unicode <literal>REPLACEMENT CHARACTER</literal> code
+      point, <literal>U+FFFD</literal> "&#xFFFD;". You can change this with
+    </para>
+    <programlisting language="C">
+      hb_buffer_set_replacement_codepoint(buf, replacement);
+    </programlisting>
+    <para>
+      passing in the replacement Unicode code point as the
+      <parameter>replacement</parameter> parameter.
+    </para>
+    <para>
+      The invisible glyph is used to replace all output glyphs that
+      are invisible. By default, the standard space character
+      <literal>U+0020</literal> is used; you can replace this (for
+      example, when using a font that provides script-specific
+      spaces) with 
+    </para>
+    <programlisting language="C">
+      hb_buffer_set_invisible_glyph(buf, replacement_glyph);
+    </programlisting>
+    <para>
+      Do note that in the <parameter>replacement_glyph</parameter>
+      parameter, you must provide the glyph ID of the replacement you
+      wish to use, not the Unicode code point.
+    </para>
+    <para>
+      HarfBuzz supports a few additional flags you might want to set
+      on your buffer under certain circumstances. The
+      <literal>HB_BUFFER_FLAG_BOT</literal> and
+      <literal>HB_BUFFER_FLAG_EOT</literal> flags tell HarfBuzz
+      that the buffer represents the beginning or end (respectively)
+      of a text element (such as a paragraph or other block). Knowing
+      this allows HarfBuzz to apply certain contextual font features
+      when shaping, such as initial or final variants in connected
+      scripts.
+    </para>
+    <para>
+      <literal>HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES</literal>
+      tells HarfBuzz not to hide glyphs with the
+      <literal>Default_Ignorable</literal> property in Unicode. This 
+      property designates control characters and other non-printing
+      code points, such as joiners and variation selectors. Normally
+      HarfBuzz replaces them in the output buffer with zero-width
+      space glyphs (using the "invisible glyph" property discussed
+      above); setting this flag causes them to be printed, which can
+      be helpful for troubleshooting.
+    </para>
+    <para>
+      Conversely, setting the
+      <literal>HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES</literal> flag
+      tells HarfBuzz to remove <literal>Default_Ignorable</literal>
+      glyphs from the output buffer entirely. Finally, setting the
+      <literal>HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE</literal>
+      flag tells HarfBuzz not to insert the dotted-circle glyph
+      (<literal>U+25CC</literal>, "&#x25CC;"), which is normally
+      inserted into buffer output when broken character sequences are
+      encountered (such as combining marks that are not attached to a
+      base character).
     </para>
   </section>
+  
   <section id="customizing-unicode-functions">
     <title>Customizing Unicode functions</title>
     <para>
+      HarfBuzz requires some simple functions for accessing
+      information from the Unicode Character Database (such as the
+      <literal>General_Category</literal> (gc) and
+      <literal>Script</literal> (sc) properties) that is useful
+      for shaping, as well as some useful operations like composing and
+      decomposing code points.
+    </para>
+    <para>
+      HarfBuzz includes its own internal, lightweight set of Unicode
+      functions. At build time, it is also possible to compile support
+      for some other options, such as the Unicode functions provided
+      by GLib or the International Components for Unicode (ICU)
+      library. Generally, this option is only of interest for client
+      programs that have specific integration requirements or that do
+      a significant amount of customization.
+    </para>
+    <para>
+      If your program has access to other Unicode functions, however,
+      such as through a system library or application framework, you
+      might prefer to use those instead of the built-in
+      options. HarfBuzz supports this by implementing its Unicode
+      functions as a set of virtual methods that you can replace —
+      without otherwise affecting HarfBuzz's functionality.
+    </para>
+    <para>
+      The Unicode functions are specified in a structure called
+      <literal>unicode_funcs</literal> which is attached to each
+      buffer. But even though <literal>unicode_funcs</literal> is
+      associated with a <type>hb_buffer_t</type>, the functions
+      themselves are called by other HarfBuzz APIs that access
+      buffers, so it would be unwise for you to hook different
+      functions into different buffers.
+    </para>
+    <para>
+      In addition, you can mark your <literal>unicode_funcs</literal>
+      as immutable by calling
+      <function>hb_unicode_funcs_make_immutable (ufuncs)</function>.
+      This is especially useful if your code is a
+      library or framework that will have its own client programs. By
+      marking your Unicode function choices as immutable, you prevent
+      your own client programs from changing the
+      <literal>unicode_funcs</literal> configuration and introducing
+      inconsistencies and errors downstream.
+    </para>
+    <para>
+      You can retrieve the Unicode-functions configuration for
+      your buffer by calling <function>hb_buffer_get_unicode_funcs()</function>:
+    </para>
+    <programlisting language="C">
+      hb_unicode_funcs_t *ufunctions;
+      ufunctions = hb_buffer_get_unicode_funcs(buf);
+    </programlisting>
+    <para>
+      The current version of <literal>unicode_funcs</literal> uses six functions:
+    </para>
+    <itemizedlist>
+      <listitem>
+	<para>
+	  <function>hb_unicode_combining_class_func_t</function>:
+	  returns the Canonical Combining Class of a code point.
+      	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  <function>hb_unicode_general_category_func_t</function>:
+	  returns the General Category (gc) of a code point.
+      	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  <function>hb_unicode_mirroring_func_t</function>: returns
+	  the Mirroring Glyph code point (for bi-directional
+	  replacement) of a code point.
+      	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  <function>hb_unicode_script_func_t</function>: returns the
+	  Script (sc) property of a code point.
+      	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  <function>hb_unicode_compose_func_t</function>: returns the
+	  canonical composition of a sequence of two code points.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  <function>hb_unicode_decompose_func_t</function>: returns
+	  the canonical decomposition of a code point.
+	</para>
+      </listitem>
+    </itemizedlist>
+    <para>
+      Note, however, that future HarfBuzz releases may alter this set.
+    </para>
+    <para>
+      Each Unicode function has a corresponding setter, with which you
+      can assign a callback to your replacement function. For example,
+      to replace
+      <function>hb_unicode_general_category_func_t</function>, you can call
+    </para>
+    <programlisting language="C">
+      hb_unicode_funcs_set_general_category_func (*ufuncs, func, *user_data, destroy)	    
+    </programlisting>
+    <para>
+      Virtualizing this set of Unicode functions is primarily intended
+      to improve portability. There is no need for every client
+      program to make the effort to replace the default options, so if
+      you are unsure, do not feel any pressure to customize
+      <literal>unicode_funcs</literal>. 
     </para>
   </section>
+  
 </chapter>
diff --git a/docs/usermanual-clusters.xml b/docs/usermanual-clusters.xml
index f48e89c..9147ff0 100644
--- a/docs/usermanual-clusters.xml
+++ b/docs/usermanual-clusters.xml
@@ -6,25 +6,41 @@
 ]>
 <chapter id="clusters">
   <title>Clusters</title>
-  <section id="clusters">
-    <title>Clusters</title>
+  <section id="clusters-and-shaping">
+    <title>Clusters and shaping</title>
     <para>
       In text shaping, a <emphasis>cluster</emphasis> is a sequence of
       characters that needs to be treated as a single, indivisible
-      unit.
+      unit. A single letter or symbol can be a cluster of its
+      own. Other clusters correspond to longer subsequences of the
+      input code points &mdash; such as a ligature or conjunct form
+      &mdash; and require the shaper to ensure that the cluster is not
+      broken during the shaping process.
     </para>
     <para>
       A cluster is distinct from a <emphasis>grapheme</emphasis>,
-      which is the smallest unit of a writing system or script,
-      because clusters are only relevant for script shaping and the
-      layout of glyphs.
+      which is the smallest unit of meaning in a writing system or
+      script.
     </para>
     <para>
-      For example, a grapheme may be a letter, a number, a logogram,
-      or a symbol. When two letters form a ligature, however, they
-      combine into a single glyph. They are therefore part of the same
-      cluster and are treated as a unit &mdash; even though the two
-      original, underlying letters are separate graphemes.
+      The definitions of the two terms are similar. However, clusters
+      are only relevant for script shaping and glyph layout. In
+      contrast, graphemes are a property of the underlying script, and
+      are of interest when client programs implement orthographic 
+      or linguistic functionality.
+    </para>
+    <para>
+      For example, two individual letters are often two separate
+      graphemes. When two letters form a ligature, however, they
+      combine into a single glyph. They are then part of the same
+      cluster and are treated as a unit by the shaping engine &mdash;
+      even though the two original, underlying letters remain separate
+      graphemes.
+    </para>
+    <para>
+      HarfBuzz is concerned with clusters, <emphasis>not</emphasis>
+      with graphemes &mdash; although client programs using HarfBuzz
+      may still care about graphemes for other reasons from time to time.
     </para>
     <para>
       During the shaping process, there are several shaping operations
@@ -32,14 +48,15 @@
       points form a ligature or a conjunct form and are replaced by a
       single glyph) or split one character into several (for example,
       when decomposing a code point through the
-      <literal>ccmp</literal> feature).
+      <literal>ccmp</literal> feature). Operations like these alter
+      clusters; HarfBuzz tracks the changes to ensure that no clusters
+      get lost or broken during shaping. 
     </para>
     <para>
-      HarfBuzz tracks clusters independently from how these
-      shaping operations affect the individual glyphs that comprise the
-      output HarfBuzz returns in a buffer. Consequently,
-      a client program using HarfBuzz can utilize the cluster
-      information to implement features such as:
+      HarfBuzz records cluster information independently from how
+      shaping operations affect the individual glyphs returned in an
+      output buffer. Consequently, a client program using HarfBuzz can
+      utilize the cluster information to implement features such as:
     </para>
     <itemizedlist>
       <listitem>
@@ -77,11 +94,14 @@
 	<para>
 	  Performing line-breaking, justification, and other
 	  line-level or paragraph-level operations that must be done
-	  after shaping is complete, but which require character-level
-	  properties.
+	  after shaping is complete, but which require examining
+	  character-level properties.
 	</para>
       </listitem>
     </itemizedlist>
+  </section>
+  <section id="working-with-harfbuzz-clusters">
+    <title>Working with HarfBuzz clusters</title>
     <para>
       When you add text to a HarfBuzz buffer, each code point must be
       assigned a <emphasis>cluster value</emphasis>.
@@ -94,7 +114,67 @@
       value does not matter.
     </para>
     <para>
-      Client programs can choose how HarfBuzz handles clusters during
+      Some of the shaping operations performed by HarfBuzz &mdash;
+      such as reordering, composition, decomposition, and substitution
+      &mdash; may alter the cluster values of some characters. The
+      final cluster values in the buffer at the end of the shaping
+      process will indicate to client programs which subsequences of
+      glyphs represent a cluster and, therefore, must not be
+      separated.
+    </para>
+    <para>
+      In addition, client programs can query the final cluster values
+      to discern other potentially important information about the
+      glyphs in the output buffer (such as whether or not a ligature
+      was formed).
+    </para>
+    <para>
+      For example, if the initial sequence of cluster values was:
+    </para>
+    <programlisting>
+      0,1,2,3,4
+    </programlisting>
+    <para>
+      and the final sequence of cluster values is:
+    </para>
+    <programlisting>
+      0,0,3,3
+    </programlisting>
+    <para>
+      then there are two clusters in the output buffer: the first
+      cluster includes the first two glyphs, and the second cluster
+      includes the third and fourth glyphs. It is also evident that a
+      ligature or conjunct has been formed, because there are fewer
+      glyphs in the output buffer (four) than there were code points
+      in the input buffer (five).
+    </para>
+    <para>
+      Although client programs using HarfBuzz are free to assign
+      initial cluster values in any manner they choose to, HarfBuzz
+      does offer some useful guarantees if the cluster values are
+      assigned in a monotonic (either non-decreasing or non-increasing)
+      order.
+    </para>
+    <para>
+      For buffers in the left-to-right (LTR)
+      or top-to-bottom (TTB) text flow direction,
+      HarfBuzz will preserve the monotonic property: client programs
+      are guaranteed that monotonically increasing initial cluster
+      values will be returned as monotonically increasing final
+      cluster values.
+    </para>
+    <para>
+      For buffers in the right-to-left (RTL)
+      or bottom-to-top (BTT) text flow direction,
+      the directionality of the buffer itself is reversed for final
+      output as a matter of design. Therefore, HarfBuzz inverts the
+      monotonic property: client programs are guaranteed that
+      monotonically increasing initial cluster values will be
+      returned as monotonically <emphasis>decreasing</emphasis> final
+      cluster values.
+    </para>
+    <para>
+      Client programs can adjust how HarfBuzz handles clusters during
       shaping by setting the
       <literal>cluster_level</literal> of the
       buffer. HarfBuzz offers three <emphasis>levels</emphasis> of
@@ -179,7 +259,7 @@
       assign initial cluster values in a buffer by reusing the indices
       of the code points in the input text. This gives a sequence of
       cluster values that is monotonically increasing (for example,
-      0,1,2,3,4,5). 
+      0,1,2,3,4). 
     </para>
     <para>
       It is not <emphasis>required</emphasis> that the cluster values
@@ -233,16 +313,44 @@
 	</para>
       </listitem>
     </itemizedlist>
-	
   </section>
+  
   <section id="a-clustering-example-for-levels-0-and-1">
     <title>A clustering example for levels 0 and 1</title>
     <para>
-      The guarantees and benefits of level 0 and level 1 can be seen
-      with some examples. First, let us examine what happens with cluster
-      values when shaping involves cluster merging with ligatures and
-      decomposition.
+      The basic shaping operations affect clusters in a predictable
+      manner when using level 0 or level 1: 
     </para>
+    <itemizedlist>
+      <listitem>
+	<para>
+	  When two or more clusters <emphasis>merge</emphasis>, the
+	  resulting merged cluster takes as its cluster value the
+	  <emphasis>minimum</emphasis> of the incoming cluster values.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  When a cluster <emphasis>decomposes</emphasis>, all of the
+	  resulting child clusters inherit as their cluster value the
+	  cluster value of the parent cluster.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  When a character is <emphasis>reordered</emphasis>, the
+	  reordered character and all clusters that the character
+	  moves past as part of the reordering are merged into one cluster.
+	</para>
+      </listitem>
+    </itemizedlist>
+    <para>
+      The functionality, guarantees, and benefits of level 0 and level
+      1 behavior can be seen with some examples. First, let us examine
+      what happens with cluster values when shaping involves cluster
+      merging with ligatures and decomposition.
+    </para>
+
     <para>
       Let's say we start with the following character sequence (top row) and
       initial cluster values (bottom row):
@@ -279,8 +387,8 @@
     <para>
       Next, let us say that the <literal>BC</literal> ligature glyph
       decomposes into three components, and <literal>D</literal> also
-      decomposes into two components. These components each inherit the
-      cluster value of their parent: 
+      decomposes into two components. Whenever a cluster decomposes,
+      its components each inherit the cluster value of their parent: 
     </para>
     <programlisting>
       A,BC0,BC1,BC2,D0,D1,E
@@ -296,6 +404,12 @@
       0,1  ,1  ,1    ,1 ,4
     </programlisting>
     <para>
+      Note that the entirety of cluster 3 merges into cluster 1, not
+      just the <literal>D0</literal> glyph. This reflects the fact
+      that the cluster <emphasis>must</emphasis> be treated as an
+      indivisible unit.
+    </para>
+    <para>
       At this point, cluster 1 means: the character sequence
       <literal>BCD</literal> is represented by glyphs
       <literal>BC0,BC1,BC2D0,D1</literal> and cannot be broken down any
@@ -319,18 +433,24 @@
       0,1,2,3,4
     </programlisting>
     <para>
-      If <literal>D</literal> is reordered to before <literal>B</literal>,
-      then HarfBuzz merges the <literal>B</literal>,
-      <literal>C</literal>, and <literal>D</literal> clusters, and we
-      get:
+      If <literal>D</literal> is reordered to the position immediately
+      before <literal>B</literal>, then HarfBuzz merges the
+      <literal>B</literal>, <literal>C</literal>, and
+      <literal>D</literal> clusters &mdash; all the clusters between
+      the final position of the reordered glyph and its original
+      position. This means that we get:
     </para>
     <programlisting>
       A,D,B,C,E
       0,1,1,1,4
     </programlisting>
     <para>
-      This is clearly not ideal, but it is the only sensible way to
-      maintain a monotonic sequence of cluster values and retain the
+      as the final cluster sequence.
+    </para>
+    <para>
+      Merging this many clusters is not ideal, but it is the only
+      sensible way for HarfBuzz to maintain the guarantee that the
+      sequence of cluster values remains monotonic and to retain the
       true relationship between glyphs and characters.
     </para>
   </section>
@@ -340,8 +460,9 @@
       The preceding examples demonstrate the main effects of using
       cluster levels 0 and 1. The only difference between the two
       levels is this: in level 0, at the very beginning of the shaping
-      process, HarfBuzz also merges clusters between any base character
-      and all Unicode marks (combining or not) that follow it.
+      process, HarfBuzz merges the cluster of each base character
+      with the clusters of all Unicode marks (combining or not) and
+      modifiers that follow it.
     </para>
     <para>
       For example, let us start with the following character sequence
@@ -362,15 +483,20 @@
       0,0    ,2
     </programlisting>
     <para>
+      This merger is performed before any other script-shaping
+      steps.
+    </para>
+    <para>
       This initial cluster merging is the default behavior of the
       Windows shaping engine, and the old HarfBuzz codebase copied
       that behavior to maintain compatibility. Consequently, it has
       remained the default behavior in the new HarfBuzz codebase.
     </para>
     <para>
-      But this initial cluster-merging behavior makes it impossible to
+      But this initial cluster-merging behavior makes it impossible
+      for client programs to implement some features (such as to
       color diacritic marks differently from their base
-      characters. That is why, in level 1, HarfBuzz does not perform
+      characters). That is why, in level 1, HarfBuzz does not perform
       the initial merging step.
     </para>
     <para>
@@ -378,29 +504,34 @@
       perform cursor positioning, level 0 is more convenient. But
       relying on cluster boundaries for cursor positioning is wrong: cursor
       positions should be determined based on Unicode grapheme
-      boundaries, not on shaping-cluster boundaries. As such, level 1
-      clusters are preferred. 
+      boundaries, not on shaping-cluster boundaries. As such, using
+      level 1 clustering behavior is recommended. 
     </para>
     <para>
-      One last note about levels 0 and 1. HarfBuzz currently does not allow a
-      <literal>MultipleSubst</literal> lookup to replace a glyph with zero
-      glyphs (in other words, to delete a glyph). But, in some other situations,
-      glyphs can be deleted. In those cases, if the glyph being deleted is
-      the last glyph of its cluster, HarfBuzz makes sure to merge the cluster
-      with a neighboring cluster.
+      One final facet of levels 0 and 1 is worth noting. HarfBuzz
+      currently does not allow any
+      <emphasis>multiple-substitution</emphasis> GSUB lookups to 
+      replace a glyph with zero glyphs (in other words, to delete a
+      glyph).
+    </para>
+    <para>
+      But, in some other situations, glyphs can be deleted. In
+      those cases, if the glyph being deleted is the last glyph of its
+      cluster, HarfBuzz makes sure to merge the deleted glyph's
+      cluster with a neighboring cluster.
     </para>
     <para>
       This is done primarily to make sure that the starting cluster of the
       text always has the cluster index pointing to the start of the text
-      for the run; more than one client currently relies on this
+      for the run; more than one client program currently relies on this
       guarantee.
     </para>
     <para>
-      Incidentally, Apple's CoreText does something else to maintain the
-      same promise: it inserts a glyph with id 65535 at the beginning of
-      the glyph string if the glyph corresponding to the first character
-      in the run was deleted. HarfBuzz might do something similar in the
-      future.
+      Incidentally, Apple's CoreText does something different to
+      maintain the same promise: it inserts a glyph with id 65535 at
+      the beginning of the glyph string if the glyph corresponding to
+      the first character in the run was deleted. HarfBuzz might do
+      something similar in the future.
     </para>
   </section>
   <section id="level-2">
@@ -415,16 +546,39 @@
       performs no merging of clusters whatsoever.
     </para>
     <para>
-      When glyphs form a ligature (or when some other feature
-      substitutes multiple glyphs with one glyph), the cluster value
-      of the first glyph is retained as the cluster value for the
-      ligature. However, no subsequent clusters &mdash; including
-      marks and modifiers &mdash; are affected.
+      This means that there is no initial base-and-mark merging step
+      (as is done in level 0), and it means that reordering moves and
+      ligature substitutions do not trigger a cluster merge.
     </para>
     <para>
-      Level 2 cluster behavior is less complex than level 0 or level
-      1, but there are a few cases in which processing cluster values
-      produced at level 2 may be tricky. 
+      Only one shaping operation directly affects clusters when using
+      level 2:
+    </para>
+    <itemizedlist>
+      <listitem>
+	<para>
+	  When a cluster <emphasis>decomposes</emphasis>, all of the
+	  resulting child clusters inherit as their cluster value the
+	  cluster value of the parent cluster.
+	</para>
+      </listitem>
+    </itemizedlist>
+    <para>
+      When glyphs do form a ligature (or when some other feature
+      substitutes multiple glyphs with one glyph) the cluster value
+      of the first glyph is retained as the cluster value for the
+      resulting ligature.
+    </para>
+    <para>
+      This occurrence sounds similar to a cluster merge, but it is
+      different. In particular, no subsequent characters &mdash;
+      including marks and modifiers &mdash; are affected. They retain
+      their previous cluster values. 
+    </para>
+    <para>
+      Level 2 cluster behavior is ultimately less complex than level 0
+      or level 1, but there are several cases for which processing
+      cluster values produced at level 2 may be tricky. 
     </para>
     <section id="ligatures-with-combining-marks-in-level-2">
       <title>Ligatures with combining marks in level 2</title>
@@ -532,10 +686,11 @@
       <para>
 	There may be other problems encountered with ligatures under
 	level 2, such as if the direction of the text is forced to
-	opposite of its natural direction (for example, left-to-right
-	Arabic). But, generally speaking, these other scenarios are
-	minor corner cases that are too obscure for most client
-	programs to need to worry about.
+	the opposite of its natural direction (for example, Arabic text
+	that is forced into left-to-right directionality). But,
+	generally speaking, these other scenarios are minor corner
+	cases that are too obscure for most client programs to need to
+	worry about.
       </para>
     </section>
   </section>
diff --git a/docs/usermanual-fonts-and-faces.xml b/docs/usermanual-fonts-and-faces.xml
index 5536004..1258bec 100644
--- a/docs/usermanual-fonts-and-faces.xml
+++ b/docs/usermanual-fonts-and-faces.xml
@@ -5,20 +5,449 @@
   <!ENTITY version SYSTEM "version.xml">
 ]>
 <chapter id="fonts-and-faces">
-  <title>Fonts and faces</title>
-  <section id="using-freetype">
+  <title>Fonts, faces, and output</title>
+    <para>
+      In the previous chapter, we saw how to set up a buffer and fill
+      it with text as Unicode code points. In order to shape this
+      buffer text with HarfBuzz, you will need also need a font
+      object.
+    </para>
+    <para>
+      HarfBuzz provides abstractions to help you cache and reuse the
+      heavier parts of working with binary fonts, so we will look at
+      how to do that. We will also look at how to work with the
+      FreeType font-rendering library and at how you can customize
+      HarfBuzz to work with other libraries.
+    </para>
+    <para>
+      Finally, we will look at how to work with OpenType variable
+      fonts, the latest update to the OpenType font format, and at
+      some other recent additions to OpenType.
+    </para>
+
+  <section id="fonts-and-faces-objects">
+    <title>Font and face objects</title>
+    <para>
+      The outcome of shaping a run of text depends on the contents of
+      a specific font file (such as the substitutions and positioning
+      moves in the 'GSUB' and 'GPOS' tables), so HarfBuzz makes
+      accessing those internals fast.
+    </para>
+    <para>
+      An <type>hb_face_t</type> represents a <emphasis>face</emphasis>
+      in HarfBuzz. This data type is a wrapper around an
+      <type>hb_blob_t</type> blob that holds the contents of a binary
+      font file. Since HarfBuzz supports TrueType Collections and
+      OpenType Collections (each of which can include multiple
+      typefaces), a HarfBuzz face also requires an index number
+      specifying which typeface in the file you want to use. Most of
+      the font files you will encounter in the wild include just a
+      single face, however, so most of the time you would pass in
+      <literal>0</literal> as the index when you create a face:
+    </para>
+    <programlisting language="C">
+      hb_blob_t* blob = hb_blob_create_from_file(file);
+      ...
+      hb_face_t* face = hb_face_create(blob, 0);
+    </programlisting>
+    <para>
+      On its own, a face object is not quite ready to use for
+      shaping. The typeface must be set to a specific point size in
+      order for some details (such as hinting) to work. In addition,
+      if the font file in question is an OpenType Variable Font, then
+      you may need to specify one or variation-axis settings (or a
+      named instance) in order to get the output you need.
+    </para>
+    <para>
+      In HarfBuzz, you do this by creating a <emphasis>font</emphasis>
+      object from your face.
+    </para>
+    <para>
+      Font objects also have the advantage of being considerably
+      lighter-weight than face objects (remember that a face contains
+      the contents of a binary font file mapped into memory). As a
+      result, you can cache and reuse a font object, but you could
+      also create a new one for each additional size you needed.
+      Creating new fonts incurs some additional overhead, of course,
+      but whether or not it is excessive is your call in the end. In
+      contrast, face objects are substantially larger, and you really
+      should cache them and reuse them whenever possible.
+    </para>
+    <para>
+      You can create a font object from a face object:
+    </para>
+    <programlisting language="C">
+      hb_font_t* hb_font = hb_font_create(hb_face);
+    </programlisting>
+    <para>
+      After creating a font, there are a few properties you should
+      set. Many fonts enable and disable hints based on the size it
+      is used at, so setting this is important for font
+      objects. <function>hb_font_set_ppem(font, x_ppem,
+      y_ppem)</function> sets the pixels-per-EM value of the font. You
+      can also set the point size of the font with
+      <function>hb_font_set_ptem(font, ptem)</function>. HarfBuzz uses the
+      industry standard 72 points per inch.
+    </para>
+    <para>
+      HarfBuzz lets you specify the degree subpixel precision you want
+      through a scaling factor. You can set horizontal and
+      vertical scaling factors on the
+      font by calling <function>hb_font_set_scale(font, x_scale,
+      y_scale)</function>. 
+    </para>
+    <para>
+      There may be times when you are handed a font object and need to
+      access the face object that it comes from. For that, you can call
+    </para>
+    <programlisting language="C">
+      hb_face = hb_font_get_face(hb_font);
+    </programlisting>
+    <para>
+      You can also create a font object from an existing font object
+      using the <function>hb_font_create_sub_font()</function>
+      function. This creates a child font object that is initiated
+      with the same attributes as its parent; it can be used to
+      quickly set up a new font for the purpose of overriding a specific
+      font-functions method.
+    </para>
+    <para>
+      All face objects and font objects are lifecycle-managed by
+      HarfBuzz. After creating a face, you increase its reference
+      count with <function>hb_face_reference(face)</function> and
+      decrease it with
+      <function>hb_face_destroy(face)</function>. Likewise, you
+      increase the reference count on a font with
+      <function>hb_font_reference(font)</function> and decrease it
+      with <function>hb_font_destroy(font)</function>.
+    </para>
+    <para>
+      You can also attach user data to face objects and font objects.
+    </para>
+  </section>
+
+ <section id="fonts-and-faces-custom-functions">
+    <title>Customizing font functions</title>
+    <para>
+      During shaping, HarfBuzz frequently needs to query font objects
+      to get at the contents and parameters of the glyphs in a font
+      file. It includes a built-in set of functions that is tailored
+      to working with OpenType fonts. However, as was the case with
+      Unicode functions in the buffers chapter, HarfBuzz also wants to
+      make it easy for you to assign a substitute set of font
+      functions if you are developing a program to work with a library
+      or platform that provides its own font functions. 
+    </para>
+    <para>
+      Therefore, the HarfBuzz API defines a set of virtual
+      methods for accessing font-object properties, and you can
+      replace the defaults with your own selections without
+      interfering with the shaping process. Each font object in
+      HarfBuzz includes a structure called
+      <literal>font_funcs</literal> that serves as a vtable for the
+      font object. The virtual methods in
+      <literal>font_funcs</literal> are:
+    </para>
+    <itemizedlist>
+      <listitem>
+    <para>
+      <function>hb_font_get_font_h_extents_func_t</function>: returns
+      the extents of the font for horizontal text.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_font_v_extents_func_t</function>: returns
+      the extents of the font for vertical text.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_nominal_glyph_func_t</function>: returns
+      the font's nominal glyph for a given code point.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_variation_glyph_func_t</function>: returns
+      the font's glyph for a given code point when it is followed by a
+      given Variation Selector.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_nominal_glyphs_func_t</function>: returns
+      the font's nominal glyphs for a series of code points.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_advance_func_t</function>: returns
+      the advance for a glyph.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_h_advance_func_t</function>: returns
+      the advance for a glyph for horizontal text.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_v_advance_func_t</function>:returns
+      the advance for a glyph for vertical text.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_advances_func_t</function>: returns
+      the advances for a series of glyphs.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_h_advances_func_t</function>: returns
+      the advances for a series of glyphs for horizontal text .
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_v_advances_func_t</function>: returns
+      the advances for a series of glyphs for vertical text.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_origin_func_t</function>: returns
+      the origin coordinates of a glyph.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_h_origin_func_t</function>: returns
+      the origin coordinates of a glyph for horizontal text.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_v_origin_func_t</function>: returns
+      the origin coordinates of a glyph for vertical text.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_extents_func_t</function>: returns
+      the extents for a glyph.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_contour_point_func_t</function>:
+      returns the coordinates of a specific contour point from a glyph.
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_name_func_t</function>: returns the
+      name of a glyph (from its glyph index).
+    </para>
+      </listitem>
+      <listitem>
+    <para>
+      <function>hb_font_get_glyph_from_name_func_t</function>: returns
+      the glyph index that corresponds to a given glyph name.
+    </para>
+      </listitem>
+    </itemizedlist>
+    <para>
+      You can fetch the font-functions configuration for a font object
+      by calling <function>hb_font_get_font_funcs()</function>:
+    </para>
+    <programlisting language="C">
+      hb_font_funcs_t *ffunctions;
+      ffunctions = hb_font_get_font_funcs (font);
+    </programlisting>
+    <para>
+      The individual methods can each be replaced with their own setter
+      function, such as
+      <function>hb_font_funcs_set_nominal_glyph_func(*ffunctions,
+      func, *user_data, destroy)</function>. 
+    </para>
+    <para>
+      Font-functions structures can be reused for multiple font
+      objects, and can be reference counted with
+      <function>hb_font_funcs_reference()</function> and
+      <function>hb_font_funcs_destroy()</function>. Just like other
+      objects in HarfBuzz, you can set user-data for each
+      font-functions structure and assign a destroy callback for
+      it.
+    </para>
+    <para>
+      You can also mark a font-functions structure as immutable,
+      with <function>hb_font_funcs_make_immutable()</function>. This
+      is especially useful if your code is a library or framework that
+      will have its own client programs. By marking your
+      font-functions structures as immutable, you prevent your client
+      programs from changing the configuration and introducing
+      inconsistencies and errors downstream.
+    </para>
+  </section>
+
+  <section id="fonts-and-faces-native-opentype">
+    <title>Font objects and HarfBuzz's native OpenType implementation</title>
+    <para>
+      By default, whenever HarfBuzz creates a font object, it will
+      configure the font to use a built-in set of font functions that
+      supports contemporary OpenType font internals. If you want to
+      work with OpenType or TrueType fonts, you should be able to use
+      these functions without difficulty.
+    </para>
+    <para>
+      Many of the methods in the font-functions structure deal with
+      the fundamental properties of glyphs that are required for
+      shaping text: extents (the maximums and minimums on each axis),
+      origins (the <literal>(0,0)</literal> coordinate point which
+      glyphs are drawn in reference to), and advances (the amount that
+      the cursor needs to be moved after drawing each glyph, including
+      any empty space for the glyph's side bearings).
+    </para>
+    <para>
+      As you can see in the list of functions, there are separate "horizontal"
+      and "vertical" variants depending on whether the text is set in
+      the horizontal or vertical direction. For some scripts, fonts
+      that are designed to support text set horizontally or vertically (for
+      example, in Japanese) may include metrics for both text
+      directions. When fonts don't include this information, HarfBuzz
+      does its best to transform what the font provides.
+    </para>
+    <para>
+      In addition to the direction-specific functions, HarfBuzz
+      provides some higher-level functions for fetching information
+      like extents and advances for a glyph. If you call
+    </para>
+    <programlisting language="C">
+      hb_font_get_glyph_advance_for_direction(font, direction, extents);
+    </programlisting>
+    <para>
+      then you can provide any <type>hb_direction_t</type> as the
+      <parameter>direction</parameter> parameter, and HarfBuzz will
+      use the correct function variant for the text direction. There
+      are similar higher-level versions of the functions for fetching
+      extents, origin coordinates, and contour-point
+      coordinates. There are also addition and subtraction functions
+      for moving points with respect to the origin.
+    </para>
+    <para>
+      There are also methods for fetching the glyph ID that
+      corresponds to a Unicode code point (possibly when followed by a
+      variation-selector code point), fetching the glyph name from the
+      font, and fetching the glyph ID that corresponds to a glyph name
+      you already have.
+    </para>
+    <para>
+      HarfBuzz also provides functions for converting between glyph
+      names and string
+      variables. <function>hb_font_glyph_to_string(font, glyph, s,
+      size)</function> retrieves the name for the glyph ID
+      <parameter>glyph</parameter> from the font object. It generates a
+      generic name of the form <literal>gidDDD</literal> (where DDD is
+      the glyph index) if there is no name for the glyph in the
+      font. The <function>hb_font_glyph_from_string(font, s, len,
+      glyph)</function> takes an input string <parameter>s</parameter>
+      and looks for a glyph with that name in the font, returning its
+      glyph ID in the <parameter>glyph</parameter>
+      output parameter. It automatically parses
+      <literal>gidDDD</literal> and <literal>uniUUUU</literal> strings.
+    </para>
+  </section>
+
+
+  <!-- Commenting out FreeType integration section-holder for now. May move
+       to the full-blown Integration Chapter. -->
+  
+  <!--   <section id="fonts-and-faces-freetype">
     <title>Using FreeType</title>
     <para>
+
     </para>
-  </section>
-  <section id="using-harfbuzzs-native-opentype-implementation">
-    <title>Using HarfBuzz's native OpenType implementation</title>
     <para>
+      
     </para>
-  </section>
-  <section id="using-your-own-font-functions">
-    <title>Using your own font functions</title>
+  </section> -->
+
+  <section id="fonts-and-faces-variable">
+    <title>Working with OpenType Variable Fonts</title>
     <para>
+      If you are working with OpenType Variable Fonts, there are a few
+      additional functions you should use to specify the
+      variation-axis settings of your font object. Without doing so,
+      your variable font's font object can still be used, but only at
+      the default setting for every axis (which, of course, is
+      sometimes what you want, but does not cover general usage).
+    </para>
+    <para>
+      HarfBuzz manages variation settings in the
+      <type>hb_variation_t</type> data type, which holds a <property>tag</property> for the
+      variation-axis identifier tag and a <property>value</property> for its
+      setting. You can retrieve the list of variation axes in a font
+      binary from the face object (not from a font object, notably) by
+      calling <function>hb_ot_var_get_axis_count(face)</function> to
+      find the number of axes, then using
+      <function>hb_ot_var_get_axis_infos()</function> to collect the 
+      axis structures:
+    </para>
+    <programlisting language="C">
+      axes = hb_ot_var_get_axis_count(face);
+      ...
+      hb_ot_var_get_axis_infos(face, 0, axes, axes_array);
+    </programlisting>
+    <para>
+      For each axis returned in the array, you can can access the
+      identifier in its <property>tag</property>. HarfBuzz also has
+      tag definitions predefined for the five standard axes specified
+      in OpenType (<literal>ital</literal> for italic,
+      <literal>opsz</literal> for optical size,
+      <literal>slnt</literal> for slant, <literal>wdth</literal> for
+      width, and <literal>wght</literal> for weight). Each axis also
+      has a <property>min_value</property>, a
+      <property>default_value</property>, and a <property>max_value</property>.
+    </para>
+    <para>
+      To set your font object's variation settings, you call the
+      <function>hb_font_set_variations()</function> function with an
+      array of <type>hb_variation_t</type> variation settings. Let's
+      say our font has weight and width axes. We need to specify each
+      of the axes by tag and assign a value on the axis:
+    </para>
+    <programlisting language="C">
+      unsigned int variation_count = 2;
+      hb_variation_t variation_data[variation_count];
+      variation_data[0].tag = HB_OT_TAG_VAR_AXIS_WIDTH;
+      variation_data[1].tag = HB_OT_TAG_VAR_AXIS_WEIGHT;
+      variation_data[0].value = 80;
+      variation_data[1].value = 750;
+      ...
+      hb_font_set_variations(font, variation_data, variation_count);
+    </programlisting>
+    <para>
+      That should give us a slightly condensed font ("normal" on the
+      <literal>wdth</literal> axis is 100) at a noticeably bolder
+      weight ("regular" is 400 on the <literal>wght</literal> axis).
+    </para>
+    <para>
+      In practice, though, you should always check that the value you
+      want to set on the axis is within the
+      [<property>min_value</property>,<property>max_value</property>]
+      range actually implemented in the font's variation axis. After
+      all, a font might only provide lighter-than-regular weights, and
+      setting a heavier value on the <literal>wght</literal> axis will
+      not change that. 
+    </para>
+    <para>
+      Once your variation settings are specified on your font object,
+      however, shaping with a variable font is just like shaping a
+      static font.
     </para>
   </section>
-</chapter>
+
+ </chapter>
diff --git a/docs/usermanual-getting-started.xml b/docs/usermanual-getting-started.xml
index 932bd94..1f26df8 100644
--- a/docs/usermanual-getting-started.xml
+++ b/docs/usermanual-getting-started.xml
@@ -75,13 +75,45 @@
 
   <section>
     <title>Terminology</title>
+    <para>
+      
+    </para>
       <variablelist>
+	<?dbfo list-presentation="blocks"?> 
+	<varlistentry>
+	  <term>script</term>
+	  <listitem>
+	    <para>
+	      In text shaping, a <emphasis>script</emphasis> is a
+	      writing system: a set of symbols, rules, and conventions
+	      that is used to represent a language or multiple
+	      languages.
+	    </para>
+	    <para>
+	      In general computing lingo, the word "script" can also
+	      be used to mean an executable program (usually one
+	      written in a human-readable programming language). For
+	      the sake of clarity, HarfBuzz documents will always use
+	      more specific terminology when referring to this
+	      meaning, such as "Python script" or "shell script." In
+	      all other instances, "script" refers to a writing system.
+	    </para>
+	    <para>
+	      For developers using HarfBuzz, it is important to note
+	      the distinction between a script and a language. Most
+	      scripts are used to write a variety of different
+	      languages, and many languages may be written in more
+	      than one script.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
 	<varlistentry>
 	  <term>shaper</term>
 	  <listitem>
 	    <para>
 	      In HarfBuzz, a <emphasis>shaper</emphasis> is a
-	      handler for a specific script shaping model. HarfBuzz
+	      handler for a specific script-shaping model. HarfBuzz
 	      implements separate shapers for Indic, Arabic, Thai and
 	      Lao, Khmer, Myanmar, Tibetan, Hangul, Hebrew, the
 	      Universal Shaping Engine (USE), and a default shaper for
@@ -95,12 +127,12 @@
 	  <listitem>
 	    <para>
 	      In text shaping, a <emphasis>cluster</emphasis> is a
-	      sequence of codepoints that must be handled as an
-	      indivisible unit. Clusters can include codepoint
+	      sequence of codepoints that must be treated as an
+	      indivisible unit. Clusters can include code-point
 	      sequences that form a ligature or base-and-mark
 	      sequences. Tracking and preserving clusters is important
 	      when shaping operations might separate or reorder
-	      codepoints.
+	      code points.
 	    </para>
 	    <para>
 	      HarfBuzz provides three cluster
@@ -111,7 +143,59 @@
 	  </listitem>
 	</varlistentry>
 	
-
+	<varlistentry>
+	  <term>grapheme</term>
+	  <listitem>
+	    <para>
+	      In linguistics, a <emphasis>grapheme</emphasis> is one
+	      of the indivisible units that make up a writing system or
+	      script. Often, graphemes are individual symbols (letters,
+	      numbers, punctuation marks, logograms, etc.) but,
+	      depending on the writing system, a particular grapheme
+	      might correspond to a sequence of several Unicode code
+	      points.
+	    </para>
+	    <para>
+	      In practice, HarfBuzz and other text-shaping engines
+	      are not generally concerned with graphemes. However, it
+	      is important for developers using HarfBuzz to recognize
+	      that there is a difference between graphemes and shaping
+	      clusters (see above). The two concepts may overlap
+	      frequently, but there is no guarantee that they will be
+	      identical.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	
+	<varlistentry>
+	  <term>syllable</term>
+	  <listitem>
+	    <para>
+	      In linguistics, a <emphasis>syllable</emphasis> is an 
+	      a sequence of sounds that makes up a building block of a
+	      particular language. Every language has its own set of
+	      rules describing what constitutes a valid syllable.
+	    </para>
+	    <para>
+	      For text-shaping purposes, the various definitions of
+	      "syllable" are important because script-specific shaping
+	      operations may be applied at the syllable level. For
+	      example, a reordering rule might specify that a vowel
+	      mark be reordered to the beginning of the syllable.
+	    </para>
+	    <para>
+	      Syllables will consist of one or more Unicode code
+	      points. The definition of a syllable for a particular
+	      writing system might correspond to how HarfBuzz
+	      identifies clusters (see above) for the same writing
+	      system. However, it is important for developers using
+	      HarfBuzz to recognize that there is a difference between
+	      syllables and shaping clusters. The two concepts may
+	      overlap frequently, but there is no guarantee that they
+	      will be identical.
+	    </para>
+	  </listitem>
+	</varlistentry>
       </variablelist>
     
   </section>
@@ -139,7 +223,7 @@
     <orderedlist numeration="arabic">
       <listitem override="2">
 	<para>
-          Guess the script, language and direction of the buffer.
+          Set the script, language and direction of the buffer.
 	</para>
       </listitem>
     </orderedlist>
@@ -191,14 +275,14 @@
     </orderedlist>
     <programlisting language="C">
       for (i = 0; i &lt; glyph_count; ++i) {
-      glyphid = glyph_info[i].codepoint;
-      x_offset = glyph_pos[i].x_offset / 64.0;
-      y_offset = glyph_pos[i].y_offset / 64.0;
-      x_advance = glyph_pos[i].x_advance / 64.0;
-      y_advance = glyph_pos[i].y_advance / 64.0;
-      draw_glyph(glyphid, cursor_x + x_offset, cursor_y + y_offset);
-      cursor_x += x_advance;
-      cursor_y += y_advance;
+          glyphid = glyph_info[i].codepoint;
+          x_offset = glyph_pos[i].x_offset / 64.0;
+          y_offset = glyph_pos[i].y_offset / 64.0;
+          x_advance = glyph_pos[i].x_advance / 64.0;
+          y_advance = glyph_pos[i].y_advance / 64.0;
+          draw_glyph(glyphid, cursor_x + x_offset, cursor_y + y_offset);
+          cursor_x += x_advance;
+          cursor_y += y_advance;
       }
     </programlisting>
     <orderedlist numeration="arabic">
diff --git a/docs/usermanual-install-harfbuzz.xml b/docs/usermanual-install-harfbuzz.xml
index a6484fc..2b61ce8 100644
--- a/docs/usermanual-install-harfbuzz.xml
+++ b/docs/usermanual-install-harfbuzz.xml
@@ -57,8 +57,7 @@
     <para>
       For example, on an Ubuntu or Debian system, you would run:
       <programlisting>
-	<command>sudo apt install</command> <package>gcc g++
-	libfreetype6-dev libglib2.0-dev libcairo2-dev</package>
+	<command>sudo apt install</command> <package>gcc g++ libfreetype6-dev libglib2.0-dev libcairo2-dev</package>
       </programlisting>
       On Fedora, RHEL, CentOS, or other Red-Hat&ndash;based systems, you would run:
       <programlisting>
@@ -126,7 +125,7 @@
       </para>
       <para>
 	If you need to build HarfBuzz from source, first put the
-	<program>ragel</program> binary on your
+	<package>ragel</package> binary on your
 	<literal>PATH</literal>, then follow the appveyor CI cmake
 	<ulink
 	    url="https://github.com/harfbuzz/harfbuzz/blob/master/appveyor.yml">build
@@ -184,8 +183,7 @@
       <para>If you are
 	using MacPorts, you should run:
       <programlisting>
-	<command>sudo port install</command> <package>autoconf
-	automake libtool pkgconfig ragel gtk-doc</package> 
+	<command>sudo port install</command> <package>autoconf automake libtool pkgconfig ragel gtk-doc</package> 
       </programlisting>
       to install the build dependencies.
       </para>
@@ -229,8 +227,9 @@
       </para>
 
       <variablelist>
+	<?dbfo list-presentation="blocks"?> 
 	<varlistentry>
-	  <term>--with-libstdc++</term>
+	  <term><command>--with-libstdc++</command></term>
 	  <listitem>
 	    <para>
 	      Allow linking with libstdc++. <emphasis>(Default = no)</emphasis>
@@ -243,11 +242,11 @@
 	</varlistentry>
 	
 	<varlistentry>
-	  <term>--with-glib</term>
+	  <term><command>--with-glib</command></term>
 	  <listitem>
 	    <para>
 	     Use <ulink url="https://developer.gnome.org/glib/">GLib</ulink>. <emphasis>(Default = auto)</emphasis>
-	    </para>	    
+	    </para>
 	    <para>
 	      This option enables or disables usage of the GLib
 	      library.  The default setting is to check for the
@@ -259,7 +258,7 @@
 	</varlistentry>
 	
 	<varlistentry>
-	  <term>--with-gobject</term>
+	  <term><command>--with-gobject</command></term>
 	  <listitem>
 	    <para>
 	      Use <ulink url="https://developer.gnome.org/gobject/stable/">GObject</ulink>. <emphasis>(Default = no)</emphasis>
@@ -275,7 +274,7 @@
 	</varlistentry>
 	
 	<varlistentry>
-	  <term>--with-cairo</term>
+	  <term><command>--with-cairo</command></term>
 	  <listitem>
 	    <para>
 	      Use <ulink url="https://cairographics.org/">Cairo</ulink>. <emphasis>(Default = auto)</emphasis>
@@ -294,11 +293,11 @@
 	</varlistentry>
 	
 	<varlistentry>
-	  <term>--with-fontconfig</term>
+	  <term><command>--with-fontconfig</command></term>
 	  <listitem>
 	    <para>
 	      Use <ulink url="https://www.freedesktop.org/wiki/Software/fontconfig/">Fontconfig</ulink>. <emphasis>(Default = auto)</emphasis>
-	    </para>	    
+	    </para>
 	    <para>
 	      This option enables or disables usage of the Fontconfig
 	      library, which provides font-matching functions and
@@ -314,11 +313,11 @@
 	</varlistentry>
 	
 	<varlistentry>
-	  <term>--with-icu</term>
+	  <term><command>--with-icu</command></term>
 	  <listitem>
 	    <para>
 	      Use the <ulink url="http://site.icu-project.org/home">ICU</ulink> library. <emphasis>(Default = auto)</emphasis>
-	    </para>	    
+	    </para>
 	    <para>
 	      This option enables or disables usage of the
 	      <emphasis>International Components for
@@ -332,29 +331,11 @@
 	</varlistentry>
 	
 	<varlistentry>
-	  <term>--with-ucdn</term>
-	  <listitem>
-	    <para>
-	      Use HarfBuzz's <ulink url="https://github.com/harfbuzz/harfbuzz/tree/master/src/hb-ucdn">built-in UCDN library</ulink>. <emphasis>(Default = auto)</emphasis>
-	    </para>	    
-	    <para>
-	      The HarfBuzz source tree includes a <emphasis>Unicode
-	      Database and Normalization</emphasis> (UCDN) library
-	      that provides access to basic character properties in
-	      the Unicode Character Database (UCD) as well as low-level
-	      normalization functions. HarfBuzz can be built without
-	      this UCDN support if the usage of a different UCDN
-	      library is desired.
-	    </para>
-	  </listitem>
-	</varlistentry>
-	
-	<varlistentry>
-	  <term>--with-graphite2</term>
+	  <term><command>--with-graphite2</command></term>
 	  <listitem>
 	    <para>
 	      Use the <ulink url="http://graphite.sil.org/">Graphite2</ulink> library. <emphasis>(Default = no)</emphasis>
-	    </para>	    
+	    </para>
 	    <para>
 	      This option enables or disables usage of the Graphite2
 	      library, which provides support for the Graphite shaping
@@ -364,11 +345,11 @@
 	</varlistentry>
 	
 	<varlistentry>
-	  <term>--with-freetype</term>
+	  <term><command>--with-freetype</command></term>
 	  <listitem>
 	    <para>
 	      Use the <ulink url="https://www.freetype.org/">FreeType</ulink> library. <emphasis>(Default = auto)</emphasis>
-	    </para>	    
+	    </para>
 	    <para>
 	      This option enables or disables usage of the FreeType
 	      font-rendering library. The default setting is to check for the
@@ -379,13 +360,13 @@
 	</varlistentry>
 	
 	<varlistentry>
-	  <term>--with-uniscribe</term>
+	  <term><command>--with-uniscribe</command></term>
 	  <listitem>
 	    <para>
 	      Use the <ulink
 	      url="https://docs.microsoft.com/en-us/windows/desktop/intl/uniscribe">Uniscribe</ulink>
 	      library (experimental). <emphasis>(Default = no)</emphasis>
-	    </para>	    
+	    </para>
 	    <para>
 	      This option enables or disables usage of the Uniscribe
 	      font-rendering library. Uniscribe is available on
@@ -397,11 +378,11 @@
 	</varlistentry>
 	
 	<varlistentry>
-	  <term>--with-directwrite</term>
+	  <term><command>--with-directwrite</command></term>
 	  <listitem>
 	    <para>
 	      Use the <ulink url="https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal">DirectWrite</ulink> library (experimental). <emphasis>(Default = no)</emphasis>
-	    </para>	    
+	    </para>
 	    <para>
 	      This option enables or disables usage of the DirectWrite
 	      font-rendering library. DirectWrite is available on
@@ -413,17 +394,29 @@
 	</varlistentry>
 	
 	<varlistentry>
-	  <term>--with-coretext</term>
+	  <term><command>--with-coretext</command></term>
 	  <listitem>
 	    <para>
 	      Use the <ulink url="https://developer.apple.com/documentation/coretext">CoreText</ulink> library. <emphasis>(Default = no)</emphasis>
-	    </para>	    
+	    </para>
 	    <para>
 	      This option enables or disables usage of the CoreText
 	      library. CoreText is available on macOS and iOS systems.
 	    </para>
 	  </listitem>
 	</varlistentry>	
+
+	<varlistentry>
+	  <term><command>--enable-gtk-doc</command></term>
+	  <listitem>
+	    <para>
+	      Use <ulink url="https://www.gtk.org/gtk-doc/">GTK-Doc</ulink>. <emphasis>(Default = no)</emphasis>
+	    </para>
+	    <para>
+	      This option enables the building of the documentation.
+	    </para>
+	  </listitem>
+	</varlistentry>
       </variablelist>
     </section>
     
diff --git a/docs/usermanual-object-model.xml b/docs/usermanual-object-model.xml
new file mode 100644
index 0000000..f571c47
--- /dev/null
+++ b/docs/usermanual-object-model.xml
@@ -0,0 +1,258 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
+<chapter id="object-model">
+  <title>The HarfBuzz object model</title>
+  <section id="object-model-intro">
+    <title>An overview of data types in HarfBuzz</title>
+    <para>
+      HarfBuzz features two kinds of data types: non-opaque,
+      pass-by-value types and opaque, heap-allocated types.  This kind
+      of separation is common in C libraries that have to provide
+      API/ABI compatibility (almost) indefinitely. 
+    </para>
+    <para>
+      <emphasis>Value types:</emphasis> The non-opaque, pass-by-value
+      types include integer types, enums, and small structs.  Exposing
+      a struct in the public API makes it impossible to expand the
+      struct in the future. As such, exposing structs is reserved for
+      cases where it’s extremely inefficient to do otherwise.
+    </para>
+    <para>
+      In HarfBuzz, several structs, like <literal>hb_glyph_info_t</literal> and
+      <literal>hb_glyph_position_t</literal>, fall into that efficiency-sensitive
+      category and are non-opaque.
+    </para>
+    <para>
+      For all non-opaque structs where future extensibility may be
+      necessary, reserved members are included to hold space for
+      possible future members.  As such, it’s important to provide
+      <function>equal()</function>, and <function>hash()</function>
+      methods for such structs, allowing users of the API do
+      effectively deal with the type without having to 
+      adapt their code to future changes. 
+    </para>
+    <para>
+      Important value types provided by HarfBuzz include the structs
+      for working with Unicode code points, glyphs, and tags for font
+      tables and features, as well as the enums for many Unicode and
+      OpenType properties.
+    </para>
+  </section>
+  
+  <section id="object-model-object-types">
+    <title>Objects in HarfBuzz</title>
+    <para>
+      <emphasis>Object types:</emphasis> Opaque struct types are used
+      for what HarfBuzz loosely calls "objects."  This doesn’t have
+      much to do with the terminology from object-oriented programming
+      (OOP), although some of the concepts are similar.
+    </para>
+    <para>
+      In HarfBuzz, all object types provide certain
+      lifecycle-management APIs.  Objects are reference-counted, and
+      constructed with various <function>create()</function> methods, referenced via
+      <function>reference()</function> and dereferenced using
+      <function>destroy()</function>.
+    </para>
+    <para>
+      For example,
+      the <literal>hb_buffer_t</literal> object has
+      <function>hb_buffer_create()</function> as its constructor,
+      <function>hb_buffer_reference()</function> to reference, and
+      <function>hb_buffer_destroy()</function> to dereference. 
+    </para>
+    <para>
+      After construction, each object's properties are accessible only
+      through the setter and getter functions described in the API
+      Reference manual.
+    </para>
+    <para>
+      Key object types provided by HarfBuzz include:
+    </para>
+    <itemizedlist spacing="compact">
+      <listitem>
+	<para>
+	  <emphasis>blobs</emphasis>, which act as low-level wrappers around binary
+	  data. Blobs are typically used to hold the contents of a
+	  binary font file.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  <emphasis>faces</emphasis>, which represent typefaces from a
+	  font file, but without specific parameters (such as size) set.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  <emphasis>fonts</emphasis>, which represent instances of a
+	  face with all of their parameters specified.
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  <emphasis>buffers</emphasis>, which hold Unicode code points
+	  for characters (before shaping) and the shaped glyph output
+	  (after shaping).
+	</para>
+      </listitem>
+      <listitem>
+	<para>
+	  <emphasis>shape plans</emphasis>, which store the settings
+	  that HarfBuzz will use when shaping a particular text
+	  segment. Shape plans are not generally used by client
+	  programs directly, but as we will see in a later chapter,
+	  they are still valuable to understand.
+	</para>
+      </listitem>
+    </itemizedlist>
+	
+  </section>
+
+  
+  
+  <section id="object-model-lifecycle">
+    <title>Object lifecycle management</title>
+    <para>
+      Each object type in HarfBuzz provides a
+      <function>create()</function> method. Some object types provide
+      additional variants of <function>create()</function> to handle
+      special cases or to speed up common tasks; those variants are
+      documented in the API reference. For example,
+      <function>hb_blob_create_from_file()</function> constructs a new
+      blob directly from the contents of a file.
+    </para>
+    <para>
+      All objects are created with an initial reference count of
+      <literal>1</literal>. Client programs can increase the reference
+      count on an object by calling its
+      <function>reference()</function> method. Whenever a client
+      program is finished with an object, it should call its 
+      corresponding <function>destroy()</function> method. The destroy
+      method will decrease the reference count on the object and,
+      whenever the reference count reaches zero, it will also destroy
+      the object and free all of the associated memory.
+    </para>
+    <para>
+      All of HarfBuzz's object-lifecycle-management APIs are
+      thread-safe (unless you compiled HarfBuzz from source with the
+      <literal>HB_NO_MT</literal> configuration flag), even when the
+      object as a whole is not thread-safe. 
+      It is also permissible to <function>reference()</function> or to 
+      <function>destroy()</function> the <literal>NULL</literal>
+      value.
+    </para>
+    <para>
+      Some objects are thread-safe after they have been constructed
+      and set up. The general pattern is to
+      <function>create()</function> the object, make a few
+      <function>set_*()</function> calls to set up the
+      object, and then use it without further modification.
+    </para>
+    <para>
+      To ensure that such an object is not modified, client programs
+      can explicitly mark an object as immutable. HarfBuzz provides
+      <function>make_immutable()</function> methods to mark an object
+      as immutable and <function>is_immutable()</function> methods to
+      test whether or not an object is immutable. Attempts to use
+      setter functions on immutable objects will fail silently; see the API
+      Reference manual for specifics. 
+    </para>
+    <para>
+      Note also that there are no "make mutable" methods. If client
+      programs need to alter an object previously marked as immutable,
+      they will need to make a duplicate of the original.
+    </para>
+    <para>
+      Finally, object constructors (and, indeed, as much of the
+      shaping API as possible) will never return
+      <literal>NULL</literal>.  Instead, if there is an allocation
+      error, each constructor will return an “empty” object
+      singleton.
+    </para>
+    <para>
+      These empty-object singletons are inert and safe (although
+      typically useless) to pass around.  This design choice avoids
+      having to check for <literal>NULL</literal> pointers all
+      throughout the code.
+    </para>
+    <para>
+      In addition, this “empty” object singleton can also be accessed
+      using the <function>get_empty()</function> method of the object
+      type in question.
+    </para>
+  </section>
+
+  
+  <section id="object-model-user-data">
+    <title>User data</title>
+    <para>
+      To better integrate with client programs, HarfBuzz's objects
+      offer a "user data" mechanism that can be used to attach
+      arbitrary data to the object.  User-data attachment can be
+      useful for tying the lifecycles of various pieces of data
+      together, or for creating language bindings. 
+    </para>
+    <para>
+      Each object type has a <function>set_user_data()</function>
+      method and a <function>get_user_data()</function> method. The
+      <function>set_user_data()</function> methods take a client-provided
+      <literal>key</literal> and a pointer,
+      <literal>user_data</literal>, pointing to the data itself. Once
+      the key-data pair has been attached to the object, the
+      <function>get_user_data()</function> method can be called with
+      the key, returning the <function>user_data</function> pointer.
+    </para>
+    <para>
+      The <function>set_user_data()</function> methods also support an
+      optional <function>destroy</function> callback. Client programs
+      can set the <function>destroy</function> callback and receive
+      notification from HarfBuzz whenever the object is destructed.
+    </para>
+    <para>
+      Finally, each <function>set_user_data()</function> method allows
+      the client program to set a <literal>replace</literal> Boolean
+      indicating whether or not the function call should replace any
+      existing <literal>user_data</literal>
+      associated with the specified key.
+    </para>
+  </section>  
+
+  
+  
+  <section id="object-model-blobs">
+    <title>Blobs</title>
+    <para>
+      While most of HarfBuzz's object types are specific to the
+      shaping process, <emphasis>blobs</emphasis> are somewhat
+      different.
+    </para>
+    <para>
+      Blobs are an abstraction desgined to negotiate lifecycle and
+      permissions for raw pieces of data.  For example, when you load
+      the raw font data into memory and want to pass it to HarfBuzz,
+      you do so in a <literal>hb_blob_t</literal> wrapper.
+    </para>
+    <para>
+      This allows you to take advantage of HarffBuzz's
+      reference-counting and <function>destroy</function>
+      callbacks. If you allocated the memory for the data using 
+      <function>malloc()</function>, you would create the blob using
+    </para>
+    <programlisting language="C">
+      hb_blob_create (data, length, HB_MEMORY_MODE_WRITABLE, NULL, free)
+    </programlisting>
+    <para>
+      That way, HarfBuzz will call <function>free()</function> on the
+      allocated memory whenever the blob drops its last reference and
+      is deconstructed.  Consequently, the user code can stop worrying
+      about freeing memory and let the reference-counting machinery
+      take care of that. 
+    </para>
+  </section>
+  
+</chapter>
diff --git a/docs/usermanual-opentype-features.xml b/docs/usermanual-opentype-features.xml
index 51ff55a..881af2a 100644
--- a/docs/usermanual-opentype-features.xml
+++ b/docs/usermanual-opentype-features.xml
@@ -6,14 +6,301 @@
 ]>
 <chapter id="shaping-and-shape-plans">
   <title>Shaping and shape plans</title>
-  <section id="opentype-features">
+  <para>
+    Once you have your face and font objects configured as desired and
+    your input buffer is filled with the characters you need to shape,
+    all you need to do is call <function>hb_shape()</function>.
+  </para>
+  <para>
+    HarfBuzz will return the shaped version of the text in the same
+    buffer that you provided, but it will be in output mode. At that
+    point, you can iterate through the glyphs in the buffer, drawing
+    each one at the specified position or handing them off to the
+    appropriate graphics library.
+  </para>
+  <para>
+    For the most part, HarfBuzz's shaping step is straightforward from
+    the outside. But that doesn't mean there will never be cases where
+    you want to look under the hood and see what is happening on the
+    inside. HarfBuzz provides facilities for doing that, too.
+  </para>
+  
+  <section id="shaping-buffer-output">
+    <title>Shaping and buffer output</title>
+    <para>
+      The <function>hb_shape()</function> function call takes four arguments: the font
+      object to use, the buffer of characters to shape, an array of
+      user-specified features to apply, and the length of that feature
+      array. The feature array can be NULL, so for the sake of
+      simplicity we will start with that case.
+    </para>
+    <para>
+      Internally, HarfBuzz looks  at the tables of the font file to
+      determine where glyph classes, substitutions, and positioning
+      are defined, using that information to decide which
+      <emphasis>shaper</emphasis> to use (<literal>ot</literal> for
+      OpenType fonts, <literal>aat</literal> for Apple Advanced
+      Typography fonts, and so on). It also looks at the direction,
+      script, and language properties of the segment to figure out
+      which script-specific shaping model is needed (at least, in
+      shapers that support multiple options).      
+    </para>
+    <para>
+      If a font has a GDEF table, then that is used for
+      glyph classes; if not, HarfBuzz will fall back to Unicode
+      categorization by code point. If a font has an AAT "morx" table,
+      then it is used for substitutions; if not, but there is a GSUB
+      table, then the GSUB table is used. If the font has an AAT
+      "kerx" table, then it is used for positioning; if not, but
+      there is a GPOS table, then the GPOS table is used. If neither
+      table is found, but there is a "kern" table, then HarfBuzz will
+      use the "kern" table. If there is no "kerx", no GPOS, and no
+      "kern", HarfBuzz will fall back to positioning marks itself.
+    </para>
+    <para>
+      With a well-behaved OpenType font, you expect GDEF, GSUB, and
+      GPOS tables to all be applied. HarfBuzz implements the
+      script-specific shaping models in internal functions, rather
+      than in the public API.
+    </para>
+    <para>
+      The algorithms
+      used for complex scripts can be quite involved; HarfBuzz tries
+      to be compatible with the OpenType Layout specification
+      and, wherever there is any ambiguity, HarfBuzz attempts to replicate the
+      output of Microsoft's Uniscribe engine. See the <ulink
+      url="https://docs.microsoft.com/en-us/typography/script-development/standard">Microsoft
+      Typography pages</ulink> for more detail.
+    </para>
+    <para>
+      In general, though, all that you need to know is that
+      <function>hb_shape()</function> returns the results of shaping
+      in the same buffer that you provided. The buffer's content type
+      will now be set to
+      <literal>HB_BUFFER_CONTENT_TYPE_GLYPHS</literal>, indicating
+      that it contains shaped output, rather than input text. You can
+      now extract the glyph information and positioning arrays:
+    </para>
+    <programlisting language="C">
+      hb_glyph_info_t *glyph_info    = hb_buffer_get_glyph_infos(buf, &amp;glyph_count);
+      hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &amp;glyph_count);
+    </programlisting>
+    <para>
+      The glyph information array holds a <type>hb_glyph_info_t</type>
+      for each output glyph, which has two fields:
+      <parameter>codepoint</parameter> and
+      <parameter>cluster</parameter>. Whereas, in the input buffer,
+      the <parameter>codepoint</parameter> field contained the Unicode
+      code point, it now contains the glyph ID of the corresponding
+      glyph in the font. The <parameter>cluster</parameter> field is
+      an integer that you can use to help identify when shaping has
+      reordered, split, or combined code points; we will say more
+      about that in the next chapter.
+    </para>
+    <para>
+      The glyph positions array holds a corresponding
+      <type>hb_glyph_position_t</type> for each output glyph,
+      containing four fields: <parameter>x_advance</parameter>,
+      <parameter>y_advance</parameter>,
+      <parameter>x_offset</parameter>, and
+      <parameter>y_offset</parameter>. The advances tell you how far
+      you need to move the drawing point after drawing this glyph,
+      depending on whether you are setting horizontal text (in which
+      case you will have x advances) or vertical text (for which you
+      will have y advances). The x and y offsets tell you where to
+      move to start drawing the glyph; usually you will have both and
+      x and a y offset, regardless of the text direction.
+    </para>
+    <para>
+      Most of the time, you will rely on a font-rendering library or
+      other graphics library to do the actual drawing of glyphs, so
+      you will need to iterate through the glyphs in the buffer and
+      pass the corresponding values off.
+    </para>
+  </section>
+  
+  <section id="shaping-opentype-features">
     <title>OpenType features</title>
     <para>
+      OpenType features enable fonts to include smart behavior,
+      implemented as "lookup" rules stored in the GSUB and GPOS
+      tables. The OpenType specification defines a long list of
+      standard features that fonts can use for these behaviors; each
+      feature has a four-character reserved name and a well-defined
+      semantic meaning.
+    </para>
+    <para>
+      Some OpenType features are defined for the purpose of supporting
+      complex-script shaping, and are automatically activated, but
+      only when a buffer's script property is set to a script that the
+      feature supports.
+    </para>
+    <para>
+      Other features are more generic and can apply to several (or
+      any) script, and shaping engines are expected to implement
+      them. By default, HarfBuzz activates several of these features
+      on every text run. They include <literal>abvm</literal>,
+      <literal>blwm</literal>, <literal>ccmp</literal>,
+      <literal>locl</literal>, <literal>mark</literal>,
+      <literal>mkmk</literal>, and <literal>rlig</literal>.
+    </para>
+    <para>
+      In addition, if the text direction is horizontal, HarfBuzz
+      also applies the <literal>calt</literal>,
+      <literal>clig</literal>, <literal>curs</literal>,
+      <literal>dist</literal>, <literal>kern</literal>,
+      <literal>liga</literal>, <literal>rclt</literal>,
+      and <literal>frac</literal> features.
+    </para>
+    <para>
+      If the text direction is vertical, HarfBuzz applies
+      the <literal>vert</literal> feature by default.
+    </para>
+    <para>
+      Still other features are designed to be purely optional and left
+      up to the application or the end user to enable or disable as desired.
+    </para>
+    <para>
+      You can adjust the set of features that HarfBuzz applies to a
+      buffer by supplying an array of <type>hb_feature_t</type>
+      features as the third argument to
+      <function>hb_shape()</function>. For a simple case, let's just
+      enable the <literal>dlig</literal> feature, which turns on any
+      "discretionary" ligatures in the font:
+    </para>
+    <programlisting language="C">
+      hb_feature_t userfeatures[1];
+      userfeatures[0].tag = HB_TAG('d','l','i','g');
+      userfeatures[0].value = 1;
+      userfeatures[0].start = HB_FEATURE_GLOBAL_START;
+      userfeatures[0].end = HB_FEATURE_GLOBAL_END;
+    </programlisting>
+    <para>
+      <literal>HB_FEATURE_GLOBAL_END</literal> and
+      <literal>HB_FEATURE_GLOBAL_END</literal> are macros we can use
+      to indicate that the features will be applied to the entire
+      buffer. We could also have used a literal <literal>0</literal>
+      for the start and a <literal>-1</literal> to indicate the end of
+      the buffer (or have selected other start and end positions, if needed).
+    </para>
+    <para>
+      When we pass the <varname>userfeatures</varname> array to
+      <function>hb_shape()</function>, any discretionary ligature
+      substitutions from our font that match the text in our buffer
+      will get performed:
+    </para>
+    <programlisting language="C">
+      hb_shape(font, buf, userfeatures, num_features);
+    </programlisting>
+    <para>
+      Just like we enabled the <literal>dlig</literal> feature by
+      setting its <parameter>value</parameter> to
+      <literal>1</literal>, you would disable a feature by setting its
+      <parameter>value</parameter> to <literal>0</literal>. Some
+      features can take other <parameter>value</parameter> settings;
+      be sure you read the full specification of each feature tag to
+      understand what it does and how to control it.
     </para>
   </section>
-  <section id="plans-and-caching">
+
+  <section id="shaping-shaper-selection">
+    <title>Shaper selection</title>
+    <para>
+      The basic version of <function>hb_shape()</function> determines
+      its shaping strategy based on examining the capabilities of the
+      font file. OpenType font tables cause HarfBuzz to try the
+      <literal>ot</literal> shaper, while AAT font tables cause HarfBuzz to try the
+      <literal>aat</literal> shaper. 
+    </para>
+    <para>
+      In the real world, however, a font might include some unusual
+      mix of tables, or one of the tables might simply be broken for
+      the script you need to shape. So, sometimes, you might not
+      want to rely on HarfBuzz's process for deciding what to do, and
+      just tell <function>hb_shape()</function> what you want it to try.
+    </para>
+    <para>
+      <function>hb_shape_full()</function> is an alternate shaping
+      function that lets you supply a list of shapers for HarfBuzz to
+      try, in order, when shaping your buffer. For example, if you
+      have determined that HarfBuzz's attempts to work around broken
+      tables gives you better results than the AAT shaper itself does,
+      you might move the AAT shaper to the end of your list of
+      preferences and call <function>hb_shape_full()</function>
+    </para>
+    <programlisting language="C">
+      char *shaperprefs[3] = {"ot", "default", "aat"};
+      ...
+      hb_shape_full(font, buf, userfeatures, num_features, shaperprefs);
+    </programlisting>
+    <para>
+      to get results you are happier with.
+    </para>
+    <para>
+      You may also want to call
+      <function>hb_shape_list_shapers()</function> to get a list of
+      the shapers that were built at compile time in your copy of HarfBuzz.
+    </para>
+  </section>
+  
+  <section id="shaping-plans-and-caching">
     <title>Plans and caching</title>
     <para>
+      Internally, HarfBuzz uses a structure called a shape plan to
+      track its decisions about how to shape the contents of a
+      buffer. The <function>hb_shape()</function> function builds up the shape plan by
+      examining segment properties and by inspecting the contents of
+      the font.
+    </para>
+    <para>
+      This process can involve some decision-making and
+      trade-offs — for example, HarfBuzz inspects the GSUB and GPOS
+      lookups for the script and language tags set on the segment
+      properties, but it falls back on the lookups under the
+      <literal>DFLT</literal> tag (and sometimes other common tags)
+      if there are actually no lookups for the tag requested.
+    </para>
+    <para>
+      HarfBuzz also includes some work-arounds for
+      handling well-known older font conventions that do not follow
+      OpenType or Unicode specifications, for buggy system fonts, and for
+      peculiarities of Microsoft Uniscribe. All of that means that a
+      shape plan, while not something that you should edit directly in
+      client code, still might be an object that you want to
+      inspect. Furthermore, if resources are tight, you might want to
+      cache the shape plan that HarfBuzz builds for your buffer and
+      font, so that you do not have to rebuild it for every shaping call.
+    </para>
+    <para>
+      You can create a cacheable shape plan with
+      <function>hb_shape_plan_create_cached(face, props,
+      user_features, num_user_features, shaper_list)</function>, where
+      <parameter>face</parameter> is a face object (not a font object,
+      notably), <parameter>props</parameter> is an
+      <type>hb_segment_properties_t</type>,
+      <parameter>user_features</parameter> is an array of
+      <type>hb_feature_t</type>s (with length
+      <parameter>num_user_features</parameter>), and
+      <parameter>shaper_list</parameter> is a list of shapers to try.
+    </para>
+    <para>
+      Shape plans are objects in HarfBuzz, so there are
+      reference-counting functions and user-data attachment functions
+      you can
+      use. <function>hb_shape_plan_reference(shape_plan)</function>
+      increases the reference count on a shape plan, while
+      <function>hb_shape_plan_destroy(shape_plan)</function> decreases
+      the reference count, destroying the shape plan when the last
+      reference is dropped.
+    </para>
+    <para>
+      You can attach user data to a shaper (with a key) using the
+      <function>hb_shape_plan_set_user_data(shape_plan,key,data,destroy,replace)</function>
+      function, optionally supplying a <function>destroy</function>
+      callback to use. You can then fetch the user data attached to a
+      shape plan with
+      <function>hb_shape_plan_get_user_data(shape_plan, key)</function>.
     </para>
   </section>
+  
 </chapter>
diff --git a/docs/usermanual-shaping-concepts.xml b/docs/usermanual-shaping-concepts.xml
index bc9f1b8..db4e309 100644
--- a/docs/usermanual-shaping-concepts.xml
+++ b/docs/usermanual-shaping-concepts.xml
@@ -182,22 +182,23 @@
       Southeast Asian scripts are also assigned
       <emphasis>Unicode Indic Syllabic Category</emphasis> (UISC) and
       <emphasis>Unicode Indic Positional Category</emphasis> (UIPC)
-      property that provides more detailed information needed for
+      properties that provide more detailed information needed for
       shaping.
     </para>
     <para>
       The UISC property sub-categorizes Letters and Marks according to
       common script-shaping behaviors. For example, UISC distinguishes
       between consonant letters, vowel letters, and vowel marks. The
-      UIPC property sub-categorizes Mark codepoints by the visual
+      UIPC property sub-categorizes Mark codepoints by the relative visual
       position that they occupy (above, below, right, left, or in
       multiple positions).
     </para>
     <para>
       Some complex scripts require that the text run be split into
-      syllables, and what constitutes a valid syllable in these
-      scripts is specified in regular expressions of the Letter and
-      Mark codepoints that take the UISC and UIPC properties into account.
+      syllables. What constitutes a valid syllable in these
+      scripts is specified in regular expressions, formed from the
+      Letter and Mark codepoints, that take the UISC and UIPC
+      properties into account.
     </para>
 
   </section>
diff --git a/docs/usermanual-utilities.xml b/docs/usermanual-utilities.xml
new file mode 100644
index 0000000..1c5370c
--- /dev/null
+++ b/docs/usermanual-utilities.xml
@@ -0,0 +1,244 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
+<chapter id="utilities">
+  <title>Utilities</title>
+  <para>
+    HarfBuzz includes several auxiliary components in addition to the
+    main APIs. These include a set of command-line tools, a set of
+    lower-level APIs for common data types that may be of interest to
+    client programs, and an embedded library for working with
+    Unicode Character Database (UCD) data.
+  </para>
+  
+  <section id="utilities-command-line-tools">
+    <title>Command-line tools</title>
+    <para>
+      HarfBuzz include three command-line tools:
+      <program>hb-shape</program>, <program>hb-view</program>, and
+      <program>hb-subset</program>. They can be used to examine
+      HarfBuzz's functionality, debug font binaries, or explore the
+      various shaping models and features from a terminal.
+    </para>
+    
+    <section id="utilities-command-line-hbshape">
+      <title>hb-shape</title>
+      <para>
+	<emphasis><program>hb-shape</program></emphasis> allows you to run HarfBuzz's
+	<function>hb_shape()</function> function on an input string and
+	to examine the outcome, in human-readable form, as terminal
+	output. <program>hb-shape</program> does
+	<emphasis>not</emphasis> render the results of the shaping call
+	into rendered text (you can use <program>hb-view</program>, below, for
+	that). Instead, it prints out the final glyph indices and
+	positions, taking all shaping operations into account, as if the
+	input string were a HarfBuzz input buffer.
+      </para>
+      <para>
+	You can specify the font to be used for shaping and, with
+	command-line options, you can add various aspects of the
+	internal state to the output that is sent to the terminal. The
+	general format is
+      </para>
+      <programlisting>
+	<command>hb-shape</command> <optional>[OPTIONS]</optional>
+      <parameter>path/to/font/file.ttf</parameter>
+      <parameter>yourinputtext</parameter>
+      </programlisting>
+      <para>
+	The default output format is plain text (although JSON output
+	can be selected instead by specifying the option
+	<optional>--output-format=json</optional>). The default output
+	syntax reports each glyph name (or glyph index if there is no
+	name) followed by its cluster value, its horizontal and vertical
+	position displacement, and its horizontal and vertical advances.
+      </para>
+      <para>
+	Output options exist to skip any of these elements in the
+	output, and to include additional data, such as Unicode
+	code-point values, glyph extents, glyph flags, or interim
+	shaping results.
+      </para>
+      <para>
+	Output can also be redirected to a file, or input read from a
+	file. Additional options enable you to enable or disable
+	specific font features, to set variation-font axis values, to
+	alter the language, script, direction, and clustering settings
+	used, to enable sanity checks, or to change which shaping engine is used.
+      </para>
+      <para>
+	For a complete explanation of the options available, run
+      </para>
+      <programlisting>
+	<command>hb-shape</command> <parameter>--help</parameter>
+      </programlisting>  
+    </section>
+    
+    <section id="utilities-command-line-hbview">
+      <title>hb-view</title>
+      <para>
+	<emphasis><program>hb-view</program></emphasis> allows you to
+	see the shaped output of an input string in rendered
+	form. Like <program>hb-shape</program>,
+	<program>hb-view</program> takes a font file and a text string
+	as its arguments:
+      </para>
+      <programlisting>
+	<command>hb-view</command> <optional>[OPTIONS]</optional>
+	<parameter>path/to/font/file.ttf</parameter>
+	<parameter>yourinputtext</parameter>
+      </programlisting>
+      <para>
+	By default, <program>hb-view</program> renders the shaped
+	text in ASCII block-character images as terminal output. By
+	appending the
+	<command>--output-file=<optional>filename</optional></command>
+	switch, you can write the output to a PNG, SVG, or PDF file
+	(among other formats).
+      </para>
+      <para>
+	As with <program>hb-shape</program>, a lengthy set of options
+	is available, with which you can  enable or disable
+	specific font features, set variation-font axis values,
+	alter the language, script, direction, and clustering settings
+	used, enable sanity checks, or change which shaping engine is
+	used.
+      </para>
+      <para>
+	You can also set the foreground and background colors used for
+	the output, independently control the width of all four
+	margins, alter the line spacing, and annotate the output image
+	with 
+      </para>
+      <para>
+	In general, <program>hb-view</program> is a quick way to
+	verify that the output of HarfBuzz's shaping operation looks
+	correct for a given text-and-font combination, but you may
+	want to use <program>hb-shape</program> to figure out exactly
+	why something does not appear as expected.
+      </para>
+    </section>
+    
+    <section id="utilities-command-line-hbsubset">
+      <title>hb-subset</title>
+      <para>
+	<emphasis><program>hb-subset</program></emphasis> allows you
+	to generate a subset of a given font, with a limited set of
+	supported characters, features, and variation settings.
+      </para>
+      <para>
+	By default, you provide an input font and an input text string
+	as the arguments to <program>hb-subset</program>, and it will
+	generate a font that covers the input text exactly like the
+	input font does, but includes no other characters or features.
+      </para>
+      <programlisting>
+	<command>hb-subset</command> <optional>[OPTIONS]</optional>
+	<parameter>path/to/font/file.ttf</parameter>
+	<parameter>yourinputtext</parameter>
+      </programlisting>
+      <para>
+	For example, to create a subset of Noto Serif that just includes the
+	numerals and the lowercase Latin alphabet, you could run
+      </para>
+      <programlisting>
+	<command>hb-subset</command> <optional>[OPTIONS]</optional>
+	<parameter>NotoSerif-Regular.ttf</parameter>
+	<parameter>0123456789abcdefghijklmnopqrstuvwxyz</parameter>
+      </programlisting>
+      <para>
+	There are options available to remove hinting from the
+	subsetted font and to specify a list of variation-axis settings.
+      </para>
+    </section>
+    
+  </section>
+  
+  <section id="utilities-common-types-apis">
+    <title>Common data types and APIs</title>
+    <para>
+      HarfBuzz includes several APIs for working with general-purpose
+      data that you may find convenient to leverage in your own
+      software. They include set operations and integer-to-integer
+      mapping operations.
+    </para>
+    <para>
+      HarfBuzz uses set operations for internal bookkeeping, such as
+      when it collects all of the glyph IDs covered by a particular
+      font feature. You can also use the set API to build sets, add
+      and remove elements, test whether or not sets contain particular
+      elements, or compute the unions, intersections, or differences
+      between sets.
+    </para>
+    <para>
+      All set elements are integers (specifically,
+      <type>hb_codepoint_t</type> 32-bit unsigned ints), and there are
+      functions for fetching the minimum and maximum element from a
+      set. The set API also includes some functions that might not 
+      be part of a generic set facility, such as the ability to add a
+      contiguous range of integer elements to a set in bulk, and the
+      ability to fetch the next-smallest or next-largest element.
+    </para>
+    <para>
+      The HarfBuzz set API includes some conveniences as well. All
+      sets are lifecycle-managed, just like other HarfBuzz
+      objects. You increase the reference count on a set with
+      <function>hb_set_reference()</function> and decrease it with
+      <function>hb_set_destroy()</function>. You can also attach
+      user data to a set, just like you can to blobs, buffers, faces,
+      fonts, and other objects, and set destroy callbacks.
+    </para>
+    <para>
+      HarfBuzz also provides an API for keeping track of
+      integer-to-integer mappings. As with the set API, each integer is
+      stored as an unsigned 32-bit <type>hb_codepoint_t</type>
+      element. Maps, like other objects, are reference counted with
+      reference and destroy functions, and you can attach user data to
+      them. The mapping operations include adding and deleting
+      integer-to-integer key:value pairs to the map, testing for the
+      presence of a key, fetching the population of the map, and so on.
+    </para>
+    <para>
+      There are several other internal HarfBuzz facilities that are
+      exposed publicly and which you may want to take advantage of
+      while processing text. HarfBuzz uses a common
+      <type>hb_tag_t</type> for a variety of OpenType tag identifiers (for
+      scripts, languages, font features, table names, variation-axis
+      names, and more), and provides functions for converting strings
+      to tags and vice-versa. 
+    </para>
+    <para>
+      Finally, HarfBuzz also includes data type for Booleans, bit
+      masks, and other simple types.
+    </para>
+  </section>
+
+  <section id="utilities-ucdn">
+    <title>UCDN</title>
+    <para>
+      HarfBuzz includes a copy of the <ulink
+      url="https://github.com/grigorig/ucdn">UCDN</ulink> (Unicode
+      Database and Normalization) library, which provides functions
+      for accessing basic Unicode character properties, performing
+      canonical composition, and performing both canonical and
+      compatibility decomposition.
+    </para>
+    <para>
+      Currently, UCDN supports direct queries for several more character
+      properties than HarfBuzz's built-in set of Unicode functions
+      does, such as the BiDirectional Class, East Asian Width, Paired
+      Bracket and Resolved Linebreak properties. If you need to access
+      more properties than HarfBuzz's internal implementation
+      provides, using the built-in UCDN functions may be a useful solution.
+    </para>
+    <para>
+      The built-in UCDN functions are compiled by default when
+      building HarfBuzz from source, but this can be disabled with a
+      compile-time switch.
+    </para>
+  </section>
+
+</chapter>
diff --git a/docs/usermanual-what-is-harfbuzz.xml b/docs/usermanual-what-is-harfbuzz.xml
index 8532d7c..3513fb2 100644
--- a/docs/usermanual-what-is-harfbuzz.xml
+++ b/docs/usermanual-what-is-harfbuzz.xml
@@ -18,7 +18,7 @@
   <para>
     HarfBuzz can properly shape all of the world's major writing
     systems. It runs on all major operating systems and software
-    platforms and it supports the modern font formats in use
+    platforms and it supports the major font formats in use
     today.
   </para>
   <section id="what-is-text-shaping">
@@ -48,7 +48,8 @@
       url="http://www.microsoft.com/typography/otspec/">OpenType</ulink>. The
     OpenType specification defines a series of <ulink url="https://github.com/n8willis/opentype-shaping-documents">shaping models</ulink> for
     various scripts from around the world. These shaping models depend on
-    the font including certain features in its <literal>GSUB</literal>
+    the font incorporating certain features as
+    <emphasis>lookups</emphasis> in its <literal>GSUB</literal> 
     and <literal>GPOS</literal> tables.
     </para>
     <para>
@@ -65,7 +66,7 @@
       Text strings will usually be tagged with a script and language
       tag that provide the context needed to perform text shaping
       correctly.  The necessary <ulink
-      url="https://docs.microsoft.com/en-us/typography/opentype/spec/scripttags">Script</ulink> 
+      url="https://docs.microsoft.com/en-us/typography/opentype/spec/scripttags">script</ulink> 
       and <ulink
       url="https://docs.microsoft.com/en-us/typography/opentype/spec/languagetags">language</ulink>
       tags are defined by OpenType.
@@ -126,8 +127,8 @@
         <para>
           Many OpenType fonts contain ligatures: combinations of
           characters that are rendered as a single unit. For instance,
-	  it is common for the <literal>fi</literal> letter
-	  combination to appear in print as the single ligature glyph
+	  it is common for the &quot;f, i&quot; letter
+	  sequence to appear in print as the single ligature glyph
 	  &quot;fi&quot;.
 	</para>
 	<para>
@@ -150,9 +151,9 @@
         </para>
 	<para>
 	  For example, in Tamil, when the letter &quot;TTA&quot; (ட)
-	  letter is followed by &quot;U&quot; (உ), the pair
+	  letter is followed by the vowel sign &quot;U&quot; (ு), the pair
 	  must be replaced by the single glyph &quot;டு&quot;. The
-	  sequence of Unicode characters &quot;டஉ&quot; needs to be
+	  sequence of Unicode characters &quot;ட,ு&quot; needs to be
 	  substituted with a single &quot;டு&quot; glyph from the
 	  font.
 	</para>
diff --git a/mingw-configure.sh b/mingw-configure.sh
new file mode 100755
index 0000000..3281ce3
--- /dev/null
+++ b/mingw-configure.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+case $1 in
+	i686 | x86_64) ;;
+	*) echo "Usage: $0 i686|x86_64" >&2; exit 1 ;;
+esac
+
+target=$1-w64-mingw32
+shift
+
+exec "$(dirname "$0")"/configure \
+	--build=`../config.guess` \
+	--host=$target \
+	--prefix=$HOME/.local/$target \
+	CC= \
+	CXX= \
+	CPP= \
+	LD= \
+	CFLAGS="-static-libgcc" \
+	CXXFLAGS="-static-libgcc -static-libstdc++" \
+	CPPFLAGS="-I$HOME/.local/$target/include" \
+	LDFLAGS=-L$HOME/.local/$target/lib \
+	PKG_CONFIG_LIBDIR=$HOME/.local/$target/lib/pkgconfig:/usr/$target/sys-root/mingw/lib/pkgconfig/ \
+	PKG_CONFIG_PATH=$HOME/.local/$target/share/pkgconfig:/usr/$target/sys-root/mingw/share/pkgconfig/ \
+	PATH=$HOME/.local/$target/bin:/usr/$target/sys-root/mingw/bin:/usr/$target/bin:$PATH \
+	--without-icu \
+	--with-uniscribe \
+	"$@"
diff --git a/mingw-ldd.py b/mingw-ldd.py
new file mode 100755
index 0000000..1d659ef
--- /dev/null
+++ b/mingw-ldd.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+
+# Copied from https://github.com/xantares/mingw-ldd/blob/master/mingw-ldd.py
+# Modified to point to right prefix location on Fedora.
+
+# WTFPL - Do What the Fuck You Want to Public License
+from __future__ import print_function
+import pefile
+import os
+import sys
+
+
+def get_dependency(filename):
+    deps = []
+    pe = pefile.PE(filename)
+    for imp in pe.DIRECTORY_ENTRY_IMPORT:
+        deps.append(imp.dll.decode())
+    return deps
+
+
+def dep_tree(root, prefix=None):
+    if not prefix:
+        arch = get_arch(root)
+        #print('Arch =', arch)
+        prefix = '/usr/'+arch+'-w64-mingw32/sys-root/mingw/bin'
+        #print('Using default prefix', prefix)
+    dep_dlls = dict()
+
+    def dep_tree_impl(root, prefix):
+        for dll in get_dependency(root):
+            if dll in dep_dlls:
+                continue
+            full_path = os.path.join(prefix, dll)
+            if os.path.exists(full_path):
+                dep_dlls[dll] = full_path
+                dep_tree_impl(full_path, prefix=prefix)
+            else:
+                dep_dlls[dll] = 'not found'
+
+    dep_tree_impl(root, prefix)
+    return (dep_dlls)
+
+
+def get_arch(filename):
+    type2arch= {pefile.OPTIONAL_HEADER_MAGIC_PE: 'i686',
+                pefile.OPTIONAL_HEADER_MAGIC_PE_PLUS: 'x86_64'}
+    pe = pefile.PE(filename)
+    try:
+        return type2arch[pe.PE_TYPE]
+    except KeyError:
+        sys.stderr.write('Error: unknown architecture')
+        sys.exit(1)
+
+if __name__ == '__main__':
+    filename = sys.argv[1]
+    for dll, full_path in dep_tree(filename).items():
+        print(' ' * 7, dll, '=>', full_path)
+
diff --git a/mingw32.sh b/mingw32.sh
index 6774405..77edffa 100755
--- a/mingw32.sh
+++ b/mingw32.sh
@@ -1,22 +1,2 @@
-#!/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 "$@"
+#!/bin/sh
+exec "$(dirname "$0")"/mingw-configure.sh i686 "$@"
diff --git a/mingw64.sh b/mingw64.sh
index 49a1431..28724a4 100755
--- a/mingw64.sh
+++ b/mingw64.sh
@@ -1,22 +1,2 @@
-#!/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 "$@"
+#!/bin/sh
+exec "$(dirname "$0")"/mingw-configure.sh x86_64 "$@"
diff --git a/src/Makefile.am b/src/Makefile.am
index 3618d03..a76d968 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,9 +12,15 @@
 TESTS =
 check_PROGRAMS =
 
+EXTRA_DIST += harfbuzz.cc
+
 # Convenience targets:
 lib: $(BUILT_SOURCES) libharfbuzz.la
 libs: $(BUILT_SOURCES) $(lib_LTLIBRARIES)
+tiny:
+	$(MAKE) $(AM_MAKEFLAGS) CPPFLAGS="-Os -DHB_TINY $(CPPFLAGS)" libs
+tinyz:
+	$(MAKE) $(AM_MAKEFLAGS) CPPFLAGS="-Oz -DHB_TINY $(CPPFLAGS)" libs
 
 lib_LTLIBRARIES = libharfbuzz.la
 
@@ -28,10 +34,6 @@
 HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources)
 HBHEADERS = $(HB_BASE_headers)
 
-if HAVE_FALLBACK
-HBSOURCES += $(HB_FALLBACK_sources)
-endif
-
 if HAVE_PTHREAD
 HBCFLAGS += $(PTHREAD_CFLAGS)
 HBNONPCLIBS += $(PTHREAD_LIBS)
@@ -80,6 +82,13 @@
 HBHEADERS += $(HB_DIRECTWRITE_headers)
 endif
 
+if HAVE_GDI
+HBCFLAGS += $(GDI_CXXFLAGS)
+HBNONPCLIBS += $(GDI_LIBS)
+HBSOURCES += $(HB_GDI_sources)
+HBHEADERS += $(HB_GDI_headers)
+endif
+
 if HAVE_CORETEXT
 HBCFLAGS += $(CORETEXT_CFLAGS)
 HBNONPCLIBS += $(CORETEXT_LIBS)
@@ -87,17 +96,6 @@
 HBHEADERS += $(HB_CORETEXT_headers)
 endif
 
-if HAVE_UCDN
-SUBDIRS += hb-ucdn
-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
-
 
 BUILT_SOURCES += \
 	hb-version.h
@@ -258,36 +256,44 @@
 	gen-indic-table.py \
 	gen-os2-unicode-ranges.py \
 	gen-tag-table.py \
+	gen-ucd-table.py \
 	gen-use-table.py \
 	gen-vowel-constraints.py \
 	$(NULL)
 EXTRA_DIST += $(GENERATORS)
 
-unicode-tables: arabic-table indic-table tag-table use-table emoji-table
+unicode-tables: \
+	arabic-table \
+	emoji-table \
+	indic-table \
+	tag-table \
+	ucd-table \
+	use-table \
+	emoji-table \
+	$(NULL)
 
 arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-arabic-table.hh \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-arabic-table.hh; false)
-
+emoji-table: gen-emoji-table.py emoji-data.txt
+	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-unicode-emoji-table.hh \
+	|| ($(RM) $(srcdir)/hb-unicode-emoji-table.hh; false)
 indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-indic-table.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-indic-table.cc; false)
-
 tag-table: gen-tag-table.py languagetags language-subtag-registry
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-tag-table.hh \
 	|| ($(RM) $(srcdir)/hb-ot-tag-table.hh; false)
-
+ucd-table: gen-ucd-table.py ucd.nounihan.grouped.zip hb-common.h
+	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ucd-table.hh \
+	|| ($(RM) $(srcdir)/hb-ucd-table.hh; false)
 use-table: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-use-table.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-use-table.cc; false)
-
 vowel-constraints: gen-vowel-constraints.py HBIndicVowelConstraints.txt Scripts.txt
 	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc \
 	|| ($(RM) $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc; false)
 
-emoji-table: gen-emoji-table.py emoji-data.txt
-	$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-unicode-emoji-table.hh \
-	|| ($(RM) $(srcdir)/hb-unicode-emoji-table.hh; false)
 
 built-sources: $(BUILT_SOURCES)
 
@@ -306,13 +312,30 @@
 	$(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \
 	|| ($(RM) "$@"; false)
 
+harfbuzz.cc: Makefile.sources
+	$(AM_V_GEN) \
+	for f in \
+		$(HB_BASE_sources) \
+		$(HB_GLIB_sources) \
+		$(HB_FT_sources) \
+		$(HB_GRAPHITE2_sources) \
+		$(HB_UNISCRIBE_sources) \
+		$(HB_GDI_sources) \
+		$(HB_DIRECTWRITE_sources) \
+		$(HB_CORETEXT_sources) \
+		; do echo '#include "'$$f'"'; done | \
+	grep '[.]cc"' > $(srcdir)/harfbuzz.cc \
+	|| ($(RM) $(srcdir)/harfbuzz.cc; false)
+BUILT_SOURCES += harfbuzz.cc
+
 noinst_PROGRAMS = \
 	main \
 	test \
 	test-buffer-serialize \
-	test-name-table \
-	test-size-params \
-	test-would-substitute \
+	test-ot-meta \
+	test-ot-name \
+	test-gpos-size-params \
+	test-gsub-would-substitute \
 	$(NULL)
 bin_PROGRAMS =
 
@@ -328,17 +351,21 @@
 test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
 test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
 
-test_name_table_SOURCES = test-name-table.cc
-test_name_table_CPPFLAGS = $(HBCFLAGS)
-test_name_table_LDADD = libharfbuzz.la $(HBLIBS)
+test_ot_meta_SOURCES = test-ot-meta.cc
+test_ot_meta_CPPFLAGS = $(HBCFLAGS)
+test_ot_meta_LDADD = libharfbuzz.la $(HBLIBS)
 
-test_size_params_SOURCES = test-size-params.cc
-test_size_params_CPPFLAGS = $(HBCFLAGS)
-test_size_params_LDADD = libharfbuzz.la $(HBLIBS)
+test_ot_name_SOURCES = test-ot-name.cc
+test_ot_name_CPPFLAGS = $(HBCFLAGS)
+test_ot_name_LDADD = libharfbuzz.la $(HBLIBS)
 
-test_would_substitute_SOURCES = test-would-substitute.cc
-test_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
-test_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
+test_gpos_size_params_SOURCES = test-gpos-size-params.cc
+test_gpos_size_params_CPPFLAGS = $(HBCFLAGS)
+test_gpos_size_params_LDADD = libharfbuzz.la $(HBLIBS)
+
+test_gsub_would_substitute_SOURCES = test-gsub-would-substitute.cc
+test_gsub_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
+test_gsub_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
 
 if HAVE_FREETYPE
 if HAVE_CAIRO_FT
@@ -384,15 +411,39 @@
 dump_use_data_CPPFLAGS = $(HBCFLAGS)
 dump_use_data_LDADD = libharfbuzz.la $(HBLIBS)
 
-check_PROGRAMS += test-ot-tag test-unicode-ranges
-TESTS += test-ot-tag test-unicode-ranges
+COMPILED_TESTS = test-algs test-iter test-meta test-number test-ot-tag test-unicode-ranges test-bimap
+COMPILED_TESTS_CPPFLAGS = $(HBCFLAGS) -DMAIN -UNDEBUG
+COMPILED_TESTS_LDADD = libharfbuzz.la $(HBLIBS)
+check_PROGRAMS += $(COMPILED_TESTS)
+TESTS += $(COMPILED_TESTS)
+
+test_algs_SOURCES = test-algs.cc hb-static.cc
+test_algs_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
+test_algs_LDADD = $(COMPILED_TESTS_LDADD)
+
+test_iter_SOURCES = test-iter.cc hb-static.cc
+test_iter_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
+test_iter_LDADD = $(COMPILED_TESTS_LDADD)
+
+test_meta_SOURCES = test-meta.cc hb-static.cc
+test_meta_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
+test_meta_LDADD = $(COMPILED_TESTS_LDADD)
+
+test_number_SOURCES = test-number.cc hb-number.cc
+test_number_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
+test_number_LDADD = $(COMPILED_TESTS_LDADD)
 
 test_ot_tag_SOURCES = hb-ot-tag.cc
-test_ot_tag_CPPFLAGS = $(HBCFLAGS) -DMAIN
-test_ot_tag_LDADD = libharfbuzz.la $(HBLIBS)
+test_ot_tag_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
+test_ot_tag_LDADD = $(COMPILED_TESTS_LDADD)
 
 test_unicode_ranges_SOURCES = test-unicode-ranges.cc
-test_unicode_ranges_LDADD = libharfbuzz.la $(HBLIBS)
+test_unicode_ranges_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
+test_unicode_ranges_LDADD = $(COMPILED_TESTS_LDADD)
+
+test_bimap_SOURCES = test-bimap.cc hb-static.cc
+test_bimap_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
+test_bimap_LDADD = $(COMPILED_TESTS_LDADD)
 
 TESTS_ENVIRONMENT = \
 	srcdir="$(srcdir)" \
@@ -422,6 +473,7 @@
 	-DHB_AAT_H_IN \
 	-DHB_GOBJECT_H \
 	-DHB_GOBJECT_H_IN \
+	-DHAVE_GOBJECT \
 	-DHB_EXTERN= \
 	$(NULL)
 HarfBuzz_0_0_gir_LIBS = \
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 98b6228..cbbad90 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -10,12 +10,14 @@
 	hb-aat-layout-kerx-table.hh \
 	hb-aat-layout-lcar-table.hh \
 	hb-aat-layout-morx-table.hh \
+	hb-aat-layout-opbd-table.hh \
 	hb-aat-layout-trak-table.hh \
 	hb-aat-layout.cc \
 	hb-aat-layout.hh \
 	hb-aat-ltag-table.hh \
 	hb-aat-map.cc \
 	hb-aat-map.hh \
+	hb-algs.hh \
 	hb-array.hh \
 	hb-atomic.hh \
 	hb-blob.cc \
@@ -30,18 +32,25 @@
 	hb-cff1-interp-cs.hh \
 	hb-cff2-interp-cs.hh \
 	hb-common.cc \
+	hb-config.hh \
 	hb-debug.hh \
-	hb-dsalgs.hh \
+	hb-dispatch.hh \
 	hb-face.cc \
 	hb-face.hh \
+	hb-fallback-shape.cc \
 	hb-font.cc \
 	hb-font.hh \
+	hb-iter.hh \
 	hb-kern.hh \
 	hb-machinery.hh \
 	hb-map.cc \
 	hb-map.hh \
+	hb-bimap.hh \
+	hb-meta.hh \
 	hb-mutex.hh \
 	hb-null.hh \
+	hb-number.cc \
+	hb-number.hh \
 	hb-object.hh \
 	hb-open-file.hh \
 	hb-open-type.hh \
@@ -59,6 +68,7 @@
 	hb-ot-color.cc \
 	hb-ot-face.cc \
 	hb-ot-face.hh \
+	hb-ot-face-table-list.hh \
 	hb-ot-font.cc \
 	hb-ot-gasp-table.hh \
 	hb-ot-glyf-table.hh \
@@ -81,7 +91,11 @@
 	hb-ot-math-table.hh \
 	hb-ot-math.cc \
 	hb-ot-maxp-table.hh \
-	hb-ot-name-language.cc \
+	hb-ot-meta-table.hh \
+	hb-ot-meta.cc \
+	hb-ot-metrics.cc \
+	hb-ot-metrics.hh \
+	hb-ot-name-language-static.hh \
 	hb-ot-name-language.hh \
 	hb-ot-name-table.hh \
 	hb-ot-name.cc \
@@ -122,10 +136,14 @@
 	hb-ot-tag.cc \
 	hb-ot-var-avar-table.hh \
 	hb-ot-var-fvar-table.hh \
+	hb-ot-var-gvar-table.hh \
 	hb-ot-var-hvar-table.hh \
 	hb-ot-var-mvar-table.hh \
 	hb-ot-var.cc \
 	hb-ot-vorg-table.hh \
+	hb-pool.hh \
+	hb-sanitize.hh \
+	hb-serialize.hh \
 	hb-set-digest.hh \
 	hb-set.cc \
 	hb-set.hh \
@@ -138,18 +156,20 @@
 	hb-shaper.hh \
 	hb-static.cc \
 	hb-string-array.hh \
+	hb-ucd-table.hh \
+	hb-ucd.cc \
 	hb-unicode-emoji-table.hh \
 	hb-unicode.cc \
 	hb-unicode.hh \
 	hb-utf.hh \
 	hb-vector.hh \
-	hb-warning.cc \
 	hb.hh \
 	$(NULL)
 
 HB_BASE_RAGEL_GENERATED_sources = \
 	hb-buffer-deserialize-json.hh \
 	hb-buffer-deserialize-text.hh \
+	hb-number-parser.hh \
 	hb-ot-shape-complex-indic-machine.hh \
 	hb-ot-shape-complex-khmer-machine.hh \
 	hb-ot-shape-complex-myanmar-machine.hh \
@@ -158,6 +178,7 @@
 HB_BASE_RAGEL_sources = \
 	hb-buffer-deserialize-json.rl \
 	hb-buffer-deserialize-text.rl \
+	hb-number-parser.rl \
 	hb-ot-shape-complex-indic-machine.rl \
 	hb-ot-shape-complex-khmer-machine.rl \
 	hb-ot-shape-complex-myanmar-machine.rl \
@@ -179,6 +200,8 @@
 	hb-ot-font.h \
 	hb-ot-layout.h \
 	hb-ot-math.h \
+	hb-ot-meta.h \
+	hb-ot-metrics.h \
 	hb-ot-name.h \
 	hb-ot-shape.h \
 	hb-ot-var.h \
@@ -191,10 +214,6 @@
 	hb.h \
 	$(NULL)
 
-HB_FALLBACK_sources = \
-	hb-fallback-shape.cc	\
-	$(NULL)
-
 # Optional Sources and Headers with external deps
 
 HB_FT_sources = hb-ft.cc
@@ -214,18 +233,20 @@
 HB_DIRECTWRITE_sources = hb-directwrite.cc
 HB_DIRECTWRITE_headers = hb-directwrite.h
 
+HB_GDI_sources = hb-gdi.cc
+HB_GDI_headers = hb-gdi.h
+
 HB_UNISCRIBE_sources = hb-uniscribe.cc
 HB_UNISCRIBE_headers = hb-uniscribe.h
 
-# Additional supplemental sources
-HB_UCDN_sources  = hb-ucdn.cc
-
 # Sources for libharfbuzz-gobject and libharfbuzz-icu
 HB_ICU_sources = hb-icu.cc
 HB_ICU_headers = hb-icu.h
 
 # Sources for libharfbuzz-subset
 HB_SUBSET_sources = \
+	hb-number.cc \
+	hb-number.hh \
 	hb-ot-cff1-table.cc \
 	hb-ot-cff2-table.cc \
 	hb-static.cc \
@@ -235,9 +256,6 @@
 	hb-subset-cff1.hh \
 	hb-subset-cff2.cc \
 	hb-subset-cff2.hh \
-	hb-subset-glyf.cc \
-	hb-subset-glyf.hh \
-	hb-subset-glyf.hh \
 	hb-subset-input.cc \
 	hb-subset-input.hh \
 	hb-subset-plan.cc \
diff --git a/src/check-symbols.sh b/src/check-symbols.sh
index cea8684..f181b63 100755
--- a/src/check-symbols.sh
+++ b/src/check-symbols.sh
@@ -7,7 +7,7 @@
 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_.*'
+IGNORED_SYMBOLS='_fini\|_init\|_fdata\|_ftext\|_fbss\|__bss_start\|__bss_start__\|__bss_end__\|_edata\|_end\|_bss_end__\|__end__\|__gcov_.*\|llvm_.*'
 
 if which nm 2>/dev/null >/dev/null; then
 	:
@@ -26,7 +26,7 @@
 		symprefix=
 		if test $suffix = dylib; then symprefix=_; fi
 
-		EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRST] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`"
+		EXPORTED_SYMBOLS=`nm "$so" | grep ' [BCDGINRST] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`
 
 		prefix=$symprefix`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
 
diff --git a/src/dev-run.sh b/src/dev-run.sh
deleted file mode 100755
index 82de688..0000000
--- a/src/dev-run.sh
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/bin/bash
-# Suggested setup to use the script:
-#  (on the root of the project)
-#  $ NOCONFIGURE=1 ./autogen.sh && mkdir build && cd build
-#  $ ../configure --with-freetype --with-glib --with-gobject --with-cairo
-#  $ 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]
-#
-# If you want to open the result rendering using a GUI app,
-#  $ src/dev-run.sh open [FONT-FILE] [TEXT]
-#
-# And if you are using iTerm2, you can use the script like this,
-#  $ src/dev-run.sh img [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"
-
-
-[ $1 = "open" ] && openimg=1 && shift
-OPEN=xdg-open
-[ "$(uname)" == "Darwin" ] && OPEN=open
-
-
-[ $1 = "img" ] && img=1 && shift
-# http://iterm2.com/documentation-images.html
-osc="\033]"
-if [[ $TERM == screen* ]]; then osc="\033Ptmux;\033\033]"; fi
-st="\a"
-if [[ $TERM == screen* ]]; then st="\a"; fi
-
-
-tmp=tmp.png
-[ -f 'build/build.ninja' ] && CMAKENINJA=TRUE
-# or "fswatch -0 . -e build/ -e .git"
-find src/ | entr printf '\0' | while read -d ""; do
-	clear
-	yes = | head -n`tput cols` | tr -d '\n'
-	if [[ $CMAKENINJA ]]; then
-		ninja -Cbuild hb-shape hb-view && {
-			build/hb-shape $@
-			if [ $openimg ]; then
-				build/hb-view $@ -O png -o $tmp
-				$OPEN $tmp
-			elif [ $img ]; then
-				build/hb-view $@ -O png -o $tmp
-				printf "\n${osc}1337;File=;inline=1:`cat $tmp | base64`${st}\n"
-			else
-				build/hb-view $@
-			fi
-		}
-	else
-		make -Cbuild/src -j5 -s lib && {
-			build/util/hb-shape $@
-			if [ $openimg ]; then
-				build/util/hb-view $@ -O png -o $tmp
-				$OPEN $tmp
-			elif [ $img ]; then
-				build/util/hb-view $@ -O png -o $tmp
-				printf "\n${osc}1337;File=;inline=1:`cat $tmp | base64`${st}\n"
-			else
-				build/util/hb-view $@
-			fi
-		}
-	fi
-done
-
-read -n 1 -p "[C]heck, [D]ebug, [R]estart, [Q]uit? " answer
-case "$answer" in
-c|C )
-	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/gen-emoji-table.py b/src/gen-emoji-table.py
index 9afe747..49770d4 100755
--- a/src/gen-emoji-table.py
+++ b/src/gen-emoji-table.py
@@ -4,6 +4,7 @@
 import sys
 import os.path
 from collections import OrderedDict
+import packTab
 
 if len (sys.argv) != 2:
 	print("usage: ./gen-emoji-table.py emoji-data.txt", file=sys.stderr)
@@ -52,14 +53,19 @@
 print ('#include "hb-unicode.hh"')
 print ()
 
-for typ,s in ranges.items():
+for typ, s in ranges.items():
 	if typ != "Extended_Pictographic": continue
+
+	arr = dict()
+	for start,end in s:
+		for i in range(start,end):
+			arr[i] = 1
+
+	sol = packTab.pack_table(arr, 0, compression=3)
+	code = packTab.Code('_hb_emoji')
+	sol.genCode(code, 'is_'+typ)
+	code.print_c(linkage='static inline')
 	print()
-	print("static const struct hb_unicode_range_t _hb_unicode_emoji_%s_table[] =" % typ)
-	print("{")
-	for pair in sorted(s):
-		print("  {0x%04X, 0x%04X}," % pair)
-	print("};")
 
 print ()
 print ("#endif /* HB_UNICODE_EMOJI_TABLE_HH */")
diff --git a/src/gen-indic-table.py b/src/gen-indic-table.py
index e65b981..912b1d7 100755
--- a/src/gen-indic-table.py
+++ b/src/gen-indic-table.py
@@ -79,10 +79,6 @@
 del combined
 num = len (data)
 
-for u in [0x17CD, 0x17CE, 0x17CF, 0x17D0, 0x17D3]:
-	if data[u][0] == 'Other':
-		data[u][0] = "Vowel_Dependent"
-
 # Move the outliers NO-BREAK SPACE and DOTTED CIRCLE out
 singles = {}
 for u in ALLOWED_SINGLES:
@@ -102,6 +98,10 @@
 		print (" * %s" % (l.strip()))
 print (" */")
 print ()
+print ('#include "hb.hh"')
+print ()
+print ('#ifndef HB_NO_OT_SHAPE')
+print ()
 print ('#include "hb-ot-shape-complex-indic.hh"')
 print ()
 
@@ -131,8 +131,10 @@
 
 what = ["INDIC_SYLLABIC_CATEGORY", "INDIC_MATRA_CATEGORY"]
 what_short = ["ISC", "IMC"]
+print ('#pragma GCC diagnostic push')
+print ('#pragma GCC diagnostic ignored "-Wunused-macros"')
+cat_defs = []
 for i in range (2):
-	print ()
 	vv = sorted (values[i].keys ())
 	for v in vv:
 		v_no_and = v.replace ('_And_', '_')
@@ -144,10 +146,18 @@
 				raise Exception ("Duplicate short value alias", v, all_shorts[i][s])
 			all_shorts[i][s] = v
 			short[i][v] = s
-		print ("#define %s_%s	%s_%s	%s/* %3d chars; %s */" %
-			(what_short[i], s, what[i], v.upper (),
-			'	'* ((48-1 - len (what[i]) - 1 - len (v)) // 8),
-			values[i][v], v))
+		cat_defs.append ((what_short[i] + '_' + s, what[i] + '_' + v.upper (), str (values[i][v]), v))
+
+maxlen_s = max ([len (c[0]) for c in cat_defs])
+maxlen_l = max ([len (c[1]) for c in cat_defs])
+maxlen_n = max ([len (c[2]) for c in cat_defs])
+for s in what_short:
+	print ()
+	for c in [c for c in cat_defs if s in c[0]]:
+		print ("#define %s %s /* %s chars; %s */" %
+			(c[0].ljust (maxlen_s), c[1].ljust (maxlen_l), c[2].rjust (maxlen_n), c[3]))
+print ()
+print ('#pragma GCC diagnostic pop')
 print ()
 print ("#define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M)")
 print ()
@@ -246,12 +256,14 @@
 print ()
 print ("#undef _")
 for i in range (2):
-	print
+	print ()
 	vv = sorted (values[i].keys ())
 	for v in vv:
 		print ("#undef %s_%s" %
 			(what_short[i], short[i][v]))
 print ()
+print ('#endif')
+print ()
 print ("/* == End of generated table == */")
 
 # Maintain at least 30% occupancy in the table */
diff --git a/src/gen-os2-unicode-ranges.py b/src/gen-os2-unicode-ranges.py
old mode 100644
new mode 100755
index d768313..515f4ca
--- a/src/gen-os2-unicode-ranges.py
+++ b/src/gen-os2-unicode-ranges.py
@@ -1,8 +1,10 @@
+#!/usr/bin/python
+
 # -*- coding: utf-8 -*-
 
 # Generates the code for a sorted unicode range array as used in hb-ot-os2-unicode-ranges.hh
 # Input is a tab seperated list of unicode ranges from the otspec
-# (https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ulunicoderange1).
+# (https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur).
 
 from __future__ import print_function, division, absolute_import
 
@@ -10,8 +12,11 @@
 import re
 import sys
 
-reload(sys)
-sys.setdefaultencoding('utf-8')
+try:
+  reload(sys)
+  sys.setdefaultencoding('utf-8')
+except NameError:
+  pass  # Python 3
 
 print ("""static OS2Range _hb_os2_unicode_ranges[] =
 {""")
@@ -32,12 +37,12 @@
       current_bit = fields[0]
       fields = fields[1:]
     elif len(fields) > 3:
-      raise Error("bad input :(.")
+      raise Exception("bad input :(.")
 
     name = fields[0]
     ranges = re.split("-", fields[1])
     if len(ranges) != 2:
-      raise Error("bad input :(.")
+      raise Exception("bad input :(.")
 
     v = tuple((int(ranges[0], 16), int(ranges[1], 16), int(current_bit), name))
     all_ranges.append(v)
diff --git a/src/gen-tag-table.py b/src/gen-tag-table.py
index 1300462..49f5b30 100755
--- a/src/gen-tag-table.py
+++ b/src/gen-tag-table.py
@@ -895,20 +895,18 @@
 def get_matching_language_name (intersection, candidates):
 	return next (iter (c for c in candidates if not intersection.isdisjoint (get_variant_set (c))))
 
-maximum_tags = 0
+def same_tag (bcp_47_tag, ot_tags):
+	return len (bcp_47_tag) == 3 and len (ot_tags) == 1 and bcp_47_tag == ot_tags[0].lower ()
+
 for language, tags in sorted (ot.from_bcp_47.items ()):
 	if language == '' or '-' in language:
 		continue
-	print ('  {\"%s\",\t{' % language, end='')
-	maximum_tags = max (maximum_tags, len (tags))
-	tag_count = len (tags)
+	commented_out = same_tag (language, tags)
 	for i, tag in enumerate (tags, start=1):
-		if i > 1:
-			print ('\t\t ', end='')
-		print (hb_tag (tag), end='')
-		if i == tag_count:
-			print ('}}', end='')
-		print (',\t/* ', end='')
+		print ('%s{\"%s\",\t%s},' % ('/*' if commented_out else '  ', language, hb_tag (tag)), end='')
+		if commented_out:
+			print ('*/', end='')
+		print ('\t/* ', end='')
 		bcp_47_name = bcp_47.names.get (language, '')
 		bcp_47_name_candidates = bcp_47_name.split ('\n')
 		intersection = language_name_intersection (bcp_47_name, ot.names[tag])
@@ -923,8 +921,6 @@
 
 print ('};')
 print ()
-print ('static_assert (HB_OT_MAX_TAGS_PER_LANGUAGE == %iu, "");' % maximum_tags)
-print ()
 
 print ('/**')
 print (' * hb_ot_tags_from_complex_language:')
@@ -1051,7 +1047,8 @@
 print (' *')
 print (' * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to')
 print (' * many language tags) and the best tag is not the alphabetically first, or if')
-print (' * the best tag consists of multiple subtags.')
+print (' * the best tag consists of multiple subtags, or if the best tag does not appear')
+print (' * in #ot_languages.')
 print (' *')
 print (' * Return value: The #hb_language_t corresponding to the BCP 47 language tag,')
 print (' * or #HB_LANGUAGE_INVALID if @tag is not ambiguous.')
@@ -1102,7 +1099,8 @@
 						'%s is not a valid disambiguation for %s' % (disambiguation[ot_tag], ot_tag))
 			elif ot_tag not in disambiguation:
 				disambiguation[ot_tag] = macrolanguages[0]
-			if disambiguation[ot_tag] == sorted (primary_tags)[0] and '-' not in disambiguation[ot_tag]:
+			different_primary_tags = sorted (t for t in primary_tags if not same_tag (t, ot.from_bcp_47.get (t)))
+			if different_primary_tags and disambiguation[ot_tag] == different_primary_tags[0] and '-' not in disambiguation[ot_tag]:
 				del disambiguation[ot_tag]
 	for ot_tag in disambiguation.keys ():
 		expect (ot_tag in ot.to_bcp_47, 'unknown OT tag: %s' % ot_tag)
diff --git a/src/gen-ucd-table.py b/src/gen-ucd-table.py
new file mode 100755
index 0000000..552c3c6
--- /dev/null
+++ b/src/gen-ucd-table.py
@@ -0,0 +1,164 @@
+#!/usr/bin/env python
+
+from __future__ import print_function, division, absolute_import
+
+import io, os.path, sys, re
+import logging
+logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
+
+if len (sys.argv) not in (2, 3):
+	print("usage: ./gen-ucd-table ucd.nounihan.grouped.xml [/path/to/hb-common.h]", file=sys.stderr)
+	sys.exit(1)
+
+# https://github.com/harfbuzz/packtab
+import packTab
+import packTab.ucdxml
+
+logging.info('Loading UCDXML...')
+ucdxml = packTab.ucdxml.load_ucdxml(sys.argv[1])
+ucd = packTab.ucdxml.ucdxml_get_repertoire(ucdxml)
+
+hb_common_h = 'hb-common.h' if len (sys.argv) < 3 else sys.argv[2]
+
+logging.info('Preparing data tables...')
+
+gc = [u['gc'] for u in ucd]
+ccc = [int(u['ccc']) for u in ucd]
+bmg = [int(v, 16) - int(u) if v else 0 for u,v in enumerate(u['bmg'] for u in ucd)]
+#gc_ccc_non0 = set((cat,klass) for cat,klass in zip(gc,ccc) if klass)
+#gc_bmg_non0 = set((cat,mirr) for cat,mirr in zip(gc, bmg) if mirr)
+
+sc = [u['sc'] for u in ucd]
+
+dm = {i:tuple(int(v, 16) for v in u['dm'].split()) for i,u in enumerate(ucd)
+      if u['dm'] != '#' and u['dt'] == 'can' and not (0xAC00 <= i < 0xAC00+11172)}
+ce = {i for i,u in enumerate(ucd) if u['Comp_Ex'] == 'Y'}
+
+assert not any(v for v in dm.values() if len(v) not in (1,2))
+dm1 = sorted(set(v for v in dm.values() if len(v) == 1))
+assert all((v[0] >> 16) in (0,2) for v in dm1)
+dm1_p0_array = ['0x%04Xu' % (v[0] & 0xFFFF) for v in dm1 if (v[0] >> 16) == 0]
+dm1_p2_array = ['0x%04Xu' % (v[0] & 0xFFFF) for v in dm1 if (v[0] >> 16) == 2]
+dm1_order = {v:i+1 for i,v in enumerate(dm1)}
+
+dm2 = sorted((v+(i if i not in ce and not ccc[i] else 0,), v)
+             for i,v in dm.items() if len(v) == 2)
+
+filt = lambda v: ((v[0] & 0xFFFFF800) == 0x0000 and
+                  (v[1] & 0xFFFFFF80) == 0x0300 and
+                  (v[2] & 0xFFF0C000) == 0x0000)
+dm2_u32_array = [v for v in dm2 if filt(v[0])]
+dm2_u64_array = [v for v in dm2 if not filt(v[0])]
+assert dm2_u32_array + dm2_u64_array == dm2
+dm2_u32_array = ["HB_CODEPOINT_ENCODE3_11_7_14 (0x%04Xu, 0x%04Xu, 0x%04Xu)" % v[0] for v in dm2_u32_array]
+dm2_u64_array = ["HB_CODEPOINT_ENCODE3 (0x%04Xu, 0x%04Xu, 0x%04Xu)" % v[0] for v in dm2_u64_array]
+
+l = 1 + len(dm1_p0_array) + len(dm1_p2_array)
+dm2_order = {v[1]:i+l for i,v in enumerate(dm2)}
+
+dm_order = {None: 0}
+dm_order.update(dm1_order)
+dm_order.update(dm2_order)
+
+gc_order = dict()
+for i,v in enumerate(('Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu',
+                      'Mc', 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf',
+                      'Pi', 'Po', 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs',)):
+    gc_order[i] = v
+    gc_order[v] = i
+
+sc_order = dict()
+sc_array = []
+sc_re = re.compile(r"\b(HB_SCRIPT_[_A-Z]*).*HB_TAG [(]'(.)','(.)','(.)','(.)'[)]")
+for line in open(hb_common_h):
+    m = sc_re.search (line)
+    if not m: continue
+    name = m.group(1)
+    tag = ''.join(m.group(i) for i in range(2, 6))
+    i = len(sc_array)
+    sc_order[tag] = i
+    sc_order[i] = tag
+    sc_array.append(name)
+
+DEFAULT = 1
+COMPACT = 3
+SLOPPY  = 5
+
+
+logging.info('Generating output...')
+print("/* == Start of generated table == */")
+print("/*")
+print(" * The following table is generated by running:")
+print(" *")
+print(" *   ./gen-ucd-table.py ucd.nounihan.grouped.xml")
+print(" *")
+print(" * on file with this description:", ucdxml.description)
+print(" */")
+print()
+print("#ifndef HB_UCD_TABLE_HH")
+print("#define HB_UCD_TABLE_HH")
+print()
+print('#include "hb.hh"')
+print()
+
+code = packTab.Code('_hb_ucd')
+sc_array, _ = code.addArray('hb_script_t', 'sc_map', sc_array)
+dm1_p0_array, _ = code.addArray('uint16_t', 'dm1_p0_map', dm1_p0_array)
+dm1_p2_array, _ = code.addArray('uint16_t', 'dm1_p2_map', dm1_p2_array)
+dm2_u32_array, _ = code.addArray('uint32_t', 'dm2_u32_map', dm2_u32_array)
+dm2_u64_array, _ = code.addArray('uint64_t', 'dm2_u64_map', dm2_u64_array)
+code.print_c(linkage='static inline')
+
+datasets = [
+    ('gc', gc, 'Cn', gc_order),
+    ('ccc', ccc, 0, None),
+    ('bmg', bmg, 0, None),
+    ('sc', sc, 'Zzzz', sc_order),
+    ('dm', dm, None, dm_order),
+]
+
+for compression in (DEFAULT, COMPACT, SLOPPY):
+    logging.info('  Compression=%d:' % compression)
+    print()
+    if compression == DEFAULT:
+        print('#ifndef HB_OPTIMIZE_SIZE')
+    elif compression == COMPACT:
+        print('#elif !defined(HB_NO_UCD_UNASSIGNED)')
+    else:
+        print('#else')
+    print()
+
+    if compression == SLOPPY:
+        for i in range(len(gc)):
+            if (i % 128) and gc[i] == 'Cn':
+                gc[i] = gc[i - 1]
+        for i in range(len(gc) - 2, -1, -1):
+            if ((i + 1) % 128) and gc[i] == 'Cn':
+                gc[i] = gc[i + 1]
+        for i in range(len(sc)):
+            if (i % 128) and sc[i] == 'Zzzz':
+                sc[i] = sc[i - 1]
+        for i in range(len(sc) - 2, -1, -1):
+            if ((i + 1) % 128) and sc[i] == 'Zzzz':
+                sc[i] = sc[i + 1]
+
+
+    code = packTab.Code('_hb_ucd')
+
+    for name,data,default,mapping in datasets:
+        sol = packTab.pack_table(data, default, mapping=mapping, compression=compression)
+        logging.info('      Dataset=%-8s FullCost=%d' % (name, sol.fullCost))
+        sol.genCode(code, name)
+
+    code.print_c(linkage='static inline')
+
+    print()
+
+print('#endif')
+print()
+
+print()
+print("#endif /* HB_UCD_TABLE_HH */")
+print()
+print("/* == End of generated table == */")
+logging.info('Done.')
diff --git a/src/gen-use-table.py b/src/gen-use-table.py
index 099f6a1..4523fb8 100755
--- a/src/gen-use-table.py
+++ b/src/gen-use-table.py
@@ -1,8 +1,10 @@
 #!/usr/bin/env python
+# flake8: noqa
 
 from __future__ import print_function, division, absolute_import
 
-import io, sys
+import io
+import sys
 
 if len (sys.argv) != 5:
 	print ("usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt", file=sys.stderr)
@@ -45,9 +47,22 @@
 
 # TODO Characters that are not in Unicode Indic files, but used in USE
 data[0][0x034F] = defaults[0]
+data[0][0x1B61] = defaults[0]
+data[0][0x1B63] = defaults[0]
+data[0][0x1B64] = defaults[0]
+data[0][0x1B65] = defaults[0]
+data[0][0x1B66] = defaults[0]
+data[0][0x1B67] = defaults[0]
+data[0][0x1B69] = defaults[0]
+data[0][0x1B6A] = defaults[0]
 data[0][0x2060] = defaults[0]
-data[0][0x20F0] = defaults[0]
-# TODO https://github.com/roozbehp/unicode-data/issues/9
+# TODO https://github.com/harfbuzz/harfbuzz/pull/1685
+data[0][0x1B5B] = 'Consonant_Placeholder'
+data[0][0x1B5C] = 'Consonant_Placeholder'
+data[0][0x1B5F] = 'Consonant_Placeholder'
+data[0][0x1B62] = 'Consonant_Placeholder'
+data[0][0x1B68] = 'Consonant_Placeholder'
+# TODO https://github.com/harfbuzz/harfbuzz/issues/1035
 data[0][0x11C44] = 'Consonant_Placeholder'
 data[0][0x11C45] = 'Consonant_Placeholder'
 # TODO https://github.com/harfbuzz/harfbuzz/pull/1399
@@ -170,7 +185,7 @@
 def is_BASE_IND(U, UISC, UGC):
 	#SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
 	return (UISC in [Consonant_Dead, Modifying_Letter] or
-		(UGC == Po and not U in [0x104B, 0x104E, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
+		(UGC == Po and not U in [0x104B, 0x104E, 0x1B5B, 0x1B5C, 0x1B5F, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
 		False # SPEC-DRAFT-OUTDATED! U == 0x002D
 		)
 def is_BASE_NUM(U, UISC, UGC):
@@ -182,15 +197,15 @@
 def is_CGJ(U, UISC, UGC):
 	return U == 0x034F
 def is_CONS_FINAL(U, UISC, UGC):
-	# Consonant_Initial_Postfixed is new in Unicode 11; not in the spec.
 	return ((UISC == Consonant_Final and UGC != Lo) or
-		UISC == Consonant_Initial_Postfixed or
 		UISC == Consonant_Succeeding_Repha)
 def is_CONS_FINAL_MOD(U, UISC, UGC):
 	#SPEC-DRAFT return  UISC in [Consonant_Final_Modifier, Syllable_Modifier]
 	return  UISC == Syllable_Modifier
 def is_CONS_MED(U, UISC, UGC):
-	return UISC == Consonant_Medial and UGC != Lo
+	# Consonant_Initial_Postfixed is new in Unicode 11; not in the spec.
+	return (UISC == Consonant_Medial and UGC != Lo or
+		UISC == Consonant_Initial_Postfixed)
 def is_CONS_MOD(U, UISC, UGC):
 	return UISC in [Nukta, Gemination_Mark, Consonant_Killer]
 def is_CONS_SUB(U, UISC, UGC):
@@ -199,7 +214,9 @@
 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] and not is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC)
+	return (UISC in [Virama, Invisible_Stacker]
+		and not is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC)
+		and not is_SAKOT(U, UISC, UGC))
 def is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC):
 	# https://github.com/harfbuzz/harfbuzz/issues/1102
 	# https://github.com/harfbuzz/harfbuzz/issues/1379
@@ -215,6 +232,7 @@
 def is_OTHER(U, UISC, UGC):
 	#SPEC-OUTDATED return UGC == Zs # or any other SCRIPT_COMMON characters
 	return (UISC == Other
+		and not is_SYM(U, UISC, UGC)
 		and not is_SYM_MOD(U, UISC, UGC)
 		and not is_CGJ(U, UISC, UGC)
 		and not is_Word_Joiner(U, UISC, UGC)
@@ -224,20 +242,22 @@
 	return UGC == 'Cn'
 def is_REPHA(U, UISC, UGC):
 	return UISC in [Consonant_Preceding_Repha, Consonant_Prefixed]
+def is_SAKOT(U, UISC, UGC):
+	return U == 0x1A60
 def is_SYM(U, UISC, UGC):
 	if U == 0x25CC: return False #SPEC-DRAFT
 	#SPEC-DRAFT return UGC in [So, Sc] or UISC == Symbol_Letter
-	return UGC in [So, Sc]
+	return UGC in [So, Sc] and U not in [0x1B62, 0x1B68]
 def is_SYM_MOD(U, UISC, UGC):
 	return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73]
 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
+	# https://github.com/harfbuzz/harfbuzz/issues/376
 	return (UISC == Pure_Killer or
 		(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
+	# https://github.com/harfbuzz/harfbuzz/issues/376
 	return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or
 		(UGC != Lo and (UISC == Bindu or U in [0xAA29])))
 
@@ -263,6 +283,7 @@
 	'Rsv':	is_Reserved,
 	'R':	is_REPHA,
 	'S':	is_SYM,
+	'Sk':	is_SAKOT,
 	'SM':	is_SYM_MOD,
 	'VS':	is_VARIATION_SELECTOR,
 	'V':	is_VOWEL,
@@ -304,7 +325,11 @@
 	'H': None,
 	'HVM': None,
 	'B': None,
-	'FM': None,
+	'FM': {
+		'Abv': [Top],
+		'Blw': [Bottom],
+		'Pst': [Not_Applicable],
+	},
 	'SUB': None,
 }
 
@@ -315,12 +340,11 @@
 
 		# Resolve Indic_Syllabic_Category
 
-		# TODO: These don't have UISC assigned in Unicode 8.0, but have UIPC
-		if U == 0x17DD: UISC = Vowel_Dependent
+		# TODO: These don't have UISC assigned in Unicode 12.0, but have UIPC
 		if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark
 
 		# Tibetan:
-		# TODO: These don't have UISC assigned in Unicode 11.0, but have UIPC
+		# TODO: These don't have UISC assigned in Unicode 12.0, but have UIPC
 		if 0x0F18 <= U <= 0x0F19 or 0x0F3E <= U <= 0x0F3F: UISC = Vowel_Dependent
 		if 0x0F86 <= U <= 0x0F87: UISC = Tone_Mark
 		# Overrides to allow NFC order matching syllable
@@ -344,48 +368,31 @@
 		# the nasalization marks, maybe only for U+1CE9..U+1CF1.
 		if U == 0x1CED: UISC = Tone_Mark
 
-		# 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
-
 		# TODO: https://github.com/harfbuzz/harfbuzz/issues/1105
 		if U == 0x11134: UISC = Gemination_Mark
 
-		# TODO: https://github.com/harfbuzz/harfbuzz/pull/1399
-		if U == 0x111C9: UISC = Consonant_Final
-
 		values = [k for k,v in items if v(U,UISC,UGC)]
 		assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values)
 		USE = values[0]
 
 		# Resolve Indic_Positional_Category
 
-		# TODO: Not in Unicode 8.0 yet, but in spec.
-		if U == 0x1B6C: UIPC = Bottom
-
-		# TODO: These should die, but have UIPC in Unicode 8.0
+		# TODO: These should die, but have UIPC in Unicode 12.0
 		if U in [0x953, 0x954]: UIPC = Not_Applicable
 
-		# TODO: In USE's override list but not in Unicode 11.0
+		# TODO: In USE's override list but not in Unicode 12.0
 		if U == 0x103C: UIPC = Left
 
-		# TODO: These are not in USE's override list that we have, nor are they in Unicode 11.0
+		# TODO: https://github.com/harfbuzz/harfbuzz/pull/2012
+		if U == 0x1C29: UIPC = Left
+
+		# TODO: These are not in USE's override list that we have, nor are they in Unicode 12.0
 		if 0xA926 <= U <= 0xA92A: UIPC = Top
-		if U == 0x111CA: UIPC = Bottom
-		if U == 0x11300: UIPC = Top
 		# TODO: https://github.com/harfbuzz/harfbuzz/pull/1037
-		if U == 0x11302: UIPC = Top
-		if U == 0x1133C: UIPC = Bottom
-		if U == 0x1171E: UIPC = Left # Correct?!
-		if 0x1CF2 <= U <= 0x1CF3: UIPC = Right
+		#  and https://github.com/harfbuzz/harfbuzz/issues/1631
+		if U in [0x11302, 0x11303, 0x114C1]: UIPC = Top
+		if U == 0x1171E: UIPC = Left
 		if 0x1CF8 <= U <= 0x1CF9: UIPC = Top
-		# https://github.com/roozbehp/unicode-data/issues/8
-		if U == 0x0A51: UIPC = Bottom
 
 		assert (UIPC in [Not_Applicable, Visual_Order_Left] or
 			USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC)
@@ -415,6 +422,10 @@
 		print (" * %s" % (l.strip()))
 print (" */")
 print ()
+print ('#include "hb.hh"')
+print ()
+print ('#ifndef HB_NO_OT_SHAPE')
+print ()
 print ('#include "hb-ot-shape-complex-use.hh"')
 print ()
 
@@ -453,6 +464,8 @@
 offset = 0
 starts = []
 ends = []
+print ('#pragma GCC diagnostic push')
+print ('#pragma GCC diagnostic ignored "-Wunused-macros"')
 for k,v in sorted(use_mapping.items()):
 	if k in use_positions and use_positions[k]: continue
 	print ("#define %s	USE_%s	/* %s */" % (k, k, v.__name__[3:]))
@@ -461,6 +474,7 @@
 	for suf in v.keys():
 		tag = k + suf
 		print ("#define %s	USE_%s" % (tag, tag))
+print ('#pragma GCC diagnostic pop')
 print ("")
 print ("static const USE_TABLE_ELEMENT_TYPE use_table[] = {")
 for u in uu:
@@ -526,6 +540,8 @@
 		tag = k + suf
 		print ("#undef %s" % tag)
 print ()
+print ()
+print ('#endif')
 print ("/* == End of generated table == */")
 
 # Maintain at least 50% occupancy in the table */
diff --git a/src/gen-vowel-constraints.py b/src/gen-vowel-constraints.py
index b7f6be2..8ca90c8 100755
--- a/src/gen-vowel-constraints.py
+++ b/src/gen-vowel-constraints.py
@@ -157,6 +157,11 @@
 for line in scripts_header:
 	print (' * %s' % line.strip ())
 print (' */')
+
+print ()
+print ('#include "hb.hh"')
+print ()
+print ('#ifndef HB_NO_OT_SHAPE')
 print ()
 print ('#include "hb-ot-shape-complex-vowel-constraints.hh"')
 print ()
@@ -180,6 +185,12 @@
 print ('\t\t\t\t       hb_buffer_t              *buffer,')
 print ('\t\t\t\t       hb_font_t                *font HB_UNUSED)')
 print ('{')
+print ('#if defined(HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS)')
+print ('  return;')
+print ('#endif')
+print ('  if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)')
+print ('    return;')
+print ()
 print ('  /* UGLY UGLY UGLY business of adding dotted-circle in the middle of')
 print ('   * vowel-sequences that look like another vowel.  Data for each script')
 print ('   * collected from the USE script development spec.')
@@ -212,8 +223,11 @@
 print ('  {')
 print ('    if (buffer->idx < count)')
 print ('      buffer->next_glyph ();')
+print ('    buffer->swap_buffers ();')
 print ('  }')
 print ('}')
 
 print ()
+print ()
+print ('#endif')
 print ('/* == End of generated functions == */')
diff --git a/src/harfbuzz.cc b/src/harfbuzz.cc
new file mode 100644
index 0000000..251a065
--- /dev/null
+++ b/src/harfbuzz.cc
@@ -0,0 +1,53 @@
+#include "hb-aat-layout.cc"
+#include "hb-aat-map.cc"
+#include "hb-blob.cc"
+#include "hb-buffer-serialize.cc"
+#include "hb-buffer.cc"
+#include "hb-common.cc"
+#include "hb-face.cc"
+#include "hb-fallback-shape.cc"
+#include "hb-font.cc"
+#include "hb-map.cc"
+#include "hb-number.cc"
+#include "hb-ot-cff1-table.cc"
+#include "hb-ot-cff2-table.cc"
+#include "hb-ot-color.cc"
+#include "hb-ot-face.cc"
+#include "hb-ot-font.cc"
+#include "hb-ot-layout.cc"
+#include "hb-ot-map.cc"
+#include "hb-ot-math.cc"
+#include "hb-ot-meta.cc"
+#include "hb-ot-metrics.cc"
+#include "hb-ot-name.cc"
+#include "hb-ot-shape-complex-arabic.cc"
+#include "hb-ot-shape-complex-default.cc"
+#include "hb-ot-shape-complex-hangul.cc"
+#include "hb-ot-shape-complex-hebrew.cc"
+#include "hb-ot-shape-complex-indic-table.cc"
+#include "hb-ot-shape-complex-indic.cc"
+#include "hb-ot-shape-complex-khmer.cc"
+#include "hb-ot-shape-complex-myanmar.cc"
+#include "hb-ot-shape-complex-thai.cc"
+#include "hb-ot-shape-complex-use-table.cc"
+#include "hb-ot-shape-complex-use.cc"
+#include "hb-ot-shape-complex-vowel-constraints.cc"
+#include "hb-ot-shape-fallback.cc"
+#include "hb-ot-shape-normalize.cc"
+#include "hb-ot-shape.cc"
+#include "hb-ot-tag.cc"
+#include "hb-ot-var.cc"
+#include "hb-set.cc"
+#include "hb-shape-plan.cc"
+#include "hb-shape.cc"
+#include "hb-shaper.cc"
+#include "hb-static.cc"
+#include "hb-ucd.cc"
+#include "hb-unicode.cc"
+#include "hb-glib.cc"
+#include "hb-ft.cc"
+#include "hb-graphite2.cc"
+#include "hb-uniscribe.cc"
+#include "hb-gdi.cc"
+#include "hb-directwrite.cc"
+#include "hb-coretext.cc"
diff --git a/src/hb-aat-fdsc-table.hh b/src/hb-aat-fdsc-table.hh
index a1af595..604d5bc 100644
--- a/src/hb-aat-fdsc-table.hh
+++ b/src/hb-aat-fdsc-table.hh
@@ -65,7 +65,7 @@
   protected:
   Tag		tag;		/* The 4-byte table tag name. */
   union {
-  Fixed		value;		/* The value for the descriptor tag. */
+  HBFixed		value;		/* The value for the descriptor tag. */
   HBUINT32	nalfType;	/* If the tag is `nalf`, see non_alphabetic_value_t */
   } u;
   public:
@@ -74,7 +74,7 @@
 
 struct fdsc
 {
-  enum { tableTag = HB_AAT_TAG_fdsc };
+  static constexpr hb_tag_t tableTag = HB_AAT_TAG_fdsc;
 
   enum {
     Weight	 = HB_TAG ('w','g','h','t'),
@@ -108,7 +108,7 @@
   }
 
   protected:
-  Fixed		version;	/* Version number of the font descriptors
+  HBFixed		version;	/* Version number of the font descriptors
 				 * table (0x00010000 for the current version). */
   LArrayOf<FontDescriptor>
 		descriptors;	/* List of tagged-coordinate pairs style descriptors
diff --git a/src/hb-aat-layout-ankr-table.hh b/src/hb-aat-layout-ankr-table.hh
index 4235b36..ef98884 100644
--- a/src/hb-aat-layout-ankr-table.hh
+++ b/src/hb-aat-layout-ankr-table.hh
@@ -58,20 +58,16 @@
 
 struct ankr
 {
-  enum { tableTag = HB_AAT_TAG_ankr };
+  static constexpr hb_tag_t tableTag = HB_AAT_TAG_ankr;
 
   const Anchor &get_anchor (hb_codepoint_t glyph_id,
 			    unsigned int i,
-			    unsigned int num_glyphs,
-			    const char *end) const
+			    unsigned int num_glyphs) const
   {
-    const Offset<HBUINT16, false> *offset = (this+lookupTable).get_value (glyph_id, num_glyphs);
+    const NNOffsetTo<GlyphAnchors> *offset = (this+lookupTable).get_value (glyph_id, num_glyphs);
     if (!offset)
       return Null(Anchor);
-    const GlyphAnchors &anchors = StructAtOffset<GlyphAnchors> (&(this+anchorData), *offset);
-    /* TODO Use sanitizer; to avoid overflows and more. */
-    if (unlikely ((const char *) &anchors + anchors.get_size () > end))
-      return Null(Anchor);
+    const GlyphAnchors &anchors = &(this+anchorData) + *offset;
     return anchors[i];
   }
 
@@ -80,15 +76,16 @@
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
 			  version == 0 &&
-			  lookupTable.sanitize (c, this)));
+			  c->check_range (this, anchorData) &&
+			  lookupTable.sanitize (c, this, &(this+anchorData))));
   }
 
   protected:
   HBUINT16	version; 	/* Version number (set to zero) */
   HBUINT16	flags;		/* Flags (currently unused; set to zero) */
-  LOffsetTo<Lookup<Offset<HBUINT16, false> >, false>
+  LOffsetTo<Lookup<NNOffsetTo<GlyphAnchors>>>
 		lookupTable;	/* Offset to the table's lookup table */
-  LOffsetTo<HBUINT8, false>
+  LNNOffsetTo<HBUINT8>
 		anchorData;	/* Offset to the glyph data table */
 
   public:
diff --git a/src/hb-aat-layout-bsln-table.hh b/src/hb-aat-layout-bsln-table.hh
index 5bc59cb..15ef2da 100644
--- a/src/hb-aat-layout-bsln-table.hh
+++ b/src/hb-aat-layout-bsln-table.hh
@@ -82,7 +82,7 @@
   }
 
   protected:
-  GlyphID	stdGlyph;	/* The specific glyph index number in this
+  HBGlyphID	stdGlyph;	/* The specific glyph index number in this
 				 * font that is used to set the baseline values.
 				 * This is the standard glyph.
 				 * This glyph must contain a set of control points
@@ -105,7 +105,7 @@
   }
 
   protected:
-  GlyphID	stdGlyph;	/* ditto */
+  HBGlyphID	stdGlyph;	/* ditto */
   HBUINT16	ctlPoints[32];	/* ditto */
   Lookup<HBUINT16>
 		lookupTable;	/* Lookup table that maps glyphs to their
@@ -116,7 +116,7 @@
 
 struct bsln
 {
-  enum { tableTag = HB_AAT_TAG_bsln };
+  static constexpr hb_tag_t tableTag = HB_AAT_TAG_bsln;
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh
index 95d6b31..473f2cd 100644
--- a/src/hb-aat-layout-common.hh
+++ b/src/hb-aat-layout-common.hh
@@ -77,7 +77,7 @@
 template <typename T>
 struct LookupSegmentSingle
 {
-  enum { TerminationWordCount = 2 };
+  static constexpr unsigned TerminationWordCount = 2u;
 
   int cmp (hb_codepoint_t g) const
   { return g < first ? -1 : g <= last ? 0 : +1 ; }
@@ -93,8 +93,8 @@
     return_trace (c->check_struct (this) && value.sanitize (c, base));
   }
 
-  GlyphID	last;		/* Last GlyphID in this segment */
-  GlyphID	first;		/* First GlyphID in this segment */
+  HBGlyphID	last;		/* Last GlyphID in this segment */
+  HBGlyphID	first;		/* First GlyphID in this segment */
   T		value;		/* The lookup value (only one) */
   public:
   DEFINE_SIZE_STATIC (4 + T::static_size);
@@ -125,7 +125,7 @@
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 2 */
-  VarSizedBinSearchArrayOf<LookupSegmentSingle<T> >
+  VarSizedBinSearchArrayOf<LookupSegmentSingle<T>>
 		segments;	/* The actual segments. These must already be sorted,
 				 * according to the first word in each one (the last
 				 * glyph in each segment). */
@@ -136,7 +136,7 @@
 template <typename T>
 struct LookupSegmentArray
 {
-  enum { TerminationWordCount = 2 };
+  static constexpr unsigned TerminationWordCount = 2u;
 
   const T* get_value (hb_codepoint_t glyph_id, const void *base) const
   {
@@ -153,18 +153,18 @@
 		  first <= last &&
 		  valuesZ.sanitize (c, base, last - first + 1));
   }
-  template <typename T2>
-  bool sanitize (hb_sanitize_context_t *c, const void *base, T2 user_data) const
+  template <typename ...Ts>
+  bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  first <= last &&
-		  valuesZ.sanitize (c, base, last - first + 1, user_data));
+		  valuesZ.sanitize (c, base, last - first + 1, hb_forward<Ts> (ds)...));
   }
 
-  GlyphID	last;		/* Last GlyphID in this segment */
-  GlyphID	first;		/* First GlyphID in this segment */
-  OffsetTo<UnsizedArrayOf<T>, HBUINT16, false>
+  HBGlyphID	last;		/* Last GlyphID in this segment */
+  HBGlyphID	first;		/* First GlyphID in this segment */
+  NNOffsetTo<UnsizedArrayOf<T>>
 		valuesZ;	/* A 16-bit offset from the start of
 				 * the table to the data. */
   public:
@@ -196,7 +196,7 @@
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 4 */
-  VarSizedBinSearchArrayOf<LookupSegmentArray<T> >
+  VarSizedBinSearchArrayOf<LookupSegmentArray<T>>
 		segments;	/* The actual segments. These must already be sorted,
 				 * according to the first word in each one (the last
 				 * glyph in each segment). */
@@ -207,7 +207,7 @@
 template <typename T>
 struct LookupSingle
 {
-  enum { TerminationWordCount = 1 };
+  static constexpr unsigned TerminationWordCount = 1u;
 
   int cmp (hb_codepoint_t g) const { return glyph.cmp (g); }
 
@@ -222,7 +222,7 @@
     return_trace (c->check_struct (this) && value.sanitize (c, base));
   }
 
-  GlyphID	glyph;		/* Last GlyphID */
+  HBGlyphID	glyph;		/* Last GlyphID */
   T		value;		/* The lookup value (only one) */
   public:
   DEFINE_SIZE_STATIC (2 + T::static_size);
@@ -253,7 +253,7 @@
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 6 */
-  VarSizedBinSearchArrayOf<LookupSingle<T> >
+  VarSizedBinSearchArrayOf<LookupSingle<T>>
 		entries;	/* The actual entries, sorted by glyph index. */
   public:
   DEFINE_SIZE_ARRAY (8, entries);
@@ -284,7 +284,7 @@
 
   protected:
   HBUINT16	format;		/* Format identifier--format = 8 */
-  GlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
+  HBGlyphID	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>
@@ -326,7 +326,7 @@
   protected:
   HBUINT16	format;		/* Format identifier--format = 8 */
   HBUINT16	valueSize;	/* Byte size of each value. */
-  GlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
+  HBGlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
   HBUINT16	glyphCount;	/* Total number of glyphs (equivalent to the last
 				 * glyph minus the value of firstGlyph plus 1). */
   UnsizedArrayOf<HBUINT8>
@@ -394,7 +394,7 @@
     case 4: return_trace (u.format4.sanitize (c, base));
     case 6: return_trace (u.format6.sanitize (c, base));
     case 8: return_trace (u.format8.sanitize (c, base));
-    case 10: return_trace (false); /* No need to support format10 apparently */
+    case 10: return_trace (false); /* We don't support format10 here currently. */
     default:return_trace (true);
     }
   }
@@ -418,15 +418,11 @@
 } /* Close namespace. */
 /* Ugly hand-coded null objects for template Lookup<> :(. */
 extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2];
-template <>
-/*static*/ inline const AAT::Lookup<OT::HBUINT16>& Null<AAT::Lookup<OT::HBUINT16> > ()
-{ return *reinterpret_cast<const AAT::Lookup<OT::HBUINT16> *> (_hb_Null_AAT_Lookup); }
-template <>
-/*static*/ inline const AAT::Lookup<OT::HBUINT32>& Null<AAT::Lookup<OT::HBUINT32> > ()
-{ return *reinterpret_cast<const AAT::Lookup<OT::HBUINT32> *> (_hb_Null_AAT_Lookup); }
-template <>
-/*static*/ inline const AAT::Lookup<OT::Offset<OT::HBUINT16, false> >& Null<AAT::Lookup<OT::Offset<OT::HBUINT16, false> > > ()
-{ return *reinterpret_cast<const AAT::Lookup<OT::Offset<OT::HBUINT16, false> > *> (_hb_Null_AAT_Lookup); }
+template <typename T>
+struct Null<AAT::Lookup<T>> {
+  static AAT::Lookup<T> const & get_null ()
+  { return *reinterpret_cast<const AAT::Lookup<T> *> (_hb_Null_AAT_Lookup); }
+};
 namespace AAT {
 
 enum { DELETED_GLYPH = 0xFFFF };
@@ -511,9 +507,10 @@
   const Entry<Extra> *get_entries () const
   { return (this+entryTable).arrayZ; }
 
-  const Entry<Extra> *get_entryZ (int state, unsigned int klass) const
+  const Entry<Extra> &get_entry (int state, unsigned int klass) const
   {
-    if (unlikely (klass >= nClasses)) return nullptr;
+    if (unlikely (klass >= nClasses))
+      klass = StateTable<Types, Entry<Extra>>::CLASS_OUT_OF_BOUNDS;
 
     const HBUSHORT *states = (this+stateArrayTable).arrayZ;
     const Entry<Extra> *entries = (this+entryTable).arrayZ;
@@ -521,7 +518,7 @@
     unsigned int entry = states[state * nClasses + klass];
     DEBUG_MSG (APPLY, nullptr, "e%u", entry);
 
-    return &entries[entry];
+    return entries[entry];
   }
 
   bool sanitize (hb_sanitize_context_t *c,
@@ -529,6 +526,7 @@
   {
     TRACE_SANITIZE (this);
     if (unlikely (!(c->check_struct (this) &&
+		    nClasses >= 4 /* Ensure pre-defined classes fit.  */ &&
 		    classTable.sanitize (c, this)))) return_trace (false);
 
     const HBUSHORT *states = (this+stateArrayTable).arrayZ;
@@ -571,14 +569,14 @@
 				       -min_state,
 				       row_stride)))
 	  return_trace (false);
-	if ((c->max_ops -= state_neg - min_state) < 0)
+	if ((c->max_ops -= state_neg - min_state) <= 0)
 	  return_trace (false);
 	{ /* Sweep new states. */
 	  const HBUSHORT *stop = &states[min_state * num_classes];
 	  if (unlikely (stop > states))
 	    return_trace (false);
 	  for (const HBUSHORT *p = states; stop < p; p--)
-	    num_entries = MAX<unsigned int> (num_entries, *(p - 1) + 1);
+	    num_entries = hb_max (num_entries, *(p - 1) + 1);
 	  state_neg = min_state;
 	}
       }
@@ -590,7 +588,7 @@
 				       max_state + 1,
 				       row_stride)))
 	  return_trace (false);
-	if ((c->max_ops -= max_state - state_pos + 1) < 0)
+	if ((c->max_ops -= max_state - state_pos + 1) <= 0)
 	  return_trace (false);
 	{ /* Sweep new states. */
 	  if (unlikely (hb_unsigned_mul_overflows ((max_state + 1), num_classes)))
@@ -599,22 +597,22 @@
 	  if (unlikely (stop < states))
 	    return_trace (false);
 	  for (const HBUSHORT *p = &states[state_pos * num_classes]; p < stop; p++)
-	    num_entries = MAX<unsigned int> (num_entries, *p + 1);
+	    num_entries = hb_max (num_entries, *p + 1);
 	  state_pos = max_state + 1;
 	}
       }
 
       if (unlikely (!c->check_array (entries, num_entries)))
 	return_trace (false);
-      if ((c->max_ops -= num_entries - entry) < 0)
+      if ((c->max_ops -= num_entries - entry) <= 0)
 	return_trace (false);
       { /* Sweep new entries. */
 	const Entry<Extra> *stop = &entries[num_entries];
 	for (const Entry<Extra> *p = &entries[entry]; p < stop; p++)
 	{
 	  int newState = new_state (p->newState);
-	  min_state = MIN (min_state, newState);
-	  max_state = MAX (max_state, newState);
+	  min_state = hb_min (min_state, newState);
+	  max_state = hb_max (max_state, newState);
 	}
 	entry = num_entries;
       }
@@ -629,11 +627,11 @@
   protected:
   HBUINT	nClasses;	/* Number of classes, which is the number of indices
 				 * in a single line in the state array. */
-  OffsetTo<ClassType, HBUINT, false>
+  NNOffsetTo<ClassType, HBUINT>
 		classTable;	/* Offset to the class table. */
-  OffsetTo<UnsizedArrayOf<HBUSHORT>, HBUINT, false>
+  NNOffsetTo<UnsizedArrayOf<HBUSHORT>, HBUINT>
 		stateArrayTable;/* Offset to the state array. */
-  OffsetTo<UnsizedArrayOf<Entry<Extra> >, HBUINT, false>
+  NNOffsetTo<UnsizedArrayOf<Entry<Extra>>, HBUINT>
 		entryTable;	/* Offset to the entry array. */
 
   public:
@@ -660,7 +658,7 @@
     return_trace (c->check_struct (this) && classArray.sanitize (c));
   }
   protected:
-  GlyphID		firstGlyph;	/* First glyph index included in the trimmed array. */
+  HBGlyphID		firstGlyph;	/* First glyph index included in the trimmed array. */
   ArrayOf<HBUCHAR>	classArray;	/* The class codes (indexed by glyph index minus
 					 * firstGlyph). */
   public:
@@ -669,7 +667,7 @@
 
 struct ObsoleteTypes
 {
-  enum { extended = false };
+  static constexpr bool extended = false;
   typedef HBUINT16 HBUINT;
   typedef HBUINT8 HBUSHORT;
   typedef ClassTable<HBUINT8> ClassTypeNarrow;
@@ -680,7 +678,7 @@
 				     const void *base,
 				     const T *array)
   {
-    return (offset - ((const char *) array - (const char *) base)) / sizeof (T);
+    return (offset - ((const char *) array - (const char *) base)) / T::static_size;
   }
   template <typename T>
   static unsigned int byteOffsetToIndex (unsigned int offset,
@@ -699,7 +697,7 @@
 };
 struct ExtendedTypes
 {
-  enum { extended = true };
+  static constexpr bool extended = true;
   typedef HBUINT32 HBUINT;
   typedef HBUINT16 HBUSHORT;
   typedef Lookup<HBUINT16> ClassTypeNarrow;
@@ -707,22 +705,22 @@
 
   template <typename T>
   static unsigned int offsetToIndex (unsigned int offset,
-				     const void *base,
-				     const T *array)
+				     const void *base HB_UNUSED,
+				     const T *array HB_UNUSED)
   {
     return offset;
   }
   template <typename T>
   static unsigned int byteOffsetToIndex (unsigned int offset,
-					 const void *base,
-					 const T *array)
+					 const void *base HB_UNUSED,
+					 const T *array HB_UNUSED)
   {
     return offset / 2;
   }
   template <typename T>
   static unsigned int wordOffsetToIndex (unsigned int offset,
-					 const void *base,
-					 const T *array)
+					 const void *base HB_UNUSED,
+					 const T *array HB_UNUSED)
   {
     return offset;
   }
@@ -745,16 +743,13 @@
       buffer->clear_output ();
 
     int state = StateTable<Types, EntryData>::STATE_START_OF_TEXT;
-    bool last_was_dont_advance = false;
     for (buffer->idx = 0; buffer->successful;)
     {
       unsigned int klass = buffer->idx < buffer->len ?
 			   machine.get_class (buffer->info[buffer->idx].codepoint, num_glyphs) :
 			   (unsigned) StateTable<Types, EntryData>::CLASS_END_OF_TEXT;
       DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
-      const Entry<EntryData> *entry = machine.get_entryZ (state, klass);
-      if (unlikely (!entry))
-	break;
+      const Entry<EntryData> &entry = machine.get_entry (state, klass);
 
       /* Unsafe-to-break before this if not in state 0, as things might
        * go differently if we start from state 0 here.
@@ -765,31 +760,28 @@
 	/* If there's no action and we're just epsilon-transitioning to state 0,
 	 * safe to break. */
 	if (c->is_actionable (this, entry) ||
-	    !(entry->newState == StateTable<Types, EntryData>::STATE_START_OF_TEXT &&
-	      entry->flags == context_t::DontAdvance))
+	    !(entry.newState == StateTable<Types, EntryData>::STATE_START_OF_TEXT &&
+	      entry.flags == context_t::DontAdvance))
 	  buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
       }
 
       /* 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);
+	const Entry<EntryData> &end_entry = machine.get_entry (state, StateTable<Types, EntryData>::CLASS_END_OF_TEXT);
 	if (c->is_actionable (this, end_entry))
 	  buffer->unsafe_to_break (buffer->idx, buffer->idx + 2);
       }
 
-      if (unlikely (!c->transition (this, entry)))
-	break;
+      c->transition (this, entry);
 
-      last_was_dont_advance = (entry->flags & context_t::DontAdvance) && buffer->max_ops-- > 0;
-
-      state = machine.new_state (entry->newState);
+      state = machine.new_state (entry.newState);
       DEBUG_MSG (APPLY, nullptr, "s%d", state);
 
       if (buffer->idx == buffer->len)
 	break;
 
-      if (!last_was_dont_advance)
+      if (!(entry.flags & context_t::DontAdvance) || buffer->max_ops-- <= 0)
 	buffer->next_glyph ();
     }
 
@@ -825,7 +817,6 @@
   hb_buffer_t *buffer;
   hb_sanitize_context_t sanitizer;
   const ankr *ankr_table;
-  const char *ankr_end;
 
   /* Unused. For debug tracing only. */
   unsigned int lookup_index;
@@ -838,7 +829,7 @@
 
   HB_INTERNAL ~hb_aat_apply_context_t ();
 
-  HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_, const char *ankr_end_);
+  HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
 
   void set_lookup_index (unsigned int i) { lookup_index = i; }
 };
diff --git a/src/hb-aat-layout-feat-table.hh b/src/hb-aat-layout-feat-table.hh
index 63c69a2..788d408 100644
--- a/src/hb-aat-layout-feat-table.hh
+++ b/src/hb-aat-layout-feat-table.hh
@@ -47,17 +47,16 @@
   hb_aat_layout_feature_selector_t get_selector () const
   { return (hb_aat_layout_feature_selector_t) (unsigned) setting; }
 
-  void get_info (hb_aat_layout_feature_selector_info_t *s,
-			hb_aat_layout_feature_selector_t default_selector) const
+  hb_aat_layout_feature_selector_info_t get_info (hb_aat_layout_feature_selector_t default_selector) const
   {
-    s->name_id = nameIndex;
-
-    s->enable = (hb_aat_layout_feature_selector_t) (unsigned int) setting;
-    s->disable = default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID ?
-		 (hb_aat_layout_feature_selector_t) (s->enable + 1) :
-		 default_selector;
-
-    s->reserved = 0;
+    return {
+      nameIndex,
+      (hb_aat_layout_feature_selector_t) (unsigned int) setting,
+      default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID
+	? (hb_aat_layout_feature_selector_t) (setting + 1)
+	: default_selector,
+      0
+    };
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -117,12 +116,12 @@
 
     if (selectors_count)
     {
-      hb_array_t<const SettingName> arr = settings_table.sub_array (start_offset, selectors_count);
-      unsigned int count = arr.len;
-      for (unsigned int i = 0; i < count; i++)
-        settings_table[start_offset + i].get_info (&selectors[i], default_selector);
+      + settings_table.sub_array (start_offset, selectors_count)
+      | hb_map ([=] (const SettingName& setting) { return setting.get_info (default_selector); })
+      | hb_sink (hb_array (selectors, *selectors_count))
+      ;
     }
-    return settings_table.len;
+    return settings_table.length;
   }
 
   hb_aat_layout_feature_type_t get_feature_type () const
@@ -155,7 +154,7 @@
 
 struct feat
 {
-  enum { tableTag = HB_AAT_TAG_feat };
+  static constexpr hb_tag_t tableTag = HB_AAT_TAG_feat;
 
   bool has_data () const { return version.to_int (); }
 
@@ -163,21 +162,18 @@
 				  unsigned int                 *count,
 				  hb_aat_layout_feature_type_t *features) const
   {
-    unsigned int feature_count = featureNameCount;
-    if (count && *count)
+    if (count)
     {
-      unsigned int len = MIN (feature_count - start_offset, *count);
-      for (unsigned int i = 0; i < len; i++)
-	features[i] = namesZ[i + start_offset].get_feature_type ();
-      *count = len;
+      + namesZ.as_array (featureNameCount).sub_array (start_offset, count)
+      | hb_map (&FeatureName::get_feature_type)
+      | hb_sink (hb_array (features, *count))
+      ;
     }
     return featureNameCount;
   }
 
   const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const
-  {
-    return namesZ.bsearch (featureNameCount, feature_type);
-  }
+  { return namesZ.bsearch (featureNameCount, feature_type); }
 
   hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const
   { return get_feature (feature).get_feature_name_id (); }
@@ -210,7 +206,7 @@
   SortedUnsizedArrayOf<FeatureName>
 		namesZ;		/* The feature name array. */
   public:
-  DEFINE_SIZE_STATIC (24);
+  DEFINE_SIZE_ARRAY (12, namesZ);
 };
 
 } /* namespace AAT */
diff --git a/src/hb-aat-layout-just-table.hh b/src/hb-aat-layout-just-table.hh
index d39945a..e1787d1 100644
--- a/src/hb-aat-layout-just-table.hh
+++ b/src/hb-aat-layout-just-table.hh
@@ -70,9 +70,9 @@
 
   ActionSubrecordHeader
 		header;
-  Fixed		lowerLimit; 	/* If the distance factor is less than this value,
+  HBFixed		lowerLimit; 	/* If the distance factor is less than this value,
 				 * then the ligature is decomposed. */
-  Fixed		upperLimit; 	/* If the distance factor is greater than this value,
+  HBFixed		upperLimit; 	/* If the distance factor is greater than this value,
 				 * then the ligature is decomposed. */
   HBUINT16 	order;		/* Numerical order in which this ligature will
 				 * be decomposed; you may want infrequent ligatures
@@ -100,7 +100,7 @@
   protected:
   ActionSubrecordHeader
 		header;
-  GlyphID	addGlyph;	/* Glyph that should be added if the distance factor
+  HBGlyphID	addGlyph;	/* Glyph that should be added if the distance factor
 				 * is growing. */
 
   public:
@@ -118,14 +118,14 @@
   protected:
   ActionSubrecordHeader
 		header;
-  Fixed 	substThreshold; /* Distance growth factor (in ems) at which
+  HBFixed 	substThreshold; /* Distance growth factor (in ems) at which
 				 * this glyph is replaced and the growth factor
 				 * recalculated. */
-  GlyphID 	addGlyph; 	/* Glyph to be added as kashida. If this value is
+  HBGlyphID 	addGlyph; 	/* Glyph to be added as kashida. If this value is
 				 * 0xFFFF, no extra glyph will be added. Note that
 				 * generally when a glyph is added, justification
 				 * will need to be redone. */
-  GlyphID 	substGlyph; 	/* Glyph to be substituted for this glyph if the
+  HBGlyphID 	substGlyph; 	/* Glyph to be substituted for this glyph if the
 				 * growth factor equals or exceeds the value of
 				 * substThreshold. */
   public:
@@ -146,13 +146,13 @@
   HBUINT32 	variationAxis;	/* The 4-byte tag identifying the ductile axis.
 				 * This would normally be 0x64756374 ('duct'),
 				 * but you may use any axis the font contains. */
-  Fixed 	minimumLimit; 	/* The lowest value for the ductility axis tha
+  HBFixed 	minimumLimit; 	/* The lowest value for the ductility axis tha
 				 * still yields an acceptable appearance. Normally
 				 * this will be 1.0. */
-  Fixed 	noStretchValue; /* This is the default value that corresponds to
+  HBFixed 	noStretchValue; /* This is the default value that corresponds to
 				 * no change in appearance. Normally, this will
 				 * be 1.0. */
-  Fixed 	maximumLimit; 	/* The highest value for the ductility axis that
+  HBFixed 	maximumLimit; 	/* The highest value for the ductility axis that
 				 * still yields an acceptable appearance. */
   public:
   DEFINE_SIZE_STATIC (22);
@@ -170,7 +170,7 @@
   ActionSubrecordHeader
 		header;
   HBUINT16 	flags;		/* Currently unused; set to 0. */
-  GlyphID 	glyph;		/* Glyph that should be added if the distance factor
+  HBGlyphID 	glyph;		/* Glyph that should be added if the distance factor
 				 * is growing. */
   public:
   DEFINE_SIZE_STATIC (10);
@@ -271,14 +271,14 @@
   };
 
   protected:
-  Fixed		beforeGrowLimit;/* The ratio by which the advance width of the
+  HBFixed		beforeGrowLimit;/* The ratio by which the advance width of the
 				 * glyph is permitted to grow on the left or top side. */
-  Fixed		beforeShrinkLimit;
+  HBFixed		beforeShrinkLimit;
 				/* The ratio by which the advance width of the
 				 * glyph is permitted to shrink on the left or top side. */
-  Fixed		afterGrowLimit;	/* The ratio by which the advance width of the glyph
+  HBFixed		afterGrowLimit;	/* The ratio by which the advance width of the glyph
 				 * is permitted to shrink on the left or top side. */
-  Fixed		afterShrinkLimit;
+  HBFixed		afterShrinkLimit;
 				/* The ratio by which the advance width of the glyph
 				 * is at most permitted to shrink on the right or
 				 * bottom side. */
@@ -309,7 +309,7 @@
   public:
   DEFINE_SIZE_STATIC (24);
 };
-  
+
 typedef OT::LArrayOf<WidthDeltaPair> WidthDeltaCluster;
 
 struct JustificationCategory
@@ -371,7 +371,7 @@
 				 * of postcompensation subtable (set to zero if none).
 				 *
 				 * The postcompensation subtable, if present in the font. */
-  Lookup<OffsetTo<WidthDeltaCluster> >
+  Lookup<OffsetTo<WidthDeltaCluster>>
   		lookupTable;	/* Lookup table associating glyphs with width delta
 				 * clusters. See the description of Width Delta Clusters
 				 * table for details on how to interpret the lookup values. */
@@ -382,7 +382,7 @@
 
 struct just
 {
-  enum { tableTag = HB_AAT_TAG_just };
+  static constexpr hb_tag_t tableTag = HB_AAT_TAG_just;
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 291f3e3..be1b339 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -82,8 +82,8 @@
   }
 
   protected:
-  GlyphID	left;
-  GlyphID	right;
+  HBGlyphID	left;
+  HBGlyphID	right;
   FWORD		value;
   public:
   DEFINE_SIZE_STATIC (6);
@@ -170,11 +170,11 @@
     DEFINE_SIZE_STATIC (2);
   };
 
-  static bool performAction (const Entry<EntryData> *entry)
-  { return entry->data.kernActionIndex != 0xFFFF; }
+  static bool performAction (const Entry<EntryData> &entry)
+  { return entry.data.kernActionIndex != 0xFFFF; }
 
-  static unsigned int kernActionIndex (const Entry<EntryData> *entry)
-  { return entry->data.kernActionIndex; }
+  static unsigned int kernActionIndex (const Entry<EntryData> &entry)
+  { return entry.data.kernActionIndex; }
 };
 template <>
 struct Format1Entry<false>
@@ -192,11 +192,11 @@
 
   typedef void EntryData;
 
-  static bool performAction (const Entry<EntryData> *entry)
-  { return entry->flags & Offset; }
+  static bool performAction (const Entry<EntryData> &entry)
+  { return entry.flags & Offset; }
 
-  static unsigned int kernActionIndex (const Entry<EntryData> *entry)
-  { return entry->flags & Offset; }
+  static unsigned int kernActionIndex (const Entry<EntryData> &entry)
+  { return entry.flags & Offset; }
 };
 
 template <typename KernSubTableHeader>
@@ -210,7 +210,7 @@
 
   struct driver_context_t
   {
-    enum { in_place = true };
+    static constexpr bool in_place = true;
     enum
     {
       DontAdvance	= Format1EntryT::DontAdvance,
@@ -228,15 +228,15 @@
 	crossStream (table->header.coverage & table->header.CrossStream) {}
 
     bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> *entry)
+			const Entry<EntryData> &entry)
     {
       return Format1EntryT::performAction (entry);
     }
-    bool transition (StateTableDriver<Types, EntryData> *driver,
-		     const Entry<EntryData> *entry)
+    void transition (StateTableDriver<Types, EntryData> *driver,
+		     const Entry<EntryData> &entry)
     {
       hb_buffer_t *buffer = driver->buffer;
-      unsigned int flags = entry->flags;
+      unsigned int flags = entry.flags;
 
       if (flags & Format1EntryT::Reset)
 	depth = 0;
@@ -251,7 +251,7 @@
 
       if (Format1EntryT::performAction (entry) && depth)
       {
-	unsigned int tuple_count = MAX (1u, table->header.tuple_count ());
+	unsigned int tuple_count = hb_max (1u, table->header.tuple_count ());
 
 	unsigned int kern_idx = Format1EntryT::kernActionIndex (entry);
 	kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ);
@@ -259,7 +259,7 @@
 	if (!c->sanitizer.check_array (actions, depth, tuple_count))
 	{
 	  depth = 0;
-	  return false;
+	  return;
 	}
 
 	hb_mask_t kern_mask = c->plan->kern_mask;
@@ -334,8 +334,6 @@
 	  }
 	}
       }
-
-      return true;
     }
 
     private:
@@ -374,7 +372,7 @@
   protected:
   KernSubTableHeader				header;
   StateTable<Types, EntryData>			machine;
-  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT, false>kernAction;
+  NNOffsetTo<UnsizedArrayOf<FWORD>, HBUINT>	kernAction;
   public:
   DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT));
 };
@@ -394,7 +392,7 @@
 
     const UnsizedArrayOf<FWORD> &arrayZ = this+array;
     unsigned int kern_idx = l + r;
-    kern_idx = Types::offsetToIndex (kern_idx, this, &arrayZ);
+    kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ);
     const FWORD *v = &arrayZ[kern_idx];
     if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
 
@@ -443,13 +441,13 @@
   protected:
   KernSubTableHeader	header;
   HBUINT		rowWidth;	/* The width, in bytes, of a row in the table. */
-  OffsetTo<typename Types::ClassTypeWide, HBUINT, false>
+  NNOffsetTo<typename Types::ClassTypeWide, HBUINT>
 			leftClassTable;	/* Offset from beginning of this subtable to
 					 * left-hand class table. */
-  OffsetTo<typename Types::ClassTypeWide, HBUINT, false>
+  NNOffsetTo<typename Types::ClassTypeWide, HBUINT>
 			rightClassTable;/* Offset from beginning of this subtable to
 					 * right-hand class table. */
-  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT, false>
+  NNOffsetTo<UnsizedArrayOf<FWORD>, HBUINT>
 			 array;		/* Offset from beginning of this subtable to
 					 * the start of the kerning array. */
   public:
@@ -471,7 +469,7 @@
 
   struct driver_context_t
   {
-    enum { in_place = true };
+    static constexpr bool in_place = true;
     enum Flags
     {
       Mark		= 0x8000,	/* If set, remember this glyph as the marked glyph. */
@@ -498,16 +496,16 @@
 	mark (0) {}
 
     bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> *entry)
+			const Entry<EntryData> &entry)
     {
-      return entry->data.ankrActionIndex != 0xFFFF;
+      return entry.data.ankrActionIndex != 0xFFFF;
     }
-    bool transition (StateTableDriver<Types, EntryData> *driver,
-		     const Entry<EntryData> *entry)
+    void transition (StateTableDriver<Types, EntryData> *driver,
+		     const Entry<EntryData> &entry)
     {
       hb_buffer_t *buffer = driver->buffer;
 
-      if (mark_set && entry->data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
+      if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
       {
 	hb_glyph_position_t &o = buffer->cur_pos();
 	switch (action_type)
@@ -515,9 +513,8 @@
 	  case 0: /* Control Point Actions.*/
 	  {
 	    /* indexed into glyph outline. */
-	    const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
-	    if (!c->sanitizer.check_array (data, 2))
-	      return false;
+	    const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex];
+	    if (!c->sanitizer.check_array (data, 2)) return;
 	    HB_UNUSED unsigned int markControlPoint = *data++;
 	    HB_UNUSED unsigned int currControlPoint = *data++;
 	    hb_position_t markX = 0;
@@ -532,7 +529,7 @@
 							      currControlPoint,
 							      HB_DIRECTION_LTR /*XXX*/,
 							      &currX, &currY))
-	      return true; /* True, such that the machine continues. */
+	      return;
 
 	    o.x_offset = markX - currX;
 	    o.y_offset = markY - currY;
@@ -542,19 +539,16 @@
 	  case 1: /* Anchor Point Actions. */
 	  {
 	   /* Indexed into 'ankr' table. */
-	    const HBUINT16 *data = &ankrData[entry->data.ankrActionIndex];
-	    if (!c->sanitizer.check_array (data, 2))
-	      return false;
+	    const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex];
+	    if (!c->sanitizer.check_array (data, 2)) return;
 	    unsigned int markAnchorPoint = *data++;
 	    unsigned int currAnchorPoint = *data++;
-	    const Anchor markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint,
-								 markAnchorPoint,
-								 c->sanitizer.get_num_glyphs (),
-								 c->ankr_end);
-	    const Anchor currAnchor = c->ankr_table->get_anchor (c->buffer->cur ().codepoint,
-								 currAnchorPoint,
-								 c->sanitizer.get_num_glyphs (),
-								 c->ankr_end);
+	    const Anchor &markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint,
+								  markAnchorPoint,
+								  c->sanitizer.get_num_glyphs ());
+	    const Anchor &currAnchor = c->ankr_table->get_anchor (c->buffer->cur ().codepoint,
+								  currAnchorPoint,
+								  c->sanitizer.get_num_glyphs ());
 
 	    o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate);
 	    o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate);
@@ -563,9 +557,8 @@
 
 	  case 2: /* Control Point Coordinate Actions. */
 	  {
-	    const FWORD *data = (const FWORD *) &ankrData[entry->data.ankrActionIndex];
-	    if (!c->sanitizer.check_array (data, 4))
-	      return false;
+	    const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex];
+	    if (!c->sanitizer.check_array (data, 4)) return;
 	    int markX = *data++;
 	    int markY = *data++;
 	    int currX = *data++;
@@ -581,13 +574,11 @@
 	buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
       }
 
-      if (entry->flags & Mark)
+      if (entry.flags & Mark)
       {
 	mark_set = true;
 	mark = buffer->idx;
       }
-
-      return true;
     }
 
     private:
@@ -721,18 +712,18 @@
   {
     struct Long
     {
-      LOffsetTo<Lookup<HBUINT32>, false>	rowIndexTable;
-      LOffsetTo<Lookup<HBUINT32>, false>	columnIndexTable;
-      LOffsetTo<UnsizedArrayOf<FWORD32>, false>	array;
+      LNNOffsetTo<Lookup<HBUINT32>>		rowIndexTable;
+      LNNOffsetTo<Lookup<HBUINT32>>		columnIndexTable;
+      LNNOffsetTo<UnsizedArrayOf<FWORD32>>	array;
     } l;
     struct Short
     {
-      LOffsetTo<Lookup<HBUINT16>, false>	rowIndexTable;
-      LOffsetTo<Lookup<HBUINT16>, false>	columnIndexTable;
-      LOffsetTo<UnsizedArrayOf<FWORD>, false>	array;
+      LNNOffsetTo<Lookup<HBUINT16>>		rowIndexTable;
+      LNNOffsetTo<Lookup<HBUINT16>>		columnIndexTable;
+      LNNOffsetTo<UnsizedArrayOf<FWORD>>	array;
     } s;
   } u;
-  LOffsetTo<UnsizedArrayOf<FWORD>, false>	vector;
+  LNNOffsetTo<UnsizedArrayOf<FWORD>>	vector;
   public:
   DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24);
 };
@@ -742,8 +733,8 @@
 {
   typedef ExtendedTypes Types;
 
-  unsigned int tuple_count () const { return tupleCount; }
-  bool is_horizontal () const       { return !(coverage & Vertical); }
+  unsigned   tuple_count () const { return tupleCount; }
+  bool     is_horizontal () const { return !(coverage & Vertical); }
 
   enum Coverage
   {
@@ -780,17 +771,17 @@
   unsigned int get_size () const { return u.header.length; }
   unsigned int get_type () const { return u.header.coverage & u.header.SubtableType; }
 
-  template <typename context_t>
-  typename context_t::return_t dispatch (context_t *c) const
+  template <typename context_t, typename ...Ts>
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
     unsigned int subtable_type = get_type ();
     TRACE_DISPATCH (this, subtable_type);
     switch (subtable_type) {
-    case 0:	return_trace (c->dispatch (u.format0));
-    case 1:	return_trace (c->dispatch (u.format1));
-    case 2:	return_trace (c->dispatch (u.format2));
-    case 4:	return_trace (c->dispatch (u.format4));
-    case 6:	return_trace (c->dispatch (u.format6));
+    case 0:	return_trace (c->dispatch (u.format0, hb_forward<Ts> (ds)...));
+    case 1:	return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+    case 2:	return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
+    case 4:	return_trace (c->dispatch (u.format4, hb_forward<Ts> (ds)...));
+    case 6:	return_trace (c->dispatch (u.format6, hb_forward<Ts> (ds)...));
     default:	return_trace (c->default_return_value ());
     }
   }
@@ -839,7 +830,7 @@
     for (unsigned int i = 0; i < count; i++)
     {
       if (st->get_type () == 1)
-        return true;
+	return true;
       st = &StructAfter<SubTable> (*st);
     }
     return false;
@@ -854,7 +845,7 @@
     for (unsigned int i = 0; i < count; i++)
     {
       if (st->u.header.coverage & st->u.header.CrossStream)
-        return true;
+	return true;
       st = &StructAfter<SubTable> (*st);
     }
     return false;
@@ -871,7 +862,7 @@
     {
       if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) ||
 	  !st->u.header.is_horizontal ())
-        continue;
+	continue;
       v += st->get_kerning (left, right);
       st = &StructAfter<SubTable> (*st);
     }
@@ -892,7 +883,7 @@
       bool reverse;
 
       if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation))
-        goto skip;
+	goto skip;
 
       if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
 	goto skip;
@@ -906,8 +897,8 @@
       if (!seenCrossStream &&
 	  (st->u.header.coverage & st->u.header.CrossStream))
       {
-        /* Attach all glyphs into a chain. */
-        seenCrossStream = true;
+	/* Attach all glyphs into a chain. */
+	seenCrossStream = true;
 	hb_glyph_position_t *pos = c->buffer->pos;
 	unsigned int count = c->buffer->len;
 	for (unsigned int i = 0; i < count; i++)
@@ -981,8 +972,8 @@
 {
   friend struct KerxTable<kerx>;
 
-  enum { tableTag = HB_AAT_TAG_kerx };
-  enum { minVersion = 2u };
+  static constexpr hb_tag_t tableTag = HB_AAT_TAG_kerx;
+  static constexpr unsigned minVersion = 2u;
 
   typedef KerxSubTableHeader SubTableHeader;
   typedef SubTableHeader::Types Types;
diff --git a/src/hb-aat-layout-lcar-table.hh b/src/hb-aat-layout-lcar-table.hh
index cac9697..7063b38 100644
--- a/src/hb-aat-layout-lcar-table.hh
+++ b/src/hb-aat-layout-lcar-table.hh
@@ -38,9 +38,83 @@
 
 typedef ArrayOf<HBINT16> LigCaretClassEntry;
 
+struct lcarFormat0
+{
+  unsigned int get_lig_carets (hb_font_t      *font,
+			       hb_direction_t  direction,
+			       hb_codepoint_t  glyph,
+			       unsigned int    start_offset,
+			       unsigned int   *caret_count /* IN/OUT */,
+			       hb_position_t  *caret_array /* OUT */,
+			       const void     *base) const
+  {
+    const OffsetTo<LigCaretClassEntry>* entry_offset = lookupTable.get_value (glyph,
+									      font->face->get_num_glyphs ());
+    const LigCaretClassEntry& array = entry_offset ? base+*entry_offset : Null (LigCaretClassEntry);
+    if (caret_count)
+    {
+      hb_array_t<const HBINT16> arr = array.sub_array (start_offset, caret_count);
+      for (unsigned int i = 0; i < arr.length; ++i)
+	caret_array[i] = font->em_scale_dir (arr[i], direction);
+    }
+    return array.len;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
+  }
+
+  protected:
+  Lookup<OffsetTo<LigCaretClassEntry>>
+		lookupTable;	/* data Lookup table associating glyphs */
+  public:
+  DEFINE_SIZE_MIN (2);
+};
+
+struct lcarFormat1
+{
+  unsigned int get_lig_carets (hb_font_t      *font,
+			       hb_direction_t  direction,
+			       hb_codepoint_t  glyph,
+			       unsigned int    start_offset,
+			       unsigned int   *caret_count /* IN/OUT */,
+			       hb_position_t  *caret_array /* OUT */,
+			       const void     *base) const
+  {
+    const OffsetTo<LigCaretClassEntry>* entry_offset = lookupTable.get_value (glyph,
+									      font->face->get_num_glyphs ());
+    const LigCaretClassEntry& array = entry_offset ? base+*entry_offset : Null (LigCaretClassEntry);
+    if (caret_count)
+    {
+      hb_array_t<const HBINT16> arr = array.sub_array (start_offset, caret_count);
+      for (unsigned int i = 0; i < arr.length; ++i)
+      {
+	hb_position_t x = 0, y = 0;
+	font->get_glyph_contour_point_for_origin (glyph, arr[i], direction, &x, &y);
+	caret_array[i] = HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
+      }
+    }
+    return array.len;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
+  }
+
+  protected:
+  Lookup<OffsetTo<LigCaretClassEntry>>
+		lookupTable;	/* data Lookup table associating glyphs */
+  public:
+  DEFINE_SIZE_MIN (2);
+};
+
 struct lcar
 {
-  enum { tableTag = HB_AAT_TAG_lcar };
+  static constexpr hb_tag_t tableTag = HB_AAT_TAG_lcar;
 
   unsigned int get_lig_carets (hb_font_t      *font,
 			       hb_direction_t  direction,
@@ -49,41 +123,36 @@
 			       unsigned int   *caret_count /* IN/OUT */,
 			       hb_position_t  *caret_array /* OUT */) const
   {
-    const OffsetTo<LigCaretClassEntry>* entry_offset = lookup.get_value (glyph,
-									 font->face->get_num_glyphs ());
-    const LigCaretClassEntry& array = entry_offset ? this+*entry_offset : Null (LigCaretClassEntry);
-    if (caret_count)
+    switch (format)
     {
-      hb_array_t<const HBINT16> arr = array.sub_array (start_offset, caret_count);
-      unsigned int count = arr.len;
-      for (unsigned int i = 0; i < count; ++i)
-	switch (format)
-	{
-	case 0: caret_array[i] = font->em_scale_dir (arr[i], direction); break;
-	case 1:
-	  hb_position_t x, y;
-	  font->get_glyph_contour_point_for_origin (glyph, arr[i], direction, &x, &y);
-	  caret_array[i] = HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
-	  break;
-	}
+    case 0: return u.format0.get_lig_carets (font, direction, glyph, start_offset,
+					     caret_count, caret_array, this);
+    case 1: return u.format1.get_lig_carets (font, direction, glyph, start_offset,
+					     caret_count, caret_array, this);
+    default:if (caret_count) *caret_count = 0; return 0;
     }
-    return array.len;
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) &&
-			  version.major == 1 &&
-			  lookup.sanitize (c, this)));
+    if (unlikely (!c->check_struct (this) || version.major != 1))
+      return_trace (false);
+
+    switch (format) {
+    case 0: return_trace (u.format0.sanitize (c, this));
+    case 1: return_trace (u.format1.sanitize (c, this));
+    default:return_trace (true);
+    }
   }
 
   protected:
   FixedVersion<>version;	/* Version number of the ligature caret table */
   HBUINT16	format;		/* Format of the ligature caret table. */
-  Lookup<OffsetTo<LigCaretClassEntry> >
-		lookup;		/* data Lookup table associating glyphs */
-
+  union {
+  lcarFormat0	format0;
+  lcarFormat0	format1;
+  } u;
   public:
   DEFINE_SIZE_MIN (8);
 };
diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
index c7600aa..d8df579 100644
--- a/src/hb-aat-layout-morx-table.hh
+++ b/src/hb-aat-layout-morx-table.hh
@@ -54,7 +54,7 @@
 
   struct driver_context_t
   {
-    enum { in_place = true };
+    static constexpr bool in_place = true;
     enum Flags
     {
       MarkFirst		= 0x8000,	/* If set, make the current glyph the first
@@ -74,21 +74,21 @@
 	start (0), end (0) {}
 
     bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> *entry)
+			const Entry<EntryData> &entry)
     {
-      return (entry->flags & Verb) && start < end;
+      return (entry.flags & Verb) && start < end;
     }
-    bool transition (StateTableDriver<Types, EntryData> *driver,
-		     const Entry<EntryData> *entry)
+    void transition (StateTableDriver<Types, EntryData> *driver,
+		     const Entry<EntryData> &entry)
     {
       hb_buffer_t *buffer = driver->buffer;
-      unsigned int flags = entry->flags;
+      unsigned int flags = entry.flags;
 
       if (flags & MarkFirst)
 	start = buffer->idx;
 
       if (flags & MarkLast)
-	end = MIN (buffer->idx + 1, buffer->len);
+	end = hb_min (buffer->idx + 1, buffer->len);
 
       if ((flags & Verb) && start < end)
       {
@@ -117,14 +117,14 @@
 	};
 
 	unsigned int m = map[flags & Verb];
-	unsigned int l = MIN<unsigned int> (2, m >> 4);
-	unsigned int r = MIN<unsigned int> (2, m & 0x0F);
+	unsigned int l = hb_min (2u, m >> 4);
+	unsigned int r = hb_min (2u, 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, hb_min (buffer->idx + 1, buffer->len));
 	  buffer->merge_clusters (start, end);
 
 	  hb_glyph_info_t *info = buffer->info;
@@ -152,8 +152,6 @@
 	  }
 	}
       }
-
-      return true;
     }
 
     public:
@@ -204,7 +202,7 @@
 
   struct driver_context_t
   {
-    enum { in_place = true };
+    static constexpr bool in_place = true;
     enum Flags
     {
       SetMark		= 0x8000,	/* If set, make the current glyph the marked glyph. */
@@ -223,65 +221,65 @@
 	subs (table+table->substitutionTables) {}
 
     bool is_actionable (StateTableDriver<Types, EntryData> *driver,
-			const Entry<EntryData> *entry)
+			const Entry<EntryData> &entry)
     {
       hb_buffer_t *buffer = driver->buffer;
 
       if (buffer->idx == buffer->len && !mark_set)
-        return false;
+	return false;
 
-      return entry->data.markIndex != 0xFFFF || entry->data.currentIndex != 0xFFFF;
+      return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
     }
-    bool transition (StateTableDriver<Types, EntryData> *driver,
-		     const Entry<EntryData> *entry)
+    void transition (StateTableDriver<Types, 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;
+	return;
 
-      const GlyphID *replacement;
+      const HBGlyphID *replacement;
 
       replacement = nullptr;
       if (Types::extended)
       {
-	if (entry->data.markIndex != 0xFFFF)
+	if (entry.data.markIndex != 0xFFFF)
 	{
-	  const Lookup<GlyphID> &lookup = subs[entry->data.markIndex];
+	  const Lookup<HBGlyphID> &lookup = subs[entry.data.markIndex];
 	  replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs);
 	}
       }
       else
       {
-	unsigned int offset = entry->data.markIndex + buffer->info[mark].codepoint;
-	const UnsizedArrayOf<GlyphID> &subs_old = (const UnsizedArrayOf<GlyphID> &) subs;
+	unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint;
+	const UnsizedArrayOf<HBGlyphID> &subs_old = (const UnsizedArrayOf<HBGlyphID> &) subs;
 	replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
 	if (!replacement->sanitize (&c->sanitizer) || !*replacement)
 	  replacement = nullptr;
       }
       if (replacement)
       {
-	buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
+	buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
 	buffer->info[mark].codepoint = *replacement;
 	ret = true;
       }
 
       replacement = nullptr;
-      unsigned int idx = MIN (buffer->idx, buffer->len - 1);
+      unsigned int idx = hb_min (buffer->idx, buffer->len - 1);
       if (Types::extended)
       {
-	if (entry->data.currentIndex != 0xFFFF)
+	if (entry.data.currentIndex != 0xFFFF)
 	{
-	  const Lookup<GlyphID> &lookup = subs[entry->data.currentIndex];
+	  const Lookup<HBGlyphID> &lookup = subs[entry.data.currentIndex];
 	  replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs);
 	}
       }
       else
       {
-	unsigned int offset = entry->data.currentIndex + buffer->info[idx].codepoint;
-	const UnsizedArrayOf<GlyphID> &subs_old = (const UnsizedArrayOf<GlyphID> &) subs;
+	unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint;
+	const UnsizedArrayOf<HBGlyphID> &subs_old = (const UnsizedArrayOf<HBGlyphID> &) subs;
 	replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
 	if (!replacement->sanitize (&c->sanitizer) || !*replacement)
 	  replacement = nullptr;
@@ -292,13 +290,11 @@
 	ret = true;
       }
 
-      if (entry->flags & SetMark)
+      if (entry.flags & SetMark)
       {
 	mark_set = true;
 	mark = buffer->idx;
       }
-
-      return true;
     }
 
     public:
@@ -308,7 +304,7 @@
     bool mark_set;
     unsigned int mark;
     const ContextualSubtable *table;
-    const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT, false> &subs;
+    const UnsizedOffsetListOf<Lookup<HBGlyphID>, HBUINT, false> &subs;
   };
 
   bool apply (hb_aat_apply_context_t *c) const
@@ -330,7 +326,8 @@
     unsigned int num_entries = 0;
     if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false);
 
-    if (!Types::extended) return_trace (true);
+    if (!Types::extended)
+      return_trace (substitutionTables.sanitize (c, this, 0));
 
     unsigned int num_lookups = 0;
 
@@ -340,9 +337,9 @@
       const EntryData &data = entries[i].data;
 
       if (data.markIndex != 0xFFFF)
-	num_lookups = MAX<unsigned int> (num_lookups, 1 + data.markIndex);
+	num_lookups = hb_max (num_lookups, 1 + data.markIndex);
       if (data.currentIndex != 0xFFFF)
-	num_lookups = MAX<unsigned int> (num_lookups, 1 + data.currentIndex);
+	num_lookups = hb_max (num_lookups, 1 + data.currentIndex);
     }
 
     return_trace (substitutionTables.sanitize (c, this, num_lookups));
@@ -351,7 +348,7 @@
   protected:
   StateTable<Types, EntryData>
 		machine;
-  OffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT, false>, HBUINT, false>
+  NNOffsetTo<UnsizedOffsetListOf<Lookup<HBGlyphID>, HBUINT, false>, HBUINT>
 		substitutionTables;
   public:
   DEFINE_SIZE_STATIC (20);
@@ -384,11 +381,11 @@
     DEFINE_SIZE_STATIC (2);
   };
 
-  static bool performAction (const Entry<EntryData> *entry)
-  { return entry->flags & PerformAction; }
+  static bool performAction (const Entry<EntryData> &entry)
+  { return entry.flags & PerformAction; }
 
-  static unsigned int ligActionIndex (const Entry<EntryData> *entry)
-  { return entry->data.ligActionIndex; }
+  static unsigned int ligActionIndex (const Entry<EntryData> &entry)
+  { return entry.data.ligActionIndex; }
 };
 template <>
 struct LigatureEntry<false>
@@ -406,11 +403,11 @@
 
   typedef void EntryData;
 
-  static bool performAction (const Entry<EntryData> *entry)
-  { return entry->flags & Offset; }
+  static bool performAction (const Entry<EntryData> &entry)
+  { return entry.flags & Offset; }
 
-  static unsigned int ligActionIndex (const Entry<EntryData> *entry)
-  { return entry->flags & Offset; }
+  static unsigned int ligActionIndex (const Entry<EntryData> &entry)
+  { return entry.flags & Offset; }
 };
 
 
@@ -424,7 +421,7 @@
 
   struct driver_context_t
   {
-    enum { in_place = false };
+    static constexpr bool in_place = false;
     enum
     {
       DontAdvance	= LigatureEntryT::DontAdvance,
@@ -452,26 +449,23 @@
 	match_length (0) {}
 
     bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> *entry)
+			const Entry<EntryData> &entry)
     {
       return LigatureEntryT::performAction (entry);
     }
-    bool transition (StateTableDriver<Types, EntryData> *driver,
-		     const Entry<EntryData> *entry)
+    void transition (StateTableDriver<Types, EntryData> *driver,
+		     const Entry<EntryData> &entry)
     {
       hb_buffer_t *buffer = driver->buffer;
 
       DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx);
-      if (entry->flags & LigatureEntryT::SetComponent)
+      if (entry.flags & LigatureEntryT::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)
+	if (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len)
 	  match_length--;
 
-	match_positions[match_length++] = buffer->out_len;
+	match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len;
 	DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len);
       }
 
@@ -481,10 +475,10 @@
 	unsigned int end = buffer->out_len;
 
 	if (unlikely (!match_length))
-	  return true;
+	  return;
 
 	if (buffer->idx >= buffer->len)
-	  return false; // TODO Work on previous instead?
+	  return; /* TODO Work on previous instead? */
 
 	unsigned int cursor = match_length;
 
@@ -494,7 +488,7 @@
 
 	unsigned int ligature_idx = 0;
 	unsigned int action;
-        do
+	do
 	{
 	  if (unlikely (!cursor))
 	  {
@@ -505,9 +499,9 @@
 	  }
 
 	  DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1);
-	  buffer->move_to (match_positions[--cursor]);
+	  buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]);
 
-	  if (unlikely (!actionData->sanitize (&c->sanitizer))) return false;
+	  if (unlikely (!actionData->sanitize (&c->sanitizer))) break;
 	  action = *actionData;
 
 	  uint32_t uoffset = action & LigActionOffset;
@@ -517,7 +511,7 @@
 	  unsigned int component_idx = buffer->cur().codepoint + offset;
 	  component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ);
 	  const HBUINT16 &componentData = component[component_idx];
-	  if (unlikely (!componentData.sanitize (&c->sanitizer))) return false;
+	  if (unlikely (!componentData.sanitize (&c->sanitizer))) break;
 	  ligature_idx += componentData;
 
 	  DEBUG_MSG (APPLY, nullptr, "Action store %u last %u",
@@ -526,24 +520,24 @@
 	  if (action & (LigActionStore | LigActionLast))
 	  {
 	    ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ);
-	    const GlyphID &ligatureData = ligature[ligature_idx];
-	    if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false;
+	    const HBGlyphID &ligatureData = ligature[ligature_idx];
+	    if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break;
 	    hb_codepoint_t lig = ligatureData;
 
 	    DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig);
 	    buffer->replace_glyph (lig);
 
-	    unsigned int lig_end = match_positions[match_length - 1] + 1;
+	    unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u;
 	    /* Now go and delete all subsequent components. */
-	    while (match_length - 1 > cursor)
+	    while (match_length - 1u > cursor)
 	    {
 	      DEBUG_MSG (APPLY, nullptr, "Skipping ligature component");
-	      buffer->move_to (match_positions[--match_length]);
+	      buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]);
 	      buffer->replace_glyph (DELETED_GLYPH);
 	    }
 
 	    buffer->move_to (lig_end);
-	    buffer->merge_out_clusters (match_positions[cursor], buffer->out_len);
+	    buffer->merge_out_clusters (match_positions[cursor % ARRAY_LENGTH (match_positions)], buffer->out_len);
 	  }
 
 	  actionData++;
@@ -551,8 +545,6 @@
 	while (!(action & LigActionLast));
 	buffer->move_to (end);
       }
-
-      return true;
     }
 
     public:
@@ -562,7 +554,7 @@
     const LigatureSubtable *table;
     const UnsizedArrayOf<HBUINT32> &ligAction;
     const UnsizedArrayOf<HBUINT16> &component;
-    const UnsizedArrayOf<GlyphID> &ligature;
+    const UnsizedArrayOf<HBGlyphID> &ligature;
     unsigned int match_length;
     unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
   };
@@ -590,11 +582,11 @@
   protected:
   StateTable<Types, EntryData>
 		machine;
-  OffsetTo<UnsizedArrayOf<HBUINT32>, HBUINT, false>
+  NNOffsetTo<UnsizedArrayOf<HBUINT32>, HBUINT>
 		ligAction;	/* Offset to the ligature action table. */
-  OffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT, false>
+  NNOffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT>
 		component;	/* Offset to the component table. */
-  OffsetTo<UnsizedArrayOf<GlyphID>, HBUINT, false>
+  NNOffsetTo<UnsizedArrayOf<HBGlyphID>, HBUINT>
 		ligature;	/* Offset to the actual ligature lists. */
   public:
   DEFINE_SIZE_STATIC (28);
@@ -614,7 +606,7 @@
     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);
+      const HBGlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
       if (replacement)
       {
 	info[i].codepoint = *replacement;
@@ -632,7 +624,7 @@
   }
 
   protected:
-  Lookup<GlyphID>	substitute;
+  Lookup<HBGlyphID>	substitute;
   public:
   DEFINE_SIZE_MIN (2);
 };
@@ -660,7 +652,7 @@
 
   struct driver_context_t
   {
-    enum { in_place = false };
+    static constexpr bool in_place = false;
     enum Flags
     {
       SetMark		= 0x8000,	/* If set, mark the current glyph. */
@@ -713,28 +705,29 @@
 		      hb_aat_apply_context_t *c_) :
 	ret (false),
 	c (c_),
-	mark_set (false),
 	mark (0),
 	insertionAction (table+table->insertionAction) {}
 
     bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> *entry)
+			const Entry<EntryData> &entry)
     {
-      return (entry->flags & (CurrentInsertCount | MarkedInsertCount)) &&
-	     (entry->data.currentInsertIndex != 0xFFFF ||entry->data.markedInsertIndex != 0xFFFF);
+      return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) &&
+	     (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF);
     }
-    bool transition (StateTableDriver<Types, EntryData> *driver,
-		     const Entry<EntryData> *entry)
+    void transition (StateTableDriver<Types, EntryData> *driver,
+		     const Entry<EntryData> &entry)
     {
       hb_buffer_t *buffer = driver->buffer;
-      unsigned int flags = entry->flags;
+      unsigned int flags = entry.flags;
 
-      if (entry->data.markedInsertIndex != 0xFFFF && mark_set)
+      unsigned mark_loc = buffer->out_len;
+
+      if (entry.data.markedInsertIndex != 0xFFFF)
       {
 	unsigned int count = (flags & MarkedInsertCount);
-	unsigned int start = entry->data.markedInsertIndex;
-	const GlyphID *glyphs = &insertionAction[start];
-	if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false;
+	unsigned int start = entry.data.markedInsertIndex;
+	const HBGlyphID *glyphs = &insertionAction[start];
+	if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
 
 	bool before = flags & MarkedInsertBefore;
 
@@ -751,15 +744,18 @@
 
 	buffer->move_to (end + count);
 
-	buffer->unsafe_to_break_from_outbuffer (mark, MIN (buffer->idx + 1, buffer->len));
+	buffer->unsafe_to_break_from_outbuffer (mark, hb_min (buffer->idx + 1, buffer->len));
       }
 
-      if (entry->data.currentInsertIndex != 0xFFFF)
+      if (flags & SetMark)
+	mark = mark_loc;
+
+      if (entry.data.currentInsertIndex != 0xFFFF)
       {
 	unsigned int count = (flags & CurrentInsertCount) >> 5;
-	unsigned int start = entry->data.currentInsertIndex;
-	const GlyphID *glyphs = &insertionAction[start];
-	if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false;
+	unsigned int start = entry.data.currentInsertIndex;
+	const HBGlyphID *glyphs = &insertionAction[start];
+	if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
 
 	bool before = flags & CurrentInsertBefore;
 
@@ -790,23 +786,14 @@
 	 */
 	buffer->move_to ((flags & DontAdvance) ? end : end + count);
       }
-
-      if (flags & SetMark)
-      {
-	mark_set = true;
-	mark = buffer->out_len;
-      }
-
-      return true;
     }
 
     public:
     bool ret;
     private:
     hb_aat_apply_context_t *c;
-    bool mark_set;
     unsigned int mark;
-    const UnsizedArrayOf<GlyphID> &insertionAction;
+    const UnsizedArrayOf<HBGlyphID> &insertionAction;
   };
 
   bool apply (hb_aat_apply_context_t *c) const
@@ -832,7 +819,7 @@
   protected:
   StateTable<Types, EntryData>
 		machine;
-  OffsetTo<UnsizedArrayOf<GlyphID>, HBUINT, false>
+  NNOffsetTo<UnsizedArrayOf<HBGlyphID>, HBUINT>
 		insertionAction;	/* Byte offset from stateHeader to the start of
 					 * the insertion glyph table. */
   public:
@@ -896,17 +883,17 @@
     Insertion		= 5
   };
 
-  template <typename context_t>
-  typename context_t::return_t dispatch (context_t *c) const
+  template <typename context_t, typename ...Ts>
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) 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));
+    case Rearrangement:		return_trace (c->dispatch (u.rearrangement, hb_forward<Ts> (ds)...));
+    case Contextual:		return_trace (c->dispatch (u.contextual, hb_forward<Ts> (ds)...));
+    case Ligature:		return_trace (c->dispatch (u.ligature, hb_forward<Ts> (ds)...));
+    case Noncontextual:		return_trace (c->dispatch (u.noncontextual, hb_forward<Ts> (ds)...));
+    case Insertion:		return_trace (c->dispatch (u.insertion, hb_forward<Ts> (ds)...));
     default:			return_trace (c->default_return_value ());
     }
   }
@@ -961,7 +948,7 @@
 	hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType;
 	hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting;
       retry:
-	const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch ((uint16_t) type);
+	const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch (type);
 	if (info && info->setting == setting)
 	{
 	  flags &= feature.disableFlags;
@@ -982,19 +969,19 @@
   void apply (hb_aat_apply_context_t *c,
 		     hb_mask_t flags) const
   {
-    const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types> > (featureZ.as_array (featureCount));
+    const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount));
     unsigned int count = subtableCount;
     for (unsigned int i = 0; i < count; i++)
     {
       bool reverse;
 
       if (!(subtable->subFeatureFlags & flags))
-        goto skip;
+	goto skip;
 
       if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) &&
 	  HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
 	  bool (subtable->get_coverage() & ChainSubtable<Types>::Vertical))
-        goto skip;
+	goto skip;
 
       /* Buffer contents is always in logical direction.  Determine if
        * we need to reverse before applying this subtable.  We reverse
@@ -1029,22 +1016,22 @@
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
       if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
-        goto skip;
+	goto skip;
 
       if (reverse)
-        c->buffer->reverse ();
+	c->buffer->reverse ();
 
       subtable->apply (c);
 
       if (reverse)
-        c->buffer->reverse ();
+	c->buffer->reverse ();
 
       (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);
 
       if (unlikely (!c->buffer->successful)) return;
 
     skip:
-      subtable = &StructAfter<ChainSubtable<Types> > (*subtable);
+      subtable = &StructAfter<ChainSubtable<Types>> (*subtable);
       c->set_lookup_index (c->lookup_index + 1);
     }
   }
@@ -1062,13 +1049,13 @@
     if (!c->check_array (featureZ.arrayZ, featureCount))
       return_trace (false);
 
-    const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types> > (featureZ.as_array (featureCount));
+    const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount));
     unsigned int count = subtableCount;
     for (unsigned int i = 0; i < count; i++)
     {
       if (!subtable->sanitize (c))
 	return_trace (false);
-      subtable = &StructAfter<ChainSubtable<Types> > (*subtable);
+      subtable = &StructAfter<ChainSubtable<Types>> (*subtable);
     }
 
     return_trace (true);
@@ -1093,10 +1080,10 @@
  * The 'mort'/'morx' Table
  */
 
-template <typename Types>
+template <typename Types, hb_tag_t TAG>
 struct mortmorx
 {
-  enum { tableTag = HB_AAT_TAG_morx };
+  static constexpr hb_tag_t tableTag = TAG;
 
   bool has_data () const { return version != 0; }
 
@@ -1108,7 +1095,7 @@
     for (unsigned int i = 0; i < count; i++)
     {
       map->chain_flags.push (chain->compile_flags (mapper));
-      chain = &StructAfter<Chain<Types> > (*chain);
+      chain = &StructAfter<Chain<Types>> (*chain);
     }
   }
 
@@ -1122,7 +1109,7 @@
     {
       chain->apply (c, c->plan->aat_map.chain_flags[i]);
       if (unlikely (!c->buffer->successful)) return;
-      chain = &StructAfter<Chain<Types> > (*chain);
+      chain = &StructAfter<Chain<Types>> (*chain);
     }
   }
 
@@ -1138,7 +1125,7 @@
     {
       if (!chain->sanitize (c, version))
 	return_trace (false);
-      chain = &StructAfter<Chain<Types> > (*chain);
+      chain = &StructAfter<Chain<Types>> (*chain);
     }
 
     return_trace (true);
@@ -1156,14 +1143,8 @@
   DEFINE_SIZE_MIN (8);
 };
 
-struct morx : mortmorx<ExtendedTypes>
-{
-  enum { tableTag = HB_AAT_TAG_morx };
-};
-struct mort : mortmorx<ObsoleteTypes>
-{
-  enum { tableTag = HB_AAT_TAG_mort };
-};
+struct morx : mortmorx<ExtendedTypes, HB_AAT_TAG_morx> {};
+struct mort : mortmorx<ObsoleteTypes, HB_AAT_TAG_mort> {};
 
 
 } /* namespace AAT */
diff --git a/src/hb-aat-layout-opbd-table.hh b/src/hb-aat-layout-opbd-table.hh
new file mode 100644
index 0000000..4e02340
--- /dev/null
+++ b/src/hb-aat-layout-opbd-table.hh
@@ -0,0 +1,173 @@
+/*
+ * Copyright © 2019  Ebrahim Byagowi
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_AAT_LAYOUT_OPBD_TABLE_HH
+#define HB_AAT_LAYOUT_OPBD_TABLE_HH
+
+#include "hb-aat-layout-common.hh"
+#include "hb-open-type.hh"
+
+/*
+ * opbd -- Optical Bounds
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
+ */
+#define HB_AAT_TAG_opbd HB_TAG('o','p','b','d')
+
+
+namespace AAT {
+
+struct OpticalBounds
+{
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  FWORD		leftSide;
+  FWORD		topSide;
+  FWORD		rightSide;
+  FWORD		bottomSide;
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
+
+struct opbdFormat0
+{
+  bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
+		   hb_glyph_extents_t *extents, const void *base) const
+  {
+    const OffsetTo<OpticalBounds> *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ());
+    if (!bounds_offset) return false;
+    const OpticalBounds &bounds = base+*bounds_offset;
+
+    if (extents)
+      *extents = {
+	font->em_scale_x (bounds.leftSide),
+	font->em_scale_y (bounds.topSide),
+	font->em_scale_x (bounds.rightSide),
+	font->em_scale_y (bounds.bottomSide)
+      };
+    return true;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
+  }
+
+  protected:
+  Lookup<OffsetTo<OpticalBounds>>
+		lookupTable;	/* Lookup table associating glyphs with the four
+				 * int16 values for the left-side, top-side,
+				 * right-side, and bottom-side optical bounds. */
+  public:
+  DEFINE_SIZE_MIN (2);
+};
+
+struct opbdFormat1
+{
+  bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
+		   hb_glyph_extents_t *extents, const void *base) const
+  {
+    const OffsetTo<OpticalBounds> *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ());
+    if (!bounds_offset) return false;
+    const OpticalBounds &bounds = base+*bounds_offset;
+
+    hb_position_t left = 0, top = 0, right = 0, bottom = 0, ignore;
+    if (font->get_glyph_contour_point (glyph_id, bounds.leftSide, &left, &ignore) ||
+	font->get_glyph_contour_point (glyph_id, bounds.topSide, &ignore, &top) ||
+	font->get_glyph_contour_point (glyph_id, bounds.rightSide, &right, &ignore) ||
+	font->get_glyph_contour_point (glyph_id, bounds.bottomSide, &ignore, &bottom))
+    {
+      if (extents)
+	*extents = {left, top, right, bottom};
+      return true;
+    }
+    return false;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
+  }
+
+  protected:
+  Lookup<OffsetTo<OpticalBounds>>
+		lookupTable;	/* Lookup table associating glyphs with the four
+				 * int16 values for the left-side, top-side,
+				 * right-side, and bottom-side optical bounds. */
+  public:
+  DEFINE_SIZE_MIN (2);
+};
+
+struct opbd
+{
+  static constexpr hb_tag_t tableTag = HB_AAT_TAG_opbd;
+
+  bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
+		   hb_glyph_extents_t *extents) const
+  {
+    switch (format)
+    {
+    case 0: return u.format0.get_bounds (font, glyph_id, extents, this);
+    case 1: return u.format1.get_bounds (font, glyph_id, extents, this);
+    default:return false;
+    }
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this) || version.major != 1))
+      return_trace (false);
+
+    switch (format)
+    {
+    case 0: return_trace (u.format0.sanitize (c, this));
+    case 1: return_trace (u.format1.sanitize (c, this));
+    default:return_trace (true);
+    }
+  }
+
+  protected:
+  FixedVersion<>version;	/* Version number of the optical bounds
+				 * table (0x00010000 for the current version). */
+  HBUINT16	format;		/* Format of the optical bounds table.
+				 * Format 0 indicates distance and Format 1 indicates
+				 * control point. */
+  union {
+  opbdFormat0 format0;
+  opbdFormat1 format1;
+  } u;
+  public:
+  DEFINE_SIZE_MIN (8);
+};
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_OPBD_TABLE_HH */
diff --git a/src/hb-aat-layout-trak-table.hh b/src/hb-aat-layout-trak-table.hh
index 0648f61..99dddd8 100644
--- a/src/hb-aat-layout-trak-table.hh
+++ b/src/hb-aat-layout-trak-table.hh
@@ -62,11 +62,11 @@
   }
 
   protected:
-  Fixed		track;		/* Track value for this record. */
+  HBFixed		track;		/* Track value for this record. */
   NameID	trackNameID;	/* The 'name' table index for this track.
 				 * (a short word or phrase like "loose"
 				 * or "very tight") */
-  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT16, false>
+  NNOffsetTo<UnsizedArrayOf<FWORD>>
 		valuesZ;	/* Offset from start of tracking table to
 				 * per-size tracking values for this track. */
 
@@ -82,7 +82,7 @@
 			const void *base) const
   {
     unsigned int sizes = nSizes;
-    hb_array_t<const Fixed> size_table ((base+sizeTable).arrayZ, sizes);
+    hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
 
     float s0 = size_table[idx].to_float ();
     float s1 = size_table[idx + 1].to_float ();
@@ -93,13 +93,6 @@
 
   int get_tracking (const void *base, float ptem) const
   {
-    /* CoreText points are CSS pixels (96 per inch),
-     * NOT typographic points (72 per inch).
-     *
-     * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
-     */
-    float csspx = ptem * 96.f / 72.f;
-
     /*
      * Choose track.
      */
@@ -127,14 +120,14 @@
     if (!sizes) return 0.;
     if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
 
-    hb_array_t<const Fixed> size_table ((base+sizeTable).arrayZ, sizes);
+    hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
     unsigned int size_index;
     for (size_index = 0; size_index < sizes - 1; size_index++)
-      if (size_table[size_index].to_float () >= csspx)
-        break;
+      if (size_table[size_index].to_float () >= ptem)
+	break;
 
-    return round (interpolate_at (size_index ? size_index - 1 : 0, csspx,
-				  *trackTableEntry, base));
+    return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem,
+				   *trackTableEntry, base));
   }
 
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
@@ -148,7 +141,7 @@
   protected:
   HBUINT16	nTracks;	/* Number of separate tracks included in this table. */
   HBUINT16	nSizes;		/* Number of point sizes included in this table. */
-  LOffsetTo<UnsizedArrayOf<Fixed>, false>
+  LOffsetTo<UnsizedArrayOf<HBFixed>, false>
 		sizeTable;	/* Offset from start of the tracking table to
 				 * Array[nSizes] of size values.. */
   UnsizedArrayOf<TrackTableEntry>
@@ -160,7 +153,7 @@
 
 struct trak
 {
-  enum { tableTag = HB_AAT_TAG_trak };
+  static constexpr hb_tag_t tableTag = HB_AAT_TAG_trak;
 
   bool has_data () const { return version.to_int (); }
 
@@ -183,7 +176,7 @@
       hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
       foreach_grapheme (buffer, start, end)
       {
-        if (!(buffer->info[start].mask & trak_mask)) continue;
+	if (!(buffer->info[start].mask & trak_mask)) continue;
 	buffer->pos[start].x_advance += advance_to_add;
 	buffer->pos[start].x_offset += offset_to_add;
       }
@@ -196,7 +189,7 @@
       hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
       foreach_grapheme (buffer, start, end)
       {
-        if (!(buffer->info[start].mask & trak_mask)) continue;
+	if (!(buffer->info[start].mask & trak_mask)) continue;
 	buffer->pos[start].y_advance += advance_to_add;
 	buffer->pos[start].y_offset += offset_to_add;
       }
diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc
index 8de8205..4e506de 100644
--- a/src/hb-aat-layout.cc
+++ b/src/hb-aat-layout.cc
@@ -25,9 +25,8 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-open-type.hh"
+#include "hb.hh"
 
-#include "hb-ot-face.hh"
 #include "hb-aat-layout.hh"
 #include "hb-aat-fdsc-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-layout-ankr-table.hh"
@@ -40,6 +39,42 @@
 #include "hb-aat-ltag-table.hh"
 
 
+/*
+ * hb_aat_apply_context_t
+ */
+
+/* Note: This context is used for kerning, even without AAT, hence the condition. */
+#if !defined(HB_NO_AAT) || !defined(HB_NO_OT_KERN)
+
+AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
+						     hb_font_t *font_,
+						     hb_buffer_t *buffer_,
+						     hb_blob_t *blob) :
+						       plan (plan_),
+						       font (font_),
+						       face (font->face),
+						       buffer (buffer_),
+						       sanitizer (),
+						       ankr_table (&Null(AAT::ankr)),
+						       lookup_index (0),
+						       debug_depth (0)
+{
+  sanitizer.init (blob);
+  sanitizer.set_num_glyphs (face->get_num_glyphs ());
+  sanitizer.start_processing ();
+  sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
+}
+
+AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t ()
+{ sanitizer.end_processing (); }
+
+void
+AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_)
+{ ankr_table = ankr_table_; }
+
+#endif
+
+
 /**
  * SECTION:hb-aat-layout
  * @title: hb-aat-layout
@@ -50,6 +85,8 @@
  **/
 
 
+#if !defined(HB_NO_AAT) || defined(HAVE_CORETEXT)
+
 /* Table data courtesy of Apple.  Converted from mnemonics to integers
  * when moving to this file. */
 static const hb_aat_feature_mapping_t feature_mappings[] =
@@ -135,49 +172,16 @@
 const hb_aat_feature_mapping_t *
 hb_aat_layout_find_feature_mapping (hb_tag_t tag)
 {
-  return (const hb_aat_feature_mapping_t *) bsearch (&tag,
-						     feature_mappings,
-						     ARRAY_LENGTH (feature_mappings),
-						     sizeof (feature_mappings[0]),
-						     hb_aat_feature_mapping_t::cmp);
+  return (const hb_aat_feature_mapping_t *) hb_bsearch (&tag,
+							feature_mappings,
+							ARRAY_LENGTH (feature_mappings),
+							sizeof (feature_mappings[0]),
+							hb_aat_feature_mapping_t::cmp);
 }
+#endif
 
 
-/*
- * hb_aat_apply_context_t
- */
-
-AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
-						     hb_font_t *font_,
-						     hb_buffer_t *buffer_,
-						     hb_blob_t *blob) :
-						       plan (plan_),
-						       font (font_),
-						       face (font->face),
-						       buffer (buffer_),
-						       sanitizer (),
-						       ankr_table (&Null(AAT::ankr)),
-						       ankr_end (nullptr),
-						       lookup_index (0),
-						       debug_depth (0)
-{
-  sanitizer.init (blob);
-  sanitizer.set_num_glyphs (face->get_num_glyphs ());
-  sanitizer.start_processing ();
-  sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
-}
-
-AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t ()
-{ sanitizer.end_processing (); }
-
-void
-AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_,
-					     const char      *ankr_end_)
-{
-  ankr_table = ankr_table_;
-  ankr_end = ankr_end_;
-}
-
+#ifndef HB_NO_AAT
 
 /*
  * mort/morx/kerx/trak
@@ -286,11 +290,8 @@
   hb_blob_t *kerx_blob = font->face->table.kerx.get_blob ();
   const AAT::kerx& kerx = *kerx_blob->as<AAT::kerx> ();
 
-  hb_blob_t *ankr_blob = font->face->table.ankr.get_blob ();;
-  const AAT::ankr& ankr = *font->face->table.ankr;
-
   AAT::hb_aat_apply_context_t c (plan, font, buffer, kerx_blob);
-  c.set_ankr_table (&ankr, ankr_blob->data + ankr_blob->length);
+  c.set_ankr_table (font->face->table.ankr.get ());
   kerx.apply (&c);
 }
 
@@ -319,14 +320,6 @@
   trak.apply (&c);
 }
 
-
-hb_language_t
-_hb_aat_language_get (hb_face_t *face,
-		      unsigned int i)
-{
-  return face->table.ltag->get_language (i);
-}
-
 /**
  * hb_aat_layout_get_feature_types:
  * @face: a face object
@@ -390,3 +383,6 @@
 {
   return face->table.feat->get_selector_infos (feature_type, start_offset, selector_count, selectors, default_index);
 }
+
+
+#endif
diff --git a/src/hb-aat-layout.h b/src/hb-aat-layout.h
index 760aaae..b617e8b 100644
--- a/src/hb-aat-layout.h
+++ b/src/hb-aat-layout.h
@@ -85,7 +85,7 @@
   HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE			= 39,
   HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE		= 103,
 
-  _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
+  _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
 } hb_aat_layout_feature_type_t;
 
 /**
@@ -424,7 +424,7 @@
   HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN		= 2,
   HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN		= 3,
 
-  _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
+  _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
 } hb_aat_layout_feature_selector_t;
 
 HB_EXTERN unsigned int
diff --git a/src/hb-aat-layout.hh b/src/hb-aat-layout.hh
index 8346d9f..8310bfc 100644
--- a/src/hb-aat-layout.hh
+++ b/src/hb-aat-layout.hh
@@ -30,7 +30,7 @@
 #include "hb.hh"
 
 #include "hb-ot-shape.hh"
-
+#include "hb-aat-ltag-table.hh"
 
 struct hb_aat_feature_mapping_t
 {
@@ -39,7 +39,7 @@
   hb_aat_layout_feature_selector_t selectorToEnable;
   hb_aat_layout_feature_selector_t selectorToDisable;
 
-  static int cmp (const void *key_, const void *entry_)
+  HB_INTERNAL static int cmp (const void *key_, const void *entry_)
   {
     hb_tag_t key = * (unsigned int *) key_;
     const hb_aat_feature_mapping_t * entry = (const hb_aat_feature_mapping_t *) entry_;
@@ -77,9 +77,5 @@
 		     hb_font_t *font,
 		     hb_buffer_t *buffer);
 
-HB_INTERNAL hb_language_t
-_hb_aat_language_get (hb_face_t *face,
-		      unsigned int i);
-
 
 #endif /* HB_AAT_LAYOUT_HH */
diff --git a/src/hb-aat-ltag-table.hh b/src/hb-aat-ltag-table.hh
index 3b00b55..711f9aa 100644
--- a/src/hb-aat-ltag-table.hh
+++ b/src/hb-aat-ltag-table.hh
@@ -50,7 +50,7 @@
   }
 
   protected:
-  OffsetTo<UnsizedArrayOf<HBUINT8>, HBUINT16, false>
+  NNOffsetTo<UnsizedArrayOf<HBUINT8>>
 		tag;		/* Offset from the start of the table to
 				 * the beginning of the string */
   HBUINT16	length;		/* String length (in bytes) */
@@ -60,7 +60,7 @@
 
 struct ltag
 {
-  enum { tableTag = HB_AAT_TAG_ltag };
+  static constexpr hb_tag_t tableTag = HB_AAT_TAG_ltag;
 
   hb_language_t get_language (unsigned int i) const
   {
diff --git a/src/hb-aat-map.cc b/src/hb-aat-map.cc
index 1c65a67..bc87935 100644
--- a/src/hb-aat-map.cc
+++ b/src/hb-aat-map.cc
@@ -26,6 +26,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
+#include "hb.hh"
+
+#ifndef HB_NO_AAT_SHAPE
+
 #include "hb-aat-map.hh"
 
 #include "hb-aat-layout.hh"
@@ -54,11 +58,11 @@
 hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
 {
   /* Sort features and merge duplicates */
-  if (features.len)
+  if (features.length)
   {
     features.qsort ();
     unsigned int j = 0;
-    for (unsigned int i = 1; i < features.len; i++)
+    for (unsigned int i = 1; i < features.length; i++)
       if (features[i].type != features[j].type)
 	features[++j] = features[i];
     features.shrink (j + 1);
@@ -66,3 +70,6 @@
 
   hb_aat_layout_compile_map (this, &m);
 }
+
+
+#endif
diff --git a/src/hb-aat-map.hh b/src/hb-aat-map.hh
index fa312db..984a59c 100644
--- a/src/hb-aat-map.hh
+++ b/src/hb-aat-map.hh
@@ -44,7 +44,7 @@
   void fini () { chain_flags.fini (); }
 
   public:
-  hb_vector_t<hb_mask_t, 1> chain_flags;
+  hb_vector_t<hb_mask_t> chain_flags;
 };
 
 struct hb_aat_map_builder_t
@@ -66,7 +66,7 @@
     hb_aat_layout_feature_selector_t  setting;
     unsigned  seq; /* For stable sorting only. */
 
-    static int cmp (const void *pa, const void *pb)
+    HB_INTERNAL 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;
@@ -74,7 +74,7 @@
 	     (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
     }
 
-    int cmp (unsigned int ty) const
+    int cmp (hb_aat_layout_feature_type_t ty) const
     {
       return (type != ty) ? (type < ty ? -1 : 1) : 0;
     }
@@ -84,7 +84,7 @@
   hb_face_t *face;
 
   public:
-  hb_vector_t<feature_info_t, 32> features;
+  hb_sorted_vector_t<feature_info_t> features;
 };
 
 
diff --git a/src/hb-algs.hh b/src/hb-algs.hh
new file mode 100644
index 0000000..042e1c2
--- /dev/null
+++ b/src/hb-algs.hh
@@ -0,0 +1,1059 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ * Copyright © 2019  Facebook, 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
+ * Facebook Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_ALGS_HH
+#define HB_ALGS_HH
+
+#include "hb.hh"
+#include "hb-meta.hh"
+#include "hb-null.hh"
+#include "hb-number.hh"
+
+
+/* Encodes three unsigned integers in one 64-bit number.  If the inputs have more than 21 bits,
+ * values will be truncated / overlap, and might not decode exactly. */
+#define HB_CODEPOINT_ENCODE3(x,y,z) (((uint64_t) (x) << 42) | ((uint64_t) (y) << 21) | (uint64_t) (z))
+#define HB_CODEPOINT_DECODE3_1(v) ((hb_codepoint_t) ((v) >> 42))
+#define HB_CODEPOINT_DECODE3_2(v) ((hb_codepoint_t) ((v) >> 21) & 0x1FFFFFu)
+#define HB_CODEPOINT_DECODE3_3(v) ((hb_codepoint_t) (v) & 0x1FFFFFu)
+
+/* Custom encoding used by hb-ucd. */
+#define HB_CODEPOINT_ENCODE3_11_7_14(x,y,z) (((uint32_t) ((x) & 0x07FFu) << 21) | (((uint32_t) (y) & 0x007Fu) << 14) | (uint32_t) ((z) & 0x3FFFu))
+#define HB_CODEPOINT_DECODE3_11_7_14_1(v) ((hb_codepoint_t) ((v) >> 21))
+#define HB_CODEPOINT_DECODE3_11_7_14_2(v) ((hb_codepoint_t) (((v) >> 14) & 0x007Fu) | 0x0300)
+#define HB_CODEPOINT_DECODE3_11_7_14_3(v) ((hb_codepoint_t) (v) & 0x3FFFu)
+
+struct
+{
+  /* Note.  This is dangerous in that if it's passed an rvalue, it returns rvalue-reference. */
+  template <typename T> constexpr auto
+  operator () (T&& v) const HB_AUTO_RETURN ( hb_forward<T> (v) )
+}
+HB_FUNCOBJ (hb_identity);
+struct
+{
+  /* Like identity(), but only retains lvalue-references.  Rvalues are returned as rvalues. */
+  template <typename T> constexpr T&
+  operator () (T& v) const { return v; }
+
+  template <typename T> constexpr hb_remove_reference<T>
+  operator () (T&& v) const { return v; }
+}
+HB_FUNCOBJ (hb_lidentity);
+struct
+{
+  /* Like identity(), but always returns rvalue. */
+  template <typename T> constexpr hb_remove_reference<T>
+  operator () (T&& v) const { return v; }
+}
+HB_FUNCOBJ (hb_ridentity);
+
+struct
+{
+  template <typename T> constexpr bool
+  operator () (T&& v) const { return bool (hb_forward<T> (v)); }
+}
+HB_FUNCOBJ (hb_bool);
+
+struct
+{
+  private:
+
+  template <typename T> constexpr auto
+  impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
+
+  template <typename T,
+	    hb_enable_if (hb_is_integral (T))> constexpr auto
+  impl (const T& v, hb_priority<0>) const HB_AUTO_RETURN
+  (
+    /* Knuth's multiplicative method: */
+    (uint32_t) v * 2654435761u
+  )
+
+  public:
+
+  template <typename T> constexpr auto
+  operator () (const T& v) const HB_RETURN (uint32_t, impl (v, hb_prioritize))
+}
+HB_FUNCOBJ (hb_hash);
+
+
+struct
+{
+  private:
+
+  /* Pointer-to-member-function. */
+  template <typename Appl, typename T, typename ...Ts> auto
+  impl (Appl&& a, hb_priority<2>, T &&v, Ts&&... ds) const HB_AUTO_RETURN
+  ((hb_deref (hb_forward<T> (v)).*hb_forward<Appl> (a)) (hb_forward<Ts> (ds)...))
+
+  /* Pointer-to-member. */
+  template <typename Appl, typename T> auto
+  impl (Appl&& a, hb_priority<1>, T &&v) const HB_AUTO_RETURN
+  ((hb_deref (hb_forward<T> (v))).*hb_forward<Appl> (a))
+
+  /* Operator(). */
+  template <typename Appl, typename ...Ts> auto
+  impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN
+  (hb_deref (hb_forward<Appl> (a)) (hb_forward<Ts> (ds)...))
+
+  public:
+
+  template <typename Appl, typename ...Ts> auto
+  operator () (Appl&& a, Ts&&... ds) const HB_AUTO_RETURN
+  (
+    impl (hb_forward<Appl> (a),
+	  hb_prioritize,
+	  hb_forward<Ts> (ds)...)
+  )
+}
+HB_FUNCOBJ (hb_invoke);
+
+template <unsigned Pos, typename Appl, typename V>
+struct hb_partial_t
+{
+  hb_partial_t (Appl a, V v) : a (a), v (v) {}
+
+  static_assert (Pos > 0, "");
+
+  template <typename ...Ts,
+	    unsigned P = Pos,
+	    hb_enable_if (P == 1)> auto
+  operator () (Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl),
+						   hb_declval (V),
+						   hb_declval (Ts)...))
+  {
+    return hb_invoke (hb_forward<Appl> (a),
+		      hb_forward<V> (v),
+		      hb_forward<Ts> (ds)...);
+  }
+  template <typename T0, typename ...Ts,
+	    unsigned P = Pos,
+	    hb_enable_if (P == 2)> auto
+  operator () (T0&& d0, Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl),
+							    hb_declval (T0),
+							    hb_declval (V),
+							    hb_declval (Ts)...))
+  {
+    return hb_invoke (hb_forward<Appl> (a),
+		      hb_forward<T0> (d0),
+		      hb_forward<V> (v),
+		      hb_forward<Ts> (ds)...);
+  }
+
+  private:
+  hb_reference_wrapper<Appl> a;
+  V v;
+};
+template <unsigned Pos=1, typename Appl, typename V>
+auto hb_partial (Appl&& a, V&& v) HB_AUTO_RETURN
+(( hb_partial_t<Pos, Appl, V> (a, v) ))
+
+/* The following, HB_PARTIALIZE, macro uses a particular corner-case
+ * of C++11 that is not particularly well-supported by all compilers.
+ * What's happening is that it's using "this" in a trailing return-type
+ * via decltype().  Broken compilers deduce the type of "this" pointer
+ * in that context differently from what it resolves to in the body
+ * of the function.
+ *
+ * One probable cause of this is that at the time of trailing return
+ * type declaration, "this" points to an incomplete type, whereas in
+ * the function body the type is complete.  That doesn't justify the
+ * error in any way, but is probably what's happening.
+ *
+ * In the case of MSVC, we get around this by using C++14 "decltype(auto)"
+ * which deduces the type from the actual return statement.  For gcc 4.8
+ * we use "+this" instead of "this" which produces an rvalue that seems
+ * to be deduced as the same type with this particular compiler, and seem
+ * to be fine as default code path as well.
+ */
+#ifdef _MSC_VER
+/* https://github.com/harfbuzz/harfbuzz/issues/1730 */ \
+#define HB_PARTIALIZE(Pos) \
+  template <typename _T> \
+  decltype(auto) operator () (_T&& _v) const \
+  { return hb_partial<Pos> (this, hb_forward<_T> (_v)); } \
+  static_assert (true, "")
+#else
+/* https://github.com/harfbuzz/harfbuzz/issues/1724 */
+#define HB_PARTIALIZE(Pos) \
+  template <typename _T> \
+  auto operator () (_T&& _v) const HB_AUTO_RETURN \
+  (hb_partial<Pos> (+this, hb_forward<_T> (_v))) \
+  static_assert (true, "")
+#endif
+
+
+struct
+{
+  private:
+
+  template <typename Pred, typename Val> auto
+  impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
+  (hb_deref (hb_forward<Pred> (p)).has (hb_forward<Val> (v)))
+
+  template <typename Pred, typename Val> auto
+  impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
+  (
+    hb_invoke (hb_forward<Pred> (p),
+	       hb_forward<Val> (v))
+  )
+
+  public:
+
+  template <typename Pred, typename Val> auto
+  operator () (Pred&& p, Val &&v) const HB_RETURN (bool,
+    impl (hb_forward<Pred> (p),
+	  hb_forward<Val> (v),
+	  hb_prioritize)
+  )
+}
+HB_FUNCOBJ (hb_has);
+
+struct
+{
+  private:
+
+  template <typename Pred, typename Val> auto
+  impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
+  (
+    hb_has (hb_forward<Pred> (p),
+	    hb_forward<Val> (v))
+  )
+
+  template <typename Pred, typename Val> auto
+  impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
+  (
+    hb_forward<Pred> (p) == hb_forward<Val> (v)
+  )
+
+  public:
+
+  template <typename Pred, typename Val> auto
+  operator () (Pred&& p, Val &&v) const HB_RETURN (bool,
+    impl (hb_forward<Pred> (p),
+	  hb_forward<Val> (v),
+	  hb_prioritize)
+  )
+}
+HB_FUNCOBJ (hb_match);
+
+struct
+{
+  private:
+
+  template <typename Proj, typename Val> auto
+  impl (Proj&& f, Val &&v, hb_priority<2>) const HB_AUTO_RETURN
+  (hb_deref (hb_forward<Proj> (f)).get (hb_forward<Val> (v)))
+
+  template <typename Proj, typename Val> auto
+  impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
+  (
+    hb_invoke (hb_forward<Proj> (f),
+	       hb_forward<Val> (v))
+  )
+
+  template <typename Proj, typename Val> auto
+  impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
+  (
+    hb_forward<Proj> (f)[hb_forward<Val> (v)]
+  )
+
+  public:
+
+  template <typename Proj, typename Val> auto
+  operator () (Proj&& f, Val &&v) const HB_AUTO_RETURN
+  (
+    impl (hb_forward<Proj> (f),
+	  hb_forward<Val> (v),
+	  hb_prioritize)
+  )
+}
+HB_FUNCOBJ (hb_get);
+
+
+template <typename T1, typename T2>
+struct hb_pair_t
+{
+  typedef T1 first_t;
+  typedef T2 second_t;
+  typedef hb_pair_t<T1, T2> pair_t;
+
+  hb_pair_t (T1 a, T2 b) : first (a), second (b) {}
+
+  template <typename Q1, typename Q2,
+	    hb_enable_if (hb_is_convertible (T1, Q1) &&
+			  hb_is_convertible (T2, T2))>
+  operator hb_pair_t<Q1, Q2> () { return hb_pair_t<Q1, Q2> (first, second); }
+
+  hb_pair_t<T1, T2> reverse () const
+  { return hb_pair_t<T1, T2> (second, first); }
+
+  bool operator == (const pair_t& o) const { return first == o.first && second == o.second; }
+  bool operator != (const pair_t& o) const { return !(*this == o); }
+  bool operator < (const pair_t& o) const { return first < o.first || (first == o.first && second < o.second); }
+  bool operator >= (const pair_t& o) const { return !(*this < o); }
+  bool operator > (const pair_t& o) const { return first > o.first || (first == o.first && second > o.second); }
+  bool operator <= (const pair_t& o) const { return !(*this > o); }
+
+  T1 first;
+  T2 second;
+};
+#define hb_pair_t(T1,T2) hb_pair_t<T1, T2>
+template <typename T1, typename T2> static inline hb_pair_t<T1, T2>
+hb_pair (T1&& a, T2&& b) { return hb_pair_t<T1, T2> (a, b); }
+
+struct
+{
+  template <typename Pair> constexpr typename Pair::first_t
+  operator () (const Pair& pair) const { return pair.first; }
+}
+HB_FUNCOBJ (hb_first);
+
+struct
+{
+  template <typename Pair> constexpr typename Pair::second_t
+  operator () (const Pair& pair) const { return pair.second; }
+}
+HB_FUNCOBJ (hb_second);
+
+/* Note.  In min/max impl, we can use hb_type_identity<T> for second argument.
+ * However, that would silently convert between different-signedness integers.
+ * Instead we accept two different types, such that compiler can err if
+ * comparing integers of different signedness. */
+struct
+{
+  template <typename T, typename T2> constexpr auto
+  operator () (T&& a, T2&& b) const HB_AUTO_RETURN
+  (hb_forward<T> (a) <= hb_forward<T2> (b) ? hb_forward<T> (a) : hb_forward<T2> (b))
+}
+HB_FUNCOBJ (hb_min);
+struct
+{
+  template <typename T, typename T2> constexpr auto
+  operator () (T&& a, T2&& b) const HB_AUTO_RETURN
+  (hb_forward<T> (a) >= hb_forward<T2> (b) ? hb_forward<T> (a) : hb_forward<T2> (b))
+}
+HB_FUNCOBJ (hb_max);
+
+
+/*
+ * Bithacks.
+ */
+
+/* Return the number of 1 bits in v. */
+template <typename T>
+static inline HB_CONST_FUNC unsigned int
+hb_popcount (T v)
+{
+#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+  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);
+  return 0; /* Shut up stupid compiler. */
+}
+
+/* Returns the number of bits needed to store number */
+template <typename T>
+static inline HB_CONST_FUNC unsigned int
+hb_bit_storage (T v)
+{
+  if (unlikely (!v)) return 0;
+
+#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+  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) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4))
+  if (sizeof (T) <= sizeof (unsigned int))
+  {
+    unsigned long where;
+    _BitScanReverse (&where, v);
+    return 1 + where;
+  }
+# if defined(_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[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL};
+    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);
+  return 0; /* Shut up stupid compiler. */
+}
+
+/* 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 (T v)
+{
+  if (unlikely (!v)) return 0;
+
+#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+  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) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4))
+  if (sizeof (T) <= sizeof (unsigned int))
+  {
+    unsigned long where;
+    _BitScanForward (&where, v);
+    return where;
+  }
+# if defined(_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 & 0x00000000FFFFFFFFULL) c -= 32;
+    if (v & 0x0000FFFF0000FFFFULL) c -= 16;
+    if (v & 0x00FF00FF00FF00FFULL) c -= 8;
+    if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4;
+    if (v & 0x3333333333333333ULL) c -= 2;
+    if (v & 0x5555555555555555ULL) 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);
+  return 0; /* Shut up stupid compiler. */
+}
+
+
+/*
+ * Tiny stuff.
+ */
+
+/* ASCII tag/character handling */
+static inline bool ISALPHA (unsigned char c)
+{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
+static inline bool ISALNUM (unsigned char c)
+{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); }
+static inline bool ISSPACE (unsigned char c)
+{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; }
+static inline unsigned char TOUPPER (unsigned char c)
+{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; }
+static inline unsigned char TOLOWER (unsigned char c)
+{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; }
+
+static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b)
+{ return (a + (b - 1)) / 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])))
+
+
+static inline int
+hb_memcmp (const void *a, const void *b, unsigned int len)
+{
+  /* It's illegal to pass NULL to memcmp(), even if len is zero.
+   * So, wrap it.
+   * https://sourceware.org/bugzilla/show_bug.cgi?id=23878 */
+  if (unlikely (!len)) return 0;
+  return memcmp (a, b, len);
+}
+
+static inline void *
+hb_memset (void *s, int c, unsigned int n)
+{
+  /* It's illegal to pass NULL to memset(), even if n is zero. */
+  if (unlikely (!n)) return 0;
+  return memset (s, c, n);
+}
+
+static inline bool
+hb_unsigned_mul_overflows (unsigned int count, unsigned int size)
+{
+  return (size > 0) && (count >= ((unsigned int) -1) / size);
+}
+
+static inline unsigned int
+hb_ceil_to_4 (unsigned int v)
+{
+  return ((v - 1) | 3) + 1;
+}
+
+template <typename T> static inline bool
+hb_in_range (T u, T lo, T hi)
+{
+  static_assert (!hb_is_signed<T>::value, "");
+
+  /* The casts below are important as if T is smaller than int,
+   * the subtract results will become a signed int! */
+  return (T)(u - lo) <= (T)(hi - lo);
+}
+template <typename T> static inline bool
+hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2)
+{
+  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2);
+}
+template <typename T> static inline bool
+hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
+{
+  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
+}
+
+
+/*
+ * Sort and search.
+ */
+template <typename ...Ts>
+static inline void *
+hb_bsearch (const void *key, const void *base,
+	    size_t nmemb, size_t size,
+	    int (*compar)(const void *_key, const void *_item, Ts... _ds),
+	    Ts... ds)
+{
+  int min = 0, max = (int) nmemb - 1;
+  while (min <= max)
+  {
+    int mid = ((unsigned int) min + (unsigned int) max) / 2;
+    const void *p = (const void *) (((const char *) base) + (mid * size));
+    int c = compar (key, p, ds...);
+    if (c < 0)
+      max = mid - 1;
+    else if (c > 0)
+      min = mid + 1;
+    else
+      return (void *) p;
+  }
+  return nullptr;
+}
+
+
+/* From https://github.com/noporpoise/sort_r
+   Feb 5, 2019 (c8c65c1e)
+   Modified to support optional argument using templates */
+
+/* Isaac Turner 29 April 2014 Public Domain */
+
+/*
+hb_qsort 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 (optional) is a pointer to be passed to the comparison function
+
+void hb_qsort(void *base, size_t nel, size_t width,
+              int (*compar)(const void *_a, const void *_b, [void *_arg]),
+              [void *arg]);
+*/
+
+#define SORT_R_SWAP(a,b,tmp) ((tmp) = (a), (a) = (b), (b) = (tmp))
+
+/* swap a and b */
+/* a and b must not be equal! */
+static inline void sort_r_swap(char *__restrict a, char *__restrict b,
+                               size_t w)
+{
+  char tmp, *end = a+w;
+  for(; a < end; a++, b++) { SORT_R_SWAP(*a, *b, tmp); }
+}
+
+/* swap a, b iff a>b */
+/* a and b must not be equal! */
+/* __restrict is same as restrict but better support on old machines */
+template <typename ...Ts>
+static inline int sort_r_cmpswap(char *__restrict a,
+                                 char *__restrict b, size_t w,
+                                 int (*compar)(const void *_a,
+                                               const void *_b,
+                                               Ts... _ds),
+                                 Ts... ds)
+{
+  if(compar(a, b, ds...) > 0) {
+    sort_r_swap(a, b, w);
+    return 1;
+  }
+  return 0;
+}
+
+/*
+Swap consecutive blocks of bytes of size na and nb starting at memory addr ptr,
+with the smallest swap so that the blocks are in the opposite order. Blocks may
+be internally re-ordered e.g.
+  12345ab  ->   ab34512
+  123abc   ->   abc123
+  12abcde  ->   deabc12
+*/
+static inline void sort_r_swap_blocks(char *ptr, size_t na, size_t nb)
+{
+  if(na > 0 && nb > 0) {
+    if(na > nb) { sort_r_swap(ptr, ptr+na, nb); }
+    else { sort_r_swap(ptr, ptr+nb, na); }
+  }
+}
+
+/* Implement recursive quicksort ourselves */
+/* Note: quicksort is not stable, equivalent values may be swapped */
+template <typename ...Ts>
+static inline void sort_r_simple(void *base, size_t nel, size_t w,
+                                 int (*compar)(const void *_a,
+                                               const void *_b,
+                                               Ts... _ds),
+                                 Ts... ds)
+{
+  char *b = (char *)base, *end = b + nel*w;
+
+  /* for(size_t i=0; i<nel; i++) {printf("%4i", *(int*)(b + i*sizeof(int)));}
+  printf("\n"); */
+
+  if(nel < 10) {
+    /* 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,ds...); pj -= w) {}
+    }
+  }
+  else
+  {
+    /* nel > 9; Quicksort */
+
+    int cmp;
+    char *pl, *ple, *pr, *pre, *pivot;
+    char *last = b+w*(nel-1), *tmp;
+
+    /*
+    Use median of second, middle and second-last items as pivot.
+    First and last may have been swapped with pivot and therefore be extreme
+    */
+    char *l[3];
+    l[0] = b + w;
+    l[1] = b+w*(nel/2);
+    l[2] = last - w;
+
+    /* printf("pivots: %i, %i, %i\n", *(int*)l[0], *(int*)l[1], *(int*)l[2]); */
+
+    if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); }
+    if(compar(l[1],l[2],ds...) > 0) {
+      SORT_R_SWAP(l[1], l[2], tmp);
+      if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); }
+    }
+
+    /* swap mid value (l[1]), and last element to put pivot as last element */
+    if(l[1] != last) { sort_r_swap(l[1], last, w); }
+
+    /*
+    pl is the next item on the left to be compared to the pivot
+    pr is the last item on the right that was compared to the pivot
+    ple is the left position to put the next item that equals the pivot
+    ple is the last right position where we put an item that equals the pivot
+                                           v- end (beyond the array)
+      EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE.
+      ^- b  ^- ple  ^- pl   ^- pr  ^- pre ^- last (where the pivot is)
+    Pivot comparison key:
+      E = equal, L = less than, u = unknown, G = greater than, E = equal
+    */
+    pivot = last;
+    ple = pl = b;
+    pre = pr = last;
+
+    /*
+    Strategy:
+    Loop into the list from the left and right at the same time to find:
+    - an item on the left that is greater than the pivot
+    - an item on the right that is less than the pivot
+    Once found, they are swapped and the loop continues.
+    Meanwhile items that are equal to the pivot are moved to the edges of the
+    array.
+    */
+    while(pl < pr) {
+      /* Move left hand items which are equal to the pivot to the far left.
+         break when we find an item that is greater than the pivot */
+      for(; pl < pr; pl += w) {
+        cmp = compar(pl, pivot, ds...);
+        if(cmp > 0) { break; }
+        else if(cmp == 0) {
+          if(ple < pl) { sort_r_swap(ple, pl, w); }
+          ple += w;
+        }
+      }
+      /* break if last batch of left hand items were equal to pivot */
+      if(pl >= pr) { break; }
+      /* Move right hand items which are equal to the pivot to the far right.
+         break when we find an item that is less than the pivot */
+      for(; pl < pr; ) {
+        pr -= w; /* Move right pointer onto an unprocessed item */
+        cmp = compar(pr, pivot, ds...);
+        if(cmp == 0) {
+          pre -= w;
+          if(pr < pre) { sort_r_swap(pr, pre, w); }
+        }
+        else if(cmp < 0) {
+          if(pl < pr) { sort_r_swap(pl, pr, w); }
+          pl += w;
+          break;
+        }
+      }
+    }
+
+    pl = pr; /* pr may have gone below pl */
+
+    /*
+    Now we need to go from: EEELLLGGGGEEEE
+                        to: LLLEEEEEEEGGGG
+    Pivot comparison key:
+      E = equal, L = less than, u = unknown, G = greater than, E = equal
+    */
+    sort_r_swap_blocks(b, ple-b, pl-ple);
+    sort_r_swap_blocks(pr, pre-pr, end-pre);
+
+    /*for(size_t i=0; i<nel; i++) {printf("%4i", *(int*)(b + i*sizeof(int)));}
+    printf("\n");*/
+
+    sort_r_simple(b, (pl-ple)/w, w, compar, ds...);
+    sort_r_simple(end-(pre-pr), (pre-pr)/w, w, compar, ds...);
+  }
+}
+
+static inline void
+hb_qsort (void *base, size_t nel, size_t width,
+	  int (*compar)(const void *_a, const void *_b))
+{
+#if defined(__OPTIMIZE_SIZE__) && !defined(HB_USE_INTERNAL_QSORT)
+  qsort (base, nel, width, compar);
+#else
+  sort_r_simple (base, nel, width, compar);
+#endif
+}
+
+static inline void
+hb_qsort (void *base, size_t nel, size_t width,
+	  int (*compar)(const void *_a, const void *_b, void *_arg),
+	  void *arg)
+{
+#ifdef HAVE_GNU_QSORT_R
+  qsort_r (base, nel, width, compar, arg);
+#else
+  sort_r_simple (base, nel, width, compar, arg);
+#endif
+}
+
+
+template <typename T, typename T2, typename T3> static inline void
+hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2)
+{
+  for (unsigned int i = 1; i < len; i++)
+  {
+    unsigned int j = i;
+    while (j && compar (&array[j - 1], &array[i]) > 0)
+      j--;
+    if (i == j)
+      continue;
+    /* Move item i to occupy place for item j, shift what's in between. */
+    {
+      T t = array[i];
+      memmove (&array[j + 1], &array[j], (i - j) * sizeof (T));
+      array[j] = t;
+    }
+    if (array2)
+    {
+      T3 t = array2[i];
+      memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T3));
+      array2[j] = t;
+    }
+  }
+}
+
+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 *) nullptr);
+}
+
+static inline hb_bool_t
+hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
+{
+  unsigned int v;
+  const char *p = s;
+  const char *end = p + len;
+  if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, base)))
+    return false;
+
+  *out = v;
+  return true;
+}
+
+
+/* Operators. */
+
+struct hb_bitwise_and
+{ HB_PARTIALIZE(2);
+  static constexpr bool passthru_left = false;
+  static constexpr bool passthru_right = false;
+  template <typename T> constexpr auto
+  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & b)
+}
+HB_FUNCOBJ (hb_bitwise_and);
+struct hb_bitwise_or
+{ HB_PARTIALIZE(2);
+  static constexpr bool passthru_left = true;
+  static constexpr bool passthru_right = true;
+  template <typename T> constexpr auto
+  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | b)
+}
+HB_FUNCOBJ (hb_bitwise_or);
+struct hb_bitwise_xor
+{ HB_PARTIALIZE(2);
+  static constexpr bool passthru_left = true;
+  static constexpr bool passthru_right = true;
+  template <typename T> constexpr auto
+  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a ^ b)
+}
+HB_FUNCOBJ (hb_bitwise_xor);
+struct hb_bitwise_sub
+{ HB_PARTIALIZE(2);
+  static constexpr bool passthru_left = true;
+  static constexpr bool passthru_right = false;
+  template <typename T> constexpr auto
+  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & ~b)
+}
+HB_FUNCOBJ (hb_bitwise_sub);
+struct
+{
+  template <typename T> constexpr auto
+  operator () (const T &a) const HB_AUTO_RETURN (~a)
+}
+HB_FUNCOBJ (hb_bitwise_neg);
+
+struct
+{ HB_PARTIALIZE(2);
+  template <typename T, typename T2> constexpr auto
+  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a + b)
+}
+HB_FUNCOBJ (hb_add);
+struct
+{ HB_PARTIALIZE(2);
+  template <typename T, typename T2> constexpr auto
+  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a - b)
+}
+HB_FUNCOBJ (hb_sub);
+struct
+{ HB_PARTIALIZE(2);
+  template <typename T, typename T2> constexpr auto
+  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a * b)
+}
+HB_FUNCOBJ (hb_mul);
+struct
+{ HB_PARTIALIZE(2);
+  template <typename T, typename T2> constexpr auto
+  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a / b)
+}
+HB_FUNCOBJ (hb_div);
+struct
+{ HB_PARTIALIZE(2);
+  template <typename T, typename T2> constexpr auto
+  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a % b)
+}
+HB_FUNCOBJ (hb_mod);
+struct
+{
+  template <typename T> constexpr auto
+  operator () (const T &a) const HB_AUTO_RETURN (+a)
+}
+HB_FUNCOBJ (hb_pos);
+struct
+{
+  template <typename T> constexpr auto
+  operator () (const T &a) const HB_AUTO_RETURN (-a)
+}
+HB_FUNCOBJ (hb_neg);
+struct
+{
+  template <typename T> constexpr auto
+  operator () (T &a) const HB_AUTO_RETURN (++a)
+}
+HB_FUNCOBJ (hb_inc);
+struct
+{
+  template <typename T> constexpr auto
+  operator () (T &a) const HB_AUTO_RETURN (--a)
+}
+HB_FUNCOBJ (hb_dec);
+
+
+/* Compiler-assisted vectorization. */
+
+/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))),
+ * basically a fixed-size bitset. */
+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]; }
+
+  void clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); }
+
+  template <typename Op>
+  hb_vector_size_t process (const Op& op) const
+  {
+    hb_vector_size_t r;
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      r.v[i] = op (v[i]);
+    return r;
+  }
+  template <typename Op>
+  hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const
+  {
+    hb_vector_size_t r;
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      r.v[i] = op (v[i], o.v[i]);
+    return r;
+  }
+  hb_vector_size_t operator | (const hb_vector_size_t &o) const
+  { return process (hb_bitwise_or, o); }
+  hb_vector_size_t operator & (const hb_vector_size_t &o) const
+  { return process (hb_bitwise_and, o); }
+  hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
+  { return process (hb_bitwise_xor, o); }
+  hb_vector_size_t operator ~ () const
+  { return process (hb_bitwise_neg); }
+
+  private:
+  static_assert (0 == byte_size % sizeof (elt_t), "");
+  elt_t v[byte_size / sizeof (elt_t)];
+};
+
+
+#endif /* HB_ALGS_HH */
diff --git a/src/hb-array.hh b/src/hb-array.hh
index d4df165..d9adf2c 100644
--- a/src/hb-array.hh
+++ b/src/hb-array.hh
@@ -28,90 +28,108 @@
 #define HB_ARRAY_HH
 
 #include "hb.hh"
+#include "hb-algs.hh"
+#include "hb-iter.hh"
+#include "hb-null.hh"
 
 
 template <typename Type>
 struct hb_sorted_array_t;
 
 template <typename Type>
-struct hb_array_t
+struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
 {
-  typedef Type ItemType;
-  enum { item_size = hb_static_size (Type) };
-
   /*
    * Constructors.
    */
-  hb_array_t () : arrayZ (nullptr), len (0) {}
-  hb_array_t (const hb_array_t &o) : arrayZ (o.arrayZ), len (o.len) {}
-  hb_array_t (Type *array_, unsigned int len_) : arrayZ (array_), len (len_) {}
-  template <unsigned int len_> hb_array_t (Type (&array_)[len_]) : arrayZ (array_), len (len_) {}
+  hb_array_t () : arrayZ (nullptr), length (0), backwards_length (0) {}
+  hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_), backwards_length (0) {}
+  template <unsigned int length_>
+  hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_), backwards_length (0) {}
+
+  template <typename U,
+	    hb_enable_if (hb_is_cr_convertible(U, Type))>
+  hb_array_t (const hb_array_t<U> &o) :
+    hb_iter_with_fallback_t<hb_array_t, Type&> (),
+    arrayZ (o.arrayZ), length (o.length), backwards_length (o.backwards_length) {}
+  template <typename U,
+	    hb_enable_if (hb_is_cr_convertible(U, Type))>
+  hb_array_t& operator = (const hb_array_t<U> &o)
+  { arrayZ = o.arrayZ; length = o.length; backwards_length = o.backwards_length; return *this; }
 
   /*
-   * Operators.
+   * Iterator implementation.
    */
-
-  Type& operator [] (int i_) const
+  typedef Type& __item_t__;
+  static constexpr bool is_random_access_iterator = true;
+  Type& __item_at__ (unsigned i) const
   {
-    unsigned int i = (unsigned int) i_;
-    if (unlikely (i >= len)) return CrapOrNull(Type);
+    if (unlikely (i >= length)) return CrapOrNull (Type);
     return arrayZ[i];
   }
+  void __forward__ (unsigned n)
+  {
+    if (unlikely (n > length))
+      n = length;
+    length -= n;
+    backwards_length += n;
+    arrayZ += n;
+  }
+  void __rewind__ (unsigned n)
+  {
+    if (unlikely (n > backwards_length))
+      n = backwards_length;
+    length += n;
+    backwards_length -= n;
+    arrayZ -= n;
+  }
+  unsigned __len__ () const { return length; }
+  /* Ouch. The operator== compares the contents of the array.  For range-based for loops,
+   * it's best if we can just compare arrayZ, though comparing contents is still fast,
+   * but also would require that Type has operator==.  As such, we optimize this operator
+   * for range-based for loop and just compare arrayZ.  No need to compare length, as we
+   * assume we're only compared to .end(). */
+  bool operator != (const hb_array_t& o) const
+  { return arrayZ != o.arrayZ; }
 
-  explicit_operator bool () const { return len; }
+  /* Extra operators.
+   */
   Type * operator & () const { return arrayZ; }
-  Type & operator * () { return (this->operator [])[0]; }
-  operator hb_array_t<const Type> () { return hb_array_t<const Type> (arrayZ, len); }
+  operator hb_array_t<const Type> () { return hb_array_t<const Type> (arrayZ, length); }
   template <typename T> operator T * () const { return arrayZ; }
 
-  hb_array_t<Type> & operator += (unsigned int count)
-  {
-    if (unlikely (count > len))
-      count = len;
-    len -= count;
-    arrayZ += count;
-    return *this;
+  HB_INTERNAL bool operator == (const hb_array_t &o) const;
+
+  uint32_t hash () const {
+    uint32_t current = 0;
+    for (unsigned int i = 0; i < this->length; i++) {
+      current = current * 31 + hb_hash (this->arrayZ[i]);
+    }
+    return current;
   }
-  hb_array_t<Type> & operator -= (unsigned int count)
-  {
-    if (unlikely (count > len))
-      count = len;
-    len -= count;
-    return *this;
-  }
-  hb_array_t<Type> & operator ++ () { *this += 1; }
-  hb_array_t<Type> & operator -- () { *this -= 1; }
-  hb_array_t<Type> operator + (unsigned int count)
-  { hb_array_t<Type> copy (*this); *this += count; return copy; }
-  hb_array_t<Type> operator - (unsigned int count)
-  { hb_array_t<Type> copy (*this); *this -= count; return copy; }
-  hb_array_t<Type>  operator ++ (int)
-  { hb_array_t<Type> copy (*this); ++*this; return copy; }
-  hb_array_t<Type>  operator -- (int)
-  { hb_array_t<Type> copy (*this); --*this; return copy; }
 
   /*
    * Compare, Sort, and Search.
    */
 
   /* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */
-  int cmp (const hb_array_t<Type> &a) const
+  int cmp (const hb_array_t &a) const
   {
-    if (len != a.len)
-      return (int) a.len - (int) len;
+    if (length != a.length)
+      return (int) a.length - (int) length;
     return hb_memcmp (a.arrayZ, arrayZ, get_size ());
   }
-  static int cmp (const void *pa, const void *pb)
+  HB_INTERNAL static int cmp (const void *pa, const void *pb)
   {
-    hb_array_t<Type> *a = (hb_array_t<Type> *) pa;
-    hb_array_t<Type> *b = (hb_array_t<Type> *) pb;
+    hb_array_t *a = (hb_array_t *) pa;
+    hb_array_t *b = (hb_array_t *) pb;
     return b->cmp (*a);
   }
 
   template <typename T>
   Type *lsearch (const T &x, Type *not_found = nullptr)
   {
-    unsigned int count = len;
+    unsigned int count = length;
     for (unsigned int i = 0; i < count; i++)
       if (!this->arrayZ[i].cmp (x))
 	return &this->arrayZ[i];
@@ -120,7 +138,7 @@
   template <typename T>
   const Type *lsearch (const T &x, const Type *not_found = nullptr) const
   {
-    unsigned int count = len;
+    unsigned int count = length;
     for (unsigned int i = 0; i < count; i++)
       if (!this->arrayZ[i].cmp (x))
 	return &this->arrayZ[i];
@@ -129,51 +147,82 @@
 
   hb_sorted_array_t<Type> qsort (int (*cmp_)(const void*, const void*))
   {
-    ::qsort (arrayZ, len, item_size, cmp_);
+    if (likely (length))
+      hb_qsort (arrayZ, length, this->get_item_size (), cmp_);
     return hb_sorted_array_t<Type> (*this);
   }
   hb_sorted_array_t<Type> qsort ()
   {
-    ::qsort (arrayZ, len, item_size, Type::cmp);
+    if (likely (length))
+      hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp);
     return hb_sorted_array_t<Type> (*this);
   }
   void qsort (unsigned int start, unsigned int end)
   {
-    end = MIN (end, len);
+    end = hb_min (end, length);
     assert (start <= end);
-    ::qsort (arrayZ + start, end - start, item_size, Type::cmp);
+    if (likely (start < end))
+      hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp);
   }
 
   /*
    * Other methods.
    */
 
-  unsigned int get_size () const { return len * item_size; }
+  unsigned int get_size () const { return length * this->get_item_size (); }
 
-  hb_array_t<Type> sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
+  hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
   {
     if (!start_offset && !seg_count)
       return *this;
 
-    unsigned int count = len;
+    unsigned int count = length;
     if (unlikely (start_offset > count))
       count = 0;
     else
       count -= start_offset;
     if (seg_count)
-      count = *seg_count = MIN (count, *seg_count);
-    return hb_array_t<Type> (arrayZ + start_offset, count);
+      count = *seg_count = hb_min (count, *seg_count);
+    return hb_array_t (arrayZ + start_offset, count);
   }
-  hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const
+  hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
   { return sub_array (start_offset, &seg_count); }
 
+  hb_array_t truncate (unsigned length) const { return sub_array (0, length); }
+
+  template <typename T,
+	    unsigned P = sizeof (Type),
+	    hb_enable_if (P == 1)>
+  const T *as () const
+  { return length < hb_null_size (T) ? &Null (T) : reinterpret_cast<const T *> (arrayZ); }
+
+  template <typename T,
+	    unsigned P = sizeof (Type),
+	    hb_enable_if (P == 1)>
+  bool in_range (const T *p, unsigned int size = T::static_size) const
+  {
+    return ((const char *) p) >= arrayZ
+	&& ((const char *) p + size) <= arrayZ + length;
+  }
+
   /* Only call if you allocated the underlying array using malloc() or similar. */
   void free ()
-  { ::free ((void *) arrayZ); arrayZ = nullptr; len = 0; }
+  { ::free ((void *) arrayZ); arrayZ = nullptr; length = 0; }
+
+  template <typename hb_serialize_context_t>
+  hb_array_t copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    auto* out = c->start_embed (arrayZ);
+    if (unlikely (!c->extend_size (out, get_size ()))) return_trace (hb_array_t ());
+    for (unsigned i = 0; i < length; i++)
+      out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */
+    return_trace (hb_array_t (out, length));
+  }
 
   template <typename hb_sanitize_context_t>
   bool sanitize (hb_sanitize_context_t *c) const
-  { return c->check_array (arrayZ, len); }
+  { return c->check_array (arrayZ, length); }
 
   /*
    * Members
@@ -181,12 +230,15 @@
 
   public:
   Type *arrayZ;
-  unsigned int len;
+  unsigned int length;
+  unsigned int backwards_length;
 };
-template <typename T>
-inline hb_array_t<T> hb_array (T *array, unsigned int len)
-{ return hb_array_t<T> (array, len); }
-
+template <typename T> inline hb_array_t<T>
+hb_array (T *array, unsigned int length)
+{ return hb_array_t<T> (array, length); }
+template <typename T, unsigned int length_> inline hb_array_t<T>
+hb_array (T (&array_)[length_])
+{ return hb_array_t<T> (array_); }
 
 enum hb_bfind_not_found_t
 {
@@ -196,17 +248,41 @@
 };
 
 template <typename Type>
-struct hb_sorted_array_t : hb_array_t<Type>
+struct hb_sorted_array_t :
+	hb_iter_t<hb_sorted_array_t<Type>, Type&>,
+	hb_array_t<Type>
 {
-  hb_sorted_array_t () : hb_array_t<Type> () {}
-  hb_sorted_array_t (const hb_array_t<Type> &o) : hb_array_t<Type> (o) {}
-  hb_sorted_array_t (Type *array_, unsigned int len_) : hb_array_t<Type> (array_, len_) {}
+  typedef hb_iter_t<hb_sorted_array_t, Type&> iter_base_t;
+  HB_ITER_USING (iter_base_t);
+  static constexpr bool is_random_access_iterator = true;
+  static constexpr bool is_sorted_iterator = true;
 
-  hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
-  { return hb_sorted_array_t<Type> (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); }
-  hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const
+  hb_sorted_array_t () : hb_array_t<Type> () {}
+  hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t<Type> (array_, length_) {}
+  template <unsigned int length_>
+  hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {}
+
+  template <typename U,
+	    hb_enable_if (hb_is_cr_convertible(U, Type))>
+  hb_sorted_array_t (const hb_array_t<U> &o) :
+    hb_iter_t<hb_sorted_array_t, Type&> (),
+    hb_array_t<Type> (o) {}
+  template <typename U,
+	    hb_enable_if (hb_is_cr_convertible(U, Type))>
+  hb_sorted_array_t& operator = (const hb_array_t<U> &o)
+  { hb_array_t<Type> (*this) = o; return *this; }
+
+  /* Iterator implementation. */
+  bool operator != (const hb_sorted_array_t& o) const
+  { return this->arrayZ != o.arrayZ || this->length != o.length; }
+
+  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
+  { return hb_sorted_array_t (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); }
+  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
   { return sub_array (start_offset, &seg_count); }
 
+  hb_sorted_array_t truncate (unsigned length) const { return sub_array (0, length); }
+
   template <typename T>
   Type *bsearch (const T &x, Type *not_found = nullptr)
   {
@@ -221,19 +297,19 @@
   }
   template <typename T>
   bool bfind (const T &x, unsigned int *i = nullptr,
-		     hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
-		     unsigned int to_store = (unsigned int) -1) const
+	      hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
+	      unsigned int to_store = (unsigned int) -1) const
   {
-    int min = 0, max = (int) this->len - 1;
+    int min = 0, max = (int) this->length - 1;
     const Type *array = this->arrayZ;
     while (min <= max)
     {
       int mid = ((unsigned int) min + (unsigned int) max) / 2;
       int c = array[mid].cmp (x);
       if (c < 0)
-        max = mid - 1;
+	max = mid - 1;
       else if (c > 0)
-        min = mid + 1;
+	min = mid + 1;
       else
       {
 	if (i)
@@ -253,7 +329,7 @@
 	  break;
 
 	case HB_BFIND_NOT_FOUND_STORE_CLOSEST:
-	  if (max < 0 || (max < (int) this->len && array[max].cmp (x) > 0))
+	  if (max < 0 || (max < (int) this->length && array[max].cmp (x) > 0))
 	    max++;
 	  *i = max;
 	  break;
@@ -262,13 +338,45 @@
     return false;
   }
 };
+template <typename T> inline hb_sorted_array_t<T>
+hb_sorted_array (T *array, unsigned int length)
+{ return hb_sorted_array_t<T> (array, length); }
+template <typename T, unsigned int length_> inline hb_sorted_array_t<T>
+hb_sorted_array (T (&array_)[length_])
+{ return hb_sorted_array_t<T> (array_); }
+
 template <typename T>
-inline hb_sorted_array_t<T> hb_sorted_array (T *array, unsigned int len)
-{ return hb_sorted_array_t<T> (array, len); }
+bool hb_array_t<T>::operator == (const hb_array_t<T> &o) const
+{
+  if (o.length != this->length) return false;
+  for (unsigned int i = 0; i < this->length; i++) {
+    if (this->arrayZ[i] != o.arrayZ[i]) return false;
+  }
+  return true;
+}
+
+/* TODO Specialize opeator== for hb_bytes_t and hb_ubytes_t. */
+
+template <>
+inline uint32_t hb_array_t<const char>::hash () const {
+  uint32_t current = 0;
+  for (unsigned int i = 0; i < this->length; i++)
+    current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
+  return current;
+}
+
+template <>
+inline uint32_t hb_array_t<const unsigned char>::hash () const {
+  uint32_t current = 0;
+  for (unsigned int i = 0; i < this->length; i++)
+    current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
+  return current;
+}
 
 
 typedef hb_array_t<const char> hb_bytes_t;
 typedef hb_array_t<const unsigned char> hb_ubytes_t;
 
 
+
 #endif /* HB_ARRAY_HH */
diff --git a/src/hb-atomic.hh b/src/hb-atomic.hh
index 9321932..b3fb296 100644
--- a/src/hb-atomic.hh
+++ b/src/hb-atomic.hh
@@ -33,6 +33,7 @@
 #define HB_ATOMIC_HH
 
 #include "hb.hh"
+#include "hb-meta.hh"
 
 
 /*
@@ -85,11 +86,11 @@
 #define hb_atomic_int_impl_add(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
 #define hb_atomic_int_impl_set_relaxed(AI, V)	(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_relaxed))
 #define hb_atomic_int_impl_set(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_release))
-#define hb_atomic_int_impl_get_relaxed(AI)	(reinterpret_cast<std::atomic<int> *> (AI)->load (std::memory_order_relaxed))
-#define hb_atomic_int_impl_get(AI)		(reinterpret_cast<std::atomic<int> *> (AI)->load (std::memory_order_acquire))
+#define hb_atomic_int_impl_get_relaxed(AI)	(reinterpret_cast<std::atomic<int> const *> (AI)->load (std::memory_order_relaxed))
+#define hb_atomic_int_impl_get(AI)		(reinterpret_cast<std::atomic<int> const *> (AI)->load (std::memory_order_acquire))
 
 #define hb_atomic_ptr_impl_set_relaxed(P, V)	(reinterpret_cast<std::atomic<void*> *> (P)->store ((V), std::memory_order_relaxed))
-#define hb_atomic_ptr_impl_get_relaxed(P)	(reinterpret_cast<std::atomic<void*> *> (P)->load (std::memory_order_relaxed))
+#define hb_atomic_ptr_impl_get_relaxed(P)	(reinterpret_cast<std::atomic<void*> const *> (P)->load (std::memory_order_relaxed))
 #define hb_atomic_ptr_impl_get(P)		(reinterpret_cast<std::atomic<void*> *> (P)->load (std::memory_order_acquire))
 static inline bool
 _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
@@ -106,7 +107,7 @@
 
 static inline void _hb_memory_barrier ()
 {
-#if !defined(MemoryBarrier)
+#if !defined(MemoryBarrier) && !defined(__MINGW32_VERSION)
   /* MinGW has a convoluted history of supporting MemoryBarrier. */
   LONG dummy = 0;
   InterlockedExchange (&dummy, 1);
@@ -184,7 +185,7 @@
 #endif
 
 
-#elif !defined(HB_NO_MT) && defined(_AIX) && defined(__IBMCPP__)
+#elif !defined(HB_NO_MT) && defined(_AIX) && (defined(__IBMCPP__) || defined(__ibmxl__))
 
 #include <builtins.h>
 
@@ -211,25 +212,19 @@
 static_assert ((sizeof (long) == sizeof (void *)), "");
 
 
-#elif !defined(HB_NO_MT)
-
-#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
-
-#define _hb_memory_barrier()
+#elif defined(HB_NO_MT)
 
 #define hb_atomic_int_impl_add(AI, V)		((*(AI) += (V)) - (V))
 
+#define _hb_memory_barrier()			do {} while (0)
+
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	(* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
 
 
-#else /* HB_NO_MT */
+#else
 
-#define hb_atomic_int_impl_add(AI, V)		((*(AI) += (V)) - (V))
-
-#define _hb_memory_barrier()
-
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)	(* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
-
+#error "Could not find any system to define atomic_int macros."
+#error "Check hb-atomic.hh for possible resolutions."
 
 #endif
 
@@ -282,7 +277,7 @@
 template <typename P>
 struct hb_atomic_ptr_t
 {
-  typedef typename hb_remove_pointer (P) T;
+  typedef hb_remove_pointer<P> T;
 
   void init (T* v_ = nullptr) { set_relaxed (v_); }
   void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); }
diff --git a/src/hb-bimap.hh b/src/hb-bimap.hh
new file mode 100644
index 0000000..cae0a4d
--- /dev/null
+++ b/src/hb-bimap.hh
@@ -0,0 +1,166 @@
+/*
+ * Copyright © 2019 Adobe Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+
+#ifndef HB_BIMAP_HH
+#define HB_BIMAP_HH
+
+#include "hb.hh"
+#include "hb-map.hh"
+
+/* Bi-directional map */
+struct hb_bimap_t
+{
+  hb_bimap_t () { init (); }
+  ~hb_bimap_t () { fini (); }
+
+  void init ()
+  {
+    forw_map.init ();
+    back_map.init ();
+  }
+
+  void fini ()
+  {
+    forw_map.fini ();
+    back_map.fini ();
+  }
+
+  void reset ()
+  {
+    forw_map.reset ();
+    back_map.reset ();
+  }
+
+  bool in_error () const { return forw_map.in_error () || back_map.in_error (); }
+
+  void set (hb_codepoint_t lhs, hb_codepoint_t rhs)
+  {
+    if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return;
+    if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; }
+    forw_map.set (lhs, rhs);
+    back_map.set (rhs, lhs);
+  }
+
+  hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); }
+  hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map.get (rhs); }
+
+  hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); }
+  bool has (hb_codepoint_t lhs, hb_codepoint_t *vp = nullptr) const { return forw_map.has (lhs, vp); }
+
+  void del (hb_codepoint_t lhs)
+  {
+    back_map.del (get (lhs));
+    forw_map.del (lhs);
+  }
+
+  void clear ()
+  {
+    forw_map.clear ();
+    back_map.clear ();
+  }
+
+  bool is_empty () const { return get_population () == 0; }
+
+  unsigned int get_population () const { return forw_map.get_population (); }
+
+  protected:
+  hb_map_t  forw_map;
+  hb_map_t  back_map;
+};
+
+/* Inremental bimap: only lhs is given, rhs is incrementally assigned */
+struct hb_inc_bimap_t : hb_bimap_t
+{
+  hb_inc_bimap_t () { init (); }
+
+  void init ()
+  {
+    hb_bimap_t::init ();
+    next_value = 0;
+  }
+
+  /* Add a mapping from lhs to rhs with a unique value if lhs is unknown.
+   * Return the rhs value as the result.
+   */
+  hb_codepoint_t add (hb_codepoint_t lhs)
+  {
+    hb_codepoint_t  rhs = forw_map[lhs];
+    if (rhs == HB_MAP_VALUE_INVALID)
+    {
+      rhs = next_value++;
+      set (lhs, rhs);
+    }
+    return rhs;
+  }
+
+  hb_codepoint_t skip ()
+  { return next_value++; }
+
+  hb_codepoint_t get_next_value () const
+  { return next_value; }
+
+  void add_set (const hb_set_t *set)
+  {
+    hb_codepoint_t i = HB_SET_VALUE_INVALID;
+    while (hb_set_next (set, &i)) add (i);
+  }
+
+  /* Create an identity map. */
+  bool identity (unsigned int size)
+  {
+    clear ();
+    for (hb_codepoint_t i = 0; i < size; i++) set (i, i);
+    return !in_error ();
+  }
+
+  protected:
+  static int cmp_id (const void* a, const void* b)
+  { return (int)*(const hb_codepoint_t *)a - (int)*(const hb_codepoint_t *)b; }
+
+  public:
+  /* Optional: after finished adding all mappings in a random order,
+   * reassign rhs to lhs so that they are in the same order. */
+  void sort ()
+  {
+    hb_codepoint_t  count = get_population ();
+    hb_vector_t <hb_codepoint_t> work;
+    work.resize (count);
+
+    for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
+      work[rhs] = back_map[rhs];
+  
+    work.qsort (cmp_id);
+  
+    clear ();
+    for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
+      set (work[rhs], rhs);
+  }
+
+  protected:
+  unsigned int	next_value;
+};
+
+#endif /* HB_BIMAP_HH */
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index b53b20b..2e72683 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -25,6 +25,18 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
+
+/* https://github.com/harfbuzz/harfbuzz/issues/1308
+ * http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html
+ * https://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html
+ */
+#if !defined(_POSIX_C_SOURCE) && !defined(_MSC_VER) && !defined(__NetBSD__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-macros"
+#define _POSIX_C_SOURCE 200809L
+#pragma GCC diagnostic pop
+#endif
+
 #include "hb.hh"
 #include "hb-blob.hh"
 
@@ -36,7 +48,6 @@
 #endif /* HAVE_SYS_MMAN_H */
 
 #include <stdio.h>
-#include <errno.h>
 #include <stdlib.h>
 
 
@@ -143,7 +154,7 @@
   hb_blob_make_immutable (parent);
 
   blob = hb_blob_create (parent->data + offset,
-			 MIN (length, parent->length - offset),
+			 hb_min (length, parent->length - offset),
 			 HB_MEMORY_MODE_READONLY,
 			 hb_blob_reference (parent),
 			 _hb_blob_destroy);
@@ -475,6 +486,7 @@
  * Mmap
  */
 
+#ifndef HB_NO_OPEN
 #ifdef HAVE_MMAP
 # include <sys/types.h>
 # include <sys/stat.h>
@@ -579,7 +591,7 @@
     ceparams.lpSecurityAttributes = nullptr;
     ceparams.hTemplateFile = nullptr;
     fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ,
-                      OPEN_EXISTING, &ceparams);
+		      OPEN_EXISTING, &ceparams);
   }
 #else
   fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr,
@@ -656,7 +668,7 @@
   }
 
   return hb_blob_create (data, len, HB_MEMORY_MODE_WRITABLE, data,
-                         (hb_destroy_func_t) free);
+			 (hb_destroy_func_t) free);
 
 fread_fail:
   fclose (fp);
@@ -664,3 +676,4 @@
   free (data);
   return hb_blob_get_empty ();
 }
+#endif /* !HB_NO_OPEN */
diff --git a/src/hb-blob.h b/src/hb-blob.h
index d1d9134..f80e9af 100644
--- a/src/hb-blob.h
+++ b/src/hb-blob.h
@@ -71,6 +71,9 @@
 		void              *user_data,
 		hb_destroy_func_t  destroy);
 
+HB_EXTERN hb_blob_t *
+hb_blob_create_from_file (const char *file_name);
+
 /* Always creates with MEMORY_MODE_READONLY.
  * Even if the parent blob is writable, we don't
  * want the user of the sub-blob to be able to
@@ -123,9 +126,6 @@
 HB_EXTERN char *
 hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length);
 
-HB_EXTERN hb_blob_t *
-hb_blob_create_from_file (const char *file_name);
-
 HB_END_DECLS
 
 #endif /* HB_BLOB_H */
diff --git a/src/hb-blob.hh b/src/hb-blob.hh
index 4ea13f8..d85bd82 100644
--- a/src/hb-blob.hh
+++ b/src/hb-blob.hh
@@ -54,13 +54,9 @@
   HB_INTERNAL bool try_make_writable_inplace ();
   HB_INTERNAL bool try_make_writable_inplace_unix ();
 
+  hb_bytes_t as_bytes () const { return hb_bytes_t (data, length); }
   template <typename Type>
-  const Type* as () const
-  {
-    return length < hb_null_size (Type) ? &Null(Type) : reinterpret_cast<const Type *> (data);
-  }
-  hb_bytes_t as_bytes () const
-  { return hb_bytes_t (data, length); }
+  const Type* as () const { return as_bytes ().as<Type> (); }
 
   public:
   hb_object_header_t header;
@@ -81,7 +77,7 @@
 template <typename P>
 struct hb_blob_ptr_t
 {
-  typedef typename hb_remove_pointer (P) T;
+  typedef hb_remove_pointer<P> T;
 
   hb_blob_ptr_t (hb_blob_t *b_ = nullptr) : b (b_) {}
   hb_blob_t * operator = (hb_blob_t *b_) { return b = b_; }
diff --git a/src/hb-buffer-serialize.cc b/src/hb-buffer-serialize.cc
index 6e265e8..e64eb0e 100644
--- a/src/hb-buffer-serialize.cc
+++ b/src/hb-buffer-serialize.cc
@@ -24,6 +24,10 @@
  * Google Author(s): Behdad Esfahbod
  */
 
+#include "hb.hh"
+
+#ifndef HB_NO_BUFFER_SERIALIZE
+
 #include "hb-buffer.hh"
 
 
@@ -85,7 +89,7 @@
 const char *
 hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format)
 {
-  switch (format)
+  switch ((unsigned) format)
   {
     case HB_BUFFER_SERIALIZE_FORMAT_TEXT:	return serialize_formats[0];
     case HB_BUFFER_SERIALIZE_FORMAT_JSON:	return serialize_formats[1];
@@ -131,41 +135,41 @@
       hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g));
       *p++ = '"';
       for (char *q = g; *q; q++) {
-        if (*q == '"')
+	if (*q == '"')
 	  *p++ = '\\';
 	*p++ = *q;
       }
       *p++ = '"';
     }
     else
-      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
+      p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
-      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
+      p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
     }
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
-      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
+      p += hb_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",
+	p += hb_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));
+	p += hb_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)
     {
       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",
+      p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d",
 		extents.x_bearing, extents.y_bearing));
-      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d",
+      p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d",
 		extents.width, extents.height));
     }
 
@@ -224,37 +228,37 @@
       p += strlen (p);
     }
     else
-      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
+      p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
-      p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster));
+      p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster));
     }
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
       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 += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset));
 
       if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
       {
 	*p++ = '+';
-	p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
+	p += hb_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));
+	  p += hb_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));
+	p += hb_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)
     {
       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), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height));
+      p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height));
     }
 
     unsigned int l = p - b;
@@ -375,43 +379,24 @@
   }
 }
 
-
-static hb_bool_t
-parse_uint (const char *pp, const char *end, uint32_t *pv)
+static bool
+parse_int (const char *pp, const char *end, int32_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;
-  uint32_t v;
-
-  errno = 0;
-  v = strtol (p, &pend, 10);
-  if (errno || p == pend || pend - p != end - pp)
+  int v;
+  const char *p = pp;
+  if (unlikely (!hb_parse_int (&p, end, &v, true/* whole buffer */)))
     return false;
 
   *pv = v;
   return true;
 }
 
-static hb_bool_t
-parse_int (const char *pp, const char *end, int32_t *pv)
+static bool
+parse_uint (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;
-  int32_t v;
-
-  errno = 0;
-  v = strtol (p, &pend, 10);
-  if (errno || p == pend || pend - p != end - pp)
+  unsigned int v;
+  const char *p = pp;
+  if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */)))
     return false;
 
   *pv = v;
@@ -484,3 +469,6 @@
 
   }
 }
+
+
+#endif
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index 2dc02e9..6131c86 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -324,7 +324,7 @@
   out_len = 0;
   out_info = info;
 
-  memset (pos, 0, sizeof (pos[0]) * len);
+  hb_memset (pos, 0, sizeof (pos[0]) * len);
 }
 
 void
@@ -524,7 +524,7 @@
   unsigned int cluster = info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
-    cluster = MIN<unsigned int> (cluster, info[i].cluster);
+    cluster = hb_min (cluster, info[i].cluster);
 
   /* Extend end */
   while (end < len && info[end - 1].cluster == info[end].cluster)
@@ -555,7 +555,7 @@
   unsigned int cluster = out_info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
-    cluster = MIN<unsigned int> (cluster, out_info[i].cluster);
+    cluster = hb_min (cluster, out_info[i].cluster);
 
   /* Extend start */
   while (start && out_info[start - 1].cluster == out_info[start].cluster)
@@ -648,8 +648,8 @@
       if (likely (script != HB_SCRIPT_COMMON &&
 		  script != HB_SCRIPT_INHERITED &&
 		  script != HB_SCRIPT_UNKNOWN)) {
-        props.script = script;
-        break;
+	props.script = script;
+	break;
       }
     }
   }
@@ -776,8 +776,10 @@
 
   free (buffer->info);
   free (buffer->pos);
+#ifndef HB_NO_BUFFER_MESSAGE
   if (buffer->message_destroy)
     buffer->message_destroy (buffer->message_data);
+#endif
 
   free (buffer);
 }
@@ -1388,7 +1390,7 @@
  **/
 hb_glyph_info_t *
 hb_buffer_get_glyph_infos (hb_buffer_t  *buffer,
-                           unsigned int *length)
+			   unsigned int *length)
 {
   if (length)
     *length = buffer->len;
@@ -1412,7 +1414,7 @@
  **/
 hb_glyph_position_t *
 hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
-                               unsigned int *length)
+			       unsigned int *length)
 {
   if (!buffer->have_positions)
     buffer->clear_positions ();
@@ -1936,9 +1938,9 @@
     for (i = 0; i < count; i++)
     {
       if (contains && info[i].codepoint == dottedcircle_glyph)
-        result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
+	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_NOTDEF_PRESENT;
     }
     result |= HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH;
     return hb_buffer_diff_flags_t (result);
@@ -1973,12 +1975,12 @@
     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)
+	  (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;
+	result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH;
+	break;
       }
       buf_pos++;
       ref_pos++;
@@ -1993,6 +1995,7 @@
  * Debugging.
  */
 
+#ifndef HB_NO_BUFFER_MESSAGE
 /**
  * hb_buffer_set_message_func:
  * @buffer: an #hb_buffer_t.
@@ -2022,11 +2025,11 @@
     buffer->message_destroy = nullptr;
   }
 }
-
 bool
 hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap)
 {
   char buf[100];
-  vsnprintf (buf, sizeof (buf),  fmt, ap);
+  vsnprintf (buf, sizeof (buf), fmt, ap);
   return (bool) this->message_func (this, font, buf, this->message_data);
 }
+#endif
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index f989d25..d5cb746 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -284,6 +284,10 @@
  *                      space glyph and zeroing the advance width.)
  *                      @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES takes
  *                      precedence over this flag. Since: 1.8.0
+ * @HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE:
+ *                      flag indicating that a dotted circle should
+ *                      not be inserted in the rendering of incorrect
+ *                      character sequences (such at <0905 093E>). Since: 2.4
  *
  * Since: 0.9.20
  */
@@ -292,7 +296,8 @@
   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_REMOVE_DEFAULT_IGNORABLES	= 0x00000008u
+  HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES	= 0x00000008u,
+  HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE	= 0x00000010u
 } hb_buffer_flags_t;
 
 HB_EXTERN void
@@ -436,11 +441,11 @@
 
 HB_EXTERN hb_glyph_info_t *
 hb_buffer_get_glyph_infos (hb_buffer_t  *buffer,
-                           unsigned int *length);
+			   unsigned int *length);
 
 HB_EXTERN hb_glyph_position_t *
 hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
-                               unsigned int *length);
+			       unsigned int *length);
 
 
 HB_EXTERN void
diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh
index 8a3170c..b5596d9 100644
--- a/src/hb-buffer.hh
+++ b/src/hb-buffer.hh
@@ -119,14 +119,16 @@
   /* Text before / after the main buffer contents.
    * Always in Unicode, and ordered outward.
    * Index 0 is for "pre-context", 1 for "post-context". */
-  enum { CONTEXT_LENGTH = 5 };
+  static constexpr unsigned CONTEXT_LENGTH = 5u;
   hb_codepoint_t context[2][CONTEXT_LENGTH];
   unsigned int context_len[2];
 
   /* Debugging API */
+#ifndef HB_NO_BUFFER_MESSAGE
   hb_buffer_message_func_t message_func;
   void *message_data;
   hb_destroy_func_t message_destroy;
+#endif
 
   /* Internal debugging. */
   /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */
@@ -347,9 +349,19 @@
 
   HB_INTERNAL void sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *));
 
-  bool messaging () { return unlikely (message_func); }
+  bool messaging ()
+  {
+#ifdef HB_NO_BUFFER_MESSAGE
+    return false;
+#else
+    return unlikely (message_func);
+#endif
+  }
   bool message (hb_font_t *font, const char *fmt, ...) HB_PRINTF_FUNC(3, 4)
   {
+#ifdef HB_NO_BUFFER_MESSAGE
+   return true;
+#else
     if (!messaging ())
       return true;
     va_list ap;
@@ -357,6 +369,7 @@
     bool ret = message_impl (font, fmt, ap);
     va_end (ap);
     return ret;
+#endif
   }
   HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0);
 
@@ -379,7 +392,7 @@
 				     unsigned int cluster) const
   {
     for (unsigned int i = start; i < end; i++)
-      cluster = MIN<unsigned int> (cluster, infos[i].cluster);
+      cluster = hb_min (cluster, infos[i].cluster);
     return cluster;
   }
   void
@@ -395,8 +408,7 @@
       }
   }
 
-  void unsafe_to_break_all ()
-  { unsafe_to_break_impl (0, len); }
+  void unsafe_to_break_all () { unsafe_to_break_impl (0, len); }
   void safe_to_break_all ()
   {
     for (unsigned int i = 0; i < len; i++)
diff --git a/src/hb-cff-interp-common.hh b/src/hb-cff-interp-common.hh
index c2be034..780f618 100644
--- a/src/hb-cff-interp-common.hh
+++ b/src/hb-cff-interp-common.hh
@@ -30,7 +30,7 @@
 
 using namespace OT;
 
-typedef unsigned int OpCode;
+typedef unsigned int op_code_t;
 
 
 /* === Dict operators === */
@@ -88,11 +88,11 @@
 
 /* Two byte escape operators 12, (0-41) */
 #define OpCode_ESC_Base		256
-#define Make_OpCode_ESC(byte2)	((OpCode)(OpCode_ESC_Base + (byte2)))
+#define Make_OpCode_ESC(byte2)	((op_code_t)(OpCode_ESC_Base + (byte2)))
 
-inline OpCode Unmake_OpCode_ESC (OpCode op)  { return (OpCode)(op - OpCode_ESC_Base); }
-inline bool Is_OpCode_ESC (OpCode op) { return op >= OpCode_ESC_Base; }
-inline unsigned int OpCode_Size (OpCode op) { return Is_OpCode_ESC (op) ? 2: 1; }
+inline op_code_t Unmake_OpCode_ESC (op_code_t op)  { return (op_code_t)(op - OpCode_ESC_Base); }
+inline bool Is_OpCode_ESC (op_code_t op) { return op >= OpCode_ESC_Base; }
+inline unsigned int OpCode_Size (op_code_t op) { return Is_OpCode_ESC (op) ? 2: 1; }
 
 #define OpCode_Copyright	Make_OpCode_ESC(0) /* CFF Top */
 #define OpCode_isFixedPitch	Make_OpCode_ESC(1) /* CFF Top (false) */
@@ -215,39 +215,29 @@
 #define OpCode_Invalid		0xFFFFu
 
 
-struct Number
+struct number_t
 {
   void init () { set_real (0.0); }
   void fini () {}
 
-  void set_int (int v)       { value = (double) v; }
-  int to_int () const        { return (int) value; }
+  void set_int (int v)       { value = v; }
+  int to_int () const        { return value; }
 
   void set_fixed (int32_t v) { value = v / 65536.0; }
-  int32_t to_fixed () const  { return (int32_t) (value * 65536.0); }
+  int32_t to_fixed () const  { return value * 65536.0; }
 
-  void set_real (double v)	 { value = v; }
+  void set_real (double v)   { value = v; }
   double to_real () const    { return value; }
 
-  int ceil () const          { return (int) ::ceil (value); }
-  int floor () const         { return (int) ::floor (value); }
-
   bool in_int_range () const
   { return ((double) (int16_t) to_int () == value); }
 
-  bool operator > (const Number &n) const
-  { return value > n.to_real (); }
+  bool operator >  (const number_t &n) const { return value > n.to_real (); }
+  bool operator <  (const number_t &n) const { return n > *this; }
+  bool operator >= (const number_t &n) const { return !(*this < n); }
+  bool operator <= (const number_t &n) const { return !(*this > n); }
 
-  bool operator < (const Number &n) const
-  { return n > *this; }
-
-  bool operator >= (const Number &n) const
-  { return !(*this < n); }
-
-  bool operator <= (const Number &n) const
-  { return !(*this > n); }
-
-  const Number &operator += (const Number &n)
+  const number_t &operator += (const number_t &n)
   {
     set_real (to_real () + n.to_real ());
 
@@ -255,7 +245,7 @@
   }
 
   protected:
-  double  value;
+  double value;
 };
 
 /* byte string */
@@ -263,7 +253,7 @@
 {
   // encode 2-byte int (Dict/CharString) or 4-byte int (Dict)
   template <typename INTTYPE, int minVal, int maxVal>
-  static bool serialize_int (hb_serialize_context_t *c, OpCode intOp, int value)
+  static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, int value)
   {
     TRACE_SERIALIZE (this);
 
@@ -272,11 +262,11 @@
 
     HBUINT8 *p = c->allocate_size<HBUINT8> (1);
     if (unlikely (p == nullptr)) return_trace (false);
-    p->set (intOp);
+    *p = intOp;
 
     INTTYPE *ip = c->allocate_size<INTTYPE> (INTTYPE::static_size);
     if (unlikely (ip == nullptr)) return_trace (false);
-    ip->set ((unsigned int)value);
+    *ip = (unsigned int) value;
 
     return_trace (true);
   }
@@ -290,106 +280,83 @@
   /* Defining null_size allows a Null object may be created. Should be safe because:
    * A descendent struct Dict uses a Null pointer to indicate a missing table,
    * checked before access.
-   * ByteStr, a wrapper struct pairing a byte pointer along with its length, always
+   * byte_str_t, a wrapper struct pairing a byte pointer along with its length, always
    * checks the length before access. A Null pointer is used as the initial pointer
    * along with zero length by the default ctor.
    */
   DEFINE_SIZE_MIN(0);
 };
 
-struct ByteStr
+/* Holder of a section of byte string within a CFFIndex entry */
+struct byte_str_t : hb_ubytes_t
 {
-  ByteStr ()
-    : str (&Null(UnsizedByteStr)), len (0) {}
-  ByteStr (const UnsizedByteStr& s, unsigned int l)
-    : str (&s), len (l) {}
-  ByteStr (const char *s, unsigned int l=0)
-    : str ((const UnsizedByteStr *)s), len (l) {}
+  byte_str_t ()
+    : hb_ubytes_t () {}
+  byte_str_t (const UnsizedByteStr& s, unsigned int l)
+    : hb_ubytes_t ((const unsigned char*)&s, l) {}
+  byte_str_t (const unsigned char *s, unsigned int l)
+    : hb_ubytes_t (s, l) {}
+  byte_str_t (const hb_ubytes_t &ub)	/* conversion from hb_ubytes_t */
+    : hb_ubytes_t (ub) {}
+
   /* sub-string */
-  ByteStr (const ByteStr &bs, unsigned int offset, unsigned int len_)
-  {
-    str = (const UnsizedByteStr *)&bs.str[offset];
-    len = len_;
-  }
-
-  bool sanitize (hb_sanitize_context_t *c) const { return str->sanitize (c, len); }
-
-  const HBUINT8& operator [] (unsigned int i) const
-  {
-    if (likely (str && (i < len)))
-      return (*str)[i];
-    else
-      return Null(HBUINT8);
-  }
-
-  bool serialize (hb_serialize_context_t *c, const ByteStr &src)
-  {
-    TRACE_SERIALIZE (this);
-    HBUINT8 *dest = c->allocate_size<HBUINT8> (src.len);
-    if (unlikely (dest == nullptr))
-      return_trace (false);
-    memcpy (dest, src.str, src.len);
-    return_trace (true);
-  }
-
-  unsigned int get_size () const { return len; }
+  byte_str_t sub_str (unsigned int offset, unsigned int len_) const
+  { return byte_str_t (hb_ubytes_t::sub_array (offset, len_)); }
 
   bool check_limit (unsigned int offset, unsigned int count) const
-  { return (offset + count <= len); }
-
-  const UnsizedByteStr *str;
-  unsigned int len;
+  { return (offset + count <= length); }
 };
 
-struct SubByteStr
+/* A byte string associated with the current offset and an error condition */
+struct byte_str_ref_t
 {
-  SubByteStr ()
-  { init (); }
+  byte_str_ref_t () { init (); }
 
   void init ()
   {
-    str = ByteStr (0);
+    str = byte_str_t ();
     offset = 0;
     error = false;
   }
 
   void fini () {}
 
-  SubByteStr (const ByteStr &str_, unsigned int offset_ = 0)
+  byte_str_ref_t (const byte_str_t &str_, unsigned int offset_ = 0)
     : str (str_), offset (offset_), error (false) {}
 
-  void reset (const ByteStr &str_, unsigned int offset_ = 0)
+  void reset (const byte_str_t &str_, unsigned int offset_ = 0)
   {
     str = str_;
     offset = offset_;
     error = false;
   }
 
-  const HBUINT8& operator [] (int i) {
-    if (unlikely ((unsigned int)(offset + i) >= str.len))
+  const unsigned char& operator [] (int i) {
+    if (unlikely ((unsigned int) (offset + i) >= str.length))
     {
       set_error ();
-      return Null(HBUINT8);
+      return Null (unsigned char);
     }
-    else
-      return str[offset + i];
+    return str[offset + i];
   }
 
-  operator ByteStr () const { return ByteStr (str, offset, str.len - offset); }
+  /* Conversion to byte_str_t */
+  operator byte_str_t () const { return str.sub_str (offset, str.length - offset); }
+
+  byte_str_t sub_str (unsigned int offset_, unsigned int len_) const
+  { return str.sub_str (offset_, len_); }
 
   bool avail (unsigned int count=1) const
-  {
-    return (!in_error () && str.check_limit (offset, count));
-  }
+  { return (!in_error () && str.check_limit (offset, count)); }
   void inc (unsigned int count=1)
   {
-    if (likely (!in_error () && (offset <= str.len) && (offset + count <= str.len)))
+    if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length)))
     {
       offset += count;
     }
     else
     {
-      offset = str.len;
+      offset = str.length;
       set_error ();
     }
   }
@@ -397,18 +364,18 @@
   void set_error ()      { error = true; }
   bool in_error () const { return error; }
 
-  ByteStr       str;
+  byte_str_t       str;
   unsigned int  offset; /* beginning of the sub-string within str */
 
   protected:
   bool	  error;
 };
 
-typedef hb_vector_t<ByteStr> ByteStrArray;
+typedef hb_vector_t<byte_str_t> byte_str_array_t;
 
 /* stack */
 template <typename ELEM, int LIMIT>
-struct Stack
+struct cff_stack_t
 {
   void init ()
   {
@@ -416,14 +383,10 @@
     count = 0;
     elements.init ();
     elements.resize (kSizeLimit);
-    for (unsigned int i = 0; i < elements.len; i++)
+    for (unsigned int i = 0; i < elements.length; i++)
       elements[i].init ();
   }
-
-  void fini ()
-  {
-    elements.fini_deep ();
-  }
+  void fini () { elements.fini_deep (); }
 
   ELEM& operator [] (unsigned int i)
   {
@@ -433,15 +396,14 @@
 
   void push (const ELEM &v)
   {
-    if (likely (count < elements.len))
+    if (likely (count < elements.length))
       elements[count++] = v;
     else
       set_error ();
   }
-
   ELEM &push ()
   {
-    if (likely (count < elements.len))
+    if (likely (count < elements.length))
       return elements[count++];
     else
     {
@@ -460,7 +422,6 @@
       return Crap(ELEM);
     }
   }
-
   void pop (unsigned int n)
   {
     if (likely (count >= n))
@@ -471,18 +432,17 @@
 
   const ELEM& peek ()
   {
-    if (likely (count > 0))
-      return elements[count-1];
-    else
+    if (unlikely (count < 0))
     {
       set_error ();
       return Null(ELEM);
     }
+    return elements[count - 1];
   }
 
   void unpop ()
   {
-    if (likely (count < elements.len))
+    if (likely (count < elements.length))
       count++;
     else
       set_error ();
@@ -494,19 +454,19 @@
   void set_error ()      { error = true; }
 
   unsigned int get_count () const { return count; }
-  bool is_empty () const { return count == 0; }
+  bool is_empty () const          { return !count; }
 
-  static const unsigned int kSizeLimit = LIMIT;
+  static constexpr unsigned kSizeLimit = LIMIT;
 
   protected:
   bool error;
   unsigned int count;
-  hb_vector_t<ELEM, kSizeLimit> elements;
+  hb_vector_t<ELEM> elements;
 };
 
 /* argument stack */
-template <typename ARG=Number>
-struct ArgStack : Stack<ARG, 513>
+template <typename ARG=number_t>
+struct arg_stack_t : cff_stack_t<ARG, 513>
 {
   void push_int (int v)
   {
@@ -538,60 +498,58 @@
       i = 0;
       S::set_error ();
     }
-    return (unsigned)i;
+    return (unsigned) i;
   }
 
-  void push_longint_from_substr (SubByteStr& substr)
+  void push_longint_from_substr (byte_str_ref_t& str_ref)
   {
-    push_int ((substr[0] << 24) | (substr[1] << 16) | (substr[2] << 8) | (substr[3]));
-    substr.inc (4);
+    push_int ((str_ref[0] << 24) | (str_ref[1] << 16) | (str_ref[2] << 8) | (str_ref[3]));
+    str_ref.inc (4);
   }
 
-  bool push_fixed_from_substr (SubByteStr& substr)
+  bool push_fixed_from_substr (byte_str_ref_t& str_ref)
   {
-    if (unlikely (!substr.avail (4)))
+    if (unlikely (!str_ref.avail (4)))
       return false;
-    push_fixed ((int32_t)*(const HBUINT32*)&substr[0]);
-    substr.inc (4);
+    push_fixed ((int32_t)*(const HBUINT32*)&str_ref[0]);
+    str_ref.inc (4);
     return true;
   }
 
   hb_array_t<const ARG> get_subarray (unsigned int start) const
-  {
-    return S::elements.sub_array (start);
-  }
+  { return S::elements.sub_array (start); }
 
   private:
-  typedef Stack<ARG, 513> S;
+  typedef cff_stack_t<ARG, 513> S;
 };
 
 /* an operator prefixed by its operands in a byte string */
-struct OpStr
+struct op_str_t
 {
   void init () {}
   void fini () {}
 
-  OpCode  op;
-  ByteStr str;
+  op_code_t  op;
+  byte_str_t str;
 };
 
 /* base of OP_SERIALIZER */
-struct OpSerializer
+struct op_serializer_t
 {
   protected:
-  bool copy_opstr (hb_serialize_context_t *c, const OpStr& opstr) const
+  bool copy_opstr (hb_serialize_context_t *c, const op_str_t& opstr) const
   {
     TRACE_SERIALIZE (this);
 
-    HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
+    HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.length);
     if (unlikely (d == nullptr)) return_trace (false);
-    memcpy (d, &opstr.str.str[0], opstr.str.len);
+    memcpy (d, &opstr.str[0], opstr.str.length);
     return_trace (true);
   }
 };
 
 template <typename VAL>
-struct ParsedValues
+struct parsed_values_t
 {
   void init ()
   {
@@ -600,118 +558,107 @@
   }
   void fini () { values.fini_deep (); }
 
-  void add_op (OpCode op, const SubByteStr& substr = SubByteStr ())
+  void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t ())
   {
     VAL *val = values.push ();
     val->op = op;
-    val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
-    opStart = substr.offset;
+    val->str = str_ref.str.sub_str (opStart, str_ref.offset - opStart);
+    opStart = str_ref.offset;
   }
 
-  void add_op (OpCode op, const SubByteStr& substr, const VAL &v)
+  void add_op (op_code_t op, const byte_str_ref_t& str_ref, const VAL &v)
   {
     VAL *val = values.push (v);
     val->op = op;
-    val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
-    opStart = substr.offset;
+    val->str = str_ref.sub_str ( opStart, str_ref.offset - opStart);
+    opStart = str_ref.offset;
   }
 
-  bool has_op (OpCode op) const
+  bool has_op (op_code_t op) const
   {
     for (unsigned int i = 0; i < get_count (); i++)
       if (get_value (i).op == op) return true;
     return false;
   }
 
-  unsigned get_count () const { return values.len; }
-  const VAL &get_value (unsigned int i) const { return values[i]; }
+  unsigned get_count () const { return values.length; }
+  const VAL &get_value (unsigned int i)   const { return values[i]; }
   const VAL &operator [] (unsigned int i) const { return get_value (i); }
 
   unsigned int       opStart;
   hb_vector_t<VAL>   values;
 };
 
-template <typename ARG=Number>
-struct InterpEnv
+template <typename ARG=number_t>
+struct interp_env_t
 {
-  void init (const ByteStr &str_)
+  void init (const byte_str_t &str_)
   {
-    substr.reset (str_);
+    str_ref.reset (str_);
     argStack.init ();
     error = false;
   }
   void fini () { argStack.fini (); }
 
   bool in_error () const
-  { return error || substr.in_error () || argStack.in_error (); }
+  { return error || str_ref.in_error () || argStack.in_error (); }
 
   void set_error () { error = true; }
 
-  OpCode fetch_op ()
+  op_code_t fetch_op ()
   {
-    OpCode  op = OpCode_Invalid;
-    if (unlikely (!substr.avail ()))
+    op_code_t  op = OpCode_Invalid;
+    if (unlikely (!str_ref.avail ()))
       return OpCode_Invalid;
-    op = (OpCode)(unsigned char)substr[0];
+    op = (op_code_t)(unsigned char)str_ref[0];
     if (op == OpCode_escape) {
-      if (unlikely (!substr.avail ()))
+      if (unlikely (!str_ref.avail ()))
 	return OpCode_Invalid;
-      op = Make_OpCode_ESC(substr[1]);
-      substr.inc ();
+      op = Make_OpCode_ESC(str_ref[1]);
+      str_ref.inc ();
     }
-    substr.inc ();
+    str_ref.inc ();
     return op;
   }
 
-  const ARG& eval_arg (unsigned int i)
-  {
-    return argStack[i];
-  }
+  const ARG& eval_arg (unsigned int i) { return argStack[i]; }
 
-  ARG& pop_arg ()
-  {
-    return argStack.pop ();
-  }
+  ARG& pop_arg () { return argStack.pop (); }
+  void pop_n_args (unsigned int n) { argStack.pop (n); }
 
-  void pop_n_args (unsigned int n)
-  {
-    argStack.pop (n);
-  }
+  void clear_args () { pop_n_args (argStack.get_count ()); }
 
-  void clear_args ()
-  {
-    pop_n_args (argStack.get_count ());
-  }
-
-  SubByteStr    substr;
-  ArgStack<ARG> argStack;
+  byte_str_ref_t
+		str_ref;
+  arg_stack_t<ARG>
+		argStack;
   protected:
-  bool	  error;
+  bool		error;
 };
 
-typedef InterpEnv<> NumInterpEnv;
+typedef interp_env_t<> num_interp_env_t;
 
-template <typename ARG=Number>
-struct OpSet
+template <typename ARG=number_t>
+struct opset_t
 {
-  static void process_op (OpCode op, InterpEnv<ARG>& env)
+  static void process_op (op_code_t op, interp_env_t<ARG>& env)
   {
     switch (op) {
       case OpCode_shortint:
-	env.argStack.push_int ((int16_t)((env.substr[0] << 8) | env.substr[1]));
-	env.substr.inc (2);
+	env.argStack.push_int ((int16_t)((env.str_ref[0] << 8) | env.str_ref[1]));
+	env.str_ref.inc (2);
 	break;
 
       case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
       case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
-	env.argStack.push_int ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + env.substr[0] + 108));
-	env.substr.inc ();
+	env.argStack.push_int ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + env.str_ref[0] + 108));
+	env.str_ref.inc ();
 	break;
 
       case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
       case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
-	env.argStack.push_int ((int16_t)(-(op - OpCode_TwoByteNegInt0) * 256 - env.substr[0] - 108));
-	env.substr.inc ();
+	env.argStack.push_int ((-(int16_t)(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108));
+	env.str_ref.inc ();
 	break;
 
       default:
@@ -730,9 +677,9 @@
 };
 
 template <typename ENV>
-struct Interpreter {
-
-  ~Interpreter() { fini (); }
+struct interpreter_t
+{
+  ~interpreter_t() { fini (); }
 
   void fini () { env.fini (); }
 
diff --git a/src/hb-cff-interp-cs-common.hh b/src/hb-cff-interp-cs-common.hh
index e78d557..d9ad4d0 100644
--- a/src/hb-cff-interp-cs-common.hh
+++ b/src/hb-cff-interp-cs-common.hh
@@ -33,38 +33,38 @@
 
 using namespace OT;
 
-enum CSType {
+enum cs_type_t {
   CSType_CharString,
   CSType_GlobalSubr,
   CSType_LocalSubr
 };
 
-struct CallContext
+struct call_context_t
 {
-  void init (const SubByteStr substr_=SubByteStr (), CSType type_=CSType_CharString, unsigned int subr_num_=0)
+  void init (const byte_str_ref_t substr_=byte_str_ref_t (), cs_type_t type_=CSType_CharString, unsigned int subr_num_=0)
   {
-    substr = substr_;
+    str_ref = substr_;
     type = type_;
     subr_num = subr_num_;
   }
 
   void fini () {}
 
-  SubByteStr      substr;
-  CSType	  type;
+  byte_str_ref_t  str_ref;
+  cs_type_t	  type;
   unsigned int    subr_num;
 };
 
 /* call stack */
 const unsigned int kMaxCallLimit = 10;
-struct CallStack : Stack<CallContext, kMaxCallLimit> {};
+struct call_stack_t : cff_stack_t<call_context_t, kMaxCallLimit> {};
 
 template <typename SUBRS>
-struct BiasedSubrs
+struct biased_subrs_t
 {
-  void init (const SUBRS &subrs_)
+  void init (const SUBRS *subrs_)
   {
-    subrs = &subrs_;
+    subrs = subrs_;
     unsigned int  nSubrs = get_count ();
     if (nSubrs < 1240)
       bias = 107;
@@ -76,13 +76,13 @@
 
   void fini () {}
 
-  unsigned int get_count () const { return (subrs == nullptr)? 0: subrs->count; }
-  unsigned int get_bias () const { return bias; }
+  unsigned int get_count () const { return (subrs == nullptr) ? 0 : subrs->count; }
+  unsigned int get_bias () const  { return bias; }
 
-  ByteStr operator [] (unsigned int index) const
+  byte_str_t operator [] (unsigned int index) const
   {
     if (unlikely ((subrs == nullptr) || index >= subrs->count))
-      return Null(ByteStr);
+      return Null(byte_str_t);
     else
       return (*subrs)[index];
   }
@@ -92,7 +92,7 @@
   const SUBRS   *subrs;
 };
 
-struct Point
+struct point_t
 {
   void init ()
   {
@@ -106,27 +106,28 @@
     y.set_int (_y);
   }
 
-  void move_x (const Number &dx) { x += dx; }
-  void move_y (const Number &dy) { y += dy; }
-  void move (const Number &dx, const Number &dy) { move_x (dx); move_y (dy); }
-  void move (const Point &d) { move_x (d.x); move_y (d.y); }
+  void move_x (const number_t &dx) { x += dx; }
+  void move_y (const number_t &dy) { y += dy; }
+  void move (const number_t &dx, const number_t &dy) { move_x (dx); move_y (dy); }
+  void move (const point_t &d) { move_x (d.x); move_y (d.y); }
 
-  Number  x;
-  Number  y;
+  number_t  x;
+  number_t  y;
 };
 
 template <typename ARG, typename SUBRS>
-struct CSInterpEnv : InterpEnv<ARG>
+struct cs_interp_env_t : interp_env_t<ARG>
 {
-  void init (const ByteStr &str, const SUBRS &globalSubrs_, const SUBRS &localSubrs_)
+  void init (const byte_str_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_)
   {
-    InterpEnv<ARG>::init (str);
+    interp_env_t<ARG>::init (str);
 
     context.init (str, CSType_CharString);
     seen_moveto = true;
     seen_hintmask = false;
     hstem_count = 0;
     vstem_count = 0;
+    hintmask_size = 0;
     pt.init ();
     callStack.init ();
     globalSubrs.init (globalSubrs_);
@@ -134,7 +135,7 @@
   }
   void fini ()
   {
-    InterpEnv<ARG>::fini ();
+    interp_env_t<ARG>::fini ();
 
     callStack.fini ();
     globalSubrs.fini ();
@@ -146,8 +147,9 @@
     return callStack.in_error () || SUPER::in_error ();
   }
 
-  bool popSubrNum (const BiasedSubrs<SUBRS>& biasedSubrs, unsigned int &subr_num)
+  bool pop_subr_num (const biased_subrs_t<SUBRS>& biasedSubrs, unsigned int &subr_num)
   {
+    subr_num = 0;
     int n = SUPER::argStack.pop_int ();
     n += biasedSubrs.get_bias ();
     if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.get_count ())))
@@ -157,29 +159,29 @@
     return true;
   }
 
-  void callSubr (const BiasedSubrs<SUBRS>& biasedSubrs, CSType type)
+  void call_subr (const biased_subrs_t<SUBRS>& biasedSubrs, cs_type_t type)
   {
-    unsigned int subr_num;
+    unsigned int subr_num = 0;
 
-    if (unlikely (!popSubrNum (biasedSubrs, subr_num)
+    if (unlikely (!pop_subr_num (biasedSubrs, subr_num)
 		 || callStack.get_count () >= kMaxCallLimit))
     {
       SUPER::set_error ();
       return;
     }
-    context.substr = SUPER::substr;
+    context.str_ref = SUPER::str_ref;
     callStack.push (context);
 
     context.init ( biasedSubrs[subr_num], type, subr_num);
-    SUPER::substr = context.substr;
+    SUPER::str_ref = context.str_ref;
   }
 
-  void returnFromSubr ()
+  void return_from_subr ()
   {
-    if (unlikely (SUPER::substr.in_error ()))
+    if (unlikely (SUPER::str_ref.in_error ()))
       SUPER::set_error ();
     context = callStack.pop ();
-    SUPER::substr = context.substr;
+    SUPER::str_ref = context.str_ref;
   }
 
   void determine_hintmask_size ()
@@ -195,14 +197,14 @@
   void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; }
   bool is_endchar () const { return endchar_flag; }
 
-  const Number &get_x () const { return pt.x; }
-  const Number &get_y () const { return pt.y; }
-  const Point &get_pt () const { return pt; }
+  const number_t &get_x () const { return pt.x; }
+  const number_t &get_y () const { return pt.y; }
+  const point_t &get_pt () const { return pt; }
 
-  void moveto (const Point &pt_ ) { pt = pt_; }
+  void moveto (const point_t &pt_ ) { pt = pt_; }
 
   public:
-  CallContext   context;
+  call_context_t   context;
   bool	  endchar_flag;
   bool	  seen_moveto;
   bool	  seen_hintmask;
@@ -210,18 +212,18 @@
   unsigned int  hstem_count;
   unsigned int  vstem_count;
   unsigned int  hintmask_size;
-  CallStack	    callStack;
-  BiasedSubrs<SUBRS>   globalSubrs;
-  BiasedSubrs<SUBRS>   localSubrs;
+  call_stack_t	callStack;
+  biased_subrs_t<SUBRS>   globalSubrs;
+  biased_subrs_t<SUBRS>   localSubrs;
 
   private:
-  Point	 pt;
+  point_t	 pt;
 
-  typedef InterpEnv<ARG> SUPER;
+  typedef interp_env_t<ARG> SUPER;
 };
 
 template <typename ENV, typename PARAM>
-struct PathProcsNull
+struct path_procs_null_t
 {
   static void rmoveto (ENV &env, PARAM& param) {}
   static void hmoveto (ENV &env, PARAM& param) {}
@@ -236,24 +238,24 @@
   static void hhcurveto (ENV &env, PARAM& param) {}
   static void vhcurveto (ENV &env, PARAM& param) {}
   static void hvcurveto (ENV &env, PARAM& param) {}
-  static void moveto (ENV &env, PARAM& param, const Point &pt) {}
-  static void line (ENV &env, PARAM& param, const Point &pt1) {}
-  static void curve (ENV &env, PARAM& param, const Point &pt1, const Point &pt2, const Point &pt3) {}
+  static void moveto (ENV &env, PARAM& param, const point_t &pt) {}
+  static void line (ENV &env, PARAM& param, const point_t &pt1) {}
+  static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) {}
   static void hflex (ENV &env, PARAM& param) {}
   static void flex (ENV &env, PARAM& param) {}
   static void hflex1 (ENV &env, PARAM& param) {}
   static void flex1 (ENV &env, PARAM& param) {}
 };
 
-template <typename ARG, typename OPSET, typename ENV, typename PARAM, typename PATH=PathProcsNull<ENV, PARAM> >
-struct CSOpSet : OpSet<ARG>
+template <typename ARG, typename OPSET, typename ENV, typename PARAM, typename PATH=path_procs_null_t<ENV, PARAM>>
+struct cs_opset_t : opset_t<ARG>
 {
-  static void process_op (OpCode op, ENV &env, PARAM& param)
+  static void process_op (op_code_t op, ENV &env, PARAM& param)
   {
     switch (op) {
 
       case OpCode_return:
-	env.returnFromSubr ();
+	env.return_from_subr ();
 	break;
       case OpCode_endchar:
 	OPSET::check_width (op, env, param);
@@ -262,15 +264,15 @@
 	break;
 
       case OpCode_fixedcs:
-	env.argStack.push_fixed_from_substr (env.substr);
+	env.argStack.push_fixed_from_substr (env.str_ref);
 	break;
 
       case OpCode_callsubr:
-	env.callSubr (env.localSubrs, CSType_LocalSubr);
+	env.call_subr (env.localSubrs, CSType_LocalSubr);
 	break;
 
       case OpCode_callgsubr:
-	env.callSubr (env.globalSubrs, CSType_GlobalSubr);
+	env.call_subr (env.globalSubrs, CSType_GlobalSubr);
 	break;
 
       case OpCode_hstem:
@@ -370,37 +372,37 @@
     }
   }
 
-  static void process_hstem (OpCode op, ENV &env, PARAM& param)
+  static void process_hstem (op_code_t op, ENV &env, PARAM& param)
   {
     env.hstem_count += env.argStack.get_count () / 2;
     OPSET::flush_args_and_op (op, env, param);
   }
 
-  static void process_vstem (OpCode op, ENV &env, PARAM& param)
+  static void process_vstem (op_code_t op, ENV &env, PARAM& param)
   {
     env.vstem_count += env.argStack.get_count () / 2;
     OPSET::flush_args_and_op (op, env, param);
   }
 
-  static void process_hintmask (OpCode op, ENV &env, PARAM& param)
+  static void process_hintmask (op_code_t op, ENV &env, PARAM& param)
   {
     env.determine_hintmask_size ();
-    if (likely (env.substr.avail (env.hintmask_size)))
+    if (likely (env.str_ref.avail (env.hintmask_size)))
     {
       OPSET::flush_hintmask (op, env, param);
-      env.substr.inc (env.hintmask_size);
+      env.str_ref.inc (env.hintmask_size);
     }
   }
 
-  static void process_post_flex (OpCode op, ENV &env, PARAM& param)
+  static void process_post_flex (op_code_t op, ENV &env, PARAM& param)
   {
     OPSET::flush_args_and_op (op, env, param);
   }
 
-  static void check_width (OpCode op, ENV &env, PARAM& param)
+  static void check_width (op_code_t op, ENV &env, PARAM& param)
   {}
 
-  static void process_post_move (OpCode op, ENV &env, PARAM& param)
+  static void process_post_move (op_code_t op, ENV &env, PARAM& param)
   {
     if (!env.seen_moveto)
     {
@@ -410,12 +412,12 @@
     OPSET::flush_args_and_op (op, env, param);
   }
 
-  static void process_post_path (OpCode op, ENV &env, PARAM& param)
+  static void process_post_path (op_code_t op, ENV &env, PARAM& param)
   {
     OPSET::flush_args_and_op (op, env, param);
   }
 
-  static void flush_args_and_op (OpCode op, ENV &env, PARAM& param)
+  static void flush_args_and_op (op_code_t op, ENV &env, PARAM& param)
   {
     OPSET::flush_args (env, param);
     OPSET::flush_op (op, env, param);
@@ -426,16 +428,16 @@
     env.pop_n_args (env.argStack.get_count ());
   }
 
-  static void flush_op (OpCode op, ENV &env, PARAM& param)
+  static void flush_op (op_code_t op, ENV &env, PARAM& param)
   {
   }
 
-  static void flush_hintmask (OpCode op, ENV &env, PARAM& param)
+  static void flush_hintmask (op_code_t op, ENV &env, PARAM& param)
   {
     OPSET::flush_args_and_op (op, env, param);
   }
 
-  static bool is_number_op (OpCode op)
+  static bool is_number_op (op_code_t op)
   {
     switch (op)
     {
@@ -454,31 +456,31 @@
   }
 
   protected:
-  typedef OpSet<ARG>  SUPER;
+  typedef opset_t<ARG>  SUPER;
 };
 
 template <typename PATH, typename ENV, typename PARAM>
-struct PathProcs
+struct path_procs_t
 {
   static void rmoveto (ENV &env, PARAM& param)
   {
-    Point pt1 = env.get_pt ();
-    const Number &dy = env.pop_arg ();
-    const Number &dx = env.pop_arg ();
+    point_t pt1 = env.get_pt ();
+    const number_t &dy = env.pop_arg ();
+    const number_t &dx = env.pop_arg ();
     pt1.move (dx, dy);
     PATH::moveto (env, param, pt1);
   }
 
   static void hmoveto (ENV &env, PARAM& param)
   {
-    Point pt1 = env.get_pt ();
+    point_t pt1 = env.get_pt ();
     pt1.move_x (env.pop_arg ());
     PATH::moveto (env, param, pt1);
   }
 
   static void vmoveto (ENV &env, PARAM& param)
   {
-    Point pt1 = env.get_pt ();
+    point_t pt1 = env.get_pt ();
     pt1.move_y (env.pop_arg ());
     PATH::moveto (env, param, pt1);
   }
@@ -487,7 +489,7 @@
   {
     for (unsigned int i = 0; i + 2 <= env.argStack.get_count (); i += 2)
     {
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
       PATH::line (env, param, pt1);
     }
@@ -495,7 +497,7 @@
 
   static void hlineto (ENV &env, PARAM& param)
   {
-    Point pt1;
+    point_t pt1;
     unsigned int i = 0;
     for (; i + 2 <= env.argStack.get_count (); i += 2)
     {
@@ -515,7 +517,7 @@
 
   static void vlineto (ENV &env, PARAM& param)
   {
-    Point pt1;
+    point_t pt1;
     unsigned int i = 0;
     for (; i + 2 <= env.argStack.get_count (); i += 2)
     {
@@ -537,11 +539,11 @@
   {
     for (unsigned int i = 0; i + 6 <= env.argStack.get_count (); i += 6)
     {
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
-      Point pt2 = pt1;
+      point_t pt2 = pt1;
       pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
-      Point pt3 = pt2;
+      point_t pt3 = pt2;
       pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
       PATH::curve (env, param, pt1, pt2, pt3);
     }
@@ -552,17 +554,17 @@
     unsigned int i = 0;
     for (; i + 6 <= env.argStack.get_count (); i += 6)
     {
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
-      Point pt2 = pt1;
+      point_t pt2 = pt1;
       pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
-      Point pt3 = pt2;
+      point_t pt3 = pt2;
       pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
       PATH::curve (env, param, pt1, pt2, pt3);
     }
     for (; i + 2 <= env.argStack.get_count (); i += 2)
     {
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
       PATH::line (env, param, pt1);
     }
@@ -574,17 +576,17 @@
     unsigned int line_limit = (env.argStack.get_count () % 6);
     for (; i + 2 <= line_limit; i += 2)
     {
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
       PATH::line (env, param, pt1);
     }
     for (; i + 6 <= env.argStack.get_count (); i += 6)
     {
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
-      Point pt2 = pt1;
+      point_t pt2 = pt1;
       pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
-      Point pt3 = pt2;
+      point_t pt3 = pt2;
       pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
       PATH::curve (env, param, pt1, pt2, pt3);
     }
@@ -593,15 +595,15 @@
   static void vvcurveto (ENV &env, PARAM& param)
   {
     unsigned int i = 0;
-    Point pt1 = env.get_pt ();
+    point_t pt1 = env.get_pt ();
     if ((env.argStack.get_count () & 1) != 0)
       pt1.move_x (env.eval_arg (i++));
     for (; i + 4 <= env.argStack.get_count (); i += 4)
     {
       pt1.move_y (env.eval_arg (i));
-      Point pt2 = pt1;
+      point_t pt2 = pt1;
       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
-      Point pt3 = pt2;
+      point_t pt3 = pt2;
       pt3.move_y (env.eval_arg (i+3));
       PATH::curve (env, param, pt1, pt2, pt3);
       pt1 = env.get_pt ();
@@ -611,15 +613,15 @@
   static void hhcurveto (ENV &env, PARAM& param)
   {
     unsigned int i = 0;
-    Point pt1 = env.get_pt ();
+    point_t pt1 = env.get_pt ();
     if ((env.argStack.get_count () & 1) != 0)
       pt1.move_y (env.eval_arg (i++));
     for (; i + 4 <= env.argStack.get_count (); i += 4)
     {
       pt1.move_x (env.eval_arg (i));
-      Point pt2 = pt1;
+      point_t pt2 = pt1;
       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
-      Point pt3 = pt2;
+      point_t pt3 = pt2;
       pt3.move_x (env.eval_arg (i+3));
       PATH::curve (env, param, pt1, pt2, pt3);
       pt1 = env.get_pt ();
@@ -628,15 +630,15 @@
 
   static void vhcurveto (ENV &env, PARAM& param)
   {
-    Point pt1, pt2, pt3;
+    point_t pt1, pt2, pt3;
     unsigned int i = 0;
     if ((env.argStack.get_count () % 8) >= 4)
     {
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move_y (env.eval_arg (i));
-      Point pt2 = pt1;
+      point_t pt2 = pt1;
       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
-      Point pt3 = pt2;
+      point_t pt3 = pt2;
       pt3.move_x (env.eval_arg (i+3));
       i += 4;
 
@@ -689,15 +691,15 @@
 
   static void hvcurveto (ENV &env, PARAM& param)
   {
-    Point pt1, pt2, pt3;
+    point_t pt1, pt2, pt3;
     unsigned int i = 0;
     if ((env.argStack.get_count () % 8) >= 4)
     {
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move_x (env.eval_arg (i));
-      Point pt2 = pt1;
+      point_t pt2 = pt1;
       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
-      Point pt3 = pt2;
+      point_t pt3 = pt2;
       pt3.move_y (env.eval_arg (i+3));
       i += 4;
 
@@ -749,31 +751,31 @@
   }
 
   /* default actions to be overridden */
-  static void moveto (ENV &env, PARAM& param, const Point &pt)
+  static void moveto (ENV &env, PARAM& param, const point_t &pt)
   { env.moveto (pt); }
 
-  static void line (ENV &env, PARAM& param, const Point &pt1)
+  static void line (ENV &env, PARAM& param, const point_t &pt1)
   { PATH::moveto (env, param, pt1); }
 
-  static void curve (ENV &env, PARAM& param, const Point &pt1, const Point &pt2, const Point &pt3)
+  static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
   { PATH::moveto (env, param, pt3); }
 
   static void hflex (ENV &env, PARAM& param)
   {
     if (likely (env.argStack.get_count () == 7))
     {
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move_x (env.eval_arg (0));
-      Point pt2 = pt1;
+      point_t pt2 = pt1;
       pt2.move (env.eval_arg (1), env.eval_arg (2));
-      Point pt3 = pt2;
+      point_t pt3 = pt2;
       pt3.move_x (env.eval_arg (3));
-      Point pt4 = pt3;
+      point_t pt4 = pt3;
       pt4.move_x (env.eval_arg (4));
-      Point pt5 = pt4;
+      point_t pt5 = pt4;
       pt5.move_x (env.eval_arg (5));
       pt5.y = pt1.y;
-      Point pt6 = pt5;
+      point_t pt6 = pt5;
       pt6.move_x (env.eval_arg (6));
 
       curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
@@ -786,17 +788,17 @@
   {
     if (likely (env.argStack.get_count () == 13))
     {
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move (env.eval_arg (0), env.eval_arg (1));
-      Point pt2 = pt1;
+      point_t pt2 = pt1;
       pt2.move (env.eval_arg (2), env.eval_arg (3));
-      Point pt3 = pt2;
+      point_t pt3 = pt2;
       pt3.move (env.eval_arg (4), env.eval_arg (5));
-      Point pt4 = pt3;
+      point_t pt4 = pt3;
       pt4.move (env.eval_arg (6), env.eval_arg (7));
-      Point pt5 = pt4;
+      point_t pt5 = pt4;
       pt5.move (env.eval_arg (8), env.eval_arg (9));
-      Point pt6 = pt5;
+      point_t pt6 = pt5;
       pt6.move (env.eval_arg (10), env.eval_arg (11));
 
       curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
@@ -809,17 +811,17 @@
   {
     if (likely (env.argStack.get_count () == 9))
     {
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move (env.eval_arg (0), env.eval_arg (1));
-      Point pt2 = pt1;
+      point_t pt2 = pt1;
       pt2.move (env.eval_arg (2), env.eval_arg (3));
-      Point pt3 = pt2;
+      point_t pt3 = pt2;
       pt3.move_x (env.eval_arg (4));
-      Point pt4 = pt3;
+      point_t pt4 = pt3;
       pt4.move_x (env.eval_arg (5));
-      Point pt5 = pt4;
+      point_t pt5 = pt4;
       pt5.move (env.eval_arg (6), env.eval_arg (7));
-      Point pt6 = pt5;
+      point_t pt6 = pt5;
       pt6.move_x (env.eval_arg (8));
       pt6.y = env.get_pt ().y;
 
@@ -833,22 +835,22 @@
   {
     if (likely (env.argStack.get_count () == 11))
     {
-      Point d;
+      point_t d;
       d.init ();
       for (unsigned int i = 0; i < 10; i += 2)
 	d.move (env.eval_arg (i), env.eval_arg (i+1));
 
-      Point pt1 = env.get_pt ();
+      point_t pt1 = env.get_pt ();
       pt1.move (env.eval_arg (0), env.eval_arg (1));
-      Point pt2 = pt1;
+      point_t pt2 = pt1;
       pt2.move (env.eval_arg (2), env.eval_arg (3));
-      Point pt3 = pt2;
+      point_t pt3 = pt2;
       pt3.move (env.eval_arg (4), env.eval_arg (5));
-      Point pt4 = pt3;
+      point_t pt4 = pt3;
       pt4.move (env.eval_arg (6), env.eval_arg (7));
-      Point pt5 = pt4;
+      point_t pt5 = pt4;
       pt5.move (env.eval_arg (8), env.eval_arg (9));
-      Point pt6 = pt5;
+      point_t pt6 = pt5;
 
       if (fabs (d.x.to_real ()) > fabs (d.y.to_real ()))
       {
@@ -869,8 +871,8 @@
 
   protected:
   static void curve2 (ENV &env, PARAM& param,
-		      const Point &pt1, const Point &pt2, const Point &pt3,
-		      const Point &pt4, const Point &pt5, const Point &pt6)
+		      const point_t &pt1, const point_t &pt2, const point_t &pt3,
+		      const point_t &pt4, const point_t &pt5, const point_t &pt6)
   {
     PATH::curve (env, param, pt1, pt2, pt3);
     PATH::curve (env, param, pt4, pt5, pt6);
@@ -878,7 +880,7 @@
 };
 
 template <typename ENV, typename OPSET, typename PARAM>
-struct CSInterpreter : Interpreter<ENV>
+struct cs_interpreter_t : interpreter_t<ENV>
 {
   bool interpret (PARAM& param)
   {
@@ -896,7 +898,7 @@
   }
 
   private:
-  typedef Interpreter<ENV> SUPER;
+  typedef interpreter_t<ENV> SUPER;
 };
 
 } /* namespace CFF */
diff --git a/src/hb-cff-interp-dict-common.hh b/src/hb-cff-interp-dict-common.hh
index 8000d76..1f03d82 100644
--- a/src/hb-cff-interp-dict-common.hh
+++ b/src/hb-cff-interp-dict-common.hh
@@ -35,28 +35,28 @@
 using namespace OT;
 
 /* an opstr and the parsed out dict value(s) */
-struct DictVal : OpStr
+struct dict_val_t : op_str_t
 {
   void init () { single_val.set_int (0); }
   void fini () {}
 
-  Number	      single_val;
+  number_t	      single_val;
 };
 
-typedef DictVal NumDictVal;
+typedef dict_val_t num_dict_val_t;
 
-template <typename VAL> struct DictValues : ParsedValues<VAL> {};
+template <typename VAL> struct dict_values_t : parsed_values_t<VAL> {};
 
-template <typename OPSTR=OpStr>
-struct TopDictValues : DictValues<OPSTR>
+template <typename OPSTR=op_str_t>
+struct top_dict_values_t : dict_values_t<OPSTR>
 {
   void init ()
   {
-    DictValues<OPSTR>::init ();
+    dict_values_t<OPSTR>::init ();
     charStringsOffset = 0;
     FDArrayOffset = 0;
   }
-  void fini () { DictValues<OPSTR>::fini (); }
+  void fini () { dict_values_t<OPSTR>::fini (); }
 
   unsigned int calculate_serialized_op_size (const OPSTR& opstr) const
   {
@@ -67,7 +67,7 @@
 	return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
 
       default:
-	return opstr.str.len;
+	return opstr.str.length;
     }
   }
 
@@ -75,152 +75,74 @@
   unsigned int  FDArrayOffset;
 };
 
-struct DictOpSet : OpSet<Number>
+struct dict_opset_t : opset_t<number_t>
 {
-  static void process_op (OpCode op, InterpEnv<Number>& env)
+  static void process_op (op_code_t op, interp_env_t<number_t>& env)
   {
     switch (op) {
       case OpCode_longintdict:  /* 5-byte integer */
-	env.argStack.push_longint_from_substr (env.substr);
+	env.argStack.push_longint_from_substr (env.str_ref);
 	break;
 
       case OpCode_BCD:  /* real number */
-	env.argStack.push_real (parse_bcd (env.substr));
+	env.argStack.push_real (parse_bcd (env.str_ref));
 	break;
 
       default:
-	OpSet<Number>::process_op (op, env);
+	opset_t<number_t>::process_op (op, env);
 	break;
     }
   }
 
-  static double parse_bcd (SubByteStr& substr)
+  /* Turns CFF's BCD format into strtod understandable string */
+  static double parse_bcd (byte_str_ref_t& str_ref)
   {
-    bool    neg = false;
-    double  int_part = 0;
-    uint64_t frac_part = 0;
-    uint32_t  frac_count = 0;
-    bool    exp_neg = false;
-    uint32_t  exp_part = 0;
-    bool    exp_overflow = false;
-    enum Part { INT_PART=0, FRAC_PART, EXP_PART } part = INT_PART;
-    enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END };
-    const uint64_t MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 1^52-1 */
-    const uint32_t MAX_EXP = 0x7FFu; /* 1^11-1 */
+    if (unlikely (str_ref.in_error ())) return .0;
 
-    double  value = 0.0;
+    enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END };
+
+    char buf[32];
     unsigned char byte = 0;
-    for (uint32_t i = 0;; i++)
+    for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count)
     {
-      char d;
-      if ((i & 1) == 0)
+      unsigned nibble;
+      if (!(i & 1))
       {
-	if (!substr.avail ())
-	{
-	  substr.set_error ();
-	  return 0.0;
-	}
-	byte = substr[0];
-	substr.inc ();
-	d = byte >> 4;
+	if (unlikely (!str_ref.avail ())) break;
+
+	byte = str_ref[0];
+	str_ref.inc ();
+	nibble = byte >> 4;
       }
       else
-	d = byte & 0x0F;
+	nibble = byte & 0x0F;
 
-      switch (d)
+      if (unlikely (nibble == RESERVED)) break;
+      else if (nibble == END)
       {
-	case RESERVED:
-	  substr.set_error ();
-	  return value;
-
-	case END:
-	  value = (double)(neg? -int_part: int_part);
-	  if (frac_count > 0)
-	  {
-	    double frac = (frac_part / pow (10.0, (double)frac_count));
-	    if (neg) frac = -frac;
-	    value += frac;
-	  }
-	  if (unlikely (exp_overflow))
-	  {
-	    if (value == 0.0)
-	      return value;
-	    if (exp_neg)
-	      return neg? -DBL_MIN: DBL_MIN;
-	    else
-	      return neg? -DBL_MAX: DBL_MAX;
-	  }
-	  if (exp_part != 0)
-	  {
-	    if (exp_neg)
-	      value /= pow (10.0, (double)exp_part);
-	    else
-	      value *= pow (10.0, (double)exp_part);
-	  }
-	  return value;
-
-	case NEG:
-	  if (i != 0)
-	  {
-	    substr.set_error ();
-	    return 0.0;
-	  }
-	  neg = true;
+	const char *p = buf;
+	double pv;
+	if (unlikely (!hb_parse_double (&p, p + count, &pv, true/* whole buffer */)))
 	  break;
-
-	case DECIMAL:
-	  if (part != INT_PART)
-	  {
-	    substr.set_error ();
-	    return value;
-	  }
-	  part = FRAC_PART;
-	  break;
-
-	case EXP_NEG:
-	  exp_neg = true;
-	  HB_FALLTHROUGH;
-
-	case EXP_POS:
-	  if (part == EXP_PART)
-	  {
-	    substr.set_error ();
-	    return value;
-	  }
-	  part = EXP_PART;
-	  break;
-
-	default:
-	  switch (part) {
-	    default:
-	    case INT_PART:
-	      int_part = (int_part * 10) + d;
-	      break;
-
-	    case FRAC_PART:
-	      if (likely (frac_part <= MAX_FRACT / 10))
-	      {
-		frac_part = (frac_part * 10) + (unsigned)d;
-		frac_count++;
-	      }
-	      break;
-
-	    case EXP_PART:
-	      if (likely (exp_part * 10 + d <= MAX_EXP))
-	      {
-	      	exp_part = (exp_part * 10) + d;
-	      }
-	      else
-	      	exp_overflow = true;
-	      break;
-	  }
+	return pv;
+      }
+      else
+      {
+	buf[count] = "0123456789.EE?-?"[nibble];
+	if (nibble == EXP_NEG)
+	{
+	  ++count;
+	  if (unlikely (count == ARRAY_LENGTH (buf))) break;
+	  buf[count] = '-';
+	}
       }
     }
 
-    return value;
+    str_ref.set_error ();
+    return .0;
   }
 
-  static bool is_hint_op (OpCode op)
+  static bool is_hint_op (op_code_t op)
   {
     switch (op)
     {
@@ -245,10 +167,10 @@
   }
 };
 
-template <typename VAL=OpStr>
-struct TopDictOpSet : DictOpSet
+template <typename VAL=op_str_t>
+struct top_dict_opset_t : dict_opset_t
 {
-  static void process_op (OpCode op, InterpEnv<Number>& env, TopDictValues<VAL> & dictval)
+  static void process_op (op_code_t op, interp_env_t<number_t>& env, top_dict_values_t<VAL> & dictval)
   {
     switch (op) {
       case OpCode_CharStrings:
@@ -263,19 +185,19 @@
 	env.clear_args ();
 	break;
       default:
-	DictOpSet::process_op (op, env);
+	dict_opset_t::process_op (op, env);
 	break;
     }
   }
 };
 
-template <typename OPSET, typename PARAM, typename ENV=NumInterpEnv>
-struct DictInterpreter : Interpreter<ENV>
+template <typename OPSET, typename PARAM, typename ENV=num_interp_env_t>
+struct dict_interpreter_t : interpreter_t<ENV>
 {
   bool interpret (PARAM& param)
   {
     param.init ();
-    while (SUPER::env.substr.avail ())
+    while (SUPER::env.str_ref.avail ())
     {
       OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
       if (unlikely (SUPER::env.in_error ()))
@@ -286,7 +208,7 @@
   }
 
   private:
-  typedef Interpreter<ENV> SUPER;
+  typedef interpreter_t<ENV> SUPER;
 };
 
 } /* namespace CFF */
diff --git a/src/hb-cff1-interp-cs.hh b/src/hb-cff1-interp-cs.hh
index 68e1d81..1c8762c 100644
--- a/src/hb-cff1-interp-cs.hh
+++ b/src/hb-cff1-interp-cs.hh
@@ -33,14 +33,14 @@
 
 using namespace OT;
 
-typedef BiasedSubrs<CFF1Subrs>   CFF1BiasedSubrs;
+typedef biased_subrs_t<CFF1Subrs>   cff1_biased_subrs_t;
 
-struct CFF1CSInterpEnv : CSInterpEnv<Number, CFF1Subrs>
+struct cff1_cs_interp_env_t : cs_interp_env_t<number_t, CFF1Subrs>
 {
   template <typename ACC>
-  void init (const ByteStr &str, ACC &acc, unsigned int fd)
+  void init (const byte_str_t &str, ACC &acc, unsigned int fd)
   {
-    SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
+    SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs);
     processed_width = false;
     has_width = false;
     arg_start = 0;
@@ -74,20 +74,20 @@
   bool	  processed_width;
   bool	  has_width;
   unsigned int  arg_start;
-  Number	width;
+  number_t	width;
   bool	  in_seac;
 
   private:
-  typedef CSInterpEnv<Number, CFF1Subrs> SUPER;
+  typedef cs_interp_env_t<number_t, CFF1Subrs> SUPER;
 };
 
-template <typename OPSET, typename PARAM, typename PATH=PathProcsNull<CFF1CSInterpEnv, PARAM> >
-struct CFF1CSOpSet : CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH>
+template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff1_cs_interp_env_t, PARAM>>
+struct cff1_cs_opset_t : cs_opset_t<number_t, OPSET, cff1_cs_interp_env_t, PARAM, PATH>
 {
   /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */
   /* Type 1-originated deprecated opcodes, seac behavior of endchar and dotsection are supported */
 
-  static void process_op (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
+  static void process_op (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param)
   {
     switch (op) {
       case OpCode_dotsection:
@@ -109,7 +109,7 @@
     }
   }
 
-  static void check_width (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
+  static void check_width (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param)
   {
     if (!env.processed_width)
     {
@@ -139,22 +139,22 @@
     }
   }
 
-  static void process_seac (CFF1CSInterpEnv &env, PARAM& param)
+  static void process_seac (cff1_cs_interp_env_t &env, PARAM& param)
   {
   }
 
-  static void flush_args (CFF1CSInterpEnv &env, PARAM& param)
+  static void flush_args (cff1_cs_interp_env_t &env, PARAM& param)
   {
     SUPER::flush_args (env, param);
     env.clear_args ();  /* pop off width */
   }
 
   private:
-  typedef CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM, PATH>  SUPER;
+  typedef cs_opset_t<number_t, OPSET, cff1_cs_interp_env_t, PARAM, PATH>  SUPER;
 };
 
 template <typename OPSET, typename PARAM>
-struct CFF1CSInterpreter : CSInterpreter<CFF1CSInterpEnv, OPSET, PARAM> {};
+struct cff1_cs_interpreter_t : cs_interpreter_t<cff1_cs_interp_env_t, OPSET, PARAM> {};
 
 } /* namespace CFF */
 
diff --git a/src/hb-cff2-interp-cs.hh b/src/hb-cff2-interp-cs.hh
index 709bdef..a72100e 100644
--- a/src/hb-cff2-interp-cs.hh
+++ b/src/hb-cff2-interp-cs.hh
@@ -33,26 +33,26 @@
 
 using namespace OT;
 
-struct BlendArg : Number
+struct blend_arg_t : number_t
 {
   void init ()
   {
-    Number::init ();
+    number_t::init ();
     deltas.init ();
   }
 
   void fini ()
   {
-    Number::fini ();
+    number_t::fini ();
     deltas.fini_deep ();
   }
 
-  void set_int (int v) { reset_blends (); Number::set_int (v); }
-  void set_fixed (int32_t v) { reset_blends (); Number::set_fixed (v); }
-  void set_real (double v) { reset_blends (); Number::set_real (v); }
+  void set_int (int v) { reset_blends (); number_t::set_int (v); }
+  void set_fixed (int32_t v) { reset_blends (); number_t::set_fixed (v); }
+  void set_real (double v) { reset_blends (); number_t::set_real (v); }
 
   void set_blends (unsigned int numValues_, unsigned int valueIndex_,
-			  unsigned int numBlends, hb_array_t<const BlendArg> blends_)
+			  unsigned int numBlends, hb_array_t<const blend_arg_t> blends_)
   {
     numValues = numValues_;
     valueIndex = valueIndex_;
@@ -61,7 +61,7 @@
       deltas[i] = blends_[i];
   }
 
-  bool blending () const { return deltas.len > 0; }
+  bool blending () const { return deltas.length > 0; }
   void reset_blends ()
   {
     numValues = valueIndex = 0;
@@ -70,19 +70,19 @@
 
   unsigned int numValues;
   unsigned int valueIndex;
-  hb_vector_t<Number> deltas;
+  hb_vector_t<number_t> deltas;
 };
 
-typedef InterpEnv<BlendArg> BlendInterpEnv;
-typedef BiasedSubrs<CFF2Subrs>   CFF2BiasedSubrs;
+typedef interp_env_t<blend_arg_t> BlendInterpEnv;
+typedef biased_subrs_t<CFF2Subrs>   cff2_biased_subrs_t;
 
-struct CFF2CSInterpEnv : CSInterpEnv<BlendArg, CFF2Subrs>
+struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
 {
   template <typename ACC>
-  void init (const ByteStr &str, ACC &acc, unsigned int fd,
+  void init (const byte_str_t &str, ACC &acc, unsigned int fd,
 		    const int *coords_=nullptr, unsigned int num_coords_=0)
   {
-    SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
+    SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs);
 
     coords = coords_;
     num_coords = num_coords_;
@@ -100,9 +100,9 @@
     SUPER::fini ();
   }
 
-  OpCode fetch_op ()
+  op_code_t fetch_op ()
   {
-    if (this->substr.avail ())
+    if (this->str_ref.avail ())
       return SUPER::fetch_op ();
 
     /* make up return or endchar op */
@@ -112,16 +112,16 @@
       return OpCode_return;
   }
 
-  const BlendArg& eval_arg (unsigned int i)
+  const blend_arg_t& eval_arg (unsigned int i)
   {
-    BlendArg  &arg = argStack[i];
+    blend_arg_t  &arg = argStack[i];
     blend_arg (arg);
     return arg;
   }
 
-  const BlendArg& pop_arg ()
+  const blend_arg_t& pop_arg ()
   {
-    BlendArg  &arg = argStack.pop ();
+    blend_arg_t  &arg = argStack.pop ();
     blend_arg (arg);
     return arg;
   }
@@ -163,14 +163,14 @@
   bool	 seen_vsindex () const { return seen_vsindex_; }
 
   protected:
-  void blend_arg (BlendArg &arg)
+  void blend_arg (blend_arg_t &arg)
   {
     if (do_blend && arg.blending ())
     {
-      if (likely (scalars.len == arg.deltas.len))
+      if (likely (scalars.length == arg.deltas.length))
       {
 	double v = arg.to_real ();
-	for (unsigned int i = 0; i < scalars.len; i++)
+	for (unsigned int i = 0; i < scalars.length; i++)
 	{
 	  v += (double)scalars[i] * arg.deltas[i].to_real ();
 	}
@@ -191,12 +191,12 @@
   bool	  seen_vsindex_;
   bool	  seen_blend;
 
-  typedef CSInterpEnv<BlendArg, CFF2Subrs> SUPER;
+  typedef cs_interp_env_t<blend_arg_t, CFF2Subrs> SUPER;
 };
-template <typename OPSET, typename PARAM, typename PATH=PathProcsNull<CFF2CSInterpEnv, PARAM> >
-struct CFF2CSOpSet : CSOpSet<BlendArg, OPSET, CFF2CSInterpEnv, PARAM, PATH>
+template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t, PARAM>>
+struct cff2_cs_opset_t : cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PARAM, PATH>
 {
-  static void process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param)
+  static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param)
   {
     switch (op) {
       case OpCode_callsubr:
@@ -228,7 +228,7 @@
     }
   }
 
-  static void process_blend (CFF2CSInterpEnv &env, PARAM& param)
+  static void process_blend (cff2_cs_interp_env_t &env, PARAM& param)
   {
     unsigned int n, k;
 
@@ -245,7 +245,7 @@
     }
     for (unsigned int i = 0; i < n; i++)
     {
-      const hb_array_t<const BlendArg>	blends = env.argStack.get_subarray (start + n + (i * k));
+      const hb_array_t<const blend_arg_t>	blends = env.argStack.get_subarray (start + n + (i * k));
       env.argStack[start + i].set_blends (n, i, k, blends);
     }
 
@@ -253,18 +253,18 @@
     env.argStack.pop (k * n);
   }
 
-  static void process_vsindex (CFF2CSInterpEnv &env, PARAM& param)
+  static void process_vsindex (cff2_cs_interp_env_t &env, PARAM& param)
   {
     env.process_vsindex ();
     env.clear_args ();
   }
 
   private:
-  typedef CSOpSet<BlendArg, OPSET, CFF2CSInterpEnv, PARAM, PATH>  SUPER;
+  typedef cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PARAM, PATH>  SUPER;
 };
 
 template <typename OPSET, typename PARAM>
-struct CFF2CSInterpreter : CSInterpreter<CFF2CSInterpEnv, OPSET, PARAM> {};
+struct cff2_cs_interpreter_t : cs_interpreter_t<cff2_cs_interp_env_t, OPSET, PARAM> {};
 
 } /* namespace CFF */
 
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 93f5b79..0ae0c05 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -27,14 +27,13 @@
  */
 
 #include "hb.hh"
-
 #include "hb-machinery.hh"
 
 #include <locale.h>
-#ifdef HAVE_XLOCALE_H
-#include <xlocale.h>
-#endif
 
+#ifdef HB_NO_SETLOCALE
+#define setlocale(Category, Locale) "C"
+#endif
 
 /**
  * SECTION:hb-common
@@ -64,10 +63,10 @@
     {
       const char *p = strchr (c, ':');
       if (!p)
-        p = c + strlen (c);
+	p = c + strlen (c);
 
 #define OPTION(name, symbol) \
-	if (0 == strncmp (c, name, p - c) && strlen (name) == p - c) u.opts.symbol = true;
+	if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast<size_t>(p - c)) do { u.opts.symbol = true; } while (0)
 
       OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
       OPTION ("aat", aat);
@@ -356,7 +355,7 @@
   {
     /* NUL-terminate it. */
     char strbuf[64];
-    len = MIN (len, (int) sizeof (strbuf) - 1);
+    len = hb_min (len, (int) sizeof (strbuf) - 1);
     memcpy (strbuf, str, len);
     strbuf[len] = '\0';
     item = lang_find_or_insert (strbuf);
@@ -382,7 +381,8 @@
 const char *
 hb_language_to_string (hb_language_t language)
 {
-  /* This is actually nullptr-safe! */
+  if (unlikely (!language)) return nullptr;
+
   return language->s;
 }
 
@@ -488,7 +488,7 @@
 
 /**
  * hb_script_to_iso15924_tag:
- * @script: an #hb_script_ to convert.
+ * @script: an #hb_script_t to convert.
  *
  * See hb_script_from_iso15924_tag().
  *
@@ -719,131 +719,24 @@
 static bool
 parse_uint (const char **pp, const char *end, unsigned int *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;
+  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
+   * such that -1 turns into "big number"... */
+  int v;
+  if (unlikely (!hb_parse_int (pp, end, &v))) return false;
 
   *pv = v;
-  *pp += pend - p;
   return true;
 }
 
 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;
+  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
+   * such that -1 turns into "big number"... */
+  int v;
+  if (unlikely (!hb_parse_int (pp, end, &v))) 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
-
-#if HB_USE_ATEXIT
-static void free_static_C_locale ();
-#endif
-
-static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer (HB_LOCALE_T),
-							  hb_C_locale_lazy_loader_t>
-{
-  static HB_LOCALE_T create ()
-  {
-    HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C");
-
-#if HB_USE_ATEXIT
-    atexit (free_static_C_locale);
-#endif
-
-    return C_locale;
-  }
-  static void destroy (HB_LOCALE_T p)
-  {
-    HB_FREE_LOCALE (p);
-  }
-  static HB_LOCALE_T get_null ()
-  {
-    return nullptr;
-  }
-} static_C_locale;
-
-#if HB_USE_ATEXIT
-static
-void free_static_C_locale ()
-{
-  static_C_locale.free_instance ();
-}
-#endif
-
-static HB_LOCALE_T
-get_C_locale ()
-{
-  return static_C_locale.get_unconst ();
-}
-#endif /* USE_XLOCALE */
-
-static bool
-parse_float (const char **pp, const char *end, float *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;
-  float v;
-
-  errno = 0;
-#ifdef USE_XLOCALE
-  v = strtod_l (p, &pend, get_C_locale ());
-#else
-  v = strtod (p, &pend);
-#endif
-  if (errno || p == pend)
-    return false;
-
-  *pv = v;
-  *pp += pend - p;
   return true;
 }
 
@@ -857,9 +750,14 @@
     (*pp)++;
 
   /* CSS allows on/off as aliases 1/0. */
-  if (*pp - p == 2 && 0 == strncmp (p, "on", 2))
+  if (*pp - p == 2
+      && TOLOWER (p[0]) == 'o'
+      && TOLOWER (p[1]) == 'n')
     *pv = 1;
-  else if (*pp - p == 3 && 0 == strncmp (p, "off", 3))
+  else if (*pp - p == 3
+	   && TOLOWER (p[0]) == 'o'
+	   && TOLOWER (p[1]) == 'f'
+	   && TOLOWER (p[2]) == 'f')
     *pv = 0;
   else
     return false;
@@ -948,7 +846,7 @@
 {
   bool had_equal = parse_char (pp, end, '=');
   bool had_value = parse_uint32 (pp, end, &feature->value) ||
-                   parse_bool (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 equal-sign is ok, but not required. */
@@ -974,7 +872,41 @@
  *
  * Parses a string into a #hb_feature_t.
  *
- * TODO: document the syntax here.
+ * The format for specifying feature strings follows. All valid CSS
+ * font-feature-settings values other than 'normal' and the global values are
+ * also accepted, though not documented below. CSS string escapes are not
+ * supported.
+ *
+ * The range indices refer to the positions between Unicode characters. The
+ * position before the first character is always 0.
+ *
+ * The format is Python-esque.  Here is how it all works:
+ *
+ * <informaltable pgwide='1' align='left' frame='none'>
+ * <tgroup cols='5'>
+ * <thead>
+ * <row><entry>Syntax</entry>    <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
+ * </thead>
+ * <tbody>
+ * <row><entry>Setting value:</entry></row>
+ * <row><entry>kern</entry>      <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
+ * <row><entry>+kern</entry>     <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
+ * <row><entry>-kern</entry>     <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
+ * <row><entry>kern=0</entry>    <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
+ * <row><entry>kern=1</entry>    <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
+ * <row><entry>aalt=2</entry>    <entry>2</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Choose 2nd alternate</entry></row>
+ * <row><entry>Setting index:</entry></row>
+ * <row><entry>kern[]</entry>    <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
+ * <row><entry>kern[:]</entry>   <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
+ * <row><entry>kern[5:]</entry>  <entry>1</entry>     <entry>5</entry>      <entry>∞</entry>   <entry>Turn feature on, partial</entry></row>
+ * <row><entry>kern[:5]</entry>  <entry>1</entry>     <entry>0</entry>      <entry>5</entry>   <entry>Turn feature on, partial</entry></row>
+ * <row><entry>kern[3:5]</entry> <entry>1</entry>     <entry>3</entry>      <entry>5</entry>   <entry>Turn feature on, range</entry></row>
+ * <row><entry>kern[3]</entry>   <entry>1</entry>     <entry>3</entry>      <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
+ * <row><entry>Mixing it all:</entry></row>
+ * <row><entry>aalt[3:5]=2</entry> <entry>2</entry>   <entry>3</entry>      <entry>5</entry>   <entry>Turn 2nd alternate on for range</entry></row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
  *
  * Return value:
  * %true if @str is successfully parsed, %false otherwise.
@@ -1032,21 +964,21 @@
   {
     s[len++] = '[';
     if (feature->start)
-      len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
+      len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
     if (feature->end != feature->start + 1) {
       s[len++] = ':';
       if (feature->end != (unsigned int) -1)
-	len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
+	len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
     }
     s[len++] = ']';
   }
   if (feature->value > 1)
   {
     s[len++] = '=';
-    len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
+    len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
   }
   assert (len < ARRAY_LENGTH (s));
-  len = MIN (len, size - 1);
+  len = hb_min (len, size - 1);
   memcpy (buf, s, len);
   buf[len] = '\0';
 }
@@ -1057,7 +989,11 @@
 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
 {
   parse_char (pp, end, '='); /* Optional. */
-  return parse_float (pp, end, &variation->value);
+  double v;
+  if (unlikely (!hb_parse_double (pp, end, &v))) return false;
+
+  variation->value = v;
+  return true;
 }
 
 static bool
@@ -1113,14 +1049,71 @@
   while (len && s[len - 1] == ' ')
     len--;
   s[len++] = '=';
-  len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
+  len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
 
   assert (len < ARRAY_LENGTH (s));
-  len = MIN (len, size - 1);
+  len = hb_min (len, size - 1);
   memcpy (buf, s, len);
   buf[len] = '\0';
 }
 
+/**
+ * hb_color_get_alpha:
+ * color: a #hb_color_t we are interested in its channels.
+ *
+ * Return value: Alpha channel value of the given color
+ *
+ * Since: 2.1.0
+ */
+uint8_t
+(hb_color_get_alpha) (hb_color_t color)
+{
+  return hb_color_get_alpha (color);
+}
+
+/**
+ * hb_color_get_red:
+ * color: a #hb_color_t we are interested in its channels.
+ *
+ * Return value: Red channel value of the given color
+ *
+ * Since: 2.1.0
+ */
+uint8_t
+(hb_color_get_red) (hb_color_t color)
+{
+  return hb_color_get_red (color);
+}
+
+/**
+ * hb_color_get_green:
+ * color: a #hb_color_t we are interested in its channels.
+ *
+ * Return value: Green channel value of the given color
+ *
+ * Since: 2.1.0
+ */
+uint8_t
+(hb_color_get_green) (hb_color_t color)
+{
+  return hb_color_get_green (color);
+}
+
+/**
+ * hb_color_get_blue:
+ * color: a #hb_color_t we are interested in its channels.
+ *
+ * Return value: Blue channel value of the given color
+ *
+ * Since: 2.1.0
+ */
+uint8_t
+(hb_color_get_blue) (hb_color_t color)
+{
+  return hb_color_get_blue (color);
+}
+
+
 /* If there is no visibility control, then hb-static.cc will NOT
  * define anything.  Instead, we get it to define one set in here
  * only, so only libharfbuzz.so defines them, not other libs. */
diff --git a/src/hb-common.h b/src/hb-common.h
index ae23698..037e508 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -63,11 +63,13 @@
 typedef unsigned __int32 uint32_t;
 typedef __int64 int64_t;
 typedef unsigned __int64 uint64_t;
+#elif defined (__KERNEL__)
+#  include <linux/types.h>
 #else
 #  include <stdint.h>
 #endif
 
-#if    __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+#if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
 #define HB_DEPRECATED __attribute__((__deprecated__))
 #elif defined(_MSC_VER) && (_MSC_VER >= 1300)
 #define HB_DEPRECATED __declspec(deprecated)
@@ -75,7 +77,7 @@
 #define HB_DEPRECATED
 #endif
 
-#if    __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
 #define HB_DEPRECATED_FOR(f) __attribute__((__deprecated__("Use '" #f "' instead")))
 #elif defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320)
 #define HB_DEPRECATED_FOR(f) __declspec(deprecated("is deprecated. Use '" #f "' instead"))
@@ -108,7 +110,7 @@
 typedef uint32_t hb_tag_t;
 
 #define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint32_t)(c1)&0xFF)<<24)|(((uint32_t)(c2)&0xFF)<<16)|(((uint32_t)(c3)&0xFF)<<8)|((uint32_t)(c4)&0xFF)))
-#define HB_UNTAG(tag)   (((tag)>>24)&0xFF), (((tag)>>16)&0xFF), (((tag)>>8)&0xFF), ((tag)&0xFF)
+#define HB_UNTAG(tag)   (uint8_t)(((tag)>>24)&0xFF), (uint8_t)(((tag)>>16)&0xFF), (uint8_t)(((tag)>>8)&0xFF), (uint8_t)((tag)&0xFF)
 
 #define HB_TAG_NONE HB_TAG(0,0,0,0)
 #define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff)
@@ -357,6 +359,14 @@
   /*11.0*/HB_SCRIPT_OLD_SOGDIAN			= HB_TAG ('S','o','g','o'),
   /*11.0*/HB_SCRIPT_SOGDIAN			= HB_TAG ('S','o','g','d'),
 
+  /*
+   * Since 2.4.0
+   */
+  /*12.0*/HB_SCRIPT_ELYMAIC			= HB_TAG ('E','l','y','m'),
+  /*12.0*/HB_SCRIPT_NANDINAGARI			= HB_TAG ('N','a','n','d'),
+  /*12.0*/HB_SCRIPT_NYIAKENG_PUACHUE_HMONG	= HB_TAG ('H','m','n','p'),
+  /*12.0*/HB_SCRIPT_WANCHO			= HB_TAG ('W','c','h','o'),
+
   /* No script set. */
   HB_SCRIPT_INVALID				= HB_TAG_NONE,
 
@@ -415,6 +425,21 @@
  */
 #define HB_FEATURE_GLOBAL_END	((unsigned int) -1)
 
+/**
+ * hb_feature_t:
+ * @tag: a feature tag
+ * @value: 0 disables the feature, non-zero (usually 1) enables the feature.
+ * For features implemented as lookup type 3 (like 'salt') the @value is a one
+ * based index into the alternates.
+ * @start: the cluster to start applying this feature setting (inclusive).
+ * @end: the cluster to end applying this feature setting (exclusive).
+ *
+ * The #hb_feature_t is the structure that holds information about requested
+ * feature application. The feature will be applied with the given value to all
+ * glyphs which are in clusters between @start (inclusive) and @end (exclusive).
+ * Setting start to @HB_FEATURE_GLOBAL_START and end to @HB_FEATURE_GLOBAL_END
+ * specifies that the feature always applies to the entire buffer.
+ */
 typedef struct hb_feature_t {
   hb_tag_t      tag;
   uint32_t      value;
@@ -459,39 +484,21 @@
 
 #define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a)))
 
-/**
- * hb_color_get_alpha:
- *
- *
- *
- * Since: 2.1.0
- */
+HB_EXTERN uint8_t
+hb_color_get_alpha (hb_color_t color);
 #define hb_color_get_alpha(color)	((color) & 0xFF)
-/**
- * hb_color_get_red:
- *
- *
- *
- * Since: 2.1.0
- */
-#define hb_color_get_red(color)		(((color) >> 8) & 0xFF)
-/**
- * hb_color_get_green:
- *
- *
- *
- * Since: 2.1.0
- */
-#define hb_color_get_green(color)	(((color) >> 16) & 0xFF)
-/**
- * hb_color_get_blue:
- *
- *
- *
- * Since: 2.1.0
- */
-#define hb_color_get_blue(color)	(((color) >> 24) & 0xFF)
 
+HB_EXTERN uint8_t
+hb_color_get_red (hb_color_t color);
+#define hb_color_get_red(color)		(((color) >> 8) & 0xFF)
+
+HB_EXTERN uint8_t
+hb_color_get_green (hb_color_t color);
+#define hb_color_get_green(color)	(((color) >> 16) & 0xFF)
+
+HB_EXTERN uint8_t
+hb_color_get_blue (hb_color_t color);
+#define hb_color_get_blue(color)	(((color) >> 24) & 0xFF)
 
 HB_END_DECLS
 
diff --git a/src/hb-config.hh b/src/hb-config.hh
new file mode 100644
index 0000000..14c5395
--- /dev/null
+++ b/src/hb-config.hh
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2019  Facebook, 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.
+ *
+ * Facebook Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_CONFIG_HH
+#define HB_CONFIG_HH
+
+#if 0 /* Make test happy. */
+#include "hb.hh"
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#ifdef HB_TINY
+#define HB_LEAN
+#define HB_MINI
+#define HB_NO_MT
+#define HB_NO_UCD_UNASSIGNED
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+#ifndef __OPTIMIZE_SIZE__
+#define __OPTIMIZE_SIZE__
+#endif
+#endif
+
+#ifdef HB_LEAN
+#define HB_DISABLE_DEPRECATED
+#define HB_NDEBUG
+#define HB_NO_ATEXIT
+#define HB_NO_BUFFER_MESSAGE
+#define HB_NO_BUFFER_SERIALIZE
+#define HB_NO_BITMAP
+#define HB_NO_CFF
+#define HB_NO_COLOR
+#define HB_NO_ERRNO
+#define HB_NO_FACE_COLLECT_UNICODES
+#define HB_NO_GETENV
+#define HB_NO_HINTING
+#define HB_NO_LANGUAGE_PRIVATE_SUBTAG
+#define HB_NO_LAYOUT_FEATURE_PARAMS
+#define HB_NO_LAYOUT_COLLECT_GLYPHS
+#define HB_NO_LAYOUT_UNUSED
+#define HB_NO_MATH
+#define HB_NO_META
+#define HB_NO_METRICS
+#define HB_NO_MMAP
+#define HB_NO_NAME
+#define HB_NO_OPEN
+#define HB_NO_SETLOCALE
+#define HB_NO_OT_FONT_GLYPH_NAMES
+#define HB_NO_OT_SHAPE_FRACTIONS
+#define HB_NO_STAT
+#define HB_NO_SUBSET_LAYOUT
+#define HB_NO_VAR
+#endif
+
+#ifdef HB_MINI
+#define HB_NO_AAT
+#define HB_NO_LEGACY
+#endif
+
+
+/* Closure of options. */
+
+#ifdef HB_DISABLE_DEPRECATED
+#define HB_IF_NOT_DEPRECATED(x)
+#else
+#define HB_IF_NOT_DEPRECATED(x) x
+#endif
+
+#ifdef HB_NO_AAT
+#define HB_NO_OT_NAME_LANGUAGE_AAT
+#define HB_NO_AAT_SHAPE
+#endif
+
+#ifdef HB_NO_BITMAP
+#define HB_NO_OT_FONT_BITMAP
+#endif
+
+#ifdef HB_NO_CFF
+#define HB_NO_OT_FONT_CFF
+#define HB_NO_SUBSET_CFF
+#endif
+
+#ifdef HB_NO_GETENV
+#define HB_NO_UNISCRIBE_BUG_COMPATIBLE
+#endif
+
+#ifdef HB_NO_LEGACY
+#define HB_NO_CMAP_LEGACY_SUBTABLES
+#define HB_NO_FALLBACK_SHAPE
+#define HB_NO_OT_KERN
+#define HB_NO_OT_LAYOUT_BLACKLIST
+#define HB_NO_OT_SHAPE_FALLBACK
+#endif
+
+#ifdef HB_NO_NAME
+#define HB_NO_OT_NAME_LANGUAGE
+#endif
+
+#ifdef HB_NO_OT
+#define HB_NO_OT_FONT
+#define HB_NO_OT_LAYOUT
+#define HB_NO_OT_TAG
+#define HB_NO_OT_SHAPE
+#endif
+
+#ifdef HB_NO_OT_SHAPE
+#define HB_NO_AAT_SHAPE
+#endif
+
+#ifdef HB_NO_OT_SHAPE_FALLBACK
+#define HB_NO_OT_SHAPE_COMPLEX_ARABIC_FALLBACK
+#define HB_NO_OT_SHAPE_COMPLEX_HEBREW_FALLBACK
+#define HB_NO_OT_SHAPE_COMPLEX_THAI_FALLBACK
+#define HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS
+#endif
+
+#ifdef NDEBUG
+#ifndef HB_NDEBUG
+#define HB_NDEBUG
+#endif
+#endif
+
+#ifdef __OPTIMIZE_SIZE__
+#ifndef HB_OPTIMIZE_SIZE
+#define HB_OPTIMIZE_SIZE
+#endif
+#endif
+
+#ifdef HAVE_CONFIG_OVERRIDE_H
+#include "config-override.h"
+#endif
+
+
+#endif /* HB_CONFIG_HH */
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc
index c181950..8885cfe 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -27,6 +27,9 @@
  */
 
 #include "hb.hh"
+
+#ifdef HAVE_CORETEXT
+
 #include "hb-shaper-impl.hh"
 
 #include "hb-coretext.h"
@@ -46,24 +49,6 @@
 /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
 #define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
 
-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)
 {
@@ -72,7 +57,7 @@
 }
 
 static hb_blob_t *
-reference_table  (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+_hb_cg_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
 {
   CGFontRef cg_font = reinterpret_cast<CGFontRef> (user_data);
   CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag);
@@ -126,7 +111,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, nullptr) == data);
+	  hb_blob_get_data ((hb_blob_t *) info, nullptr) == data);
 
   hb_blob_destroy ((hb_blob_t *) info);
 }
@@ -171,7 +156,7 @@
   if (CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSText")) ||
       CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSDisplay")))
   {
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
+#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1080
 # define kCTFontUIFontSystem kCTFontSystemFontType
 # define kCTFontUIFontEmphasizedSystem kCTFontEmphasizedSystemFontType
 #endif
@@ -214,7 +199,7 @@
   }
 
   CFURLRef original_url = nullptr;
-#if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
   ATSFontRef atsFont;
   FSRef fsref;
   OSStatus status;
@@ -244,25 +229,25 @@
        * process in Blink. This can be detected by the new file URL location
        * that the newly found font points to. */
       CFURLRef new_url = nullptr;
-#if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
       atsFont = CTFontGetPlatformFont (new_ct_font, NULL);
       status = ATSFontGetFileReference (atsFont, &fsref);
       if (status == noErr)
-        new_url = CFURLCreateFromFSRef (NULL, &fsref);
+	new_url = CFURLCreateFromFSRef (NULL, &fsref);
 #else
       new_url = (CFURLRef) CTFontCopyAttribute (new_ct_font, kCTFontURLAttribute);
 #endif
       // Keep reconfigured font if URL cannot be retrieved (seems to be the case
       // on Mac OS 10.12 Sierra), speculative fix for crbug.com/625606
       if (!original_url || !new_url || CFEqual (original_url, new_url)) {
-        CFRelease (ct_font);
-        ct_font = new_ct_font;
+	CFRelease (ct_font);
+	ct_font = new_ct_font;
       } else {
-        CFRelease (new_ct_font);
-        DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed.");
+	CFRelease (new_ct_font);
+	DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed.");
       }
       if (new_url)
-        CFRelease (new_url);
+	CFRelease (new_url);
     }
     else
       DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed");
@@ -296,7 +281,7 @@
 hb_face_t *
 hb_coretext_face_create (CGFontRef cg_font)
 {
-  return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), _hb_cg_font_release);
+  return hb_face_create_for_tables (_hb_cg_reference_table, CGFontRetain (cg_font), _hb_cg_font_release);
 }
 
 /*
@@ -317,7 +302,8 @@
   if (unlikely (!face_data)) return nullptr;
   CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext;
 
-  CTFontRef ct_font = create_ct_font (cg_font, coretext_font_size_from_ptem (font->ptem));
+  CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem);
+  CTFontRef ct_font = create_ct_font (cg_font, font_size);
 
   if (unlikely (!ct_font))
   {
@@ -341,7 +327,7 @@
   const hb_coretext_font_data_t *data = font->data.coretext;
   if (unlikely (!data)) return nullptr;
 
-  if (fabs (CTFontGetSize((CTFontRef) data) - coretext_font_size_from_ptem (font->ptem)) > .5)
+  if (fabs (CTFontGetSize ((CTFontRef) data) - (CGFloat) font->ptem) > .5)
   {
     /* XXX-MT-bug
      * Note that evaluating condition above can be dangerous if another thread
@@ -381,7 +367,7 @@
   if (unlikely (hb_object_is_immutable (font)))
     return font;
 
-  hb_font_set_ptem (font, coretext_font_size_to_ptem (CTFontGetSize(ct_font)));
+  hb_font_set_ptem (font, CTFontGetSize (ct_font));
 
   /* Let there be dragons here... */
   font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font));
@@ -410,7 +396,7 @@
   feature_record_t rec;
   unsigned int order;
 
-  static int cmp (const void *pa, const void *pb) {
+  HB_INTERNAL 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 :
@@ -428,7 +414,7 @@
   bool start;
   active_feature_t feature;
 
-  static int cmp (const void *pa, const void *pb) {
+  HB_INTERNAL 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 :
@@ -447,9 +433,9 @@
 hb_bool_t
 _hb_coretext_shape (hb_shape_plan_t    *shape_plan,
 		    hb_font_t          *font,
-                    hb_buffer_t        *buffer,
-                    const hb_feature_t *features,
-                    unsigned int        num_features)
+		    hb_buffer_t        *buffer,
+		    const hb_feature_t *features,
+		    unsigned int        num_features)
 {
   hb_face_t *face = font->face;
   CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext;
@@ -491,7 +477,7 @@
     {
       const hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag);
       if (!mapping)
-        continue;
+	continue;
 
       active_feature_t feature;
       feature.rec.feature = mapping->aatFeatureType;
@@ -527,22 +513,22 @@
     /* Scan events and save features for each range. */
     hb_vector_t<active_feature_t> active_features;
     unsigned int last_index = 0;
-    for (unsigned int i = 0; i < feature_events.len; i++)
+    for (unsigned int i = 0; i < feature_events.length; i++)
     {
       feature_event_t *event = &feature_events[i];
 
       if (event->index != last_index)
       {
-        /* Save a snapshot of active features and the range. */
+	/* Save a snapshot of active features and the range. */
 	range_record_t *range = range_records.push ();
 
-	if (active_features.len)
+	if (active_features.length)
 	{
 	  CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
 
 	  /* TODO sort and resolve conflicting features? */
 	  /* active_features.qsort (); */
-	  for (unsigned int j = 0; j < active_features.len; j++)
+	  for (unsigned int j = 0; j < active_features.length; j++)
 	  {
 	    CFStringRef keys[] = {
 	      kCTFontFeatureTypeIdentifierKey,
@@ -594,11 +580,11 @@
 
       if (event->start)
       {
-        active_features.push (event->feature);
+	active_features.push (event->feature);
       } else {
-        active_feature_t *feature = active_features.find (&event->feature);
+	active_feature_t *feature = active_features.find (&event->feature);
 	if (feature)
-	  active_features.remove (feature - active_features.arrayZ ());
+	  active_features.remove (feature - active_features.arrayZ);
       }
     }
   }
@@ -608,7 +594,7 @@
 
 #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \
   Type *name = (Type *) scratch; \
-  { \
+  do { \
     unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
     if (unlikely (_consumed > scratch_size)) \
     { \
@@ -617,7 +603,7 @@
     } \
     scratch += _consumed; \
     scratch_size -= _consumed; \
-  }
+  } while (0)
 
   ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/);
   unsigned int chars_len = 0;
@@ -649,7 +635,7 @@
     DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \
     ret = false; \
     goto fail; \
-  } HB_STMT_END;
+  } HB_STMT_END
 
   bool ret = true;
   CFStringRef string_ref = nullptr;
@@ -711,18 +697,18 @@
 /* What's the iOS equivalent of this check?
  * The symbols was introduced in iOS 7.0.
  * At any rate, our fallback is safe and works fine. */
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
+#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1090
 #  define kCTLanguageAttributeName CFSTR ("NSLanguage")
 #endif
-        CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault,
+	CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault,
 							    hb_language_to_string (buffer->props.language),
 							    kCFStringEncodingUTF8,
 							    kCFAllocatorNull);
 	if (unlikely (!lang))
-        {
+	{
 	  CFRelease (attr_string);
 	  FAIL ("CFStringCreateWithCStringNoCopy failed");
-        }
+	}
 	CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
 					kCTLanguageAttributeName, lang);
 	CFRelease (lang);
@@ -730,7 +716,7 @@
       CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
 				      kCTFontAttributeName, ct_font);
 
-      if (num_features && range_records.len)
+      if (num_features && range_records.length)
       {
 	unsigned int start = 0;
 	range_record_t *last_range = &range_records[0];
@@ -771,7 +757,7 @@
 	      feature.start < chars_len && feature.start < feature.end)
 	  {
 	    CFRange feature_range = CFRangeMake (feature.start,
-	                                         MIN (feature.end, chars_len) - feature.start);
+						 hb_min (feature.end, chars_len) - feature.start);
 	    if (feature.value)
 	      CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName);
 	    else
@@ -783,7 +769,7 @@
 
       int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
       CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level);
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
       extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel;
 #endif
       CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault,
@@ -795,8 +781,8 @@
       CFRelease (level_number);
       if (unlikely (!options))
       {
-        CFRelease (attr_string);
-        FAIL ("CFDictionaryCreate failed");
+	CFRelease (attr_string);
+	FAIL ("CFDictionaryCreate failed");
       }
 
       CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options);
@@ -879,7 +865,7 @@
 	 * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098
 	 */
 	bool matched = false;
-	for (unsigned int i = 0; i < range_records.len; i++)
+	for (unsigned int i = 0; i < range_records.length; i++)
 	  if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font))
 	  {
 	    matched = true;
@@ -907,7 +893,7 @@
 	if (!matched)
 	{
 	  CFRange range = CTRunGetStringRange (run);
-          DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld",
+	  DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld",
 		     range.location, range.location + range.length);
 	  if (!buffer->ensure_inplace (buffer->len + range.length))
 	    goto resize_and_retry;
@@ -935,7 +921,7 @@
 		  continue;
 	      }
 	      if (buffer->unicode->is_default_ignorable (ch))
-	        continue;
+		continue;
 
 	      info->codepoint = notdef;
 	      info->cluster = log_clusters[j];
@@ -977,10 +963,10 @@
 
 #define SCRATCH_RESTORE() \
   scratch_size = scratch_size_saved; \
-  scratch = scratch_saved;
+  scratch = scratch_saved
 
       { /* Setup glyphs */
-        SCRATCH_SAVE();
+	SCRATCH_SAVE();
 	const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : nullptr;
 	if (!glyphs) {
 	  ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry);
@@ -1003,12 +989,12 @@
 	SCRATCH_RESTORE();
       }
       {
-        /* Setup positions.
+	/* Setup positions.
 	 * Note that CoreText does not return advances for glyphs.  As such,
 	 * for all but last glyph, we use the delta position to next glyph as
 	 * 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();
+	SCRATCH_SAVE();
 	const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : nullptr;
 	if (!positions) {
 	  ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry);
@@ -1069,7 +1055,7 @@
     if (false)
     {
       /* Make sure all runs had the expected direction. */
-      bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
+      HB_UNUSED bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
       assert (bool (status_and & kCTRunStatusRightToLeft) == backward);
       assert (bool (status_or  & kCTRunStatusRightToLeft) == backward);
     }
@@ -1116,7 +1102,7 @@
 	unsigned int cluster = info[count - 1].cluster;
 	for (unsigned int i = count - 1; i > 0; i--)
 	{
-	  cluster = MIN (cluster, info[i - 1].cluster);
+	  cluster = hb_min (cluster, info[i - 1].cluster);
 	  info[i - 1].cluster = cluster;
 	}
       }
@@ -1125,7 +1111,7 @@
 	unsigned int cluster = info[0].cluster;
 	for (unsigned int i = 1; i < count; i++)
 	{
-	  cluster = MIN (cluster, info[i].cluster);
+	  cluster = hb_min (cluster, info[i].cluster);
 	  info[i].cluster = cluster;
 	}
       }
@@ -1142,7 +1128,7 @@
   if (line)
     CFRelease (line);
 
-  for (unsigned int i = 0; i < range_records.len; i++)
+  for (unsigned int i = 0; i < range_records.length; i++)
     if (range_records[i].font)
       CFRelease (range_records[i].font);
 
@@ -1150,57 +1136,4 @@
 }
 
 
-/*
- * AAT shaper
- */
-
-/*
- * shaper face data
- */
-
-struct hb_coretext_aat_face_data_t {};
-
-hb_coretext_aat_face_data_t *
-_hb_coretext_aat_shaper_face_data_create (hb_face_t *face)
-{
-  return hb_aat_layout_has_substitution (face) || hb_aat_layout_has_positioning (face) ?
-	 (hb_coretext_aat_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
-}
-
-void
-_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_face_data_t *data HB_UNUSED)
-{
-}
-
-
-/*
- * shaper font data
- */
-
-struct hb_coretext_aat_font_data_t {};
-
-hb_coretext_aat_font_data_t *
-_hb_coretext_aat_shaper_font_data_create (hb_font_t *font)
-{
-  return font->data.coretext ? (hb_coretext_aat_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr;
-}
-
-void
-_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_font_data_t *data HB_UNUSED)
-{
-}
-
-
-/*
- * shaper
- */
-
-hb_bool_t
-_hb_coretext_aat_shape (hb_shape_plan_t    *shape_plan,
-			hb_font_t          *font,
-			hb_buffer_t        *buffer,
-			const hb_feature_t *features,
-			unsigned int        num_features)
-{
-  return _hb_coretext_shape (shape_plan, font, buffer, features, num_features);
-}
+#endif
diff --git a/src/hb-debug.hh b/src/hb-debug.hh
index 41a85b5..a7e52c8 100644
--- a/src/hb-debug.hh
+++ b/src/hb-debug.hh
@@ -29,7 +29,7 @@
 
 #include "hb.hh"
 #include "hb-atomic.hh"
-#include "hb-dsalgs.hh"
+#include "hb-algs.hh"
 
 
 #ifndef HB_DEBUG
@@ -63,6 +63,9 @@
 static inline hb_options_t
 hb_options ()
 {
+#ifdef HB_NO_GETENV
+  return hb_options_t ();
+#endif
   /* Make a local copy, so we can access bitfield threadsafely. */
   hb_options_union_t u;
   u.i = _hb_options.get_relaxed ();
@@ -158,7 +161,7 @@
       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),
+	     bars + sizeof (bars) - 1 - hb_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);
@@ -246,8 +249,8 @@
 };
 
 template <>
-struct hb_printer_t<hb_void_t> {
-  const char *print (hb_void_t) { return ""; }
+struct hb_printer_t<hb_empty_t> {
+  const char *print (hb_empty_t) { return ""; }
 };
 
 
@@ -263,7 +266,7 @@
   }
 }
 template <>
-/*static*/ inline void _hb_warn_no_return<hb_void_t> (bool returned HB_UNUSED)
+/*static*/ inline void _hb_warn_no_return<hb_empty_t> (bool returned HB_UNUSED)
 {}
 
 template <int max_level, typename ret_t>
@@ -293,22 +296,23 @@
     if (plevel) --*plevel;
   }
 
-  ret_t ret (ret_t v,
-	     const char *func = "",
-	     unsigned int line = 0)
+  template <typename T>
+  T ret (T&& v,
+	 const char *func = "",
+	 unsigned int line = 0)
   {
     if (unlikely (returned)) {
       fprintf (stderr, "OUCH, double calls to return_trace().  This is a bug, please report.\n");
-      return v;
+      return hb_forward<T> (v);
     }
 
     _hb_debug_msg<max_level> (what, obj, func, true, plevel ? *plevel : 1, -1,
 			      "return %s (line %d)",
-			      hb_printer_t<ret_t>().print (v), line);
+			      hb_printer_t<decltype (v)>().print (v), line);
     if (plevel) --*plevel;
     plevel = nullptr;
     returned = true;
-    return v;
+    return hb_forward<T> (v);
   }
 
   private:
@@ -327,18 +331,20 @@
 				   const char *message,
 				   ...) HB_PRINTF_FUNC(6, 7) {}
 
-  ret_t ret (ret_t v,
-	     const char *func HB_UNUSED = nullptr,
-	     unsigned int line HB_UNUSED = 0) { return v; }
+  template <typename T>
+  T ret (T&& v,
+	 const char *func HB_UNUSED = nullptr,
+	 unsigned int line HB_UNUSED = 0) { return hb_forward<T> (v); }
 };
 
 /* For disabled tracing; optimize out everything.
  * https://github.com/harfbuzz/harfbuzz/pull/605 */
 template <typename ret_t>
 struct hb_no_trace_t {
-  ret_t ret (ret_t v,
-	     const char *func HB_UNUSED = "",
-	     unsigned int line HB_UNUSED = 0) { return v; }
+  template <typename T>
+  T ret (T&& v,
+	 const char *func HB_UNUSED = nullptr,
+	 unsigned int line HB_UNUSED = 0) { return hb_forward<T> (v); }
 };
 
 #define return_trace(RET) return trace.ret (RET, HB_FUNC, __LINE__)
@@ -401,30 +407,6 @@
 #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
@@ -432,7 +414,7 @@
 #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
@@ -444,7 +426,7 @@
 #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
@@ -456,39 +438,24 @@
 #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 + \
+	HB_DEBUG_SUBSET + \
 	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);
+	 "format %d", (int) format)
 #else
 #define TRACE_DISPATCH(this, format) hb_no_trace_t<typename context_t::return_t> trace
 #endif
diff --git a/src/hb-deprecated.h b/src/hb-deprecated.h
index 4a5e702..43f89a4 100644
--- a/src/hb-deprecated.h
+++ b/src/hb-deprecated.h
@@ -63,7 +63,7 @@
 					       hb_codepoint_t *glyph,
 					       void *user_data);
 
-HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func or hb_font_funcs_set_variation_glyph_func) void
+HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) void
 hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
 			      hb_font_get_glyph_func_t func,
 			      void *user_data, hb_destroy_func_t destroy);
@@ -165,30 +165,9 @@
 				    hb_codepoint_t     *decomposed);
 
 
-typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data,
-							   hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
-							   void *user_data);
-typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t;
 typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t;
 
 /**
- * hb_font_funcs_set_glyph_h_kerning_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
- *
- * 
- *
- * Since: 0.9.2
- * Deprecated: 2.0.0
- **/
-HB_EXTERN void
-hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs,
-					hb_font_get_glyph_h_kerning_func_t func,
-					void *user_data, hb_destroy_func_t destroy);
-
-/**
  * hb_font_funcs_set_glyph_v_kerning_func:
  * @ffuncs: font functions.
  * @func: (closure user_data) (destroy destroy) (scope notified):
@@ -206,19 +185,9 @@
 					void *user_data, hb_destroy_func_t destroy);
 
 HB_EXTERN hb_position_t
-hb_font_get_glyph_h_kerning (hb_font_t *font,
-			     hb_codepoint_t left_glyph, hb_codepoint_t right_glyph);
-HB_EXTERN hb_position_t
 hb_font_get_glyph_v_kerning (hb_font_t *font,
 			     hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph);
 
-HB_EXTERN void
-hb_font_get_glyph_kerning_for_direction (hb_font_t *font,
-					 hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
-					 hb_direction_t direction,
-					 hb_position_t *x, hb_position_t *y);
-
-
 #endif
 
 HB_END_DECLS
diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc
index e93cf90..efb2029 100644
--- a/src/hb-directwrite.cc
+++ b/src/hb-directwrite.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2015-2018  Ebrahim Byagowi
+ * Copyright © 2015-2019  Ebrahim Byagowi
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -23,13 +23,23 @@
  */
 
 #include "hb.hh"
+
+#ifdef HAVE_DIRECTWRITE
+
 #include "hb-shaper-impl.hh"
 
-#include <DWrite_1.h>
+#include <dwrite_1.h>
 
 #include "hb-directwrite.h"
 
 
+/* Declare object creator for dynamic support of DWRITE */
+typedef HRESULT (* WINAPI t_DWriteCreateFactory)(
+  DWRITE_FACTORY_TYPE factoryType,
+  REFIID              iid,
+  IUnknown            **factory
+);
+
 /*
  * hb-directwrite uses new/delete syntatically but as we let users
  * to override malloc/free, we will redefine new/delete so users
@@ -71,6 +81,8 @@
     *fontFileStream = mFontFileStream;
     return S_OK;
   }
+
+  virtual ~DWriteFontFileLoader() {}
 };
 
 class DWriteFontFileStream : public IDWriteFontFileStream
@@ -122,6 +134,8 @@
 
   virtual HRESULT STDMETHODCALLTYPE
   GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; }
+
+  virtual ~DWriteFontFileStream() {}
 };
 
 
@@ -131,10 +145,11 @@
 
 struct hb_directwrite_face_data_t
 {
+  HMODULE dwrite_dll;
   IDWriteFactory *dwriteFactory;
   IDWriteFontFile *fontFile;
-  IDWriteFontFileStream *fontFileStream;
-  IDWriteFontFileLoader *fontFileLoader;
+  DWriteFontFileStream *fontFileStream;
+  DWriteFontFileLoader *fontFileLoader;
   IDWriteFontFace *fontFace;
   hb_blob_t *faceBlob;
 };
@@ -146,12 +161,43 @@
   if (unlikely (!data))
     return nullptr;
 
-  // TODO: factory and fontFileLoader should be cached separately
-  IDWriteFactory* dwriteFactory;
-  DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory),
-		       (IUnknown**) &dwriteFactory);
+#define FAIL(...) \
+  HB_STMT_START { \
+    DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
+    return nullptr; \
+  } HB_STMT_END
+
+  data->dwrite_dll = LoadLibrary (TEXT ("DWRITE"));
+  if (unlikely (!data->dwrite_dll))
+    FAIL ("Cannot find DWrite.DLL");
+
+  t_DWriteCreateFactory p_DWriteCreateFactory;
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-function-type"
+#endif
+
+  p_DWriteCreateFactory = (t_DWriteCreateFactory)
+			  GetProcAddress (data->dwrite_dll, "DWriteCreateFactory");
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
+  if (unlikely (!p_DWriteCreateFactory))
+    FAIL ("Cannot find DWriteCreateFactory().");
 
   HRESULT hr;
+
+  // TODO: factory and fontFileLoader should be cached separately
+  IDWriteFactory* dwriteFactory;
+  hr = p_DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory),
+			      (IUnknown**) &dwriteFactory);
+
+  if (unlikely (hr != S_OK))
+    FAIL ("Failed to run DWriteCreateFactory().");
+
   hb_blob_t *blob = hb_face_reference_blob (face);
   DWriteFontFileStream *fontFileStream;
   fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr),
@@ -165,12 +211,6 @@
   hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey),
 						     fontFileLoader, &fontFile);
 
-#define FAIL(...) \
-  HB_STMT_START { \
-    DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
-    return nullptr; \
-  } HB_STMT_END;
-
   if (FAILED (hr))
     FAIL ("Failed to load font file from data!");
 
@@ -217,6 +257,8 @@
     delete data->fontFileStream;
   if (data->faceBlob)
     hb_blob_destroy (data->faceBlob);
+  if (data->dwrite_dll)
+    FreeLibrary (data->dwrite_dll);
   if (data)
     delete data;
 }
@@ -281,7 +323,7 @@
 public:
   TextAnalysis (const wchar_t* text, uint32_t textLength,
 		const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection)
-	       : mText (text), mTextLength (textLength), mLocaleName (localeName),
+	       : mTextLength (textLength), mText (text), mLocaleName (localeName),
 		 mReadingDirection (readingDirection), mCurrentRun (nullptr) {}
   ~TextAnalysis ()
   {
@@ -497,11 +539,6 @@
   Run  mRunHead;
 };
 
-static inline uint16_t hb_uint16_swap (const uint16_t v)
-{ return (v >> 8) | (v << 8); }
-static inline uint32_t hb_uint32_swap (const uint32_t v)
-{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
-
 /*
  * shaper
  */
@@ -516,7 +553,6 @@
 {
   hb_face_t *face = font->face;
   const hb_directwrite_face_data_t *face_data = face->data.directwrite;
-  const hb_directwrite_font_data_t *font_data = font->data.directwrite;
   IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
   IDWriteFontFace *fontFace = face_data->fontFace;
 
@@ -527,12 +563,12 @@
   hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
 #define ALLOCATE_ARRAY(Type, name, len) \
   Type *name = (Type *) scratch; \
-  { \
+  do { \
     unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
     assert (_consumed <= scratch_size); \
     scratch += _consumed; \
     scratch_size -= _consumed; \
-  }
+  } while (0)
 
 #define utf16_index() var1.u32
 
@@ -589,7 +625,7 @@
   HB_STMT_START { \
     DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
     return false; \
-  } HB_STMT_END;
+  } HB_STMT_END
 
   if (FAILED (hr))
     FAIL ("Analyzer failed to generate results.");
@@ -655,10 +691,10 @@
    * alignment needed after the WORD array.  sizeof (WORD) == 2. */
   unsigned int glyphs_size = (scratch_size * sizeof (int) - 2)
 			     / (sizeof (WORD) +
-			        sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) +
-			        sizeof (int) +
-			        sizeof (DWRITE_GLYPH_OFFSET) +
-			        sizeof (uint32_t));
+				sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) +
+				sizeof (int) +
+				sizeof (DWRITE_GLYPH_OFFSET) +
+				sizeof (uint32_t));
   ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
 
 #undef ALLOCATE_ARRAY
@@ -770,15 +806,15 @@
 
   /* Calculate visual-clusters.  That's what we ship. */
   for (unsigned int i = 0; i < glyphCount; i++)
-    vis_clusters[i] = -1;
+    vis_clusters[i] = (uint32_t) -1;
   for (unsigned int i = 0; i < buffer->len; i++)
   {
     uint32_t *p =
       &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]];
-    *p = MIN (*p, buffer->info[i].cluster);
+    *p = hb_min (*p, buffer->info[i].cluster);
   }
   for (unsigned int i = 1; i < glyphCount; i++)
-    if (vis_clusters[i] == -1)
+    if (vis_clusters[i] == (uint32_t) -1)
       vis_clusters[i] = vis_clusters[i - 1];
 
 #undef utf16_index
@@ -843,16 +879,12 @@
 				     features, num_features, 0);
 }
 
-/*
- * Public [experimental] API
- */
-
-hb_bool_t
-hb_directwrite_shape_experimental_width (hb_font_t          *font,
-					 hb_buffer_t        *buffer,
-					 const hb_feature_t *features,
-					 unsigned int        num_features,
-					 float               width)
+HB_UNUSED static bool
+_hb_directwrite_shape_experimental_width (hb_font_t          *font,
+					  hb_buffer_t        *buffer,
+					  const hb_feature_t *features,
+					  unsigned int        num_features,
+					  float               width)
 {
   static const char *shapers = "directwrite";
   hb_shape_plan_t *shape_plan;
@@ -865,3 +897,83 @@
 
   return res;
 }
+
+struct _hb_directwrite_font_table_context {
+  IDWriteFontFace *face;
+  void *table_context;
+};
+
+static void
+_hb_directwrite_table_data_release (void *data)
+{
+  _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) data;
+  context->face->ReleaseFontTable (context->table_context);
+  delete context;
+}
+
+static hb_blob_t *
+_hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+{
+  IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data);
+  const void *data;
+  uint32_t length;
+  void *table_context;
+  BOOL exists;
+  if (!dw_face || FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data,
+						    &length, &table_context, &exists)))
+    return nullptr;
+
+  if (!data || !exists || !length)
+  {
+    dw_face->ReleaseFontTable (table_context);
+    return nullptr;
+  }
+
+  _hb_directwrite_font_table_context *context = new _hb_directwrite_font_table_context;
+  context->face = dw_face;
+  context->table_context = table_context;
+
+  return hb_blob_create ((const char *) data, length, HB_MEMORY_MODE_READONLY,
+			 context, _hb_directwrite_table_data_release);
+}
+
+static void
+_hb_directwrite_font_release (void *data)
+{
+  if (data)
+    ((IDWriteFontFace *) data)->Release ();
+}
+
+/**
+ * hb_directwrite_face_create:
+ * @font_face: a DirectWrite IDWriteFontFace object.
+ *
+ * Return value: #hb_face_t object corresponding to the given input
+ *
+ * Since: 2.4.0
+ **/
+hb_face_t *
+hb_directwrite_face_create (IDWriteFontFace *font_face)
+{
+  if (font_face)
+    font_face->AddRef ();
+  return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face,
+				    _hb_directwrite_font_release);
+}
+
+/**
+* hb_directwrite_face_get_font_face:
+* @face: a #hb_face_t object
+*
+* Return value: DirectWrite IDWriteFontFace object corresponding to the given input
+*
+* Since: 2.5.0
+**/
+IDWriteFontFace *
+hb_directwrite_face_get_font_face (hb_face_t *face)
+{
+  return face->data.directwrite->fontFace;
+}
+
+
+#endif
diff --git a/src/hb-directwrite.h b/src/hb-directwrite.h
index 9bfd1f7..f837627 100644
--- a/src/hb-directwrite.h
+++ b/src/hb-directwrite.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2015  Ebrahim Byagowi
+ * Copyright © 2015-2019  Ebrahim Byagowi
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -29,10 +29,11 @@
 
 HB_BEGIN_DECLS
 
-HB_EXTERN hb_bool_t
-hb_directwrite_shape_experimental_width (hb_font_t *font, hb_buffer_t *buffer,
-					 const hb_feature_t *features,
-					 unsigned int num_features, float width);
+HB_EXTERN hb_face_t *
+hb_directwrite_face_create (IDWriteFontFace *font_face);
+
+HB_EXTERN IDWriteFontFace *
+hb_directwrite_face_get_font_face (hb_face_t *face);
 
 HB_END_DECLS
 
diff --git a/src/hb-dispatch.hh b/src/hb-dispatch.hh
new file mode 100644
index 0000000..1ce3fac
--- /dev/null
+++ b/src/hb-dispatch.hh
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
+ * Copyright © 2012,2018  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_DISPATCH_HH
+#define HB_DISPATCH_HH
+
+#include "hb.hh"
+
+/*