Merge pull request #2701 from googlefonts/Mark-To-Ligature_grieger

[subset] GPOS 5 MarkToLigature subsetting support
diff --git a/.ci/build-win32.sh b/.ci/build-win32.sh
index d4d7c81..38e633c 100755
--- a/.ci/build-win32.sh
+++ b/.ci/build-win32.sh
@@ -11,5 +11,5 @@
 find win32build -name '*.dll' -exec cp {} win32build/harfbuzz-win32 \;
 i686-w64-mingw32-strip win32build/harfbuzz-win32/*.{dll,exe}
 rm -f harfbuzz-win32.zip
-(cd win32build/harfbuzz-win32 && zip ../../harfbuzz-win32.zip -r .)
+(cd win32build && zip -r ../harfbuzz-win32.zip harfbuzz-win32)
 echo "harfbuzz-win32.zip is ready."
diff --git a/.circleci/config.yml b/.circleci/config.yml
index d7efa7e..26fa939 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -10,17 +10,6 @@
 
 jobs:
 
-  macos-10_12_6-aat-fonts:
-    macos:
-      xcode: "9.0.1"
-    steps:
-      - checkout
-      - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install pkg-config ragel freetype glib cairo python3 ninja
-      - run: pip3 install meson --upgrade
-      - run: meson build
-      - run: meson compile -Cbuild # or ninja -Cbuild
-      - run: meson test -Cbuild --print-errorlogs
-
   macos-10_13_6-aat-fonts:
     macos:
       xcode: "10.1.0"
@@ -31,6 +20,8 @@
       - run: meson build
       - run: meson compile -Cbuild
       - run: meson test -Cbuild --print-errorlogs
+      - store_artifacts:
+          path: build/meson-logs/
 
   macos-10_14_4-aat-fonts:
     macos:
@@ -42,6 +33,8 @@
       - run: PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig" meson build -Dcoretext=enabled
       - run: meson compile -Cbuild
       - run: meson test -Cbuild --print-errorlogs
+      - store_artifacts:
+          path: build/meson-logs/
 
   macos-10_15_3-aat-fonts:
     macos:
@@ -53,13 +46,15 @@
       - run: PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig" meson build -Dcoretext=enabled -Dgraphite=enabled -Dauto_features=enabled
       - run: meson compile -Cbuild
       - run: meson test -Cbuild --print-errorlogs
+      - store_artifacts:
+          path: build/meson-logs/
 
   # will be dropped with autotools removal
   distcheck:
     executor: autotools-executor
     steps:
       - checkout
-      - run: sudo apt update && DEBIAN_FRONTEND=noninteractive sudo apt install -y git ninja-build binutils libtool autoconf automake make gcc g++ pkg-config ragel gtk-doc-tools libfontconfig1-dev libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python3 python3-pip cmake
+      - run: sudo apt update && DEBIAN_FRONTEND=noninteractive sudo apt install -y git ninja-build binutils libtool autoconf automake make gcc g++ pkg-config ragel gtk-doc-tools gobject-introspection libfontconfig1-dev libfreetype6-dev libglib2.0-dev libgirepository1.0-dev libcairo2-dev libicu-dev libgraphite2-dev python3 python3-pip cmake
       - run: pip3 install fonttools meson --upgrade
       - run: ./autogen.sh
       - run: make -j32
@@ -90,7 +85,7 @@
       - run: meson build --buildtype=debugoptimized
       - run: ninja -Cbuild -j9
       # TOOD: increase timeouts and remove --no-suite=slow
-      - run: RUN_VALGRIND=1 HB_TEST_SHAPE_FUZZER_TIMEOUT=5 meson test -Cbuild --no-suite=slow --wrap='valgrind --leak-check=full --error-exitcode=1' --print-errorlogs
+      - run: RUN_VALGRIND=1 meson test -Cbuild --no-suite=slow --wrap='valgrind --leak-check=full --error-exitcode=1' --print-errorlogs
 
   alpine:
     docker:
@@ -110,7 +105,6 @@
       - checkout
       - run: pacman --noconfirm -Syu freetype2 meson git clang cairo icu gettext gobject-introspection gcc gcc-libs glib2 graphite pkg-config ragel python python-pip base-devel gtk-doc
       - run: pip install flake8 fonttools
-      - run: pip install git+https://github.com/mesonbuild/meson
       - run: flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
       - run: meson build -Dgraphite=enabled -Dauto_features=enabled -Dexperimental_api=true
       - run: meson compile -Cbuild -j9
@@ -147,7 +141,8 @@
     executor: win32-executor
     steps:
       - checkout
-      - run: sudo apt update && DEBIAN_FRONTEND=noninteractive sudo apt install -y ninja-build binutils meson gcc g++ pkg-config ragel gtk-doc-tools libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python3 python3-pip git g++-mingw-w64-i686 zip
+      - run: sudo apt update && DEBIAN_FRONTEND=noninteractive sudo apt install -y ninja-build binutils gcc g++ pkg-config ragel gtk-doc-tools libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python3 python3-pip git g++-mingw-w64-i686 zip
+      - run: pip3 install meson==0.56.0 --upgrade
       - run: .ci/build-win32.sh
       - store_artifacts:
           path: harfbuzz-win32.zip
@@ -170,7 +165,6 @@
 
   build:
     jobs:
-      - macos-10_12_6-aat-fonts
       - macos-10_13_6-aat-fonts
       - macos-10_14_4-aat-fonts
       - macos-10_15_3-aat-fonts
@@ -188,7 +182,7 @@
               ignore: /.*/
       - fedora-valgrind
       - alpine
-      - archlinux
+     #- archlinux
       - sanitizers
       - crossbuild-win32:
           filters:
diff --git a/.github/workflows/msys2-ci.yml b/.github/workflows/msys2-ci.yml
index ea4f15f..26219b0 100644
--- a/.github/workflows/msys2-ci.yml
+++ b/.github/workflows/msys2-ci.yml
@@ -39,9 +39,12 @@
             mingw-w64-${{ matrix.MSYS2_ARCH }}-meson
             mingw-w64-${{ matrix.MSYS2_ARCH }}-ninja
             mingw-w64-${{ matrix.MSYS2_ARCH }}-pkg-config
-            mingw-w64-${{ matrix.MSYS2_ARCH }}-python-fonttools
-            mingw-w64-${{ matrix.MSYS2_ARCH }}-python3
+            mingw-w64-${{ matrix.MSYS2_ARCH }}-python
+            mingw-w64-${{ matrix.MSYS2_ARCH }}-python-pip
             mingw-w64-${{ matrix.MSYS2_ARCH }}-ragel
+      - name: Install Python Dependencies
+        run: |
+          pip install --upgrade fonttools
       - name: Build
         run: |
           meson build \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5e0bc9f..1111859 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -233,7 +233,7 @@
 if (HB_HAVE_ICU)
   add_definitions(-DHAVE_ICU)
 
-  # https://github.com/WebKit/webkit/blob/master/Source/cmake/FindICU.cmake
+  # https://github.com/WebKit/webkit/blob/fdd7733f2f30eab7fe096a9791f98c60f62f49c0/Source/cmake/FindICU.cmake
   find_package(PkgConfig)
   pkg_check_modules(PC_ICU QUIET icu-uc)
 
@@ -302,6 +302,7 @@
 endif ()
 
 if (HB_HAVE_GOBJECT)
+  add_definitions(-DHAVE_GOBJECT)
   include (FindPerl)
 
   # Use the hints from glib-2.0.pc to find glib-mkenums
diff --git a/Makefile.am b/Makefile.am
index c27b005..f476c96 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -42,6 +42,7 @@
 	perf/texts/fa-thelittleprince.txt \
 	meson-cc-tests/intel-atomic-primitives-test.c \
 	meson-cc-tests/solaris-atomic-operations.c \
+	mingw-configure.sh \
 	$(NULL)
 
 MAINTAINERCLEANFILES = \
diff --git a/NEWS b/NEWS
index f211a37..f09c2fa 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,33 @@
+Overview of changes leading to 2.7.4
+Sunday, December 27, 2020
+====================================
+- Fix missing --enable-introspection configure option from previous release
+  tarball.
+- Documentation updates.
+
+Overview of changes leading to 2.7.3
+Wednesday, December 23, 2020
+====================================
+- Update USE shaper to 2020-08-13 specification, and other improvements.
+- Don’t disable liga feature in myanmar shaper, to match Uniscribe.
+- Improvements to language and script tags handling.
+- Update language system tag registry to OpenType 1.8.4
+- Support for serializing and deserializing Unicode buffers. Serialized buffers
+  are now delimited with `<>` or `[]` based on whether it is a Unicode or
+  glyphs buffer.
+- Increase buffer work limits to handle fonts with many complex lookups.
+- Handle more shaping operations in trace output.
+- Memory access fixes.
+- More OOM fixes.
+- Improved documentation.
+- Build system improvements.
+- New API:
++hb_buffer_has_positions()
++hb_buffer_serialize()
++hb_buffer_serialize_unicode()
++hb_buffer_deserialize_unicode()
+
+
 Overview of changes leading to 2.7.2
 Saturday, August 29, 2020
 ====================================
diff --git a/README.mingw.md b/README.mingw.md
index b9943d7..d9bd347 100644
--- a/README.mingw.md
+++ b/README.mingw.md
@@ -4,17 +4,33 @@
 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`.
+You want to follow the 32bit instructions. The 64bit equivalents are included
+for reference.
 
-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. Use `apt install g++-mingw-w64` on Debian.
+1. Install Wine.
+   - Fedora: `dnf install wine`.
 
-3. See how `.ci/build-win32.sh` uses meson or run that script anyway.
+2. Install `mingw-w64` compiler.
+   - Fedora, 32bit: `dnf install mingw32-gcc-c++`
+   - Fedora, 64bit: `dnf install mingw64-gcc-c++`
+   - Debian: `apt install g++-mingw-w64`
+   - Mac: `brew install mingw-w64`
 
-Now you can use hb-shape by `(cd win32build/harfbuzz-win32 && wine hb-shape.exe)`
-but if you like to shape with the Microsoft Uniscribe,
+3. If you have drank the `meson` koolaid, look at `.ci/build-win32.sh` to see how to
+   invoke `meson` now, or just run that script.  Otherwise, here's how to use the
+   old trusty autotools instead:
+
+   a) Install dependencies.
+      - Fedora, 32bit: `dnf install mingw32-glib2 mingw32-cairo mingw32-freetype`
+      - Fedora, 64bit: `dnf install mingw64-glib2 mingw64-cairo mingw64-freetype`
+
+   b) Configure:
+     - `NOCONFIGURE=1 ./autogen.sh && mkdir winbuild && cd winbuild`
+     - 32bit: `../mingw-configure.sh i686`
+     - 64bit: `../mingw-configure.sh x86_64`
+
+Now you can use `hb-shape` by `(cd win32build/util && wine hb-shape.exe)`
+but if you like to shape with the Microsoft Uniscribe:
 
 4. 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
diff --git a/configure.ac b/configure.ac
index a3c8a61..2e16b48 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [2.7.2],
+        [2.7.4],
         [https://github.com/harfbuzz/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -25,7 +25,7 @@
 AC_PROG_CXX
 AX_CXX_COMPILE_STDCXX(11)
 AC_SYS_LARGEFILE
-PKG_PROG_PKG_CONFIG([0.20])
+PKG_PROG_PKG_CONFIG([0.28])
 AM_MISSING_PROG([RAGEL], [ragel])
 AM_MISSING_PROG([GIT], [git])
 
@@ -239,25 +239,6 @@
 have_icu=false
 if test "x$with_icu" = "xyes" -o "x$with_icu" = "xbuiltin" -o "x$with_icu" = "xauto"; then
 	PKG_CHECK_MODULES(ICU, icu-uc, have_icu=true, :)
-
-	dnl Fallback to icu-config if ICU pkg-config files could not be found
-	if test "$have_icu" != "true"; then
-		AC_CHECK_TOOL(ICU_CONFIG, icu-config, no)
-		AC_MSG_CHECKING([for ICU by using icu-config fallback])
-		if test "$ICU_CONFIG" != "no" && "$ICU_CONFIG" --version >/dev/null; then
-			have_icu=true
-			# We don't use --cflags as this gives us a lot of things that we don't
-			# necessarily want, like debugging and optimization flags
-			# See man (1) icu-config for more info.
-			ICU_CFLAGS=`$ICU_CONFIG --cppflags`
-			ICU_LIBS=`$ICU_CONFIG --ldflags-searchpath --ldflags-libsonly`
-			AC_SUBST(ICU_CFLAGS)
-			AC_SUBST(ICU_LIBS)
-			AC_MSG_RESULT([yes])
-		else
-			AC_MSG_RESULT([no])
-		fi
-	fi
 fi
 if test \( "x$with_icu" = "xyes" -o "x$with_icu" = "xbuiltin" \) -a "x$have_icu" != "xtrue"; then
 	AC_MSG_ERROR([icu support requested but icu-uc not found])
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 987feb5..5c03209 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -29,15 +29,12 @@
 # Extra options to supply to gtkdoc-scan.
 # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
 SCAN_OPTIONS=--rebuild-types --deprecated-guards="HB_DISABLE_DEPRECATED" \
-	--ignore-decorators="HB_EXTERN"
+	--ignore-decorators='HB_EXTERN|HB_DEPRECATED'
 
 # 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 './*/*.h' | sed 's@^.*/@@'`
-if HAVE_GOBJECT
-else
 IGNORE_HFILES+=hb-gobject.h hb-gobject-enums.h hb-gobject-structs.h
-endif
 
 # Extra options to supply to gtkdoc-mkdb.
 # e.g. MKDB_OPTIONS=--xml-mode --output-format=xml
diff --git a/docs/harfbuzz-docs.xml b/docs/harfbuzz-docs.xml
index ea4c8ae..7f97189 100644
--- a/docs/harfbuzz-docs.xml
+++ b/docs/harfbuzz-docs.xml
@@ -26,7 +26,7 @@
     </abstract>
   </bookinfo>
 
-  <part>
+  <part id="user-manual">
     <title>User's manual</title>
       <xi:include href="usermanual-what-is-harfbuzz.xml"/>
       <xi:include href="usermanual-install-harfbuzz.xml"/>
@@ -41,7 +41,7 @@
       <xi:include href="usermanual-integration.xml"/>
   </part>
 
-  <part>
+  <part id="reference-manual">
     <partinfo>
       <releaseinfo>
         This document is for HarfBuzz &version;.
@@ -49,40 +49,9 @@
         <ulink role="online-location" url="http://[SERVER]/libharfbuzz/index.html">http://[SERVER]/libharfbuzz/</ulink>.-->
       </releaseinfo>
     </partinfo>
-
-    <note>
-      <para>
-        The current HarfBuzz codebase is versioned 2.x.x and is stable
-	and under active maintenance. This is what is used in latest
-	versions of Firefox, GNOME, ChromeOS, Chrome, LibreOffice,
-	XeTeX, Android, and KDE, among other places. 
-      </para>
-      <para>
-        Prior to 2012, the original HarfBuzz codebase (which, these
-	days, is referred to as <emphasis>harfbuzz-old</emphasis>) was 
-        derived from code in <ulink
-	url="http://freetype.org/">FreeType</ulink>, <ulink
-	url="http://pango.org/">Pango</ulink>, and 
-        <ulink url="http://qt-project.org/">Qt</ulink>.
-        It is <emphasis>not</emphasis> actively developed or
-	maintained, and is extremely buggy. All users of harfbuzz-old
-	are encouraged to switch over to the new HarfBuzz as soon as possible.
-      </para>
-      <para>
-	To make this distinction clearer in discussions, the current
-	HarfBuzz codebase is sometimes referred to as
-	<emphasis>harfbuzz-ng</emphasis>.
-      </para>
-      <para>
-	For reference purposes, the harfbuzz-old source tree is archived 
-        <ulink
-	    url="http://cgit.freedesktop.org/harfbuzz.old/">here</ulink>. There
-	are no release tarballs of harfbuzz-old whatsoever.
-      </para>
-    </note>
       
     <title>Reference manual</title>
-      <chapter>
+      <chapter id="core-api">
         <title>Core API</title>
         <xi:include href="xml/hb-blob.xml"/>
         <xi:include href="xml/hb-buffer.xml"/>
@@ -98,31 +67,34 @@
         <xi:include href="xml/hb-version.xml"/>
       </chapter>
 
-      <chapter>
+      <chapter id="opentype-api">
         <title>OpenType API</title>
         <xi:include href="xml/hb-ot-color.xml"/>
         <xi:include href="xml/hb-ot-font.xml"/>
         <xi:include href="xml/hb-ot-layout.xml"/>
         <xi:include href="xml/hb-ot-math.xml"/>
+        <xi:include href="xml/hb-ot-meta.xml"/>
+        <xi:include href="xml/hb-ot-metrics.xml"/>
         <xi:include href="xml/hb-ot-name.xml"/>
         <xi:include href="xml/hb-ot-shape.xml"/>
         <xi:include href="xml/hb-ot-var.xml"/>
       </chapter>
 
-      <chapter>
+      <chapter id="apple-advanced-typography-api">
         <title>Apple Advanced Typography API</title>
         <xi:include href="xml/hb-aat-layout.xml"/>
       </chapter>
 
-      <chapter>
+      <chapter id="integration-api">
         <title>Integration API</title>
         <xi:include href="xml/hb-coretext.xml"/>
         <xi:include href="xml/hb-ft.xml"/>
         <xi:include href="xml/hb-glib.xml"/>
-        <xi:include href="xml/hb-gobject.xml"/>
         <xi:include href="xml/hb-graphite2.xml"/>
         <xi:include href="xml/hb-icu.xml"/>
         <xi:include href="xml/hb-uniscribe.xml"/>
+        <xi:include href="xml/hb-gdi.xml"/>
+        <xi:include href="xml/hb-directwrite.xml"/>
       </chapter>
 
       <!--chapter id="object-tree">
@@ -133,6 +105,10 @@
       <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-7-3" role="2.7.3"><title>Index of new symbols in 2.7.3</title><xi:include href="xml/api-index-2.7.3.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-2-6-8" role="2.6.8"><title>Index of new symbols in 2.6.8</title><xi:include href="xml/api-index-2.6.8.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-2-6-5" role="2.6.5"><title>Index of new symbols in 2.6.5</title><xi:include href="xml/api-index-2.6.5.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-2-6-3" role="2.6.3"><title>Index of new symbols in 2.6.3</title><xi:include href="xml/api-index-2.6.3.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>
@@ -147,10 +123,12 @@
       <index id="api-index-1-8-0" role="1.8.0"><title>Index of new symbols in 1.8.0</title><xi:include href="xml/api-index-1.8.0.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-7-7" role="1.7.7"><title>Index of new symbols in 1.7.7</title><xi:include href="xml/api-index-1.7.7.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-7-5" role="1.7.5"><title>Index of new symbols in 1.7.5</title><xi:include href="xml/api-index-1.7.5.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-7-2" role="1.7.2"><title>Index of new symbols in 1.7.2</title><xi:include href="xml/api-index-1.7.2.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-6-0" role="1.6.0"><title>Index of new symbols in 1.6.0</title><xi:include href="xml/api-index-1.6.0.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-5-0" role="1.5.0"><title>Index of new symbols in 1.5.0</title><xi:include href="xml/api-index-1.5.0.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-4-3" role="1.4.3"><title>Index of new symbols in 1.4.3</title><xi:include href="xml/api-index-1.4.3.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-4-2" role="1.4.2"><title>Index of new symbols in 1.4.2</title><xi:include href="xml/api-index-1.4.2.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-1-4-0" role="1.4.0"><title>Index of new symbols in 1.4.0</title><xi:include href="xml/api-index-1.4.0.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-3-3" role="1.3.3"><title>Index of new symbols in 1.3.3</title><xi:include href="xml/api-index-1.3.3.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-2-3" role="1.2.3"><title>Index of new symbols in 1.2.3</title><xi:include href="xml/api-index-1.2.3.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-1-1-3" role="1.1.3"><title>Index of new symbols in 1.1.3</title><xi:include href="xml/api-index-1.1.3.xml"><xi:fallback /></xi:include></index>
@@ -160,10 +138,13 @@
       <index id="api-index-0-9-41" role="0.9.41"><title>Index of new symbols in 0.9.41</title><xi:include href="xml/api-index-0.9.41.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-0-9-39" role="0.9.39"><title>Index of new symbols in 0.9.39</title><xi:include href="xml/api-index-0.9.39.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-0-9-38" role="0.9.38"><title>Index of new symbols in 0.9.38</title><xi:include href="xml/api-index-0.9.38.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-33" role="0.9.33"><title>Index of new symbols in 0.9.33</title><xi:include href="xml/api-index-0.9.33.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-0-9-31" role="0.9.31"><title>Index of new symbols in 0.9.31</title><xi:include href="xml/api-index-0.9.31.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-0-9-30" role="0.9.30"><title>Index of new symbols in 0.9.30</title><xi:include href="xml/api-index-0.9.30.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-0-9-28" role="0.9.28"><title>Index of new symbols in 0.9.28</title><xi:include href="xml/api-index-0.9.28.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-26" role="0.9.26"><title>Index of new symbols in 0.9.26</title><xi:include href="xml/api-index-0.9.26.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-0-9-22" role="0.9.22"><title>Index of new symbols in 0.9.22</title><xi:include href="xml/api-index-0.9.22.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-9-21" role="0.9.21"><title>Index of new symbols in 0.9.21</title><xi:include href="xml/api-index-0.9.21.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-0-9-20" role="0.9.20"><title>Index of new symbols in 0.9.20</title><xi:include href="xml/api-index-0.9.20.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-0-9-11" role="0.9.11"><title>Index of new symbols in 0.9.11</title><xi:include href="xml/api-index-0.9.11.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-0-9-10" role="0.9.10"><title>Index of new symbols in 0.9.10</title><xi:include href="xml/api-index-0.9.10.xml"><xi:fallback /></xi:include></index>
@@ -171,7 +152,37 @@
       <index id="api-index-0-9-7" role="0.9.7"><title>Index of new symbols in 0.9.7</title><xi:include href="xml/api-index-0.9.7.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-0-9-5" role="0.9.5"><title>Index of new symbols in 0.9.5</title><xi:include href="xml/api-index-0.9.5.xml"><xi:fallback /></xi:include></index>
       <index id="api-index-0-9-2" role="0.9.2"><title>Index of new symbols in 0.9.2</title><xi:include href="xml/api-index-0.9.2.xml"><xi:fallback /></xi:include></index>
+      <index id="api-index-0-6-0" role="0.6.0"><title>Index of new symbols in 0.6.0</title><xi:include href="xml/api-index-0.6.0.xml"><xi:fallback /></xi:include></index>
 
       <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
   </part>
+
+  <note>
+    <para>
+      The current HarfBuzz codebase is versioned 2.x.x and is stable
+      and under active maintenance. This is what is used in latest
+      versions of Firefox, GNOME, ChromeOS, Chrome, LibreOffice,
+      XeTeX, Android, and KDE, among other places.
+    </para>
+    <para>
+      Prior to 2012, the original HarfBuzz codebase (which, these days, is
+      referred to as <emphasis>harfbuzz-old</emphasis>) was derived from code
+      in <ulink url="http://freetype.org/">FreeType</ulink>,
+      <ulink url="http://pango.org/">Pango</ulink>, and
+      <ulink url="http://qt-project.org/">Qt</ulink>.
+      It is <emphasis>not</emphasis> actively developed or  maintained, and is
+      extremely buggy. All users of harfbuzz-old are encouraged to switch over
+      to the new HarfBuzz as soon as possible.
+    </para>
+    <para>
+      To make this distinction clearer in discussions, the current HarfBuzz
+      codebase is sometimes referred to as <emphasis>harfbuzz-ng</emphasis>.
+    </para>
+    <para>
+      For reference purposes, the harfbuzz-old source tree is archived
+      <ulink url="http://cgit.freedesktop.org/harfbuzz.old/">here</ulink>.
+      There are no release tarballs of harfbuzz-old whatsoever.
+    </para>
+  </note>
+
 </book>
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index fe9b89c..6b58662 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -80,6 +80,7 @@
 hb_buffer_get_user_data
 hb_buffer_get_glyph_infos
 hb_buffer_get_glyph_positions
+hb_buffer_has_positions
 hb_buffer_get_invisible_glyph
 hb_buffer_set_invisible_glyph
 hb_buffer_set_replacement_codepoint
@@ -88,8 +89,11 @@
 hb_buffer_reverse
 hb_buffer_reverse_range
 hb_buffer_reverse_clusters
+hb_buffer_serialize
 hb_buffer_serialize_glyphs
 hb_buffer_deserialize_glyphs
+hb_buffer_serialize_unicode
+hb_buffer_deserialize_unicode
 hb_buffer_serialize_format_from_string
 hb_buffer_serialize_format_to_string
 hb_buffer_serialize_list_formats
@@ -141,7 +145,6 @@
 hb_tag_t
 hb_script_t
 hb_user_data_key_t
-hb_var_int_t
 HB_TAG
 HB_TAG_NONE
 HB_TAG_MAX
@@ -159,6 +162,7 @@
 <SUBSECTION Private>
 HB_BEGIN_DECLS
 HB_END_DECLS
+hb_var_int_t
 int16_t
 int32_t
 int64_t
@@ -325,7 +329,6 @@
 hb_font_get_user_data
 hb_font_get_variation_glyph
 hb_font_get_variation_glyph_func_t
-hb_font_get_var_coords_design
 hb_font_get_var_coords_normalized
 hb_font_glyph_from_string
 hb_font_glyph_to_string
@@ -355,6 +358,11 @@
 hb_font_get_font_v_extents_func_t
 hb_font_get_h_extents
 hb_font_get_v_extents
+hb_font_extents_t
+hb_glyph_extents_t
+<SUBSECTION Private>
+hb_font_get_var_coords_design
+hb_font_draw_glyph
 </SECTION>
 
 <SECTION>
@@ -387,78 +395,6 @@
 </SECTION>
 
 <SECTION>
-<FILE>hb-gobject</FILE>
-HB_GOBJECT_TYPE_BLOB
-HB_GOBJECT_TYPE_BUFFER
-HB_GOBJECT_TYPE_BUFFER_CONTENT_TYPE
-HB_GOBJECT_TYPE_BUFFER_DIFF_FLAGS
-HB_GOBJECT_TYPE_BUFFER_FLAGS
-HB_GOBJECT_TYPE_BUFFER_SERIALIZE_FLAGS
-HB_GOBJECT_TYPE_BUFFER_SERIALIZE_FORMAT
-HB_GOBJECT_TYPE_DIRECTION
-HB_GOBJECT_TYPE_FACE
-HB_GOBJECT_TYPE_FONT
-HB_GOBJECT_TYPE_FONT_FUNCS
-HB_GOBJECT_TYPE_GLYPH_FLAGS
-HB_GOBJECT_TYPE_MAP
-HB_GOBJECT_TYPE_MEMORY_MODE
-HB_GOBJECT_TYPE_OT_COLOR_PALETTE_FLAGS
-HB_GOBJECT_TYPE_OT_LAYOUT_GLYPH_CLASS
-HB_GOBJECT_TYPE_OT_MATH_CONSTANT
-HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART
-HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART_FLAGS
-HB_GOBJECT_TYPE_OT_MATH_GLYPH_VARIANT
-HB_GOBJECT_TYPE_OT_MATH_KERN
-HB_GOBJECT_TYPE_SCRIPT
-HB_GOBJECT_TYPE_SHAPE_PLAN
-HB_GOBJECT_TYPE_UNICODE_COMBINING_CLASS
-HB_GOBJECT_TYPE_UNICODE_FUNCS
-HB_GOBJECT_TYPE_UNICODE_GENERAL_CATEGORY
-HB_GOBJECT_TYPE_BUFFER_CLUSTER_LEVEL
-HB_GOBJECT_TYPE_FEATURE
-HB_GOBJECT_TYPE_GLYPH_INFO
-HB_GOBJECT_TYPE_GLYPH_POSITION
-HB_GOBJECT_TYPE_SEGMENT_PROPERTIES
-HB_GOBJECT_TYPE_SET
-HB_GOBJECT_TYPE_USER_DATA_KEY
-hb_gobject_blob_get_type
-hb_gobject_buffer_content_type_get_type
-hb_gobject_buffer_diff_flags_get_type
-hb_gobject_buffer_flags_get_type
-hb_gobject_buffer_get_type
-hb_gobject_buffer_serialize_flags_get_type
-hb_gobject_buffer_serialize_format_get_type
-hb_gobject_direction_get_type
-hb_gobject_face_get_type
-hb_gobject_font_funcs_get_type
-hb_gobject_font_get_type
-hb_gobject_glyph_flags_get_type
-hb_gobject_map_get_type
-hb_gobject_memory_mode_get_type
-hb_gobject_ot_color_palette_flags_get_type
-hb_gobject_ot_layout_glyph_class_get_type
-hb_gobject_ot_math_constant_get_type
-hb_gobject_ot_math_glyph_part_get_type
-hb_gobject_ot_math_glyph_part_flags_get_type
-hb_gobject_ot_math_glyph_variant_get_type
-hb_gobject_ot_math_kern_get_type
-hb_gobject_script_get_type
-hb_gobject_shape_plan_get_type
-hb_gobject_unicode_combining_class_get_type
-hb_gobject_unicode_funcs_get_type
-hb_gobject_unicode_general_category_get_type
-hb_gobject_buffer_cluster_level_get_type
-hb_gobject_feature_get_type
-hb_gobject_glyph_info_get_type
-hb_gobject_glyph_position_get_type
-hb_gobject_segment_properties_get_type
-hb_gobject_set_get_type
-hb_gobject_user_data_key_get_type
-<SUBSECTION Private>
-HB_GOBJECT_H_IN
-</SECTION>
-
-<SECTION>
 <FILE>hb-graphite2</FILE>
 HB_GRAPHITE2_TAG_SILF
 hb_graphite2_face_get_gr_face
@@ -565,7 +501,6 @@
 hb_ot_layout_get_ligature_carets
 hb_ot_layout_get_size_params
 hb_ot_layout_glyph_class_t
-hb_ot_layout_glyph_sequence_func_t
 hb_ot_layout_has_glyph_classes
 hb_ot_layout_has_positioning
 hb_ot_layout_has_substitution
@@ -592,6 +527,8 @@
 Xhb_ot_layout_lookup_enumerate_sequences
 Xhb_ot_layout_lookup_position
 Xhb_ot_layout_lookup_substitute
+hb_ot_layout_glyph_sequence_t
+hb_ot_layout_glyph_sequence_func_t
 </SECTION>
 
 <SECTION>
diff --git a/docs/meson.build b/docs/meson.build
index f3bf460..73d9521 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -40,23 +40,20 @@
   'HarfBuzz.svg',
 ]
 
-ignore_headers = []
-if not have_gobject
-  ignore_headers += [
-    'hb-gobject.h',
-    'hb-gobject-enums.h',
-    'hb-gobject-enums-tmp.h',
-    'hb-gobject-structs.h',
-  ]
-endif
+ignore_headers = [
+  'hb-gobject.h',
+  'hb-gobject-enums.h',
+  'hb-gobject-enums-tmp.h',
+  'hb-gobject-structs.h',
+]
 
 gnome.gtkdoc('harfbuzz',
   main_sgml: 'harfbuzz-docs.xml',
-  src_dir: [join_paths(meson.current_source_dir(), '..'),
-            join_paths(meson.current_build_dir(), '..'),
+  src_dir: [join_paths(meson.current_source_dir(), '../src'),
+            join_paths(meson.current_build_dir(), '../src'),
            ],
   scan_args: ['--deprecated-guards=HB_DISABLE_DEPRECATED',
-              '--ignore-decorators=HB_EXTERN',
+              '--ignore-decorators=HB_EXTERN|HB_DEPRECATED',
              ],
   mkdb_args: ['--source-suffixes=h,cc',
               '--xml-mode',
@@ -65,5 +62,5 @@
   content_files: content_files,
   html_assets: html_images,
   ignore_headers: ignore_headers,
-  dependencies: [libharfbuzz_dep, libharfbuzz_gobject_dep],
+  dependencies: [libharfbuzz_dep],
   install: true)
diff --git a/docs/usermanual-buffers-language-script-and-direction.xml b/docs/usermanual-buffers-language-script-and-direction.xml
index d98c79b..0235d2d 100644
--- a/docs/usermanual-buffers-language-script-and-direction.xml
+++ b/docs/usermanual-buffers-language-script-and-direction.xml
@@ -136,10 +136,12 @@
       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
+      The safest approach is to add all of the text available (even
+      if your text contains a mix of scripts, directions, languages
+      and fonts), 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.
+      want shaped (which must all have the same script, direction,
+      language and font), so that HarfBuzz has access to any context.
     </para>
     <para>
       You can also add Unicode code points directly with
diff --git a/docs/usermanual-fonts-and-faces.xml b/docs/usermanual-fonts-and-faces.xml
index 1258bec..abf5dc2 100644
--- a/docs/usermanual-fonts-and-faces.xml
+++ b/docs/usermanual-fonts-and-faces.xml
@@ -260,18 +260,18 @@
       </listitem>
     </itemizedlist>
     <para>
-      You can fetch the font-functions configuration for a font object
-      by calling <function>hb_font_get_font_funcs()</function>:
+      You can create new font-functions by calling
+      <function>hb_font_funcs_create()</function>:
     </para>
     <programlisting language="C">
-      hb_font_funcs_t *ffunctions;
-      ffunctions = hb_font_get_font_funcs (font);
+      hb_font_funcs_t *ffunctions = hb_font_funcs_create ();
+      hb_font_set_funcs (font, ffunctions, font_data, destroy);
     </programlisting>
     <para>
-      The individual methods can each be replaced with their own setter
+      The individual methods can each be set with their own setter
       function, such as
-      <function>hb_font_funcs_set_nominal_glyph_func(*ffunctions,
-      func, *user_data, destroy)</function>. 
+      <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
@@ -291,6 +291,20 @@
       programs from changing the configuration and introducing
       inconsistencies and errors downstream.
     </para>
+    <para>
+      To override only some functions while using the default implementation
+      for the others, you will need to create a sub-font. By default, the
+      sub-font uses the font functions of its parent except for the functions
+      that were explicitly set. The following code will override only the
+      <function>hb_font_get_nominal_glyph_func_t</function> for the sub-font:
+    </para>
+    <programlisting language="C">
+      hb_font_t *subfont = hb_font_create_sub_font (font)
+      hb_font_funcs_t *ffunctions = hb_font_funcs_create ();
+      hb_font_funcs_set_nominal_glyph_func (ffunctions, func, user_data, destroy);
+      hb_font_set_funcs (subfont, ffunctions, font_data, destroy);
+      hb_font_funcs_destroy (ffunctions);
+    </programlisting>
   </section>
 
   <section id="fonts-and-faces-native-opentype">
diff --git a/docs/usermanual-getting-started.xml b/docs/usermanual-getting-started.xml
index 1f26df8..e0df4a5 100644
--- a/docs/usermanual-getting-started.xml
+++ b/docs/usermanual-getting-started.xml
@@ -6,7 +6,7 @@
 ]>
 <chapter id="getting-started">
   <title>Getting started with HarfBuzz</title>
-  <section>
+  <section id="an-overview-of-the-harfbuzz-shaping-api">
     <title>An overview of the HarfBuzz shaping API</title>
     <para>
       The core of the HarfBuzz shaping API is the function
@@ -73,7 +73,7 @@
     </para>
   </section>
 
-  <section>
+  <section id="terminology">
     <title>Terminology</title>
     <para>
       
@@ -201,7 +201,7 @@
   </section>
 
 
-  <section>
+  <section id="a-simple-shaping-example">
     <title>A simple shaping example</title>
 
     <para>
@@ -216,6 +216,7 @@
     </orderedlist>
     <programlisting language="C">
       #include &lt;hb.h&gt;
+
       hb_buffer_t *buf;
       buf = hb_buffer_create();
       hb_buffer_add_utf8(buf, text, -1, 0, -1);
@@ -235,15 +236,14 @@
     <orderedlist numeration="arabic">
       <listitem override="3">
 	<para>
-          Create a face and a font, using FreeType for now.
+          Create a face and a font from a font file.
 	</para>
       </listitem>
     </orderedlist>
     <programlisting language="C">
-      #include &lt;hb-ft.h&gt;
-      FT_New_Face(ft_library, font_path, index, &amp;face);
-      FT_Set_Char_Size(face, 0, 1000, 0, 0);
-      hb_font_t *font = hb_ft_font_create(face);
+      hb_blob_t *blob = hb_blob_create_from_file(filename);
+      hb_face_t *face = hb_face_create(blob, 0);
+      hb_font_t *font = hb_font_create(face);
     </programlisting>
     <orderedlist numeration="arabic">
       <listitem override="4">
@@ -263,6 +263,7 @@
       </listitem>
     </orderedlist>
     <programlisting language="C">
+      unsigned int glyph_count;
       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>
@@ -274,13 +275,15 @@
       </listitem>
     </orderedlist>
     <programlisting language="C">
-      for (i = 0; i &lt; glyph_count; ++i) {
-          glyphid = glyph_info[i].codepoint;
-          x_offset = glyph_pos[i].x_offset / 64.0;
-          y_offset = glyph_pos[i].y_offset / 64.0;
-          x_advance = glyph_pos[i].x_advance / 64.0;
-          y_advance = glyph_pos[i].y_advance / 64.0;
-          draw_glyph(glyphid, cursor_x + x_offset, cursor_y + y_offset);
+      hb_position_t cursor_x = 0;
+      hb_position_t cursor_y = 0;
+      for (unsigned int i = 0; i &lt; glyph_count; i++) {
+          hb_codepoint_t glyphid  = glyph_info[i].codepoint;
+          hb_position_t x_offset  = glyph_pos[i].x_offset;
+          hb_position_t y_offset  = glyph_pos[i].y_offset;
+          hb_position_t x_advance = glyph_pos[i].x_advance;
+          hb_position_t y_advance = glyph_pos[i].y_advance;
+       /* draw_glyph(glyphid, cursor_x + x_offset, cursor_y + y_offset); */
           cursor_x += x_advance;
           cursor_y += y_advance;
       }
@@ -294,7 +297,9 @@
     </orderedlist>
     <programlisting language="C">
       hb_buffer_destroy(buf);
-      hb_font_destroy(hb_ft_font);
+      hb_font_destroy(font);
+      hb_face_destroy(face);
+      hb_blob_destroy(blob);
     </programlisting>
     
     <para>
diff --git a/docs/usermanual-utilities.xml b/docs/usermanual-utilities.xml
index 6e30c46..0208dbf 100644
--- a/docs/usermanual-utilities.xml
+++ b/docs/usermanual-utilities.xml
@@ -10,8 +10,7 @@
     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.
+    client programs.
   </para>
   
   <section id="utilities-command-line-tools">
@@ -216,29 +215,4 @@
     </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 3513fb2..4534783 100644
--- a/docs/usermanual-what-is-harfbuzz.xml
+++ b/docs/usermanual-what-is-harfbuzz.xml
@@ -226,7 +226,7 @@
   </section>
   
 
-  <section>
+  <section id="what-does-harfbuzz-do">
     <title>What does HarfBuzz do?</title>
     <para>
       HarfBuzz provides text shaping through a cross-platform
diff --git a/meson.build b/meson.build
index bf3925d..83c1799 100644
--- a/meson.build
+++ b/meson.build
@@ -1,6 +1,6 @@
 project('harfbuzz', 'c', 'cpp',
   meson_version: '>= 0.47.0',
-  version: '2.7.2',
+  version: '2.7.4',
   default_options: [
     'cpp_eh=none',          # Just to support msvc, we are passing -fno-rtti also anyway
     'cpp_rtti=false',       # Just to support msvc, we are passing -fno-exceptions also anyway
@@ -127,34 +127,31 @@
 cairo_ft_dep = null_dep
 if not get_option('cairo').disabled()
   cairo_dep = dependency('cairo', required: false)
+  cairo_ft_dep = dependency('cairo-ft', required: false)
 
   if (not cairo_dep.found() and
       cpp.get_id() == 'msvc' and
       cpp.has_header('cairo.h'))
     cairo_dep = cpp.find_library('cairo', required: false)
+    if cairo_dep.found() and cpp.has_function('cairo_ft_font_face_create_for_ft_face',
+                                              prefix: '#include <cairo-ft.h>',
+                                              dependencies: cairo_dep)
+      cairo_ft_dep = cairo_dep
+    endif
   endif
 
   if not cairo_dep.found()
-    # Note that we don't have harfbuzz -> cairo -> freetype2 -> harfbuzz fallback
-    # dependency cycle here because we have configured freetype2 above with
-    # harfbuzz support disabled, so when cairo will lookup freetype2 dependency
-    # it will be forced to use that one.
-    cairo_dep = dependency('cairo', fallback: ['cairo', 'libcairo_dep'],
-                           required: get_option('cairo'))
-  endif
-
-  # Ensure that cairo-ft is fetched from the same library as cairo itself
-  if cairo_dep.found()
-    if cairo_dep.type_name() == 'internal'
-      # It is true at least for the port we have
-      cairo_ft_dep = cairo_dep
-    elif (cairo_dep.type_name() == 'library' and
-          cpp.has_function('cairo_ft_font_face_create_for_ft_face',
-                           prefix: '#include <cairo-ft.h>',
-                           dependencies: cairo_dep))
-      cairo_ft_dep = cairo_dep
-    else # including the most important type for us, 'pkgconfig'
-      cairo_ft_dep = dependency('cairo-ft', required: get_option('cairo'))
+    # Requires Meson 0.54.0 to use cairo subproject
+    if meson.version().version_compare('>=0.54.0')
+      # Note that we don't have harfbuzz -> cairo -> freetype2 -> harfbuzz fallback
+      # dependency cycle here because we have configured freetype2 above with
+      # harfbuzz support disabled, so when cairo will lookup freetype2 dependency
+      # it will be forced to use that one.
+      cairo_dep = dependency('cairo', fallback: 'cairo', required: get_option('cairo'))
+      cairo_ft_dep = dependency('cairo-ft', fallback: 'cairo', required: get_option('cairo'))
+    elif get_option('cairo').enabled()
+      error('cairo feature is enabled but it cannot be found on the system and ' +
+            'meson>=0.54.0 is required to build it as subproject')
     endif
   endif
 endif
@@ -355,16 +352,8 @@
   subdir('test')
 endif
 
-# get_option('wrap_mode') isn't available in <0.49 and this
-# is just an internal tool
-if meson.version().version_compare('>=0.49')
-  if (not get_option('benchmark').disabled() and
-      get_option('wrap_mode') != 'nodownload' and
-      host_machine.system() != 'windows' and
-      not meson.is_subproject() and
-      not meson.is_cross_build())
-    subdir('perf')
-  endif
+if not get_option('benchmark').disabled()
+  subdir('perf')
 endif
 
 if not get_option('docs').disabled()
@@ -405,6 +394,11 @@
     {'Documentation': conf.get('HAVE_GTK_DOC', 0) == 1,
      'GObject bindings': conf.get('HAVE_GOBJECT', 0) == 1,
      'Introspection': conf.get('HAVE_INTROSPECTION', 0) == 1,
+     'Experimental APIs': conf.get('HB_EXPERIMENTAL_API', 0) == 1,
+    },
+  'Testing':
+    {'Tests': get_option('tests').enabled(),
+     'Benchmark': get_option('benchmark').enabled(),
     },
 }
 if meson.version().version_compare('>=0.53')
diff --git a/meson_options.txt b/meson_options.txt
index 8bc190b..980099f 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -28,7 +28,7 @@
 option('docs', type: 'feature', value: 'auto', yield: true,
   description: 'Generate documentation with gtk-doc')
 
-option('benchmark', type: 'feature', value: 'auto',
+option('benchmark', type: 'feature', value: 'disabled',
   description: 'Enable benchmark tests')
 option('icu_builtin', type: 'boolean', value: false,
   description: 'Don\'t separate ICU support as harfbuzz-icu module')
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/src/Makefile.am b/src/Makefile.am
index c341d40..7ae881c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -341,25 +341,6 @@
 test_gsub_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
 test_gsub_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
 
-check_PROGRAMS += \
-	dump-indic-data \
-	dump-khmer-data \
-	dump-myanmar-data \
-	dump-use-data \
-	$(NULL)
-dump_indic_data_SOURCES = dump-indic-data.cc hb-ot-shape-complex-indic-table.cc
-dump_indic_data_CPPFLAGS = $(HBCFLAGS)
-dump_indic_data_LDADD = libharfbuzz.la $(HBLIBS)
-dump_khmer_data_SOURCES = dump-khmer-data.cc hb-ot-shape-complex-indic-table.cc
-dump_khmer_data_CPPFLAGS = $(HBCFLAGS)
-dump_khmer_data_LDADD = libharfbuzz.la $(HBLIBS)
-dump_myanmar_data_SOURCES = dump-myanmar-data.cc hb-ot-shape-complex-indic-table.cc
-dump_myanmar_data_CPPFLAGS = $(HBCFLAGS)
-dump_myanmar_data_LDADD = libharfbuzz.la $(HBLIBS)
-dump_use_data_SOURCES = dump-use-data.cc hb-ot-shape-complex-use-table.cc
-dump_use_data_CPPFLAGS = $(HBCFLAGS)
-dump_use_data_LDADD = libharfbuzz.la $(HBLIBS)
-
 COMPILED_TESTS = test-algs test-array 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)
@@ -444,14 +425,7 @@
 HarfBuzz_0_0_gir_CFLAGS = \
 	$(INCLUDES) \
 	$(HBCFLAGS) \
-	-DHB_H \
-	-DHB_H_IN \
-	-DHB_OT_H \
-	-DHB_OT_H_IN \
-	-DHB_AAT_H \
-	-DHB_AAT_H_IN \
-	-DHB_GOBJECT_H \
-	-DHB_GOBJECT_H_IN \
+	-DHB_NO_SINGLE_HEADER_ERROR \
 	-DHAVE_GOBJECT \
 	-DHB_EXTERN= \
 	$(NULL)
diff --git a/src/Makefile.sources b/src/Makefile.sources
index f7d4587..6a6d301 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -118,13 +118,13 @@
 	hb-ot-shape-complex-indic.hh \
 	hb-ot-shape-complex-khmer.cc \
 	hb-ot-shape-complex-khmer.hh \
-	hb-ot-shape-complex-machine-index.hh \
 	hb-ot-shape-complex-myanmar.cc \
 	hb-ot-shape-complex-myanmar.hh \
+	hb-ot-shape-complex-syllabic.cc \
+	hb-ot-shape-complex-syllabic.hh \
 	hb-ot-shape-complex-thai.cc \
-	hb-ot-shape-complex-use-table.cc \
+	hb-ot-shape-complex-use-table.hh \
 	hb-ot-shape-complex-use.cc \
-	hb-ot-shape-complex-use.hh \
 	hb-ot-shape-complex-vowel-constraints.cc \
 	hb-ot-shape-complex-vowel-constraints.hh \
 	hb-ot-shape-complex.hh \
@@ -266,10 +266,8 @@
 	hb-subset-input.hh \
 	hb-subset-plan.cc \
 	hb-subset-plan.hh \
-	hb-subset-plan.hh \
 	hb-subset.cc \
 	hb-subset.hh \
-	hb-subset.hh \
 	$(NULL)
 
 HB_SUBSET_headers = \
diff --git a/src/dump-indic-data.cc b/src/dump-indic-data.cc
deleted file mode 100644
index 8ddc9d5..0000000
--- a/src/dump-indic-data.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright © 2018  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#include "hb-ot-shape-complex-indic.hh"
-
-int
-main ()
-{
-  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
-  {
-    hb_glyph_info_t info;
-    info.codepoint = u;
-    set_indic_properties (info);
-    if (info.indic_category() != INDIC_SYLLABIC_CATEGORY_OTHER ||
-	info.indic_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE)
-      printf("U+%04X	%u	%u\n", u,
-	     info.indic_category(),
-	     info.indic_position());
-  }
-}
diff --git a/src/dump-khmer-data.cc b/src/dump-khmer-data.cc
deleted file mode 100644
index cffbb92..0000000
--- a/src/dump-khmer-data.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright © 2018  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#include "hb-ot-shape-complex-khmer.hh"
-
-int
-main ()
-{
-  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
-  {
-    hb_glyph_info_t info;
-    info.codepoint = u;
-    set_khmer_properties (info);
-    if (info.khmer_category() != INDIC_SYLLABIC_CATEGORY_OTHER)
-      printf("U+%04X	%u\n", u,
-	     info.khmer_category());
-  }
-}
diff --git a/src/dump-myanmar-data.cc b/src/dump-myanmar-data.cc
deleted file mode 100644
index c1a303f..0000000
--- a/src/dump-myanmar-data.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright © 2018  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#include "hb-ot-shape-complex-myanmar.hh"
-
-int
-main ()
-{
-  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
-  {
-    hb_glyph_info_t info;
-    info.codepoint = u;
-    set_myanmar_properties (info);
-    if (info.myanmar_category() != INDIC_SYLLABIC_CATEGORY_OTHER ||
-	info.myanmar_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE)
-      printf("U+%04X	%u	%u\n", u,
-	     info.myanmar_category(),
-	     info.myanmar_position());
-  }
-}
diff --git a/src/gen-hb-version.py b/src/gen-hb-version.py
index bf16f88..879811f 100755
--- a/src/gen-hb-version.py
+++ b/src/gen-hb-version.py
@@ -2,7 +2,7 @@
 
 "This tool is intended to be used from meson"
 
-import os, sys, shutil
+import os, sys, shutil, re
 
 if len (sys.argv) < 4:
 	sys.exit(__doc__)
@@ -14,6 +14,15 @@
 INPUT = sys.argv[3]
 CURRENT_SOURCE_DIR = os.path.dirname(INPUT)
 
+try:
+	with open (OUTPUT, "r") as old_output:
+		for line in old_output:
+			old_version = re.match (r"#define HB_VERSION_STRING \"(\d.\d.\d)\"", line)
+			if old_version and old_version[1] == version:
+				sys.exit ()
+except IOError:
+	pass
+
 with open (INPUT, "r", encoding='utf-8') as template:
 	with open (OUTPUT, "wb") as output:
 		output.write (template.read ()
diff --git a/src/gen-indic-table.py b/src/gen-indic-table.py
index 9d4d548..367e55e 100755
--- a/src/gen-indic-table.py
+++ b/src/gen-indic-table.py
@@ -200,7 +200,7 @@
 offset = 0
 starts = []
 ends = []
-print ("static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = {")
+print ("static const uint16_t indic_table[] = {")
 for u in uu:
 	if u <= last:
 		continue
@@ -234,7 +234,7 @@
 page_bits = 12
 print ("}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy))
 print ()
-print ("INDIC_TABLE_ELEMENT_TYPE")
+print ("uint16_t")
 print ("hb_indic_get_categories (hb_codepoint_t u)")
 print ("{")
 print ("  switch (u >> %d)" % page_bits)
diff --git a/src/gen-tag-table.py b/src/gen-tag-table.py
index 9b5e626..093dd81 100755
--- a/src/gen-tag-table.py
+++ b/src/gen-tag-table.py
@@ -47,6 +47,8 @@
 			raise AssertionError
 		raise AssertionError (message)
 
+DEFAULT_LANGUAGE_SYSTEM = ''
+
 # from https://www-01.sil.org/iso639-3/iso-639-3.tab
 ISO_639_3_TO_1 = {
 	'aar': 'aa',
@@ -468,11 +470,8 @@
 			if ot_macrolanguages:
 				for ot_macrolanguage in ot_macrolanguages:
 					for language in languages:
-						# Remove the following condition if e.g. nn should map to NYN,NOR
-						# instead of just NYN.
-						if language not in original_ot_from_bcp_47:
-							self.add_language (language, ot_macrolanguage)
-							self.ranks[ot_macrolanguage] += 1
+						self.add_language (language, ot_macrolanguage)
+						self.ranks[ot_macrolanguage] += 1
 			else:
 				for language in languages:
 					if language in original_ot_from_bcp_47:
@@ -556,7 +555,7 @@
 						self.grandfathered.add (subtag.lower ())
 				elif line.startswith ('Description: '):
 					description = line.split (' ', 1)[1].replace (' (individual language)', '')
-					description = re.sub (' (\((individual |macro)language\)|languages)$', '',
+					description = re.sub (' (\(family\)|\((individual |macro)language\)|languages)$', '',
 							description)
 					if subtag in self.names:
 						self.names[subtag] += '\n' + description
@@ -591,7 +590,9 @@
 					elif not has_preferred_value and line.startswith ('Macrolanguage: '):
 						self._add_macrolanguage (line.split (' ')[1], subtag)
 				elif subtag_type == 'variant':
-					if line.startswith ('Prefix: '):
+					if line.startswith ('Deprecated: '):
+						self.scopes[subtag] = ' (retired code)' + self.scopes.get (subtag, '')
+					elif line.startswith ('Prefix: '):
 						self.prefixes[subtag].add (line.split (' ')[1])
 				elif line.startswith ('File-Date: '):
 					self.header = line
@@ -622,6 +623,17 @@
 				for macrolanguage in macrolanguages:
 					self._add_macrolanguage (biggest_macrolanguage, macrolanguage)
 
+	def _get_name_piece (self, subtag):
+		"""Return the first name of a subtag plus its scope suffix.
+
+		Args:
+			subtag (str): A BCP 47 subtag.
+
+		Returns:
+			The name form of ``subtag``.
+		"""
+		return self.names[subtag].split ('\n')[0] + self.scopes.get (subtag, '')
+
 	def get_name (self, lt):
 		"""Return the names of the subtags in a language tag.
 
@@ -631,13 +643,13 @@
 		Returns:
 			The name form of ``lt``.
 		"""
-		name = self.names[lt.language].split ('\n')[0]
+		name = self._get_name_piece (lt.language)
 		if lt.script:
-			name += '; ' + self.names[lt.script.title ()].split ('\n')[0]
+			name += '; ' + self._get_name_piece (lt.script.title ())
 		if lt.region:
-			name += '; ' + self.names[lt.region.upper ()].split ('\n')[0]
+			name += '; ' + self._get_name_piece (lt.region.upper ())
 		if lt.variant:
-			name += '; ' + self.names[lt.variant].split ('\n')[0]
+			name += '; ' + self._get_name_piece (lt.variant)
 		return name
 
 bcp_47 = BCP47Parser ()
@@ -673,6 +685,8 @@
 ot.remove_language_ot ('IRT')
 ot.add_language ('ga-Latg', 'IRT')
 
+ot.add_language ('hy-arevmda', 'HYE')
+
 ot.remove_language_ot ('KGE')
 ot.add_language ('und-Geok', 'KGE')
 
@@ -700,6 +714,7 @@
 ot.add_language ('qub', 'QWH')
 ot.add_language ('qud', 'QVI')
 ot.add_language ('qug', 'QVI')
+ot.add_language ('qul', 'QUH')
 ot.add_language ('qup', 'QVI')
 ot.add_language ('qur', 'QWH')
 ot.add_language ('qus', 'QUH')
@@ -746,14 +761,17 @@
 ot.remove_language_ot ('ZHH')
 ot.remove_language_ot ('ZHP')
 ot.remove_language_ot ('ZHT')
+ot.remove_language_ot ('ZHTM')
 bcp_47.macrolanguages['zh'].remove ('lzh')
 bcp_47.macrolanguages['zh'].remove ('yue')
 ot.add_language ('zh-Hant-MO', 'ZHH')
+ot.add_language ('zh-Hant-MO', 'ZHTM')
 ot.add_language ('zh-Hant-HK', 'ZHH')
 ot.add_language ('zh-Hans', 'ZHS')
 ot.add_language ('zh-Hant', 'ZHT')
 ot.add_language ('zh-HK', 'ZHH')
 ot.add_language ('zh-MO', 'ZHH')
+ot.add_language ('zh-MO', 'ZHTM')
 ot.add_language ('zh-TW', 'ZHT')
 ot.add_language ('lzh', 'ZHT')
 ot.add_language ('lzh-Hans', 'ZHS')
@@ -785,6 +803,7 @@
 disambiguation = {
 	'ALT': 'alt',
 	'ARK': 'rki',
+	'ATH': 'ath',
 	'BHI': 'bhb',
 	'BLN': 'bjt',
 	'BTI': 'beb',
@@ -796,6 +815,7 @@
 	'ECR': 'crj',
 	'HAL': 'cfm',
 	'HND': 'hnd',
+	'HYE': 'hyw',
 	'KIS': 'kqs',
 	'KUI': 'uki',
 	'LRC': 'bqi',
@@ -808,15 +828,23 @@
 	'QVI': 'qvi',
 	'QWH': 'qwh',
 	'SIG': 'stv',
-	'TNE': 'yrk',
+	'SRB': 'sr',
 	'ZHH': 'zh-HK',
 	'ZHS': 'zh-Hans',
 	'ZHT': 'zh-Hant',
+	'ZHTM': 'zh-MO',
 }
 
 ot.inherit_from_macrolanguages ()
 bcp_47.remove_extra_macrolanguages ()
 ot.inherit_from_macrolanguages ()
+ot.names[DEFAULT_LANGUAGE_SYSTEM] = '*/'
+ot.ranks[DEFAULT_LANGUAGE_SYSTEM] = max (ot.ranks.values ()) + 1
+for tricky_ot_tag in filter (lambda tag: re.match ('[A-Z]{3}$', tag), ot.names):
+	possible_bcp_47_tag = tricky_ot_tag.lower ()
+	if possible_bcp_47_tag in bcp_47.names and not ot.from_bcp_47[possible_bcp_47_tag]:
+		ot.add_language (possible_bcp_47_tag, DEFAULT_LANGUAGE_SYSTEM)
+		bcp_47.macrolanguages[possible_bcp_47_tag] = set ()
 ot.sort_languages ()
 
 print ('/* == Start of generated table == */')
@@ -845,6 +873,8 @@
 	Returns:
 		A snippet of C++ representing ``tag``.
 	"""
+	if tag == DEFAULT_LANGUAGE_SYSTEM:
+		return 'HB_TAG_NONE\t       '
 	return "HB_TAG('%s','%s','%s','%s')" % tuple (('%-4s' % tag)[:4])
 
 def get_variant_set (name):
@@ -893,14 +923,18 @@
 		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])
+		ot_name = ot.names[tag]
 		scope = bcp_47.scopes.get (language, '')
-		if not intersection:
-			write ('%s%s -> %s' % (bcp_47_name_candidates[0], scope, ot.names[tag]))
+		if tag == DEFAULT_LANGUAGE_SYSTEM:
+			write (f'{bcp_47_name_candidates[0]}{scope} != {ot.names[language.upper ()]}')
 		else:
-			name = get_matching_language_name (intersection, bcp_47_name_candidates)
-			bcp_47.names[language] = name
-			write ('%s%s' % (name if len (name) > len (ot.names[tag]) else ot.names[tag], scope))
+			intersection = language_name_intersection (bcp_47_name, ot_name)
+			if not intersection:
+				write ('%s%s -> %s' % (bcp_47_name_candidates[0], scope, ot_name))
+			else:
+				name = get_matching_language_name (intersection, bcp_47_name_candidates)
+				bcp_47.names[language] = name
+				write ('%s%s' % (name if len (name) > len (ot_name) else ot_name, scope))
 		print (' */')
 
 print ('};')
@@ -982,22 +1016,24 @@
 	print ("  case '%s':" % initial)
 	for lt, tags in items:
 		print ('    if (', end='')
+		script = lt.script
+		region = lt.region
 		if lt.grandfathered:
 			print ('0 == strcmp (&lang_str[1], "%s")' % lt.language[1:], end='')
 		else:
 			string_literal = lt.language[1:] + '-'
-			if lt.script:
-				string_literal += lt.script
-				lt.script = None
-				if lt.region:
-					string_literal += '-' + lt.region
-					lt.region = None
+			if script:
+				string_literal += script
+				script = None
+				if region:
+					string_literal += '-' + region
+					region = None
 			if string_literal[-1] == '-':
 				print ('0 == strncmp (&lang_str[1], "%s", %i)' % (string_literal, len (string_literal)), end='')
 			else:
 				print ('lang_matches (&lang_str[1], "%s")' % string_literal, end='')
-		print_subtag_matches (lt.script, True)
-		print_subtag_matches (lt.region, True)
+		print_subtag_matches (script, True)
+		print_subtag_matches (region, True)
 		print_subtag_matches (lt.variant, True)
 		print (')')
 		print ('    {')
@@ -1064,7 +1100,10 @@
 	global disambiguation
 	global ot
 	for ot_tag, bcp_47_tags in ot.to_bcp_47.items ():
-		primary_tags = list (t for t in bcp_47_tags if t not in bcp_47.grandfathered and ot.from_bcp_47.get (t)[0] == ot_tag)
+		if ot_tag == DEFAULT_LANGUAGE_SYSTEM:
+			primary_tags = []
+		else:
+			primary_tags = list (t for t in bcp_47_tags if t not in bcp_47.grandfathered and ot.from_bcp_47.get (t)[0] == ot_tag)
 		if len (primary_tags) == 1:
 			expect (ot_tag not in disambiguation, 'unnecessary disambiguation for OT tag: %s' % ot_tag)
 			if '-' in primary_tags[0]:
@@ -1087,8 +1126,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]
-			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]:
+			different_bcp_47_tags = sorted (t for t in bcp_47_tags if not same_tag (t, ot.from_bcp_47.get (t)))
+			if different_bcp_47_tags and disambiguation[ot_tag] == different_bcp_47_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-use-table.py b/src/gen-use-table.py
index 0e2d57b..35d9abf 100755
--- a/src/gen-use-table.py
+++ b/src/gen-use-table.py
@@ -9,8 +9,8 @@
 * https://unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
 * https://unicode.org/Public/UCD/latest/ucd/ArabicShaping.txt
 * https://unicode.org/Public/UCD/latest/ucd/Blocks.txt
-* ms-use/IndicPositionalCategory-Additional.txt
 * ms-use/IndicSyllabicCategory-Additional.txt
+* ms-use/IndicPositionalCategory-Additional.txt
 """
 
 import sys
@@ -243,12 +243,9 @@
 			Vowel_Independent,
 			] or
 		# TODO: https://github.com/MicrosoftDocs/typography-issues/issues/484
-		AJT in [jt_C, jt_D, jt_L, jt_R] and not is_ZWJ(U, UISC, UGC, AJT) or
+		AJT in [jt_C, jt_D, jt_L, jt_R] and UISC != Joiner or
 		(UGC == Lo and UISC in [Avagraha, Bindu, Consonant_Final, Consonant_Medial,
 					Consonant_Subjoined, Vowel, Vowel_Dependent]))
-def is_BASE_IND(U, UISC, UGC, AJT):
-	return (UISC in [Consonant_Dead, Modifying_Letter] or
-		(UGC == Po and not U in [0x0F04, 0x0F05, 0x0F06, 0x104B, 0x104E, 0x1800, 0x1807, 0x180A, 0x1B5B, 0x1B5C, 0x1B5F, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]))
 def is_BASE_NUM(U, UISC, UGC, AJT):
 	return UISC == Brahmi_Joining_Number
 def is_BASE_OTHER(U, UISC, UGC, AJT):
@@ -290,19 +287,13 @@
 	return UISC == Hieroglyph_Segment_End
 def is_ZWNJ(U, UISC, UGC, AJT):
 	return UISC == Non_Joiner
-def is_ZWJ(U, UISC, UGC, AJT):
-	return UISC == Joiner
-def is_Word_Joiner(U, UISC, UGC, AJT):
-	return U == 0x2060
 def is_OTHER(U, UISC, UGC, AJT):
-	return (UISC == Other
+	return ((UGC in [Cn, Po] or UISC in [Consonant_Dead, Joiner, Modifying_Letter, Other])
 		and not is_BASE(U, UISC, UGC, AJT)
+		and not is_BASE_OTHER(U, UISC, UGC, AJT)
 		and not is_SYM(U, UISC, UGC, AJT)
 		and not is_SYM_MOD(U, UISC, UGC, AJT)
-		and not is_Word_Joiner(U, UISC, UGC, AJT)
 	)
-def is_Reserved(U, UISC, UGC, AJT):
-	return UGC == 'Cn'
 def is_REPHA(U, UISC, UGC, AJT):
 	return UISC in [Consonant_Preceding_Repha, Consonant_Prefixed]
 def is_SAKOT(U, UISC, UGC, AJT):
@@ -321,10 +312,9 @@
 	return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or
 		(UGC != Lo and (UISC == Bindu or U in [0xAA29])))
 
-# CGJ and VS are handled in find_syllables
+# CGJ, VS, WJ, and ZWJ are handled in find_syllables
 use_mapping = {
 	'B':	is_BASE,
-	'IND':	is_BASE_IND,
 	'N':	is_BASE_NUM,
 	'GB':	is_BASE_OTHER,
 	'F':	is_CONS_FINAL,
@@ -341,10 +331,7 @@
 	'SB':	is_HIEROGLYPH_SEGMENT_BEGIN,
 	'SE':	is_HIEROGLYPH_SEGMENT_END,
 	'ZWNJ':	is_ZWNJ,
-	'ZWJ':	is_ZWJ,
-	'WJ':	is_Word_Joiner,
 	'O':	is_OTHER,
-	'Rsv':	is_Reserved,
 	'R':	is_REPHA,
 	'S':	is_SYM,
 	'Sk':	is_SAKOT,
@@ -402,11 +389,6 @@
 	items = use_mapping.items()
 	for U,(UISC,UIPC,UGC,AJT,UBlock) in data.items():
 
-		if UGC == Cn: continue
-
-		# TODO: These variation selectors are overridden to IND, but we want to ignore them
-		if U in range (0xFE00, 0xFE0F + 1): continue
-
 		# Resolve Indic_Syllabic_Category
 
 		# TODO: These don't have UISC assigned in Unicode 13.0.0, but have UIPC
@@ -475,11 +457,12 @@
 		print (" * %s" % (l.strip()))
 print (" */")
 print ()
+print ("#ifndef HB_OT_SHAPE_COMPLEX_USE_TABLE_HH")
+print ("#define HB_OT_SHAPE_COMPLEX_USE_TABLE_HH")
+print ()
 print ('#include "hb.hh"')
 print ()
-print ('#ifndef HB_NO_OT_SHAPE')
-print ()
-print ('#include "hb-ot-shape-complex-use.hh"')
+print ('#include "hb-ot-shape-complex-use-machine.hh"')
 print ()
 
 total = 0
@@ -521,18 +504,20 @@
 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:]))
+	print ("#define %s	USE(%s)	/* %s */" % (k, k, v.__name__[3:]))
 for k,v in sorted(use_positions.items()):
 	if not v: continue
 	for suf in v.keys():
 		tag = k + suf
-		print ("#define %s	USE_%s" % (tag, tag))
+		print ("#define %s	USE(%s)" % (tag, tag))
 print ('#pragma GCC diagnostic pop')
 print ("")
-print ("static const USE_TABLE_ELEMENT_TYPE use_table[] = {")
+print ("static const uint8_t use_table[] = {")
 for u in uu:
 	if u <= last:
 		continue
+	if data[u][0] == 'O':
+		continue
 	block = data[u][1]
 
 	start = u//8*8
@@ -563,7 +548,7 @@
 page_bits = 12
 print ("}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy))
 print ()
-print ("USE_TABLE_ELEMENT_TYPE")
+print ("static inline uint8_t")
 print ("hb_use_get_category (hb_codepoint_t u)")
 print ("{")
 print ("  switch (u >> %d)" % page_bits)
@@ -580,7 +565,7 @@
 print ("    default:")
 print ("      break;")
 print ("  }")
-print ("  return USE_O;")
+print ("  return USE(O);")
 print ("}")
 print ()
 for k in sorted(use_mapping.keys()):
@@ -593,7 +578,7 @@
 		print ("#undef %s" % tag)
 print ()
 print ()
-print ('#endif')
+print ("#endif /* HB_OT_SHAPE_COMPLEX_USE_TABLE_HH */")
 print ("/* == End of generated table == */")
 
 # Maintain at least 50% occupancy in the table */
diff --git a/src/harfbuzz.cc b/src/harfbuzz.cc
index 226d013..fe00100 100644
--- a/src/harfbuzz.cc
+++ b/src/harfbuzz.cc
@@ -29,8 +29,8 @@
 #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-syllabic.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"
diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc
index 0d667d7..0e9f2b4 100644
--- a/src/hb-aat-layout.cc
+++ b/src/hb-aat-layout.cc
@@ -218,7 +218,7 @@
 }
 
 
-/*
+/**
  * hb_aat_layout_has_substitution:
  * @face: #hb_face_t to work upon
  *
@@ -227,7 +227,7 @@
  *
  * <note>Note: does not examine the `GSUB` table.</note>
  *
- * Return value: true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 2.3.0
  */
@@ -285,7 +285,7 @@
   hb_ot_layout_delete_glyphs_inplace (buffer, is_deleted_glyph);
 }
 
-/*
+/**
  * hb_aat_layout_has_positioning:
  * @face: #hb_face_t to work upon
  *
@@ -294,7 +294,7 @@
  *
  * <note>Note: does not examine the `GPOS` table.</note>
  *
- * Return value: true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 2.3.0
  */
@@ -318,14 +318,14 @@
 }
 
 
-/*
+/**
  * hb_aat_layout_has_tracking:
  * @face:: #hb_face_t to work upon
  *
  * Tests whether the specified face includes any tracking information
  * in the `trak` table.
  *
- * Return value: true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 2.3.0
  */
@@ -350,7 +350,7 @@
  * hb_aat_layout_get_feature_types:
  * @face: #hb_face_t to work upon
  * @start_offset: offset of the first feature type to retrieve
- * @feature_count: (inout) (allow-none): Input = the maximum number of feature types to return;
+ * @feature_count: (inout) (optional): Input = the maximum number of feature types to return;
  *                 Output = the actual number of feature types returned (may be zero)
  * @features: (out caller-allocates) (array length=feature_count): Array of feature types found
  *
@@ -374,9 +374,9 @@
  * @face: #hb_face_t to work upon
  * @feature_type: The #hb_aat_layout_feature_type_t of the requested feature type
  *
- * Fetches the name ID of the specified feature type in the face's `name` table.
+ * Fetches the name identifier of the specified feature type in the face's `name` table.
  *
- * Return value: Name ID of the requested feature type
+ * Return value: Name identifier of the requested feature type
  *
  * Since: 2.2.0
  */
@@ -388,15 +388,15 @@
 }
 
 /**
- * hb_aat_layout_feature_type_get_selectors:
+ * hb_aat_layout_feature_type_get_selector_infos:
  * @face: #hb_face_t to work upon
  * @feature_type: The #hb_aat_layout_feature_type_t of the requested feature type
  * @start_offset: offset of the first feature type to retrieve
- * @selector_count: (inout) (allow-none): Input = the maximum number of selectors to return;
+ * @selector_count: (inout) (optional): Input = the maximum number of selectors to return;
  *                  Output = the actual number of selectors returned (may be zero)
- * @selectors: (out caller-allocates) (array length=selector_count): A buffer pointer.
- *             The selectors available for the feature type queries.
- * @default_index: (out) (allow-none): The index of the feature's default selector, if any
+ * @selectors: (out caller-allocates) (array length=selector_count) (optional):
+ *             A buffer pointer. The selectors available for the feature type queries.
+ * @default_index: (out) (optional): The index of the feature's default selector, if any
  *
  * Fetches a list of the selectors available for the specified feature in the given face.
  *
diff --git a/src/hb-aat-layout.h b/src/hb-aat-layout.h
index 89acb35..9af2740 100644
--- a/src/hb-aat-layout.h
+++ b/src/hb-aat-layout.h
@@ -22,7 +22,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-#ifndef HB_AAT_H_IN
+#if !defined(HB_AAT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-aat.h> instead."
 #endif
 
@@ -38,47 +38,47 @@
 /**
  * hb_aat_layout_feature_type_t:
  * @HB_AAT_LAYOUT_FEATURE_TYPE_INVALID: Initial, unset feature type
- * @HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE:
- * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE:
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC: [All Typographic Features](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type0)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES: [Ligatures](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type1)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION: [Cursive Connection](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type2)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE: [Letter Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type3)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION: [Vertical Substitution](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type4)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT: [Linguistic Rearrangement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type5)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING: [Number Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type6)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE: [Smart Swash](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type8)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE: [Diacritics](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type9)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION: [Vertical Position](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type10)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS: [Fractions](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type11)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE: [Overlapping Characters](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type13)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS: [Typographic Extras](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type14)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS: [Mathematical Extras](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type15)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE: [Ornament Sets](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type16)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES: [Character Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type17)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE: [Design Complexity](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type18)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS: [Style Options](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type19)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE: [Character Shape](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type20)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE: [Number Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type21)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING: [Text Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type22)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION: [Transliteration](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type23)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE: [Annotation](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type24)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE: [Kana Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type25)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE: [Ideographic Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type26)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE: [Unicode Decomposition](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type27)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA: [Ruby Kana](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type28)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE: [CJK Symbol Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type29)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE: [Ideographic Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type30)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE: [CJK Vertical Roman Placement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type31)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN: [Italic CJK Roman](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type32)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT: [Case Sensitive Layout](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type33)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA: [Alternate Kana](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type34)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES: [Stylistic Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type35)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES: [Contextual Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type36)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE: [Lower Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type37)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE: [Upper Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type38)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE: [Language Tag](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type39)
+ * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE: [CJK Roman Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type103)
  *
- * The possible feature types defined for AAT shaping.
+ * The possible feature types defined for AAT shaping, from Apple [Font Feature Registry](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html).
  *
  * Since: 2.2.0
  */
@@ -732,8 +732,15 @@
 hb_aat_layout_feature_type_get_name_id (hb_face_t                    *face,
 					hb_aat_layout_feature_type_t  feature_type);
 
-typedef struct hb_aat_layout_feature_selector_info_t
-{
+/**
+ * hb_aat_layout_feature_selector_info_t:
+ * @name_id: The selector's name identifier
+ * @enable: The value to turn the selector on
+ * @disable: The value to turn the selector off
+ *
+ * Structure representing a setting for an #hb_aat_layout_feature_type_t.
+ */
+typedef struct hb_aat_layout_feature_selector_info_t {
   hb_ot_name_id_t			name_id;
   hb_aat_layout_feature_selector_t	enable;
   hb_aat_layout_feature_selector_t	disable;
diff --git a/src/hb-algs.hh b/src/hb-algs.hh
index 30b5812..5b9c14e 100644
--- a/src/hb-algs.hh
+++ b/src/hb-algs.hh
@@ -215,7 +215,9 @@
 
   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)))
+  (
+    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
@@ -269,7 +271,9 @@
 
   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)))
+  (
+    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
@@ -296,6 +300,40 @@
 }
 HB_FUNCOBJ (hb_get);
 
+struct
+{
+  private:
+
+  template <typename T1, typename T2> auto
+  impl (T1&& v1, T2 &&v2, hb_priority<2>) const HB_AUTO_RETURN
+  (
+    hb_forward<T2> (v2).cmp (hb_forward<T1> (v1)) == 0
+  )
+
+  template <typename T1, typename T2> auto
+  impl (T1&& v1, T2 &&v2, hb_priority<1>) const HB_AUTO_RETURN
+  (
+    hb_forward<T1> (v1).cmp (hb_forward<T2> (v2)) == 0
+  )
+
+  template <typename T1, typename T2> auto
+  impl (T1&& v1, T2 &&v2, hb_priority<0>) const HB_AUTO_RETURN
+  (
+    hb_forward<T1> (v1) == hb_forward<T2> (v2)
+  )
+
+  public:
+
+  template <typename T1, typename T2> auto
+  operator () (T1&& v1, T2 &&v2) const HB_AUTO_RETURN
+  (
+    impl (hb_forward<T1> (v1),
+	  hb_forward<T2> (v2),
+	  hb_prioritize)
+  )
+}
+HB_FUNCOBJ (hb_equal);
+
 
 template <typename T1, typename T2>
 struct hb_pair_t
@@ -350,14 +388,14 @@
 {
   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))
+  (a <= 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))
+  (a >= b ? hb_forward<T> (a) : hb_forward<T2> (b))
 }
 HB_FUNCOBJ (hb_max);
 struct
@@ -988,32 +1026,24 @@
 
 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)
 }
diff --git a/src/hb-array.hh b/src/hb-array.hh
index 568cd02..02bd8d8 100644
--- a/src/hb-array.hh
+++ b/src/hb-array.hh
@@ -142,7 +142,7 @@
   bool lfind (const T &x, unsigned *pos = nullptr) const
   {
     for (unsigned i = 0; i < length; ++i)
-      if (!this->arrayZ[i].cmp (x))
+      if (hb_equal (x, this->arrayZ[i]))
       {
 	if (pos)
 	  *pos = i;
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index 94ed50f..17aa4f4 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -58,7 +58,7 @@
  * @length: Length of @data in bytes.
  * @mode: Memory mode for @data.
  * @user_data: Data parameter to pass to @destroy.
- * @destroy: Callback to call when @data is not needed anymore.
+ * @destroy: (nullable): Callback to call when @data is not needed anymore.
  *
  * Creates a new "blob" object wrapping @data.  The @mode parameter is used
  * to negotiate ownership and lifecycle of @data.
@@ -116,7 +116,7 @@
  * @length: Length of sub-blob.
  *
  * Returns a blob that represents a range of bytes in @parent.  The new
- * blob is always created with %HB_MEMORY_MODE_READONLY, meaning that it
+ * blob is always created with #HB_MEMORY_MODE_READONLY, meaning that it
  * will never modify data in the parent blob.  The parent data is not
  * expected to be modified, and will result in undefined behavior if it
  * is.
@@ -156,7 +156,7 @@
  *
  * Makes a writable copy of @blob.
  *
- * Return value: New blob, or nullptr if allocation failed.
+ * Return value: The new blob, or nullptr if allocation failed
  *
  * Since: 1.8.0
  **/
@@ -182,7 +182,7 @@
  *
  * See TODO:link object types for more information.
  *
- * Return value: (transfer full): the empty blob.
+ * Return value: (transfer full): The empty blob.
  *
  * Since: 0.9.2
  **/
@@ -234,13 +234,15 @@
 
 /**
  * hb_blob_set_user_data: (skip)
- * @blob: a blob.
- * @key: key for data to set.
- * @data: data to set.
- * @destroy: callback to call when @data is not needed anymore.
- * @replace: whether to replace an existing data with the same key.
+ * @blob: An #hb_blob_t
+ * @key: The user-data key to set
+ * @data: A pointer to the user data to set
+ * @destroy: (nullable): A callback to call when @data is not needed anymore
+ * @replace: Whether to replace an existing data with the same key
  *
- * Return value:
+ * Attaches a user-data key/data pair to the specified blob.
+ *
+ * Return value: %true if success, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -256,12 +258,13 @@
 
 /**
  * hb_blob_get_user_data: (skip)
- * @blob: a blob.
- * @key: key for data to get.
+ * @blob: a blob
+ * @key: The user-data key to query
  *
+ * Fetches the user data associated with the specified key,
+ * attached to the specified font-functions structure.
  *
- *
- * Return value: (transfer none):
+ * Return value: (transfer none): A pointer to the user data
  *
  * Since: 0.9.2
  **/
@@ -275,9 +278,9 @@
 
 /**
  * hb_blob_make_immutable:
- * @blob: a blob.
+ * @blob: a blob
  *
- *
+ * Makes a blob immutable.
  *
  * Since: 0.9.2
  **/
@@ -294,9 +297,9 @@
  * hb_blob_is_immutable:
  * @blob: a blob.
  *
+ * Tests whether a blob is immutable.
  *
- *
- * Return value: TODO
+ * Return value: %true if @blob is immutable, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -311,9 +314,9 @@
  * hb_blob_get_length:
  * @blob: a blob.
  *
+ * Fetches the length of a blob's data.
  *
- *
- * Return value: the length of blob data in bytes.
+ * Return value: the length of @blob data in bytes.
  *
  * Since: 0.9.2
  **/
@@ -326,11 +329,11 @@
 /**
  * hb_blob_get_data:
  * @blob: a blob.
- * @length: (out):
+ * @length: (out): The length in bytes of the data retrieved
  *
+ * Fetches the data from a blob.
  *
- *
- * Returns: (transfer none) (array length=length):
+ * Returns: (transfer none) (array length=length): the byte data of @blob.
  *
  * Since: 0.9.2
  **/
@@ -558,9 +561,12 @@
 
 /**
  * hb_blob_create_from_file:
- * @file_name: font filename.
+ * @file_name: A font filename
  *
- * Returns: A hb_blob_t pointer with the content of the file
+ * Creates a new blob containing the data from the
+ * specified binary font file.
+ *
+ * Returns: An #hb_blob_t pointer with the content of the file
  *
  * Since: 1.7.7
  **/
diff --git a/src/hb-blob.h b/src/hb-blob.h
index f80e9af..86f1278 100644
--- a/src/hb-blob.h
+++ b/src/hb-blob.h
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
@@ -36,25 +36,36 @@
 HB_BEGIN_DECLS
 
 
-/*
- * Note re various memory-modes:
+/**
+ * hb_memory_mode_t:
+ * @HB_MEMORY_MODE_DUPLICATE: HarfBuzz immediately makes a copy of the data.
+ * @HB_MEMORY_MODE_READONLY: HarfBuzz client will never modify the data,
+ *     and HarfBuzz will never modify the data.
+ * @HB_MEMORY_MODE_WRITABLE: HarfBuzz client made a copy of the data solely
+ *     for HarfBuzz, so HarfBuzz may modify the data.
+ * @HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE: See above
+ *
+ * Data type holding the memory modes available to
+ * client programs.
+ *
+ * Regarding these various memory-modes:
  *
  * - In no case shall the HarfBuzz client modify memory
  *   that is passed to HarfBuzz in a blob.  If there is
- *   any such possibility, MODE_DUPLICATE should be used
+ *   any such possibility, @HB_MEMORY_MODE_DUPLICATE should be used
  *   such that HarfBuzz makes a copy immediately,
  *
- * - Use MODE_READONLY otherwise, unless you really really
+ * - Use @HB_MEMORY_MODE_READONLY otherwise, unless you really really
  *   really know what you are doing,
  *
- * - MODE_WRITABLE is appropriate if you really made a
+ * - @HB_MEMORY_MODE_WRITABLE is appropriate if you really made a
  *   copy of data solely for the purpose of passing to
  *   HarfBuzz and doing that just once (no reuse!),
  *
- * - If the font is mmap()ed, it's ok to use
- *   READONLY_MAY_MAKE_WRITABLE, however, using that mode
- *   correctly is very tricky.  Use MODE_READONLY instead.
- */
+ * - If the font is mmap()ed, it's okay to use
+ *   @HB_MEMORY_READONLY_MAY_MAKE_WRITABLE, however, using that mode
+ *   correctly is very tricky.  Use @HB_MEMORY_MODE_READONLY instead.
+ **/
 typedef enum {
   HB_MEMORY_MODE_DUPLICATE,
   HB_MEMORY_MODE_READONLY,
@@ -62,6 +73,14 @@
   HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE
 } hb_memory_mode_t;
 
+/**
+ * hb_blob_t:
+ *
+ * Data type for blobs. A blob wraps a chunk of binary
+ * data and facilitates its lifecycle management between
+ * a client program and HarfBuzz.
+ *
+ **/
 typedef struct hb_blob_t hb_blob_t;
 
 HB_EXTERN hb_blob_t *
diff --git a/src/hb-blob.hh b/src/hb-blob.hh
index d85bd82..b03dfc1 100644
--- a/src/hb-blob.hh
+++ b/src/hb-blob.hh
@@ -90,6 +90,7 @@
   unsigned int get_length () const { return b.get ()->length; }
   void destroy () { hb_blob_destroy (b.get ()); b = nullptr; }
 
+  private:
   hb_nonnull_ptr_t<hb_blob_t> b;
 };
 
diff --git a/src/hb-buffer-deserialize-json.hh b/src/hb-buffer-deserialize-json.hh
index 1f9e2e9..01db295 100644
--- a/src/hb-buffer-deserialize-json.hh
+++ b/src/hb-buffer-deserialize-json.hh
@@ -1,30 +1,29 @@
-
 #line 1 "hb-buffer-deserialize-json.rl"
 /*
- * Copyright © 2013  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
+* Copyright © 2013  Google, Inc.
+*
+*  This is part of HarfBuzz, a text shaping library.
+*
+* Permission is hereby granted, without written agreement and without
+* license or royalty fees, to use, copy, modify, and distribute this
+* software and its documentation for any purpose, provided that the
+* above copyright notice and the following two paragraphs appear in
+* all copies of this software.
+*
+* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+* DAMAGE.
+*
+* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+* FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+*
+* Google Author(s): Behdad Esfahbod
+*/
 
 #ifndef HB_BUFFER_DESERIALIZE_JSON_HH
 #define HB_BUFFER_DESERIALIZE_JSON_HH
@@ -32,612 +31,577 @@
 #include "hb.hh"
 
 
-#line 36 "hb-buffer-deserialize-json.hh"
+#line 35 "hb-buffer-deserialize-json.hh"
 static const unsigned char _deserialize_json_trans_keys[] = {
-	0u, 0u, 9u, 123u, 9u, 34u, 97u, 103u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 
-	48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 
-	9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 
-	120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 
-	9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 
-	65u, 122u, 34u, 122u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0
+	1u, 0u, 0u, 18u, 0u, 2u, 10u, 15u,
+	16u, 17u, 2u, 2u, 0u, 7u, 0u, 6u,
+	5u, 6u, 0u, 19u, 0u, 19u, 0u, 19u,
+	2u, 2u, 0u, 7u, 0u, 6u, 5u, 6u,
+	0u, 19u, 0u, 19u, 14u, 14u, 2u, 2u,
+	0u, 7u, 0u, 6u, 0u, 19u, 0u, 19u,
+	16u, 17u, 2u, 2u, 0u, 7u, 0u, 6u,
+	5u, 6u, 0u, 19u, 0u, 19u, 2u, 2u,
+	0u, 7u, 0u, 6u, 5u, 6u, 0u, 19u,
+	0u, 19u, 2u, 2u, 0u, 7u, 0u, 6u,
+	2u, 8u, 0u, 19u, 2u, 8u, 0u, 19u,
+	0u, 19u, 2u, 2u, 0u, 7u, 0u, 6u,
+	0u, 19u, 0u, 9u, 0u, 18u, 1u, 0u,
+	0u
 };
 
-static const char _deserialize_json_key_spans[] = {
-	0, 115, 26, 7, 2, 1, 50, 49, 
-	10, 117, 117, 117, 1, 50, 49, 10, 
-	117, 117, 1, 1, 50, 49, 117, 117, 
-	2, 1, 50, 49, 10, 117, 117, 1, 
-	50, 49, 10, 117, 117, 1, 50, 49, 
-	58, 89, 117, 117, 85, 115, 0
+static const signed char _deserialize_json_char_class[] = {
+	0, 0, 0, 0, 0, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 0,
+	1, 2, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 3, 4, 1, 1, 5,
+	6, 6, 6, 6, 6, 6, 6, 6,
+	6, 7, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 8, 9, 1, 1, 1,
+	10, 1, 11, 12, 1, 1, 13, 1,
+	1, 1, 1, 14, 1, 1, 1, 1,
+	1, 1, 1, 1, 15, 1, 1, 16,
+	17, 1, 18, 1, 19, 0
 };
 
 static const short _deserialize_json_index_offsets[] = {
-	0, 0, 116, 143, 151, 154, 156, 207, 
-	257, 268, 386, 504, 622, 624, 675, 725, 
-	736, 854, 972, 974, 976, 1027, 1077, 1195, 
-	1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666, 
-	1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069, 
-	2119, 2178, 2268, 2386, 2504, 2590, 2706
+	0, 0, 19, 22, 28, 30, 31, 39,
+	46, 48, 68, 88, 108, 109, 117, 124,
+	126, 146, 166, 167, 168, 176, 183, 203,
+	223, 225, 226, 234, 241, 243, 263, 283,
+	284, 292, 299, 301, 321, 341, 342, 350,
+	357, 364, 384, 391, 411, 431, 432, 440,
+	447, 467, 477, 496, 0
 };
 
-static const char _deserialize_json_indicies[] = {
-	0, 0, 0, 0, 0, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	0, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 2, 1, 3, 3, 3, 
-	3, 3, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 3, 1, 4, 1, 
-	5, 1, 6, 7, 1, 1, 8, 1, 
-	9, 10, 1, 11, 1, 11, 11, 11, 
-	11, 11, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 11, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 12, 1, 
-	12, 12, 12, 12, 12, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 12, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 13, 1, 1, 14, 
-	15, 15, 15, 15, 15, 15, 15, 15, 
-	15, 1, 16, 17, 17, 17, 17, 17, 
-	17, 17, 17, 17, 1, 18, 18, 18, 
-	18, 18, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 18, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	19, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 20, 1, 21, 21, 21, 21, 21, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 21, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 3, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 22, 
-	1, 18, 18, 18, 18, 18, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	18, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 19, 1, 1, 1, 
-	17, 17, 17, 17, 17, 17, 17, 17, 
-	17, 17, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 20, 1, 23, 
-	1, 23, 23, 23, 23, 23, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	23, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 24, 1, 24, 24, 24, 24, 
-	24, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 24, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	25, 1, 1, 26, 27, 27, 27, 27, 
-	27, 27, 27, 27, 27, 1, 28, 29, 
-	29, 29, 29, 29, 29, 29, 29, 29, 
-	1, 30, 30, 30, 30, 30, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	30, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 31, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 32, 1, 30, 
-	30, 30, 30, 30, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 30, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 31, 1, 1, 1, 29, 29, 
-	29, 29, 29, 29, 29, 29, 29, 29, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 32, 1, 33, 1, 34, 
-	1, 34, 34, 34, 34, 34, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	34, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 35, 1, 35, 35, 35, 35, 
-	35, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 35, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 36, 37, 37, 37, 37, 
-	37, 37, 37, 37, 37, 1, 38, 38, 
-	38, 38, 38, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 38, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 39, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 40, 1, 38, 38, 38, 38, 
-	38, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 38, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 39, 
-	1, 1, 1, 41, 41, 41, 41, 41, 
-	41, 41, 41, 41, 41, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	40, 1, 42, 43, 1, 44, 1, 44, 
-	44, 44, 44, 44, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 44, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	45, 1, 45, 45, 45, 45, 45, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 45, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 46, 1, 
-	1, 47, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 1, 49, 50, 50, 50, 
-	50, 50, 50, 50, 50, 50, 1, 51, 
-	51, 51, 51, 51, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 51, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 52, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 53, 1, 51, 51, 51, 
-	51, 51, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 51, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	52, 1, 1, 1, 50, 50, 50, 50, 
-	50, 50, 50, 50, 50, 50, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 53, 1, 54, 1, 54, 54, 54, 
-	54, 54, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 54, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 55, 1, 
-	55, 55, 55, 55, 55, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 55, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 56, 1, 1, 57, 
-	58, 58, 58, 58, 58, 58, 58, 58, 
-	58, 1, 59, 60, 60, 60, 60, 60, 
-	60, 60, 60, 60, 1, 61, 61, 61, 
-	61, 61, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 61, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	62, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 63, 1, 61, 61, 61, 61, 61, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 61, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 62, 1, 
-	1, 1, 60, 60, 60, 60, 60, 60, 
-	60, 60, 60, 60, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 63, 
-	1, 64, 1, 64, 64, 64, 64, 64, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 64, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 65, 1, 65, 65, 
-	65, 65, 65, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 65, 1, 66, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 67, 68, 68, 
-	68, 68, 68, 68, 68, 68, 68, 1, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 1, 1, 1, 1, 1, 1, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 69, 69, 69, 69, 69, 69, 
-	69, 69, 1, 70, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 71, 71, 
-	1, 71, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 1, 1, 1, 1, 1, 
-	1, 1, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 1, 1, 1, 1, 
-	71, 1, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 71, 71, 71, 71, 
-	71, 71, 71, 71, 1, 72, 72, 72, 
-	72, 72, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 72, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	73, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 74, 1, 72, 72, 72, 72, 72, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 72, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 73, 1, 
-	1, 1, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 75, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 74, 
-	1, 76, 76, 76, 76, 76, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	76, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 77, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 78, 1, 0, 
-	0, 0, 0, 0, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 0, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 2, 1, 1, 0
+static const signed char _deserialize_json_indicies[] = {
+	1, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 2, 3, 0, 4, 5, 6,
+	7, 8, 0, 9, 10, 11, 12, 12,
+	0, 0, 0, 0, 0, 0, 13, 13,
+	0, 0, 0, 14, 15, 16, 18, 19,
+	20, 0, 0, 21, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 22, 23, 0, 0, 3,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 24,
+	20, 0, 0, 21, 0, 19, 19, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 22, 25, 25, 0, 0,
+	0, 0, 0, 0, 26, 26, 0, 0,
+	0, 27, 28, 29, 31, 32, 33, 0,
+	0, 34, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 35, 33, 0, 0, 34, 0, 32,
+	32, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 35, 36, 37,
+	37, 0, 0, 0, 0, 0, 0, 38,
+	38, 0, 0, 0, 0, 39, 40, 42,
+	0, 0, 43, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 44, 42, 0, 0, 43, 0,
+	45, 45, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 44, 46,
+	47, 48, 48, 0, 0, 0, 0, 0,
+	0, 49, 49, 0, 0, 0, 50, 51,
+	52, 54, 55, 56, 0, 0, 57, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 58, 56,
+	0, 0, 57, 0, 55, 55, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 58, 59, 59, 0, 0, 0,
+	0, 0, 0, 60, 60, 0, 0, 0,
+	61, 62, 63, 65, 66, 67, 0, 0,
+	68, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	69, 67, 0, 0, 68, 0, 66, 66,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 69, 70, 70, 0,
+	0, 0, 0, 0, 0, 71, 71, 0,
+	72, 0, 0, 73, 74, 76, 75, 75,
+	75, 75, 75, 77, 79, 0, 0, 80,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 81,
+	75, 0, 0, 0, 0, 0, 75, 83,
+	0, 0, 84, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 85, 83, 0, 0, 84, 0,
+	87, 87, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 85, 88,
+	88, 0, 0, 0, 0, 0, 0, 89,
+	89, 0, 0, 0, 0, 90, 91, 83,
+	0, 0, 84, 0, 93, 93, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 85, 94, 0, 0, 95, 0,
+	0, 0, 0, 0, 96, 1, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 2,
+	0
 };
 
-static const char _deserialize_json_trans_targs[] = {
-	1, 0, 2, 2, 3, 4, 18, 24, 
-	37, 5, 12, 6, 7, 8, 9, 11, 
-	9, 11, 10, 2, 44, 10, 44, 13, 
-	14, 15, 16, 17, 16, 17, 10, 2, 
-	44, 19, 20, 21, 22, 23, 10, 2, 
-	44, 23, 25, 31, 26, 27, 28, 29, 
-	30, 29, 30, 10, 2, 44, 32, 33, 
-	34, 35, 36, 35, 36, 10, 2, 44, 
-	38, 39, 40, 42, 43, 41, 10, 41, 
-	10, 2, 44, 43, 44, 45, 46
+static const signed char _deserialize_json_index_defaults[] = {
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	75, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0
 };
 
-static const char _deserialize_json_trans_actions[] = {
-	0, 0, 1, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 2, 2, 2, 
-	0, 0, 3, 3, 4, 0, 5, 0, 
-	0, 2, 2, 2, 0, 0, 6, 6, 
-	7, 0, 0, 0, 2, 2, 8, 8, 
-	9, 0, 0, 0, 0, 0, 2, 2, 
-	2, 0, 0, 10, 10, 11, 0, 0, 
-	2, 2, 2, 0, 0, 12, 12, 13, 
-	0, 0, 0, 2, 2, 2, 14, 0, 
-	15, 15, 16, 0, 0, 0, 0
+static const signed char _deserialize_json_cond_targs[] = {
+	0, 1, 2, 2, 3, 4, 18, 24,
+	37, 45, 5, 12, 6, 7, 8, 9,
+	11, 8, 9, 11, 10, 2, 49, 10,
+	49, 13, 14, 15, 16, 17, 15, 16,
+	17, 10, 2, 49, 19, 20, 21, 22,
+	23, 22, 10, 2, 49, 23, 25, 31,
+	26, 27, 28, 29, 30, 28, 29, 30,
+	10, 2, 49, 32, 33, 34, 35, 36,
+	34, 35, 36, 10, 2, 49, 38, 39,
+	40, 43, 44, 40, 41, 42, 41, 10,
+	2, 49, 43, 10, 2, 49, 44, 44,
+	46, 47, 43, 48, 48, 48, 49, 50,
+	51, 0
+};
+
+static const signed char _deserialize_json_cond_actions[] = {
+	0, 0, 1, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 2, 2,
+	2, 0, 0, 0, 3, 3, 4, 0,
+	5, 0, 0, 2, 2, 2, 0, 0,
+	0, 6, 6, 7, 0, 0, 0, 2,
+	2, 0, 8, 8, 9, 0, 0, 0,
+	0, 0, 2, 2, 2, 0, 0, 0,
+	10, 10, 11, 0, 0, 2, 2, 2,
+	0, 0, 0, 12, 12, 13, 0, 0,
+	2, 14, 14, 0, 15, 0, 0, 16,
+	16, 17, 0, 18, 18, 19, 0, 15,
+	0, 0, 20, 20, 0, 21, 0, 0,
+	0, 0
 };
 
 static const int deserialize_json_start = 1;
-static const int deserialize_json_first_final = 44;
+static const int deserialize_json_first_final = 49;
 static const int deserialize_json_error = 0;
 
 static const int deserialize_json_en_main = 1;
 
 
-#line 97 "hb-buffer-deserialize-json.rl"
+#line 108 "hb-buffer-deserialize-json.rl"
 
 
 static hb_bool_t
-_hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer,
-				    const char *buf,
-				    unsigned int buf_len,
-				    const char **end_ptr,
-				    hb_font_t *font)
+_hb_buffer_deserialize_json (hb_buffer_t *buffer,
+const char *buf,
+unsigned int buf_len,
+const char **end_ptr,
+hb_font_t *font)
 {
-  const char *p = buf, *pe = buf + buf_len;
-
-  /* Ensure we have positions. */
-  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
-
-  while (p < pe && ISSPACE (*p))
-    p++;
-  if (p < pe && *p == (buffer->len ? ',' : '['))
-  {
-    *end_ptr = ++p;
-  }
-
-  const char *tok = nullptr;
-  int cs;
-  hb_glyph_info_t info = {0};
-  hb_glyph_position_t pos = {0};
-  
-#line 466 "hb-buffer-deserialize-json.hh"
-	{
-	cs = deserialize_json_start;
+	const char *p = buf, *pe = buf + buf_len;
+	
+	/* Ensure we have positions. */
+	(void) hb_buffer_get_glyph_positions (buffer, nullptr);
+	
+	while (p < pe && ISSPACE (*p))
+	p++;
+	if (p < pe && *p == (buffer->len ? ',' : '['))
+		{
+		*end_ptr = ++p;
 	}
-
-#line 471 "hb-buffer-deserialize-json.hh"
+	
+	const char *tok = nullptr;
+	int cs;
+	hb_glyph_info_t info = {0};
+	hb_glyph_position_t pos = {0};
+	
+#line 223 "hb-buffer-deserialize-json.hh"
 	{
-	int _slen;
-	int _trans;
-	const unsigned char *_keys;
-	const char *_inds;
-	if ( p == pe )
-		goto _test_eof;
-	if ( cs == 0 )
-		goto _out;
-_resume:
-	_keys = _deserialize_json_trans_keys + (cs<<1);
-	_inds = _deserialize_json_indicies + _deserialize_json_index_offsets[cs];
-
-	_slen = _deserialize_json_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
-		(*p) <= _keys[1] ?
-		(*p) - _keys[0] : _slen ];
-
-	cs = _deserialize_json_trans_targs[_trans];
-
-	if ( _deserialize_json_trans_actions[_trans] == 0 )
-		goto _again;
-
-	switch ( _deserialize_json_trans_actions[_trans] ) {
-	case 1:
+		cs = (int)deserialize_json_start;
+	}
+	
+#line 228 "hb-buffer-deserialize-json.hh"
+	{
+		unsigned int _trans = 0;
+		const unsigned char * _keys;
+		const signed char * _inds;
+		int _ic;
+		_resume: {}
+		if ( p == pe )
+			goto _out;
+		_keys = ( _deserialize_json_trans_keys + ((cs<<1)));
+		_inds = ( _deserialize_json_indicies + (_deserialize_json_index_offsets[cs]));
+		
+		if ( ( (*( p))) <= 125 && ( (*( p))) >= 9 ) {
+			_ic = (int)_deserialize_json_char_class[(int)( (*( p))) - 9];
+			if ( _ic <= (int)(*( _keys+1)) && _ic >= (int)(*( _keys)) )
+				_trans = (unsigned int)(*( _inds + (int)( _ic - (int)(*( _keys)) ) )); 
+			else
+				_trans = (unsigned int)_deserialize_json_index_defaults[cs];
+		}
+		else {
+			_trans = (unsigned int)_deserialize_json_index_defaults[cs];
+		}
+		
+		cs = (int)_deserialize_json_cond_targs[_trans];
+		
+		if ( _deserialize_json_cond_actions[_trans] != 0 ) {
+			
+			switch ( _deserialize_json_cond_actions[_trans] ) {
+				case 1:  {
+					{
 #line 38 "hb-buffer-deserialize-json.rl"
-	{
-	memset (&info, 0, sizeof (info));
-	memset (&pos , 0, sizeof (pos ));
-}
-	break;
-	case 5:
+						
+						memset (&info, 0, sizeof (info));
+						memset (&pos , 0, sizeof (pos ));
+					}
+					
+#line 264 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 5:  {
+					{
 #line 43 "hb-buffer-deserialize-json.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 2:
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 280 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 2:  {
+					{
 #line 51 "hb-buffer-deserialize-json.rl"
-	{
-	tok = p;
-}
-	break;
-	case 14:
+						
+						tok = p;
+					}
+					
+#line 292 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 15:  {
+					{
 #line 55 "hb-buffer-deserialize-json.rl"
-	{
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
-	break;
-	case 15:
-#line 62 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_uint (tok, p, &info.codepoint)) return false; }
-	break;
-	case 8:
-#line 63 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
-	break;
-	case 10:
-#line 64 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
-	break;
-	case 12:
-#line 65 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-	break;
-	case 3:
+						if (unlikely (!buffer->ensure_glyphs ())) return false; }
+					
+#line 302 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 21:  {
+					{
+#line 56 "hb-buffer-deserialize-json.rl"
+						if (unlikely (!buffer->ensure_unicode ())) return false; }
+					
+#line 312 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 16:  {
+					{
+#line 58 "hb-buffer-deserialize-json.rl"
+						
+						/* TODO Unescape \" and \\ if found. */
+						if (!hb_font_glyph_from_string (font,
+						tok, p - tok,
+						&info.codepoint))
+						return false;
+					}
+					
+#line 328 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 18:  {
+					{
 #line 66 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-	break;
-	case 6:
+						if (!parse_uint (tok, p, &info.codepoint)) return false; }
+					
+#line 338 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 8:  {
+					{
 #line 67 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
-	break;
-	case 16:
-#line 62 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_uint (tok, p, &info.codepoint)) return false; }
+						if (!parse_uint (tok, p, &info.cluster )) return false; }
+					
+#line 348 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 10:  {
+					{
+#line 68 "hb-buffer-deserialize-json.rl"
+						if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+					
+#line 358 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 12:  {
+					{
+#line 69 "hb-buffer-deserialize-json.rl"
+						if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+					
+#line 368 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 3:  {
+					{
+#line 70 "hb-buffer-deserialize-json.rl"
+						if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+					
+#line 378 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 6:  {
+					{
+#line 71 "hb-buffer-deserialize-json.rl"
+						if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+					
+#line 388 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 14:  {
+					{
+#line 51 "hb-buffer-deserialize-json.rl"
+						
+						tok = p;
+					}
+					
+#line 400 "hb-buffer-deserialize-json.hh"
+					
+					{
+#line 55 "hb-buffer-deserialize-json.rl"
+						if (unlikely (!buffer->ensure_glyphs ())) return false; }
+					
+#line 406 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 20:  {
+					{
+#line 51 "hb-buffer-deserialize-json.rl"
+						
+						tok = p;
+					}
+					
+#line 418 "hb-buffer-deserialize-json.hh"
+					
+					{
+#line 56 "hb-buffer-deserialize-json.rl"
+						if (unlikely (!buffer->ensure_unicode ())) return false; }
+					
+#line 424 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 17:  {
+					{
+#line 58 "hb-buffer-deserialize-json.rl"
+						
+						/* TODO Unescape \" and \\ if found. */
+						if (!hb_font_glyph_from_string (font,
+						tok, p - tok,
+						&info.codepoint))
+						return false;
+					}
+					
+#line 440 "hb-buffer-deserialize-json.hh"
+					
+					{
 #line 43 "hb-buffer-deserialize-json.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 9:
-#line 63 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
-#line 43 "hb-buffer-deserialize-json.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 11:
-#line 64 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
-#line 43 "hb-buffer-deserialize-json.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 13:
-#line 65 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-#line 43 "hb-buffer-deserialize-json.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 4:
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 452 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 19:  {
+					{
 #line 66 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+						if (!parse_uint (tok, p, &info.codepoint)) return false; }
+					
+#line 462 "hb-buffer-deserialize-json.hh"
+					
+					{
 #line 43 "hb-buffer-deserialize-json.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 7:
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 474 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 9:  {
+					{
 #line 67 "hb-buffer-deserialize-json.rl"
-	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+						if (!parse_uint (tok, p, &info.cluster )) return false; }
+					
+#line 484 "hb-buffer-deserialize-json.hh"
+					
+					{
 #line 43 "hb-buffer-deserialize-json.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 496 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 11:  {
+					{
+#line 68 "hb-buffer-deserialize-json.rl"
+						if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+					
+#line 506 "hb-buffer-deserialize-json.hh"
+					
+					{
+#line 43 "hb-buffer-deserialize-json.rl"
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 518 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 13:  {
+					{
+#line 69 "hb-buffer-deserialize-json.rl"
+						if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+					
+#line 528 "hb-buffer-deserialize-json.hh"
+					
+					{
+#line 43 "hb-buffer-deserialize-json.rl"
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 540 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 4:  {
+					{
+#line 70 "hb-buffer-deserialize-json.rl"
+						if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+					
+#line 550 "hb-buffer-deserialize-json.hh"
+					
+					{
+#line 43 "hb-buffer-deserialize-json.rl"
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 562 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+				case 7:  {
+					{
+#line 71 "hb-buffer-deserialize-json.rl"
+						if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+					
+#line 572 "hb-buffer-deserialize-json.hh"
+					
+					{
+#line 43 "hb-buffer-deserialize-json.rl"
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 584 "hb-buffer-deserialize-json.hh"
+					
+					
+					break; 
+				}
+			}
+			
+		}
+		
+		if ( cs != 0 ) {
+			p += 1;
+			goto _resume;
+		}
+		_out: {}
+	}
+	
+#line 136 "hb-buffer-deserialize-json.rl"
+	
+	
 	*end_ptr = p;
-}
-	break;
-#line 624 "hb-buffer-deserialize-json.hh"
-	}
-
-_again:
-	if ( cs == 0 )
-		goto _out;
-	if ( ++p != pe )
-		goto _resume;
-	_test_eof: {}
-	_out: {}
-	}
-
-#line 125 "hb-buffer-deserialize-json.rl"
-
-
-  *end_ptr = p;
-
-  return p == pe && *(p-1) != ']';
+	
+	return p == pe && *(p-1) != ']';
 }
 
 #endif /* HB_BUFFER_DESERIALIZE_JSON_HH */
diff --git a/src/hb-buffer-deserialize-json.rl b/src/hb-buffer-deserialize-json.rl
index f3abb02..382423f 100644
--- a/src/hb-buffer-deserialize-json.rl
+++ b/src/hb-buffer-deserialize-json.rl
@@ -52,14 +52,18 @@
 	tok = p;
 }
 
-action parse_glyph {
+action ensure_glyphs { if (unlikely (!buffer->ensure_glyphs ())) return false; }
+action ensure_unicode { if (unlikely (!buffer->ensure_unicode ())) return false; }
+
+action parse_glyph_name {
+	/* TODO Unescape \" and \\ if found. */
 	if (!hb_font_glyph_from_string (font,
 					tok, p - tok,
 					&info.codepoint))
 	  return false;
 }
 
-action parse_gid       { if (!parse_uint (tok, p, &info.codepoint)) return false; }
+action parse_codepoint { if (!parse_uint (tok, p, &info.codepoint)) return false; }
 action parse_cluster   { if (!parse_uint (tok, p, &info.cluster )) return false; }
 action parse_x_offset  { if (!parse_int  (tok, p, &pos.x_offset )) return false; }
 action parse_y_offset  { if (!parse_int  (tok, p, &pos.y_offset )) return false; }
@@ -72,20 +76,27 @@
 comma = space* ',' space*;
 colon = space* ':' space*;
 
-glyph_id = unum;
-glyph_name = alpha (alnum|'_'|'.'|'-')*;
+codepoint = unum;
+glyph_name = '"' ([^\\"] | '\\' [\\"])* '"';
 
-glyph_string   = '"' (glyph_name >tok %parse_glyph) '"';
-glyph_number = (glyph_id >tok %parse_gid);
+parse_glyph_name   = (glyph_name >tok %parse_glyph_name);
+parse_codepoint = (codepoint >tok %parse_codepoint);
 
-glyph	= "\"g\""  colon (glyph_string | glyph_number);
+glyph	= "\"g\""  colon (parse_glyph_name | parse_codepoint);
+unicode	= "\"u\""  colon parse_codepoint;
 cluster	= "\"cl\"" colon (unum >tok %parse_cluster);
 xoffset	= "\"dx\"" colon (num >tok %parse_x_offset);
 yoffset	= "\"dy\"" colon (num >tok %parse_y_offset);
 xadvance= "\"ax\"" colon (num >tok %parse_x_advance);
 yadvance= "\"ay\"" colon (num >tok %parse_y_advance);
 
-element = glyph | cluster | xoffset | yoffset | xadvance | yadvance;
+element = glyph @ensure_glyphs
+	| unicode @ensure_unicode
+	| cluster
+	| xoffset
+	| yoffset
+	| xadvance
+	| yadvance;
 item	=
 	( '{' space* element (comma element)* space* '}')
 	>clear_item
@@ -97,7 +108,7 @@
 }%%
 
 static hb_bool_t
-_hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer,
+_hb_buffer_deserialize_json (hb_buffer_t *buffer,
 				    const char *buf,
 				    unsigned int buf_len,
 				    const char **end_ptr,
diff --git a/src/hb-buffer-deserialize-text.hh b/src/hb-buffer-deserialize-text.hh
index 67f0a12..fb36f56 100644
--- a/src/hb-buffer-deserialize-text.hh
+++ b/src/hb-buffer-deserialize-text.hh
@@ -1,30 +1,29 @@
-
 #line 1 "hb-buffer-deserialize-text.rl"
 /*
- * Copyright © 2013  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
+* Copyright © 2013  Google, Inc.
+*
+*  This is part of HarfBuzz, a text shaping library.
+*
+* Permission is hereby granted, without written agreement and without
+* license or royalty fees, to use, copy, modify, and distribute this
+* software and its documentation for any purpose, provided that the
+* above copyright notice and the following two paragraphs appear in
+* all copies of this software.
+*
+* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+* DAMAGE.
+*
+* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+* FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+*
+* Google Author(s): Behdad Esfahbod
+*/
 
 #ifndef HB_BUFFER_DESERIALIZE_TEXT_HH
 #define HB_BUFFER_DESERIALIZE_TEXT_HH
@@ -32,540 +31,734 @@
 #include "hb.hh"
 
 
-#line 36 "hb-buffer-deserialize-text.hh"
+#line 35 "hb-buffer-deserialize-text.hh"
 static const unsigned char _deserialize_text_trans_keys[] = {
-	0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, 
-	48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, 
-	9u, 122u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 
-	9u, 124u, 9u, 124u, 9u, 124u, 0
+	1u, 0u, 0u, 13u, 12u, 12u, 2u, 2u,
+	5u, 11u, 0u, 12u, 5u, 6u, 4u, 6u,
+	5u, 6u, 5u, 6u, 4u, 6u, 5u, 6u,
+	3u, 3u, 4u, 6u, 5u, 6u, 3u, 6u,
+	2u, 16u, 4u, 6u, 5u, 6u, 0u, 16u,
+	0u, 16u, 1u, 0u, 0u, 12u, 0u, 16u,
+	0u, 16u, 0u, 16u, 0u, 16u, 0u, 16u,
+	0u, 16u, 0u, 16u, 0u, 16u, 0u, 16u,
+	0u, 16u, 0u, 16u, 0u, 16u, 0u, 16u,
+	0u, 16u, 0u
 };
 
-static const char _deserialize_text_key_spans[] = {
-	0, 114, 13, 10, 13, 10, 10, 13, 
-	10, 1, 13, 10, 14, 116, 116, 0, 
-	114, 116, 116, 116, 116, 116, 116, 116, 
-	116, 116, 116
+static const signed char _deserialize_text_char_class[] = {
+	0, 0, 0, 0, 0, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 0,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 2, 3, 4, 1, 1, 5,
+	6, 6, 6, 6, 6, 6, 6, 6,
+	6, 1, 1, 7, 8, 9, 1, 10,
+	11, 11, 11, 11, 11, 11, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 12, 1, 1, 1,
+	1, 1, 13, 14, 15, 1, 1, 1,
+	11, 11, 11, 11, 11, 11, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 16, 0
 };
 
 static const short _deserialize_text_index_offsets[] = {
-	0, 0, 115, 129, 140, 154, 165, 176, 
-	190, 201, 203, 217, 228, 243, 360, 477, 
-	478, 593, 710, 827, 944, 1061, 1178, 1295, 
-	1412, 1529, 1646
+	0, 0, 14, 15, 16, 23, 36, 38,
+	41, 43, 45, 48, 50, 51, 54, 56,
+	60, 75, 78, 80, 97, 114, 114, 127,
+	144, 161, 178, 195, 212, 229, 246, 263,
+	280, 297, 314, 331, 348, 0
 };
 
-static const char _deserialize_text_indicies[] = {
-	0, 0, 0, 0, 0, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	0, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	2, 3, 3, 3, 3, 3, 3, 3, 
-	3, 3, 1, 1, 1, 1, 1, 1, 
-	1, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 1, 1, 1, 1, 1, 
-	1, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 1, 5, 1, 1, 6, 
-	7, 7, 7, 7, 7, 7, 7, 7, 
-	7, 1, 8, 9, 9, 9, 9, 9, 
-	9, 9, 9, 9, 1, 10, 1, 1, 
-	11, 12, 12, 12, 12, 12, 12, 12, 
-	12, 12, 1, 13, 14, 14, 14, 14, 
-	14, 14, 14, 14, 14, 1, 15, 16, 
-	16, 16, 16, 16, 16, 16, 16, 16, 
-	1, 17, 1, 1, 18, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 1, 20, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 1, 22, 1, 23, 1, 1, 24, 
-	25, 25, 25, 25, 25, 25, 25, 25, 
-	25, 1, 26, 27, 27, 27, 27, 27, 
-	27, 27, 27, 27, 1, 22, 1, 1, 
-	1, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 1, 28, 28, 28, 28, 
-	28, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 28, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 29, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	30, 1, 1, 31, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	32, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 33, 
-	1, 34, 34, 34, 34, 34, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	34, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 35, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 36, 1, 1, 0, 
-	0, 0, 0, 0, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 0, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 2, 3, 
-	3, 3, 3, 3, 3, 3, 3, 3, 
-	1, 1, 1, 1, 1, 1, 1, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 1, 1, 1, 1, 1, 1, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 4, 4, 4, 4, 4, 4, 4, 
-	4, 1, 28, 28, 28, 28, 28, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 28, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 29, 1, 1, 1, 
-	1, 37, 37, 37, 37, 37, 37, 37, 
-	37, 37, 37, 1, 1, 1, 30, 1, 
-	1, 31, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 32, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 33, 1, 38, 
-	38, 38, 38, 38, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 38, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 39, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 40, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 41, 1, 42, 42, 42, 42, 
-	42, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 42, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	43, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 44, 
-	1, 42, 42, 42, 42, 42, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	42, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	14, 14, 14, 14, 14, 14, 14, 14, 
-	14, 14, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 43, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 44, 1, 38, 38, 
-	38, 38, 38, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 38, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 39, 1, 1, 1, 9, 9, 9, 
-	9, 9, 9, 9, 9, 9, 9, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 40, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 41, 1, 45, 45, 45, 45, 45, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 45, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 46, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 47, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 48, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 49, 1, 
-	50, 50, 50, 50, 50, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 50, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 51, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 52, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 53, 1, 50, 50, 50, 
-	50, 50, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 50, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 51, 
-	1, 1, 1, 1, 27, 27, 27, 27, 
-	27, 27, 27, 27, 27, 27, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 52, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	53, 1, 45, 45, 45, 45, 45, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 45, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 46, 1, 1, 1, 
-	1, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 1, 1, 1, 1, 1, 
-	1, 47, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 48, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 49, 1, 28, 
-	28, 28, 28, 28, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 28, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 29, 1, 55, 55, 1, 55, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	1, 1, 1, 30, 1, 1, 31, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 1, 1, 32, 1, 55, 1, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 1, 33, 1, 0
+static const signed char _deserialize_text_indicies[] = {
+	1, 0, 0, 0, 0, 0, 0, 2,
+	0, 0, 0, 0, 0, 3, 4, 6,
+	7, 7, 0, 0, 0, 0, 7, 8,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 4, 10, 11, 13, 14,
+	15, 17, 18, 20, 21, 23, 24, 25,
+	27, 28, 29, 31, 32, 33, 35, 36,
+	29, 0, 28, 28, 38, 38, 0, 0,
+	0, 0, 38, 0, 38, 0, 0, 0,
+	38, 38, 38, 40, 41, 42, 44, 45,
+	47, 0, 0, 0, 0, 48, 48, 0,
+	49, 50, 0, 48, 0, 0, 0, 0,
+	51, 52, 0, 0, 0, 0, 0, 0,
+	0, 0, 53, 0, 0, 0, 0, 0,
+	0, 54, 8, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 4, 56,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	57, 0, 0, 0, 0, 0, 0, 58,
+	56, 0, 0, 0, 0, 60, 60, 0,
+	0, 57, 0, 0, 0, 0, 0, 0,
+	58, 63, 62, 64, 0, 62, 62, 62,
+	62, 65, 62, 66, 62, 62, 62, 67,
+	68, 69, 71, 38, 72, 0, 38, 38,
+	38, 38, 73, 38, 74, 38, 38, 38,
+	37, 75, 76, 78, 0, 0, 79, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 80, 81, 82, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 53, 83, 84, 62, 64,
+	0, 62, 62, 62, 62, 65, 62, 66,
+	62, 62, 62, 67, 68, 69, 86, 0,
+	87, 0, 0, 0, 0, 0, 0, 0,
+	88, 0, 0, 0, 0, 57, 89, 91,
+	0, 92, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 93, 94,
+	91, 0, 92, 0, 0, 36, 36, 0,
+	0, 0, 0, 0, 0, 0, 0, 93,
+	94, 86, 0, 87, 0, 0, 97, 97,
+	0, 0, 0, 88, 0, 0, 0, 0,
+	57, 89, 99, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 100, 101, 99, 0, 0, 0, 0,
+	45, 45, 0, 0, 0, 0, 0, 0,
+	0, 0, 100, 101, 78, 0, 0, 79,
+	0, 18, 18, 0, 0, 0, 0, 0,
+	0, 0, 0, 80, 81, 0
 };
 
-static const char _deserialize_text_trans_targs[] = {
-	1, 0, 13, 17, 26, 3, 18, 21, 
-	18, 21, 5, 19, 20, 19, 20, 22, 
-	25, 8, 9, 12, 9, 12, 10, 11, 
-	23, 24, 23, 24, 14, 2, 6, 7, 
-	15, 16, 14, 15, 16, 17, 14, 4, 
-	15, 16, 14, 15, 16, 14, 2, 7, 
-	15, 16, 14, 2, 15, 16, 25, 26
+static const signed char _deserialize_text_index_defaults[] = {
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 62, 38, 0, 0, 62, 0, 0,
+	0, 0, 0, 0, 0, 0
 };
 
-static const char _deserialize_text_trans_actions[] = {
-	0, 0, 1, 1, 1, 2, 2, 2, 
-	0, 0, 2, 2, 2, 0, 0, 2, 
-	2, 2, 2, 2, 0, 0, 3, 2, 
-	2, 2, 0, 0, 4, 5, 5, 5, 
-	4, 4, 0, 0, 0, 0, 6, 7, 
-	6, 6, 8, 8, 8, 9, 10, 10, 
-	9, 9, 11, 12, 11, 11, 0, 0
+static const signed char _deserialize_text_cond_targs[] = {
+	0, 1, 2, 25, 3, 3, 4, 19,
+	5, 6, 23, 24, 7, 8, 27, 36,
+	8, 27, 36, 9, 30, 33, 10, 11,
+	12, 15, 11, 12, 15, 13, 13, 14,
+	31, 32, 14, 31, 32, 16, 26, 17,
+	18, 34, 35, 18, 34, 35, 19, 20,
+	19, 6, 21, 22, 20, 21, 22, 23,
+	20, 21, 22, 24, 24, 25, 26, 26,
+	7, 9, 10, 16, 21, 29, 26, 26,
+	7, 9, 10, 21, 29, 27, 28, 17,
+	21, 29, 28, 29, 29, 30, 28, 7,
+	10, 29, 31, 28, 7, 21, 29, 32,
+	33, 33, 34, 28, 21, 29, 35, 36,
+	0
 };
 
-static const char _deserialize_text_eof_actions[] = {
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 4, 0, 0, 
-	0, 4, 6, 8, 8, 6, 9, 11, 
-	11, 9, 4
+static const signed char _deserialize_text_cond_actions[] = {
+	0, 0, 0, 0, 1, 0, 0, 2,
+	0, 0, 2, 2, 0, 3, 4, 4,
+	0, 5, 5, 0, 4, 4, 0, 3,
+	3, 3, 0, 0, 0, 6, 0, 3,
+	4, 4, 0, 5, 5, 0, 5, 0,
+	3, 4, 4, 0, 5, 5, 7, 7,
+	8, 9, 7, 7, 0, 0, 0, 10,
+	10, 10, 10, 10, 8, 11, 12, 13,
+	14, 14, 14, 15, 11, 11, 16, 17,
+	18, 18, 18, 16, 16, 19, 19, 20,
+	19, 19, 0, 0, 13, 10, 10, 21,
+	21, 10, 22, 22, 23, 22, 22, 22,
+	10, 5, 24, 24, 24, 24, 24, 19,
+	0
+};
+
+static const signed char _deserialize_text_eof_trans[] = {
+	1, 2, 3, 6, 7, 9, 10, 13,
+	17, 20, 23, 27, 28, 31, 35, 29,
+	38, 40, 44, 47, 53, 54, 55, 56,
+	60, 62, 71, 78, 83, 70, 86, 91,
+	96, 97, 99, 103, 104, 0
 };
 
 static const int deserialize_text_start = 1;
-static const int deserialize_text_first_final = 13;
+static const int deserialize_text_first_final = 19;
 static const int deserialize_text_error = 0;
 
 static const int deserialize_text_en_main = 1;
 
 
-#line 91 "hb-buffer-deserialize-text.rl"
+#line 114 "hb-buffer-deserialize-text.rl"
 
 
 static hb_bool_t
-_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer,
-				    const char *buf,
-				    unsigned int buf_len,
-				    const char **end_ptr,
-				    hb_font_t *font)
+_hb_buffer_deserialize_text (hb_buffer_t *buffer,
+const char *buf,
+unsigned int buf_len,
+const char **end_ptr,
+hb_font_t *font)
 {
-  const char *p = buf, *pe = buf + buf_len;
-
-  /* Ensure we have positions. */
-  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
-
-  while (p < pe && ISSPACE (*p))
-    p++;
-  if (p < pe && *p == (buffer->len ? '|' : '['))
-  {
-    *end_ptr = ++p;
-  }
-
-  const char *eof = pe, *tok = nullptr;
-  int cs;
-  hb_glyph_info_t info = {0};
-  hb_glyph_position_t pos = {0};
-  
-#line 343 "hb-buffer-deserialize-text.hh"
+	const char *p = buf, *pe = buf + buf_len;
+	
+	/* Ensure we have positions. */
+	(void) hb_buffer_get_glyph_positions (buffer, nullptr);
+	
+	while (p < pe && ISSPACE (*p))
+	p++;
+	
+	const char *eof = pe, *tok = nullptr;
+	int cs;
+	hb_glyph_info_t info = {0};
+	hb_glyph_position_t pos = {0};
+	
+#line 204 "hb-buffer-deserialize-text.hh"
 	{
-	cs = deserialize_text_start;
+		cs = (int)deserialize_text_start;
 	}
-
-#line 348 "hb-buffer-deserialize-text.hh"
+	
+#line 209 "hb-buffer-deserialize-text.hh"
 	{
-	int _slen;
-	int _trans;
-	const unsigned char *_keys;
-	const char *_inds;
-	if ( p == pe )
-		goto _test_eof;
-	if ( cs == 0 )
-		goto _out;
-_resume:
-	_keys = _deserialize_text_trans_keys + (cs<<1);
-	_inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs];
-
-	_slen = _deserialize_text_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
-		(*p) <= _keys[1] ?
-		(*p) - _keys[0] : _slen ];
-
-	cs = _deserialize_text_trans_targs[_trans];
-
-	if ( _deserialize_text_trans_actions[_trans] == 0 )
-		goto _again;
-
-	switch ( _deserialize_text_trans_actions[_trans] ) {
-	case 2:
-#line 51 "hb-buffer-deserialize-text.rl"
-	{
-	tok = p;
-}
-	break;
-	case 5:
-#line 55 "hb-buffer-deserialize-text.rl"
-	{
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
-	break;
-	case 10:
-#line 62 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
-	break;
-	case 3:
-#line 63 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
-	break;
-	case 12:
-#line 64 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-	break;
-	case 7:
-#line 65 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-	break;
-	case 1:
+		unsigned int _trans = 0;
+		const unsigned char * _keys;
+		const signed char * _inds;
+		int _ic;
+		_resume: {}
+		if ( p == pe && p != eof )
+			goto _out;
+		if ( p == eof ) {
+			if ( _deserialize_text_eof_trans[cs] > 0 ) {
+				_trans = (unsigned int)_deserialize_text_eof_trans[cs] - 1;
+			}
+		}
+		else {
+			_keys = ( _deserialize_text_trans_keys + ((cs<<1)));
+			_inds = ( _deserialize_text_indicies + (_deserialize_text_index_offsets[cs]));
+			
+			if ( ( (*( p))) <= 124 && ( (*( p))) >= 9 ) {
+				_ic = (int)_deserialize_text_char_class[(int)( (*( p))) - 9];
+				if ( _ic <= (int)(*( _keys+1)) && _ic >= (int)(*( _keys)) )
+					_trans = (unsigned int)(*( _inds + (int)( _ic - (int)(*( _keys)) ) )); 
+				else
+					_trans = (unsigned int)_deserialize_text_index_defaults[cs];
+			}
+			else {
+				_trans = (unsigned int)_deserialize_text_index_defaults[cs];
+			}
+			
+		}
+		cs = (int)_deserialize_text_cond_targs[_trans];
+		
+		if ( _deserialize_text_cond_actions[_trans] != 0 ) {
+			
+			switch ( _deserialize_text_cond_actions[_trans] ) {
+				case 1:  {
+					{
 #line 38 "hb-buffer-deserialize-text.rl"
-	{
-	memset (&info, 0, sizeof (info));
-	memset (&pos , 0, sizeof (pos ));
-}
+						
+						memset (&info, 0, sizeof (info));
+						memset (&pos , 0, sizeof (pos ));
+					}
+					
+#line 252 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 3:  {
+					{
 #line 51 "hb-buffer-deserialize-text.rl"
-	{
-	tok = p;
-}
-	break;
-	case 4:
+						
+						tok = p;
+					}
+					
+#line 264 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 5:  {
+					{
 #line 55 "hb-buffer-deserialize-text.rl"
-	{
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 9:
-#line 62 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 11:
-#line 64 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 6:
-#line 65 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 8:
+						if (unlikely (!buffer->ensure_glyphs ())) return false; }
+					
+#line 274 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 8:  {
+					{
+#line 56 "hb-buffer-deserialize-text.rl"
+						if (unlikely (!buffer->ensure_unicode ())) return false; }
+					
+#line 284 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 18:  {
+					{
+#line 58 "hb-buffer-deserialize-text.rl"
+						
+						/* TODO Unescape delimeters. */
+						if (!hb_font_glyph_from_string (font,
+						tok, p - tok,
+						&info.codepoint))
+						return false;
+					}
+					
+#line 300 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 9:  {
+					{
 #line 66 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-#line 480 "hb-buffer-deserialize-text.hh"
-	}
-
-_again:
-	if ( cs == 0 )
-		goto _out;
-	if ( ++p != pe )
-		goto _resume;
-	_test_eof: {}
-	if ( p == eof )
-	{
-	switch ( _deserialize_text_eof_actions[cs] ) {
-	case 4:
+						if (!parse_hex (tok, p, &info.codepoint )) return false; }
+					
+#line 310 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 21:  {
+					{
+#line 68 "hb-buffer-deserialize-text.rl"
+						if (!parse_uint (tok, p, &info.cluster )) return false; }
+					
+#line 320 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 6:  {
+					{
+#line 69 "hb-buffer-deserialize-text.rl"
+						if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+					
+#line 330 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 23:  {
+					{
+#line 70 "hb-buffer-deserialize-text.rl"
+						if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+					
+#line 340 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 20:  {
+					{
+#line 71 "hb-buffer-deserialize-text.rl"
+						if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+					
+#line 350 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 15:  {
+					{
+#line 38 "hb-buffer-deserialize-text.rl"
+						
+						memset (&info, 0, sizeof (info));
+						memset (&pos , 0, sizeof (pos ));
+					}
+					
+#line 363 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 51 "hb-buffer-deserialize-text.rl"
+						
+						tok = p;
+					}
+					
+#line 371 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 4:  {
+					{
+#line 51 "hb-buffer-deserialize-text.rl"
+						
+						tok = p;
+					}
+					
+#line 383 "hb-buffer-deserialize-text.hh"
+					
+					{
 #line 55 "hb-buffer-deserialize-text.rl"
-	{
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
+						if (unlikely (!buffer->ensure_glyphs ())) return false; }
+					
+#line 389 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 2:  {
+					{
+#line 51 "hb-buffer-deserialize-text.rl"
+						
+						tok = p;
+					}
+					
+#line 401 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 56 "hb-buffer-deserialize-text.rl"
+						if (unlikely (!buffer->ensure_unicode ())) return false; }
+					
+#line 407 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 16:  {
+					{
+#line 58 "hb-buffer-deserialize-text.rl"
+						
+						/* TODO Unescape delimeters. */
+						if (!hb_font_glyph_from_string (font,
+						tok, p - tok,
+						&info.codepoint))
+						return false;
+					}
+					
+#line 423 "hb-buffer-deserialize-text.hh"
+					
+					{
 #line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 9:
-#line 62 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 11:
-#line 64 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 6:
-#line 65 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 8:
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 435 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 7:  {
+					{
 #line 66 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+						if (!parse_hex (tok, p, &info.codepoint )) return false; }
+					
+#line 445 "hb-buffer-deserialize-text.hh"
+					
+					{
 #line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 457 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 10:  {
+					{
+#line 68 "hb-buffer-deserialize-text.rl"
+						if (!parse_uint (tok, p, &info.cluster )) return false; }
+					
+#line 467 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 43 "hb-buffer-deserialize-text.rl"
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 479 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 22:  {
+					{
+#line 70 "hb-buffer-deserialize-text.rl"
+						if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+					
+#line 489 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 43 "hb-buffer-deserialize-text.rl"
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 501 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 19:  {
+					{
+#line 71 "hb-buffer-deserialize-text.rl"
+						if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+					
+#line 511 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 43 "hb-buffer-deserialize-text.rl"
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 523 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 24:  {
+					{
+#line 72 "hb-buffer-deserialize-text.rl"
+						if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+					
+#line 533 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 43 "hb-buffer-deserialize-text.rl"
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 545 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 12:  {
+					{
+#line 38 "hb-buffer-deserialize-text.rl"
+						
+						memset (&info, 0, sizeof (info));
+						memset (&pos , 0, sizeof (pos ));
+					}
+					
+#line 558 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 51 "hb-buffer-deserialize-text.rl"
+						
+						tok = p;
+					}
+					
+#line 566 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 55 "hb-buffer-deserialize-text.rl"
+						if (unlikely (!buffer->ensure_glyphs ())) return false; }
+					
+#line 572 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 14:  {
+					{
+#line 38 "hb-buffer-deserialize-text.rl"
+						
+						memset (&info, 0, sizeof (info));
+						memset (&pos , 0, sizeof (pos ));
+					}
+					
+#line 585 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 51 "hb-buffer-deserialize-text.rl"
+						
+						tok = p;
+					}
+					
+#line 593 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 58 "hb-buffer-deserialize-text.rl"
+						
+						/* TODO Unescape delimeters. */
+						if (!hb_font_glyph_from_string (font,
+						tok, p - tok,
+						&info.codepoint))
+						return false;
+					}
+					
+#line 605 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 17:  {
+					{
+#line 58 "hb-buffer-deserialize-text.rl"
+						
+						/* TODO Unescape delimeters. */
+						if (!hb_font_glyph_from_string (font,
+						tok, p - tok,
+						&info.codepoint))
+						return false;
+					}
+					
+#line 621 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 55 "hb-buffer-deserialize-text.rl"
+						if (unlikely (!buffer->ensure_glyphs ())) return false; }
+					
+#line 627 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 43 "hb-buffer-deserialize-text.rl"
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 639 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 11:  {
+					{
+#line 38 "hb-buffer-deserialize-text.rl"
+						
+						memset (&info, 0, sizeof (info));
+						memset (&pos , 0, sizeof (pos ));
+					}
+					
+#line 652 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 51 "hb-buffer-deserialize-text.rl"
+						
+						tok = p;
+					}
+					
+#line 660 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 58 "hb-buffer-deserialize-text.rl"
+						
+						/* TODO Unescape delimeters. */
+						if (!hb_font_glyph_from_string (font,
+						tok, p - tok,
+						&info.codepoint))
+						return false;
+					}
+					
+#line 672 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 43 "hb-buffer-deserialize-text.rl"
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 684 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+				case 13:  {
+					{
+#line 38 "hb-buffer-deserialize-text.rl"
+						
+						memset (&info, 0, sizeof (info));
+						memset (&pos , 0, sizeof (pos ));
+					}
+					
+#line 697 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 51 "hb-buffer-deserialize-text.rl"
+						
+						tok = p;
+					}
+					
+#line 705 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 58 "hb-buffer-deserialize-text.rl"
+						
+						/* TODO Unescape delimeters. */
+						if (!hb_font_glyph_from_string (font,
+						tok, p - tok,
+						&info.codepoint))
+						return false;
+					}
+					
+#line 717 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 55 "hb-buffer-deserialize-text.rl"
+						if (unlikely (!buffer->ensure_glyphs ())) return false; }
+					
+#line 723 "hb-buffer-deserialize-text.hh"
+					
+					{
+#line 43 "hb-buffer-deserialize-text.rl"
+						
+						buffer->add_info (info);
+						if (unlikely (!buffer->successful))
+						return false;
+						buffer->pos[buffer->len - 1] = pos;
+						*end_ptr = p;
+					}
+					
+#line 735 "hb-buffer-deserialize-text.hh"
+					
+					
+					break; 
+				}
+			}
+			
+		}
+		
+		if ( p == eof ) {
+			if ( cs >= 19 )
+				goto _out;
+		}
+		else {
+			if ( cs != 0 ) {
+				p += 1;
+				goto _resume;
+			}
+		}
+		_out: {}
+	}
+	
+#line 138 "hb-buffer-deserialize-text.rl"
+	
+	
 	*end_ptr = p;
-}
-	break;
-#line 557 "hb-buffer-deserialize-text.hh"
-	}
-	}
-
-	_out: {}
-	}
-
-#line 119 "hb-buffer-deserialize-text.rl"
-
-
-  *end_ptr = p;
-
-  return p == pe && *(p-1) != ']';
+	
+	return p == pe && *(p-1) != ']';
 }
 
 #endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */
diff --git a/src/hb-buffer-deserialize-text.rl b/src/hb-buffer-deserialize-text.rl
index 6268a6c..a217028 100644
--- a/src/hb-buffer-deserialize-text.rl
+++ b/src/hb-buffer-deserialize-text.rl
@@ -52,30 +52,37 @@
 	tok = p;
 }
 
+action ensure_glyphs { if (unlikely (!buffer->ensure_glyphs ())) return false; }
+action ensure_unicode { if (unlikely (!buffer->ensure_unicode ())) return false; }
+
 action parse_glyph {
+	/* TODO Unescape delimeters. */
 	if (!hb_font_glyph_from_string (font,
 					tok, p - tok,
 					&info.codepoint))
 	  return false;
 }
 
+action parse_hexdigits  {if (!parse_hex (tok, p, &info.codepoint )) return false; }
+
 action parse_cluster   { if (!parse_uint (tok, p, &info.cluster )) return false; }
 action parse_x_offset  { if (!parse_int  (tok, p, &pos.x_offset )) return false; }
 action parse_y_offset  { if (!parse_int  (tok, p, &pos.y_offset )) return false; }
 action parse_x_advance { if (!parse_int  (tok, p, &pos.x_advance)) return false; }
 action parse_y_advance { if (!parse_int  (tok, p, &pos.y_advance)) return false; }
 
-unum	= '0' | [1-9] digit*;
+unum  = '0' | [1-9] digit*;
 num	= '-'? unum;
 
 glyph_id = unum;
-glyph_name = alpha (alnum|'_'|'.'|'-')*;
+glyph_name = ([^\\\]=@+,|] | '\\' [\\\]=@+,|]) *;
 
 glyph	= (glyph_id | glyph_name) >tok %parse_glyph;
 cluster	= '=' (unum >tok %parse_cluster);
 offsets	= '@' (num >tok %parse_x_offset)   ',' (num >tok %parse_y_offset );
 advances= '+' (num >tok %parse_x_advance) (',' (num >tok %parse_y_advance))?;
-item	=
+
+glyph_item	=
 	(
 		glyph
 		cluster?
@@ -83,15 +90,31 @@
 		advances?
 	)
 	>clear_item
+	@ensure_glyphs
 	%add_item
 	;
 
-main := space* item (space* '|' space* item)* space* ('|'|']')?;
+unicode = 'U' '+' xdigit+ >tok %parse_hexdigits;
+
+unicode_item	=
+	(
+		unicode
+		cluster?
+	)
+	>clear_item
+	@ensure_unicode
+	%add_item
+	;
+
+glyphs = glyph_item (space* '|' space* glyph_item)* space* ('|'|']')?;
+unicodes = unicode_item (space* '|' space* unicode_item)* space* ('|'|'>')?;
+
+main := space* ( ('[' glyphs) | ('<' unicodes) );
 
 }%%
 
 static hb_bool_t
-_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer,
+_hb_buffer_deserialize_text (hb_buffer_t *buffer,
 				    const char *buf,
 				    unsigned int buf_len,
 				    const char **end_ptr,
@@ -104,10 +127,6 @@
 
   while (p < pe && ISSPACE (*p))
     p++;
-  if (p < pe && *p == (buffer->len ? '|' : '['))
-  {
-    *end_ptr = ++p;
-  }
 
   const char *eof = pe, *tok = nullptr;
   int cs;
diff --git a/src/hb-buffer-serialize.cc b/src/hb-buffer-serialize.cc
index aeeb197..6539b89 100644
--- a/src/hb-buffer-serialize.cc
+++ b/src/hb-buffer-serialize.cc
@@ -91,26 +91,26 @@
 {
   switch ((unsigned) format)
   {
-    case HB_BUFFER_SERIALIZE_FORMAT_TEXT:	return serialize_formats[0];
-    case HB_BUFFER_SERIALIZE_FORMAT_JSON:	return serialize_formats[1];
+    case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0];
+    case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1];
     default:
-    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:	return nullptr;
+    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:  return nullptr;
   }
 }
 
 static unsigned int
 _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer,
-				  unsigned int start,
-				  unsigned int end,
-				  char *buf,
-				  unsigned int buf_size,
-				  unsigned int *buf_consumed,
-				  hb_font_t *font,
-				  hb_buffer_serialize_flags_t flags)
+                                  unsigned int start,
+                                  unsigned int end,
+                                  char *buf,
+                                  unsigned int buf_size,
+                                  unsigned int *buf_consumed,
+                                  hb_font_t *font,
+                                  hb_buffer_serialize_flags_t flags)
 {
   hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
   hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ?
-			     nullptr : hb_buffer_get_glyph_positions (buffer, nullptr);
+                             nullptr : hb_buffer_get_glyph_positions (buffer, nullptr);
 
   *buf_consumed = 0;
   hb_position_t x = 0, y = 0;
@@ -125,6 +125,8 @@
 
     if (i)
       *p++ = ',';
+    else
+      *p++ = '[';
 
     *p++ = '{';
 
@@ -134,8 +136,9 @@
       char g[128];
       hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g));
       *p++ = '"';
-      for (char *q = g; *q; q++) {
-	if (*q == '"')
+      for (char *q = g; *q; q++)
+      {
+	if (unlikely (*q == '"' || *q == '\\'))
 	  *p++ = '\\';
 	*p++ = *q;
       }
@@ -151,16 +154,16 @@
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
       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));
+		   x+pos[i].x_offset, y+pos[i].y_offset));
       if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
-	p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
-			       pos[i].x_advance, pos[i].y_advance));
+        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 += hb_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)
@@ -168,12 +171,14 @@
       hb_glyph_extents_t extents;
       hb_font_get_glyph_extents(font, info[i].codepoint, &extents);
       p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d",
-		extents.x_bearing, extents.y_bearing));
+                                extents.x_bearing, extents.y_bearing));
       p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d",
-		extents.width, extents.height));
+                                extents.width, extents.height));
     }
 
     *p++ = '}';
+    if (i == end-1)
+      *p++ = ']';
 
     unsigned int l = p - b;
     if (buf_size > l)
@@ -197,18 +202,71 @@
 }
 
 static unsigned int
+_hb_buffer_serialize_unicode_json (hb_buffer_t *buffer,
+          unsigned int start,
+          unsigned int end,
+          char *buf,
+          unsigned int buf_size,
+          unsigned int *buf_consumed,
+          hb_buffer_serialize_flags_t flags)
+{
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
+
+  *buf_consumed = 0;
+  for (unsigned int i = start; i < end; i++)
+  {
+    char b[1024];
+    char *p = b;
+
+    if (i)
+      *p++ = ',';
+    else
+      *p++ = '[';
+
+    *p++ = '{';
+
+    APPEND ("\"u\":");
+
+    p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
+      p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
+    }
+
+    *p++ = '}';
+
+    if (i == end-1)
+      *p++ = ']';
+
+    unsigned int l = p - b;
+    if (buf_size > l)
+    {
+      memcpy (buf, b, l);
+      buf += l;
+      buf_size -= l;
+      *buf_consumed += l;
+      *buf = '\0';
+    } else
+      return i - start;
+
+  }
+
+  return end - start;
+}
+
+static unsigned int
 _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
-				  unsigned int start,
-				  unsigned int end,
-				  char *buf,
-				  unsigned int buf_size,
-				  unsigned int *buf_consumed,
-				  hb_font_t *font,
-				  hb_buffer_serialize_flags_t flags)
+                                  unsigned int start,
+                                  unsigned int end,
+                                  char *buf,
+                                  unsigned int buf_size,
+                                  unsigned int *buf_consumed,
+                                  hb_font_t *font,
+                                  hb_buffer_serialize_flags_t flags)
 {
   hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
   hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ?
-			     nullptr : hb_buffer_get_glyph_positions (buffer, nullptr);
+           nullptr : hb_buffer_get_glyph_positions (buffer, nullptr);
 
   *buf_consumed = 0;
   hb_position_t x = 0, y = 0;
@@ -221,9 +279,12 @@
 
     if (i)
       *p++ = '|';
+    else
+      *p++ = '[';
 
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES))
     {
+      /* TODO Escape delimiters we use. */
       hb_font_glyph_to_string (font, info[i].codepoint, p, 128);
       p += strlen (p);
     }
@@ -237,21 +298,21 @@
     if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
     {
       if (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));
+        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 += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
-	if (pos[i].y_advance)
-	  p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance));
+        *p++ = '+';
+        p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
+        if (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 += hb_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)
@@ -261,6 +322,10 @@
       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));
     }
 
+    if (i == end-1) {
+      *p++ = ']';
+    }
+
     unsigned int l = p - b;
     if (buf_size > l)
     {
@@ -282,6 +347,51 @@
   return end - start;
 }
 
+
+static unsigned int
+_hb_buffer_serialize_unicode_text (hb_buffer_t *buffer,
+                                   unsigned int start,
+                                   unsigned int end,
+                                   char *buf,
+                                   unsigned int buf_size,
+                                   unsigned int *buf_consumed,
+                                   hb_buffer_serialize_flags_t flags)
+{
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
+  *buf_consumed = 0;
+  for (unsigned int i = start; i < end; i++)
+  {
+    char b[1024];
+    char *p = b;
+
+    if (i)
+      *p++ = '|';
+    else
+      *p++ = '<';
+
+    p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "U+%04X", info[i].codepoint));
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
+      p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster));
+    }
+
+    if (i == end-1)
+      *p++ = '>';
+
+    unsigned int l = p - b;
+    if (buf_size > l)
+    {
+      memcpy (buf, b, l);
+      buf += l;
+      buf_size -= l;
+      *buf_consumed += l;
+      *buf = '\0';
+    } else
+      return i - start;
+  }
+  return end - start;
+}
+
 /**
  * hb_buffer_serialize_glyphs:
  * @buffer: an #hb_buffer_t buffer.
@@ -290,8 +400,8 @@
  * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to
  *       write serialized buffer into.
  * @buf_size: the size of @buf.
- * @buf_consumed: (out) (allow-none): if not %NULL, will be set to the number of byes written into @buf.
- * @font: (allow-none): the #hb_font_t used to shape this buffer, needed to
+ * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of byes written into @buf.
+ * @font: (nullable): the #hb_font_t used to shape this buffer, needed to
  *        read glyph names and extents. If %NULL, and empty font will be used.
  * @format: the #hb_buffer_serialize_format_t to use for formatting the output.
  * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties
@@ -308,6 +418,7 @@
  * ```
  * [uni0651=0@518,0+0|uni0628=0+1897]
  * ```
+ *
  * - The serialized glyphs are delimited with `[` and `]`.
  * - Glyphs are separated with `|`
  * - Each glyph starts with glyph name, or glyph index if
@@ -316,12 +427,28 @@
  *   - If #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set, the #hb_glyph_position_t in the format:
  *     - If both #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not 0, `@x_offset,y_offset`. Then,
  *     - `+x_advance`, then `,y_advance` if #hb_glyph_position_t.y_advance is not 0. Then,
- *   - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the
- *     #hb_glyph_extents_t in the format
- *     `&lt;x_bearing,y_bearing,width,height&gt;`
+ *   - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the #hb_glyph_extents_t in the format `<x_bearing,y_bearing,width,height>`
  *
  * ## json
- * TODO.
+ * A machine-readable, structured format.
+ * The serialized glyphs will look something like:
+ *
+ * ```
+ * [{"g":"uni0651","cl":0,"dx":518,"dy":0,"ax":0,"ay":0},
+ * {"g":"uni0628","cl":0,"dx":0,"dy":0,"ax":1897,"ay":0}]
+ * ```
+ *
+ * Each glyph is a JSON object, with the following properties:
+ * - `g`: the glyph name or glyph index if
+ *   #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set.
+ * - `cl`: #hb_glyph_info_t.cluster if
+ *   #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set.
+ * - `dx`,`dy`,`ax`,`ay`: #hb_glyph_position_t.x_offset, #hb_glyph_position_t.y_offset,
+ *    #hb_glyph_position_t.x_advance and #hb_glyph_position_t.y_advance
+ *    respectively, if #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set.
+ * - `xb`,`yb`,`w`,`h`: #hb_glyph_extents_t.x_bearing, #hb_glyph_extents_t.y_bearing,
+ *    #hb_glyph_extents_t.width and #hb_glyph_extents_t.height respectively if
+ *    #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set.
  *
  * Return value:
  * The number of serialized items.
@@ -330,16 +457,17 @@
  **/
 unsigned int
 hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
-			    unsigned int start,
-			    unsigned int end,
-			    char *buf,
-			    unsigned int buf_size,
-			    unsigned int *buf_consumed,
-			    hb_font_t *font,
-			    hb_buffer_serialize_format_t format,
-			    hb_buffer_serialize_flags_t flags)
+                            unsigned int start,
+                            unsigned int end,
+                            char *buf,
+                            unsigned int buf_size,
+                            unsigned int *buf_consumed,
+                            hb_font_t *font,
+                            hb_buffer_serialize_format_t format,
+                            hb_buffer_serialize_flags_t flags)
 {
-  assert (start <= end && end <= buffer->len);
+  end = hb_clamp (end, start, buffer->len);
+  start = hb_min (start, end);
 
   unsigned int sconsumed;
   if (!buf_consumed)
@@ -348,8 +476,7 @@
   if (buf_size)
     *buf = '\0';
 
-  assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) ||
-	  (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS));
+  buffer->assert_glyphs ();
 
   if (!buffer->have_positions)
     flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS;
@@ -364,13 +491,13 @@
   {
     case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
       return _hb_buffer_serialize_glyphs_text (buffer, start, end,
-					       buf, buf_size, buf_consumed,
-					       font, flags);
+                 buf, buf_size, buf_consumed,
+                 font, flags);
 
     case HB_BUFFER_SERIALIZE_FORMAT_JSON:
       return _hb_buffer_serialize_glyphs_json (buffer, start, end,
-					       buf, buf_size, buf_consumed,
-					       font, flags);
+                 buf, buf_size, buf_consumed,
+                 font, flags);
 
     default:
     case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
@@ -379,6 +506,184 @@
   }
 }
 
+/**
+ * hb_buffer_serialize_unicode:
+ * @buffer: an #hb_buffer_t buffer.
+ * @start: the first item in @buffer to serialize.
+ * @end: the last item in @buffer to serialize.
+ * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to
+ *       write serialized buffer into.
+ * @buf_size: the size of @buf.
+ * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of byes written into @buf.
+ * @format: the #hb_buffer_serialize_format_t to use for formatting the output.
+ * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties
+ *         to serialize.
+ *
+ * Serializes @buffer into a textual representation of its content,
+ * when the buffer contains Unicode codepoints (i.e., before shaping). This is
+ * useful for showing the contents of the buffer, for example during debugging.
+ * There are currently two supported serialization formats:
+ *
+ * ## text
+ * A human-readable, plain text format.
+ * The serialized codepoints will look something like:
+ *
+ * ```
+ *  <U+0651=0|U+0628=1>
+ * ```
+ *
+ * - Glyphs are separated with `|`
+ * - Unicode codepoints are expressed as zero-padded four (or more)
+ *   digit hexadecimal numbers preceded by `U+`
+ * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, the cluster
+ *   will be indicated with a `=` then #hb_glyph_info_t.cluster.
+ *
+ * ## json
+ * A machine-readable, structured format.
+ * The serialized codepoints will be a list of objects with the following
+ * properties:
+ * - `u`: the Unicode codepoint as a decimal integer
+ * - `cl`: #hb_glyph_info_t.cluster if
+ *   #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set.
+ *
+ * For example:
+ *
+ * ```
+ * [{u:1617,cl:0},{u:1576,cl:1}]
+ * ```
+ *
+ * Return value:
+ * The number of serialized items.
+ *
+ * Since: 2.7.3
+ **/
+unsigned int
+hb_buffer_serialize_unicode (hb_buffer_t *buffer,
+                             unsigned int start,
+                             unsigned int end,
+                             char *buf,
+                             unsigned int buf_size,
+                             unsigned int *buf_consumed,
+                             hb_buffer_serialize_format_t format,
+                             hb_buffer_serialize_flags_t flags)
+{
+  end = hb_clamp (end, start, buffer->len);
+  start = hb_min (start, end);
+
+  unsigned int sconsumed;
+  if (!buf_consumed)
+    buf_consumed = &sconsumed;
+  *buf_consumed = 0;
+  if (buf_size)
+    *buf = '\0';
+
+  buffer->assert_unicode ();
+
+  if (unlikely (start == end))
+    return 0;
+
+  switch (format)
+  {
+    case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
+      return _hb_buffer_serialize_unicode_text (buffer, start, end,
+                                                buf, buf_size, buf_consumed, flags);
+
+    case HB_BUFFER_SERIALIZE_FORMAT_JSON:
+      return _hb_buffer_serialize_unicode_json (buffer, start, end,
+                                                buf, buf_size, buf_consumed, flags);
+
+    default:
+    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
+      return 0;
+
+  }
+}
+
+static unsigned int
+_hb_buffer_serialize_invalid (hb_buffer_t *buffer,
+                              unsigned int start,
+                              unsigned int end,
+                              char *buf,
+                              unsigned int buf_size,
+                              unsigned int *buf_consumed,
+                              hb_buffer_serialize_format_t format,
+                              hb_buffer_serialize_flags_t flags)
+{
+  assert (!buffer->len);
+
+  unsigned int sconsumed;
+  if (!buf_consumed)
+    buf_consumed = &sconsumed;
+  if (buf_size < 3)
+    return 0;
+  if (format == HB_BUFFER_SERIALIZE_FORMAT_JSON) {
+    *buf++ = '[';
+    *buf++ = ']';
+    *buf = '\0';
+  } else if (format == HB_BUFFER_SERIALIZE_FORMAT_TEXT) {
+    *buf++ = '!';
+    *buf++ = '!';
+    *buf = '\0';
+  }
+  *buf_consumed = 2;
+  return 0;
+}
+
+/**
+ * hb_buffer_serialize:
+ * @buffer: an #hb_buffer_t buffer.
+ * @start: the first item in @buffer to serialize.
+ * @end: the last item in @buffer to serialize.
+ * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to
+ *       write serialized buffer into.
+ * @buf_size: the size of @buf.
+ * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of byes written into @buf.
+ * @font: (nullable): the #hb_font_t used to shape this buffer, needed to
+ *        read glyph names and extents. If %NULL, and empty font will be used.
+ * @format: the #hb_buffer_serialize_format_t to use for formatting the output.
+ * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties
+ *         to serialize.
+ *
+ * Serializes @buffer into a textual representation of its content, whether
+ * Unicode codepoints or glyph identifiers and positioning information. This is
+ * useful for showing the contents of the buffer, for example during debugging.
+ * See the documentation of hb_buffer_serialize_unicode() and
+ * hb_buffer_serialize_glyphs() for a description of the output format.
+ *
+ * Return value:
+ * The number of serialized items.
+ *
+ * Since: 2.7.3
+ **/
+unsigned int
+hb_buffer_serialize (hb_buffer_t *buffer,
+                     unsigned int start,
+                     unsigned int end,
+                     char *buf,
+                     unsigned int buf_size,
+                     unsigned int *buf_consumed,
+                     hb_font_t *font,
+                     hb_buffer_serialize_format_t format,
+                     hb_buffer_serialize_flags_t flags)
+{
+  switch (buffer->content_type)
+  {
+
+    case HB_BUFFER_CONTENT_TYPE_GLYPHS:
+      return hb_buffer_serialize_glyphs (buffer, start, end, buf, buf_size,
+					 buf_consumed, font, format, flags);
+
+    case HB_BUFFER_CONTENT_TYPE_UNICODE:
+      return hb_buffer_serialize_unicode (buffer, start, end, buf, buf_size,
+					  buf_consumed, format, flags);
+
+    case HB_BUFFER_CONTENT_TYPE_INVALID:
+    default:
+      return _hb_buffer_serialize_invalid (buffer, start, end, buf, buf_size,
+					   buf_consumed, format, flags);
+  }
+}
+
 static bool
 parse_int (const char *pp, const char *end, int32_t *pv)
 {
@@ -403,43 +708,57 @@
   return true;
 }
 
+static bool
+parse_hex (const char *pp, const char *end, uint32_t *pv)
+{
+  unsigned int v;
+  const char *p = pp;
+  if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, 16)))
+    return false;
+
+  *pv = v;
+  return true;
+}
+
 #include "hb-buffer-deserialize-json.hh"
 #include "hb-buffer-deserialize-text.hh"
 
 /**
  * hb_buffer_deserialize_glyphs:
  * @buffer: an #hb_buffer_t buffer.
- * @buf: (array length=buf_len):
- * @buf_len:
- * @end_ptr: (out):
- * @font:
- * @format:
+ * @buf: (array length=buf_len): string to deserialize
+ * @buf_len: the size of @buf, or -1 if it is %NULL-terminated
+ * @end_ptr: (out) (optional): output pointer to the character after last
+ *                               consumed one.
+ * @font: (nullable): font for getting glyph IDs
+ * @format: the #hb_buffer_serialize_format_t of the input @buf
  *
+ * Deserializes glyphs @buffer from textual representation in the format
+ * produced by hb_buffer_serialize_glyphs().
  *
- *
- * Return value:
+ * Return value: %true if @buf is not fully consumed, %false otherwise.
  *
  * Since: 0.9.7
  **/
 hb_bool_t
 hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
-			      const char *buf,
-			      int buf_len, /* -1 means nul-terminated */
-			      const char **end_ptr, /* May be NULL */
-			      hb_font_t *font, /* May be NULL */
-			      hb_buffer_serialize_format_t format)
+                              const char *buf,
+                              int buf_len, /* -1 means nul-terminated */
+                              const char **end_ptr, /* May be NULL */
+                              hb_font_t *font, /* May be NULL */
+                              hb_buffer_serialize_format_t format)
 {
   const char *end;
   if (!end_ptr)
     end_ptr = &end;
   *end_ptr = buf;
 
-  assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) ||
-	  (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS));
+  buffer->assert_glyphs ();
 
   if (unlikely (hb_object_is_immutable (buffer)))
   {
-    *end_ptr = buf;
+    if (end_ptr)
+      *end_ptr = buf;
     return false;
   }
 
@@ -460,14 +779,84 @@
   switch (format)
   {
     case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
-      return _hb_buffer_deserialize_glyphs_text (buffer,
-						 buf, buf_len, end_ptr,
-						 font);
+      return _hb_buffer_deserialize_text (buffer,
+                                          buf, buf_len, end_ptr,
+                                          font);
 
     case HB_BUFFER_SERIALIZE_FORMAT_JSON:
-      return _hb_buffer_deserialize_glyphs_json (buffer,
-						 buf, buf_len, end_ptr,
-						 font);
+      return _hb_buffer_deserialize_json (buffer,
+                                          buf, buf_len, end_ptr,
+                                          font);
+
+    default:
+    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
+      return false;
+
+  }
+}
+
+
+/**
+ * hb_buffer_deserialize_unicode:
+ * @buffer: an #hb_buffer_t buffer.
+ * @buf: (array length=buf_len): string to deserialize
+ * @buf_len: the size of @buf, or -1 if it is %NULL-terminated
+ * @end_ptr: (out) (optional): output pointer to the character after last
+ *                               consumed one.
+ * @format: the #hb_buffer_serialize_format_t of the input @buf
+ *
+ * Deserializes Unicode @buffer from textual representation in the format
+ * produced by hb_buffer_serialize_unicode().
+ *
+ * Return value: %true if @buf is not fully consumed, %false otherwise.
+ *
+ * Since: 2.7.3
+ **/
+hb_bool_t
+hb_buffer_deserialize_unicode (hb_buffer_t *buffer,
+                               const char *buf,
+                               int buf_len, /* -1 means nul-terminated */
+                               const char **end_ptr, /* May be NULL */
+                               hb_buffer_serialize_format_t format)
+{
+  const char *end;
+  if (!end_ptr)
+    end_ptr = &end;
+  *end_ptr = buf;
+
+  buffer->assert_unicode ();
+
+  if (unlikely (hb_object_is_immutable (buffer)))
+  {
+    if (end_ptr)
+      *end_ptr = buf;
+    return false;
+  }
+
+  if (buf_len == -1)
+    buf_len = strlen (buf);
+
+  if (!buf_len)
+  {
+    *end_ptr = buf;
+    return false;
+  }
+
+  hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_UNICODE);
+
+  hb_font_t* font = hb_font_get_empty ();
+
+  switch (format)
+  {
+    case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
+      return _hb_buffer_deserialize_text (buffer,
+                                          buf, buf_len, end_ptr,
+                                          font);
+
+    case HB_BUFFER_SERIALIZE_FORMAT_JSON:
+      return _hb_buffer_deserialize_json (buffer,
+                                          buf, buf_len, end_ptr,
+                                          font);
 
     default:
     case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index 4fadbb7..4dcc956 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -37,8 +37,9 @@
  * @short_description: Input and output buffers
  * @include: hb.h
  *
- * Buffers serve dual role in HarfBuzz; they hold the input characters that are
- * passed to hb_shape(), and after shaping they hold the output glyphs.
+ * Buffers serve a dual role in HarfBuzz; before shaping, they hold
+ * the input characters that are passed to hb_shape(), and after
+ * shaping they hold the output glyphs.
  **/
 
 
@@ -50,7 +51,7 @@
  * Checks the equality of two #hb_segment_properties_t's.
  *
  * Return value:
- * %true if all properties of @a equal those of @b, false otherwise.
+ * %true if all properties of @a equal those of @b, %false otherwise.
  *
  * Since: 0.9.7
  **/
@@ -617,8 +618,7 @@
 void
 hb_buffer_t::guess_segment_properties ()
 {
-  assert ((content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) ||
-	  (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID)));
+  assert_unicode ();
 
   /* If script is set to INVALID, guess from buffer contents */
   if (props.script == HB_SCRIPT_INVALID) {
@@ -706,9 +706,9 @@
 /**
  * hb_buffer_get_empty:
  *
+ * Fetches an empty #hb_buffer_t.
  *
- *
- * Return value: (transfer full):
+ * Return value: (transfer full): The empty buffer
  *
  * Since: 0.9.2
  **/
@@ -720,7 +720,7 @@
 
 /**
  * hb_buffer_reference: (skip)
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * Increases the reference count on @buffer by one. This prevents @buffer from
  * being destroyed until a matching call to hb_buffer_destroy() is made.
@@ -738,7 +738,7 @@
 
 /**
  * hb_buffer_destroy: (skip)
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * Deallocate the @buffer.
  * Decreases the reference count on @buffer by one. If the result is zero, then
@@ -765,15 +765,15 @@
 
 /**
  * hb_buffer_set_user_data: (skip)
- * @buffer: an #hb_buffer_t.
- * @key:
- * @data:
- * @destroy:
- * @replace:
+ * @buffer: An #hb_buffer_t
+ * @key: The user-data key
+ * @data: A pointer to the user data
+ * @destroy: (nullable): A callback to call when @data is not needed anymore
+ * @replace: Whether to replace an existing data with the same key
  *
+ * Attaches a user-data key/data pair to the specified buffer. 
  *
- *
- * Return value:
+ * Return value: %true if success, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -789,12 +789,13 @@
 
 /**
  * hb_buffer_get_user_data: (skip)
- * @buffer: an #hb_buffer_t.
- * @key:
+ * @buffer: An #hb_buffer_t
+ * @key: The user-data key to query
  *
+ * Fetches the user data associated with the specified key,
+ * attached to the specified buffer.
  *
- *
- * Return value:
+ * Return value: (transfer none): A pointer to the user data
  *
  * Since: 0.9.2
  **/
@@ -808,11 +809,11 @@
 
 /**
  * hb_buffer_set_content_type:
- * @buffer: an #hb_buffer_t.
- * @content_type: the type of buffer contents to set
+ * @buffer: An #hb_buffer_t
+ * @content_type: The type of buffer contents to set
  *
- * Sets the type of @buffer contents, buffers are either empty, contain
- * characters (before shaping) or glyphs (the result of shaping).
+ * Sets the type of @buffer contents. Buffers are either empty, contain
+ * characters (before shaping), or contain glyphs (the result of shaping).
  *
  * Since: 0.9.5
  **/
@@ -825,12 +826,13 @@
 
 /**
  * hb_buffer_get_content_type:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
- * see hb_buffer_set_content_type().
+ * Fetches the type of @buffer contents. Buffers are either empty, contain
+ * characters (before shaping), or contain glyphs (the result of shaping).
  *
  * Return value:
- * The type of @buffer contents.
+ * The type of @buffer contents
  *
  * Since: 0.9.5
  **/
@@ -843,10 +845,11 @@
 
 /**
  * hb_buffer_set_unicode_funcs:
- * @buffer: an #hb_buffer_t.
- * @unicode_funcs:
+ * @buffer: An #hb_buffer_t
+ * @unicode_funcs: The Unicode-functions structure
  *
- *
+ * Sets the Unicode-functions structure of a buffer to
+ * @unicode_funcs.
  *
  * Since: 0.9.2
  **/
@@ -867,11 +870,11 @@
 
 /**
  * hb_buffer_get_unicode_funcs:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
+ * Fetches the Unicode-functions structure of a buffer.
  *
- *
- * Return value:
+ * Return value: The Unicode-functions structure
  *
  * Since: 0.9.2
  **/
@@ -883,7 +886,7 @@
 
 /**
  * hb_buffer_set_direction:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  * @direction: the #hb_direction_t of the @buffer
  *
  * Set the text flow direction of the buffer. No shaping can happen without
@@ -909,7 +912,7 @@
 
 /**
  * hb_buffer_get_direction:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * See hb_buffer_set_direction()
  *
@@ -926,8 +929,8 @@
 
 /**
  * hb_buffer_set_script:
- * @buffer: an #hb_buffer_t.
- * @script: an #hb_script_t to set.
+ * @buffer: An #hb_buffer_t
+ * @script: An #hb_script_t to set.
  *
  * Sets the script of @buffer to @script.
  *
@@ -953,12 +956,12 @@
 
 /**
  * hb_buffer_get_script:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
- * See hb_buffer_set_script().
+ * Fetches the script of @buffer.
  *
  * Return value:
- * The #hb_script_t of the @buffer.
+ * The #hb_script_t of the @buffer
  *
  * Since: 0.9.2
  **/
@@ -970,8 +973,8 @@
 
 /**
  * hb_buffer_set_language:
- * @buffer: an #hb_buffer_t.
- * @language: an hb_language_t to set.
+ * @buffer: An #hb_buffer_t
+ * @language: An hb_language_t to set
  *
  * Sets the language of @buffer to @language.
  *
@@ -997,7 +1000,7 @@
 
 /**
  * hb_buffer_get_language:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * See hb_buffer_set_language().
  *
@@ -1014,8 +1017,8 @@
 
 /**
  * hb_buffer_set_segment_properties:
- * @buffer: an #hb_buffer_t.
- * @props: an #hb_segment_properties_t to use.
+ * @buffer: An #hb_buffer_t
+ * @props: An #hb_segment_properties_t to use
  *
  * Sets the segment properties of the buffer, a shortcut for calling
  * hb_buffer_set_direction(), hb_buffer_set_script() and
@@ -1035,8 +1038,8 @@
 
 /**
  * hb_buffer_get_segment_properties:
- * @buffer: an #hb_buffer_t.
- * @props: (out): the output #hb_segment_properties_t.
+ * @buffer: An #hb_buffer_t
+ * @props: (out): The output #hb_segment_properties_t
  *
  * Sets @props to the #hb_segment_properties_t of @buffer.
  *
@@ -1052,8 +1055,8 @@
 
 /**
  * hb_buffer_set_flags:
- * @buffer: an #hb_buffer_t.
- * @flags: the buffer flags to set.
+ * @buffer: An #hb_buffer_t
+ * @flags: The buffer flags to set
  *
  * Sets @buffer flags to @flags. See #hb_buffer_flags_t.
  *
@@ -1071,12 +1074,12 @@
 
 /**
  * hb_buffer_get_flags:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
- * See hb_buffer_set_flags().
+ * Fetches the #hb_buffer_flags_t of @buffer.
  *
  * Return value:
- * The @buffer flags.
+ * The @buffer flags
  *
  * Since: 0.9.7
  **/
@@ -1088,10 +1091,12 @@
 
 /**
  * hb_buffer_set_cluster_level:
- * @buffer: an #hb_buffer_t.
- * @cluster_level:
+ * @buffer: An #hb_buffer_t
+ * @cluster_level: The cluster level to set on the buffer
  *
- *
+ * Sets the cluster level of a buffer. The #hb_buffer_cluster_level_t
+ * dictates one aspect of how HarfBuzz will treat non-base characters 
+ * during shaping.
  *
  * Since: 0.9.42
  **/
@@ -1107,11 +1112,13 @@
 
 /**
  * hb_buffer_get_cluster_level:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
+ * Fetches the cluster level of a buffer. The #hb_buffer_cluster_level_t
+ * dictates one aspect of how HarfBuzz will treat non-base characters 
+ * during shaping.
  *
- *
- * Return value:
+ * Return value: The cluster level of @buffer
  *
  * Since: 0.9.42
  **/
@@ -1124,13 +1131,13 @@
 
 /**
  * hb_buffer_set_replacement_codepoint:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  * @replacement: the replacement #hb_codepoint_t
  *
  * Sets the #hb_codepoint_t that replaces invalid entries for a given encoding
  * when adding text to @buffer.
  *
- * Default is %HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT.
+ * Default is #HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT.
  *
  * Since: 0.9.31
  **/
@@ -1146,12 +1153,13 @@
 
 /**
  * hb_buffer_get_replacement_codepoint:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
- * See hb_buffer_set_replacement_codepoint().
+ * Fetches the #hb_codepoint_t that replaces invalid entries for a given encoding
+ * when adding text to @buffer.
  *
  * Return value:
- * The @buffer replacement #hb_codepoint_t.
+ * The @buffer replacement #hb_codepoint_t
  *
  * Since: 0.9.31
  **/
@@ -1164,7 +1172,7 @@
 
 /**
  * hb_buffer_set_invisible_glyph:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  * @invisible: the invisible #hb_codepoint_t
  *
  * Sets the #hb_codepoint_t that replaces invisible characters in
@@ -1186,12 +1194,12 @@
 
 /**
  * hb_buffer_get_invisible_glyph:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * See hb_buffer_set_invisible_glyph().
  *
  * Return value:
- * The @buffer invisible #hb_codepoint_t.
+ * The @buffer invisible #hb_codepoint_t
  *
  * Since: 2.0.0
  **/
@@ -1204,7 +1212,7 @@
 
 /**
  * hb_buffer_reset:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * Resets the buffer to its initial status, as if it was just newly created
  * with hb_buffer_create().
@@ -1219,7 +1227,7 @@
 
 /**
  * hb_buffer_clear_contents:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * Similar to hb_buffer_reset(), but does not clear the Unicode functions and
  * the replacement code point.
@@ -1234,13 +1242,13 @@
 
 /**
  * hb_buffer_pre_allocate:
- * @buffer: an #hb_buffer_t.
- * @size: number of items to pre allocate.
+ * @buffer: An #hb_buffer_t
+ * @size: Number of items to pre allocate.
  *
  * Pre allocates memory for @buffer to fit at least @size number of items.
  *
  * Return value:
- * %true if @buffer memory allocation succeeded, %false otherwise.
+ * %true if @buffer memory allocation succeeded, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -1252,7 +1260,7 @@
 
 /**
  * hb_buffer_allocation_successful:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * Check if allocating memory for the buffer succeeded.
  *
@@ -1269,9 +1277,9 @@
 
 /**
  * hb_buffer_add:
- * @buffer: an #hb_buffer_t.
- * @codepoint: a Unicode code point.
- * @cluster: the cluster value of @codepoint.
+ * @buffer: An #hb_buffer_t
+ * @codepoint: A Unicode code point.
+ * @cluster: The cluster value of @codepoint.
  *
  * Appends a character with the Unicode value of @codepoint to @buffer, and
  * gives it the initial cluster value of @cluster. Clusters can be any thing
@@ -1295,8 +1303,8 @@
 
 /**
  * hb_buffer_set_length:
- * @buffer: an #hb_buffer_t.
- * @length: the new length of @buffer.
+ * @buffer: An #hb_buffer_t
+ * @length: The new length of @buffer
  *
  * Similar to hb_buffer_pre_allocate(), but clears any new items added at the
  * end.
@@ -1337,7 +1345,7 @@
 
 /**
  * hb_buffer_get_length:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * Returns the number of items in the buffer.
  *
@@ -1355,8 +1363,8 @@
 
 /**
  * hb_buffer_get_glyph_infos:
- * @buffer: an #hb_buffer_t.
- * @length: (out): output array length.
+ * @buffer: An #hb_buffer_t
+ * @length: (out): The output-array length.
  *
  * Returns @buffer glyph information array.  Returned pointer
  * is valid as long as @buffer contents are not modified.
@@ -1379,8 +1387,8 @@
 
 /**
  * hb_buffer_get_glyph_positions:
- * @buffer: an #hb_buffer_t.
- * @length: (out): output length.
+ * @buffer: An #hb_buffer_t
+ * @length: (out): The output length
  *
  * Returns @buffer glyph position array.  Returned pointer
  * is valid as long as @buffer contents are not modified.
@@ -1405,13 +1413,32 @@
 }
 
 /**
+ * hb_buffer_has_positions:
+ * @buffer: an #hb_buffer_t.
+ *
+ * Returns whether @buffer has glyph position data.
+ * A buffer gains position data when hb_buffer_get_glyph_positions() is called on it,
+ * and cleared of position data when hb_buffer_clear_contents() is called.
+ *
+ * Return value:
+ * %true if the @buffer has position array, %false otherwise.
+ *
+ * Since: 2.7.3
+ **/
+HB_EXTERN hb_bool_t
+hb_buffer_has_positions (hb_buffer_t  *buffer)
+{
+  return buffer->have_positions;
+}
+
+/**
  * hb_glyph_info_get_glyph_flags:
- * @info: a #hb_glyph_info_t.
+ * @info: a #hb_glyph_info_t
  *
  * Returns glyph flags encoded within a #hb_glyph_info_t.
  *
  * Return value:
- * The #hb_glyph_flags_t encoded within @info.
+ * The #hb_glyph_flags_t encoded within @info
  *
  * Since: 1.5.0
  **/
@@ -1423,7 +1450,7 @@
 
 /**
  * hb_buffer_reverse:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * Reverses buffer contents.
  *
@@ -1437,11 +1464,11 @@
 
 /**
  * hb_buffer_reverse_range:
- * @buffer: an #hb_buffer_t.
- * @start: start index.
- * @end: end index.
+ * @buffer: An #hb_buffer_t
+ * @start: start index
+ * @end: end index
  *
- * Reverses buffer contents between start to end.
+ * Reverses buffer contents between @start and @end.
  *
  * Since: 0.9.41
  **/
@@ -1454,7 +1481,7 @@
 
 /**
  * hb_buffer_reverse_clusters:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * Reverses buffer clusters.  That is, the buffer contents are
  * reversed, then each cluster (consecutive items having the
@@ -1470,24 +1497,24 @@
 
 /**
  * hb_buffer_guess_segment_properties:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * Sets unset buffer segment properties based on buffer Unicode
  * contents.  If buffer is not empty, it must have content type
- * %HB_BUFFER_CONTENT_TYPE_UNICODE.
+ * #HB_BUFFER_CONTENT_TYPE_UNICODE.
  *
- * If buffer script is not set (ie. is %HB_SCRIPT_INVALID), it
+ * If buffer script is not set (ie. is #HB_SCRIPT_INVALID), it
  * will be set to the Unicode script of the first character in
- * the buffer that has a script other than %HB_SCRIPT_COMMON,
- * %HB_SCRIPT_INHERITED, and %HB_SCRIPT_UNKNOWN.
+ * the buffer that has a script other than #HB_SCRIPT_COMMON,
+ * #HB_SCRIPT_INHERITED, and #HB_SCRIPT_UNKNOWN.
  *
- * Next, if buffer direction is not set (ie. is %HB_DIRECTION_INVALID),
+ * Next, if buffer direction is not set (ie. is #HB_DIRECTION_INVALID),
  * it will be set to the natural horizontal direction of the
  * buffer script as returned by hb_script_get_horizontal_direction().
- * If hb_script_get_horizontal_direction() returns %HB_DIRECTION_INVALID,
- * then %HB_DIRECTION_LTR is used.
+ * If hb_script_get_horizontal_direction() returns #HB_DIRECTION_INVALID,
+ * then #HB_DIRECTION_LTR is used.
  *
- * Finally, if buffer language is not set (ie. is %HB_LANGUAGE_INVALID),
+ * Finally, if buffer language is not set (ie. is #HB_LANGUAGE_INVALID),
  * it will be set to the process's default language as returned by
  * hb_language_get_default().  This may change in the future by
  * taking buffer script into consideration when choosing a language.
@@ -1513,8 +1540,7 @@
   typedef typename utf_t::codepoint_t T;
   const hb_codepoint_t replacement = buffer->replacement;
 
-  assert ((buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) ||
-	  (!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)));
+  buffer->assert_unicode ();
 
   if (unlikely (hb_object_is_immutable (buffer)))
     return;
@@ -1573,12 +1599,12 @@
 
 /**
  * hb_buffer_add_utf8:
- * @buffer: an #hb_buffer_t.
- * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8
+ * @buffer: An #hb_buffer_t
+ * @text: (array length=text_length) (element-type uint8_t): An array of UTF-8
  *               characters to append.
- * @text_length: the length of the @text, or -1 if it is %NULL terminated.
- * @item_offset: the offset of the first character to add to the @buffer.
- * @item_length: the number of characters to add to the @buffer, or -1 for the
+ * @text_length: The length of the @text, or -1 if it is %NULL terminated.
+ * @item_offset: The offset of the first character to add to the @buffer.
+ * @item_length: The number of characters to add to the @buffer, or -1 for the
  *               end of @text (assuming it is %NULL terminated).
  *
  * See hb_buffer_add_codepoints().
@@ -1600,12 +1626,12 @@
 
 /**
  * hb_buffer_add_utf16:
- * @buffer: an #hb_buffer_t.
- * @text: (array length=text_length): an array of UTF-16 characters to append.
- * @text_length: the length of the @text, or -1 if it is %NULL terminated.
- * @item_offset: the offset of the first character to add to the @buffer.
- * @item_length: the number of characters to add to the @buffer, or -1 for the
- *               end of @text (assuming it is %NULL terminated).
+ * @buffer: An #hb_buffer_t
+ * @text: (array length=text_length): An array of UTF-16 characters to append
+ * @text_length: The length of the @text, or -1 if it is %NULL terminated
+ * @item_offset: The offset of the first character to add to the @buffer
+ * @item_length: The number of characters to add to the @buffer, or -1 for the
+ *               end of @text (assuming it is %NULL terminated)
  *
  * See hb_buffer_add_codepoints().
  *
@@ -1626,12 +1652,12 @@
 
 /**
  * hb_buffer_add_utf32:
- * @buffer: an #hb_buffer_t.
- * @text: (array length=text_length): an array of UTF-32 characters to append.
- * @text_length: the length of the @text, or -1 if it is %NULL terminated.
- * @item_offset: the offset of the first character to add to the @buffer.
- * @item_length: the number of characters to add to the @buffer, or -1 for the
- *               end of @text (assuming it is %NULL terminated).
+ * @buffer: An #hb_buffer_t
+ * @text: (array length=text_length): An array of UTF-32 characters to append
+ * @text_length: The length of the @text, or -1 if it is %NULL terminated
+ * @item_offset: The offset of the first character to add to the @buffer
+ * @item_length: The number of characters to add to the @buffer, or -1 for the
+ *               end of @text (assuming it is %NULL terminated)
  *
  * See hb_buffer_add_codepoints().
  *
@@ -1652,13 +1678,13 @@
 
 /**
  * hb_buffer_add_latin1:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8
- *               characters to append.
- * @text_length: the length of the @text, or -1 if it is %NULL terminated.
- * @item_offset: the offset of the first character to add to the @buffer.
+ *               characters to append
+ * @text_length: the length of the @text, or -1 if it is %NULL terminated
+ * @item_offset: the offset of the first character to add to the @buffer
  * @item_length: the number of characters to add to the @buffer, or -1 for the
- *               end of @text (assuming it is %NULL terminated).
+ *               end of @text (assuming it is %NULL terminated)
  *
  * Similar to hb_buffer_add_codepoints(), but allows only access to first 256
  * Unicode code points that can fit in 8-bit strings.
@@ -1714,8 +1740,8 @@
 
 /**
  * hb_buffer_append:
- * @buffer: an #hb_buffer_t.
- * @source: source #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
+ * @source: source #hb_buffer_t
  * @start: start index into source buffer to copy.  Use 0 to copy from start of buffer.
  * @end: end index into source buffer to copy.  Use @HB_FEATURE_GLOBAL_END to copy to end of buffer.
  *
@@ -1821,7 +1847,7 @@
 
 /**
  * hb_buffer_normalize_glyphs:
- * @buffer: an #hb_buffer_t.
+ * @buffer: An #hb_buffer_t
  *
  * Reorders a glyph buffer to have canonical in-cluster glyph order / position.
  * The resulting clusters should behave identical to pre-reordering clusters.
@@ -1834,8 +1860,8 @@
 hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
 {
   assert (buffer->have_positions);
-  assert ((buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) ||
-	  (!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)));
+
+  buffer->assert_glyphs ();
 
   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
 
@@ -1876,8 +1902,8 @@
  * @dottedcircle_glyph: glyph id of U+25CC DOTTED CIRCLE, or (hb_codepont_t) -1.
  * @position_fuzz: allowed absolute difference in position values.
  *
- * If dottedcircle_glyph is (hb_codepoint_t) -1 then %HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
- * and %HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned.  This should be used by most
+ * If dottedcircle_glyph is (hb_codepoint_t) -1 then #HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
+ * and #HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned.  This should be used by most
  * callers if just comparing two buffers is needed.
  *
  * Since: 1.5.0
@@ -1967,12 +1993,12 @@
 #ifndef HB_NO_BUFFER_MESSAGE
 /**
  * hb_buffer_set_message_func:
- * @buffer: an #hb_buffer_t.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @buffer: An #hb_buffer_t
+ * @func: (closure user_data) (destroy destroy) (scope notified): Callback function
+ * @user_data: (nullable): Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_buffer_message_func_t.
  *
  * Since: 1.1.3
  **/
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index 2f581f3..865ccb2 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -27,7 +27,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
@@ -59,8 +59,7 @@
  * The #hb_glyph_info_t is the structure that holds information about the
  * glyphs and their relation to input text.
  */
-typedef struct hb_glyph_info_t
-{
+typedef struct hb_glyph_info_t {
   hb_codepoint_t codepoint;
   /*< private >*/
   hb_mask_t      mask;
@@ -91,6 +90,8 @@
  * 				   breaking point only.
  * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags.
  *
+ * Flags for #hb_glyph_info_t.
+ *
  * Since: 1.5.0
  */
 typedef enum { /*< flags >*/
@@ -151,6 +152,11 @@
   void           *reserved2;
 } hb_segment_properties_t;
 
+/**
+ * HB_SEGMENT_PROPERTIES_DEFAULT:
+ *
+ * The default #hb_segment_properties_t of of freshly created #hb_buffer_t.
+ */
 #define HB_SEGMENT_PROPERTIES_DEFAULT {HB_DIRECTION_INVALID, \
 				       HB_SCRIPT_INVALID, \
 				       HB_LANGUAGE_INVALID, \
@@ -204,6 +210,8 @@
  * @HB_BUFFER_CONTENT_TYPE_INVALID: Initial value for new buffer.
  * @HB_BUFFER_CONTENT_TYPE_UNICODE: The buffer contains input characters (before shaping).
  * @HB_BUFFER_CONTENT_TYPE_GLYPHS: The buffer contains output glyphs (after shaping).
+ *
+ * The type of #hb_buffer_t contents.
  */
 typedef enum {
   HB_BUFFER_CONTENT_TYPE_INVALID = 0,
@@ -289,6 +297,8 @@
  *                      not be inserted in the rendering of incorrect
  *                      character sequences (such at <0905 093E>). Since: 2.4
  *
+ * Flags for #hb_buffer_t.
+ *
  * Since: 0.9.20
  */
 typedef enum { /*< flags >*/
@@ -315,6 +325,23 @@
  * @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: Don't group cluster values.
  * @HB_BUFFER_CLUSTER_LEVEL_DEFAULT: Default cluster level,
  *   equal to @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES.
+ * 
+ * Data type for holding HarfBuzz's clustering behavior options. The cluster level
+ * dictates one aspect of how HarfBuzz will treat non-base characters 
+ * during shaping.
+ *
+ * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES, non-base
+ * characters are merged into the cluster of the base character that precedes them.
+ *
+ * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS, non-base characters are initially
+ * assigned their own cluster values, which are not merged into preceding base
+ * clusters. This allows HarfBuzz to perform additional operations like reorder
+ * sequences of adjacent marks.
+ *
+ * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES is the default, because it maintains
+ * backward compatibility with older versions of HarfBuzz. New client programs that
+ * do not need to maintain such backward compatibility are recommended to use
+ * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS instead of the default.
  *
  * Since: 0.9.42
  */
@@ -447,6 +474,9 @@
 hb_buffer_get_glyph_positions (hb_buffer_t  *buffer,
 			       unsigned int *length);
 
+HB_EXTERN hb_bool_t
+hb_buffer_has_positions (hb_buffer_t  *buffer);
+
 
 HB_EXTERN void
 hb_buffer_normalize_glyphs (hb_buffer_t *buffer);
@@ -518,6 +548,27 @@
 			    hb_buffer_serialize_format_t format,
 			    hb_buffer_serialize_flags_t flags);
 
+HB_EXTERN unsigned int
+hb_buffer_serialize_unicode (hb_buffer_t *buffer,
+					unsigned int start,
+					unsigned int end,
+					char *buf,
+					unsigned int buf_size,
+					unsigned int *buf_consumed,
+					hb_buffer_serialize_format_t format,
+					hb_buffer_serialize_flags_t flags);
+
+HB_EXTERN unsigned int
+hb_buffer_serialize (hb_buffer_t *buffer,
+					unsigned int start,
+					unsigned int end,
+					char *buf,
+					unsigned int buf_size,
+					unsigned int *buf_consumed,
+					hb_font_t *font,
+					hb_buffer_serialize_format_t format,
+					hb_buffer_serialize_flags_t flags);
+
 HB_EXTERN hb_bool_t
 hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
 			      const char *buf,
@@ -526,11 +577,48 @@
 			      hb_font_t *font,
 			      hb_buffer_serialize_format_t format);
 
+HB_EXTERN hb_bool_t
+hb_buffer_deserialize_unicode (hb_buffer_t *buffer,
+            const char *buf,
+            int buf_len,
+            const char **end_ptr,
+            hb_buffer_serialize_format_t format);
+
+
 
 /*
  * Compare buffers
  */
 
+/**
+ * hb_buffer_diff_flags_t:
+ * @HB_BUFFER_DIFF_FLAG_EQUAL: equal buffers.
+ * @HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH: buffers with different
+ *     #hb_buffer_content_type_t.
+ * @HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH: buffers with differing length.
+ * @HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT: `.notdef` glyph is present in the
+ *     reference buffer.
+ * @HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT: dotted circle glyph is present
+ *     in the reference buffer.
+ * @HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH: difference in #hb_glyph_info_t.codepoint
+ * @HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH: difference in #hb_glyph_info_t.cluster
+ * @HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH: difference in #hb_glyph_flags_t.
+ * @HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH: difference in #hb_glyph_position_t.
+ *
+ * Flags from comparing two #hb_buffer_t's.
+ *
+ * Buffer with different #hb_buffer_content_type_t cannot be meaningfully
+ * compared in any further detail.
+ *
+ * For buffers with differing length, the per-glyph comparison is not
+ * attempted, though we do still scan reference buffer for dotted circle and
+ * `.notdef` glyphs.
+ *
+ * If the buffers have the same length, we compare them glyph-by-glyph and
+ * report which aspect(s) of the glyph info/position are different.
+ *
+ * Since: 1.5.0
+ */
 typedef enum { /*< flags >*/
   HB_BUFFER_DIFF_FLAG_EQUAL			= 0x0000,
 
@@ -570,6 +658,23 @@
  * Debugging.
  */
 
+/**
+ * hb_buffer_message_func_t:
+ * @buffer: An #hb_buffer_t to work upon
+ * @font: The #hb_font_t the @buffer is shaped with
+ * @message: %NULL-terminated message passed to the function
+ * @user_data: User data pointer passed by the caller
+ *
+ * A callback method for #hb_buffer_t. The method gets called with the
+ * #hb_buffer_t it was set on, the #hb_font_t the buffer is shaped with and a
+ * message describing what step of the shaping process will be performed.
+ * Returning %false from this method will skip this shaping step and move to
+ * the next one.
+ *
+ * Return value: %true to perform the shaping step, %false to skip it.
+ *
+ * Since: 1.1.3
+ */
 typedef hb_bool_t	(*hb_buffer_message_func_t)	(hb_buffer_t *buffer,
 							 hb_font_t   *font,
 							 const char  *message,
diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh
index 3420ba4..9cad520 100644
--- a/src/hb-buffer.hh
+++ b/src/hb-buffer.hh
@@ -35,20 +35,20 @@
 
 
 #ifndef HB_BUFFER_MAX_LEN_FACTOR
-#define HB_BUFFER_MAX_LEN_FACTOR 32
+#define HB_BUFFER_MAX_LEN_FACTOR 64
 #endif
 #ifndef HB_BUFFER_MAX_LEN_MIN
-#define HB_BUFFER_MAX_LEN_MIN 8192
+#define HB_BUFFER_MAX_LEN_MIN 16384
 #endif
 #ifndef HB_BUFFER_MAX_LEN_DEFAULT
 #define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */
 #endif
 
 #ifndef HB_BUFFER_MAX_OPS_FACTOR
-#define HB_BUFFER_MAX_OPS_FACTOR 64
+#define HB_BUFFER_MAX_OPS_FACTOR 1024
 #endif
 #ifndef HB_BUFFER_MAX_OPS_MIN
-#define HB_BUFFER_MAX_OPS_MIN 1024
+#define HB_BUFFER_MAX_OPS_MIN 16384
 #endif
 #ifndef HB_BUFFER_MAX_OPS_DEFAULT
 #define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */
@@ -339,6 +339,39 @@
   bool ensure_inplace (unsigned int size)
   { return likely (!size || size < allocated); }
 
+  void assert_glyphs ()
+  {
+    assert ((content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) ||
+	    (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID)));
+  }
+  void assert_unicode ()
+  {
+    assert ((content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) ||
+	    (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID)));
+  }
+  bool ensure_glyphs ()
+  {
+    if (unlikely (content_type != HB_BUFFER_CONTENT_TYPE_GLYPHS))
+    {
+      if (content_type != HB_BUFFER_CONTENT_TYPE_INVALID)
+	return false;
+      assert (len == 0);
+      content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
+    }
+    return true;
+  }
+  bool ensure_unicode ()
+  {
+    if (unlikely (content_type != HB_BUFFER_CONTENT_TYPE_UNICODE))
+    {
+      if (content_type != HB_BUFFER_CONTENT_TYPE_INVALID)
+	return false;
+      assert (len == 0);
+      content_type = HB_BUFFER_CONTENT_TYPE_UNICODE;
+    }
+    return true;
+  }
+
   HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out);
   HB_INTERNAL bool shift_forward (unsigned int count);
 
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 5acfa78..7bb878b 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -86,12 +86,15 @@
 
 /**
  * hb_tag_from_string:
- * @str: (array length=len) (element-type uint8_t):
- * @len:
+ * @str: (array length=len) (element-type uint8_t): String to convert
+ * @len: Length of @str, or -1 if it is %NULL-terminated
  *
+ * Converts a string into an #hb_tag_t. Valid tags
+ * are four characters. Shorter input strings will be
+ * padded with spaces. Longer input strings will be
+ * truncated.
  *
- *
- * Return value:
+ * Return value: The #hb_tag_t corresponding to @str
  *
  * Since: 0.9.2
  **/
@@ -116,10 +119,11 @@
 
 /**
  * hb_tag_to_string:
- * @tag:
- * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t):
+ * @tag: #hb_tag_t to convert
+ * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string
  *
- *
+ * Converts an #hb_tag_t to a string and returns it in @buf. 
+ * Strings will be four characters long.
  *
  * Since: 0.9.5
  **/
@@ -144,12 +148,17 @@
 
 /**
  * hb_direction_from_string:
- * @str: (array length=len) (element-type uint8_t):
- * @len:
+ * @str: (array length=len) (element-type uint8_t): String to convert
+ * @len: Length of @str, or -1 if it is %NULL-terminated
  *
+ * Converts a string to an #hb_direction_t. 
  *
+ * Matching is loose and applies only to the first letter. For
+ * examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.
  *
- * Return value:
+ * Unmatched strings will return #HB_DIRECTION_INVALID.
+ * 
+ * Return value: The #hb_direction_t matching @str
  *
  * Since: 0.9.2
  **/
@@ -172,11 +181,11 @@
 
 /**
  * hb_direction_to_string:
- * @direction:
+ * @direction: The #hb_direction_t to convert
  *
+ * Converts an #hb_direction_t to a string.
  *
- *
- * Return value: (transfer none):
+ * Return value: (transfer none): The string corresponding to @direction
  *
  * Since: 0.9.2
  **/
@@ -367,9 +376,9 @@
 
 /**
  * hb_language_to_string:
- * @language: an #hb_language_t to convert.
+ * @language: The #hb_language_t to convert
  *
- * See hb_language_from_string().
+ * Converts an #hb_language_t to a string.
  *
  * Return value: (transfer none):
  * A %NULL-terminated string representing the @language. Must not be freed by
@@ -388,16 +397,17 @@
 /**
  * hb_language_get_default:
  *
- * Get default language from current locale.
+ * Fetch the default language from current locale.
  *
- * Note that the first time this function is called, it calls
+ * <note>Note that the first time this function is called, it calls
  * "setlocale (LC_CTYPE, nullptr)" to fetch current locale.  The underlying
  * setlocale function is, in many implementations, NOT threadsafe.  To avoid
  * problems, call this function once before multiple threads can call it.
  * This function is only used from hb_buffer_guess_segment_properties() by
- * HarfBuzz itself.
+ * HarfBuzz itself.</note>
  *
- * Return value: (transfer none):
+ * Return value: (transfer none): The default language of the locale as
+ * an #hb_language_t
  *
  * Since: 0.9.2
  **/
@@ -448,7 +458,12 @@
     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
 
     /* Script variants from https://unicode.org/iso15924/ */
+    case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;
     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
+    case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;
+    case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;
+    case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;
+    case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;
     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
@@ -489,7 +504,7 @@
  * hb_script_to_iso15924_tag:
  * @script: an #hb_script_t to convert.
  *
- * See hb_script_from_iso15924_tag().
+ * Converts an #hb_script_t to a corresponding ISO 15924 script tag.
  *
  * Return value:
  * An #hb_tag_t representing an ISO 15924 script tag.
@@ -504,11 +519,16 @@
 
 /**
  * hb_script_get_horizontal_direction:
- * @script:
+ * @script: The #hb_script_t to query
  *
+ * Fetches the #hb_direction_t of a script when it is
+ * set horizontally. All right-to-left scripts will return
+ * #HB_DIRECTION_RTL. All left-to-right scripts will return
+ * #HB_DIRECTION_LTR.  Scripts that can be written either
+ * horizontally or vertically will return #HB_DIRECTION_INVALID.
+ * Unknown scripts will return #HB_DIRECTION_LTR.
  *
- *
- * Return value:
+ * Return value: The horizontal #hb_direction_t of @script
  *
  * Since: 0.9.2
  **/
@@ -613,9 +633,9 @@
 
 /**
  * hb_version:
- * @major: (out): Library major version component.
- * @minor: (out): Library minor version component.
- * @micro: (out): Library micro version component.
+ * @major: (out): Library major version component
+ * @minor: (out): Library minor version component
+ * @micro: (out): Library micro version component
  *
  * Returns library version as three integer components.
  *
@@ -636,7 +656,7 @@
  *
  * Returns library version as a string with three components.
  *
- * Return value: library version string.
+ * Return value: Library version string
  *
  * Since: 0.9.2
  **/
@@ -648,13 +668,15 @@
 
 /**
  * hb_version_atleast:
- * @major:
- * @minor:
- * @micro:
+ * @major: Library major version component
+ * @minor: Library minor version component
+ * @micro: Library micro version component
  *
+ * Tests the library version against a minimum value,
+ * as three integer components.
  *
- *
- * Return value:
+ * Return value: %true if the library is equal to or greater than
+ * the test value, %false otherwise
  *
  * Since: 0.9.30
  **/
@@ -883,7 +905,7 @@
  * </informaltable>
  *
  * Return value:
- * %true if @str is successfully parsed, %false otherwise.
+ * %true if @str is successfully parsed, %false otherwise
  *
  * Since: 0.9.5
  **/
@@ -981,6 +1003,21 @@
 
 /**
  * hb_variation_from_string:
+ * @str: (array length=len) (element-type uint8_t): a string to parse
+ * @len: length of @str, or -1 if string is %NULL terminated
+ * @variation: (out): the #hb_variation_t to initialize with the parsed values
+ *
+ * Parses a string into a #hb_variation_t.
+ *
+ * The format for specifying variation settings follows. All valid CSS
+ * font-variation-settings values other than 'normal' and 'inherited' are also
+ * accepted, though, not documented below.
+ *
+ * The format is a tag, optionally followed by an equals sign, followed by a
+ * number. For example `wght=500`, or `slnt=-7.5`.
+ *
+ * Return value:
+ * %true if @str is successfully parsed, %false otherwise
  *
  * Since: 1.4.2
  */
@@ -1007,6 +1044,13 @@
 
 /**
  * hb_variation_to_string:
+ * @variation: an #hb_variation_t to convert
+ * @buf: (array length=size) (out): output string
+ * @size: the allocated size of @buf
+ *
+ * Converts an #hb_variation_t into a %NULL-terminated string in the format
+ * understood by hb_variation_from_string(). The client in responsible for
+ * allocating big enough size for @buf, 128 bytes is more than enough.
  *
  * Since: 1.4.2
  */
@@ -1033,9 +1077,11 @@
 
 /**
  * hb_color_get_alpha:
- * color: a #hb_color_t we are interested in its channels.
+ * @color: an #hb_color_t we are interested in its channels.
  *
- * Return value: Alpha channel value of the given color
+ * Fetches the alpha channel of the given @color.
+ *
+ * Return value: Alpha channel value
  *
  * Since: 2.1.0
  */
@@ -1047,9 +1093,11 @@
 
 /**
  * hb_color_get_red:
- * color: a #hb_color_t we are interested in its channels.
+ * @color: an #hb_color_t we are interested in its channels.
  *
- * Return value: Red channel value of the given color
+ * Fetches the red channel of the given @color.
+ *
+ * Return value: Red channel value
  *
  * Since: 2.1.0
  */
@@ -1061,9 +1109,11 @@
 
 /**
  * hb_color_get_green:
- * color: a #hb_color_t we are interested in its channels.
+ * @color: an #hb_color_t we are interested in its channels.
  *
- * Return value: Green channel value of the given color
+ * Fetches the green channel of the given @color.
+ *
+ * Return value: Green channel value
  *
  * Since: 2.1.0
  */
@@ -1075,9 +1125,11 @@
 
 /**
  * hb_color_get_blue:
- * color: a #hb_color_t we are interested in its channels.
+ * @color: an #hb_color_t we are interested in its channels.
  *
- * Return value: Blue channel value of the given color
+ * Fetches the blue channel of the given @color.
+ *
+ * Return value: Blue channel value
  *
  * Since: 2.1.0
  */
diff --git a/src/hb-common.h b/src/hb-common.h
index a97a5f5..532fd42 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -26,7 +26,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
@@ -88,11 +88,37 @@
 
 HB_BEGIN_DECLS
 
-
+/**
+ * hb_bool_t:
+ * 
+ * Data type for booleans.
+ *
+ **/
 typedef int hb_bool_t;
 
+/**
+ * hb_codepoint_t:
+ * 
+ * Data type for holding Unicode codepoints. Also
+ * used to hold glyph IDs.
+ *
+ **/
 typedef uint32_t hb_codepoint_t;
+/**
+ * hb_position_t:
+ * 
+ * Data type for holding a single coordinate value.
+ * Contour points and other multi-dimensional data are
+ * stored as tuples of #hb_position_t's.
+ *
+ **/
 typedef int32_t hb_position_t;
+/**
+ * hb_mask_t:
+ * 
+ * Data type for bitmasks.
+ *
+ **/
 typedef uint32_t hb_mask_t;
 
 typedef union _hb_var_int_t {
@@ -107,13 +133,63 @@
 
 /* hb_tag_t */
 
+/**
+ * hb_tag_t:
+ *
+ * Data type for tag identifiers. Tags are four
+ * byte integers, each byte representing a character.
+ *
+ * Tags are used to identify tables, design-variation axes,
+ * scripts, languages, font features, and baselines with
+ * human-readable names.
+ *
+ **/
 typedef uint32_t hb_tag_t;
 
+/**
+ * HB_TAG:
+ * @c1: 1st character of the tag
+ * @c2: 2nd character of the tag
+ * @c3: 3rd character of the tag
+ * @c4: 4th character of the tag
+ *
+ * Constructs an #hb_tag_t from four character literals.
+ *
+ **/
 #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)))
+
+/**
+ * HB_UNTAG:
+ * @tag: an #hb_tag_t
+ *
+ * Extracts four character literals from an #hb_tag_t.
+ *
+ * Since: 0.6.0
+ *
+ **/
 #define HB_UNTAG(tag)   (uint8_t)(((tag)>>24)&0xFF), (uint8_t)(((tag)>>16)&0xFF), (uint8_t)(((tag)>>8)&0xFF), (uint8_t)((tag)&0xFF)
 
+/**
+ * HB_TAG_NONE:
+ *
+ * Unset #hb_tag_t.
+ */
 #define HB_TAG_NONE HB_TAG(0,0,0,0)
+/**
+ * HB_TAG_MAX:
+ *
+ * Maximum possible unsigned #hb_tag_t.
+ *
+ * Since: 0.9.26
+ */
 #define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff)
+/**
+ * HB_TAG_MAX_SIGNED:
+ *
+ * Maximum possible signed #hb_tag_t.
+ *
+ * Since: 0.9.33
+ */
 #define HB_TAG_MAX_SIGNED HB_TAG(0x7f,0xff,0xff,0xff)
 
 /* len=-1 means str is NUL-terminated. */
@@ -132,6 +208,13 @@
  * @HB_DIRECTION_RTL: Text is set horizontally from right to left.
  * @HB_DIRECTION_TTB: Text is set vertically from top to bottom.
  * @HB_DIRECTION_BTT: Text is set vertically from bottom to top.
+ *
+ * The direction of a text segment or buffer.
+ * 
+ * A segment can also be tested for horizontal or vertical
+ * orientation (irrespective of specific direction) with 
+ * HB_DIRECTION_IS_HORIZONTAL() or HB_DIRECTION_IS_VERTICAL().
+ *
  */
 typedef enum {
   HB_DIRECTION_INVALID = 0,
@@ -148,17 +231,71 @@
 HB_EXTERN const char *
 hb_direction_to_string (hb_direction_t direction);
 
+/**
+ * HB_DIRECTION_IS_VALID:
+ * @dir: #hb_direction_t to test
+ *
+ * Tests whether a text direction is valid.
+ *
+ **/
 #define HB_DIRECTION_IS_VALID(dir)	((((unsigned int) (dir)) & ~3U) == 4)
 /* Direction must be valid for the following */
+/**
+ * HB_DIRECTION_IS_HORIZONTAL:
+ * @dir: #hb_direction_t to test
+ *
+ * Tests whether a text direction is horizontal. Requires
+ * that the direction be valid.
+ *
+ **/
 #define HB_DIRECTION_IS_HORIZONTAL(dir)	((((unsigned int) (dir)) & ~1U) == 4)
+/**
+ * HB_DIRECTION_IS_VERTICAL:
+ * @dir: #hb_direction_t to test
+ *
+ * Tests whether a text direction is vertical. Requires
+ * that the direction be valid.
+ *
+ **/
 #define HB_DIRECTION_IS_VERTICAL(dir)	((((unsigned int) (dir)) & ~1U) == 6)
+/**
+ * HB_DIRECTION_IS_FORWARD:
+ * @dir: #hb_direction_t to test
+ *
+ * Tests whether a text direction moves forward (from left to right, or from
+ * top to bottom). Requires that the direction be valid.
+ *
+ **/
 #define HB_DIRECTION_IS_FORWARD(dir)	((((unsigned int) (dir)) & ~2U) == 4)
+/**
+ * HB_DIRECTION_IS_BACKWARD:
+ * @dir: #hb_direction_t to test
+ *
+ * Tests whether a text direction moves backward (from right to left, or from
+ * bottom to top). Requires that the direction be valid.
+ *
+ **/
 #define HB_DIRECTION_IS_BACKWARD(dir)	((((unsigned int) (dir)) & ~2U) == 5)
+/**
+ * HB_DIRECTION_REVERSE:
+ * @dir: #hb_direction_t to reverse
+ *
+ * Reverses a text direction. Requires that the direction
+ * be valid.
+ *
+ **/
 #define HB_DIRECTION_REVERSE(dir)	((hb_direction_t) (((unsigned int) (dir)) ^ 1))
 
 
 /* hb_language_t */
 
+/**
+ * hb_language_t:
+ *
+ * Data type for languages. Each #hb_language_t corresponds to a BCP 47
+ * language tag.
+ *
+ */
 typedef const struct hb_language_impl_t *hb_language_t;
 
 HB_EXTERN hb_language_t
@@ -167,216 +304,389 @@
 HB_EXTERN const char *
 hb_language_to_string (hb_language_t language);
 
+/**
+ * HB_LANGUAGE_INVALID:
+ *
+ * An unset #hb_language_t.
+ *
+ * Since: 0.6.0
+ */
 #define HB_LANGUAGE_INVALID ((hb_language_t) 0)
 
 HB_EXTERN hb_language_t
 hb_language_get_default (void);
 
 
-/* hb_script_t */
+/**
+ * hb_script_t:
+ * @HB_SCRIPT_COMMON: `Zyyy`
+ * @HB_SCRIPT_INHERITED: `Zinh`
+ * @HB_SCRIPT_UNKNOWN: `Zzzz`
+ * @HB_SCRIPT_ARABIC: `Arab`
+ * @HB_SCRIPT_ARMENIAN: `Armn`
+ * @HB_SCRIPT_BENGALI: `Beng`
+ * @HB_SCRIPT_CYRILLIC: `Cyrl`
+ * @HB_SCRIPT_DEVANAGARI: `Deva`
+ * @HB_SCRIPT_GEORGIAN: `Geor`
+ * @HB_SCRIPT_GREEK: `Grek`
+ * @HB_SCRIPT_GUJARATI: `Gujr`
+ * @HB_SCRIPT_GURMUKHI: `Guru`
+ * @HB_SCRIPT_HANGUL: `Hang`
+ * @HB_SCRIPT_HAN: `Hani`
+ * @HB_SCRIPT_HEBREW: `Hebr`
+ * @HB_SCRIPT_HIRAGANA: `Hira`
+ * @HB_SCRIPT_KANNADA: `Knda`
+ * @HB_SCRIPT_KATAKANA: `Kana`
+ * @HB_SCRIPT_LAO: `Laoo`
+ * @HB_SCRIPT_LATIN: `Latn`
+ * @HB_SCRIPT_MALAYALAM: `Mlym`
+ * @HB_SCRIPT_ORIYA: `Orya`
+ * @HB_SCRIPT_TAMIL: `Taml`
+ * @HB_SCRIPT_TELUGU: `Telu`
+ * @HB_SCRIPT_THAI: `Thai`
+ * @HB_SCRIPT_TIBETAN: `Tibt`
+ * @HB_SCRIPT_BOPOMOFO: `Bopo`
+ * @HB_SCRIPT_BRAILLE: `Brai`
+ * @HB_SCRIPT_CANADIAN_SYLLABICS: `Cans`
+ * @HB_SCRIPT_CHEROKEE: `Cher`
+ * @HB_SCRIPT_ETHIOPIC: `Ethi`
+ * @HB_SCRIPT_KHMER: `Khmr`
+ * @HB_SCRIPT_MONGOLIAN: `Mong`
+ * @HB_SCRIPT_MYANMAR: `Mymr`
+ * @HB_SCRIPT_OGHAM: `Ogam`
+ * @HB_SCRIPT_RUNIC: `Runr`
+ * @HB_SCRIPT_SINHALA: `Sinh`
+ * @HB_SCRIPT_SYRIAC: `Syrc`
+ * @HB_SCRIPT_THAANA: `Thaa`
+ * @HB_SCRIPT_YI: `Yiii`
+ * @HB_SCRIPT_DESERET: `Dsrt`
+ * @HB_SCRIPT_GOTHIC: `Goth`
+ * @HB_SCRIPT_OLD_ITALIC: `Ital`
+ * @HB_SCRIPT_BUHID: `Buhd`
+ * @HB_SCRIPT_HANUNOO: `Hano`
+ * @HB_SCRIPT_TAGALOG: `Tglg`
+ * @HB_SCRIPT_TAGBANWA: `Tagb`
+ * @HB_SCRIPT_CYPRIOT: `Cprt`
+ * @HB_SCRIPT_LIMBU: `Limb`
+ * @HB_SCRIPT_LINEAR_B: `Linb`
+ * @HB_SCRIPT_OSMANYA: `Osma`
+ * @HB_SCRIPT_SHAVIAN: `Shaw`
+ * @HB_SCRIPT_TAI_LE: `Tale`
+ * @HB_SCRIPT_UGARITIC: `Ugar`
+ * @HB_SCRIPT_BUGINESE: `Bugi`
+ * @HB_SCRIPT_COPTIC: `Copt`
+ * @HB_SCRIPT_GLAGOLITIC: `Glag`
+ * @HB_SCRIPT_KHAROSHTHI: `Khar`
+ * @HB_SCRIPT_NEW_TAI_LUE: `Talu`
+ * @HB_SCRIPT_OLD_PERSIAN: `Xpeo`
+ * @HB_SCRIPT_SYLOTI_NAGRI: `Sylo`
+ * @HB_SCRIPT_TIFINAGH: `Tfng`
+ * @HB_SCRIPT_BALINESE: `Bali`
+ * @HB_SCRIPT_CUNEIFORM: `Xsux`
+ * @HB_SCRIPT_NKO: `Nkoo`
+ * @HB_SCRIPT_PHAGS_PA: `Phag`
+ * @HB_SCRIPT_PHOENICIAN: `Phnx`
+ * @HB_SCRIPT_CARIAN: `Cari`
+ * @HB_SCRIPT_CHAM: `Cham`
+ * @HB_SCRIPT_KAYAH_LI: `Kali`
+ * @HB_SCRIPT_LEPCHA: `Lepc`
+ * @HB_SCRIPT_LYCIAN: `Lyci`
+ * @HB_SCRIPT_LYDIAN: `Lydi`
+ * @HB_SCRIPT_OL_CHIKI: `Olck`
+ * @HB_SCRIPT_REJANG: `Rjng`
+ * @HB_SCRIPT_SAURASHTRA: `Saur`
+ * @HB_SCRIPT_SUNDANESE: `Sund`
+ * @HB_SCRIPT_VAI: `Vaii`
+ * @HB_SCRIPT_AVESTAN: `Avst`
+ * @HB_SCRIPT_BAMUM: `Bamu`
+ * @HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: `Egyp`
+ * @HB_SCRIPT_IMPERIAL_ARAMAIC: `Armi`
+ * @HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: `Phli`
+ * @HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: `Prti`
+ * @HB_SCRIPT_JAVANESE: `Java`
+ * @HB_SCRIPT_KAITHI: `Kthi`
+ * @HB_SCRIPT_LISU: `Lisu`
+ * @HB_SCRIPT_MEETEI_MAYEK: `Mtei`
+ * @HB_SCRIPT_OLD_SOUTH_ARABIAN: `Sarb`
+ * @HB_SCRIPT_OLD_TURKIC: `Orkh`
+ * @HB_SCRIPT_SAMARITAN: `Samr`
+ * @HB_SCRIPT_TAI_THAM: `Lana`
+ * @HB_SCRIPT_TAI_VIET: `Tavt`
+ * @HB_SCRIPT_BATAK: `Batk`
+ * @HB_SCRIPT_BRAHMI: `Brah`
+ * @HB_SCRIPT_MANDAIC: `Mand`
+ * @HB_SCRIPT_CHAKMA: `Cakm`
+ * @HB_SCRIPT_MEROITIC_CURSIVE: `Merc`
+ * @HB_SCRIPT_MEROITIC_HIEROGLYPHS: `Mero`
+ * @HB_SCRIPT_MIAO: `Plrd`
+ * @HB_SCRIPT_SHARADA: `Shrd`
+ * @HB_SCRIPT_SORA_SOMPENG: `Sora`
+ * @HB_SCRIPT_TAKRI: `Takr`
+ * @HB_SCRIPT_BASSA_VAH: `Bass`, Since: 0.9.30
+ * @HB_SCRIPT_CAUCASIAN_ALBANIAN: `Aghb`, Since: 0.9.30
+ * @HB_SCRIPT_DUPLOYAN: `Dupl`, Since: 0.9.30
+ * @HB_SCRIPT_ELBASAN: `Elba`, Since: 0.9.30
+ * @HB_SCRIPT_GRANTHA: `Gran`, Since: 0.9.30
+ * @HB_SCRIPT_KHOJKI: `Khoj`, Since: 0.9.30
+ * @HB_SCRIPT_KHUDAWADI: `Sind`, Since: 0.9.30
+ * @HB_SCRIPT_LINEAR_A: `Lina`, Since: 0.9.30
+ * @HB_SCRIPT_MAHAJANI: `Mahj`, Since: 0.9.30
+ * @HB_SCRIPT_MANICHAEAN: `Mani`, Since: 0.9.30
+ * @HB_SCRIPT_MENDE_KIKAKUI: `Mend`, Since: 0.9.30
+ * @HB_SCRIPT_MODI: `Modi`, Since: 0.9.30
+ * @HB_SCRIPT_MRO: `Mroo`, Since: 0.9.30
+ * @HB_SCRIPT_NABATAEAN: `Nbat`, Since: 0.9.30
+ * @HB_SCRIPT_OLD_NORTH_ARABIAN: `Narb`, Since: 0.9.30
+ * @HB_SCRIPT_OLD_PERMIC: `Perm`, Since: 0.9.30
+ * @HB_SCRIPT_PAHAWH_HMONG: `Hmng`, Since: 0.9.30
+ * @HB_SCRIPT_PALMYRENE: `Palm`, Since: 0.9.30
+ * @HB_SCRIPT_PAU_CIN_HAU: `Pauc`, Since: 0.9.30
+ * @HB_SCRIPT_PSALTER_PAHLAVI: `Phlp`, Since: 0.9.30
+ * @HB_SCRIPT_SIDDHAM: `Sidd`, Since: 0.9.30
+ * @HB_SCRIPT_TIRHUTA: `Tirh`, Since: 0.9.30
+ * @HB_SCRIPT_WARANG_CITI: `Wara`, Since: 0.9.30
+ * @HB_SCRIPT_AHOM: `Ahom`, Since: 0.9.30
+ * @HB_SCRIPT_ANATOLIAN_HIEROGLYPHS: `Hluw`, Since: 0.9.30
+ * @HB_SCRIPT_HATRAN: `Hatr`, Since: 0.9.30
+ * @HB_SCRIPT_MULTANI: `Mult`, Since: 0.9.30
+ * @HB_SCRIPT_OLD_HUNGARIAN: `Hung`, Since: 0.9.30
+ * @HB_SCRIPT_SIGNWRITING: `Sgnw`, Since: 0.9.30
+ * @HB_SCRIPT_ADLAM: `Adlm`, Since: 1.3.0
+ * @HB_SCRIPT_BHAIKSUKI: `Bhks`, Since: 1.3.0
+ * @HB_SCRIPT_MARCHEN: `Marc`, Since: 1.3.0
+ * @HB_SCRIPT_OSAGE: `Osge`, Since: 1.3.0
+ * @HB_SCRIPT_TANGUT: `Tang`, Since: 1.3.0
+ * @HB_SCRIPT_NEWA: `Newa`, Since: 1.3.0
+ * @HB_SCRIPT_MASARAM_GONDI: `Gonm`, Since: 1.6.0
+ * @HB_SCRIPT_NUSHU: `Nshu`, Since: 1.6.0
+ * @HB_SCRIPT_SOYOMBO: `Soyo`, Since: 1.6.0
+ * @HB_SCRIPT_ZANABAZAR_SQUARE: `Zanb`, Since: 1.6.0
+ * @HB_SCRIPT_DOGRA: `Dogr`, Since: 1.8.0
+ * @HB_SCRIPT_GUNJALA_GONDI: `Gong`, Since: 1.8.0
+ * @HB_SCRIPT_HANIFI_ROHINGYA: `Rohg`, Since: 1.8.0
+ * @HB_SCRIPT_MAKASAR: `Maka`, Since: 1.8.0
+ * @HB_SCRIPT_MEDEFAIDRIN: `Medf`, Since: 1.8.0
+ * @HB_SCRIPT_OLD_SOGDIAN: `Sogo`, Since: 1.8.0
+ * @HB_SCRIPT_SOGDIAN: `Sogd`, Since: 1.8.0
+ * @HB_SCRIPT_ELYMAIC: `Elym`, Since: 2.4.0
+ * @HB_SCRIPT_NANDINAGARI: `Nand`, Since: 2.4.0
+ * @HB_SCRIPT_NYIAKENG_PUACHUE_HMONG: `Hmnp`, Since: 2.4.0
+ * @HB_SCRIPT_WANCHO: `Wcho`, Since: 2.4.0
+ * @HB_SCRIPT_CHORASMIAN: `Chrs`, Since: 2.6.7
+ * @HB_SCRIPT_DIVES_AKURU: `Diak`, Since: 2.6.7
+ * @HB_SCRIPT_KHITAN_SMALL_SCRIPT: `Kits`, Since: 2.6.7
+ * @HB_SCRIPT_YEZIDI: `Yezi`, Since: 2.6.7
+ * @HB_SCRIPT_INVALID: No script set
+ *
+ * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding
+ * to the four-letter values defined by [ISO 15924](https://unicode.org/iso15924/).
+ *
+ * See also the Script (sc) property of the Unicode Character Database.
+ *
+ **/
 
-/* https://unicode.org/iso15924/ */
 /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
-/* Unicode Character Database property: Script (sc) */
 typedef enum
 {
-  /*1.1*/ HB_SCRIPT_COMMON			= HB_TAG ('Z','y','y','y'),
-  /*1.1*/ HB_SCRIPT_INHERITED			= HB_TAG ('Z','i','n','h'),
-  /*5.0*/ HB_SCRIPT_UNKNOWN			= HB_TAG ('Z','z','z','z'),
+  HB_SCRIPT_COMMON			= HB_TAG ('Z','y','y','y'), /*1.1*/
+  HB_SCRIPT_INHERITED			= HB_TAG ('Z','i','n','h'), /*1.1*/
+  HB_SCRIPT_UNKNOWN			= HB_TAG ('Z','z','z','z'), /*5.0*/
 
-  /*1.1*/ HB_SCRIPT_ARABIC			= HB_TAG ('A','r','a','b'),
-  /*1.1*/ HB_SCRIPT_ARMENIAN			= HB_TAG ('A','r','m','n'),
-  /*1.1*/ HB_SCRIPT_BENGALI			= HB_TAG ('B','e','n','g'),
-  /*1.1*/ HB_SCRIPT_CYRILLIC			= HB_TAG ('C','y','r','l'),
-  /*1.1*/ HB_SCRIPT_DEVANAGARI			= HB_TAG ('D','e','v','a'),
-  /*1.1*/ HB_SCRIPT_GEORGIAN			= HB_TAG ('G','e','o','r'),
-  /*1.1*/ HB_SCRIPT_GREEK			= HB_TAG ('G','r','e','k'),
-  /*1.1*/ HB_SCRIPT_GUJARATI			= HB_TAG ('G','u','j','r'),
-  /*1.1*/ HB_SCRIPT_GURMUKHI			= HB_TAG ('G','u','r','u'),
-  /*1.1*/ HB_SCRIPT_HANGUL			= HB_TAG ('H','a','n','g'),
-  /*1.1*/ HB_SCRIPT_HAN				= HB_TAG ('H','a','n','i'),
-  /*1.1*/ HB_SCRIPT_HEBREW			= HB_TAG ('H','e','b','r'),
-  /*1.1*/ HB_SCRIPT_HIRAGANA			= HB_TAG ('H','i','r','a'),
-  /*1.1*/ HB_SCRIPT_KANNADA			= HB_TAG ('K','n','d','a'),
-  /*1.1*/ HB_SCRIPT_KATAKANA			= HB_TAG ('K','a','n','a'),
-  /*1.1*/ HB_SCRIPT_LAO				= HB_TAG ('L','a','o','o'),
-  /*1.1*/ HB_SCRIPT_LATIN			= HB_TAG ('L','a','t','n'),
-  /*1.1*/ HB_SCRIPT_MALAYALAM			= HB_TAG ('M','l','y','m'),
-  /*1.1*/ HB_SCRIPT_ORIYA			= HB_TAG ('O','r','y','a'),
-  /*1.1*/ HB_SCRIPT_TAMIL			= HB_TAG ('T','a','m','l'),
-  /*1.1*/ HB_SCRIPT_TELUGU			= HB_TAG ('T','e','l','u'),
-  /*1.1*/ HB_SCRIPT_THAI			= HB_TAG ('T','h','a','i'),
+  HB_SCRIPT_ARABIC			= HB_TAG ('A','r','a','b'), /*1.1*/
+  HB_SCRIPT_ARMENIAN			= HB_TAG ('A','r','m','n'), /*1.1*/
+  HB_SCRIPT_BENGALI			= HB_TAG ('B','e','n','g'), /*1.1*/
+  HB_SCRIPT_CYRILLIC			= HB_TAG ('C','y','r','l'), /*1.1*/
+  HB_SCRIPT_DEVANAGARI			= HB_TAG ('D','e','v','a'), /*1.1*/
+  HB_SCRIPT_GEORGIAN			= HB_TAG ('G','e','o','r'), /*1.1*/
+  HB_SCRIPT_GREEK			= HB_TAG ('G','r','e','k'), /*1.1*/
+  HB_SCRIPT_GUJARATI			= HB_TAG ('G','u','j','r'), /*1.1*/
+  HB_SCRIPT_GURMUKHI			= HB_TAG ('G','u','r','u'), /*1.1*/
+  HB_SCRIPT_HANGUL			= HB_TAG ('H','a','n','g'), /*1.1*/
+  HB_SCRIPT_HAN				= HB_TAG ('H','a','n','i'), /*1.1*/
+  HB_SCRIPT_HEBREW			= HB_TAG ('H','e','b','r'), /*1.1*/
+  HB_SCRIPT_HIRAGANA			= HB_TAG ('H','i','r','a'), /*1.1*/
+  HB_SCRIPT_KANNADA			= HB_TAG ('K','n','d','a'), /*1.1*/
+  HB_SCRIPT_KATAKANA			= HB_TAG ('K','a','n','a'), /*1.1*/
+  HB_SCRIPT_LAO				= HB_TAG ('L','a','o','o'), /*1.1*/
+  HB_SCRIPT_LATIN			= HB_TAG ('L','a','t','n'), /*1.1*/
+  HB_SCRIPT_MALAYALAM			= HB_TAG ('M','l','y','m'), /*1.1*/
+  HB_SCRIPT_ORIYA			= HB_TAG ('O','r','y','a'), /*1.1*/
+  HB_SCRIPT_TAMIL			= HB_TAG ('T','a','m','l'), /*1.1*/
+  HB_SCRIPT_TELUGU			= HB_TAG ('T','e','l','u'), /*1.1*/
+  HB_SCRIPT_THAI			= HB_TAG ('T','h','a','i'), /*1.1*/
 
-  /*2.0*/ HB_SCRIPT_TIBETAN			= HB_TAG ('T','i','b','t'),
+  HB_SCRIPT_TIBETAN			= HB_TAG ('T','i','b','t'), /*2.0*/
 
-  /*3.0*/ HB_SCRIPT_BOPOMOFO			= HB_TAG ('B','o','p','o'),
-  /*3.0*/ HB_SCRIPT_BRAILLE			= HB_TAG ('B','r','a','i'),
-  /*3.0*/ HB_SCRIPT_CANADIAN_SYLLABICS		= HB_TAG ('C','a','n','s'),
-  /*3.0*/ HB_SCRIPT_CHEROKEE			= HB_TAG ('C','h','e','r'),
-  /*3.0*/ HB_SCRIPT_ETHIOPIC			= HB_TAG ('E','t','h','i'),
-  /*3.0*/ HB_SCRIPT_KHMER			= HB_TAG ('K','h','m','r'),
-  /*3.0*/ HB_SCRIPT_MONGOLIAN			= HB_TAG ('M','o','n','g'),
-  /*3.0*/ HB_SCRIPT_MYANMAR			= HB_TAG ('M','y','m','r'),
-  /*3.0*/ HB_SCRIPT_OGHAM			= HB_TAG ('O','g','a','m'),
-  /*3.0*/ HB_SCRIPT_RUNIC			= HB_TAG ('R','u','n','r'),
-  /*3.0*/ HB_SCRIPT_SINHALA			= HB_TAG ('S','i','n','h'),
-  /*3.0*/ HB_SCRIPT_SYRIAC			= HB_TAG ('S','y','r','c'),
-  /*3.0*/ HB_SCRIPT_THAANA			= HB_TAG ('T','h','a','a'),
-  /*3.0*/ HB_SCRIPT_YI				= HB_TAG ('Y','i','i','i'),
+  HB_SCRIPT_BOPOMOFO			= HB_TAG ('B','o','p','o'), /*3.0*/
+  HB_SCRIPT_BRAILLE			= HB_TAG ('B','r','a','i'), /*3.0*/
+  HB_SCRIPT_CANADIAN_SYLLABICS		= HB_TAG ('C','a','n','s'), /*3.0*/
+  HB_SCRIPT_CHEROKEE			= HB_TAG ('C','h','e','r'), /*3.0*/
+  HB_SCRIPT_ETHIOPIC			= HB_TAG ('E','t','h','i'), /*3.0*/
+  HB_SCRIPT_KHMER			= HB_TAG ('K','h','m','r'), /*3.0*/
+  HB_SCRIPT_MONGOLIAN			= HB_TAG ('M','o','n','g'), /*3.0*/
+  HB_SCRIPT_MYANMAR			= HB_TAG ('M','y','m','r'), /*3.0*/
+  HB_SCRIPT_OGHAM			= HB_TAG ('O','g','a','m'), /*3.0*/
+  HB_SCRIPT_RUNIC			= HB_TAG ('R','u','n','r'), /*3.0*/
+  HB_SCRIPT_SINHALA			= HB_TAG ('S','i','n','h'), /*3.0*/
+  HB_SCRIPT_SYRIAC			= HB_TAG ('S','y','r','c'), /*3.0*/
+  HB_SCRIPT_THAANA			= HB_TAG ('T','h','a','a'), /*3.0*/
+  HB_SCRIPT_YI				= HB_TAG ('Y','i','i','i'), /*3.0*/
 
-  /*3.1*/ HB_SCRIPT_DESERET			= HB_TAG ('D','s','r','t'),
-  /*3.1*/ HB_SCRIPT_GOTHIC			= HB_TAG ('G','o','t','h'),
-  /*3.1*/ HB_SCRIPT_OLD_ITALIC			= HB_TAG ('I','t','a','l'),
+  HB_SCRIPT_DESERET			= HB_TAG ('D','s','r','t'), /*3.1*/
+  HB_SCRIPT_GOTHIC			= HB_TAG ('G','o','t','h'), /*3.1*/
+  HB_SCRIPT_OLD_ITALIC			= HB_TAG ('I','t','a','l'), /*3.1*/
 
-  /*3.2*/ HB_SCRIPT_BUHID			= HB_TAG ('B','u','h','d'),
-  /*3.2*/ HB_SCRIPT_HANUNOO			= HB_TAG ('H','a','n','o'),
-  /*3.2*/ HB_SCRIPT_TAGALOG			= HB_TAG ('T','g','l','g'),
-  /*3.2*/ HB_SCRIPT_TAGBANWA			= HB_TAG ('T','a','g','b'),
+  HB_SCRIPT_BUHID			= HB_TAG ('B','u','h','d'), /*3.2*/
+  HB_SCRIPT_HANUNOO			= HB_TAG ('H','a','n','o'), /*3.2*/
+  HB_SCRIPT_TAGALOG			= HB_TAG ('T','g','l','g'), /*3.2*/
+  HB_SCRIPT_TAGBANWA			= HB_TAG ('T','a','g','b'), /*3.2*/
 
-  /*4.0*/ HB_SCRIPT_CYPRIOT			= HB_TAG ('C','p','r','t'),
-  /*4.0*/ HB_SCRIPT_LIMBU			= HB_TAG ('L','i','m','b'),
-  /*4.0*/ HB_SCRIPT_LINEAR_B			= HB_TAG ('L','i','n','b'),
-  /*4.0*/ HB_SCRIPT_OSMANYA			= HB_TAG ('O','s','m','a'),
-  /*4.0*/ HB_SCRIPT_SHAVIAN			= HB_TAG ('S','h','a','w'),
-  /*4.0*/ HB_SCRIPT_TAI_LE			= HB_TAG ('T','a','l','e'),
-  /*4.0*/ HB_SCRIPT_UGARITIC			= HB_TAG ('U','g','a','r'),
+  HB_SCRIPT_CYPRIOT			= HB_TAG ('C','p','r','t'), /*4.0*/
+  HB_SCRIPT_LIMBU			= HB_TAG ('L','i','m','b'), /*4.0*/
+  HB_SCRIPT_LINEAR_B			= HB_TAG ('L','i','n','b'), /*4.0*/
+  HB_SCRIPT_OSMANYA			= HB_TAG ('O','s','m','a'), /*4.0*/
+  HB_SCRIPT_SHAVIAN			= HB_TAG ('S','h','a','w'), /*4.0*/
+  HB_SCRIPT_TAI_LE			= HB_TAG ('T','a','l','e'), /*4.0*/
+  HB_SCRIPT_UGARITIC			= HB_TAG ('U','g','a','r'), /*4.0*/
 
-  /*4.1*/ HB_SCRIPT_BUGINESE			= HB_TAG ('B','u','g','i'),
-  /*4.1*/ HB_SCRIPT_COPTIC			= HB_TAG ('C','o','p','t'),
-  /*4.1*/ HB_SCRIPT_GLAGOLITIC			= HB_TAG ('G','l','a','g'),
-  /*4.1*/ HB_SCRIPT_KHAROSHTHI			= HB_TAG ('K','h','a','r'),
-  /*4.1*/ HB_SCRIPT_NEW_TAI_LUE			= HB_TAG ('T','a','l','u'),
-  /*4.1*/ HB_SCRIPT_OLD_PERSIAN			= HB_TAG ('X','p','e','o'),
-  /*4.1*/ HB_SCRIPT_SYLOTI_NAGRI		= HB_TAG ('S','y','l','o'),
-  /*4.1*/ HB_SCRIPT_TIFINAGH			= HB_TAG ('T','f','n','g'),
+  HB_SCRIPT_BUGINESE			= HB_TAG ('B','u','g','i'), /*4.1*/
+  HB_SCRIPT_COPTIC			= HB_TAG ('C','o','p','t'), /*4.1*/
+  HB_SCRIPT_GLAGOLITIC			= HB_TAG ('G','l','a','g'), /*4.1*/
+  HB_SCRIPT_KHAROSHTHI			= HB_TAG ('K','h','a','r'), /*4.1*/
+  HB_SCRIPT_NEW_TAI_LUE			= HB_TAG ('T','a','l','u'), /*4.1*/
+  HB_SCRIPT_OLD_PERSIAN			= HB_TAG ('X','p','e','o'), /*4.1*/
+  HB_SCRIPT_SYLOTI_NAGRI		= HB_TAG ('S','y','l','o'), /*4.1*/
+  HB_SCRIPT_TIFINAGH			= HB_TAG ('T','f','n','g'), /*4.1*/
 
-  /*5.0*/ HB_SCRIPT_BALINESE			= HB_TAG ('B','a','l','i'),
-  /*5.0*/ HB_SCRIPT_CUNEIFORM			= HB_TAG ('X','s','u','x'),
-  /*5.0*/ HB_SCRIPT_NKO				= HB_TAG ('N','k','o','o'),
-  /*5.0*/ HB_SCRIPT_PHAGS_PA			= HB_TAG ('P','h','a','g'),
-  /*5.0*/ HB_SCRIPT_PHOENICIAN			= HB_TAG ('P','h','n','x'),
+  HB_SCRIPT_BALINESE			= HB_TAG ('B','a','l','i'), /*5.0*/
+  HB_SCRIPT_CUNEIFORM			= HB_TAG ('X','s','u','x'), /*5.0*/
+  HB_SCRIPT_NKO				= HB_TAG ('N','k','o','o'), /*5.0*/
+  HB_SCRIPT_PHAGS_PA			= HB_TAG ('P','h','a','g'), /*5.0*/
+  HB_SCRIPT_PHOENICIAN			= HB_TAG ('P','h','n','x'), /*5.0*/
 
-  /*5.1*/ HB_SCRIPT_CARIAN			= HB_TAG ('C','a','r','i'),
-  /*5.1*/ HB_SCRIPT_CHAM			= HB_TAG ('C','h','a','m'),
-  /*5.1*/ HB_SCRIPT_KAYAH_LI			= HB_TAG ('K','a','l','i'),
-  /*5.1*/ HB_SCRIPT_LEPCHA			= HB_TAG ('L','e','p','c'),
-  /*5.1*/ HB_SCRIPT_LYCIAN			= HB_TAG ('L','y','c','i'),
-  /*5.1*/ HB_SCRIPT_LYDIAN			= HB_TAG ('L','y','d','i'),
-  /*5.1*/ HB_SCRIPT_OL_CHIKI			= HB_TAG ('O','l','c','k'),
-  /*5.1*/ HB_SCRIPT_REJANG			= HB_TAG ('R','j','n','g'),
-  /*5.1*/ HB_SCRIPT_SAURASHTRA			= HB_TAG ('S','a','u','r'),
-  /*5.1*/ HB_SCRIPT_SUNDANESE			= HB_TAG ('S','u','n','d'),
-  /*5.1*/ HB_SCRIPT_VAI				= HB_TAG ('V','a','i','i'),
+  HB_SCRIPT_CARIAN			= HB_TAG ('C','a','r','i'), /*5.1*/
+  HB_SCRIPT_CHAM			= HB_TAG ('C','h','a','m'), /*5.1*/
+  HB_SCRIPT_KAYAH_LI			= HB_TAG ('K','a','l','i'), /*5.1*/
+  HB_SCRIPT_LEPCHA			= HB_TAG ('L','e','p','c'), /*5.1*/
+  HB_SCRIPT_LYCIAN			= HB_TAG ('L','y','c','i'), /*5.1*/
+  HB_SCRIPT_LYDIAN			= HB_TAG ('L','y','d','i'), /*5.1*/
+  HB_SCRIPT_OL_CHIKI			= HB_TAG ('O','l','c','k'), /*5.1*/
+  HB_SCRIPT_REJANG			= HB_TAG ('R','j','n','g'), /*5.1*/
+  HB_SCRIPT_SAURASHTRA			= HB_TAG ('S','a','u','r'), /*5.1*/
+  HB_SCRIPT_SUNDANESE			= HB_TAG ('S','u','n','d'), /*5.1*/
+  HB_SCRIPT_VAI				= HB_TAG ('V','a','i','i'), /*5.1*/
 
-  /*5.2*/ HB_SCRIPT_AVESTAN			= HB_TAG ('A','v','s','t'),
-  /*5.2*/ HB_SCRIPT_BAMUM			= HB_TAG ('B','a','m','u'),
-  /*5.2*/ HB_SCRIPT_EGYPTIAN_HIEROGLYPHS	= HB_TAG ('E','g','y','p'),
-  /*5.2*/ HB_SCRIPT_IMPERIAL_ARAMAIC		= HB_TAG ('A','r','m','i'),
-  /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PAHLAVI	= HB_TAG ('P','h','l','i'),
-  /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PARTHIAN	= HB_TAG ('P','r','t','i'),
-  /*5.2*/ HB_SCRIPT_JAVANESE			= HB_TAG ('J','a','v','a'),
-  /*5.2*/ HB_SCRIPT_KAITHI			= HB_TAG ('K','t','h','i'),
-  /*5.2*/ HB_SCRIPT_LISU			= HB_TAG ('L','i','s','u'),
-  /*5.2*/ HB_SCRIPT_MEETEI_MAYEK		= HB_TAG ('M','t','e','i'),
-  /*5.2*/ HB_SCRIPT_OLD_SOUTH_ARABIAN		= HB_TAG ('S','a','r','b'),
-  /*5.2*/ HB_SCRIPT_OLD_TURKIC			= HB_TAG ('O','r','k','h'),
-  /*5.2*/ HB_SCRIPT_SAMARITAN			= HB_TAG ('S','a','m','r'),
-  /*5.2*/ HB_SCRIPT_TAI_THAM			= HB_TAG ('L','a','n','a'),
-  /*5.2*/ HB_SCRIPT_TAI_VIET			= HB_TAG ('T','a','v','t'),
+  HB_SCRIPT_AVESTAN			= HB_TAG ('A','v','s','t'), /*5.2*/
+  HB_SCRIPT_BAMUM			= HB_TAG ('B','a','m','u'), /*5.2*/
+  HB_SCRIPT_EGYPTIAN_HIEROGLYPHS	= HB_TAG ('E','g','y','p'), /*5.2*/
+  HB_SCRIPT_IMPERIAL_ARAMAIC		= HB_TAG ('A','r','m','i'), /*5.2*/
+  HB_SCRIPT_INSCRIPTIONAL_PAHLAVI	= HB_TAG ('P','h','l','i'), /*5.2*/
+  HB_SCRIPT_INSCRIPTIONAL_PARTHIAN	= HB_TAG ('P','r','t','i'), /*5.2*/
+  HB_SCRIPT_JAVANESE			= HB_TAG ('J','a','v','a'), /*5.2*/
+  HB_SCRIPT_KAITHI			= HB_TAG ('K','t','h','i'), /*5.2*/
+  HB_SCRIPT_LISU			= HB_TAG ('L','i','s','u'), /*5.2*/
+  HB_SCRIPT_MEETEI_MAYEK		= HB_TAG ('M','t','e','i'), /*5.2*/
+  HB_SCRIPT_OLD_SOUTH_ARABIAN		= HB_TAG ('S','a','r','b'), /*5.2*/
+  HB_SCRIPT_OLD_TURKIC			= HB_TAG ('O','r','k','h'), /*5.2*/
+  HB_SCRIPT_SAMARITAN			= HB_TAG ('S','a','m','r'), /*5.2*/
+  HB_SCRIPT_TAI_THAM			= HB_TAG ('L','a','n','a'), /*5.2*/
+  HB_SCRIPT_TAI_VIET			= HB_TAG ('T','a','v','t'), /*5.2*/
 
-  /*6.0*/ HB_SCRIPT_BATAK			= HB_TAG ('B','a','t','k'),
-  /*6.0*/ HB_SCRIPT_BRAHMI			= HB_TAG ('B','r','a','h'),
-  /*6.0*/ HB_SCRIPT_MANDAIC			= HB_TAG ('M','a','n','d'),
+  HB_SCRIPT_BATAK			= HB_TAG ('B','a','t','k'), /*6.0*/
+  HB_SCRIPT_BRAHMI			= HB_TAG ('B','r','a','h'), /*6.0*/
+  HB_SCRIPT_MANDAIC			= HB_TAG ('M','a','n','d'), /*6.0*/
 
-  /*6.1*/ HB_SCRIPT_CHAKMA			= HB_TAG ('C','a','k','m'),
-  /*6.1*/ HB_SCRIPT_MEROITIC_CURSIVE		= HB_TAG ('M','e','r','c'),
-  /*6.1*/ HB_SCRIPT_MEROITIC_HIEROGLYPHS	= HB_TAG ('M','e','r','o'),
-  /*6.1*/ HB_SCRIPT_MIAO			= HB_TAG ('P','l','r','d'),
-  /*6.1*/ HB_SCRIPT_SHARADA			= HB_TAG ('S','h','r','d'),
-  /*6.1*/ HB_SCRIPT_SORA_SOMPENG		= HB_TAG ('S','o','r','a'),
-  /*6.1*/ HB_SCRIPT_TAKRI			= HB_TAG ('T','a','k','r'),
+  HB_SCRIPT_CHAKMA			= HB_TAG ('C','a','k','m'), /*6.1*/
+  HB_SCRIPT_MEROITIC_CURSIVE		= HB_TAG ('M','e','r','c'), /*6.1*/
+  HB_SCRIPT_MEROITIC_HIEROGLYPHS	= HB_TAG ('M','e','r','o'), /*6.1*/
+  HB_SCRIPT_MIAO			= HB_TAG ('P','l','r','d'), /*6.1*/
+  HB_SCRIPT_SHARADA			= HB_TAG ('S','h','r','d'), /*6.1*/
+  HB_SCRIPT_SORA_SOMPENG		= HB_TAG ('S','o','r','a'), /*6.1*/
+  HB_SCRIPT_TAKRI			= HB_TAG ('T','a','k','r'), /*6.1*/
 
   /*
    * Since: 0.9.30
    */
-  /*7.0*/ HB_SCRIPT_BASSA_VAH			= HB_TAG ('B','a','s','s'),
-  /*7.0*/ HB_SCRIPT_CAUCASIAN_ALBANIAN		= HB_TAG ('A','g','h','b'),
-  /*7.0*/ HB_SCRIPT_DUPLOYAN			= HB_TAG ('D','u','p','l'),
-  /*7.0*/ HB_SCRIPT_ELBASAN			= HB_TAG ('E','l','b','a'),
-  /*7.0*/ HB_SCRIPT_GRANTHA			= HB_TAG ('G','r','a','n'),
-  /*7.0*/ HB_SCRIPT_KHOJKI			= HB_TAG ('K','h','o','j'),
-  /*7.0*/ HB_SCRIPT_KHUDAWADI			= HB_TAG ('S','i','n','d'),
-  /*7.0*/ HB_SCRIPT_LINEAR_A			= HB_TAG ('L','i','n','a'),
-  /*7.0*/ HB_SCRIPT_MAHAJANI			= HB_TAG ('M','a','h','j'),
-  /*7.0*/ HB_SCRIPT_MANICHAEAN			= HB_TAG ('M','a','n','i'),
-  /*7.0*/ HB_SCRIPT_MENDE_KIKAKUI		= HB_TAG ('M','e','n','d'),
-  /*7.0*/ HB_SCRIPT_MODI			= HB_TAG ('M','o','d','i'),
-  /*7.0*/ HB_SCRIPT_MRO				= HB_TAG ('M','r','o','o'),
-  /*7.0*/ HB_SCRIPT_NABATAEAN			= HB_TAG ('N','b','a','t'),
-  /*7.0*/ HB_SCRIPT_OLD_NORTH_ARABIAN		= HB_TAG ('N','a','r','b'),
-  /*7.0*/ HB_SCRIPT_OLD_PERMIC			= HB_TAG ('P','e','r','m'),
-  /*7.0*/ HB_SCRIPT_PAHAWH_HMONG		= HB_TAG ('H','m','n','g'),
-  /*7.0*/ HB_SCRIPT_PALMYRENE			= HB_TAG ('P','a','l','m'),
-  /*7.0*/ HB_SCRIPT_PAU_CIN_HAU			= HB_TAG ('P','a','u','c'),
-  /*7.0*/ HB_SCRIPT_PSALTER_PAHLAVI		= HB_TAG ('P','h','l','p'),
-  /*7.0*/ HB_SCRIPT_SIDDHAM			= HB_TAG ('S','i','d','d'),
-  /*7.0*/ HB_SCRIPT_TIRHUTA			= HB_TAG ('T','i','r','h'),
-  /*7.0*/ HB_SCRIPT_WARANG_CITI			= HB_TAG ('W','a','r','a'),
+  HB_SCRIPT_BASSA_VAH			= HB_TAG ('B','a','s','s'), /*7.0*/
+  HB_SCRIPT_CAUCASIAN_ALBANIAN		= HB_TAG ('A','g','h','b'), /*7.0*/
+  HB_SCRIPT_DUPLOYAN			= HB_TAG ('D','u','p','l'), /*7.0*/
+  HB_SCRIPT_ELBASAN			= HB_TAG ('E','l','b','a'), /*7.0*/
+  HB_SCRIPT_GRANTHA			= HB_TAG ('G','r','a','n'), /*7.0*/
+  HB_SCRIPT_KHOJKI			= HB_TAG ('K','h','o','j'), /*7.0*/
+  HB_SCRIPT_KHUDAWADI			= HB_TAG ('S','i','n','d'), /*7.0*/
+  HB_SCRIPT_LINEAR_A			= HB_TAG ('L','i','n','a'), /*7.0*/
+  HB_SCRIPT_MAHAJANI			= HB_TAG ('M','a','h','j'), /*7.0*/
+  HB_SCRIPT_MANICHAEAN			= HB_TAG ('M','a','n','i'), /*7.0*/
+  HB_SCRIPT_MENDE_KIKAKUI		= HB_TAG ('M','e','n','d'), /*7.0*/
+  HB_SCRIPT_MODI			= HB_TAG ('M','o','d','i'), /*7.0*/
+  HB_SCRIPT_MRO				= HB_TAG ('M','r','o','o'), /*7.0*/
+  HB_SCRIPT_NABATAEAN			= HB_TAG ('N','b','a','t'), /*7.0*/
+  HB_SCRIPT_OLD_NORTH_ARABIAN		= HB_TAG ('N','a','r','b'), /*7.0*/
+  HB_SCRIPT_OLD_PERMIC			= HB_TAG ('P','e','r','m'), /*7.0*/
+  HB_SCRIPT_PAHAWH_HMONG		= HB_TAG ('H','m','n','g'), /*7.0*/
+  HB_SCRIPT_PALMYRENE			= HB_TAG ('P','a','l','m'), /*7.0*/
+  HB_SCRIPT_PAU_CIN_HAU			= HB_TAG ('P','a','u','c'), /*7.0*/
+  HB_SCRIPT_PSALTER_PAHLAVI		= HB_TAG ('P','h','l','p'), /*7.0*/
+  HB_SCRIPT_SIDDHAM			= HB_TAG ('S','i','d','d'), /*7.0*/
+  HB_SCRIPT_TIRHUTA			= HB_TAG ('T','i','r','h'), /*7.0*/
+  HB_SCRIPT_WARANG_CITI			= HB_TAG ('W','a','r','a'), /*7.0*/
 
-  /*8.0*/ HB_SCRIPT_AHOM			= HB_TAG ('A','h','o','m'),
-  /*8.0*/ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS	= HB_TAG ('H','l','u','w'),
-  /*8.0*/ HB_SCRIPT_HATRAN			= HB_TAG ('H','a','t','r'),
-  /*8.0*/ HB_SCRIPT_MULTANI			= HB_TAG ('M','u','l','t'),
-  /*8.0*/ HB_SCRIPT_OLD_HUNGARIAN		= HB_TAG ('H','u','n','g'),
-  /*8.0*/ HB_SCRIPT_SIGNWRITING			= HB_TAG ('S','g','n','w'),
+  HB_SCRIPT_AHOM			= HB_TAG ('A','h','o','m'), /*8.0*/
+  HB_SCRIPT_ANATOLIAN_HIEROGLYPHS	= HB_TAG ('H','l','u','w'), /*8.0*/
+  HB_SCRIPT_HATRAN			= HB_TAG ('H','a','t','r'), /*8.0*/
+  HB_SCRIPT_MULTANI			= HB_TAG ('M','u','l','t'), /*8.0*/
+  HB_SCRIPT_OLD_HUNGARIAN		= HB_TAG ('H','u','n','g'), /*8.0*/
+  HB_SCRIPT_SIGNWRITING			= HB_TAG ('S','g','n','w'), /*8.0*/
 
   /*
    * Since 1.3.0
    */
-  /*9.0*/ HB_SCRIPT_ADLAM			= HB_TAG ('A','d','l','m'),
-  /*9.0*/ HB_SCRIPT_BHAIKSUKI			= HB_TAG ('B','h','k','s'),
-  /*9.0*/ HB_SCRIPT_MARCHEN			= HB_TAG ('M','a','r','c'),
-  /*9.0*/ HB_SCRIPT_OSAGE			= HB_TAG ('O','s','g','e'),
-  /*9.0*/ HB_SCRIPT_TANGUT			= HB_TAG ('T','a','n','g'),
-  /*9.0*/ HB_SCRIPT_NEWA			= HB_TAG ('N','e','w','a'),
+  HB_SCRIPT_ADLAM			= HB_TAG ('A','d','l','m'), /*9.0*/
+  HB_SCRIPT_BHAIKSUKI			= HB_TAG ('B','h','k','s'), /*9.0*/
+  HB_SCRIPT_MARCHEN			= HB_TAG ('M','a','r','c'), /*9.0*/
+  HB_SCRIPT_OSAGE			= HB_TAG ('O','s','g','e'), /*9.0*/
+  HB_SCRIPT_TANGUT			= HB_TAG ('T','a','n','g'), /*9.0*/
+  HB_SCRIPT_NEWA			= HB_TAG ('N','e','w','a'), /*9.0*/
 
   /*
    * Since 1.6.0
    */
-  /*10.0*/HB_SCRIPT_MASARAM_GONDI		= HB_TAG ('G','o','n','m'),
-  /*10.0*/HB_SCRIPT_NUSHU			= HB_TAG ('N','s','h','u'),
-  /*10.0*/HB_SCRIPT_SOYOMBO			= HB_TAG ('S','o','y','o'),
-  /*10.0*/HB_SCRIPT_ZANABAZAR_SQUARE		= HB_TAG ('Z','a','n','b'),
+  HB_SCRIPT_MASARAM_GONDI		= HB_TAG ('G','o','n','m'), /*10.0*/
+  HB_SCRIPT_NUSHU			= HB_TAG ('N','s','h','u'), /*10.0*/
+  HB_SCRIPT_SOYOMBO			= HB_TAG ('S','o','y','o'), /*10.0*/
+  HB_SCRIPT_ZANABAZAR_SQUARE		= HB_TAG ('Z','a','n','b'), /*10.0*/
 
   /*
    * Since 1.8.0
    */
-  /*11.0*/HB_SCRIPT_DOGRA			= HB_TAG ('D','o','g','r'),
-  /*11.0*/HB_SCRIPT_GUNJALA_GONDI		= HB_TAG ('G','o','n','g'),
-  /*11.0*/HB_SCRIPT_HANIFI_ROHINGYA		= HB_TAG ('R','o','h','g'),
-  /*11.0*/HB_SCRIPT_MAKASAR			= HB_TAG ('M','a','k','a'),
-  /*11.0*/HB_SCRIPT_MEDEFAIDRIN			= HB_TAG ('M','e','d','f'),
-  /*11.0*/HB_SCRIPT_OLD_SOGDIAN			= HB_TAG ('S','o','g','o'),
-  /*11.0*/HB_SCRIPT_SOGDIAN			= HB_TAG ('S','o','g','d'),
+  HB_SCRIPT_DOGRA			= HB_TAG ('D','o','g','r'), /*11.0*/
+  HB_SCRIPT_GUNJALA_GONDI		= HB_TAG ('G','o','n','g'), /*11.0*/
+  HB_SCRIPT_HANIFI_ROHINGYA		= HB_TAG ('R','o','h','g'), /*11.0*/
+  HB_SCRIPT_MAKASAR			= HB_TAG ('M','a','k','a'), /*11.0*/
+  HB_SCRIPT_MEDEFAIDRIN			= HB_TAG ('M','e','d','f'), /*11.0*/
+  HB_SCRIPT_OLD_SOGDIAN			= HB_TAG ('S','o','g','o'), /*11.0*/
+  HB_SCRIPT_SOGDIAN			= HB_TAG ('S','o','g','d'), /*11.0*/
 
   /*
    * 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'),
+  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'), /*12.0*/
 
   /*
    * Since 2.6.7
    */
-  /*13.0*/HB_SCRIPT_CHORASMIAN			= HB_TAG ('C','h','r','s'),
-  /*13.0*/HB_SCRIPT_DIVES_AKURU			= HB_TAG ('D','i','a','k'),
-  /*13.0*/HB_SCRIPT_KHITAN_SMALL_SCRIPT		= HB_TAG ('K','i','t','s'),
-  /*13.0*/HB_SCRIPT_YEZIDI			= HB_TAG ('Y','e','z','i'),
+  HB_SCRIPT_CHORASMIAN			= HB_TAG ('C','h','r','s'), /*13.0*/
+  HB_SCRIPT_DIVES_AKURU			= HB_TAG ('D','i','a','k'), /*13.0*/
+  HB_SCRIPT_KHITAN_SMALL_SCRIPT		= HB_TAG ('K','i','t','s'), /*13.0*/
+  HB_SCRIPT_YEZIDI			= HB_TAG ('Y','e','z','i'), /*13.0*/
 
   /* No script set. */
-  HB_SCRIPT_INVALID				= HB_TAG_NONE,
+  HB_SCRIPT_INVALID			= HB_TAG_NONE,
+
+  /*< private >*/
 
   /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t
    * without risking undefined behavior.  We have two, for historical reasons.
@@ -410,24 +720,44 @@
 
 /* User data */
 
+/**
+ * hb_user_data_key_t:
+ *
+ * Data structure for holding user-data keys.
+ *
+ **/
 typedef struct hb_user_data_key_t {
   /*< private >*/
   char unused;
 } hb_user_data_key_t;
 
+/**
+ * hb_destroy_func_t:
+ * @user_data: the data to be destroyed
+ *
+ * A virtual method for destroy user-data callbacks.
+ *
+ */
 typedef void (*hb_destroy_func_t) (void *user_data);
 
 
 /* Font features and variations. */
 
 /**
- * HB_FEATURE_GLOBAL_START
+ * HB_FEATURE_GLOBAL_START:
+ *
+ * Special setting for #hb_feature_t.start to apply the feature from the start
+ * of the buffer.
  *
  * Since: 2.0.0
  */
 #define HB_FEATURE_GLOBAL_START	0
+
 /**
- * HB_FEATURE_GLOBAL_END
+ * HB_FEATURE_GLOBAL_END:
+ *
+ * Special setting for #hb_feature_t.end to apply the feature from to the end
+ * of the buffer.
  *
  * Since: 2.0.0
  */
@@ -435,17 +765,17 @@
 
 /**
  * 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.
+ * @tag: The #hb_tag_t tag of the feature
+ * @value: The value of the feature. 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
+ * 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 {
@@ -465,7 +795,13 @@
 
 /**
  * hb_variation_t:
+ * @tag: The #hb_tag_t tag of the variation-axis name
+ * @value: The value of the variation axis
  *
+ * Data type for holding variation data. Registered OpenType
+ * variation-axis tags are listed in
+ * [OpenType Axis Tag Registry](https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg).
+ * 
  * Since: 1.4.2
  */
 typedef struct hb_variation_t {
@@ -484,12 +820,24 @@
 /**
  * hb_color_t:
  *
- * Data type for holding color values.
+ * Data type for holding color values. Colors are eight bits per
+ * channel RGB plus alpha transparency.
  *
  * Since: 2.1.0
  */
 typedef uint32_t hb_color_t;
 
+/**
+ * HB_COLOR:
+ * @b: blue channel value
+ * @g: green channel value
+ * @r: red channel value
+ * @a: alpha channel value
+ *
+ * Constructs an #hb_color_t from four integers.
+ *
+ * Since: 2.1.0
+ */
 #define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a)))
 
 HB_EXTERN uint8_t
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc
index 7b6b2bd..2ca6a0d 100644
--- a/src/hb-coretext.cc
+++ b/src/hb-coretext.cc
@@ -402,7 +402,7 @@
 }
 
 /**
- * hb_coretext_face_get_ct_font:
+ * hb_coretext_font_get_ct_font:
  * @font: #hb_font_t to work upon
  *
  * Fetches the CTFontRef associated with the specified
diff --git a/src/hb-deprecated.h b/src/hb-deprecated.h
index 43f89a4..5f19125 100644
--- a/src/hb-deprecated.h
+++ b/src/hb-deprecated.h
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
@@ -53,11 +53,50 @@
 #ifndef HB_DISABLE_DEPRECATED
 
 
+/**
+ * HB_SCRIPT_CANADIAN_ABORIGINAL:
+ *
+ * Use #HB_SCRIPT_CANADIAN_SYLLABICS instead:
+ *
+ * Deprecated: 0.9.20
+ */
 #define HB_SCRIPT_CANADIAN_ABORIGINAL		HB_SCRIPT_CANADIAN_SYLLABICS
 
+/**
+ * HB_BUFFER_FLAGS_DEFAULT:
+ *
+ * Use #HB_BUFFER_FLAG_DEFAULT instead.
+ *
+ * Deprecated: 0.9.20
+ */
 #define HB_BUFFER_FLAGS_DEFAULT			HB_BUFFER_FLAG_DEFAULT
+/**
+ * HB_BUFFER_SERIALIZE_FLAGS_DEFAULT:
+ *
+ * Use #HB_BUFFER_SERIALIZE_FLAG_DEFAULT instead.
+ *
+ * Deprecated: 0.9.20
+ */
 #define HB_BUFFER_SERIALIZE_FLAGS_DEFAULT	HB_BUFFER_SERIALIZE_FLAG_DEFAULT
 
+/**
+ * hb_font_get_glyph_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @unicode: The Unicode code point to query
+ * @variation_selector: The  variation-selector code point to query
+ * @glyph: (out): The glyph ID retrieved
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the glyph ID for a specified Unicode code point
+ * font, with an optional variation selector.
+ *
+ * Return value: %true if data found, %false otherwise
+ * Deprecated: 1.2.3
+ *
+ **/
 typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data,
 					       hb_codepoint_t unicode, hb_codepoint_t variation_selector,
 					       hb_codepoint_t *glyph,
@@ -73,6 +112,11 @@
 
 /**
  * hb_unicode_eastasian_width_func_t:
+ * @ufuncs: A Unicode-functions structure
+ * @unicode: The code point to query
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_unicode_funcs_t structure.
  *
  * Deprecated: 2.0.0
  */
@@ -82,12 +126,12 @@
 
 /**
  * hb_unicode_funcs_set_eastasian_width_func:
- * @ufuncs: a Unicode function structure
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ufuncs: a Unicode-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- * 
+ * Sets the implementation function for #hb_unicode_eastasian_width_func_t.
  *
  * Since: 0.9.2
  * Deprecated: 2.0.0
@@ -99,6 +143,10 @@
 
 /**
  * hb_unicode_eastasian_width:
+ * @ufuncs: a Unicode-function structure
+ * @unicode: The code point to query
+ *
+ * Don't use. Not used by HarfBuzz.
  *
  * Since: 0.9.2
  * Deprecated: 2.0.0
@@ -112,7 +160,7 @@
  * hb_unicode_decompose_compatibility_func_t:
  * @ufuncs: a Unicode function structure
  * @u: codepoint to decompose
- * @decomposed: address of codepoint array (of length %HB_UNICODE_MAX_DECOMPOSITION_LEN) to write decomposition into
+ * @decomposed: address of codepoint array (of length #HB_UNICODE_MAX_DECOMPOSITION_LEN) to write decomposition into
  * @user_data: user data pointer as passed to hb_unicode_funcs_set_decompose_compatibility_func()
  *
  * Fully decompose @u to its Unicode compatibility decomposition. The codepoints of the decomposition will be written to @decomposed.
@@ -120,7 +168,7 @@
  *
  * If @u has no compatibility decomposition, zero should be returned.
  *
- * The Unicode standard guarantees that a buffer of length %HB_UNICODE_MAX_DECOMPOSITION_LEN codepoints will always be sufficient for any
+ * The Unicode standard guarantees that a buffer of length #HB_UNICODE_MAX_DECOMPOSITION_LEN codepoints will always be sufficient for any
  * compatibility decomposition plus an terminating value of 0.  Consequently, @decompose must be allocated by the caller to be at least this length.  Implementations
  * of this function type must ensure that they do not write past the provided array.
  *
@@ -144,10 +192,12 @@
 
 /**
  * hb_unicode_funcs_set_decompose_compatibility_func:
- * @ufuncs: a Unicode function structure
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ufuncs: A Unicode-functions structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
+ *
+ * Sets the implementation function for #hb_unicode_decompose_compatibility_func_t.
  *
  * 
  *
@@ -165,16 +215,25 @@
 				    hb_codepoint_t     *decomposed);
 
 
+/**
+ * hb_font_get_glyph_v_kerning_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the kerning-adjustment value for a glyph-pair in
+ * the specified font, for vertical text segments.
+ *
+ **/
 typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t;
 
 /**
  * hb_font_funcs_set_glyph_v_kerning_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- * 
+ * Sets the implementation function for #hb_font_get_glyph_v_kerning_func_t.
  *
  * Since: 0.9.2
  * Deprecated: 2.0.0
diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc
index f2fce07..a073021 100644
--- a/src/hb-directwrite.cc
+++ b/src/hb-directwrite.cc
@@ -33,6 +33,15 @@
 #include "hb-directwrite.h"
 
 
+/**
+ * SECTION:hb-directwrite
+ * @title: hb-directwrite
+ * @short_description: DirectWrite integration
+ * @include: hb-directwrite.h
+ *
+ * Functions for using HarfBuzz with DirectWrite fonts.
+ **/
+
 /* Declare object creator for dynamic support of DWRITE */
 typedef HRESULT (* WINAPI t_DWriteCreateFactory)(
   DWRITE_FACTORY_TYPE factoryType,
@@ -948,6 +957,8 @@
  * hb_directwrite_face_create:
  * @font_face: a DirectWrite IDWriteFontFace object.
  *
+ * Constructs a new face object from the specified DirectWrite IDWriteFontFace.
+ *
  * Return value: #hb_face_t object corresponding to the given input
  *
  * Since: 2.4.0
@@ -965,6 +976,8 @@
 * hb_directwrite_face_get_font_face:
 * @face: a #hb_face_t object
 *
+* Gets the DirectWrite IDWriteFontFace associated with @face.
+*
 * Return value: DirectWrite IDWriteFontFace object corresponding to the given input
 *
 * Since: 2.5.0
diff --git a/src/hb-draw.h b/src/hb-draw.h
index 98eccf4..bddc876 100644
--- a/src/hb-draw.h
+++ b/src/hb-draw.h
@@ -22,7 +22,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
diff --git a/src/hb-face.cc b/src/hb-face.cc
index 848c4ff..a15687c 100644
--- a/src/hb-face.cc
+++ b/src/hb-face.cc
@@ -100,7 +100,7 @@
  * hb_face_create_for_tables:
  * @reference_table_func: (closure user_data) (destroy destroy) (scope notified): Table-referencing function
  * @user_data: A pointer to the user data
- * @destroy: (optional): A callback to call when @data is not needed anymore
+ * @destroy: (nullable): A callback to call when @data is not needed anymore
  *
  * Variant of hb_face_create(), built for those cases where it is more
  * convenient to provide data for individual tables instead of the whole font
@@ -235,7 +235,7 @@
  *
  * Fetches the singleton empty face object.
  *
- * Return value: (transfer full) The empty face object
+ * Return value: (transfer full): The empty face object
  *
  * Since: 0.9.2
  **/
@@ -299,12 +299,12 @@
  * @face: A face object
  * @key: The user-data key to set
  * @data: A pointer to the user data
- * @destroy: (optional): A callback to call when @data is not needed anymore
+ * @destroy: (nullable): A callback to call when @data is not needed anymore
  * @replace: Whether to replace an existing data with the same key
  *
  * Attaches a user-data key/data pair to the given face object. 
  *
- * Return value: %true if success, false otherwise
+ * Return value: %true if success, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -360,7 +360,7 @@
  *
  * Tests whether the given face object is immutable.
  *
- * Return value: True is @face is immutable, false otherwise
+ * Return value: %true is @face is immutable, %false otherwise
  *
  * Since: 0.9.2
  **/
diff --git a/src/hb-face.h b/src/hb-face.h
index 3b18f7e..6ef2f8b 100644
--- a/src/hb-face.h
+++ b/src/hb-face.h
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
@@ -58,6 +58,19 @@
 hb_face_create (hb_blob_t    *blob,
 		unsigned int  index);
 
+/**
+ * hb_reference_table_func_t:
+ * @face: an #hb_face_t to reference table for
+ * @tag: the tag of the table to reference
+ * @user_data: User data pointer passed by the caller
+ *
+ * Callback function for hb_face_create_for_tables().
+ *
+ * Return value: (transfer full): A pointer to the @tag table within @face
+ *
+ * Since: 0.9.2
+ */
+
 typedef hb_blob_t * (*hb_reference_table_func_t)  (hb_face_t *face, hb_tag_t tag, void *user_data);
 
 /* calls destroy() when not needing user_data anymore */
diff --git a/src/hb-font.cc b/src/hb-font.cc
index 2795948..37a0e7f 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -43,10 +43,20 @@
  * @short_description: Font objects
  * @include: hb.h
  *
- * Font objects represent a font face at a certain size and other
- * parameters (pixels per EM, points per EM, variation settings.)
- * Fonts are created from font faces, and are used as input to
- * hb_shape() among other things.
+ * Functions for working with font objects.
+ *
+ * A font object represents a font face at a specific size and with
+ * certain other parameters (pixels-per-em, points-per-em, variation
+ * settings) specified. Font objects are created from font face
+ * objects, and are used as input to hb_shape(), among other things.
+ *
+ * Client programs can optionally pass in their own functions that
+ * implement the basic, lower-level queries of font objects. This set
+ * of font functions is defined by the virtual methods in
+ * #hb_font_funcs_t.
+ *
+ * HarfBuzz provides a built-in set of lightweight default
+ * functions for each method in #hb_font_funcs_t.
  **/
 
 
@@ -55,19 +65,20 @@
  */
 
 static hb_bool_t
-hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED,
-				void *font_data HB_UNUSED,
+hb_font_get_font_h_extents_nil (hb_font_t         *font HB_UNUSED,
+				void              *font_data HB_UNUSED,
 				hb_font_extents_t *extents,
-				void *user_data HB_UNUSED)
+				void              *user_data HB_UNUSED)
 {
   memset (extents, 0, sizeof (*extents));
   return false;
 }
+
 static hb_bool_t
-hb_font_get_font_h_extents_default (hb_font_t *font,
-				    void *font_data HB_UNUSED,
+hb_font_get_font_h_extents_default (hb_font_t         *font,
+				    void              *font_data HB_UNUSED,
 				    hb_font_extents_t *extents,
-				    void *user_data HB_UNUSED)
+				    void              *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_font_h_extents (extents);
   if (ret) {
@@ -79,19 +90,20 @@
 }
 
 static hb_bool_t
-hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED,
-				void *font_data HB_UNUSED,
+hb_font_get_font_v_extents_nil (hb_font_t         *font HB_UNUSED,
+				void              *font_data HB_UNUSED,
 				hb_font_extents_t *extents,
-				void *user_data HB_UNUSED)
+				void              *user_data HB_UNUSED)
 {
   memset (extents, 0, sizeof (*extents));
   return false;
 }
+
 static hb_bool_t
-hb_font_get_font_v_extents_default (hb_font_t *font,
-				    void *font_data HB_UNUSED,
+hb_font_get_font_v_extents_default (hb_font_t         *font,
+				    void              *font_data HB_UNUSED,
 				    hb_font_extents_t *extents,
-				    void *user_data HB_UNUSED)
+				    void              *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_font_v_extents (extents);
   if (ret) {
@@ -103,21 +115,22 @@
 }
 
 static hb_bool_t
-hb_font_get_nominal_glyph_nil (hb_font_t *font HB_UNUSED,
-			       void *font_data HB_UNUSED,
-			       hb_codepoint_t unicode HB_UNUSED,
+hb_font_get_nominal_glyph_nil (hb_font_t      *font HB_UNUSED,
+			       void           *font_data HB_UNUSED,
+			       hb_codepoint_t  unicode HB_UNUSED,
 			       hb_codepoint_t *glyph,
-			       void *user_data HB_UNUSED)
+			       void           *user_data HB_UNUSED)
 {
   *glyph = 0;
   return false;
 }
+
 static hb_bool_t
-hb_font_get_nominal_glyph_default (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_codepoint_t unicode,
+hb_font_get_nominal_glyph_default (hb_font_t      *font,
+				   void           *font_data HB_UNUSED,
+				   hb_codepoint_t  unicode,
 				   hb_codepoint_t *glyph,
-				   void *user_data HB_UNUSED)
+				   void           *user_data HB_UNUSED)
 {
   if (font->has_nominal_glyphs_func_set ())
   {
@@ -127,15 +140,16 @@
 }
 
 #define hb_font_get_nominal_glyphs_nil hb_font_get_nominal_glyphs_default
+
 static unsigned int
-hb_font_get_nominal_glyphs_default (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    unsigned int count,
+hb_font_get_nominal_glyphs_default (hb_font_t            *font,
+				    void                 *font_data HB_UNUSED,
+				    unsigned int          count,
 				    const hb_codepoint_t *first_unicode,
-				    unsigned int unicode_stride,
-				    hb_codepoint_t *first_glyph,
-				    unsigned int glyph_stride,
-				    void *user_data HB_UNUSED)
+				    unsigned int          unicode_stride,
+				    hb_codepoint_t       *first_glyph,
+				    unsigned int          glyph_stride,
+				    void                 *user_data HB_UNUSED)
 {
   if (font->has_nominal_glyph_func_set ())
   {
@@ -156,41 +170,43 @@
 }
 
 static hb_bool_t
-hb_font_get_variation_glyph_nil (hb_font_t *font HB_UNUSED,
-				 void *font_data HB_UNUSED,
-				 hb_codepoint_t unicode HB_UNUSED,
-				 hb_codepoint_t variation_selector HB_UNUSED,
+hb_font_get_variation_glyph_nil (hb_font_t      *font HB_UNUSED,
+				 void           *font_data HB_UNUSED,
+				 hb_codepoint_t  unicode HB_UNUSED,
+				 hb_codepoint_t  variation_selector HB_UNUSED,
 				 hb_codepoint_t *glyph,
-				 void *user_data HB_UNUSED)
+				 void           *user_data HB_UNUSED)
 {
   *glyph = 0;
   return false;
 }
+
 static hb_bool_t
-hb_font_get_variation_glyph_default (hb_font_t *font,
-				     void *font_data HB_UNUSED,
-				     hb_codepoint_t unicode,
-				     hb_codepoint_t variation_selector,
+hb_font_get_variation_glyph_default (hb_font_t      *font,
+				     void           *font_data HB_UNUSED,
+				     hb_codepoint_t  unicode,
+				     hb_codepoint_t  variation_selector,
 				     hb_codepoint_t *glyph,
-				     void *user_data HB_UNUSED)
+				     void           *user_data HB_UNUSED)
 {
   return font->parent->get_variation_glyph (unicode, variation_selector, glyph);
 }
 
 
 static hb_position_t
-hb_font_get_glyph_h_advance_nil (hb_font_t *font,
-				 void *font_data HB_UNUSED,
-				 hb_codepoint_t glyph HB_UNUSED,
-				 void *user_data HB_UNUSED)
+hb_font_get_glyph_h_advance_nil (hb_font_t      *font,
+				 void           *font_data HB_UNUSED,
+				 hb_codepoint_t  glyph HB_UNUSED,
+				 void           *user_data HB_UNUSED)
 {
   return font->x_scale;
 }
+
 static hb_position_t
-hb_font_get_glyph_h_advance_default (hb_font_t *font,
-				     void *font_data HB_UNUSED,
-				     hb_codepoint_t glyph,
-				     void *user_data HB_UNUSED)
+hb_font_get_glyph_h_advance_default (hb_font_t      *font,
+				     void           *font_data HB_UNUSED,
+				     hb_codepoint_t  glyph,
+				     void           *user_data HB_UNUSED)
 {
   if (font->has_glyph_h_advances_func_set ())
   {
@@ -202,19 +218,20 @@
 }
 
 static hb_position_t
-hb_font_get_glyph_v_advance_nil (hb_font_t *font,
-				 void *font_data HB_UNUSED,
-				 hb_codepoint_t glyph HB_UNUSED,
-				 void *user_data HB_UNUSED)
+hb_font_get_glyph_v_advance_nil (hb_font_t      *font,
+				 void           *font_data HB_UNUSED,
+				 hb_codepoint_t  glyph HB_UNUSED,
+				 void           *user_data HB_UNUSED)
 {
   /* TODO use font_extents.ascender+descender */
   return font->y_scale;
 }
+
 static hb_position_t
-hb_font_get_glyph_v_advance_default (hb_font_t *font,
-				     void *font_data HB_UNUSED,
-				     hb_codepoint_t glyph,
-				     void *user_data HB_UNUSED)
+hb_font_get_glyph_v_advance_default (hb_font_t      *font,
+				     void           *font_data HB_UNUSED,
+				     hb_codepoint_t  glyph,
+				     void           *user_data HB_UNUSED)
 {
   if (font->has_glyph_v_advances_func_set ())
   {
@@ -226,15 +243,16 @@
 }
 
 #define hb_font_get_glyph_h_advances_nil hb_font_get_glyph_h_advances_default
+
 static void
-hb_font_get_glyph_h_advances_default (hb_font_t* font,
-				      void* font_data HB_UNUSED,
-				      unsigned int count,
+hb_font_get_glyph_h_advances_default (hb_font_t*            font,
+				      void*                 font_data HB_UNUSED,
+				      unsigned int          count,
 				      const hb_codepoint_t *first_glyph,
-				      unsigned int glyph_stride,
-				      hb_position_t *first_advance,
-				      unsigned int advance_stride,
-				      void *user_data HB_UNUSED)
+				      unsigned int          glyph_stride,
+				      hb_position_t        *first_advance,
+				      unsigned int          advance_stride,
+				      void                 *user_data HB_UNUSED)
 {
   if (font->has_glyph_h_advance_func_set ())
   {
@@ -259,14 +277,14 @@
 
 #define hb_font_get_glyph_v_advances_nil hb_font_get_glyph_v_advances_default
 static void
-hb_font_get_glyph_v_advances_default (hb_font_t* font,
-				      void* font_data HB_UNUSED,
-				      unsigned int count,
+hb_font_get_glyph_v_advances_default (hb_font_t*            font,
+				      void*                 font_data HB_UNUSED,
+				      unsigned int          count,
 				      const hb_codepoint_t *first_glyph,
-				      unsigned int glyph_stride,
-				      hb_position_t *first_advance,
-				      unsigned int advance_stride,
-				      void *user_data HB_UNUSED)
+				      unsigned int          glyph_stride,
+				      hb_position_t        *first_advance,
+				      unsigned int          advance_stride,
+				      void                 *user_data HB_UNUSED)
 {
   if (font->has_glyph_v_advance_func_set ())
   {
@@ -290,23 +308,24 @@
 }
 
 static hb_bool_t
-hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED,
-				void *font_data HB_UNUSED,
-				hb_codepoint_t glyph HB_UNUSED,
-				hb_position_t *x,
-				hb_position_t *y,
-				void *user_data HB_UNUSED)
+hb_font_get_glyph_h_origin_nil (hb_font_t      *font HB_UNUSED,
+				void           *font_data HB_UNUSED,
+				hb_codepoint_t  glyph HB_UNUSED,
+				hb_position_t  *x,
+				hb_position_t  *y,
+				void           *user_data HB_UNUSED)
 {
   *x = *y = 0;
   return true;
 }
+
 static hb_bool_t
-hb_font_get_glyph_h_origin_default (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t glyph,
-				    hb_position_t *x,
-				    hb_position_t *y,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_h_origin_default (hb_font_t      *font,
+				    void           *font_data HB_UNUSED,
+				    hb_codepoint_t  glyph,
+				    hb_position_t  *x,
+				    hb_position_t  *y,
+				    void           *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y);
   if (ret)
@@ -315,23 +334,24 @@
 }
 
 static hb_bool_t
-hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED,
-				void *font_data HB_UNUSED,
-				hb_codepoint_t glyph HB_UNUSED,
-				hb_position_t *x,
-				hb_position_t *y,
-				void *user_data HB_UNUSED)
+hb_font_get_glyph_v_origin_nil (hb_font_t      *font HB_UNUSED,
+				void           *font_data HB_UNUSED,
+				hb_codepoint_t  glyph HB_UNUSED,
+				hb_position_t  *x,
+				hb_position_t  *y,
+				void           *user_data HB_UNUSED)
 {
   *x = *y = 0;
   return false;
 }
+
 static hb_bool_t
-hb_font_get_glyph_v_origin_default (hb_font_t *font,
-				    void *font_data HB_UNUSED,
-				    hb_codepoint_t glyph,
-				    hb_position_t *x,
-				    hb_position_t *y,
-				    void *user_data HB_UNUSED)
+hb_font_get_glyph_v_origin_default (hb_font_t      *font,
+				    void           *font_data HB_UNUSED,
+				    hb_codepoint_t  glyph,
+				    hb_position_t  *x,
+				    hb_position_t  *y,
+				    void           *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y);
   if (ret)
@@ -340,61 +360,64 @@
 }
 
 static hb_position_t
-hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED,
-				 void *font_data HB_UNUSED,
-				 hb_codepoint_t left_glyph HB_UNUSED,
-				 hb_codepoint_t right_glyph HB_UNUSED,
-				 void *user_data HB_UNUSED)
+hb_font_get_glyph_h_kerning_nil (hb_font_t      *font HB_UNUSED,
+				 void           *font_data HB_UNUSED,
+				 hb_codepoint_t  left_glyph HB_UNUSED,
+				 hb_codepoint_t  right_glyph HB_UNUSED,
+				 void           *user_data HB_UNUSED)
 {
   return 0;
 }
+
 static hb_position_t
-hb_font_get_glyph_h_kerning_default (hb_font_t *font,
-				     void *font_data HB_UNUSED,
-				     hb_codepoint_t left_glyph,
-				     hb_codepoint_t right_glyph,
-				     void *user_data HB_UNUSED)
+hb_font_get_glyph_h_kerning_default (hb_font_t      *font,
+				     void           *font_data HB_UNUSED,
+				     hb_codepoint_t  left_glyph,
+				     hb_codepoint_t  right_glyph,
+				     void           *user_data HB_UNUSED)
 {
   return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph));
 }
 
 #ifndef HB_DISABLE_DEPRECATED
 static hb_position_t
-hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED,
-				 void *font_data HB_UNUSED,
-				 hb_codepoint_t top_glyph HB_UNUSED,
-				 hb_codepoint_t bottom_glyph HB_UNUSED,
-				 void *user_data HB_UNUSED)
+hb_font_get_glyph_v_kerning_nil (hb_font_t      *font HB_UNUSED,
+				 void           *font_data HB_UNUSED,
+				 hb_codepoint_t  top_glyph HB_UNUSED,
+				 hb_codepoint_t  bottom_glyph HB_UNUSED,
+				 void           *user_data HB_UNUSED)
 {
   return 0;
 }
+
 static hb_position_t
-hb_font_get_glyph_v_kerning_default (hb_font_t *font,
-				     void *font_data HB_UNUSED,
-				     hb_codepoint_t top_glyph,
-				     hb_codepoint_t bottom_glyph,
-				     void *user_data HB_UNUSED)
+hb_font_get_glyph_v_kerning_default (hb_font_t      *font,
+				     void           *font_data HB_UNUSED,
+				     hb_codepoint_t  top_glyph,
+				     hb_codepoint_t  bottom_glyph,
+				     void           *user_data HB_UNUSED)
 {
   return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph));
 }
 #endif
 
 static hb_bool_t
-hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED,
-			       void *font_data HB_UNUSED,
-			       hb_codepoint_t glyph HB_UNUSED,
+hb_font_get_glyph_extents_nil (hb_font_t          *font HB_UNUSED,
+			       void               *font_data HB_UNUSED,
+			       hb_codepoint_t      glyph HB_UNUSED,
 			       hb_glyph_extents_t *extents,
-			       void *user_data HB_UNUSED)
+			       void               *user_data HB_UNUSED)
 {
   memset (extents, 0, sizeof (*extents));
   return false;
 }
+
 static hb_bool_t
-hb_font_get_glyph_extents_default (hb_font_t *font,
-				   void *font_data HB_UNUSED,
-				   hb_codepoint_t glyph,
+hb_font_get_glyph_extents_default (hb_font_t          *font,
+				   void               *font_data HB_UNUSED,
+				   hb_codepoint_t      glyph,
 				   hb_glyph_extents_t *extents,
-				   void *user_data HB_UNUSED)
+				   void               *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents);
   if (ret) {
@@ -405,25 +428,26 @@
 }
 
 static hb_bool_t
-hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED,
-				     void *font_data HB_UNUSED,
-				     hb_codepoint_t glyph HB_UNUSED,
-				     unsigned int point_index HB_UNUSED,
-				     hb_position_t *x,
-				     hb_position_t *y,
-				     void *user_data HB_UNUSED)
+hb_font_get_glyph_contour_point_nil (hb_font_t      *font HB_UNUSED,
+				     void           *font_data HB_UNUSED,
+				     hb_codepoint_t  glyph HB_UNUSED,
+				     unsigned int    point_index HB_UNUSED,
+				     hb_position_t  *x,
+				     hb_position_t  *y,
+				     void           *user_data HB_UNUSED)
 {
   *x = *y = 0;
   return false;
 }
+
 static hb_bool_t
-hb_font_get_glyph_contour_point_default (hb_font_t *font,
-					 void *font_data HB_UNUSED,
-					 hb_codepoint_t glyph,
-					 unsigned int point_index,
-					 hb_position_t *x,
-					 hb_position_t *y,
-					 void *user_data HB_UNUSED)
+hb_font_get_glyph_contour_point_default (hb_font_t      *font,
+					 void           *font_data HB_UNUSED,
+					 hb_codepoint_t  glyph,
+					 unsigned int    point_index,
+					 hb_position_t  *x,
+					 hb_position_t  *y,
+					 void           *user_data HB_UNUSED)
 {
   hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y);
   if (ret)
@@ -432,42 +456,47 @@
 }
 
 static hb_bool_t
-hb_font_get_glyph_name_nil (hb_font_t *font HB_UNUSED,
-			    void *font_data HB_UNUSED,
-			    hb_codepoint_t glyph HB_UNUSED,
-			    char *name, unsigned int size,
-			    void *user_data HB_UNUSED)
+hb_font_get_glyph_name_nil (hb_font_t      *font HB_UNUSED,
+			    void           *font_data HB_UNUSED,
+			    hb_codepoint_t  glyph HB_UNUSED,
+			    char           *name,
+			    unsigned int    size,
+			    void           *user_data HB_UNUSED)
 {
   if (size) *name = '\0';
   return false;
 }
+
 static hb_bool_t
-hb_font_get_glyph_name_default (hb_font_t *font,
-				void *font_data HB_UNUSED,
-				hb_codepoint_t glyph,
-				char *name, unsigned int size,
-				void *user_data HB_UNUSED)
+hb_font_get_glyph_name_default (hb_font_t      *font,
+				void           *font_data HB_UNUSED,
+				hb_codepoint_t  glyph,
+				char           *name,
+				unsigned int    size,
+				void           *user_data HB_UNUSED)
 {
   return font->parent->get_glyph_name (glyph, name, size);
 }
 
 static hb_bool_t
-hb_font_get_glyph_from_name_nil (hb_font_t *font HB_UNUSED,
-				 void *font_data HB_UNUSED,
-				 const char *name HB_UNUSED,
-				 int len HB_UNUSED, /* -1 means nul-terminated */
+hb_font_get_glyph_from_name_nil (hb_font_t      *font HB_UNUSED,
+				 void           *font_data HB_UNUSED,
+				 const char     *name HB_UNUSED,
+				 int             len HB_UNUSED, /* -1 means nul-terminated */
 				 hb_codepoint_t *glyph,
-				 void *user_data HB_UNUSED)
+				 void           *user_data HB_UNUSED)
 {
   *glyph = 0;
   return false;
 }
+
 static hb_bool_t
-hb_font_get_glyph_from_name_default (hb_font_t *font,
-				     void *font_data HB_UNUSED,
-				     const char *name, int len, /* -1 means nul-terminated */
+hb_font_get_glyph_from_name_default (hb_font_t      *font,
+				     void           *font_data HB_UNUSED,
+				     const char     *name,
+				     int             len, /* -1 means nul-terminated */
 				     hb_codepoint_t *glyph,
-				     void *user_data HB_UNUSED)
+				     void           *user_data HB_UNUSED)
 {
   return font->parent->get_glyph_from_name (name, len, glyph);
 }
@@ -521,9 +550,9 @@
 /**
  * hb_font_funcs_create: (Xconstructor)
  *
+ * Creates a new #hb_font_funcs_t structure of font functions.
  *
- *
- * Return value: (transfer full):
+ * Return value: (transfer full): The font-functions structure
  *
  * Since: 0.9.2
  **/
@@ -543,9 +572,9 @@
 /**
  * hb_font_funcs_get_empty:
  *
+ * Fetches an empty font-functions structure.
  *
- *
- * Return value: (transfer full):
+ * Return value: (transfer full): The font-functions structure
  *
  * Since: 0.9.2
  **/
@@ -557,11 +586,11 @@
 
 /**
  * hb_font_funcs_reference: (skip)
- * @ffuncs: font functions.
+ * @ffuncs: The font-functions structure
  *
+ * Increases the reference count on a font-functions structure.
  *
- *
- * Return value:
+ * Return value: The font-functions structure
  *
  * Since: 0.9.2
  **/
@@ -573,9 +602,11 @@
 
 /**
  * hb_font_funcs_destroy: (skip)
- * @ffuncs: font functions.
+ * @ffuncs: The font-functions structure
  *
- *
+ * Decreases the reference count on a font-functions structure. When
+ * the reference count reaches zero, the font-functions structure is
+ * destroyed, freeing all memory.
  *
  * Since: 0.9.2
  **/
@@ -594,15 +625,15 @@
 
 /**
  * hb_font_funcs_set_user_data: (skip)
- * @ffuncs: font functions.
- * @key:
- * @data:
- * @destroy:
- * @replace:
+ * @ffuncs: The font-functions structure
+ * @key: The user-data key to set
+ * @data: A pointer to the user data set
+ * @destroy: (nullable): A callback to call when @data is not needed anymore
+ * @replace: Whether to replace an existing data with the same key
  *
+ * Attaches a user-data key/data pair to the specified font-functions structure. 
  *
- *
- * Return value:
+ * Return value: %true if success, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -610,7 +641,7 @@
 hb_font_funcs_set_user_data (hb_font_funcs_t    *ffuncs,
 			     hb_user_data_key_t *key,
 			     void *              data,
-			     hb_destroy_func_t   destroy,
+			     hb_destroy_func_t   destroy /* May be NULL. */,
 			     hb_bool_t           replace)
 {
   return hb_object_set_user_data (ffuncs, key, data, destroy, replace);
@@ -618,12 +649,13 @@
 
 /**
  * hb_font_funcs_get_user_data: (skip)
- * @ffuncs: font functions.
- * @key:
+ * @ffuncs: The font-functions structure
+ * @key: The user-data key to query
  *
+ * Fetches the user data associated with the specified key,
+ * attached to the specified font-functions structure.
  *
- *
- * Return value: (transfer none):
+ * Return value: (transfer none): A pointer to the user data
  *
  * Since: 0.9.2
  **/
@@ -637,9 +669,9 @@
 
 /**
  * hb_font_funcs_make_immutable:
- * @ffuncs: font functions.
+ * @ffuncs: The font-functions structure
  *
- *
+ * Makes a font-functions structure immutable.
  *
  * Since: 0.9.2
  **/
@@ -654,11 +686,11 @@
 
 /**
  * hb_font_funcs_is_immutable:
- * @ffuncs: font functions.
+ * @ffuncs: The font-functions structure
  *
+ * Tests whether a font-functions structure is immutable.
  *
- *
- * Return value:
+ * Return value: %true if @ffuncs is immutable, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -718,17 +750,18 @@
 
 /**
  * hb_font_get_h_extents:
- * @font: a font.
- * @extents: (out):
+ * @font: #hb_font_t to work upon
+ * @extents: (out): The font extents retrieved
  *
+ * Fetches the extents for a specified font, for horizontal
+ * text segments.
  *
- *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 1.1.3
  **/
 hb_bool_t
-hb_font_get_h_extents (hb_font_t *font,
+hb_font_get_h_extents (hb_font_t         *font,
 		       hb_font_extents_t *extents)
 {
   return font->get_font_h_extents (extents);
@@ -736,17 +769,18 @@
 
 /**
  * hb_font_get_v_extents:
- * @font: a font.
- * @extents: (out):
+ * @font: #hb_font_t to work upon
+ * @extents: (out): The font extents retrieved
  *
+ * Fetches the extents for a specified font, for vertical
+ * text segments.
  *
- *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 1.1.3
  **/
 hb_bool_t
-hb_font_get_v_extents (hb_font_t *font,
+hb_font_get_v_extents (hb_font_t         *font,
 		       hb_font_extents_t *extents)
 {
   return font->get_font_v_extents (extents);
@@ -754,20 +788,25 @@
 
 /**
  * hb_font_get_glyph:
- * @font: a font.
- * @unicode:
- * @variation_selector:
- * @glyph: (out):
+ * @font: #hb_font_t to work upon
+ * @unicode: The Unicode code point to query
+ * @variation_selector: A variation-selector code point
+ * @glyph: (out): The glyph ID retrieved
  *
+ * Fetches the glyph ID for a Unicode code point in the specified
+ * font, with an optional variation selector.
  *
+ * If @variation_selector is 0, calls hb_font_get_nominal_glyph();
+ * otherwise calls hb_font_get_variation_glyph().
  *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_font_get_glyph (hb_font_t *font,
-		   hb_codepoint_t unicode, hb_codepoint_t variation_selector,
+hb_font_get_glyph (hb_font_t      *font,
+		   hb_codepoint_t  unicode,
+		   hb_codepoint_t  variation_selector,
 		   hb_codepoint_t *glyph)
 {
   if (unlikely (variation_selector))
@@ -777,19 +816,24 @@
 
 /**
  * hb_font_get_nominal_glyph:
- * @font: a font.
- * @unicode:
- * @glyph: (out):
+ * @font: #hb_font_t to work upon
+ * @unicode: The Unicode code point to query
+ * @glyph: (out): The glyph ID retrieved
  *
+ * Fetches the nominal glyph ID for a Unicode code point in the
+ * specified font. 
  *
+ * This version of the function should not be used to fetch glyph IDs
+ * for code points modified by variation selectors. For variation-selector
+ * support, user hb_font_get_variation_glyph() or use hb_font_get_glyph().
  *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 1.2.3
  **/
 hb_bool_t
-hb_font_get_nominal_glyph (hb_font_t *font,
-			   hb_codepoint_t unicode,
+hb_font_get_nominal_glyph (hb_font_t      *font,
+			   hb_codepoint_t  unicode,
 			   hb_codepoint_t *glyph)
 {
   return font->get_nominal_glyph (unicode, glyph);
@@ -797,11 +841,17 @@
 
 /**
  * hb_font_get_nominal_glyphs:
- * @font: a font.
+ * @font: #hb_font_t to work upon
+ * @count: number of code points to query
+ * @first_unicode: The first Unicode code point to query
+ * @unicode_stride: The stride between successive code points
+ * @first_glyph: (out): The first glyph ID retrieved
+ * @glyph_stride: The stride between successive glyph IDs
  *
+ * Fetches the nominal glyph IDs for a sequence of Unicode code points. Glyph
+ * IDs must be returned in a #hb_codepoint_t output parameter.
  *
- *
- * Return value:
+ * Return value: the number of code points processed
  *
  * Since: 2.6.3
  **/
@@ -820,20 +870,23 @@
 
 /**
  * hb_font_get_variation_glyph:
- * @font: a font.
- * @unicode:
- * @variation_selector:
- * @glyph: (out):
+ * @font: #hb_font_t to work upon
+ * @unicode: The Unicode code point to query
+ * @variation_selector: The  variation-selector code point to query
+ * @glyph: (out): The glyph ID retrieved
  *
+ * Fetches the glyph ID for a Unicode code point when followed by
+ * by the specified variation-selector code point, in the specified
+ * font.
  *
- *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 1.2.3
  **/
 hb_bool_t
-hb_font_get_variation_glyph (hb_font_t *font,
-			     hb_codepoint_t unicode, hb_codepoint_t variation_selector,
+hb_font_get_variation_glyph (hb_font_t      *font,
+			     hb_codepoint_t  unicode,
+			     hb_codepoint_t  variation_selector,
 			     hb_codepoint_t *glyph)
 {
   return font->get_variation_glyph (unicode, variation_selector, glyph);
@@ -841,134 +894,157 @@
 
 /**
  * hb_font_get_glyph_h_advance:
- * @font: a font.
- * @glyph:
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
  *
+ * Fetches the advance for a glyph ID in the specified font,
+ * for horizontal text segments.
  *
- *
- * Return value:
+ * Return value: The advance of @glyph within @font
  *
  * Since: 0.9.2
  **/
 hb_position_t
-hb_font_get_glyph_h_advance (hb_font_t *font,
-			     hb_codepoint_t glyph)
+hb_font_get_glyph_h_advance (hb_font_t      *font,
+			     hb_codepoint_t  glyph)
 {
   return font->get_glyph_h_advance (glyph);
 }
 
 /**
  * hb_font_get_glyph_v_advance:
- * @font: a font.
- * @glyph:
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
  *
+ * Fetches the advance for a glyph ID in the specified font,
+ * for vertical text segments.
  *
- *
- * Return value:
+ * Return value: The advance of @glyph within @font
  *
  * Since: 0.9.2
  **/
 hb_position_t
-hb_font_get_glyph_v_advance (hb_font_t *font,
-			     hb_codepoint_t glyph)
+hb_font_get_glyph_v_advance (hb_font_t      *font,
+			     hb_codepoint_t  glyph)
 {
   return font->get_glyph_v_advance (glyph);
 }
 
 /**
  * hb_font_get_glyph_h_advances:
- * @font: a font.
+ * @font: #hb_font_t to work upon
+ * @count: The number of glyph IDs in the sequence queried
+ * @first_glyph: The first glyph ID to query
+ * @glyph_stride: The stride between successive glyph IDs
+ * @first_advance: (out): The first advance retrieved
+ * @advance_stride: The stride between successive advances
  *
- *
+ * Fetches the advances for a sequence of glyph IDs in the specified
+ * font, for horizontal text segments. 
  *
  * Since: 1.8.6
  **/
 void
-hb_font_get_glyph_h_advances (hb_font_t* font,
-			      unsigned int count,
+hb_font_get_glyph_h_advances (hb_font_t*            font,
+			      unsigned int          count,
 			      const hb_codepoint_t *first_glyph,
-			      unsigned glyph_stride,
-			      hb_position_t *first_advance,
-			      unsigned advance_stride)
+			      unsigned              glyph_stride,
+			      hb_position_t        *first_advance,
+			      unsigned              advance_stride)
 {
   font->get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
 }
 /**
  * hb_font_get_glyph_v_advances:
- * @font: a font.
+ * @font: #hb_font_t to work upon
+ * @count: The number of glyph IDs in the sequence queried
+ * @first_glyph: The first glyph ID to query
+ * @glyph_stride: The stride between successive glyph IDs
+ * @first_advance: (out): The first advance retrieved
+ * @advance_stride: (out): The stride between successive advances
  *
- *
+ * Fetches the advances for a sequence of glyph IDs in the specified
+ * font, for vertical text segments.  
  *
  * Since: 1.8.6
  **/
 void
-hb_font_get_glyph_v_advances (hb_font_t* font,
-			      unsigned int count,
+hb_font_get_glyph_v_advances (hb_font_t*            font,
+			      unsigned int          count,
 			      const hb_codepoint_t *first_glyph,
-			      unsigned glyph_stride,
-			      hb_position_t *first_advance,
-			      unsigned advance_stride)
+			      unsigned              glyph_stride,
+			      hb_position_t        *first_advance,
+			      unsigned              advance_stride)
 {
   font->get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
 }
 
 /**
  * hb_font_get_glyph_h_origin:
- * @font: a font.
- * @glyph:
- * @x: (out):
- * @y: (out):
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @x: (out): The X coordinate of the origin
+ * @y: (out): The Y coordinate of the origin
  *
+ * Fetches the (X,Y) coordinates of the origin for a glyph ID
+ * in the specified font, for horizontal text segments.
  *
- *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_font_get_glyph_h_origin (hb_font_t *font,
-			    hb_codepoint_t glyph,
-			    hb_position_t *x, hb_position_t *y)
+hb_font_get_glyph_h_origin (hb_font_t      *font,
+			    hb_codepoint_t  glyph,
+			    hb_position_t  *x,
+			    hb_position_t  *y)
 {
   return font->get_glyph_h_origin (glyph, x, y);
 }
 
 /**
  * hb_font_get_glyph_v_origin:
- * @font: a font.
- * @glyph:
- * @x: (out):
- * @y: (out):
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @x: (out): The X coordinate of the origin
+ * @y: (out): The Y coordinate of the origin
  *
+ * Fetches the (X,Y) coordinates of the origin for a glyph ID
+ * in the specified font, for vertical text segments.
  *
- *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_font_get_glyph_v_origin (hb_font_t *font,
-			    hb_codepoint_t glyph,
-			    hb_position_t *x, hb_position_t *y)
+hb_font_get_glyph_v_origin (hb_font_t      *font,
+			    hb_codepoint_t  glyph,
+			    hb_position_t  *x,
+			    hb_position_t  *y)
 {
   return font->get_glyph_v_origin (glyph, x, y);
 }
 
 /**
  * hb_font_get_glyph_h_kerning:
- * @font: a font.
- * @left_glyph:
- * @right_glyph:
+ * @font: #hb_font_t to work upon
+ * @left_glyph: The glyph ID of the left glyph in the glyph pair
+ * @right_glyph: The glyph ID of the right glyph in the glyph pair
  *
+ * Fetches the kerning-adjustment value for a glyph-pair in
+ * the specified font, for horizontal text segments.
  *
+ * <note>It handles legacy kerning only (as returned by the corresponding
+ * #hb_font_funcs_t function).</note>
  *
- * Return value:
+ * Return value: The kerning adjustment value
  *
  * Since: 0.9.2
  **/
 hb_position_t
-hb_font_get_glyph_h_kerning (hb_font_t *font,
-			     hb_codepoint_t left_glyph, hb_codepoint_t right_glyph)
+hb_font_get_glyph_h_kerning (hb_font_t      *font,
+			     hb_codepoint_t  left_glyph,
+			     hb_codepoint_t  right_glyph)
 {
   return font->get_glyph_h_kerning (left_glyph, right_glyph);
 }
@@ -976,20 +1052,25 @@
 #ifndef HB_DISABLE_DEPRECATED
 /**
  * hb_font_get_glyph_v_kerning:
- * @font: a font.
- * @top_glyph:
- * @bottom_glyph:
+ * @font: #hb_font_t to work upon
+ * @top_glyph: The glyph ID of the top glyph in the glyph pair
+ * @bottom_glyph: The glyph ID of the bottom glyph in the glyph pair
  *
+ * Fetches the kerning-adjustment value for a glyph-pair in
+ * the specified font, for vertical text segments.
  *
+ * <note>It handles legacy kerning only (as returned by the corresponding
+ * #hb_font_funcs_t function).</note>
  *
- * Return value:
+ * Return value: The kerning adjustment value
  *
  * Since: 0.9.2
  * Deprecated: 2.0.0
  **/
 hb_position_t
-hb_font_get_glyph_v_kerning (hb_font_t *font,
-			     hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph)
+hb_font_get_glyph_v_kerning (hb_font_t      *font,
+			     hb_codepoint_t  top_glyph,
+			     hb_codepoint_t  bottom_glyph)
 {
   return font->get_glyph_v_kerning (top_glyph, bottom_glyph);
 }
@@ -997,19 +1078,20 @@
 
 /**
  * hb_font_get_glyph_extents:
- * @font: a font.
- * @glyph:
- * @extents: (out):
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @extents: (out): The #hb_glyph_extents_t retrieved
  *
+ * Fetches the #hb_glyph_extents_t data for a glyph ID
+ * in the specified font.
  *
- *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_font_get_glyph_extents (hb_font_t *font,
-			   hb_codepoint_t glyph,
+hb_font_get_glyph_extents (hb_font_t          *font,
+			   hb_codepoint_t      glyph,
 			   hb_glyph_extents_t *extents)
 {
   return font->get_glyph_extents (glyph, extents);
@@ -1017,63 +1099,70 @@
 
 /**
  * hb_font_get_glyph_contour_point:
- * @font: a font.
- * @glyph:
- * @point_index:
- * @x: (out):
- * @y: (out):
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @point_index: The contour-point index to query
+ * @x: (out): The X value retrieved for the contour point
+ * @y: (out): The Y value retrieved for the contour point
  *
+ * Fetches the (x,y) coordinates of a specified contour-point index
+ * in the specified glyph, within the specified font.
  *
- *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_font_get_glyph_contour_point (hb_font_t *font,
-				 hb_codepoint_t glyph, unsigned int point_index,
-				 hb_position_t *x, hb_position_t *y)
+hb_font_get_glyph_contour_point (hb_font_t      *font,
+				 hb_codepoint_t  glyph,
+				 unsigned int    point_index,
+				 hb_position_t  *x,
+				 hb_position_t  *y)
 {
   return font->get_glyph_contour_point (glyph, point_index, x, y);
 }
 
 /**
  * hb_font_get_glyph_name:
- * @font: a font.
- * @glyph:
- * @name: (array length=size):
- * @size:
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @name: (out) (array length=size): Name string retrieved for the glyph ID
+ * @size: Length of the glyph-name string retrieved
  *
+ * Fetches the glyph-name string for a glyph ID in the specified @font.
  *
- *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_font_get_glyph_name (hb_font_t *font,
-			hb_codepoint_t glyph,
-			char *name, unsigned int size)
+hb_font_get_glyph_name (hb_font_t      *font,
+			hb_codepoint_t  glyph,
+			char           *name,
+			unsigned int    size)
 {
   return font->get_glyph_name (glyph, name, size);
 }
 
 /**
  * hb_font_get_glyph_from_name:
- * @font: a font.
- * @name: (array length=len):
- * @len:
- * @glyph: (out):
+ * @font: #hb_font_t to work upon
+ * @name: (array length=len): The name string to query
+ * @len: The length of the name queried
+ * @glyph: (out): The glyph ID retrieved
  *
+ * Fetches the glyph ID that corresponds to a name string in the specified @font.
  *
+ * <note>Note: @len == -1 means the name string is null-terminated.</note>
  *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_font_get_glyph_from_name (hb_font_t *font,
-			     const char *name, int len, /* -1 means nul-terminated */
+hb_font_get_glyph_from_name (hb_font_t      *font,
+			     const char     *name,
+			     int             len, /* -1 means nul-terminated */
 			     hb_codepoint_t *glyph)
 {
   return font->get_glyph_from_name (name, len, glyph);
@@ -1084,164 +1173,211 @@
 
 /**
  * hb_font_get_extents_for_direction:
- * @font: a font.
- * @direction:
- * @extents: (out):
+ * @font: #hb_font_t to work upon
+ * @direction: The direction of the text segment
+ * @extents: (out): The #hb_font_extents_t retrieved
  *
+ * Fetches the extents for a font in a text segment of the
+ * specified direction.
  *
+ * Calls the appropriate direction-specific variant (horizontal
+ * or vertical) depending on the value of @direction.
  *
  * Since: 1.1.3
  **/
 void
-hb_font_get_extents_for_direction (hb_font_t *font,
-				   hb_direction_t direction,
+hb_font_get_extents_for_direction (hb_font_t         *font,
+				   hb_direction_t     direction,
 				   hb_font_extents_t *extents)
 {
   return font->get_extents_for_direction (direction, extents);
 }
 /**
  * hb_font_get_glyph_advance_for_direction:
- * @font: a font.
- * @glyph:
- * @direction:
- * @x: (out):
- * @y: (out):
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @direction: The direction of the text segment
+ * @x: (out): The horizontal advance retrieved
+ * @y: (out):  The vertical advance retrieved
  *
+ * Fetches the advance for a glyph ID from the specified font,
+ * in a text segment of the specified direction.
  *
+ * Calls the appropriate direction-specific variant (horizontal
+ * or vertical) depending on the value of @direction.
  *
  * Since: 0.9.2
  **/
 void
-hb_font_get_glyph_advance_for_direction (hb_font_t *font,
-					 hb_codepoint_t glyph,
-					 hb_direction_t direction,
-					 hb_position_t *x, hb_position_t *y)
+hb_font_get_glyph_advance_for_direction (hb_font_t      *font,
+					 hb_codepoint_t  glyph,
+					 hb_direction_t  direction,
+					 hb_position_t  *x,
+					 hb_position_t  *y)
 {
   return font->get_glyph_advance_for_direction (glyph, direction, x, y);
 }
 /**
  * hb_font_get_glyph_advances_for_direction:
- * @font: a font.
- * @direction:
+ * @font: #hb_font_t to work upon
+ * @direction: The direction of the text segment
+ * @count: The number of glyph IDs in the sequence queried
+ * @first_glyph: The first glyph ID to query
+ * @glyph_stride: The stride between successive glyph IDs
+ * @first_advance: (out): The first advance retrieved
+ * @advance_stride: (out): The stride between successive advances
  *
+ * Fetches the advances for a sequence of glyph IDs in the specified
+ * font, in a text segment of the specified direction.
  *
+ * Calls the appropriate direction-specific variant (horizontal
+ * or vertical) depending on the value of @direction.
  *
  * Since: 1.8.6
  **/
 HB_EXTERN void
-hb_font_get_glyph_advances_for_direction (hb_font_t* font,
-					  hb_direction_t direction,
-					  unsigned int count,
+hb_font_get_glyph_advances_for_direction (hb_font_t*            font,
+					  hb_direction_t        direction,
+					  unsigned int          count,
 					  const hb_codepoint_t *first_glyph,
-					  unsigned glyph_stride,
-					  hb_position_t *first_advance,
-					  unsigned advance_stride)
+					  unsigned              glyph_stride,
+					  hb_position_t        *first_advance,
+					  unsigned              advance_stride)
 {
   font->get_glyph_advances_for_direction (direction, count, first_glyph, glyph_stride, first_advance, advance_stride);
 }
 
 /**
  * hb_font_get_glyph_origin_for_direction:
- * @font: a font.
- * @glyph:
- * @direction:
- * @x: (out):
- * @y: (out):
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @direction: The direction of the text segment
+ * @x: (out): The X coordinate retrieved for the origin
+ * @y: (out): The Y coordinate retrieved for the origin
  *
+ * Fetches the (X,Y) coordinates of the origin for a glyph in
+ * the specified font.
  *
+ * Calls the appropriate direction-specific variant (horizontal
+ * or vertical) depending on the value of @direction.
  *
  * Since: 0.9.2
  **/
 void
-hb_font_get_glyph_origin_for_direction (hb_font_t *font,
-					hb_codepoint_t glyph,
-					hb_direction_t direction,
-					hb_position_t *x, hb_position_t *y)
+hb_font_get_glyph_origin_for_direction (hb_font_t      *font,
+					hb_codepoint_t  glyph,
+					hb_direction_t  direction,
+					hb_position_t  *x,
+					hb_position_t  *y)
 {
   return font->get_glyph_origin_for_direction (glyph, direction, x, y);
 }
 
 /**
  * hb_font_add_glyph_origin_for_direction:
- * @font: a font.
- * @glyph:
- * @direction:
- * @x: (out):
- * @y: (out):
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @direction: The direction of the text segment
+ * @x: (inout): Input = The original X coordinate 
+ *     Output = The X coordinate plus the X-coordinate of the origin
+ * @y: (inout): Input = The original Y coordinate
+ *     Output = The Y coordinate plus the Y-coordinate of the origin
  *
+ * Adds the origin coordinates to an (X,Y) point coordinate, in
+ * the specified glyph ID in the specified font.
  *
+ * Calls the appropriate direction-specific variant (horizontal
+ * or vertical) depending on the value of @direction.
  *
  * Since: 0.9.2
  **/
 void
-hb_font_add_glyph_origin_for_direction (hb_font_t *font,
-					hb_codepoint_t glyph,
-					hb_direction_t direction,
-					hb_position_t *x, hb_position_t *y)
+hb_font_add_glyph_origin_for_direction (hb_font_t      *font,
+					hb_codepoint_t  glyph,
+					hb_direction_t  direction,
+					hb_position_t  *x,
+					hb_position_t  *y)
 {
   return font->add_glyph_origin_for_direction (glyph, direction, x, y);
 }
 
 /**
  * hb_font_subtract_glyph_origin_for_direction:
- * @font: a font.
- * @glyph:
- * @direction:
- * @x: (out):
- * @y: (out):
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @direction: The direction of the text segment
+ * @x: (inout): Input = The original X coordinate 
+ *     Output = The X coordinate minus the X-coordinate of the origin
+ * @y: (inout): Input = The original Y coordinate
+ *     Output = The Y coordinate minus the Y-coordinate of the origin
  *
+ * Subtracts the origin coordinates from an (X,Y) point coordinate,
+ * in the specified glyph ID in the specified font.
  *
+ * Calls the appropriate direction-specific variant (horizontal
+ * or vertical) depending on the value of @direction.
  *
  * Since: 0.9.2
  **/
 void
-hb_font_subtract_glyph_origin_for_direction (hb_font_t *font,
-					     hb_codepoint_t glyph,
-					     hb_direction_t direction,
-					     hb_position_t *x, hb_position_t *y)
+hb_font_subtract_glyph_origin_for_direction (hb_font_t      *font,
+					     hb_codepoint_t  glyph,
+					     hb_direction_t  direction,
+					     hb_position_t  *x,
+					     hb_position_t  *y)
 {
   return font->subtract_glyph_origin_for_direction (glyph, direction, x, y);
 }
 
 /**
  * hb_font_get_glyph_kerning_for_direction:
- * @font: a font.
- * @first_glyph:
- * @second_glyph:
- * @direction:
- * @x: (out):
- * @y: (out):
+ * @font: #hb_font_t to work upon
+ * @first_glyph: The glyph ID of the first glyph in the glyph pair to query
+ * @second_glyph: The glyph ID of the second glyph in the glyph pair to query
+ * @direction: The direction of the text segment
+ * @x: (out): The horizontal kerning-adjustment value retrieved
+ * @y: (out): The vertical kerning-adjustment value retrieved
  *
+ * Fetches the kerning-adjustment value for a glyph-pair in the specified font.
  *
+ * Calls the appropriate direction-specific variant (horizontal
+ * or vertical) depending on the value of @direction.
  *
  * Since: 0.9.2
  **/
 void
-hb_font_get_glyph_kerning_for_direction (hb_font_t *font,
-					 hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
-					 hb_direction_t direction,
-					 hb_position_t *x, hb_position_t *y)
+hb_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)
 {
   return font->get_glyph_kerning_for_direction (first_glyph, second_glyph, direction, x, y);
 }
 
 /**
  * hb_font_get_glyph_extents_for_origin:
- * @font: a font.
- * @glyph:
- * @direction:
- * @extents: (out):
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @direction: The direction of the text segment
+ * @extents: (out): The #hb_glyph_extents_t retrieved
  *
+ * Fetches the #hb_glyph_extents_t data for a glyph ID
+ * in the specified font, with respect to the origin in
+ * a text segment in the specified direction.
  *
+ * Calls the appropriate direction-specific variant (horizontal
+ * or vertical) depending on the value of @direction.
  *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_font_get_glyph_extents_for_origin (hb_font_t *font,
-				      hb_codepoint_t glyph,
-				      hb_direction_t direction,
+hb_font_get_glyph_extents_for_origin (hb_font_t          *font,
+				      hb_codepoint_t      glyph,
+				      hb_direction_t      direction,
 				      hb_glyph_extents_t *extents)
 {
   return font->get_glyph_extents_for_origin (glyph, direction, extents);
@@ -1249,65 +1385,79 @@
 
 /**
  * hb_font_get_glyph_contour_point_for_origin:
- * @font: a font.
- * @glyph:
- * @point_index:
- * @direction:
- * @x: (out):
- * @y: (out):
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @point_index: The contour-point index to query
+ * @direction: The direction of the text segment
+ * @x: (out): The X value retrieved for the contour point
+ * @y: (out): The Y value retrieved for the contour point
  *
+ * Fetches the (X,Y) coordinates of a specified contour-point index
+ * in the specified glyph ID in the specified font, with respect
+ * to the origin in a text segment in the specified direction.
  *
+ * Calls the appropriate direction-specific variant (horizontal
+ * or vertical) depending on the value of @direction.
  *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_font_get_glyph_contour_point_for_origin (hb_font_t *font,
-					    hb_codepoint_t glyph, unsigned int point_index,
-					    hb_direction_t direction,
-					    hb_position_t *x, hb_position_t *y)
+hb_font_get_glyph_contour_point_for_origin (hb_font_t      *font,
+					    hb_codepoint_t  glyph,
+					    unsigned int    point_index,
+					    hb_direction_t  direction,
+					    hb_position_t  *x,
+					    hb_position_t  *y)
 {
   return font->get_glyph_contour_point_for_origin (glyph, point_index, direction, x, y);
 }
 
-/* Generates gidDDD if glyph has no name. */
 /**
  * hb_font_glyph_to_string:
- * @font: a font.
- * @glyph:
- * @s: (array length=size):
- * @size:
+ * @font: #hb_font_t to work upon
+ * @glyph: The glyph ID to query
+ * @s: (out) (array length=size): The string containing the glyph name
+ * @size: Length of string @s
  *
+ * Fetches the name of the specified glyph ID in @font and returns
+ * it in string @s.
  *
+ * If the glyph ID has no name in @font, a string of the form `gidDDD` is
+ * generated, with `DDD` being the glyph ID.
  *
  * Since: 0.9.2
  **/
 void
-hb_font_glyph_to_string (hb_font_t *font,
-			 hb_codepoint_t glyph,
-			 char *s, unsigned int size)
+hb_font_glyph_to_string (hb_font_t      *font,
+			 hb_codepoint_t  glyph,
+			 char           *s,
+			 unsigned int    size)
 {
   font->glyph_to_string (glyph, s, size);
 }
 
-/* Parses gidDDD and uniUUUU strings automatically. */
 /**
  * hb_font_glyph_from_string:
- * @font: a font.
- * @s: (array length=len) (element-type uint8_t):
- * @len:
- * @glyph: (out):
+ * @font: #hb_font_t to work upon
+ * @s: (array length=len) (element-type uint8_t): string to query
+ * @len: The length of the string @s
+ * @glyph: (out): The glyph ID corresponding to the string requested
  *
+ * Fetches the glyph ID from @font that matches the specified string.
+ * Strings of the format `gidDDD` or `uniUUUU` are parsed automatically.
  *
+ * <note>Note: @len == -1 means the string is null-terminated.</note>
  *
- * Return value:
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 0.9.2
  **/
 hb_bool_t
-hb_font_glyph_from_string (hb_font_t *font,
-			   const char *s, int len, /* -1 means nul-terminated */
+hb_font_glyph_from_string (hb_font_t      *font,
+			   const char     *s,
+			   int             len,
 			   hb_codepoint_t *glyph)
 {
   return font->glyph_from_string (s, len, glyph);
@@ -1369,9 +1519,9 @@
  * hb_font_create: (Xconstructor)
  * @face: a face.
  *
+ * Constructs a new font object from the specified face.
  *
- *
- * Return value: (transfer full):
+ * Return value: (transfer full): The new font object
  *
  * Since: 0.9.2
  **/
@@ -1404,11 +1554,12 @@
 
 /**
  * hb_font_create_sub_font:
- * @parent: parent font.
+ * @parent: The parent font object
  *
+ * Constructs a sub-font font object from the specified @parent font,
+ * replicating the parent's properties.
  *
- *
- * Return value: (transfer full):
+ * Return value: (transfer full): The new sub-font font object
  *
  * Since: 0.9.2
  **/
@@ -1456,9 +1607,9 @@
 /**
  * hb_font_get_empty:
  *
+ * Fetches the empty font object.
  *
- *
- * Return value: (transfer full)
+ * Return value: (transfer full): The empty font object
  *
  * Since: 0.9.2
  **/
@@ -1470,11 +1621,11 @@
 
 /**
  * hb_font_reference: (skip)
- * @font: a font.
+ * @font: #hb_font_t to work upon
  *
+ * Increases the reference count on the given font object.
  *
- *
- * Return value: (transfer full):
+ * Return value: (transfer full): The @font object
  *
  * Since: 0.9.2
  **/
@@ -1486,9 +1637,11 @@
 
 /**
  * hb_font_destroy: (skip)
- * @font: a font.
+ * @font: #hb_font_t to work upon
  *
- *
+ * Decreases the reference count on the given font object. When the
+ * reference count reaches zero, the font is destroyed,
+ * freeing all memory.
  *
  * Since: 0.9.2
  **/
@@ -1514,15 +1667,15 @@
 
 /**
  * hb_font_set_user_data: (skip)
- * @font: a font.
- * @key:
- * @data:
- * @destroy:
- * @replace:
+ * @font: #hb_font_t to work upon
+ * @key: The user-data key 
+ * @data: A pointer to the user data
+ * @destroy: (nullable): A callback to call when @data is not needed anymore
+ * @replace: Whether to replace an existing data with the same key
  *
+ * Attaches a user-data key/data pair to the specified font object. 
  *
- *
- * Return value:
+ * Return value: %true if success, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -1530,7 +1683,7 @@
 hb_font_set_user_data (hb_font_t          *font,
 		       hb_user_data_key_t *key,
 		       void *              data,
-		       hb_destroy_func_t   destroy,
+		       hb_destroy_func_t   destroy /* May be NULL. */,
 		       hb_bool_t           replace)
 {
   return hb_object_set_user_data (font, key, data, destroy, replace);
@@ -1538,12 +1691,13 @@
 
 /**
  * hb_font_get_user_data: (skip)
- * @font: a font.
- * @key:
+ * @font: #hb_font_t to work upon
+ * @key: The user-data key to query
  *
+ * Fetches the user-data object associated with the specified key,
+ * attached to the specified font object.
  *
- *
- * Return value: (transfer none):
+ * Return value: (transfer none): Pointer to the user data
  *
  * Since: 0.9.2
  **/
@@ -1556,9 +1710,9 @@
 
 /**
  * hb_font_make_immutable:
- * @font: a font.
+ * @font: #hb_font_t to work upon
  *
- *
+ * Makes @font immutable.
  *
  * Since: 0.9.2
  **/
@@ -1576,11 +1730,11 @@
 
 /**
  * hb_font_is_immutable:
- * @font: a font.
+ * @font: #hb_font_t to work upon
  *
+ * Tests whether a font object is immutable.
  *
- *
- * Return value:
+ * Return value: %true if @font is immutable, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -1592,10 +1746,10 @@
 
 /**
  * hb_font_set_parent:
- * @font: a font.
- * @parent: new parent.
+ * @font: #hb_font_t to work upon
+ * @parent: The parent font object to assign
  *
- * Sets parent font of @font.
+ * Sets the parent font of @font.
  *
  * Since: 1.0.5
  **/
@@ -1618,11 +1772,11 @@
 
 /**
  * hb_font_get_parent:
- * @font: a font.
+ * @font: #hb_font_t to work upon
  *
+ * Fetches the parent font of @font.
  *
- *
- * Return value: (transfer none):
+ * Return value: (transfer none): The parent font object
  *
  * Since: 0.9.2
  **/
@@ -1634,10 +1788,10 @@
 
 /**
  * hb_font_set_face:
- * @font: a font.
- * @face: new face.
+ * @font: #hb_font_t to work upon
+ * @face: The #hb_face_t to assign
  *
- * Sets font-face of @font.
+ * Sets @face as the font-face value of @font.
  *
  * Since: 1.4.3
  **/
@@ -1662,11 +1816,11 @@
 
 /**
  * hb_font_get_face:
- * @font: a font.
+ * @font: #hb_font_t to work upon
  *
+ * Fetches the face associated with the specified font object.
  *
- *
- * Return value: (transfer none):
+ * Return value: (transfer none): The #hb_face_t value
  *
  * Since: 0.9.2
  **/
@@ -1679,12 +1833,13 @@
 
 /**
  * hb_font_set_funcs:
- * @font: a font.
- * @klass: (closure font_data) (destroy destroy) (scope notified):
- * @font_data:
- * @destroy:
+ * @font: #hb_font_t to work upon
+ * @klass: (closure font_data) (destroy destroy) (scope notified): The font-functions structure.
+ * @font_data: Data to attach to @font
+ * @destroy: (nullable): The function to call when @font_data is not needed anymore
  *
- *
+ * Replaces the font-functions structure attached to a font, updating
+ * the font's user-data with @font-data and the @destroy callback.
  *
  * Since: 0.9.2
  **/
@@ -1692,7 +1847,7 @@
 hb_font_set_funcs (hb_font_t         *font,
 		   hb_font_funcs_t   *klass,
 		   void              *font_data,
-		   hb_destroy_func_t  destroy)
+		   hb_destroy_func_t  destroy /* May be NULL. */)
 {
   if (hb_object_is_immutable (font))
   {
@@ -1716,18 +1871,19 @@
 
 /**
  * hb_font_set_funcs_data:
- * @font: a font.
- * @font_data: (destroy destroy) (scope notified):
- * @destroy:
+ * @font: #hb_font_t to work upon
+ * @font_data: (destroy destroy) (scope notified): Data to attach to @font
+ * @destroy: (nullable): The function to call when @font_data is not needed anymore
  *
- *
+ * Replaces the user data attached to a font, updating the font's 
+ * @destroy callback.
  *
  * Since: 0.9.2
  **/
 void
 hb_font_set_funcs_data (hb_font_t         *font,
-			void              *font_data,
-			hb_destroy_func_t  destroy)
+		        void              *font_data,
+		        hb_destroy_func_t  destroy /* May be NULL. */)
 {
   /* Destroy user_data? */
   if (hb_object_is_immutable (font))
@@ -1747,18 +1903,18 @@
 
 /**
  * hb_font_set_scale:
- * @font: a font.
- * @x_scale:
- * @y_scale:
+ * @font: #hb_font_t to work upon
+ * @x_scale: Horizontal scale value to assign
+ * @y_scale: Vertical scale value to assign
  *
- *
+ * Sets the horizontal and vertical scale of a font.
  *
  * Since: 0.9.2
  **/
 void
 hb_font_set_scale (hb_font_t *font,
-		   int x_scale,
-		   int y_scale)
+		   int        x_scale,
+		   int        y_scale)
 {
   if (hb_object_is_immutable (font))
     return;
@@ -1770,18 +1926,18 @@
 
 /**
  * hb_font_get_scale:
- * @font: a font.
- * @x_scale: (out):
- * @y_scale: (out):
+ * @font: #hb_font_t to work upon
+ * @x_scale: (out): Horizontal scale value
+ * @y_scale: (out): Vertical scale value
  *
- *
+ * Fetches the horizontal and vertical scale of a font.
  *
  * Since: 0.9.2
  **/
 void
 hb_font_get_scale (hb_font_t *font,
-		   int *x_scale,
-		   int *y_scale)
+		   int       *x_scale,
+		   int       *y_scale)
 {
   if (x_scale) *x_scale = font->x_scale;
   if (y_scale) *y_scale = font->y_scale;
@@ -1789,18 +1945,18 @@
 
 /**
  * hb_font_set_ppem:
- * @font: a font.
- * @x_ppem:
- * @y_ppem:
+ * @font: #hb_font_t to work upon
+ * @x_ppem: Horizontal ppem value to assign
+ * @y_ppem: Vertical ppem value to assign
  *
- *
+ * Sets the horizontal and vertical pixels-per-em (ppem) of a font. 
  *
  * Since: 0.9.2
  **/
 void
-hb_font_set_ppem (hb_font_t *font,
-		  unsigned int x_ppem,
-		  unsigned int y_ppem)
+hb_font_set_ppem (hb_font_t    *font,
+		  unsigned int  x_ppem,
+		  unsigned int  y_ppem)
 {
   if (hb_object_is_immutable (font))
     return;
@@ -1811,16 +1967,16 @@
 
 /**
  * hb_font_get_ppem:
- * @font: a font.
- * @x_ppem: (out):
- * @y_ppem: (out):
+ * @font: #hb_font_t to work upon
+ * @x_ppem: (out): Horizontal ppem value
+ * @y_ppem: (out): Vertical ppem value
  *
- *
+ * Fetches the horizontal and vertical points-per-em (ppem) of a font. 
  *
  * Since: 0.9.2
  **/
 void
-hb_font_get_ppem (hb_font_t *font,
+hb_font_get_ppem (hb_font_t    *font,
 		  unsigned int *x_ppem,
 		  unsigned int *y_ppem)
 {
@@ -1830,17 +1986,19 @@
 
 /**
  * hb_font_set_ptem:
- * @font: a font.
+ * @font: #hb_font_t to work upon
  * @ptem: font size in points.
  *
- * Sets "point size" of the font.  Set to 0 to unset.
+ * Sets the "point size" of a font. Set to zero to unset.
+ * Used in CoreText to implement optical sizing.
  *
- * There are 72 points in an inch.
+ * <note>Note: There are 72 points in an inch.</note>
  *
  * Since: 1.6.0
  **/
 void
-hb_font_set_ptem (hb_font_t *font, float ptem)
+hb_font_set_ptem (hb_font_t *font,
+		  float      ptem)
 {
   if (hb_object_is_immutable (font))
     return;
@@ -1850,11 +2008,12 @@
 
 /**
  * hb_font_get_ptem:
- * @font: a font.
+ * @font: #hb_font_t to work upon
  *
- * Gets the "point size" of the font.  A value of 0 means unset.
+ * Fetches the "point size" of a font. Used in CoreText to
+ * implement optical sizing.
  *
- * Return value: Point size.
+ * Return value: Point size.  A value of zero means "not set."
  *
  * Since: 0.9.2
  **/
@@ -1871,13 +2030,18 @@
 
 /**
  * hb_font_set_variations:
+ * @font: #hb_font_t to work upon
+ * @variations: (array length=variations_length): Array of variation settings to apply
+ * @variations_length: Number of variations to apply
+ *
+ * Applies a list of font-variation settings to a font.
  *
  * Since: 1.4.2
  */
 void
-hb_font_set_variations (hb_font_t *font,
+hb_font_set_variations (hb_font_t            *font,
 			const hb_variation_t *variations,
-			unsigned int variations_length)
+			unsigned int          variations_length)
 {
   if (hb_object_is_immutable (font))
     return;
@@ -1919,13 +2083,19 @@
 
 /**
  * hb_font_set_var_coords_design:
+ * @font: #hb_font_t to work upon
+ * @coords: (array length=coords_length): Array of variation coordinates to apply
+ * @coords_length: Number of coordinates to apply
+ *
+ * Applies a list of variation coordinates (in design-space units)
+ * to a font.
  *
  * Since: 1.4.2
  */
 void
-hb_font_set_var_coords_design (hb_font_t *font,
-			       const float *coords,
-			       unsigned int coords_length)
+hb_font_set_var_coords_design (hb_font_t    *font,
+			       const float  *coords,
+			       unsigned int  coords_length)
 {
   if (hb_object_is_immutable (font))
     return;
@@ -1976,13 +2146,21 @@
 
 /**
  * hb_font_set_var_coords_normalized:
+ * @font: #hb_font_t to work upon
+ * @coords: (array length=coords_length): Array of variation coordinates to apply
+ * @coords_length: Number of coordinates to apply
+ *
+ * Applies a list of variation coordinates (in normalized units)
+ * to a font.
+ *
+ * <note>Note: Coordinates should be normalized to 2.14.</note>
  *
  * Since: 1.4.2
  */
 void
-hb_font_set_var_coords_normalized (hb_font_t *font,
-				   const int *coords, /* 2.14 normalized */
-				   unsigned int coords_length)
+hb_font_set_var_coords_normalized (hb_font_t    *font,
+				   const int    *coords, /* 2.14 normalized */
+				   unsigned int  coords_length)
 {
   if (hb_object_is_immutable (font))
     return;
@@ -2016,6 +2194,11 @@
 
 /**
  * hb_font_get_var_coords_normalized:
+ * @font: #hb_font_t to work upon
+ * @length: Number of coordinates retrieved
+ *
+ * Fetches the list of normalized variation coordinates currently
+ * set on a font.
  *
  * Return value is valid as long as variation coordinates of the font
  * are not modified.
@@ -2023,7 +2206,7 @@
  * Since: 1.4.2
  */
 const int *
-hb_font_get_var_coords_normalized (hb_font_t *font,
+hb_font_get_var_coords_normalized (hb_font_t    *font,
 				   unsigned int *length)
 {
   if (length)
@@ -2035,10 +2218,14 @@
 #ifdef HB_EXPERIMENTAL_API
 /**
  * hb_font_get_var_coords_design:
+ * @font: #hb_font_t to work upon
+ * @length: (out): number of coordinates
  *
  * Return value is valid as long as variation coordinates of the font
  * are not modified.
  *
+ * Return value: coordinates array
+ *
  * Since: EXPERIMENTAL
  */
 const float *
@@ -2115,23 +2302,23 @@
 typedef hb_trampoline_t<hb_font_get_glyph_func_t> hb_font_get_glyph_trampoline_t;
 
 static hb_bool_t
-hb_font_get_nominal_glyph_trampoline (hb_font_t *font,
-				      void *font_data,
-				      hb_codepoint_t unicode,
+hb_font_get_nominal_glyph_trampoline (hb_font_t      *font,
+				      void           *font_data,
+				      hb_codepoint_t  unicode,
 				      hb_codepoint_t *glyph,
-				      void *user_data)
+				      void           *user_data)
 {
   hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data;
   return trampoline->func (font, font_data, unicode, 0, glyph, trampoline->closure.user_data);
 }
 
 static hb_bool_t
-hb_font_get_variation_glyph_trampoline (hb_font_t *font,
-					void *font_data,
-					hb_codepoint_t unicode,
-					hb_codepoint_t variation_selector,
+hb_font_get_variation_glyph_trampoline (hb_font_t      *font,
+					void           *font_data,
+					hb_codepoint_t  unicode,
+					hb_codepoint_t  variation_selector,
 					hb_codepoint_t *glyph,
-					void *user_data)
+					void           *user_data)
 {
   hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data;
   return trampoline->func (font, font_data, unicode, variation_selector, glyph, trampoline->closure.user_data);
@@ -2139,10 +2326,10 @@
 
 /**
  * hb_font_funcs_set_glyph_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified): callback function.
- * @user_data: data to pass to @func.
- * @destroy: function to call when @user_data is not needed anymore.
+ * @ffuncs: The font-functions structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): callback function
+ * @user_data: data to pass to @func
+ * @destroy: (nullable): function to call when @user_data is not needed anymore
  *
  * Deprecated.  Use hb_font_funcs_set_nominal_glyph_func() and
  * hb_font_funcs_set_variation_glyph_func() instead.
@@ -2151,9 +2338,10 @@
  * Deprecated: 1.2.3
  **/
 void
-hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
-			      hb_font_get_glyph_func_t func,
-			      void *user_data, hb_destroy_func_t destroy)
+hb_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 /* May be NULL. */)
 {
   if (hb_object_is_immutable (ffuncs))
   {
diff --git a/src/hb-font.h b/src/hb-font.h
index e1a5719..15dc126 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
@@ -37,7 +37,12 @@
 
 HB_BEGIN_DECLS
 
-
+/**
+ * hb_font_t:
+ *
+ * Data type for holding fonts.
+ *
+ */
 typedef struct hb_font_t hb_font_t;
 
 
@@ -45,6 +50,19 @@
  * hb_font_funcs_t
  */
 
+/**
+ * hb_font_funcs_t:
+ *
+ * Data type containing a set of virtual methods used for
+ * working on #hb_font_t font objects.
+ *
+ * HarfBuzz provides a lightweight default function for each of 
+ * the methods in #hb_font_funcs_t. Client programs can implement
+ * their own replacements for the individual font functions, as
+ * needed, and replace the default by calling the setter for a
+ * method.
+ *
+ **/
 typedef struct hb_font_funcs_t hb_font_funcs_t;
 
 HB_EXTERN hb_font_funcs_t *
@@ -81,12 +99,21 @@
 
 /* font and glyph extents */
 
-/* Note that typically ascender is positive and descender negative in coordinate systems that grow up. */
-typedef struct hb_font_extents_t
-{
-  hb_position_t ascender; /* typographic ascender. */
-  hb_position_t descender; /* typographic descender. */
-  hb_position_t line_gap; /* suggested line spacing gap. */
+/**
+ * hb_font_extents_t:
+ * @ascender: The height of typographic ascenders.
+ * @descender: The depth of typographic descenders.
+ * @line_gap: The suggested line-spacing gap.
+ *
+ * Font-wide extent values, measured in font units.
+ *
+ * Note that typically @ascender is positive and @descender
+ * negative, in coordinate systems that grow up.
+ **/
+typedef struct hb_font_extents_t {
+  hb_position_t ascender;
+  hb_position_t descender;
+  hb_position_t line_gap;
   /*< private >*/
   hb_position_t reserved9;
   hb_position_t reserved8;
@@ -99,33 +126,130 @@
   hb_position_t reserved1;
 } hb_font_extents_t;
 
-/* Note that height is negative in coordinate systems that grow up. */
-typedef struct hb_glyph_extents_t
-{
-  hb_position_t x_bearing; /* left side of glyph from origin. */
-  hb_position_t y_bearing; /* top side of glyph from origin. */
-  hb_position_t width; /* distance from left to right side. */
-  hb_position_t height; /* distance from top to bottom side. */
+/**
+ * hb_glyph_extents_t:
+ * @x_bearing: Distance from the x-origin to the left extremum of the glyph.
+ * @y_bearing: Distance from the top extremum of the glyph to the y-origin.
+ * @width: Distance from the left extremum of the glyph to the right extremum.
+ * @height: Distance from the top extremum of the glyph to the bottom extremum.
+ *
+ * Glyph extent values, measured in font units.
+ *
+ * Note that @height is negative, in coordinate systems that grow up.
+ **/
+typedef struct hb_glyph_extents_t {
+  hb_position_t x_bearing;
+  hb_position_t y_bearing;
+  hb_position_t width;
+  hb_position_t height;
 } hb_glyph_extents_t;
 
 /* func types */
 
+/**
+ * hb_font_get_font_extents_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @extents: (out): The font extents retrieved
+ * @user_data: User data pointer passed by the caller
+ *
+ * This method should retrieve the extents for a font.
+ *
+ **/
 typedef hb_bool_t (*hb_font_get_font_extents_func_t) (hb_font_t *font, void *font_data,
 						       hb_font_extents_t *extents,
 						       void *user_data);
+
+/**
+ * hb_font_get_font_h_extents_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the extents for a font, for horizontal-direction
+ * text segments. Extents must be returned in an #hb_glyph_extents output
+ * parameter.
+ * 
+ **/
 typedef hb_font_get_font_extents_func_t hb_font_get_font_h_extents_func_t;
+
+/**
+ * hb_font_get_font_v_extents_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the extents for a font, for vertical-direction
+ * text segments. Extents must be returned in an #hb_glyph_extents output
+ * parameter.
+ * 
+ **/
 typedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t;
 
 
+/**
+ * hb_font_get_nominal_glyph_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @unicode: The Unicode code point to query
+ * @glyph: (out): The glyph ID retrieved
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the nominal glyph ID for a specified Unicode code
+ * point. Glyph IDs must be returned in a #hb_codepoint_t output parameter.
+ * 
+ * Return value: %true if data found, %false otherwise
+ *
+ **/
 typedef hb_bool_t (*hb_font_get_nominal_glyph_func_t) (hb_font_t *font, void *font_data,
 						       hb_codepoint_t unicode,
 						       hb_codepoint_t *glyph,
 						       void *user_data);
+
+/**
+ * hb_font_get_variation_glyph_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @unicode: The Unicode code point to query
+ * @variation_selector: The  variation-selector code point to query
+ * @glyph: (out): The glyph ID retrieved
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the glyph ID for a specified Unicode code point
+ * followed by a specified Variation Selector code point. Glyph IDs must be
+ * returned in a #hb_codepoint_t output parameter.
+ * 
+ * Return value: %true if data found, %false otherwise
+ *
+ **/
 typedef hb_bool_t (*hb_font_get_variation_glyph_func_t) (hb_font_t *font, void *font_data,
 							 hb_codepoint_t unicode, hb_codepoint_t variation_selector,
 							 hb_codepoint_t *glyph,
 							 void *user_data);
 
+
+/**
+ * hb_font_get_nominal_glyphs_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @count: number of code points to query
+ * @first_unicode: The first Unicode code point to query
+ * @unicode_stride: The stride between successive code points
+ * @first_glyph: (out): The first glyph ID retrieved
+ * @glyph_stride: The stride between successive glyph IDs
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the nominal glyph IDs for a sequence of
+ * Unicode code points. Glyph IDs must be returned in a #hb_codepoint_t
+ * output parameter.
+ *
+ * Return value: the number of code points processed
+ * 
+ **/
 typedef unsigned int (*hb_font_get_nominal_glyphs_func_t) (hb_font_t *font, void *font_data,
 							   unsigned int count,
 							   const hb_codepoint_t *first_unicode,
@@ -134,13 +258,65 @@
 							   unsigned int glyph_stride,
 							   void *user_data);
 
-
+/**
+ * hb_font_get_glyph_advance_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @glyph: The glyph ID to query
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the advance for a specified glyph. The
+ * method must return an #hb_position_t.
+ * 
+ * Return value: The advance of @glyph within @font
+ *
+ **/
 typedef hb_position_t (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, void *font_data,
 							   hb_codepoint_t glyph,
 							   void *user_data);
+
+/**
+ * hb_font_get_glyph_h_advance_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the advance for a specified glyph, in
+ * horizontal-direction text segments. Advances must be returned in
+ * an #hb_position_t output parameter.
+ * 
+ **/
 typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t;
+
+/**
+ * hb_font_get_glyph_v_advance_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the advance for a specified glyph, in
+ * vertical-direction text segments. Advances must be returned in
+ * an #hb_position_t output parameter.
+ * 
+ **/
 typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t;
 
+/**
+ * hb_font_get_glyph_advances_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @count: The number of glyph IDs in the sequence queried
+ * @first_glyph: The first glyph ID to query
+ * @glyph_stride: The stride between successive glyph IDs
+ * @first_advance: (out): The first advance retrieved
+ * @advance_stride: The stride between successive advances
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the advances for a sequence of glyphs.
+ * 
+ **/
 typedef void (*hb_font_get_glyph_advances_func_t) (hb_font_t* font, void* font_data,
 						   unsigned int count,
 						   const hb_codepoint_t *first_glyph,
@@ -148,36 +324,188 @@
 						   hb_position_t *first_advance,
 						   unsigned advance_stride,
 						   void *user_data);
+
+/**
+ * hb_font_get_glyph_h_advances_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the advances for a sequence of glyphs, in
+ * horizontal-direction text segments.
+ * 
+ **/
 typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_h_advances_func_t;
+
+/**
+ * hb_font_get_glyph_v_advances_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the advances for a sequence of glyphs, in
+ * vertical-direction text segments.
+ * 
+ **/
 typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t;
 
+/**
+ * hb_font_get_glyph_origin_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @glyph: The glyph ID to query
+ * @x: (out): The X coordinate of the origin
+ * @y: (out): The Y coordinate of the origin
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the (X,Y) coordinates (in font units) of the
+ * origin for a glyph. Each coordinate must be returned in an #hb_position_t
+ * output parameter.
+ *
+ * Return value: %true if data found, %false otherwise
+ * 
+ **/
 typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data,
 						      hb_codepoint_t glyph,
 						      hb_position_t *x, hb_position_t *y,
 						      void *user_data);
+
+/**
+ * hb_font_get_glyph_h_origin_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the (X,Y) coordinates (in font units) of the
+ * origin for a glyph, for horizontal-direction text segments. Each
+ * coordinate must be returned in an #hb_position_t output parameter.
+ * 
+ **/
 typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t;
+
+/**
+ * hb_font_get_glyph_v_origin_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the (X,Y) coordinates (in font units) of the
+ * origin for a glyph, for vertical-direction text segments. Each coordinate
+ * must be returned in an #hb_position_t output parameter.
+ * 
+ **/
 typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t;
 
+/**
+ * hb_font_get_glyph_kerning_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @first_glyph: The glyph ID of the first glyph in the glyph pair
+ * @second_glyph: The glyph ID of the second glyph in the glyph pair
+ * @user_data: User data pointer passed by the caller
+ *
+ * This method should retrieve the kerning-adjustment value for a glyph-pair in
+ * the specified font, for horizontal text segments.
+ *
+ **/
 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);
+/**
+ * hb_font_get_glyph_h_kerning_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the kerning-adjustment value for a glyph-pair in
+ * the specified font, for horizontal text segments.
+ *
+ **/
 typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t;
 
 
+/**
+ * hb_font_get_glyph_extents_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @glyph: The glyph ID to query
+ * @extents: (out): The #hb_glyph_extents_t retrieved
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the extents for a specified glyph. Extents must be 
+ * returned in an #hb_glyph_extents output parameter.
+ *
+ * Return value: %true if data found, %false otherwise
+ * 
+ **/
 typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data,
 						       hb_codepoint_t glyph,
 						       hb_glyph_extents_t *extents,
 						       void *user_data);
+
+/**
+ * hb_font_get_glyph_contour_point_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @glyph: The glyph ID to query
+ * @point_index: The contour-point index to query
+ * @x: (out): The X value retrieved for the contour point
+ * @y: (out): The Y value retrieved for the contour point
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the (X,Y) coordinates (in font units) for a
+ * specified contour point in a glyph. Each coordinate must be returned as
+ * an #hb_position_t output parameter.
+ * 
+ * Return value: %true if data found, %false otherwise
+ *
+ **/
 typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, void *font_data,
 							     hb_codepoint_t glyph, unsigned int point_index,
 							     hb_position_t *x, hb_position_t *y,
 							     void *user_data);
 
 
+/**
+ * hb_font_get_glyph_name_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @glyph: The glyph ID to query
+ * @name: (out) (array length=size): Name string retrieved for the glyph ID
+ * @size: Length of the glyph-name string retrieved
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the glyph name that corresponds to a
+ * glyph ID. The name should be returned in a string output parameter.
+ * 
+ * Return value: %true if data found, %false otherwise
+ *
+ **/
 typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_data,
 						    hb_codepoint_t glyph,
 						    char *name, unsigned int size,
 						    void *user_data);
+
+/**
+ * hb_font_get_glyph_from_name_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @name: (array length=len): The name string to query
+ * @len: The length of the name queried
+ * @glyph: (out): The glyph ID retrieved
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the glyph ID that corresponds to a glyph-name
+ * string. 
+ * 
+ * Return value: %true if data found, %false otherwise
+ *
+ **/
 typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *font_data,
 							 const char *name, int len, /* -1 means nul-terminated */
 							 hb_codepoint_t *glyph,
@@ -188,12 +516,12 @@
 
 /**
  * hb_font_funcs_set_font_h_extents_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_font_h_extents_func_t.
  *
  * Since: 1.1.2
  **/
@@ -204,12 +532,12 @@
 
 /**
  * hb_font_funcs_set_font_v_extents_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_font_v_extents_func_t.
  *
  * Since: 1.1.2
  **/
@@ -220,12 +548,12 @@
 
 /**
  * hb_font_funcs_set_nominal_glyph_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_nominal_glyph_func_t.
  *
  * Since: 1.2.3
  **/
@@ -236,12 +564,12 @@
 
 /**
  * hb_font_funcs_set_nominal_glyphs_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_nominal_glyphs_func_t.
  *
  * Since: 2.0.0
  **/
@@ -252,12 +580,12 @@
 
 /**
  * hb_font_funcs_set_variation_glyph_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_variation_glyph_func_t.
  *
  * Since: 1.2.3
  **/
@@ -268,12 +596,12 @@
 
 /**
  * hb_font_funcs_set_glyph_h_advance_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_glyph_h_advance_func_t.
  *
  * Since: 0.9.2
  **/
@@ -284,12 +612,12 @@
 
 /**
  * hb_font_funcs_set_glyph_v_advance_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_glyph_v_advance_func_t.
  *
  * Since: 0.9.2
  **/
@@ -300,12 +628,12 @@
 
 /**
  * hb_font_funcs_set_glyph_h_advances_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_glyph_h_advances_func_t.
  *
  * Since: 1.8.6
  **/
@@ -316,12 +644,12 @@
 
 /**
  * hb_font_funcs_set_glyph_v_advances_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_glyph_v_advances_func_t.
  *
  * Since: 1.8.6
  **/
@@ -332,12 +660,12 @@
 
 /**
  * hb_font_funcs_set_glyph_h_origin_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_glyph_h_origin_func_t.
  *
  * Since: 0.9.2
  **/
@@ -348,12 +676,12 @@
 
 /**
  * hb_font_funcs_set_glyph_v_origin_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_glyph_v_origin_func_t.
  *
  * Since: 0.9.2
  **/
@@ -364,12 +692,12 @@
 
 /**
  * hb_font_funcs_set_glyph_h_kerning_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_glyph_h_kerning_func_t.
  *
  * Since: 0.9.2
  **/
@@ -380,12 +708,12 @@
 
 /**
  * hb_font_funcs_set_glyph_extents_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_glyph_extents_func_t.
  *
  * Since: 0.9.2
  **/
@@ -396,12 +724,12 @@
 
 /**
  * hb_font_funcs_set_glyph_contour_point_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_glyph_contour_point_func_t.
  *
  * Since: 0.9.2
  **/
@@ -412,12 +740,12 @@
 
 /**
  * hb_font_funcs_set_glyph_name_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_glyph_name_func_t.
  *
  * Since: 0.9.2
  **/
@@ -428,12 +756,12 @@
 
 /**
  * hb_font_funcs_set_glyph_from_name_func:
- * @ffuncs: font functions.
- * @func: (closure user_data) (destroy destroy) (scope notified):
- * @user_data:
- * @destroy:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
- *
+ * Sets the implementation function for #hb_font_get_glyph_from_name_func_t.
  *
  * Since: 0.9.2
  **/
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index 2680873..b6ded40 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -179,13 +179,13 @@
 }
 
 /**
- * hb_ft_get_face:
+ * hb_ft_font_get_face:
  * @font: #hb_font_t to work upon
  *
  * Fetches the FT_Face associated with the specified #hb_font_t
  * font object.
  *
- * Return value: the FT_Face found
+ * Return value: (nullable): the FT_Face found or %NULL
  *
  * Since: 0.9.2
  **/
@@ -202,11 +202,12 @@
 
 /**
  * hb_ft_font_lock_face:
- * @font:
+ * @font: #hb_font_t to work upon
  *
+ * Gets the FT_Face associated with @font, This face will be kept around until
+ * you call hb_ft_font_unlock_face().
  *
- *
- * Return value:
+ * Return value: (nullable): the FT_Face associated with @font or %NULL
  * Since: 2.6.5
  **/
 FT_Face
@@ -224,11 +225,10 @@
 
 /**
  * hb_ft_font_unlock_face:
- * @font:
+ * @font: #hb_font_t to work upon
  *
+ * Releases an FT_Face previously obtained with hb_ft_font_lock_face().
  *
- *
- * Return value:
  * Since: 2.6.5
  **/
 void
@@ -661,7 +661,7 @@
 /**
  * hb_ft_face_create:
  * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon
- * @destroy: A callback to call when the face object is not needed anymore
+ * @destroy: (nullable): A callback to call when the face object is not needed anymore
  *
  * Creates an #hb_face_t face object from the specified FT_Face.
  *
@@ -771,13 +771,13 @@
 /**
  * hb_ft_font_create:
  * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon
- * @destroy: (optional): A callback to call when the font object is not needed anymore
+ * @destroy: (nullable): A callback to call when the font object is not needed anymore
  *
  * Creates an #hb_font_t font object from the specified FT_Face.
  *
  * <note>Note: You must set the face size on @ft_face before calling
- * hb_ft_font_create() on it. Otherwise, HarfBuzz will not pick up
- * the face size.</note>
+ * hb_ft_font_create() on it. HarfBuzz assumes size is always set and will
+ * access `size` member of FT_Face unconditionally.</note>
  *
  * This variant of the function does not provide any life-cycle management.
  *
@@ -814,7 +814,7 @@
 }
 
 /**
- * hb_ft_font_has_changed:
+ * hb_ft_font_changed:
  * @font: #hb_font_t to work upon
  *
  * Refreshes the state of @font when the underlying FT_Face has changed.
@@ -884,8 +884,8 @@
  * Creates an #hb_font_t font object from the specified FT_Face.
  *
  * <note>Note: You must set the face size on @ft_face before calling
- * hb_ft_font_create_references() on it. Otherwise, HarfBuzz will not pick up
- * the face size.</note>
+ * hb_ft_font_create_referenced() on it. HarfBuzz assumes size is always set
+ * and will access `size` member of FT_Face unconditionally.</note>
  *
  * This is the preferred variant of the hb_ft_font_create*
  * function family, because it calls FT_Reference_Face() on @ft_face,
diff --git a/src/hb-gdi.cc b/src/hb-gdi.cc
index f6306ef..dc4659c 100644
--- a/src/hb-gdi.cc
+++ b/src/hb-gdi.cc
@@ -28,6 +28,16 @@
 
 #include "hb-gdi.h"
 
+
+/**
+ * SECTION:hb-gdi
+ * @title: hb-gdi
+ * @short_description: GDI integration
+ * @include: hb-gdi.h
+ *
+ * Functions for using HarfBuzz with GDI fonts.
+ **/
+
 static hb_blob_t *
 _hb_gdi_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
 {
@@ -60,6 +70,8 @@
  * hb_gdi_face_create:
  * @hfont: a HFONT object.
  *
+ * Constructs a new face object from the specified GDI HFONT.
+ *
  * Return value: #hb_face_t object corresponding to the given input
  *
  * Since: 2.6.0
diff --git a/src/hb-gobject-enums.h.tmpl b/src/hb-gobject-enums.h.tmpl
index f8bd29e..a846786 100644
--- a/src/hb-gobject-enums.h.tmpl
+++ b/src/hb-gobject-enums.h.tmpl
@@ -25,7 +25,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_GOBJECT_H_IN
+#if !defined(HB_GOBJECT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-gobject.h> instead."
 #endif
 
diff --git a/src/hb-gobject-structs.h b/src/hb-gobject-structs.h
index 6fad8d7..63467f8 100644
--- a/src/hb-gobject-structs.h
+++ b/src/hb-gobject-structs.h
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_GOBJECT_H_IN
+#if !defined(HB_GOBJECT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-gobject.h> instead."
 #endif
 
@@ -40,47 +40,22 @@
 
 /* Object types */
 
-/**
- * hb_gobject_blob_get_type:
- *
- * Since: 0.9.2
- **/
 HB_EXTERN GType
 hb_gobject_blob_get_type (void);
 #define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ())
 
-/**
- * hb_gobject_buffer_get_type:
- *
- * Since: 0.9.2
- **/
 HB_EXTERN GType
 hb_gobject_buffer_get_type (void);
 #define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ())
 
-/**
- * hb_gobject_face_get_type:
- *
- * Since: 0.9.2
- **/
 HB_EXTERN GType
 hb_gobject_face_get_type (void);
 #define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ())
 
-/**
- * hb_gobject_font_get_type:
- *
- * Since: 0.9.2
- **/
 HB_EXTERN GType
 hb_gobject_font_get_type (void);
 #define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ())
 
-/**
- * hb_gobject_font_funcs_get_type:
- *
- * Since: 0.9.2
- **/
 HB_EXTERN GType
 hb_gobject_font_funcs_get_type (void);
 #define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ())
@@ -97,11 +72,6 @@
 hb_gobject_shape_plan_get_type (void);
 #define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ())
 
-/**
- * hb_gobject_unicode_funcs_get_type:
- *
- * Since: 0.9.2
- **/
 HB_EXTERN GType
 hb_gobject_unicode_funcs_get_type (void);
 #define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ())
diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index d8a72dc..220ba4f 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -195,6 +195,11 @@
 #ifndef HB_DISABLE_DEPRECATED
 /**
  * hb_graphite2_font_get_gr_font:
+ * @font: An #hb_font_t
+ *
+ * Always returns %NULL. Use hb_graphite2_face_get_gr_face() instead.
+ *
+ * Return value: (nullable): Graphite2 font associated with @font.
  *
  * Since: 0.9.10
  * Deprecated: 1.4.2
diff --git a/src/hb-iter.hh b/src/hb-iter.hh
index 981c5c2..f701815 100644
--- a/src/hb-iter.hh
+++ b/src/hb-iter.hh
@@ -922,7 +922,7 @@
 template <typename C, typename V,
 	  hb_requires (hb_is_iterable (C))>
 inline void
-hb_fill (C& c, const V &v)
+hb_fill (C&& c, const V &v)
 {
   for (auto i = hb_iter (c); i; i++)
     *i = v;
diff --git a/src/hb-map.cc b/src/hb-map.cc
index 191be14..aabf713 100644
--- a/src/hb-map.cc
+++ b/src/hb-map.cc
@@ -42,7 +42,9 @@
 /**
  * hb_map_create: (Xconstructor)
  *
- * Return value: (transfer full):
+ * Creates a new, initially empty map.
+ *
+ * Return value: (transfer full): The new #hb_map_t
  *
  * Since: 1.7.7
  **/
@@ -62,7 +64,9 @@
 /**
  * hb_map_get_empty:
  *
- * Return value: (transfer full):
+ * Fetches the singleton empty #hb_map_t.
+ *
+ * Return value: (transfer full): The empty #hb_map_t
  *
  * Since: 1.7.7
  **/
@@ -74,9 +78,11 @@
 
 /**
  * hb_map_reference: (skip)
- * @map: a map.
+ * @map: A map
  *
- * Return value: (transfer full):
+ * Increases the reference count on a map.
+ *
+ * Return value: (transfer full): The map
  *
  * Since: 1.7.7
  **/
@@ -88,7 +94,11 @@
 
 /**
  * hb_map_destroy: (skip)
- * @map: a map.
+ * @map: A map
+ *
+ * Decreases the reference count on a map. When
+ * the reference count reaches zero, the map is
+ * destroyed, freeing all memory.
  *
  * Since: 1.7.7
  **/
@@ -104,13 +114,15 @@
 
 /**
  * hb_map_set_user_data: (skip)
- * @map: a map.
- * @key:
- * @data:
- * @destroy:
- * @replace:
+ * @map: A map
+ * @key: The user-data key to set
+ * @data: A pointer to the user data to set
+ * @destroy: (nullable): A callback to call when @data is not needed anymore
+ * @replace: Whether to replace an existing data with the same key
  *
- * Return value:
+ * Attaches a user-data key/data pair to the specified map.
+ *
+ * Return value: %true if success, %false otherwise
  *
  * Since: 1.7.7
  **/
@@ -126,10 +138,13 @@
 
 /**
  * hb_map_get_user_data: (skip)
- * @map: a map.
- * @key:
+ * @map: A map
+ * @key: The user-data key to query
  *
- * Return value: (transfer none):
+ * Fetches the user data associated with the specified key,
+ * attached to the specified map.
+ *
+ * Return value: (transfer none): A pointer to the user data
  *
  * Since: 1.7.7
  **/
@@ -143,11 +158,11 @@
 
 /**
  * hb_map_allocation_successful:
- * @map: a map.
+ * @map: A map
  *
+ * Tests whether memory allocation for a set was successful.
  *
- *
- * Return value:
+ * Return value: %true if allocation succeeded, %false otherwise
  *
  * Since: 1.7.7
  **/
@@ -160,11 +175,11 @@
 
 /**
  * hb_map_set:
- * @map: a map.
- * @key:
- * @value:
+ * @map: A map
+ * @key: The key to store in the map
+ * @value: The value to store for @key
  *
- *
+ * Stores @key:@value in the map.
  *
  * Since: 1.7.7
  **/
@@ -178,10 +193,10 @@
 
 /**
  * hb_map_get:
- * @map: a map.
- * @key:
+ * @map: A map
+ * @key: The key to query
  *
- *
+ * Fetches the value stored for @key in @map.
  *
  * Since: 1.7.7
  **/
@@ -194,10 +209,10 @@
 
 /**
  * hb_map_del:
- * @map: a map.
- * @key:
+ * @map: A map
+ * @key: The key to delete
  *
- *
+ * Removes @key and its stored value from @map.
  *
  * Since: 1.7.7
  **/
@@ -210,10 +225,12 @@
 
 /**
  * hb_map_has:
- * @map: a map.
- * @key:
+ * @map: A map
+ * @key: The key to query
  *
+ * Tests whether @key is an element of @map.
  *
+ * Return value: %true if @key is found in @map, %false otherwise
  *
  * Since: 1.7.7
  **/
@@ -227,9 +244,9 @@
 
 /**
  * hb_map_clear:
- * @map: a map.
+ * @map: A map
  *
- *
+ * Clears out the contents of @map.
  *
  * Since: 1.7.7
  **/
@@ -241,9 +258,11 @@
 
 /**
  * hb_map_is_empty:
- * @map: a map.
+ * @map: A map
  *
+ * Tests whether @map is empty (contains no elements).
  *
+ * Return value: %true if @map is empty
  *
  * Since: 1.7.7
  **/
@@ -255,9 +274,11 @@
 
 /**
  * hb_map_get_population:
- * @map: a map.
+ * @map: A map
  *
+ * Returns the number of key-value pairs in the map.
  *
+ * Return value: The population of @map
  *
  * Since: 1.7.7
  **/
diff --git a/src/hb-map.h b/src/hb-map.h
index b77843c..6a45a7b 100644
--- a/src/hb-map.h
+++ b/src/hb-map.h
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
@@ -36,11 +36,21 @@
 HB_BEGIN_DECLS
 
 
-/*
+/**
+ * HB_MAP_VALUE_INVALID:
+ *
+ * Unset #hb_map_t value.
+ *
  * Since: 1.7.7
  */
 #define HB_MAP_VALUE_INVALID ((hb_codepoint_t) -1)
 
+/**
+ * hb_map_t:
+ *
+ * Data type for holding integer-to-integer hash maps.
+ *
+ **/
 typedef struct hb_map_t hb_map_t;
 
 
diff --git a/src/hb-null.hh b/src/hb-null.hh
index 9853939..d09f858 100644
--- a/src/hb-null.hh
+++ b/src/hb-null.hh
@@ -177,6 +177,7 @@
   T * get () const { return v ? v : const_cast<T *> (&Null (T)); }
   T * get_raw () const { return v; }
 
+  private:
   T *v;
 };
 
diff --git a/src/hb-open-type.hh b/src/hb-open-type.hh
index 50558cf..8c4271b 100644
--- a/src/hb-open-type.hh
+++ b/src/hb-open-type.hh
@@ -80,14 +80,21 @@
 
     return pb->cmp (*pa);
   }
-  template <typename Type2>
+  template <typename Type2,
+	    hb_enable_if (hb_is_integral (Type2) &&
+			  sizeof (Type2) < sizeof (int) &&
+			  sizeof (Type) < sizeof (int))>
   int cmp (Type2 a) const
   {
     Type b = v;
-    if (sizeof (Type) < sizeof (int) && sizeof (Type2) < sizeof (int))
-      return (int) a - (int) b;
-    else
-      return a < b ? -1 : a == b ? 0 : +1;
+    return (int) a - (int) b;
+  }
+  template <typename Type2,
+	    hb_enable_if (hb_is_convertible (Type2, Type))>
+  int cmp (Type2 a) const
+  {
+    Type b = v;
+    return a < b ? -1 : a == b ? 0 : +1;
   }
   bool sanitize (hb_sanitize_context_t *c) const
   {
@@ -433,8 +440,6 @@
   { return hb_array (arrayZ, len); }
   hb_array_t<const Type> as_array (unsigned int len) const
   { return hb_array (arrayZ, len); }
-  operator hb_array_t<      Type> ()       { return as_array (); }
-  operator hb_array_t<const Type> () const { return as_array (); }
 
   template <typename T>
   Type &lsearch (unsigned int len, const T &x, Type &not_found = Crap (Type))
diff --git a/src/hb-ot-color-colr-table.hh b/src/hb-ot-color-colr-table.hh
index 92a49bb..e2a1ff4 100644
--- a/src/hb-ot-color-colr-table.hh
+++ b/src/hb-ot-color-colr-table.hh
@@ -214,7 +214,7 @@
 				if (unlikely (!old_record))
 				  return hb_pair_t<bool, BaseGlyphRecord> (false, Null (BaseGlyphRecord));
 
-				BaseGlyphRecord new_record;
+				BaseGlyphRecord new_record = {};
 				new_record.glyphId = new_gid;
 				new_record.numLayers = old_record->numLayers;
 				return hb_pair_t<bool, BaseGlyphRecord> (true, new_record);
diff --git a/src/hb-ot-color.cc b/src/hb-ot-color.cc
index 0e7203a..fb8ed08 100644
--- a/src/hb-ot-color.cc
+++ b/src/hb-ot-color.cc
@@ -64,7 +64,7 @@
  *
  * Tests whether a face includes a `CPAL` color-palette table.
  *
- * Return value: true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 2.1.0
  */
@@ -195,7 +195,7 @@
  *
  * Tests whether a face includes any `COLR` color layers.
  *
- * Return value: true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 2.1.0
  */
@@ -242,7 +242,7 @@
  *
  * Tests whether a face includes any `SVG` glyph images.
  *
- * Return value: true if data found, false otherwise.
+ * Return value: %true if data found, %false otherwise.
  *
  * Since: 2.1.0
  */
@@ -280,7 +280,7 @@
  *
  * Tests whether a face has PNG glyph images (either in `CBDT` or `sbix` tables).
  *
- * Return value: true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 2.1.0
  */
diff --git a/src/hb-ot-color.h b/src/hb-ot-color.h
index 63ef20a..c23ce4d 100644
--- a/src/hb-ot-color.h
+++ b/src/hb-ot-color.h
@@ -26,7 +26,7 @@
  * Google Author(s): Sascha Brawer, Behdad Esfahbod
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-ot.h> instead."
 #endif
 
@@ -66,6 +66,8 @@
  * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND: Flag indicating that the color
  *   palette is appropriate to use when displaying the font on a dark background such as black.
  *
+ * Flags that describe the properties of color palette.
+ *
  * Since: 2.1.0
  */
 typedef enum { /*< flags >*/
@@ -95,13 +97,14 @@
 
 /**
  * hb_ot_color_layer_t:
+ * @glyph: the glyph ID of the layer
+ * @color_index: the palette color index of the layer
  *
  * Pairs of glyph and color index.
  *
  * Since: 2.1.0
  **/
-typedef struct hb_ot_color_layer_t
-{
+typedef struct hb_ot_color_layer_t {
   hb_codepoint_t glyph;
   unsigned int   color_index;
 } hb_ot_color_layer_t;
diff --git a/src/hb-ot-deprecated.h b/src/hb-ot-deprecated.h
index bc72f8a..ce6b6fe 100644
--- a/src/hb-ot-deprecated.h
+++ b/src/hb-ot-deprecated.h
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-ot.h> instead."
 #endif
 
@@ -41,6 +41,13 @@
 
 
 /* https://github.com/harfbuzz/harfbuzz/issues/1734 */
+/**
+ * HB_MATH_GLYPH_PART_FLAG_EXTENDER:
+ *
+ * Use #HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER instead.
+ *
+ * Deprecated: 2.5.1
+ */
 #define HB_MATH_GLYPH_PART_FLAG_EXTENDER HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER
 
 
@@ -71,6 +78,8 @@
 /**
  * HB_OT_VAR_NO_AXIS_INDEX:
  *
+ * Do not use.
+ *
  * Since: 1.4.2
  * Deprecated: 2.2.0
  */
@@ -78,12 +87,18 @@
 
 /**
  * hb_ot_var_axis_t:
+ * @tag: axis tag
+ * @name_id: axis name identifier
+ * @min_value: minimum value of the axis
+ * @default_value: default value of the axis
+ * @max_value: maximum value of the axis
+ *
+ * Use #hb_ot_var_axis_info_t instead.
  *
  * Since: 1.4.2
  * Deprecated: 2.2.0
  */
-typedef struct hb_ot_var_axis_t
-{
+typedef struct hb_ot_var_axis_t {
   hb_tag_t tag;
   hb_ot_name_id_t name_id;
   float min_value;
diff --git a/src/hb-ot-font.h b/src/hb-ot-font.h
index 80eaa54..e7959d1 100644
--- a/src/hb-ot-font.h
+++ b/src/hb-ot-font.h
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod, Roozbeh Pournader
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-ot.h> instead."
 #endif
 
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh
index 6ab950a..4985d5e 100644
--- a/src/hb-ot-layout-common.hh
+++ b/src/hb-ot-layout-common.hh
@@ -1128,7 +1128,7 @@
     out->lookupType = lookupType;
     out->lookupFlag = lookupFlag;
 
-    const hb_set_t *glyphset = c->plan->glyphset ();
+    const hb_set_t *glyphset = c->plan->glyphset_gsub ();
     unsigned int lookup_type = get_type ();
     + hb_iter (get_subtables <TSubTable> ())
     | hb_filter ([this, glyphset, lookup_type] (const OffsetTo<TSubTable> &_) { return (this+_).intersects (glyphset, lookup_type); })
@@ -1251,8 +1251,9 @@
   {
     /* TODO Speed up, using hb_set_next() and bsearch()? */
     unsigned int count = glyphArray.len;
+    const HBGlyphID *arr = glyphArray.arrayZ;
     for (unsigned int i = 0; i < count; i++)
-      if (glyphs->has (glyphArray[i]))
+      if (glyphs->has (arr[i]))
 	return true;
     return false;
   }
@@ -1356,18 +1357,21 @@
   bool intersects (const hb_set_t *glyphs) const
   {
     /* TODO Speed up, using hb_set_next() and bsearch()? */
-    unsigned int count = rangeRecord.len;
-    for (unsigned int i = 0; i < count; i++)
-      if (rangeRecord[i].intersects (glyphs))
+    /* TODO(iter) Rewrite as dagger. */
+    unsigned count = rangeRecord.len;
+    const RangeRecord *arr = rangeRecord.arrayZ;
+    for (unsigned i = 0; i < count; i++)
+      if (arr[i].intersects (glyphs))
 	return true;
     return false;
   }
   bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
   {
-    unsigned int i;
-    unsigned int count = rangeRecord.len;
-    for (i = 0; i < count; i++) {
-      const RangeRecord &range = rangeRecord[i];
+    /* TODO(iter) Rewrite as dagger. */
+    unsigned count = rangeRecord.len;
+    const RangeRecord *arr = rangeRecord.arrayZ;
+    for (unsigned i = 0; i < count; i++) {
+      const RangeRecord &range = arr[i];
       if (range.value <= index &&
 	  index < (unsigned int) range.value + (range.last - range.first) &&
 	  range.intersects (glyphs))
@@ -1502,7 +1506,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto it =
@@ -1729,7 +1733,7 @@
 	       hb_map_t *klass_map = nullptr /*OUT*/) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->_glyphset_gsub;
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     hb_sorted_vector_t<HBGlyphID> glyphs;
@@ -1815,8 +1819,13 @@
       if (hb_set_next (glyphs, &g)) return true;
       /* Fall through. */
     }
+    /* TODO Speed up, using set overlap first? */
+    /* TODO(iter) Rewrite as dagger. */
+    HBUINT16 k; /* TODO(constexpr) use constructor to initialize. */
+    k = klass;
+    const HBUINT16 *arr = classValue.arrayZ;
     for (unsigned int i = 0; i < count; i++)
-      if (classValue[i] == klass && glyphs->has (startGlyph + i))
+      if (arr[i] == k && glyphs->has (startGlyph + i))
 	return true;
     return false;
   }
@@ -1898,7 +1907,7 @@
 	       hb_map_t *klass_map = nullptr /*OUT*/) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->_glyphset_gsub;
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     hb_sorted_vector_t<HBGlyphID> glyphs;
@@ -1984,8 +1993,13 @@
 	return true;
       /* Fall through. */
     }
+    /* TODO Speed up, using set overlap first? */
+    /* TODO(iter) Rewrite as dagger. */
+    HBUINT16 k; /* TODO(constexpr) use constructor to initialize. */
+    k = klass;
+    const RangeRecord *arr = rangeRecord.arrayZ;
     for (unsigned int i = 0; i < count; i++)
-      if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs))
+      if (arr[i].value == k && arr[i].intersects (glyphs))
 	return true;
     return false;
   }
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index b7eee4d..b2b2f0e 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -775,7 +775,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto it =
@@ -890,7 +890,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     unsigned sub_length = valueFormat.get_len ();
@@ -1149,7 +1149,7 @@
     if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
     out->len = 0;
 
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     unsigned len1 = valueFormats[0].get_len ();
@@ -1270,7 +1270,7 @@
   {
     TRACE_SUBSET (this);
 
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto *out = c->serializer->start_embed (*this);
@@ -1461,7 +1461,7 @@
 		})
     ;
 
-    const hb_set_t &glyphset = *c->plan->_glyphset_gsub;
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto it =
@@ -1748,7 +1748,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto *out = c->serializer->start_embed (*this);
@@ -1924,7 +1924,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto *out = c->serializer->start_embed (*this);
@@ -2387,7 +2387,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto *out = c->serializer->start_embed (*this);
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 2f41d67..5f10ecb 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -356,7 +356,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     if (!intersects (&glyphset)) return_trace (false);
@@ -447,7 +447,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto *out = c->serializer->start_embed (*this);
@@ -582,7 +582,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto it =
@@ -682,7 +682,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto *out = c->serializer->start_embed (*this);
@@ -840,7 +840,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
@@ -1058,7 +1058,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto *out = c->serializer->start_embed (*this);
diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh
index cb62b02..101d03e 100644
--- a/src/hb-ot-layout-gsubgpos.hh
+++ b/src/hb-ot-layout-gsubgpos.hh
@@ -146,7 +146,6 @@
     if (is_lookup_visited (lookup_index))
       return;
 
-    set_lookup_visited (lookup_index);
     nesting_level_left--;
     recurse_func (this, lookup_index);
     nesting_level_left++;
@@ -1188,7 +1187,7 @@
 
     /* Don't recurse to ourself at same position.
      * Note that this test is too naive, it doesn't catch longer loops. */
-    if (idx == 0 && lookupRecord[i].lookupListIndex == c->lookup_index)
+    if (unlikely (idx == 0 && lookupRecord[i].lookupListIndex == c->lookup_index))
       continue;
 
     if (unlikely (!buffer->move_to (match_positions[idx])))
@@ -1226,7 +1225,8 @@
      *     mean that n match positions where removed, as there might
      *     have been marks and default-ignorables in the sequence.  We
      *     should instead drop match positions between current-position
-     *     and current-position + n instead.
+     *     and current-position + n instead. Though, am not sure which
+     *     one is better. Both cases have valid uses. Sigh.
      *
      * It should be possible to construct tests for both of these cases.
      */
@@ -1389,9 +1389,11 @@
 			    lookup_context);
   }
 
-  void closure_lookups (hb_closure_lookups_context_t *c) const
+  void closure_lookups (hb_closure_lookups_context_t *c,
+                        ContextClosureLookupContext &lookup_context) const
   {
     if (unlikely (c->lookup_limit_exceeded ())) return;
+    if (!intersects (c->glyphs, lookup_context)) return;
 
     const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>>
 						       (inputZ.as_array (inputCount ? inputCount - 1 : 0));
@@ -1521,14 +1523,13 @@
     ;
   }
 
-  void closure_lookups (hb_closure_lookups_context_t *c) const
+  void closure_lookups (hb_closure_lookups_context_t *c,
+                        ContextClosureLookupContext &lookup_context) const
   {
     if (unlikely (c->lookup_limit_exceeded ())) return;
-
-    return
     + hb_iter (rule)
     | hb_map (hb_add (this))
-    | hb_apply ([&] (const Rule &_) { _.closure_lookups (c); })
+    | hb_apply ([&] (const Rule &_) { _.closure_lookups (c, lookup_context); })
     ;
   }
 
@@ -1647,9 +1648,16 @@
 
   void closure_lookups (hb_closure_lookups_context_t *c) const
   {
-    + hb_iter (ruleSet)
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_glyph},
+      nullptr
+    };
+
+    + hb_zip (this+coverage, ruleSet)
+    | hb_filter (*c->glyphs, hb_first)
+    | hb_map (hb_second)
     | hb_map (hb_add (this))
-    | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c); })
+    | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c, lookup_context); })
     ;
   }
 
@@ -1700,7 +1708,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto *out = c->serializer->start_embed (*this);
@@ -1791,10 +1799,24 @@
 
   void closure_lookups (hb_closure_lookups_context_t *c) const
   {
+    if (!(this+coverage).intersects (c->glyphs))
+      return;
+
+    const ClassDef &class_def = this+classDef;
+
+    struct ContextClosureLookupContext lookup_context = {
+      {intersects_class},
+      &class_def
+    };
+
     + hb_iter (ruleSet)
     | hb_map (hb_add (this))
-    | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c); })
-    ;
+    | hb_enumerate
+    | hb_filter ([&] (const hb_pair_t<unsigned, const RuleSet &> p)
+    { return class_def.intersects_class (c->glyphs, p.first); })
+    | hb_map (hb_second)
+    | hb_apply ([&] (const RuleSet & _)
+    { _.closure_lookups (c, lookup_context); });
   }
 
   void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {}
@@ -1945,6 +1967,8 @@
 
   void closure_lookups (hb_closure_lookups_context_t *c) const
   {
+    if (!intersects (c->glyphs))
+      return;
     const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
     recurse_lookups (c, lookupCount, lookupRecord);
   }
@@ -2238,9 +2262,11 @@
 				  lookup_context);
   }
 
-  void closure_lookups (hb_closure_lookups_context_t *c) const
+  void closure_lookups (hb_closure_lookups_context_t *c,
+                        ChainContextClosureLookupContext &lookup_context) const
   {
     if (unlikely (c->lookup_limit_exceeded ())) return;
+    if (!intersects (c->glyphs, lookup_context)) return;
 
     const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
     const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
@@ -2328,12 +2354,19 @@
 				       | hb_map (mapping));
 
     const ArrayOf<LookupRecord> &lookupRecord = StructAfter<ArrayOf<LookupRecord>> (lookahead);
-    HBUINT16 lookupCount;
-    lookupCount = lookupRecord.len;
-    if (!c->copy (lookupCount)) return_trace (nullptr);
 
-    for (unsigned i = 0; i < (unsigned) lookupCount; i++)
+    HBUINT16* lookupCount = c->embed (&(lookupRecord.len));
+    if (!lookupCount) return_trace (nullptr);
+
+    for (unsigned i = 0; i < lookupRecord.len; i++)
+    {
+      if (!lookup_map->has (lookupRecord[i].lookupListIndex))
+      {
+        (*lookupCount)--;
+        continue;
+      }
       if (!c->copy (lookupRecord[i], lookup_map)) return_trace (nullptr);
+    }
 
     return_trace (out);
   }
@@ -2351,7 +2384,7 @@
 
     if (!backtrack_map)
     {
-      const hb_set_t &glyphset = *c->plan->glyphset ();
+      const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
       if (!hb_all (backtrack, glyphset) ||
 	  !hb_all (input, glyphset) ||
 	  !hb_all (lookahead, glyphset))
@@ -2424,14 +2457,14 @@
     ;
   }
 
-  void closure_lookups (hb_closure_lookups_context_t *c) const
+  void closure_lookups (hb_closure_lookups_context_t *c,
+                        ChainContextClosureLookupContext &lookup_context) const
   {
     if (unlikely (c->lookup_limit_exceeded ())) return;
 
-    return
     + hb_iter (rule)
     | hb_map (hb_add (this))
-    | hb_apply ([&] (const ChainRule &_) { _.closure_lookups (c); })
+    | hb_apply ([&] (const ChainRule &_) { _.closure_lookups (c, lookup_context); })
     ;
   }
 
@@ -2552,9 +2585,16 @@
 
   void closure_lookups (hb_closure_lookups_context_t *c) const
   {
-    + hb_iter (ruleSet)
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_glyph},
+      {nullptr, nullptr, nullptr}
+    };
+
+    + hb_zip (this+coverage, ruleSet)
+    | hb_filter (*c->glyphs, hb_first)
+    | hb_map (hb_second)
     | hb_map (hb_add (this))
-    | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c); })
+    | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c, lookup_context); })
     ;
   }
 
@@ -2604,7 +2644,7 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto *out = c->serializer->start_embed (*this);
@@ -2701,9 +2741,28 @@
 
   void closure_lookups (hb_closure_lookups_context_t *c) const
   {
+    if (!(this+coverage).intersects (c->glyphs))
+      return;
+
+    const ClassDef &backtrack_class_def = this+backtrackClassDef;
+    const ClassDef &input_class_def = this+inputClassDef;
+    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
+
+    struct ChainContextClosureLookupContext lookup_context = {
+      {intersects_class},
+      {&backtrack_class_def,
+       &input_class_def,
+       &lookahead_class_def}
+    };
+
     + hb_iter (ruleSet)
     | hb_map (hb_add (this))
-    | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c); })
+    | hb_enumerate
+    | hb_filter([&] (unsigned klass)
+    { return input_class_def.intersects_class (c->glyphs, klass); }, hb_first)
+    | hb_map (hb_second)
+    | hb_apply ([&] (const ChainRuleSet &_)
+    { _.closure_lookups (c, lookup_context); })
     ;
   }
 
@@ -2794,9 +2853,10 @@
     if (unlikely (!c->serializer->check_success (!lookahead_klass_map.in_error ())))
       return_trace (false);
 
-    unsigned non_zero_index = 0, index = 0;
+    int non_zero_index = -1, index = 0;
     bool ret = true;
     const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups;
+    auto last_non_zero = c->serializer->snapshot ();
     for (const OffsetTo<ChainRuleSet>& _ : + hb_enumerate (ruleSet)
 					   | hb_filter (input_klass_map, hb_first)
 					   | hb_map (hb_second))
@@ -2812,19 +2872,20 @@
 			       &backtrack_klass_map,
 			       &input_klass_map,
 			       &lookahead_klass_map))
+      {
+        last_non_zero = c->serializer->snapshot ();
 	non_zero_index = index;
+      }
 
       index++;
     }
 
     if (!ret) return_trace (ret);
 
-    //prune empty trailing ruleSets
-    --index;
-    while (index > non_zero_index)
-    {
-      out->ruleSet.pop ();
-      index--;
+    // prune empty trailing ruleSets
+    if (index > non_zero_index) {
+      c->serializer->revert (last_non_zero);
+      out->ruleSet.len = non_zero_index + 1;
     }
 
     return_trace (bool (out->ruleSet));
@@ -2908,6 +2969,9 @@
 
   void closure_lookups (hb_closure_lookups_context_t *c) const
   {
+    if (!intersects (c->glyphs))
+      return;
+
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
@@ -2986,13 +3050,16 @@
     TRACE_SERIALIZE (this);
     auto *out = c->serializer->start_embed<OffsetArrayOf<Coverage>> ();
 
-    if (unlikely (!c->serializer->allocate_size<HBUINT16> (HBUINT16::static_size))) return_trace (false);
+    if (unlikely (!c->serializer->allocate_size<HBUINT16> (HBUINT16::static_size)))
+      return_trace (false);
 
-    + it
-    | hb_apply (subset_offset_array (c, *out, base))
-    ;
+    for (auto& offset : it) {
+      auto *o = out->serialize_append (c->serializer);
+      if (unlikely (!o) || !o->serialize_subset (c, offset, base))
+        return_trace (false);
+    }
 
-    return_trace (out->len);
+    return_trace (true);
   }
 
   bool subset (hb_subset_context_t *c) const
@@ -3320,20 +3387,31 @@
     return_trace (true);
   }
 
-  void closure_features (const hb_map_t *lookup_indexes, /* IN */
-			 hb_set_t       *feature_indexes /* OUT */) const
+  void prune_features (const hb_map_t *lookup_indices, /* IN */
+                       hb_set_t       *feature_indices /* IN/OUT */) const
   {
-    unsigned int feature_count = hb_min (get_feature_count (), (unsigned) HB_MAX_FEATURES);
-    for (unsigned i = 0; i < feature_count; i++)
+#ifndef HB_NO_VAR
+    // This is the set of feature indices which have alternate versions defined
+    // if the FeatureVariation's table and the alternate version(s) intersect the
+    // set of lookup indices.
+    hb_set_t alternate_feature_indices;
+    if (version.to_int () >= 0x00010001u)
+      (this+featureVars).closure_features (lookup_indices, &alternate_feature_indices);
+    if (alternate_feature_indices.in_error()) {
+      feature_indices->successful = false;
+      return;
+    }
+#endif
+
+    for (unsigned i : feature_indices->iter())
     {
       const Feature& f = get_feature (i);
-      if ((!f.featureParams.is_null ()) || f.intersects_lookup_indexes (lookup_indexes))
-	feature_indexes->add (i);
+
+      if (f.featureParams.is_null ()
+          && !f.intersects_lookup_indexes (lookup_indices)
+          && !alternate_feature_indices.has (i))
+        feature_indices->del (i);
     }
-#ifndef HB_NO_VAR
-    if (version.to_int () >= 0x00010001u)
-      (this+featureVars).closure_features (lookup_indexes, feature_indexes);
-#endif
   }
 
   unsigned int get_size () const
@@ -3378,7 +3456,11 @@
 
       this->accels = (hb_ot_layout_lookup_accelerator_t *) calloc (this->lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t));
       if (unlikely (!this->accels))
+      {
 	this->lookup_count = 0;
+	this->table.destroy ();
+	this->table = hb_blob_get_empty ();
+      }
 
       for (unsigned int i = 0; i < this->lookup_count; i++)
 	this->accels[i].init (table->get_lookup (i));
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 7918f86..089c794 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -76,7 +76,7 @@
  * Tests whether a face includes any kerning data in the 'kern' table.
  * Does NOT test for kerning lookups in the GPOS table.
  *
- * Return value: %true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  **/
 bool
@@ -92,7 +92,7 @@
  * Tests whether a face includes any state-machine kerning in the 'kern' table.
  * Does NOT examine the GPOS table.
  *
- * Return value: %true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  **/
 bool
@@ -112,7 +112,7 @@
  *
  * Does NOT examine the GPOS table.
  *
- * Return value: %true is data found, false otherwise
+ * Return value: %true is data found, %false otherwise
  *
  **/
 bool
@@ -268,7 +268,7 @@
  *
  * Tests whether a face has any glyph classes defined in its GDEF table.
  *
- * Return value: %true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  **/
 hb_bool_t
@@ -322,7 +322,7 @@
  * @face: The #hb_face_t to work on
  * @glyph: The #hb_codepoint_t code point to query
  * @start_offset: offset of the first attachment point to retrieve
- * @point_count: (inout) (allow-none): Input = the maximum number of attachment points to return;
+ * @point_count: (inout) (optional): Input = the maximum number of attachment points to return;
  *               Output = the actual number of attachment points returned (may be zero)
  * @point_array: (out) (array length=point_count): The array of attachment points found for the query
  *
@@ -350,7 +350,7 @@
  * @direction: The #hb_direction_t text direction to use
  * @glyph: The #hb_codepoint_t code point to query
  * @start_offset: offset of the first caret position to retrieve
- * @caret_count: (inout) (allow-none): Input = the maximum number of caret positions to return;
+ * @caret_count: (inout) (optional): Input = the maximum number of caret positions to return;
  *               Output = the actual number of caret positions returned (may be zero)
  * @caret_array: (out) (array length=caret_count): The array of caret positions found for the query
  *
@@ -410,9 +410,9 @@
 /**
  * hb_ot_layout_table_get_script_tags:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @start_offset: offset of the first script tag to retrieve
- * @script_count: (inout) (allow-none): Input = the maximum number of script tags to return;
+ * @script_count: (inout) (optional): Input = the maximum number of script tags to return;
  *                Output = the actual number of script tags returned (may be zero)
  * @script_tags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query
  *
@@ -437,14 +437,14 @@
 /**
  * hb_ot_layout_table_find_script:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_tag: #hb_tag_t of the script tag requested
  * @script_index: (out): The index of the requested script tag
  *
  * Fetches the index if a given script tag in the specified face's GSUB table
  * or GPOS table.
  *
- * Return value: %true if the script is found, false otherwise
+ * Return value: %true if the script is found, %false otherwise
  *
  **/
 hb_bool_t
@@ -481,7 +481,7 @@
 /**
  * hb_ot_layout_table_choose_script:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_tags: Array of #hb_tag_t script tags
  * @script_index: (out): The index of the requested script tag
  * @chosen_script: (out): #hb_tag_t of the script tag requested
@@ -504,11 +504,22 @@
 /**
  * hb_ot_layout_table_select_script:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_count: Number of script tags in the array
  * @script_tags: Array of #hb_tag_t script tags
- * @script_index: (out): The index of the requested script
- * @chosen_script: (out): #hb_tag_t of the requested script
+ * @script_index: (out) (optional): The index of the requested script
+ * @chosen_script: (out) (optional): #hb_tag_t of the requested script
+ *
+ * Selects an OpenType script for @table_tag from the @script_tags array.
+ *
+ * If the table does not have any of the requested scripts, then `DFLT`,
+ * `dflt`, and `latn` tags are tried in that order. If the table still does not
+ * have any of these scripts, @script_index and @chosen_script are set to
+ * #HB_OT_LAYOUT_NO_SCRIPT_INDEX.
+ *
+ * Return value:
+ * %true if one of the requested scripts is selected, %false if a fallback
+ * script is selected or if no scripts are selected.
  *
  * Since: 2.0.0
  **/
@@ -566,9 +577,9 @@
 /**
  * hb_ot_layout_table_get_feature_tags:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @start_offset: offset of the first feature tag to retrieve
- * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return;
+ * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
  *                 Output = the actual number of feature tags returned (may be zero)
  * @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table
  *
@@ -591,14 +602,14 @@
 /**
  * hb_ot_layout_table_find_feature:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @feature_tag: The #hb_tag_t og the requested feature tag
  * @feature_index: (out): The index of the requested feature
  *
  * Fetches the index for a given feature tag in the specified face's GSUB table
  * or GPOS table.
  *
- * Return value: %true if the feature is found, false otherwise
+ * Return value: %true if the feature is found, %false otherwise
  **/
 bool
 hb_ot_layout_table_find_feature (hb_face_t    *face,
@@ -626,10 +637,10 @@
 /**
  * hb_ot_layout_script_get_language_tags:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_index: The index of the requested script tag
  * @start_offset: offset of the first language tag to retrieve
- * @language_count: (inout) (allow-none): Input = the maximum number of language tags to return;
+ * @language_count: (inout) (optional): Input = the maximum number of language tags to return;
  *                  Output = the actual number of language tags returned (may be zero)
  * @language_tags: (out) (array length=language_count): Array of language tags found in the table
  *
@@ -655,7 +666,7 @@
 /**
  * hb_ot_layout_script_find_language:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_index: The index of the requested script tag
  * @language_tag: The #hb_tag_t of the requested language
  * @language_index: The index of the requested language
@@ -663,7 +674,7 @@
  * Fetches the index of a given language tag in the specified face's GSUB table
  * or GPOS table, underneath the specified script tag.
  *
- * Return value: %true if the language tag is found, false otherwise
+ * Return value: %true if the language tag is found, %false otherwise
  *
  * Since: ??
  * Deprecated: ??
@@ -688,7 +699,7 @@
 /**
  * hb_ot_layout_script_select_language:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_index: The index of the requested script tag
  * @language_count: The number of languages in the specified script
  * @language_tags: The array of language tags
@@ -697,7 +708,7 @@
  * Fetches the index of a given language tag in the specified face's GSUB table
  * or GPOS table, underneath the specified script index.
  *
- * Return value: %true if the language tag is found, false otherwise
+ * Return value: %true if the language tag is found, %false otherwise
  *
  * Since: 2.0.0
  **/
@@ -731,7 +742,7 @@
 /**
  * hb_ot_layout_language_get_required_feature_index:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_index: The index of the requested script tag
  * @language_index: The index of the requested language tag
  * @feature_index: (out): The index of the requested feature
@@ -739,7 +750,7 @@
  * Fetches the index of a requested feature in the given face's GSUB or GPOS table,
  * underneath the specified script and language.
  *
- * Return value: %true if the feature is found, false otherwise
+ * Return value: %true if the feature is found, %false otherwise
  *
  **/
 hb_bool_t
@@ -761,7 +772,7 @@
 /**
  * hb_ot_layout_language_get_required_feature:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_index: The index of the requested script tag
  * @language_index: The index of the requested language tag
  * @feature_index: (out): The index of the requested feature
@@ -770,7 +781,7 @@
  * Fetches the tag of a requested feature index in the given face's GSUB or GPOS table,
  * underneath the specified script and language.
  *
- * Return value: %true if the feature is found, false otherwise
+ * Return value: %true if the feature is found, %false otherwise
  *
  * Since: 0.9.30
  **/
@@ -796,11 +807,11 @@
 /**
  * hb_ot_layout_language_get_feature_indexes:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_index: The index of the requested script tag
  * @language_index: The index of the requested language tag
  * @start_offset: offset of the first feature tag to retrieve
- * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return;
+ * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
  *                 Output: the actual number of feature tags returned (may be zero)
  * @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query
  *
@@ -827,11 +838,11 @@
 /**
  * hb_ot_layout_language_get_feature_tags:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_index: The index of the requested script tag
  * @language_index: The index of the requested language tag
  * @start_offset: offset of the first feature tag to retrieve
- * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return;
+ * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
  *                 Output = the actual number of feature tags returned (may be zero)
  * @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query
  *
@@ -868,7 +879,7 @@
 /**
  * hb_ot_layout_language_find_feature:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_index: The index of the requested script tag
  * @language_index: The index of the requested language tag
  * @feature_tag: #hb_tag_t of the feature tag requested
@@ -877,7 +888,7 @@
  * Fetches the index of a given feature tag in the specified face's GSUB table
  * or GPOS table, underneath the specified script and language.
  *
- * Return value: %true if the feature is found, false otherwise
+ * Return value: %true if the feature is found, %false otherwise
  *
  **/
 hb_bool_t
@@ -910,10 +921,10 @@
 /**
  * hb_ot_layout_feature_get_lookups:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @feature_index: The index of the requested feature
  * @start_offset: offset of the first lookup to retrieve
- * @lookup_count: (inout) (allow-none): Input = the maximum number of lookups to return;
+ * @lookup_count: (inout) (optional): Input = the maximum number of lookups to return;
  *                Output = the actual number of lookups returned (may be zero)
  * @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query
  *
@@ -944,7 +955,7 @@
 /**
  * hb_ot_layout_table_get_lookup_count:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  *
  * Fetches the total number of lookups enumerated in the specified
  * face's GSUB table or GPOS table.
@@ -1101,7 +1112,7 @@
 /**
  * hb_ot_layout_collect_features:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @scripts: The array of scripts to collect features for
  * @languages: The array of languages to collect features for
  * @features: The array of features to collect
@@ -1152,7 +1163,7 @@
 /**
  * hb_ot_layout_collect_lookups:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @scripts: The array of scripts to collect lookups for
  * @languages: The array of languages to collect lookups for
  * @features: The array of features to collect lookups for
@@ -1191,7 +1202,7 @@
 /**
  * hb_ot_layout_lookup_collect_glyphs:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @lookup_index: The index of the feature lookup to query
  * @glyphs_before: (out): Array of glyphs preceding the substitution range
  * @glyphs_input: (out): Array of input glyphs that would be substituted by the lookup
@@ -1243,7 +1254,7 @@
 /**
  * hb_ot_layout_table_find_feature_variations:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @coords: The variation coordinates to query
  * @num_coords: The number of variation coordinates
  * @variations_index: (out): The array of feature variations found for the query
@@ -1268,11 +1279,11 @@
 /**
  * hb_ot_layout_feature_with_variations_get_lookups:
  * @face: #hb_face_t to work upon
- * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @feature_index: The index of the feature to query
  * @variations_index: The index of the feature variation to query
  * @start_offset: offset of the first lookup to retrieve
- * @lookup_count: (inout) (allow-none): Input = the maximum number of lookups to return;
+ * @lookup_count: (inout) (optional): Input = the maximum number of lookups to return;
  *                Output = the actual number of lookups returned (may be zero)
  * @lookup_indexes: (out) (array length=lookup_count): The array of lookups found for the query
  *
@@ -1310,7 +1321,7 @@
  *
  * Tests whether the specified face includes any GSUB substitutions.
  *
- * Return value: %true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  **/
 hb_bool_t
@@ -1331,7 +1342,7 @@
  * Tests whether a specified lookup in the specified face would
  * trigger a substitution on the given glyph sequence.
  *
- * Return value: %true if a substitution would be triggered, false otherwise
+ * Return value: %true if a substitution would be triggered, %false otherwise
  *
  * Since: 0.9.7
  **/
@@ -1488,7 +1499,9 @@
  * hb_ot_layout_has_positioning:
  * @face: #hb_face_t to work upon
  *
- * Return value: %true if the face has GPOS data, false otherwise
+ * Tests whether the specified face includes any GPOS positioning.
+ *
+ * Return value: %true if the face has GPOS data, %false otherwise
  *
  **/
 hb_bool_t
@@ -1561,7 +1574,7 @@
  * For more information on this distinction, see the [`size` feature documentation](
  * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-size).
  *
- * Return value: %true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 0.9.10
  **/
@@ -1610,22 +1623,22 @@
  * @face: #hb_face_t to work upon
  * @table_tag: table tag to query, "GSUB" or "GPOS".
  * @feature_index: index of feature to query.
- * @label_id: (out) (allow-none): The ‘name’ table name ID that specifies a string
+ * @label_id: (out) (optional): The ‘name’ table name ID that specifies a string
  *            for a user-interface label for this feature. (May be NULL.)
- * @tooltip_id: (out) (allow-none): The ‘name’ table name ID that specifies a string
+ * @tooltip_id: (out) (optional): The ‘name’ table name ID that specifies a string
  *              that an application can use for tooltip text for this
  *              feature. (May be NULL.)
- * @sample_id: (out) (allow-none): The ‘name’ table name ID that specifies sample text
+ * @sample_id: (out) (optional): The ‘name’ table name ID that specifies sample text
  *             that illustrates the effect of this feature. (May be NULL.)
- * @num_named_parameters: (out) (allow-none):  Number of named parameters. (May be zero.)
- * @first_param_id: (out) (allow-none): The first ‘name’ table name ID used to specify
+ * @num_named_parameters: (out) (optional):  Number of named parameters. (May be zero.)
+ * @first_param_id: (out) (optional): The first ‘name’ table name ID used to specify
  *                  strings for user-interface labels for the feature
  *                  parameters. (Must be zero if numParameters is zero.)
  *
  * Fetches name indices from feature parameters for "Stylistic Set" ('ssXX') or
  * "Character Variant" ('cvXX') features.
  *
- * Return value: %true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 2.0.0
  **/
@@ -1685,7 +1698,7 @@
  * @table_tag: table tag to query, "GSUB" or "GPOS".
  * @feature_index: index of feature to query.
  * @start_offset: offset of the first character to retrieve
- * @char_count: (inout) (allow-none): Input = the maximum number of characters to return;
+ * @char_count: (inout) (optional): Input = the maximum number of characters to return;
  *              Output = the actual number of characters returned (may be zero)
  * @characters: (out caller-allocates) (array length=char_count): A buffer pointer.
  *              The Unicode codepoints of the characters for which this feature provides
@@ -1881,7 +1894,7 @@
   GSUBProxy proxy (font->face);
   if (!buffer->message (font, "start table GSUB")) return;
   apply (proxy, plan, font, buffer);
-  (void)buffer->message (font, "end table GSUB");
+  (void) buffer->message (font, "end table GSUB");
 }
 
 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
@@ -1889,7 +1902,7 @@
   GPOSProxy proxy (font->face);
   if (!buffer->message (font, "start table GPOS")) return;
   apply (proxy, plan, font, buffer);
-  (void)buffer->message (font, "end table GPOS");
+  (void) buffer->message (font, "end table GPOS");
 }
 
 void
@@ -1907,7 +1920,7 @@
  * @baseline_tag: a baseline tag
  * @direction: text direction.
  * @script_tag:  script tag.
- * @language_tag: language tag.
+ * @language_tag: language tag, currently unused.
  * @coord: (out): baseline value if found.
  *
  * Fetches a baseline value from the face.
@@ -1964,7 +1977,7 @@
  * @lookup_index: index of the feature lookup to query.
  * @glyph: a glyph id.
  * @start_offset: starting offset.
- * @alternate_count: (inout) (allow-none): Input = the maximum number of alternate glyphs to return;
+ * @alternate_count: (inout) (optional): Input = the maximum number of alternate glyphs to return;
  *                   Output = the actual number of alternate glyphs returned (may be zero).
  * @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer.
  *                    Alternate glyphs associated with the glyph id.
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index 545d5f7..d47ba0f 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-ot.h> instead."
 #endif
 
@@ -38,10 +38,35 @@
 HB_BEGIN_DECLS
 
 
+/**
+ * HB_OT_TAG_BASE:
+ *
+ * OpenType [Baseline Table](https://docs.microsoft.com/en-us/typography/opentype/spec/base).
+ */
 #define HB_OT_TAG_BASE HB_TAG('B','A','S','E')
+/**
+ * HB_OT_TAG_GDEF:
+ *
+ * OpenType [Glyph Definition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gdef).
+ */
 #define HB_OT_TAG_GDEF HB_TAG('G','D','E','F')
+/**
+ * HB_OT_TAG_GSUB:
+ *
+ * OpenType [Glyph Substitution Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gsub).
+ */
 #define HB_OT_TAG_GSUB HB_TAG('G','S','U','B')
+/**
+ * HB_OT_TAG_GPOS:
+ *
+ * OpenType [Glyph Positioning Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos).
+ */
 #define HB_OT_TAG_GPOS HB_TAG('G','P','O','S')
+/**
+ * HB_OT_TAG_JSTF:
+ *
+ * OpenType [Justification Table](https://docs.microsoft.com/en-us/typography/opentype/spec/jstf).
+ */
 #define HB_OT_TAG_JSTF HB_TAG('J','S','T','F')
 
 
@@ -49,18 +74,34 @@
  * Script & Language tags.
  */
 
+/**
+ * HB_OT_TAG_DEFAULT_SCRIPT:
+ *
+ * OpenType script tag, `DFLT`, for features that are not script-specific.
+ *
+ */
 #define HB_OT_TAG_DEFAULT_SCRIPT	HB_TAG ('D', 'F', 'L', 'T')
+/**
+ * HB_OT_TAG_DEFAULT_LANGUAGE:
+ *
+ * OpenType language tag, `dflt`. Not a valid language tag, but some fonts
+ * mistakenly use it.
+ */
 #define HB_OT_TAG_DEFAULT_LANGUAGE	HB_TAG ('d', 'f', 'l', 't')
 
 /**
  * HB_OT_MAX_TAGS_PER_SCRIPT:
  *
+ * Maximum number of OpenType tags that can correspond to a give #hb_script_t.
+ *
  * Since: 2.0.0
  **/
 #define HB_OT_MAX_TAGS_PER_SCRIPT	3u
 /**
  * HB_OT_MAX_TAGS_PER_LANGUAGE:
  *
+ * Maximum number of OpenType tags that can correspond to a give #hb_language_t.
+ *
  * Since: 2.0.0
  **/
 #define HB_OT_MAX_TAGS_PER_LANGUAGE	3u
@@ -144,9 +185,29 @@
  * GSUB/GPOS feature query and enumeration interface
  */
 
+/**
+ * HB_OT_LAYOUT_NO_SCRIPT_INDEX:
+ *
+ * Special value for script index indicating unsupported script.
+ */
 #define HB_OT_LAYOUT_NO_SCRIPT_INDEX		0xFFFFu
+/**
+ * HB_OT_LAYOUT_NO_FEATURE_INDEX:
+ *
+ * Special value for feature index indicating unsupported feature.
+ */
 #define HB_OT_LAYOUT_NO_FEATURE_INDEX		0xFFFFu
+/**
+ * HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX:
+ *
+ * Special value for language index indicating default or unsupported language.
+ */
 #define HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX	0xFFFFu
+/**
+ * HB_OT_LAYOUT_NO_VARIATIONS_INDEX:
+ *
+ * Special value for variations index indicating unsupported variation.
+ */
 #define HB_OT_LAYOUT_NO_VARIATIONS_INDEX	0xFFFFFFFFu
 
 HB_EXTERN unsigned int
@@ -433,7 +494,7 @@
  * @HB_OT_LAYOUT_BASELINE_TAG_MATH: The baseline about which mathematical characters are centered.
  * In vertical writing mode when mathematical characters rotated 90 degrees clockwise, are centered.
  *
- * Baseline tags from https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags
+ * Baseline tags from [Baseline Tags](https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags) registry.
  *
  * Since: 2.6.0
  */
@@ -446,6 +507,7 @@
   HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT	= HB_TAG ('i','d','t','p'),
   HB_OT_LAYOUT_BASELINE_TAG_MATH			= HB_TAG ('m','a','t','h'),
 
+  /*< private >*/
   _HB_OT_LAYOUT_BASELINE_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
 } hb_ot_layout_baseline_tag_t;
 
diff --git a/src/hb-ot-math.cc b/src/hb-ot-math.cc
index 9d8c6e7..5781d25 100644
--- a/src/hb-ot-math.cc
+++ b/src/hb-ot-math.cc
@@ -56,7 +56,7 @@
  *
  * Tests whether a face has a `MATH` table.
  *
- * Return value: true if the table is found, false otherwise
+ * Return value: %true if the table is found, %false otherwise
  *
  * Since: 1.3.3
  **/
@@ -142,7 +142,7 @@
  *
  * Tests whether the given glyph index is an extended shape in the face.
  *
- * Return value: true if the glyph is an extended shape, false otherwise
+ * Return value: %true if the glyph is an extended shape, %false otherwise
  *
  * Since: 1.3.3
  **/
diff --git a/src/hb-ot-math.h b/src/hb-ot-math.h
index ad864a7..d3ffa19 100644
--- a/src/hb-ot-math.h
+++ b/src/hb-ot-math.h
@@ -24,7 +24,7 @@
  * Igalia Author(s): Frédéric Wang
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-ot.h> instead."
 #endif
 
@@ -40,18 +40,89 @@
  * MATH
  */
 
+/**
+ * HB_OT_TAG_MATH:
+ *
+ * OpenType [Mathematical Typesetting Table](https://docs.microsoft.com/en-us/typography/opentype/spec/math).
+ *
+ * Since: 1.3.3
+ */
 #define HB_OT_TAG_MATH HB_TAG('M','A','T','H')
 
-/* Use with hb_buffer_set_script() for math shaping. */
+/**
+ * HB_OT_MATH_SCRIPT:
+ *
+ * OpenType script tag for math shaping, for use with
+ * Use with hb_buffer_set_script().
+ *
+ * Since: 1.3.3
+ */
 #define HB_OT_MATH_SCRIPT HB_TAG('m','a','t','h')
 
 /* Types */
 
 /**
  * hb_ot_math_constant_t:
+ * @HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN: scriptPercentScaleDown
+ * @HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN: scriptScriptPercentScaleDown
+ * @HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT: delimitedSubFormulaMinHeight
+ * @HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT: displayOperatorMinHeight
+ * @HB_OT_MATH_CONSTANT_MATH_LEADING: mathLeading
+ * @HB_OT_MATH_CONSTANT_AXIS_HEIGHT: axisHeight
+ * @HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT: accentBaseHeight
+ * @HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT: flattenedAccentBaseHeight
+ * @HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN: subscriptShiftDown
+ * @HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX: subscriptTopMax
+ * @HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN: subscriptBaselineDropMin
+ * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP: superscriptShiftUp
+ * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED: superscriptShiftUpCramped
+ * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN: superscriptBottomMin
+ * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX: superscriptBaselineDropMax
+ * @HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN: subSuperscriptGapMin
+ * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT: superscriptBottomMaxWithSubscript
+ * @HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT: spaceAfterScript
+ * @HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN: upperLimitGapMin
+ * @HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN: upperLimitBaselineRiseMin
+ * @HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN: lowerLimitGapMin
+ * @HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN: lowerLimitBaselineDropMin
+ * @HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP: stackTopShiftUp
+ * @HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP: stackTopDisplayStyleShiftUp
+ * @HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN: stackBottomShiftDown
+ * @HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN: stackBottomDisplayStyleShiftDown
+ * @HB_OT_MATH_CONSTANT_STACK_GAP_MIN: stackGapMin
+ * @HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN: stackDisplayStyleGapMin
+ * @HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP: stretchStackTopShiftUp
+ * @HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN: stretchStackBottomShiftDown
+ * @HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN: stretchStackGapAboveMin
+ * @HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN: stretchStackGapBelowMin
+ * @HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP: fractionNumeratorShiftUp
+ * @HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP: fractionNumeratorDisplayStyleShiftUp
+ * @HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN: fractionDenominatorShiftDown
+ * @HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN: fractionDenominatorDisplayStyleShiftDown
+ * @HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN: fractionNumeratorGapMin
+ * @HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN: fractionNumDisplayStyleGapMin
+ * @HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS: fractionRuleThickness
+ * @HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN: fractionDenominatorGapMin
+ * @HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN: fractionDenomDisplayStyleGapMin
+ * @HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP: skewedFractionHorizontalGap
+ * @HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP: skewedFractionVerticalGap
+ * @HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP: overbarVerticalGap
+ * @HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS: overbarRuleThickness
+ * @HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER: overbarExtraAscender
+ * @HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP: underbarVerticalGap
+ * @HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS: underbarRuleThickness
+ * @HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER: underbarExtraDescender
+ * @HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP: radicalVerticalGap
+ * @HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP: radicalDisplayStyleVerticalGap
+ * @HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS: radicalRuleThickness
+ * @HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER: radicalExtraAscender
+ * @HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE: radicalKernBeforeDegree
+ * @HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE: radicalKernAfterDegree
+ * @HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT: radicalDegreeBottomRaisePercent
  *
- * The 'MATH' table constants specified at
- * https://docs.microsoft.com/en-us/typography/opentype/spec/math
+ * The 'MATH' table constants, refer to
+ * [OpenType documentation](https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathconstants-table)
+ * For more explanations.
  *
  * Since: 1.3.3
  */
@@ -116,6 +187,10 @@
 
 /**
  * hb_ot_math_kern_t:
+ * @HB_OT_MATH_KERN_TOP_RIGHT: The top right corner of the glyph.
+ * @HB_OT_MATH_KERN_TOP_LEFT: The top left corner of the glyph.
+ * @HB_OT_MATH_KERN_BOTTOM_RIGHT: The bottom right corner of the glyph.
+ * @HB_OT_MATH_KERN_BOTTOM_LEFT: The bottom left corner of the glyph.
  *
  * The math kerning-table types defined for the four corners
  * of a glyph.
@@ -145,6 +220,8 @@
 
 /**
  * hb_ot_math_glyph_part_flags_t:
+ * @HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER: This is an extender glyph part that
+ * can be repeated to reach the desired length.
  *
  * Flags for math glyph parts.
  *
diff --git a/src/hb-ot-meta.cc b/src/hb-ot-meta.cc
index 54a0e10..35c8eb5 100644
--- a/src/hb-ot-meta.cc
+++ b/src/hb-ot-meta.cc
@@ -41,9 +41,11 @@
  * hb_ot_meta_get_entry_tags:
  * @face: a face object
  * @start_offset: iteration's start offset
- * @entries_count:(inout) (allow-none): buffer size as input, filled size as output
+ * @entries_count:(inout) (optional): buffer size as input, filled size as output
  * @entries: (out caller-allocates) (array length=entries_count): entries tags buffer
  *
+ * Fetches all available feature types.
+ *
  * Return value: Number of all available feature types.
  *
  * Since: 2.6.0
diff --git a/src/hb-ot-meta.h b/src/hb-ot-meta.h
index 0278d84..7748eb4 100644
--- a/src/hb-ot-meta.h
+++ b/src/hb-ot-meta.h
@@ -22,7 +22,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-ot.h> instead."
 #endif
 
@@ -54,6 +54,7 @@
   HB_OT_META_TAG_DESIGN_LANGUAGES	= HB_TAG ('d','l','n','g'),
   HB_OT_META_TAG_SUPPORTED_LANGUAGES	= HB_TAG ('s','l','n','g'),
 
+  /*< private >*/
   _HB_OT_META_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
 } hb_ot_meta_tag_t;
 
diff --git a/src/hb-ot-metrics.cc b/src/hb-ot-metrics.cc
index 181ac4d..72aeff8 100644
--- a/src/hb-ot-metrics.cc
+++ b/src/hb-ot-metrics.cc
@@ -33,6 +33,15 @@
 #include "hb-ot-face.hh"
 
 
+/**
+ * SECTION:hb-ot-metrics
+ * @title: hb-ot-metrics
+ * @short_description: OpenType Metrics
+ * @include: hb-ot.h
+ *
+ * Functions for fetching metrics from fonts.
+ **/
+
 static float
 _fix_ascender_descender (float value, hb_ot_metrics_tag_t metrics_tag)
 {
@@ -110,11 +119,11 @@
 
 /**
  * hb_ot_metrics_get_position:
- * @font: a #hb_font_t object.
+ * @font: an #hb_font_t object.
  * @metrics_tag: tag of metrics value you like to fetch.
  * @position: (out) (optional): result of metrics value from the font.
  *
- * It fetches metrics value corresponding to a given tag from a font.
+ * Fetches metrics value corresponding to @metrics_tag from @font.
  *
  * Returns: Whether found the requested metrics in the font.
  * Since: 2.6.0
@@ -184,10 +193,13 @@
 #ifndef HB_NO_VAR
 /**
  * hb_ot_metrics_get_variation:
- * @font:
- * @metrics_tag:
+ * @font: an #hb_font_t object.
+ * @metrics_tag: tag of metrics value you like to fetch.
  *
- * Returns:
+ * Fetches metrics value corresponding to @metrics_tag from @font with the
+ * current font variation settings applied.
+ *
+ * Returns: The requested metric value.
  *
  * Since: 2.6.0
  **/
@@ -199,10 +211,13 @@
 
 /**
  * hb_ot_metrics_get_x_variation:
- * @font:
- * @metrics_tag:
+ * @font: an #hb_font_t object.
+ * @metrics_tag: tag of metrics value you like to fetch.
  *
- * Returns:
+ * Fetches horizontal metrics value corresponding to @metrics_tag from @font
+ * with the current font variation settings applied.
+ *
+ * Returns: The requested metric value.
  *
  * Since: 2.6.0
  **/
@@ -214,10 +229,13 @@
 
 /**
  * hb_ot_metrics_get_y_variation:
- * @font:
- * @metrics_tag:
+ * @font: an #hb_font_t object.
+ * @metrics_tag: tag of metrics value you like to fetch.
  *
- * Returns:
+ * Fetches vertical metrics value corresponding to @metrics_tag from @font with
+ * the current font variation settings applied.
+ *
+ * Returns: The requested metric value.
  *
  * Since: 2.6.0
  **/
diff --git a/src/hb-ot-metrics.h b/src/hb-ot-metrics.h
index 42c7363..5841fc8 100644
--- a/src/hb-ot-metrics.h
+++ b/src/hb-ot-metrics.h
@@ -22,7 +22,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-ot.h> instead."
 #endif
 
@@ -66,7 +66,8 @@
  * @HB_OT_METRICS_TAG_UNDERLINE_SIZE: underline size.
  * @HB_OT_METRICS_TAG_UNDERLINE_OFFSET: underline offset.
  *
- * From https://docs.microsoft.com/en-us/typography/opentype/spec/mvar#value-tags
+ * Metric tags corresponding to [MVAR Value
+ * Tags](https://docs.microsoft.com/en-us/typography/opentype/spec/mvar#value-tags)
  *
  * Since: 2.6.0
  **/
@@ -100,6 +101,7 @@
   HB_OT_METRICS_TAG_UNDERLINE_SIZE		= HB_TAG ('u','n','d','s'),
   HB_OT_METRICS_TAG_UNDERLINE_OFFSET		= HB_TAG ('u','n','d','o'),
 
+  /*< private >*/
   _HB_OT_METRICS_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
 } hb_ot_metrics_tag_t;
 
diff --git a/src/hb-ot-name.cc b/src/hb-ot-name.cc
index 10122b8..4588226 100644
--- a/src/hb-ot-name.cc
+++ b/src/hb-ot-name.cc
@@ -46,7 +46,7 @@
 /**
  * hb_ot_name_list_names:
  * @face: font face.
- * @num_entries: (out) (allow-none): number of returned entries.
+ * @num_entries: (out) (optional): number of returned entries.
  *
  * Enumerates all available name IDs and language combinations. Returned
  * array is owned by the @face and should not be modified.  It can be
@@ -150,7 +150,7 @@
  * @face: font face.
  * @name_id: OpenType name identifier to fetch.
  * @language: language to fetch the name for.
- * @text_size: (inout) (allow-none): input size of @text buffer, and output size of
+ * @text_size: (inout) (optional): input size of @text buffer, and output size of
  *                                   text written to buffer.
  * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into.
  *
@@ -177,7 +177,7 @@
  * @face: font face.
  * @name_id: OpenType name identifier to fetch.
  * @language: language to fetch the name for.
- * @text_size: (inout) (allow-none): input size of @text buffer, and output size of
+ * @text_size: (inout) (optional): input size of @text buffer, and output size of
  *                                   text written to buffer.
  * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into.
  *
@@ -203,7 +203,7 @@
  * @face: font face.
  * @name_id: OpenType name identifier to fetch.
  * @language: language to fetch the name for.
- * @text_size: (inout) (allow-none): input size of @text buffer, and output size of
+ * @text_size: (inout) (optional): input size of @text buffer, and output size of
  *                                   text written to buffer.
  * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into.
  *
diff --git a/src/hb-ot-name.h b/src/hb-ot-name.h
index 3b4ad58..9359014 100644
--- a/src/hb-ot-name.h
+++ b/src/hb-ot-name.h
@@ -22,7 +22,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-ot.h> instead."
 #endif
 
@@ -88,8 +88,7 @@
  *
  * Since: 2.1.0
  **/
-typedef struct hb_ot_name_entry_t
-{
+typedef struct hb_ot_name_entry_t {
   hb_ot_name_id_t name_id;
   /*< private >*/
   hb_var_int_t    var;
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index 1e93f0e..1f244f9 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -33,7 +33,7 @@
 
 
 /* buffer var allocations */
-#define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
+#define arabic_shaping_action() complex_var_u8_auxiliary() /* arabic shaping action */
 
 #define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0
 
diff --git a/src/hb-ot-shape-complex-hangul.cc b/src/hb-ot-shape-complex-hangul.cc
index f5915f4..46d6559 100644
--- a/src/hb-ot-shape-complex-hangul.cc
+++ b/src/hb-ot-shape-complex-hangul.cc
@@ -119,7 +119,7 @@
 #define isHangulTone(u) (hb_in_range<hb_codepoint_t> ((u), 0x302Eu, 0x302Fu))
 
 /* buffer var allocations */
-#define hangul_shaping_feature() complex_var_u8_0() /* hangul jamo shaping feature */
+#define hangul_shaping_feature() complex_var_u8_auxiliary() /* hangul jamo shaping feature */
 
 static bool
 is_zero_width_char (hb_font_t *font,
diff --git a/src/hb-ot-shape-complex-indic-machine.hh b/src/hb-ot-shape-complex-indic-machine.hh
index 670b6bf..74bf3ca 100644
--- a/src/hb-ot-shape-complex-indic-machine.hh
+++ b/src/hb-ot-shape-complex-indic-machine.hh
@@ -31,8 +31,37 @@
 
 #include "hb.hh"
 
+enum indic_syllable_type_t {
+  indic_consonant_syllable,
+  indic_vowel_syllable,
+  indic_standalone_cluster,
+  indic_symbol_cluster,
+  indic_broken_cluster,
+  indic_non_indic_cluster,
+};
 
-#line 36 "hb-ot-shape-complex-indic-machine.hh"
+
+#line 45 "hb-ot-shape-complex-indic-machine.hh"
+#define indic_syllable_machine_ex_A 10u
+#define indic_syllable_machine_ex_C 1u
+#define indic_syllable_machine_ex_CM 17u
+#define indic_syllable_machine_ex_CS 19u
+#define indic_syllable_machine_ex_DOTTEDCIRCLE 12u
+#define indic_syllable_machine_ex_H 4u
+#define indic_syllable_machine_ex_M 7u
+#define indic_syllable_machine_ex_N 3u
+#define indic_syllable_machine_ex_PLACEHOLDER 11u
+#define indic_syllable_machine_ex_RS 13u
+#define indic_syllable_machine_ex_Ra 16u
+#define indic_syllable_machine_ex_Repha 15u
+#define indic_syllable_machine_ex_SM 8u
+#define indic_syllable_machine_ex_Symbol 18u
+#define indic_syllable_machine_ex_V 2u
+#define indic_syllable_machine_ex_ZWJ 6u
+#define indic_syllable_machine_ex_ZWNJ 5u
+
+
+#line 65 "hb-ot-shape-complex-indic-machine.hh"
 static const unsigned char _indic_syllable_machine_trans_keys[] = {
 	8u, 8u, 4u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 
 	4u, 13u, 4u, 8u, 8u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 6u, 6u, 16u, 16u, 
@@ -384,18 +413,18 @@
 static const int indic_syllable_machine_en_main = 39;
 
 
-#line 36 "hb-ot-shape-complex-indic-machine.rl"
+#line 46 "hb-ot-shape-complex-indic-machine.rl"
 
 
 
-#line 93 "hb-ot-shape-complex-indic-machine.rl"
+#line 102 "hb-ot-shape-complex-indic-machine.rl"
 
 
 #define found_syllable(syllable_type) \
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | indic_##syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
@@ -407,7 +436,7 @@
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 411 "hb-ot-shape-complex-indic-machine.hh"
+#line 440 "hb-ot-shape-complex-indic-machine.hh"
 	{
 	cs = indic_syllable_machine_start;
 	ts = 0;
@@ -415,7 +444,7 @@
 	act = 0;
 	}
 
-#line 113 "hb-ot-shape-complex-indic-machine.rl"
+#line 122 "hb-ot-shape-complex-indic-machine.rl"
 
 
   p = 0;
@@ -423,7 +452,7 @@
 
   unsigned int syllable_serial = 1;
   
-#line 427 "hb-ot-shape-complex-indic-machine.hh"
+#line 456 "hb-ot-shape-complex-indic-machine.hh"
 	{
 	int _slen;
 	int _trans;
@@ -437,7 +466,7 @@
 #line 1 "NONE"
 	{ts = p;}
 	break;
-#line 441 "hb-ot-shape-complex-indic-machine.hh"
+#line 470 "hb-ot-shape-complex-indic-machine.hh"
 	}
 
 	_keys = _indic_syllable_machine_trans_keys + (cs<<1);
@@ -460,64 +489,64 @@
 	{te = p+1;}
 	break;
 	case 11:
-#line 89 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p+1;{ found_syllable (non_indic_cluster); }}
+#line 98 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (indic_non_indic_cluster); }}
 	break;
 	case 13:
-#line 84 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (consonant_syllable); }}
+#line 93 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_consonant_syllable); }}
 	break;
 	case 14:
-#line 85 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (vowel_syllable); }}
+#line 94 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_vowel_syllable); }}
 	break;
 	case 17:
-#line 86 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (standalone_cluster); }}
+#line 95 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_standalone_cluster); }}
 	break;
 	case 19:
-#line 87 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (symbol_cluster); }}
+#line 96 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_symbol_cluster); }}
 	break;
 	case 15:
-#line 88 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (broken_cluster); }}
+#line 97 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_broken_cluster); }}
 	break;
 	case 16:
-#line 89 "hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ found_syllable (non_indic_cluster); }}
+#line 98 "hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (indic_non_indic_cluster); }}
 	break;
 	case 1:
-#line 84 "hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
+#line 93 "hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (indic_consonant_syllable); }}
 	break;
 	case 3:
-#line 85 "hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (vowel_syllable); }}
+#line 94 "hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (indic_vowel_syllable); }}
 	break;
 	case 7:
-#line 86 "hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (standalone_cluster); }}
+#line 95 "hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (indic_standalone_cluster); }}
 	break;
 	case 8:
-#line 87 "hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (symbol_cluster); }}
+#line 96 "hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (indic_symbol_cluster); }}
 	break;
 	case 4:
-#line 88 "hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
+#line 97 "hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (indic_broken_cluster); }}
 	break;
 	case 6:
 #line 1 "NONE"
 	{	switch( act ) {
 	case 1:
-	{{p = ((te))-1;} found_syllable (consonant_syllable); }
+	{{p = ((te))-1;} found_syllable (indic_consonant_syllable); }
 	break;
 	case 5:
-	{{p = ((te))-1;} found_syllable (broken_cluster); }
+	{{p = ((te))-1;} found_syllable (indic_broken_cluster); }
 	break;
 	case 6:
-	{{p = ((te))-1;} found_syllable (non_indic_cluster); }
+	{{p = ((te))-1;} found_syllable (indic_non_indic_cluster); }
 	break;
 	}
 	}
@@ -525,22 +554,22 @@
 	case 18:
 #line 1 "NONE"
 	{te = p+1;}
-#line 84 "hb-ot-shape-complex-indic-machine.rl"
+#line 93 "hb-ot-shape-complex-indic-machine.rl"
 	{act = 1;}
 	break;
 	case 5:
 #line 1 "NONE"
 	{te = p+1;}
-#line 88 "hb-ot-shape-complex-indic-machine.rl"
+#line 97 "hb-ot-shape-complex-indic-machine.rl"
 	{act = 5;}
 	break;
 	case 12:
 #line 1 "NONE"
 	{te = p+1;}
-#line 89 "hb-ot-shape-complex-indic-machine.rl"
+#line 98 "hb-ot-shape-complex-indic-machine.rl"
 	{act = 6;}
 	break;
-#line 544 "hb-ot-shape-complex-indic-machine.hh"
+#line 573 "hb-ot-shape-complex-indic-machine.hh"
 	}
 
 _again:
@@ -549,7 +578,7 @@
 #line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 553 "hb-ot-shape-complex-indic-machine.hh"
+#line 582 "hb-ot-shape-complex-indic-machine.hh"
 	}
 
 	if ( ++p != pe )
@@ -565,7 +594,7 @@
 
 	}
 
-#line 121 "hb-ot-shape-complex-indic-machine.rl"
+#line 130 "hb-ot-shape-complex-indic-machine.rl"
 
 }
 
diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index 1613548..df9583f 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -29,32 +29,41 @@
 
 #include "hb.hh"
 
+enum indic_syllable_type_t {
+  indic_consonant_syllable,
+  indic_vowel_syllable,
+  indic_standalone_cluster,
+  indic_symbol_cluster,
+  indic_broken_cluster,
+  indic_non_indic_cluster,
+};
+
 %%{
   machine indic_syllable_machine;
   alphtype unsigned char;
+  write exports;
   write data;
 }%%
 
 %%{
 
-# Same order as enum indic_category_t.  Not sure how to avoid duplication.
-C    = 1;
-V    = 2;
-N    = 3;
-H    = 4;
-ZWNJ = 5;
-ZWJ  = 6;
-M    = 7;
-SM   = 8;
-A    = 10;
-PLACEHOLDER = 11;
-DOTTEDCIRCLE = 12;
-RS    = 13;
-Repha = 15;
-Ra    = 16;
-CM    = 17;
-Symbol= 18;
-CS    = 19;
+export C    = 1;
+export V    = 2;
+export N    = 3;
+export H    = 4;
+export ZWNJ = 5;
+export ZWJ  = 6;
+export M    = 7;
+export SM   = 8;
+export A    = 10;
+export PLACEHOLDER = 11;
+export DOTTEDCIRCLE = 12;
+export RS    = 13;
+export Repha = 15;
+export Ra    = 16;
+export CM    = 17;
+export Symbol= 18;
+export CS    = 19;
 
 c = (C | Ra);			# is_consonant
 n = ((ZWNJ?.RS)? (N.N?)?);	# is_consonant_modifier
@@ -81,12 +90,12 @@
 other =			any;
 
 main := |*
-	consonant_syllable	=> { found_syllable (consonant_syllable); };
-	vowel_syllable		=> { found_syllable (vowel_syllable); };
-	standalone_cluster	=> { found_syllable (standalone_cluster); };
-	symbol_cluster		=> { found_syllable (symbol_cluster); };
-	broken_cluster		=> { found_syllable (broken_cluster); };
-	other			=> { found_syllable (non_indic_cluster); };
+	consonant_syllable	=> { found_syllable (indic_consonant_syllable); };
+	vowel_syllable		=> { found_syllable (indic_vowel_syllable); };
+	standalone_cluster	=> { found_syllable (indic_standalone_cluster); };
+	symbol_cluster		=> { found_syllable (indic_symbol_cluster); };
+	broken_cluster		=> { found_syllable (indic_broken_cluster); };
+	other			=> { found_syllable (indic_non_indic_cluster); };
 *|;
 
 
@@ -96,7 +105,7 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | indic_##syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
diff --git a/src/hb-ot-shape-complex-indic-table.cc b/src/hb-ot-shape-complex-indic-table.cc
index a150fd2..dd204b2 100644
--- a/src/hb-ot-shape-complex-indic-table.cc
+++ b/src/hb-ot-shape-complex-indic-table.cc
@@ -82,7 +82,7 @@
 #define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M)
 
 
-static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = {
+static const uint16_t indic_table[] = {
 
 
 #define indic_offset_0x0028u 0
@@ -404,7 +404,7 @@
 
 }; /* Table items: 1792; occupancy: 70% */
 
-INDIC_TABLE_ELEMENT_TYPE
+uint16_t
 hb_indic_get_categories (hb_codepoint_t u)
 {
   switch (u >> 12)
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 34972f8..09742b3 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -29,6 +29,7 @@
 #ifndef HB_NO_OT_SHAPE
 
 #include "hb-ot-shape-complex-indic.hh"
+#include "hb-ot-shape-complex-indic-machine.hh"
 #include "hb-ot-shape-complex-vowel-constraints.hh"
 #include "hb-ot-layout.hh"
 
@@ -337,19 +338,6 @@
   return POS_BASE_C;
 }
 
-
-enum indic_syllable_type_t {
-  indic_consonant_syllable,
-  indic_vowel_syllable,
-  indic_standalone_cluster,
-  indic_symbol_cluster,
-  indic_broken_cluster,
-  indic_non_indic_cluster,
-};
-
-#include "hb-ot-shape-complex-indic-machine.hh"
-
-
 static void
 setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		   hb_buffer_t              *buffer,
@@ -938,79 +926,24 @@
   }
 }
 
-static inline void
-insert_dotted_circles_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
-			     hb_font_t *font,
-			     hb_buffer_t *buffer)
-{
-  if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
-    return;
-
-  /* Note: This loop is extra overhead, but should not be measurable.
-   * TODO Use a buffer scratch flag to remove the loop. */
-  bool has_broken_syllables = false;
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  for (unsigned int i = 0; i < count; i++)
-    if ((info[i].syllable() & 0x0F) == indic_broken_cluster)
-    {
-      has_broken_syllables = true;
-      break;
-    }
-  if (likely (!has_broken_syllables))
-    return;
-
-
-  hb_codepoint_t dottedcircle_glyph;
-  if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph))
-    return;
-
-  hb_glyph_info_t dottedcircle = {0};
-  dottedcircle.codepoint = 0x25CCu;
-  set_indic_properties (dottedcircle);
-  dottedcircle.codepoint = dottedcircle_glyph;
-
-  buffer->clear_output ();
-
-  buffer->idx = 0;
-  unsigned int last_syllable = 0;
-  while (buffer->idx < buffer->len && buffer->successful)
-  {
-    unsigned int syllable = buffer->cur().syllable();
-    indic_syllable_type_t syllable_type = (indic_syllable_type_t) (syllable & 0x0F);
-    if (unlikely (last_syllable != syllable && syllable_type == indic_broken_cluster))
-    {
-      last_syllable = syllable;
-
-      hb_glyph_info_t ginfo = dottedcircle;
-      ginfo.cluster = buffer->cur().cluster;
-      ginfo.mask = buffer->cur().mask;
-      ginfo.syllable() = buffer->cur().syllable();
-
-      /* Insert dottedcircle after possible Repha. */
-      while (buffer->idx < buffer->len && buffer->successful &&
-	     last_syllable == buffer->cur().syllable() &&
-	     buffer->cur().indic_category() == OT_Repha)
-	buffer->next_glyph ();
-
-      buffer->output_info (ginfo);
-    }
-    else
-      buffer->next_glyph ();
-  }
-  buffer->swap_buffers ();
-}
-
 static void
 initial_reordering_indic (const hb_ot_shape_plan_t *plan,
 			  hb_font_t *font,
 			  hb_buffer_t *buffer)
 {
+  if (!buffer->message (font, "start reordering indic initial"))
+    return;
+
   update_consonant_positions_indic (plan, font, buffer);
-  insert_dotted_circles_indic (plan, font, buffer);
+  hb_syllabic_insert_dotted_circles (font, buffer,
+				     indic_broken_cluster,
+				     OT_DOTTEDCIRCLE,
+				     OT_Repha);
 
   foreach_syllable (buffer, start, end)
     initial_reordering_syllable_indic (plan, font->face, buffer, start, end);
+
+  (void) buffer->message (font, "end reordering indic initial");
 }
 
 static void
@@ -1485,8 +1418,11 @@
   unsigned int count = buffer->len;
   if (unlikely (!count)) return;
 
-  foreach_syllable (buffer, start, end)
-    final_reordering_syllable_indic (plan, buffer, start, end);
+  if (buffer->message (font, "start reordering indic final")) {
+    foreach_syllable (buffer, start, end)
+      final_reordering_syllable_indic (plan, buffer, start, end);
+    (void) buffer->message (font, "end reordering indic final");
+  }
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
diff --git a/src/hb-ot-shape-complex-indic.hh b/src/hb-ot-shape-complex-indic.hh
index 41bd8bd..8c6e171 100644
--- a/src/hb-ot-shape-complex-indic.hh
+++ b/src/hb-ot-shape-complex-indic.hh
@@ -29,16 +29,14 @@
 
 #include "hb.hh"
 
-#include "hb-ot-shape-complex.hh"
+#include "hb-ot-shape-complex-syllabic.hh"
 
 
 /* buffer var allocations */
-#define indic_category() complex_var_u8_0() /* indic_category_t */
-#define indic_position() complex_var_u8_1() /* indic_position_t */
+#define indic_category() complex_var_u8_category() /* indic_category_t */
+#define indic_position() complex_var_u8_auxiliary() /* indic_position_t */
 
 
-#define INDIC_TABLE_ELEMENT_TYPE uint16_t
-
 /* Cateories used in the OpenType spec:
  * https://docs.microsoft.com/en-us/typography/script-development/devanagari
  */
@@ -194,7 +192,7 @@
     ) \
    )
 
-HB_INTERNAL INDIC_TABLE_ELEMENT_TYPE
+HB_INTERNAL uint16_t
 hb_indic_get_categories (hb_codepoint_t u);
 
 
@@ -307,17 +305,12 @@
   0x0D30u, /* Malayalam */	/* No Reph, Logical Repha */
 
   0x0DBBu, /* Sinhala */	/* Reph formed only with ZWJ */
-
-  0x179Au, /* Khmer */
 };
 
 static inline bool
 is_ra (hb_codepoint_t u)
 {
-  for (unsigned int i = 0; i < ARRAY_LENGTH (ra_chars); i++)
-    if (u == ra_chars[i])
-      return true;
-  return false;
+  return hb_array (ra_chars).lfind (u);
 }
 
 static inline void
@@ -325,7 +318,7 @@
 {
   hb_codepoint_t u = info.codepoint;
   unsigned int type = hb_indic_get_categories (u);
-  indic_category_t cat = (indic_category_t) (type & 0x7Fu);
+  indic_category_t cat = (indic_category_t) (type & 0xFFu);
   indic_position_t pos = (indic_position_t) (type >> 8);
 
 
diff --git a/src/hb-ot-shape-complex-khmer-machine.hh b/src/hb-ot-shape-complex-khmer-machine.hh
index a040318..82ab186 100644
--- a/src/hb-ot-shape-complex-khmer-machine.hh
+++ b/src/hb-ot-shape-complex-khmer-machine.hh
@@ -1,211 +1,179 @@
-
 #line 1 "hb-ot-shape-complex-khmer-machine.rl"
 /*
- * Copyright © 2011,2012  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
+* Copyright © 2011,2012  Google, Inc.
+*
+*  This is part of HarfBuzz, a text shaping library.
+*
+* Permission is hereby granted, without written agreement and without
+* license or royalty fees, to use, copy, modify, and distribute this
+* software and its documentation for any purpose, provided that the
+* above copyright notice and the following two paragraphs appear in
+* all copies of this software.
+*
+* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+* DAMAGE.
+*
+* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+* FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+*
+* Google Author(s): Behdad Esfahbod
+*/
 
 #ifndef HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
 
 #include "hb.hh"
 
-
-#line 36 "hb-ot-shape-complex-khmer-machine.hh"
-static const unsigned char _khmer_syllable_machine_trans_keys[] = {
-	5u, 26u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, 5u, 21u, 
-	5u, 26u, 5u, 21u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, 
-	5u, 21u, 5u, 26u, 5u, 21u, 5u, 26u, 1u, 29u, 5u, 29u, 5u, 29u, 5u, 29u, 
-	22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 26u, 5u, 29u, 
-	5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 29u, 5u, 29u, 
-	0
+enum khmer_syllable_type_t {
+	khmer_consonant_syllable,
+	khmer_broken_cluster,
+	khmer_non_khmer_cluster,
 };
 
-static const char _khmer_syllable_machine_key_spans[] = {
-	22, 17, 22, 17, 16, 17, 22, 17, 
-	22, 17, 17, 22, 17, 16, 17, 22, 
-	17, 22, 17, 22, 29, 25, 25, 25, 
-	1, 18, 25, 25, 25, 16, 22, 25, 
-	25, 1, 18, 25, 25, 16, 25, 25
+
+#line 41 "hb-ot-shape-complex-khmer-machine.hh"
+#define khmer_syllable_machine_ex_C 1u
+#define khmer_syllable_machine_ex_Coeng 14u
+#define khmer_syllable_machine_ex_DOTTEDCIRCLE 12u
+#define khmer_syllable_machine_ex_PLACEHOLDER 11u
+#define khmer_syllable_machine_ex_Ra 16u
+#define khmer_syllable_machine_ex_Robatic 20u
+#define khmer_syllable_machine_ex_V 2u
+#define khmer_syllable_machine_ex_VAbv 26u
+#define khmer_syllable_machine_ex_VBlw 27u
+#define khmer_syllable_machine_ex_VPre 28u
+#define khmer_syllable_machine_ex_VPst 29u
+#define khmer_syllable_machine_ex_Xgroup 21u
+#define khmer_syllable_machine_ex_Ygroup 22u
+#define khmer_syllable_machine_ex_ZWJ 6u
+#define khmer_syllable_machine_ex_ZWNJ 5u
+
+
+#line 59 "hb-ot-shape-complex-khmer-machine.hh"
+static const unsigned char _khmer_syllable_machine_trans_keys[] = {
+	2u, 8u, 2u, 6u, 2u, 8u, 2u, 6u,
+	0u, 0u, 2u, 6u, 2u, 8u, 2u, 6u,
+	2u, 8u, 2u, 6u, 2u, 6u, 2u, 8u,
+	2u, 6u, 0u, 0u, 2u, 6u, 2u, 8u,
+	2u, 6u, 2u, 8u, 2u, 6u, 2u, 8u,
+	0u, 11u, 2u, 11u, 2u, 11u, 2u, 11u,
+	7u, 7u, 2u, 7u, 2u, 11u, 2u, 11u,
+	2u, 11u, 0u, 0u, 2u, 8u, 2u, 11u,
+	2u, 11u, 7u, 7u, 2u, 7u, 2u, 11u,
+	2u, 11u, 0u, 0u, 2u, 11u, 2u, 11u,
+	0u
+};
+
+static const signed char _khmer_syllable_machine_char_class[] = {
+	0, 0, 1, 1, 2, 2, 1, 1,
+	1, 1, 3, 3, 1, 4, 1, 0,
+	1, 1, 1, 5, 6, 7, 1, 1,
+	1, 8, 9, 10, 11, 0
 };
 
 static const short _khmer_syllable_machine_index_offsets[] = {
-	0, 23, 41, 64, 82, 99, 117, 140, 
-	158, 181, 199, 217, 240, 258, 275, 293, 
-	316, 334, 357, 375, 398, 428, 454, 480, 
-	506, 508, 527, 553, 579, 605, 622, 645, 
-	671, 697, 699, 718, 744, 770, 787, 813
+	0, 7, 12, 19, 24, 25, 30, 37,
+	42, 49, 54, 59, 66, 71, 72, 77,
+	84, 89, 96, 101, 108, 120, 130, 140,
+	150, 151, 157, 167, 177, 187, 188, 195,
+	205, 215, 216, 222, 232, 242, 243, 253,
+	0
 };
 
-static const char _khmer_syllable_machine_indicies[] = {
-	1, 1, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 2, 
-	3, 0, 0, 0, 0, 4, 0, 1, 
-	1, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 3, 
-	0, 1, 1, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 3, 0, 0, 0, 0, 4, 0, 
-	5, 5, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	4, 0, 6, 6, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 6, 0, 7, 7, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 8, 0, 9, 9, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 10, 0, 0, 
-	0, 0, 4, 0, 9, 9, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 10, 0, 11, 11, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 12, 0, 
-	0, 0, 0, 4, 0, 11, 11, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 12, 0, 14, 
-	14, 13, 13, 13, 13, 13, 13, 13, 
-	13, 13, 13, 13, 13, 13, 13, 15, 
-	13, 14, 14, 16, 16, 16, 16, 16, 
-	16, 16, 16, 16, 16, 16, 16, 16, 
-	16, 15, 16, 16, 16, 16, 17, 16, 
-	18, 18, 16, 16, 16, 16, 16, 16, 
-	16, 16, 16, 16, 16, 16, 16, 16, 
-	17, 16, 19, 19, 16, 16, 16, 16, 
-	16, 16, 16, 16, 16, 16, 16, 16, 
-	16, 19, 16, 20, 20, 16, 16, 16, 
-	16, 16, 16, 16, 16, 16, 16, 16, 
-	16, 16, 16, 21, 16, 22, 22, 16, 
-	16, 16, 16, 16, 16, 16, 16, 16, 
-	16, 16, 16, 16, 16, 23, 16, 16, 
-	16, 16, 17, 16, 22, 22, 16, 16, 
-	16, 16, 16, 16, 16, 16, 16, 16, 
-	16, 16, 16, 16, 23, 16, 24, 24, 
-	16, 16, 16, 16, 16, 16, 16, 16, 
-	16, 16, 16, 16, 16, 16, 25, 16, 
-	16, 16, 16, 17, 16, 24, 24, 16, 
-	16, 16, 16, 16, 16, 16, 16, 16, 
-	16, 16, 16, 16, 16, 25, 16, 14, 
-	14, 16, 16, 16, 16, 16, 16, 16, 
-	16, 16, 16, 16, 16, 16, 26, 15, 
-	16, 16, 16, 16, 17, 16, 28, 28, 
-	27, 27, 29, 29, 27, 27, 27, 27, 
-	2, 2, 27, 30, 27, 28, 27, 27, 
-	27, 27, 15, 19, 27, 27, 27, 17, 
-	23, 25, 21, 27, 32, 32, 31, 31, 
-	31, 31, 31, 31, 31, 33, 31, 31, 
-	31, 31, 31, 2, 3, 6, 31, 31, 
-	31, 4, 10, 12, 8, 31, 34, 34, 
-	31, 31, 31, 31, 31, 31, 31, 35, 
-	31, 31, 31, 31, 31, 31, 3, 6, 
-	31, 31, 31, 4, 10, 12, 8, 31, 
-	5, 5, 31, 31, 31, 31, 31, 31, 
-	31, 35, 31, 31, 31, 31, 31, 31, 
-	4, 6, 31, 31, 31, 31, 31, 31, 
-	8, 31, 6, 31, 7, 7, 31, 31, 
-	31, 31, 31, 31, 31, 35, 31, 31, 
-	31, 31, 31, 31, 8, 6, 31, 36, 
-	36, 31, 31, 31, 31, 31, 31, 31, 
-	35, 31, 31, 31, 31, 31, 31, 10, 
-	6, 31, 31, 31, 4, 31, 31, 8, 
-	31, 37, 37, 31, 31, 31, 31, 31, 
-	31, 31, 35, 31, 31, 31, 31, 31, 
-	31, 12, 6, 31, 31, 31, 4, 10, 
-	31, 8, 31, 34, 34, 31, 31, 31, 
-	31, 31, 31, 31, 33, 31, 31, 31, 
-	31, 31, 31, 3, 6, 31, 31, 31, 
-	4, 10, 12, 8, 31, 28, 28, 31, 
-	31, 31, 31, 31, 31, 31, 31, 31, 
-	31, 31, 31, 31, 28, 31, 14, 14, 
-	38, 38, 38, 38, 38, 38, 38, 38, 
-	38, 38, 38, 38, 38, 38, 15, 38, 
-	38, 38, 38, 17, 38, 40, 40, 39, 
-	39, 39, 39, 39, 39, 39, 41, 39, 
-	39, 39, 39, 39, 39, 15, 19, 39, 
-	39, 39, 17, 23, 25, 21, 39, 18, 
-	18, 39, 39, 39, 39, 39, 39, 39, 
-	41, 39, 39, 39, 39, 39, 39, 17, 
-	19, 39, 39, 39, 39, 39, 39, 21, 
-	39, 19, 39, 20, 20, 39, 39, 39, 
-	39, 39, 39, 39, 41, 39, 39, 39, 
-	39, 39, 39, 21, 19, 39, 42, 42, 
-	39, 39, 39, 39, 39, 39, 39, 41, 
-	39, 39, 39, 39, 39, 39, 23, 19, 
-	39, 39, 39, 17, 39, 39, 21, 39, 
-	43, 43, 39, 39, 39, 39, 39, 39, 
-	39, 41, 39, 39, 39, 39, 39, 39, 
-	25, 19, 39, 39, 39, 17, 23, 39, 
-	21, 39, 44, 44, 39, 39, 39, 39, 
-	39, 39, 39, 39, 39, 39, 39, 39, 
-	39, 44, 39, 45, 45, 39, 39, 39, 
-	39, 39, 39, 39, 30, 39, 39, 39, 
-	39, 39, 26, 15, 19, 39, 39, 39, 
-	17, 23, 25, 21, 39, 40, 40, 39, 
-	39, 39, 39, 39, 39, 39, 30, 39, 
-	39, 39, 39, 39, 39, 15, 19, 39, 
-	39, 39, 17, 23, 25, 21, 39, 0
+static const signed char _khmer_syllable_machine_indicies[] = {
+	1, 0, 0, 2, 3, 0, 4, 1,
+	0, 0, 0, 3, 1, 0, 0, 0,
+	3, 0, 4, 5, 0, 0, 0, 4,
+	6, 7, 0, 0, 0, 8, 9, 0,
+	0, 0, 10, 0, 4, 9, 0, 0,
+	0, 10, 11, 0, 0, 0, 12, 0,
+	4, 11, 0, 0, 0, 12, 14, 13,
+	13, 13, 15, 14, 16, 16, 16, 15,
+	16, 17, 18, 16, 16, 16, 17, 19,
+	20, 16, 16, 16, 21, 22, 16, 16,
+	16, 23, 16, 17, 22, 16, 16, 16,
+	23, 24, 16, 16, 16, 25, 16, 17,
+	24, 16, 16, 16, 25, 14, 16, 16,
+	26, 15, 16, 17, 29, 28, 30, 2,
+	31, 28, 15, 19, 17, 23, 25, 21,
+	33, 32, 34, 2, 3, 6, 4, 10,
+	12, 8, 35, 32, 36, 32, 3, 6,
+	4, 10, 12, 8, 5, 32, 36, 32,
+	4, 6, 32, 32, 32, 8, 6, 7,
+	32, 36, 32, 8, 6, 37, 32, 36,
+	32, 10, 6, 4, 32, 32, 8, 38,
+	32, 36, 32, 12, 6, 4, 10, 32,
+	8, 35, 32, 34, 32, 3, 6, 4,
+	10, 12, 8, 29, 14, 39, 39, 39,
+	15, 39, 17, 41, 40, 42, 40, 15,
+	19, 17, 23, 25, 21, 18, 40, 42,
+	40, 17, 19, 40, 40, 40, 21, 19,
+	20, 40, 42, 40, 21, 19, 43, 40,
+	42, 40, 23, 19, 17, 40, 40, 21,
+	44, 40, 42, 40, 25, 19, 17, 23,
+	40, 21, 45, 46, 40, 31, 26, 15,
+	19, 17, 23, 25, 21, 41, 40, 31,
+	40, 15, 19, 17, 23, 25, 21, 0
 };
 
-static const char _khmer_syllable_machine_trans_targs[] = {
-	20, 1, 28, 22, 23, 3, 24, 5, 
-	25, 7, 26, 9, 27, 20, 10, 31, 
-	20, 32, 12, 33, 14, 34, 16, 35, 
-	18, 36, 39, 20, 21, 30, 37, 20, 
-	0, 29, 2, 4, 6, 8, 20, 20, 
-	11, 13, 15, 17, 38, 19
+static const signed char _khmer_syllable_machine_index_defaults[] = {
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 13, 16, 16, 16, 16, 16,
+	16, 16, 16, 16, 28, 32, 32, 32,
+	32, 32, 32, 32, 32, 32, 39, 40,
+	40, 40, 40, 40, 40, 40, 40, 40,
+	0
 };
 
-static const char _khmer_syllable_machine_trans_actions[] = {
-	1, 0, 2, 2, 2, 0, 0, 0, 
-	2, 0, 2, 0, 2, 3, 0, 4, 
-	5, 2, 0, 0, 0, 2, 0, 2, 
-	0, 2, 4, 8, 2, 9, 0, 10, 
-	0, 0, 0, 0, 0, 0, 11, 12, 
-	0, 0, 0, 0, 4, 0
+static const signed char _khmer_syllable_machine_cond_targs[] = {
+	20, 1, 28, 22, 23, 3, 24, 5,
+	25, 7, 26, 9, 27, 20, 10, 31,
+	20, 32, 12, 33, 14, 34, 16, 35,
+	18, 36, 39, 20, 20, 21, 30, 37,
+	20, 0, 29, 2, 4, 6, 8, 20,
+	20, 11, 13, 15, 17, 38, 19, 0
 };
 
-static const char _khmer_syllable_machine_to_state_actions[] = {
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 6, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0
+static const signed char _khmer_syllable_machine_cond_actions[] = {
+	1, 0, 2, 2, 2, 0, 0, 0,
+	2, 0, 2, 0, 2, 3, 0, 4,
+	5, 2, 0, 0, 0, 2, 0, 2,
+	0, 2, 4, 0, 8, 2, 9, 0,
+	10, 0, 0, 0, 0, 0, 0, 11,
+	12, 0, 0, 0, 0, 4, 0, 0
 };
 
-static const char _khmer_syllable_machine_from_state_actions[] = {
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 7, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0
+static const signed char _khmer_syllable_machine_to_state_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 6, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0
 };
 
-static const unsigned char _khmer_syllable_machine_eof_trans[] = {
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 14, 17, 17, 17, 17, 17, 
-	17, 17, 17, 17, 0, 32, 32, 32, 
-	32, 32, 32, 32, 32, 32, 39, 40, 
-	40, 40, 40, 40, 40, 40, 40, 40
+static const signed char _khmer_syllable_machine_from_state_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 7, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0
+};
+
+static const signed char _khmer_syllable_machine_eof_trans[] = {
+	1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 14, 17, 17, 17, 17, 17,
+	17, 17, 17, 17, 28, 33, 33, 33,
+	33, 33, 33, 33, 33, 33, 40, 41,
+	41, 41, 41, 41, 41, 41, 41, 41,
+	0
 };
 
 static const int khmer_syllable_machine_start = 20;
@@ -215,156 +183,271 @@
 static const int khmer_syllable_machine_en_main = 20;
 
 
-#line 36 "hb-ot-shape-complex-khmer-machine.rl"
+#line 43 "hb-ot-shape-complex-khmer-machine.rl"
 
 
 
-#line 80 "hb-ot-shape-complex-khmer-machine.rl"
+#line 86 "hb-ot-shape-complex-khmer-machine.rl"
 
 
 #define found_syllable(syllable_type) \
-  HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
-    for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | khmer_##syllable_type; \
-    syllable_serial++; \
-    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
-  } HB_STMT_END
+HB_STMT_START { \
+	if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+		for (unsigned int i = ts; i < te; i++) \
+	info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+	syllable_serial++; \
+	if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+	} HB_STMT_END
 
 static void
 find_syllables_khmer (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts, te, act HB_UNUSED;
-  int cs;
-  hb_glyph_info_t *info = buffer->info;
-  
-#line 242 "hb-ot-shape-complex-khmer-machine.hh"
+	unsigned int p, pe, eof, ts, te, act HB_UNUSED;
+	int cs;
+	hb_glyph_info_t *info = buffer->info;
+	
+#line 210 "hb-ot-shape-complex-khmer-machine.hh"
 	{
-	cs = khmer_syllable_machine_start;
-	ts = 0;
-	te = 0;
-	act = 0;
+		cs = (int)khmer_syllable_machine_start;
+		ts = 0;
+		te = 0;
+		act = 0;
 	}
-
-#line 100 "hb-ot-shape-complex-khmer-machine.rl"
-
-
-  p = 0;
-  pe = eof = buffer->len;
-
-  unsigned int syllable_serial = 1;
-  
-#line 258 "hb-ot-shape-complex-khmer-machine.hh"
+	
+#line 106 "hb-ot-shape-complex-khmer-machine.rl"
+	
+	
+	p = 0;
+	pe = eof = buffer->len;
+	
+	unsigned int syllable_serial = 1;
+	
+#line 226 "hb-ot-shape-complex-khmer-machine.hh"
 	{
-	int _slen;
-	int _trans;
-	const unsigned char *_keys;
-	const char *_inds;
-	if ( p == pe )
-		goto _test_eof;
-_resume:
-	switch ( _khmer_syllable_machine_from_state_actions[cs] ) {
-	case 7:
+		unsigned int _trans = 0;
+		const unsigned char * _keys;
+		const signed char * _inds;
+		int _ic;
+		_resume: {}
+		if ( p == pe && p != eof )
+			goto _out;
+		switch ( _khmer_syllable_machine_from_state_actions[cs] ) {
+			case 7:  {
+				{
 #line 1 "NONE"
-	{ts = p;}
-	break;
-#line 272 "hb-ot-shape-complex-khmer-machine.hh"
-	}
-
-	_keys = _khmer_syllable_machine_trans_keys + (cs<<1);
-	_inds = _khmer_syllable_machine_indicies + _khmer_syllable_machine_index_offsets[cs];
-
-	_slen = _khmer_syllable_machine_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].khmer_category()) &&
-		( info[p].khmer_category()) <= _keys[1] ?
-		( info[p].khmer_category()) - _keys[0] : _slen ];
-
-_eof_trans:
-	cs = _khmer_syllable_machine_trans_targs[_trans];
-
-	if ( _khmer_syllable_machine_trans_actions[_trans] == 0 )
-		goto _again;
-
-	switch ( _khmer_syllable_machine_trans_actions[_trans] ) {
-	case 2:
+					{ts = p;}}
+				
+#line 241 "hb-ot-shape-complex-khmer-machine.hh"
+				
+				
+				break; 
+			}
+		}
+		
+		if ( p == eof ) {
+			if ( _khmer_syllable_machine_eof_trans[cs] > 0 ) {
+				_trans = (unsigned int)_khmer_syllable_machine_eof_trans[cs] - 1;
+			}
+		}
+		else {
+			_keys = ( _khmer_syllable_machine_trans_keys + ((cs<<1)));
+			_inds = ( _khmer_syllable_machine_indicies + (_khmer_syllable_machine_index_offsets[cs]));
+			
+			if ( (info[p].khmer_category()) <= 29 && (info[p].khmer_category()) >= 1 ) {
+				_ic = (int)_khmer_syllable_machine_char_class[(int)(info[p].khmer_category()) - 1];
+				if ( _ic <= (int)(*( _keys+1)) && _ic >= (int)(*( _keys)) )
+					_trans = (unsigned int)(*( _inds + (int)( _ic - (int)(*( _keys)) ) )); 
+				else
+					_trans = (unsigned int)_khmer_syllable_machine_index_defaults[cs];
+			}
+			else {
+				_trans = (unsigned int)_khmer_syllable_machine_index_defaults[cs];
+			}
+			
+		}
+		cs = (int)_khmer_syllable_machine_cond_targs[_trans];
+		
+		if ( _khmer_syllable_machine_cond_actions[_trans] != 0 ) {
+			
+			switch ( _khmer_syllable_machine_cond_actions[_trans] ) {
+				case 2:  {
+					{
 #line 1 "NONE"
-	{te = p+1;}
-	break;
-	case 8:
-#line 76 "hb-ot-shape-complex-khmer-machine.rl"
-	{te = p+1;{ found_syllable (non_khmer_cluster); }}
-	break;
-	case 10:
-#line 74 "hb-ot-shape-complex-khmer-machine.rl"
-	{te = p;p--;{ found_syllable (consonant_syllable); }}
-	break;
-	case 12:
-#line 75 "hb-ot-shape-complex-khmer-machine.rl"
-	{te = p;p--;{ found_syllable (broken_cluster); }}
-	break;
-	case 11:
-#line 76 "hb-ot-shape-complex-khmer-machine.rl"
-	{te = p;p--;{ found_syllable (non_khmer_cluster); }}
-	break;
-	case 1:
-#line 74 "hb-ot-shape-complex-khmer-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
-	break;
-	case 5:
-#line 75 "hb-ot-shape-complex-khmer-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
-	break;
-	case 3:
+						{te = p+1;}}
+					
+#line 279 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					
+					break; 
+				}
+				case 8:  {
+					{
+#line 82 "hb-ot-shape-complex-khmer-machine.rl"
+						{te = p+1;{
+#line 82 "hb-ot-shape-complex-khmer-machine.rl"
+								found_syllable (khmer_non_khmer_cluster); }
+						}}
+					
+#line 292 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					
+					break; 
+				}
+				case 10:  {
+					{
+#line 80 "hb-ot-shape-complex-khmer-machine.rl"
+						{te = p;p = p - 1;{
+#line 80 "hb-ot-shape-complex-khmer-machine.rl"
+								found_syllable (khmer_consonant_syllable); }
+						}}
+					
+#line 305 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					
+					break; 
+				}
+				case 12:  {
+					{
+#line 81 "hb-ot-shape-complex-khmer-machine.rl"
+						{te = p;p = p - 1;{
+#line 81 "hb-ot-shape-complex-khmer-machine.rl"
+								found_syllable (khmer_broken_cluster); }
+						}}
+					
+#line 318 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					
+					break; 
+				}
+				case 11:  {
+					{
+#line 82 "hb-ot-shape-complex-khmer-machine.rl"
+						{te = p;p = p - 1;{
+#line 82 "hb-ot-shape-complex-khmer-machine.rl"
+								found_syllable (khmer_non_khmer_cluster); }
+						}}
+					
+#line 331 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					
+					break; 
+				}
+				case 1:  {
+					{
+#line 80 "hb-ot-shape-complex-khmer-machine.rl"
+						{p = ((te))-1;
+							{
+#line 80 "hb-ot-shape-complex-khmer-machine.rl"
+								found_syllable (khmer_consonant_syllable); }
+						}}
+					
+#line 345 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					
+					break; 
+				}
+				case 5:  {
+					{
+#line 81 "hb-ot-shape-complex-khmer-machine.rl"
+						{p = ((te))-1;
+							{
+#line 81 "hb-ot-shape-complex-khmer-machine.rl"
+								found_syllable (khmer_broken_cluster); }
+						}}
+					
+#line 359 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					
+					break; 
+				}
+				case 3:  {
+					{
 #line 1 "NONE"
-	{	switch( act ) {
-	case 2:
-	{{p = ((te))-1;} found_syllable (broken_cluster); }
-	break;
-	case 3:
-	{{p = ((te))-1;} found_syllable (non_khmer_cluster); }
-	break;
-	}
-	}
-	break;
-	case 4:
+						{switch( act ) {
+								case 2:  {
+									p = ((te))-1;
+									{
+#line 81 "hb-ot-shape-complex-khmer-machine.rl"
+										found_syllable (khmer_broken_cluster); }
+									break; 
+								}
+								case 3:  {
+									p = ((te))-1;
+									{
+#line 82 "hb-ot-shape-complex-khmer-machine.rl"
+										found_syllable (khmer_non_khmer_cluster); }
+									break; 
+								}
+							}}
+					}
+					
+#line 385 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					
+					break; 
+				}
+				case 4:  {
+					{
 #line 1 "NONE"
-	{te = p+1;}
-#line 75 "hb-ot-shape-complex-khmer-machine.rl"
-	{act = 2;}
-	break;
-	case 9:
+						{te = p+1;}}
+					
+#line 395 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					{
+#line 81 "hb-ot-shape-complex-khmer-machine.rl"
+						{act = 2;}}
+					
+#line 401 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					
+					break; 
+				}
+				case 9:  {
+					{
 #line 1 "NONE"
-	{te = p+1;}
-#line 76 "hb-ot-shape-complex-khmer-machine.rl"
-	{act = 3;}
-	break;
-#line 342 "hb-ot-shape-complex-khmer-machine.hh"
-	}
-
-_again:
-	switch ( _khmer_syllable_machine_to_state_actions[cs] ) {
-	case 6:
+						{te = p+1;}}
+					
+#line 411 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					{
+#line 82 "hb-ot-shape-complex-khmer-machine.rl"
+						{act = 3;}}
+					
+#line 417 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					
+					break; 
+				}
+			}
+			
+		}
+		
+		if ( p == eof ) {
+			if ( cs >= 20 )
+				goto _out;
+		}
+		else {
+			switch ( _khmer_syllable_machine_to_state_actions[cs] ) {
+				case 6:  {
+					{
 #line 1 "NONE"
-	{ts = 0;}
-	break;
-#line 351 "hb-ot-shape-complex-khmer-machine.hh"
+						{ts = 0;}}
+					
+#line 437 "hb-ot-shape-complex-khmer-machine.hh"
+					
+					
+					break; 
+				}
+			}
+			
+			p += 1;
+			goto _resume;
+		}
+		_out: {}
 	}
-
-	if ( ++p != pe )
-		goto _resume;
-	_test_eof: {}
-	if ( p == eof )
-	{
-	if ( _khmer_syllable_machine_eof_trans[cs] > 0 ) {
-		_trans = _khmer_syllable_machine_eof_trans[cs] - 1;
-		goto _eof_trans;
-	}
-	}
-
-	}
-
-#line 108 "hb-ot-shape-complex-khmer-machine.rl"
-
+	
+#line 114 "hb-ot-shape-complex-khmer-machine.rl"
+	
 }
 
 #undef found_syllable
diff --git a/src/hb-ot-shape-complex-khmer-machine.rl b/src/hb-ot-shape-complex-khmer-machine.rl
index e7f1453..c9cf33f 100644
--- a/src/hb-ot-shape-complex-khmer-machine.rl
+++ b/src/hb-ot-shape-complex-khmer-machine.rl
@@ -29,30 +29,36 @@
 
 #include "hb.hh"
 
+enum khmer_syllable_type_t {
+  khmer_consonant_syllable,
+  khmer_broken_cluster,
+  khmer_non_khmer_cluster,
+};
+
 %%{
   machine khmer_syllable_machine;
   alphtype unsigned char;
+  write exports;
   write data;
 }%%
 
 %%{
 
-# Same order as enum khmer_category_t.  Not sure how to avoid duplication.
-C    = 1;
-V    = 2;
-ZWNJ = 5;
-ZWJ  = 6;
-PLACEHOLDER = 11;
-DOTTEDCIRCLE = 12;
-Coeng= 14;
-Ra   = 16;
-Robatic = 20;
-Xgroup  = 21;
-Ygroup  = 22;
-VAbv = 26;
-VBlw = 27;
-VPre = 28;
-VPst = 29;
+export C    = 1;
+export V    = 2;
+export ZWNJ = 5;
+export ZWJ  = 6;
+export PLACEHOLDER = 11;
+export DOTTEDCIRCLE = 12;
+export Coeng= 14;
+export Ra   = 16;
+export Robatic = 20;
+export Xgroup  = 21;
+export Ygroup  = 22;
+export VAbv = 26;
+export VBlw = 27;
+export VPre = 28;
+export VPst = 29;
 
 c = (C | Ra | V);
 cn = c.((ZWJ|ZWNJ)?.Robatic)?;
@@ -71,9 +77,9 @@
 other =			any;
 
 main := |*
-	consonant_syllable	=> { found_syllable (consonant_syllable); };
-	broken_cluster		=> { found_syllable (broken_cluster); };
-	other			=> { found_syllable (non_khmer_cluster); };
+	consonant_syllable	=> { found_syllable (khmer_consonant_syllable); };
+	broken_cluster		=> { found_syllable (khmer_broken_cluster); };
+	other			=> { found_syllable (khmer_non_khmer_cluster); };
 *|;
 
 
@@ -83,7 +89,7 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | khmer_##syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
diff --git a/src/hb-ot-shape-complex-khmer.cc b/src/hb-ot-shape-complex-khmer.cc
index 3da8374..11c79b2 100644
--- a/src/hb-ot-shape-complex-khmer.cc
+++ b/src/hb-ot-shape-complex-khmer.cc
@@ -29,6 +29,7 @@
 #ifndef HB_NO_OT_SHAPE
 
 #include "hb-ot-shape-complex-khmer.hh"
+#include "hb-ot-shape-complex-khmer-machine.hh"
 #include "hb-ot-layout.hh"
 
 
@@ -186,15 +187,6 @@
   free (data);
 }
 
-
-enum khmer_syllable_type_t {
-  khmer_consonant_syllable,
-  khmer_broken_cluster,
-  khmer_non_khmer_cluster,
-};
-
-#include "hb-ot-shape-complex-khmer-machine.hh"
-
 static void
 setup_masks_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		   hb_buffer_t              *buffer,
@@ -321,79 +313,22 @@
   }
 }
 
-static inline void
-insert_dotted_circles_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED,
-			     hb_font_t *font,
-			     hb_buffer_t *buffer)
-{
-  if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
-    return;
-
-  /* Note: This loop is extra overhead, but should not be measurable.
-   * TODO Use a buffer scratch flag to remove the loop. */
-  bool has_broken_syllables = false;
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  for (unsigned int i = 0; i < count; i++)
-    if ((info[i].syllable() & 0x0F) == khmer_broken_cluster)
-    {
-      has_broken_syllables = true;
-      break;
-    }
-  if (likely (!has_broken_syllables))
-    return;
-
-
-  hb_codepoint_t dottedcircle_glyph;
-  if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph))
-    return;
-
-  hb_glyph_info_t dottedcircle = {0};
-  dottedcircle.codepoint = 0x25CCu;
-  set_khmer_properties (dottedcircle);
-  dottedcircle.codepoint = dottedcircle_glyph;
-
-  buffer->clear_output ();
-
-  buffer->idx = 0;
-  unsigned int last_syllable = 0;
-  while (buffer->idx < buffer->len && buffer->successful)
-  {
-    unsigned int syllable = buffer->cur().syllable();
-    khmer_syllable_type_t syllable_type = (khmer_syllable_type_t) (syllable & 0x0F);
-    if (unlikely (last_syllable != syllable && syllable_type == khmer_broken_cluster))
-    {
-      last_syllable = syllable;
-
-      hb_glyph_info_t ginfo = dottedcircle;
-      ginfo.cluster = buffer->cur().cluster;
-      ginfo.mask = buffer->cur().mask;
-      ginfo.syllable() = buffer->cur().syllable();
-
-      /* Insert dottedcircle after possible Repha. */
-      while (buffer->idx < buffer->len && buffer->successful &&
-	     last_syllable == buffer->cur().syllable() &&
-	     buffer->cur().khmer_category() == OT_Repha)
-	buffer->next_glyph ();
-
-      buffer->output_info (ginfo);
-    }
-    else
-      buffer->next_glyph ();
-  }
-  buffer->swap_buffers ();
-}
-
 static void
 reorder_khmer (const hb_ot_shape_plan_t *plan,
 	       hb_font_t *font,
 	       hb_buffer_t *buffer)
 {
-  insert_dotted_circles_khmer (plan, font, buffer);
+  if (buffer->message (font, "start reordering khmer"))
+  {
+    hb_syllabic_insert_dotted_circles (font, buffer,
+				       khmer_broken_cluster,
+				       OT_DOTTEDCIRCLE,
+				       OT_Repha);
 
-  foreach_syllable (buffer, start, end)
-    reorder_syllable_khmer (plan, font->face, buffer, start, end);
-
+    foreach_syllable (buffer, start, end)
+      reorder_syllable_khmer (plan, font->face, buffer, start, end);
+    (void) buffer->message (font, "end reordering khmer");
+  }
   HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category);
 }
 
diff --git a/src/hb-ot-shape-complex-khmer.hh b/src/hb-ot-shape-complex-khmer.hh
index 11a77bf..e24d68a 100644
--- a/src/hb-ot-shape-complex-khmer.hh
+++ b/src/hb-ot-shape-complex-khmer.hh
@@ -54,7 +54,7 @@
 {
   hb_codepoint_t u = info.codepoint;
   unsigned int type = hb_indic_get_categories (u);
-  khmer_category_t cat = (khmer_category_t) (type & 0x7Fu);
+  khmer_category_t cat = (khmer_category_t) (type & 0xFFu);
   indic_position_t pos = (indic_position_t) (type >> 8);
 
 
diff --git a/src/hb-ot-shape-complex-machine-index.hh b/src/hb-ot-shape-complex-machine-index.hh
deleted file mode 100644
index 9ec1f3e..0000000
--- a/src/hb-ot-shape-complex-machine-index.hh
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright © 2019,2020  David Corbett
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_MACHINE_INDEX_HH
-#define HB_OT_SHAPE_COMPLEX_MACHINE_INDEX_HH
-
-#include "hb.hh"
-
-
-template <typename Iter>
-struct machine_index_t :
-  hb_iter_with_fallback_t<machine_index_t<Iter>,
-			  typename Iter::item_t>
-{
-  machine_index_t (const Iter& it) : it (it) {}
-  machine_index_t (const machine_index_t& o) : it (o.it) {}
-
-  static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator;
-  static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator;
-
-  typename Iter::item_t __item__ () const { return *it; }
-  typename Iter::item_t __item_at__ (unsigned i) const { return it[i]; }
-  unsigned __len__ () const { return it.len (); }
-  void __next__ () { ++it; }
-  void __forward__ (unsigned n) { it += n; }
-  void __prev__ () { --it; }
-  void __rewind__ (unsigned n) { it -= n; }
-  void operator = (unsigned n)
-  { unsigned index = (*it).first; if (index < n) it += n - index; else if (index > n) it -= index - n; }
-  void operator = (const machine_index_t& o) { *this = (*o.it).first; }
-  bool operator == (const machine_index_t& o) const { return (*it).first == (*o.it).first; }
-  bool operator != (const machine_index_t& o) const { return !(*this == o); }
-
-  private:
-  Iter it;
-};
-struct
-{
-  template <typename Iter,
-	    hb_requires (hb_is_iterable (Iter))>
-  machine_index_t<hb_iter_type<Iter>>
-  operator () (Iter&& it) const
-  { return machine_index_t<hb_iter_type<Iter>> (hb_iter (it)); }
-}
-HB_FUNCOBJ (machine_index);
-
-
-#endif /* HB_OT_SHAPE_COMPLEX_MACHINE_INDEX_HH */
diff --git a/src/hb-ot-shape-complex-myanmar-machine.hh b/src/hb-ot-shape-complex-myanmar-machine.hh
index c2f4c00..c094978 100644
--- a/src/hb-ot-shape-complex-myanmar-machine.hh
+++ b/src/hb-ot-shape-complex-myanmar-machine.hh
@@ -31,8 +31,43 @@
 
 #include "hb.hh"
 
+enum myanmar_syllable_type_t {
+  myanmar_consonant_syllable,
+  myanmar_punctuation_cluster,
+  myanmar_broken_cluster,
+  myanmar_non_myanmar_cluster,
+};
 
-#line 36 "hb-ot-shape-complex-myanmar-machine.hh"
+
+#line 43 "hb-ot-shape-complex-myanmar-machine.hh"
+#define myanmar_syllable_machine_ex_A 10u
+#define myanmar_syllable_machine_ex_As 18u
+#define myanmar_syllable_machine_ex_C 1u
+#define myanmar_syllable_machine_ex_CS 19u
+#define myanmar_syllable_machine_ex_D 32u
+#define myanmar_syllable_machine_ex_D0 20u
+#define myanmar_syllable_machine_ex_DB 3u
+#define myanmar_syllable_machine_ex_GB 11u
+#define myanmar_syllable_machine_ex_H 4u
+#define myanmar_syllable_machine_ex_IV 2u
+#define myanmar_syllable_machine_ex_MH 21u
+#define myanmar_syllable_machine_ex_MR 22u
+#define myanmar_syllable_machine_ex_MW 23u
+#define myanmar_syllable_machine_ex_MY 24u
+#define myanmar_syllable_machine_ex_P 31u
+#define myanmar_syllable_machine_ex_PT 25u
+#define myanmar_syllable_machine_ex_Ra 16u
+#define myanmar_syllable_machine_ex_V 8u
+#define myanmar_syllable_machine_ex_VAbv 26u
+#define myanmar_syllable_machine_ex_VBlw 27u
+#define myanmar_syllable_machine_ex_VPre 28u
+#define myanmar_syllable_machine_ex_VPst 29u
+#define myanmar_syllable_machine_ex_VS 30u
+#define myanmar_syllable_machine_ex_ZWJ 6u
+#define myanmar_syllable_machine_ex_ZWNJ 5u
+
+
+#line 71 "hb-ot-shape-complex-myanmar-machine.hh"
 static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
 	1u, 32u, 3u, 30u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 
 	3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 16u, 3u, 29u, 3u, 29u, 3u, 29u, 
@@ -293,18 +328,18 @@
 static const int myanmar_syllable_machine_en_main = 0;
 
 
-#line 36 "hb-ot-shape-complex-myanmar-machine.rl"
+#line 44 "hb-ot-shape-complex-myanmar-machine.rl"
 
 
 
-#line 94 "hb-ot-shape-complex-myanmar-machine.rl"
+#line 101 "hb-ot-shape-complex-myanmar-machine.rl"
 
 
 #define found_syllable(syllable_type) \
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | myanmar_##syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
@@ -316,7 +351,7 @@
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 320 "hb-ot-shape-complex-myanmar-machine.hh"
+#line 355 "hb-ot-shape-complex-myanmar-machine.hh"
 	{
 	cs = myanmar_syllable_machine_start;
 	ts = 0;
@@ -324,7 +359,7 @@
 	act = 0;
 	}
 
-#line 114 "hb-ot-shape-complex-myanmar-machine.rl"
+#line 121 "hb-ot-shape-complex-myanmar-machine.rl"
 
 
   p = 0;
@@ -332,7 +367,7 @@
 
   unsigned int syllable_serial = 1;
   
-#line 336 "hb-ot-shape-complex-myanmar-machine.hh"
+#line 371 "hb-ot-shape-complex-myanmar-machine.hh"
 	{
 	int _slen;
 	int _trans;
@@ -346,7 +381,7 @@
 #line 1 "NONE"
 	{ts = p;}
 	break;
-#line 350 "hb-ot-shape-complex-myanmar-machine.hh"
+#line 385 "hb-ot-shape-complex-myanmar-machine.hh"
 	}
 
 	_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
@@ -365,38 +400,38 @@
 
 	switch ( _myanmar_syllable_machine_trans_actions[_trans] ) {
 	case 6:
-#line 86 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p+1;{ found_syllable (consonant_syllable); }}
+#line 93 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (myanmar_consonant_syllable); }}
 	break;
 	case 4:
-#line 87 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p+1;{ found_syllable (non_myanmar_cluster); }}
+#line 94 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }}
 	break;
 	case 10:
-#line 88 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p+1;{ found_syllable (punctuation_cluster); }}
+#line 95 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (myanmar_punctuation_cluster); }}
 	break;
 	case 8:
-#line 89 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p+1;{ found_syllable (broken_cluster); }}
+#line 96 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (myanmar_broken_cluster); }}
 	break;
 	case 3:
-#line 90 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p+1;{ found_syllable (non_myanmar_cluster); }}
+#line 97 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }}
 	break;
 	case 5:
-#line 86 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p;p--;{ found_syllable (consonant_syllable); }}
+#line 93 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (myanmar_consonant_syllable); }}
 	break;
 	case 7:
-#line 89 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p;p--;{ found_syllable (broken_cluster); }}
+#line 96 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (myanmar_broken_cluster); }}
 	break;
 	case 9:
-#line 90 "hb-ot-shape-complex-myanmar-machine.rl"
-	{te = p;p--;{ found_syllable (non_myanmar_cluster); }}
+#line 97 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (myanmar_non_myanmar_cluster); }}
 	break;
-#line 400 "hb-ot-shape-complex-myanmar-machine.hh"
+#line 435 "hb-ot-shape-complex-myanmar-machine.hh"
 	}
 
 _again:
@@ -405,7 +440,7 @@
 #line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 409 "hb-ot-shape-complex-myanmar-machine.hh"
+#line 444 "hb-ot-shape-complex-myanmar-machine.hh"
 	}
 
 	if ( ++p != pe )
@@ -421,7 +456,7 @@
 
 	}
 
-#line 122 "hb-ot-shape-complex-myanmar-machine.rl"
+#line 129 "hb-ot-shape-complex-myanmar-machine.rl"
 
 }
 
diff --git a/src/hb-ot-shape-complex-myanmar-machine.rl b/src/hb-ot-shape-complex-myanmar-machine.rl
index 098a63e..15905b2 100644
--- a/src/hb-ot-shape-complex-myanmar-machine.rl
+++ b/src/hb-ot-shape-complex-myanmar-machine.rl
@@ -29,40 +29,47 @@
 
 #include "hb.hh"
 
+enum myanmar_syllable_type_t {
+  myanmar_consonant_syllable,
+  myanmar_punctuation_cluster,
+  myanmar_broken_cluster,
+  myanmar_non_myanmar_cluster,
+};
+
 %%{
   machine myanmar_syllable_machine;
   alphtype unsigned char;
+  write exports;
   write data;
 }%%
 
 %%{
 
-# Same order as enum myanmar_category_t.  Not sure how to avoid duplication.
-A    = 10;
-As   = 18;
-C    = 1;
-D    = 32;
-D0   = 20;
-DB   = 3;
-GB   = 11;
-H    = 4;
-IV   = 2;
-MH   = 21;
-MR   = 22;
-MW   = 23;
-MY   = 24;
-PT   = 25;
-V    = 8;
-VAbv = 26;
-VBlw = 27;
-VPre = 28;
-VPst = 29;
-VS   = 30;
-ZWJ  = 6;
-ZWNJ = 5;
-Ra   = 16;
-P    = 31;
-CS   = 19;
+export A    = 10;
+export As   = 18;
+export C    = 1;
+export D    = 32;
+export D0   = 20;
+export DB   = 3;
+export GB   = 11;
+export H    = 4;
+export IV   = 2;
+export MH   = 21;
+export MR   = 22;
+export MW   = 23;
+export MY   = 24;
+export PT   = 25;
+export V    = 8;
+export VAbv = 26;
+export VBlw = 27;
+export VPre = 28;
+export VPst = 29;
+export VS   = 30;
+export ZWJ  = 6;
+export ZWNJ = 5;
+export Ra   = 16;
+export P    = 31;
+export CS   = 19;
 
 j = ZWJ|ZWNJ;			# Joiners
 k = (Ra As H);			# Kinzi
@@ -83,11 +90,11 @@
 other =			any;
 
 main := |*
-	consonant_syllable	=> { found_syllable (consonant_syllable); };
-	j			=> { found_syllable (non_myanmar_cluster); };
-	punctuation_cluster	=> { found_syllable (punctuation_cluster); };
-	broken_cluster		=> { found_syllable (broken_cluster); };
-	other			=> { found_syllable (non_myanmar_cluster); };
+	consonant_syllable	=> { found_syllable (myanmar_consonant_syllable); };
+	j			=> { found_syllable (myanmar_non_myanmar_cluster); };
+	punctuation_cluster	=> { found_syllable (myanmar_punctuation_cluster); };
+	broken_cluster		=> { found_syllable (myanmar_broken_cluster); };
+	other			=> { found_syllable (myanmar_non_myanmar_cluster); };
 *|;
 
 
@@ -97,7 +104,7 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
     for (unsigned int i = ts; i < te; i++) \
-      info[i].syllable() = (syllable_serial << 4) | myanmar_##syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc
index 294ab34..bc5dcb9 100644
--- a/src/hb-ot-shape-complex-myanmar.cc
+++ b/src/hb-ot-shape-complex-myanmar.cc
@@ -29,6 +29,7 @@
 #ifndef HB_NO_OT_SHAPE
 
 #include "hb-ot-shape-complex-myanmar.hh"
+#include "hb-ot-shape-complex-myanmar-machine.hh"
 
 
 /*
@@ -97,17 +98,6 @@
     map->enable_feature (myanmar_other_features[i], F_MANUAL_ZWJ);
 }
 
-
-enum myanmar_syllable_type_t {
-  myanmar_consonant_syllable,
-  myanmar_punctuation_cluster,
-  myanmar_broken_cluster,
-  myanmar_non_myanmar_cluster,
-};
-
-#include "hb-ot-shape-complex-myanmar-machine.hh"
-
-
 static void
 setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		     hb_buffer_t              *buffer,
@@ -265,72 +255,21 @@
   }
 }
 
-static inline void
-insert_dotted_circles_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
-			       hb_font_t *font,
-			       hb_buffer_t *buffer)
-{
-  if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
-    return;
-
-  /* Note: This loop is extra overhead, but should not be measurable.
-   * TODO Use a buffer scratch flag to remove the loop. */
-  bool has_broken_syllables = false;
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  for (unsigned int i = 0; i < count; i++)
-    if ((info[i].syllable() & 0x0F) == myanmar_broken_cluster)
-    {
-      has_broken_syllables = true;
-      break;
-    }
-  if (likely (!has_broken_syllables))
-    return;
-
-
-  hb_codepoint_t dottedcircle_glyph;
-  if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph))
-    return;
-
-  hb_glyph_info_t dottedcircle = {0};
-  dottedcircle.codepoint = 0x25CCu;
-  set_myanmar_properties (dottedcircle);
-  dottedcircle.codepoint = dottedcircle_glyph;
-
-  buffer->clear_output ();
-
-  buffer->idx = 0;
-  unsigned int last_syllable = 0;
-  while (buffer->idx < buffer->len && buffer->successful)
-  {
-    unsigned int syllable = buffer->cur().syllable();
-    myanmar_syllable_type_t syllable_type = (myanmar_syllable_type_t) (syllable & 0x0F);
-    if (unlikely (last_syllable != syllable && syllable_type == myanmar_broken_cluster))
-    {
-      last_syllable = syllable;
-
-      hb_glyph_info_t ginfo = dottedcircle;
-      ginfo.cluster = buffer->cur().cluster;
-      ginfo.mask = buffer->cur().mask;
-      ginfo.syllable() = buffer->cur().syllable();
-
-      buffer->output_info (ginfo);
-    }
-    else
-      buffer->next_glyph ();
-  }
-  buffer->swap_buffers ();
-}
-
 static void
 reorder_myanmar (const hb_ot_shape_plan_t *plan,
 		 hb_font_t *font,
 		 hb_buffer_t *buffer)
 {
-  insert_dotted_circles_myanmar (plan, font, buffer);
+  if (buffer->message (font, "start reordering myanmar"))
+  {
+    hb_syllabic_insert_dotted_circles (font, buffer,
+				       myanmar_broken_cluster,
+				       OT_GB);
 
-  foreach_syllable (buffer, start, end)
-    reorder_syllable_myanmar (plan, font->face, buffer, start, end);
+    foreach_syllable (buffer, start, end)
+      reorder_syllable_myanmar (plan, font->face, buffer, start, end);
+    (void) buffer->message (font, "end reordering myanmar");
+  }
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position);
diff --git a/src/hb-ot-shape-complex-myanmar.hh b/src/hb-ot-shape-complex-myanmar.hh
index 7b9821e..a6d68aa 100644
--- a/src/hb-ot-shape-complex-myanmar.hh
+++ b/src/hb-ot-shape-complex-myanmar.hh
@@ -64,7 +64,7 @@
 {
   hb_codepoint_t u = info.codepoint;
   unsigned int type = hb_indic_get_categories (u);
-  unsigned int cat = type & 0x7Fu;
+  unsigned int cat = type & 0xFFu;
   indic_position_t pos = (indic_position_t) (type >> 8);
 
   /* Myanmar
diff --git a/src/hb-ot-shape-complex-syllabic.cc b/src/hb-ot-shape-complex-syllabic.cc
new file mode 100644
index 0000000..9f8c790
--- /dev/null
+++ b/src/hb-ot-shape-complex-syllabic.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2021  Behdad Esfahbod.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb.hh"
+
+#ifndef HB_NO_OT_SHAPE
+
+#include "hb-ot-shape-complex-syllabic.hh"
+
+
+void
+hb_syllabic_insert_dotted_circles (hb_font_t *font,
+				   hb_buffer_t *buffer,
+				   unsigned int broken_syllable_type,
+				   unsigned int dottedcircle_category,
+				   int repha_category)
+{
+  if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
+    return;
+
+  /* Note: This loop is extra overhead, but should not be measurable.
+   * TODO Use a buffer scratch flag to remove the loop. */
+  bool has_broken_syllables = false;
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  for (unsigned int i = 0; i < count; i++)
+    if ((info[i].syllable() & 0x0F) == broken_syllable_type)
+    {
+      has_broken_syllables = true;
+      break;
+    }
+  if (likely (!has_broken_syllables))
+    return;
+
+
+  hb_codepoint_t dottedcircle_glyph;
+  if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph))
+    return;
+
+  hb_glyph_info_t dottedcircle = {0};
+  dottedcircle.codepoint = 0x25CCu;
+  dottedcircle.complex_var_u8_category() = dottedcircle_category;
+  dottedcircle.codepoint = dottedcircle_glyph;
+
+  buffer->clear_output ();
+
+  buffer->idx = 0;
+  unsigned int last_syllable = 0;
+  while (buffer->idx < buffer->len && buffer->successful)
+  {
+    unsigned int syllable = buffer->cur().syllable();
+    if (unlikely (last_syllable != syllable && (syllable & 0x0F) == broken_syllable_type))
+    {
+      last_syllable = syllable;
+
+      hb_glyph_info_t ginfo = dottedcircle;
+      ginfo.cluster = buffer->cur().cluster;
+      ginfo.mask = buffer->cur().mask;
+      ginfo.syllable() = buffer->cur().syllable();
+
+      /* Insert dottedcircle after possible Repha. */
+      if (repha_category != -1)
+      {
+	while (buffer->idx < buffer->len && buffer->successful &&
+	       last_syllable == buffer->cur().syllable() &&
+	       buffer->cur().complex_var_u8_category() == (unsigned) repha_category)
+	  buffer->next_glyph ();
+      }
+
+      buffer->output_info (ginfo);
+    }
+    else
+      buffer->next_glyph ();
+  }
+  buffer->swap_buffers ();
+}
+
+
+#endif
diff --git a/src/dump-use-data.cc b/src/hb-ot-shape-complex-syllabic.hh
similarity index 70%
rename from src/dump-use-data.cc
rename to src/hb-ot-shape-complex-syllabic.hh
index d639426..c80b8fe 100644
--- a/src/dump-use-data.cc
+++ b/src/hb-ot-shape-complex-syllabic.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2018  Google, Inc.
+ * Copyright © 2021  Behdad Esfahbod.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -20,19 +20,22 @@
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-complex-use.hh"
+#ifndef HB_OT_SHAPE_COMPLEX_SYLLABIC_HH
+#define HB_OT_SHAPE_COMPLEX_SYLLABIC_HH
 
-int
-main ()
-{
-  for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++)
-  {
-    unsigned int category = hb_use_get_category (u);
-    if (category != USE_O)
-      printf("U+%04X	%u\n", u, category);
-  }
-}
+#include "hb.hh"
+
+#include "hb-ot-shape-complex.hh"
+
+
+HB_INTERNAL void
+hb_syllabic_insert_dotted_circles (hb_font_t *font,
+				   hb_buffer_t *buffer,
+				   unsigned int broken_syllable_type,
+				   unsigned int dottedcircle_category,
+				   int repha_category = -1);
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_SYLLABIC_HH */
diff --git a/src/hb-ot-shape-complex-use-machine.hh b/src/hb-ot-shape-complex-use-machine.hh
index d733003..b4b2b75 100644
--- a/src/hb-ot-shape-complex-use-machine.hh
+++ b/src/hb-ot-shape-complex-use-machine.hh
@@ -1,544 +1,727 @@
-
 #line 1 "hb-ot-shape-complex-use-machine.rl"
 /*
- * Copyright © 2015  Mozilla Foundation.
- * Copyright © 2015  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Mozilla Author(s): Jonathan Kew
- * Google Author(s): Behdad Esfahbod
- */
+* Copyright © 2015  Mozilla Foundation.
+* Copyright © 2015  Google, Inc.
+*
+*  This is part of HarfBuzz, a text shaping library.
+*
+* Permission is hereby granted, without written agreement and without
+* license or royalty fees, to use, copy, modify, and distribute this
+* software and its documentation for any purpose, provided that the
+* above copyright notice and the following two paragraphs appear in
+* all copies of this software.
+*
+* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+* DAMAGE.
+*
+* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+* FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+*
+* Mozilla Author(s): Jonathan Kew
+* Google Author(s): Behdad Esfahbod
+*/
 
 #ifndef HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
 
 #include "hb.hh"
-#include "hb-ot-shape-complex-machine-index.hh"
 
+#include "hb-ot-shape-complex-syllabic.hh"
 
-#line 39 "hb-ot-shape-complex-use-machine.hh"
-static const unsigned char _use_syllable_machine_trans_keys[] = {
-	12u, 48u, 1u, 15u, 1u, 1u, 12u, 48u, 1u, 1u, 0u, 51u, 11u, 48u, 11u, 48u, 
-	1u, 15u, 1u, 1u, 22u, 48u, 23u, 48u, 24u, 47u, 25u, 47u, 26u, 47u, 45u, 46u, 
-	46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 1u, 1u, 24u, 48u, 23u, 48u, 23u, 48u, 
-	23u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 11u, 48u, 1u, 48u, 13u, 13u, 4u, 4u, 
-	11u, 48u, 41u, 42u, 42u, 42u, 11u, 48u, 22u, 48u, 23u, 48u, 24u, 47u, 25u, 47u, 
-	26u, 47u, 45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 24u, 48u, 23u, 48u, 
-	23u, 48u, 23u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 11u, 48u, 1u, 48u, 1u, 15u, 
-	4u, 4u, 13u, 13u, 12u, 48u, 1u, 48u, 11u, 48u, 41u, 42u, 42u, 42u, 1u, 5u, 
-	50u, 52u, 49u, 52u, 49u, 51u, 0
+/* buffer var allocations */
+#define use_category() complex_var_u8_category()
+
+#define USE(Cat) use_syllable_machine_ex_##Cat
+
+enum use_syllable_type_t {
+	use_independent_cluster,
+	use_virama_terminated_cluster,
+	use_sakot_terminated_cluster,
+	use_standard_cluster,
+	use_number_joiner_terminated_cluster,
+	use_numeral_cluster,
+	use_symbol_cluster,
+	use_hieroglyph_cluster,
+	use_broken_cluster,
+	use_non_cluster,
 };
 
-static const char _use_syllable_machine_key_spans[] = {
-	37, 15, 1, 37, 1, 52, 38, 38, 
-	15, 1, 27, 26, 24, 23, 22, 2, 
-	1, 25, 25, 25, 1, 25, 26, 26, 
-	26, 27, 27, 27, 38, 48, 1, 1, 
-	38, 2, 1, 38, 27, 26, 24, 23, 
-	22, 2, 1, 25, 25, 25, 25, 26, 
-	26, 26, 27, 27, 27, 38, 48, 15, 
-	1, 1, 37, 48, 38, 2, 1, 5, 
-	3, 4, 3
+
+#line 57 "hb-ot-shape-complex-use-machine.hh"
+#define use_syllable_machine_ex_B 1u
+#define use_syllable_machine_ex_CMAbv 31u
+#define use_syllable_machine_ex_CMBlw 32u
+#define use_syllable_machine_ex_CS 43u
+#define use_syllable_machine_ex_FAbv 24u
+#define use_syllable_machine_ex_FBlw 25u
+#define use_syllable_machine_ex_FMAbv 45u
+#define use_syllable_machine_ex_FMBlw 46u
+#define use_syllable_machine_ex_FMPst 47u
+#define use_syllable_machine_ex_FPst 26u
+#define use_syllable_machine_ex_G 49u
+#define use_syllable_machine_ex_GB 5u
+#define use_syllable_machine_ex_H 12u
+#define use_syllable_machine_ex_HN 13u
+#define use_syllable_machine_ex_HVM 44u
+#define use_syllable_machine_ex_J 50u
+#define use_syllable_machine_ex_MAbv 27u
+#define use_syllable_machine_ex_MBlw 28u
+#define use_syllable_machine_ex_MPre 30u
+#define use_syllable_machine_ex_MPst 29u
+#define use_syllable_machine_ex_N 4u
+#define use_syllable_machine_ex_O 0u
+#define use_syllable_machine_ex_R 18u
+#define use_syllable_machine_ex_S 19u
+#define use_syllable_machine_ex_SB 51u
+#define use_syllable_machine_ex_SE 52u
+#define use_syllable_machine_ex_SMAbv 41u
+#define use_syllable_machine_ex_SMBlw 42u
+#define use_syllable_machine_ex_SUB 11u
+#define use_syllable_machine_ex_Sk 48u
+#define use_syllable_machine_ex_VAbv 33u
+#define use_syllable_machine_ex_VBlw 34u
+#define use_syllable_machine_ex_VMAbv 37u
+#define use_syllable_machine_ex_VMBlw 38u
+#define use_syllable_machine_ex_VMPre 23u
+#define use_syllable_machine_ex_VMPst 39u
+#define use_syllable_machine_ex_VPre 22u
+#define use_syllable_machine_ex_VPst 35u
+#define use_syllable_machine_ex_ZWNJ 14u
+
+
+#line 99 "hb-ot-shape-complex-use-machine.hh"
+static const unsigned char _use_syllable_machine_trans_keys[] = {
+	1u, 1u, 1u, 1u, 0u, 37u, 5u, 34u,
+	5u, 34u, 1u, 1u, 10u, 34u, 11u, 34u,
+	12u, 33u, 13u, 33u, 14u, 33u, 31u, 32u,
+	32u, 32u, 12u, 34u, 12u, 34u, 12u, 34u,
+	1u, 1u, 12u, 34u, 11u, 34u, 11u, 34u,
+	11u, 34u, 10u, 34u, 10u, 34u, 10u, 34u,
+	5u, 34u, 1u, 34u, 7u, 7u, 3u, 3u,
+	5u, 34u, 27u, 28u, 28u, 28u, 5u, 34u,
+	10u, 34u, 11u, 34u, 12u, 33u, 13u, 33u,
+	14u, 33u, 31u, 32u, 32u, 32u, 12u, 34u,
+	12u, 34u, 12u, 34u, 12u, 34u, 11u, 34u,
+	11u, 34u, 11u, 34u, 10u, 34u, 10u, 34u,
+	10u, 34u, 5u, 34u, 1u, 34u, 1u, 1u,
+	3u, 3u, 7u, 7u, 1u, 34u, 5u, 34u,
+	27u, 28u, 28u, 28u, 1u, 4u, 36u, 38u,
+	35u, 38u, 35u, 37u, 0u
+};
+
+static const signed char _use_syllable_machine_char_class[] = {
+	0, 1, 2, 2, 3, 4, 2, 2,
+	2, 2, 2, 5, 6, 7, 2, 2,
+	2, 2, 8, 9, 2, 2, 10, 11,
+	12, 13, 14, 15, 16, 17, 18, 19,
+	20, 21, 22, 23, 2, 24, 25, 26,
+	2, 27, 28, 29, 30, 31, 32, 33,
+	34, 35, 36, 37, 38, 0
 };
 
 static const short _use_syllable_machine_index_offsets[] = {
-	0, 38, 54, 56, 94, 96, 149, 188, 
-	227, 243, 245, 273, 300, 325, 349, 372, 
-	375, 377, 403, 429, 455, 457, 483, 510, 
-	537, 564, 592, 620, 648, 687, 736, 738, 
-	740, 779, 782, 784, 823, 851, 878, 903, 
-	927, 950, 953, 955, 981, 1007, 1033, 1059, 
-	1086, 1113, 1140, 1168, 1196, 1224, 1263, 1312, 
-	1328, 1330, 1332, 1370, 1419, 1458, 1461, 1463, 
-	1469, 1473, 1478
+	0, 1, 2, 40, 70, 100, 101, 126,
+	150, 172, 193, 213, 215, 216, 239, 262,
+	285, 286, 309, 333, 357, 381, 406, 431,
+	456, 486, 520, 521, 522, 552, 554, 555,
+	585, 610, 634, 656, 677, 697, 699, 700,
+	723, 746, 769, 792, 816, 840, 864, 889,
+	914, 939, 969, 1003, 1004, 1005, 1006, 1040,
+	1070, 1072, 1073, 1077, 1080, 1084, 0
 };
 
-static const char _use_syllable_machine_indicies[] = {
-	1, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	1, 0, 0, 0, 1, 0, 3, 2, 
-	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 2, 2, 2, 4, 2, 3, 2, 
-	6, 5, 5, 5, 5, 5, 5, 5, 
-	5, 5, 5, 5, 5, 5, 5, 5, 
-	5, 5, 5, 5, 5, 5, 5, 5, 
-	5, 5, 5, 5, 5, 5, 5, 5, 
-	6, 5, 5, 5, 6, 5, 7, 5, 
-	8, 9, 10, 8, 11, 12, 10, 10, 
-	10, 10, 10, 3, 13, 14, 10, 15, 
-	8, 8, 16, 17, 10, 10, 18, 19, 
-	20, 21, 22, 23, 24, 18, 25, 26, 
-	27, 28, 29, 30, 10, 31, 32, 33, 
-	10, 34, 35, 36, 37, 38, 39, 40, 
-	13, 41, 10, 42, 10, 44, 1, 43, 
-	43, 45, 43, 43, 43, 43, 43, 43, 
-	46, 47, 48, 49, 50, 51, 52, 46, 
-	53, 9, 54, 55, 56, 57, 43, 58, 
-	59, 60, 43, 43, 43, 43, 61, 62, 
-	63, 64, 1, 43, 44, 1, 43, 43, 
-	45, 43, 43, 43, 43, 43, 43, 46, 
-	47, 48, 49, 50, 51, 52, 46, 53, 
-	54, 54, 55, 56, 57, 43, 58, 59, 
-	60, 43, 43, 43, 43, 61, 62, 63, 
-	64, 1, 43, 44, 65, 65, 65, 65, 
-	65, 65, 65, 65, 65, 65, 65, 65, 
-	65, 66, 65, 44, 65, 46, 47, 48, 
-	49, 50, 43, 43, 43, 43, 43, 43, 
-	55, 56, 57, 43, 58, 59, 60, 43, 
-	43, 43, 43, 47, 62, 63, 64, 67, 
-	43, 47, 48, 49, 50, 43, 43, 43, 
-	43, 43, 43, 43, 43, 43, 43, 58, 
-	59, 60, 43, 43, 43, 43, 43, 62, 
-	63, 64, 67, 43, 48, 49, 50, 43, 
-	43, 43, 43, 43, 43, 43, 43, 43, 
-	43, 43, 43, 43, 43, 43, 43, 43, 
-	43, 62, 63, 64, 43, 49, 50, 43, 
-	43, 43, 43, 43, 43, 43, 43, 43, 
-	43, 43, 43, 43, 43, 43, 43, 43, 
-	43, 62, 63, 64, 43, 50, 43, 43, 
-	43, 43, 43, 43, 43, 43, 43, 43, 
-	43, 43, 43, 43, 43, 43, 43, 43, 
-	62, 63, 64, 43, 62, 63, 43, 63, 
-	43, 48, 49, 50, 43, 43, 43, 43, 
-	43, 43, 43, 43, 43, 43, 58, 59, 
-	60, 43, 43, 43, 43, 43, 62, 63, 
-	64, 67, 43, 48, 49, 50, 43, 43, 
-	43, 43, 43, 43, 43, 43, 43, 43, 
-	43, 59, 60, 43, 43, 43, 43, 43, 
-	62, 63, 64, 67, 43, 48, 49, 50, 
-	43, 43, 43, 43, 43, 43, 43, 43, 
-	43, 43, 43, 43, 60, 43, 43, 43, 
-	43, 43, 62, 63, 64, 67, 43, 69, 
-	68, 48, 49, 50, 43, 43, 43, 43, 
-	43, 43, 43, 43, 43, 43, 43, 43, 
-	43, 43, 43, 43, 43, 43, 62, 63, 
-	64, 67, 43, 47, 48, 49, 50, 43, 
-	43, 43, 43, 43, 43, 55, 56, 57, 
-	43, 58, 59, 60, 43, 43, 43, 43, 
-	47, 62, 63, 64, 67, 43, 47, 48, 
-	49, 50, 43, 43, 43, 43, 43, 43, 
-	43, 56, 57, 43, 58, 59, 60, 43, 
-	43, 43, 43, 47, 62, 63, 64, 67, 
-	43, 47, 48, 49, 50, 43, 43, 43, 
-	43, 43, 43, 43, 43, 57, 43, 58, 
-	59, 60, 43, 43, 43, 43, 47, 62, 
-	63, 64, 67, 43, 46, 47, 48, 49, 
-	50, 43, 52, 46, 43, 43, 43, 55, 
-	56, 57, 43, 58, 59, 60, 43, 43, 
-	43, 43, 47, 62, 63, 64, 67, 43, 
-	46, 47, 48, 49, 50, 43, 43, 46, 
-	43, 43, 43, 55, 56, 57, 43, 58, 
-	59, 60, 43, 43, 43, 43, 47, 62, 
-	63, 64, 67, 43, 46, 47, 48, 49, 
-	50, 51, 52, 46, 43, 43, 43, 55, 
-	56, 57, 43, 58, 59, 60, 43, 43, 
-	43, 43, 47, 62, 63, 64, 67, 43, 
-	44, 1, 43, 43, 45, 43, 43, 43, 
-	43, 43, 43, 46, 47, 48, 49, 50, 
-	51, 52, 46, 53, 43, 54, 55, 56, 
-	57, 43, 58, 59, 60, 43, 43, 43, 
-	43, 61, 62, 63, 64, 1, 43, 44, 
-	65, 65, 65, 65, 65, 65, 65, 65, 
-	65, 65, 65, 65, 65, 66, 65, 65, 
-	65, 65, 65, 65, 65, 47, 48, 49, 
-	50, 65, 65, 65, 65, 65, 65, 65, 
-	65, 65, 65, 58, 59, 60, 65, 65, 
-	65, 65, 65, 62, 63, 64, 67, 65, 
-	71, 70, 11, 72, 44, 1, 43, 43, 
-	45, 43, 43, 43, 43, 43, 43, 46, 
-	47, 48, 49, 50, 51, 52, 46, 53, 
-	9, 54, 55, 56, 57, 43, 58, 59, 
-	60, 43, 17, 73, 43, 61, 62, 63, 
-	64, 1, 43, 17, 73, 74, 73, 74, 
-	3, 6, 75, 75, 76, 75, 75, 75, 
-	75, 75, 75, 18, 19, 20, 21, 22, 
-	23, 24, 18, 25, 27, 27, 28, 29, 
-	30, 75, 31, 32, 33, 75, 75, 75, 
-	75, 37, 38, 39, 40, 6, 75, 18, 
-	19, 20, 21, 22, 75, 75, 75, 75, 
-	75, 75, 28, 29, 30, 75, 31, 32, 
-	33, 75, 75, 75, 75, 19, 38, 39, 
-	40, 77, 75, 19, 20, 21, 22, 75, 
-	75, 75, 75, 75, 75, 75, 75, 75, 
-	75, 31, 32, 33, 75, 75, 75, 75, 
-	75, 38, 39, 40, 77, 75, 20, 21, 
-	22, 75, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 38, 39, 40, 75, 21, 
-	22, 75, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 38, 39, 40, 75, 22, 
-	75, 75, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 75, 75, 75, 75, 75, 
-	75, 75, 38, 39, 40, 75, 38, 39, 
-	75, 39, 75, 20, 21, 22, 75, 75, 
-	75, 75, 75, 75, 75, 75, 75, 75, 
-	31, 32, 33, 75, 75, 75, 75, 75, 
-	38, 39, 40, 77, 75, 20, 21, 22, 
-	75, 75, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 32, 33, 75, 75, 75, 
-	75, 75, 38, 39, 40, 77, 75, 20, 
-	21, 22, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 75, 75, 75, 33, 75, 
-	75, 75, 75, 75, 38, 39, 40, 77, 
-	75, 20, 21, 22, 75, 75, 75, 75, 
-	75, 75, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 75, 75, 75, 38, 39, 
-	40, 77, 75, 19, 20, 21, 22, 75, 
-	75, 75, 75, 75, 75, 28, 29, 30, 
-	75, 31, 32, 33, 75, 75, 75, 75, 
-	19, 38, 39, 40, 77, 75, 19, 20, 
-	21, 22, 75, 75, 75, 75, 75, 75, 
-	75, 29, 30, 75, 31, 32, 33, 75, 
-	75, 75, 75, 19, 38, 39, 40, 77, 
-	75, 19, 20, 21, 22, 75, 75, 75, 
-	75, 75, 75, 75, 75, 30, 75, 31, 
-	32, 33, 75, 75, 75, 75, 19, 38, 
-	39, 40, 77, 75, 18, 19, 20, 21, 
-	22, 75, 24, 18, 75, 75, 75, 28, 
-	29, 30, 75, 31, 32, 33, 75, 75, 
-	75, 75, 19, 38, 39, 40, 77, 75, 
-	18, 19, 20, 21, 22, 75, 75, 18, 
-	75, 75, 75, 28, 29, 30, 75, 31, 
-	32, 33, 75, 75, 75, 75, 19, 38, 
-	39, 40, 77, 75, 18, 19, 20, 21, 
-	22, 23, 24, 18, 75, 75, 75, 28, 
-	29, 30, 75, 31, 32, 33, 75, 75, 
-	75, 75, 19, 38, 39, 40, 77, 75, 
-	3, 6, 75, 75, 76, 75, 75, 75, 
-	75, 75, 75, 18, 19, 20, 21, 22, 
-	23, 24, 18, 25, 75, 27, 28, 29, 
-	30, 75, 31, 32, 33, 75, 75, 75, 
-	75, 37, 38, 39, 40, 6, 75, 3, 
-	75, 75, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 75, 75, 4, 75, 75, 
-	75, 75, 75, 75, 75, 19, 20, 21, 
-	22, 75, 75, 75, 75, 75, 75, 75, 
-	75, 75, 75, 31, 32, 33, 75, 75, 
-	75, 75, 75, 38, 39, 40, 77, 75, 
-	3, 78, 78, 78, 78, 78, 78, 78, 
-	78, 78, 78, 78, 78, 78, 4, 78, 
-	79, 75, 14, 75, 6, 78, 78, 78, 
-	78, 78, 78, 78, 78, 78, 78, 78, 
-	78, 78, 78, 78, 78, 78, 78, 78, 
-	78, 78, 78, 78, 78, 78, 78, 78, 
-	78, 78, 78, 78, 6, 78, 78, 78, 
-	6, 78, 9, 75, 75, 75, 9, 75, 
-	75, 75, 75, 75, 3, 6, 14, 75, 
-	76, 75, 75, 75, 75, 75, 75, 18, 
-	19, 20, 21, 22, 23, 24, 18, 25, 
-	26, 27, 28, 29, 30, 75, 31, 32, 
-	33, 75, 34, 35, 75, 37, 38, 39, 
-	40, 6, 75, 3, 6, 75, 75, 76, 
-	75, 75, 75, 75, 75, 75, 18, 19, 
-	20, 21, 22, 23, 24, 18, 25, 26, 
-	27, 28, 29, 30, 75, 31, 32, 33, 
-	75, 75, 75, 75, 37, 38, 39, 40, 
-	6, 75, 34, 35, 75, 35, 75, 9, 
-	78, 78, 78, 9, 78, 81, 80, 41, 
-	80, 41, 81, 80, 81, 80, 41, 80, 
-	42, 80, 0
+static const signed char _use_syllable_machine_indicies[] = {
+	1, 2, 4, 5, 6, 7, 8, 1,
+	9, 10, 11, 12, 13, 14, 15, 16,
+	17, 18, 19, 13, 20, 21, 22, 23,
+	24, 25, 26, 27, 28, 29, 30, 31,
+	32, 33, 34, 35, 9, 36, 6, 37,
+	39, 40, 38, 38, 38, 41, 42, 43,
+	44, 45, 46, 47, 41, 48, 5, 49,
+	50, 51, 52, 53, 54, 55, 38, 38,
+	38, 56, 57, 58, 59, 40, 39, 40,
+	38, 38, 38, 41, 42, 43, 44, 45,
+	46, 47, 41, 48, 49, 49, 50, 51,
+	52, 53, 54, 55, 38, 38, 38, 56,
+	57, 58, 59, 40, 39, 41, 42, 43,
+	44, 45, 38, 38, 38, 38, 38, 38,
+	50, 51, 52, 53, 54, 55, 38, 38,
+	38, 42, 57, 58, 59, 61, 42, 43,
+	44, 45, 38, 38, 38, 38, 38, 38,
+	38, 38, 38, 53, 54, 55, 38, 38,
+	38, 38, 57, 58, 59, 61, 43, 44,
+	45, 38, 38, 38, 38, 38, 38, 38,
+	38, 38, 38, 38, 38, 38, 38, 38,
+	38, 57, 58, 59, 44, 45, 38, 38,
+	38, 38, 38, 38, 38, 38, 38, 38,
+	38, 38, 38, 38, 38, 38, 57, 58,
+	59, 45, 38, 38, 38, 38, 38, 38,
+	38, 38, 38, 38, 38, 38, 38, 38,
+	38, 38, 57, 58, 59, 57, 58, 58,
+	43, 44, 45, 38, 38, 38, 38, 38,
+	38, 38, 38, 38, 53, 54, 55, 38,
+	38, 38, 38, 57, 58, 59, 61, 43,
+	44, 45, 38, 38, 38, 38, 38, 38,
+	38, 38, 38, 38, 54, 55, 38, 38,
+	38, 38, 57, 58, 59, 61, 43, 44,
+	45, 38, 38, 38, 38, 38, 38, 38,
+	38, 38, 38, 38, 55, 38, 38, 38,
+	38, 57, 58, 59, 61, 63, 43, 44,
+	45, 38, 38, 38, 38, 38, 38, 38,
+	38, 38, 38, 38, 38, 38, 38, 38,
+	38, 57, 58, 59, 61, 42, 43, 44,
+	45, 38, 38, 38, 38, 38, 38, 50,
+	51, 52, 53, 54, 55, 38, 38, 38,
+	42, 57, 58, 59, 61, 42, 43, 44,
+	45, 38, 38, 38, 38, 38, 38, 38,
+	51, 52, 53, 54, 55, 38, 38, 38,
+	42, 57, 58, 59, 61, 42, 43, 44,
+	45, 38, 38, 38, 38, 38, 38, 38,
+	38, 52, 53, 54, 55, 38, 38, 38,
+	42, 57, 58, 59, 61, 41, 42, 43,
+	44, 45, 38, 47, 41, 38, 38, 38,
+	50, 51, 52, 53, 54, 55, 38, 38,
+	38, 42, 57, 58, 59, 61, 41, 42,
+	43, 44, 45, 38, 38, 41, 38, 38,
+	38, 50, 51, 52, 53, 54, 55, 38,
+	38, 38, 42, 57, 58, 59, 61, 41,
+	42, 43, 44, 45, 46, 47, 41, 38,
+	38, 38, 50, 51, 52, 53, 54, 55,
+	38, 38, 38, 42, 57, 58, 59, 61,
+	39, 40, 38, 38, 38, 41, 42, 43,
+	44, 45, 46, 47, 41, 48, 38, 49,
+	50, 51, 52, 53, 54, 55, 38, 38,
+	38, 56, 57, 58, 59, 40, 39, 60,
+	60, 60, 60, 60, 60, 60, 60, 60,
+	42, 43, 44, 45, 60, 60, 60, 60,
+	60, 60, 60, 60, 60, 53, 54, 55,
+	60, 60, 60, 60, 57, 58, 59, 61,
+	65, 7, 39, 40, 38, 38, 38, 41,
+	42, 43, 44, 45, 46, 47, 41, 48,
+	5, 49, 50, 51, 52, 53, 54, 55,
+	12, 67, 38, 56, 57, 58, 59, 40,
+	12, 67, 67, 1, 70, 69, 69, 69,
+	13, 14, 15, 16, 17, 18, 19, 13,
+	20, 22, 22, 23, 24, 25, 26, 27,
+	28, 69, 69, 69, 32, 33, 34, 35,
+	70, 13, 14, 15, 16, 17, 69, 69,
+	69, 69, 69, 69, 23, 24, 25, 26,
+	27, 28, 69, 69, 69, 14, 33, 34,
+	35, 71, 14, 15, 16, 17, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 26,
+	27, 28, 69, 69, 69, 69, 33, 34,
+	35, 71, 15, 16, 17, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 33, 34, 35,
+	16, 17, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 33, 34, 35, 17, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 33, 34,
+	35, 33, 34, 34, 15, 16, 17, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	26, 27, 28, 69, 69, 69, 69, 33,
+	34, 35, 71, 15, 16, 17, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	27, 28, 69, 69, 69, 69, 33, 34,
+	35, 71, 15, 16, 17, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	28, 69, 69, 69, 69, 33, 34, 35,
+	71, 15, 16, 17, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 33, 34, 35, 71,
+	14, 15, 16, 17, 69, 69, 69, 69,
+	69, 69, 23, 24, 25, 26, 27, 28,
+	69, 69, 69, 14, 33, 34, 35, 71,
+	14, 15, 16, 17, 69, 69, 69, 69,
+	69, 69, 69, 24, 25, 26, 27, 28,
+	69, 69, 69, 14, 33, 34, 35, 71,
+	14, 15, 16, 17, 69, 69, 69, 69,
+	69, 69, 69, 69, 25, 26, 27, 28,
+	69, 69, 69, 14, 33, 34, 35, 71,
+	13, 14, 15, 16, 17, 69, 19, 13,
+	69, 69, 69, 23, 24, 25, 26, 27,
+	28, 69, 69, 69, 14, 33, 34, 35,
+	71, 13, 14, 15, 16, 17, 69, 69,
+	13, 69, 69, 69, 23, 24, 25, 26,
+	27, 28, 69, 69, 69, 14, 33, 34,
+	35, 71, 13, 14, 15, 16, 17, 18,
+	19, 13, 69, 69, 69, 23, 24, 25,
+	26, 27, 28, 69, 69, 69, 14, 33,
+	34, 35, 71, 1, 70, 69, 69, 69,
+	13, 14, 15, 16, 17, 18, 19, 13,
+	20, 69, 22, 23, 24, 25, 26, 27,
+	28, 69, 69, 69, 32, 33, 34, 35,
+	70, 1, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 14, 15, 16, 17, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	26, 27, 28, 69, 69, 69, 69, 33,
+	34, 35, 71, 1, 73, 10, 5, 69,
+	69, 5, 1, 70, 10, 69, 69, 13,
+	14, 15, 16, 17, 18, 19, 13, 20,
+	21, 22, 23, 24, 25, 26, 27, 28,
+	29, 30, 69, 32, 33, 34, 35, 70,
+	1, 70, 69, 69, 69, 13, 14, 15,
+	16, 17, 18, 19, 13, 20, 21, 22,
+	23, 24, 25, 26, 27, 28, 69, 69,
+	69, 32, 33, 34, 35, 70, 29, 30,
+	30, 5, 72, 72, 5, 75, 74, 36,
+	36, 75, 74, 75, 36, 74, 37, 0
 };
 
-static const char _use_syllable_machine_trans_targs[] = {
-	5, 8, 5, 35, 2, 5, 1, 46, 
-	5, 6, 5, 30, 32, 55, 56, 58, 
-	59, 33, 36, 37, 38, 39, 40, 50, 
-	51, 52, 60, 53, 47, 48, 49, 43, 
-	44, 45, 61, 62, 63, 54, 41, 42, 
-	5, 64, 66, 5, 7, 0, 10, 11, 
-	12, 13, 14, 25, 26, 27, 28, 22, 
-	23, 24, 17, 18, 19, 29, 15, 16, 
-	5, 5, 9, 20, 5, 21, 5, 31, 
-	5, 34, 5, 5, 3, 4, 5, 57, 
-	5, 65
+static const signed char _use_syllable_machine_index_defaults[] = {
+	0, 0, 6, 38, 38, 60, 38, 38,
+	38, 38, 38, 38, 38, 38, 38, 38,
+	62, 38, 38, 38, 38, 38, 38, 38,
+	38, 60, 64, 66, 38, 68, 68, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 69, 69, 69, 69, 69,
+	69, 69, 69, 72, 69, 69, 69, 69,
+	69, 69, 72, 74, 74, 74, 0
 };
 
-static const char _use_syllable_machine_trans_actions[] = {
-	1, 0, 2, 3, 0, 4, 0, 5, 
-	8, 5, 9, 0, 5, 10, 0, 10, 
-	3, 0, 5, 5, 0, 0, 0, 5, 
-	5, 5, 3, 3, 5, 5, 5, 5, 
-	5, 5, 0, 0, 0, 3, 0, 0, 
-	11, 0, 0, 12, 5, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 5, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	13, 14, 0, 0, 15, 0, 16, 0, 
-	17, 0, 18, 19, 0, 0, 20, 0, 
-	21, 0
+static const signed char _use_syllable_machine_cond_targs[] = {
+	2, 31, 42, 2, 2, 3, 2, 26,
+	28, 51, 52, 54, 29, 32, 33, 34,
+	35, 36, 46, 47, 48, 55, 49, 43,
+	44, 45, 39, 40, 41, 56, 57, 58,
+	50, 37, 38, 2, 59, 61, 2, 4,
+	5, 6, 7, 8, 9, 10, 21, 22,
+	23, 24, 18, 19, 20, 13, 14, 15,
+	25, 11, 12, 2, 2, 16, 2, 17,
+	2, 27, 2, 30, 2, 2, 0, 1,
+	2, 53, 2, 60, 0
 };
 
-static const char _use_syllable_machine_to_state_actions[] = {
-	0, 0, 0, 0, 0, 6, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0
+static const signed char _use_syllable_machine_cond_actions[] = {
+	1, 2, 2, 0, 5, 0, 6, 0,
+	0, 0, 0, 2, 0, 2, 2, 0,
+	0, 0, 2, 2, 2, 2, 2, 2,
+	2, 2, 2, 2, 2, 0, 0, 0,
+	2, 0, 0, 7, 0, 0, 8, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 9, 10, 0, 11, 0,
+	12, 0, 13, 0, 14, 15, 0, 0,
+	16, 0, 17, 0, 0
 };
 
-static const char _use_syllable_machine_from_state_actions[] = {
-	0, 0, 0, 0, 0, 7, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0
+static const signed char _use_syllable_machine_to_state_actions[] = {
+	0, 0, 3, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0
 };
 
-static const short _use_syllable_machine_eof_trans[] = {
-	1, 3, 3, 6, 6, 0, 44, 44, 
-	66, 66, 44, 44, 44, 44, 44, 44, 
-	44, 44, 44, 44, 69, 44, 44, 44, 
-	44, 44, 44, 44, 44, 66, 71, 73, 
-	44, 75, 75, 76, 76, 76, 76, 76, 
-	76, 76, 76, 76, 76, 76, 76, 76, 
-	76, 76, 76, 76, 76, 76, 76, 79, 
-	76, 76, 79, 76, 76, 76, 76, 79, 
-	81, 81, 81
+static const signed char _use_syllable_machine_from_state_actions[] = {
+	0, 0, 4, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0
 };
 
-static const int use_syllable_machine_start = 5;
-static const int use_syllable_machine_first_final = 5;
+static const signed char _use_syllable_machine_eof_trans[] = {
+	1, 1, 4, 39, 39, 61, 39, 39,
+	39, 39, 39, 39, 39, 39, 39, 39,
+	63, 39, 39, 39, 39, 39, 39, 39,
+	39, 61, 65, 67, 39, 69, 69, 70,
+	70, 70, 70, 70, 70, 70, 70, 70,
+	70, 70, 70, 70, 70, 70, 70, 70,
+	70, 70, 70, 73, 70, 70, 70, 70,
+	70, 70, 73, 75, 75, 75, 0
+};
+
+static const int use_syllable_machine_start = 2;
+static const int use_syllable_machine_first_final = 2;
 static const int use_syllable_machine_error = -1;
 
-static const int use_syllable_machine_en_main = 5;
+static const int use_syllable_machine_en_main = 2;
 
 
-#line 39 "hb-ot-shape-complex-use-machine.rl"
+#line 59 "hb-ot-shape-complex-use-machine.rl"
 
 
 
-#line 166 "hb-ot-shape-complex-use-machine.rl"
+#line 176 "hb-ot-shape-complex-use-machine.rl"
 
 
 #define found_syllable(syllable_type) \
-  HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", (*ts).second.first, (*te).second.first, #syllable_type); \
-    for (unsigned i = (*ts).second.first; i < (*te).second.first; ++i) \
-      info[i].syllable() = (syllable_serial << 4) | use_##syllable_type; \
-    syllable_serial++; \
-    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
-  } HB_STMT_END
+HB_STMT_START { \
+	if (0) fprintf (stderr, "syllable %d..%d %s\n", (*ts).second.first, (*te).second.first, #syllable_type); \
+		for (unsigned i = (*ts).second.first; i < (*te).second.first; ++i) \
+	info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+	syllable_serial++; \
+	if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+	} HB_STMT_END
+
+
+template <typename Iter>
+struct machine_index_t :
+hb_iter_with_fallback_t<machine_index_t<Iter>,
+typename Iter::item_t>
+{
+	machine_index_t (const Iter& it) : it (it) {}
+	machine_index_t (const machine_index_t& o) : it (o.it) {}
+	
+	static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator;
+	static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator;
+	
+	typename Iter::item_t __item__ () const { return *it; }
+	typename Iter::item_t __item_at__ (unsigned i) const { return it[i]; }
+	unsigned __len__ () const { return it.len (); }
+	void __next__ () { ++it; }
+	void __forward__ (unsigned n) { it += n; }
+	void __prev__ () { --it; }
+	void __rewind__ (unsigned n) { it -= n; }
+	void operator = (unsigned n)
+	{ unsigned index = (*it).first; if (index < n) it += n - index; else if (index > n) it -= index - n; }
+	void operator = (const machine_index_t& o) { *this = (*o.it).first; }
+	bool operator == (const machine_index_t& o) const { return (*it).first == (*o.it).first; }
+	bool operator != (const machine_index_t& o) const { return !(*this == o); }
+	
+	private:
+	Iter it;
+};
+struct
+{
+	template <typename Iter,
+	hb_requires (hb_is_iterable (Iter))>
+	machine_index_t<hb_iter_type<Iter>>
+	operator () (Iter&& it) const
+	{ return machine_index_t<hb_iter_type<Iter>> (hb_iter (it)); }
+}
+HB_FUNCOBJ (machine_index);
+
+
 
 static bool
 not_standard_default_ignorable (const hb_glyph_info_t &i)
-{ return !((i.use_category() == USE_O || i.use_category() == USE_Rsv) && _hb_glyph_info_is_default_ignorable (&i)); }
+{ return !(i.use_category() == USE(O) && _hb_glyph_info_is_default_ignorable (&i)); }
 
-static void
+static inline void
 find_syllables_use (hb_buffer_t *buffer)
 {
-  hb_glyph_info_t *info = buffer->info;
-  auto p =
-    + hb_iter (info, buffer->len)
-    | hb_enumerate
-    | hb_filter ([] (const hb_glyph_info_t &i) { return not_standard_default_ignorable (i); },
-		 hb_second)
-    | hb_filter ([&] (const hb_pair_t<unsigned, const hb_glyph_info_t &> p)
-		 {
-		   if (p.second.use_category() == USE_ZWNJ)
-		     for (unsigned i = p.first + 1; i < buffer->len; ++i)
-		       if (not_standard_default_ignorable (info[i]))
-			 return !_hb_glyph_info_is_unicode_mark (&info[i]);
-		   return true;
-		 })
-    | hb_enumerate
-    | machine_index
-    ;
-  auto pe = p + p.len ();
-  auto eof = +pe;
-  auto ts = +p;
-  auto te = +p;
-  unsigned int act;
-  int cs;
-  
-#line 383 "hb-ot-shape-complex-use-machine.hh"
+	hb_glyph_info_t *info = buffer->info;
+	auto p =
+	+ hb_iter (info, buffer->len)
+	| hb_enumerate
+	| hb_filter ([] (const hb_glyph_info_t &i) { return not_standard_default_ignorable (i); },
+	hb_second)
+	| hb_filter ([&] (const hb_pair_t<unsigned, const hb_glyph_info_t &> p)
 	{
-	cs = use_syllable_machine_start;
-	ts = 0;
-	te = 0;
-	act = 0;
-	}
-
-#line 210 "hb-ot-shape-complex-use-machine.rl"
-
-
-  unsigned int syllable_serial = 1;
-  
-#line 396 "hb-ot-shape-complex-use-machine.hh"
+		if (p.second.use_category() == USE(ZWNJ))
+			for (unsigned i = p.first + 1; i < buffer->len; ++i)
+		if (not_standard_default_ignorable (info[i]))
+			return !_hb_glyph_info_is_unicode_mark (&info[i]);
+		return true;
+	})
+	| hb_enumerate
+	| machine_index
+	;
+	auto pe = p + p.len ();
+	auto eof = +pe;
+	auto ts = +p;
+	auto te = +p;
+	unsigned int act HB_UNUSED;
+	int cs;
+	
+#line 443 "hb-ot-shape-complex-use-machine.hh"
 	{
-	int _slen;
-	int _trans;
-	const unsigned char *_keys;
-	const char *_inds;
-	if ( p == pe )
-		goto _test_eof;
-_resume:
-	switch ( _use_syllable_machine_from_state_actions[cs] ) {
-	case 7:
-#line 1 "NONE"
-	{ts = p;}
-	break;
-#line 410 "hb-ot-shape-complex-use-machine.hh"
+		cs = (int)use_syllable_machine_start;
+		ts = 0;
+		te = 0;
 	}
-
-	_keys = _use_syllable_machine_trans_keys + (cs<<1);
-	_inds = _use_syllable_machine_indicies + _use_syllable_machine_index_offsets[cs];
-
-	_slen = _use_syllable_machine_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=( (*p).second.second.use_category()) &&
-		( (*p).second.second.use_category()) <= _keys[1] ?
-		( (*p).second.second.use_category()) - _keys[0] : _slen ];
-
-_eof_trans:
-	cs = _use_syllable_machine_trans_targs[_trans];
-
-	if ( _use_syllable_machine_trans_actions[_trans] == 0 )
-		goto _again;
-
-	switch ( _use_syllable_machine_trans_actions[_trans] ) {
-	case 5:
-#line 1 "NONE"
-	{te = p+1;}
-	break;
-	case 8:
-#line 153 "hb-ot-shape-complex-use-machine.rl"
-	{te = p+1;{ found_syllable (independent_cluster); }}
-	break;
-	case 13:
-#line 156 "hb-ot-shape-complex-use-machine.rl"
-	{te = p+1;{ found_syllable (standard_cluster); }}
-	break;
-	case 11:
-#line 161 "hb-ot-shape-complex-use-machine.rl"
-	{te = p+1;{ found_syllable (broken_cluster); }}
-	break;
-	case 9:
-#line 162 "hb-ot-shape-complex-use-machine.rl"
-	{te = p+1;{ found_syllable (non_cluster); }}
-	break;
-	case 14:
-#line 154 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (virama_terminated_cluster); }}
-	break;
-	case 15:
-#line 155 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (sakot_terminated_cluster); }}
-	break;
-	case 12:
-#line 156 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (standard_cluster); }}
-	break;
-	case 17:
-#line 157 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (number_joiner_terminated_cluster); }}
-	break;
-	case 16:
-#line 158 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (numeral_cluster); }}
-	break;
-	case 18:
-#line 159 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (symbol_cluster); }}
-	break;
-	case 21:
-#line 160 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (hieroglyph_cluster); }}
-	break;
-	case 19:
-#line 161 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (broken_cluster); }}
-	break;
-	case 20:
-#line 162 "hb-ot-shape-complex-use-machine.rl"
-	{te = p;p--;{ found_syllable (non_cluster); }}
-	break;
-	case 1:
-#line 156 "hb-ot-shape-complex-use-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (standard_cluster); }}
-	break;
-	case 4:
-#line 161 "hb-ot-shape-complex-use-machine.rl"
-	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
-	break;
-	case 2:
-#line 1 "NONE"
-	{	switch( act ) {
-	case 9:
-	{{p = ((te))-1;} found_syllable (broken_cluster); }
-	break;
-	case 10:
-	{{p = ((te))-1;} found_syllable (non_cluster); }
-	break;
-	}
-	}
-	break;
-	case 3:
-#line 1 "NONE"
-	{te = p+1;}
-#line 161 "hb-ot-shape-complex-use-machine.rl"
-	{act = 9;}
-	break;
-	case 10:
-#line 1 "NONE"
-	{te = p+1;}
-#line 162 "hb-ot-shape-complex-use-machine.rl"
-	{act = 10;}
-	break;
-#line 516 "hb-ot-shape-complex-use-machine.hh"
-	}
-
-_again:
-	switch ( _use_syllable_machine_to_state_actions[cs] ) {
-	case 6:
-#line 1 "NONE"
-	{ts = 0;}
-	break;
-#line 525 "hb-ot-shape-complex-use-machine.hh"
-	}
-
-	if ( ++p != pe )
-		goto _resume;
-	_test_eof: {}
-	if ( p == eof )
+	
+#line 260 "hb-ot-shape-complex-use-machine.rl"
+	
+	
+	unsigned int syllable_serial = 1;
+	
+#line 455 "hb-ot-shape-complex-use-machine.hh"
 	{
-	if ( _use_syllable_machine_eof_trans[cs] > 0 ) {
-		_trans = _use_syllable_machine_eof_trans[cs] - 1;
-		goto _eof_trans;
+		unsigned int _trans = 0;
+		const unsigned char * _keys;
+		const signed char * _inds;
+		int _ic;
+		_resume: {}
+		if ( p == pe && p != eof )
+			goto _out;
+		switch ( _use_syllable_machine_from_state_actions[cs] ) {
+			case 4:  {
+				{
+#line 1 "NONE"
+					{ts = p;}}
+				
+#line 470 "hb-ot-shape-complex-use-machine.hh"
+				
+				
+				break; 
+			}
+		}
+		
+		if ( p == eof ) {
+			if ( _use_syllable_machine_eof_trans[cs] > 0 ) {
+				_trans = (unsigned int)_use_syllable_machine_eof_trans[cs] - 1;
+			}
+		}
+		else {
+			_keys = ( _use_syllable_machine_trans_keys + ((cs<<1)));
+			_inds = ( _use_syllable_machine_indicies + (_use_syllable_machine_index_offsets[cs]));
+			
+			if ( ((*p).second.second.use_category()) <= 52 ) {
+				_ic = (int)_use_syllable_machine_char_class[(int)((*p).second.second.use_category()) - 0];
+				if ( _ic <= (int)(*( _keys+1)) && _ic >= (int)(*( _keys)) )
+					_trans = (unsigned int)(*( _inds + (int)( _ic - (int)(*( _keys)) ) )); 
+				else
+					_trans = (unsigned int)_use_syllable_machine_index_defaults[cs];
+			}
+			else {
+				_trans = (unsigned int)_use_syllable_machine_index_defaults[cs];
+			}
+			
+		}
+		cs = (int)_use_syllable_machine_cond_targs[_trans];
+		
+		if ( _use_syllable_machine_cond_actions[_trans] != 0 ) {
+			
+			switch ( _use_syllable_machine_cond_actions[_trans] ) {
+				case 2:  {
+					{
+#line 1 "NONE"
+						{te = p+1;}}
+					
+#line 508 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 5:  {
+					{
+#line 163 "hb-ot-shape-complex-use-machine.rl"
+						{te = p+1;{
+#line 163 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_independent_cluster); }
+						}}
+					
+#line 521 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 9:  {
+					{
+#line 166 "hb-ot-shape-complex-use-machine.rl"
+						{te = p+1;{
+#line 166 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_standard_cluster); }
+						}}
+					
+#line 534 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 7:  {
+					{
+#line 171 "hb-ot-shape-complex-use-machine.rl"
+						{te = p+1;{
+#line 171 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_broken_cluster); }
+						}}
+					
+#line 547 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 6:  {
+					{
+#line 172 "hb-ot-shape-complex-use-machine.rl"
+						{te = p+1;{
+#line 172 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_non_cluster); }
+						}}
+					
+#line 560 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 10:  {
+					{
+#line 164 "hb-ot-shape-complex-use-machine.rl"
+						{te = p;p = p - 1;{
+#line 164 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_virama_terminated_cluster); }
+						}}
+					
+#line 573 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 11:  {
+					{
+#line 165 "hb-ot-shape-complex-use-machine.rl"
+						{te = p;p = p - 1;{
+#line 165 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_sakot_terminated_cluster); }
+						}}
+					
+#line 586 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 8:  {
+					{
+#line 166 "hb-ot-shape-complex-use-machine.rl"
+						{te = p;p = p - 1;{
+#line 166 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_standard_cluster); }
+						}}
+					
+#line 599 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 13:  {
+					{
+#line 167 "hb-ot-shape-complex-use-machine.rl"
+						{te = p;p = p - 1;{
+#line 167 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_number_joiner_terminated_cluster); }
+						}}
+					
+#line 612 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 12:  {
+					{
+#line 168 "hb-ot-shape-complex-use-machine.rl"
+						{te = p;p = p - 1;{
+#line 168 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_numeral_cluster); }
+						}}
+					
+#line 625 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 14:  {
+					{
+#line 169 "hb-ot-shape-complex-use-machine.rl"
+						{te = p;p = p - 1;{
+#line 169 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_symbol_cluster); }
+						}}
+					
+#line 638 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 17:  {
+					{
+#line 170 "hb-ot-shape-complex-use-machine.rl"
+						{te = p;p = p - 1;{
+#line 170 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_hieroglyph_cluster); }
+						}}
+					
+#line 651 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 15:  {
+					{
+#line 171 "hb-ot-shape-complex-use-machine.rl"
+						{te = p;p = p - 1;{
+#line 171 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_broken_cluster); }
+						}}
+					
+#line 664 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 16:  {
+					{
+#line 172 "hb-ot-shape-complex-use-machine.rl"
+						{te = p;p = p - 1;{
+#line 172 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_non_cluster); }
+						}}
+					
+#line 677 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+				case 1:  {
+					{
+#line 171 "hb-ot-shape-complex-use-machine.rl"
+						{p = ((te))-1;
+							{
+#line 171 "hb-ot-shape-complex-use-machine.rl"
+								found_syllable (use_broken_cluster); }
+						}}
+					
+#line 691 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+			}
+			
+		}
+		
+		if ( p == eof ) {
+			if ( cs >= 2 )
+				goto _out;
+		}
+		else {
+			switch ( _use_syllable_machine_to_state_actions[cs] ) {
+				case 3:  {
+					{
+#line 1 "NONE"
+						{ts = 0;}}
+					
+#line 711 "hb-ot-shape-complex-use-machine.hh"
+					
+					
+					break; 
+				}
+			}
+			
+			p += 1;
+			goto _resume;
+		}
+		_out: {}
 	}
-	}
-
-	}
-
-#line 215 "hb-ot-shape-complex-use-machine.rl"
-
+	
+#line 265 "hb-ot-shape-complex-use-machine.rl"
+	
 }
 
 #undef found_syllable
diff --git a/src/hb-ot-shape-complex-use-machine.rl b/src/hb-ot-shape-complex-use-machine.rl
index df9b638..00d82b4 100644
--- a/src/hb-ot-shape-complex-use-machine.rl
+++ b/src/hb-ot-shape-complex-use-machine.rl
@@ -30,76 +30,86 @@
 #define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
 
 #include "hb.hh"
-#include "hb-ot-shape-complex-machine-index.hh"
+
+#include "hb-ot-shape-complex-syllabic.hh"
+
+/* buffer var allocations */
+#define use_category() complex_var_u8_category()
+
+#define USE(Cat) use_syllable_machine_ex_##Cat
+
+enum use_syllable_type_t {
+  use_independent_cluster,
+  use_virama_terminated_cluster,
+  use_sakot_terminated_cluster,
+  use_standard_cluster,
+  use_number_joiner_terminated_cluster,
+  use_numeral_cluster,
+  use_symbol_cluster,
+  use_hieroglyph_cluster,
+  use_broken_cluster,
+  use_non_cluster,
+};
 
 %%{
   machine use_syllable_machine;
   alphtype unsigned char;
+  write exports;
   write data;
 }%%
 
 %%{
 
-# Same order as enum use_category_t.  Not sure how to avoid duplication.
+# Categories used in the Universal Shaping Engine spec:
+# https://docs.microsoft.com/en-us/typography/script-development/use
 
-O	= 0; # OTHER
+export O	= 0; # OTHER
 
-B	= 1; # BASE
-IND	= 3; # BASE_IND
-N	= 4; # BASE_NUM
-GB	= 5; # BASE_OTHER
-#F	= 7; # CONS_FINAL
-#FM	= 8; # CONS_FINAL_MOD
-#M	= 9; # CONS_MED
-#CM	= 10; # CONS_MOD
-SUB	= 11; # CONS_SUB
-H	= 12; # HALANT
+export B	= 1; # BASE
+export N	= 4; # BASE_NUM
+export GB	= 5; # BASE_OTHER
+export SUB	= 11; # CONS_SUB
+export H	= 12; # HALANT
 
-HN	= 13; # HALANT_NUM
-ZWNJ	= 14; # Zero width non-joiner
-ZWJ	= 15; # Zero width joiner
-WJ	= 16; # Word joiner
-Rsv	= 17; # Reserved characters
-R	= 18; # REPHA
-S	= 19; # SYM
-#SM	= 20; # SYM_MOD
-#V	= 36; # VOWEL
-#VM	= 40; # VOWEL_MOD
-CS	= 43; # CONS_WITH_STACKER
-HVM	= 44; # HALANT_OR_VOWEL_MODIFIER
-Sk	= 48; # SAKOT
-G	= 49; # HIEROGLYPH
-J	= 50; # HIEROGLYPH_JOINER
-SB	= 51; # HIEROGLYPH_SEGMENT_BEGIN
-SE	= 52; # HIEROGLYPH_SEGMENT_END
+export HN	= 13; # HALANT_NUM
+export ZWNJ	= 14; # Zero width non-joiner
+export R	= 18; # REPHA
+export S	= 19; # SYM
+export CS	= 43; # CONS_WITH_STACKER
+export HVM	= 44; # HALANT_OR_VOWEL_MODIFIER
+export Sk	= 48; # SAKOT
+export G	= 49; # HIEROGLYPH
+export J	= 50; # HIEROGLYPH_JOINER
+export SB	= 51; # HIEROGLYPH_SEGMENT_BEGIN
+export SE	= 52; # HIEROGLYPH_SEGMENT_END
 
-FAbv	= 24; # CONS_FINAL_ABOVE
-FBlw	= 25; # CONS_FINAL_BELOW
-FPst	= 26; # CONS_FINAL_POST
-MAbv	= 27; # CONS_MED_ABOVE
-MBlw	= 28; # CONS_MED_BELOW
-MPst	= 29; # CONS_MED_POST
-MPre	= 30; # CONS_MED_PRE
-CMAbv	= 31; # CONS_MOD_ABOVE
-CMBlw	= 32; # CONS_MOD_BELOW
-VAbv	= 33; # VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST
-VBlw	= 34; # VOWEL_BELOW / VOWEL_BELOW_POST
-VPst	= 35; # VOWEL_POST	UIPC = Right
-VPre	= 22; # VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST
-VMAbv	= 37; # VOWEL_MOD_ABOVE
-VMBlw	= 38; # VOWEL_MOD_BELOW
-VMPst	= 39; # VOWEL_MOD_POST
-VMPre	= 23; # VOWEL_MOD_PRE
-SMAbv	= 41; # SYM_MOD_ABOVE
-SMBlw	= 42; # SYM_MOD_BELOW
-FMAbv	= 45; # CONS_FINAL_MOD	UIPC = Top
-FMBlw	= 46; # CONS_FINAL_MOD	UIPC = Bottom
-FMPst	= 47; # CONS_FINAL_MOD	UIPC = Not_Applicable
+export FAbv	= 24; # CONS_FINAL_ABOVE
+export FBlw	= 25; # CONS_FINAL_BELOW
+export FPst	= 26; # CONS_FINAL_POST
+export MAbv	= 27; # CONS_MED_ABOVE
+export MBlw	= 28; # CONS_MED_BELOW
+export MPst	= 29; # CONS_MED_POST
+export MPre	= 30; # CONS_MED_PRE
+export CMAbv	= 31; # CONS_MOD_ABOVE
+export CMBlw	= 32; # CONS_MOD_BELOW
+export VAbv	= 33; # VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST
+export VBlw	= 34; # VOWEL_BELOW / VOWEL_BELOW_POST
+export VPst	= 35; # VOWEL_POST	UIPC = Right
+export VPre	= 22; # VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST
+export VMAbv	= 37; # VOWEL_MOD_ABOVE
+export VMBlw	= 38; # VOWEL_MOD_BELOW
+export VMPst	= 39; # VOWEL_MOD_POST
+export VMPre	= 23; # VOWEL_MOD_PRE
+export SMAbv	= 41; # SYM_MOD_ABOVE
+export SMBlw	= 42; # SYM_MOD_BELOW
+export FMAbv	= 45; # CONS_FINAL_MOD	UIPC = Top
+export FMBlw	= 46; # CONS_FINAL_MOD	UIPC = Bottom
+export FMPst	= 47; # CONS_FINAL_MOD	UIPC = Not_Applicable
+
 
 h = H | HVM | Sk;
 
-# Override: Adhoc ZWJ placement. https://github.com/harfbuzz/harfbuzz/issues/542#issuecomment-353169729
-consonant_modifiers = CMAbv* CMBlw* ((ZWJ?.h.ZWJ? B | SUB) CMAbv? CMBlw*)*;
+consonant_modifiers = CMAbv* CMBlw* ((h B | SUB) CMAbv? CMBlw*)*;
 medial_consonants = MPre? MAbv? MBlw? MPst?;
 dependent_vowels = VPre* VAbv* VBlw* VPst*;
 vowel_modifiers = HVM? VMPre* VMAbv* VMBlw* VMPst*;
@@ -126,7 +136,7 @@
 virama_terminated_cluster =
 	complex_syllable_start
 	consonant_modifiers
-	ZWJ?.h.ZWJ?
+	h
 ;
 sakot_terminated_cluster =
 	complex_syllable_start
@@ -146,20 +156,20 @@
 numeral_cluster = N numeral_cluster_tail?;
 symbol_cluster = (S | GB) symbol_cluster_tail?;
 hieroglyph_cluster = SB+ | SB* G SE* (J SE* (G SE*)?)*;
-independent_cluster = (IND | O | Rsv | WJ);
+independent_cluster = O;
 other = any;
 
 main := |*
-	independent_cluster			=> { found_syllable (independent_cluster); };
-	virama_terminated_cluster		=> { found_syllable (virama_terminated_cluster); };
-	sakot_terminated_cluster		=> { found_syllable (sakot_terminated_cluster); };
-	standard_cluster			=> { found_syllable (standard_cluster); };
-	number_joiner_terminated_cluster	=> { found_syllable (number_joiner_terminated_cluster); };
-	numeral_cluster				=> { found_syllable (numeral_cluster); };
-	symbol_cluster				=> { found_syllable (symbol_cluster); };
-	hieroglyph_cluster			=> { found_syllable (hieroglyph_cluster); };
-	broken_cluster				=> { found_syllable (broken_cluster); };
-	other					=> { found_syllable (non_cluster); };
+	independent_cluster			=> { found_syllable (use_independent_cluster); };
+	virama_terminated_cluster		=> { found_syllable (use_virama_terminated_cluster); };
+	sakot_terminated_cluster		=> { found_syllable (use_sakot_terminated_cluster); };
+	standard_cluster			=> { found_syllable (use_standard_cluster); };
+	number_joiner_terminated_cluster	=> { found_syllable (use_number_joiner_terminated_cluster); };
+	numeral_cluster				=> { found_syllable (use_numeral_cluster); };
+	symbol_cluster				=> { found_syllable (use_symbol_cluster); };
+	hieroglyph_cluster			=> { found_syllable (use_hieroglyph_cluster); };
+	broken_cluster				=> { found_syllable (use_broken_cluster); };
+	other					=> { found_syllable (use_non_cluster); };
 *|;
 
 
@@ -169,16 +179,56 @@
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", (*ts).second.first, (*te).second.first, #syllable_type); \
     for (unsigned i = (*ts).second.first; i < (*te).second.first; ++i) \
-      info[i].syllable() = (syllable_serial << 4) | use_##syllable_type; \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
     syllable_serial++; \
     if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
+
+template <typename Iter>
+struct machine_index_t :
+  hb_iter_with_fallback_t<machine_index_t<Iter>,
+			  typename Iter::item_t>
+{
+  machine_index_t (const Iter& it) : it (it) {}
+  machine_index_t (const machine_index_t& o) : it (o.it) {}
+
+  static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator;
+  static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator;
+
+  typename Iter::item_t __item__ () const { return *it; }
+  typename Iter::item_t __item_at__ (unsigned i) const { return it[i]; }
+  unsigned __len__ () const { return it.len (); }
+  void __next__ () { ++it; }
+  void __forward__ (unsigned n) { it += n; }
+  void __prev__ () { --it; }
+  void __rewind__ (unsigned n) { it -= n; }
+  void operator = (unsigned n)
+  { unsigned index = (*it).first; if (index < n) it += n - index; else if (index > n) it -= index - n; }
+  void operator = (const machine_index_t& o) { *this = (*o.it).first; }
+  bool operator == (const machine_index_t& o) const { return (*it).first == (*o.it).first; }
+  bool operator != (const machine_index_t& o) const { return !(*this == o); }
+
+  private:
+  Iter it;
+};
+struct
+{
+  template <typename Iter,
+	    hb_requires (hb_is_iterable (Iter))>
+  machine_index_t<hb_iter_type<Iter>>
+  operator () (Iter&& it) const
+  { return machine_index_t<hb_iter_type<Iter>> (hb_iter (it)); }
+}
+HB_FUNCOBJ (machine_index);
+
+
+
 static bool
 not_standard_default_ignorable (const hb_glyph_info_t &i)
-{ return !((i.use_category() == USE_O || i.use_category() == USE_Rsv) && _hb_glyph_info_is_default_ignorable (&i)); }
+{ return !(i.use_category() == USE(O) && _hb_glyph_info_is_default_ignorable (&i)); }
 
-static void
+static inline void
 find_syllables_use (hb_buffer_t *buffer)
 {
   hb_glyph_info_t *info = buffer->info;
@@ -189,7 +239,7 @@
 		 hb_second)
     | hb_filter ([&] (const hb_pair_t<unsigned, const hb_glyph_info_t &> p)
 		 {
-		   if (p.second.use_category() == USE_ZWNJ)
+		   if (p.second.use_category() == USE(ZWNJ))
 		     for (unsigned i = p.first + 1; i < buffer->len; ++i)
 		       if (not_standard_default_ignorable (info[i]))
 			 return !_hb_glyph_info_is_unicode_mark (&info[i]);
@@ -202,7 +252,7 @@
   auto eof = +pe;
   auto ts = +p;
   auto te = +p;
-  unsigned int act;
+  unsigned int act HB_UNUSED;
   int cs;
   %%{
     write init;
diff --git a/src/hb-ot-shape-complex-use-table.cc b/src/hb-ot-shape-complex-use-table.hh
similarity index 95%
rename from src/hb-ot-shape-complex-use-table.cc
rename to src/hb-ot-shape-complex-use-table.hh
index 1e05f9a..a35894c 100644
--- a/src/hb-ot-shape-complex-use-table.cc
+++ b/src/hb-ot-shape-complex-use-table.hh
@@ -31,60 +31,57 @@
  * UnicodeData.txt does not have a header.
  */
 
+#ifndef HB_OT_SHAPE_COMPLEX_USE_TABLE_HH
+#define HB_OT_SHAPE_COMPLEX_USE_TABLE_HH
+
 #include "hb.hh"
 
-#ifndef HB_NO_OT_SHAPE
-
-#include "hb-ot-shape-complex-use.hh"
+#include "hb-ot-shape-complex-use-machine.hh"
 
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-macros"
-#define B	USE_B	/* BASE */
-#define CS	USE_CS	/* CONS_WITH_STACKER */
-#define G	USE_G	/* HIEROGLYPH */
-#define GB	USE_GB	/* BASE_OTHER */
-#define H	USE_H	/* HALANT */
-#define HN	USE_HN	/* HALANT_NUM */
-#define HVM	USE_HVM	/* HALANT_OR_VOWEL_MODIFIER */
-#define IND	USE_IND	/* BASE_IND */
-#define J	USE_J	/* HIEROGLYPH_JOINER */
-#define N	USE_N	/* BASE_NUM */
-#define O	USE_O	/* OTHER */
-#define R	USE_R	/* REPHA */
-#define Rsv	USE_Rsv	/* Reserved */
-#define S	USE_S	/* SYM */
-#define SB	USE_SB	/* HIEROGLYPH_SEGMENT_BEGIN */
-#define SE	USE_SE	/* HIEROGLYPH_SEGMENT_END */
-#define SUB	USE_SUB	/* CONS_SUB */
-#define Sk	USE_Sk	/* SAKOT */
-#define WJ	USE_WJ	/* Word_Joiner */
-#define ZWJ	USE_ZWJ	/* ZWJ */
-#define ZWNJ	USE_ZWNJ	/* ZWNJ */
-#define CMAbv	USE_CMAbv
-#define CMBlw	USE_CMBlw
-#define FAbv	USE_FAbv
-#define FBlw	USE_FBlw
-#define FPst	USE_FPst
-#define FMAbv	USE_FMAbv
-#define FMBlw	USE_FMBlw
-#define FMPst	USE_FMPst
-#define MAbv	USE_MAbv
-#define MBlw	USE_MBlw
-#define MPst	USE_MPst
-#define MPre	USE_MPre
-#define SMAbv	USE_SMAbv
-#define SMBlw	USE_SMBlw
-#define VAbv	USE_VAbv
-#define VBlw	USE_VBlw
-#define VPst	USE_VPst
-#define VPre	USE_VPre
-#define VMAbv	USE_VMAbv
-#define VMBlw	USE_VMBlw
-#define VMPst	USE_VMPst
-#define VMPre	USE_VMPre
+#define B	USE(B)	/* BASE */
+#define CS	USE(CS)	/* CONS_WITH_STACKER */
+#define G	USE(G)	/* HIEROGLYPH */
+#define GB	USE(GB)	/* BASE_OTHER */
+#define H	USE(H)	/* HALANT */
+#define HN	USE(HN)	/* HALANT_NUM */
+#define HVM	USE(HVM)	/* HALANT_OR_VOWEL_MODIFIER */
+#define J	USE(J)	/* HIEROGLYPH_JOINER */
+#define N	USE(N)	/* BASE_NUM */
+#define O	USE(O)	/* OTHER */
+#define R	USE(R)	/* REPHA */
+#define S	USE(S)	/* SYM */
+#define SB	USE(SB)	/* HIEROGLYPH_SEGMENT_BEGIN */
+#define SE	USE(SE)	/* HIEROGLYPH_SEGMENT_END */
+#define SUB	USE(SUB)	/* CONS_SUB */
+#define Sk	USE(Sk)	/* SAKOT */
+#define ZWNJ	USE(ZWNJ)	/* ZWNJ */
+#define CMAbv	USE(CMAbv)
+#define CMBlw	USE(CMBlw)
+#define FAbv	USE(FAbv)
+#define FBlw	USE(FBlw)
+#define FPst	USE(FPst)
+#define FMAbv	USE(FMAbv)
+#define FMBlw	USE(FMBlw)
+#define FMPst	USE(FMPst)
+#define MAbv	USE(MAbv)
+#define MBlw	USE(MBlw)
+#define MPst	USE(MPst)
+#define MPre	USE(MPre)
+#define SMAbv	USE(SMAbv)
+#define SMBlw	USE(SMBlw)
+#define VAbv	USE(VAbv)
+#define VBlw	USE(VBlw)
+#define VPst	USE(VPst)
+#define VPre	USE(VPre)
+#define VMAbv	USE(VMAbv)
+#define VMBlw	USE(VMBlw)
+#define VMPst	USE(VMPst)
+#define VMPre	USE(VMPre)
 #pragma GCC diagnostic pop
 
-static const USE_TABLE_ELEMENT_TYPE use_table[] = {
+static const uint8_t use_table[] = {
 
 
 #define use_offset_0x0028u 0
@@ -148,7 +145,7 @@
   /* 0990 */     B,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 09A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,
   /* 09B0 */     B,     O,     B,     O,     O,     O,     B,     B,     B,     B,     O,     O, CMBlw,     B,  VPst,  VPre,
-  /* 09C0 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,   IND,     O,
+  /* 09C0 */  VPst,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,  VPre,  VPre,     O,     O,  VPre,  VPre,     H,     O,     O,
   /* 09D0 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     B,     B,     O,     B,
   /* 09E0 */     B,     B,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 09F0 */     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     B,     O, FMAbv,     O,
@@ -188,7 +185,7 @@
 
   /* Tamil */
 
-  /* 0B80 */     O,     O, VMAbv,   IND,     O,     B,     B,     B,     B,     B,     B,     O,     O,     O,     B,     B,
+  /* 0B80 */     O,     O, VMAbv,     O,     O,     B,     B,     B,     B,     B,     B,     O,     O,     O,     B,     B,
   /* 0B90 */     B,     O,     B,     B,     B,     B,     O,     O,     O,     B,     B,     O,     B,     O,     B,     B,
   /* 0BA0 */     O,     O,     O,     B,     B,     O,     O,     O,     B,     B,     B,     O,     O,     O,     B,     B,
   /* 0BB0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,  VPst,  VPst,
@@ -226,9 +223,9 @@
   /* 0D20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0D30 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,     B,  VPst,  VPst,
   /* 0D40 */  VPst,  VPst,  VPst,  VBlw,  VBlw,     O,  VPre,  VPre,  VPre,     O,  VPre,  VPre,  VPre,     H,     R,     O,
-  /* 0D50 */     O,     O,     O,     O,   IND,   IND,   IND,  VPst,     O,     O,     O,     O,     O,     O,     O,     B,
+  /* 0D50 */     O,     O,     O,     O,     O,     O,     O,  VPst,     O,     O,     O,     O,     O,     O,     O,     B,
   /* 0D60 */     B,     B,  VBlw,  VBlw,     O,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* 0D70 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,   IND,   IND,   IND,   IND,   IND,   IND,
+  /* 0D70 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
 
   /* Sinhala */
 
@@ -253,8 +250,8 @@
   /* 0F40 */     B,     B,     B,     B,     B,     B,     B,     B,     O,     B,     B,     B,     B,     B,     B,     B,
   /* 0F50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 0F60 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,
-  /* 0F70 */     O, CMBlw,  VBlw,  VAbv,  VAbv,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw, VMAbv,   IND,
-  /* 0F80 */  VBlw,  VAbv, VMAbv, VMAbv,  VBlw,   IND, VMAbv, VMAbv,     B,     B,     B,     B,     B,   SUB,   SUB,   SUB,
+  /* 0F70 */     O, CMBlw,  VBlw,  VAbv,  VAbv,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VBlw,  VBlw,  VBlw, VMAbv,     O,
+  /* 0F80 */  VBlw,  VAbv, VMAbv, VMAbv,  VBlw,     O, VMAbv, VMAbv,     B,     B,     B,     B,     B,   SUB,   SUB,   SUB,
   /* 0F90 */   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,     O,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,
   /* 0FA0 */   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,
   /* 0FB0 */   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,     O,     O,     O,
@@ -411,7 +408,7 @@
 
   /* 1CD0 */ VMAbv, VMAbv, VMAbv,     O, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMAbv, VMAbv, VMBlw, VMBlw, VMBlw, VMBlw,
   /* 1CE0 */ VMAbv, VMPst, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw,     O,     O,     O,     O, VMBlw,     O,     O,
-  /* 1CF0 */     O,     O,   IND,   IND, VMAbv,    CS,    CS, VMPst, VMAbv, VMAbv,    GB,     O,     O,     O,     O,     O,
+  /* 1CF0 */     O,     O,     O,     O, VMAbv,    CS,    CS, VMPst, VMAbv, VMAbv,    GB,     O,     O,     O,     O,     O,
 
 #define use_offset_0x1df8u 3040
 
@@ -423,32 +420,31 @@
 
 
   /* General Punctuation */
-                                                                         O,     O,     O,     O,  ZWNJ,   ZWJ,     O,     O,
+                                                                         O,     O,     O,     O,  ZWNJ,     O,     O,     O,
   /* 2010 */    GB,    GB,    GB,    GB,    GB,     O,     O,     O,
 
-#define use_offset_0x2060u 3064
+#define use_offset_0x2070u 3064
 
-  /* 2060 */    WJ,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
 
   /* Superscripts and Subscripts */
 
   /* 2070 */     O,     O,     O,     O, FMPst,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 2080 */     O,     O, FMPst, FMPst, FMPst,     O,     O,     O,
 
-#define use_offset_0x20f0u 3104
+#define use_offset_0x20f0u 3088
 
 
   /* Combining Diacritical Marks for Symbols */
 
   /* 20F0 */ VMAbv,     O,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x25c8u 3112
+#define use_offset_0x25c8u 3096
 
 
   /* Geometric Shapes */
                                                                          O,     O,     O,     O,     B,     O,     O,     O,
 
-#define use_offset_0x2d30u 3120
+#define use_offset_0x2d30u 3104
 
 
   /* Tifinagh */
@@ -459,7 +455,7 @@
   /* 2D60 */     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,     B,
   /* 2D70 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     H,
 
-#define use_offset_0xa800u 3200
+#define use_offset_0xa800u 3184
 
 
   /* Syloti Nagri */
@@ -546,7 +542,7 @@
   /* AAE0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPre,  VBlw,  VAbv,  VPre,  VPst,
   /* AAF0 */     O,     O,     O,     O,     O, VMPst,     H,     O,
 
-#define use_offset_0xabc0u 3960
+#define use_offset_0xabc0u 3944
 
 
   /* Meetei Mayek */
@@ -556,7 +552,7 @@
   /* ABE0 */     B,     B,     B,  VPst,  VPst,  VAbv,  VPst,  VPst,  VBlw,  VPst,  VPst,     O, VMPst,  VBlw,     O,     O,
   /* ABF0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x10a00u 4024
+#define use_offset_0x10a00u 4008
 
 
   /* Kharoshthi */
@@ -567,7 +563,7 @@
   /* 10A30 */     B,     B,     B,     B,     B,     B,     O,     O, CMAbv, CMBlw, CMBlw,     O,     O,     O,     O,     H,
   /* 10A40 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x10ac0u 4104
+#define use_offset_0x10ac0u 4088
 
 
   /* Manichaean */
@@ -576,7 +572,7 @@
   /* 10AD0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 10AE0 */     B,     B,     B,     B,     B, CMBlw, CMBlw,     O,
 
-#define use_offset_0x10b80u 4144
+#define use_offset_0x10b80u 4128
 
 
   /* Psalter Pahlavi */
@@ -585,7 +581,7 @@
   /* 10B90 */     B,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 10BA0 */     O,     O,     O,     O,     O,     O,     O,     O,     O,     B,     B,     B,     B,     B,     B,     O,
 
-#define use_offset_0x10d00u 4192
+#define use_offset_0x10d00u 4176
 
 
   /* Hanifi Rohingya */
@@ -595,7 +591,7 @@
   /* 10D20 */     B,     B,     B,     B, VMAbv, VMAbv, VMAbv, CMAbv,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 10D30 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x10e80u 4256
+#define use_offset_0x10e80u 4240
 
 
   /* Yezidi */
@@ -605,7 +601,7 @@
   /* 10EA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,  VAbv,  VAbv,     O,     O,     O,
   /* 10EB0 */     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x10f30u 4312
+#define use_offset_0x10f30u 4296
 
 
   /* Sogdian */
@@ -614,7 +610,7 @@
   /* 10F40 */     B,     B,     B,     B,     B,     B, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw,
   /* 10F50 */ VMBlw,     B,     B,     B,     B,     O,     O,     O,
 
-#define use_offset_0x10fb0u 4352
+#define use_offset_0x10fb0u 4336
 
 
   /* Chorasmian */
@@ -643,7 +639,7 @@
   /* 110A0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 110B0 */  VPst,  VPre,  VPst,  VBlw,  VBlw,  VAbv,  VAbv,  VPst,  VPst,     H, CMBlw,     O,     O,     O,     O,     O,
 
-#define use_offset_0x11100u 4624
+#define use_offset_0x11100u 4608
 
 
   /* Chakma */
@@ -681,7 +677,7 @@
   /* 11220 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPst,  VPst,  VPst,  VBlw,
   /* 11230 */  VAbv,  VAbv,  VAbv,  VAbv, VMAbv,     H, CMAbv, CMAbv,     O,     O,     O,     O,     O,     O, VMAbv,     O,
 
-#define use_offset_0x11280u 4944
+#define use_offset_0x11280u 4928
 
 
   /* Multani */
@@ -709,7 +705,7 @@
   /* 11360 */     B,     B,  VPst,  VPst,     O,     O, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
   /* 11370 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,     O,     O,
 
-#define use_offset_0x11400u 5192
+#define use_offset_0x11400u 5176
 
 
   /* Newa */
@@ -732,7 +728,7 @@
   /* 114C0 */ VMAbv, VMAbv,     H, CMBlw,     B,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 114D0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x11580u 5416
+#define use_offset_0x11580u 5400
 
 
   /* Siddham */
@@ -772,10 +768,10 @@
 
   /* 11700 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11710 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,  MBlw,  MPre,  MAbv,
-  /* 11720 */  VPst,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VAbv,  VBlw,  VAbv,  VAbv, VMAbv,     O,     O,     O,     O,
+  /* 11720 */  VPst,  VPst,  VAbv,  VAbv,  VBlw,  VBlw,  VPre,  VAbv,  VBlw,  VAbv,  VAbv,  VAbv,     O,     O,     O,     O,
   /* 11730 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,
 
-#define use_offset_0x11800u 5864
+#define use_offset_0x11800u 5848
 
 
   /* Dogra */
@@ -785,7 +781,7 @@
   /* 11820 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,  VPst,  VPre,  VPst,  VBlw,
   /* 11830 */  VBlw,  VBlw,  VBlw,  VAbv,  VAbv,  VAbv,  VAbv, VMAbv, VMPst,     H, CMBlw,     O,     O,     O,     O,     O,
 
-#define use_offset_0x11900u 5928
+#define use_offset_0x11900u 5912
 
 
   /* Dives Akuru */
@@ -797,7 +793,7 @@
   /* 11940 */  MPst,     R,  MPst, CMBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 11950 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x119a0u 6024
+#define use_offset_0x119a0u 6008
 
 
   /* Nandinagari */
@@ -825,7 +821,7 @@
   /* 11A80 */     B,     B,     B,     B,     R,     R,     R,     R,     R,     R,  FBlw,  FBlw,  FBlw,  FBlw,  FBlw,  FBlw,
   /* 11A90 */  FBlw,  FBlw,  FBlw,  FBlw,  FBlw,  FBlw, VMAbv, VMPst, CMAbv,     H,     O,     O,     O,     B,     O,     O,
 
-#define use_offset_0x11c00u 6280
+#define use_offset_0x11c00u 6264
 
 
   /* Bhaiksuki */
@@ -846,7 +842,7 @@
   /* 11CA0 */   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,     O,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,   SUB,
   /* 11CB0 */  VBlw,  VPre,  VBlw,  VAbv,  VPst, VMAbv, VMAbv,     O,
 
-#define use_offset_0x11d00u 6464
+#define use_offset_0x11d00u 6448
 
 
   /* Masaram Gondi */
@@ -866,7 +862,7 @@
   /* 11D90 */  VAbv,  VAbv,     O,  VPst,  VPst, VMAbv, VMPst,     H,     O,     O,     O,     O,     O,     O,     O,     O,
   /* 11DA0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x11ee0u 6640
+#define use_offset_0x11ee0u 6624
 
 
   /* Makasar */
@@ -874,7 +870,7 @@
   /* 11EE0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 11EF0 */     B,     B,    GB,  VAbv,  VBlw,  VPre,  VPst,     O,
 
-#define use_offset_0x13000u 6664
+#define use_offset_0x13000u 6648
 
 
   /* Egyptian Hieroglyphs */
@@ -951,7 +947,7 @@
 
   /* 13430 */     J,     J,     J,     J,     J,     J,     J,    SB,    SE,     O,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x16b00u 7752
+#define use_offset_0x16b00u 7736
 
 
   /* Pahawh Hmong */
@@ -961,7 +957,7 @@
   /* 16B20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 16B30 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     O,
 
-#define use_offset_0x16f00u 7808
+#define use_offset_0x16f00u 7792
 
 
   /* Miao */
@@ -971,20 +967,20 @@
   /* 16F20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 16F30 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 16F40 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O, CMBlw,
-  /* 16F50 */   IND,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,
+  /* 16F50 */     O,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,
   /* 16F60 */  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,
   /* 16F70 */  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,
   /* 16F80 */  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,  VBlw,     O,     O,     O,     O,     O,     O,     O, VMBlw,
   /* 16F90 */ VMBlw, VMBlw, VMBlw,     O,     O,     O,     O,     O,
 
-#define use_offset_0x16fe0u 7960
+#define use_offset_0x16fe0u 7944
 
 
   /* Ideographic Symbols and Punctuation */
 
   /* 16FE0 */     O,     O,     O,     O,     B,     O,     O,     O,
 
-#define use_offset_0x18b00u 7968
+#define use_offset_0x18b00u 7952
 
 
   /* Khitan Small Script */
@@ -1020,7 +1016,7 @@
   /* 18CC0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* 18CD0 */     B,     B,     B,     B,     B,     B,     O,     O,
 
-#define use_offset_0x1bc00u 8440
+#define use_offset_0x1bc00u 8424
 
 
   /* Duployan */
@@ -1036,7 +1032,7 @@
   /* 1BC80 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,     O,
   /* 1BC90 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O, CMBlw, CMBlw,     O,
 
-#define use_offset_0x1e100u 8600
+#define use_offset_0x1e100u 8584
 
 
   /* Nyiakeng Puachue Hmong */
@@ -1047,7 +1043,7 @@
   /* 1E130 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,     B,     B,     B,     B,     B,     B,     B,     O,     O,
   /* 1E140 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     B,     B,
 
-#define use_offset_0x1e2c0u 8680
+#define use_offset_0x1e2c0u 8664
 
 
   /* Wancho */
@@ -1057,7 +1053,7 @@
   /* 1E2E0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B, VMAbv, VMAbv, VMAbv, VMAbv,
   /* 1E2F0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-#define use_offset_0x1e900u 8744
+#define use_offset_0x1e900u 8728
 
 
   /* Adlam */
@@ -1069,9 +1065,9 @@
   /* 1E940 */     B,     B,     B,     B, CMAbv, CMAbv, CMAbv, CMAbv, CMAbv, CMAbv, CMAbv,     B,     O,     O,     O,     O,
   /* 1E950 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
-}; /* Table items: 8840; occupancy: 79% */
+}; /* Table items: 8824; occupancy: 79% */
 
-USE_TABLE_ELEMENT_TYPE
+static inline uint8_t
 hb_use_get_category (hb_codepoint_t u)
 {
   switch (u >> 12)
@@ -1097,7 +1093,7 @@
 
     case 0x2u:
       if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2017u)) return use_table[u - 0x2008u + use_offset_0x2008u];
-      if (hb_in_range<hb_codepoint_t> (u, 0x2060u, 0x2087u)) return use_table[u - 0x2060u + use_offset_0x2060u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x2070u, 0x2087u)) return use_table[u - 0x2070u + use_offset_0x2070u];
       if (hb_in_range<hb_codepoint_t> (u, 0x20F0u, 0x20F7u)) return use_table[u - 0x20F0u + use_offset_0x20f0u];
       if (hb_in_range<hb_codepoint_t> (u, 0x25C8u, 0x25CFu)) return use_table[u - 0x25C8u + use_offset_0x25c8u];
       if (hb_in_range<hb_codepoint_t> (u, 0x2D30u, 0x2D7Fu)) return use_table[u - 0x2D30u + use_offset_0x2d30u];
@@ -1159,7 +1155,7 @@
     default:
       break;
   }
-  return USE_O;
+  return USE(O);
 }
 
 #undef B
@@ -1169,19 +1165,15 @@
 #undef H
 #undef HN
 #undef HVM
-#undef IND
 #undef J
 #undef N
 #undef O
 #undef R
-#undef Rsv
 #undef S
 #undef SB
 #undef SE
 #undef SUB
 #undef Sk
-#undef WJ
-#undef ZWJ
 #undef ZWNJ
 #undef CMAbv
 #undef CMBlw
@@ -1207,5 +1199,5 @@
 #undef VMPre
 
 
-#endif
+#endif /* HB_OT_SHAPE_COMPLEX_USE_TABLE_HH */
 /* == End of generated table == */
diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc
index d789b9d..0d0b7e7 100644
--- a/src/hb-ot-shape-complex-use.cc
+++ b/src/hb-ot-shape-complex-use.cc
@@ -30,14 +30,12 @@
 
 #ifndef HB_NO_OT_SHAPE
 
-#include "hb-ot-shape-complex-use.hh"
+#include "hb-ot-shape-complex-use-machine.hh"
+#include "hb-ot-shape-complex-use-table.hh"
 #include "hb-ot-shape-complex-arabic.hh"
 #include "hb-ot-shape-complex-arabic-joining-list.hh"
 #include "hb-ot-shape-complex-vowel-constraints.hh"
 
-/* buffer var allocations */
-#define use_category() complex_var_u8_1()
-
 
 /*
  * Universal Shaping Engine.
@@ -69,11 +67,11 @@
 };
 /* Same order as use_topographical_features. */
 enum joining_form_t {
-  USE_ISOL,
-  USE_INIT,
-  USE_MEDI,
-  USE_FINA,
-  _USE_NONE
+  JOINING_FORM_ISOL,
+  JOINING_FORM_INIT,
+  JOINING_FORM_MEDI,
+  JOINING_FORM_FINA,
+  _JOINING_FORM_NONE
 };
 static const hb_tag_t
 use_other_features[] =
@@ -186,22 +184,6 @@
   free (data);
 }
 
-enum use_syllable_type_t {
-  use_independent_cluster,
-  use_virama_terminated_cluster,
-  use_sakot_terminated_cluster,
-  use_standard_cluster,
-  use_number_joiner_terminated_cluster,
-  use_numeral_cluster,
-  use_symbol_cluster,
-  use_hieroglyph_cluster,
-  use_broken_cluster,
-  use_non_cluster,
-};
-
-#include "hb-ot-shape-complex-use-machine.hh"
-
-
 static void
 setup_masks_use (const hb_ot_shape_plan_t *plan,
 		 hb_buffer_t              *buffer,
@@ -239,7 +221,7 @@
 
   foreach_syllable (buffer, start, end)
   {
-    unsigned int limit = info[start].use_category() == USE_R ? 1 : hb_min (3u, end - start);
+    unsigned int limit = info[start].use_category() == USE(R) ? 1 : hb_min (3u, end - start);
     for (unsigned int i = start; i < start + limit; i++)
       info[i].mask |= mask;
   }
@@ -253,7 +235,7 @@
   if (use_plan->arabic_plan)
     return;
 
-  static_assert ((USE_INIT < 4 && USE_ISOL < 4 && USE_MEDI < 4 && USE_FINA < 4), "");
+  static_assert ((JOINING_FORM_INIT < 4 && JOINING_FORM_ISOL < 4 && JOINING_FORM_MEDI < 4 && JOINING_FORM_FINA < 4), "");
   hb_mask_t masks[4], all_masks = 0;
   for (unsigned int i = 0; i < 4; i++)
   {
@@ -267,7 +249,7 @@
   hb_mask_t other_masks = ~all_masks;
 
   unsigned int last_start = 0;
-  joining_form_t last_form = _USE_NONE;
+  joining_form_t last_form = _JOINING_FORM_NONE;
   hb_glyph_info_t *info = buffer->info;
   foreach_syllable (buffer, start, end)
   {
@@ -279,7 +261,7 @@
       case use_hieroglyph_cluster:
       case use_non_cluster:
 	/* These don't join.  Nothing to do. */
-	last_form = _USE_NONE;
+	last_form = _JOINING_FORM_NONE;
 	break;
 
       case use_virama_terminated_cluster:
@@ -289,18 +271,18 @@
       case use_numeral_cluster:
       case use_broken_cluster:
 
-	bool join = last_form == USE_FINA || last_form == USE_ISOL;
+	bool join = last_form == JOINING_FORM_FINA || last_form == JOINING_FORM_ISOL;
 
 	if (join)
 	{
 	  /* Fixup previous syllable's form. */
-	  last_form = last_form == USE_FINA ? USE_MEDI : USE_INIT;
+	  last_form = last_form == JOINING_FORM_FINA ? JOINING_FORM_MEDI : JOINING_FORM_INIT;
 	  for (unsigned int i = last_start; i < start; i++)
 	    info[i].mask = (info[i].mask & other_masks) | masks[last_form];
 	}
 
 	/* Form for this syllable. */
-	last_form = join ? USE_FINA : USE_ISOL;
+	last_form = join ? JOINING_FORM_FINA : JOINING_FORM_ISOL;
 	for (unsigned int i = start; i < end; i++)
 	  info[i].mask = (info[i].mask & other_masks) | masks[last_form];
 
@@ -336,11 +318,11 @@
 
   foreach_syllable (buffer, start, end)
   {
-    /* Mark a substituted repha as USE_R. */
+    /* Mark a substituted repha as USE(R). */
     for (unsigned int i = start; i < end && (info[i].mask & mask); i++)
       if (_hb_glyph_info_substituted (&info[i]))
       {
-	info[i].use_category() = USE_R;
+	info[i].use_category() = USE(R);
 	break;
       }
   }
@@ -359,7 +341,7 @@
     for (unsigned int i = start; i < end; i++)
       if (_hb_glyph_info_substituted (&info[i]))
       {
-	info[i].use_category() = USE_VPre;
+	info[i].use_category() = USE(VPre);
 	break;
       }
   }
@@ -368,7 +350,7 @@
 static inline bool
 is_halant_use (const hb_glyph_info_t &info)
 {
-  return (info.use_category() == USE_H || info.use_category() == USE_HVM) &&
+  return (info.use_category() == USE(H) || info.use_category() == USE(HVM)) &&
 	 !_hb_glyph_info_ligated (&info);
 }
 
@@ -387,25 +369,24 @@
 
   hb_glyph_info_t *info = buffer->info;
 
-#define POST_BASE_FLAGS64 (FLAG64 (USE_FM) | \
-			   FLAG64 (USE_FAbv) | \
-			   FLAG64 (USE_FBlw) | \
-			   FLAG64 (USE_FPst) | \
-			   FLAG64 (USE_MAbv) | \
-			   FLAG64 (USE_MBlw) | \
-			   FLAG64 (USE_MPst) | \
-			   FLAG64 (USE_MPre) | \
-			   FLAG64 (USE_VAbv) | \
-			   FLAG64 (USE_VBlw) | \
-			   FLAG64 (USE_VPst) | \
-			   FLAG64 (USE_VPre) | \
-			   FLAG64 (USE_VMAbv) | \
-			   FLAG64 (USE_VMBlw) | \
-			   FLAG64 (USE_VMPst) | \
-			   FLAG64 (USE_VMPre))
+#define POST_BASE_FLAGS64 (FLAG64 (USE(FAbv)) | \
+			   FLAG64 (USE(FBlw)) | \
+			   FLAG64 (USE(FPst)) | \
+			   FLAG64 (USE(MAbv)) | \
+			   FLAG64 (USE(MBlw)) | \
+			   FLAG64 (USE(MPst)) | \
+			   FLAG64 (USE(MPre)) | \
+			   FLAG64 (USE(VAbv)) | \
+			   FLAG64 (USE(VBlw)) | \
+			   FLAG64 (USE(VPst)) | \
+			   FLAG64 (USE(VPre)) | \
+			   FLAG64 (USE(VMAbv)) | \
+			   FLAG64 (USE(VMBlw)) | \
+			   FLAG64 (USE(VMPst)) | \
+			   FLAG64 (USE(VMPre)))
 
   /* Move things forward. */
-  if (info[start].use_category() == USE_R && end - start > 1)
+  if (info[start].use_category() == USE(R) && end - start > 1)
   {
     /* Got a repha.  Reorder it towards the end, but before the first post-base
      * glyph. */
@@ -442,7 +423,7 @@
        * shift things in between forward. */
       j = i + 1;
     }
-    else if (((flag) & (FLAG (USE_VPre) | FLAG (USE_VMPre))) &&
+    else if (((flag) & (FLAG (USE(VPre)) | FLAG (USE(VMPre)))) &&
 	     /* Only move the first component of a MultipleSubst. */
 	     0 == _hb_glyph_info_get_lig_comp (&info[i]) &&
 	     j < i)
@@ -455,73 +436,23 @@
   }
 }
 
-static inline void
-insert_dotted_circles_use (const hb_ot_shape_plan_t *plan HB_UNUSED,
-			   hb_font_t *font,
-			   hb_buffer_t *buffer)
-{
-  if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
-    return;
-
-  /* Note: This loop is extra overhead, but should not be measurable.
-   * TODO Use a buffer scratch flag to remove the loop. */
-  bool has_broken_syllables = false;
-  unsigned int count = buffer->len;
-  hb_glyph_info_t *info = buffer->info;
-  for (unsigned int i = 0; i < count; i++)
-    if ((info[i].syllable() & 0x0F) == use_broken_cluster)
-    {
-      has_broken_syllables = true;
-      break;
-    }
-  if (likely (!has_broken_syllables))
-    return;
-
-  hb_glyph_info_t dottedcircle = {0};
-  if (!font->get_nominal_glyph (0x25CCu, &dottedcircle.codepoint))
-    return;
-  dottedcircle.use_category() = hb_use_get_category (0x25CC);
-
-  buffer->clear_output ();
-
-  buffer->idx = 0;
-  unsigned int last_syllable = 0;
-  while (buffer->idx < buffer->len && buffer->successful)
-  {
-    unsigned int syllable = buffer->cur().syllable();
-    use_syllable_type_t syllable_type = (use_syllable_type_t) (syllable & 0x0F);
-    if (unlikely (last_syllable != syllable && syllable_type == use_broken_cluster))
-    {
-      last_syllable = syllable;
-
-      hb_glyph_info_t ginfo = dottedcircle;
-      ginfo.cluster = buffer->cur().cluster;
-      ginfo.mask = buffer->cur().mask;
-      ginfo.syllable() = buffer->cur().syllable();
-
-      /* Insert dottedcircle after possible Repha. */
-      while (buffer->idx < buffer->len && buffer->successful &&
-	     last_syllable == buffer->cur().syllable() &&
-	     buffer->cur().use_category() == USE_R)
-	buffer->next_glyph ();
-
-      buffer->output_info (ginfo);
-    }
-    else
-      buffer->next_glyph ();
-  }
-  buffer->swap_buffers ();
-}
-
 static void
 reorder_use (const hb_ot_shape_plan_t *plan,
 	     hb_font_t *font,
 	     hb_buffer_t *buffer)
 {
-  insert_dotted_circles_use (plan, font, buffer);
+  if (buffer->message (font, "start reordering USE"))
+  {
+    hb_syllabic_insert_dotted_circles (font, buffer,
+				       use_broken_cluster,
+				       USE(B),
+				       USE(R));
 
-  foreach_syllable (buffer, start, end)
-    reorder_syllable_use (buffer, start, end);
+    foreach_syllable (buffer, start, end)
+      reorder_syllable_use (buffer, start, end);
+
+    (void) buffer->message (font, "end reordering USE");
+  }
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, use_category);
 }
diff --git a/src/hb-ot-shape-complex-use.hh b/src/hb-ot-shape-complex-use.hh
deleted file mode 100644
index 1ed3b5a..0000000
--- a/src/hb-ot-shape-complex-use.hh
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright © 2015  Mozilla Foundation.
- * Copyright © 2015  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Mozilla Author(s): Jonathan Kew
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_USE_HH
-#define HB_OT_SHAPE_COMPLEX_USE_HH
-
-#include "hb.hh"
-
-
-#include "hb-ot-shape-complex.hh"
-
-
-#define USE_TABLE_ELEMENT_TYPE uint8_t
-
-/* Cateories used in the Universal Shaping Engine spec:
- * https://docs.microsoft.com/en-us/typography/script-development/use
- */
-/* Note: This enum is duplicated in the -machine.rl source file.
- * Not sure how to avoid duplication. */
-enum use_category_t {
-  USE_O		= 0,	/* OTHER */
-
-  USE_B		= 1,	/* BASE */
-  USE_IND	= 3,	/* BASE_IND */
-  USE_N		= 4,	/* BASE_NUM */
-  USE_GB	= 5,	/* BASE_OTHER */
-//  USE_F		= 7,	/* CONS_FINAL */
-  USE_FM	= 8,	/* CONS_FINAL_MOD */
-//  USE_M		= 9,	/* CONS_MED */
-//  USE_CM	= 10,	/* CONS_MOD */
-  USE_SUB	= 11,	/* CONS_SUB */
-  USE_H		= 12,	/* HALANT */
-
-  USE_HN	= 13,	/* HALANT_NUM */
-  USE_ZWNJ	= 14,	/* Zero width non-joiner */
-  USE_ZWJ	= 15,	/* Zero width joiner */
-  USE_WJ	= 16,	/* Word joiner */
-  USE_Rsv	= 17,	/* Reserved characters */
-  USE_R		= 18,	/* REPHA */
-  USE_S		= 19,	/* SYM */
-//  USE_SM	= 20,	/* SYM_MOD */
-//  USE_V	= 36,	/* VOWEL */
-//  USE_VM	= 40,	/* VOWEL_MOD */
-  USE_CS	= 43,	/* CONS_WITH_STACKER */
-
-  /* https://github.com/harfbuzz/harfbuzz/issues/1102 */
-  USE_HVM	= 44,	/* HALANT_OR_VOWEL_MODIFIER */
-
-  USE_Sk	= 48,	/* SAKOT */
-  USE_G		= 49,	/* HIEROGLYPH */
-  USE_J		= 50,	/* HIEROGLYPH_JOINER */
-  USE_SB	= 51,	/* HIEROGLYPH_SEGMENT_BEGIN */
-  USE_SE	= 52,	/* HIEROGLYPH_SEGMENT_END */
-
-  USE_FAbv	= 24,	/* CONS_FINAL_ABOVE */
-  USE_FBlw	= 25,	/* CONS_FINAL_BELOW */
-  USE_FPst	= 26,	/* CONS_FINAL_POST */
-  USE_MAbv	= 27,	/* CONS_MED_ABOVE */
-  USE_MBlw	= 28,	/* CONS_MED_BELOW */
-  USE_MPst	= 29,	/* CONS_MED_POST */
-  USE_MPre	= 30,	/* CONS_MED_PRE */
-  USE_CMAbv	= 31,	/* CONS_MOD_ABOVE */
-  USE_CMBlw	= 32,	/* CONS_MOD_BELOW */
-  USE_VAbv	= 33,	/* VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST */
-  USE_VBlw	= 34,	/* VOWEL_BELOW / VOWEL_BELOW_POST */
-  USE_VPst	= 35,	/* VOWEL_POST	UIPC = Right */
-  USE_VPre	= 22,	/* VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST */
-  USE_VMAbv	= 37,	/* VOWEL_MOD_ABOVE */
-  USE_VMBlw	= 38,	/* VOWEL_MOD_BELOW */
-  USE_VMPst	= 39,	/* VOWEL_MOD_POST */
-  USE_VMPre	= 23,	/* VOWEL_MOD_PRE */
-  USE_SMAbv	= 41,	/* SYM_MOD_ABOVE */
-  USE_SMBlw	= 42,	/* SYM_MOD_BELOW */
-  USE_FMAbv	= 45,	/* CONS_FINAL_MOD	UIPC = Top */
-  USE_FMBlw	= 46,	/* CONS_FINAL_MOD	UIPC = Bottom */
-  USE_FMPst	= 47,	/* CONS_FINAL_MOD	UIPC = Not_Applicable */
-};
-
-HB_INTERNAL USE_TABLE_ELEMENT_TYPE
-hb_use_get_category (hb_codepoint_t u);
-
-#endif /* HB_OT_SHAPE_COMPLEX_USE_HH */
diff --git a/src/hb-ot-shape-complex.hh b/src/hb-ot-shape-complex.hh
index a1a7a6a..b592cad 100644
--- a/src/hb-ot-shape-complex.hh
+++ b/src/hb-ot-shape-complex.hh
@@ -35,8 +35,8 @@
 
 
 /* buffer var allocations, used by complex shapers */
-#define complex_var_u8_0()	var2.u8[2]
-#define complex_var_u8_1()	var2.u8[3]
+#define complex_var_u8_category()	var2.u8[2]
+#define complex_var_u8_auxiliary()	var2.u8[3]
 
 
 #define HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS 32
@@ -186,27 +186,8 @@
     case HB_SCRIPT_ARABIC:
 
     /* Unicode-3.0 additions */
-    case HB_SCRIPT_MONGOLIAN:
     case HB_SCRIPT_SYRIAC:
 
-    /* Unicode-5.0 additions */
-    case HB_SCRIPT_NKO:
-    case HB_SCRIPT_PHAGS_PA:
-
-    /* Unicode-6.0 additions */
-    case HB_SCRIPT_MANDAIC:
-
-    /* Unicode-7.0 additions */
-    case HB_SCRIPT_MANICHAEAN:
-    case HB_SCRIPT_PSALTER_PAHLAVI:
-
-    /* Unicode-9.0 additions */
-    case HB_SCRIPT_ADLAM:
-
-    /* Unicode-11.0 additions */
-    case HB_SCRIPT_HANIFI_ROHINGYA:
-    case HB_SCRIPT_SOGDIAN:
-
       /* For Arabic script, use the Arabic shaper even if no OT script tag was found.
        * This is because we do fallback shaping for Arabic script (and not others).
        * But note that Arabic shaping is applicable only to horizontal layout; for
@@ -294,7 +275,7 @@
     case HB_SCRIPT_TIBETAN:
 
     /* Unicode-3.0 additions */
-    //case HB_SCRIPT_MONGOLIAN:
+    case HB_SCRIPT_MONGOLIAN:
     //case HB_SCRIPT_SINHALA:
 
     /* Unicode-3.2 additions */
@@ -315,8 +296,8 @@
 
     /* Unicode-5.0 additions */
     case HB_SCRIPT_BALINESE:
-    //case HB_SCRIPT_NKO:
-    //case HB_SCRIPT_PHAGS_PA:
+    case HB_SCRIPT_NKO:
+    case HB_SCRIPT_PHAGS_PA:
 
     /* Unicode-5.1 additions */
     case HB_SCRIPT_CHAM:
@@ -337,7 +318,7 @@
     /* Unicode-6.0 additions */
     case HB_SCRIPT_BATAK:
     case HB_SCRIPT_BRAHMI:
-    //case HB_SCRIPT_MANDAIC:
+    case HB_SCRIPT_MANDAIC:
 
     /* Unicode-6.1 additions */
     case HB_SCRIPT_CHAKMA:
@@ -351,10 +332,10 @@
     case HB_SCRIPT_KHOJKI:
     case HB_SCRIPT_KHUDAWADI:
     case HB_SCRIPT_MAHAJANI:
-    //case HB_SCRIPT_MANICHAEAN:
+    case HB_SCRIPT_MANICHAEAN:
     case HB_SCRIPT_MODI:
     case HB_SCRIPT_PAHAWH_HMONG:
-    //case HB_SCRIPT_PSALTER_PAHLAVI:
+    case HB_SCRIPT_PSALTER_PAHLAVI:
     case HB_SCRIPT_SIDDHAM:
     case HB_SCRIPT_TIRHUTA:
 
@@ -363,7 +344,7 @@
     case HB_SCRIPT_MULTANI:
 
     /* Unicode-9.0 additions */
-    //case HB_SCRIPT_ADLAM:
+    case HB_SCRIPT_ADLAM:
     case HB_SCRIPT_BHAIKSUKI:
     case HB_SCRIPT_MARCHEN:
     case HB_SCRIPT_NEWA:
@@ -376,11 +357,11 @@
     /* Unicode-11.0 additions */
     case HB_SCRIPT_DOGRA:
     case HB_SCRIPT_GUNJALA_GONDI:
-    //case HB_SCRIPT_HANIFI_ROHINGYA:
+    case HB_SCRIPT_HANIFI_ROHINGYA:
     case HB_SCRIPT_MAKASAR:
     case HB_SCRIPT_MEDEFAIDRIN:
     case HB_SCRIPT_OLD_SOGDIAN:
-    //case HB_SCRIPT_SOGDIAN:
+    case HB_SCRIPT_SOGDIAN:
 
     /* Unicode-12.0 additions */
     case HB_SCRIPT_ELYMAIC:
diff --git a/src/hb-ot-shape-fallback.cc b/src/hb-ot-shape-fallback.cc
index 42bf524..7d00a35 100644
--- a/src/hb-ot-shape-fallback.cc
+++ b/src/hb-ot-shape-fallback.cc
@@ -301,7 +301,7 @@
       /* Don't shift down "above" marks too much. */
       if ((y_gap > 0) != (pos.y_offset > 0))
       {
-	unsigned int correction = -pos.y_offset / 2;
+	int correction = -pos.y_offset / 2;
 	base_extents.y_bearing += correction;
 	base_extents.height -= correction;
 	pos.y_offset += correction;
diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index 9157c06..3eabae1 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -373,7 +373,7 @@
 
   /* Second round, reorder (inplace) */
 
-  if (!all_simple)
+  if (!all_simple && buffer->message(font, "start reorder"))
   {
     count = buffer->len;
     for (unsigned int i = 0; i < count; i++)
@@ -399,6 +399,7 @@
 
       i = end;
     }
+    (void) buffer->message(font, "end reorder");
   }
   if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CGJ)
   {
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 010a6f2..7d90558 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -896,8 +896,11 @@
     hb_aat_layout_remove_deleted_glyphs (c->buffer);
 #endif
 
-  if (c->plan->shaper->postprocess_glyphs)
+  if (c->plan->shaper->postprocess_glyphs &&
+    c->buffer->message(c->font, "start postprocess-glyphs")) {
     c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
+    (void) c->buffer->message(c->font, "end postprocess-glyphs");
+  }
 }
 
 
@@ -1120,8 +1123,11 @@
 
   hb_ensure_native_direction (c->buffer);
 
-  if (c->plan->shaper->preprocess_text)
+  if (c->plan->shaper->preprocess_text &&
+    c->buffer->message(c->font, "start preprocess-text")) {
     c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
+    (void) c->buffer->message(c->font, "end preprocess-text");
+  }
 
   hb_ot_substitute_pre (c);
   hb_ot_position (c);
diff --git a/src/hb-ot-shape.h b/src/hb-ot-shape.h
index 7b1bcc0..afdff72 100644
--- a/src/hb-ot-shape.h
+++ b/src/hb-ot-shape.h
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-ot.h> instead."
 #endif
 
diff --git a/src/hb-ot-tag-table.hh b/src/hb-ot-tag-table.hh
index 30d732d..f1c391c 100644
--- a/src/hb-ot-tag-table.hh
+++ b/src/hb-ot-tag-table.hh
@@ -6,8 +6,8 @@
  *
  * on files with these headers:
  *
- * <meta name="updated_at" content="2019-05-22 06:05 PM" />
- * File-Date: 2020-07-17
+ * <meta name="updated_at" content="2020-11-17 08:21 AM" />
+ * File-Date: 2020-09-29
  */
 
 #ifndef HB_OT_TAG_TABLE_HH
@@ -19,14 +19,18 @@
   {"aao",	HB_TAG('A','R','A',' ')},	/* Algerian Saharan Arabic -> Arabic */
   {"aat",	HB_TAG('S','Q','I',' ')},	/* Arvanitika Albanian -> Albanian */
   {"ab",	HB_TAG('A','B','K',' ')},	/* Abkhazian */
+  {"aba",	HB_TAG_NONE	       },	/* Abé != Abaza */
   {"abh",	HB_TAG('A','R','A',' ')},	/* Tajiki Arabic -> Arabic */
   {"abq",	HB_TAG('A','B','A',' ')},	/* Abaza */
+  {"abs",	HB_TAG('C','P','P',' ')},	/* Ambonese Malay -> Creoles */
   {"abv",	HB_TAG('A','R','A',' ')},	/* Baharna Arabic -> Arabic */
   {"acf",	HB_TAG('F','A','N',' ')},	/* Saint Lucian Creole French -> French Antillean */
+  {"acf",	HB_TAG('C','P','P',' ')},	/* Saint Lucian Creole French -> Creoles */
 /*{"ach",	HB_TAG('A','C','H',' ')},*/	/* Acoli -> Acholi */
   {"acm",	HB_TAG('A','R','A',' ')},	/* Mesopotamian Arabic -> Arabic */
   {"acq",	HB_TAG('A','R','A',' ')},	/* Ta'izzi-Adeni Arabic -> Arabic */
-/*{"acr",	HB_TAG('A','C','R',' ')},*/	/* Achi */
+  {"acr",	HB_TAG('A','C','R',' ')},	/* Achi */
+  {"acr",	HB_TAG('M','Y','N',' ')},	/* Achi -> Mayan */
   {"acw",	HB_TAG('A','R','A',' ')},	/* Hijazi Arabic -> Arabic */
   {"acx",	HB_TAG('A','R','A',' ')},	/* Omani Arabic -> Arabic */
   {"acy",	HB_TAG('A','R','A',' ')},	/* Cypriot Arabic -> Arabic */
@@ -38,15 +42,21 @@
   {"aec",	HB_TAG('A','R','A',' ')},	/* Saidi Arabic -> Arabic */
   {"af",	HB_TAG('A','F','K',' ')},	/* Afrikaans */
   {"afb",	HB_TAG('A','R','A',' ')},	/* Gulf Arabic -> Arabic */
+  {"afk",	HB_TAG_NONE	       },	/* Nanubae != Afrikaans */
+  {"afs",	HB_TAG('C','P','P',' ')},	/* Afro-Seminole Creole -> Creoles */
+  {"agu",	HB_TAG('M','Y','N',' ')},	/* Aguacateco -> Mayan */
+  {"agw",	HB_TAG_NONE	       },	/* Kahua != Agaw */
   {"ahg",	HB_TAG('A','G','W',' ')},	/* Qimant -> Agaw */
   {"aht",	HB_TAG('A','T','H',' ')},	/* Ahtena -> Athapaskan */
+  {"aig",	HB_TAG('C','P','P',' ')},	/* Antigua and Barbuda Creole English -> Creoles */
   {"aii",	HB_TAG('S','W','A',' ')},	/* Assyrian Neo-Aramaic -> Swadaya Aramaic */
   {"aii",	HB_TAG('S','Y','R',' ')},	/* Assyrian Neo-Aramaic -> Syriac */
 /*{"aio",	HB_TAG('A','I','O',' ')},*/	/* Aiton */
   {"aiw",	HB_TAG('A','R','I',' ')},	/* Aari */
   {"ajp",	HB_TAG('A','R','A',' ')},	/* South Levantine Arabic -> Arabic */
   {"ak",	HB_TAG('A','K','A',' ')},	/* Akan [macrolanguage] */
-  {"ak",	HB_TAG('T','W','I',' ')},	/* Akan [macrolanguage] -> Twi */
+  {"akb",	HB_TAG('A','K','B',' ')},	/* Batak Angkola */
+  {"akb",	HB_TAG('B','T','K',' ')},	/* Batak Angkola -> Batak */
   {"aln",	HB_TAG('S','Q','I',' ')},	/* Gheg Albanian -> Albanian */
   {"als",	HB_TAG('S','Q','I',' ')},	/* Tosk Albanian -> Albanian */
 /*{"alt",	HB_TAG('A','L','T',' ')},*/	/* Southern Altai -> Altai */
@@ -55,6 +65,8 @@
   {"amw",	HB_TAG('S','Y','R',' ')},	/* Western Neo-Aramaic -> Syriac */
   {"an",	HB_TAG('A','R','G',' ')},	/* Aragonese */
 /*{"ang",	HB_TAG('A','N','G',' ')},*/	/* Old English (ca. 450-1100) -> Anglo-Saxon */
+  {"aoa",	HB_TAG('C','P','P',' ')},	/* Angolar -> Creoles */
+  {"apa",	HB_TAG('A','T','H',' ')},	/* Apache [family] -> Athapaskan */
   {"apc",	HB_TAG('A','R','A',' ')},	/* North Levantine Arabic -> Arabic */
   {"apd",	HB_TAG('A','R','A',' ')},	/* Sudanese Arabic -> Arabic */
   {"apj",	HB_TAG('A','T','H',' ')},	/* Jicarilla Apache -> Athapaskan */
@@ -64,16 +76,20 @@
   {"apw",	HB_TAG('A','T','H',' ')},	/* Western Apache -> Athapaskan */
   {"ar",	HB_TAG('A','R','A',' ')},	/* Arabic [macrolanguage] */
   {"arb",	HB_TAG('A','R','A',' ')},	/* Standard Arabic -> Arabic */
+  {"ari",	HB_TAG_NONE	       },	/* Arikara != Aari */
+  {"ark",	HB_TAG_NONE	       },	/* Arikapú != Rakhine */
   {"arn",	HB_TAG('M','A','P',' ')},	/* Mapudungun */
   {"arq",	HB_TAG('A','R','A',' ')},	/* Algerian Arabic -> Arabic */
   {"ars",	HB_TAG('A','R','A',' ')},	/* Najdi Arabic -> Arabic */
   {"ary",	HB_TAG('M','O','R',' ')},	/* Moroccan Arabic -> Moroccan */
+  {"ary",	HB_TAG('A','R','A',' ')},	/* Moroccan Arabic -> Arabic */
   {"arz",	HB_TAG('A','R','A',' ')},	/* Egyptian Arabic -> Arabic */
   {"as",	HB_TAG('A','S','M',' ')},	/* Assamese */
 /*{"ast",	HB_TAG('A','S','T',' ')},*/	/* Asturian */
 /*{"ath",	HB_TAG('A','T','H',' ')},*/	/* Athapascan [family] -> Athapaskan */
   {"atj",	HB_TAG('R','C','R',' ')},	/* Atikamekw -> R-Cree */
   {"atv",	HB_TAG('A','L','T',' ')},	/* Northern Altai -> Altai */
+  {"auj",	HB_TAG('B','B','R',' ')},	/* Awjilah -> Berber */
   {"auz",	HB_TAG('A','R','A',' ')},	/* Uzbeki Arabic -> Arabic */
   {"av",	HB_TAG('A','V','R',' ')},	/* Avaric -> Avar */
   {"avl",	HB_TAG('A','R','A',' ')},	/* Eastern Egyptian Bedawi Arabic -> Arabic */
@@ -86,17 +102,29 @@
   {"ayp",	HB_TAG('A','R','A',' ')},	/* North Mesopotamian Arabic -> Arabic */
   {"ayr",	HB_TAG('A','Y','M',' ')},	/* Central Aymara -> Aymara */
   {"az",	HB_TAG('A','Z','E',' ')},	/* Azerbaijani [macrolanguage] */
-/*{"azb",	HB_TAG('A','Z','B',' ')},*/	/* South Azerbaijani -> Torki */
+  {"azb",	HB_TAG('A','Z','B',' ')},	/* South Azerbaijani -> Torki */
+  {"azb",	HB_TAG('A','Z','E',' ')},	/* South Azerbaijani -> Azerbaijani */
+  {"azd",	HB_TAG('N','A','H',' ')},	/* Eastern Durango Nahuatl -> Nahuatl */
   {"azj",	HB_TAG('A','Z','E',' ')},	/* North Azerbaijani -> Azerbaijani */
+  {"azn",	HB_TAG('N','A','H',' ')},	/* Western Durango Nahuatl -> Nahuatl */
+  {"azz",	HB_TAG('N','A','H',' ')},	/* Highland Puebla Nahuatl -> Nahuatl */
   {"ba",	HB_TAG('B','S','H',' ')},	/* Bashkir */
   {"bad",	HB_TAG('B','A','D','0')},	/* Banda [family] */
+  {"bag",	HB_TAG_NONE	       },	/* Tuki != Baghelkhandi */
+  {"bah",	HB_TAG('C','P','P',' ')},	/* Bahamas Creole English -> Creoles */
   {"bai",	HB_TAG('B','M','L',' ')},	/* Bamileke [family] */
   {"bal",	HB_TAG('B','L','I',' ')},	/* Baluchi [macrolanguage] */
 /*{"ban",	HB_TAG('B','A','N',' ')},*/	/* Balinese */
 /*{"bar",	HB_TAG('B','A','R',' ')},*/	/* Bavarian */
-/*{"bbc",	HB_TAG('B','B','C',' ')},*/	/* Batak Toba */
+  {"bau",	HB_TAG_NONE	       },	/* Bada (Nigeria) != Baulé */
+  {"bbc",	HB_TAG('B','B','C',' ')},	/* Batak Toba */
+  {"bbc",	HB_TAG('B','T','K',' ')},	/* Batak Toba -> Batak */
+  {"bbj",	HB_TAG('B','M','L',' ')},	/* Ghomálá' -> Bamileke */
+  {"bbp",	HB_TAG('B','A','D','0')},	/* West Central Banda -> Banda */
+  {"bbr",	HB_TAG_NONE	       },	/* Girawa != Berber */
   {"bbz",	HB_TAG('A','R','A',' ')},	/* Babalia Creole Arabic (retired code) -> Arabic */
   {"bcc",	HB_TAG('B','L','I',' ')},	/* Southern Balochi -> Baluchi */
+  {"bch",	HB_TAG_NONE	       },	/* Bariai != Bench */
   {"bci",	HB_TAG('B','A','U',' ')},	/* Baoulé -> Baulé */
   {"bcl",	HB_TAG('B','I','K',' ')},	/* Central Bikol -> Bikol */
   {"bcq",	HB_TAG('B','C','H',' ')},	/* Bench */
@@ -107,6 +135,8 @@
   {"beb",	HB_TAG('B','T','I',' ')},	/* Bebele -> Beti */
 /*{"bem",	HB_TAG('B','E','M',' ')},*/	/* Bemba (Zambia) */
   {"ber",	HB_TAG('B','B','R',' ')},	/* Berber [family] */
+  {"bew",	HB_TAG('C','P','P',' ')},	/* Betawi -> Creoles */
+  {"bfl",	HB_TAG('B','A','D','0')},	/* Banda-Ndélé -> Banda */
   {"bfq",	HB_TAG('B','A','D',' ')},	/* Badaga */
   {"bft",	HB_TAG('B','L','T',' ')},	/* Balti */
   {"bfu",	HB_TAG('L','A','H',' ')},	/* Gahri -> Lahuli */
@@ -115,7 +145,8 @@
 /*{"bgc",	HB_TAG('B','G','C',' ')},*/	/* Haryanvi */
   {"bgn",	HB_TAG('B','L','I',' ')},	/* Western Balochi -> Baluchi */
   {"bgp",	HB_TAG('B','L','I',' ')},	/* Eastern Balochi -> Baluchi */
-/*{"bgq",	HB_TAG('B','G','Q',' ')},*/	/* Bagri */
+  {"bgq",	HB_TAG('B','G','Q',' ')},	/* Bagri */
+  {"bgq",	HB_TAG('R','A','J',' ')},	/* Bagri -> Rajasthani */
   {"bgr",	HB_TAG('Q','I','N',' ')},	/* Bawm Chin -> Chin */
   {"bhb",	HB_TAG('B','H','I',' ')},	/* Bhili */
 /*{"bhi",	HB_TAG('B','H','I',' ')},*/	/* Bhilali -> Bhili */
@@ -123,58 +154,107 @@
 /*{"bho",	HB_TAG('B','H','O',' ')},*/	/* Bhojpuri */
   {"bhr",	HB_TAG('M','L','G',' ')},	/* Bara Malagasy -> Malagasy */
   {"bi",	HB_TAG('B','I','S',' ')},	/* Bislama */
+  {"bi",	HB_TAG('C','P','P',' ')},	/* Bislama -> Creoles */
 /*{"bik",	HB_TAG('B','I','K',' ')},*/	/* Bikol [macrolanguage] */
+  {"bil",	HB_TAG_NONE	       },	/* Bile != Bilen */
   {"bin",	HB_TAG('E','D','O',' ')},	/* Edo */
+  {"biu",	HB_TAG('Q','I','N',' ')},	/* Biete -> Chin */
 /*{"bjj",	HB_TAG('B','J','J',' ')},*/	/* Kanauji */
   {"bjn",	HB_TAG('M','L','Y',' ')},	/* Banjar -> Malay */
+  {"bjo",	HB_TAG('B','A','D','0')},	/* Mid-Southern Banda -> Banda */
   {"bjq",	HB_TAG('M','L','G',' ')},	/* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */
+  {"bjs",	HB_TAG('C','P','P',' ')},	/* Bajan -> Creoles */
   {"bjt",	HB_TAG('B','L','N',' ')},	/* Balanta-Ganja -> Balante */
+  {"bkf",	HB_TAG_NONE	       },	/* Beeke != Blackfoot */
+  {"bko",	HB_TAG('B','M','L',' ')},	/* Kwa' -> Bamileke */
   {"bla",	HB_TAG('B','K','F',' ')},	/* Siksika -> Blackfoot */
   {"ble",	HB_TAG('B','L','N',' ')},	/* Balanta-Kentohe -> Balante */
-/*{"blk",	HB_TAG('B','L','K',' ')},*/	/* Pa’o Karen */
+  {"bli",	HB_TAG_NONE	       },	/* Bolia != Baluchi */
+  {"blk",	HB_TAG('B','L','K',' ')},	/* Pa’o Karen */
+  {"blk",	HB_TAG('K','R','N',' ')},	/* Pa'o Karen -> Karen */
   {"bln",	HB_TAG('B','I','K',' ')},	/* Southern Catanduanes Bikol -> Bikol */
+  {"blt",	HB_TAG_NONE	       },	/* Tai Dam != Balti */
   {"bm",	HB_TAG('B','M','B',' ')},	/* Bambara (Bamanankan) */
+  {"bmb",	HB_TAG_NONE	       },	/* Bembe != Bambara (Bamanankan) */
+  {"bml",	HB_TAG_NONE	       },	/* Bomboli != Bamileke */
   {"bmm",	HB_TAG('M','L','G',' ')},	/* Northern Betsimisaraka Malagasy -> Malagasy */
   {"bn",	HB_TAG('B','E','N',' ')},	/* Bengali */
   {"bo",	HB_TAG('T','I','B',' ')},	/* Tibetan */
+  {"bpd",	HB_TAG('B','A','D','0')},	/* Banda-Banda -> Banda */
+  {"bpl",	HB_TAG('C','P','P',' ')},	/* Broome Pearling Lugger Pidgin -> Creoles */
+  {"bpq",	HB_TAG('C','P','P',' ')},	/* Banda Malay -> Creoles */
 /*{"bpy",	HB_TAG('B','P','Y',' ')},*/	/* Bishnupriya -> Bishnupriya Manipuri */
   {"bqi",	HB_TAG('L','R','C',' ')},	/* Bakhtiari -> Luri */
+  {"bqk",	HB_TAG('B','A','D','0')},	/* Banda-Mbrès -> Banda */
   {"br",	HB_TAG('B','R','E',' ')},	/* Breton */
   {"bra",	HB_TAG('B','R','I',' ')},	/* Braj -> Braj Bhasha */
+  {"brc",	HB_TAG('C','P','P',' ')},	/* Berbice Creole Dutch -> Creoles */
 /*{"brh",	HB_TAG('B','R','H',' ')},*/	/* Brahui */
+  {"bri",	HB_TAG_NONE	       },	/* Mokpwe != Braj Bhasha */
+  {"brm",	HB_TAG_NONE	       },	/* Barambu != Burmese */
 /*{"brx",	HB_TAG('B','R','X',' ')},*/	/* Bodo (India) */
   {"bs",	HB_TAG('B','O','S',' ')},	/* Bosnian */
+  {"bsh",	HB_TAG_NONE	       },	/* Kati != Bashkir */
 /*{"bsk",	HB_TAG('B','S','K',' ')},*/	/* Burushaski */
   {"btb",	HB_TAG('B','T','I',' ')},	/* Beti (Cameroon) (retired code) */
+  {"btd",	HB_TAG('B','T','D',' ')},	/* Batak Dairi (Pakpak) */
+  {"btd",	HB_TAG('B','T','K',' ')},	/* Batak Dairi -> Batak */
+  {"bti",	HB_TAG_NONE	       },	/* Burate != Beti */
   {"btj",	HB_TAG('M','L','Y',' ')},	/* Bacanese Malay -> Malay */
+/*{"btk",	HB_TAG('B','T','K',' ')},*/	/* Batak [family] */
+  {"btm",	HB_TAG('B','T','M',' ')},	/* Batak Mandailing */
+  {"btm",	HB_TAG('B','T','K',' ')},	/* Batak Mandailing -> Batak */
   {"bto",	HB_TAG('B','I','K',' ')},	/* Rinconada Bikol -> Bikol */
-/*{"bts",	HB_TAG('B','T','S',' ')},*/	/* Batak Simalungun */
+  {"bts",	HB_TAG('B','T','S',' ')},	/* Batak Simalungun */
+  {"bts",	HB_TAG('B','T','K',' ')},	/* Batak Simalungun -> Batak */
+  {"btx",	HB_TAG('B','T','X',' ')},	/* Batak Karo */
+  {"btx",	HB_TAG('B','T','K',' ')},	/* Batak Karo -> Batak */
+  {"btz",	HB_TAG('B','T','Z',' ')},	/* Batak Alas-Kluet */
+  {"btz",	HB_TAG('B','T','K',' ')},	/* Batak Alas-Kluet -> Batak */
 /*{"bug",	HB_TAG('B','U','G',' ')},*/	/* Buginese -> Bugis */
   {"bum",	HB_TAG('B','T','I',' ')},	/* Bulu (Cameroon) -> Beti */
   {"bve",	HB_TAG('M','L','Y',' ')},	/* Berau Malay -> Malay */
   {"bvu",	HB_TAG('M','L','Y',' ')},	/* Bukit Malay -> Malay */
+  {"bwe",	HB_TAG('K','R','N',' ')},	/* Bwe Karen -> Karen */
   {"bxk",	HB_TAG('L','U','H',' ')},	/* Bukusu -> Luyia */
+  {"bxo",	HB_TAG('C','P','P',' ')},	/* Barikanchi -> Creoles */
   {"bxp",	HB_TAG('B','T','I',' ')},	/* Bebil -> Beti */
   {"bxr",	HB_TAG('R','B','U',' ')},	/* Russia Buriat -> Russian Buriat */
   {"byn",	HB_TAG('B','I','L',' ')},	/* Bilin -> Bilen */
-/*{"byv",	HB_TAG('B','Y','V',' ')},*/	/* Medumba */
+  {"byv",	HB_TAG('B','Y','V',' ')},	/* Medumba */
+  {"byv",	HB_TAG('B','M','L',' ')},	/* Medumba -> Bamileke */
   {"bzc",	HB_TAG('M','L','G',' ')},	/* Southern Betsimisaraka Malagasy -> Malagasy */
+  {"bzj",	HB_TAG('C','P','P',' ')},	/* Belize Kriol English -> Creoles */
+  {"bzk",	HB_TAG('C','P','P',' ')},	/* Nicaragua Creole English -> Creoles */
   {"ca",	HB_TAG('C','A','T',' ')},	/* Catalan */
+  {"caa",	HB_TAG('M','Y','N',' ')},	/* Chortí -> Mayan */
+  {"cac",	HB_TAG('M','Y','N',' ')},	/* Chuj -> Mayan */
   {"caf",	HB_TAG('C','R','R',' ')},	/* Southern Carrier -> Carrier */
   {"caf",	HB_TAG('A','T','H',' ')},	/* Southern Carrier -> Athapaskan */
-/*{"cak",	HB_TAG('C','A','K',' ')},*/	/* Kaqchikel */
-/*{"cbk",	HB_TAG('C','B','K',' ')},*/	/* Chavacano -> Zamboanga Chavacano */
+  {"cak",	HB_TAG('C','A','K',' ')},	/* Kaqchikel */
+  {"cak",	HB_TAG('M','Y','N',' ')},	/* Kaqchikel -> Mayan */
+  {"cbk",	HB_TAG('C','B','K',' ')},	/* Chavacano -> Zamboanga Chavacano */
+  {"cbk",	HB_TAG('C','P','P',' ')},	/* Chavacano -> Creoles */
   {"cbl",	HB_TAG('Q','I','N',' ')},	/* Bualkhaw Chin -> Chin */
+  {"ccl",	HB_TAG('C','P','P',' ')},	/* Cutchi-Swahili -> Creoles */
+  {"ccm",	HB_TAG('C','P','P',' ')},	/* Malaccan Creole Malay -> Creoles */
   {"cco",	HB_TAG('C','C','H','N')},	/* Comaltepec Chinantec -> Chinantec */
   {"ccq",	HB_TAG('A','R','K',' ')},	/* Chaungtha (retired code) -> Rakhine */
-  {"cdo",	HB_TAG('Z','H','S',' ')},	/* Min Dong Chinese -> Chinese Simplified */
+  {"cdo",	HB_TAG('Z','H','S',' ')},	/* Min Dong Chinese -> Chinese, Simplified */
   {"ce",	HB_TAG('C','H','E',' ')},	/* Chechen */
 /*{"ceb",	HB_TAG('C','E','B',' ')},*/	/* Cebuano */
+  {"cek",	HB_TAG('Q','I','N',' ')},	/* Eastern Khumi Chin -> Chin */
+  {"cey",	HB_TAG('Q','I','N',' ')},	/* Ekai Chin -> Chin */
   {"cfm",	HB_TAG('H','A','L',' ')},	/* Halam (Falam Chin) */
+  {"cfm",	HB_TAG('Q','I','N',' ')},	/* Falam Chin -> Chin */
 /*{"cgg",	HB_TAG('C','G','G',' ')},*/	/* Chiga */
   {"ch",	HB_TAG('C','H','A',' ')},	/* Chamorro */
+  {"chf",	HB_TAG('M','Y','N',' ')},	/* Tabasco Chontal -> Mayan */
+  {"chg",	HB_TAG_NONE	       },	/* Chagatai != Chaha Gurage */
+  {"chh",	HB_TAG_NONE	       },	/* Chinook != Chattisgarhi */
   {"chj",	HB_TAG('C','C','H','N')},	/* Ojitlán Chinantec -> Chinantec */
   {"chk",	HB_TAG('C','H','K','0')},	/* Chuukese */
+  {"chn",	HB_TAG('C','P','P',' ')},	/* Chinook jargon -> Creoles */
 /*{"cho",	HB_TAG('C','H','O',' ')},*/	/* Choctaw */
   {"chp",	HB_TAG('C','H','P',' ')},	/* Chipewyan */
   {"chp",	HB_TAG('S','A','Y',' ')},	/* Chipewyan -> Sayisi */
@@ -186,59 +266,85 @@
   {"ciw",	HB_TAG('O','J','B',' ')},	/* Chippewa -> Ojibway */
 /*{"cja",	HB_TAG('C','J','A',' ')},*/	/* Western Cham */
 /*{"cjm",	HB_TAG('C','J','M',' ')},*/	/* Eastern Cham */
-  {"cjy",	HB_TAG('Z','H','S',' ')},	/* Jinyu Chinese -> Chinese Simplified */
+  {"cjy",	HB_TAG('Z','H','S',' ')},	/* Jinyu Chinese -> Chinese, Simplified */
   {"cka",	HB_TAG('Q','I','N',' ')},	/* Khumi Awa Chin (retired code) -> Chin */
   {"ckb",	HB_TAG('K','U','R',' ')},	/* Central Kurdish -> Kurdish */
+  {"ckn",	HB_TAG('Q','I','N',' ')},	/* Kaang Chin -> Chin */
+  {"cks",	HB_TAG('C','P','P',' ')},	/* Tayo -> Creoles */
   {"ckt",	HB_TAG('C','H','K',' ')},	/* Chukot -> Chukchi */
+  {"ckz",	HB_TAG('M','Y','N',' ')},	/* Cakchiquel-Quiché Mixed Language -> Mayan */
   {"clc",	HB_TAG('A','T','H',' ')},	/* Chilcotin -> Athapaskan */
   {"cld",	HB_TAG('S','Y','R',' ')},	/* Chaldean Neo-Aramaic -> Syriac */
   {"cle",	HB_TAG('C','C','H','N')},	/* Lealao Chinantec -> Chinantec */
-  {"cmn",	HB_TAG('Z','H','S',' ')},	/* Mandarin Chinese -> Chinese Simplified */
+  {"clj",	HB_TAG('Q','I','N',' ')},	/* Laitu Chin -> Chin */
+  {"clt",	HB_TAG('Q','I','N',' ')},	/* Lautu Chin -> Chin */
+  {"cmn",	HB_TAG('Z','H','S',' ')},	/* Mandarin Chinese -> Chinese, Simplified */
   {"cmr",	HB_TAG('Q','I','N',' ')},	/* Mro-Khimi Chin -> Chin */
   {"cnb",	HB_TAG('Q','I','N',' ')},	/* Chinbon Chin -> Chin */
   {"cnh",	HB_TAG('Q','I','N',' ')},	/* Hakha Chin -> Chin */
   {"cnk",	HB_TAG('Q','I','N',' ')},	/* Khumi Chin -> Chin */
   {"cnl",	HB_TAG('C','C','H','N')},	/* Lalana Chinantec -> Chinantec */
-  {"cnp",	HB_TAG('Z','H','S',' ')},	/* Northern Ping Chinese -> Chinese Simplified */
+  {"cnp",	HB_TAG('Z','H','S',' ')},	/* Northern Ping Chinese -> Chinese, Simplified */
+  {"cnr",	HB_TAG('S','R','B',' ')},	/* Montenegrin -> Serbian */
   {"cnt",	HB_TAG('C','C','H','N')},	/* Tepetotutla Chinantec -> Chinantec */
+  {"cnu",	HB_TAG('B','B','R',' ')},	/* Chenoua -> Berber */
   {"cnw",	HB_TAG('Q','I','N',' ')},	/* Ngawn Chin -> Chin */
   {"co",	HB_TAG('C','O','S',' ')},	/* Corsican */
   {"coa",	HB_TAG('M','L','Y',' ')},	/* Cocos Islands Malay -> Malay */
+  {"cob",	HB_TAG('M','Y','N',' ')},	/* Chicomuceltec -> Mayan */
 /*{"cop",	HB_TAG('C','O','P',' ')},*/	/* Coptic */
   {"coq",	HB_TAG('A','T','H',' ')},	/* Coquille -> Athapaskan */
   {"cpa",	HB_TAG('C','C','H','N')},	/* Palantla Chinantec -> Chinantec */
   {"cpe",	HB_TAG('C','P','P',' ')},	/* English-based creoles and pidgins [family] -> Creoles */
   {"cpf",	HB_TAG('C','P','P',' ')},	/* French-based creoles and pidgins [family] -> Creoles */
+  {"cpi",	HB_TAG('C','P','P',' ')},	/* Chinese Pidgin English -> Creoles */
 /*{"cpp",	HB_TAG('C','P','P',' ')},*/	/* Portuguese-based creoles and pidgins [family] -> Creoles */
-  {"cpx",	HB_TAG('Z','H','S',' ')},	/* Pu-Xian Chinese -> Chinese Simplified */
+  {"cpx",	HB_TAG('Z','H','S',' ')},	/* Pu-Xian Chinese -> Chinese, Simplified */
   {"cqd",	HB_TAG('H','M','N',' ')},	/* Chuanqiandian Cluster Miao -> Hmong */
   {"cqu",	HB_TAG('Q','U','H',' ')},	/* Chilean Quechua (retired code) -> Quechua (Bolivia) */
+  {"cqu",	HB_TAG('Q','U','Z',' ')},	/* Chilean Quechua (retired code) -> Quechua */
   {"cr",	HB_TAG('C','R','E',' ')},	/* Cree [macrolanguage] */
-  {"cr",	HB_TAG('Y','C','R',' ')},	/* Cree [macrolanguage] -> Y-Cree */
   {"crh",	HB_TAG('C','R','T',' ')},	/* Crimean Tatar */
+  {"cri",	HB_TAG('C','P','P',' ')},	/* Sãotomense -> Creoles */
   {"crj",	HB_TAG('E','C','R',' ')},	/* Southern East Cree -> Eastern Cree */
+  {"crj",	HB_TAG('Y','C','R',' ')},	/* Southern East Cree -> Y-Cree */
+  {"crj",	HB_TAG('C','R','E',' ')},	/* Southern East Cree -> Cree */
   {"crk",	HB_TAG('W','C','R',' ')},	/* Plains Cree -> West-Cree */
+  {"crk",	HB_TAG('Y','C','R',' ')},	/* Plains Cree -> Y-Cree */
+  {"crk",	HB_TAG('C','R','E',' ')},	/* Plains Cree -> Cree */
   {"crl",	HB_TAG('E','C','R',' ')},	/* Northern East Cree -> Eastern Cree */
+  {"crl",	HB_TAG('Y','C','R',' ')},	/* Northern East Cree -> Y-Cree */
+  {"crl",	HB_TAG('C','R','E',' ')},	/* Northern East Cree -> Cree */
   {"crm",	HB_TAG('M','C','R',' ')},	/* Moose Cree */
   {"crm",	HB_TAG('L','C','R',' ')},	/* Moose Cree -> L-Cree */
+  {"crm",	HB_TAG('C','R','E',' ')},	/* Moose Cree -> Cree */
   {"crp",	HB_TAG('C','P','P',' ')},	/* Creoles and pidgins [family] -> Creoles */
+  {"crr",	HB_TAG_NONE	       },	/* Carolina Algonquian != Carrier */
+  {"crs",	HB_TAG('C','P','P',' ')},	/* Seselwa Creole French -> Creoles */
+  {"crt",	HB_TAG_NONE	       },	/* Iyojwa'ja Chorote != Crimean Tatar */
   {"crx",	HB_TAG('C','R','R',' ')},	/* Carrier */
   {"crx",	HB_TAG('A','T','H',' ')},	/* Carrier -> Athapaskan */
   {"cs",	HB_TAG('C','S','Y',' ')},	/* Czech */
   {"csa",	HB_TAG('C','C','H','N')},	/* Chiltepec Chinantec -> Chinantec */
 /*{"csb",	HB_TAG('C','S','B',' ')},*/	/* Kashubian */
   {"csh",	HB_TAG('Q','I','N',' ')},	/* Asho Chin -> Chin */
+  {"csj",	HB_TAG('Q','I','N',' ')},	/* Songlai Chin -> Chin */
+  {"csl",	HB_TAG_NONE	       },	/* Chinese Sign Language != Church Slavonic */
   {"cso",	HB_TAG('C','C','H','N')},	/* Sochiapam Chinantec -> Chinantec */
-  {"csp",	HB_TAG('Z','H','S',' ')},	/* Southern Ping Chinese -> Chinese Simplified */
+  {"csp",	HB_TAG('Z','H','S',' ')},	/* Southern Ping Chinese -> Chinese, Simplified */
+  {"csv",	HB_TAG('Q','I','N',' ')},	/* Sumtu Chin -> Chin */
   {"csw",	HB_TAG('N','C','R',' ')},	/* Swampy Cree -> N-Cree */
   {"csw",	HB_TAG('N','H','C',' ')},	/* Swampy Cree -> Norway House Cree */
+  {"csw",	HB_TAG('C','R','E',' ')},	/* Swampy Cree -> Cree */
   {"csy",	HB_TAG('Q','I','N',' ')},	/* Siyin Chin -> Chin */
   {"ctc",	HB_TAG('A','T','H',' ')},	/* Chetco -> Athapaskan */
   {"ctd",	HB_TAG('Q','I','N',' ')},	/* Tedim Chin -> Chin */
   {"cte",	HB_TAG('C','C','H','N')},	/* Tepinapa Chinantec -> Chinantec */
 /*{"ctg",	HB_TAG('C','T','G',' ')},*/	/* Chittagonian */
+  {"cth",	HB_TAG('Q','I','N',' ')},	/* Thaiphum Chin -> Chin */
   {"ctl",	HB_TAG('C','C','H','N')},	/* Tlacoatzintepec Chinantec -> Chinantec */
   {"cts",	HB_TAG('B','I','K',' ')},	/* Northern Catanduanes Bikol -> Bikol */
+  {"ctu",	HB_TAG('M','Y','N',' ')},	/* Chol -> Mayan */
   {"cu",	HB_TAG('C','S','L',' ')},	/* Church Slavonic */
   {"cuc",	HB_TAG('C','C','H','N')},	/* Usila Chinantec -> Chinantec */
 /*{"cuk",	HB_TAG('C','U','K',' ')},*/	/* San Blas Kuna */
@@ -246,39 +352,50 @@
   {"cvn",	HB_TAG('C','C','H','N')},	/* Valle Nacional Chinantec -> Chinantec */
   {"cwd",	HB_TAG('D','C','R',' ')},	/* Woods Cree */
   {"cwd",	HB_TAG('T','C','R',' ')},	/* Woods Cree -> TH-Cree */
+  {"cwd",	HB_TAG('C','R','E',' ')},	/* Woods Cree -> Cree */
   {"cy",	HB_TAG('W','E','L',' ')},	/* Welsh */
-  {"czh",	HB_TAG('Z','H','S',' ')},	/* Huizhou Chinese -> Chinese Simplified */
-  {"czo",	HB_TAG('Z','H','S',' ')},	/* Min Zhong Chinese -> Chinese Simplified */
+  {"czh",	HB_TAG('Z','H','S',' ')},	/* Huizhou Chinese -> Chinese, Simplified */
+  {"czo",	HB_TAG('Z','H','S',' ')},	/* Min Zhong Chinese -> Chinese, Simplified */
   {"czt",	HB_TAG('Q','I','N',' ')},	/* Zotung Chin -> Chin */
   {"da",	HB_TAG('D','A','N',' ')},	/* Danish */
   {"dao",	HB_TAG('Q','I','N',' ')},	/* Daai Chin -> Chin */
   {"dap",	HB_TAG('N','I','S',' ')},	/* Nisi (India) (retired code) */
 /*{"dar",	HB_TAG('D','A','R',' ')},*/	/* Dargwa */
 /*{"dax",	HB_TAG('D','A','X',' ')},*/	/* Dayi */
+  {"dcr",	HB_TAG('C','P','P',' ')},	/* Negerhollands -> Creoles */
   {"de",	HB_TAG('D','E','U',' ')},	/* German */
   {"den",	HB_TAG('S','L','A',' ')},	/* Slave (Athapascan) [macrolanguage] -> Slavey */
   {"den",	HB_TAG('A','T','H',' ')},	/* Slave (Athapascan) [macrolanguage] -> Athapaskan */
-/*{"dgo",	HB_TAG('D','G','O',' ')},*/	/* Dogri */
+  {"dep",	HB_TAG('C','P','P',' ')},	/* Pidgin Delaware -> Creoles */
+  {"dgo",	HB_TAG('D','G','O',' ')},	/* Dogri (individual language) */
+  {"dgo",	HB_TAG('D','G','R',' ')},	/* Dogri (macrolanguage) */
   {"dgr",	HB_TAG('A','T','H',' ')},	/* Dogrib -> Athapaskan */
   {"dhd",	HB_TAG('M','A','W',' ')},	/* Dhundari -> Marwari */
 /*{"dhg",	HB_TAG('D','H','G',' ')},*/	/* Dhangu */
+  {"dhv",	HB_TAG_NONE	       },	/* Dehu != Divehi (Dhivehi, Maldivian) (deprecated) */
   {"dib",	HB_TAG('D','N','K',' ')},	/* South Central Dinka -> Dinka */
   {"dik",	HB_TAG('D','N','K',' ')},	/* Southwestern Dinka -> Dinka */
   {"din",	HB_TAG('D','N','K',' ')},	/* Dinka [macrolanguage] */
   {"dip",	HB_TAG('D','N','K',' ')},	/* Northeastern Dinka -> Dinka */
-/*{"diq",	HB_TAG('D','I','Q',' ')},*/	/* Dimli */
+  {"diq",	HB_TAG('D','I','Q',' ')},	/* Dimli */
+  {"diq",	HB_TAG('Z','Z','A',' ')},	/* Dimli -> Zazaki */
   {"diw",	HB_TAG('D','N','K',' ')},	/* Northwestern Dinka -> Dinka */
   {"dje",	HB_TAG('D','J','R',' ')},	/* Zarma */
+  {"djk",	HB_TAG('C','P','P',' ')},	/* Eastern Maroon Creole -> Creoles */
   {"djr",	HB_TAG('D','J','R','0')},	/* Djambarrpuyngu */
   {"dks",	HB_TAG('D','N','K',' ')},	/* Southeastern Dinka -> Dinka */
   {"dng",	HB_TAG('D','U','N',' ')},	/* Dungan */
 /*{"dnj",	HB_TAG('D','N','J',' ')},*/	/* Dan */
-  {"doi",	HB_TAG('D','G','R',' ')},	/* Dogri [macrolanguage] */
+  {"dnk",	HB_TAG_NONE	       },	/* Dengka != Dinka */
+  {"doi",	HB_TAG('D','G','R',' ')},	/* Dogri (macrolanguage) [macrolanguage] */
   {"drh",	HB_TAG('M','N','G',' ')},	/* Darkhat (retired code) -> Mongolian */
+  {"dri",	HB_TAG_NONE	       },	/* C'Lela != Dari */
   {"drw",	HB_TAG('D','R','I',' ')},	/* Darwazi (retired code) -> Dari */
+  {"drw",	HB_TAG('F','A','R',' ')},	/* Darwazi (retired code) -> Persian */
   {"dsb",	HB_TAG('L','S','B',' ')},	/* Lower Sorbian */
   {"dty",	HB_TAG('N','E','P',' ')},	/* Dotyali -> Nepali */
 /*{"duj",	HB_TAG('D','U','J',' ')},*/	/* Dhuwal (retired code) */
+  {"dun",	HB_TAG_NONE	       },	/* Dusun Deyah != Dungan */
   {"dup",	HB_TAG('M','L','Y',' ')},	/* Duano -> Malay */
   {"dv",	HB_TAG('D','I','V',' ')},	/* Divehi (Dhivehi, Maldivian) */
   {"dv",	HB_TAG('D','H','V',' ')},	/* Divehi (Dhivehi, Maldivian) (deprecated) */
@@ -287,16 +404,20 @@
   {"dwy",	HB_TAG('D','U','J',' ')},	/* Dhuwaya -> Dhuwal */
   {"dyu",	HB_TAG('J','U','L',' ')},	/* Dyula -> Jula */
   {"dz",	HB_TAG('D','Z','N',' ')},	/* Dzongkha */
+  {"dzn",	HB_TAG_NONE	       },	/* Dzando != Dzongkha */
+  {"ecr",	HB_TAG_NONE	       },	/* Eteocretan != Eastern Cree */
   {"ee",	HB_TAG('E','W','E',' ')},	/* Ewe */
 /*{"efi",	HB_TAG('E','F','I',' ')},*/	/* Efik */
   {"ekk",	HB_TAG('E','T','I',' ')},	/* Standard Estonian -> Estonian */
+  {"eky",	HB_TAG('K','R','N',' ')},	/* Eastern Kayah -> Karen */
   {"el",	HB_TAG('E','L','L',' ')},	/* Modern Greek (1453-) -> Greek */
   {"emk",	HB_TAG('E','M','K',' ')},	/* Eastern Maninkakan */
   {"emk",	HB_TAG('M','N','K',' ')},	/* Eastern Maninkakan -> Maninka */
+  {"emy",	HB_TAG('M','Y','N',' ')},	/* Epigraphic Mayan -> Mayan */
   {"en",	HB_TAG('E','N','G',' ')},	/* English */
   {"enb",	HB_TAG('K','A','L',' ')},	/* Markweeta -> Kalenjin */
-  {"enf",	HB_TAG('F','N','E',' ')},	/* Forest Enets -> Forest Nenets */
-  {"enh",	HB_TAG('T','N','E',' ')},	/* Tundra Enets -> Tundra Nenets */
+  {"enf",	HB_TAG('F','N','E',' ')},	/* Forest Enets */
+  {"enh",	HB_TAG('T','N','E',' ')},	/* Tundra Enets */
   {"eo",	HB_TAG('N','T','O',' ')},	/* Esperanto */
   {"es",	HB_TAG('E','S','P',' ')},	/* Spanish */
   {"esg",	HB_TAG('G','O','N',' ')},	/* Aheri Gondi -> Gondi */
@@ -306,13 +427,18 @@
   {"et",	HB_TAG('E','T','I',' ')},	/* Estonian [macrolanguage] */
   {"eto",	HB_TAG('B','T','I',' ')},	/* Eton (Cameroon) -> Beti */
   {"eu",	HB_TAG('E','U','Q',' ')},	/* Basque */
+  {"euq",	HB_TAG_NONE	       },	/* Basque [family] != Basque */
   {"eve",	HB_TAG('E','V','N',' ')},	/* Even */
   {"evn",	HB_TAG('E','V','K',' ')},	/* Evenki */
   {"ewo",	HB_TAG('B','T','I',' ')},	/* Ewondo -> Beti */
   {"eyo",	HB_TAG('K','A','L',' ')},	/* Keiyo -> Kalenjin */
   {"fa",	HB_TAG('F','A','R',' ')},	/* Persian [macrolanguage] */
+  {"fab",	HB_TAG('C','P','P',' ')},	/* Fa d'Ambu -> Creoles */
   {"fan",	HB_TAG('F','A','N','0')},	/* Fang (Equatorial Guinea) */
-/*{"fat",	HB_TAG('F','A','T',' ')},*/	/* Fanti */
+  {"fan",	HB_TAG('B','T','I',' ')},	/* Fang (Equatorial Guinea) -> Beti */
+  {"far",	HB_TAG_NONE	       },	/* Fataleka != Persian */
+  {"fat",	HB_TAG('F','A','T',' ')},	/* Fanti */
+  {"fat",	HB_TAG('A','K','A',' ')},	/* Fanti -> Akan */
   {"fbl",	HB_TAG('B','I','K',' ')},	/* West Albay Bikol -> Bikol */
   {"ff",	HB_TAG('F','U','L',' ')},	/* Fulah [macrolanguage] */
   {"ffm",	HB_TAG('F','U','L',' ')},	/* Maasina Fulfulde -> Fulah */
@@ -321,9 +447,13 @@
   {"fj",	HB_TAG('F','J','I',' ')},	/* Fijian */
   {"flm",	HB_TAG('H','A','L',' ')},	/* Halam (Falam Chin) (retired code) */
   {"flm",	HB_TAG('Q','I','N',' ')},	/* Falam Chin (retired code) -> Chin */
-/*{"fmp",	HB_TAG('F','M','P',' ')},*/	/* Fe’fe’ */
+  {"fmp",	HB_TAG('F','M','P',' ')},	/* Fe’fe’ */
+  {"fmp",	HB_TAG('B','M','L',' ')},	/* Fe'fe' -> Bamileke */
+  {"fng",	HB_TAG('C','P','P',' ')},	/* Fanagalo -> Creoles */
   {"fo",	HB_TAG('F','O','S',' ')},	/* Faroese */
 /*{"fon",	HB_TAG('F','O','N',' ')},*/	/* Fon */
+  {"fos",	HB_TAG_NONE	       },	/* Siraya != Faroese */
+  {"fpe",	HB_TAG('C','P','P',' ')},	/* Fernando Po Creole English -> Creoles */
   {"fr",	HB_TAG('F','R','A',' ')},	/* French */
 /*{"frc",	HB_TAG('F','R','C',' ')},*/	/* Cajun French */
 /*{"frp",	HB_TAG('F','R','P',' ')},*/	/* Arpitan */
@@ -331,59 +461,89 @@
   {"fuc",	HB_TAG('F','U','L',' ')},	/* Pulaar -> Fulah */
   {"fue",	HB_TAG('F','U','L',' ')},	/* Borgu Fulfulde -> Fulah */
   {"fuf",	HB_TAG('F','T','A',' ')},	/* Pular -> Futa */
+  {"fuf",	HB_TAG('F','U','L',' ')},	/* Pular -> Fulah */
   {"fuh",	HB_TAG('F','U','L',' ')},	/* Western Niger Fulfulde -> Fulah */
   {"fui",	HB_TAG('F','U','L',' ')},	/* Bagirmi Fulfulde -> Fulah */
   {"fuq",	HB_TAG('F','U','L',' ')},	/* Central-Eastern Niger Fulfulde -> Fulah */
   {"fur",	HB_TAG('F','R','L',' ')},	/* Friulian */
-/*{"fuv",	HB_TAG('F','U','V',' ')},*/	/* Nigerian Fulfulde */
+  {"fuv",	HB_TAG('F','U','V',' ')},	/* Nigerian Fulfulde */
+  {"fuv",	HB_TAG('F','U','L',' ')},	/* Nigerian Fulfulde -> Fulah */
   {"fy",	HB_TAG('F','R','I',' ')},	/* Western Frisian -> Frisian */
   {"ga",	HB_TAG('I','R','I',' ')},	/* Irish */
   {"gaa",	HB_TAG('G','A','D',' ')},	/* Ga */
+  {"gac",	HB_TAG('C','P','P',' ')},	/* Mixed Great Andamanese -> Creoles */
+  {"gad",	HB_TAG_NONE	       },	/* Gaddang != Ga */
+  {"gae",	HB_TAG_NONE	       },	/* Guarequena != Scottish Gaelic (Gaelic) */
 /*{"gag",	HB_TAG('G','A','G',' ')},*/	/* Gagauz */
-  {"gan",	HB_TAG('Z','H','S',' ')},	/* Gan Chinese -> Chinese Simplified */
+  {"gal",	HB_TAG_NONE	       },	/* Galolen != Galician */
+  {"gan",	HB_TAG('Z','H','S',' ')},	/* Gan Chinese -> Chinese, Simplified */
+  {"gar",	HB_TAG_NONE	       },	/* Galeya != Garshuni */
+  {"gaw",	HB_TAG_NONE	       },	/* Nobonob != Garhwali */
   {"gax",	HB_TAG('O','R','O',' ')},	/* Borana-Arsi-Guji Oromo -> Oromo */
   {"gaz",	HB_TAG('O','R','O',' ')},	/* West Central Oromo -> Oromo */
   {"gbm",	HB_TAG('G','A','W',' ')},	/* Garhwali */
   {"gce",	HB_TAG('A','T','H',' ')},	/* Galice -> Athapaskan */
+  {"gcf",	HB_TAG('C','P','P',' ')},	/* Guadeloupean Creole French -> Creoles */
+  {"gcl",	HB_TAG('C','P','P',' ')},	/* Grenadian Creole English -> Creoles */
+  {"gcr",	HB_TAG('C','P','P',' ')},	/* Guianese Creole French -> Creoles */
   {"gd",	HB_TAG('G','A','E',' ')},	/* Scottish Gaelic (Gaelic) */
   {"gda",	HB_TAG('R','A','J',' ')},	/* Gade Lohar -> Rajasthani */
 /*{"gez",	HB_TAG('G','E','Z',' ')},*/	/* Geez */
   {"ggo",	HB_TAG('G','O','N',' ')},	/* Southern Gondi (retired code) -> Gondi */
+  {"gha",	HB_TAG('B','B','R',' ')},	/* Ghadamès -> Berber */
+  {"ghk",	HB_TAG('K','R','N',' ')},	/* Geko Karen -> Karen */
+  {"gho",	HB_TAG('B','B','R',' ')},	/* Ghomara -> Berber */
+  {"gib",	HB_TAG('C','P','P',' ')},	/* Gibanawa -> Creoles */
 /*{"gih",	HB_TAG('G','I','H',' ')},*/	/* Githabul */
   {"gil",	HB_TAG('G','I','L','0')},	/* Kiribati (Gilbertese) */
   {"gju",	HB_TAG('R','A','J',' ')},	/* Gujari -> Rajasthani */
-/*{"gkp",	HB_TAG('G','K','P',' ')},*/	/* Guinea Kpelle -> Kpelle (Guinea) */
+  {"gkp",	HB_TAG('G','K','P',' ')},	/* Guinea Kpelle -> Kpelle (Guinea) */
+  {"gkp",	HB_TAG('K','P','L',' ')},	/* Guinea Kpelle -> Kpelle */
   {"gl",	HB_TAG('G','A','L',' ')},	/* Galician */
   {"gld",	HB_TAG('N','A','N',' ')},	/* Nanai */
 /*{"glk",	HB_TAG('G','L','K',' ')},*/	/* Gilaki */
+  {"gmz",	HB_TAG_NONE	       },	/* Mgbolizhia != Gumuz */
   {"gn",	HB_TAG('G','U','A',' ')},	/* Guarani [macrolanguage] */
+  {"gnb",	HB_TAG('Q','I','N',' ')},	/* Gangte -> Chin */
 /*{"gnn",	HB_TAG('G','N','N',' ')},*/	/* Gumatj */
   {"gno",	HB_TAG('G','O','N',' ')},	/* Northern Gondi -> Gondi */
   {"gnw",	HB_TAG('G','U','A',' ')},	/* Western Bolivian Guaraní -> Guarani */
 /*{"gog",	HB_TAG('G','O','G',' ')},*/	/* Gogo */
   {"gom",	HB_TAG('K','O','K',' ')},	/* Goan Konkani -> Konkani */
 /*{"gon",	HB_TAG('G','O','N',' ')},*/	/* Gondi [macrolanguage] */
+  {"goq",	HB_TAG('C','P','P',' ')},	/* Gorap -> Creoles */
+  {"gox",	HB_TAG('B','A','D','0')},	/* Gobu -> Banda */
+  {"gpe",	HB_TAG('C','P','P',' ')},	/* Ghanaian Pidgin English -> Creoles */
+  {"gro",	HB_TAG_NONE	       },	/* Groma != Garo */
+  {"grr",	HB_TAG('B','B','R',' ')},	/* Taznatit -> Berber */
   {"grt",	HB_TAG('G','R','O',' ')},	/* Garo */
   {"gru",	HB_TAG('S','O','G',' ')},	/* Kistane -> Sodo Gurage */
   {"gsw",	HB_TAG('A','L','S',' ')},	/* Alsatian */
   {"gu",	HB_TAG('G','U','J',' ')},	/* Gujarati */
+  {"gua",	HB_TAG_NONE	       },	/* Shiki != Guarani */
 /*{"guc",	HB_TAG('G','U','C',' ')},*/	/* Wayuu */
 /*{"guf",	HB_TAG('G','U','F',' ')},*/	/* Gupapuyngu */
   {"gug",	HB_TAG('G','U','A',' ')},	/* Paraguayan Guaraní -> Guarani */
   {"gui",	HB_TAG('G','U','A',' ')},	/* Eastern Bolivian Guaraní -> Guarani */
   {"guk",	HB_TAG('G','M','Z',' ')},	/* Gumuz */
+  {"gul",	HB_TAG('C','P','P',' ')},	/* Sea Island Creole English -> Creoles */
   {"gun",	HB_TAG('G','U','A',' ')},	/* Mbyá Guaraní -> Guarani */
 /*{"guz",	HB_TAG('G','U','Z',' ')},*/	/* Gusii */
   {"gv",	HB_TAG('M','N','X',' ')},	/* Manx */
   {"gwi",	HB_TAG('A','T','H',' ')},	/* Gwichʼin -> Athapaskan */
+  {"gyn",	HB_TAG('C','P','P',' ')},	/* Guyanese Creole English -> Creoles */
   {"ha",	HB_TAG('H','A','U',' ')},	/* Hausa */
   {"haa",	HB_TAG('A','T','H',' ')},	/* Han -> Athapaskan */
   {"hae",	HB_TAG('O','R','O',' ')},	/* Eastern Oromo -> Oromo */
-  {"hak",	HB_TAG('Z','H','S',' ')},	/* Hakka Chinese -> Chinese Simplified */
+  {"hai",	HB_TAG_NONE	       },	/* Haida [macrolanguage] != Haitian (Haitian Creole) */
+  {"hak",	HB_TAG('Z','H','S',' ')},	/* Hakka Chinese -> Chinese, Simplified */
+  {"hal",	HB_TAG_NONE	       },	/* Halang != Halam (Falam Chin) */
   {"har",	HB_TAG('H','R','I',' ')},	/* Harari */
 /*{"haw",	HB_TAG('H','A','W',' ')},*/	/* Hawaiian */
 /*{"hay",	HB_TAG('H','A','Y',' ')},*/	/* Haya */
 /*{"haz",	HB_TAG('H','A','Z',' ')},*/	/* Hazaragi */
+  {"hbn",	HB_TAG_NONE	       },	/* Heiban != Hammer-Banna */
+  {"hca",	HB_TAG('C','P','P',' ')},	/* Andaman Creole Hindi -> Creoles */
   {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
   {"hea",	HB_TAG('H','M','N',' ')},	/* Northern Qiandong Miao -> Hmong */
   {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
@@ -403,6 +563,7 @@
 /*{"hmn",	HB_TAG('H','M','N',' ')},*/	/* Hmong [macrolanguage] */
   {"hmp",	HB_TAG('H','M','N',' ')},	/* Northern Mashan Hmong -> Hmong */
   {"hmq",	HB_TAG('H','M','N',' ')},	/* Eastern Qiandong Miao -> Hmong */
+  {"hmr",	HB_TAG('Q','I','N',' ')},	/* Hmar -> Chin */
   {"hms",	HB_TAG('H','M','N',' ')},	/* Southern Qiandong Miao -> Hmong */
   {"hmw",	HB_TAG('H','M','N',' ')},	/* Western Mashan Hmong -> Hmong */
   {"hmy",	HB_TAG('H','M','N',' ')},	/* Southern Guiyang Hmong -> Hmong */
@@ -412,17 +573,23 @@
   {"hnj",	HB_TAG('H','M','N',' ')},	/* Hmong Njua -> Hmong */
   {"hno",	HB_TAG('H','N','D',' ')},	/* Northern Hindko -> Hindko */
   {"ho",	HB_TAG('H','M','O',' ')},	/* Hiri Motu */
+  {"ho",	HB_TAG('C','P','P',' ')},	/* Hiri Motu -> Creoles */
   {"hoc",	HB_TAG('H','O',' ',' ')},	/* Ho */
   {"hoi",	HB_TAG('A','T','H',' ')},	/* Holikachuk -> Athapaskan */
   {"hoj",	HB_TAG('H','A','R',' ')},	/* Hadothi -> Harauti */
+  {"hoj",	HB_TAG('R','A','J',' ')},	/* Hadothi -> Rajasthani */
   {"hr",	HB_TAG('H','R','V',' ')},	/* Croatian */
+  {"hra",	HB_TAG('Q','I','N',' ')},	/* Hrangkhol -> Chin */
   {"hrm",	HB_TAG('H','M','N',' ')},	/* Horned Miao -> Hmong */
   {"hsb",	HB_TAG('U','S','B',' ')},	/* Upper Sorbian */
-  {"hsn",	HB_TAG('Z','H','S',' ')},	/* Xiang Chinese -> Chinese Simplified */
+  {"hsn",	HB_TAG('Z','H','S',' ')},	/* Xiang Chinese -> Chinese, Simplified */
   {"ht",	HB_TAG('H','A','I',' ')},	/* Haitian (Haitian Creole) */
+  {"ht",	HB_TAG('C','P','P',' ')},	/* Haitian -> Creoles */
   {"hu",	HB_TAG('H','U','N',' ')},	/* Hungarian */
   {"huj",	HB_TAG('H','M','N',' ')},	/* Northern Guiyang Hmong -> Hmong */
   {"hup",	HB_TAG('A','T','H',' ')},	/* Hupa -> Athapaskan */
+  {"hus",	HB_TAG('M','Y','N',' ')},	/* Huastec -> Mayan */
+  {"hwc",	HB_TAG('C','P','P',' ')},	/* Hawai'i Creole English -> Creoles */
   {"hy",	HB_TAG('H','Y','E','0')},	/* Armenian -> Armenian East */
   {"hy",	HB_TAG('H','Y','E',' ')},	/* Armenian */
   {"hyw",	HB_TAG('H','Y','E',' ')},	/* Western Armenian -> Armenian */
@@ -430,38 +597,65 @@
   {"ia",	HB_TAG('I','N','A',' ')},	/* Interlingua (International Auxiliary Language Association) */
 /*{"iba",	HB_TAG('I','B','A',' ')},*/	/* Iban */
 /*{"ibb",	HB_TAG('I','B','B',' ')},*/	/* Ibibio */
+  {"iby",	HB_TAG('I','J','O',' ')},	/* Ibani -> Ijo */
+  {"icr",	HB_TAG('C','P','P',' ')},	/* Islander Creole English -> Creoles */
   {"id",	HB_TAG('I','N','D',' ')},	/* Indonesian */
+  {"id",	HB_TAG('M','L','Y',' ')},	/* Indonesian -> Malay */
   {"ida",	HB_TAG('L','U','H',' ')},	/* Idakho-Isukha-Tiriki -> Luyia */
+  {"idb",	HB_TAG('C','P','P',' ')},	/* Indo-Portuguese -> Creoles */
   {"ie",	HB_TAG('I','L','E',' ')},	/* Interlingue */
   {"ig",	HB_TAG('I','B','O',' ')},	/* Igbo */
   {"igb",	HB_TAG('E','B','I',' ')},	/* Ebira */
+  {"ihb",	HB_TAG('C','P','P',' ')},	/* Iha Based Pidgin -> Creoles */
   {"ii",	HB_TAG('Y','I','M',' ')},	/* Sichuan Yi -> Yi Modern */
   {"ijc",	HB_TAG('I','J','O',' ')},	/* Izon -> Ijo */
+  {"ije",	HB_TAG('I','J','O',' ')},	/* Biseni -> Ijo */
+  {"ijn",	HB_TAG('I','J','O',' ')},	/* Kalabari -> Ijo */
 /*{"ijo",	HB_TAG('I','J','O',' ')},*/	/* Ijo [family] */
+  {"ijs",	HB_TAG('I','J','O',' ')},	/* Southeast Ijo -> Ijo */
   {"ik",	HB_TAG('I','P','K',' ')},	/* Inupiaq [macrolanguage] -> Inupiat */
   {"ike",	HB_TAG('I','N','U',' ')},	/* Eastern Canadian Inuktitut -> Inuktitut */
   {"ikt",	HB_TAG('I','N','U',' ')},	/* Inuinnaqtun -> Inuktitut */
 /*{"ilo",	HB_TAG('I','L','O',' ')},*/	/* Iloko -> Ilokano */
   {"in",	HB_TAG('I','N','D',' ')},	/* Indonesian (retired code) */
+  {"in",	HB_TAG('M','L','Y',' ')},	/* Indonesian (retired code) -> Malay */
   {"ing",	HB_TAG('A','T','H',' ')},	/* Degexit'an -> Athapaskan */
   {"inh",	HB_TAG('I','N','G',' ')},	/* Ingush */
   {"io",	HB_TAG('I','D','O',' ')},	/* Ido */
+  {"iri",	HB_TAG_NONE	       },	/* Rigwe != Irish */
   {"is",	HB_TAG('I','S','L',' ')},	/* Icelandic */
+  {"ism",	HB_TAG_NONE	       },	/* Masimasi != Inari Sami */
   {"it",	HB_TAG('I','T','A',' ')},	/* Italian */
+  {"itz",	HB_TAG('M','Y','N',' ')},	/* Itzá -> Mayan */
   {"iu",	HB_TAG('I','N','U',' ')},	/* Inuktitut [macrolanguage] */
   {"iw",	HB_TAG('I','W','R',' ')},	/* Hebrew (retired code) */
+  {"ixl",	HB_TAG('M','Y','N',' ')},	/* Ixil -> Mayan */
   {"ja",	HB_TAG('J','A','N',' ')},	/* Japanese */
+  {"jac",	HB_TAG('M','Y','N',' ')},	/* Popti' -> Mayan */
   {"jak",	HB_TAG('M','L','Y',' ')},	/* Jakun -> Malay */
-/*{"jam",	HB_TAG('J','A','M',' ')},*/	/* Jamaican Creole English -> Jamaican Creole */
+  {"jam",	HB_TAG('J','A','M',' ')},	/* Jamaican Creole English -> Jamaican Creole */
+  {"jam",	HB_TAG('C','P','P',' ')},	/* Jamaican Creole English -> Creoles */
+  {"jan",	HB_TAG_NONE	       },	/* Jandai != Japanese */
   {"jax",	HB_TAG('M','L','Y',' ')},	/* Jambi Malay -> Malay */
+  {"jbe",	HB_TAG('B','B','R',' ')},	/* Judeo-Berber -> Berber */
+  {"jbn",	HB_TAG('B','B','R',' ')},	/* Nafusi -> Berber */
 /*{"jbo",	HB_TAG('J','B','O',' ')},*/	/* Lojban */
 /*{"jct",	HB_TAG('J','C','T',' ')},*/	/* Krymchak */
+  {"jgo",	HB_TAG('B','M','L',' ')},	/* Ngomba -> Bamileke */
   {"ji",	HB_TAG('J','I','I',' ')},	/* Yiddish (retired code) */
+  {"jii",	HB_TAG_NONE	       },	/* Jiiddu != Yiddish */
+  {"jkm",	HB_TAG('K','R','N',' ')},	/* Mobwa Karen -> Karen */
+  {"jkp",	HB_TAG('K','R','N',' ')},	/* Paku Karen -> Karen */
+  {"jud",	HB_TAG_NONE	       },	/* Worodougou != Ladino */
+  {"jul",	HB_TAG_NONE	       },	/* Jirel != Jula */
   {"jv",	HB_TAG('J','A','V',' ')},	/* Javanese */
+  {"jvd",	HB_TAG('C','P','P',' ')},	/* Javindo -> Creoles */
   {"jw",	HB_TAG('J','A','V',' ')},	/* Javanese (retired code) */
   {"ka",	HB_TAG('K','A','T',' ')},	/* Georgian */
   {"kaa",	HB_TAG('K','R','K',' ')},	/* Karakalpak */
   {"kab",	HB_TAG('K','A','B','0')},	/* Kabyle */
+  {"kab",	HB_TAG('B','B','R',' ')},	/* Kabyle -> Berber */
+  {"kac",	HB_TAG_NONE	       },	/* Kachin != Kachchi */
   {"kam",	HB_TAG('K','M','B',' ')},	/* Kamba (Kenya) */
   {"kar",	HB_TAG('K','R','N',' ')},	/* Karen [family] */
   {"kbd",	HB_TAG('K','A','B',' ')},	/* Kabardian */
@@ -469,83 +663,139 @@
   {"kca",	HB_TAG('K','H','K',' ')},	/* Khanty -> Khanty-Kazim */
   {"kca",	HB_TAG('K','H','S',' ')},	/* Khanty -> Khanty-Shurishkar */
   {"kca",	HB_TAG('K','H','V',' ')},	/* Khanty -> Khanty-Vakhi */
+  {"kcn",	HB_TAG('C','P','P',' ')},	/* Nubi -> Creoles */
 /*{"kde",	HB_TAG('K','D','E',' ')},*/	/* Makonde */
   {"kdr",	HB_TAG('K','R','M',' ')},	/* Karaim */
   {"kdt",	HB_TAG('K','U','Y',' ')},	/* Kuy */
-/*{"kea",	HB_TAG('K','E','A',' ')},*/	/* Kabuverdianu (Crioulo) */
-/*{"kek",	HB_TAG('K','E','K',' ')},*/	/* Kekchi */
+  {"kea",	HB_TAG('K','E','A',' ')},	/* Kabuverdianu (Crioulo) */
+  {"kea",	HB_TAG('C','P','P',' ')},	/* Kabuverdianu -> Creoles */
+  {"keb",	HB_TAG_NONE	       },	/* Kélé != Kebena */
+  {"kek",	HB_TAG('K','E','K',' ')},	/* Kekchi */
+  {"kek",	HB_TAG('M','Y','N',' ')},	/* Kekchí -> Mayan */
   {"kex",	HB_TAG('K','K','N',' ')},	/* Kukna -> Kokni */
   {"kfa",	HB_TAG('K','O','D',' ')},	/* Kodava -> Kodagu */
   {"kfr",	HB_TAG('K','A','C',' ')},	/* Kachhi -> Kachchi */
   {"kfx",	HB_TAG('K','U','L',' ')},	/* Kullu Pahari -> Kulvi */
   {"kfy",	HB_TAG('K','M','N',' ')},	/* Kumaoni */
   {"kg",	HB_TAG('K','O','N','0')},	/* Kongo [macrolanguage] */
+  {"kge",	HB_TAG_NONE	       },	/* Komering != Khutsuri Georgian */
   {"kha",	HB_TAG('K','S','I',' ')},	/* Khasi */
   {"khb",	HB_TAG('X','B','D',' ')},	/* Lü */
   {"khk",	HB_TAG('M','N','G',' ')},	/* Halh Mongolian -> Mongolian */
+  {"khn",	HB_TAG_NONE	       },	/* Khandesi != Khamti Shan (Microsoft fonts) */
+  {"khs",	HB_TAG_NONE	       },	/* Kasua != Khanty-Shurishkar */
   {"kht",	HB_TAG('K','H','T',' ')},	/* Khamti -> Khamti Shan */
   {"kht",	HB_TAG('K','H','N',' ')},	/* Khamti -> Khamti Shan (Microsoft fonts) */
+  {"khv",	HB_TAG_NONE	       },	/* Khvarshi != Khanty-Vakhi */
 /*{"khw",	HB_TAG('K','H','W',' ')},*/	/* Khowar */
   {"ki",	HB_TAG('K','I','K',' ')},	/* Kikuyu (Gikuyu) */
-/*{"kiu",	HB_TAG('K','I','U',' ')},*/	/* Kirmanjki */
+  {"kis",	HB_TAG_NONE	       },	/* Kis != Kisii */
+  {"kiu",	HB_TAG('K','I','U',' ')},	/* Kirmanjki */
+  {"kiu",	HB_TAG('Z','Z','A',' ')},	/* Kirmanjki -> Zazaki */
   {"kj",	HB_TAG('K','U','A',' ')},	/* Kuanyama */
+  {"kjb",	HB_TAG('M','Y','N',' ')},	/* Q'anjob'al -> Mayan */
 /*{"kjd",	HB_TAG('K','J','D',' ')},*/	/* Southern Kiwai */
   {"kjh",	HB_TAG('K','H','A',' ')},	/* Khakas -> Khakass */
-/*{"kjp",	HB_TAG('K','J','P',' ')},*/	/* Pwo Eastern Karen -> Eastern Pwo Karen */
+  {"kjp",	HB_TAG('K','J','P',' ')},	/* Pwo Eastern Karen -> Eastern Pwo Karen */
+  {"kjp",	HB_TAG('K','R','N',' ')},	/* Pwo Eastern Karen -> Karen */
+  {"kjt",	HB_TAG('K','R','N',' ')},	/* Phrae Pwo Karen -> Karen */
 /*{"kjz",	HB_TAG('K','J','Z',' ')},*/	/* Bumthangkha */
   {"kk",	HB_TAG('K','A','Z',' ')},	/* Kazakh */
+  {"kkn",	HB_TAG_NONE	       },	/* Kon Keu != Kokni */
   {"kkz",	HB_TAG('A','T','H',' ')},	/* Kaska -> Athapaskan */
   {"kl",	HB_TAG('G','R','N',' ')},	/* Greenlandic */
+  {"klm",	HB_TAG_NONE	       },	/* Migum != Kalmyk */
   {"kln",	HB_TAG('K','A','L',' ')},	/* Kalenjin [macrolanguage] */
   {"km",	HB_TAG('K','H','M',' ')},	/* Khmer */
   {"kmb",	HB_TAG('M','B','N',' ')},	/* Kimbundu -> Mbundu */
+  {"kmn",	HB_TAG_NONE	       },	/* Awtuw != Kumaoni */
+  {"kmo",	HB_TAG_NONE	       },	/* Kwoma != Komo */
   {"kmr",	HB_TAG('K','U','R',' ')},	/* Northern Kurdish -> Kurdish */
+  {"kms",	HB_TAG_NONE	       },	/* Kamasau != Komso */
+  {"kmv",	HB_TAG('C','P','P',' ')},	/* Karipúna Creole French -> Creoles */
   {"kmw",	HB_TAG('K','M','O',' ')},	/* Komo (Democratic Republic of Congo) */
 /*{"kmz",	HB_TAG('K','M','Z',' ')},*/	/* Khorasani Turkish -> Khorasani Turkic */
   {"kn",	HB_TAG('K','A','N',' ')},	/* Kannada */
   {"knc",	HB_TAG('K','N','R',' ')},	/* Central Kanuri -> Kanuri */
   {"kng",	HB_TAG('K','O','N','0')},	/* Koongo -> Kongo */
+  {"knj",	HB_TAG('M','Y','N',' ')},	/* Western Kanjobal -> Mayan */
   {"knn",	HB_TAG('K','O','K',' ')},	/* Konkani */
+  {"knr",	HB_TAG_NONE	       },	/* Kaningra != Kanuri */
   {"ko",	HB_TAG('K','O','R',' ')},	/* Korean */
+  {"ko",	HB_TAG('K','O','H',' ')},	/* Korean -> Korean Old Hangul */
+  {"kod",	HB_TAG_NONE	       },	/* Kodi != Kodagu */
+  {"koh",	HB_TAG_NONE	       },	/* Koyo != Korean Old Hangul */
   {"koi",	HB_TAG('K','O','P',' ')},	/* Komi-Permyak */
+  {"koi",	HB_TAG('K','O','M',' ')},	/* Komi-Permyak -> Komi */
 /*{"kok",	HB_TAG('K','O','K',' ')},*/	/* Konkani [macrolanguage] */
+  {"kop",	HB_TAG_NONE	       },	/* Waube != Komi-Permyak */
 /*{"kos",	HB_TAG('K','O','S',' ')},*/	/* Kosraean */
   {"koy",	HB_TAG('A','T','H',' ')},	/* Koyukon -> Athapaskan */
+  {"koz",	HB_TAG_NONE	       },	/* Korak != Komi-Zyrian */
   {"kpe",	HB_TAG('K','P','L',' ')},	/* Kpelle [macrolanguage] */
+  {"kpl",	HB_TAG_NONE	       },	/* Kpala != Kpelle */
+  {"kpp",	HB_TAG('K','R','N',' ')},	/* Paku Karen (retired code) -> Karen */
   {"kpv",	HB_TAG('K','O','Z',' ')},	/* Komi-Zyrian */
+  {"kpv",	HB_TAG('K','O','M',' ')},	/* Komi-Zyrian -> Komi */
   {"kpy",	HB_TAG('K','Y','K',' ')},	/* Koryak */
   {"kqs",	HB_TAG('K','I','S',' ')},	/* Northern Kissi -> Kisii */
   {"kqy",	HB_TAG('K','R','T',' ')},	/* Koorete */
   {"kr",	HB_TAG('K','N','R',' ')},	/* Kanuri [macrolanguage] */
   {"krc",	HB_TAG('K','A','R',' ')},	/* Karachay-Balkar -> Karachay */
   {"krc",	HB_TAG('B','A','L',' ')},	/* Karachay-Balkar -> Balkar */
-/*{"kri",	HB_TAG('K','R','I',' ')},*/	/* Krio */
+  {"kri",	HB_TAG('K','R','I',' ')},	/* Krio */
+  {"kri",	HB_TAG('C','P','P',' ')},	/* Krio -> Creoles */
+  {"krk",	HB_TAG_NONE	       },	/* Kerek != Karakalpak */
 /*{"krl",	HB_TAG('K','R','L',' ')},*/	/* Karelian */
+  {"krm",	HB_TAG_NONE	       },	/* Krim (retired code) != Karaim */
+  {"krn",	HB_TAG_NONE	       },	/* Sapo != Karen */
   {"krt",	HB_TAG('K','N','R',' ')},	/* Tumari Kanuri -> Kanuri */
   {"kru",	HB_TAG('K','U','U',' ')},	/* Kurukh */
   {"ks",	HB_TAG('K','S','H',' ')},	/* Kashmiri */
   {"ksh",	HB_TAG('K','S','H','0')},	/* Kölsch -> Ripuarian */
+  {"ksi",	HB_TAG_NONE	       },	/* Krisa != Khasi */
+  {"ksm",	HB_TAG_NONE	       },	/* Kumba != Kildin Sami */
   {"kss",	HB_TAG('K','I','S',' ')},	/* Southern Kisi -> Kisii */
-/*{"ksw",	HB_TAG('K','S','W',' ')},*/	/* S’gaw Karen */
+  {"ksw",	HB_TAG('K','S','W',' ')},	/* S’gaw Karen */
+  {"ksw",	HB_TAG('K','R','N',' ')},	/* S'gaw Karen -> Karen */
   {"ktb",	HB_TAG('K','E','B',' ')},	/* Kambaata -> Kebena */
   {"ktu",	HB_TAG('K','O','N',' ')},	/* Kituba (Democratic Republic of Congo) -> Kikongo */
   {"ktw",	HB_TAG('A','T','H',' ')},	/* Kato -> Athapaskan */
   {"ku",	HB_TAG('K','U','R',' ')},	/* Kurdish [macrolanguage] */
+  {"kui",	HB_TAG_NONE	       },	/* Kuikúro-Kalapálo != Kui */
+  {"kul",	HB_TAG_NONE	       },	/* Kulere != Kulvi */
 /*{"kum",	HB_TAG('K','U','M',' ')},*/	/* Kumyk */
   {"kuu",	HB_TAG('A','T','H',' ')},	/* Upper Kuskokwim -> Athapaskan */
+  {"kuw",	HB_TAG('B','A','D','0')},	/* Kpagua -> Banda */
+  {"kuy",	HB_TAG_NONE	       },	/* Kuuku-Ya'u != Kuy */
   {"kv",	HB_TAG('K','O','M',' ')},	/* Komi [macrolanguage] */
   {"kvb",	HB_TAG('M','L','Y',' ')},	/* Kubu -> Malay */
+  {"kvl",	HB_TAG('K','R','N',' ')},	/* Kayaw -> Karen */
+  {"kvq",	HB_TAG('K','R','N',' ')},	/* Geba Karen -> Karen */
   {"kvr",	HB_TAG('M','L','Y',' ')},	/* Kerinci -> Malay */
+  {"kvt",	HB_TAG('K','R','N',' ')},	/* Lahta Karen -> Karen */
+  {"kvu",	HB_TAG('K','R','N',' ')},	/* Yinbaw Karen -> Karen */
+  {"kvy",	HB_TAG('K','R','N',' ')},	/* Yintale Karen -> Karen */
   {"kw",	HB_TAG('C','O','R',' ')},	/* Cornish */
+  {"kww",	HB_TAG('C','P','P',' ')},	/* Kwinti -> Creoles */
   {"kwy",	HB_TAG('K','O','N','0')},	/* San Salvador Kongo -> Kongo */
   {"kxc",	HB_TAG('K','M','S',' ')},	/* Konso -> Komso */
   {"kxd",	HB_TAG('M','L','Y',' ')},	/* Brunei -> Malay */
+  {"kxf",	HB_TAG('K','R','N',' ')},	/* Manumanaw Karen -> Karen */
+  {"kxk",	HB_TAG('K','R','N',' ')},	/* Zayein Karen -> Karen */
   {"kxl",	HB_TAG('K','U','U',' ')},	/* Nepali Kurux (retired code) -> Kurukh */
   {"kxu",	HB_TAG('K','U','I',' ')},	/* Kui (India) (retired code) */
   {"ky",	HB_TAG('K','I','R',' ')},	/* Kirghiz (Kyrgyz) */
-/*{"kyu",	HB_TAG('K','Y','U',' ')},*/	/* Western Kayah */
+  {"kyk",	HB_TAG_NONE	       },	/* Kamayo != Koryak */
+  {"kyu",	HB_TAG('K','Y','U',' ')},	/* Western Kayah */
+  {"kyu",	HB_TAG('K','R','N',' ')},	/* Western Kayah -> Karen */
   {"la",	HB_TAG('L','A','T',' ')},	/* Latin */
+  {"lac",	HB_TAG('M','Y','N',' ')},	/* Lacandon -> Mayan */
   {"lad",	HB_TAG('J','U','D',' ')},	/* Ladino */
+  {"lah",	HB_TAG_NONE	       },	/* Lahnda [macrolanguage] != Lahuli */
+  {"lak",	HB_TAG_NONE	       },	/* Laka (Nigeria) != Lak */
+  {"lam",	HB_TAG_NONE	       },	/* Lamba != Lambani */
+  {"laz",	HB_TAG_NONE	       },	/* Aribwatsa != Laz */
   {"lb",	HB_TAG('L','T','Z',' ')},	/* Luxembourgish */
   {"lbe",	HB_TAG('L','A','K',' ')},	/* Lak */
   {"lbj",	HB_TAG('L','D','K',' ')},	/* Ladakhi */
@@ -553,85 +803,128 @@
   {"lce",	HB_TAG('M','L','Y',' ')},	/* Loncong -> Malay */
   {"lcf",	HB_TAG('M','L','Y',' ')},	/* Lubu -> Malay */
   {"ldi",	HB_TAG('K','O','N','0')},	/* Laari -> Kongo */
+  {"ldk",	HB_TAG_NONE	       },	/* Leelau != Ladakhi */
 /*{"lez",	HB_TAG('L','E','Z',' ')},*/	/* Lezghian -> Lezgi */
   {"lg",	HB_TAG('L','U','G',' ')},	/* Ganda */
   {"li",	HB_TAG('L','I','M',' ')},	/* Limburgish */
   {"lif",	HB_TAG('L','M','B',' ')},	/* Limbu */
 /*{"lij",	HB_TAG('L','I','J',' ')},*/	/* Ligurian */
+  {"lir",	HB_TAG('C','P','P',' ')},	/* Liberian English -> Creoles */
 /*{"lis",	HB_TAG('L','I','S',' ')},*/	/* Lisu */
   {"liw",	HB_TAG('M','L','Y',' ')},	/* Col -> Malay */
+  {"liy",	HB_TAG('B','A','D','0')},	/* Banda-Bambari -> Banda */
 /*{"ljp",	HB_TAG('L','J','P',' ')},*/	/* Lampung Api -> Lampung */
   {"lkb",	HB_TAG('L','U','H',' ')},	/* Kabras -> Luyia */
 /*{"lki",	HB_TAG('L','K','I',' ')},*/	/* Laki */
   {"lko",	HB_TAG('L','U','H',' ')},	/* Khayo -> Luyia */
   {"lks",	HB_TAG('L','U','H',' ')},	/* Kisa -> Luyia */
   {"lld",	HB_TAG('L','A','D',' ')},	/* Ladin */
+  {"lma",	HB_TAG_NONE	       },	/* East Limba != Low Mari */
+  {"lmb",	HB_TAG_NONE	       },	/* Merei != Limbu */
   {"lmn",	HB_TAG('L','A','M',' ')},	/* Lambadi -> Lambani */
 /*{"lmo",	HB_TAG('L','M','O',' ')},*/	/* Lombard */
+  {"lmw",	HB_TAG_NONE	       },	/* Lake Miwok != Lomwe */
   {"ln",	HB_TAG('L','I','N',' ')},	/* Lingala */
+  {"lna",	HB_TAG('B','A','D','0')},	/* Langbashe -> Banda */
+  {"lnl",	HB_TAG('B','A','D','0')},	/* South Central Banda -> Banda */
   {"lo",	HB_TAG('L','A','O',' ')},	/* Lao */
 /*{"lom",	HB_TAG('L','O','M',' ')},*/	/* Loma (Liberia) */
+  {"lou",	HB_TAG('C','P','P',' ')},	/* Louisiana Creole -> Creoles */
 /*{"lrc",	HB_TAG('L','R','C',' ')},*/	/* Northern Luri -> Luri */
   {"lri",	HB_TAG('L','U','H',' ')},	/* Marachi -> Luyia */
   {"lrm",	HB_TAG('L','U','H',' ')},	/* Marama -> Luyia */
+  {"lrt",	HB_TAG('C','P','P',' ')},	/* Larantuka Malay -> Creoles */
   {"lsm",	HB_TAG('L','U','H',' ')},	/* Saamia -> Luyia */
   {"lt",	HB_TAG('L','T','H',' ')},	/* Lithuanian */
   {"ltg",	HB_TAG('L','V','I',' ')},	/* Latgalian -> Latvian */
+  {"lth",	HB_TAG_NONE	       },	/* Thur != Lithuanian */
   {"lto",	HB_TAG('L','U','H',' ')},	/* Tsotso -> Luyia */
   {"lts",	HB_TAG('L','U','H',' ')},	/* Tachoni -> Luyia */
   {"lu",	HB_TAG('L','U','B',' ')},	/* Luba-Katanga */
 /*{"lua",	HB_TAG('L','U','A',' ')},*/	/* Luba-Lulua */
 /*{"luo",	HB_TAG('L','U','O',' ')},*/	/* Luo (Kenya and Tanzania) */
   {"lus",	HB_TAG('M','I','Z',' ')},	/* Lushai -> Mizo */
+  {"lus",	HB_TAG('Q','I','N',' ')},	/* Lushai -> Chin */
   {"luy",	HB_TAG('L','U','H',' ')},	/* Luyia [macrolanguage] */
   {"luz",	HB_TAG('L','R','C',' ')},	/* Southern Luri -> Luri */
   {"lv",	HB_TAG('L','V','I',' ')},	/* Latvian [macrolanguage] */
+  {"lvi",	HB_TAG_NONE	       },	/* Lavi != Latvian */
   {"lvs",	HB_TAG('L','V','I',' ')},	/* Standard Latvian -> Latvian */
   {"lwg",	HB_TAG('L','U','H',' ')},	/* Wanga -> Luyia */
-  {"lzh",	HB_TAG('Z','H','T',' ')},	/* Literary Chinese -> Chinese Traditional */
+  {"lzh",	HB_TAG('Z','H','T',' ')},	/* Literary Chinese -> Chinese, Traditional */
   {"lzz",	HB_TAG('L','A','Z',' ')},	/* Laz */
 /*{"mad",	HB_TAG('M','A','D',' ')},*/	/* Madurese -> Madura */
 /*{"mag",	HB_TAG('M','A','G',' ')},*/	/* Magahi */
   {"mai",	HB_TAG('M','T','H',' ')},	/* Maithili */
+  {"maj",	HB_TAG_NONE	       },	/* Jalapa De Díaz Mazatec != Majang */
   {"mak",	HB_TAG('M','K','R',' ')},	/* Makasar */
-/*{"mam",	HB_TAG('M','A','M',' ')},*/	/* Mam */
+  {"mam",	HB_TAG('M','A','M',' ')},	/* Mam */
+  {"mam",	HB_TAG('M','Y','N',' ')},	/* Mam -> Mayan */
   {"man",	HB_TAG('M','N','K',' ')},	/* Mandingo [macrolanguage] -> Maninka */
+  {"map",	HB_TAG_NONE	       },	/* Austronesian [family] != Mapudungun */
+  {"maw",	HB_TAG_NONE	       },	/* Mampruli != Marwari */
   {"max",	HB_TAG('M','L','Y',' ')},	/* North Moluccan Malay -> Malay */
+  {"max",	HB_TAG('C','P','P',' ')},	/* North Moluccan Malay -> Creoles */
+  {"mbf",	HB_TAG('C','P','P',' ')},	/* Baba Malay -> Creoles */
+  {"mbn",	HB_TAG_NONE	       },	/* Macaguán != Mbundu */
 /*{"mbo",	HB_TAG('M','B','O',' ')},*/	/* Mbo (Cameroon) */
+  {"mch",	HB_TAG_NONE	       },	/* Maquiritari != Manchu */
+  {"mcm",	HB_TAG('C','P','P',' ')},	/* Malaccan Creole Portuguese -> Creoles */
+  {"mcr",	HB_TAG_NONE	       },	/* Menya != Moose Cree */
   {"mct",	HB_TAG('B','T','I',' ')},	/* Mengisa -> Beti */
+  {"mde",	HB_TAG_NONE	       },	/* Maba (Chad) != Mende */
   {"mdf",	HB_TAG('M','O','K',' ')},	/* Moksha */
 /*{"mdr",	HB_TAG('M','D','R',' ')},*/	/* Mandar */
   {"mdy",	HB_TAG('M','L','E',' ')},	/* Male (Ethiopia) */
   {"men",	HB_TAG('M','D','E',' ')},	/* Mende (Sierra Leone) */
   {"meo",	HB_TAG('M','L','Y',' ')},	/* Kedah Malay -> Malay */
 /*{"mer",	HB_TAG('M','E','R',' ')},*/	/* Meru */
-/*{"mfa",	HB_TAG('M','F','A',' ')},*/	/* Pattani Malay */
+  {"mfa",	HB_TAG('M','F','A',' ')},	/* Pattani Malay */
+  {"mfa",	HB_TAG('M','L','Y',' ')},	/* Pattani Malay -> Malay */
   {"mfb",	HB_TAG('M','L','Y',' ')},	/* Bangka -> Malay */
-/*{"mfe",	HB_TAG('M','F','E',' ')},*/	/* Morisyen */
+  {"mfe",	HB_TAG('M','F','E',' ')},	/* Morisyen */
+  {"mfe",	HB_TAG('C','P','P',' ')},	/* Morisyen -> Creoles */
+  {"mfp",	HB_TAG('C','P','P',' ')},	/* Makassar Malay -> Creoles */
   {"mg",	HB_TAG('M','L','G',' ')},	/* Malagasy [macrolanguage] */
   {"mh",	HB_TAG('M','A','H',' ')},	/* Marshallese */
+  {"mhc",	HB_TAG('M','Y','N',' ')},	/* Mocho -> Mayan */
   {"mhr",	HB_TAG('L','M','A',' ')},	/* Eastern Mari -> Low Mari */
   {"mhv",	HB_TAG('A','R','K',' ')},	/* Arakanese (retired code) -> Rakhine */
   {"mi",	HB_TAG('M','R','I',' ')},	/* Maori */
-/*{"min",	HB_TAG('M','I','N',' ')},*/	/* Minangkabau */
+  {"min",	HB_TAG('M','I','N',' ')},	/* Minangkabau */
+  {"min",	HB_TAG('M','L','Y',' ')},	/* Minangkabau -> Malay */
+  {"miz",	HB_TAG_NONE	       },	/* Coatzospan Mixtec != Mizo */
   {"mk",	HB_TAG('M','K','D',' ')},	/* Macedonian */
+  {"mkn",	HB_TAG('C','P','P',' ')},	/* Kupang Malay -> Creoles */
+  {"mkr",	HB_TAG_NONE	       },	/* Malas != Makasar */
   {"mku",	HB_TAG('M','N','K',' ')},	/* Konyanka Maninka -> Maninka */
 /*{"mkw",	HB_TAG('M','K','W',' ')},*/	/* Kituba (Congo) */
   {"ml",	HB_TAG('M','A','L',' ')},	/* Malayalam -> Malayalam Traditional */
   {"ml",	HB_TAG('M','L','R',' ')},	/* Malayalam -> Malayalam Reformed */
+  {"mle",	HB_TAG_NONE	       },	/* Manambu != Male */
+  {"mln",	HB_TAG_NONE	       },	/* Malango != Malinke */
   {"mlq",	HB_TAG('M','L','N',' ')},	/* Western Maninkakan -> Malinke */
   {"mlq",	HB_TAG('M','N','K',' ')},	/* Western Maninkakan -> Maninka */
+  {"mlr",	HB_TAG_NONE	       },	/* Vame != Malayalam Reformed */
   {"mmr",	HB_TAG('H','M','N',' ')},	/* Western Xiangxi Miao -> Hmong */
   {"mn",	HB_TAG('M','N','G',' ')},	/* Mongolian [macrolanguage] */
   {"mnc",	HB_TAG('M','C','H',' ')},	/* Manchu */
+  {"mnd",	HB_TAG_NONE	       },	/* Mondé != Mandinka */
+  {"mng",	HB_TAG_NONE	       },	/* Eastern Mnong != Mongolian */
+  {"mnh",	HB_TAG('B','A','D','0')},	/* Mono (Democratic Republic of Congo) -> Banda */
 /*{"mni",	HB_TAG('M','N','I',' ')},*/	/* Manipuri */
   {"mnk",	HB_TAG('M','N','D',' ')},	/* Mandinka */
   {"mnk",	HB_TAG('M','N','K',' ')},	/* Mandinka -> Maninka */
-  {"mnp",	HB_TAG('Z','H','S',' ')},	/* Min Bei Chinese -> Chinese Simplified */
+  {"mnp",	HB_TAG('Z','H','S',' ')},	/* Min Bei Chinese -> Chinese, Simplified */
   {"mns",	HB_TAG('M','A','N',' ')},	/* Mansi */
   {"mnw",	HB_TAG('M','O','N',' ')},	/* Mon */
+  {"mnx",	HB_TAG_NONE	       },	/* Manikion != Manx */
   {"mo",	HB_TAG('M','O','L',' ')},	/* Moldavian (retired code) */
+  {"mod",	HB_TAG('C','P','P',' ')},	/* Mobilian -> Creoles */
 /*{"moh",	HB_TAG('M','O','H',' ')},*/	/* Mohawk */
+  {"mok",	HB_TAG_NONE	       },	/* Morori != Moksha */
+  {"mop",	HB_TAG('M','Y','N',' ')},	/* Mopán Maya -> Mayan */
+  {"mor",	HB_TAG_NONE	       },	/* Moro != Moroccan */
 /*{"mos",	HB_TAG('M','O','S',' ')},*/	/* Mossi */
   {"mpe",	HB_TAG('M','A','J',' ')},	/* Majang */
   {"mqg",	HB_TAG('M','L','Y',' ')},	/* Kota Bangun Kutai Malay -> Malay */
@@ -642,9 +935,14 @@
   {"msc",	HB_TAG('M','N','K',' ')},	/* Sankaran Maninka -> Maninka */
   {"msh",	HB_TAG('M','L','G',' ')},	/* Masikoro Malagasy -> Malagasy */
   {"msi",	HB_TAG('M','L','Y',' ')},	/* Sabah Malay -> Malay */
+  {"msi",	HB_TAG('C','P','P',' ')},	/* Sabah Malay -> Creoles */
   {"mt",	HB_TAG('M','T','S',' ')},	/* Maltese */
+  {"mth",	HB_TAG_NONE	       },	/* Munggui != Maithili */
   {"mtr",	HB_TAG('M','A','W',' ')},	/* Mewari -> Marwari */
+  {"mts",	HB_TAG_NONE	       },	/* Yora != Maltese */
+  {"mud",	HB_TAG('C','P','P',' ')},	/* Mednyj Aleut -> Creoles */
   {"mui",	HB_TAG('M','L','Y',' ')},	/* Musi -> Malay */
+  {"mun",	HB_TAG_NONE	       },	/* Munda [family] != Mundari */
   {"mup",	HB_TAG('R','A','J',' ')},	/* Malvi -> Rajasthani */
   {"muq",	HB_TAG('H','M','N',' ')},	/* Eastern Xiangxi Miao -> Hmong */
 /*{"mus",	HB_TAG('M','U','S',' ')},*/	/* Creek -> Muscogee */
@@ -653,49 +951,101 @@
   {"mvf",	HB_TAG('M','N','G',' ')},	/* Peripheral Mongolian -> Mongolian */
   {"mwk",	HB_TAG('M','N','K',' ')},	/* Kita Maninkakan -> Maninka */
 /*{"mwl",	HB_TAG('M','W','L',' ')},*/	/* Mirandese */
+  {"mwq",	HB_TAG('Q','I','N',' ')},	/* Mün Chin -> Chin */
   {"mwr",	HB_TAG('M','A','W',' ')},	/* Marwari [macrolanguage] */
-/*{"mww",	HB_TAG('M','W','W',' ')},*/	/* Hmong Daw */
+  {"mww",	HB_TAG('M','W','W',' ')},	/* Hmong Daw */
+  {"mww",	HB_TAG('H','M','N',' ')},	/* Hmong Daw -> Hmong */
   {"my",	HB_TAG('B','R','M',' ')},	/* Burmese */
   {"mym",	HB_TAG('M','E','N',' ')},	/* Me’en */
 /*{"myn",	HB_TAG('M','Y','N',' ')},*/	/* Mayan [family] */
   {"myq",	HB_TAG('M','N','K',' ')},	/* Forest Maninka (retired code) -> Maninka */
   {"myv",	HB_TAG('E','R','Z',' ')},	/* Erzya */
+  {"mzb",	HB_TAG('B','B','R',' ')},	/* Tumzabt -> Berber */
 /*{"mzn",	HB_TAG('M','Z','N',' ')},*/	/* Mazanderani */
+  {"mzs",	HB_TAG('C','P','P',' ')},	/* Macanese -> Creoles */
   {"na",	HB_TAG('N','A','U',' ')},	/* Nauru -> Nauruan */
-/*{"nag",	HB_TAG('N','A','G',' ')},*/	/* Naga Pidgin -> Naga-Assamese */
+  {"nag",	HB_TAG('N','A','G',' ')},	/* Naga Pidgin -> Naga-Assamese */
+  {"nag",	HB_TAG('C','P','P',' ')},	/* Naga Pidgin -> Creoles */
 /*{"nah",	HB_TAG('N','A','H',' ')},*/	/* Nahuatl [family] */
-  {"nan",	HB_TAG('Z','H','S',' ')},	/* Min Nan Chinese -> Chinese Simplified */
+  {"nan",	HB_TAG('Z','H','S',' ')},	/* Min Nan Chinese -> Chinese, Simplified */
 /*{"nap",	HB_TAG('N','A','P',' ')},*/	/* Neapolitan */
+  {"nas",	HB_TAG_NONE	       },	/* Naasioi != Naskapi */
+  {"naz",	HB_TAG('N','A','H',' ')},	/* Coatepec Nahuatl -> Nahuatl */
   {"nb",	HB_TAG('N','O','R',' ')},	/* Norwegian Bokmål -> Norwegian */
+  {"nch",	HB_TAG('N','A','H',' ')},	/* Central Huasteca Nahuatl -> Nahuatl */
+  {"nci",	HB_TAG('N','A','H',' ')},	/* Classical Nahuatl -> Nahuatl */
+  {"ncj",	HB_TAG('N','A','H',' ')},	/* Northern Puebla Nahuatl -> Nahuatl */
+  {"ncl",	HB_TAG('N','A','H',' ')},	/* Michoacán Nahuatl -> Nahuatl */
+  {"ncr",	HB_TAG_NONE	       },	/* Ncane != N-Cree */
+  {"ncx",	HB_TAG('N','A','H',' ')},	/* Central Puebla Nahuatl -> Nahuatl */
   {"nd",	HB_TAG('N','D','B',' ')},	/* North Ndebele -> Ndebele */
+  {"ndb",	HB_TAG_NONE	       },	/* Kenswei Nsei != Ndebele */
 /*{"ndc",	HB_TAG('N','D','C',' ')},*/	/* Ndau */
+  {"ndg",	HB_TAG_NONE	       },	/* Ndengereko != Ndonga */
 /*{"nds",	HB_TAG('N','D','S',' ')},*/	/* Low Saxon */
   {"ne",	HB_TAG('N','E','P',' ')},	/* Nepali [macrolanguage] */
+  {"nef",	HB_TAG('C','P','P',' ')},	/* Nefamese -> Creoles */
 /*{"new",	HB_TAG('N','E','W',' ')},*/	/* Newari */
   {"ng",	HB_TAG('N','D','G',' ')},	/* Ndonga */
 /*{"nga",	HB_TAG('N','G','A',' ')},*/	/* Ngbaka */
   {"ngl",	HB_TAG('L','M','W',' ')},	/* Lomwe */
+  {"ngm",	HB_TAG('C','P','P',' ')},	/* Ngatik Men's Creole -> Creoles */
   {"ngo",	HB_TAG('S','X','T',' ')},	/* Ngoni -> Sutu */
+  {"ngr",	HB_TAG_NONE	       },	/* Engdewu != Nagari */
+  {"ngu",	HB_TAG('N','A','H',' ')},	/* Guerrero Nahuatl -> Nahuatl */
+  {"nhc",	HB_TAG('N','A','H',' ')},	/* Tabasco Nahuatl -> Nahuatl */
   {"nhd",	HB_TAG('G','U','A',' ')},	/* Chiripá -> Guarani */
+  {"nhe",	HB_TAG('N','A','H',' ')},	/* Eastern Huasteca Nahuatl -> Nahuatl */
+  {"nhg",	HB_TAG('N','A','H',' ')},	/* Tetelcingo Nahuatl -> Nahuatl */
+  {"nhi",	HB_TAG('N','A','H',' ')},	/* Zacatlán-Ahuacatlán-Tepetzintla Nahuatl -> Nahuatl */
+  {"nhk",	HB_TAG('N','A','H',' ')},	/* Isthmus-Cosoleacaque Nahuatl -> Nahuatl */
+  {"nhm",	HB_TAG('N','A','H',' ')},	/* Morelos Nahuatl -> Nahuatl */
+  {"nhn",	HB_TAG('N','A','H',' ')},	/* Central Nahuatl -> Nahuatl */
+  {"nhp",	HB_TAG('N','A','H',' ')},	/* Isthmus-Pajapan Nahuatl -> Nahuatl */
+  {"nhq",	HB_TAG('N','A','H',' ')},	/* Huaxcaleca Nahuatl -> Nahuatl */
+  {"nht",	HB_TAG('N','A','H',' ')},	/* Ometepec Nahuatl -> Nahuatl */
+  {"nhv",	HB_TAG('N','A','H',' ')},	/* Temascaltepec Nahuatl -> Nahuatl */
+  {"nhw",	HB_TAG('N','A','H',' ')},	/* Western Huasteca Nahuatl -> Nahuatl */
+  {"nhx",	HB_TAG('N','A','H',' ')},	/* Isthmus-Mecayapan Nahuatl -> Nahuatl */
+  {"nhy",	HB_TAG('N','A','H',' ')},	/* Northern Oaxaca Nahuatl -> Nahuatl */
+  {"nhz",	HB_TAG('N','A','H',' ')},	/* Santa María La Alta Nahuatl -> Nahuatl */
   {"niq",	HB_TAG('K','A','L',' ')},	/* Nandi -> Kalenjin */
+  {"nis",	HB_TAG_NONE	       },	/* Nimi != Nisi */
 /*{"niu",	HB_TAG('N','I','U',' ')},*/	/* Niuean */
   {"niv",	HB_TAG('G','I','L',' ')},	/* Gilyak */
+  {"njt",	HB_TAG('C','P','P',' ')},	/* Ndyuka-Trio Pidgin -> Creoles */
   {"njz",	HB_TAG('N','I','S',' ')},	/* Nyishi -> Nisi */
+  {"nko",	HB_TAG_NONE	       },	/* Nkonya != N’Ko */
+  {"nkx",	HB_TAG('I','J','O',' ')},	/* Nkoroo -> Ijo */
   {"nl",	HB_TAG('N','L','D',' ')},	/* Dutch */
+  {"nla",	HB_TAG('B','M','L',' ')},	/* Ngombale -> Bamileke */
   {"nle",	HB_TAG('L','U','H',' ')},	/* East Nyala -> Luyia */
+  {"nln",	HB_TAG('N','A','H',' ')},	/* Durango Nahuatl (retired code) -> Nahuatl */
+  {"nlv",	HB_TAG('N','A','H',' ')},	/* Orizaba Nahuatl -> Nahuatl */
   {"nn",	HB_TAG('N','Y','N',' ')},	/* Norwegian Nynorsk (Nynorsk, Norwegian) */
+  {"nn",	HB_TAG('N','O','R',' ')},	/* Norwegian Nynorsk -> Norwegian */
+  {"nnh",	HB_TAG('B','M','L',' ')},	/* Ngiemboon -> Bamileke */
+  {"nnz",	HB_TAG('B','M','L',' ')},	/* Nda'nda' -> Bamileke */
   {"no",	HB_TAG('N','O','R',' ')},	/* Norwegian [macrolanguage] */
   {"nod",	HB_TAG('N','T','A',' ')},	/* Northern Thai -> Northern Tai */
 /*{"noe",	HB_TAG('N','O','E',' ')},*/	/* Nimadi */
 /*{"nog",	HB_TAG('N','O','G',' ')},*/	/* Nogai */
 /*{"nov",	HB_TAG('N','O','V',' ')},*/	/* Novial */
   {"npi",	HB_TAG('N','E','P',' ')},	/* Nepali */
+  {"npl",	HB_TAG('N','A','H',' ')},	/* Southeastern Puebla Nahuatl -> Nahuatl */
   {"nqo",	HB_TAG('N','K','O',' ')},	/* N’Ko */
   {"nr",	HB_TAG('N','D','B',' ')},	/* South Ndebele -> Ndebele */
   {"nsk",	HB_TAG('N','A','S',' ')},	/* Naskapi */
-/*{"nso",	HB_TAG('N','S','O',' ')},*/	/* Pedi -> Sotho, Northern */
+  {"nsm",	HB_TAG_NONE	       },	/* Sumi Naga != Northern Sami */
+/*{"nso",	HB_TAG('N','S','O',' ')},*/	/* Northern Sotho */
+  {"nsu",	HB_TAG('N','A','H',' ')},	/* Sierra Negra Nahuatl -> Nahuatl */
+  {"nto",	HB_TAG_NONE	       },	/* Ntomba != Esperanto */
+  {"nue",	HB_TAG('B','A','D','0')},	/* Ngundu -> Banda */
+  {"nuu",	HB_TAG('B','A','D','0')},	/* Ngbundu -> Banda */
+  {"nuz",	HB_TAG('N','A','H',' ')},	/* Tlamacazapa Nahuatl -> Nahuatl */
   {"nv",	HB_TAG('N','A','V',' ')},	/* Navajo */
   {"nv",	HB_TAG('A','T','H',' ')},	/* Navajo -> Athapaskan */
+  {"nwe",	HB_TAG('B','M','L',' ')},	/* Ngwe -> Bamileke */
   {"ny",	HB_TAG('C','H','I',' ')},	/* Chichewa (Chewa, Nyanja) */
   {"nyd",	HB_TAG('L','U','H',' ')},	/* Nyore -> Luyia */
 /*{"nym",	HB_TAG('N','Y','M',' ')},*/	/* Nyamwezi */
@@ -707,21 +1057,33 @@
   {"ojc",	HB_TAG('O','J','B',' ')},	/* Central Ojibwa -> Ojibway */
   {"ojg",	HB_TAG('O','J','B',' ')},	/* Eastern Ojibwa -> Ojibway */
   {"ojs",	HB_TAG('O','C','R',' ')},	/* Severn Ojibwa -> Oji-Cree */
+  {"ojs",	HB_TAG('O','J','B',' ')},	/* Severn Ojibwa -> Ojibway */
   {"ojw",	HB_TAG('O','J','B',' ')},	/* Western Ojibwa -> Ojibway */
+  {"okd",	HB_TAG('I','J','O',' ')},	/* Okodia -> Ijo */
   {"oki",	HB_TAG('K','A','L',' ')},	/* Okiek -> Kalenjin */
   {"okm",	HB_TAG('K','O','H',' ')},	/* Middle Korean (10th-16th cent.) -> Korean Old Hangul */
+  {"okr",	HB_TAG('I','J','O',' ')},	/* Kirike -> Ijo */
   {"om",	HB_TAG('O','R','O',' ')},	/* Oromo [macrolanguage] */
+  {"onx",	HB_TAG('C','P','P',' ')},	/* Onin Based Pidgin -> Creoles */
+  {"oor",	HB_TAG('C','P','P',' ')},	/* Oorlams -> Creoles */
   {"or",	HB_TAG('O','R','I',' ')},	/* Odia (formerly Oriya) [macrolanguage] */
   {"orc",	HB_TAG('O','R','O',' ')},	/* Orma -> Oromo */
   {"orn",	HB_TAG('M','L','Y',' ')},	/* Orang Kanaq -> Malay */
+  {"oro",	HB_TAG_NONE	       },	/* Orokolo != Oromo */
+  {"orr",	HB_TAG('I','J','O',' ')},	/* Oruma -> Ijo */
   {"ors",	HB_TAG('M','L','Y',' ')},	/* Orang Seletar -> Malay */
   {"ory",	HB_TAG('O','R','I',' ')},	/* Odia (formerly Oriya) */
   {"os",	HB_TAG('O','S','S',' ')},	/* Ossetian */
   {"otw",	HB_TAG('O','J','B',' ')},	/* Ottawa -> Ojibway */
+  {"oua",	HB_TAG('B','B','R',' ')},	/* Tagargrent -> Berber */
   {"pa",	HB_TAG('P','A','N',' ')},	/* Punjabi */
+  {"paa",	HB_TAG_NONE	       },	/* Papuan [family] != Palestinian Aramaic */
 /*{"pag",	HB_TAG('P','A','G',' ')},*/	/* Pangasinan */
+  {"pal",	HB_TAG_NONE	       },	/* Pahlavi != Pali */
 /*{"pam",	HB_TAG('P','A','M',' ')},*/	/* Pampanga -> Pampangan */
   {"pap",	HB_TAG('P','A','P','0')},	/* Papiamento -> Papiamentu */
+  {"pap",	HB_TAG('C','P','P',' ')},	/* Papiamento -> Creoles */
+  {"pas",	HB_TAG_NONE	       },	/* Papasena != Pashto */
 /*{"pau",	HB_TAG('P','A','U',' ')},*/	/* Palauan */
   {"pbt",	HB_TAG('P','A','S',' ')},	/* Southern Pashto -> Pashto */
   {"pbu",	HB_TAG('P','A','S',' ')},	/* Northern Pashto -> Pashto */
@@ -729,83 +1091,146 @@
 /*{"pcd",	HB_TAG('P','C','D',' ')},*/	/* Picard */
   {"pce",	HB_TAG('P','L','G',' ')},	/* Ruching Palaung -> Palaung */
   {"pck",	HB_TAG('Q','I','N',' ')},	/* Paite Chin -> Chin */
+  {"pcm",	HB_TAG('C','P','P',' ')},	/* Nigerian Pidgin -> Creoles */
 /*{"pdc",	HB_TAG('P','D','C',' ')},*/	/* Pennsylvania German */
+  {"pdu",	HB_TAG('K','R','N',' ')},	/* Kayan -> Karen */
+  {"pea",	HB_TAG('C','P','P',' ')},	/* Peranakan Indonesian -> Creoles */
   {"pel",	HB_TAG('M','L','Y',' ')},	/* Pekal -> Malay */
   {"pes",	HB_TAG('F','A','R',' ')},	/* Iranian Persian -> Persian */
+  {"pey",	HB_TAG('C','P','P',' ')},	/* Petjo -> Creoles */
   {"pga",	HB_TAG('A','R','A',' ')},	/* Sudanese Creole Arabic -> Arabic */
+  {"pga",	HB_TAG('C','P','P',' ')},	/* Sudanese Creole Arabic -> Creoles */
 /*{"phk",	HB_TAG('P','H','K',' ')},*/	/* Phake */
   {"pi",	HB_TAG('P','A','L',' ')},	/* Pali */
-/*{"pih",	HB_TAG('P','I','H',' ')},*/	/* Pitcairn-Norfolk -> Norfolk */
+  {"pih",	HB_TAG('P','I','H',' ')},	/* Pitcairn-Norfolk -> Norfolk */
+  {"pih",	HB_TAG('C','P','P',' ')},	/* Pitcairn-Norfolk -> Creoles */
+  {"pil",	HB_TAG_NONE	       },	/* Yom != Filipino */
+  {"pis",	HB_TAG('C','P','P',' ')},	/* Pijin -> Creoles */
+  {"pkh",	HB_TAG('Q','I','N',' ')},	/* Pankhu -> Chin */
   {"pko",	HB_TAG('K','A','L',' ')},	/* Pökoot -> Kalenjin */
   {"pl",	HB_TAG('P','L','K',' ')},	/* Polish */
+  {"plg",	HB_TAG_NONE	       },	/* Pilagá != Palaung */
+  {"plk",	HB_TAG_NONE	       },	/* Kohistani Shina != Polish */
   {"pll",	HB_TAG('P','L','G',' ')},	/* Shwe Palaung -> Palaung */
+  {"pln",	HB_TAG('C','P','P',' ')},	/* Palenquero -> Creoles */
   {"plp",	HB_TAG('P','A','P',' ')},	/* Palpa (retired code) */
   {"plt",	HB_TAG('M','L','G',' ')},	/* Plateau Malagasy -> Malagasy */
+  {"pml",	HB_TAG('C','P','P',' ')},	/* Lingua Franca -> Creoles */
 /*{"pms",	HB_TAG('P','M','S',' ')},*/	/* Piemontese */
+  {"pmy",	HB_TAG('C','P','P',' ')},	/* Papuan Malay -> Creoles */
 /*{"pnb",	HB_TAG('P','N','B',' ')},*/	/* Western Panjabi */
-/*{"poh",	HB_TAG('P','O','H',' ')},*/	/* Poqomchi' -> Pocomchi */
+  {"poc",	HB_TAG('M','Y','N',' ')},	/* Poqomam -> Mayan */
+  {"poh",	HB_TAG('P','O','H',' ')},	/* Poqomchi' -> Pocomchi */
+  {"poh",	HB_TAG('M','Y','N',' ')},	/* Poqomchi' -> Mayan */
 /*{"pon",	HB_TAG('P','O','N',' ')},*/	/* Pohnpeian */
+  {"pov",	HB_TAG('C','P','P',' ')},	/* Upper Guinea Crioulo -> Creoles */
   {"ppa",	HB_TAG('B','A','G',' ')},	/* Pao (retired code) -> Baghelkhandi */
+  {"pre",	HB_TAG('C','P','P',' ')},	/* Principense -> Creoles */
 /*{"pro",	HB_TAG('P','R','O',' ')},*/	/* Old Provençal (to 1500) -> Provençal / Old Provençal */
   {"prs",	HB_TAG('D','R','I',' ')},	/* Dari */
+  {"prs",	HB_TAG('F','A','R',' ')},	/* Dari -> Persian */
   {"ps",	HB_TAG('P','A','S',' ')},	/* Pashto [macrolanguage] */
   {"pse",	HB_TAG('M','L','Y',' ')},	/* Central Malay -> Malay */
   {"pst",	HB_TAG('P','A','S',' ')},	/* Central Pashto -> Pashto */
   {"pt",	HB_TAG('P','T','G',' ')},	/* Portuguese */
-/*{"pwo",	HB_TAG('P','W','O',' ')},*/	/* Pwo Western Karen -> Western Pwo Karen */
+  {"pub",	HB_TAG('Q','I','N',' ')},	/* Purum -> Chin */
+  {"puz",	HB_TAG('Q','I','N',' ')},	/* Purum Naga (retired code) -> Chin */
+  {"pwo",	HB_TAG('P','W','O',' ')},	/* Pwo Western Karen -> Western Pwo Karen */
+  {"pwo",	HB_TAG('K','R','N',' ')},	/* Pwo Western Karen -> Karen */
+  {"pww",	HB_TAG('K','R','N',' ')},	/* Pwo Northern Karen -> Karen */
   {"qu",	HB_TAG('Q','U','Z',' ')},	/* Quechua [macrolanguage] */
   {"qub",	HB_TAG('Q','W','H',' ')},	/* Huallaga Huánuco Quechua -> Quechua (Peru) */
-/*{"quc",	HB_TAG('Q','U','C',' ')},*/	/* K’iche’ */
+  {"qub",	HB_TAG('Q','U','Z',' ')},	/* Huallaga Huánuco Quechua -> Quechua */
+  {"quc",	HB_TAG('Q','U','C',' ')},	/* K’iche’ */
+  {"quc",	HB_TAG('M','Y','N',' ')},	/* K'iche' -> Mayan */
   {"qud",	HB_TAG('Q','V','I',' ')},	/* Calderón Highland Quichua -> Quechua (Ecuador) */
+  {"qud",	HB_TAG('Q','U','Z',' ')},	/* Calderón Highland Quichua -> Quechua */
   {"quf",	HB_TAG('Q','U','Z',' ')},	/* Lambayeque Quechua -> Quechua */
   {"qug",	HB_TAG('Q','V','I',' ')},	/* Chimborazo Highland Quichua -> Quechua (Ecuador) */
-/*{"quh",	HB_TAG('Q','U','H',' ')},*/	/* South Bolivian Quechua -> Quechua (Bolivia) */
+  {"qug",	HB_TAG('Q','U','Z',' ')},	/* Chimborazo Highland Quichua -> Quechua */
+  {"quh",	HB_TAG('Q','U','H',' ')},	/* South Bolivian Quechua -> Quechua (Bolivia) */
+  {"quh",	HB_TAG('Q','U','Z',' ')},	/* South Bolivian Quechua -> Quechua */
   {"quk",	HB_TAG('Q','U','Z',' ')},	/* Chachapoyas Quechua -> Quechua */
+  {"qul",	HB_TAG('Q','U','H',' ')},	/* North Bolivian Quechua -> Quechua (Bolivia) */
   {"qul",	HB_TAG('Q','U','Z',' ')},	/* North Bolivian Quechua -> Quechua */
+  {"qum",	HB_TAG('M','Y','N',' ')},	/* Sipacapense -> Mayan */
   {"qup",	HB_TAG('Q','V','I',' ')},	/* Southern Pastaza Quechua -> Quechua (Ecuador) */
+  {"qup",	HB_TAG('Q','U','Z',' ')},	/* Southern Pastaza Quechua -> Quechua */
   {"qur",	HB_TAG('Q','W','H',' ')},	/* Yanahuanca Pasco Quechua -> Quechua (Peru) */
+  {"qur",	HB_TAG('Q','U','Z',' ')},	/* Yanahuanca Pasco Quechua -> Quechua */
   {"qus",	HB_TAG('Q','U','H',' ')},	/* Santiago del Estero Quichua -> Quechua (Bolivia) */
+  {"qus",	HB_TAG('Q','U','Z',' ')},	/* Santiago del Estero Quichua -> Quechua */
+  {"quv",	HB_TAG('M','Y','N',' ')},	/* Sacapulteco -> Mayan */
   {"quw",	HB_TAG('Q','V','I',' ')},	/* Tena Lowland Quichua -> Quechua (Ecuador) */
+  {"quw",	HB_TAG('Q','U','Z',' ')},	/* Tena Lowland Quichua -> Quechua */
   {"qux",	HB_TAG('Q','W','H',' ')},	/* Yauyos Quechua -> Quechua (Peru) */
+  {"qux",	HB_TAG('Q','U','Z',' ')},	/* Yauyos Quechua -> Quechua */
   {"quy",	HB_TAG('Q','U','Z',' ')},	/* Ayacucho Quechua -> Quechua */
 /*{"quz",	HB_TAG('Q','U','Z',' ')},*/	/* Cusco Quechua -> Quechua */
   {"qva",	HB_TAG('Q','W','H',' ')},	/* Ambo-Pasco Quechua -> Quechua (Peru) */
+  {"qva",	HB_TAG('Q','U','Z',' ')},	/* Ambo-Pasco Quechua -> Quechua */
   {"qvc",	HB_TAG('Q','U','Z',' ')},	/* Cajamarca Quechua -> Quechua */
   {"qve",	HB_TAG('Q','U','Z',' ')},	/* Eastern Apurímac Quechua -> Quechua */
   {"qvh",	HB_TAG('Q','W','H',' ')},	/* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */
-/*{"qvi",	HB_TAG('Q','V','I',' ')},*/	/* Imbabura Highland Quichua -> Quechua (Ecuador) */
+  {"qvh",	HB_TAG('Q','U','Z',' ')},	/* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua */
+  {"qvi",	HB_TAG('Q','V','I',' ')},	/* Imbabura Highland Quichua -> Quechua (Ecuador) */
+  {"qvi",	HB_TAG('Q','U','Z',' ')},	/* Imbabura Highland Quichua -> Quechua */
   {"qvj",	HB_TAG('Q','V','I',' ')},	/* Loja Highland Quichua -> Quechua (Ecuador) */
+  {"qvj",	HB_TAG('Q','U','Z',' ')},	/* Loja Highland Quichua -> Quechua */
   {"qvl",	HB_TAG('Q','W','H',' ')},	/* Cajatambo North Lima Quechua -> Quechua (Peru) */
+  {"qvl",	HB_TAG('Q','U','Z',' ')},	/* Cajatambo North Lima Quechua -> Quechua */
   {"qvm",	HB_TAG('Q','W','H',' ')},	/* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */
+  {"qvm",	HB_TAG('Q','U','Z',' ')},	/* Margos-Yarowilca-Lauricocha Quechua -> Quechua */
   {"qvn",	HB_TAG('Q','W','H',' ')},	/* North Junín Quechua -> Quechua (Peru) */
+  {"qvn",	HB_TAG('Q','U','Z',' ')},	/* North Junín Quechua -> Quechua */
   {"qvo",	HB_TAG('Q','V','I',' ')},	/* Napo Lowland Quechua -> Quechua (Ecuador) */
+  {"qvo",	HB_TAG('Q','U','Z',' ')},	/* Napo Lowland Quechua -> Quechua */
   {"qvp",	HB_TAG('Q','W','H',' ')},	/* Pacaraos Quechua -> Quechua (Peru) */
+  {"qvp",	HB_TAG('Q','U','Z',' ')},	/* Pacaraos Quechua -> Quechua */
   {"qvs",	HB_TAG('Q','U','Z',' ')},	/* San Martín Quechua -> Quechua */
   {"qvw",	HB_TAG('Q','W','H',' ')},	/* Huaylla Wanca Quechua -> Quechua (Peru) */
+  {"qvw",	HB_TAG('Q','U','Z',' ')},	/* Huaylla Wanca Quechua -> Quechua */
   {"qvz",	HB_TAG('Q','V','I',' ')},	/* Northern Pastaza Quichua -> Quechua (Ecuador) */
+  {"qvz",	HB_TAG('Q','U','Z',' ')},	/* Northern Pastaza Quichua -> Quechua */
   {"qwa",	HB_TAG('Q','W','H',' ')},	/* Corongo Ancash Quechua -> Quechua (Peru) */
+  {"qwa",	HB_TAG('Q','U','Z',' ')},	/* Corongo Ancash Quechua -> Quechua */
   {"qwc",	HB_TAG('Q','U','Z',' ')},	/* Classical Quechua -> Quechua */
-/*{"qwh",	HB_TAG('Q','W','H',' ')},*/	/* Huaylas Ancash Quechua -> Quechua (Peru) */
+  {"qwh",	HB_TAG('Q','W','H',' ')},	/* Huaylas Ancash Quechua -> Quechua (Peru) */
+  {"qwh",	HB_TAG('Q','U','Z',' ')},	/* Huaylas Ancash Quechua -> Quechua */
   {"qws",	HB_TAG('Q','W','H',' ')},	/* Sihuas Ancash Quechua -> Quechua (Peru) */
+  {"qws",	HB_TAG('Q','U','Z',' ')},	/* Sihuas Ancash Quechua -> Quechua */
+  {"qwt",	HB_TAG('A','T','H',' ')},	/* Kwalhioqua-Tlatskanai -> Athapaskan */
   {"qxa",	HB_TAG('Q','W','H',' ')},	/* Chiquián Ancash Quechua -> Quechua (Peru) */
+  {"qxa",	HB_TAG('Q','U','Z',' ')},	/* Chiquián Ancash Quechua -> Quechua */
   {"qxc",	HB_TAG('Q','W','H',' ')},	/* Chincha Quechua -> Quechua (Peru) */
+  {"qxc",	HB_TAG('Q','U','Z',' ')},	/* Chincha Quechua -> Quechua */
   {"qxh",	HB_TAG('Q','W','H',' ')},	/* Panao Huánuco Quechua -> Quechua (Peru) */
+  {"qxh",	HB_TAG('Q','U','Z',' ')},	/* Panao Huánuco Quechua -> Quechua */
   {"qxl",	HB_TAG('Q','V','I',' ')},	/* Salasaca Highland Quichua -> Quechua (Ecuador) */
+  {"qxl",	HB_TAG('Q','U','Z',' ')},	/* Salasaca Highland Quichua -> Quechua */
   {"qxn",	HB_TAG('Q','W','H',' ')},	/* Northern Conchucos Ancash Quechua -> Quechua (Peru) */
+  {"qxn",	HB_TAG('Q','U','Z',' ')},	/* Northern Conchucos Ancash Quechua -> Quechua */
   {"qxo",	HB_TAG('Q','W','H',' ')},	/* Southern Conchucos Ancash Quechua -> Quechua (Peru) */
+  {"qxo",	HB_TAG('Q','U','Z',' ')},	/* Southern Conchucos Ancash Quechua -> Quechua */
   {"qxp",	HB_TAG('Q','U','Z',' ')},	/* Puno Quechua -> Quechua */
   {"qxr",	HB_TAG('Q','V','I',' ')},	/* Cañar Highland Quichua -> Quechua (Ecuador) */
+  {"qxr",	HB_TAG('Q','U','Z',' ')},	/* Cañar Highland Quichua -> Quechua */
   {"qxt",	HB_TAG('Q','W','H',' ')},	/* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */
+  {"qxt",	HB_TAG('Q','U','Z',' ')},	/* Santa Ana de Tusi Pasco Quechua -> Quechua */
   {"qxu",	HB_TAG('Q','U','Z',' ')},	/* Arequipa-La Unión Quechua -> Quechua */
   {"qxw",	HB_TAG('Q','W','H',' ')},	/* Jauja Wanca Quechua -> Quechua (Peru) */
+  {"qxw",	HB_TAG('Q','U','Z',' ')},	/* Jauja Wanca Quechua -> Quechua */
   {"rag",	HB_TAG('L','U','H',' ')},	/* Logooli -> Luyia */
 /*{"raj",	HB_TAG('R','A','J',' ')},*/	/* Rajasthani [macrolanguage] */
+  {"ral",	HB_TAG('Q','I','N',' ')},	/* Ralte -> Chin */
 /*{"rar",	HB_TAG('R','A','R',' ')},*/	/* Rarotongan */
   {"rbb",	HB_TAG('P','L','G',' ')},	/* Rumai Palaung -> Palaung */
   {"rbl",	HB_TAG('B','I','K',' ')},	/* Miraya Bikol -> Bikol */
+  {"rcf",	HB_TAG('C','P','P',' ')},	/* Réunion Creole French -> Creoles */
 /*{"rej",	HB_TAG('R','E','J',' ')},*/	/* Rejang */
 /*{"ria",	HB_TAG('R','I','A',' ')},*/	/* Riang (India) */
-/*{"rif",	HB_TAG('R','I','F',' ')},*/	/* Tarifit */
+  {"rif",	HB_TAG('R','I','F',' ')},	/* Tarifit */
+  {"rif",	HB_TAG('B','B','R',' ')},	/* Tarifit -> Berber */
 /*{"rit",	HB_TAG('R','I','T',' ')},*/	/* Ritharrngu -> Ritarungo */
   {"rki",	HB_TAG('A','R','K',' ')},	/* Rakhine */
 /*{"rkw",	HB_TAG('R','K','W',' ')},*/	/* Arakwal */
@@ -815,13 +1240,16 @@
   {"rml",	HB_TAG('R','O','Y',' ')},	/* Baltic Romani -> Romany */
   {"rmn",	HB_TAG('R','O','Y',' ')},	/* Balkan Romani -> Romany */
   {"rmo",	HB_TAG('R','O','Y',' ')},	/* Sinte Romani -> Romany */
+  {"rms",	HB_TAG_NONE	       },	/* Romanian Sign Language != Romansh */
   {"rmw",	HB_TAG('R','O','Y',' ')},	/* Welsh Romani -> Romany */
-/*{"rmy",	HB_TAG('R','M','Y',' ')},*/	/* Vlax Romani */
+  {"rmy",	HB_TAG('R','M','Y',' ')},	/* Vlax Romani */
+  {"rmy",	HB_TAG('R','O','Y',' ')},	/* Vlax Romani -> Romany */
   {"rmz",	HB_TAG('A','R','K',' ')},	/* Marma -> Rakhine */
   {"rn",	HB_TAG('R','U','N',' ')},	/* Rundi */
-  {"rnl",	HB_TAG('H','A','L',' ')},	/* Ranglong -> Halam (Falam Chin) */
   {"ro",	HB_TAG('R','O','M',' ')},	/* Romanian */
   {"rom",	HB_TAG('R','O','Y',' ')},	/* Romany [macrolanguage] */
+  {"rop",	HB_TAG('C','P','P',' ')},	/* Kriol -> Creoles */
+  {"rtc",	HB_TAG('Q','I','N',' ')},	/* Rungtu Chin -> Chin */
 /*{"rtm",	HB_TAG('R','T','M',' ')},*/	/* Rotuman */
   {"ru",	HB_TAG('R','U','S',' ')},	/* Russian */
   {"rue",	HB_TAG('R','S','Y',' ')},	/* Rusyn */
@@ -829,11 +1257,16 @@
   {"rw",	HB_TAG('R','U','A',' ')},	/* Kinyarwanda */
   {"rwr",	HB_TAG('M','A','W',' ')},	/* Marwari (India) */
   {"sa",	HB_TAG('S','A','N',' ')},	/* Sanskrit */
+  {"sad",	HB_TAG_NONE	       },	/* Sandawe != Sadri */
   {"sah",	HB_TAG('Y','A','K',' ')},	/* Yakut -> Sakha */
   {"sam",	HB_TAG('P','A','A',' ')},	/* Samaritan Aramaic -> Palestinian Aramaic */
 /*{"sas",	HB_TAG('S','A','S',' ')},*/	/* Sasak */
 /*{"sat",	HB_TAG('S','A','T',' ')},*/	/* Santali */
+  {"say",	HB_TAG_NONE	       },	/* Saya != Sayisi */
   {"sc",	HB_TAG('S','R','D',' ')},	/* Sardinian [macrolanguage] */
+  {"scf",	HB_TAG('C','P','P',' ')},	/* San Miguel Creole French -> Creoles */
+  {"sch",	HB_TAG('Q','I','N',' ')},	/* Sakachep -> Chin */
+  {"sci",	HB_TAG('C','P','P',' ')},	/* Sri Lankan Creole Malay -> Creoles */
   {"sck",	HB_TAG('S','A','D',' ')},	/* Sadri */
 /*{"scn",	HB_TAG('S','C','N',' ')},*/	/* Sicilian */
 /*{"sco",	HB_TAG('S','C','O',' ')},*/	/* Scots */
@@ -844,6 +1277,7 @@
   {"sdc",	HB_TAG('S','R','D',' ')},	/* Sassarese Sardinian -> Sardinian */
   {"sdh",	HB_TAG('K','U','R',' ')},	/* Southern Kurdish -> Kurdish */
   {"sdn",	HB_TAG('S','R','D',' ')},	/* Gallurese Sardinian -> Sardinian */
+  {"sds",	HB_TAG('B','B','R',' ')},	/* Sened -> Berber */
   {"se",	HB_TAG('N','S','M',' ')},	/* Northern Sami */
   {"seh",	HB_TAG('S','N','A',' ')},	/* Sena */
   {"sek",	HB_TAG('A','T','H',' ')},	/* Sekani -> Athapaskan */
@@ -853,50 +1287,78 @@
   {"sg",	HB_TAG('S','G','O',' ')},	/* Sango */
 /*{"sga",	HB_TAG('S','G','A',' ')},*/	/* Old Irish (to 900) */
   {"sgc",	HB_TAG('K','A','L',' ')},	/* Kipsigis -> Kalenjin */
+  {"sgo",	HB_TAG_NONE	       },	/* Songa (retired code) != Sango */
 /*{"sgs",	HB_TAG('S','G','S',' ')},*/	/* Samogitian */
   {"sgw",	HB_TAG('C','H','G',' ')},	/* Sebat Bet Gurage -> Chaha Gurage */
-/*{"shi",	HB_TAG('S','H','I',' ')},*/	/* Tachelhit */
+  {"shi",	HB_TAG('S','H','I',' ')},	/* Tachelhit */
+  {"shi",	HB_TAG('B','B','R',' ')},	/* Tachelhit -> Berber */
+  {"shl",	HB_TAG('Q','I','N',' ')},	/* Shendu -> Chin */
 /*{"shn",	HB_TAG('S','H','N',' ')},*/	/* Shan */
   {"shu",	HB_TAG('A','R','A',' ')},	/* Chadian Arabic -> Arabic */
+  {"shy",	HB_TAG('B','B','R',' ')},	/* Tachawit -> Berber */
   {"si",	HB_TAG('S','N','H',' ')},	/* Sinhala (Sinhalese) */
+  {"sib",	HB_TAG_NONE	       },	/* Sebop != Sibe */
 /*{"sid",	HB_TAG('S','I','D',' ')},*/	/* Sidamo */
+  {"sig",	HB_TAG_NONE	       },	/* Paasaal != Silte Gurage */
+  {"siz",	HB_TAG('B','B','R',' ')},	/* Siwi -> Berber */
   {"sjd",	HB_TAG('K','S','M',' ')},	/* Kildin Sami */
   {"sjo",	HB_TAG('S','I','B',' ')},	/* Xibe -> Sibe */
+  {"sjs",	HB_TAG('B','B','R',' ')},	/* Senhaja De Srair -> Berber */
   {"sk",	HB_TAG('S','K','Y',' ')},	/* Slovak */
   {"skg",	HB_TAG('M','L','G',' ')},	/* Sakalava Malagasy -> Malagasy */
   {"skr",	HB_TAG('S','R','K',' ')},	/* Saraiki */
+  {"sks",	HB_TAG_NONE	       },	/* Maia != Skolt Sami */
+  {"skw",	HB_TAG('C','P','P',' ')},	/* Skepi Creole Dutch -> Creoles */
+  {"sky",	HB_TAG_NONE	       },	/* Sikaiana != Slovak */
   {"sl",	HB_TAG('S','L','V',' ')},	/* Slovenian */
+  {"sla",	HB_TAG_NONE	       },	/* Slavic [family] != Slavey */
   {"sm",	HB_TAG('S','M','O',' ')},	/* Samoan */
   {"sma",	HB_TAG('S','S','M',' ')},	/* Southern Sami */
   {"smj",	HB_TAG('L','S','M',' ')},	/* Lule Sami */
+  {"sml",	HB_TAG_NONE	       },	/* Central Sama != Somali */
   {"smn",	HB_TAG('I','S','M',' ')},	/* Inari Sami */
   {"sms",	HB_TAG('S','K','S',' ')},	/* Skolt Sami */
+  {"smt",	HB_TAG('Q','I','N',' ')},	/* Simte -> Chin */
   {"sn",	HB_TAG('S','N','A','0')},	/* Shona */
+  {"snh",	HB_TAG_NONE	       },	/* Shinabo (retired code) != Sinhala (Sinhalese) */
 /*{"snk",	HB_TAG('S','N','K',' ')},*/	/* Soninke */
   {"so",	HB_TAG('S','M','L',' ')},	/* Somali */
+  {"sog",	HB_TAG_NONE	       },	/* Sogdian != Sodo Gurage */
 /*{"sop",	HB_TAG('S','O','P',' ')},*/	/* Songe */
   {"spv",	HB_TAG('O','R','I',' ')},	/* Sambalpuri -> Odia (formerly Oriya) */
   {"spy",	HB_TAG('K','A','L',' ')},	/* Sabaot -> Kalenjin */
   {"sq",	HB_TAG('S','Q','I',' ')},	/* Albanian [macrolanguage] */
   {"sr",	HB_TAG('S','R','B',' ')},	/* Serbian */
+  {"srb",	HB_TAG_NONE	       },	/* Sora != Serbian */
   {"src",	HB_TAG('S','R','D',' ')},	/* Logudorese Sardinian -> Sardinian */
+  {"srk",	HB_TAG_NONE	       },	/* Serudung Murut != Saraiki */
+  {"srm",	HB_TAG('C','P','P',' ')},	/* Saramaccan -> Creoles */
+  {"srn",	HB_TAG('C','P','P',' ')},	/* Sranan Tongo -> Creoles */
   {"sro",	HB_TAG('S','R','D',' ')},	/* Campidanese Sardinian -> Sardinian */
 /*{"srr",	HB_TAG('S','R','R',' ')},*/	/* Serer */
   {"srs",	HB_TAG('A','T','H',' ')},	/* Sarsi -> Athapaskan */
   {"ss",	HB_TAG('S','W','Z',' ')},	/* Swati */
   {"ssh",	HB_TAG('A','R','A',' ')},	/* Shihhi Arabic -> Arabic */
-  {"st",	HB_TAG('S','O','T',' ')},	/* Southern Sotho -> Sotho, Southern */
+  {"ssl",	HB_TAG_NONE	       },	/* Western Sisaala != South Slavey */
+  {"ssm",	HB_TAG_NONE	       },	/* Semnam != Southern Sami */
+  {"st",	HB_TAG('S','O','T',' ')},	/* Southern Sotho */
+  {"sta",	HB_TAG('C','P','P',' ')},	/* Settla -> Creoles */
 /*{"stq",	HB_TAG('S','T','Q',' ')},*/	/* Saterfriesisch -> Saterland Frisian */
   {"stv",	HB_TAG('S','I','G',' ')},	/* Silt'e -> Silte Gurage */
   {"su",	HB_TAG('S','U','N',' ')},	/* Sundanese */
 /*{"suk",	HB_TAG('S','U','K',' ')},*/	/* Sukuma */
   {"suq",	HB_TAG('S','U','R',' ')},	/* Suri */
+  {"sur",	HB_TAG_NONE	       },	/* Mwaghavul != Suri */
   {"sv",	HB_TAG('S','V','E',' ')},	/* Swedish */
 /*{"sva",	HB_TAG('S','V','A',' ')},*/	/* Svan */
+  {"svc",	HB_TAG('C','P','P',' ')},	/* Vincentian Creole English -> Creoles */
+  {"sve",	HB_TAG_NONE	       },	/* Serili != Swedish */
   {"sw",	HB_TAG('S','W','K',' ')},	/* Swahili [macrolanguage] */
   {"swb",	HB_TAG('C','M','R',' ')},	/* Maore Comorian -> Comorian */
   {"swc",	HB_TAG('S','W','K',' ')},	/* Congo Swahili -> Swahili */
   {"swh",	HB_TAG('S','W','K',' ')},	/* Swahili */
+  {"swk",	HB_TAG_NONE	       },	/* Malawi Sena != Swahili */
+  {"swn",	HB_TAG('B','B','R',' ')},	/* Sawknah -> Berber */
   {"swv",	HB_TAG('M','A','W',' ')},	/* Shekhawati -> Marwari */
 /*{"sxu",	HB_TAG('S','X','U',' ')},*/	/* Upper Saxon */
   {"syc",	HB_TAG('S','Y','R',' ')},	/* Classical Syriac -> Syriac */
@@ -906,11 +1368,16 @@
   {"ta",	HB_TAG('T','A','M',' ')},	/* Tamil */
   {"taa",	HB_TAG('A','T','H',' ')},	/* Lower Tanana -> Athapaskan */
 /*{"tab",	HB_TAG('T','A','B',' ')},*/	/* Tabassaran -> Tabasaran */
+  {"taj",	HB_TAG_NONE	       },	/* Eastern Tamang != Tajiki */
   {"taq",	HB_TAG('T','M','H',' ')},	/* Tamasheq -> Tamashek */
+  {"taq",	HB_TAG('B','B','R',' ')},	/* Tamasheq -> Berber */
+  {"tas",	HB_TAG('C','P','P',' ')},	/* Tay Boi -> Creoles */
   {"tau",	HB_TAG('A','T','H',' ')},	/* Upper Tanana -> Athapaskan */
   {"tcb",	HB_TAG('A','T','H',' ')},	/* Tanacross -> Athapaskan */
   {"tce",	HB_TAG('A','T','H',' ')},	/* Southern Tutchone -> Athapaskan */
+  {"tch",	HB_TAG('C','P','P',' ')},	/* Turks And Caicos Creole English -> Creoles */
   {"tcp",	HB_TAG('Q','I','N',' ')},	/* Tawr Chin -> Chin */
+  {"tcs",	HB_TAG('C','P','P',' ')},	/* Torres Strait Creole -> Creoles */
   {"tcy",	HB_TAG('T','U','L',' ')},	/* Tulu -> Tumbuka */
   {"tcz",	HB_TAG('Q','I','N',' ')},	/* Thado Chin -> Chin */
 /*{"tdd",	HB_TAG('T','D','D',' ')},*/	/* Tai Nüa -> Dehong Dai */
@@ -919,41 +1386,70 @@
   {"tec",	HB_TAG('K','A','L',' ')},	/* Terik -> Kalenjin */
   {"tem",	HB_TAG('T','M','N',' ')},	/* Timne -> Temne */
 /*{"tet",	HB_TAG('T','E','T',' ')},*/	/* Tetum */
+  {"tez",	HB_TAG('B','B','R',' ')},	/* Tetserret -> Berber */
   {"tfn",	HB_TAG('A','T','H',' ')},	/* Tanaina -> Athapaskan */
   {"tg",	HB_TAG('T','A','J',' ')},	/* Tajik -> Tajiki */
+  {"tgh",	HB_TAG('C','P','P',' ')},	/* Tobagonian Creole English -> Creoles */
   {"tgj",	HB_TAG('N','I','S',' ')},	/* Tagin -> Nisi */
+  {"tgn",	HB_TAG_NONE	       },	/* Tandaganon != Tongan */
+  {"tgr",	HB_TAG_NONE	       },	/* Tareng != Tigre */
   {"tgx",	HB_TAG('A','T','H',' ')},	/* Tagish -> Athapaskan */
+  {"tgy",	HB_TAG_NONE	       },	/* Togoyo != Tigrinya */
   {"th",	HB_TAG('T','H','A',' ')},	/* Thai */
   {"tht",	HB_TAG('A','T','H',' ')},	/* Tahltan -> Athapaskan */
   {"thv",	HB_TAG('T','M','H',' ')},	/* Tahaggart Tamahaq -> Tamashek */
+  {"thv",	HB_TAG('B','B','R',' ')},	/* Tahaggart Tamahaq -> Berber */
   {"thz",	HB_TAG('T','M','H',' ')},	/* Tayart Tamajeq -> Tamashek */
+  {"thz",	HB_TAG('B','B','R',' ')},	/* Tayart Tamajeq -> Berber */
   {"ti",	HB_TAG('T','G','Y',' ')},	/* Tigrinya */
+  {"tia",	HB_TAG('B','B','R',' ')},	/* Tidikelt Tamazight -> Berber */
   {"tig",	HB_TAG('T','G','R',' ')},	/* Tigre */
 /*{"tiv",	HB_TAG('T','I','V',' ')},*/	/* Tiv */
+  {"tjo",	HB_TAG('B','B','R',' ')},	/* Temacine Tamazight -> Berber */
   {"tk",	HB_TAG('T','K','M',' ')},	/* Turkmen */
   {"tkg",	HB_TAG('M','L','G',' ')},	/* Tesaka Malagasy -> Malagasy */
+  {"tkm",	HB_TAG_NONE	       },	/* Takelma != Turkmen */
   {"tl",	HB_TAG('T','G','L',' ')},	/* Tagalog */
-/*{"tmh",	HB_TAG('T','M','H',' ')},*/	/* Tamashek [macrolanguage] */
+  {"tmg",	HB_TAG('C','P','P',' ')},	/* Ternateño -> Creoles */
+  {"tmh",	HB_TAG('T','M','H',' ')},	/* Tamashek [macrolanguage] */
+  {"tmh",	HB_TAG('B','B','R',' ')},	/* Tamashek [macrolanguage] -> Berber */
+  {"tmn",	HB_TAG_NONE	       },	/* Taman (Indonesia) != Temne */
   {"tmw",	HB_TAG('M','L','Y',' ')},	/* Temuan -> Malay */
   {"tn",	HB_TAG('T','N','A',' ')},	/* Tswana */
+  {"tna",	HB_TAG_NONE	       },	/* Tacana != Tswana */
+  {"tne",	HB_TAG_NONE	       },	/* Tinoc Kallahan (retired code) != Tundra Enets */
   {"tnf",	HB_TAG('D','R','I',' ')},	/* Tangshewi (retired code) -> Dari */
+  {"tnf",	HB_TAG('F','A','R',' ')},	/* Tangshewi (retired code) -> Persian */
+  {"tng",	HB_TAG_NONE	       },	/* Tobanga != Tonga */
   {"to",	HB_TAG('T','G','N',' ')},	/* Tonga (Tonga Islands) -> Tongan */
   {"tod",	HB_TAG('T','O','D','0')},	/* Toma */
   {"toi",	HB_TAG('T','N','G',' ')},	/* Tonga (Zambia) */
+  {"toj",	HB_TAG('M','Y','N',' ')},	/* Tojolabal -> Mayan */
   {"tol",	HB_TAG('A','T','H',' ')},	/* Tolowa -> Athapaskan */
-/*{"tpi",	HB_TAG('T','P','I',' ')},*/	/* Tok Pisin */
+  {"tor",	HB_TAG('B','A','D','0')},	/* Togbo-Vara Banda -> Banda */
+  {"tpi",	HB_TAG('T','P','I',' ')},	/* Tok Pisin */
+  {"tpi",	HB_TAG('C','P','P',' ')},	/* Tok Pisin -> Creoles */
   {"tr",	HB_TAG('T','R','K',' ')},	/* Turkish */
+  {"trf",	HB_TAG('C','P','P',' ')},	/* Trinidadian Creole English -> Creoles */
+  {"trk",	HB_TAG_NONE	       },	/* Turkic [family] != Turkish */
   {"tru",	HB_TAG('T','U','A',' ')},	/* Turoyo -> Turoyo Aramaic */
   {"tru",	HB_TAG('S','Y','R',' ')},	/* Turoyo -> Syriac */
   {"ts",	HB_TAG('T','S','G',' ')},	/* Tsonga */
+  {"tsg",	HB_TAG_NONE	       },	/* Tausug != Tsonga */
 /*{"tsj",	HB_TAG('T','S','J',' ')},*/	/* Tshangla */
   {"tt",	HB_TAG('T','A','T',' ')},	/* Tatar */
+  {"ttc",	HB_TAG('M','Y','N',' ')},	/* Tektiteko -> Mayan */
   {"ttm",	HB_TAG('A','T','H',' ')},	/* Northern Tutchone -> Athapaskan */
   {"ttq",	HB_TAG('T','M','H',' ')},	/* Tawallammat Tamajaq -> Tamashek */
+  {"ttq",	HB_TAG('B','B','R',' ')},	/* Tawallammat Tamajaq -> Berber */
+  {"tua",	HB_TAG_NONE	       },	/* Wiarumus != Turoyo Aramaic */
+  {"tul",	HB_TAG_NONE	       },	/* Tula != Tumbuka */
 /*{"tum",	HB_TAG('T','U','M',' ')},*/	/* Tumbuka -> Tulu */
   {"tuu",	HB_TAG('A','T','H',' ')},	/* Tututni -> Athapaskan */
+  {"tuv",	HB_TAG_NONE	       },	/* Turkana != Tuvin */
   {"tuy",	HB_TAG('K','A','L',' ')},	/* Tugen -> Kalenjin */
 /*{"tvl",	HB_TAG('T','V','L',' ')},*/	/* Tuvalu */
+  {"tvy",	HB_TAG('C','P','P',' ')},	/* Timor Pidgin -> Creoles */
   {"tw",	HB_TAG('T','W','I',' ')},	/* Twi */
   {"tw",	HB_TAG('A','K','A',' ')},	/* Twi -> Akan */
   {"txc",	HB_TAG('A','T','H',' ')},	/* Tsetsaut -> Athapaskan */
@@ -961,33 +1457,48 @@
   {"ty",	HB_TAG('T','H','T',' ')},	/* Tahitian */
   {"tyv",	HB_TAG('T','U','V',' ')},	/* Tuvinian -> Tuvin */
 /*{"tyz",	HB_TAG('T','Y','Z',' ')},*/	/* Tày */
-/*{"tzm",	HB_TAG('T','Z','M',' ')},*/	/* Central Atlas Tamazight -> Tamazight */
-/*{"tzo",	HB_TAG('T','Z','O',' ')},*/	/* Tzotzil */
+  {"tzh",	HB_TAG('M','Y','N',' ')},	/* Tzeltal -> Mayan */
+  {"tzj",	HB_TAG('M','Y','N',' ')},	/* Tz'utujil -> Mayan */
+  {"tzm",	HB_TAG('T','Z','M',' ')},	/* Central Atlas Tamazight -> Tamazight */
+  {"tzm",	HB_TAG('B','B','R',' ')},	/* Central Atlas Tamazight -> Berber */
+  {"tzo",	HB_TAG('T','Z','O',' ')},	/* Tzotzil */
+  {"tzo",	HB_TAG('M','Y','N',' ')},	/* Tzotzil -> Mayan */
   {"ubl",	HB_TAG('B','I','K',' ')},	/* Buhi'non Bikol -> Bikol */
 /*{"udm",	HB_TAG('U','D','M',' ')},*/	/* Udmurt */
   {"ug",	HB_TAG('U','Y','G',' ')},	/* Uyghur */
   {"uk",	HB_TAG('U','K','R',' ')},	/* Ukrainian */
   {"uki",	HB_TAG('K','U','I',' ')},	/* Kui (India) */
+  {"uln",	HB_TAG('C','P','P',' ')},	/* Unserdeutsch -> Creoles */
 /*{"umb",	HB_TAG('U','M','B',' ')},*/	/* Umbundu */
   {"unr",	HB_TAG('M','U','N',' ')},	/* Mundari */
   {"ur",	HB_TAG('U','R','D',' ')},	/* Urdu */
   {"urk",	HB_TAG('M','L','Y',' ')},	/* Urak Lawoi' -> Malay */
+  {"usp",	HB_TAG('M','Y','N',' ')},	/* Uspanteco -> Mayan */
   {"uz",	HB_TAG('U','Z','B',' ')},	/* Uzbek [macrolanguage] */
   {"uzn",	HB_TAG('U','Z','B',' ')},	/* Northern Uzbek -> Uzbek */
   {"uzs",	HB_TAG('U','Z','B',' ')},	/* Southern Uzbek -> Uzbek */
+  {"vap",	HB_TAG('Q','I','N',' ')},	/* Vaiphei -> Chin */
   {"ve",	HB_TAG('V','E','N',' ')},	/* Venda */
 /*{"vec",	HB_TAG('V','E','C',' ')},*/	/* Venetian */
   {"vi",	HB_TAG('V','I','T',' ')},	/* Vietnamese */
+  {"vic",	HB_TAG('C','P','P',' ')},	/* Virgin Islands Creole English -> Creoles */
+  {"vit",	HB_TAG_NONE	       },	/* Viti != Vietnamese */
   {"vkk",	HB_TAG('M','L','Y',' ')},	/* Kaur -> Malay */
+  {"vkp",	HB_TAG('C','P','P',' ')},	/* Korlai Creole Portuguese -> Creoles */
   {"vkt",	HB_TAG('M','L','Y',' ')},	/* Tenggarong Kutai Malay -> Malay */
   {"vls",	HB_TAG('F','L','E',' ')},	/* Vlaams -> Dutch (Flemish) */
   {"vmw",	HB_TAG('M','A','K',' ')},	/* Makhuwa */
   {"vo",	HB_TAG('V','O','L',' ')},	/* Volapük */
 /*{"vro",	HB_TAG('V','R','O',' ')},*/	/* Võro */
   {"wa",	HB_TAG('W','L','N',' ')},	/* Walloon */
+  {"wag",	HB_TAG_NONE	       },	/* Wa'ema != Wagdi */
 /*{"war",	HB_TAG('W','A','R',' ')},*/	/* Waray (Philippines) -> Waray-Waray */
   {"wbm",	HB_TAG('W','A',' ',' ')},	/* Wa */
   {"wbr",	HB_TAG('W','A','G',' ')},	/* Wagdi */
+  {"wbr",	HB_TAG('R','A','J',' ')},	/* Wagdi -> Rajasthani */
+  {"wea",	HB_TAG('K','R','N',' ')},	/* Wewaw -> Karen */
+  {"wes",	HB_TAG('C','P','P',' ')},	/* Cameroon Pidgin -> Creoles */
+  {"weu",	HB_TAG('Q','I','N',' ')},	/* Rawngtu Chin -> Chin */
   {"wlc",	HB_TAG('C','M','R',' ')},	/* Mwali Comorian -> Comorian */
   {"wle",	HB_TAG('S','I','G',' ')},	/* Wolane -> Silte Gurage */
   {"wlk",	HB_TAG('A','T','H',' ')},	/* Wailaki -> Athapaskan */
@@ -996,45 +1507,56 @@
   {"wry",	HB_TAG('M','A','W',' ')},	/* Merwari -> Marwari */
   {"wsg",	HB_TAG('G','O','N',' ')},	/* Adilabad Gondi -> Gondi */
 /*{"wtm",	HB_TAG('W','T','M',' ')},*/	/* Mewati */
-  {"wuu",	HB_TAG('Z','H','S',' ')},	/* Wu Chinese -> Chinese Simplified */
+  {"wuu",	HB_TAG('Z','H','S',' ')},	/* Wu Chinese -> Chinese, Simplified */
   {"xal",	HB_TAG('K','L','M',' ')},	/* Kalmyk */
   {"xal",	HB_TAG('T','O','D',' ')},	/* Kalmyk -> Todo */
   {"xan",	HB_TAG('S','E','K',' ')},	/* Xamtanga -> Sekota */
+  {"xbd",	HB_TAG_NONE	       },	/* Bindal != Lü */
   {"xh",	HB_TAG('X','H','S',' ')},	/* Xhosa */
 /*{"xjb",	HB_TAG('X','J','B',' ')},*/	/* Minjungbal -> Minjangbal */
 /*{"xkf",	HB_TAG('X','K','F',' ')},*/	/* Khengkha */
+  {"xmg",	HB_TAG('B','M','L',' ')},	/* Mengaka -> Bamileke */
   {"xmm",	HB_TAG('M','L','Y',' ')},	/* Manado Malay -> Malay */
+  {"xmm",	HB_TAG('C','P','P',' ')},	/* Manado Malay -> Creoles */
   {"xmv",	HB_TAG('M','L','G',' ')},	/* Antankarana Malagasy -> Malagasy */
   {"xmw",	HB_TAG('M','L','G',' ')},	/* Tsimihety Malagasy -> Malagasy */
-  {"xnr",	HB_TAG('D','G','R',' ')},	/* Kangri -> Dogri */
+  {"xnr",	HB_TAG('D','G','R',' ')},	/* Kangri -> Dogri (macrolanguage) */
 /*{"xog",	HB_TAG('X','O','G',' ')},*/	/* Soga */
-/*{"xpe",	HB_TAG('X','P','E',' ')},*/	/* Liberia Kpelle -> Kpelle (Liberia) */
+  {"xpe",	HB_TAG('X','P','E',' ')},	/* Liberia Kpelle -> Kpelle (Liberia) */
+  {"xpe",	HB_TAG('K','P','L',' ')},	/* Liberia Kpelle -> Kpelle */
   {"xsl",	HB_TAG('S','S','L',' ')},	/* South Slavey */
   {"xsl",	HB_TAG('S','L','A',' ')},	/* South Slavey -> Slavey */
   {"xsl",	HB_TAG('A','T','H',' ')},	/* South Slavey -> Athapaskan */
   {"xst",	HB_TAG('S','I','G',' ')},	/* Silt'e (retired code) -> Silte Gurage */
+  {"xup",	HB_TAG('A','T','H',' ')},	/* Upper Umpqua -> Athapaskan */
   {"xwo",	HB_TAG('T','O','D',' ')},	/* Written Oirat -> Todo */
+  {"yaj",	HB_TAG('B','A','D','0')},	/* Banda-Yangere -> Banda */
+  {"yak",	HB_TAG_NONE	       },	/* Yakama != Sakha */
 /*{"yao",	HB_TAG('Y','A','O',' ')},*/	/* Yao */
 /*{"yap",	HB_TAG('Y','A','P',' ')},*/	/* Yapese */
+  {"yba",	HB_TAG_NONE	       },	/* Yala != Yoruba */
+  {"ybb",	HB_TAG('B','M','L',' ')},	/* Yemba -> Bamileke */
   {"ybd",	HB_TAG('A','R','K',' ')},	/* Yangbye (retired code) -> Rakhine */
   {"ydd",	HB_TAG('J','I','I',' ')},	/* Eastern Yiddish -> Yiddish */
   {"yi",	HB_TAG('J','I','I',' ')},	/* Yiddish [macrolanguage] */
   {"yih",	HB_TAG('J','I','I',' ')},	/* Western Yiddish -> Yiddish */
+  {"yim",	HB_TAG_NONE	       },	/* Yimchungru Naga != Yi Modern */
   {"yo",	HB_TAG('Y','B','A',' ')},	/* Yoruba */
   {"yos",	HB_TAG('Q','I','N',' ')},	/* Yos (retired code) -> Chin */
-  {"yrk",	HB_TAG('T','N','E',' ')},	/* Nenets -> Tundra Nenets */
-  {"yrk",	HB_TAG('F','N','E',' ')},	/* Nenets -> Forest Nenets */
-  {"yue",	HB_TAG('Z','H','H',' ')},	/* Yue Chinese -> Chinese, Hong Kong SAR */
+  {"yua",	HB_TAG('M','Y','N',' ')},	/* Yucateco -> Mayan */
+  {"yue",	HB_TAG('Z','H','H',' ')},	/* Yue Chinese -> Chinese, Traditional, Hong Kong SAR */
   {"za",	HB_TAG('Z','H','A',' ')},	/* Zhuang [macrolanguage] */
   {"zch",	HB_TAG('Z','H','A',' ')},	/* Central Hongshuihe Zhuang -> Zhuang */
   {"zdj",	HB_TAG('C','M','R',' ')},	/* Ngazidja Comorian -> Comorian */
 /*{"zea",	HB_TAG('Z','E','A',' ')},*/	/* Zeeuws -> Zealandic */
   {"zeh",	HB_TAG('Z','H','A',' ')},	/* Eastern Hongshuihe Zhuang -> Zhuang */
+  {"zen",	HB_TAG('B','B','R',' ')},	/* Zenaga -> Berber */
   {"zgb",	HB_TAG('Z','H','A',' ')},	/* Guibei Zhuang -> Zhuang */
-/*{"zgh",	HB_TAG('Z','G','H',' ')},*/	/* Standard Moroccan Tamazight */
+  {"zgh",	HB_TAG('Z','G','H',' ')},	/* Standard Moroccan Tamazight */
+  {"zgh",	HB_TAG('B','B','R',' ')},	/* Standard Moroccan Tamazight -> Berber */
   {"zgm",	HB_TAG('Z','H','A',' ')},	/* Minz Zhuang -> Zhuang */
   {"zgn",	HB_TAG('Z','H','A',' ')},	/* Guibian Zhuang -> Zhuang */
-  {"zh",	HB_TAG('Z','H','S',' ')},	/* Chinese [macrolanguage] -> Chinese Simplified */
+  {"zh",	HB_TAG('Z','H','S',' ')},	/* Chinese, Simplified [macrolanguage] */
   {"zhd",	HB_TAG('Z','H','A',' ')},	/* Dai Zhuang -> Zhuang */
   {"zhn",	HB_TAG('Z','H','A',' ')},	/* Nong Zhuang -> Zhuang */
   {"zlj",	HB_TAG('Z','H','A',' ')},	/* Liujiang Zhuang -> Zhuang */
@@ -1042,6 +1564,8 @@
   {"zln",	HB_TAG('Z','H','A',' ')},	/* Lianshan Zhuang -> Zhuang */
   {"zlq",	HB_TAG('Z','H','A',' ')},	/* Liuqian Zhuang -> Zhuang */
   {"zmi",	HB_TAG('M','L','Y',' ')},	/* Negeri Sembilan Malay -> Malay */
+  {"zmz",	HB_TAG('B','A','D','0')},	/* Mbandja -> Banda */
+  {"znd",	HB_TAG_NONE	       },	/* Zande [family] != Zande */
   {"zne",	HB_TAG('Z','N','D',' ')},	/* Zande */
   {"zom",	HB_TAG('Q','I','N',' ')},	/* Zou -> Chin */
   {"zqe",	HB_TAG('Z','H','A',' ')},	/* Qiubei Zhuang -> Zhuang */
@@ -1052,6 +1576,7 @@
   {"zyg",	HB_TAG('Z','H','A',' ')},	/* Yang Zhuang -> Zhuang */
   {"zyj",	HB_TAG('Z','H','A',' ')},	/* Youjiang Zhuang -> Zhuang */
   {"zyn",	HB_TAG('Z','H','A',' ')},	/* Yongnan Zhuang -> Zhuang */
+  {"zyp",	HB_TAG('Q','I','N',' ')},	/* Zyphe Chin -> Chin */
 /*{"zza",	HB_TAG('Z','Z','A',' ')},*/	/* Zazaki [macrolanguage] */
   {"zzj",	HB_TAG('Z','H','A',' ')},	/* Zuojiang Zhuang -> Zhuang */
 };
@@ -1090,6 +1615,13 @@
     *count = 1;
     return true;
   }
+  if (subtag_matches (lang_str, limit, "-arevmda"))
+  {
+    /* Armenian; Western Armenian (retired code) */
+    tags[0] = HB_TAG('H','Y','E',' ');  /* Armenian */
+    *count = 1;
+    return true;
+  }
   if (subtag_matches (lang_str, limit, "-provenc"))
   {
     /* Occitan (post 1500); Provençal */
@@ -1137,7 +1669,7 @@
   case 'a':
     if (0 == strcmp (&lang_str[1], "rt-lojban"))
     {
-      /* Lojban */
+      /* Lojban (retired code) */
       tags[0] = HB_TAG('J','B','O',' ');  /* Lojban */
       *count = 1;
       return true;
@@ -1146,225 +1678,273 @@
   case 'c':
     if (lang_matches (&lang_str[1], "do-hant-hk"))
     {
-      /* Min Dong Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Min Dong Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "do-hant-mo"))
     {
-      /* Min Dong Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Min Dong Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "jy-hant-hk"))
     {
-      /* Jinyu Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Jinyu Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "jy-hant-mo"))
     {
-      /* Jinyu Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Jinyu Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "mn-hant-hk"))
     {
-      /* Mandarin Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Mandarin Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "mn-hant-mo"))
     {
-      /* Mandarin Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Mandarin Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "np-hant-hk"))
     {
-      /* Northern Ping Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Northern Ping Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "np-hant-mo"))
     {
-      /* Northern Ping Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Northern Ping Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "px-hant-hk"))
     {
-      /* Pu-Xian Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Pu-Xian Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "px-hant-mo"))
     {
-      /* Pu-Xian Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Pu-Xian Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "sp-hant-hk"))
     {
-      /* Southern Ping Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Southern Ping Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "sp-hant-mo"))
     {
-      /* Southern Ping Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Southern Ping Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "zh-hant-hk"))
     {
-      /* Huizhou Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Huizhou Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "zh-hant-mo"))
     {
-      /* Huizhou Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Huizhou Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "zo-hant-hk"))
     {
-      /* Min Zhong Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Min Zhong Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "zo-hant-mo"))
     {
-      /* Min Zhong Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Min Zhong Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "do-hans"))
     {
-      /* Min Dong Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Min Dong Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "do-hant"))
     {
-      /* Min Dong Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Min Dong Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "jy-hans"))
     {
-      /* Jinyu Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Jinyu Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "jy-hant"))
     {
-      /* Jinyu Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Jinyu Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "mn-hans"))
     {
-      /* Mandarin Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Mandarin Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "mn-hant"))
     {
-      /* Mandarin Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Mandarin Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "np-hans"))
     {
-      /* Northern Ping Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Northern Ping Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "np-hant"))
     {
-      /* Northern Ping Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Northern Ping Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "px-hans"))
     {
-      /* Pu-Xian Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Pu-Xian Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "px-hant"))
     {
-      /* Pu-Xian Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Pu-Xian Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "sp-hans"))
     {
-      /* Southern Ping Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Southern Ping Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "sp-hant"))
     {
-      /* Southern Ping Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Southern Ping Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "zh-hans"))
     {
-      /* Huizhou Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Huizhou Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "zh-hant"))
     {
-      /* Huizhou Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Huizhou Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "zo-hans"))
     {
-      /* Min Zhong Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Min Zhong Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "zo-hant"))
     {
-      /* Min Zhong Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Min Zhong Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1372,7 +1952,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Min Dong Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1380,15 +1960,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Min Dong Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "do-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Min Dong Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1396,7 +1982,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Jinyu Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1404,15 +1990,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Jinyu Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "jy-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Jinyu Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1420,7 +2012,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Mandarin Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1428,15 +2020,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Mandarin Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "mn-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Mandarin Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1444,7 +2042,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Northern Ping Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1452,15 +2050,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Northern Ping Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Northern Ping Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1468,7 +2072,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Pu-Xian Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1476,15 +2080,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Pu-Xian Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "px-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Pu-Xian Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1492,7 +2102,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Southern Ping Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1500,15 +2110,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Southern Ping Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sp-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Southern Ping Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1516,7 +2132,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Huizhou Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1524,15 +2140,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Huizhou Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zh-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Huizhou Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1540,7 +2162,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Min Zhong Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1548,15 +2170,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Min Zhong Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "zo-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Min Zhong Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1564,35 +2192,41 @@
   case 'g':
     if (lang_matches (&lang_str[1], "an-hant-hk"))
     {
-      /* Gan Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Gan Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "an-hant-mo"))
     {
-      /* Gan Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Gan Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "an-hans"))
     {
-      /* Gan Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Gan Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "an-hant"))
     {
-      /* Gan Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Gan Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "a-latg"))
     {
-      /* Irish */
+      /* Irish; Latin (Gaelic variant) */
       tags[0] = HB_TAG('I','R','T',' ');  /* Irish Traditional */
       *count = 1;
       return true;
@@ -1601,7 +2235,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Gan Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1609,15 +2243,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Gan Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Gan Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1625,57 +2265,69 @@
   case 'h':
     if (lang_matches (&lang_str[1], "ak-hant-hk"))
     {
-      /* Hakka Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Hakka Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "ak-hant-mo"))
     {
-      /* Hakka Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Hakka Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "sn-hant-hk"))
     {
-      /* Xiang Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Xiang Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "sn-hant-mo"))
     {
-      /* Xiang Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Xiang Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "ak-hans"))
     {
-      /* Hakka Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Hakka Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "ak-hant"))
     {
-      /* Hakka Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Hakka Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "sn-hans"))
     {
-      /* Xiang Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Xiang Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "sn-hant"))
     {
-      /* Xiang Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Xiang Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1683,7 +2335,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Hakka Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1691,15 +2343,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Hakka Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "ak-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Hakka Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1707,7 +2365,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Xiang Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1715,15 +2373,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Xiang Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "sn-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Xiang Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1731,7 +2395,7 @@
   case 'i':
     if (0 == strcmp (&lang_str[1], "-navajo"))
     {
-      /* Navajo */
+      /* Navajo (retired code) */
       unsigned int i;
       hb_tag_t possible_tags[] = {
 	HB_TAG('N','A','V',' '),  /* Navajo */
@@ -1744,14 +2408,14 @@
     }
     if (0 == strcmp (&lang_str[1], "-hak"))
     {
-      /* Hakka */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Hakka (retired code) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (0 == strcmp (&lang_str[1], "-lux"))
     {
-      /* Luxembourgish */
+      /* Luxembourgish (retired code) */
       tags[0] = HB_TAG('L','T','Z',' ');  /* Luxembourgish */
       *count = 1;
       return true;
@@ -1760,8 +2424,8 @@
   case 'l':
     if (lang_matches (&lang_str[1], "zh-hans"))
     {
-      /* Literary Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Literary Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
@@ -1769,29 +2433,35 @@
   case 'm':
     if (lang_matches (&lang_str[1], "np-hant-hk"))
     {
-      /* Min Bei Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Min Bei Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "np-hant-mo"))
     {
-      /* Min Bei Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Min Bei Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "np-hans"))
     {
-      /* Min Bei Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Min Bei Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "np-hant"))
     {
-      /* Min Bei Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Min Bei Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1799,7 +2469,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Min Bei Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1807,15 +2477,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Min Bei Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "np-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Min Bei Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1823,29 +2499,35 @@
   case 'n':
     if (lang_matches (&lang_str[1], "an-hant-hk"))
     {
-      /* Min Nan Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Min Nan Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "an-hant-mo"))
     {
-      /* Min Nan Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Min Nan Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "an-hans"))
     {
-      /* Min Nan Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Min Nan Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "an-hant"))
     {
-      /* Min Nan Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Min Nan Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1853,7 +2535,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Min Nan Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1861,30 +2543,42 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Min Nan Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "an-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Min Nan Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
     if (0 == strcmp (&lang_str[1], "o-bok"))
     {
-      /* Norwegian Bokmal */
+      /* Norwegian Bokmal (retired code) */
       tags[0] = HB_TAG('N','O','R',' ');  /* Norwegian */
       *count = 1;
       return true;
     }
     if (0 == strcmp (&lang_str[1], "o-nyn"))
     {
-      /* Norwegian Nynorsk */
-      tags[0] = HB_TAG('N','Y','N',' ');  /* Norwegian Nynorsk (Nynorsk, Norwegian) */
-      *count = 1;
+      /* Norwegian Nynorsk (retired code) */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('N','Y','N',' '),  /* Norwegian Nynorsk (Nynorsk, Norwegian) */
+	HB_TAG('N','O','R',' '),  /* Norwegian */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     break;
@@ -1901,29 +2595,35 @@
   case 'w':
     if (lang_matches (&lang_str[1], "uu-hant-hk"))
     {
-      /* Wu Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Wu Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "uu-hant-mo"))
     {
-      /* Wu Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Wu Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (lang_matches (&lang_str[1], "uu-hans"))
     {
-      /* Wu Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Wu Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "uu-hant"))
     {
-      /* Wu Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Wu Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1931,7 +2631,7 @@
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
       /* Wu Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
@@ -1939,15 +2639,21 @@
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
       /* Wu Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "uu-", 3)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
       /* Wu Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -1955,8 +2661,8 @@
   case 'y':
     if (lang_matches (&lang_str[1], "ue-hans"))
     {
-      /* Yue Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Yue Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
@@ -1964,67 +2670,79 @@
   case 'z':
     if (lang_matches (&lang_str[1], "h-hant-hk"))
     {
-      /* Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Chinese [macrolanguage]; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "h-hant-mo"))
     {
-      /* Chinese */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Chinese [macrolanguage]; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strcmp (&lang_str[1], "h-min-nan"))
     {
-      /* Minnan, Hokkien, Amoy, Taiwanese, Southern Min, Southern Fujian, Hoklo, Southern Fukien, Ho-lo */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Minnan, Hokkien, Amoy, Taiwanese, Southern Min, Southern Fujian, Hoklo, Southern Fukien, Ho-lo (retired code) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "h-hans"))
     {
-      /* Chinese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Chinese [macrolanguage]; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (lang_matches (&lang_str[1], "h-hant"))
     {
-      /* Chinese */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Chinese [macrolanguage]; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
     if (0 == strcmp (&lang_str[1], "h-min"))
     {
-      /* Min, Fuzhou, Hokkien, Amoy, or Taiwanese */
-      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese Simplified */
+      /* Min, Fuzhou, Hokkien, Amoy, or Taiwanese (retired code) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
       *count = 1;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "h-", 2)
 	&& subtag_matches (lang_str, limit, "-hk"))
     {
-      /* Chinese; Hong Kong */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
+      /* Chinese [macrolanguage]; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
       *count = 1;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "h-", 2)
 	&& subtag_matches (lang_str, limit, "-mo"))
     {
-      /* Chinese; Macao */
-      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Hong Kong SAR */
-      *count = 1;
+      /* Chinese [macrolanguage]; Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
       return true;
     }
     if (0 == strncmp (&lang_str[1], "h-", 2)
 	&& subtag_matches (lang_str, limit, "-tw"))
     {
-      /* Chinese; Taiwan, Province of China */
-      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese Traditional */
+      /* Chinese [macrolanguage]; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
       *count = 1;
       return true;
     }
@@ -2055,101 +2773,127 @@
   case HB_TAG('A','P','P','H'):  /* Phonetic transcription—Americanist conventions */
     return hb_language_from_string ("und-fonnapa", -1);  /* Undetermined; North American Phonetic Alphabet */
   case HB_TAG('A','R','A',' '):  /* Arabic */
-    return hb_language_from_string ("ar", -1);  /* Arabic */
+    return hb_language_from_string ("ar", -1);  /* Arabic [macrolanguage] */
   case HB_TAG('A','R','K',' '):  /* Rakhine */
     return hb_language_from_string ("rki", -1);  /* Rakhine */
   case HB_TAG('A','T','H',' '):  /* Athapaskan */
-    return hb_language_from_string ("ath", -1);  /* Athapascan */
+    return hb_language_from_string ("ath", -1);  /* Athapascan [family] */
+  case HB_TAG('B','B','R',' '):  /* Berber */
+    return hb_language_from_string ("ber", -1);  /* Berber [family] */
   case HB_TAG('B','I','K',' '):  /* Bikol */
-    return hb_language_from_string ("bik", -1);  /* Bikol */
+    return hb_language_from_string ("bik", -1);  /* Bikol [macrolanguage] */
+  case HB_TAG('B','T','K',' '):  /* Batak */
+    return hb_language_from_string ("btk", -1);  /* Batak [family] */
   case HB_TAG('C','P','P',' '):  /* Creoles */
-    return hb_language_from_string ("crp", -1);  /* Creoles and pidgins */
+    return hb_language_from_string ("crp", -1);  /* Creoles and pidgins [family] */
   case HB_TAG('C','R','R',' '):  /* Carrier */
     return hb_language_from_string ("crx", -1);  /* Carrier */
+  case HB_TAG('D','G','R',' '):  /* Dogri (macrolanguage) */
+    return hb_language_from_string ("doi", -1);  /* Dogri [macrolanguage] */
   case HB_TAG('D','N','K',' '):  /* Dinka */
-    return hb_language_from_string ("din", -1);  /* Dinka */
+    return hb_language_from_string ("din", -1);  /* Dinka [macrolanguage] */
   case HB_TAG('D','R','I',' '):  /* Dari */
     return hb_language_from_string ("prs", -1);  /* Dari */
   case HB_TAG('D','Z','N',' '):  /* Dzongkha */
     return hb_language_from_string ("dz", -1);  /* Dzongkha */
   case HB_TAG('E','T','I',' '):  /* Estonian */
-    return hb_language_from_string ("et", -1);  /* Estonian */
+    return hb_language_from_string ("et", -1);  /* Estonian [macrolanguage] */
+  case HB_TAG('F','A','R',' '):  /* Persian */
+    return hb_language_from_string ("fa", -1);  /* Persian [macrolanguage] */
   case HB_TAG('G','O','N',' '):  /* Gondi */
-    return hb_language_from_string ("gon", -1);  /* Gondi */
+    return hb_language_from_string ("gon", -1);  /* Gondi [macrolanguage] */
   case HB_TAG('H','M','N',' '):  /* Hmong */
-    return hb_language_from_string ("hmn", -1);  /* Hmong */
+    return hb_language_from_string ("hmn", -1);  /* Hmong [macrolanguage] */
   case HB_TAG('H','N','D',' '):  /* Hindko */
     return hb_language_from_string ("hnd", -1);  /* Southern Hindko */
   case HB_TAG('H','Y','E',' '):  /* Armenian */
     return hb_language_from_string ("hyw", -1);  /* Western Armenian */
   case HB_TAG('I','J','O',' '):  /* Ijo */
-    return hb_language_from_string ("ijo", -1);  /* Ijo */
+    return hb_language_from_string ("ijo", -1);  /* Ijo [family] */
   case HB_TAG('I','N','U',' '):  /* Inuktitut */
-    return hb_language_from_string ("iu", -1);  /* Inuktitut */
+    return hb_language_from_string ("iu", -1);  /* Inuktitut [macrolanguage] */
   case HB_TAG('I','P','K',' '):  /* Inupiat */
-    return hb_language_from_string ("ik", -1);  /* Inupiaq */
+    return hb_language_from_string ("ik", -1);  /* Inupiaq [macrolanguage] */
   case HB_TAG('I','P','P','H'):  /* Phonetic transcription—IPA conventions */
     return hb_language_from_string ("und-fonipa", -1);  /* Undetermined; International Phonetic Alphabet */
   case HB_TAG('I','R','T',' '):  /* Irish Traditional */
     return hb_language_from_string ("ga-Latg", -1);  /* Irish; Latin (Gaelic variant) */
   case HB_TAG('J','I','I',' '):  /* Yiddish */
-    return hb_language_from_string ("yi", -1);  /* Yiddish */
+    return hb_language_from_string ("yi", -1);  /* Yiddish [macrolanguage] */
   case HB_TAG('K','A','L',' '):  /* Kalenjin */
-    return hb_language_from_string ("kln", -1);  /* Kalenjin */
+    return hb_language_from_string ("kln", -1);  /* Kalenjin [macrolanguage] */
   case HB_TAG('K','G','E',' '):  /* Khutsuri Georgian */
     return hb_language_from_string ("und-Geok", -1);  /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */
   case HB_TAG('K','N','R',' '):  /* Kanuri */
-    return hb_language_from_string ("kr", -1);  /* Kanuri */
+    return hb_language_from_string ("kr", -1);  /* Kanuri [macrolanguage] */
+  case HB_TAG('K','O','H',' '):  /* Korean Old Hangul */
+    return hb_language_from_string ("okm", -1);  /* Middle Korean (10th-16th cent.) */
   case HB_TAG('K','O','K',' '):  /* Konkani */
-    return hb_language_from_string ("kok", -1);  /* Konkani */
+    return hb_language_from_string ("kok", -1);  /* Konkani [macrolanguage] */
+  case HB_TAG('K','O','M',' '):  /* Komi */
+    return hb_language_from_string ("kv", -1);  /* Komi [macrolanguage] */
+  case HB_TAG('K','P','L',' '):  /* Kpelle */
+    return hb_language_from_string ("kpe", -1);  /* Kpelle [macrolanguage] */
+  case HB_TAG('K','R','N',' '):  /* Karen */
+    return hb_language_from_string ("kar", -1);  /* Karen [family] */
   case HB_TAG('K','U','I',' '):  /* Kui */
     return hb_language_from_string ("uki", -1);  /* Kui (India) */
   case HB_TAG('K','U','R',' '):  /* Kurdish */
-    return hb_language_from_string ("ku", -1);  /* Kurdish */
+    return hb_language_from_string ("ku", -1);  /* Kurdish [macrolanguage] */
   case HB_TAG('L','U','H',' '):  /* Luyia */
-    return hb_language_from_string ("luy", -1);  /* Luyia */
+    return hb_language_from_string ("luy", -1);  /* Luyia [macrolanguage] */
   case HB_TAG('L','V','I',' '):  /* Latvian */
-    return hb_language_from_string ("lv", -1);  /* Latvian */
+    return hb_language_from_string ("lv", -1);  /* Latvian [macrolanguage] */
   case HB_TAG('M','A','W',' '):  /* Marwari */
-    return hb_language_from_string ("mwr", -1);  /* Marwari */
+    return hb_language_from_string ("mwr", -1);  /* Marwari [macrolanguage] */
   case HB_TAG('M','L','G',' '):  /* Malagasy */
-    return hb_language_from_string ("mg", -1);  /* Malagasy */
+    return hb_language_from_string ("mg", -1);  /* Malagasy [macrolanguage] */
   case HB_TAG('M','L','Y',' '):  /* Malay */
-    return hb_language_from_string ("ms", -1);  /* Malay */
+    return hb_language_from_string ("ms", -1);  /* Malay [macrolanguage] */
   case HB_TAG('M','N','G',' '):  /* Mongolian */
-    return hb_language_from_string ("mn", -1);  /* Mongolian */
+    return hb_language_from_string ("mn", -1);  /* Mongolian [macrolanguage] */
+  case HB_TAG('M','N','K',' '):  /* Maninka */
+    return hb_language_from_string ("man", -1);  /* Mandingo [macrolanguage] */
   case HB_TAG('M','O','L',' '):  /* Moldavian */
     return hb_language_from_string ("ro-MD", -1);  /* Romanian; Moldova */
+  case HB_TAG('M','Y','N',' '):  /* Mayan */
+    return hb_language_from_string ("myn", -1);  /* Mayan [family] */
+  case HB_TAG('N','A','H',' '):  /* Nahuatl */
+    return hb_language_from_string ("nah", -1);  /* Nahuatl [family] */
   case HB_TAG('N','E','P',' '):  /* Nepali */
-    return hb_language_from_string ("ne", -1);  /* Nepali */
+    return hb_language_from_string ("ne", -1);  /* Nepali [macrolanguage] */
   case HB_TAG('N','I','S',' '):  /* Nisi */
     return hb_language_from_string ("njz", -1);  /* Nyishi */
   case HB_TAG('N','O','R',' '):  /* Norwegian */
-    return hb_language_from_string ("no", -1);  /* Norwegian */
+    return hb_language_from_string ("no", -1);  /* Norwegian [macrolanguage] */
   case HB_TAG('O','J','B',' '):  /* Ojibway */
-    return hb_language_from_string ("oj", -1);  /* Ojibwa */
+    return hb_language_from_string ("oj", -1);  /* Ojibwa [macrolanguage] */
   case HB_TAG('O','R','O',' '):  /* Oromo */
-    return hb_language_from_string ("om", -1);  /* Oromo */
+    return hb_language_from_string ("om", -1);  /* Oromo [macrolanguage] */
   case HB_TAG('P','A','S',' '):  /* Pashto */
-    return hb_language_from_string ("ps", -1);  /* Pashto */
+    return hb_language_from_string ("ps", -1);  /* Pashto [macrolanguage] */
   case HB_TAG('P','G','R',' '):  /* Polytonic Greek */
     return hb_language_from_string ("el-polyton", -1);  /* Modern Greek (1453-); Polytonic Greek */
   case HB_TAG('P','R','O',' '):  /* Provençal / Old Provençal */
     return hb_language_from_string ("pro", -1);  /* Old Provençal (to 1500) */
   case HB_TAG('Q','U','H',' '):  /* Quechua (Bolivia) */
     return hb_language_from_string ("quh", -1);  /* South Bolivian Quechua */
+  case HB_TAG('Q','U','Z',' '):  /* Quechua */
+    return hb_language_from_string ("qu", -1);  /* Quechua [macrolanguage] */
   case HB_TAG('Q','V','I',' '):  /* Quechua (Ecuador) */
     return hb_language_from_string ("qvi", -1);  /* Imbabura Highland Quichua */
   case HB_TAG('Q','W','H',' '):  /* Quechua (Peru) */
     return hb_language_from_string ("qwh", -1);  /* Huaylas Ancash Quechua */
   case HB_TAG('R','A','J',' '):  /* Rajasthani */
-    return hb_language_from_string ("raj", -1);  /* Rajasthani */
+    return hb_language_from_string ("raj", -1);  /* Rajasthani [macrolanguage] */
   case HB_TAG('R','O','Y',' '):  /* Romany */
-    return hb_language_from_string ("rom", -1);  /* Romany */
+    return hb_language_from_string ("rom", -1);  /* Romany [macrolanguage] */
   case HB_TAG('S','Q','I',' '):  /* Albanian */
-    return hb_language_from_string ("sq", -1);  /* Albanian */
+    return hb_language_from_string ("sq", -1);  /* Albanian [macrolanguage] */
+  case HB_TAG('S','R','B',' '):  /* Serbian */
+    return hb_language_from_string ("sr", -1);  /* Serbian */
   case HB_TAG('S','Y','R',' '):  /* Syriac */
-    return hb_language_from_string ("syr", -1);  /* Syriac */
+    return hb_language_from_string ("syr", -1);  /* Syriac [macrolanguage] */
   case HB_TAG('S','Y','R','E'):  /* Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre') */
     return hb_language_from_string ("und-Syre", -1);  /* Undetermined; Syriac (Estrangelo variant) */
   case HB_TAG('S','Y','R','J'):  /* Syriac, Western script-variant (equivalent to ISO 15924 'Syrj') */
@@ -2157,19 +2901,19 @@
   case HB_TAG('S','Y','R','N'):  /* Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn') */
     return hb_language_from_string ("und-Syrn", -1);  /* Undetermined; Syriac (Eastern variant) */
   case HB_TAG('T','M','H',' '):  /* Tamashek */
-    return hb_language_from_string ("tmh", -1);  /* Tamashek */
-  case HB_TAG('T','N','E',' '):  /* Tundra Nenets */
-    return hb_language_from_string ("yrk", -1);  /* Nenets */
+    return hb_language_from_string ("tmh", -1);  /* Tamashek [macrolanguage] */
   case HB_TAG('T','O','D',' '):  /* Todo */
     return hb_language_from_string ("xwo", -1);  /* Written Oirat */
-  case HB_TAG('T','W','I',' '):  /* Twi */
-    return hb_language_from_string ("tw", -1);  /* Twi */
-  case HB_TAG('Z','H','H',' '):  /* Chinese, Hong Kong SAR */
-    return hb_language_from_string ("zh-HK", -1);  /* Chinese; Hong Kong */
-  case HB_TAG('Z','H','S',' '):  /* Chinese Simplified */
-    return hb_language_from_string ("zh-Hans", -1);  /* Chinese; Han (Simplified variant) */
-  case HB_TAG('Z','H','T',' '):  /* Chinese Traditional */
-    return hb_language_from_string ("zh-Hant", -1);  /* Chinese; Han (Traditional variant) */
+  case HB_TAG('Z','H','H',' '):  /* Chinese, Traditional, Hong Kong SAR */
+    return hb_language_from_string ("zh-HK", -1);  /* Chinese [macrolanguage]; Hong Kong */
+  case HB_TAG('Z','H','S',' '):  /* Chinese, Simplified */
+    return hb_language_from_string ("zh-Hans", -1);  /* Chinese [macrolanguage]; Han (Simplified variant) */
+  case HB_TAG('Z','H','T',' '):  /* Chinese, Traditional */
+    return hb_language_from_string ("zh-Hant", -1);  /* Chinese [macrolanguage]; Han (Traditional variant) */
+  case HB_TAG('Z','H','T','M'):  /* Chinese, Traditional, Macao SAR */
+    return hb_language_from_string ("zh-MO", -1);  /* Chinese [macrolanguage]; Macao */
+  case HB_TAG('Z','Z','A',' '):  /* Zazaki */
+    return hb_language_from_string ("zza", -1);  /* Zazaki [macrolanguage] */
   default:
     return HB_LANGUAGE_INVALID;
   }
diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc
index 7ec91c5..fc145a4 100644
--- a/src/hb-ot-tag.cc
+++ b/src/hb-ot-tag.cc
@@ -164,6 +164,15 @@
   *count = i;
 }
 
+/**
+ * hb_ot_tag_to_script:
+ * @tag: a script tag
+ *
+ * Converts a script tag to an #hb_script_t.
+ *
+ * Return value: The #hb_script_t corresponding to @tag.
+ *
+ **/
 hb_script_t
 hb_ot_tag_to_script (hb_tag_t tag)
 {
@@ -280,6 +289,7 @@
       for (i = 0;
 	   i < *count &&
 	   tag_idx + i < ARRAY_LENGTH (ot_languages) &&
+	   ot_languages[tag_idx + i].tag != HB_TAG_NONE &&
 	   0 == strcmp (ot_languages[tag_idx + i].language, ot_languages[tag_idx].language);
 	   i++)
 	tags[i] = ot_languages[tag_idx + i].tag;
@@ -350,13 +360,13 @@
  * hb_ot_tags_from_script_and_language:
  * @script: an #hb_script_t to convert.
  * @language: an #hb_language_t to convert.
- * @script_count: (allow-none): maximum number of script tags to retrieve (IN)
+ * @script_count: (inout) (optional): maximum number of script tags to retrieve (IN)
  * and actual number of script tags retrieved (OUT)
- * @script_tags: (out) (allow-none): array of size at least @script_count to store the
+ * @script_tags: (out) (optional): array of size at least @script_count to store the
  * script tag results
- * @language_count: (allow-none): maximum number of language tags to retrieve
+ * @language_count: (inout) (optional): maximum number of language tags to retrieve
  * (IN) and actual number of language tags retrieved (OUT)
- * @language_tags: (out) (allow-none): array of size at least @language_count to store
+ * @language_tags: (out) (optional): array of size at least @language_count to store
  * the language tag results
  *
  * Converts an #hb_script_t and an #hb_language_t to script and language tags.
@@ -423,10 +433,12 @@
 
 /**
  * hb_ot_tag_to_language:
+ * @tag: an language tag
  *
+ * Converts a language tag to an #hb_language_t.
  *
- *
- * Return value: (transfer none):
+ * Return value: (transfer none) (nullable):
+ * The #hb_language_t corresponding to @tag.
  *
  * Since: 0.9.2
  **/
@@ -477,9 +489,9 @@
  * hb_ot_tags_to_script_and_language:
  * @script_tag: a script tag
  * @language_tag: a language tag
- * @script: (allow-none): the #hb_script_t corresponding to @script_tag (OUT).
- * @language: (allow-none): the #hb_language_t corresponding to @script_tag and
- * @language_tag (OUT).
+ * @script: (out) (optional): the #hb_script_t corresponding to @script_tag.
+ * @language: (out) (optional): the #hb_language_t corresponding to @script_tag and
+ * @language_tag.
  *
  * Converts a script tag and a language tag to an #hb_script_t and an
  * #hb_language_t.
diff --git a/src/hb-ot-var.cc b/src/hb-ot-var.cc
index 1fe5738..6b42b45 100644
--- a/src/hb-ot-var.cc
+++ b/src/hb-ot-var.cc
@@ -56,7 +56,7 @@
  *
  * Tests whether a face includes any OpenType variation data in the `fvar` table.
  *
- * Return value: true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 1.4.2
  **/
@@ -87,7 +87,7 @@
  * hb_ot_var_get_axes:
  * @face: #hb_face_t to work upon
  * @start_offset: offset of the first lookup to retrieve
- * @axes_count: (inout) (allow-none): Input = the maximum number of variation axes to return;
+ * @axes_count: (inout) (optional): Input = the maximum number of variation axes to return;
  *                Output = the actual number of variation axes returned (may be zero)
  * @axes_array: (out caller-allocates) (array length=axes_count): The array of variation axes found
  *
@@ -133,7 +133,7 @@
  * hb_ot_var_get_axis_infos:
  * @face: #hb_face_t to work upon
  * @start_offset: offset of the first lookup to retrieve
- * @axes_count: (inout) (allow-none): Input = the maximum number of variation axes to return;
+ * @axes_count: (inout) (optional): Input = the maximum number of variation axes to return;
  *                Output = the actual number of variation axes returned (may be zero)
  * @axes_array: (out caller-allocates) (array length=axes_count): The array of variation axes found
  *
@@ -162,7 +162,7 @@
  * Fetches the variation-axis information corresponding to the specified axis tag
  * in the specified face.
  *
- * Return value: true if data found, false otherwise
+ * Return value: %true if data found, %false otherwise
  *
  * Since: 2.2.0
  **/
@@ -237,7 +237,7 @@
  * hb_ot_var_named_instance_get_design_coords:
  * @face: The #hb_face_t to work on
  * @instance_index: The index of the named instance to query
- * @coords_length: (inout) (allow-none): Input = the maximum number of coordinates to return;
+ * @coords_length: (inout) (optional): Input = the maximum number of coordinates to return;
  *                 Output = the actual number of coordinates returned (may be zero)
  * @coords: (out) (array length=coords_length): The array of coordinates found for the query
  *
diff --git a/src/hb-ot-var.h b/src/hb-ot-var.h
index fc5a9f3..ce201d3 100644
--- a/src/hb-ot-var.h
+++ b/src/hb-ot-var.h
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_OT_H_IN
+#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb-ot.h> instead."
 #endif
 
@@ -36,34 +36,38 @@
 HB_BEGIN_DECLS
 
 /**
- * hb_tag_t:
- * @HB_OT_TAG_VAR_AXIS_ITALIC: Registered tag for the roman/italic axis
+ * HB_OT_TAG_VAR_AXIS_ITALIC:
+ *
+ * Registered tag for the roman/italic axis.
  */
 #define HB_OT_TAG_VAR_AXIS_ITALIC	HB_TAG('i','t','a','l')
 
 /**
- * hb_tag_t:
- * @HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE: Registered tag for the optical-size axis
+ * HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE:
  *
+ * Registered tag for the optical-size axis.
  * <note>Note: The optical-size axis supersedes the OpenType `size` feature.</note>
  */
 #define HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE	HB_TAG('o','p','s','z')
 
 /**
- * hb_tag_t:
- * @HB_OT_TAG_VAR_AXIS_SLANT: Registered tag for the slant axis
+ * HB_OT_TAG_VAR_AXIS_SLANT:
+ *
+ * Registered tag for the slant axis
  */
 #define HB_OT_TAG_VAR_AXIS_SLANT	HB_TAG('s','l','n','t')
 
 /**
- * hb_tag_t:
- * @HB_OT_TAG_VAR_AXIS_WIDTH: Registered tag for the width axis
+ * HB_OT_TAG_VAR_AXIS_WIDTH:
+ *
+ * Registered tag for the width axis.
  */
 #define HB_OT_TAG_VAR_AXIS_WIDTH	HB_TAG('w','d','t','h')
 
 /**
- * hb_tag_t:
- * @HB_OT_TAG_VAR_AXIS_WEIGHT: Registered tag for the weight axis
+ * HB_OT_TAG_VAR_AXIS_WEIGHT:
+ *
+ * Registered tag for the weight axis.
  */
 #define HB_OT_TAG_VAR_AXIS_WEIGHT	HB_TAG('w','g','h','t')
 
@@ -88,11 +92,14 @@
  * hb_ot_var_axis_flags_t:
  * @HB_OT_VAR_AXIS_FLAG_HIDDEN: The axis should not be exposed directly in user interfaces.
  *
+ * Flags for #hb_ot_var_axis_info_t.
+ *
  * Since: 2.2.0
  */
 typedef enum { /*< flags >*/
   HB_OT_VAR_AXIS_FLAG_HIDDEN	= 0x00000001u,
 
+  /*< private >*/
   _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= HB_TAG_MAX_SIGNED /*< skip >*/
 } hb_ot_var_axis_flags_t;
 
@@ -115,8 +122,7 @@
  *
  * Since: 2.2.0
  */
-typedef struct hb_ot_var_axis_info_t
-{
+typedef struct hb_ot_var_axis_info_t {
   unsigned int			axis_index;
   hb_tag_t			tag;
   hb_ot_name_id_t		name_id;
diff --git a/src/hb-sanitize.hh b/src/hb-sanitize.hh
index 024b4d1..1675e84 100644
--- a/src/hb-sanitize.hh
+++ b/src/hb-sanitize.hh
@@ -73,7 +73,7 @@
  * === The sanitize() contract ===
  *
  * The sanitize() method of each object type shall return true if it's safe to
- * call other methods of the object, and false otherwise.
+ * call other methods of the object, and %false otherwise.
  *
  * Note that what sanitize() checks for might align with what the specification
  * describes as valid table data, but does not have to be.  In particular, we
@@ -113,8 +113,8 @@
 #ifndef HB_SANITIZE_MAX_OPS_MAX
 #define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF
 #endif
-#ifndef HB_SANITIZE_MAX_SUTABLES
-#define HB_SANITIZE_MAX_SUTABLES 0x4000
+#ifndef HB_SANITIZE_MAX_SUBTABLES
+#define HB_SANITIZE_MAX_SUBTABLES 0x4000
 #endif
 
 struct hb_sanitize_context_t :
@@ -139,7 +139,7 @@
   bool visit_subtables (unsigned count)
   {
     max_subtables += count;
-    return max_subtables < HB_SANITIZE_MAX_SUTABLES;
+    return max_subtables < HB_SANITIZE_MAX_SUBTABLES;
   }
 
   private:
diff --git a/src/hb-set.cc b/src/hb-set.cc
index 0551ed8..55e3b90 100644
--- a/src/hb-set.cc
+++ b/src/hb-set.cc
@@ -30,11 +30,11 @@
 /**
  * SECTION:hb-set
  * @title: hb-set
- * @short_description: Object representing a set of integers
+ * @short_description: Objects representing a set of integers
  * @include: hb.h
  *
  * Set objects represent a mathematical set of integer values.  They are
- * used in non-shaping API to query certain set of characters or glyphs,
+ * used in non-shaping APIs to query certain sets of characters or glyphs,
  * or other integer values.
  **/
 
@@ -42,7 +42,9 @@
 /**
  * hb_set_create: (Xconstructor)
  *
- * Return value: (transfer full):
+ * Creates a new, initially empty set.
+ *
+ * Return value: (transfer full): The new #hb_set_t
  *
  * Since: 0.9.2
  **/
@@ -62,7 +64,9 @@
 /**
  * hb_set_get_empty:
  *
- * Return value: (transfer full):
+ * Fetches the singleton empty #hb_set_t.
+ *
+ * Return value: (transfer full): The empty #hb_set_t
  *
  * Since: 0.9.2
  **/
@@ -74,9 +78,11 @@
 
 /**
  * hb_set_reference: (skip)
- * @set: a set.
+ * @set: A set
  *
- * Return value: (transfer full):
+ * Increases the reference count on a set.
+ *
+ * Return value: (transfer full): The set
  *
  * Since: 0.9.2
  **/
@@ -88,7 +94,11 @@
 
 /**
  * hb_set_destroy: (skip)
- * @set: a set.
+ * @set: A set
+ *
+ * Decreases the reference count on a set. When
+ * the reference count reaches zero, the set is
+ * destroyed, freeing all memory.
  *
  * Since: 0.9.2
  **/
@@ -104,13 +114,15 @@
 
 /**
  * hb_set_set_user_data: (skip)
- * @set: a set.
- * @key:
- * @data:
- * @destroy:
- * @replace:
+ * @set: A set
+ * @key: The user-data key to set
+ * @data: A pointer to the user data to set
+ * @destroy: (nullable): A callback to call when @data is not needed anymore
+ * @replace: Whether to replace an existing data with the same key
  *
- * Return value:
+ * Attaches a user-data key/data pair to the specified set.
+ *
+ * Return value: %true if success, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -126,10 +138,13 @@
 
 /**
  * hb_set_get_user_data: (skip)
- * @set: a set.
- * @key:
+ * @set: A set
+ * @key: The user-data key to query
  *
- * Return value: (transfer none):
+ * Fetches the user data associated with the specified key,
+ * attached to the specified set.
+ *
+ * Return value: (transfer none): A pointer to the user data
  *
  * Since: 0.9.2
  **/
@@ -143,11 +158,11 @@
 
 /**
  * hb_set_allocation_successful:
- * @set: a set.
+ * @set: A set
  *
+ * Tests whether memory allocation for a set was successful.
  *
- *
- * Return value:
+ * Return value: %true if allocation succeeded, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -159,9 +174,9 @@
 
 /**
  * hb_set_clear:
- * @set: a set.
+ * @set: A set
  *
- *
+ * Clears out the contents of a set.
  *
  * Since: 0.9.2
  **/
@@ -175,9 +190,9 @@
  * hb_set_is_empty:
  * @set: a set.
  *
+ * Tests whether a set is empty (contains no elements).
  *
- *
- * Return value:
+ * Return value: %true if @set is empty
  *
  * Since: 0.9.7
  **/
@@ -189,12 +204,12 @@
 
 /**
  * hb_set_has:
- * @set: a set.
- * @codepoint:
+ * @set: A set
+ * @codepoint: The element to query
  *
+ * Tests whether @codepoint belongs to @set.
  *
- *
- * Return value:
+ * Return value: %true if @codepoint is in @set, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -207,10 +222,10 @@
 
 /**
  * hb_set_add:
- * @set: a set.
- * @codepoint:
+ * @set: A set
+ * @codepoint: The element to add to @set
  *
- *
+ * Adds @codepoint to @set.
  *
  * Since: 0.9.2
  **/
@@ -223,11 +238,12 @@
 
 /**
  * hb_set_add_range:
- * @set: a set.
- * @first:
- * @last:
+ * @set: A set
+ * @first: The first element to add to @set
+ * @last: The final element to add to @set
  *
- *
+ * Adds all of the elements from @first to @last
+ * (inclusive) to @set.
  *
  * Since: 0.9.7
  **/
@@ -241,10 +257,10 @@
 
 /**
  * hb_set_del:
- * @set: a set.
- * @codepoint:
+ * @set: A set
+ * @codepoint: Removes @codepoint from @set
  *
- *
+ * Removes @codepoint from @set.
  *
  * Since: 0.9.2
  **/
@@ -257,11 +273,12 @@
 
 /**
  * hb_set_del_range:
- * @set: a set.
- * @first:
- * @last:
+ * @set: A set
+ * @first: The first element to remove from @set
+ * @last: The final element to remove from @set
  *
- *
+ * Removes all of the elements from @first to @last
+ * (inclusive) from @set.
  *
  * Since: 0.9.7
  **/
@@ -275,12 +292,13 @@
 
 /**
  * hb_set_is_equal:
- * @set: a set.
- * @other: other set.
+ * @set: A set
+ * @other: Another set
  *
+ * Tests whether @set and @other are equal (contain the same
+ * elements).
  *
- *
- * Return value: %TRUE if the two sets are equal, %FALSE otherwise.
+ * Return value: %true if the two sets are equal, %false otherwise.
  *
  * Since: 0.9.7
  **/
@@ -293,12 +311,12 @@
 
 /**
  * hb_set_is_subset:
- * @set: a set.
- * @larger_set: other set.
+ * @set: A set
+ * @larger_set: Another set
  *
+ * Tests whether @set is a subset of @larger_set.
  *
- *
- * Return value: %TRUE if the @set is a subset of (or equal to) @larger_set, %FALSE otherwise.
+ * Return value: %true if the @set is a subset of (or equal to) @larger_set, %false otherwise.
  *
  * Since: 1.8.1
  **/
@@ -311,10 +329,10 @@
 
 /**
  * hb_set_set:
- * @set: a set.
- * @other:
+ * @set: A set
+ * @other: Another set
  *
- *
+ * Makes the contents of @set equal to the contents of @other.
  *
  * Since: 0.9.2
  **/
@@ -327,10 +345,10 @@
 
 /**
  * hb_set_union:
- * @set: a set.
- * @other:
+ * @set: A set
+ * @other: Another set
  *
- *
+ * Makes @set the union of @set and @other.
  *
  * Since: 0.9.2
  **/
@@ -343,10 +361,10 @@
 
 /**
  * hb_set_intersect:
- * @set: a set.
- * @other:
+ * @set: A set
+ * @other: Another set
  *
- *
+ * Makes @set the intersection of @set and @other.
  *
  * Since: 0.9.2
  **/
@@ -359,10 +377,10 @@
 
 /**
  * hb_set_subtract:
- * @set: a set.
- * @other:
+ * @set: A set
+ * @other: Another set
  *
- *
+ * Subtracts the contents of @other from @set.
  *
  * Since: 0.9.2
  **/
@@ -375,10 +393,11 @@
 
 /**
  * hb_set_symmetric_difference:
- * @set: a set.
- * @other:
+ * @set: A set
+ * @other: Another set
  *
- *
+ * Makes @set the symmetric difference of @set
+ * and @other.
  *
  * Since: 0.9.2
  **/
@@ -392,9 +411,9 @@
 #ifndef HB_DISABLE_DEPRECATED
 /**
  * hb_set_invert:
- * @set: a set.
+ * @set: A set
  *
- *
+ * Inverts the contents of @set.
  *
  * Since: 0.9.10
  *
@@ -408,11 +427,11 @@
 
 /**
  * hb_set_get_population:
- * @set: a set.
+ * @set: A set
  *
- * Returns the number of numbers in the set.
+ * Returns the number of elements in the set.
  *
- * Return value: set population.
+ * Return value: The population of @set
  *
  * Since: 0.9.7
  **/
@@ -424,11 +443,11 @@
 
 /**
  * hb_set_get_min:
- * @set: a set.
+ * @set: A set
  *
- * Finds the minimum number in the set.
+ * Finds the smallest element in the set.
  *
- * Return value: minimum of the set, or %HB_SET_VALUE_INVALID if set is empty.
+ * Return value: minimum of @set, or #HB_SET_VALUE_INVALID if @set is empty.
  *
  * Since: 0.9.7
  **/
@@ -440,11 +459,11 @@
 
 /**
  * hb_set_get_max:
- * @set: a set.
+ * @set: A set
  *
- * Finds the maximum number in the set.
+ * Finds the largest element in the set.
  *
- * Return value: minimum of the set, or %HB_SET_VALUE_INVALID if set is empty.
+ * Return value: maximum of @set, or #HB_SET_VALUE_INVALID if @set is empty.
  *
  * Since: 0.9.7
  **/
@@ -456,14 +475,15 @@
 
 /**
  * hb_set_next:
- * @set: a set.
- * @codepoint: (inout):
+ * @set: A set
+ * @codepoint: (inout): Input = Code point to query
+ *             Output = Code point retrieved
  *
- * Gets the next number in @set that is greater than current value of @codepoint.
+ * Fetches the next element in @set that is greater than current value of @codepoint.
  *
- * Set @codepoint to %HB_SET_VALUE_INVALID to get started.
+ * Set @codepoint to #HB_SET_VALUE_INVALID to get started.
  *
- * Return value: whether there was a next value.
+ * Return value: %true if there was a next value, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -476,14 +496,15 @@
 
 /**
  * hb_set_previous:
- * @set: a set.
- * @codepoint: (inout):
+ * @set: A set
+ * @codepoint: (inout): Input = Code point to query
+ *             Output = Code point retrieved
  *
- * Gets the previous number in @set that is lower than current value of @codepoint.
+ * Fetches the previous element in @set that is lower than current value of @codepoint.
  *
- * Set @codepoint to %HB_SET_VALUE_INVALID to get started.
+ * Set @codepoint to #HB_SET_VALUE_INVALID to get started.
  *
- * Return value: whether there was a previous value.
+ * Return value: %true if there was a previous value, %false otherwise
  *
  * Since: 1.8.0
  **/
@@ -496,16 +517,17 @@
 
 /**
  * hb_set_next_range:
- * @set: a set.
- * @first: (out): output first codepoint in the range.
- * @last: (inout): input current last and output last codepoint in the range.
+ * @set: A set
+ * @first: (out): The first code point in the range
+ * @last: (inout): Input = The current last code point in the range
+ *         Output = The last code point in the range
  *
- * Gets the next consecutive range of numbers in @set that
+ * Fetches the next consecutive range of elements in @set that
  * are greater than current value of @last.
  *
- * Set @last to %HB_SET_VALUE_INVALID to get started.
+ * Set @last to #HB_SET_VALUE_INVALID to get started.
  *
- * Return value: whether there was a next range.
+ * Return value: %true if there was a next range, %false otherwise
  *
  * Since: 0.9.7
  **/
@@ -519,16 +541,17 @@
 
 /**
  * hb_set_previous_range:
- * @set: a set.
- * @first: (inout): input current first and output first codepoint in the range.
- * @last: (out): output last codepoint in the range.
+ * @set: A set
+ * @first: (inout): Input = The current first code point in the range
+ *         Output = The first code point in the range
+ * @last: (out): The last code point in the range
  *
- * Gets the previous consecutive range of numbers in @set that
- * are less than current value of @first.
+ * Fetches the previous consecutive range of elements in @set that
+ * are greater than current value of @last.
  *
- * Set @first to %HB_SET_VALUE_INVALID to get started.
+ * Set @first to #HB_SET_VALUE_INVALID to get started.
  *
- * Return value: whether there was a previous range.
+ * Return value: %true if there was a previous range, %false otherwise
  *
  * Since: 1.8.0
  **/
diff --git a/src/hb-set.h b/src/hb-set.h
index ed0e05d..0ad27f4 100644
--- a/src/hb-set.h
+++ b/src/hb-set.h
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
@@ -36,11 +36,24 @@
 HB_BEGIN_DECLS
 
 
-/*
+/**
+ * HB_SET_VALUE_INVALID:
+ *
+ * Unset #hb_set_t value.
+ *
  * Since: 0.9.21
  */
 #define HB_SET_VALUE_INVALID ((hb_codepoint_t) -1)
 
+/**
+ * hb_set_t:
+ *
+ * Data type for holding a set of integers. #hb_set_t's are
+ * used to gather and contain glyph IDs, Unicode code
+ * points, and various other collections of discrete 
+ * values.
+ *
+ **/
 typedef struct hb_set_t hb_set_t;
 
 
diff --git a/src/hb-set.hh b/src/hb-set.hh
index b6e2086..40e44d7 100644
--- a/src/hb-set.hh
+++ b/src/hb-set.hh
@@ -244,7 +244,7 @@
 
   bool resize (unsigned int count)
   {
-    if (unlikely (!successful)) return false;
+    if (unlikely (count > pages.length && !successful)) return false;
     if (!pages.resize (count) || !page_map.resize (count))
     {
       pages.resize (page_map.length);
@@ -266,9 +266,9 @@
   {
     if (unlikely (hb_object_is_immutable (this)))
       return;
-    population = 0;
-    page_map.resize (0);
-    pages.resize (0);
+
+    if (resize (0))
+      population = 0;
   }
   bool is_empty () const
   {
@@ -389,6 +389,11 @@
   {
     if (ds <= de)
     {
+      // Pre-allocate the workspace that compact() will need so we can bail on allocation failure
+      // before attempting to rewrite the page map.
+      hb_vector_t<unsigned> compact_workspace;
+      if (unlikely (!allocate_compact_workspace (compact_workspace))) return;
+
       unsigned int write_index = 0;
       for (unsigned int i = 0; i < page_map.length; i++)
       {
@@ -396,11 +401,12 @@
 	if (m < ds || de < m)
 	  page_map[write_index++] = page_map[i];
       }
-      compact (write_index);
+      compact (compact_workspace, write_index);
       resize (write_index);
     }
   }
 
+
   public:
   void del_range (hb_codepoint_t a, hb_codepoint_t b)
   {
@@ -512,20 +518,37 @@
     return true;
   }
 
-  void compact (unsigned int length)
+  bool allocate_compact_workspace(hb_vector_t<unsigned>& workspace)
   {
-    hb_vector_t<uint32_t> old_index_to_page_map_index;
-    old_index_to_page_map_index.resize(pages.length);
-    for (uint32_t i = 0; i < old_index_to_page_map_index.length; i++)
-      old_index_to_page_map_index[i] = 0xFFFFFFFF;
+    if (unlikely(!workspace.resize (pages.length)))
+    {
+      successful = false;
+      return false;
+    }
 
-    for (uint32_t i = 0; i < length; i++)
+    return true;
+  }
+
+
+  /*
+   * workspace should be a pre-sized vector allocated to hold at exactly pages.length
+   * elements.
+   */
+  void compact (hb_vector_t<unsigned>& workspace,
+                unsigned int length)
+  {
+    assert(workspace.length == pages.length);
+    hb_vector_t<unsigned>& old_index_to_page_map_index = workspace;
+
+    hb_fill (old_index_to_page_map_index.writer(), 0xFFFFFFFF);
+    /* TODO(iter) Rewrite as dagger? */
+    for (unsigned i = 0; i < length; i++)
       old_index_to_page_map_index[page_map[i].index] =  i;
 
     compact_pages (old_index_to_page_map_index);
   }
 
-  void compact_pages (const hb_vector_t<uint32_t>& old_index_to_page_map_index)
+  void compact_pages (const hb_vector_t<unsigned>& old_index_to_page_map_index)
   {
     unsigned int write_index = 0;
     for (unsigned int i = 0; i < pages.length; i++)
@@ -543,6 +566,9 @@
   template <typename Op>
   void process (const Op& op, const hb_set_t *other)
   {
+    const bool passthru_left = op (1, 0);
+    const bool passthru_right = op (0, 1);
+
     if (unlikely (!successful)) return;
 
     dirty ();
@@ -554,11 +580,17 @@
     unsigned int count = 0, newCount = 0;
     unsigned int a = 0, b = 0;
     unsigned int write_index = 0;
+
+    // Pre-allocate the workspace that compact() will need so we can bail on allocation failure
+    // before attempting to rewrite the page map.
+    hb_vector_t<unsigned> compact_workspace;
+    if (!passthru_left && unlikely (!allocate_compact_workspace (compact_workspace))) return;
+
     for (; a < na && b < nb; )
     {
       if (page_map[a].major == other->page_map[b].major)
       {
-	if (!Op::passthru_left)
+	if (!passthru_left)
 	{
 	  // Move page_map entries that we're keeping from the left side set
 	  // to the front of the page_map vector. This isn't necessary if
@@ -575,27 +607,27 @@
       }
       else if (page_map[a].major < other->page_map[b].major)
       {
-	if (Op::passthru_left)
+	if (passthru_left)
 	  count++;
 	a++;
       }
       else
       {
-	if (Op::passthru_right)
+	if (passthru_right)
 	  count++;
 	b++;
       }
     }
-    if (Op::passthru_left)
+    if (passthru_left)
       count += na - a;
-    if (Op::passthru_right)
+    if (passthru_right)
       count += nb - b;
 
-    if (!Op::passthru_left)
+    if (!passthru_left)
     {
       na  = write_index;
       next_page = write_index;
-      compact (write_index);
+      compact (compact_workspace, write_index);
     }
 
     if (!resize (count))
@@ -619,7 +651,7 @@
       else if (page_map[a - 1].major > other->page_map[b - 1].major)
       {
 	a--;
-	if (Op::passthru_left)
+	if (passthru_left)
 	{
 	  count--;
 	  page_map[count] = page_map[a];
@@ -628,7 +660,7 @@
       else
       {
 	b--;
-	if (Op::passthru_right)
+	if (passthru_right)
 	{
 	  count--;
 	  page_map[count].major = other->page_map[b].major;
@@ -637,14 +669,14 @@
 	}
       }
     }
-    if (Op::passthru_left)
+    if (passthru_left)
       while (a)
       {
 	a--;
 	count--;
 	page_map[count] = page_map [a];
       }
-    if (Op::passthru_right)
+    if (passthru_right)
       while (b)
       {
 	b--;
@@ -655,6 +687,9 @@
       }
     assert (!count);
     if (pages.length > newCount)
+      // This resize() doesn't need to be checked because we can't get here
+      // if the set is currently in_error() and this only resizes downwards
+      // which will always succeed if the set is not in_error().
       resize (newCount);
   }
 
diff --git a/src/hb-shape-plan.cc b/src/hb-shape-plan.cc
index 2e03003..0d9eadd 100644
--- a/src/hb-shape-plan.cc
+++ b/src/hb-shape-plan.cc
@@ -329,12 +329,12 @@
  * @shape_plan: A shaping plan
  * @key: The user-data key to set
  * @data: A pointer to the user data
- * @destroy: A callback to call when @data is not needed anymore
+ * @destroy: (nullable): A callback to call when @data is not needed anymore
  * @replace: Whether to replace an existing data with the same key
  *
  * Attaches a user-data key/data pair to the given shaping plan. 
  *
- * Return value:
+ * Return value: %true if success, %false otherwise.
  *
  * Since: 0.9.7
  **/
@@ -401,7 +401,8 @@
     return true;
 
   assert (!hb_object_is_immutable (buffer));
-  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
+
+  buffer->assert_unicode ();
 
   if (unlikely (hb_object_is_inert (shape_plan)))
     return false;
@@ -438,7 +439,7 @@
  * Executes the given shaping plan on the specified buffer, using
  * the given @font and @features.
  *
- * Return value: 
+ * Return value: %true if success, %false otherwise.
  *
  * Since: 0.9.7
  **/
diff --git a/src/hb-shape-plan.h b/src/hb-shape-plan.h
index 336524e..fc7c041 100644
--- a/src/hb-shape-plan.h
+++ b/src/hb-shape-plan.h
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index a3debce..c442f44 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -111,10 +111,10 @@
  * hb_shape_full:
  * @font: an #hb_font_t to use for shaping
  * @buffer: an #hb_buffer_t to shape
- * @features: (array length=num_features) (allow-none): an array of user
+ * @features: (array length=num_features) (nullable): an array of user
  *    specified #hb_feature_t or %NULL
  * @num_features: the length of @features array
- * @shaper_list: (array zero-terminated=1) (allow-none): a %NULL-terminated
+ * @shaper_list: (array zero-terminated=1) (nullable): a %NULL-terminated
  *    array of shapers to use or %NULL
  *
  * See hb_shape() for details. If @shaper_list is not %NULL, the specified
@@ -146,7 +146,7 @@
  * hb_shape:
  * @font: an #hb_font_t to use for shaping
  * @buffer: an #hb_buffer_t to shape
- * @features: (array length=num_features) (allow-none): an array of user
+ * @features: (array length=num_features) (nullable): an array of user
  *    specified #hb_feature_t or %NULL
  * @num_features: the length of @features array
  *
diff --git a/src/hb-shape.h b/src/hb-shape.h
index 39507ff..922f8c0 100644
--- a/src/hb-shape.h
+++ b/src/hb-shape.h
@@ -26,7 +26,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
diff --git a/src/hb-style.cc b/src/hb-style.cc
index 86b9f7d..2f45d11 100644
--- a/src/hb-style.cc
+++ b/src/hb-style.cc
@@ -65,6 +65,7 @@
   HB_STYLE_TAG_WIDTH		= HB_TAG ('w','d','t','h'),
   HB_STYLE_TAG_WEIGHT		= HB_TAG ('w','g','h','t'),
 
+  /*< private >*/
   _HB_STYLE_TAG_MAX_VALUE	= HB_TAG_MAX_SIGNED /*< skip >*/
 } hb_style_tag_t;
 
diff --git a/src/hb-style.h b/src/hb-style.h
index 1209c79..f5776ce 100644
--- a/src/hb-style.h
+++ b/src/hb-style.h
@@ -22,7 +22,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 24beada..d055783 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -88,10 +88,17 @@
 			 &lookup_indices);
   _remap_indexes (&lookup_indices, gsub_lookups);
 
-  //closure features
+  // Collect and prune features
   hb_set_t feature_indices;
-  gsub->closure_features (gsub_lookups, &feature_indices);
+  hb_ot_layout_collect_features (face,
+                                 HB_OT_TAG_GSUB,
+                                 nullptr,
+                                 nullptr,
+                                 nullptr,
+                                 &feature_indices);
+  gsub->prune_features (gsub_lookups, &feature_indices);
   _remap_indexes (&feature_indices, gsub_features);
+
   gsub.destroy ();
 }
 
@@ -114,9 +121,15 @@
 			 &lookup_indices);
   _remap_indexes (&lookup_indices, gpos_lookups);
 
-  //closure features
+  // Collect and prune features
   hb_set_t feature_indices;
-  gpos->closure_features (gpos_lookups, &feature_indices);
+  hb_ot_layout_collect_features (face,
+                                 HB_OT_TAG_GPOS,
+                                 nullptr,
+                                 nullptr,
+                                 nullptr,
+                                 &feature_indices);
+  gpos->prune_features (gpos_lookups, &feature_indices);
   _remap_indexes (&feature_indices, gpos_features);
   gpos.destroy ();
 }
@@ -243,7 +256,11 @@
 
 #ifndef HB_NO_VAR
   if (close_over_gdef)
-    _collect_layout_variation_indices (plan->source, plan->_glyphset, plan->gpos_lookups, plan->layout_variation_indices, plan->layout_variation_idx_map);
+    _collect_layout_variation_indices (plan->source,
+                                       plan->_glyphset_gsub,
+                                       plan->gpos_lookups,
+                                       plan->layout_variation_indices,
+                                       plan->layout_variation_idx_map);
 #endif
 
 #ifndef HB_NO_SUBSET_CFF
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index 71de715..7470bb1 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -276,12 +276,12 @@
  * @ufuncs: The Unicode-functions structure
  * @key: The user-data key
  * @data: A pointer to the user data
- * @destroy: A callback to call when @data is not needed anymore
+ * @destroy: (nullable): A callback to call when @data is not needed anymore
  * @replace: Whether to replace an existing data with the same key
  *
  * Attaches a user-data key/data pair to the specified Unicode-functions structure. 
  *
- * Return value: %true if success, false otherwise
+ * Return value: %true if success, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -340,7 +340,7 @@
  * Tests whether the specified Unicode-functions structure
  * is immutable.
  *
- * Return value: %true if @ufuncs is immutable, false otherwise
+ * Return value: %true if @ufuncs is immutable, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -421,7 +421,7 @@
  * Calls the composition function of the specified
  * Unicode-functions structure @ufuncs.
  *
- * Return value: %true if @a and @b composed, false otherwise
+ * Return value: %true if @a and @b composed, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -446,7 +446,7 @@
  * Calls the decomposition function of the specified
  * Unicode-functions structure @ufuncs.
  *
- * Return value: %true if @ab was decomposed, false otherwise
+ * Return value: %true if @ab was decomposed, %false otherwise
  *
  * Since: 0.9.2
  **/
@@ -469,7 +469,7 @@
  * Fetches the compatibility decomposition of a Unicode
  * code point. Deprecated.
  *
- * Return value:
+ * Return value: length of @decomposed.
  *
  * Since: 0.9.2
  * Deprecated: 2.0.0
diff --git a/src/hb-unicode.h b/src/hb-unicode.h
index 2353f0f..c04ee15 100644
--- a/src/hb-unicode.h
+++ b/src/hb-unicode.h
@@ -28,7 +28,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
@@ -41,7 +41,9 @@
 
 
 /**
- * HB_UNICODE_MAX
+ * HB_UNICODE_MAX:
+ *
+ * Maximum valid Unicode code point.
  *
  * Since: 1.9.0
  **/
@@ -50,36 +52,36 @@
 
 /**
  * hb_unicode_general_category_t:
- * @HB_UNICODE_GENERAL_CATEGORY_CONTROL:              (Cc)
- * @HB_UNICODE_GENERAL_CATEGORY_FORMAT:		      (Cf)
- * @HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED:	      (Cn)
- * @HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE:	      (Co)
- * @HB_UNICODE_GENERAL_CATEGORY_SURROGATE:	      (Cs)
- * @HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER:     (Ll)
- * @HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER:      (Lm)
- * @HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER:	      (Lo)
- * @HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER:     (Lt)
- * @HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER:     (Lu)
- * @HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK:	      (Mc)
- * @HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK:	      (Me)
- * @HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK:     (Mn)
- * @HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER:	      (Nd)
- * @HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER:	      (Nl)
- * @HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER:	      (No)
- * @HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION:  (Pc)
- * @HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION:     (Pd)
- * @HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION:    (Pe)
- * @HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION:    (Pf)
- * @HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION:  (Pi)
- * @HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION:    (Po)
- * @HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION:     (Ps)
- * @HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL:      (Sc)
- * @HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL:      (Sk)
- * @HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL:	      (Sm)
- * @HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL:	      (So)
- * @HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR:	      (Zl)
- * @HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR:  (Zp)
- * @HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR:      (Zs)
+ * @HB_UNICODE_GENERAL_CATEGORY_CONTROL:              [Cc]
+ * @HB_UNICODE_GENERAL_CATEGORY_FORMAT:		      [Cf]
+ * @HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED:	      [Cn]
+ * @HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE:	      [Co]
+ * @HB_UNICODE_GENERAL_CATEGORY_SURROGATE:	      [Cs]
+ * @HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER:     [Ll]
+ * @HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER:      [Lm]
+ * @HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER:	      [Lo]
+ * @HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER:     [Lt]
+ * @HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER:     [Lu]
+ * @HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK:	      [Mc]
+ * @HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK:	      [Me]
+ * @HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK:     [Mn]
+ * @HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER:	      [Nd]
+ * @HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER:	      [Nl]
+ * @HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER:	      [No]
+ * @HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION:  [Pc]
+ * @HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION:     [Pd]
+ * @HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION:    [Pe]
+ * @HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION:    [Pf]
+ * @HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION:  [Pi]
+ * @HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION:    [Po]
+ * @HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION:     [Ps]
+ * @HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL:      [Sc]
+ * @HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL:      [Sk]
+ * @HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL:	      [Sm]
+ * @HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL:	      [So]
+ * @HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR:	      [Zl]
+ * @HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR:  [Zp]
+ * @HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR:      [Zs]
  *
  * Data type for the "General_Category" (gc) property from
  * the Unicode Character Database.
@@ -122,61 +124,63 @@
 
 /**
  * hb_unicode_combining_class_t:
- * @HB_UNICODE_COMBINING_CLASS_NOT_REORDERED
- * @HB_UNICODE_COMBINING_CLASS_OVERLAY
- * @HB_UNICODE_COMBINING_CLASS_NUKTA
- * @HB_UNICODE_COMBINING_CLASS_KANA_VOICING
- * @HB_UNICODE_COMBINING_CLASS_VIRAMA
- * @HB_UNICODE_COMBINING_CLASS_CCC11: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC12: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC13: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC14: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC15: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC16: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC17: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC18: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC19: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC20: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC21: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC22: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC23: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC24: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC25: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC26: (Hebrew)
- * @HB_UNICODE_COMBINING_CLASS_CCC28: (Arabic)
- * @HB_UNICODE_COMBINING_CLASS_CCC29: (Arabic)
- * @HB_UNICODE_COMBINING_CLASS_CCC30: (Arabic)
- * @HB_UNICODE_COMBINING_CLASS_CCC31: (Arabic)
- * @HB_UNICODE_COMBINING_CLASS_CCC32: (Arabic)
- * @HB_UNICODE_COMBINING_CLASS_CCC33: (Arabic)
- * @HB_UNICODE_COMBINING_CLASS_CCC34: (Arabic)
- * @HB_UNICODE_COMBINING_CLASS_CCC35: (Arabic)
- * @HB_UNICODE_COMBINING_CLASS_CCC36: (Syriac)
- * @HB_UNICODE_COMBINING_CLASS_CCC84: (Telugu)
- * @HB_UNICODE_COMBINING_CLASS_CCC91: (Telugu)
- * @HB_UNICODE_COMBINING_CLASS_CCC103: (Thai)
- * @HB_UNICODE_COMBINING_CLASS_CCC107: (Thai)
- * @HB_UNICODE_COMBINING_CLASS_CCC118: (Lao)
- * @HB_UNICODE_COMBINING_CLASS_CCC122: (Lao)
- * @HB_UNICODE_COMBINING_CLASS_CCC129: (Tibetan)
- * @HB_UNICODE_COMBINING_CLASS_CCC130: (Tibetan)
- * @HB_UNICODE_COMBINING_CLASS_CCC133: (Tibetan)
- * @HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT
- * @HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW
- * @HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE
- * @HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT
- * @HB_UNICODE_COMBINING_CLASS_BELOW_LEFT
- * @HB_UNICODE_COMBINING_CLASS_BELOW
- * @HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT
- * @HB_UNICODE_COMBINING_CLASS_LEFT
- * @HB_UNICODE_COMBINING_CLASS_RIGHT
- * @HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT
- * @HB_UNICODE_COMBINING_CLASS_ABOVE
- * @HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT
- * @HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW
- * @HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE
- * @HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT
- * @HB_UNICODE_COMBINING_CLASS_INVALID: 255
+ * @HB_UNICODE_COMBINING_CLASS_NOT_REORDERED: Spacing and enclosing marks; also many vowel and consonant signs, even if nonspacing
+ * @HB_UNICODE_COMBINING_CLASS_OVERLAY: Marks which overlay a base letter or symbol
+ * @HB_UNICODE_COMBINING_CLASS_NUKTA: Diacritic nukta marks in Brahmi-derived scripts
+ * @HB_UNICODE_COMBINING_CLASS_KANA_VOICING: Hiragana/Katakana voicing marks
+ * @HB_UNICODE_COMBINING_CLASS_VIRAMA: Viramas
+ * @HB_UNICODE_COMBINING_CLASS_CCC10: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC11: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC12: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC13: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC14: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC15: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC16: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC17: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC18: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC19: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC20: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC21: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC22: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC23: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC24: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC25: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC26: [Hebrew]
+ * @HB_UNICODE_COMBINING_CLASS_CCC27: [Arabic]
+ * @HB_UNICODE_COMBINING_CLASS_CCC28: [Arabic]
+ * @HB_UNICODE_COMBINING_CLASS_CCC29: [Arabic]
+ * @HB_UNICODE_COMBINING_CLASS_CCC30: [Arabic]
+ * @HB_UNICODE_COMBINING_CLASS_CCC31: [Arabic]
+ * @HB_UNICODE_COMBINING_CLASS_CCC32: [Arabic]
+ * @HB_UNICODE_COMBINING_CLASS_CCC33: [Arabic]
+ * @HB_UNICODE_COMBINING_CLASS_CCC34: [Arabic]
+ * @HB_UNICODE_COMBINING_CLASS_CCC35: [Arabic]
+ * @HB_UNICODE_COMBINING_CLASS_CCC36: [Syriac]
+ * @HB_UNICODE_COMBINING_CLASS_CCC84: [Telugu]
+ * @HB_UNICODE_COMBINING_CLASS_CCC91: [Telugu]
+ * @HB_UNICODE_COMBINING_CLASS_CCC103: [Thai]
+ * @HB_UNICODE_COMBINING_CLASS_CCC107: [Thai]
+ * @HB_UNICODE_COMBINING_CLASS_CCC118: [Lao]
+ * @HB_UNICODE_COMBINING_CLASS_CCC122: [Lao]
+ * @HB_UNICODE_COMBINING_CLASS_CCC129: [Tibetan]
+ * @HB_UNICODE_COMBINING_CLASS_CCC130: [Tibetan]
+ * @HB_UNICODE_COMBINING_CLASS_CCC133: [Tibetan]
+ * @HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT: Marks attached at the bottom left
+ * @HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW: Marks attached directly below
+ * @HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE: Marks attached directly above
+ * @HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT: Marks attached at the top right
+ * @HB_UNICODE_COMBINING_CLASS_BELOW_LEFT: Distinct marks at the bottom left
+ * @HB_UNICODE_COMBINING_CLASS_BELOW: Distinct marks directly below
+ * @HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT: Distinct marks at the bottom right
+ * @HB_UNICODE_COMBINING_CLASS_LEFT: Distinct marks to the left
+ * @HB_UNICODE_COMBINING_CLASS_RIGHT: Distinct marks to the right
+ * @HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT: Distinct marks at the top left
+ * @HB_UNICODE_COMBINING_CLASS_ABOVE: Distinct marks directly above
+ * @HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT: Distinct marks at the top right
+ * @HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW: Distinct marks subtending two bases
+ * @HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE: Distinct marks extending above two bases
+ * @HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT: Greek iota subscript only
+ * @HB_UNICODE_COMBINING_CLASS_INVALID: Invalid combining class
  *
  * Data type for the Canonical_Combining_Class (ccc) property
  * from the Unicode Character Database.
@@ -335,11 +339,16 @@
 
 /**
  * hb_unicode_combining_class_func_t:
+ * @ufuncs: A Unicode-functions structure
+ * @unicode: The code point to query
+ * @user_data: User data pointer passed by the caller
  *
  * A virtual method for the #hb_unicode_funcs_t structure.
  *
  * This method should retrieve the Canonical Combining Class (ccc)
  * property for a specified Unicode code point. 
+ *
+ * Return value: The #hb_unicode_combining_class_t of @unicode
  * 
  **/
 typedef hb_unicode_combining_class_t	(*hb_unicode_combining_class_func_t)	(hb_unicode_funcs_t *ufuncs,
@@ -348,12 +357,17 @@
 
 /**
  * hb_unicode_general_category_func_t:
+ * @ufuncs: A Unicode-functions structure
+ * @unicode: The code point to query
+ * @user_data: User data pointer passed by the caller
  *
  * A virtual method for the #hb_unicode_funcs_t structure.
  *
  * This method should retrieve the General Category property for
  * a specified Unicode code point.
  * 
+ * Return value: The #hb_unicode_general_category_t of @unicode
+ *
  **/
 typedef hb_unicode_general_category_t	(*hb_unicode_general_category_func_t)	(hb_unicode_funcs_t *ufuncs,
 										 hb_codepoint_t      unicode,
@@ -361,6 +375,9 @@
 
 /**
  * hb_unicode_mirroring_func_t:
+ * @ufuncs: A Unicode-functions structure
+ * @unicode: The code point to query
+ * @user_data: User data pointer passed by the caller
  *
  * A virtual method for the #hb_unicode_funcs_t structure.
  *
@@ -371,6 +388,8 @@
  * Bi-Directional Mirroring Glyph defined, the method should
  * return the original code point.</note>
  * 
+ * Return value: The #hb_codepoint_t of the Mirroring Glyph for @unicode
+ *
  **/
 typedef hb_codepoint_t			(*hb_unicode_mirroring_func_t)		(hb_unicode_funcs_t *ufuncs,
 										 hb_codepoint_t      unicode,
@@ -378,11 +397,16 @@
 
 /**
  * hb_unicode_script_func_t:
+ * @ufuncs: A Unicode-functions structure
+ * @unicode: The code point to query
+ * @user_data: User data pointer passed by the caller
  *
  * A virtual method for the #hb_unicode_funcs_t structure.
  *
  * This method should retrieve the Script property for a 
  * specified Unicode code point.
+ *
+ * Return value: The #hb_script_t of @unicode
  * 
  **/
 typedef hb_script_t			(*hb_unicode_script_func_t)		(hb_unicode_funcs_t *ufuncs,
@@ -391,6 +415,11 @@
 
 /**
  * hb_unicode_compose_func_t:
+ * @ufuncs: A Unicode-functions structure
+ * @a: The first code point to compose
+ * @b: The second code point to compose
+ * @ab: (out): The composed code point
+ * @user_data: user data pointer passed by the caller
  *
  * A virtual method for the #hb_unicode_funcs_t structure.
  *
@@ -400,6 +429,8 @@
  * The method must return an #hb_bool_t indicating the success
  * of the composition.
  * 
+ * Return value: %true is @a,@b composed, %false otherwise
+ *
  **/
 typedef hb_bool_t			(*hb_unicode_compose_func_t)		(hb_unicode_funcs_t *ufuncs,
 										 hb_codepoint_t      a,
@@ -409,6 +440,11 @@
 
 /**
  * hb_unicode_decompose_func_t:
+ * @ufuncs: A Unicode-functions structure
+ * @ab: The code point to decompose
+ * @a: (out): The first decomposed code point
+ * @b: (out): The second decomposed code point
+ * @user_data: user data pointer passed by the caller
  *
  * A virtual method for the #hb_unicode_funcs_t structure.
  *
@@ -417,6 +453,8 @@
  * output parameters (if successful). The method must return an
  * #hb_bool_t indicating the success of the composition.
  * 
+ * Return value: %true if @ab decomposed, %false otherwise
+ *
  **/
 typedef hb_bool_t			(*hb_unicode_decompose_func_t)		(hb_unicode_funcs_t *ufuncs,
 										 hb_codepoint_t      ab,
@@ -431,7 +469,7 @@
  * @ufuncs: A Unicode-functions structure
  * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
  * @user_data: Data to pass to @func
- * @destroy: The function to call when @user_data is not needed anymore
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
  * Sets the implementation function for #hb_unicode_combining_class_func_t.
  *
@@ -447,7 +485,7 @@
  * @ufuncs: A Unicode-functions structure
  * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
  * @user_data: Data to pass to @func
- * @destroy: The function to call when @user_data is not needed anymore
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
  * Sets the implementation function for #hb_unicode_general_category_func_t.
  *
@@ -463,7 +501,7 @@
  * @ufuncs: A Unicode-functions structure
  * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
  * @user_data: Data to pass to @func
- * @destroy: The function to call when @user_data is not needed anymore
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
  * Sets the implementation function for #hb_unicode_mirroring_func_t.
  *
@@ -479,7 +517,7 @@
  * @ufuncs: A Unicode-functions structure
  * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
  * @user_data: Data to pass to @func
- * @destroy: The function to call when @user_data is not needed anymore
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
  * Sets the implementation function for #hb_unicode_script_func_t.
  *
@@ -495,7 +533,7 @@
  * @ufuncs: A Unicode-functions structure
  * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
  * @user_data: Data to pass to @func
- * @destroy: The function to call when @user_data is not needed anymore
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
  * Sets the implementation function for #hb_unicode_compose_func_t.
  *
@@ -511,7 +549,7 @@
  * @ufuncs: A Unicode-functions structure
  * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
  * @user_data: Data to pass to @func
- * @destroy: The function to call when @user_data is not needed anymore
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
  *
  * Sets the implementation function for #hb_unicode_decompose_func_t.
  *
@@ -588,40 +626,12 @@
 hb_unicode_script (hb_unicode_funcs_t *ufuncs,
 		   hb_codepoint_t unicode);
 
-/**
- * hb_unicode_compose:
- * @ufuncs: The Unicode-functions structure
- * @a: The first code point to compose
- * @b: The second code point to compose
- * @ab: (out): The composed code point 
- *
- * Composes the code point sequence @a,@b by canonical equivalence into
- * code point @ab.
- *
- * Return value: True is @a,@b composed, false otherwise
- *
- * Since: 0.9.2
- **/
 HB_EXTERN hb_bool_t
 hb_unicode_compose (hb_unicode_funcs_t *ufuncs,
 		    hb_codepoint_t      a,
 		    hb_codepoint_t      b,
 		    hb_codepoint_t     *ab);
 
-/**
- * hb_unicode_decompose:
- * @ufuncs: The Unicode-functions structure
- * @ab: The code point to decompose
- * @a: (out): The first decomposed code point
- * @b: (out): The second decomposed code point
- *
- * Decomposes code point @ab by canonical equivalence, into code points
- * @a and @b.
- *
- * Return value: True if @ab decomposed, false otherwise
- *
- * Since: 0.9.2
- **/
 HB_EXTERN hb_bool_t
 hb_unicode_decompose (hb_unicode_funcs_t *ufuncs,
 		      hb_codepoint_t      ab,
diff --git a/src/hb-version.h b/src/hb-version.h
index 92d61b8..423339e 100644
--- a/src/hb-version.h
+++ b/src/hb-version.h
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
@@ -36,12 +36,41 @@
 HB_BEGIN_DECLS
 
 
+/**
+ * HB_VERSION_MAJOR:
+ *
+ * The major component of the library version available at compile-time.
+ */
 #define HB_VERSION_MAJOR 2
+/**
+ * HB_VERSION_MINOR:
+ *
+ * The minor component of the library version available at compile-time.
+ */
 #define HB_VERSION_MINOR 7
-#define HB_VERSION_MICRO 2
+/**
+ * HB_VERSION_MICRO:
+ *
+ * The micro component of the library version available at compile-time.
+ */
+#define HB_VERSION_MICRO 4
 
-#define HB_VERSION_STRING "2.7.2"
+/**
+ * HB_VERSION_STRING:
+ *
+ * A string literal containing the library version available at compile-time.
+ */
+#define HB_VERSION_STRING "2.7.4"
 
+/**
+ * HB_VERSION_ATLEAST:
+ * @major: the major component of the version number
+ * @minor: the minor component of the version number
+ * @micro: the micro component of the version number
+ *
+ * Tests the library version at compile-time against a minimum value,
+ * as three integer components.
+ */
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
diff --git a/src/hb-version.h.in b/src/hb-version.h.in
index 0ffd889..abcb73f 100644
--- a/src/hb-version.h.in
+++ b/src/hb-version.h.in
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#ifndef HB_H_IN
+#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
 #error "Include <hb.h> instead."
 #endif
 
@@ -36,12 +36,41 @@
 HB_BEGIN_DECLS
 
 
+/**
+ * HB_VERSION_MAJOR:
+ *
+ * The major component of the library version available at compile-time.
+ */
 #define HB_VERSION_MAJOR @HB_VERSION_MAJOR@
+/**
+ * HB_VERSION_MINOR:
+ *
+ * The minor component of the library version available at compile-time.
+ */
 #define HB_VERSION_MINOR @HB_VERSION_MINOR@
+/**
+ * HB_VERSION_MICRO:
+ *
+ * The micro component of the library version available at compile-time.
+ */
 #define HB_VERSION_MICRO @HB_VERSION_MICRO@
 
+/**
+ * HB_VERSION_STRING:
+ *
+ * A string literal containing the library version available at compile-time.
+ */
 #define HB_VERSION_STRING "@HB_VERSION@"
 
+/**
+ * HB_VERSION_ATLEAST:
+ * @major: the major component of the version number
+ * @minor: the minor component of the version number
+ * @micro: the micro component of the version number
+ *
+ * Tests the library version at compile-time against a minimum value,
+ * as three integer components.
+ */
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
diff --git a/src/meson.build b/src/meson.build
index 1929024..ec3b7c7 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -126,10 +126,11 @@
   'hb-ot-shape-complex-khmer.hh',
   'hb-ot-shape-complex-myanmar.cc',
   'hb-ot-shape-complex-myanmar.hh',
+  'hb-ot-shape-complex-syllabic.cc',
+  'hb-ot-shape-complex-syllabic.hh',
   'hb-ot-shape-complex-thai.cc',
-  'hb-ot-shape-complex-use-table.cc',
+  'hb-ot-shape-complex-use-table.hh',
   'hb-ot-shape-complex-use.cc',
-  'hb-ot-shape-complex-use.hh',
   'hb-ot-shape-complex-vowel-constraints.cc',
   'hb-ot-shape-complex-vowel-constraints.hh',
   'hb-ot-shape-complex.hh',
@@ -271,10 +272,8 @@
   'hb-subset-input.hh',
   'hb-subset-plan.cc',
   'hb-subset-plan.hh',
-  'hb-subset-plan.hh',
   'hb-subset.cc',
   'hb-subset.hh',
-  'hb-subset.hh',
 )
 
 hb_subset_headers = files('hb-subset.h')
@@ -299,7 +298,7 @@
       build_by_default: true,
       input: rl,
       output: hh,
-      command: [ragel_helper, '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'],
+      command: [ragel_helper, '@OUTPUT@', meson.current_source_dir(), '@INPUT@'],
     )
   endforeach
 endif
@@ -311,7 +310,7 @@
          hb_graphite2_sources + hb_uniscribe_sources + hb_gdi_sources +
          hb_directwrite_sources + hb_coretext_sources,
   command: [find_program('gen-harfbuzzcc.py'),
-            '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'],
+            '@OUTPUT@', meson.current_source_dir(), '@INPUT@'],
 )
 
 incsrc = include_directories('.')
@@ -475,21 +474,6 @@
     endforeach
   endif
 
-  check_programs = {
-    'dump-indic-data': ['dump-indic-data.cc', 'hb-ot-shape-complex-indic-table.cc'],
-    'dump-khmer-data': ['dump-khmer-data.cc', 'hb-ot-shape-complex-indic-table.cc'],
-    'dump-myanmar-data': ['dump-myanmar-data.cc', 'hb-ot-shape-complex-indic-table.cc'],
-    'dump-use-data': ['dump-use-data.cc', 'hb-ot-shape-complex-use-table.cc'],
-  }
-  foreach name, source : check_programs
-    executable(name, source,
-      include_directories: incconfig,
-      cpp_args: cpp_args,
-      dependencies: libharfbuzz_dep,
-      install: false,
-    )
-  endforeach
-
   compiled_tests = {
     'test-algs': ['test-algs.cc', 'hb-static.cc'],
     'test-array': ['test-array.cc'],
@@ -635,12 +619,7 @@
   )
 
   gir = find_program('g-ir-scanner', required: get_option('introspection'))
-  build_gir = gir.found()
-
-  build_gir = build_gir and not meson.is_cross_build()
-  if not build_gir and get_option('introspection').enabled()
-    error('Introspection support is requested but it isn\'t available in cross builds')
-  endif
+  build_gir = gir.found() and (not meson.is_cross_build() or get_option('introspection').enabled())
 
   build_gir = build_gir and get_option('default_library') != 'static'
   if not build_gir and get_option('introspection').enabled()
@@ -661,14 +640,8 @@
       header: 'hb-gobject.h',
       install: true,
       extra_args:  ['--cflags-begin',
-                    '-DHB_H',
-                    '-DHB_H_IN',
-                    '-DHB_OT_H',
-                    '-DHB_OT_H_IN',
-                    '-DHB_AAT_H',
-                    '-DHB_AAT_H_IN',
-                    '-DHB_GOBJECT_H',
-                    '-DHB_GOBJECT_H_IN',
+                    '-DHB_NO_SINGLE_HEADER_ERROR',
+                    '-DHAVE_GOBJECT',
                     '-DHB_EXTERN=',
                     '--cflags-end'])
   endif
diff --git a/src/ms-use/IndicSyllabicCategory-Additional.txt b/src/ms-use/IndicSyllabicCategory-Additional.txt
index 5624166..0912055 100644
--- a/src/ms-use/IndicSyllabicCategory-Additional.txt
+++ b/src/ms-use/IndicSyllabicCategory-Additional.txt
@@ -13,7 +13,6 @@
 193A          ; Bindu  # Mn       LIMBU SIGN KEMPHRENG
 AA29          ; Bindu  # Mn       CHAM VOWEL SIGN AA
 10A0D         ; Bindu  # Mn       KHAROSHTHI SIGN DOUBLE RING BELOW
-1172B         ; Bindu  # Mn       AHOM SIGN KILLER
 
 # ================================================
 
@@ -96,7 +95,10 @@
 16F00..16F4A ; Consonant # Lo   [75] MIAO LETTER PA..MIAO LETTER RTE
 16FE4        ; Consonant # Mn        KHITAN SMALL SCRIPT FILLER
 18B00..18CD5 ; Consonant # Lo  [470] KHITAN SMALL SCRIPT CHARACTER-18B00..KHITAN SMALL SCRIPT CHARACTER-18CD5
-1BC00..1BC99 ; Consonant # Lo  [139] DUPLOYAN LETTER H..DUPLOYAN AFFIX LOW ARROW
+1BC00..1BC6A ; Consonant # Lo  [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M
+1BC70..1BC7C ; Consonant # Lo   [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK 
+1BC80..1BC88 ; Consonant # Lo    [9] DUPLOYAN AFFIX HIGH ACUTE..DUPLOYAN AFFIX HIGH VERTICAL
+1BC90..1BC99 ; Consonant # Lo   [10] DUPLOYAN AFFIX LOW ACUTE..DUPLOYAN AFFIX LOW ARROW
 1E100..1E12C ; Consonant # Lo   [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W
 1E137..1E13D ; Consonant # Lm    [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
 1E14E        ; Consonant # Lo        NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ
diff --git a/src/update-unicode-tables.make b/src/update-unicode-tables.make
index 3053ac0..95edb9a 100755
--- a/src/update-unicode-tables.make
+++ b/src/update-unicode-tables.make
@@ -4,7 +4,7 @@
 	hb-ot-shape-complex-arabic-joining-list.hh \
 	hb-ot-shape-complex-arabic-table.hh hb-unicode-emoji-table.hh \
 	hb-ot-shape-complex-indic-table.cc hb-ot-tag-table.hh \
-	hb-ucd-table.hh hb-ot-shape-complex-use-table.cc \
+	hb-ucd-table.hh hb-ot-shape-complex-use-table.hh \
 	hb-ot-shape-complex-vowel-constraints.cc
 
 .PHONY: all clean packtab
@@ -21,7 +21,7 @@
 	./$^ > $@ || ($(RM) $@; false)
 hb-ucd-table.hh: gen-ucd-table.py ucd.nounihan.grouped.zip hb-common.h
 	./$^ > $@ || ($(RM) $@; false)
-hb-ot-shape-complex-use-table.cc: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt ArabicShaping.txt Blocks.txt ms-use/IndicSyllabicCategory-Additional.txt ms-use/IndicPositionalCategory-Additional.txt
+hb-ot-shape-complex-use-table.hh: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt ArabicShaping.txt Blocks.txt ms-use/IndicSyllabicCategory-Additional.txt ms-use/IndicPositionalCategory-Additional.txt
 	./$^ > $@ || ($(RM) $@; false)
 hb-ot-shape-complex-vowel-constraints.cc: gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt
 	./$^ > $@ || ($(RM) $@; false)
diff --git a/subprojects/cairo.wrap b/subprojects/cairo.wrap
index 2fd8fb1..afb2695 100644
--- a/subprojects/cairo.wrap
+++ b/subprojects/cairo.wrap
@@ -1,5 +1,5 @@
 [wrap-git]
 directory=cairo
-url=https://github.com/ebraminio/cairo.git
+url=https://gitlab.freedesktop.org/cairo/cairo.git
 depth=1
-revision=meson
+revision=1.17.4
diff --git a/subprojects/expat.wrap b/subprojects/expat.wrap
index 0b7c53e..53b7623 100644
--- a/subprojects/expat.wrap
+++ b/subprojects/expat.wrap
@@ -1,10 +1,9 @@
-[wrap-file]
-directory = expat-2.2.5
-
-source_url = https://github.com/libexpat/libexpat/releases/download/R_2_2_5/expat-2.2.5.tar.bz2
-source_filename = expat-2.2.5.tar.bz2
-source_hash = d9dc32efba7e74f788fcc4f212a43216fc37cf5f23f4c2339664d473353aedf6
-
-patch_url = https://wrapdb.mesonbuild.com/v1/projects/expat/2.2.5/4/get_zip
-patch_filename = expat-2.2.5-4-wrap.zip
-patch_hash = 25748839be2bbdd2ff586d1a05aa6fc37aeada75c78416df6e8347a6321abaac
+[wrap-file]

+directory = expat-2.2.9

+source_url = https://github.com/libexpat/libexpat/releases/download/R_2_2_9/expat-2.2.9.tar.xz

+source_filename = expat-2.2.9.tar.bz2

+source_hash = 1ea6965b15c2106b6bbe883397271c80dfa0331cdf821b2c319591b55eadc0a4

+patch_url = https://wrapdb.mesonbuild.com/v1/projects/expat/2.2.9/3/get_zip

+patch_filename = expat-2.2.9-3-wrap.zip

+patch_hash = e9aaace62e9a158b5e96f5c38c9f81f369179206acd87697653d777c0d3975d3

+

diff --git a/subprojects/fontconfig.wrap b/subprojects/fontconfig.wrap
index 6b18b2b..a8c933b 100644
--- a/subprojects/fontconfig.wrap
+++ b/subprojects/fontconfig.wrap
@@ -1,6 +1,6 @@
 [wrap-git]
 directory=fontconfig
-url=https://github.com/centricular/fontconfig.git
+url=https://gitlab.freedesktop.org/fontconfig/fontconfig.git
 depth=1
-push-url=git@github.com:centricular/fontconfig.git
-revision=meson
+push-url=git@gitlab.freedesktop.org/fontconfig/fontconfig.git
+revision=master
diff --git a/subprojects/google-benchmark.wrap b/subprojects/google-benchmark.wrap
index 180c6ee..876927d 100644
--- a/subprojects/google-benchmark.wrap
+++ b/subprojects/google-benchmark.wrap
@@ -1,8 +1,9 @@
 [wrap-file]
-directory = benchmark-1.4.1
-source_url = https://github.com/google/benchmark/archive/v1.4.1.zip
-source_filename = benchmark-1.4.1.zip
-source_hash = 61ae07eb5d4a0b02753419eb17a82b7d322786bb36ab62bd3df331a4d47c00a7
-patch_url = https://wrapdb.mesonbuild.com/v1/projects/google-benchmark/1.4.1/1/get_zip
-patch_filename = google-benchmark-1.4.1-1-wrap.zip
-patch_hash = 4cc5fe02ebd4fc82e110919b7977d7463eb2a99e4ecb9feca920eab6fd911d67
+directory = benchmark-1.5.2
+source_url = https://github.com/google/benchmark/archive/v1.5.2.zip
+source_filename = benchmark-1.5.2.zip
+source_hash = 21e6e096c9a9a88076b46bd38c33660f565fa050ca427125f64c4a8bf60f336b
+patch_url = https://wrapdb.mesonbuild.com/v1/projects/google-benchmark/1.5.2/1/get_zip
+patch_filename = google-benchmark-1.5.2-1-wrap.zip
+patch_hash = 49f41e4a7e68ac258b6509b9de9857441903be4fb473454c4cba8be885f0c6c3
+
diff --git a/subprojects/libpng.wrap b/subprojects/libpng.wrap
index 30774eb..6503e7b 100644
--- a/subprojects/libpng.wrap
+++ b/subprojects/libpng.wrap
@@ -1,10 +1,12 @@
-[wrap-file]
-directory = libpng-1.6.35
-
-source_url = https://github.com/glennrp/libpng/archive/v1.6.35.tar.gz
-source_filename = libpng-1.6.35.tar.gz
-source_hash = 6d59d6a154ccbb772ec11772cb8f8beb0d382b61e7ccc62435bf7311c9f4b210
-
-patch_url = https://wrapdb.mesonbuild.com/v1/projects/libpng/1.6.35/5/get_zip
-patch_filename = libpng-1.6.35-5-wrap.zip
-patch_hash = da42b18e8d75a88615bdbc1c7bbf1f739ae19f63a8e70d96c90bc448326ae6b7
+[wrap-file]

+directory = libpng-1.6.37

+source_url = https://github.com/glennrp/libpng/archive/v1.6.37.tar.gz

+source_filename = libpng-1.6.37.tar.gz

+source_hash = ca74a0dace179a8422187671aee97dd3892b53e168627145271cad5b5ac81307

+patch_url = https://wrapdb.mesonbuild.com/v1/projects/libpng/1.6.37/3/get_zip

+patch_filename = libpng-1.6.37-3-wrap.zip

+patch_hash = 6c9f32fd9150b3a96ab89be52af664e32207e10aa9f5fb9aa015989ee2dd7100

+

+[provide]

+libpng = libpng_dep

+

diff --git a/subprojects/zlib.wrap b/subprojects/zlib.wrap
index b62b63a..ce20fb0 100644
--- a/subprojects/zlib.wrap
+++ b/subprojects/zlib.wrap
@@ -1,5 +1,12 @@
-[wrap-git]
-directory=zlib
-url=https://github.com/centricular/zlib.git
-depth=1
-revision=meson
+[wrap-file]
+directory = zlib-1.2.11
+source_url = http://zlib.net/fossils/zlib-1.2.11.tar.gz
+source_filename = zlib-1.2.11.tar.gz
+source_hash = c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
+patch_url = https://wrapdb.mesonbuild.com/v1/projects/zlib/1.2.11/5/get_zip
+patch_filename = zlib-1.2.11-5-wrap.zip
+patch_hash = 728c8e24acbc2e6682fbd950fec39e2fc77528af361adb87259f8a8511434004
+
+[provide]
+zlib = zlib_dep
+
diff --git a/test/api/test-buffer.c b/test/api/test-buffer.c
index 228f0f3..6c90109 100644
--- a/test/api/test-buffer.c
+++ b/test/api/test-buffer.c
@@ -71,7 +71,7 @@
 
     case BUFFER_ONE_BY_ONE:
       for (i = 1; i < G_N_ELEMENTS (utf32) - 1; i++)
-	hb_buffer_add (b, utf32[i], i);
+      hb_buffer_add (b, utf32[i], i);
       break;
 
     case BUFFER_UTF32:
@@ -856,6 +856,75 @@
   g_assert (!hb_buffer_allocation_successful (b));
 }
 
+typedef struct {
+  const char *contents;
+  hb_buffer_serialize_format_t format;
+  unsigned int num_items;
+  hb_bool_t success;
+} serialization_test_t;
+
+static const serialization_test_t serialization_tests[] = {
+  { "<U+0640=0|U+0635=1>", HB_BUFFER_SERIALIZE_FORMAT_TEXT, 2, 1 },
+  { "[{\"u\":1600,\"cl\":0},{\"u\":1589,\"cl\":1}]", HB_BUFFER_SERIALIZE_FORMAT_JSON, 2, 1 },
+
+  /* Mixed glyphs/Unicodes -> parse fail */
+  { "[{\"u\":1600,\"cl\":0},{\"g\":1589,\"cl\":1}]", HB_BUFFER_SERIALIZE_FORMAT_JSON, 0, 0 },
+  { "<U+0640=0|uni0635=1>", HB_BUFFER_SERIALIZE_FORMAT_TEXT, 0, 0 },
+};
+
+static void
+test_buffer_serialize_deserialize (void)
+{
+  hb_buffer_t *b;
+  unsigned int i;
+
+  for (i = 0; i < G_N_ELEMENTS (serialization_tests); i++)
+  {
+    unsigned int consumed;
+    char round_trip[1024];
+    hb_bool_t retval;
+
+    b = hb_buffer_create ();
+    hb_buffer_set_replacement_codepoint (b, (hb_codepoint_t) -1);
+
+    const serialization_test_t *test = &serialization_tests[i];
+    g_test_message ("serialize test #%d", i);
+
+    retval = hb_buffer_deserialize_unicode (b, test->contents, -1, NULL, test->format);
+
+    // Expected parse failure, got one, don't round-trip
+    if (test->success != 0)
+    {
+      unsigned int num_glyphs = hb_buffer_get_length (b);
+      g_assert_cmpint (num_glyphs, ==, test->num_items);
+
+      hb_buffer_serialize_unicode (b, 0, num_glyphs, round_trip,
+				   sizeof(round_trip), &consumed, test->format,
+				   HB_BUFFER_SERIALIZE_FLAG_DEFAULT);
+      g_assert_cmpstr (round_trip, ==, test->contents);
+    }
+
+    hb_buffer_destroy (b);
+
+  }
+
+  char test[1024];
+  unsigned int consumed;
+  hb_buffer_t *indeterminate = hb_buffer_get_empty ();
+  hb_buffer_serialize (indeterminate, 0, (unsigned) -1,
+		       test, sizeof(test), &consumed, NULL,
+		       HB_BUFFER_SERIALIZE_FORMAT_JSON,
+		       HB_BUFFER_SERIALIZE_FLAG_DEFAULT);
+  g_assert_cmpstr ( test, ==, "[]");
+
+  hb_buffer_serialize (indeterminate, 0, (unsigned) - 1,
+		       test, sizeof(test), &consumed, NULL,
+		       HB_BUFFER_SERIALIZE_FORMAT_TEXT,
+		       HB_BUFFER_SERIALIZE_FLAG_DEFAULT);
+  g_assert_cmpstr ( test, ==, "!!");
+
+}
+
 int
 main (int argc, char **argv)
 {
@@ -880,6 +949,7 @@
   hb_test_add (test_buffer_utf16_conversion);
   hb_test_add (test_buffer_utf32_conversion);
   hb_test_add (test_buffer_empty);
+  hb_test_add (test_buffer_serialize_deserialize);
 
   return hb_test_run();
 }
diff --git a/test/api/test-ot-tag.c b/test/api/test-ot-tag.c
index 6669ea9..75131ab 100644
--- a/test/api/test-ot-tag.c
+++ b/test/api/test-ot-tag.c
@@ -335,6 +335,8 @@
   test_language_two_way ("FAR", "fa");
   test_tag_from_language ("FAR", "fa_IR");
 
+  test_language_two_way ("MNK", "man"); /* Mandingo [macrolanguage] */
+
   test_language_two_way ("SWA", "aii"); /* Swadaya Aramaic */
 
   test_language_two_way ("SYR", "syr"); /* Syriac [macrolanguage] */
@@ -347,10 +349,12 @@
   test_tag_from_language ("ZHS", "zh"); /* Chinese */
   test_tag_from_language ("ZHS", "zh-cn"); /* Chinese (China) */
   test_tag_from_language ("ZHS", "zh-sg"); /* Chinese (Singapore) */
-  test_tag_from_language ("ZHH", "zh-mo"); /* Chinese (Macao) */
-  test_tag_from_language ("ZHH", "zh-hant-mo"); /* Chinese (Macao) */
+  test_tag_from_language ("ZHTM", "zh-mo"); /* Chinese (Macao) */
+  test_tag_from_language ("ZHTM", "zh-hant-mo"); /* Chinese (Macao) */
+  test_tag_from_language ("ZHS", "zh-hans-mo"); /* Chinese (Simplified, Macao) */
   test_language_two_way ("ZHH", "zh-HK"); /* Chinese (Hong Kong) */
   test_tag_from_language ("ZHH", "zH-HanT-hK"); /* Chinese (Hong Kong) */
+  test_tag_from_language ("ZHS", "zH-HanS-hK"); /* Chinese (Simplified, Hong Kong) */
   test_tag_from_language ("ZHT", "zh-tw"); /* Chinese (Taiwan) */
   test_language_two_way ("ZHS", "zh-Hans"); /* Chinese (Simplified) */
   test_language_two_way ("ZHT", "zh-Hant"); /* Chinese (Traditional) */
@@ -479,6 +483,10 @@
   test_tag_from_language ("ZHS", "zh-min-nan");
   test_tag_from_language ("ZHS", "zh-xiang");
 
+  /* BCP 47 tags that look similar to unrelated language system tags */
+  test_tag_from_language ("SQI", "als");
+  test_tag_from_language ("dflt", "far");
+
   /* A UN M.49 region code, not an extended language subtag */
   test_tag_from_language ("ARA", "ar-001");
 
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5345734743031808 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5345734743031808
new file mode 100644
index 0000000..193cf89
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5345734743031808
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5741295280848896 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5741295280848896
deleted file mode 100644
index 0c5c734..0000000
--- a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5741295280848896
+++ /dev/null
Binary files differ
diff --git a/test/fuzzing/hb-set-fuzzer.cc b/test/fuzzing/hb-set-fuzzer.cc
index 79b9fc5..0a547f3 100644
--- a/test/fuzzing/hb-set-fuzzer.cc
+++ b/test/fuzzing/hb-set-fuzzer.cc
@@ -7,6 +7,12 @@
 
 #include "hb.h"
 
+// Only allow ~5,000 set values between the two input sets.
+// Arbitarily long input sets do not trigger any meaningful
+// differences in behaviour so there's no benefit from allowing
+// the fuzzer to create super large sets.
+#define MAX_INPUT_SIZE 20000
+
 enum set_operation_t : uint8_t
 {
   INTERSECT = 0,
@@ -37,6 +43,9 @@
   if (size < sizeof (instructions_t))
     return 0;
 
+  if (size > MAX_INPUT_SIZE)
+    return 0;
+
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
   const instructions_t &instructions = reinterpret_cast<const instructions_t &> (data);
diff --git a/test/fuzzing/meson.build b/test/fuzzing/meson.build
index 8f69885..9eaf3fa 100644
--- a/test/fuzzing/meson.build
+++ b/test/fuzzing/meson.build
@@ -31,7 +31,6 @@
 
 env = environment()
 env.set('srcdir', meson.current_source_dir())
-env.set('HB_TEST_SUBSET_FUZZER_TIMEOUT', '50')
 
 test('shape_fuzzer', find_program('run-shape-fuzzer-tests.py'),
   args: [
diff --git a/test/fuzzing/run-draw-fuzzer-tests.py b/test/fuzzing/run-draw-fuzzer-tests.py
index acae7a8..8b5a2e8 100755
--- a/test/fuzzing/run-draw-fuzzer-tests.py
+++ b/test/fuzzing/run-draw-fuzzer-tests.py
@@ -9,7 +9,7 @@
 		p = subprocess.Popen (command, stderr=tempf)
 
 		try:
-			p.wait (timeout=int (os.getenv ("HB_TEST_SHAPE_FUZZER_TIMEOUT", "2")))
+			p.wait ()
 			tempf.seek (0)
 			text = tempf.read ()
 
diff --git a/test/fuzzing/run-shape-fuzzer-tests.py b/test/fuzzing/run-shape-fuzzer-tests.py
index 348d138..382f609 100755
--- a/test/fuzzing/run-shape-fuzzer-tests.py
+++ b/test/fuzzing/run-shape-fuzzer-tests.py
@@ -9,7 +9,7 @@
 		p = subprocess.Popen (command, stderr=tempf)
 
 		try:
-			p.wait (timeout=int (os.getenv ("HB_TEST_SHAPE_FUZZER_TIMEOUT", "2")))
+			p.wait ()
 			tempf.seek (0)
 			text = tempf.read ()
 
diff --git a/test/fuzzing/run-subset-fuzzer-tests.py b/test/fuzzing/run-subset-fuzzer-tests.py
index 800a436..da7d1e5 100755
--- a/test/fuzzing/run-subset-fuzzer-tests.py
+++ b/test/fuzzing/run-subset-fuzzer-tests.py
@@ -9,7 +9,7 @@
 		p = subprocess.Popen (command, stderr=tempf)
 
 		try:
-			p.wait (timeout=int (os.getenv ("HB_TEST_SUBSET_FUZZER_TIMEOUT", "12")))
+			p.wait ()
 			tempf.seek (0)
 			text = tempf.read ()
 
diff --git a/test/shaping/data/in-house/tests/use-syllable.tests b/test/shaping/data/in-house/tests/use-syllable.tests
index 61f2887..10f9e06 100644
--- a/test/shaping/data/in-house/tests/use-syllable.tests
+++ b/test/shaping/data/in-house/tests/use-syllable.tests
@@ -18,3 +18,5 @@
 ../fonts/573d3a3177c9a8646e94c8a0d7b224334340946a.ttf:--font-funcs=ft:U+11410,U+11442,U+200C,U+034F,U+11411:[Ga=0+576|Virama=0@70,70+0|Gha=4+566]
 ../fonts/573d3a3177c9a8646e94c8a0d7b224334340946a.ttf:--font-funcs=ft:U+11410,U+200C,U+11442,U+034F,U+11411:[Ga.icd=0+367|Gha.diag=1@100,0+386]
 ../fonts/e68a88939e0f06e34d2bc911f09b70890289c8fd.ttf::U+AA00,U+200C,U+AA34:[raMedial_cham_pre=0+400|a_cham=0+1121]
+../fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf::U+11124,U+200D,U+11127:[u11124=0+514|u11127=0+0]
+../fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf::U+11124,U+2060,U+11127:[u11124=0+514|u11127=1+0]
diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am
index 47989b1..133971a 100644
--- a/test/subset/data/Makefile.am
+++ b/test/subset/data/Makefile.am
@@ -20,6 +20,7 @@
 	expected/layout.gpos5 \
 	expected/layout.gpos6 \
 	expected/layout.gpos8 \
+	expected/layout.gpos8.amiri \
 	expected/layout.gsub3 \
 	expected/layout.gsub6 \
 	expected/layout.gdef \
diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources
index 718e719..46680a5 100644
--- a/test/subset/data/Makefile.sources
+++ b/test/subset/data/Makefile.sources
@@ -19,6 +19,7 @@
 	tests/layout.gpos5.tests \
 	tests/layout.gpos6.tests \
 	tests/layout.gpos8.tests \
+	tests/layout.gpos8.amiri.tests \
 	tests/layout.gsub3.tests \
 	tests/layout.gsub6.tests \
 	tests/layout.tests \
diff --git a/test/subset/data/expected/layout.context/gpos_context1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.context/gpos_context1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
index eff7821..6354eff 100644
--- a/test/subset/data/expected/layout.context/gpos_context1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.context/gpos_context1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.context/gpos_context1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.context/gpos_context1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
index eff7821..6354eff 100644
--- a/test/subset/data/expected/layout.context/gpos_context1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.context/gpos_context1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.context/gpos_context2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.context/gpos_context2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
index 339bfe8..0691569 100644
--- a/test/subset/data/expected/layout.context/gpos_context2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.context/gpos_context2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.context/gpos_context2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.context/gpos_context2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
index 339bfe8..0691569 100644
--- a/test/subset/data/expected/layout.context/gpos_context2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.context/gpos_context2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.context/gpos_context3_simple_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.context/gpos_context3_simple_f1.keep-layout-retain-gids.retain-all-codepoint.otf
index 5588fd3..684e991 100644
--- a/test/subset/data/expected/layout.context/gpos_context3_simple_f1.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.context/gpos_context3_simple_f1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.context/gpos_context3_simple_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.context/gpos_context3_simple_f1.keep-layout.retain-all-codepoint.otf
index 5588fd3..684e991 100644
--- a/test/subset/data/expected/layout.context/gpos_context3_simple_f1.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.context/gpos_context3_simple_f1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.retain-all-codepoint.otf
index d9b5dfb..8dbc820 100644
--- a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.retain-all-codepoint.otf
index d9b5dfb..8dbc820 100644
--- a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.retain-all-codepoint.otf
index d4f9fc0..3765d20 100644
--- a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.retain-all-codepoint.otf
index d4f9fc0..3765d20 100644
--- a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos6/gpos6_font1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos6/gpos6_font1.keep-layout-retain-gids.retain-all-codepoint.otf
index f636342..0feba1b 100644
--- a/test/subset/data/expected/layout.gpos6/gpos6_font1.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos6/gpos6_font1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos6/gpos6_font1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos6/gpos6_font1.keep-layout.retain-all-codepoint.otf
index f636342..0feba1b 100644
--- a/test/subset/data/expected/layout.gpos6/gpos6_font1.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos6/gpos6_font1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.627,644,623,62D,644,627,645,2E.ttf b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.627,644,623,62D,644,627,645,2E.ttf
new file mode 100644
index 0000000..f361131
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.627,644,623,62D,644,627,645,2E.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.627,644,62D,628.ttf b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.627,644,62D,628.ttf
new file mode 100644
index 0000000..d298491
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.627,644,62D,628.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.627,644.ttf b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.627,644.ttf
new file mode 100644
index 0000000..1b90606
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.627,644.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.633,645,627,621,20,644,627.ttf b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.633,645,627,621,20,644,627.ttf
new file mode 100644
index 0000000..969fda2
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.633,645,627,621,20,644,627.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.63A,64A,631.ttf b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.63A,64A,631.ttf
new file mode 100644
index 0000000..54ef721
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout-retain-gids.63A,64A,631.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.627,644,623,62D,644,627,645,2E.ttf b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.627,644,623,62D,644,627,645,2E.ttf
new file mode 100644
index 0000000..57ef191
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.627,644,623,62D,644,627,645,2E.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.627,644,62D,628.ttf b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.627,644,62D,628.ttf
new file mode 100644
index 0000000..a9c3d36
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.627,644,62D,628.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.627,644.ttf b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.627,644.ttf
new file mode 100644
index 0000000..a535889
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.627,644.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.633,645,627,621,20,644,627.ttf b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.633,645,627,621,20,644,627.ttf
new file mode 100644
index 0000000..0577408
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.633,645,627,621,20,644,627.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.63A,64A,631.ttf b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.63A,64A,631.ttf
new file mode 100644
index 0000000..8e4c014
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos8.amiri/Amiri-Regular.keep-layout.63A,64A,631.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8/gpos_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos8/gpos_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
index 3fabe71..aee1579 100644
--- a/test/subset/data/expected/layout.gpos8/gpos_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos8/gpos_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8/gpos_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos8/gpos_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
index 3fabe71..aee1579 100644
--- a/test/subset/data/expected/layout.gpos8/gpos_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos8/gpos_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8/gpos_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos8/gpos_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
index 53dccf9..48b30c7 100644
--- a/test/subset/data/expected/layout.gpos8/gpos_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos8/gpos_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8/gpos_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos8/gpos_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
index 53dccf9..48b30c7 100644
--- a/test/subset/data/expected/layout.gpos8/gpos_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos8/gpos_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8/gpos_chaining3_simple_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos8/gpos_chaining3_simple_f1.keep-layout-retain-gids.retain-all-codepoint.otf
index e7cc68d..2f0e4b9 100644
--- a/test/subset/data/expected/layout.gpos8/gpos_chaining3_simple_f1.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos8/gpos_chaining3_simple_f1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos8/gpos_chaining3_simple_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos8/gpos_chaining3_simple_f1.keep-layout.retain-all-codepoint.otf
index e7cc68d..2f0e4b9 100644
--- a/test/subset/data/expected/layout.gpos8/gpos_chaining3_simple_f1.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gpos8/gpos_chaining3_simple_f1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
index 1f90754..3e7ecef 100644
--- a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
index 1f90754..3e7ecef 100644
--- a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
index e764393..6b55fe6 100644
--- a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
index e764393..6b55fe6 100644
--- a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.retain-all-codepoint.otf
index 737f85a..2af5abb 100644
--- a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.retain-all-codepoint.otf
index 737f85a..2af5abb 100644
--- a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.retain-all-codepoint.otf
+++ b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41,42,43.ttf b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41,42,43.ttf
index aa007ba..95e055f 100644
--- a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41,42,43.ttf
+++ b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41,42,43.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41,43.ttf b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41,43.ttf
index f3be30c..1702a10 100644
--- a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41,43.ttf
+++ b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41,43.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41.ttf b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41.ttf
index 44c329e..c03e8cb 100644
--- a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41.ttf
+++ b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.41.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.43.ttf b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.43.ttf
index b0a1ea3..be5e6d9 100644
--- a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.43.ttf
+++ b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.43.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.CA,CB.ttf b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.CA,CB.ttf
index 16ad9d5..9798bc4 100644
--- a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.CA,CB.ttf
+++ b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout-retain-gids.CA,CB.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41,42,43.ttf b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41,42,43.ttf
index d0d9d5a..33aa3f7 100644
--- a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41,42,43.ttf
+++ b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41,42,43.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41,43.ttf b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41,43.ttf
index f4d881f..c207f09 100644
--- a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41,43.ttf
+++ b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41,43.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41.ttf b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41.ttf
index 9e6dd28..70251b3 100644
--- a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41.ttf
+++ b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.41.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.43.ttf b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.43.ttf
index 50260c5..2109faf 100644
--- a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.43.ttf
+++ b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.43.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.CA,CB.ttf b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.CA,CB.ttf
index 22d5b61..16c6d43 100644
--- a/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.CA,CB.ttf
+++ b/test/subset/data/expected/layout/Roboto-Regular.smallcaps.keep-layout.CA,CB.ttf
Binary files differ
diff --git a/test/subset/data/fonts/Amiri-Regular.ttf b/test/subset/data/fonts/Amiri-Regular.ttf
new file mode 100644
index 0000000..47cb4e1
--- /dev/null
+++ b/test/subset/data/fonts/Amiri-Regular.ttf
Binary files differ
diff --git a/test/subset/data/profiles/keep-layout-retain-gids.txt b/test/subset/data/profiles/keep-layout-retain-gids.txt
index f4787ad..9248ef6 100644
--- a/test/subset/data/profiles/keep-layout-retain-gids.txt
+++ b/test/subset/data/profiles/keep-layout-retain-gids.txt
@@ -1,2 +1,2 @@
---drop-tables-=GSUB,GPOS
+--drop-tables-=GSUB,GPOS,GDEF
 --retain-gids
diff --git a/test/subset/data/profiles/keep-layout.txt b/test/subset/data/profiles/keep-layout.txt
index 56da0ff..969e34a 100644
--- a/test/subset/data/profiles/keep-layout.txt
+++ b/test/subset/data/profiles/keep-layout.txt
@@ -1 +1 @@
---drop-tables-=GSUB,GPOS
+--drop-tables-=GSUB,GPOS,GDEF
diff --git a/test/subset/data/tests/layout.gpos8.amiri.tests b/test/subset/data/tests/layout.gpos8.amiri.tests
new file mode 100644
index 0000000..a885cb4
--- /dev/null
+++ b/test/subset/data/tests/layout.gpos8.amiri.tests
@@ -0,0 +1,13 @@
+FONTS:
+Amiri-Regular.ttf
+
+PROFILES:
+keep-layout.txt
+keep-layout-retain-gids.txt
+
+SUBSETS:
+ال
+الأحلام.
+غير
+سماء لا
+الحب
diff --git a/test/subset/meson.build b/test/subset/meson.build
index 48d8558..de28fda 100644
--- a/test/subset/meson.build
+++ b/test/subset/meson.build
@@ -12,6 +12,7 @@
   'layout.gpos5',
   'layout.gpos6',
   'layout.gpos8',
+  'layout.gpos8.amiri',
   'layout.gsub3',
   'layout.gsub6',
   'layout.gdef',
diff --git a/util/hb-shape.cc b/util/hb-shape.cc
index 78488f4..01b3d45 100644
--- a/util/hb-shape.cc
+++ b/util/hb-shape.cc
@@ -137,7 +137,7 @@
     g_string_set_size (gs, 0);
     format.serialize_line_no (line_no, gs);
     g_string_append_printf (gs, "trace: %s	buffer: ", message);
-    format.serialize_glyphs (buffer, font, output_format, format_flags, gs);
+    format.serialize (buffer, font, output_format, format_flags, gs);
     g_string_append_c (gs, '\n');
     fprintf (options.fp, "%s", gs->str);
   }
diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index 253e48a..97a3a2f 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -70,27 +70,27 @@
 
   hb_bool_t
   write_file (const char *output_file, hb_blob_t *blob) {
-    unsigned int data_length;
-    const char* data = hb_blob_get_data (blob, &data_length);
+    unsigned int size;
+    const char* data = hb_blob_get_data (blob, &size);
 
-    FILE *fp_out = fopen(output_file, "wb");
-    if (!fp_out) {
-      fprintf(stderr, "Unable to open output file\n");
-      return false;
-    }
-    int bytes_written = fwrite(data, 1, data_length, fp_out);
+    if (!output_file)
+      fail (true, "No output file was specified");
 
-    fclose (fp_out);
+    FILE *fp = fopen(output_file, "wb");
+    if (!fp)
+      fail (false, "Cannot open output file `%s': %s",
+	    g_filename_display_name (output_file), strerror (errno));
 
-    if (bytes_written == -1) {
-      fprintf(stderr, "Unable to write output file\n");
-      return false;
+    while (size) {
+      size_t ret = fwrite (data, 1, size, fp);
+      size -= ret;
+      data += ret;
+      if (size && ferror (fp))
+        fail (false, "Failed to write output: %s", strerror (errno));
     }
-    if ((unsigned int) bytes_written != data_length) {
-      fprintf(stderr, "Expected %u bytes written, got %d\n", data_length,
-	      bytes_written);
-      return false;
-    }
+
+    fclose (fp);
+
     return true;
   }
 
diff --git a/util/options.cc b/util/options.cc
index ee09ac5..caf8233 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -910,31 +910,12 @@
 }
 
 void
-format_options_t::serialize_unicode (hb_buffer_t *buffer,
-				     GString     *gs)
-{
-  unsigned int num_glyphs = hb_buffer_get_length (buffer);
-  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
-
-  g_string_append_c (gs, '<');
-  for (unsigned int i = 0; i < num_glyphs; i++)
-  {
-    if (i)
-      g_string_append_c (gs, ',');
-    g_string_append_printf (gs, "U+%04X", info->codepoint);
-    info++;
-  }
-  g_string_append_c (gs, '>');
-}
-
-void
-format_options_t::serialize_glyphs (hb_buffer_t *buffer,
+format_options_t::serialize (hb_buffer_t *buffer,
 				    hb_font_t   *font,
 				    hb_buffer_serialize_format_t output_format,
 				    hb_buffer_serialize_flags_t flags,
 				    GString     *gs)
 {
-  g_string_append_c (gs, '[');
   unsigned int num_glyphs = hb_buffer_get_length (buffer);
   unsigned int start = 0;
 
@@ -942,15 +923,15 @@
   {
     char buf[32768];
     unsigned int consumed;
-    start += hb_buffer_serialize_glyphs (buffer, start, num_glyphs,
+    start += hb_buffer_serialize (buffer, start, num_glyphs,
 					 buf, sizeof (buf), &consumed,
 					 font, output_format, flags);
     if (!consumed)
       break;
     g_string_append (gs, buf);
   }
-  g_string_append_c (gs, ']');
 }
+
 void
 format_options_t::serialize_line_no (unsigned int  line_no,
 				     GString      *gs)
@@ -978,7 +959,7 @@
   if (show_unicode)
   {
     serialize_line_no (line_no, gs);
-    serialize_unicode (buffer, gs);
+    serialize (buffer, font, HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT, gs);
     g_string_append_c (gs, '\n');
   }
 }
@@ -1003,6 +984,6 @@
 					      GString      *gs)
 {
   serialize_line_no (line_no, gs);
-  serialize_glyphs (buffer, font, output_format, format_flags, gs);
+  serialize (buffer, font, output_format, format_flags, gs);
   g_string_append_c (gs, '\n');
 }
diff --git a/util/options.hh b/util/options.hh
index 2c10578..30b8f5b 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -635,9 +635,7 @@
 
   void add_options (option_parser_t *parser) override;
 
-  void serialize_unicode (hb_buffer_t  *buffer,
-			  GString      *gs);
-  void serialize_glyphs (hb_buffer_t  *buffer,
+  void serialize (hb_buffer_t  *buffer,
 			 hb_font_t    *font,
 			 hb_buffer_serialize_format_t format,
 			 hb_buffer_serialize_flags_t flags,